Skip to content

Commit

Permalink
virtio: Manage device and driver capabilities via the admin commands
Browse files Browse the repository at this point in the history
Manage device and driver capabilities via the admin commands.

The device exposes its supported features and resource object limits via
an administrative command called VIRTIO_ADMIN_CMD_CAP_ID_LIST_QUERY,
using the 'self group type.'

Each capability is identified by a unique ID, and the driver
communicates the functionality and resource limits it plans to utilize.

The capability VIRTIO_DEV_PARTS_CAP specifically represents the device's
parts resource object limit.

Manage the device's parts resource object ID using a common IDA for both
get and set operations.

Acked-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
Link: https://lore.kernel.org/r/20241113115200.209269-4-yishaih@nvidia.com
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
  • Loading branch information
Yishai Hadas authored and Alex Williamson committed Nov 13, 2024
1 parent 704806c commit bfcad51
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 2 deletions.
13 changes: 11 additions & 2 deletions drivers/virtio/virtio_pci_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ struct virtio_pci_admin_vq {
/* Protects virtqueue access. */
spinlock_t lock;
u64 supported_cmds;
u64 supported_caps;
u8 max_dev_parts_objects;
struct ida dev_parts_ida;
/* Name of the admin queue: avq.$vq_index. */
char name[10];
u16 vq_index;
Expand Down Expand Up @@ -167,15 +170,21 @@ struct virtio_device *virtio_pci_vf_get_pf_dev(struct pci_dev *pdev);
BIT_ULL(VIRTIO_ADMIN_CMD_LEGACY_DEV_CFG_READ) | \
BIT_ULL(VIRTIO_ADMIN_CMD_LEGACY_NOTIFY_INFO))

#define VIRTIO_DEV_PARTS_ADMIN_CMD_BITMAP \
(BIT_ULL(VIRTIO_ADMIN_CMD_CAP_ID_LIST_QUERY) | \
BIT_ULL(VIRTIO_ADMIN_CMD_DRIVER_CAP_SET) | \
BIT_ULL(VIRTIO_ADMIN_CMD_DEVICE_CAP_GET))

/* Unlike modern drivers which support hardware virtio devices, legacy drivers
* assume software-based devices: e.g. they don't use proper memory barriers
* on ARM, use big endian on PPC, etc. X86 drivers are mostly ok though, more
* or less by chance. For now, only support legacy IO on X86.
*/
#ifdef CONFIG_VIRTIO_PCI_ADMIN_LEGACY
#define VIRTIO_ADMIN_CMD_BITMAP VIRTIO_LEGACY_ADMIN_CMD_BITMAP
#define VIRTIO_ADMIN_CMD_BITMAP (VIRTIO_LEGACY_ADMIN_CMD_BITMAP | \
VIRTIO_DEV_PARTS_ADMIN_CMD_BITMAP)
#else
#define VIRTIO_ADMIN_CMD_BITMAP 0
#define VIRTIO_ADMIN_CMD_BITMAP VIRTIO_DEV_PARTS_ADMIN_CMD_BITMAP
#endif

void vp_modern_avq_done(struct virtqueue *vq);
Expand Down
105 changes: 105 additions & 0 deletions drivers/virtio/virtio_pci_modern.c
Original file line number Diff line number Diff line change
Expand Up @@ -230,12 +230,117 @@ static void virtio_pci_admin_cmd_list_init(struct virtio_device *virtio_dev)
kfree(data);
}

static void
virtio_pci_admin_cmd_dev_parts_objects_enable(struct virtio_device *virtio_dev)
{
struct virtio_pci_device *vp_dev = to_vp_device(virtio_dev);
struct virtio_admin_cmd_cap_get_data *get_data;
struct virtio_admin_cmd_cap_set_data *set_data;
struct virtio_dev_parts_cap *result;
struct virtio_admin_cmd cmd = {};
struct scatterlist result_sg;
struct scatterlist data_sg;
u8 resource_objects_limit;
u16 set_data_size;
int ret;

get_data = kzalloc(sizeof(*get_data), GFP_KERNEL);
if (!get_data)
return;

result = kzalloc(sizeof(*result), GFP_KERNEL);
if (!result)
goto end;

get_data->id = cpu_to_le16(VIRTIO_DEV_PARTS_CAP);
sg_init_one(&data_sg, get_data, sizeof(*get_data));
sg_init_one(&result_sg, result, sizeof(*result));
cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_DEVICE_CAP_GET);
cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV);
cmd.data_sg = &data_sg;
cmd.result_sg = &result_sg;
ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd);
if (ret)
goto err_get;

set_data_size = sizeof(*set_data) + sizeof(*result);
set_data = kzalloc(set_data_size, GFP_KERNEL);
if (!set_data)
goto err_get;

set_data->id = cpu_to_le16(VIRTIO_DEV_PARTS_CAP);

/* Set the limit to the minimum value between the GET and SET values
* supported by the device. Since the obj_id for VIRTIO_DEV_PARTS_CAP
* is a globally unique value per PF, there is no possibility of
* overlap between GET and SET operations.
*/
resource_objects_limit = min(result->get_parts_resource_objects_limit,
result->set_parts_resource_objects_limit);
result->get_parts_resource_objects_limit = resource_objects_limit;
result->set_parts_resource_objects_limit = resource_objects_limit;
memcpy(set_data->cap_specific_data, result, sizeof(*result));
sg_init_one(&data_sg, set_data, set_data_size);
cmd.data_sg = &data_sg;
cmd.result_sg = NULL;
cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_DRIVER_CAP_SET);
ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd);
if (ret)
goto err_set;

/* Allocate IDR to manage the dev caps objects */
ida_init(&vp_dev->admin_vq.dev_parts_ida);
vp_dev->admin_vq.max_dev_parts_objects = resource_objects_limit;

err_set:
kfree(set_data);
err_get:
kfree(result);
end:
kfree(get_data);
}

static void virtio_pci_admin_cmd_cap_init(struct virtio_device *virtio_dev)
{
struct virtio_pci_device *vp_dev = to_vp_device(virtio_dev);
struct virtio_admin_cmd_query_cap_id_result *data;
struct virtio_admin_cmd cmd = {};
struct scatterlist result_sg;
int ret;

data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return;

sg_init_one(&result_sg, data, sizeof(*data));
cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_CAP_ID_LIST_QUERY);
cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV);
cmd.result_sg = &result_sg;

ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd);
if (ret)
goto end;

/* Max number of caps fits into a single u64 */
BUILD_BUG_ON(sizeof(data->supported_caps) > sizeof(u64));

vp_dev->admin_vq.supported_caps = le64_to_cpu(data->supported_caps[0]);

if (!(vp_dev->admin_vq.supported_caps & (1 << VIRTIO_DEV_PARTS_CAP)))
goto end;

virtio_pci_admin_cmd_dev_parts_objects_enable(virtio_dev);
end:
kfree(data);
}

static void vp_modern_avq_activate(struct virtio_device *vdev)
{
if (!virtio_has_feature(vdev, VIRTIO_F_ADMIN_VQ))
return;

virtio_pci_admin_cmd_list_init(vdev);
virtio_pci_admin_cmd_cap_init(vdev);
}

static void vp_modern_avq_cleanup(struct virtio_device *vdev)
Expand Down

0 comments on commit bfcad51

Please sign in to comment.