@@ -81,6 +81,12 @@ static const struct chan_ops not_configged_ops = {
8181};
8282#endif /* CONFIG_NOCONFIG_CHAN */
8383
84+ static inline bool need_output_blocking (void )
85+ {
86+ return time_travel_mode == TT_MODE_INFCPU ||
87+ time_travel_mode == TT_MODE_EXTERNAL ;
88+ }
89+
8490static int open_one_chan (struct chan * chan )
8591{
8692 int fd , err ;
@@ -96,15 +102,43 @@ static int open_one_chan(struct chan *chan)
96102 return fd ;
97103
98104 err = os_set_fd_block (fd , 0 );
99- if (err ) {
100- (* chan -> ops -> close )(fd , chan -> data );
101- return err ;
102- }
105+ if (err )
106+ goto out_close ;
107+
108+ chan -> fd_in = fd ;
109+ chan -> fd_out = fd ;
110+
111+ /*
112+ * In time-travel modes infinite-CPU and external we need to guarantee
113+ * that any writes to the output succeed immdiately from the point of
114+ * the VM. The best way to do this is to put the FD in blocking mode
115+ * and simply wait/retry until everything is written.
116+ * As every write is guaranteed to complete, we also do not need to
117+ * request an IRQ for the output.
118+ *
119+ * Note that input cannot happen in a time synchronized way. We permit
120+ * it, but time passes very quickly if anything waits for a read.
121+ */
122+ if (chan -> output && need_output_blocking ()) {
123+ err = os_dup_file (chan -> fd_out );
124+ if (err < 0 )
125+ goto out_close ;
103126
104- chan -> fd = fd ;
127+ chan -> fd_out = err ;
128+
129+ err = os_set_fd_block (chan -> fd_out , 1 );
130+ if (err ) {
131+ os_close_file (chan -> fd_out );
132+ goto out_close ;
133+ }
134+ }
105135
106136 chan -> opened = 1 ;
107137 return 0 ;
138+
139+ out_close :
140+ (* chan -> ops -> close )(fd , chan -> data );
141+ return err ;
108142}
109143
110144static int open_chan (struct list_head * chans )
@@ -125,7 +159,7 @@ static int open_chan(struct list_head *chans)
125159void chan_enable_winch (struct chan * chan , struct tty_port * port )
126160{
127161 if (chan && chan -> primary && chan -> ops -> winch )
128- register_winch (chan -> fd , port );
162+ register_winch (chan -> fd_in , port );
129163}
130164
131165static void line_timer_cb (struct work_struct * work )
@@ -156,8 +190,9 @@ int enable_chan(struct line *line)
156190
157191 if (chan -> enabled )
158192 continue ;
159- err = line_setup_irq (chan -> fd , chan -> input , chan -> output , line ,
160- chan );
193+ err = line_setup_irq (chan -> fd_in , chan -> input ,
194+ chan -> output && !need_output_blocking (),
195+ line , chan );
161196 if (err )
162197 goto out_close ;
163198
@@ -196,7 +231,8 @@ void free_irqs(void)
196231
197232 if (chan -> input && chan -> enabled )
198233 um_free_irq (chan -> line -> read_irq , chan );
199- if (chan -> output && chan -> enabled )
234+ if (chan -> output && chan -> enabled &&
235+ !need_output_blocking ())
200236 um_free_irq (chan -> line -> write_irq , chan );
201237 chan -> enabled = 0 ;
202238 }
@@ -216,15 +252,19 @@ static void close_one_chan(struct chan *chan, int delay_free_irq)
216252 } else {
217253 if (chan -> input && chan -> enabled )
218254 um_free_irq (chan -> line -> read_irq , chan );
219- if (chan -> output && chan -> enabled )
255+ if (chan -> output && chan -> enabled &&
256+ !need_output_blocking ())
220257 um_free_irq (chan -> line -> write_irq , chan );
221258 chan -> enabled = 0 ;
222259 }
260+ if (chan -> fd_out != chan -> fd_in )
261+ os_close_file (chan -> fd_out );
223262 if (chan -> ops -> close != NULL )
224- (* chan -> ops -> close )(chan -> fd , chan -> data );
263+ (* chan -> ops -> close )(chan -> fd_in , chan -> data );
225264
226265 chan -> opened = 0 ;
227- chan -> fd = -1 ;
266+ chan -> fd_in = -1 ;
267+ chan -> fd_out = -1 ;
228268}
229269
230270void close_chan (struct line * line )
@@ -244,7 +284,7 @@ void close_chan(struct line *line)
244284void deactivate_chan (struct chan * chan , int irq )
245285{
246286 if (chan && chan -> enabled )
247- deactivate_fd (chan -> fd , irq );
287+ deactivate_fd (chan -> fd_in , irq );
248288}
249289
250290int write_chan (struct chan * chan , const u8 * buf , size_t len , int write_irq )
@@ -254,7 +294,7 @@ int write_chan(struct chan *chan, const u8 *buf, size_t len, int write_irq)
254294 if (len == 0 || !chan || !chan -> ops -> write )
255295 return 0 ;
256296
257- n = chan -> ops -> write (chan -> fd , buf , len , chan -> data );
297+ n = chan -> ops -> write (chan -> fd_out , buf , len , chan -> data );
258298 if (chan -> primary ) {
259299 ret = n ;
260300 }
@@ -268,7 +308,7 @@ int console_write_chan(struct chan *chan, const char *buf, int len)
268308 if (!chan || !chan -> ops -> console_write )
269309 return 0 ;
270310
271- n = chan -> ops -> console_write (chan -> fd , buf , len );
311+ n = chan -> ops -> console_write (chan -> fd_out , buf , len );
272312 if (chan -> primary )
273313 ret = n ;
274314 return ret ;
@@ -296,14 +336,14 @@ int chan_window_size(struct line *line, unsigned short *rows_out,
296336 if (chan && chan -> primary ) {
297337 if (chan -> ops -> window_size == NULL )
298338 return 0 ;
299- return chan -> ops -> window_size (chan -> fd , chan -> data ,
339+ return chan -> ops -> window_size (chan -> fd_in , chan -> data ,
300340 rows_out , cols_out );
301341 }
302342 chan = line -> chan_out ;
303343 if (chan && chan -> primary ) {
304344 if (chan -> ops -> window_size == NULL )
305345 return 0 ;
306- return chan -> ops -> window_size (chan -> fd , chan -> data ,
346+ return chan -> ops -> window_size (chan -> fd_in , chan -> data ,
307347 rows_out , cols_out );
308348 }
309349 return 0 ;
@@ -319,7 +359,7 @@ static void free_one_chan(struct chan *chan)
319359 (* chan -> ops -> free )(chan -> data );
320360
321361 if (chan -> primary && chan -> output )
322- ignore_sigio_fd (chan -> fd );
362+ ignore_sigio_fd (chan -> fd_in );
323363 kfree (chan );
324364}
325365
@@ -478,7 +518,8 @@ static struct chan *parse_chan(struct line *line, char *str, int device,
478518 .output = 0 ,
479519 .opened = 0 ,
480520 .enabled = 0 ,
481- .fd = -1 ,
521+ .fd_in = -1 ,
522+ .fd_out = -1 ,
482523 .ops = ops ,
483524 .data = data });
484525 return chan ;
@@ -549,7 +590,7 @@ void chan_interrupt(struct line *line, int irq)
549590 schedule_delayed_work (& line -> task , 1 );
550591 goto out ;
551592 }
552- err = chan -> ops -> read (chan -> fd , & c , chan -> data );
593+ err = chan -> ops -> read (chan -> fd_in , & c , chan -> data );
553594 if (err > 0 )
554595 tty_insert_flip_char (port , c , TTY_NORMAL );
555596 } while (err > 0 );
0 commit comments