virtio-pci: Introduce APIs to execute device parts admin commands

Introduce APIs to handle the execution of device parts admin commands.

These APIs cover functionalities such as mode setting, object creation
and destruction, and operations like parts get/set and metadata
retrieval.

These APIs will be utilized in upcoming patches within this series.

Acked-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: Yishai Hadas <yishaih@nvidia.com>
Link: https://lore.kernel.org/r/20241113115200.209269-5-yishaih@nvidia.com
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
This commit is contained in:
Yishai Hadas 2024-11-13 13:51:57 +02:00 committed by Alex Williamson
parent bfcad51860
commit 52a22c0ed0
3 changed files with 366 additions and 1 deletions

View File

@ -173,7 +173,13 @@ struct virtio_device *virtio_pci_vf_get_pf_dev(struct pci_dev *pdev);
#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))
BIT_ULL(VIRTIO_ADMIN_CMD_DEVICE_CAP_GET) | \
BIT_ULL(VIRTIO_ADMIN_CMD_RESOURCE_OBJ_CREATE) | \
BIT_ULL(VIRTIO_ADMIN_CMD_RESOURCE_OBJ_DESTROY) | \
BIT_ULL(VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_GET) | \
BIT_ULL(VIRTIO_ADMIN_CMD_DEV_PARTS_GET) | \
BIT_ULL(VIRTIO_ADMIN_CMD_DEV_PARTS_SET) | \
BIT_ULL(VIRTIO_ADMIN_CMD_DEV_MODE_SET))
/* Unlike modern drivers which support hardware virtio devices, legacy drivers
* assume software-based devices: e.g. they don't use proper memory barriers

View File

@ -15,6 +15,7 @@
*/
#include <linux/delay.h>
#include <linux/virtio_pci_admin.h>
#define VIRTIO_PCI_NO_LEGACY
#define VIRTIO_RING_NO_LEGACY
#include "virtio_pci_common.h"
@ -875,6 +876,353 @@ static bool vp_get_shm_region(struct virtio_device *vdev,
return true;
}
/*
* virtio_pci_admin_has_dev_parts - Checks whether the device parts
* functionality is supported
* @pdev: VF pci_dev
*
* Returns true on success.
*/
bool virtio_pci_admin_has_dev_parts(struct pci_dev *pdev)
{
struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev);
struct virtio_pci_device *vp_dev;
if (!virtio_dev)
return false;
if (!virtio_has_feature(virtio_dev, VIRTIO_F_ADMIN_VQ))
return false;
vp_dev = to_vp_device(virtio_dev);
if (!((vp_dev->admin_vq.supported_cmds & VIRTIO_DEV_PARTS_ADMIN_CMD_BITMAP) ==
VIRTIO_DEV_PARTS_ADMIN_CMD_BITMAP))
return false;
return vp_dev->admin_vq.max_dev_parts_objects;
}
EXPORT_SYMBOL_GPL(virtio_pci_admin_has_dev_parts);
/*
* virtio_pci_admin_mode_set - Sets the mode of a member device
* @pdev: VF pci_dev
* @flags: device mode's flags
*
* Note: caller must serialize access for the given device.
* Returns 0 on success, or negative on failure.
*/
int virtio_pci_admin_mode_set(struct pci_dev *pdev, u8 flags)
{
struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev);
struct virtio_admin_cmd_dev_mode_set_data *data;
struct virtio_admin_cmd cmd = {};
struct scatterlist data_sg;
int vf_id;
int ret;
if (!virtio_dev)
return -ENODEV;
vf_id = pci_iov_vf_id(pdev);
if (vf_id < 0)
return vf_id;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->flags = flags;
sg_init_one(&data_sg, data, sizeof(*data));
cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_DEV_MODE_SET);
cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV);
cmd.group_member_id = cpu_to_le64(vf_id + 1);
cmd.data_sg = &data_sg;
ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd);
kfree(data);
return ret;
}
EXPORT_SYMBOL_GPL(virtio_pci_admin_mode_set);
/*
* virtio_pci_admin_obj_create - Creates an object for a given type and operation,
* following the max objects that can be created for that request.
* @pdev: VF pci_dev
* @obj_type: Object type
* @operation_type: Operation type
* @obj_id: Output unique object id
*
* Note: caller must serialize access for the given device.
* Returns 0 on success, or negative on failure.
*/
int virtio_pci_admin_obj_create(struct pci_dev *pdev, u16 obj_type, u8 operation_type,
u32 *obj_id)
{
struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev);
u16 data_size = sizeof(struct virtio_admin_cmd_resource_obj_create_data);
struct virtio_admin_cmd_resource_obj_create_data *obj_create_data;
struct virtio_resource_obj_dev_parts obj_dev_parts = {};
struct virtio_pci_admin_vq *avq;
struct virtio_admin_cmd cmd = {};
struct scatterlist data_sg;
void *data;
int id = -1;
int vf_id;
int ret;
if (!virtio_dev)
return -ENODEV;
vf_id = pci_iov_vf_id(pdev);
if (vf_id < 0)
return vf_id;
if (obj_type != VIRTIO_RESOURCE_OBJ_DEV_PARTS)
return -EOPNOTSUPP;
if (operation_type != VIRTIO_RESOURCE_OBJ_DEV_PARTS_TYPE_GET &&
operation_type != VIRTIO_RESOURCE_OBJ_DEV_PARTS_TYPE_SET)
return -EINVAL;
avq = &to_vp_device(virtio_dev)->admin_vq;
if (!avq->max_dev_parts_objects)
return -EOPNOTSUPP;
id = ida_alloc_range(&avq->dev_parts_ida, 0,
avq->max_dev_parts_objects - 1, GFP_KERNEL);
if (id < 0)
return id;
*obj_id = id;
data_size += sizeof(obj_dev_parts);
data = kzalloc(data_size, GFP_KERNEL);
if (!data) {
ret = -ENOMEM;
goto end;
}
obj_create_data = data;
obj_create_data->hdr.type = cpu_to_le16(obj_type);
obj_create_data->hdr.id = cpu_to_le32(*obj_id);
obj_dev_parts.type = operation_type;
memcpy(obj_create_data->resource_obj_specific_data, &obj_dev_parts,
sizeof(obj_dev_parts));
sg_init_one(&data_sg, data, data_size);
cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_RESOURCE_OBJ_CREATE);
cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV);
cmd.group_member_id = cpu_to_le64(vf_id + 1);
cmd.data_sg = &data_sg;
ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd);
kfree(data);
end:
if (ret)
ida_free(&avq->dev_parts_ida, id);
return ret;
}
EXPORT_SYMBOL_GPL(virtio_pci_admin_obj_create);
/*
* virtio_pci_admin_obj_destroy - Destroys an object of a given type and id
* @pdev: VF pci_dev
* @obj_type: Object type
* @id: Object id
*
* Note: caller must serialize access for the given device.
* Returns 0 on success, or negative on failure.
*/
int virtio_pci_admin_obj_destroy(struct pci_dev *pdev, u16 obj_type, u32 id)
{
struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev);
struct virtio_admin_cmd_resource_obj_cmd_hdr *data;
struct virtio_pci_device *vp_dev;
struct virtio_admin_cmd cmd = {};
struct scatterlist data_sg;
int vf_id;
int ret;
if (!virtio_dev)
return -ENODEV;
vf_id = pci_iov_vf_id(pdev);
if (vf_id < 0)
return vf_id;
if (obj_type != VIRTIO_RESOURCE_OBJ_DEV_PARTS)
return -EINVAL;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->type = cpu_to_le16(obj_type);
data->id = cpu_to_le32(id);
sg_init_one(&data_sg, data, sizeof(*data));
cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_RESOURCE_OBJ_DESTROY);
cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV);
cmd.group_member_id = cpu_to_le64(vf_id + 1);
cmd.data_sg = &data_sg;
ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd);
if (!ret) {
vp_dev = to_vp_device(virtio_dev);
ida_free(&vp_dev->admin_vq.dev_parts_ida, id);
}
kfree(data);
return ret;
}
EXPORT_SYMBOL_GPL(virtio_pci_admin_obj_destroy);
/*
* virtio_pci_admin_dev_parts_metadata_get - Gets the metadata of the device parts
* identified by the below attributes.
* @pdev: VF pci_dev
* @obj_type: Object type
* @id: Object id
* @metadata_type: Metadata type
* @out: Upon success holds the output for 'metadata type size'
*
* Note: caller must serialize access for the given device.
* Returns 0 on success, or negative on failure.
*/
int virtio_pci_admin_dev_parts_metadata_get(struct pci_dev *pdev, u16 obj_type,
u32 id, u8 metadata_type, u32 *out)
{
struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev);
struct virtio_admin_cmd_dev_parts_metadata_result *result;
struct virtio_admin_cmd_dev_parts_metadata_data *data;
struct scatterlist data_sg, result_sg;
struct virtio_admin_cmd cmd = {};
int vf_id;
int ret;
if (!virtio_dev)
return -ENODEV;
if (metadata_type != VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_TYPE_SIZE)
return -EOPNOTSUPP;
vf_id = pci_iov_vf_id(pdev);
if (vf_id < 0)
return vf_id;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
result = kzalloc(sizeof(*result), GFP_KERNEL);
if (!result) {
ret = -ENOMEM;
goto end;
}
data->hdr.type = cpu_to_le16(obj_type);
data->hdr.id = cpu_to_le32(id);
data->type = metadata_type;
sg_init_one(&data_sg, data, sizeof(*data));
sg_init_one(&result_sg, result, sizeof(*result));
cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_DEV_PARTS_METADATA_GET);
cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV);
cmd.group_member_id = cpu_to_le64(vf_id + 1);
cmd.data_sg = &data_sg;
cmd.result_sg = &result_sg;
ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd);
if (!ret)
*out = le32_to_cpu(result->parts_size.size);
kfree(result);
end:
kfree(data);
return ret;
}
EXPORT_SYMBOL_GPL(virtio_pci_admin_dev_parts_metadata_get);
/*
* virtio_pci_admin_dev_parts_get - Gets the device parts identified by the below attributes.
* @pdev: VF pci_dev
* @obj_type: Object type
* @id: Object id
* @get_type: Get type
* @res_sg: Upon success holds the output result data
* @res_size: Upon success holds the output result size
*
* Note: caller must serialize access for the given device.
* Returns 0 on success, or negative on failure.
*/
int virtio_pci_admin_dev_parts_get(struct pci_dev *pdev, u16 obj_type, u32 id,
u8 get_type, struct scatterlist *res_sg,
u32 *res_size)
{
struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev);
struct virtio_admin_cmd_dev_parts_get_data *data;
struct scatterlist data_sg;
struct virtio_admin_cmd cmd = {};
int vf_id;
int ret;
if (!virtio_dev)
return -ENODEV;
if (get_type != VIRTIO_ADMIN_CMD_DEV_PARTS_GET_TYPE_ALL)
return -EOPNOTSUPP;
vf_id = pci_iov_vf_id(pdev);
if (vf_id < 0)
return vf_id;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->hdr.type = cpu_to_le16(obj_type);
data->hdr.id = cpu_to_le32(id);
data->type = get_type;
sg_init_one(&data_sg, data, sizeof(*data));
cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_DEV_PARTS_GET);
cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV);
cmd.group_member_id = cpu_to_le64(vf_id + 1);
cmd.data_sg = &data_sg;
cmd.result_sg = res_sg;
ret = vp_modern_admin_cmd_exec(virtio_dev, &cmd);
if (!ret)
*res_size = cmd.result_sg_size;
kfree(data);
return ret;
}
EXPORT_SYMBOL_GPL(virtio_pci_admin_dev_parts_get);
/*
* virtio_pci_admin_dev_parts_set - Sets the device parts identified by the below attributes.
* @pdev: VF pci_dev
* @data_sg: The device parts data, its layout follows struct virtio_admin_cmd_dev_parts_set_data
*
* Note: caller must serialize access for the given device.
* Returns 0 on success, or negative on failure.
*/
int virtio_pci_admin_dev_parts_set(struct pci_dev *pdev, struct scatterlist *data_sg)
{
struct virtio_device *virtio_dev = virtio_pci_vf_get_pf_dev(pdev);
struct virtio_admin_cmd cmd = {};
int vf_id;
if (!virtio_dev)
return -ENODEV;
vf_id = pci_iov_vf_id(pdev);
if (vf_id < 0)
return vf_id;
cmd.opcode = cpu_to_le16(VIRTIO_ADMIN_CMD_DEV_PARTS_SET);
cmd.group_type = cpu_to_le16(VIRTIO_ADMIN_GROUP_TYPE_SRIOV);
cmd.group_member_id = cpu_to_le64(vf_id + 1);
cmd.data_sg = data_sg;
return vp_modern_admin_cmd_exec(virtio_dev, &cmd);
}
EXPORT_SYMBOL_GPL(virtio_pci_admin_dev_parts_set);
static const struct virtio_config_ops virtio_pci_config_nodev_ops = {
.get = NULL,
.set = NULL,

View File

@ -20,4 +20,15 @@ int virtio_pci_admin_legacy_io_notify_info(struct pci_dev *pdev,
u64 *bar_offset);
#endif
bool virtio_pci_admin_has_dev_parts(struct pci_dev *pdev);
int virtio_pci_admin_mode_set(struct pci_dev *pdev, u8 mode);
int virtio_pci_admin_obj_create(struct pci_dev *pdev, u16 obj_type, u8 operation_type,
u32 *obj_id);
int virtio_pci_admin_obj_destroy(struct pci_dev *pdev, u16 obj_type, u32 id);
int virtio_pci_admin_dev_parts_metadata_get(struct pci_dev *pdev, u16 obj_type,
u32 id, u8 metadata_type, u32 *out);
int virtio_pci_admin_dev_parts_get(struct pci_dev *pdev, u16 obj_type, u32 id,
u8 get_type, struct scatterlist *res_sg, u32 *res_size);
int virtio_pci_admin_dev_parts_set(struct pci_dev *pdev, struct scatterlist *data_sg);
#endif /* _LINUX_VIRTIO_PCI_ADMIN_H */