Skip to content

Commit b2f9b77

Browse files
benzeajmberg-intel
authored andcommitted
um: chan: use blocking IO for console output for time-travel
When in time-travel mode (infinite-cpu or external) time should not pass for writing to the console. As such, it makes sense to put the FD for the output side into blocking mode and simply let any write to it hang. If we did not do this, then time could pass waiting for the console to become writable again. This is not desirable as it has random effects on the clock between runs. Implement this by duplicating the FD if output is active in a relevant mode and setting the duplicate to be blocking. This avoids changing the input channel to be blocking should it exists. After this, use the blocking FD for all write operations and do not allocate an IRQ it is set. Without time-travel mode fd_out will always match fd_in and IRQs are registered. Signed-off-by: Benjamin Berg <benjamin.berg@intel.com> Link: https://patch.msgid.link/20231018123643.1255813-4-benjamin@sipsolutions.net Signed-off-by: Johannes Berg <johannes.berg@intel.com>
1 parent 4cfb44d commit b2f9b77

4 files changed

Lines changed: 74 additions & 21 deletions

File tree

arch/um/drivers/chan.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ struct chan {
2222
unsigned int output:1;
2323
unsigned int opened:1;
2424
unsigned int enabled:1;
25-
int fd;
25+
int fd_in;
26+
int fd_out; /* only different to fd_in if blocking output is needed */
2627
const struct chan_ops *ops;
2728
void *data;
2829
};

arch/um/drivers/chan_kern.c

Lines changed: 61 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
8490
static 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

110144
static int open_chan(struct list_head *chans)
@@ -125,7 +159,7 @@ static int open_chan(struct list_head *chans)
125159
void 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

131165
static 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

230270
void close_chan(struct line *line)
@@ -244,7 +284,7 @@ void close_chan(struct line *line)
244284
void 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

250290
int 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);

arch/um/include/shared/os.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ extern int os_set_fd_block(int fd, int blocking);
163163
extern int os_accept_connection(int fd);
164164
extern int os_create_unix_socket(const char *file, int len, int close_on_exec);
165165
extern int os_shutdown_socket(int fd, int r, int w);
166+
extern int os_dup_file(int fd);
166167
extern void os_close_file(int fd);
167168
extern int os_rcv_fd(int fd, int *helper_pid_out);
168169
extern int os_connect_socket(const char *name);

arch/um/os-Linux/file.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,16 @@ int os_connect_socket(const char *name)
240240
return err;
241241
}
242242

243+
int os_dup_file(int fd)
244+
{
245+
int new_fd = dup(fd);
246+
247+
if (new_fd < 0)
248+
return -errno;
249+
250+
return new_fd;
251+
}
252+
243253
void os_close_file(int fd)
244254
{
245255
close(fd);

0 commit comments

Comments
 (0)