Commit dd9ada56 authored by Johannes Berg's avatar Johannes Berg Committed by Richard Weinberger

um: virtio: Implement VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS

Implement in-band notifications that are necessary for running
vhost-user devices under externally synchronized time-travel
mode (which is in a follow-up patch). This feature makes what
usually should be eventfd notifications in-band messages.

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: default avatarJohannes Berg <johannes.berg@intel.com>
Signed-off-by: default avatarRichard Weinberger <richard@nod.at>
parent 4b786e24
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
#define VHOST_USER_PROTOCOL_F_REPLY_ACK 3 #define VHOST_USER_PROTOCOL_F_REPLY_ACK 3
#define VHOST_USER_PROTOCOL_F_SLAVE_REQ 5 #define VHOST_USER_PROTOCOL_F_SLAVE_REQ 5
#define VHOST_USER_PROTOCOL_F_CONFIG 9 #define VHOST_USER_PROTOCOL_F_CONFIG 9
#define VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS 14
/* Vring state index masks */ /* Vring state index masks */
#define VHOST_USER_VRING_INDEX_MASK 0xff #define VHOST_USER_VRING_INDEX_MASK 0xff
#define VHOST_USER_VRING_POLL_MASK BIT(8) #define VHOST_USER_VRING_POLL_MASK BIT(8)
...@@ -24,7 +25,8 @@ ...@@ -24,7 +25,8 @@
/* Supported protocol features */ /* Supported protocol features */
#define VHOST_USER_SUPPORTED_PROTOCOL_F (BIT_ULL(VHOST_USER_PROTOCOL_F_REPLY_ACK) | \ #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_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 { enum vhost_user_request {
VHOST_USER_GET_FEATURES = 1, VHOST_USER_GET_FEATURES = 1,
...@@ -52,12 +54,14 @@ enum vhost_user_request { ...@@ -52,12 +54,14 @@ enum vhost_user_request {
VHOST_USER_SET_VRING_ENDIAN = 23, VHOST_USER_SET_VRING_ENDIAN = 23,
VHOST_USER_GET_CONFIG = 24, VHOST_USER_GET_CONFIG = 24,
VHOST_USER_SET_CONFIG = 25, VHOST_USER_SET_CONFIG = 25,
VHOST_USER_VRING_KICK = 35,
}; };
enum vhost_user_slave_request { enum vhost_user_slave_request {
VHOST_USER_SLAVE_IOTLB_MSG = 1, VHOST_USER_SLAVE_IOTLB_MSG = 1,
VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2, VHOST_USER_SLAVE_CONFIG_CHANGE_MSG = 2,
VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3, VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG = 3,
VHOST_USER_SLAVE_VRING_CALL = 4,
}; };
struct vhost_user_header { struct vhost_user_header {
......
...@@ -53,6 +53,7 @@ struct virtio_uml_device { ...@@ -53,6 +53,7 @@ struct virtio_uml_device {
struct virtio_device vdev; struct virtio_device vdev;
struct platform_device *pdev; struct platform_device *pdev;
spinlock_t sock_lock;
int sock, req_fd; int sock, req_fd;
u64 features; u64 features;
u64 protocol_features; u64 protocol_features;
...@@ -189,6 +190,7 @@ static int vhost_user_send(struct virtio_uml_device *vu_dev, ...@@ -189,6 +190,7 @@ static int vhost_user_send(struct virtio_uml_device *vu_dev,
int *fds, size_t num_fds) int *fds, size_t num_fds)
{ {
size_t size = sizeof(msg->header) + msg->header.size; size_t size = sizeof(msg->header) + msg->header.size;
unsigned long flags;
bool request_ack; bool request_ack;
int rc; int rc;
...@@ -207,24 +209,28 @@ static int vhost_user_send(struct virtio_uml_device *vu_dev, ...@@ -207,24 +209,28 @@ static int vhost_user_send(struct virtio_uml_device *vu_dev,
if (request_ack) if (request_ack)
msg->header.flags |= VHOST_USER_FLAG_NEED_REPLY; 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); rc = full_sendmsg_fds(vu_dev->sock, msg, size, fds, num_fds);
if (rc < 0) if (rc < 0)
return rc; goto out;
if (request_ack) { if (request_ack) {
uint64_t status; uint64_t status;
rc = vhost_user_recv_u64(vu_dev, &status); rc = vhost_user_recv_u64(vu_dev, &status);
if (rc) if (rc)
return rc; goto out;
if (status) { if (status) {
vu_err(vu_dev, "slave reports error: %llu\n", 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, static int vhost_user_send_no_payload(struct virtio_uml_device *vu_dev,
...@@ -324,6 +330,7 @@ static void vhost_user_reply(struct virtio_uml_device *vu_dev, ...@@ -324,6 +330,7 @@ static void vhost_user_reply(struct virtio_uml_device *vu_dev,
static irqreturn_t vu_req_interrupt(int irq, void *data) static irqreturn_t vu_req_interrupt(int irq, void *data)
{ {
struct virtio_uml_device *vu_dev = data; struct virtio_uml_device *vu_dev = data;
struct virtqueue *vq;
int response = 1; int response = 1;
struct { struct {
struct vhost_user_msg msg; struct vhost_user_msg msg;
...@@ -343,6 +350,15 @@ static irqreturn_t vu_req_interrupt(int irq, void *data) ...@@ -343,6 +350,15 @@ static irqreturn_t vu_req_interrupt(int irq, void *data)
virtio_config_changed(&vu_dev->vdev); virtio_config_changed(&vu_dev->vdev);
response = 0; response = 0;
break; 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: case VHOST_USER_SLAVE_IOTLB_MSG:
/* not supported - VIRTIO_F_IOMMU_PLATFORM */ /* not supported - VIRTIO_F_IOMMU_PLATFORM */
case VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG: case VHOST_USER_SLAVE_VRING_HOST_NOTIFIER_MSG:
...@@ -684,6 +700,15 @@ static bool vu_notify(struct virtqueue *vq) ...@@ -684,6 +700,15 @@ static bool vu_notify(struct virtqueue *vq)
const uint64_t n = 1; const uint64_t n = 1;
int rc; 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 { do {
rc = os_write_file(info->kick_fd, &n, sizeof(n)); rc = os_write_file(info->kick_fd, &n, sizeof(n));
} while (rc == -EINTR); } while (rc == -EINTR);
...@@ -749,9 +774,12 @@ static void vu_del_vq(struct virtqueue *vq) ...@@ -749,9 +774,12 @@ static void vu_del_vq(struct virtqueue *vq)
{ {
struct virtio_uml_vq_info *info = vq->priv; struct virtio_uml_vq_info *info = vq->priv;
if (info->call_fd >= 0) {
um_free_irq(VIRTIO_IRQ, vq); um_free_irq(VIRTIO_IRQ, vq);
os_close_file(info->call_fd); os_close_file(info->call_fd);
}
if (info->kick_fd >= 0)
os_close_file(info->kick_fd); os_close_file(info->kick_fd);
vring_del_virtqueue(vq); vring_del_virtqueue(vq);
...@@ -782,6 +810,15 @@ static int vu_setup_vq_call_fd(struct virtio_uml_device *vu_dev, ...@@ -782,6 +810,15 @@ static int vu_setup_vq_call_fd(struct virtio_uml_device *vu_dev,
int call_fds[2]; int call_fds[2];
int rc; 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 0;
}
/* Use a pipe for call fd, since SIGIO is not supported for eventfd */ /* Use a pipe for call fd, since SIGIO is not supported for eventfd */
rc = os_pipe(call_fds, true, true); rc = os_pipe(call_fds, true, true);
if (rc < 0) if (rc < 0)
...@@ -838,10 +875,15 @@ static struct virtqueue *vu_setup_vq(struct virtio_device *vdev, ...@@ -838,10 +875,15 @@ static struct virtqueue *vu_setup_vq(struct virtio_device *vdev,
vq->priv = info; vq->priv = info;
num = virtqueue_get_vring_size(vq); num = virtqueue_get_vring_size(vq);
if (vu_dev->protocol_features &
BIT_ULL(VHOST_USER_PROTOCOL_F_INBAND_NOTIFICATIONS)) {
info->kick_fd = -1;
} else {
rc = os_eventfd(0, 0); rc = os_eventfd(0, 0);
if (rc < 0) if (rc < 0)
goto error_kick; goto error_kick;
info->kick_fd = rc; info->kick_fd = rc;
}
rc = vu_setup_vq_call_fd(vu_dev, vq); rc = vu_setup_vq_call_fd(vu_dev, vq);
if (rc) if (rc)
...@@ -866,9 +908,12 @@ static struct virtqueue *vu_setup_vq(struct virtio_device *vdev, ...@@ -866,9 +908,12 @@ static struct virtqueue *vu_setup_vq(struct virtio_device *vdev,
return vq; return vq;
error_setup: error_setup:
if (info->call_fd >= 0) {
um_free_irq(VIRTIO_IRQ, vq); um_free_irq(VIRTIO_IRQ, vq);
os_close_file(info->call_fd); os_close_file(info->call_fd);
}
error_call: error_call:
if (info->kick_fd >= 0)
os_close_file(info->kick_fd); os_close_file(info->kick_fd);
error_kick: error_kick:
vring_del_virtqueue(vq); vring_del_virtqueue(vq);
...@@ -908,10 +953,12 @@ static int vu_find_vqs(struct virtio_device *vdev, unsigned nvqs, ...@@ -908,10 +953,12 @@ static int vu_find_vqs(struct virtio_device *vdev, unsigned nvqs,
list_for_each_entry(vq, &vdev->vqs, list) { list_for_each_entry(vq, &vdev->vqs, list) {
struct virtio_uml_vq_info *info = vq->priv; struct virtio_uml_vq_info *info = vq->priv;
if (info->kick_fd >= 0) {
rc = vhost_user_set_vring_kick(vu_dev, vq->index, rc = vhost_user_set_vring_kick(vu_dev, vq->index,
info->kick_fd); info->kick_fd);
if (rc) if (rc)
goto error_setup; goto error_setup;
}
rc = vhost_user_set_vring_enable(vu_dev, vq->index, true); rc = vhost_user_set_vring_enable(vu_dev, vq->index, true);
if (rc) if (rc)
...@@ -1008,6 +1055,8 @@ static int virtio_uml_probe(struct platform_device *pdev) ...@@ -1008,6 +1055,8 @@ static int virtio_uml_probe(struct platform_device *pdev)
return rc; return rc;
vu_dev->sock = rc; vu_dev->sock = rc;
spin_lock_init(&vu_dev->sock_lock);
rc = vhost_user_init(vu_dev); rc = vhost_user_init(vu_dev);
if (rc) if (rc)
goto error_init; goto error_init;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment