xref: /kernel/linux/linux-6.6/drivers/vdpa/vdpa.c (revision 62306a36)
162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * vDPA bus.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2020, Red Hat. All rights reserved.
662306a36Sopenharmony_ci *     Author: Jason Wang <jasowang@redhat.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci#include <linux/idr.h>
1262306a36Sopenharmony_ci#include <linux/slab.h>
1362306a36Sopenharmony_ci#include <linux/vdpa.h>
1462306a36Sopenharmony_ci#include <uapi/linux/vdpa.h>
1562306a36Sopenharmony_ci#include <net/genetlink.h>
1662306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
1762306a36Sopenharmony_ci#include <linux/virtio_ids.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistatic LIST_HEAD(mdev_head);
2062306a36Sopenharmony_ci/* A global mutex that protects vdpa management device and device level operations. */
2162306a36Sopenharmony_cistatic DECLARE_RWSEM(vdpa_dev_lock);
2262306a36Sopenharmony_cistatic DEFINE_IDA(vdpa_index_ida);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_civoid vdpa_set_status(struct vdpa_device *vdev, u8 status)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	down_write(&vdev->cf_lock);
2762306a36Sopenharmony_ci	vdev->config->set_status(vdev, status);
2862306a36Sopenharmony_ci	up_write(&vdev->cf_lock);
2962306a36Sopenharmony_ci}
3062306a36Sopenharmony_ciEXPORT_SYMBOL(vdpa_set_status);
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistatic struct genl_family vdpa_nl_family;
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_cistatic int vdpa_dev_probe(struct device *d)
3562306a36Sopenharmony_ci{
3662306a36Sopenharmony_ci	struct vdpa_device *vdev = dev_to_vdpa(d);
3762306a36Sopenharmony_ci	struct vdpa_driver *drv = drv_to_vdpa(vdev->dev.driver);
3862306a36Sopenharmony_ci	const struct vdpa_config_ops *ops = vdev->config;
3962306a36Sopenharmony_ci	u32 max_num, min_num = 1;
4062306a36Sopenharmony_ci	int ret = 0;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	d->dma_mask = &d->coherent_dma_mask;
4362306a36Sopenharmony_ci	ret = dma_set_mask_and_coherent(d, DMA_BIT_MASK(64));
4462306a36Sopenharmony_ci	if (ret)
4562306a36Sopenharmony_ci		return ret;
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci	max_num = ops->get_vq_num_max(vdev);
4862306a36Sopenharmony_ci	if (ops->get_vq_num_min)
4962306a36Sopenharmony_ci		min_num = ops->get_vq_num_min(vdev);
5062306a36Sopenharmony_ci	if (max_num < min_num)
5162306a36Sopenharmony_ci		return -EINVAL;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (drv && drv->probe)
5462306a36Sopenharmony_ci		ret = drv->probe(vdev);
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	return ret;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic void vdpa_dev_remove(struct device *d)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	struct vdpa_device *vdev = dev_to_vdpa(d);
6262306a36Sopenharmony_ci	struct vdpa_driver *drv = drv_to_vdpa(vdev->dev.driver);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (drv && drv->remove)
6562306a36Sopenharmony_ci		drv->remove(vdev);
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int vdpa_dev_match(struct device *dev, struct device_driver *drv)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	struct vdpa_device *vdev = dev_to_vdpa(dev);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	/* Check override first, and if set, only use the named driver */
7362306a36Sopenharmony_ci	if (vdev->driver_override)
7462306a36Sopenharmony_ci		return strcmp(vdev->driver_override, drv->name) == 0;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	/* Currently devices must be supported by all vDPA bus drivers */
7762306a36Sopenharmony_ci	return 1;
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_cistatic ssize_t driver_override_store(struct device *dev,
8162306a36Sopenharmony_ci				     struct device_attribute *attr,
8262306a36Sopenharmony_ci				     const char *buf, size_t count)
8362306a36Sopenharmony_ci{
8462306a36Sopenharmony_ci	struct vdpa_device *vdev = dev_to_vdpa(dev);
8562306a36Sopenharmony_ci	int ret;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	ret = driver_set_override(dev, &vdev->driver_override, buf, count);
8862306a36Sopenharmony_ci	if (ret)
8962306a36Sopenharmony_ci		return ret;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return count;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic ssize_t driver_override_show(struct device *dev,
9562306a36Sopenharmony_ci				    struct device_attribute *attr, char *buf)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct vdpa_device *vdev = dev_to_vdpa(dev);
9862306a36Sopenharmony_ci	ssize_t len;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	device_lock(dev);
10162306a36Sopenharmony_ci	len = snprintf(buf, PAGE_SIZE, "%s\n", vdev->driver_override);
10262306a36Sopenharmony_ci	device_unlock(dev);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	return len;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_cistatic DEVICE_ATTR_RW(driver_override);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic struct attribute *vdpa_dev_attrs[] = {
10962306a36Sopenharmony_ci	&dev_attr_driver_override.attr,
11062306a36Sopenharmony_ci	NULL,
11162306a36Sopenharmony_ci};
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic const struct attribute_group vdpa_dev_group = {
11462306a36Sopenharmony_ci	.attrs  = vdpa_dev_attrs,
11562306a36Sopenharmony_ci};
11662306a36Sopenharmony_ci__ATTRIBUTE_GROUPS(vdpa_dev);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic struct bus_type vdpa_bus = {
11962306a36Sopenharmony_ci	.name  = "vdpa",
12062306a36Sopenharmony_ci	.dev_groups = vdpa_dev_groups,
12162306a36Sopenharmony_ci	.match = vdpa_dev_match,
12262306a36Sopenharmony_ci	.probe = vdpa_dev_probe,
12362306a36Sopenharmony_ci	.remove = vdpa_dev_remove,
12462306a36Sopenharmony_ci};
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic void vdpa_release_dev(struct device *d)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	struct vdpa_device *vdev = dev_to_vdpa(d);
12962306a36Sopenharmony_ci	const struct vdpa_config_ops *ops = vdev->config;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (ops->free)
13262306a36Sopenharmony_ci		ops->free(vdev);
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	ida_simple_remove(&vdpa_index_ida, vdev->index);
13562306a36Sopenharmony_ci	kfree(vdev->driver_override);
13662306a36Sopenharmony_ci	kfree(vdev);
13762306a36Sopenharmony_ci}
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci/**
14062306a36Sopenharmony_ci * __vdpa_alloc_device - allocate and initilaize a vDPA device
14162306a36Sopenharmony_ci * This allows driver to some prepartion after device is
14262306a36Sopenharmony_ci * initialized but before registered.
14362306a36Sopenharmony_ci * @parent: the parent device
14462306a36Sopenharmony_ci * @config: the bus operations that is supported by this device
14562306a36Sopenharmony_ci * @ngroups: number of groups supported by this device
14662306a36Sopenharmony_ci * @nas: number of address spaces supported by this device
14762306a36Sopenharmony_ci * @size: size of the parent structure that contains private data
14862306a36Sopenharmony_ci * @name: name of the vdpa device; optional.
14962306a36Sopenharmony_ci * @use_va: indicate whether virtual address must be used by this device
15062306a36Sopenharmony_ci *
15162306a36Sopenharmony_ci * Driver should use vdpa_alloc_device() wrapper macro instead of
15262306a36Sopenharmony_ci * using this directly.
15362306a36Sopenharmony_ci *
15462306a36Sopenharmony_ci * Return: Returns an error when parent/config/dma_dev is not set or fail to get
15562306a36Sopenharmony_ci *	   ida.
15662306a36Sopenharmony_ci */
15762306a36Sopenharmony_cistruct vdpa_device *__vdpa_alloc_device(struct device *parent,
15862306a36Sopenharmony_ci					const struct vdpa_config_ops *config,
15962306a36Sopenharmony_ci					unsigned int ngroups, unsigned int nas,
16062306a36Sopenharmony_ci					size_t size, const char *name,
16162306a36Sopenharmony_ci					bool use_va)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	struct vdpa_device *vdev;
16462306a36Sopenharmony_ci	int err = -EINVAL;
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	if (!config)
16762306a36Sopenharmony_ci		goto err;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (!!config->dma_map != !!config->dma_unmap)
17062306a36Sopenharmony_ci		goto err;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	/* It should only work for the device that use on-chip IOMMU */
17362306a36Sopenharmony_ci	if (use_va && !(config->dma_map || config->set_map))
17462306a36Sopenharmony_ci		goto err;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	err = -ENOMEM;
17762306a36Sopenharmony_ci	vdev = kzalloc(size, GFP_KERNEL);
17862306a36Sopenharmony_ci	if (!vdev)
17962306a36Sopenharmony_ci		goto err;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	err = ida_alloc(&vdpa_index_ida, GFP_KERNEL);
18262306a36Sopenharmony_ci	if (err < 0)
18362306a36Sopenharmony_ci		goto err_ida;
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	vdev->dev.bus = &vdpa_bus;
18662306a36Sopenharmony_ci	vdev->dev.parent = parent;
18762306a36Sopenharmony_ci	vdev->dev.release = vdpa_release_dev;
18862306a36Sopenharmony_ci	vdev->index = err;
18962306a36Sopenharmony_ci	vdev->config = config;
19062306a36Sopenharmony_ci	vdev->features_valid = false;
19162306a36Sopenharmony_ci	vdev->use_va = use_va;
19262306a36Sopenharmony_ci	vdev->ngroups = ngroups;
19362306a36Sopenharmony_ci	vdev->nas = nas;
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	if (name)
19662306a36Sopenharmony_ci		err = dev_set_name(&vdev->dev, "%s", name);
19762306a36Sopenharmony_ci	else
19862306a36Sopenharmony_ci		err = dev_set_name(&vdev->dev, "vdpa%u", vdev->index);
19962306a36Sopenharmony_ci	if (err)
20062306a36Sopenharmony_ci		goto err_name;
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_ci	init_rwsem(&vdev->cf_lock);
20362306a36Sopenharmony_ci	device_initialize(&vdev->dev);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	return vdev;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cierr_name:
20862306a36Sopenharmony_ci	ida_simple_remove(&vdpa_index_ida, vdev->index);
20962306a36Sopenharmony_cierr_ida:
21062306a36Sopenharmony_ci	kfree(vdev);
21162306a36Sopenharmony_cierr:
21262306a36Sopenharmony_ci	return ERR_PTR(err);
21362306a36Sopenharmony_ci}
21462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__vdpa_alloc_device);
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_cistatic int vdpa_name_match(struct device *dev, const void *data)
21762306a36Sopenharmony_ci{
21862306a36Sopenharmony_ci	struct vdpa_device *vdev = container_of(dev, struct vdpa_device, dev);
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci	return (strcmp(dev_name(&vdev->dev), data) == 0);
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic int __vdpa_register_device(struct vdpa_device *vdev, u32 nvqs)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct device *dev;
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_ci	vdev->nvqs = nvqs;
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_ci	lockdep_assert_held(&vdpa_dev_lock);
23062306a36Sopenharmony_ci	dev = bus_find_device(&vdpa_bus, NULL, dev_name(&vdev->dev), vdpa_name_match);
23162306a36Sopenharmony_ci	if (dev) {
23262306a36Sopenharmony_ci		put_device(dev);
23362306a36Sopenharmony_ci		return -EEXIST;
23462306a36Sopenharmony_ci	}
23562306a36Sopenharmony_ci	return device_add(&vdev->dev);
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci/**
23962306a36Sopenharmony_ci * _vdpa_register_device - register a vDPA device with vdpa lock held
24062306a36Sopenharmony_ci * Caller must have a succeed call of vdpa_alloc_device() before.
24162306a36Sopenharmony_ci * Caller must invoke this routine in the management device dev_add()
24262306a36Sopenharmony_ci * callback after setting up valid mgmtdev for this vdpa device.
24362306a36Sopenharmony_ci * @vdev: the vdpa device to be registered to vDPA bus
24462306a36Sopenharmony_ci * @nvqs: number of virtqueues supported by this device
24562306a36Sopenharmony_ci *
24662306a36Sopenharmony_ci * Return: Returns an error when fail to add device to vDPA bus
24762306a36Sopenharmony_ci */
24862306a36Sopenharmony_ciint _vdpa_register_device(struct vdpa_device *vdev, u32 nvqs)
24962306a36Sopenharmony_ci{
25062306a36Sopenharmony_ci	if (!vdev->mdev)
25162306a36Sopenharmony_ci		return -EINVAL;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci	return __vdpa_register_device(vdev, nvqs);
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(_vdpa_register_device);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci/**
25862306a36Sopenharmony_ci * vdpa_register_device - register a vDPA device
25962306a36Sopenharmony_ci * Callers must have a succeed call of vdpa_alloc_device() before.
26062306a36Sopenharmony_ci * @vdev: the vdpa device to be registered to vDPA bus
26162306a36Sopenharmony_ci * @nvqs: number of virtqueues supported by this device
26262306a36Sopenharmony_ci *
26362306a36Sopenharmony_ci * Return: Returns an error when fail to add to vDPA bus
26462306a36Sopenharmony_ci */
26562306a36Sopenharmony_ciint vdpa_register_device(struct vdpa_device *vdev, u32 nvqs)
26662306a36Sopenharmony_ci{
26762306a36Sopenharmony_ci	int err;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	down_write(&vdpa_dev_lock);
27062306a36Sopenharmony_ci	err = __vdpa_register_device(vdev, nvqs);
27162306a36Sopenharmony_ci	up_write(&vdpa_dev_lock);
27262306a36Sopenharmony_ci	return err;
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vdpa_register_device);
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_ci/**
27762306a36Sopenharmony_ci * _vdpa_unregister_device - unregister a vDPA device
27862306a36Sopenharmony_ci * Caller must invoke this routine as part of management device dev_del()
27962306a36Sopenharmony_ci * callback.
28062306a36Sopenharmony_ci * @vdev: the vdpa device to be unregisted from vDPA bus
28162306a36Sopenharmony_ci */
28262306a36Sopenharmony_civoid _vdpa_unregister_device(struct vdpa_device *vdev)
28362306a36Sopenharmony_ci{
28462306a36Sopenharmony_ci	lockdep_assert_held(&vdpa_dev_lock);
28562306a36Sopenharmony_ci	WARN_ON(!vdev->mdev);
28662306a36Sopenharmony_ci	device_unregister(&vdev->dev);
28762306a36Sopenharmony_ci}
28862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(_vdpa_unregister_device);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci/**
29162306a36Sopenharmony_ci * vdpa_unregister_device - unregister a vDPA device
29262306a36Sopenharmony_ci * @vdev: the vdpa device to be unregisted from vDPA bus
29362306a36Sopenharmony_ci */
29462306a36Sopenharmony_civoid vdpa_unregister_device(struct vdpa_device *vdev)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	down_write(&vdpa_dev_lock);
29762306a36Sopenharmony_ci	device_unregister(&vdev->dev);
29862306a36Sopenharmony_ci	up_write(&vdpa_dev_lock);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vdpa_unregister_device);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci/**
30362306a36Sopenharmony_ci * __vdpa_register_driver - register a vDPA device driver
30462306a36Sopenharmony_ci * @drv: the vdpa device driver to be registered
30562306a36Sopenharmony_ci * @owner: module owner of the driver
30662306a36Sopenharmony_ci *
30762306a36Sopenharmony_ci * Return: Returns an err when fail to do the registration
30862306a36Sopenharmony_ci */
30962306a36Sopenharmony_ciint __vdpa_register_driver(struct vdpa_driver *drv, struct module *owner)
31062306a36Sopenharmony_ci{
31162306a36Sopenharmony_ci	drv->driver.bus = &vdpa_bus;
31262306a36Sopenharmony_ci	drv->driver.owner = owner;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	return driver_register(&drv->driver);
31562306a36Sopenharmony_ci}
31662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(__vdpa_register_driver);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci/**
31962306a36Sopenharmony_ci * vdpa_unregister_driver - unregister a vDPA device driver
32062306a36Sopenharmony_ci * @drv: the vdpa device driver to be unregistered
32162306a36Sopenharmony_ci */
32262306a36Sopenharmony_civoid vdpa_unregister_driver(struct vdpa_driver *drv)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	driver_unregister(&drv->driver);
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vdpa_unregister_driver);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci/**
32962306a36Sopenharmony_ci * vdpa_mgmtdev_register - register a vdpa management device
33062306a36Sopenharmony_ci *
33162306a36Sopenharmony_ci * @mdev: Pointer to vdpa management device
33262306a36Sopenharmony_ci * vdpa_mgmtdev_register() register a vdpa management device which supports
33362306a36Sopenharmony_ci * vdpa device management.
33462306a36Sopenharmony_ci * Return: Returns 0 on success or failure when required callback ops are not
33562306a36Sopenharmony_ci *         initialized.
33662306a36Sopenharmony_ci */
33762306a36Sopenharmony_ciint vdpa_mgmtdev_register(struct vdpa_mgmt_dev *mdev)
33862306a36Sopenharmony_ci{
33962306a36Sopenharmony_ci	if (!mdev->device || !mdev->ops || !mdev->ops->dev_add || !mdev->ops->dev_del)
34062306a36Sopenharmony_ci		return -EINVAL;
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_ci	INIT_LIST_HEAD(&mdev->list);
34362306a36Sopenharmony_ci	down_write(&vdpa_dev_lock);
34462306a36Sopenharmony_ci	list_add_tail(&mdev->list, &mdev_head);
34562306a36Sopenharmony_ci	up_write(&vdpa_dev_lock);
34662306a36Sopenharmony_ci	return 0;
34762306a36Sopenharmony_ci}
34862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vdpa_mgmtdev_register);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic int vdpa_match_remove(struct device *dev, void *data)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	struct vdpa_device *vdev = container_of(dev, struct vdpa_device, dev);
35362306a36Sopenharmony_ci	struct vdpa_mgmt_dev *mdev = vdev->mdev;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	if (mdev == data)
35662306a36Sopenharmony_ci		mdev->ops->dev_del(mdev, vdev);
35762306a36Sopenharmony_ci	return 0;
35862306a36Sopenharmony_ci}
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_civoid vdpa_mgmtdev_unregister(struct vdpa_mgmt_dev *mdev)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	down_write(&vdpa_dev_lock);
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	list_del(&mdev->list);
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	/* Filter out all the entries belong to this management device and delete it. */
36762306a36Sopenharmony_ci	bus_for_each_dev(&vdpa_bus, NULL, mdev, vdpa_match_remove);
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	up_write(&vdpa_dev_lock);
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vdpa_mgmtdev_unregister);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_cistatic void vdpa_get_config_unlocked(struct vdpa_device *vdev,
37462306a36Sopenharmony_ci				     unsigned int offset,
37562306a36Sopenharmony_ci				     void *buf, unsigned int len)
37662306a36Sopenharmony_ci{
37762306a36Sopenharmony_ci	const struct vdpa_config_ops *ops = vdev->config;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	/*
38062306a36Sopenharmony_ci	 * Config accesses aren't supposed to trigger before features are set.
38162306a36Sopenharmony_ci	 * If it does happen we assume a legacy guest.
38262306a36Sopenharmony_ci	 */
38362306a36Sopenharmony_ci	if (!vdev->features_valid)
38462306a36Sopenharmony_ci		vdpa_set_features_unlocked(vdev, 0);
38562306a36Sopenharmony_ci	ops->get_config(vdev, offset, buf, len);
38662306a36Sopenharmony_ci}
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci/**
38962306a36Sopenharmony_ci * vdpa_get_config - Get one or more device configuration fields.
39062306a36Sopenharmony_ci * @vdev: vdpa device to operate on
39162306a36Sopenharmony_ci * @offset: starting byte offset of the field
39262306a36Sopenharmony_ci * @buf: buffer pointer to read to
39362306a36Sopenharmony_ci * @len: length of the configuration fields in bytes
39462306a36Sopenharmony_ci */
39562306a36Sopenharmony_civoid vdpa_get_config(struct vdpa_device *vdev, unsigned int offset,
39662306a36Sopenharmony_ci		     void *buf, unsigned int len)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	down_read(&vdev->cf_lock);
39962306a36Sopenharmony_ci	vdpa_get_config_unlocked(vdev, offset, buf, len);
40062306a36Sopenharmony_ci	up_read(&vdev->cf_lock);
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vdpa_get_config);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci/**
40562306a36Sopenharmony_ci * vdpa_set_config - Set one or more device configuration fields.
40662306a36Sopenharmony_ci * @vdev: vdpa device to operate on
40762306a36Sopenharmony_ci * @offset: starting byte offset of the field
40862306a36Sopenharmony_ci * @buf: buffer pointer to read from
40962306a36Sopenharmony_ci * @length: length of the configuration fields in bytes
41062306a36Sopenharmony_ci */
41162306a36Sopenharmony_civoid vdpa_set_config(struct vdpa_device *vdev, unsigned int offset,
41262306a36Sopenharmony_ci		     const void *buf, unsigned int length)
41362306a36Sopenharmony_ci{
41462306a36Sopenharmony_ci	down_write(&vdev->cf_lock);
41562306a36Sopenharmony_ci	vdev->config->set_config(vdev, offset, buf, length);
41662306a36Sopenharmony_ci	up_write(&vdev->cf_lock);
41762306a36Sopenharmony_ci}
41862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(vdpa_set_config);
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic bool mgmtdev_handle_match(const struct vdpa_mgmt_dev *mdev,
42162306a36Sopenharmony_ci				 const char *busname, const char *devname)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	/* Bus name is optional for simulated management device, so ignore the
42462306a36Sopenharmony_ci	 * device with bus if bus attribute is provided.
42562306a36Sopenharmony_ci	 */
42662306a36Sopenharmony_ci	if ((busname && !mdev->device->bus) || (!busname && mdev->device->bus))
42762306a36Sopenharmony_ci		return false;
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	if (!busname && strcmp(dev_name(mdev->device), devname) == 0)
43062306a36Sopenharmony_ci		return true;
43162306a36Sopenharmony_ci
43262306a36Sopenharmony_ci	if (busname && (strcmp(mdev->device->bus->name, busname) == 0) &&
43362306a36Sopenharmony_ci	    (strcmp(dev_name(mdev->device), devname) == 0))
43462306a36Sopenharmony_ci		return true;
43562306a36Sopenharmony_ci
43662306a36Sopenharmony_ci	return false;
43762306a36Sopenharmony_ci}
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_cistatic struct vdpa_mgmt_dev *vdpa_mgmtdev_get_from_attr(struct nlattr **attrs)
44062306a36Sopenharmony_ci{
44162306a36Sopenharmony_ci	struct vdpa_mgmt_dev *mdev;
44262306a36Sopenharmony_ci	const char *busname = NULL;
44362306a36Sopenharmony_ci	const char *devname;
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci	if (!attrs[VDPA_ATTR_MGMTDEV_DEV_NAME])
44662306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
44762306a36Sopenharmony_ci	devname = nla_data(attrs[VDPA_ATTR_MGMTDEV_DEV_NAME]);
44862306a36Sopenharmony_ci	if (attrs[VDPA_ATTR_MGMTDEV_BUS_NAME])
44962306a36Sopenharmony_ci		busname = nla_data(attrs[VDPA_ATTR_MGMTDEV_BUS_NAME]);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	list_for_each_entry(mdev, &mdev_head, list) {
45262306a36Sopenharmony_ci		if (mgmtdev_handle_match(mdev, busname, devname))
45362306a36Sopenharmony_ci			return mdev;
45462306a36Sopenharmony_ci	}
45562306a36Sopenharmony_ci	return ERR_PTR(-ENODEV);
45662306a36Sopenharmony_ci}
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_cistatic int vdpa_nl_mgmtdev_handle_fill(struct sk_buff *msg, const struct vdpa_mgmt_dev *mdev)
45962306a36Sopenharmony_ci{
46062306a36Sopenharmony_ci	if (mdev->device->bus &&
46162306a36Sopenharmony_ci	    nla_put_string(msg, VDPA_ATTR_MGMTDEV_BUS_NAME, mdev->device->bus->name))
46262306a36Sopenharmony_ci		return -EMSGSIZE;
46362306a36Sopenharmony_ci	if (nla_put_string(msg, VDPA_ATTR_MGMTDEV_DEV_NAME, dev_name(mdev->device)))
46462306a36Sopenharmony_ci		return -EMSGSIZE;
46562306a36Sopenharmony_ci	return 0;
46662306a36Sopenharmony_ci}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_cistatic u64 vdpa_mgmtdev_get_classes(const struct vdpa_mgmt_dev *mdev,
46962306a36Sopenharmony_ci				    unsigned int *nclasses)
47062306a36Sopenharmony_ci{
47162306a36Sopenharmony_ci	u64 supported_classes = 0;
47262306a36Sopenharmony_ci	unsigned int n = 0;
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	for (int i = 0; mdev->id_table[i].device; i++) {
47562306a36Sopenharmony_ci		if (mdev->id_table[i].device > 63)
47662306a36Sopenharmony_ci			continue;
47762306a36Sopenharmony_ci		supported_classes |= BIT_ULL(mdev->id_table[i].device);
47862306a36Sopenharmony_ci		n++;
47962306a36Sopenharmony_ci	}
48062306a36Sopenharmony_ci	if (nclasses)
48162306a36Sopenharmony_ci		*nclasses = n;
48262306a36Sopenharmony_ci
48362306a36Sopenharmony_ci	return supported_classes;
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_cistatic int vdpa_mgmtdev_fill(const struct vdpa_mgmt_dev *mdev, struct sk_buff *msg,
48762306a36Sopenharmony_ci			     u32 portid, u32 seq, int flags)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	void *hdr;
49062306a36Sopenharmony_ci	int err;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	hdr = genlmsg_put(msg, portid, seq, &vdpa_nl_family, flags, VDPA_CMD_MGMTDEV_NEW);
49362306a36Sopenharmony_ci	if (!hdr)
49462306a36Sopenharmony_ci		return -EMSGSIZE;
49562306a36Sopenharmony_ci	err = vdpa_nl_mgmtdev_handle_fill(msg, mdev);
49662306a36Sopenharmony_ci	if (err)
49762306a36Sopenharmony_ci		goto msg_err;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (nla_put_u64_64bit(msg, VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES,
50062306a36Sopenharmony_ci			      vdpa_mgmtdev_get_classes(mdev, NULL),
50162306a36Sopenharmony_ci			      VDPA_ATTR_UNSPEC)) {
50262306a36Sopenharmony_ci		err = -EMSGSIZE;
50362306a36Sopenharmony_ci		goto msg_err;
50462306a36Sopenharmony_ci	}
50562306a36Sopenharmony_ci	if (nla_put_u32(msg, VDPA_ATTR_DEV_MGMTDEV_MAX_VQS,
50662306a36Sopenharmony_ci			mdev->max_supported_vqs)) {
50762306a36Sopenharmony_ci		err = -EMSGSIZE;
50862306a36Sopenharmony_ci		goto msg_err;
50962306a36Sopenharmony_ci	}
51062306a36Sopenharmony_ci	if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_SUPPORTED_FEATURES,
51162306a36Sopenharmony_ci			      mdev->supported_features, VDPA_ATTR_PAD)) {
51262306a36Sopenharmony_ci		err = -EMSGSIZE;
51362306a36Sopenharmony_ci		goto msg_err;
51462306a36Sopenharmony_ci	}
51562306a36Sopenharmony_ci
51662306a36Sopenharmony_ci	genlmsg_end(msg, hdr);
51762306a36Sopenharmony_ci	return 0;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_cimsg_err:
52062306a36Sopenharmony_ci	genlmsg_cancel(msg, hdr);
52162306a36Sopenharmony_ci	return err;
52262306a36Sopenharmony_ci}
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cistatic int vdpa_nl_cmd_mgmtdev_get_doit(struct sk_buff *skb, struct genl_info *info)
52562306a36Sopenharmony_ci{
52662306a36Sopenharmony_ci	struct vdpa_mgmt_dev *mdev;
52762306a36Sopenharmony_ci	struct sk_buff *msg;
52862306a36Sopenharmony_ci	int err;
52962306a36Sopenharmony_ci
53062306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
53162306a36Sopenharmony_ci	if (!msg)
53262306a36Sopenharmony_ci		return -ENOMEM;
53362306a36Sopenharmony_ci
53462306a36Sopenharmony_ci	down_read(&vdpa_dev_lock);
53562306a36Sopenharmony_ci	mdev = vdpa_mgmtdev_get_from_attr(info->attrs);
53662306a36Sopenharmony_ci	if (IS_ERR(mdev)) {
53762306a36Sopenharmony_ci		up_read(&vdpa_dev_lock);
53862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(info->extack, "Fail to find the specified mgmt device");
53962306a36Sopenharmony_ci		err = PTR_ERR(mdev);
54062306a36Sopenharmony_ci		goto out;
54162306a36Sopenharmony_ci	}
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	err = vdpa_mgmtdev_fill(mdev, msg, info->snd_portid, info->snd_seq, 0);
54462306a36Sopenharmony_ci	up_read(&vdpa_dev_lock);
54562306a36Sopenharmony_ci	if (err)
54662306a36Sopenharmony_ci		goto out;
54762306a36Sopenharmony_ci	err = genlmsg_reply(msg, info);
54862306a36Sopenharmony_ci	return err;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ciout:
55162306a36Sopenharmony_ci	nlmsg_free(msg);
55262306a36Sopenharmony_ci	return err;
55362306a36Sopenharmony_ci}
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_cistatic int
55662306a36Sopenharmony_civdpa_nl_cmd_mgmtdev_get_dumpit(struct sk_buff *msg, struct netlink_callback *cb)
55762306a36Sopenharmony_ci{
55862306a36Sopenharmony_ci	struct vdpa_mgmt_dev *mdev;
55962306a36Sopenharmony_ci	int start = cb->args[0];
56062306a36Sopenharmony_ci	int idx = 0;
56162306a36Sopenharmony_ci	int err;
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_ci	down_read(&vdpa_dev_lock);
56462306a36Sopenharmony_ci	list_for_each_entry(mdev, &mdev_head, list) {
56562306a36Sopenharmony_ci		if (idx < start) {
56662306a36Sopenharmony_ci			idx++;
56762306a36Sopenharmony_ci			continue;
56862306a36Sopenharmony_ci		}
56962306a36Sopenharmony_ci		err = vdpa_mgmtdev_fill(mdev, msg, NETLINK_CB(cb->skb).portid,
57062306a36Sopenharmony_ci					cb->nlh->nlmsg_seq, NLM_F_MULTI);
57162306a36Sopenharmony_ci		if (err)
57262306a36Sopenharmony_ci			goto out;
57362306a36Sopenharmony_ci		idx++;
57462306a36Sopenharmony_ci	}
57562306a36Sopenharmony_ciout:
57662306a36Sopenharmony_ci	up_read(&vdpa_dev_lock);
57762306a36Sopenharmony_ci	cb->args[0] = idx;
57862306a36Sopenharmony_ci	return msg->len;
57962306a36Sopenharmony_ci}
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ci#define VDPA_DEV_NET_ATTRS_MASK (BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MACADDR) | \
58262306a36Sopenharmony_ci				 BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MTU)     | \
58362306a36Sopenharmony_ci				 BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MAX_VQP))
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci/*
58662306a36Sopenharmony_ci * Bitmask for all per-device features: feature bits VIRTIO_TRANSPORT_F_START
58762306a36Sopenharmony_ci * through VIRTIO_TRANSPORT_F_END are unset, i.e. 0xfffffc000fffffff for
58862306a36Sopenharmony_ci * all 64bit features. If the features are extended beyond 64 bits, or new
58962306a36Sopenharmony_ci * "holes" are reserved for other type of features than per-device, this
59062306a36Sopenharmony_ci * macro would have to be updated.
59162306a36Sopenharmony_ci */
59262306a36Sopenharmony_ci#define VIRTIO_DEVICE_F_MASK (~0ULL << (VIRTIO_TRANSPORT_F_END + 1) | \
59362306a36Sopenharmony_ci			      ((1ULL << VIRTIO_TRANSPORT_F_START) - 1))
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_cistatic int vdpa_nl_cmd_dev_add_set_doit(struct sk_buff *skb, struct genl_info *info)
59662306a36Sopenharmony_ci{
59762306a36Sopenharmony_ci	struct vdpa_dev_set_config config = {};
59862306a36Sopenharmony_ci	struct nlattr **nl_attrs = info->attrs;
59962306a36Sopenharmony_ci	struct vdpa_mgmt_dev *mdev;
60062306a36Sopenharmony_ci	unsigned int ncls = 0;
60162306a36Sopenharmony_ci	const u8 *macaddr;
60262306a36Sopenharmony_ci	const char *name;
60362306a36Sopenharmony_ci	u64 classes;
60462306a36Sopenharmony_ci	int err = 0;
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_ci	if (!info->attrs[VDPA_ATTR_DEV_NAME])
60762306a36Sopenharmony_ci		return -EINVAL;
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	name = nla_data(info->attrs[VDPA_ATTR_DEV_NAME]);
61062306a36Sopenharmony_ci
61162306a36Sopenharmony_ci	if (nl_attrs[VDPA_ATTR_DEV_NET_CFG_MACADDR]) {
61262306a36Sopenharmony_ci		macaddr = nla_data(nl_attrs[VDPA_ATTR_DEV_NET_CFG_MACADDR]);
61362306a36Sopenharmony_ci		memcpy(config.net.mac, macaddr, sizeof(config.net.mac));
61462306a36Sopenharmony_ci		config.mask |= BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MACADDR);
61562306a36Sopenharmony_ci	}
61662306a36Sopenharmony_ci	if (nl_attrs[VDPA_ATTR_DEV_NET_CFG_MTU]) {
61762306a36Sopenharmony_ci		config.net.mtu =
61862306a36Sopenharmony_ci			nla_get_u16(nl_attrs[VDPA_ATTR_DEV_NET_CFG_MTU]);
61962306a36Sopenharmony_ci		config.mask |= BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MTU);
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci	if (nl_attrs[VDPA_ATTR_DEV_NET_CFG_MAX_VQP]) {
62262306a36Sopenharmony_ci		config.net.max_vq_pairs =
62362306a36Sopenharmony_ci			nla_get_u16(nl_attrs[VDPA_ATTR_DEV_NET_CFG_MAX_VQP]);
62462306a36Sopenharmony_ci		if (!config.net.max_vq_pairs) {
62562306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(info->extack,
62662306a36Sopenharmony_ci					   "At least one pair of VQs is required");
62762306a36Sopenharmony_ci			return -EINVAL;
62862306a36Sopenharmony_ci		}
62962306a36Sopenharmony_ci		config.mask |= BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MAX_VQP);
63062306a36Sopenharmony_ci	}
63162306a36Sopenharmony_ci	if (nl_attrs[VDPA_ATTR_DEV_FEATURES]) {
63262306a36Sopenharmony_ci		u64 missing = 0x0ULL;
63362306a36Sopenharmony_ci
63462306a36Sopenharmony_ci		config.device_features =
63562306a36Sopenharmony_ci			nla_get_u64(nl_attrs[VDPA_ATTR_DEV_FEATURES]);
63662306a36Sopenharmony_ci		if (nl_attrs[VDPA_ATTR_DEV_NET_CFG_MACADDR] &&
63762306a36Sopenharmony_ci		    !(config.device_features & BIT_ULL(VIRTIO_NET_F_MAC)))
63862306a36Sopenharmony_ci			missing |= BIT_ULL(VIRTIO_NET_F_MAC);
63962306a36Sopenharmony_ci		if (nl_attrs[VDPA_ATTR_DEV_NET_CFG_MTU] &&
64062306a36Sopenharmony_ci		    !(config.device_features & BIT_ULL(VIRTIO_NET_F_MTU)))
64162306a36Sopenharmony_ci			missing |= BIT_ULL(VIRTIO_NET_F_MTU);
64262306a36Sopenharmony_ci		if (nl_attrs[VDPA_ATTR_DEV_NET_CFG_MAX_VQP] &&
64362306a36Sopenharmony_ci		    config.net.max_vq_pairs > 1 &&
64462306a36Sopenharmony_ci		    !(config.device_features & BIT_ULL(VIRTIO_NET_F_MQ)))
64562306a36Sopenharmony_ci			missing |= BIT_ULL(VIRTIO_NET_F_MQ);
64662306a36Sopenharmony_ci		if (missing) {
64762306a36Sopenharmony_ci			NL_SET_ERR_MSG_FMT_MOD(info->extack,
64862306a36Sopenharmony_ci					       "Missing features 0x%llx for provided attributes",
64962306a36Sopenharmony_ci					       missing);
65062306a36Sopenharmony_ci			return -EINVAL;
65162306a36Sopenharmony_ci		}
65262306a36Sopenharmony_ci		config.mask |= BIT_ULL(VDPA_ATTR_DEV_FEATURES);
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	/* Skip checking capability if user didn't prefer to configure any
65662306a36Sopenharmony_ci	 * device networking attributes. It is likely that user might have used
65762306a36Sopenharmony_ci	 * a device specific method to configure such attributes or using device
65862306a36Sopenharmony_ci	 * default attributes.
65962306a36Sopenharmony_ci	 */
66062306a36Sopenharmony_ci	if ((config.mask & VDPA_DEV_NET_ATTRS_MASK) &&
66162306a36Sopenharmony_ci	    !netlink_capable(skb, CAP_NET_ADMIN))
66262306a36Sopenharmony_ci		return -EPERM;
66362306a36Sopenharmony_ci
66462306a36Sopenharmony_ci	down_write(&vdpa_dev_lock);
66562306a36Sopenharmony_ci	mdev = vdpa_mgmtdev_get_from_attr(info->attrs);
66662306a36Sopenharmony_ci	if (IS_ERR(mdev)) {
66762306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(info->extack, "Fail to find the specified management device");
66862306a36Sopenharmony_ci		err = PTR_ERR(mdev);
66962306a36Sopenharmony_ci		goto err;
67062306a36Sopenharmony_ci	}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci	if ((config.mask & mdev->config_attr_mask) != config.mask) {
67362306a36Sopenharmony_ci		NL_SET_ERR_MSG_FMT_MOD(info->extack,
67462306a36Sopenharmony_ci				       "Some provided attributes are not supported: 0x%llx",
67562306a36Sopenharmony_ci				       config.mask & ~mdev->config_attr_mask);
67662306a36Sopenharmony_ci		err = -EOPNOTSUPP;
67762306a36Sopenharmony_ci		goto err;
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	classes = vdpa_mgmtdev_get_classes(mdev, &ncls);
68162306a36Sopenharmony_ci	if (config.mask & VDPA_DEV_NET_ATTRS_MASK &&
68262306a36Sopenharmony_ci	    !(classes & BIT_ULL(VIRTIO_ID_NET))) {
68362306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(info->extack,
68462306a36Sopenharmony_ci				   "Network class attributes provided on unsupported management device");
68562306a36Sopenharmony_ci		err = -EINVAL;
68662306a36Sopenharmony_ci		goto err;
68762306a36Sopenharmony_ci	}
68862306a36Sopenharmony_ci	if (!(config.mask & VDPA_DEV_NET_ATTRS_MASK) &&
68962306a36Sopenharmony_ci	    config.mask & BIT_ULL(VDPA_ATTR_DEV_FEATURES) &&
69062306a36Sopenharmony_ci	    classes & BIT_ULL(VIRTIO_ID_NET) && ncls > 1 &&
69162306a36Sopenharmony_ci	    config.device_features & VIRTIO_DEVICE_F_MASK) {
69262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(info->extack,
69362306a36Sopenharmony_ci				   "Management device supports multi-class while device features specified are ambiguous");
69462306a36Sopenharmony_ci		err = -EINVAL;
69562306a36Sopenharmony_ci		goto err;
69662306a36Sopenharmony_ci	}
69762306a36Sopenharmony_ci
69862306a36Sopenharmony_ci	err = mdev->ops->dev_add(mdev, name, &config);
69962306a36Sopenharmony_cierr:
70062306a36Sopenharmony_ci	up_write(&vdpa_dev_lock);
70162306a36Sopenharmony_ci	return err;
70262306a36Sopenharmony_ci}
70362306a36Sopenharmony_ci
70462306a36Sopenharmony_cistatic int vdpa_nl_cmd_dev_del_set_doit(struct sk_buff *skb, struct genl_info *info)
70562306a36Sopenharmony_ci{
70662306a36Sopenharmony_ci	struct vdpa_mgmt_dev *mdev;
70762306a36Sopenharmony_ci	struct vdpa_device *vdev;
70862306a36Sopenharmony_ci	struct device *dev;
70962306a36Sopenharmony_ci	const char *name;
71062306a36Sopenharmony_ci	int err = 0;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	if (!info->attrs[VDPA_ATTR_DEV_NAME])
71362306a36Sopenharmony_ci		return -EINVAL;
71462306a36Sopenharmony_ci	name = nla_data(info->attrs[VDPA_ATTR_DEV_NAME]);
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	down_write(&vdpa_dev_lock);
71762306a36Sopenharmony_ci	dev = bus_find_device(&vdpa_bus, NULL, name, vdpa_name_match);
71862306a36Sopenharmony_ci	if (!dev) {
71962306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(info->extack, "device not found");
72062306a36Sopenharmony_ci		err = -ENODEV;
72162306a36Sopenharmony_ci		goto dev_err;
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci	vdev = container_of(dev, struct vdpa_device, dev);
72462306a36Sopenharmony_ci	if (!vdev->mdev) {
72562306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(info->extack, "Only user created device can be deleted by user");
72662306a36Sopenharmony_ci		err = -EINVAL;
72762306a36Sopenharmony_ci		goto mdev_err;
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci	mdev = vdev->mdev;
73062306a36Sopenharmony_ci	mdev->ops->dev_del(mdev, vdev);
73162306a36Sopenharmony_cimdev_err:
73262306a36Sopenharmony_ci	put_device(dev);
73362306a36Sopenharmony_cidev_err:
73462306a36Sopenharmony_ci	up_write(&vdpa_dev_lock);
73562306a36Sopenharmony_ci	return err;
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_cistatic int
73962306a36Sopenharmony_civdpa_dev_fill(struct vdpa_device *vdev, struct sk_buff *msg, u32 portid, u32 seq,
74062306a36Sopenharmony_ci	      int flags, struct netlink_ext_ack *extack)
74162306a36Sopenharmony_ci{
74262306a36Sopenharmony_ci	u16 max_vq_size;
74362306a36Sopenharmony_ci	u16 min_vq_size = 1;
74462306a36Sopenharmony_ci	u32 device_id;
74562306a36Sopenharmony_ci	u32 vendor_id;
74662306a36Sopenharmony_ci	void *hdr;
74762306a36Sopenharmony_ci	int err;
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_ci	hdr = genlmsg_put(msg, portid, seq, &vdpa_nl_family, flags, VDPA_CMD_DEV_NEW);
75062306a36Sopenharmony_ci	if (!hdr)
75162306a36Sopenharmony_ci		return -EMSGSIZE;
75262306a36Sopenharmony_ci
75362306a36Sopenharmony_ci	err = vdpa_nl_mgmtdev_handle_fill(msg, vdev->mdev);
75462306a36Sopenharmony_ci	if (err)
75562306a36Sopenharmony_ci		goto msg_err;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	device_id = vdev->config->get_device_id(vdev);
75862306a36Sopenharmony_ci	vendor_id = vdev->config->get_vendor_id(vdev);
75962306a36Sopenharmony_ci	max_vq_size = vdev->config->get_vq_num_max(vdev);
76062306a36Sopenharmony_ci	if (vdev->config->get_vq_num_min)
76162306a36Sopenharmony_ci		min_vq_size = vdev->config->get_vq_num_min(vdev);
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	err = -EMSGSIZE;
76462306a36Sopenharmony_ci	if (nla_put_string(msg, VDPA_ATTR_DEV_NAME, dev_name(&vdev->dev)))
76562306a36Sopenharmony_ci		goto msg_err;
76662306a36Sopenharmony_ci	if (nla_put_u32(msg, VDPA_ATTR_DEV_ID, device_id))
76762306a36Sopenharmony_ci		goto msg_err;
76862306a36Sopenharmony_ci	if (nla_put_u32(msg, VDPA_ATTR_DEV_VENDOR_ID, vendor_id))
76962306a36Sopenharmony_ci		goto msg_err;
77062306a36Sopenharmony_ci	if (nla_put_u32(msg, VDPA_ATTR_DEV_MAX_VQS, vdev->nvqs))
77162306a36Sopenharmony_ci		goto msg_err;
77262306a36Sopenharmony_ci	if (nla_put_u16(msg, VDPA_ATTR_DEV_MAX_VQ_SIZE, max_vq_size))
77362306a36Sopenharmony_ci		goto msg_err;
77462306a36Sopenharmony_ci	if (nla_put_u16(msg, VDPA_ATTR_DEV_MIN_VQ_SIZE, min_vq_size))
77562306a36Sopenharmony_ci		goto msg_err;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	genlmsg_end(msg, hdr);
77862306a36Sopenharmony_ci	return 0;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_cimsg_err:
78162306a36Sopenharmony_ci	genlmsg_cancel(msg, hdr);
78262306a36Sopenharmony_ci	return err;
78362306a36Sopenharmony_ci}
78462306a36Sopenharmony_ci
78562306a36Sopenharmony_cistatic int vdpa_nl_cmd_dev_get_doit(struct sk_buff *skb, struct genl_info *info)
78662306a36Sopenharmony_ci{
78762306a36Sopenharmony_ci	struct vdpa_device *vdev;
78862306a36Sopenharmony_ci	struct sk_buff *msg;
78962306a36Sopenharmony_ci	const char *devname;
79062306a36Sopenharmony_ci	struct device *dev;
79162306a36Sopenharmony_ci	int err;
79262306a36Sopenharmony_ci
79362306a36Sopenharmony_ci	if (!info->attrs[VDPA_ATTR_DEV_NAME])
79462306a36Sopenharmony_ci		return -EINVAL;
79562306a36Sopenharmony_ci	devname = nla_data(info->attrs[VDPA_ATTR_DEV_NAME]);
79662306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
79762306a36Sopenharmony_ci	if (!msg)
79862306a36Sopenharmony_ci		return -ENOMEM;
79962306a36Sopenharmony_ci
80062306a36Sopenharmony_ci	down_read(&vdpa_dev_lock);
80162306a36Sopenharmony_ci	dev = bus_find_device(&vdpa_bus, NULL, devname, vdpa_name_match);
80262306a36Sopenharmony_ci	if (!dev) {
80362306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(info->extack, "device not found");
80462306a36Sopenharmony_ci		err = -ENODEV;
80562306a36Sopenharmony_ci		goto err;
80662306a36Sopenharmony_ci	}
80762306a36Sopenharmony_ci	vdev = container_of(dev, struct vdpa_device, dev);
80862306a36Sopenharmony_ci	if (!vdev->mdev) {
80962306a36Sopenharmony_ci		err = -EINVAL;
81062306a36Sopenharmony_ci		goto mdev_err;
81162306a36Sopenharmony_ci	}
81262306a36Sopenharmony_ci	err = vdpa_dev_fill(vdev, msg, info->snd_portid, info->snd_seq, 0, info->extack);
81362306a36Sopenharmony_ci	if (err)
81462306a36Sopenharmony_ci		goto mdev_err;
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ci	err = genlmsg_reply(msg, info);
81762306a36Sopenharmony_ci	put_device(dev);
81862306a36Sopenharmony_ci	up_read(&vdpa_dev_lock);
81962306a36Sopenharmony_ci	return err;
82062306a36Sopenharmony_ci
82162306a36Sopenharmony_cimdev_err:
82262306a36Sopenharmony_ci	put_device(dev);
82362306a36Sopenharmony_cierr:
82462306a36Sopenharmony_ci	up_read(&vdpa_dev_lock);
82562306a36Sopenharmony_ci	nlmsg_free(msg);
82662306a36Sopenharmony_ci	return err;
82762306a36Sopenharmony_ci}
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_cistruct vdpa_dev_dump_info {
83062306a36Sopenharmony_ci	struct sk_buff *msg;
83162306a36Sopenharmony_ci	struct netlink_callback *cb;
83262306a36Sopenharmony_ci	int start_idx;
83362306a36Sopenharmony_ci	int idx;
83462306a36Sopenharmony_ci};
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_cistatic int vdpa_dev_dump(struct device *dev, void *data)
83762306a36Sopenharmony_ci{
83862306a36Sopenharmony_ci	struct vdpa_device *vdev = container_of(dev, struct vdpa_device, dev);
83962306a36Sopenharmony_ci	struct vdpa_dev_dump_info *info = data;
84062306a36Sopenharmony_ci	int err;
84162306a36Sopenharmony_ci
84262306a36Sopenharmony_ci	if (!vdev->mdev)
84362306a36Sopenharmony_ci		return 0;
84462306a36Sopenharmony_ci	if (info->idx < info->start_idx) {
84562306a36Sopenharmony_ci		info->idx++;
84662306a36Sopenharmony_ci		return 0;
84762306a36Sopenharmony_ci	}
84862306a36Sopenharmony_ci	err = vdpa_dev_fill(vdev, info->msg, NETLINK_CB(info->cb->skb).portid,
84962306a36Sopenharmony_ci			    info->cb->nlh->nlmsg_seq, NLM_F_MULTI, info->cb->extack);
85062306a36Sopenharmony_ci	if (err)
85162306a36Sopenharmony_ci		return err;
85262306a36Sopenharmony_ci
85362306a36Sopenharmony_ci	info->idx++;
85462306a36Sopenharmony_ci	return 0;
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cistatic int vdpa_nl_cmd_dev_get_dumpit(struct sk_buff *msg, struct netlink_callback *cb)
85862306a36Sopenharmony_ci{
85962306a36Sopenharmony_ci	struct vdpa_dev_dump_info info;
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_ci	info.msg = msg;
86262306a36Sopenharmony_ci	info.cb = cb;
86362306a36Sopenharmony_ci	info.start_idx = cb->args[0];
86462306a36Sopenharmony_ci	info.idx = 0;
86562306a36Sopenharmony_ci
86662306a36Sopenharmony_ci	down_read(&vdpa_dev_lock);
86762306a36Sopenharmony_ci	bus_for_each_dev(&vdpa_bus, NULL, &info, vdpa_dev_dump);
86862306a36Sopenharmony_ci	up_read(&vdpa_dev_lock);
86962306a36Sopenharmony_ci	cb->args[0] = info.idx;
87062306a36Sopenharmony_ci	return msg->len;
87162306a36Sopenharmony_ci}
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_cistatic int vdpa_dev_net_mq_config_fill(struct sk_buff *msg, u64 features,
87462306a36Sopenharmony_ci				       const struct virtio_net_config *config)
87562306a36Sopenharmony_ci{
87662306a36Sopenharmony_ci	u16 val_u16;
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	if ((features & BIT_ULL(VIRTIO_NET_F_MQ)) == 0 &&
87962306a36Sopenharmony_ci	    (features & BIT_ULL(VIRTIO_NET_F_RSS)) == 0)
88062306a36Sopenharmony_ci		return 0;
88162306a36Sopenharmony_ci
88262306a36Sopenharmony_ci	val_u16 = __virtio16_to_cpu(true, config->max_virtqueue_pairs);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	return nla_put_u16(msg, VDPA_ATTR_DEV_NET_CFG_MAX_VQP, val_u16);
88562306a36Sopenharmony_ci}
88662306a36Sopenharmony_ci
88762306a36Sopenharmony_cistatic int vdpa_dev_net_mtu_config_fill(struct sk_buff *msg, u64 features,
88862306a36Sopenharmony_ci					const struct virtio_net_config *config)
88962306a36Sopenharmony_ci{
89062306a36Sopenharmony_ci	u16 val_u16;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	if ((features & BIT_ULL(VIRTIO_NET_F_MTU)) == 0)
89362306a36Sopenharmony_ci		return 0;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	val_u16 = __virtio16_to_cpu(true, config->mtu);
89662306a36Sopenharmony_ci
89762306a36Sopenharmony_ci	return nla_put_u16(msg, VDPA_ATTR_DEV_NET_CFG_MTU, val_u16);
89862306a36Sopenharmony_ci}
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_cistatic int vdpa_dev_net_mac_config_fill(struct sk_buff *msg, u64 features,
90162306a36Sopenharmony_ci					const struct virtio_net_config *config)
90262306a36Sopenharmony_ci{
90362306a36Sopenharmony_ci	if ((features & BIT_ULL(VIRTIO_NET_F_MAC)) == 0)
90462306a36Sopenharmony_ci		return 0;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	return  nla_put(msg, VDPA_ATTR_DEV_NET_CFG_MACADDR,
90762306a36Sopenharmony_ci			sizeof(config->mac), config->mac);
90862306a36Sopenharmony_ci}
90962306a36Sopenharmony_ci
91062306a36Sopenharmony_cistatic int vdpa_dev_net_status_config_fill(struct sk_buff *msg, u64 features,
91162306a36Sopenharmony_ci					   const struct virtio_net_config *config)
91262306a36Sopenharmony_ci{
91362306a36Sopenharmony_ci	u16 val_u16;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	if ((features & BIT_ULL(VIRTIO_NET_F_STATUS)) == 0)
91662306a36Sopenharmony_ci		return 0;
91762306a36Sopenharmony_ci
91862306a36Sopenharmony_ci	val_u16 = __virtio16_to_cpu(true, config->status);
91962306a36Sopenharmony_ci	return nla_put_u16(msg, VDPA_ATTR_DEV_NET_STATUS, val_u16);
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_cistatic int vdpa_dev_net_config_fill(struct vdpa_device *vdev, struct sk_buff *msg)
92362306a36Sopenharmony_ci{
92462306a36Sopenharmony_ci	struct virtio_net_config config = {};
92562306a36Sopenharmony_ci	u64 features_device;
92662306a36Sopenharmony_ci
92762306a36Sopenharmony_ci	vdev->config->get_config(vdev, 0, &config, sizeof(config));
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	features_device = vdev->config->get_device_features(vdev);
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_FEATURES, features_device,
93262306a36Sopenharmony_ci			      VDPA_ATTR_PAD))
93362306a36Sopenharmony_ci		return -EMSGSIZE;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	if (vdpa_dev_net_mtu_config_fill(msg, features_device, &config))
93662306a36Sopenharmony_ci		return -EMSGSIZE;
93762306a36Sopenharmony_ci
93862306a36Sopenharmony_ci	if (vdpa_dev_net_mac_config_fill(msg, features_device, &config))
93962306a36Sopenharmony_ci		return -EMSGSIZE;
94062306a36Sopenharmony_ci
94162306a36Sopenharmony_ci	if (vdpa_dev_net_status_config_fill(msg, features_device, &config))
94262306a36Sopenharmony_ci		return -EMSGSIZE;
94362306a36Sopenharmony_ci
94462306a36Sopenharmony_ci	return vdpa_dev_net_mq_config_fill(msg, features_device, &config);
94562306a36Sopenharmony_ci}
94662306a36Sopenharmony_ci
94762306a36Sopenharmony_cistatic int
94862306a36Sopenharmony_civdpa_dev_config_fill(struct vdpa_device *vdev, struct sk_buff *msg, u32 portid, u32 seq,
94962306a36Sopenharmony_ci		     int flags, struct netlink_ext_ack *extack)
95062306a36Sopenharmony_ci{
95162306a36Sopenharmony_ci	u64 features_driver;
95262306a36Sopenharmony_ci	u8 status = 0;
95362306a36Sopenharmony_ci	u32 device_id;
95462306a36Sopenharmony_ci	void *hdr;
95562306a36Sopenharmony_ci	int err;
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	down_read(&vdev->cf_lock);
95862306a36Sopenharmony_ci	hdr = genlmsg_put(msg, portid, seq, &vdpa_nl_family, flags,
95962306a36Sopenharmony_ci			  VDPA_CMD_DEV_CONFIG_GET);
96062306a36Sopenharmony_ci	if (!hdr) {
96162306a36Sopenharmony_ci		err = -EMSGSIZE;
96262306a36Sopenharmony_ci		goto out;
96362306a36Sopenharmony_ci	}
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	if (nla_put_string(msg, VDPA_ATTR_DEV_NAME, dev_name(&vdev->dev))) {
96662306a36Sopenharmony_ci		err = -EMSGSIZE;
96762306a36Sopenharmony_ci		goto msg_err;
96862306a36Sopenharmony_ci	}
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci	device_id = vdev->config->get_device_id(vdev);
97162306a36Sopenharmony_ci	if (nla_put_u32(msg, VDPA_ATTR_DEV_ID, device_id)) {
97262306a36Sopenharmony_ci		err = -EMSGSIZE;
97362306a36Sopenharmony_ci		goto msg_err;
97462306a36Sopenharmony_ci	}
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	/* only read driver features after the feature negotiation is done */
97762306a36Sopenharmony_ci	status = vdev->config->get_status(vdev);
97862306a36Sopenharmony_ci	if (status & VIRTIO_CONFIG_S_FEATURES_OK) {
97962306a36Sopenharmony_ci		features_driver = vdev->config->get_driver_features(vdev);
98062306a36Sopenharmony_ci		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_NEGOTIATED_FEATURES, features_driver,
98162306a36Sopenharmony_ci				      VDPA_ATTR_PAD)) {
98262306a36Sopenharmony_ci			err = -EMSGSIZE;
98362306a36Sopenharmony_ci			goto msg_err;
98462306a36Sopenharmony_ci		}
98562306a36Sopenharmony_ci	}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_ci	switch (device_id) {
98862306a36Sopenharmony_ci	case VIRTIO_ID_NET:
98962306a36Sopenharmony_ci		err = vdpa_dev_net_config_fill(vdev, msg);
99062306a36Sopenharmony_ci		break;
99162306a36Sopenharmony_ci	default:
99262306a36Sopenharmony_ci		err = -EOPNOTSUPP;
99362306a36Sopenharmony_ci		break;
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci	if (err)
99662306a36Sopenharmony_ci		goto msg_err;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci	up_read(&vdev->cf_lock);
99962306a36Sopenharmony_ci	genlmsg_end(msg, hdr);
100062306a36Sopenharmony_ci	return 0;
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_cimsg_err:
100362306a36Sopenharmony_ci	genlmsg_cancel(msg, hdr);
100462306a36Sopenharmony_ciout:
100562306a36Sopenharmony_ci	up_read(&vdev->cf_lock);
100662306a36Sopenharmony_ci	return err;
100762306a36Sopenharmony_ci}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_cistatic int vdpa_fill_stats_rec(struct vdpa_device *vdev, struct sk_buff *msg,
101062306a36Sopenharmony_ci			       struct genl_info *info, u32 index)
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	struct virtio_net_config config = {};
101362306a36Sopenharmony_ci	u64 features;
101462306a36Sopenharmony_ci	u8 status;
101562306a36Sopenharmony_ci	int err;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	status = vdev->config->get_status(vdev);
101862306a36Sopenharmony_ci	if (!(status & VIRTIO_CONFIG_S_FEATURES_OK)) {
101962306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(info->extack, "feature negotiation not complete");
102062306a36Sopenharmony_ci		return -EAGAIN;
102162306a36Sopenharmony_ci	}
102262306a36Sopenharmony_ci	vdpa_get_config_unlocked(vdev, 0, &config, sizeof(config));
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_ci	features = vdev->config->get_driver_features(vdev);
102562306a36Sopenharmony_ci	if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_NEGOTIATED_FEATURES,
102662306a36Sopenharmony_ci			      features, VDPA_ATTR_PAD))
102762306a36Sopenharmony_ci		return -EMSGSIZE;
102862306a36Sopenharmony_ci
102962306a36Sopenharmony_ci	err = vdpa_dev_net_mq_config_fill(msg, features, &config);
103062306a36Sopenharmony_ci	if (err)
103162306a36Sopenharmony_ci		return err;
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	if (nla_put_u32(msg, VDPA_ATTR_DEV_QUEUE_INDEX, index))
103462306a36Sopenharmony_ci		return -EMSGSIZE;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci	err = vdev->config->get_vendor_vq_stats(vdev, index, msg, info->extack);
103762306a36Sopenharmony_ci	if (err)
103862306a36Sopenharmony_ci		return err;
103962306a36Sopenharmony_ci
104062306a36Sopenharmony_ci	return 0;
104162306a36Sopenharmony_ci}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_cistatic int vendor_stats_fill(struct vdpa_device *vdev, struct sk_buff *msg,
104462306a36Sopenharmony_ci			     struct genl_info *info, u32 index)
104562306a36Sopenharmony_ci{
104662306a36Sopenharmony_ci	int err;
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	down_read(&vdev->cf_lock);
104962306a36Sopenharmony_ci	if (!vdev->config->get_vendor_vq_stats) {
105062306a36Sopenharmony_ci		err = -EOPNOTSUPP;
105162306a36Sopenharmony_ci		goto out;
105262306a36Sopenharmony_ci	}
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	err = vdpa_fill_stats_rec(vdev, msg, info, index);
105562306a36Sopenharmony_ciout:
105662306a36Sopenharmony_ci	up_read(&vdev->cf_lock);
105762306a36Sopenharmony_ci	return err;
105862306a36Sopenharmony_ci}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_cistatic int vdpa_dev_vendor_stats_fill(struct vdpa_device *vdev,
106162306a36Sopenharmony_ci				      struct sk_buff *msg,
106262306a36Sopenharmony_ci				      struct genl_info *info, u32 index)
106362306a36Sopenharmony_ci{
106462306a36Sopenharmony_ci	u32 device_id;
106562306a36Sopenharmony_ci	void *hdr;
106662306a36Sopenharmony_ci	int err;
106762306a36Sopenharmony_ci	u32 portid = info->snd_portid;
106862306a36Sopenharmony_ci	u32 seq = info->snd_seq;
106962306a36Sopenharmony_ci	u32 flags = 0;
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	hdr = genlmsg_put(msg, portid, seq, &vdpa_nl_family, flags,
107262306a36Sopenharmony_ci			  VDPA_CMD_DEV_VSTATS_GET);
107362306a36Sopenharmony_ci	if (!hdr)
107462306a36Sopenharmony_ci		return -EMSGSIZE;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	if (nla_put_string(msg, VDPA_ATTR_DEV_NAME, dev_name(&vdev->dev))) {
107762306a36Sopenharmony_ci		err = -EMSGSIZE;
107862306a36Sopenharmony_ci		goto undo_msg;
107962306a36Sopenharmony_ci	}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci	device_id = vdev->config->get_device_id(vdev);
108262306a36Sopenharmony_ci	if (nla_put_u32(msg, VDPA_ATTR_DEV_ID, device_id)) {
108362306a36Sopenharmony_ci		err = -EMSGSIZE;
108462306a36Sopenharmony_ci		goto undo_msg;
108562306a36Sopenharmony_ci	}
108662306a36Sopenharmony_ci
108762306a36Sopenharmony_ci	switch (device_id) {
108862306a36Sopenharmony_ci	case VIRTIO_ID_NET:
108962306a36Sopenharmony_ci		if (index > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) {
109062306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(info->extack, "queue index exceeds max value");
109162306a36Sopenharmony_ci			err = -ERANGE;
109262306a36Sopenharmony_ci			break;
109362306a36Sopenharmony_ci		}
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci		err = vendor_stats_fill(vdev, msg, info, index);
109662306a36Sopenharmony_ci		break;
109762306a36Sopenharmony_ci	default:
109862306a36Sopenharmony_ci		err = -EOPNOTSUPP;
109962306a36Sopenharmony_ci		break;
110062306a36Sopenharmony_ci	}
110162306a36Sopenharmony_ci	genlmsg_end(msg, hdr);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	return err;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ciundo_msg:
110662306a36Sopenharmony_ci	genlmsg_cancel(msg, hdr);
110762306a36Sopenharmony_ci	return err;
110862306a36Sopenharmony_ci}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_cistatic int vdpa_nl_cmd_dev_config_get_doit(struct sk_buff *skb, struct genl_info *info)
111162306a36Sopenharmony_ci{
111262306a36Sopenharmony_ci	struct vdpa_device *vdev;
111362306a36Sopenharmony_ci	struct sk_buff *msg;
111462306a36Sopenharmony_ci	const char *devname;
111562306a36Sopenharmony_ci	struct device *dev;
111662306a36Sopenharmony_ci	int err;
111762306a36Sopenharmony_ci
111862306a36Sopenharmony_ci	if (!info->attrs[VDPA_ATTR_DEV_NAME])
111962306a36Sopenharmony_ci		return -EINVAL;
112062306a36Sopenharmony_ci	devname = nla_data(info->attrs[VDPA_ATTR_DEV_NAME]);
112162306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
112262306a36Sopenharmony_ci	if (!msg)
112362306a36Sopenharmony_ci		return -ENOMEM;
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	down_read(&vdpa_dev_lock);
112662306a36Sopenharmony_ci	dev = bus_find_device(&vdpa_bus, NULL, devname, vdpa_name_match);
112762306a36Sopenharmony_ci	if (!dev) {
112862306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(info->extack, "device not found");
112962306a36Sopenharmony_ci		err = -ENODEV;
113062306a36Sopenharmony_ci		goto dev_err;
113162306a36Sopenharmony_ci	}
113262306a36Sopenharmony_ci	vdev = container_of(dev, struct vdpa_device, dev);
113362306a36Sopenharmony_ci	if (!vdev->mdev) {
113462306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(info->extack, "unmanaged vdpa device");
113562306a36Sopenharmony_ci		err = -EINVAL;
113662306a36Sopenharmony_ci		goto mdev_err;
113762306a36Sopenharmony_ci	}
113862306a36Sopenharmony_ci	err = vdpa_dev_config_fill(vdev, msg, info->snd_portid, info->snd_seq,
113962306a36Sopenharmony_ci				   0, info->extack);
114062306a36Sopenharmony_ci	if (!err)
114162306a36Sopenharmony_ci		err = genlmsg_reply(msg, info);
114262306a36Sopenharmony_ci
114362306a36Sopenharmony_cimdev_err:
114462306a36Sopenharmony_ci	put_device(dev);
114562306a36Sopenharmony_cidev_err:
114662306a36Sopenharmony_ci	up_read(&vdpa_dev_lock);
114762306a36Sopenharmony_ci	if (err)
114862306a36Sopenharmony_ci		nlmsg_free(msg);
114962306a36Sopenharmony_ci	return err;
115062306a36Sopenharmony_ci}
115162306a36Sopenharmony_ci
115262306a36Sopenharmony_cistatic int vdpa_dev_config_dump(struct device *dev, void *data)
115362306a36Sopenharmony_ci{
115462306a36Sopenharmony_ci	struct vdpa_device *vdev = container_of(dev, struct vdpa_device, dev);
115562306a36Sopenharmony_ci	struct vdpa_dev_dump_info *info = data;
115662306a36Sopenharmony_ci	int err;
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	if (!vdev->mdev)
115962306a36Sopenharmony_ci		return 0;
116062306a36Sopenharmony_ci	if (info->idx < info->start_idx) {
116162306a36Sopenharmony_ci		info->idx++;
116262306a36Sopenharmony_ci		return 0;
116362306a36Sopenharmony_ci	}
116462306a36Sopenharmony_ci	err = vdpa_dev_config_fill(vdev, info->msg, NETLINK_CB(info->cb->skb).portid,
116562306a36Sopenharmony_ci				   info->cb->nlh->nlmsg_seq, NLM_F_MULTI,
116662306a36Sopenharmony_ci				   info->cb->extack);
116762306a36Sopenharmony_ci	if (err)
116862306a36Sopenharmony_ci		return err;
116962306a36Sopenharmony_ci
117062306a36Sopenharmony_ci	info->idx++;
117162306a36Sopenharmony_ci	return 0;
117262306a36Sopenharmony_ci}
117362306a36Sopenharmony_ci
117462306a36Sopenharmony_cistatic int
117562306a36Sopenharmony_civdpa_nl_cmd_dev_config_get_dumpit(struct sk_buff *msg, struct netlink_callback *cb)
117662306a36Sopenharmony_ci{
117762306a36Sopenharmony_ci	struct vdpa_dev_dump_info info;
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	info.msg = msg;
118062306a36Sopenharmony_ci	info.cb = cb;
118162306a36Sopenharmony_ci	info.start_idx = cb->args[0];
118262306a36Sopenharmony_ci	info.idx = 0;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	down_read(&vdpa_dev_lock);
118562306a36Sopenharmony_ci	bus_for_each_dev(&vdpa_bus, NULL, &info, vdpa_dev_config_dump);
118662306a36Sopenharmony_ci	up_read(&vdpa_dev_lock);
118762306a36Sopenharmony_ci	cb->args[0] = info.idx;
118862306a36Sopenharmony_ci	return msg->len;
118962306a36Sopenharmony_ci}
119062306a36Sopenharmony_ci
119162306a36Sopenharmony_cistatic int vdpa_nl_cmd_dev_stats_get_doit(struct sk_buff *skb,
119262306a36Sopenharmony_ci					  struct genl_info *info)
119362306a36Sopenharmony_ci{
119462306a36Sopenharmony_ci	struct vdpa_device *vdev;
119562306a36Sopenharmony_ci	struct sk_buff *msg;
119662306a36Sopenharmony_ci	const char *devname;
119762306a36Sopenharmony_ci	struct device *dev;
119862306a36Sopenharmony_ci	u32 index;
119962306a36Sopenharmony_ci	int err;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	if (!info->attrs[VDPA_ATTR_DEV_NAME])
120262306a36Sopenharmony_ci		return -EINVAL;
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci	if (!info->attrs[VDPA_ATTR_DEV_QUEUE_INDEX])
120562306a36Sopenharmony_ci		return -EINVAL;
120662306a36Sopenharmony_ci
120762306a36Sopenharmony_ci	devname = nla_data(info->attrs[VDPA_ATTR_DEV_NAME]);
120862306a36Sopenharmony_ci	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
120962306a36Sopenharmony_ci	if (!msg)
121062306a36Sopenharmony_ci		return -ENOMEM;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci	index = nla_get_u32(info->attrs[VDPA_ATTR_DEV_QUEUE_INDEX]);
121362306a36Sopenharmony_ci	down_read(&vdpa_dev_lock);
121462306a36Sopenharmony_ci	dev = bus_find_device(&vdpa_bus, NULL, devname, vdpa_name_match);
121562306a36Sopenharmony_ci	if (!dev) {
121662306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(info->extack, "device not found");
121762306a36Sopenharmony_ci		err = -ENODEV;
121862306a36Sopenharmony_ci		goto dev_err;
121962306a36Sopenharmony_ci	}
122062306a36Sopenharmony_ci	vdev = container_of(dev, struct vdpa_device, dev);
122162306a36Sopenharmony_ci	if (!vdev->mdev) {
122262306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(info->extack, "unmanaged vdpa device");
122362306a36Sopenharmony_ci		err = -EINVAL;
122462306a36Sopenharmony_ci		goto mdev_err;
122562306a36Sopenharmony_ci	}
122662306a36Sopenharmony_ci	err = vdpa_dev_vendor_stats_fill(vdev, msg, info, index);
122762306a36Sopenharmony_ci	if (err)
122862306a36Sopenharmony_ci		goto mdev_err;
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci	err = genlmsg_reply(msg, info);
123162306a36Sopenharmony_ci
123262306a36Sopenharmony_ci	put_device(dev);
123362306a36Sopenharmony_ci	up_read(&vdpa_dev_lock);
123462306a36Sopenharmony_ci
123562306a36Sopenharmony_ci	return err;
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_cimdev_err:
123862306a36Sopenharmony_ci	put_device(dev);
123962306a36Sopenharmony_cidev_err:
124062306a36Sopenharmony_ci	nlmsg_free(msg);
124162306a36Sopenharmony_ci	up_read(&vdpa_dev_lock);
124262306a36Sopenharmony_ci	return err;
124362306a36Sopenharmony_ci}
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_cistatic const struct nla_policy vdpa_nl_policy[VDPA_ATTR_MAX + 1] = {
124662306a36Sopenharmony_ci	[VDPA_ATTR_MGMTDEV_BUS_NAME] = { .type = NLA_NUL_STRING },
124762306a36Sopenharmony_ci	[VDPA_ATTR_MGMTDEV_DEV_NAME] = { .type = NLA_STRING },
124862306a36Sopenharmony_ci	[VDPA_ATTR_DEV_NAME] = { .type = NLA_STRING },
124962306a36Sopenharmony_ci	[VDPA_ATTR_DEV_NET_CFG_MACADDR] = NLA_POLICY_ETH_ADDR,
125062306a36Sopenharmony_ci	[VDPA_ATTR_DEV_NET_CFG_MAX_VQP] = { .type = NLA_U16 },
125162306a36Sopenharmony_ci	/* virtio spec 1.1 section 5.1.4.1 for valid MTU range */
125262306a36Sopenharmony_ci	[VDPA_ATTR_DEV_NET_CFG_MTU] = NLA_POLICY_MIN(NLA_U16, 68),
125362306a36Sopenharmony_ci	[VDPA_ATTR_DEV_QUEUE_INDEX] = { .type = NLA_U32 },
125462306a36Sopenharmony_ci	[VDPA_ATTR_DEV_FEATURES] = { .type = NLA_U64 },
125562306a36Sopenharmony_ci};
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_cistatic const struct genl_ops vdpa_nl_ops[] = {
125862306a36Sopenharmony_ci	{
125962306a36Sopenharmony_ci		.cmd = VDPA_CMD_MGMTDEV_GET,
126062306a36Sopenharmony_ci		.doit = vdpa_nl_cmd_mgmtdev_get_doit,
126162306a36Sopenharmony_ci		.dumpit = vdpa_nl_cmd_mgmtdev_get_dumpit,
126262306a36Sopenharmony_ci	},
126362306a36Sopenharmony_ci	{
126462306a36Sopenharmony_ci		.cmd = VDPA_CMD_DEV_NEW,
126562306a36Sopenharmony_ci		.doit = vdpa_nl_cmd_dev_add_set_doit,
126662306a36Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
126762306a36Sopenharmony_ci	},
126862306a36Sopenharmony_ci	{
126962306a36Sopenharmony_ci		.cmd = VDPA_CMD_DEV_DEL,
127062306a36Sopenharmony_ci		.doit = vdpa_nl_cmd_dev_del_set_doit,
127162306a36Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
127262306a36Sopenharmony_ci	},
127362306a36Sopenharmony_ci	{
127462306a36Sopenharmony_ci		.cmd = VDPA_CMD_DEV_GET,
127562306a36Sopenharmony_ci		.doit = vdpa_nl_cmd_dev_get_doit,
127662306a36Sopenharmony_ci		.dumpit = vdpa_nl_cmd_dev_get_dumpit,
127762306a36Sopenharmony_ci	},
127862306a36Sopenharmony_ci	{
127962306a36Sopenharmony_ci		.cmd = VDPA_CMD_DEV_CONFIG_GET,
128062306a36Sopenharmony_ci		.doit = vdpa_nl_cmd_dev_config_get_doit,
128162306a36Sopenharmony_ci		.dumpit = vdpa_nl_cmd_dev_config_get_dumpit,
128262306a36Sopenharmony_ci	},
128362306a36Sopenharmony_ci	{
128462306a36Sopenharmony_ci		.cmd = VDPA_CMD_DEV_VSTATS_GET,
128562306a36Sopenharmony_ci		.doit = vdpa_nl_cmd_dev_stats_get_doit,
128662306a36Sopenharmony_ci		.flags = GENL_ADMIN_PERM,
128762306a36Sopenharmony_ci	},
128862306a36Sopenharmony_ci};
128962306a36Sopenharmony_ci
129062306a36Sopenharmony_cistatic struct genl_family vdpa_nl_family __ro_after_init = {
129162306a36Sopenharmony_ci	.name = VDPA_GENL_NAME,
129262306a36Sopenharmony_ci	.version = VDPA_GENL_VERSION,
129362306a36Sopenharmony_ci	.maxattr = VDPA_ATTR_MAX,
129462306a36Sopenharmony_ci	.policy = vdpa_nl_policy,
129562306a36Sopenharmony_ci	.netnsok = false,
129662306a36Sopenharmony_ci	.module = THIS_MODULE,
129762306a36Sopenharmony_ci	.ops = vdpa_nl_ops,
129862306a36Sopenharmony_ci	.n_ops = ARRAY_SIZE(vdpa_nl_ops),
129962306a36Sopenharmony_ci	.resv_start_op = VDPA_CMD_DEV_VSTATS_GET + 1,
130062306a36Sopenharmony_ci};
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_cistatic int vdpa_init(void)
130362306a36Sopenharmony_ci{
130462306a36Sopenharmony_ci	int err;
130562306a36Sopenharmony_ci
130662306a36Sopenharmony_ci	err = bus_register(&vdpa_bus);
130762306a36Sopenharmony_ci	if (err)
130862306a36Sopenharmony_ci		return err;
130962306a36Sopenharmony_ci	err = genl_register_family(&vdpa_nl_family);
131062306a36Sopenharmony_ci	if (err)
131162306a36Sopenharmony_ci		goto err;
131262306a36Sopenharmony_ci	return 0;
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_cierr:
131562306a36Sopenharmony_ci	bus_unregister(&vdpa_bus);
131662306a36Sopenharmony_ci	return err;
131762306a36Sopenharmony_ci}
131862306a36Sopenharmony_ci
131962306a36Sopenharmony_cistatic void __exit vdpa_exit(void)
132062306a36Sopenharmony_ci{
132162306a36Sopenharmony_ci	genl_unregister_family(&vdpa_nl_family);
132262306a36Sopenharmony_ci	bus_unregister(&vdpa_bus);
132362306a36Sopenharmony_ci	ida_destroy(&vdpa_index_ida);
132462306a36Sopenharmony_ci}
132562306a36Sopenharmony_cicore_initcall(vdpa_init);
132662306a36Sopenharmony_cimodule_exit(vdpa_exit);
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ciMODULE_AUTHOR("Jason Wang <jasowang@redhat.com>");
132962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
1330