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

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
Loading
Loading
Loading
Loading
+8 −4
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@
#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	14
/* 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 = 35,
};

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 {
+67 −18
Original line number Diff line number Diff line
@@ -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;
@@ -189,6 +190,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;

@@ -207,24 +209,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,
@@ -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)
{
	struct virtio_uml_device *vu_dev = data;
	struct virtqueue *vq;
	int response = 1;
	struct {
		struct vhost_user_msg msg;
@@ -343,6 +350,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:
@@ -684,6 +700,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);
@@ -749,9 +774,12 @@ static void vu_del_vq(struct virtqueue *vq)
{
	struct virtio_uml_vq_info *info = vq->priv;

	if (info->call_fd >= 0) {
		um_free_irq(VIRTIO_IRQ, vq);

		os_close_file(info->call_fd);
	}

	if (info->kick_fd >= 0)
		os_close_file(info->kick_fd);

	vring_del_virtqueue(vq);
@@ -782,6 +810,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 0;
	}

	/* Use a pipe for call fd, since SIGIO is not supported for eventfd */
	rc = os_pipe(call_fds, true, true);
	if (rc < 0)
@@ -838,10 +875,15 @@ static struct virtqueue *vu_setup_vq(struct virtio_device *vdev,
	vq->priv = info;
	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);
		if (rc < 0)
			goto error_kick;
		info->kick_fd = rc;
	}

	rc = vu_setup_vq_call_fd(vu_dev, vq);
	if (rc)
@@ -866,9 +908,12 @@ static struct virtqueue *vu_setup_vq(struct virtio_device *vdev,
	return vq;

error_setup:
	if (info->call_fd >= 0) {
		um_free_irq(VIRTIO_IRQ, vq);
		os_close_file(info->call_fd);
	}
error_call:
	if (info->kick_fd >= 0)
		os_close_file(info->kick_fd);
error_kick:
	vring_del_virtqueue(vq);
@@ -908,10 +953,12 @@ static int vu_find_vqs(struct virtio_device *vdev, unsigned nvqs,
	list_for_each_entry(vq, &vdev->vqs, list) {
		struct virtio_uml_vq_info *info = vq->priv;

		if (info->kick_fd >= 0) {
			rc = vhost_user_set_vring_kick(vu_dev, vq->index,
						       info->kick_fd);
			if (rc)
				goto error_setup;
		}

		rc = vhost_user_set_vring_enable(vu_dev, vq->index, true);
		if (rc)
@@ -1008,6 +1055,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;