Skip to content

Commit c8177ab

Browse files
jmberg-intelrichardweinberger
authored andcommitted
um: time-travel: rework interrupt handling in ext mode
In external time-travel mode, where time is controlled via the controller application socket, interrupt handling is a little tricky. For example on virtio, the following happens: * we receive a message (that requires an ACK) on the vhost-user socket * we add a time-travel event to handle the interrupt (this causes communication on the time socket) * we ACK the original vhost-user message * we then handle the interrupt once the event is triggered This protocol ensures that the sender of the interrupt only continues to run in the simulation when the time-travel event has been added. So far, this was only done in the virtio driver, but it was actually wrong, because only virtqueue interrupts were handled this way, and config change interrupts were handled immediately. Additionally, the messages were actually handled in the real Linux interrupt handler, but Linux interrupt handlers are part of the simulation and shouldn't run while there's no time event. To really do this properly and only handle all kinds of interrupts in the time-travel event when we are scheduled to run in the simulation, rework this to plug in to the lower interrupt layers in UML directly: Add a um_request_irq_tt() function that let's a time-travel aware driver request an interrupt with an additional timetravel_handler() that is called outside of the context of the simulation, to handle the message only. It then adds an event to the time-travel calendar if necessary, and no "real" Linux code runs outside of the time simulation. This also hooks in with suspend/resume properly now, since this new timetravel_handler() can run while Linux is suspended and interrupts are disabled, and decide to wake up (or not) the system based on the message it received. Importantly in this case, it ACKs the message before the system even resumes and interrupts are re-enabled, thus allowing the simulation to progress properly. Signed-off-by: Johannes Berg <[email protected]> Signed-off-by: Richard Weinberger <[email protected]>
1 parent 9b84512 commit c8177ab

File tree

5 files changed

+267
-69
lines changed

5 files changed

+267
-69
lines changed

arch/um/drivers/virtio_uml.c

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -55,16 +55,14 @@ struct virtio_uml_device {
5555
u64 protocol_features;
5656
u8 status;
5757
u8 registered:1;
58+
59+
u8 config_changed_irq:1;
60+
uint64_t vq_irq_vq_map;
5861
};
5962

6063
struct virtio_uml_vq_info {
6164
int kick_fd, call_fd;
6265
char name[32];
63-
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
64-
struct virtqueue *vq;
65-
vq_callback_t *callback;
66-
struct time_travel_event defer;
67-
#endif
6866
bool suspended;
6967
};
7068

@@ -351,9 +349,9 @@ static void vhost_user_reply(struct virtio_uml_device *vu_dev,
351349
rc, size);
352350
}
353351

354-
static irqreturn_t vu_req_interrupt(int irq, void *data)
352+
static irqreturn_t vu_req_read_message(struct virtio_uml_device *vu_dev,
353+
struct time_travel_event *ev)
355354
{
356-
struct virtio_uml_device *vu_dev = data;
357355
struct virtqueue *vq;
358356
int response = 1;
359357
struct {
@@ -371,14 +369,14 @@ static irqreturn_t vu_req_interrupt(int irq, void *data)
371369

372370
switch (msg.msg.header.request) {
373371
case VHOST_USER_SLAVE_CONFIG_CHANGE_MSG:
374-
virtio_config_changed(&vu_dev->vdev);
372+
vu_dev->config_changed_irq = true;
375373
response = 0;
376374
break;
377375
case VHOST_USER_SLAVE_VRING_CALL:
378376
virtio_device_for_each_vq((&vu_dev->vdev), vq) {
379377
if (vq->index == msg.msg.payload.vring_state.index) {
380378
response = 0;
381-
vring_interrupt(0 /* ignored */, vq);
379+
vu_dev->vq_irq_vq_map |= BIT_ULL(vq->index);
382380
break;
383381
}
384382
}
@@ -392,12 +390,45 @@ static irqreturn_t vu_req_interrupt(int irq, void *data)
392390
msg.msg.header.request);
393391
}
394392

393+
if (ev)
394+
time_travel_add_irq_event(ev);
395+
395396
if (msg.msg.header.flags & VHOST_USER_FLAG_NEED_REPLY)
396397
vhost_user_reply(vu_dev, &msg.msg, response);
397398

398399
return IRQ_HANDLED;
399400
}
400401

402+
static irqreturn_t vu_req_interrupt(int irq, void *data)
403+
{
404+
struct virtio_uml_device *vu_dev = data;
405+
irqreturn_t ret = IRQ_HANDLED;
406+
407+
if (!um_irq_timetravel_handler_used())
408+
ret = vu_req_read_message(vu_dev, NULL);
409+
410+
if (vu_dev->vq_irq_vq_map) {
411+
struct virtqueue *vq;
412+
413+
virtio_device_for_each_vq((&vu_dev->vdev), vq) {
414+
if (vu_dev->vq_irq_vq_map & BIT_ULL(vq->index))
415+
vring_interrupt(0 /* ignored */, vq);
416+
}
417+
vu_dev->vq_irq_vq_map = 0;
418+
} else if (vu_dev->config_changed_irq) {
419+
virtio_config_changed(&vu_dev->vdev);
420+
vu_dev->config_changed_irq = false;
421+
}
422+
423+
return ret;
424+
}
425+
426+
static void vu_req_interrupt_comm_handler(int irq, int fd, void *data,
427+
struct time_travel_event *ev)
428+
{
429+
vu_req_read_message(data, ev);
430+
}
431+
401432
static int vhost_user_init_slave_req(struct virtio_uml_device *vu_dev)
402433
{
403434
int rc, req_fds[2];
@@ -408,9 +439,10 @@ static int vhost_user_init_slave_req(struct virtio_uml_device *vu_dev)
408439
return rc;
409440
vu_dev->req_fd = req_fds[0];
410441

411-
rc = um_request_irq(UM_IRQ_ALLOC, vu_dev->req_fd, IRQ_READ,
412-
vu_req_interrupt, IRQF_SHARED,
413-
vu_dev->pdev->name, vu_dev);
442+
rc = um_request_irq_tt(UM_IRQ_ALLOC, vu_dev->req_fd, IRQ_READ,
443+
vu_req_interrupt, IRQF_SHARED,
444+
vu_dev->pdev->name, vu_dev,
445+
vu_req_interrupt_comm_handler);
414446
if (rc < 0)
415447
goto err_close;
416448

@@ -882,23 +914,6 @@ static int vu_setup_vq_call_fd(struct virtio_uml_device *vu_dev,
882914
return rc;
883915
}
884916

885-
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
886-
static void vu_defer_irq_handle(struct time_travel_event *d)
887-
{
888-
struct virtio_uml_vq_info *info;
889-
890-
info = container_of(d, struct virtio_uml_vq_info, defer);
891-
info->callback(info->vq);
892-
}
893-
894-
static void vu_defer_irq_callback(struct virtqueue *vq)
895-
{
896-
struct virtio_uml_vq_info *info = vq->priv;
897-
898-
time_travel_add_irq_event(&info->defer);
899-
}
900-
#endif
901-
902917
static struct virtqueue *vu_setup_vq(struct virtio_device *vdev,
903918
unsigned index, vq_callback_t *callback,
904919
const char *name, bool ctx)
@@ -918,18 +933,6 @@ static struct virtqueue *vu_setup_vq(struct virtio_device *vdev,
918933
snprintf(info->name, sizeof(info->name), "%s.%d-%s", pdev->name,
919934
pdev->id, name);
920935

921-
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
922-
/*
923-
* When we get an interrupt, we must bounce it through the simulation
924-
* calendar (the time-travel=ext:... socket).
925-
*/
926-
if (time_travel_mode == TT_MODE_EXTERNAL && callback) {
927-
info->callback = callback;
928-
callback = vu_defer_irq_callback;
929-
time_travel_set_event_fn(&info->defer, vu_defer_irq_handle);
930-
}
931-
#endif
932-
933936
vq = vring_create_virtqueue(index, num, PAGE_SIZE, vdev, true, true,
934937
ctx, vu_notify, callback, info->name);
935938
if (!vq) {
@@ -938,9 +941,6 @@ static struct virtqueue *vu_setup_vq(struct virtio_device *vdev,
938941
}
939942
vq->priv = info;
940943
num = virtqueue_get_vring_size(vq);
941-
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
942-
info->vq = vq;
943-
#endif
944944

945945
if (vu_dev->protocol_features &
946946
BIT_ULL(VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS)) {
@@ -999,6 +999,10 @@ static int vu_find_vqs(struct virtio_device *vdev, unsigned nvqs,
999999
int i, queue_idx = 0, rc;
10001000
struct virtqueue *vq;
10011001

1002+
/* not supported for now */
1003+
if (WARN_ON(nvqs > 64))
1004+
return -EINVAL;
1005+
10021006
rc = vhost_user_set_mem_table(vu_dev);
10031007
if (rc)
10041008
return rc;

arch/um/include/linux/time-internal.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#ifndef __TIMER_INTERNAL_H__
88
#define __TIMER_INTERNAL_H__
99
#include <linux/list.h>
10+
#include <asm/bug.h>
1011

1112
#define TIMER_MULTIPLIER 256
1213
#define TIMER_MIN_DELTA 500
@@ -74,6 +75,11 @@ static inline void time_travel_propagate_time(void)
7475
static inline void time_travel_wait_readable(int fd)
7576
{
7677
}
78+
79+
static inline void time_travel_add_irq_event(struct time_travel_event *e)
80+
{
81+
WARN_ON(1);
82+
}
7783
#endif /* CONFIG_UML_TIME_TRAVEL_SUPPORT */
7884

7985
/*

arch/um/include/shared/irq_kern.h

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#define __IRQ_KERN_H__
88

99
#include <linux/interrupt.h>
10+
#include <linux/time-internal.h>
1011
#include <asm/ptrace.h>
1112
#include "irq_user.h"
1213

@@ -15,5 +16,64 @@
1516
int um_request_irq(int irq, int fd, enum um_irq_type type,
1617
irq_handler_t handler, unsigned long irqflags,
1718
const char *devname, void *dev_id);
19+
20+
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
21+
/**
22+
* um_request_irq_tt - request an IRQ with timetravel handler
23+
*
24+
* @irq: the IRQ number, or %UM_IRQ_ALLOC
25+
* @fd: The file descriptor to request an IRQ for
26+
* @type: read or write
27+
* @handler: the (generic style) IRQ handler
28+
* @irqflags: Linux IRQ flags
29+
* @devname: name for this to show
30+
* @dev_id: data pointer to pass to the IRQ handler
31+
* @timetravel_handler: the timetravel interrupt handler, invoked with the IRQ
32+
* number, fd, dev_id and time-travel event pointer.
33+
*
34+
* Returns: The interrupt number assigned or a negative error.
35+
*
36+
* Note that the timetravel handler is invoked only if the time_travel_mode is
37+
* %TT_MODE_EXTERNAL, and then it is invoked even while the system is suspended!
38+
* This function must call time_travel_add_irq_event() for the event passed with
39+
* an appropriate delay, before sending an ACK on the socket it was invoked for.
40+
*
41+
* If this was called while the system is suspended, then adding the event will
42+
* cause the system to resume.
43+
*
44+
* Since this function will almost certainly have to handle the FD's condition,
45+
* a read will consume the message, and after that it is up to the code using
46+
* it to pass such a message to the @handler in whichever way it can.
47+
*
48+
* If time_travel_mode is not %TT_MODE_EXTERNAL the @timetravel_handler will
49+
* not be invoked at all and the @handler must handle the FD becoming
50+
* readable (or writable) instead. Use um_irq_timetravel_handler_used() to
51+
* distinguish these cases.
52+
*
53+
* See virtio_uml.c for an example.
54+
*/
55+
int um_request_irq_tt(int irq, int fd, enum um_irq_type type,
56+
irq_handler_t handler, unsigned long irqflags,
57+
const char *devname, void *dev_id,
58+
void (*timetravel_handler)(int, int, void *,
59+
struct time_travel_event *));
60+
#else
61+
static inline
62+
int um_request_irq_tt(int irq, int fd, enum um_irq_type type,
63+
irq_handler_t handler, unsigned long irqflags,
64+
const char *devname, void *dev_id,
65+
void (*timetravel_handler)(int, int, void *,
66+
struct time_travel_event *))
67+
{
68+
return um_request_irq(irq, fd, type, handler, irqflags,
69+
devname, dev_id);
70+
}
71+
#endif
72+
73+
static inline bool um_irq_timetravel_handler_used(void)
74+
{
75+
return time_travel_mode == TT_MODE_EXTERNAL;
76+
}
77+
1878
void um_free_irq(int irq, void *dev_id);
1979
#endif

0 commit comments

Comments
 (0)