* [RFC 1/3] um: virtio: implement VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS
@ 2019-09-16 13:10 Johannes Berg
2019-09-16 13:10 ` [RFC 2/3] um: virtio: wrap irq request/free functions Johannes Berg
2019-09-16 13:10 ` [RFC 3/3] um: implement time-travel=virtio Johannes Berg
0 siblings, 2 replies; 3+ messages in thread
From: Johannes Berg @ 2019-09-16 13:10 UTC (permalink / raw)
To: linux-um; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
We'll prefer this feature, under the assumption that only a
few (simulation) devices will ever support it, since it's not
very efficient.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
arch/um/drivers/vhost_user.h | 12 ++++--
arch/um/drivers/virtio_uml.c | 75 +++++++++++++++++++++++++++++-------
2 files changed, 69 insertions(+), 18 deletions(-)
diff --git a/arch/um/drivers/vhost_user.h b/arch/um/drivers/vhost_user.h
index 45ff5ea22fea..ffa6b632d70e 100644
--- a/arch/um/drivers/vhost_user.h
+++ b/arch/um/drivers/vhost_user.h
@@ -10,9 +10,10 @@
/* Feature bits */
#define VHOST_USER_F_PROTOCOL_FEATURES 30
/* Protocol feature bits */
-#define VHOST_USER_PROTOCOL_F_REPLY_ACK 3
-#define VHOST_USER_PROTOCOL_F_SLAVE_REQ 5
-#define VHOST_USER_PROTOCOL_F_CONFIG 9
+#define VHOST_USER_PROTOCOL_F_REPLY_ACK 3
+#define VHOST_USER_PROTOCOL_F_SLAVE_REQ 5
+#define VHOST_USER_PROTOCOL_F_CONFIG 9
+#define VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS 13
/* Vring state index masks */
#define VHOST_USER_VRING_INDEX_MASK 0xff
#define VHOST_USER_VRING_POLL_MASK BIT(8)
@@ -24,7 +25,8 @@
/* Supported protocol features */
#define VHOST_USER_SUPPORTED_PROTOCOL_F (BIT_ULL(VHOST_USER_PROTOCOL_F_REPLY_ACK) | \
BIT_ULL(VHOST_USER_PROTOCOL_F_SLAVE_REQ) | \
- BIT_ULL(VHOST_USER_PROTOCOL_F_CONFIG))
+ BIT_ULL(VHOST_USER_PROTOCOL_F_CONFIG) | \
+ BIT_ULL(VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS))
enum vhost_user_request {
VHOST_USER_GET_FEATURES = 1,
@@ -52,12 +54,14 @@ enum vhost_user_request {
VHOST_USER_SET_VRING_ENDIAN = 23,
VHOST_USER_GET_CONFIG = 24,
VHOST_USER_SET_CONFIG = 25,
+ VHOST_USER_VRING_KICK = 34,
};
enum vhost_user_slave_request {
VHOST_USER_SLAVE_IOTLB_MSG = 1,
VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2,
VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3,
+ VHOST_USER_SLAVE_VRING_CALL = 4,
};
struct vhost_user_header {
diff --git a/arch/um/drivers/virtio_uml.c b/arch/um/drivers/virtio_uml.c
index 1705fad4ad8f..f25bcd147c6b 100644
--- a/arch/um/drivers/virtio_uml.c
+++ b/arch/um/drivers/virtio_uml.c
@@ -53,6 +53,7 @@ struct virtio_uml_device {
struct virtio_device vdev;
struct platform_device *pdev;
+ spinlock_t sock_lock;
int sock, req_fd;
u64 features;
u64 protocol_features;
@@ -188,6 +189,7 @@ static int vhost_user_send(struct virtio_uml_device *vu_dev,
int *fds, size_t num_fds)
{
size_t size = sizeof(msg->header) + msg->header.size;
+ unsigned long flags;
bool request_ack;
int rc;
@@ -206,24 +208,28 @@ static int vhost_user_send(struct virtio_uml_device *vu_dev,
if (request_ack)
msg->header.flags |= VHOST_USER_FLAG_NEED_REPLY;
+ spin_lock_irqsave(&vu_dev->sock_lock, flags);
rc = full_sendmsg_fds(vu_dev->sock, msg, size, fds, num_fds);
if (rc < 0)
- return rc;
+ goto out;
if (request_ack) {
uint64_t status;
rc = vhost_user_recv_u64(vu_dev, &status);
if (rc)
- return rc;
+ goto out;
if (status) {
vu_err(vu_dev, "slave reports error: %llu\n", status);
- return -EIO;
+ rc = -EIO;
+ goto out;
}
}
- return 0;
+out:
+ spin_unlock_irqrestore(&vu_dev->sock_lock, flags);
+ return rc;
}
static int vhost_user_send_no_payload(struct virtio_uml_device *vu_dev,
@@ -323,6 +329,7 @@ static void vhost_user_reply(struct virtio_uml_device *vu_dev,
static irqreturn_t vu_req_interrupt(int irq, void *data)
{
struct virtio_uml_device *vu_dev = data;
+ struct virtqueue *vq;
int response = 1;
struct {
struct vhost_user_msg msg;
@@ -342,6 +349,15 @@ static irqreturn_t vu_req_interrupt(int irq, void *data)
virtio_config_changed(&vu_dev->vdev);
response = 0;
break;
+ case VHOST_USER_SLAVE_VRING_CALL:
+ virtio_device_for_each_vq((&vu_dev->vdev), vq) {
+ if (vq->index == msg.msg.payload.vring_state.index) {
+ response = 0;
+ vring_interrupt(0 /* ignored */, vq);
+ break;
+ }
+ }
+ break;
case VHOST_USER_SLAVE_IOTLB_MSG:
/* not supported - VIRTIO_F_IOMMU_PLATFORM */
case VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG:
@@ -683,6 +699,15 @@ static bool vu_notify(struct virtqueue *vq)
const uint64_t n = 1;
int rc;
+ if (info->kick_fd < 0) {
+ struct virtio_uml_device *vu_dev;
+
+ vu_dev = to_virtio_uml_device(vq->vdev);
+
+ return vhost_user_set_vring_state(vu_dev, VHOST_USER_VRING_KICK,
+ vq->index, 0) == 0;
+ }
+
do {
rc = os_write_file(info->kick_fd, &n, sizeof(n));
} while (rc == -EINTR);
@@ -748,10 +773,13 @@ static void vu_del_vq(struct virtqueue *vq)
{
struct virtio_uml_vq_info *info = vq->priv;
- um_free_irq(VIRTIO_IRQ, vq);
+ if (info->call_fd >= 0) {
+ um_free_irq(VIRTIO_IRQ, vq);
+ os_close_file(info->call_fd);
+ }
- os_close_file(info->call_fd);
- os_close_file(info->kick_fd);
+ if (info->kick_fd >= 0)
+ os_close_file(info->kick_fd);
vring_del_virtqueue(vq);
kfree(info);
@@ -781,6 +809,15 @@ static int vu_setup_vq_call_fd(struct virtio_uml_device *vu_dev,
int call_fds[2];
int rc;
+ /* no call FD needed/desired in this case */
+ if (vu_dev->protocol_features &
+ BIT_ULL(VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS) &&
+ vu_dev->protocol_features &
+ BIT_ULL(VHOST_USER_PROTOCOL_F_SLAVE_REQ)) {
+ info->call_fd = -1;
+ return vhost_user_set_vring_call(vu_dev, vq->index, -1);
+ }
+
/* Use a pipe for call fd, since SIGIO is not supported for eventfd */
rc = os_pipe(call_fds, true, true);
if (rc < 0)
@@ -837,10 +874,15 @@ static struct virtqueue *vu_setup_vq(struct virtio_device *vdev,
vq->priv = info;
num = virtqueue_get_vring_size(vq);
- rc = os_eventfd(0, 0);
- if (rc < 0)
- goto error_kick;
- info->kick_fd = rc;
+ if (vu_dev->protocol_features &
+ BIT_ULL(VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS)) {
+ info->kick_fd = -1;
+ } else {
+ rc = os_eventfd(0, 0);
+ if (rc < 0)
+ goto error_kick;
+ info->kick_fd = rc;
+ }
rc = vu_setup_vq_call_fd(vu_dev, vq);
if (rc)
@@ -865,10 +907,13 @@ static struct virtqueue *vu_setup_vq(struct virtio_device *vdev,
return vq;
error_setup:
- um_free_irq(VIRTIO_IRQ, vq);
- os_close_file(info->call_fd);
+ if (info->call_fd >= 0) {
+ um_free_irq(VIRTIO_IRQ, vq);
+ os_close_file(info->call_fd);
+ }
error_call:
- os_close_file(info->kick_fd);
+ if (info->kick_fd >= 0)
+ os_close_file(info->kick_fd);
error_kick:
vring_del_virtqueue(vq);
error_create:
@@ -1012,6 +1057,8 @@ static int virtio_uml_probe(struct platform_device *pdev)
return rc;
vu_dev->sock = rc;
+ spin_lock_init(&vu_dev->sock_lock);
+
rc = vhost_user_init(vu_dev);
if (rc)
goto error_init;
--
2.20.1
_______________________________________________
linux-um mailing list
linux-um@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-um
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [RFC 2/3] um: virtio: wrap irq request/free functions
2019-09-16 13:10 [RFC 1/3] um: virtio: implement VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS Johannes Berg
@ 2019-09-16 13:10 ` Johannes Berg
2019-09-16 13:10 ` [RFC 3/3] um: implement time-travel=virtio Johannes Berg
1 sibling, 0 replies; 3+ messages in thread
From: Johannes Berg @ 2019-09-16 13:10 UTC (permalink / raw)
To: linux-um; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
For the time-travel=virtio patch, we need some different
logic here - just split out the refactoring to make the
review easier.
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
arch/um/drivers/virtio_uml.c | 32 +++++++++++++++++++++++---------
1 file changed, 23 insertions(+), 9 deletions(-)
diff --git a/arch/um/drivers/virtio_uml.c b/arch/um/drivers/virtio_uml.c
index f25bcd147c6b..c23129b1c220 100644
--- a/arch/um/drivers/virtio_uml.c
+++ b/arch/um/drivers/virtio_uml.c
@@ -373,6 +373,20 @@ static irqreturn_t vu_req_interrupt(int irq, void *data)
return IRQ_HANDLED;
}
+static int vu_request_irq(struct virtio_uml_device *vu_dev,
+ int fd, irq_handler_t handler,
+ const char *name, void *data)
+{
+ return um_request_irq(VIRTIO_IRQ, fd, IRQ_READ, handler,
+ IRQF_SHARED, name, data);
+}
+
+static void vu_free_irq(struct virtio_uml_device *vu_dev,
+ int fd, void *data)
+{
+ um_free_irq(VIRTIO_IRQ, data);
+}
+
static int vhost_user_init_slave_req(struct virtio_uml_device *vu_dev)
{
int rc, req_fds[2];
@@ -383,8 +397,7 @@ static int vhost_user_init_slave_req(struct virtio_uml_device *vu_dev)
return rc;
vu_dev->req_fd = req_fds[0];
- rc = um_request_irq(VIRTIO_IRQ, vu_dev->req_fd, IRQ_READ,
- vu_req_interrupt, IRQF_SHARED,
+ rc = vu_request_irq(vu_dev, vu_dev->req_fd, vu_req_interrupt,
vu_dev->pdev->name, vu_dev);
if (rc)
goto err_close;
@@ -397,7 +410,7 @@ static int vhost_user_init_slave_req(struct virtio_uml_device *vu_dev)
goto out;
err_free_irq:
- um_free_irq(VIRTIO_IRQ, vu_dev);
+ vu_free_irq(vu_dev, vu_dev->req_fd, vu_dev);
err_close:
os_close_file(req_fds[0]);
out:
@@ -771,10 +784,11 @@ static void vu_reset(struct virtio_device *vdev)
static void vu_del_vq(struct virtqueue *vq)
{
+ struct virtio_uml_device *vu_dev = to_virtio_uml_device(vq->vdev);
struct virtio_uml_vq_info *info = vq->priv;
if (info->call_fd >= 0) {
- um_free_irq(VIRTIO_IRQ, vq);
+ vu_free_irq(vu_dev, info->call_fd, vq);
os_close_file(info->call_fd);
}
@@ -824,8 +838,8 @@ static int vu_setup_vq_call_fd(struct virtio_uml_device *vu_dev,
return rc;
info->call_fd = call_fds[0];
- rc = um_request_irq(VIRTIO_IRQ, info->call_fd, IRQ_READ,
- vu_interrupt, IRQF_SHARED, info->name, vq);
+ rc = vu_request_irq(vu_dev, info->call_fd, vu_interrupt,
+ info->name, vq);
if (rc)
goto close_both;
@@ -836,7 +850,7 @@ static int vu_setup_vq_call_fd(struct virtio_uml_device *vu_dev,
goto out;
release_irq:
- um_free_irq(VIRTIO_IRQ, vq);
+ vu_free_irq(vu_dev, info->call_fd, vq);
close_both:
os_close_file(call_fds[0]);
out:
@@ -908,7 +922,7 @@ static struct virtqueue *vu_setup_vq(struct virtio_device *vdev,
error_setup:
if (info->call_fd >= 0) {
- um_free_irq(VIRTIO_IRQ, vq);
+ vu_free_irq(vu_dev, info->call_fd, vq);
os_close_file(info->call_fd);
}
error_call:
@@ -1015,7 +1029,7 @@ static void virtio_uml_release_dev(struct device *d)
/* might not have been opened due to not negotiating the feature */
if (vu_dev->req_fd >= 0) {
- um_free_irq(VIRTIO_IRQ, vu_dev);
+ vu_free_irq(vu_dev, vu_dev->req_fd, vu_dev);
os_close_file(vu_dev->req_fd);
}
--
2.20.1
_______________________________________________
linux-um mailing list
linux-um@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-um
^ permalink raw reply related [flat|nested] 3+ messages in thread
* [RFC 3/3] um: implement time-travel=virtio
2019-09-16 13:10 [RFC 1/3] um: virtio: implement VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS Johannes Berg
2019-09-16 13:10 ` [RFC 2/3] um: virtio: wrap irq request/free functions Johannes Berg
@ 2019-09-16 13:10 ` Johannes Berg
1 sibling, 0 replies; 3+ messages in thread
From: Johannes Berg @ 2019-09-16 13:10 UTC (permalink / raw)
To: linux-um; +Cc: Johannes Berg
From: Johannes Berg <johannes.berg@intel.com>
This implements synchronized time-travel mode, which - using a special
virtio device, lets multiple machines take part in a time-travelling
simulation together.
Note that the 1234 device ID is not assigned, so don't use this code :-)
Also this code is a lot more complex than I'd like, and has all the
weird poll() semantics etc. It works, but occasionally deadlocks and
I haven't yet found why.
Instead of continuing this, I'm going to try to work on a much simpler
simulation time driver not using virtio, since this is a special thing.
Then only the first patch in this series and some smaller virtio
changes will hopefully be required.
---
arch/um/Kconfig | 13 ++
arch/um/drivers/virtio_uml.c | 204 ++++++++++++++++-
arch/um/include/linux/time-internal.h | 14 ++
arch/um/include/shared/os.h | 6 +
arch/um/kernel/time.c | 315 +++++++++++++++++++++++++-
arch/um/os-Linux/file.c | 116 ++++++++++
include/uapi/linux/virtio_ids.h | 2 +
include/uapi/linux/virtio_simtime.h | 99 ++++++++
8 files changed, 755 insertions(+), 14 deletions(-)
create mode 100644 include/uapi/linux/virtio_simtime.h
diff --git a/arch/um/Kconfig b/arch/um/Kconfig
index 3debe293169c..22011875f620 100644
--- a/arch/um/Kconfig
+++ b/arch/um/Kconfig
@@ -197,6 +197,19 @@ config UML_TIME_TRAVEL_SUPPORT
It is safe to say Y, but you probably don't need this.
+config UML_TIME_TRAVEL_VIRTIO
+ bool "VirtIO-backed time-travel mode (for cross-system simulation)"
+ depends on UML_TIME_TRAVEL_SUPPORT
+ depends on VIRTIO_UML=y
+ help
+ Enable this option to support synchronized time travel (time
+ simulation) across multiple machines, using VirtIO transport.
+
+ You can find an example for the necessary userspace in the QEMU
+ git repository (vhost-user-simtime).
+
+ It is safe to say Y, but you probably don't need this.
+
endmenu
source "arch/um/drivers/Kconfig"
diff --git a/arch/um/drivers/virtio_uml.c b/arch/um/drivers/virtio_uml.c
index c23129b1c220..e4d13b45182a 100644
--- a/arch/um/drivers/virtio_uml.c
+++ b/arch/um/drivers/virtio_uml.c
@@ -26,6 +26,7 @@
#include <linux/virtio.h>
#include <linux/virtio_config.h>
#include <linux/virtio_ring.h>
+#include <linux/time-internal.h>
#include <shared/as-layout.h>
#include <irq_kern.h>
#include <init.h>
@@ -39,6 +40,12 @@
#define MAX_SUPPORTED_QUEUE_SIZE 256
+#ifdef CONFIG_UML_TIME_TRAVEL_VIRTIO
+static LIST_HEAD(virtio_uml_devices);
+static DEFINE_SPINLOCK(virtio_uml_devices_lock);
+static struct os_poll *virtio_poll;
+#endif
+
#define to_virtio_uml_device(_vdev) \
container_of(_vdev, struct virtio_uml_device, vdev)
@@ -53,6 +60,10 @@ struct virtio_uml_device {
struct virtio_device vdev;
struct platform_device *pdev;
+#ifdef CONFIG_UML_TIME_TRAVEL_VIRTIO
+ struct list_head list;
+#endif
+
spinlock_t sock_lock;
int sock, req_fd;
u64 features;
@@ -63,6 +74,11 @@ struct virtio_uml_device {
struct virtio_uml_vq_info {
int kick_fd, call_fd;
char name[32];
+#ifdef CONFIG_UML_TIME_TRAVEL_VIRTIO
+ struct virtqueue *vq;
+ vq_callback_t *callback;
+ struct time_travel_event defer;
+#endif
};
extern unsigned long long physmem_size, highmem;
@@ -120,7 +136,43 @@ static int vhost_user_recv(struct virtio_uml_device *vu_dev,
size_t max_payload_size)
{
size_t size;
- int rc = vhost_user_recv_header(fd, msg);
+ int rc;
+
+#ifdef CONFIG_UML_TIME_TRAVEL_VIRTIO
+ /*
+ * In virtio time-travel mode, we're handling all the vhost-user
+ * FDs by polling them whenever appropriate. However, we get into
+ * situations where we get some updates messages from some device
+ * while we're waiting for a response, or similar; so we need to
+ * poll and handle other things while reading a message.
+ *
+ * Note that this MUST be done only once for each message, if we
+ * were to do this in e.g. full_read() above then we could
+ * - send a simtime message
+ * - wait for a response
+ * - receive a partial response
+ * - receive some other message on another device
+ * - schedule an interrupt for it, and that causes us
+ * to talk to the simtime device again
+ * - wait for a response (again)
+ * - receive the partial message as though it was the full one
+ *
+ * This is avoided by doing the poll() only once for each message
+ * and reading it completely once it should be available, even if
+ * the sender might be creating it in multiple pieces, and we may
+ * receive it in multiple rounds inside full_read().
+ */
+ if (time_travel_virtio) {
+ bool remove = os_poll_add(&virtio_poll, fd);
+
+ while (!os_poll_check_readable(virtio_poll, fd))
+ virtio_uml_poll_fds();
+ if (remove)
+ os_poll_remove(&virtio_poll, fd);
+ }
+#endif
+
+ rc = vhost_user_recv_header(fd, msg);
if (rc == -ECONNRESET) {
struct virtio_uml_platform_data *pdata;
@@ -208,7 +260,17 @@ static int vhost_user_send(struct virtio_uml_device *vu_dev,
if (request_ack)
msg->header.flags |= VHOST_USER_FLAG_NEED_REPLY;
- spin_lock_irqsave(&vu_dev->sock_lock, flags);
+ /*
+ * Don't let an interrupt break us in the middle of some message
+ * handshake, we need it to be done properly as "send request"
+ * followed by "read response".
+ *
+ * In time-travel=virtio mode, we may actually end up recursing
+ * this for different sockets, but we don't have real interrupts
+ * connected to virtio anyway, so we don't need locking.
+ */
+ if (!time_travel_virtio)
+ spin_lock_irqsave(&vu_dev->sock_lock, flags);
rc = full_sendmsg_fds(vu_dev->sock, msg, size, fds, num_fds);
if (rc < 0)
goto out;
@@ -221,14 +283,17 @@ static int vhost_user_send(struct virtio_uml_device *vu_dev,
goto out;
if (status) {
- vu_err(vu_dev, "slave reports error: %llu\n", status);
+ vu_err(vu_dev, "slave reports error: %llu to message %*ph (fd %d)\n",
+ status, (int)sizeof(msg->header), &msg->header,
+ vu_dev->sock);
rc = -EIO;
goto out;
}
}
out:
- spin_unlock_irqrestore(&vu_dev->sock_lock, flags);
+ if (!time_travel_virtio)
+ spin_unlock_irqrestore(&vu_dev->sock_lock, flags);
return rc;
}
@@ -377,6 +442,29 @@ static int vu_request_irq(struct virtio_uml_device *vu_dev,
int fd, irq_handler_t handler,
const char *name, void *data)
{
+#ifdef CONFIG_UML_TIME_TRAVEL_VIRTIO
+ /*
+ * In time-travel=virtio mode, virtio becomes part of the simulation
+ * handling, and we don't want real IRQs to happen. Instead, we will
+ * poll all the file descriptors whenever we need to, in order to
+ * process the messages coming from other parts of the simulation.
+ * We also defer all interrupts to after simulation scheduling.
+ *
+ * We could process the interrupts normally, but we need to do this
+ * anyway for the simtime device because we may need to talk to it
+ * with interrupts disabled, so just do all processing the same way
+ * for all file descriptors.
+ */
+ if (time_travel_virtio) {
+ int ret;
+
+ ret = os_poll_add(&virtio_poll, fd);
+ if (ret < 0)
+ return ret;
+ return 0;
+ }
+#endif
+
return um_request_irq(VIRTIO_IRQ, fd, IRQ_READ, handler,
IRQF_SHARED, name, data);
}
@@ -384,6 +472,13 @@ static int vu_request_irq(struct virtio_uml_device *vu_dev,
static void vu_free_irq(struct virtio_uml_device *vu_dev,
int fd, void *data)
{
+#ifdef CONFIG_UML_TIME_TRAVEL_VIRTIO
+ if (time_travel_virtio) {
+ os_poll_remove(&virtio_poll, fd);
+ return;
+ }
+#endif
+
um_free_irq(VIRTIO_IRQ, data);
}
@@ -712,6 +807,9 @@ static bool vu_notify(struct virtqueue *vq)
const uint64_t n = 1;
int rc;
+ if (time_travel_virtio && vq->vdev->id.device != VIRTIO_ID_SIMTIME)
+ time_travel_propagate_time();
+
if (info->kick_fd < 0) {
struct virtio_uml_device *vu_dev;
@@ -744,6 +842,40 @@ static irqreturn_t vu_interrupt(int irq, void *opaque)
return ret;
}
+#ifdef CONFIG_UML_TIME_TRAVEL_VIRTIO
+/* See the comment in vu_request_irq() */
+void virtio_uml_poll_fds(void)
+{
+ struct virtio_uml_device *vu_dev;
+ unsigned long flags;
+ int ret;
+
+ local_irq_save(flags);
+ do {
+ ret = os_poll(virtio_poll, -1);
+ if (ret < 0 && ret != -EINTR)
+ pr_err("virtio_uml_poll_fds() poll returned %d\n", ret);
+ } while (ret < 0);
+
+ list_for_each_entry(vu_dev, &virtio_uml_devices, list) {
+ struct virtqueue *vq;
+
+ if (os_poll_check_readable(virtio_poll, vu_dev->req_fd)) {
+ if (vu_req_interrupt(0 /* ignored */, vu_dev) == IRQ_NONE)
+ os_poll_remove(&virtio_poll, vu_dev->req_fd);
+ }
+
+ virtio_device_for_each_vq((&vu_dev->vdev), vq) {
+ struct virtio_uml_vq_info *info = vq->priv;
+
+ if (info->call_fd >= 0 &&
+ os_poll_check_readable(virtio_poll, info->call_fd))
+ vu_interrupt(0 /* ignored */, vq);
+ }
+ }
+ local_irq_restore(flags);
+}
+#endif
static void vu_get(struct virtio_device *vdev, unsigned offset,
void *buf, unsigned len)
@@ -860,6 +992,26 @@ static int vu_setup_vq_call_fd(struct virtio_uml_device *vu_dev,
return rc;
}
+#ifdef CONFIG_UML_TIME_TRAVEL_VIRTIO
+static void vu_defer_irq_handle(struct time_travel_event *d)
+{
+ struct virtio_uml_vq_info *info;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ info = container_of(d, struct virtio_uml_vq_info, defer);
+ info->callback(info->vq);
+ local_irq_restore(flags);
+}
+
+static void vu_defer_irq_callback(struct virtqueue *vq)
+{
+ struct virtio_uml_vq_info *info = vq->priv;
+
+ time_travel_add_irq_event(&info->defer);
+}
+#endif
+
static struct virtqueue *vu_setup_vq(struct virtio_device *vdev,
unsigned index, vq_callback_t *callback,
const char *name, bool ctx)
@@ -879,6 +1031,19 @@ static struct virtqueue *vu_setup_vq(struct virtio_device *vdev,
snprintf(info->name, sizeof(info->name), "%s.%d-%s", pdev->name,
pdev->id, name);
+#ifdef CONFIG_UML_TIME_TRAVEL_VIRTIO
+ /*
+ * When we get an interrupt, we must bounce it through the simulation
+ * calendar (the simtime device), except for the simtime device itself
+ * since that's part of the simulation control.
+ */
+ if (time_travel_virtio && vdev->id.device != VIRTIO_ID_SIMTIME) {
+ info->callback = callback;
+ callback = vu_defer_irq_callback;
+ time_travel_set_event_fn(&info->defer, vu_defer_irq_handle);
+ }
+#endif
+
vq = vring_create_virtqueue(index, num, PAGE_SIZE, vdev, true, true,
ctx, vu_notify, callback, info->name);
if (!vq) {
@@ -887,6 +1052,9 @@ static struct virtqueue *vu_setup_vq(struct virtio_device *vdev,
}
vq->priv = info;
num = virtqueue_get_vring_size(vq);
+#ifdef CONFIG_UML_TIME_TRAVEL_VIRTIO
+ info->vq = vq;
+#endif
if (vu_dev->protocol_features &
BIT_ULL(VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS)) {
@@ -1038,15 +1206,13 @@ static void virtio_uml_release_dev(struct device *d)
/* Platform device */
-struct virtio_uml_platform_data {
- u32 virtio_device_id;
- const char *socket_path;
-};
-
static int virtio_uml_probe(struct platform_device *pdev)
{
struct virtio_uml_platform_data *pdata = pdev->dev.platform_data;
struct virtio_uml_device *vu_dev;
+#ifdef CONFIG_UML_TIME_TRAVEL_VIRTIO
+ unsigned long flags;
+#endif
int rc;
if (!pdata)
@@ -1082,6 +1248,12 @@ static int virtio_uml_probe(struct platform_device *pdev)
rc = register_virtio_device(&vu_dev->vdev);
if (rc)
put_device(&vu_dev->vdev.dev);
+
+#ifdef CONFIG_UML_TIME_TRAVEL_VIRTIO
+ spin_lock_irqsave(&virtio_uml_devices_lock, flags);
+ list_add_tail(&vu_dev->list, &virtio_uml_devices);
+ spin_unlock_irqrestore(&virtio_uml_devices_lock, flags);
+#endif
return rc;
error_init:
@@ -1092,7 +1264,19 @@ static int virtio_uml_probe(struct platform_device *pdev)
static int virtio_uml_remove(struct platform_device *pdev)
{
struct virtio_uml_device *vu_dev = platform_get_drvdata(pdev);
+#ifdef CONFIG_UML_TIME_TRAVEL_VIRTIO
+ unsigned long flags;
+#endif
+
+ /* This is totally necessary even during shutdown, just leak it */
+ if (vu_dev->vdev.id.device == VIRTIO_ID_SIMTIME)
+ return 0;
+#ifdef CONFIG_UML_TIME_TRAVEL_VIRTIO
+ spin_lock_irqsave(&virtio_uml_devices_lock, flags);
+ list_del(&vu_dev->list);
+ spin_unlock_irqrestore(&virtio_uml_devices_lock, flags);
+#endif
unregister_virtio_device(&vu_dev->vdev);
return 0;
}
@@ -1125,6 +1309,8 @@ static void vu_conn_broken(struct work_struct *wk)
{
struct virtio_uml_platform_data *pdata;
+printk(KERN_DEBUG "************ broken\n");
+
pdata = container_of(wk, struct virtio_uml_platform_data, conn_broken_wk);
vu_unregister_cmdline_device(&pdata->pdev->dev, NULL);
}
diff --git a/arch/um/include/linux/time-internal.h b/arch/um/include/linux/time-internal.h
index 752a47d44557..0f1828851180 100644
--- a/arch/um/include/linux/time-internal.h
+++ b/arch/um/include/linux/time-internal.h
@@ -47,5 +47,19 @@ static inline void time_travel_sleep(unsigned long long duration)
/* this is a macro so the event/function need not exist */
#define time_travel_set_event_fn(e, fn) do {} while (0)
+#endif
+
+#ifdef CONFIG_UML_TIME_TRAVEL_VIRTIO
+extern bool time_travel_virtio;
+void time_travel_propagate_time(void);
+
+void virtio_uml_poll_fds(void);
+void time_travel_add_irq_event(struct time_travel_event *e);
+#else
+#define time_travel_virtio false
+
+static inline void time_travel_propagate_time(void)
+{
+}
#endif /* CONFIG_UML_TIME_TRAVEL_SUPPORT */
#endif /* __TIMER_INTERNAL_H__ */
diff --git a/arch/um/include/shared/os.h b/arch/um/include/shared/os.h
index 69cc88b51168..6a6acb2f80ab 100644
--- a/arch/um/include/shared/os.h
+++ b/arch/um/include/shared/os.h
@@ -181,6 +181,12 @@ extern int os_falloc_punch(int fd, unsigned long long offset, int count);
extern int os_eventfd(unsigned int initval, int flags);
extern int os_sendmsg_fds(int fd, const void *buf, unsigned int len,
const int *fds, unsigned int fds_num);
+struct os_poll;
+/* Returns 1 if added, 0 if already present, -ENOMEM on OOM */
+int os_poll_add(struct os_poll **poll, int fd);
+void os_poll_remove(struct os_poll **poll, int fd);
+int os_poll(struct os_poll *poll, int timeout);
+bool os_poll_check_readable(struct os_poll *p, int fd);
/* start_up.c */
extern void os_early_checks(void);
diff --git a/arch/um/kernel/time.c b/arch/um/kernel/time.c
index d3ebb6a448d7..b698b9efcce4 100644
--- a/arch/um/kernel/time.c
+++ b/arch/um/kernel/time.c
@@ -15,6 +15,10 @@
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/threads.h>
+#include <linux/module.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_simtime.h>
#include <asm/irq.h>
#include <asm/param.h>
#include <kern_util.h>
@@ -42,6 +46,265 @@ static void time_travel_set_time(unsigned long long ns)
time_travel_time = ns;
}
+#ifdef CONFIG_UML_TIME_TRAVEL_VIRTIO
+bool time_travel_virtio;
+static struct virtio_device *time_travel_virtio_dev;
+static struct virtqueue *time_travel_virtio_vqs[2]; /* request, notify */
+static unsigned long long time_travel_virtio_prev_request;
+static bool time_travel_virtio_prev_request_valid;
+static LIST_HEAD(time_travel_deferred_irqs);
+static unsigned long long time_travel_virtio_free_until;
+static bool time_travel_virtio_free_until_valid;
+static unsigned int time_travel_virtio_waiting;
+static unsigned int virtio_requests_open;
+
+static struct virtio_simtime_msg *simtime_msg_tx, *simtime_msg_resp;
+static struct virtio_simtime_msg *simtime_msg_rx;
+static struct scatterlist _simtime_msg_sgs[3];
+static struct scatterlist *simtime_msg_sgs[2] = {
+ &_simtime_msg_sgs[0],
+ &_simtime_msg_sgs[1],
+};
+
+static void time_travel_virtio_req(u64 op, u64 time)
+{
+ unsigned int wait;
+
+ simtime_msg_tx->op = op;
+ simtime_msg_tx->time = time;
+ virt_wmb();
+
+ BUG_ON(!simtime_msg_tx->op);
+ BUG_ON(!time_travel_virtio_dev);
+
+ wait = virtio_requests_open++;
+
+ virtqueue_add_sgs(time_travel_virtio_vqs[0], simtime_msg_sgs, 1, 1,
+ simtime_msg_tx, GFP_ATOMIC);
+ virtqueue_kick(time_travel_virtio_vqs[0]);
+
+ while (virtio_requests_open > wait) {
+ virtio_uml_poll_fds();
+ if (virtqueue_is_broken(time_travel_virtio_vqs[1]))
+ panic("timer-virtio device broke!\n");
+ }
+ if (op == VIRTIO_SIMTIME_GET)
+ time_travel_set_time(simtime_msg_resp->time);
+}
+
+static void time_travel_virtio_update_request(unsigned long long time)
+{
+ if (!time_travel_virtio_dev)
+ return;
+
+ /* asked for exactly this time previously */
+ if (time_travel_virtio_prev_request_valid &&
+ time >= time_travel_virtio_prev_request)
+ return;
+
+ time_travel_virtio_prev_request = time;
+ time_travel_virtio_prev_request_valid = true;
+ time_travel_virtio_req(VIRTIO_SIMTIME_REQUEST, time);
+}
+
+void time_travel_propagate_time(void)
+{
+ time_travel_virtio_req(VIRTIO_SIMTIME_UPDATE, time_travel_time);
+}
+
+/* returns true if we must do a wait to the simtime device */
+static bool time_travel_virtio_request(unsigned long long time)
+{
+ /*
+ * If we received an external sync point ("free until") then we
+ * don't have to request/wait for anything until then, unless
+ * we're already waiting.
+ */
+ if (!time_travel_virtio_waiting && time_travel_virtio_free_until_valid &&
+ time < time_travel_virtio_free_until)
+ return false;
+
+ time_travel_virtio_update_request(time);
+ return true;
+}
+
+static void time_travel_virtio_wait(void)
+{
+ int wait = time_travel_virtio_waiting;
+
+ time_travel_virtio_prev_request_valid = false;
+ time_travel_virtio_waiting++;
+
+ time_travel_virtio_req(VIRTIO_SIMTIME_WAIT, -1);
+
+ /*
+ * Here we are deep in the idle loop, so we have to break out of the
+ * kernel abstraction in a sense and implement this in terms of the
+ * UML system waiting on the VQ interrupt while sleeping, when we get
+ * the signal it'll call time_travel_virtio_vq_notify_done() completing the
+ * call.
+ */
+ while (time_travel_virtio_waiting > wait) {
+ virtio_uml_poll_fds();
+ if (virtqueue_is_broken(time_travel_virtio_vqs[1]))
+ panic("timer-virtio device broke!\n");
+ }
+ /* we might request more stuff while polling - reset when we run */
+ time_travel_virtio_prev_request_valid = false;
+}
+
+static void virtio_set_time(unsigned long long to)
+{
+ if (unlikely(!time_travel_virtio_dev)) {
+ time_travel_set_time(to);
+ return;
+ }
+
+ if (time_travel_virtio_request(to))
+ time_travel_virtio_wait();
+ else
+ time_travel_set_time(to);
+}
+
+static void time_travel_virtio_get_time(void)
+{
+ time_travel_virtio_req(VIRTIO_SIMTIME_GET, -1);
+}
+
+static void time_travel_virtio_vq_request_done(struct virtqueue *vq)
+{
+ unsigned int len;
+ struct virtio_simtime_msg *msg = virtqueue_get_buf(vq, &len);
+
+ if (!msg)
+ return;
+
+ virtio_requests_open--;
+}
+
+static void time_travel_virtio_vq_notify_done(struct virtqueue *vq)
+{
+ unsigned int len;
+ struct virtio_simtime_msg *msg = virtqueue_get_buf(vq, &len);
+
+ BUG_ON(!msg || len != sizeof(*msg));
+ switch (msg->op) {
+ case VIRTIO_SIMTIME_RUN:
+ BUG_ON(!time_travel_virtio_waiting);
+ time_travel_set_time(msg->time);
+ /* write the time before setting waiting = false */
+ wmb();
+ time_travel_virtio_waiting--;
+ break;
+ case VIRTIO_SIMTIME_FREE_UNTIL:
+ time_travel_virtio_free_until = msg->time;
+ time_travel_virtio_free_until_valid = true;
+ break;
+ default:
+ pr_err("unexpected simtime message %lld\n",
+ (unsigned long long)msg->op);
+ }
+
+ /* resubmit the buffer */
+ virtqueue_add_inbuf(time_travel_virtio_vqs[1], &_simtime_msg_sgs[2], 1,
+ simtime_msg_rx, GFP_ATOMIC);
+ virtqueue_kick(time_travel_virtio_vqs[1]);
+}
+
+static int time_travel_virtio_probe(struct virtio_device *vdev)
+{
+ static const char *const names[2] = { "request", "notify", };
+ static vq_callback_t *callbacks[2] = {
+ time_travel_virtio_vq_request_done,
+ time_travel_virtio_vq_notify_done,
+ };
+ int err;
+
+ if (WARN(!time_travel_virtio,
+ "Without virtio time-travel mode, virtio timer devices are not useful!"))
+ return -EIO;
+
+ if (WARN(time_travel_virtio_dev,
+ "Multiple virtio timer devices are not useful!"))
+ return -EIO;
+
+ time_travel_virtio_dev = vdev;
+
+ err = virtio_find_vqs(vdev, 2, time_travel_virtio_vqs, callbacks, names, NULL);
+ if (WARN_ON(err))
+ return err;
+
+ virtio_device_ready(vdev);
+
+ /* submit a buffer for the notification VQ for later */
+ virtqueue_add_inbuf(time_travel_virtio_vqs[1], &_simtime_msg_sgs[2], 1,
+ simtime_msg_rx, GFP_KERNEL);
+ virtqueue_kick(time_travel_virtio_vqs[1]);
+
+ pr_info("virtio-timer device connected\n");
+
+ return 0;
+}
+
+static void time_travel_virtio_remove(struct virtio_device *vdev)
+{
+ panic("virtio-timer device must not be removed\n");
+}
+
+static struct virtio_device_id time_travel_virtio_id_table[] = {
+ { VIRTIO_ID_SIMTIME, VIRTIO_DEV_ANY_ID },
+ { 0 },
+};
+
+static struct virtio_driver time_travel_virtio_driver = {
+ .driver.name = "virtio-timer",
+ .driver.owner = THIS_MODULE,
+ .id_table = time_travel_virtio_id_table,
+ .probe = time_travel_virtio_probe,
+ .remove = time_travel_virtio_remove,
+};
+
+static int time_travel_virtio_init(void)
+{
+ simtime_msg_tx = kmalloc_array(3, sizeof(*simtime_msg_tx), GFP_KERNEL);
+ BUG_ON(!simtime_msg_tx);
+ simtime_msg_resp = simtime_msg_tx + 1;
+ simtime_msg_rx = simtime_msg_tx + 2;
+
+ sg_init_one(&_simtime_msg_sgs[0], simtime_msg_tx,
+ sizeof(*simtime_msg_tx));
+ sg_init_one(&_simtime_msg_sgs[1], simtime_msg_resp,
+ sizeof(*simtime_msg_resp));
+ sg_init_one(&_simtime_msg_sgs[2], simtime_msg_rx,
+ sizeof(*simtime_msg_rx));
+
+ return register_virtio_driver(&time_travel_virtio_driver);
+}
+module_init(time_travel_virtio_init);
+#else
+#define time_travel_virtio_dev NULL
+
+static void virtio_set_time(unsigned long long to)
+{
+}
+
+static void time_travel_virtio_get_time(void)
+{
+}
+
+static void time_travel_virtio_update_request(unsigned long long time)
+{
+}
+#endif /* CONFIG_VIRTIO_UML */
+
+static void __time_travel_update_time(unsigned long long ns)
+{
+ if (time_travel_virtio)
+ virtio_set_time(ns);
+ else
+ time_travel_set_time(ns);
+}
+
static void time_travel_add_event(struct time_travel_event *e,
unsigned long long time)
{
@@ -76,6 +339,7 @@ static void time_travel_add_event(struct time_travel_event *e,
tmp = list_first_entry(&time_travel_events,
struct time_travel_event,
list);
+ time_travel_virtio_update_request(tmp->time);
time_travel_next_event = tmp->time;
}
@@ -88,8 +352,17 @@ void time_travel_periodic_timer(struct time_travel_event *e)
static void time_travel_deliver_event(struct time_travel_event *e)
{
- /* this is basically just deliver_alarm(), handles IRQs itself */
- e->fn(e);
+ if (e == &time_travel_timer_event) {
+ /*
+ * deliver_alarm() does the irq_enter/irq_exit
+ * by itself, so must handle it specially here
+ */
+ e->fn(e);
+ } else {
+ irq_enter();
+ e->fn(e);
+ irq_exit();
+ }
}
static bool time_travel_del_event(struct time_travel_event *e)
@@ -112,7 +385,7 @@ static void time_travel_update_time(unsigned long long next, bool retearly)
if (e && e->time <= next) {
BUG_ON(!time_travel_del_event(e));
- time_travel_set_time(e->time);
+ __time_travel_update_time(e->time);
if (irqs_disabled()) {
mark_time_travel_irq_pending();
e->pending = true;
@@ -122,8 +395,14 @@ static void time_travel_update_time(unsigned long long next, bool retearly)
time_travel_deliver_event(e);
}
} else {
- time_travel_set_time(next);
+ __time_travel_update_time(next);
}
+
+ e = list_first_entry_or_null(&time_travel_events,
+ struct time_travel_event,
+ list);
+ if (e)
+ time_travel_virtio_update_request(e->time);
} while (!retearly && time_travel_time < next);
}
@@ -140,7 +419,20 @@ void time_travel_deliver_pending(void)
}
}
-static void time_travel_oneshot_timer(struct time_travel_event *e)
+void time_travel_add_irq_event(struct time_travel_event *e)
+{
+ BUG_ON(!time_travel_virtio);
+
+ time_travel_virtio_get_time();
+ /*
+ * We could model interrupt latency here, for now just
+ * don't have any latency at all and request the exact
+ * same time (again) to run the interrupt...
+ */
+ time_travel_add_event(e, time_travel_time);
+}
+
+void time_travel_oneshot_timer(struct time_travel_event *e)
{
deliver_alarm();
}
@@ -152,6 +444,9 @@ void time_travel_sleep(unsigned long long duration)
if (time_travel_mode == TT_MODE_BASIC)
os_timer_disable();
+ if (unlikely(time_travel_virtio && !time_travel_virtio_dev))
+ panic("Must have a virtio timer device for time-travel=virtio");
+
time_travel_update_time(next, true);
if (time_travel_mode == TT_MODE_BASIC &&
@@ -402,6 +697,16 @@ int setup_time_travel(char *str)
return 1;
}
+#ifdef CONFIG_UML_TIME_TRAVEL_VIRTIO
+ if (strcmp(str, "=virtio") == 0) {
+ time_travel_mode = TT_MODE_INFCPU;
+ time_travel_virtio = true;
+ timer_clockevent.name = "time-travel-timer-external";
+ timer_clocksource.name = "time-travel-clock-external";
+ return 1;
+ }
+#endif
+
if (!*str) {
time_travel_mode = TT_MODE_BASIC;
timer_clockevent.name = "time-travel-timer";
diff --git a/arch/um/os-Linux/file.c b/arch/um/os-Linux/file.c
index 5133e3afb96f..3e598f47fa9e 100644
--- a/arch/um/os-Linux/file.c
+++ b/arch/um/os-Linux/file.c
@@ -5,6 +5,7 @@
#include <stdio.h>
#include <unistd.h>
+#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
@@ -16,6 +17,7 @@
#include <sys/un.h>
#include <sys/types.h>
#include <sys/eventfd.h>
+#include <poll.h>
#include <os.h>
static void copy_stat(struct uml_stat *dst, const struct stat64 *src)
@@ -664,3 +666,117 @@ int os_sendmsg_fds(int fd, const void *buf, unsigned int len, const int *fds,
return -errno;
return err;
}
+
+struct os_poll {
+ unsigned int max, n;
+ struct pollfd fds[];
+};
+
+int os_poll_add(struct os_poll **pp, int fd)
+{
+ struct os_poll *p = *pp;
+
+ if (p) {
+ int i;
+
+ for (i = 0; i < p->n; i++)
+ if (p->fds[i].fd == fd)
+ return 0;
+ }
+
+ if (!p || p->n >= p->max) {
+ unsigned int n = p ? 2 * p->max : 10;
+ unsigned int nsize = sizeof(*p) + n * sizeof(p->fds[0]);
+ struct os_poll *pnew;
+
+ /* NB: realloc isn't wrapped, open-code it */
+ pnew = malloc(nsize);
+ if (!pnew)
+ return -ENOMEM;
+ memset(pnew, 0, nsize);
+ if (p) {
+ memcpy(pnew, p, sizeof(*p) + p->n * sizeof(p->fds[0]));
+ free(p);
+ }
+ p = pnew;
+ p->max = n;
+ }
+
+ p->fds[p->n].fd = fd;
+ p->fds[p->n].events = POLLIN;
+ p->fds[p->n].revents = 0;
+ p->n++;
+
+ *pp = p;
+ return 1;
+}
+
+void os_poll_remove(struct os_poll **pp, int fd)
+{
+ struct os_poll *p = *pp;
+ int i, found = -1;
+
+ for (i = 0; i < p->n; i++) {
+ if (p->fds[i].fd == fd) {
+ found = i;
+ break;
+ }
+ }
+
+ if (found < 0)
+ return;
+
+ p->n--;
+ p->fds[found] = p->fds[p->n];
+ if (p->n == 0) {
+ free(p);
+ *pp = NULL;
+ }
+}
+
+int os_poll(struct os_poll *p, int timeout)
+{
+ int ret, i;
+
+ if (!p)
+ return -ENOENT;
+
+ for (i = 0; i < p->n; i++)
+ p->fds[i].revents = 0;
+
+ ret = poll(p->fds, p->n, timeout);
+
+ if (ret < 0)
+ return -errno;
+ return ret;
+}
+
+bool os_poll_check_readable(struct os_poll *p, int fd)
+{
+ int i;
+
+ if (!p)
+ return false;
+
+ for (i = 0; i < p->n; i++) {
+ if (p->fds[i].fd == fd)
+ return p->fds[i].revents & POLLIN;
+ }
+
+ return false;
+}
+
+bool os_poll_present(struct os_poll *p, int fd)
+{
+ int i;
+
+ if (!p)
+ return false;
+
+ for (i = 0; i < p->n; i++) {
+ if (p->fds[i].fd == fd)
+ return true;
+ }
+
+ return false;
+}
diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
index 348fd0176f75..745a6dc5af90 100644
--- a/include/uapi/linux/virtio_ids.h
+++ b/include/uapi/linux/virtio_ids.h
@@ -46,4 +46,6 @@
#define VIRTIO_ID_IOMMU 23 /* virtio IOMMU */
#define VIRTIO_ID_PMEM 27 /* virtio pmem */
+#define VIRTIO_ID_SIMTIME 1234 /* virtio time travel device for UML */
+
#endif /* _LINUX_VIRTIO_IDS_H */
diff --git a/include/uapi/linux/virtio_simtime.h b/include/uapi/linux/virtio_simtime.h
new file mode 100644
index 000000000000..72b77b1c7e8f
--- /dev/null
+++ b/include/uapi/linux/virtio_simtime.h
@@ -0,0 +1,99 @@
+/*
+ * This header file is BSD licensed so anyone can use the definitions to
+ * implement compatible drivers/servers:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Intel nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL INTEL OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Copyright (C) 2019 Intel Corporation
+ */
+#ifndef _UAPI_LINUX_VIRTIO_SIMTIME_H
+#define _UAPI_LINUX_VIRTIO_SIMTIME_H
+#include <linux/types.h>
+#include <linux/virtio_types.h>
+#include <linux/virtio_ids.h>
+
+/*
+ * A message passed on either of the VQs (input and output).
+ */
+struct virtio_simtime_msg {
+ __u64 op; /* see below */
+ __u64 time; /* time in nanoseconds */
+};
+
+/**
+ * enum virtio_simtime_ops - Operation codes
+ */
+enum virtio_simtime_ops {
+ /**
+ * @VIRTIO_SIMTIME_REQUEST: request to run at the given time
+ * (host -> device)
+ */
+ VIRTIO_SIMTIME_REQUEST = 1,
+
+ /**
+ * @VIRTIO_SIMTIME_WAIT: Indicate waiting for the previously requested
+ * runtime, new requests may be made while waiting (e.g. due to
+ * interrupts); the time field is ignored. The device must process
+ * this message and later send a %VIRTIO_SIMTIME_RUN message when
+ * the host can run again.
+ * (host -> device)
+ */
+ VIRTIO_SIMTIME_WAIT = 2,
+
+ /**
+ * @VIRTIO_SIMTIME_GET: return the current time from the device, it
+ * must fill the input buffer that's given; the time field is
+ * ignored
+ * (host -> device, which fills the input buffer)
+ */
+ VIRTIO_SIMTIME_GET = 3,
+
+ /**
+ * @VIRTIO_SIMTIME_UPDATE: time update to the device, must be sent e.g.
+ * before kicking an interrupt to another device
+ * (host -> device)
+ */
+ VIRTIO_SIMTIME_UPDATE = 4,
+
+ /**
+ * @VIRTIO_SIMTIME_RUN: run time request granted, current time is in
+ * the time field
+ * (device -> host)
+ */
+ VIRTIO_SIMTIME_RUN = 5,
+
+ /**
+ * @VIRTIO_SIMTIME_FREE_UNTIL: Enable free-running until the given time,
+ * this is a message from the device telling the host that it can
+ * freely do its own scheduling for anything before the indicated
+ * time.
+ * Note that if a device sends this message once, the host may
+ * assume that it will also do so in the future, if it implements
+ * wraparound semantics for the time field.
+ * (device -> host)
+ */
+ VIRTIO_SIMTIME_FREE_UNTIL = 6,
+};
+
+#endif /* _UAPI_LINUX_VIRTIO_SIMTIME_H */
--
2.20.1
_______________________________________________
linux-um mailing list
linux-um@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-um
^ permalink raw reply related [flat|nested] 3+ messages in thread
end of thread, other threads:[~2019-09-16 13:10 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-09-16 13:10 [RFC 1/3] um: virtio: implement VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS Johannes Berg
2019-09-16 13:10 ` [RFC 2/3] um: virtio: wrap irq request/free functions Johannes Berg
2019-09-16 13:10 ` [RFC 3/3] um: implement time-travel=virtio Johannes Berg
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.