[PATCH vfio 05/11] virtio-pci: Introduce admin command sending function

Yishai Hadas
Thu, 21 Sep 2023 12:40:34 -0000

Add support for sending admin command through admin virtqueue interface,
and expose generic API to execute virtio admin command. Reuse the send
synchronous command helper function at virtio transport layer. In
addition, add new result state of admin command and admin commands range
definitions.

Signed-off-by: Feng Liu <feliu at nvidia.com>
Reviewed-by: Parav Pandit <parav at nvidia.com>
Reviewed-by: Jiri Pirko <jiri at nvidia.com>
Signed-off-by: Yishai Hadas <yishaih at nvidia.com>
---
 drivers/virtio/virtio.c                |  7 +++
 drivers/virtio/virtio_pci_common.h     |  1 +
 drivers/virtio/virtio_pci_modern.c     |  2 +
 drivers/virtio/virtio_pci_modern_avq.c | 73 ++++++++++++++++++++++++++
 include/linux/virtio.h                 | 11 ++++
 include/linux/virtio_config.h          |  3 ++
 include/uapi/linux/virtio_pci.h        | 22 ++++++++
 7 files changed, 119 insertions(+)

diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c
index f4080692b351..dd71f584a1bd 100644
--- a/drivers/virtio/virtio.c
+++ b/drivers/virtio/virtio.c
@@ -582,6 +582,13 @@ int virtio_device_restore(struct virtio_device *dev)
 EXPORT_SYMBOL_GPL(virtio_device_restore);
 #endif

+int virtio_admin_cmd_exec(struct virtio_device *vdev,
+			  struct virtio_admin_cmd *cmd)
+{
+	return vdev->config->exec_admin_cmd(vdev, cmd);
+}
+EXPORT_SYMBOL_GPL(virtio_admin_cmd_exec);
+
 static int virtio_init(void)
 {
 	if (bus_register(&virtio_bus) != 0)
diff --git a/drivers/virtio/virtio_pci_common.h
b/drivers/virtio/virtio_pci_common.h
index 9bffa95274b6..a579f1338263 100644
--- a/drivers/virtio/virtio_pci_common.h
+++ b/drivers/virtio/virtio_pci_common.h
@@ -128,6 +128,7 @@ int vp_find_vqs(struct virtio_device *vdev, unsigned int
nvqs,
 const char *vp_bus_name(struct virtio_device *vdev);
 void vp_destroy_avq(struct virtio_device *vdev);
 int vp_create_avq(struct virtio_device *vdev);
+int vp_avq_cmd_exec(struct virtio_device *vdev, struct virtio_admin_cmd *cmd);

 /* Setup the affinity for a virtqueue:
  * - force the affinity for per vq vector
diff --git a/drivers/virtio/virtio_pci_modern.c
b/drivers/virtio/virtio_pci_modern.c
index a72c87687196..cac18872b088 100644
--- a/drivers/virtio/virtio_pci_modern.c
+++ b/drivers/virtio/virtio_pci_modern.c
@@ -515,6 +515,7 @@ static const struct virtio_config_ops
virtio_pci_config_nodev_ops = {
 	.enable_vq_after_reset = vp_modern_enable_vq_after_reset,
 	.create_avq = vp_create_avq,
 	.destroy_avq = vp_destroy_avq,
+	.exec_admin_cmd = vp_avq_cmd_exec,
 };

 static const struct virtio_config_ops virtio_pci_config_ops = {
@@ -537,6 +538,7 @@ static const struct virtio_config_ops virtio_pci_config_ops
= {
 	.enable_vq_after_reset = vp_modern_enable_vq_after_reset,
 	.create_avq = vp_create_avq,
 	.destroy_avq = vp_destroy_avq,
+	.exec_admin_cmd = vp_avq_cmd_exec,
 };

 /* the PCI probing function */
diff --git a/drivers/virtio/virtio_pci_modern_avq.c
b/drivers/virtio/virtio_pci_modern_avq.c
index 114579ad788f..ca3fe10f616d 100644
--- a/drivers/virtio/virtio_pci_modern_avq.c
+++ b/drivers/virtio/virtio_pci_modern_avq.c
@@ -19,6 +19,79 @@ static u16 vp_modern_avq_index(struct
virtio_pci_modern_device *mdev)
 	return vp_ioread16(&cfg->admin_queue_index);
 }

+#define VIRTIO_AVQ_SGS_MAX	4
+
+int vp_avq_cmd_exec(struct virtio_device *vdev, struct virtio_admin_cmd *cmd)
+{
+	struct scatterlist *sgs[VIRTIO_AVQ_SGS_MAX], hdr, stat;
+	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
+	struct virtio_admin_cmd_status *va_status;
+	unsigned int out_num = 0, in_num = 0;
+	struct virtio_admin_cmd_hdr *va_hdr;
+	struct virtqueue *avq;
+	u16 status;
+	int ret;
+
+	avq = vp_dev->admin ? vp_dev->admin->info.vq : NULL;
+	if (!avq)
+		return -EOPNOTSUPP;
+
+	va_status = kzalloc(sizeof(*va_status), GFP_KERNEL);
+	if (!va_status)
+		return -ENOMEM;
+
+	va_hdr = kzalloc(sizeof(*va_hdr), GFP_KERNEL);
+	if (!va_hdr) {
+		ret = -ENOMEM;
+		goto err_alloc;
+	}
+
+	va_hdr->opcode = cmd->opcode;
+	va_hdr->group_type = cmd->group_type;
+	va_hdr->group_member_id = cmd->group_member_id;
+
+	/* Add header */
+	sg_init_one(&hdr, va_hdr, sizeof(*va_hdr));
+	sgs[out_num] = &hdr;
+	out_num++;
+
+	if (cmd->data_sg) {
+		sgs[out_num] = cmd->data_sg;
+		out_num++;
+	}
+
+	/* Add return status */
+	sg_init_one(&stat, va_status, sizeof(*va_status));
+	sgs[out_num + in_num] = &stat;
+	in_num++;
+
+	if (cmd->result_sg) {
+		sgs[out_num + in_num] = cmd->result_sg;
+		in_num++;
+	}
+
+	ret = virtqueue_exec_cmd(avq, sgs, out_num, in_num, sgs, GFP_KERNEL);
+	if (ret) {
+		dev_err(&vdev->dev,
+			"Failed to execute command on admin vq: %d\n.", ret);
+		goto err_cmd_exec;
+	}
+
+	status = le16_to_cpu(va_status->status);
+	if (status != VIRTIO_ADMIN_STATUS_OK) {
+		dev_err(&vdev->dev,
+			"admin command error: status(%#x) qualifier(%#x)\n",
+			status, le16_to_cpu(va_status->status_qualifier));
+		ret = -status;
+	}
+
+err_cmd_exec:
+	kfree(va_hdr);
+err_alloc:
+	kfree(va_status);
+	return ret;
+}
+
 int vp_create_avq(struct virtio_device *vdev)
 {
 	struct virtio_pci_device *vp_dev = to_vp_device(vdev);
diff --git a/include/linux/virtio.h b/include/linux/virtio.h
index 9d39706bed10..094a2ef1c8b8 100644
--- a/include/linux/virtio.h
+++ b/include/linux/virtio.h
@@ -110,6 +110,14 @@ int virtqueue_exec_cmd(struct virtqueue *vq,
 		       void *data,
 		       gfp_t gfp);

+struct virtio_admin_cmd {
+	__le16 opcode;
+	__le16 group_type;
+	__le64 group_member_id;
+	struct scatterlist *data_sg;
+	struct scatterlist *result_sg;
+};
+
 /**
  * struct virtio_device - representation of a device using virtio
  * @index: unique position on the virtio bus
@@ -207,6 +215,9 @@ static inline struct virtio_driver *drv_to_virtio(struct
device_driver *drv)
 	return container_of(drv, struct virtio_driver, driver);
 }

+int virtio_admin_cmd_exec(struct virtio_device *vdev,
+			  struct virtio_admin_cmd *cmd);
+
 int register_virtio_driver(struct virtio_driver *drv);
 void unregister_virtio_driver(struct virtio_driver *drv);

diff --git a/include/linux/virtio_config.h b/include/linux/virtio_config.h
index 028c51ea90ee..e213173e1291 100644
--- a/include/linux/virtio_config.h
+++ b/include/linux/virtio_config.h
@@ -95,6 +95,7 @@ typedef void vq_callback_t(struct virtqueue *);
  *	set.
  * @create_avq: initialize admin virtqueue resource.
  * @destroy_avq: destroy admin virtqueue resource.
+ * @exec_admin_cmd: Send admin command and get result.
  */
 struct virtio_config_ops {
 	void (*get)(struct virtio_device *vdev, unsigned offset,
@@ -124,6 +125,8 @@ struct virtio_config_ops {
 	int (*enable_vq_after_reset)(struct virtqueue *vq);
 	int (*create_avq)(struct virtio_device *vdev);
 	void (*destroy_avq)(struct virtio_device *vdev);
+	int (*exec_admin_cmd)(struct virtio_device *vdev,
+			      struct virtio_admin_cmd *cmd);
 };

 /* If driver didn't advertise the feature, it will never appear. */
diff --git a/include/uapi/linux/virtio_pci.h b/include/uapi/linux/virtio_pci.h
index f703afc7ad31..1f1ac6ac07df 100644
--- a/include/uapi/linux/virtio_pci.h
+++ b/include/uapi/linux/virtio_pci.h
@@ -207,4 +207,26 @@ struct virtio_pci_cfg_cap {

 #endif /* VIRTIO_PCI_NO_MODERN */

+/* Admin command status. */
+#define VIRTIO_ADMIN_STATUS_OK		0
+
+struct virtio_admin_cmd_hdr {
+	__le16 opcode;
+	/*
+	 * 1 - SR-IOV
+	 * 2-65535 - reserved
+	 */
+	__le16 group_type;
+	/* Unused, reserved for future extensions. */
+	__u8 reserved1[12];
+	__le64 group_member_id;
+} __packed;
+
+struct virtio_admin_cmd_status {
+	__le16 status;
+	__le16 status_qualifier;
+	/* Unused, reserved for future extensions. */
+	__u8 reserved2[4];
+} __packed;
+
 #endif
--
2.27.0