Skip to content

Commit

Permalink
virtio-pci: Introduce admin virtqueue
Browse files Browse the repository at this point in the history
Introduce support for the admin virtqueue. By negotiating
VIRTIO_F_ADMIN_VQ feature, driver detects capability and creates one
administration virtqueue. Administration virtqueue implementation in
virtio pci generic layer, enables multiple types of upper layer
drivers such as vfio, net, blk to utilize it.

Signed-off-by: Feng Liu <feliu@nvidia.com>
Reviewed-by: Parav Pandit <parav@nvidia.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Acked-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
Link: https://lore.kernel.org/r/20231219093247.170936-3-yishaih@nvidia.com
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
  • Loading branch information
Feng Liu authored and awilliam committed Dec 19, 2023
1 parent 838bebb commit fd27ef6
Show file tree
Hide file tree
Showing 8 changed files with 157 additions and 8 deletions.
37 changes: 33 additions & 4 deletions drivers/virtio/virtio.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,9 +302,15 @@ static int virtio_dev_probe(struct device *_d)
if (err)
goto err;

if (dev->config->create_avq) {
err = dev->config->create_avq(dev);
if (err)
goto err;
}

err = drv->probe(dev);
if (err)
goto err;
goto err_probe;

/* If probe didn't do it, mark device DRIVER_OK ourselves. */
if (!(dev->config->get_status(dev) & VIRTIO_CONFIG_S_DRIVER_OK))
Expand All @@ -316,6 +322,10 @@ static int virtio_dev_probe(struct device *_d)
virtio_config_enable(dev);

return 0;

err_probe:
if (dev->config->destroy_avq)
dev->config->destroy_avq(dev);
err:
virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED);
return err;
Expand All @@ -331,6 +341,9 @@ static void virtio_dev_remove(struct device *_d)

drv->remove(dev);

if (dev->config->destroy_avq)
dev->config->destroy_avq(dev);

/* Driver should have reset device. */
WARN_ON_ONCE(dev->config->get_status(dev));

Expand Down Expand Up @@ -489,13 +502,20 @@ EXPORT_SYMBOL_GPL(unregister_virtio_device);
int virtio_device_freeze(struct virtio_device *dev)
{
struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
int ret;

virtio_config_disable(dev);

dev->failed = dev->config->get_status(dev) & VIRTIO_CONFIG_S_FAILED;

if (drv && drv->freeze)
return drv->freeze(dev);
if (drv && drv->freeze) {
ret = drv->freeze(dev);
if (ret)
return ret;
}

if (dev->config->destroy_avq)
dev->config->destroy_avq(dev);

return 0;
}
Expand Down Expand Up @@ -532,10 +552,16 @@ int virtio_device_restore(struct virtio_device *dev)
if (ret)
goto err;

if (dev->config->create_avq) {
ret = dev->config->create_avq(dev);
if (ret)
goto err;
}

if (drv->restore) {
ret = drv->restore(dev);
if (ret)
goto err;
goto err_restore;
}

/* If restore didn't do it, mark device DRIVER_OK ourselves. */
Expand All @@ -546,6 +572,9 @@ int virtio_device_restore(struct virtio_device *dev)

return 0;

err_restore:
if (dev->config->destroy_avq)
dev->config->destroy_avq(dev);
err:
virtio_add_status(dev, VIRTIO_CONFIG_S_FAILED);
return ret;
Expand Down
3 changes: 3 additions & 0 deletions drivers/virtio/virtio_pci_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@ void vp_del_vqs(struct virtio_device *vdev)
int i;

list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
if (vp_dev->is_avq(vdev, vq->index))
continue;

if (vp_dev->per_vq_vectors) {
int v = vp_dev->vqs[vq->index]->msix_vector;

Expand Down
15 changes: 14 additions & 1 deletion drivers/virtio/virtio_pci_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ struct virtio_pci_vq_info {
unsigned int msix_vector;
};

struct virtio_pci_admin_vq {
/* Virtqueue info associated with this admin queue. */
struct virtio_pci_vq_info info;
/* Name of the admin queue: avq.$vq_index. */
char name[10];
u16 vq_index;
};

/* Our device structure */
struct virtio_pci_device {
struct virtio_device vdev;
Expand All @@ -58,9 +66,13 @@ struct virtio_pci_device {
spinlock_t lock;
struct list_head virtqueues;

/* array of all queues for house-keeping */
/* Array of all virtqueues reported in the
* PCI common config num_queues field
*/
struct virtio_pci_vq_info **vqs;

struct virtio_pci_admin_vq admin_vq;

/* MSI-X support */
int msix_enabled;
int intx_enabled;
Expand All @@ -86,6 +98,7 @@ struct virtio_pci_device {
void (*del_vq)(struct virtio_pci_vq_info *info);

u16 (*config_vector)(struct virtio_pci_device *vp_dev, u16 vector);
bool (*is_avq)(struct virtio_device *vdev, unsigned int index);
};

/* Constants for MSI-X */
Expand Down
75 changes: 73 additions & 2 deletions drivers/virtio/virtio_pci_modern.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,25 @@
#define VIRTIO_RING_NO_LEGACY
#include "virtio_pci_common.h"

#define VIRTIO_AVQ_SGS_MAX 4

static u64 vp_get_features(struct virtio_device *vdev)
{
struct virtio_pci_device *vp_dev = to_vp_device(vdev);

return vp_modern_get_features(&vp_dev->mdev);
}

static bool vp_is_avq(struct virtio_device *vdev, unsigned int index)
{
struct virtio_pci_device *vp_dev = to_vp_device(vdev);

if (!virtio_has_feature(vdev, VIRTIO_F_ADMIN_VQ))
return false;

return index == vp_dev->admin_vq.vq_index;
}

static void vp_transport_features(struct virtio_device *vdev, u64 features)
{
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
Expand All @@ -37,6 +49,9 @@ static void vp_transport_features(struct virtio_device *vdev, u64 features)

if (features & BIT_ULL(VIRTIO_F_RING_RESET))
__virtio_set_bit(vdev, VIRTIO_F_RING_RESET);

if (features & BIT_ULL(VIRTIO_F_ADMIN_VQ))
__virtio_set_bit(vdev, VIRTIO_F_ADMIN_VQ);
}

static int __vp_check_common_size_one_feature(struct virtio_device *vdev, u32 fbit,
Expand Down Expand Up @@ -69,6 +84,9 @@ static int vp_check_common_size(struct virtio_device *vdev)
if (vp_check_common_size_one_feature(vdev, VIRTIO_F_RING_RESET, queue_reset))
return -EINVAL;

if (vp_check_common_size_one_feature(vdev, VIRTIO_F_ADMIN_VQ, admin_queue_num))
return -EINVAL;

return 0;
}

Expand Down Expand Up @@ -345,6 +363,7 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
struct virtio_pci_modern_device *mdev = &vp_dev->mdev;
bool (*notify)(struct virtqueue *vq);
struct virtqueue *vq;
bool is_avq;
u16 num;
int err;

Expand All @@ -353,11 +372,13 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
else
notify = vp_notify;

if (index >= vp_modern_get_num_queues(mdev))
is_avq = vp_is_avq(&vp_dev->vdev, index);
if (index >= vp_modern_get_num_queues(mdev) && !is_avq)
return ERR_PTR(-EINVAL);

num = is_avq ?
VIRTIO_AVQ_SGS_MAX : vp_modern_get_queue_size(mdev, index);
/* Check if queue is either not available or already active. */
num = vp_modern_get_queue_size(mdev, index);
if (!num || vp_modern_get_queue_enable(mdev, index))
return ERR_PTR(-ENOENT);

Expand All @@ -383,6 +404,9 @@ static struct virtqueue *setup_vq(struct virtio_pci_device *vp_dev,
goto err;
}

if (is_avq)
vp_dev->admin_vq.info.vq = vq;

return vq;

err:
Expand Down Expand Up @@ -418,6 +442,9 @@ static void del_vq(struct virtio_pci_vq_info *info)
struct virtio_pci_device *vp_dev = to_vp_device(vq->vdev);
struct virtio_pci_modern_device *mdev = &vp_dev->mdev;

if (vp_is_avq(&vp_dev->vdev, vq->index))
vp_dev->admin_vq.info.vq = NULL;

if (vp_dev->msix_enabled)
vp_modern_queue_vector(mdev, vq->index,
VIRTIO_MSI_NO_VECTOR);
Expand Down Expand Up @@ -527,6 +554,45 @@ static bool vp_get_shm_region(struct virtio_device *vdev,
return true;
}

static int vp_modern_create_avq(struct virtio_device *vdev)
{
struct virtio_pci_device *vp_dev = to_vp_device(vdev);
struct virtio_pci_admin_vq *avq;
struct virtqueue *vq;
u16 admin_q_num;

if (!virtio_has_feature(vdev, VIRTIO_F_ADMIN_VQ))
return 0;

admin_q_num = vp_modern_avq_num(&vp_dev->mdev);
if (!admin_q_num)
return -EINVAL;

avq = &vp_dev->admin_vq;
avq->vq_index = vp_modern_avq_index(&vp_dev->mdev);
sprintf(avq->name, "avq.%u", avq->vq_index);
vq = vp_dev->setup_vq(vp_dev, &vp_dev->admin_vq.info, avq->vq_index, NULL,
avq->name, NULL, VIRTIO_MSI_NO_VECTOR);
if (IS_ERR(vq)) {
dev_err(&vdev->dev, "failed to setup admin virtqueue, err=%ld",
PTR_ERR(vq));
return PTR_ERR(vq);
}

vp_modern_set_queue_enable(&vp_dev->mdev, avq->info.vq->index, true);
return 0;
}

static void vp_modern_destroy_avq(struct virtio_device *vdev)
{
struct virtio_pci_device *vp_dev = to_vp_device(vdev);

if (!virtio_has_feature(vdev, VIRTIO_F_ADMIN_VQ))
return;

vp_dev->del_vq(&vp_dev->admin_vq.info);
}

static const struct virtio_config_ops virtio_pci_config_nodev_ops = {
.get = NULL,
.set = NULL,
Expand All @@ -545,6 +611,8 @@ static const struct virtio_config_ops virtio_pci_config_nodev_ops = {
.get_shm_region = vp_get_shm_region,
.disable_vq_and_reset = vp_modern_disable_vq_and_reset,
.enable_vq_after_reset = vp_modern_enable_vq_after_reset,
.create_avq = vp_modern_create_avq,
.destroy_avq = vp_modern_destroy_avq,
};

static const struct virtio_config_ops virtio_pci_config_ops = {
Expand All @@ -565,6 +633,8 @@ static const struct virtio_config_ops virtio_pci_config_ops = {
.get_shm_region = vp_get_shm_region,
.disable_vq_and_reset = vp_modern_disable_vq_and_reset,
.enable_vq_after_reset = vp_modern_enable_vq_after_reset,
.create_avq = vp_modern_create_avq,
.destroy_avq = vp_modern_destroy_avq,
};

/* the PCI probing function */
Expand All @@ -588,6 +658,7 @@ int virtio_pci_modern_probe(struct virtio_pci_device *vp_dev)
vp_dev->config_vector = vp_config_vector;
vp_dev->setup_vq = setup_vq;
vp_dev->del_vq = del_vq;
vp_dev->is_avq = vp_is_avq;
vp_dev->isr = mdev->isr;
vp_dev->vdev.id = mdev->id;

Expand Down
24 changes: 23 additions & 1 deletion drivers/virtio/virtio_pci_modern_dev.c
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ static inline void check_offsets(void)
offsetof(struct virtio_pci_modern_common_cfg, queue_notify_data));
BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_RESET !=
offsetof(struct virtio_pci_modern_common_cfg, queue_reset));
BUILD_BUG_ON(VIRTIO_PCI_COMMON_ADM_Q_IDX !=
offsetof(struct virtio_pci_modern_common_cfg, admin_queue_index));
BUILD_BUG_ON(VIRTIO_PCI_COMMON_ADM_Q_NUM !=
offsetof(struct virtio_pci_modern_common_cfg, admin_queue_num));
}

/*
Expand Down Expand Up @@ -296,7 +300,7 @@ int vp_modern_probe(struct virtio_pci_modern_device *mdev)
mdev->common = vp_modern_map_capability(mdev, common,
sizeof(struct virtio_pci_common_cfg), 4, 0,
offsetofend(struct virtio_pci_modern_common_cfg,
queue_reset),
admin_queue_num),
&mdev->common_len, NULL);
if (!mdev->common)
goto err_map_common;
Expand Down Expand Up @@ -719,6 +723,24 @@ void __iomem *vp_modern_map_vq_notify(struct virtio_pci_modern_device *mdev,
}
EXPORT_SYMBOL_GPL(vp_modern_map_vq_notify);

u16 vp_modern_avq_num(struct virtio_pci_modern_device *mdev)
{
struct virtio_pci_modern_common_cfg __iomem *cfg;

cfg = (struct virtio_pci_modern_common_cfg __iomem *)mdev->common;
return vp_ioread16(&cfg->admin_queue_num);
}
EXPORT_SYMBOL_GPL(vp_modern_avq_num);

u16 vp_modern_avq_index(struct virtio_pci_modern_device *mdev)
{
struct virtio_pci_modern_common_cfg __iomem *cfg;

cfg = (struct virtio_pci_modern_common_cfg __iomem *)mdev->common;
return vp_ioread16(&cfg->admin_queue_index);
}
EXPORT_SYMBOL_GPL(vp_modern_avq_index);

MODULE_VERSION("0.1");
MODULE_DESCRIPTION("Modern Virtio PCI Device");
MODULE_AUTHOR("Jason Wang <jasowang@redhat.com>");
Expand Down
4 changes: 4 additions & 0 deletions include/linux/virtio_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,8 @@ typedef void vq_callback_t(struct virtqueue *);
* Returns 0 on success or error status
* If disable_vq_and_reset is set, then enable_vq_after_reset must also be
* set.
* @create_avq: create admin virtqueue resource.
* @destroy_avq: destroy admin virtqueue resource.
*/
struct virtio_config_ops {
void (*get)(struct virtio_device *vdev, unsigned offset,
Expand Down Expand Up @@ -120,6 +122,8 @@ struct virtio_config_ops {
struct virtio_shm_region *region, u8 id);
int (*disable_vq_and_reset)(struct virtqueue *vq);
int (*enable_vq_after_reset)(struct virtqueue *vq);
int (*create_avq)(struct virtio_device *vdev);
void (*destroy_avq)(struct virtio_device *vdev);
};

/* If driver didn't advertise the feature, it will never appear. */
Expand Down
2 changes: 2 additions & 0 deletions include/linux/virtio_pci_modern.h
Original file line number Diff line number Diff line change
Expand Up @@ -125,4 +125,6 @@ int vp_modern_probe(struct virtio_pci_modern_device *mdev);
void vp_modern_remove(struct virtio_pci_modern_device *mdev);
int vp_modern_get_queue_reset(struct virtio_pci_modern_device *mdev, u16 index);
void vp_modern_set_queue_reset(struct virtio_pci_modern_device *mdev, u16 index);
u16 vp_modern_avq_num(struct virtio_pci_modern_device *mdev);
u16 vp_modern_avq_index(struct virtio_pci_modern_device *mdev);
#endif
5 changes: 5 additions & 0 deletions include/uapi/linux/virtio_pci.h
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ struct virtio_pci_modern_common_cfg {

__le16 queue_notify_data; /* read-write */
__le16 queue_reset; /* read-write */

__le16 admin_queue_index; /* read-only */
__le16 admin_queue_num; /* read-only */
};

/* Fields in VIRTIO_PCI_CAP_PCI_CFG: */
Expand Down Expand Up @@ -215,6 +218,8 @@ struct virtio_pci_cfg_cap {
#define VIRTIO_PCI_COMMON_Q_USEDHI 52
#define VIRTIO_PCI_COMMON_Q_NDATA 56
#define VIRTIO_PCI_COMMON_Q_RESET 58
#define VIRTIO_PCI_COMMON_ADM_Q_IDX 60
#define VIRTIO_PCI_COMMON_ADM_Q_NUM 62

#endif /* VIRTIO_PCI_NO_MODERN */

Expand Down

0 comments on commit fd27ef6

Please sign in to comment.