xref: /openbmc/linux/drivers/vdpa/vdpa.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1c9b9f5f8SMichael S. Tsirkin // SPDX-License-Identifier: GPL-2.0-only
2c9b9f5f8SMichael S. Tsirkin /*
3c9b9f5f8SMichael S. Tsirkin  * vDPA bus.
4c9b9f5f8SMichael S. Tsirkin  *
5c9b9f5f8SMichael S. Tsirkin  * Copyright (c) 2020, Red Hat. All rights reserved.
6c9b9f5f8SMichael S. Tsirkin  *     Author: Jason Wang <jasowang@redhat.com>
7c9b9f5f8SMichael S. Tsirkin  *
8c9b9f5f8SMichael S. Tsirkin  */
9c9b9f5f8SMichael S. Tsirkin 
10c9b9f5f8SMichael S. Tsirkin #include <linux/module.h>
11c9b9f5f8SMichael S. Tsirkin #include <linux/idr.h>
12c9b9f5f8SMichael S. Tsirkin #include <linux/slab.h>
13c9b9f5f8SMichael S. Tsirkin #include <linux/vdpa.h>
1433b34750SParav Pandit #include <uapi/linux/vdpa.h>
1533b34750SParav Pandit #include <net/genetlink.h>
1633b34750SParav Pandit #include <linux/mod_devicetable.h>
17ad69dd0bSParav Pandit #include <linux/virtio_ids.h>
18c9b9f5f8SMichael S. Tsirkin 
1933b34750SParav Pandit static LIST_HEAD(mdev_head);
20fd70a406SParav Pandit /* A global mutex that protects vdpa management device and device level operations. */
210078ad90SEli Cohen static DECLARE_RWSEM(vdpa_dev_lock);
22c9b9f5f8SMichael S. Tsirkin static DEFINE_IDA(vdpa_index_ida);
23c9b9f5f8SMichael S. Tsirkin 
vdpa_set_status(struct vdpa_device * vdev,u8 status)2473bc0dbbSEli Cohen void vdpa_set_status(struct vdpa_device *vdev, u8 status)
2573bc0dbbSEli Cohen {
26a6a51adcSEli Cohen 	down_write(&vdev->cf_lock);
2773bc0dbbSEli Cohen 	vdev->config->set_status(vdev, status);
28a6a51adcSEli Cohen 	up_write(&vdev->cf_lock);
2973bc0dbbSEli Cohen }
3073bc0dbbSEli Cohen EXPORT_SYMBOL(vdpa_set_status);
3173bc0dbbSEli Cohen 
3233b34750SParav Pandit static struct genl_family vdpa_nl_family;
3333b34750SParav Pandit 
vdpa_dev_probe(struct device * d)34c9b9f5f8SMichael S. Tsirkin static int vdpa_dev_probe(struct device *d)
35c9b9f5f8SMichael S. Tsirkin {
36c9b9f5f8SMichael S. Tsirkin 	struct vdpa_device *vdev = dev_to_vdpa(d);
37c9b9f5f8SMichael S. Tsirkin 	struct vdpa_driver *drv = drv_to_vdpa(vdev->dev.driver);
38c53e5d1bSWu Zongyong 	const struct vdpa_config_ops *ops = vdev->config;
39c53e5d1bSWu Zongyong 	u32 max_num, min_num = 1;
40c9b9f5f8SMichael S. Tsirkin 	int ret = 0;
41c9b9f5f8SMichael S. Tsirkin 
4299fb2b83SJason Wang 	d->dma_mask = &d->coherent_dma_mask;
4399fb2b83SJason Wang 	ret = dma_set_mask_and_coherent(d, DMA_BIT_MASK(64));
4499fb2b83SJason Wang 	if (ret)
4599fb2b83SJason Wang 		return ret;
4699fb2b83SJason Wang 
47c53e5d1bSWu Zongyong 	max_num = ops->get_vq_num_max(vdev);
48c53e5d1bSWu Zongyong 	if (ops->get_vq_num_min)
49c53e5d1bSWu Zongyong 		min_num = ops->get_vq_num_min(vdev);
50c53e5d1bSWu Zongyong 	if (max_num < min_num)
51c53e5d1bSWu Zongyong 		return -EINVAL;
52c53e5d1bSWu Zongyong 
53c9b9f5f8SMichael S. Tsirkin 	if (drv && drv->probe)
54c9b9f5f8SMichael S. Tsirkin 		ret = drv->probe(vdev);
55c9b9f5f8SMichael S. Tsirkin 
56c9b9f5f8SMichael S. Tsirkin 	return ret;
57c9b9f5f8SMichael S. Tsirkin }
58c9b9f5f8SMichael S. Tsirkin 
vdpa_dev_remove(struct device * d)59fc7a6209SUwe Kleine-König static void vdpa_dev_remove(struct device *d)
60c9b9f5f8SMichael S. Tsirkin {
61c9b9f5f8SMichael S. Tsirkin 	struct vdpa_device *vdev = dev_to_vdpa(d);
62c9b9f5f8SMichael S. Tsirkin 	struct vdpa_driver *drv = drv_to_vdpa(vdev->dev.driver);
63c9b9f5f8SMichael S. Tsirkin 
64c9b9f5f8SMichael S. Tsirkin 	if (drv && drv->remove)
65c9b9f5f8SMichael S. Tsirkin 		drv->remove(vdev);
66c9b9f5f8SMichael S. Tsirkin }
67c9b9f5f8SMichael S. Tsirkin 
vdpa_dev_match(struct device * dev,struct device_driver * drv)68539fec78SStefano Garzarella static int vdpa_dev_match(struct device *dev, struct device_driver *drv)
69539fec78SStefano Garzarella {
70539fec78SStefano Garzarella 	struct vdpa_device *vdev = dev_to_vdpa(dev);
71539fec78SStefano Garzarella 
72539fec78SStefano Garzarella 	/* Check override first, and if set, only use the named driver */
73539fec78SStefano Garzarella 	if (vdev->driver_override)
74539fec78SStefano Garzarella 		return strcmp(vdev->driver_override, drv->name) == 0;
75539fec78SStefano Garzarella 
76539fec78SStefano Garzarella 	/* Currently devices must be supported by all vDPA bus drivers */
77539fec78SStefano Garzarella 	return 1;
78539fec78SStefano Garzarella }
79539fec78SStefano Garzarella 
driver_override_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)80539fec78SStefano Garzarella static ssize_t driver_override_store(struct device *dev,
81539fec78SStefano Garzarella 				     struct device_attribute *attr,
82539fec78SStefano Garzarella 				     const char *buf, size_t count)
83539fec78SStefano Garzarella {
84539fec78SStefano Garzarella 	struct vdpa_device *vdev = dev_to_vdpa(dev);
85240bf4e6SKrzysztof Kozlowski 	int ret;
86539fec78SStefano Garzarella 
87240bf4e6SKrzysztof Kozlowski 	ret = driver_set_override(dev, &vdev->driver_override, buf, count);
88240bf4e6SKrzysztof Kozlowski 	if (ret)
89240bf4e6SKrzysztof Kozlowski 		return ret;
90539fec78SStefano Garzarella 
91539fec78SStefano Garzarella 	return count;
92539fec78SStefano Garzarella }
93539fec78SStefano Garzarella 
driver_override_show(struct device * dev,struct device_attribute * attr,char * buf)94539fec78SStefano Garzarella static ssize_t driver_override_show(struct device *dev,
95539fec78SStefano Garzarella 				    struct device_attribute *attr, char *buf)
96539fec78SStefano Garzarella {
97539fec78SStefano Garzarella 	struct vdpa_device *vdev = dev_to_vdpa(dev);
98539fec78SStefano Garzarella 	ssize_t len;
99539fec78SStefano Garzarella 
100539fec78SStefano Garzarella 	device_lock(dev);
101539fec78SStefano Garzarella 	len = snprintf(buf, PAGE_SIZE, "%s\n", vdev->driver_override);
102539fec78SStefano Garzarella 	device_unlock(dev);
103539fec78SStefano Garzarella 
104539fec78SStefano Garzarella 	return len;
105539fec78SStefano Garzarella }
106539fec78SStefano Garzarella static DEVICE_ATTR_RW(driver_override);
107539fec78SStefano Garzarella 
108539fec78SStefano Garzarella static struct attribute *vdpa_dev_attrs[] = {
109539fec78SStefano Garzarella 	&dev_attr_driver_override.attr,
110539fec78SStefano Garzarella 	NULL,
111539fec78SStefano Garzarella };
112539fec78SStefano Garzarella 
113539fec78SStefano Garzarella static const struct attribute_group vdpa_dev_group = {
114539fec78SStefano Garzarella 	.attrs  = vdpa_dev_attrs,
115539fec78SStefano Garzarella };
116539fec78SStefano Garzarella __ATTRIBUTE_GROUPS(vdpa_dev);
117539fec78SStefano Garzarella 
118c9b9f5f8SMichael S. Tsirkin static struct bus_type vdpa_bus = {
119c9b9f5f8SMichael S. Tsirkin 	.name  = "vdpa",
120539fec78SStefano Garzarella 	.dev_groups = vdpa_dev_groups,
121539fec78SStefano Garzarella 	.match = vdpa_dev_match,
122c9b9f5f8SMichael S. Tsirkin 	.probe = vdpa_dev_probe,
123c9b9f5f8SMichael S. Tsirkin 	.remove = vdpa_dev_remove,
124c9b9f5f8SMichael S. Tsirkin };
125c9b9f5f8SMichael S. Tsirkin 
vdpa_release_dev(struct device * d)126c9b9f5f8SMichael S. Tsirkin static void vdpa_release_dev(struct device *d)
127c9b9f5f8SMichael S. Tsirkin {
128c9b9f5f8SMichael S. Tsirkin 	struct vdpa_device *vdev = dev_to_vdpa(d);
129c9b9f5f8SMichael S. Tsirkin 	const struct vdpa_config_ops *ops = vdev->config;
130c9b9f5f8SMichael S. Tsirkin 
131c9b9f5f8SMichael S. Tsirkin 	if (ops->free)
132c9b9f5f8SMichael S. Tsirkin 		ops->free(vdev);
133c9b9f5f8SMichael S. Tsirkin 
134c9b9f5f8SMichael S. Tsirkin 	ida_simple_remove(&vdpa_index_ida, vdev->index);
135539fec78SStefano Garzarella 	kfree(vdev->driver_override);
136c9b9f5f8SMichael S. Tsirkin 	kfree(vdev);
137c9b9f5f8SMichael S. Tsirkin }
138c9b9f5f8SMichael S. Tsirkin 
139c9b9f5f8SMichael S. Tsirkin /**
140c9b9f5f8SMichael S. Tsirkin  * __vdpa_alloc_device - allocate and initilaize a vDPA device
141c9b9f5f8SMichael S. Tsirkin  * This allows driver to some prepartion after device is
142c9b9f5f8SMichael S. Tsirkin  * initialized but before registered.
143c9b9f5f8SMichael S. Tsirkin  * @parent: the parent device
144c9b9f5f8SMichael S. Tsirkin  * @config: the bus operations that is supported by this device
145d4821902SGautam Dawar  * @ngroups: number of groups supported by this device
146db9adcbfSGautam Dawar  * @nas: number of address spaces supported by this device
147c9b9f5f8SMichael S. Tsirkin  * @size: size of the parent structure that contains private data
148fd70a406SParav Pandit  * @name: name of the vdpa device; optional.
149d8945ec4SXie Yongji  * @use_va: indicate whether virtual address must be used by this device
150c9b9f5f8SMichael S. Tsirkin  *
15124eae8ebSJason Wang  * Driver should use vdpa_alloc_device() wrapper macro instead of
152c9b9f5f8SMichael S. Tsirkin  * using this directly.
153c9b9f5f8SMichael S. Tsirkin  *
154c0a54b4bSParav Pandit  * Return: Returns an error when parent/config/dma_dev is not set or fail to get
155c9b9f5f8SMichael S. Tsirkin  *	   ida.
156c9b9f5f8SMichael S. Tsirkin  */
__vdpa_alloc_device(struct device * parent,const struct vdpa_config_ops * config,unsigned int ngroups,unsigned int nas,size_t size,const char * name,bool use_va)157c9b9f5f8SMichael S. Tsirkin struct vdpa_device *__vdpa_alloc_device(struct device *parent,
158c9b9f5f8SMichael S. Tsirkin 					const struct vdpa_config_ops *config,
159db9adcbfSGautam Dawar 					unsigned int ngroups, unsigned int nas,
160d8945ec4SXie Yongji 					size_t size, const char *name,
161d8945ec4SXie Yongji 					bool use_va)
162c9b9f5f8SMichael S. Tsirkin {
163c9b9f5f8SMichael S. Tsirkin 	struct vdpa_device *vdev;
164c9b9f5f8SMichael S. Tsirkin 	int err = -EINVAL;
165c9b9f5f8SMichael S. Tsirkin 
166c9b9f5f8SMichael S. Tsirkin 	if (!config)
167c9b9f5f8SMichael S. Tsirkin 		goto err;
168c9b9f5f8SMichael S. Tsirkin 
169c9b9f5f8SMichael S. Tsirkin 	if (!!config->dma_map != !!config->dma_unmap)
170c9b9f5f8SMichael S. Tsirkin 		goto err;
171c9b9f5f8SMichael S. Tsirkin 
172d8945ec4SXie Yongji 	/* It should only work for the device that use on-chip IOMMU */
173d8945ec4SXie Yongji 	if (use_va && !(config->dma_map || config->set_map))
174d8945ec4SXie Yongji 		goto err;
175d8945ec4SXie Yongji 
176c9b9f5f8SMichael S. Tsirkin 	err = -ENOMEM;
177c9b9f5f8SMichael S. Tsirkin 	vdev = kzalloc(size, GFP_KERNEL);
178c9b9f5f8SMichael S. Tsirkin 	if (!vdev)
179c9b9f5f8SMichael S. Tsirkin 		goto err;
180c9b9f5f8SMichael S. Tsirkin 
181418eddefSParav Pandit 	err = ida_alloc(&vdpa_index_ida, GFP_KERNEL);
182c9b9f5f8SMichael S. Tsirkin 	if (err < 0)
183c9b9f5f8SMichael S. Tsirkin 		goto err_ida;
184c9b9f5f8SMichael S. Tsirkin 
185c9b9f5f8SMichael S. Tsirkin 	vdev->dev.bus = &vdpa_bus;
186c9b9f5f8SMichael S. Tsirkin 	vdev->dev.parent = parent;
187c9b9f5f8SMichael S. Tsirkin 	vdev->dev.release = vdpa_release_dev;
188c9b9f5f8SMichael S. Tsirkin 	vdev->index = err;
189c9b9f5f8SMichael S. Tsirkin 	vdev->config = config;
190452639a6SMichael S. Tsirkin 	vdev->features_valid = false;
191d8945ec4SXie Yongji 	vdev->use_va = use_va;
192d4821902SGautam Dawar 	vdev->ngroups = ngroups;
193db9adcbfSGautam Dawar 	vdev->nas = nas;
194c9b9f5f8SMichael S. Tsirkin 
195fd70a406SParav Pandit 	if (name)
196fd70a406SParav Pandit 		err = dev_set_name(&vdev->dev, "%s", name);
197fd70a406SParav Pandit 	else
198c9b9f5f8SMichael S. Tsirkin 		err = dev_set_name(&vdev->dev, "vdpa%u", vdev->index);
199c9b9f5f8SMichael S. Tsirkin 	if (err)
200c9b9f5f8SMichael S. Tsirkin 		goto err_name;
201c9b9f5f8SMichael S. Tsirkin 
202a6a51adcSEli Cohen 	init_rwsem(&vdev->cf_lock);
203c9b9f5f8SMichael S. Tsirkin 	device_initialize(&vdev->dev);
204c9b9f5f8SMichael S. Tsirkin 
205c9b9f5f8SMichael S. Tsirkin 	return vdev;
206c9b9f5f8SMichael S. Tsirkin 
207c9b9f5f8SMichael S. Tsirkin err_name:
208c9b9f5f8SMichael S. Tsirkin 	ida_simple_remove(&vdpa_index_ida, vdev->index);
209c9b9f5f8SMichael S. Tsirkin err_ida:
210c9b9f5f8SMichael S. Tsirkin 	kfree(vdev);
211c9b9f5f8SMichael S. Tsirkin err:
212c9b9f5f8SMichael S. Tsirkin 	return ERR_PTR(err);
213c9b9f5f8SMichael S. Tsirkin }
214c9b9f5f8SMichael S. Tsirkin EXPORT_SYMBOL_GPL(__vdpa_alloc_device);
215c9b9f5f8SMichael S. Tsirkin 
vdpa_name_match(struct device * dev,const void * data)216fd70a406SParav Pandit static int vdpa_name_match(struct device *dev, const void *data)
217fd70a406SParav Pandit {
218fd70a406SParav Pandit 	struct vdpa_device *vdev = container_of(dev, struct vdpa_device, dev);
219fd70a406SParav Pandit 
220fd70a406SParav Pandit 	return (strcmp(dev_name(&vdev->dev), data) == 0);
221fd70a406SParav Pandit }
222fd70a406SParav Pandit 
__vdpa_register_device(struct vdpa_device * vdev,u32 nvqs)22381d46d69SLongpeng static int __vdpa_register_device(struct vdpa_device *vdev, u32 nvqs)
224903f7bcaSParav Pandit {
225903f7bcaSParav Pandit 	struct device *dev;
226903f7bcaSParav Pandit 
227f00bdce0SJason Wang 	vdev->nvqs = nvqs;
228f00bdce0SJason Wang 
2290078ad90SEli Cohen 	lockdep_assert_held(&vdpa_dev_lock);
230903f7bcaSParav Pandit 	dev = bus_find_device(&vdpa_bus, NULL, dev_name(&vdev->dev), vdpa_name_match);
231903f7bcaSParav Pandit 	if (dev) {
232903f7bcaSParav Pandit 		put_device(dev);
233903f7bcaSParav Pandit 		return -EEXIST;
234903f7bcaSParav Pandit 	}
235903f7bcaSParav Pandit 	return device_add(&vdev->dev);
236903f7bcaSParav Pandit }
237903f7bcaSParav Pandit 
238903f7bcaSParav Pandit /**
239903f7bcaSParav Pandit  * _vdpa_register_device - register a vDPA device with vdpa lock held
240903f7bcaSParav Pandit  * Caller must have a succeed call of vdpa_alloc_device() before.
241903f7bcaSParav Pandit  * Caller must invoke this routine in the management device dev_add()
242903f7bcaSParav Pandit  * callback after setting up valid mgmtdev for this vdpa device.
243903f7bcaSParav Pandit  * @vdev: the vdpa device to be registered to vDPA bus
244f00bdce0SJason Wang  * @nvqs: number of virtqueues supported by this device
245903f7bcaSParav Pandit  *
246c0a54b4bSParav Pandit  * Return: Returns an error when fail to add device to vDPA bus
247903f7bcaSParav Pandit  */
_vdpa_register_device(struct vdpa_device * vdev,u32 nvqs)24881d46d69SLongpeng int _vdpa_register_device(struct vdpa_device *vdev, u32 nvqs)
249903f7bcaSParav Pandit {
250903f7bcaSParav Pandit 	if (!vdev->mdev)
251903f7bcaSParav Pandit 		return -EINVAL;
252903f7bcaSParav Pandit 
253f00bdce0SJason Wang 	return __vdpa_register_device(vdev, nvqs);
254903f7bcaSParav Pandit }
255903f7bcaSParav Pandit EXPORT_SYMBOL_GPL(_vdpa_register_device);
256903f7bcaSParav Pandit 
257c9b9f5f8SMichael S. Tsirkin /**
258c9b9f5f8SMichael S. Tsirkin  * vdpa_register_device - register a vDPA device
259ac8b85f9SJason Wang  * Callers must have a succeed call of vdpa_alloc_device() before.
260c9b9f5f8SMichael S. Tsirkin  * @vdev: the vdpa device to be registered to vDPA bus
261f00bdce0SJason Wang  * @nvqs: number of virtqueues supported by this device
262c9b9f5f8SMichael S. Tsirkin  *
263c0a54b4bSParav Pandit  * Return: Returns an error when fail to add to vDPA bus
264c9b9f5f8SMichael S. Tsirkin  */
vdpa_register_device(struct vdpa_device * vdev,u32 nvqs)26581d46d69SLongpeng int vdpa_register_device(struct vdpa_device *vdev, u32 nvqs)
266c9b9f5f8SMichael S. Tsirkin {
267fd70a406SParav Pandit 	int err;
268fd70a406SParav Pandit 
2690078ad90SEli Cohen 	down_write(&vdpa_dev_lock);
270f00bdce0SJason Wang 	err = __vdpa_register_device(vdev, nvqs);
2710078ad90SEli Cohen 	up_write(&vdpa_dev_lock);
272fd70a406SParav Pandit 	return err;
273c9b9f5f8SMichael S. Tsirkin }
274c9b9f5f8SMichael S. Tsirkin EXPORT_SYMBOL_GPL(vdpa_register_device);
275c9b9f5f8SMichael S. Tsirkin 
276c9b9f5f8SMichael S. Tsirkin /**
277903f7bcaSParav Pandit  * _vdpa_unregister_device - unregister a vDPA device
278903f7bcaSParav Pandit  * Caller must invoke this routine as part of management device dev_del()
279903f7bcaSParav Pandit  * callback.
280903f7bcaSParav Pandit  * @vdev: the vdpa device to be unregisted from vDPA bus
281903f7bcaSParav Pandit  */
_vdpa_unregister_device(struct vdpa_device * vdev)282903f7bcaSParav Pandit void _vdpa_unregister_device(struct vdpa_device *vdev)
283903f7bcaSParav Pandit {
2840078ad90SEli Cohen 	lockdep_assert_held(&vdpa_dev_lock);
285903f7bcaSParav Pandit 	WARN_ON(!vdev->mdev);
286903f7bcaSParav Pandit 	device_unregister(&vdev->dev);
287903f7bcaSParav Pandit }
288903f7bcaSParav Pandit EXPORT_SYMBOL_GPL(_vdpa_unregister_device);
289903f7bcaSParav Pandit 
290903f7bcaSParav Pandit /**
291c9b9f5f8SMichael S. Tsirkin  * vdpa_unregister_device - unregister a vDPA device
292c9b9f5f8SMichael S. Tsirkin  * @vdev: the vdpa device to be unregisted from vDPA bus
293c9b9f5f8SMichael S. Tsirkin  */
vdpa_unregister_device(struct vdpa_device * vdev)294c9b9f5f8SMichael S. Tsirkin void vdpa_unregister_device(struct vdpa_device *vdev)
295c9b9f5f8SMichael S. Tsirkin {
2960078ad90SEli Cohen 	down_write(&vdpa_dev_lock);
297c9b9f5f8SMichael S. Tsirkin 	device_unregister(&vdev->dev);
2980078ad90SEli Cohen 	up_write(&vdpa_dev_lock);
299c9b9f5f8SMichael S. Tsirkin }
300c9b9f5f8SMichael S. Tsirkin EXPORT_SYMBOL_GPL(vdpa_unregister_device);
301c9b9f5f8SMichael S. Tsirkin 
302c9b9f5f8SMichael S. Tsirkin /**
303c9b9f5f8SMichael S. Tsirkin  * __vdpa_register_driver - register a vDPA device driver
304c9b9f5f8SMichael S. Tsirkin  * @drv: the vdpa device driver to be registered
305c9b9f5f8SMichael S. Tsirkin  * @owner: module owner of the driver
306c9b9f5f8SMichael S. Tsirkin  *
307c0a54b4bSParav Pandit  * Return: Returns an err when fail to do the registration
308c9b9f5f8SMichael S. Tsirkin  */
__vdpa_register_driver(struct vdpa_driver * drv,struct module * owner)309c9b9f5f8SMichael S. Tsirkin int __vdpa_register_driver(struct vdpa_driver *drv, struct module *owner)
310c9b9f5f8SMichael S. Tsirkin {
311c9b9f5f8SMichael S. Tsirkin 	drv->driver.bus = &vdpa_bus;
312c9b9f5f8SMichael S. Tsirkin 	drv->driver.owner = owner;
313c9b9f5f8SMichael S. Tsirkin 
314c9b9f5f8SMichael S. Tsirkin 	return driver_register(&drv->driver);
315c9b9f5f8SMichael S. Tsirkin }
316c9b9f5f8SMichael S. Tsirkin EXPORT_SYMBOL_GPL(__vdpa_register_driver);
317c9b9f5f8SMichael S. Tsirkin 
318c9b9f5f8SMichael S. Tsirkin /**
319c9b9f5f8SMichael S. Tsirkin  * vdpa_unregister_driver - unregister a vDPA device driver
320c9b9f5f8SMichael S. Tsirkin  * @drv: the vdpa device driver to be unregistered
321c9b9f5f8SMichael S. Tsirkin  */
vdpa_unregister_driver(struct vdpa_driver * drv)322c9b9f5f8SMichael S. Tsirkin void vdpa_unregister_driver(struct vdpa_driver *drv)
323c9b9f5f8SMichael S. Tsirkin {
324c9b9f5f8SMichael S. Tsirkin 	driver_unregister(&drv->driver);
325c9b9f5f8SMichael S. Tsirkin }
326c9b9f5f8SMichael S. Tsirkin EXPORT_SYMBOL_GPL(vdpa_unregister_driver);
327c9b9f5f8SMichael S. Tsirkin 
32833b34750SParav Pandit /**
32933b34750SParav Pandit  * vdpa_mgmtdev_register - register a vdpa management device
33033b34750SParav Pandit  *
33133b34750SParav Pandit  * @mdev: Pointer to vdpa management device
33233b34750SParav Pandit  * vdpa_mgmtdev_register() register a vdpa management device which supports
33333b34750SParav Pandit  * vdpa device management.
334c0a54b4bSParav Pandit  * Return: Returns 0 on success or failure when required callback ops are not
335c0a54b4bSParav Pandit  *         initialized.
33633b34750SParav Pandit  */
vdpa_mgmtdev_register(struct vdpa_mgmt_dev * mdev)33733b34750SParav Pandit int vdpa_mgmtdev_register(struct vdpa_mgmt_dev *mdev)
33833b34750SParav Pandit {
33933b34750SParav Pandit 	if (!mdev->device || !mdev->ops || !mdev->ops->dev_add || !mdev->ops->dev_del)
34033b34750SParav Pandit 		return -EINVAL;
34133b34750SParav Pandit 
34233b34750SParav Pandit 	INIT_LIST_HEAD(&mdev->list);
3430078ad90SEli Cohen 	down_write(&vdpa_dev_lock);
34433b34750SParav Pandit 	list_add_tail(&mdev->list, &mdev_head);
3450078ad90SEli Cohen 	up_write(&vdpa_dev_lock);
34633b34750SParav Pandit 	return 0;
34733b34750SParav Pandit }
34833b34750SParav Pandit EXPORT_SYMBOL_GPL(vdpa_mgmtdev_register);
34933b34750SParav Pandit 
vdpa_match_remove(struct device * dev,void * data)350903f7bcaSParav Pandit static int vdpa_match_remove(struct device *dev, void *data)
351903f7bcaSParav Pandit {
352903f7bcaSParav Pandit 	struct vdpa_device *vdev = container_of(dev, struct vdpa_device, dev);
353903f7bcaSParav Pandit 	struct vdpa_mgmt_dev *mdev = vdev->mdev;
354903f7bcaSParav Pandit 
355903f7bcaSParav Pandit 	if (mdev == data)
356903f7bcaSParav Pandit 		mdev->ops->dev_del(mdev, vdev);
357903f7bcaSParav Pandit 	return 0;
358903f7bcaSParav Pandit }
359903f7bcaSParav Pandit 
vdpa_mgmtdev_unregister(struct vdpa_mgmt_dev * mdev)36033b34750SParav Pandit void vdpa_mgmtdev_unregister(struct vdpa_mgmt_dev *mdev)
36133b34750SParav Pandit {
3620078ad90SEli Cohen 	down_write(&vdpa_dev_lock);
363903f7bcaSParav Pandit 
36433b34750SParav Pandit 	list_del(&mdev->list);
365903f7bcaSParav Pandit 
366903f7bcaSParav Pandit 	/* Filter out all the entries belong to this management device and delete it. */
367903f7bcaSParav Pandit 	bus_for_each_dev(&vdpa_bus, NULL, mdev, vdpa_match_remove);
368903f7bcaSParav Pandit 
3690078ad90SEli Cohen 	up_write(&vdpa_dev_lock);
37033b34750SParav Pandit }
37133b34750SParav Pandit EXPORT_SYMBOL_GPL(vdpa_mgmtdev_unregister);
37233b34750SParav Pandit 
vdpa_get_config_unlocked(struct vdpa_device * vdev,unsigned int offset,void * buf,unsigned int len)37330ef7a8aSEli Cohen static void vdpa_get_config_unlocked(struct vdpa_device *vdev,
37430ef7a8aSEli Cohen 				     unsigned int offset,
37530ef7a8aSEli Cohen 				     void *buf, unsigned int len)
37630ef7a8aSEli Cohen {
37730ef7a8aSEli Cohen 	const struct vdpa_config_ops *ops = vdev->config;
37830ef7a8aSEli Cohen 
37930ef7a8aSEli Cohen 	/*
38030ef7a8aSEli Cohen 	 * Config accesses aren't supposed to trigger before features are set.
38130ef7a8aSEli Cohen 	 * If it does happen we assume a legacy guest.
38230ef7a8aSEli Cohen 	 */
38330ef7a8aSEli Cohen 	if (!vdev->features_valid)
384e0077cc1SSi-Wei Liu 		vdpa_set_features_unlocked(vdev, 0);
38530ef7a8aSEli Cohen 	ops->get_config(vdev, offset, buf, len);
38630ef7a8aSEli Cohen }
38730ef7a8aSEli Cohen 
3886dbb1f16SParav Pandit /**
3896dbb1f16SParav Pandit  * vdpa_get_config - Get one or more device configuration fields.
3906dbb1f16SParav Pandit  * @vdev: vdpa device to operate on
3916dbb1f16SParav Pandit  * @offset: starting byte offset of the field
3926dbb1f16SParav Pandit  * @buf: buffer pointer to read to
3936dbb1f16SParav Pandit  * @len: length of the configuration fields in bytes
3946dbb1f16SParav Pandit  */
vdpa_get_config(struct vdpa_device * vdev,unsigned int offset,void * buf,unsigned int len)3956dbb1f16SParav Pandit void vdpa_get_config(struct vdpa_device *vdev, unsigned int offset,
3966dbb1f16SParav Pandit 		     void *buf, unsigned int len)
3976dbb1f16SParav Pandit {
398a6a51adcSEli Cohen 	down_read(&vdev->cf_lock);
39930ef7a8aSEli Cohen 	vdpa_get_config_unlocked(vdev, offset, buf, len);
400a6a51adcSEli Cohen 	up_read(&vdev->cf_lock);
4016dbb1f16SParav Pandit }
4026dbb1f16SParav Pandit EXPORT_SYMBOL_GPL(vdpa_get_config);
4036dbb1f16SParav Pandit 
4046dbb1f16SParav Pandit /**
4056dbb1f16SParav Pandit  * vdpa_set_config - Set one or more device configuration fields.
4066dbb1f16SParav Pandit  * @vdev: vdpa device to operate on
4076dbb1f16SParav Pandit  * @offset: starting byte offset of the field
4086dbb1f16SParav Pandit  * @buf: buffer pointer to read from
4096dbb1f16SParav Pandit  * @length: length of the configuration fields in bytes
4106dbb1f16SParav Pandit  */
vdpa_set_config(struct vdpa_device * vdev,unsigned int offset,const void * buf,unsigned int length)4116dbb1f16SParav Pandit void vdpa_set_config(struct vdpa_device *vdev, unsigned int offset,
4126dbb1f16SParav Pandit 		     const void *buf, unsigned int length)
4136dbb1f16SParav Pandit {
414a6a51adcSEli Cohen 	down_write(&vdev->cf_lock);
4156dbb1f16SParav Pandit 	vdev->config->set_config(vdev, offset, buf, length);
416a6a51adcSEli Cohen 	up_write(&vdev->cf_lock);
4176dbb1f16SParav Pandit }
4186dbb1f16SParav Pandit EXPORT_SYMBOL_GPL(vdpa_set_config);
4196dbb1f16SParav Pandit 
mgmtdev_handle_match(const struct vdpa_mgmt_dev * mdev,const char * busname,const char * devname)42033b34750SParav Pandit static bool mgmtdev_handle_match(const struct vdpa_mgmt_dev *mdev,
42133b34750SParav Pandit 				 const char *busname, const char *devname)
42233b34750SParav Pandit {
42333b34750SParav Pandit 	/* Bus name is optional for simulated management device, so ignore the
42433b34750SParav Pandit 	 * device with bus if bus attribute is provided.
42533b34750SParav Pandit 	 */
42633b34750SParav Pandit 	if ((busname && !mdev->device->bus) || (!busname && mdev->device->bus))
42733b34750SParav Pandit 		return false;
42833b34750SParav Pandit 
42933b34750SParav Pandit 	if (!busname && strcmp(dev_name(mdev->device), devname) == 0)
43033b34750SParav Pandit 		return true;
43133b34750SParav Pandit 
43233b34750SParav Pandit 	if (busname && (strcmp(mdev->device->bus->name, busname) == 0) &&
43333b34750SParav Pandit 	    (strcmp(dev_name(mdev->device), devname) == 0))
43433b34750SParav Pandit 		return true;
43533b34750SParav Pandit 
43633b34750SParav Pandit 	return false;
43733b34750SParav Pandit }
43833b34750SParav Pandit 
vdpa_mgmtdev_get_from_attr(struct nlattr ** attrs)43933b34750SParav Pandit static struct vdpa_mgmt_dev *vdpa_mgmtdev_get_from_attr(struct nlattr **attrs)
44033b34750SParav Pandit {
44133b34750SParav Pandit 	struct vdpa_mgmt_dev *mdev;
44233b34750SParav Pandit 	const char *busname = NULL;
44333b34750SParav Pandit 	const char *devname;
44433b34750SParav Pandit 
44533b34750SParav Pandit 	if (!attrs[VDPA_ATTR_MGMTDEV_DEV_NAME])
44633b34750SParav Pandit 		return ERR_PTR(-EINVAL);
44733b34750SParav Pandit 	devname = nla_data(attrs[VDPA_ATTR_MGMTDEV_DEV_NAME]);
44833b34750SParav Pandit 	if (attrs[VDPA_ATTR_MGMTDEV_BUS_NAME])
44933b34750SParav Pandit 		busname = nla_data(attrs[VDPA_ATTR_MGMTDEV_BUS_NAME]);
45033b34750SParav Pandit 
45133b34750SParav Pandit 	list_for_each_entry(mdev, &mdev_head, list) {
45233b34750SParav Pandit 		if (mgmtdev_handle_match(mdev, busname, devname))
45333b34750SParav Pandit 			return mdev;
45433b34750SParav Pandit 	}
45533b34750SParav Pandit 	return ERR_PTR(-ENODEV);
45633b34750SParav Pandit }
45733b34750SParav Pandit 
vdpa_nl_mgmtdev_handle_fill(struct sk_buff * msg,const struct vdpa_mgmt_dev * mdev)45833b34750SParav Pandit static int vdpa_nl_mgmtdev_handle_fill(struct sk_buff *msg, const struct vdpa_mgmt_dev *mdev)
45933b34750SParav Pandit {
46033b34750SParav Pandit 	if (mdev->device->bus &&
46133b34750SParav Pandit 	    nla_put_string(msg, VDPA_ATTR_MGMTDEV_BUS_NAME, mdev->device->bus->name))
46233b34750SParav Pandit 		return -EMSGSIZE;
46333b34750SParav Pandit 	if (nla_put_string(msg, VDPA_ATTR_MGMTDEV_DEV_NAME, dev_name(mdev->device)))
46433b34750SParav Pandit 		return -EMSGSIZE;
46533b34750SParav Pandit 	return 0;
46633b34750SParav Pandit }
46733b34750SParav Pandit 
vdpa_mgmtdev_get_classes(const struct vdpa_mgmt_dev * mdev,unsigned int * nclasses)468dbb6f1c4SSi-Wei Liu static u64 vdpa_mgmtdev_get_classes(const struct vdpa_mgmt_dev *mdev,
469dbb6f1c4SSi-Wei Liu 				    unsigned int *nclasses)
470dbb6f1c4SSi-Wei Liu {
471dbb6f1c4SSi-Wei Liu 	u64 supported_classes = 0;
472dbb6f1c4SSi-Wei Liu 	unsigned int n = 0;
473dbb6f1c4SSi-Wei Liu 
474dbb6f1c4SSi-Wei Liu 	for (int i = 0; mdev->id_table[i].device; i++) {
475dbb6f1c4SSi-Wei Liu 		if (mdev->id_table[i].device > 63)
476dbb6f1c4SSi-Wei Liu 			continue;
477dbb6f1c4SSi-Wei Liu 		supported_classes |= BIT_ULL(mdev->id_table[i].device);
478dbb6f1c4SSi-Wei Liu 		n++;
479dbb6f1c4SSi-Wei Liu 	}
480dbb6f1c4SSi-Wei Liu 	if (nclasses)
481dbb6f1c4SSi-Wei Liu 		*nclasses = n;
482dbb6f1c4SSi-Wei Liu 
483dbb6f1c4SSi-Wei Liu 	return supported_classes;
484dbb6f1c4SSi-Wei Liu }
485dbb6f1c4SSi-Wei Liu 
vdpa_mgmtdev_fill(const struct vdpa_mgmt_dev * mdev,struct sk_buff * msg,u32 portid,u32 seq,int flags)48633b34750SParav Pandit static int vdpa_mgmtdev_fill(const struct vdpa_mgmt_dev *mdev, struct sk_buff *msg,
48733b34750SParav Pandit 			     u32 portid, u32 seq, int flags)
48833b34750SParav Pandit {
48933b34750SParav Pandit 	void *hdr;
49033b34750SParav Pandit 	int err;
49133b34750SParav Pandit 
49233b34750SParav Pandit 	hdr = genlmsg_put(msg, portid, seq, &vdpa_nl_family, flags, VDPA_CMD_MGMTDEV_NEW);
49333b34750SParav Pandit 	if (!hdr)
49433b34750SParav Pandit 		return -EMSGSIZE;
49533b34750SParav Pandit 	err = vdpa_nl_mgmtdev_handle_fill(msg, mdev);
49633b34750SParav Pandit 	if (err)
49733b34750SParav Pandit 		goto msg_err;
49833b34750SParav Pandit 
49933b34750SParav Pandit 	if (nla_put_u64_64bit(msg, VDPA_ATTR_MGMTDEV_SUPPORTED_CLASSES,
500dbb6f1c4SSi-Wei Liu 			      vdpa_mgmtdev_get_classes(mdev, NULL),
501dbb6f1c4SSi-Wei Liu 			      VDPA_ATTR_UNSPEC)) {
50233b34750SParav Pandit 		err = -EMSGSIZE;
50333b34750SParav Pandit 		goto msg_err;
50433b34750SParav Pandit 	}
505cd2629f6SEli Cohen 	if (nla_put_u32(msg, VDPA_ATTR_DEV_MGMTDEV_MAX_VQS,
506cd2629f6SEli Cohen 			mdev->max_supported_vqs)) {
507cd2629f6SEli Cohen 		err = -EMSGSIZE;
508cd2629f6SEli Cohen 		goto msg_err;
509cd2629f6SEli Cohen 	}
510cd2629f6SEli Cohen 	if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_SUPPORTED_FEATURES,
511cd2629f6SEli Cohen 			      mdev->supported_features, VDPA_ATTR_PAD)) {
512cd2629f6SEli Cohen 		err = -EMSGSIZE;
513cd2629f6SEli Cohen 		goto msg_err;
514cd2629f6SEli Cohen 	}
51533b34750SParav Pandit 
51633b34750SParav Pandit 	genlmsg_end(msg, hdr);
51733b34750SParav Pandit 	return 0;
51833b34750SParav Pandit 
51933b34750SParav Pandit msg_err:
52033b34750SParav Pandit 	genlmsg_cancel(msg, hdr);
52133b34750SParav Pandit 	return err;
52233b34750SParav Pandit }
52333b34750SParav Pandit 
vdpa_nl_cmd_mgmtdev_get_doit(struct sk_buff * skb,struct genl_info * info)52433b34750SParav Pandit static int vdpa_nl_cmd_mgmtdev_get_doit(struct sk_buff *skb, struct genl_info *info)
52533b34750SParav Pandit {
52633b34750SParav Pandit 	struct vdpa_mgmt_dev *mdev;
52733b34750SParav Pandit 	struct sk_buff *msg;
52833b34750SParav Pandit 	int err;
52933b34750SParav Pandit 
53033b34750SParav Pandit 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
53133b34750SParav Pandit 	if (!msg)
53233b34750SParav Pandit 		return -ENOMEM;
53333b34750SParav Pandit 
5340078ad90SEli Cohen 	down_read(&vdpa_dev_lock);
53533b34750SParav Pandit 	mdev = vdpa_mgmtdev_get_from_attr(info->attrs);
53633b34750SParav Pandit 	if (IS_ERR(mdev)) {
5370078ad90SEli Cohen 		up_read(&vdpa_dev_lock);
53833b34750SParav Pandit 		NL_SET_ERR_MSG_MOD(info->extack, "Fail to find the specified mgmt device");
53933b34750SParav Pandit 		err = PTR_ERR(mdev);
54033b34750SParav Pandit 		goto out;
54133b34750SParav Pandit 	}
54233b34750SParav Pandit 
54333b34750SParav Pandit 	err = vdpa_mgmtdev_fill(mdev, msg, info->snd_portid, info->snd_seq, 0);
5440078ad90SEli Cohen 	up_read(&vdpa_dev_lock);
54533b34750SParav Pandit 	if (err)
54633b34750SParav Pandit 		goto out;
54733b34750SParav Pandit 	err = genlmsg_reply(msg, info);
54833b34750SParav Pandit 	return err;
54933b34750SParav Pandit 
55033b34750SParav Pandit out:
55133b34750SParav Pandit 	nlmsg_free(msg);
55233b34750SParav Pandit 	return err;
55333b34750SParav Pandit }
55433b34750SParav Pandit 
55533b34750SParav Pandit static int
vdpa_nl_cmd_mgmtdev_get_dumpit(struct sk_buff * msg,struct netlink_callback * cb)55633b34750SParav Pandit vdpa_nl_cmd_mgmtdev_get_dumpit(struct sk_buff *msg, struct netlink_callback *cb)
55733b34750SParav Pandit {
55833b34750SParav Pandit 	struct vdpa_mgmt_dev *mdev;
55933b34750SParav Pandit 	int start = cb->args[0];
56033b34750SParav Pandit 	int idx = 0;
56133b34750SParav Pandit 	int err;
56233b34750SParav Pandit 
5630078ad90SEli Cohen 	down_read(&vdpa_dev_lock);
56433b34750SParav Pandit 	list_for_each_entry(mdev, &mdev_head, list) {
56533b34750SParav Pandit 		if (idx < start) {
56633b34750SParav Pandit 			idx++;
56733b34750SParav Pandit 			continue;
56833b34750SParav Pandit 		}
56933b34750SParav Pandit 		err = vdpa_mgmtdev_fill(mdev, msg, NETLINK_CB(cb->skb).portid,
57033b34750SParav Pandit 					cb->nlh->nlmsg_seq, NLM_F_MULTI);
57133b34750SParav Pandit 		if (err)
57233b34750SParav Pandit 			goto out;
57333b34750SParav Pandit 		idx++;
57433b34750SParav Pandit 	}
57533b34750SParav Pandit out:
5760078ad90SEli Cohen 	up_read(&vdpa_dev_lock);
57733b34750SParav Pandit 	cb->args[0] = idx;
57833b34750SParav Pandit 	return msg->len;
57933b34750SParav Pandit }
58033b34750SParav Pandit 
58147a1401aSEli Cohen #define VDPA_DEV_NET_ATTRS_MASK (BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MACADDR) | \
58247a1401aSEli Cohen 				 BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MTU)     | \
58347a1401aSEli Cohen 				 BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MAX_VQP))
584d8ca2fa5SParav Pandit 
585dbb6f1c4SSi-Wei Liu /*
586dbb6f1c4SSi-Wei Liu  * Bitmask for all per-device features: feature bits VIRTIO_TRANSPORT_F_START
587dbb6f1c4SSi-Wei Liu  * through VIRTIO_TRANSPORT_F_END are unset, i.e. 0xfffffc000fffffff for
588dbb6f1c4SSi-Wei Liu  * all 64bit features. If the features are extended beyond 64 bits, or new
589dbb6f1c4SSi-Wei Liu  * "holes" are reserved for other type of features than per-device, this
590dbb6f1c4SSi-Wei Liu  * macro would have to be updated.
591dbb6f1c4SSi-Wei Liu  */
592dbb6f1c4SSi-Wei Liu #define VIRTIO_DEVICE_F_MASK (~0ULL << (VIRTIO_TRANSPORT_F_END + 1) | \
593dbb6f1c4SSi-Wei Liu 			      ((1ULL << VIRTIO_TRANSPORT_F_START) - 1))
594dbb6f1c4SSi-Wei Liu 
vdpa_nl_cmd_dev_add_set_doit(struct sk_buff * skb,struct genl_info * info)595903f7bcaSParav Pandit static int vdpa_nl_cmd_dev_add_set_doit(struct sk_buff *skb, struct genl_info *info)
596903f7bcaSParav Pandit {
597d8ca2fa5SParav Pandit 	struct vdpa_dev_set_config config = {};
598d8ca2fa5SParav Pandit 	struct nlattr **nl_attrs = info->attrs;
599903f7bcaSParav Pandit 	struct vdpa_mgmt_dev *mdev;
600dbb6f1c4SSi-Wei Liu 	unsigned int ncls = 0;
601d8ca2fa5SParav Pandit 	const u8 *macaddr;
602903f7bcaSParav Pandit 	const char *name;
603dbb6f1c4SSi-Wei Liu 	u64 classes;
604903f7bcaSParav Pandit 	int err = 0;
605903f7bcaSParav Pandit 
606903f7bcaSParav Pandit 	if (!info->attrs[VDPA_ATTR_DEV_NAME])
607903f7bcaSParav Pandit 		return -EINVAL;
608903f7bcaSParav Pandit 
609903f7bcaSParav Pandit 	name = nla_data(info->attrs[VDPA_ATTR_DEV_NAME]);
610903f7bcaSParav Pandit 
611d8ca2fa5SParav Pandit 	if (nl_attrs[VDPA_ATTR_DEV_NET_CFG_MACADDR]) {
612d8ca2fa5SParav Pandit 		macaddr = nla_data(nl_attrs[VDPA_ATTR_DEV_NET_CFG_MACADDR]);
613d8ca2fa5SParav Pandit 		memcpy(config.net.mac, macaddr, sizeof(config.net.mac));
61447a1401aSEli Cohen 		config.mask |= BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MACADDR);
615d8ca2fa5SParav Pandit 	}
616d8ca2fa5SParav Pandit 	if (nl_attrs[VDPA_ATTR_DEV_NET_CFG_MTU]) {
617d8ca2fa5SParav Pandit 		config.net.mtu =
618d8ca2fa5SParav Pandit 			nla_get_u16(nl_attrs[VDPA_ATTR_DEV_NET_CFG_MTU]);
61947a1401aSEli Cohen 		config.mask |= BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MTU);
620d8ca2fa5SParav Pandit 	}
621aba21affSEli Cohen 	if (nl_attrs[VDPA_ATTR_DEV_NET_CFG_MAX_VQP]) {
622aba21affSEli Cohen 		config.net.max_vq_pairs =
623aba21affSEli Cohen 			nla_get_u16(nl_attrs[VDPA_ATTR_DEV_NET_CFG_MAX_VQP]);
624aba21affSEli Cohen 		if (!config.net.max_vq_pairs) {
625aba21affSEli Cohen 			NL_SET_ERR_MSG_MOD(info->extack,
626aba21affSEli Cohen 					   "At least one pair of VQs is required");
627aba21affSEli Cohen 			return -EINVAL;
628aba21affSEli Cohen 		}
629aba21affSEli Cohen 		config.mask |= BIT_ULL(VDPA_ATTR_DEV_NET_CFG_MAX_VQP);
630aba21affSEli Cohen 	}
63190fea5a8SJason Wang 	if (nl_attrs[VDPA_ATTR_DEV_FEATURES]) {
632e7d09cd1SSi-Wei Liu 		u64 missing = 0x0ULL;
633e7d09cd1SSi-Wei Liu 
63490fea5a8SJason Wang 		config.device_features =
63590fea5a8SJason Wang 			nla_get_u64(nl_attrs[VDPA_ATTR_DEV_FEATURES]);
636e7d09cd1SSi-Wei Liu 		if (nl_attrs[VDPA_ATTR_DEV_NET_CFG_MACADDR] &&
637e7d09cd1SSi-Wei Liu 		    !(config.device_features & BIT_ULL(VIRTIO_NET_F_MAC)))
638e7d09cd1SSi-Wei Liu 			missing |= BIT_ULL(VIRTIO_NET_F_MAC);
639e7d09cd1SSi-Wei Liu 		if (nl_attrs[VDPA_ATTR_DEV_NET_CFG_MTU] &&
640e7d09cd1SSi-Wei Liu 		    !(config.device_features & BIT_ULL(VIRTIO_NET_F_MTU)))
641e7d09cd1SSi-Wei Liu 			missing |= BIT_ULL(VIRTIO_NET_F_MTU);
642e7d09cd1SSi-Wei Liu 		if (nl_attrs[VDPA_ATTR_DEV_NET_CFG_MAX_VQP] &&
643e7d09cd1SSi-Wei Liu 		    config.net.max_vq_pairs > 1 &&
644e7d09cd1SSi-Wei Liu 		    !(config.device_features & BIT_ULL(VIRTIO_NET_F_MQ)))
645e7d09cd1SSi-Wei Liu 			missing |= BIT_ULL(VIRTIO_NET_F_MQ);
646e7d09cd1SSi-Wei Liu 		if (missing) {
647e7d09cd1SSi-Wei Liu 			NL_SET_ERR_MSG_FMT_MOD(info->extack,
648e7d09cd1SSi-Wei Liu 					       "Missing features 0x%llx for provided attributes",
649e7d09cd1SSi-Wei Liu 					       missing);
650e7d09cd1SSi-Wei Liu 			return -EINVAL;
651e7d09cd1SSi-Wei Liu 		}
65290fea5a8SJason Wang 		config.mask |= BIT_ULL(VDPA_ATTR_DEV_FEATURES);
65390fea5a8SJason Wang 	}
654d8ca2fa5SParav Pandit 
655d8ca2fa5SParav Pandit 	/* Skip checking capability if user didn't prefer to configure any
656d8ca2fa5SParav Pandit 	 * device networking attributes. It is likely that user might have used
657d8ca2fa5SParav Pandit 	 * a device specific method to configure such attributes or using device
658d8ca2fa5SParav Pandit 	 * default attributes.
659d8ca2fa5SParav Pandit 	 */
660d8ca2fa5SParav Pandit 	if ((config.mask & VDPA_DEV_NET_ATTRS_MASK) &&
661d8ca2fa5SParav Pandit 	    !netlink_capable(skb, CAP_NET_ADMIN))
662d8ca2fa5SParav Pandit 		return -EPERM;
663d8ca2fa5SParav Pandit 
6640078ad90SEli Cohen 	down_write(&vdpa_dev_lock);
665903f7bcaSParav Pandit 	mdev = vdpa_mgmtdev_get_from_attr(info->attrs);
666903f7bcaSParav Pandit 	if (IS_ERR(mdev)) {
667903f7bcaSParav Pandit 		NL_SET_ERR_MSG_MOD(info->extack, "Fail to find the specified management device");
668903f7bcaSParav Pandit 		err = PTR_ERR(mdev);
669903f7bcaSParav Pandit 		goto err;
670903f7bcaSParav Pandit 	}
671275487b4SSi-Wei Liu 
672d8ca2fa5SParav Pandit 	if ((config.mask & mdev->config_attr_mask) != config.mask) {
673275487b4SSi-Wei Liu 		NL_SET_ERR_MSG_FMT_MOD(info->extack,
674275487b4SSi-Wei Liu 				       "Some provided attributes are not supported: 0x%llx",
675275487b4SSi-Wei Liu 				       config.mask & ~mdev->config_attr_mask);
676d8ca2fa5SParav Pandit 		err = -EOPNOTSUPP;
677d8ca2fa5SParav Pandit 		goto err;
678d8ca2fa5SParav Pandit 	}
679903f7bcaSParav Pandit 
680dbb6f1c4SSi-Wei Liu 	classes = vdpa_mgmtdev_get_classes(mdev, &ncls);
681dbb6f1c4SSi-Wei Liu 	if (config.mask & VDPA_DEV_NET_ATTRS_MASK &&
682dbb6f1c4SSi-Wei Liu 	    !(classes & BIT_ULL(VIRTIO_ID_NET))) {
683dbb6f1c4SSi-Wei Liu 		NL_SET_ERR_MSG_MOD(info->extack,
684dbb6f1c4SSi-Wei Liu 				   "Network class attributes provided on unsupported management device");
685dbb6f1c4SSi-Wei Liu 		err = -EINVAL;
686dbb6f1c4SSi-Wei Liu 		goto err;
687dbb6f1c4SSi-Wei Liu 	}
688dbb6f1c4SSi-Wei Liu 	if (!(config.mask & VDPA_DEV_NET_ATTRS_MASK) &&
689dbb6f1c4SSi-Wei Liu 	    config.mask & BIT_ULL(VDPA_ATTR_DEV_FEATURES) &&
690dbb6f1c4SSi-Wei Liu 	    classes & BIT_ULL(VIRTIO_ID_NET) && ncls > 1 &&
691dbb6f1c4SSi-Wei Liu 	    config.device_features & VIRTIO_DEVICE_F_MASK) {
692dbb6f1c4SSi-Wei Liu 		NL_SET_ERR_MSG_MOD(info->extack,
693dbb6f1c4SSi-Wei Liu 				   "Management device supports multi-class while device features specified are ambiguous");
694dbb6f1c4SSi-Wei Liu 		err = -EINVAL;
695dbb6f1c4SSi-Wei Liu 		goto err;
696dbb6f1c4SSi-Wei Liu 	}
697dbb6f1c4SSi-Wei Liu 
698d8ca2fa5SParav Pandit 	err = mdev->ops->dev_add(mdev, name, &config);
699903f7bcaSParav Pandit err:
7000078ad90SEli Cohen 	up_write(&vdpa_dev_lock);
701903f7bcaSParav Pandit 	return err;
702903f7bcaSParav Pandit }
703903f7bcaSParav Pandit 
vdpa_nl_cmd_dev_del_set_doit(struct sk_buff * skb,struct genl_info * info)704903f7bcaSParav Pandit static int vdpa_nl_cmd_dev_del_set_doit(struct sk_buff *skb, struct genl_info *info)
705903f7bcaSParav Pandit {
706903f7bcaSParav Pandit 	struct vdpa_mgmt_dev *mdev;
707903f7bcaSParav Pandit 	struct vdpa_device *vdev;
708903f7bcaSParav Pandit 	struct device *dev;
709903f7bcaSParav Pandit 	const char *name;
710903f7bcaSParav Pandit 	int err = 0;
711903f7bcaSParav Pandit 
712903f7bcaSParav Pandit 	if (!info->attrs[VDPA_ATTR_DEV_NAME])
713903f7bcaSParav Pandit 		return -EINVAL;
714903f7bcaSParav Pandit 	name = nla_data(info->attrs[VDPA_ATTR_DEV_NAME]);
715903f7bcaSParav Pandit 
7160078ad90SEli Cohen 	down_write(&vdpa_dev_lock);
717903f7bcaSParav Pandit 	dev = bus_find_device(&vdpa_bus, NULL, name, vdpa_name_match);
718903f7bcaSParav Pandit 	if (!dev) {
719903f7bcaSParav Pandit 		NL_SET_ERR_MSG_MOD(info->extack, "device not found");
720903f7bcaSParav Pandit 		err = -ENODEV;
721903f7bcaSParav Pandit 		goto dev_err;
722903f7bcaSParav Pandit 	}
723903f7bcaSParav Pandit 	vdev = container_of(dev, struct vdpa_device, dev);
724903f7bcaSParav Pandit 	if (!vdev->mdev) {
725903f7bcaSParav Pandit 		NL_SET_ERR_MSG_MOD(info->extack, "Only user created device can be deleted by user");
726903f7bcaSParav Pandit 		err = -EINVAL;
727903f7bcaSParav Pandit 		goto mdev_err;
728903f7bcaSParav Pandit 	}
729903f7bcaSParav Pandit 	mdev = vdev->mdev;
730903f7bcaSParav Pandit 	mdev->ops->dev_del(mdev, vdev);
731903f7bcaSParav Pandit mdev_err:
732903f7bcaSParav Pandit 	put_device(dev);
733903f7bcaSParav Pandit dev_err:
7340078ad90SEli Cohen 	up_write(&vdpa_dev_lock);
735903f7bcaSParav Pandit 	return err;
736903f7bcaSParav Pandit }
737903f7bcaSParav Pandit 
738bc0d90eeSParav Pandit static int
vdpa_dev_fill(struct vdpa_device * vdev,struct sk_buff * msg,u32 portid,u32 seq,int flags,struct netlink_ext_ack * extack)739bc0d90eeSParav Pandit vdpa_dev_fill(struct vdpa_device *vdev, struct sk_buff *msg, u32 portid, u32 seq,
740bc0d90eeSParav Pandit 	      int flags, struct netlink_ext_ack *extack)
741bc0d90eeSParav Pandit {
742bc0d90eeSParav Pandit 	u16 max_vq_size;
743e47be840SWu Zongyong 	u16 min_vq_size = 1;
744bc0d90eeSParav Pandit 	u32 device_id;
745bc0d90eeSParav Pandit 	u32 vendor_id;
746bc0d90eeSParav Pandit 	void *hdr;
747bc0d90eeSParav Pandit 	int err;
748bc0d90eeSParav Pandit 
749bc0d90eeSParav Pandit 	hdr = genlmsg_put(msg, portid, seq, &vdpa_nl_family, flags, VDPA_CMD_DEV_NEW);
750bc0d90eeSParav Pandit 	if (!hdr)
751bc0d90eeSParav Pandit 		return -EMSGSIZE;
752bc0d90eeSParav Pandit 
753bc0d90eeSParav Pandit 	err = vdpa_nl_mgmtdev_handle_fill(msg, vdev->mdev);
754bc0d90eeSParav Pandit 	if (err)
755bc0d90eeSParav Pandit 		goto msg_err;
756bc0d90eeSParav Pandit 
757bc0d90eeSParav Pandit 	device_id = vdev->config->get_device_id(vdev);
758bc0d90eeSParav Pandit 	vendor_id = vdev->config->get_vendor_id(vdev);
759bc0d90eeSParav Pandit 	max_vq_size = vdev->config->get_vq_num_max(vdev);
760e47be840SWu Zongyong 	if (vdev->config->get_vq_num_min)
761e47be840SWu Zongyong 		min_vq_size = vdev->config->get_vq_num_min(vdev);
762bc0d90eeSParav Pandit 
763bc0d90eeSParav Pandit 	err = -EMSGSIZE;
764bc0d90eeSParav Pandit 	if (nla_put_string(msg, VDPA_ATTR_DEV_NAME, dev_name(&vdev->dev)))
765bc0d90eeSParav Pandit 		goto msg_err;
766bc0d90eeSParav Pandit 	if (nla_put_u32(msg, VDPA_ATTR_DEV_ID, device_id))
767bc0d90eeSParav Pandit 		goto msg_err;
768bc0d90eeSParav Pandit 	if (nla_put_u32(msg, VDPA_ATTR_DEV_VENDOR_ID, vendor_id))
769bc0d90eeSParav Pandit 		goto msg_err;
770bc0d90eeSParav Pandit 	if (nla_put_u32(msg, VDPA_ATTR_DEV_MAX_VQS, vdev->nvqs))
771bc0d90eeSParav Pandit 		goto msg_err;
772bc0d90eeSParav Pandit 	if (nla_put_u16(msg, VDPA_ATTR_DEV_MAX_VQ_SIZE, max_vq_size))
773bc0d90eeSParav Pandit 		goto msg_err;
774e47be840SWu Zongyong 	if (nla_put_u16(msg, VDPA_ATTR_DEV_MIN_VQ_SIZE, min_vq_size))
775e47be840SWu Zongyong 		goto msg_err;
776bc0d90eeSParav Pandit 
777bc0d90eeSParav Pandit 	genlmsg_end(msg, hdr);
778bc0d90eeSParav Pandit 	return 0;
779bc0d90eeSParav Pandit 
780bc0d90eeSParav Pandit msg_err:
781bc0d90eeSParav Pandit 	genlmsg_cancel(msg, hdr);
782bc0d90eeSParav Pandit 	return err;
783bc0d90eeSParav Pandit }
784bc0d90eeSParav Pandit 
vdpa_nl_cmd_dev_get_doit(struct sk_buff * skb,struct genl_info * info)785bc0d90eeSParav Pandit static int vdpa_nl_cmd_dev_get_doit(struct sk_buff *skb, struct genl_info *info)
786bc0d90eeSParav Pandit {
787bc0d90eeSParav Pandit 	struct vdpa_device *vdev;
788bc0d90eeSParav Pandit 	struct sk_buff *msg;
789bc0d90eeSParav Pandit 	const char *devname;
790bc0d90eeSParav Pandit 	struct device *dev;
791bc0d90eeSParav Pandit 	int err;
792bc0d90eeSParav Pandit 
793bc0d90eeSParav Pandit 	if (!info->attrs[VDPA_ATTR_DEV_NAME])
794bc0d90eeSParav Pandit 		return -EINVAL;
795bc0d90eeSParav Pandit 	devname = nla_data(info->attrs[VDPA_ATTR_DEV_NAME]);
796bc0d90eeSParav Pandit 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
797bc0d90eeSParav Pandit 	if (!msg)
798bc0d90eeSParav Pandit 		return -ENOMEM;
799bc0d90eeSParav Pandit 
8000078ad90SEli Cohen 	down_read(&vdpa_dev_lock);
801bc0d90eeSParav Pandit 	dev = bus_find_device(&vdpa_bus, NULL, devname, vdpa_name_match);
802bc0d90eeSParav Pandit 	if (!dev) {
803bc0d90eeSParav Pandit 		NL_SET_ERR_MSG_MOD(info->extack, "device not found");
804bc0d90eeSParav Pandit 		err = -ENODEV;
805bc0d90eeSParav Pandit 		goto err;
806bc0d90eeSParav Pandit 	}
807bc0d90eeSParav Pandit 	vdev = container_of(dev, struct vdpa_device, dev);
808bc0d90eeSParav Pandit 	if (!vdev->mdev) {
809bc0d90eeSParav Pandit 		err = -EINVAL;
810bc0d90eeSParav Pandit 		goto mdev_err;
811bc0d90eeSParav Pandit 	}
812bc0d90eeSParav Pandit 	err = vdpa_dev_fill(vdev, msg, info->snd_portid, info->snd_seq, 0, info->extack);
8137a6691f1SEli Cohen 	if (err)
8147a6691f1SEli Cohen 		goto mdev_err;
8157a6691f1SEli Cohen 
816bc0d90eeSParav Pandit 	err = genlmsg_reply(msg, info);
8177a6691f1SEli Cohen 	put_device(dev);
8180078ad90SEli Cohen 	up_read(&vdpa_dev_lock);
8197a6691f1SEli Cohen 	return err;
8207a6691f1SEli Cohen 
821bc0d90eeSParav Pandit mdev_err:
822bc0d90eeSParav Pandit 	put_device(dev);
823bc0d90eeSParav Pandit err:
8240078ad90SEli Cohen 	up_read(&vdpa_dev_lock);
825bc0d90eeSParav Pandit 	nlmsg_free(msg);
826bc0d90eeSParav Pandit 	return err;
827bc0d90eeSParav Pandit }
828bc0d90eeSParav Pandit 
829bc0d90eeSParav Pandit struct vdpa_dev_dump_info {
830bc0d90eeSParav Pandit 	struct sk_buff *msg;
831bc0d90eeSParav Pandit 	struct netlink_callback *cb;
832bc0d90eeSParav Pandit 	int start_idx;
833bc0d90eeSParav Pandit 	int idx;
834bc0d90eeSParav Pandit };
835bc0d90eeSParav Pandit 
vdpa_dev_dump(struct device * dev,void * data)836bc0d90eeSParav Pandit static int vdpa_dev_dump(struct device *dev, void *data)
837bc0d90eeSParav Pandit {
838bc0d90eeSParav Pandit 	struct vdpa_device *vdev = container_of(dev, struct vdpa_device, dev);
839bc0d90eeSParav Pandit 	struct vdpa_dev_dump_info *info = data;
840bc0d90eeSParav Pandit 	int err;
841bc0d90eeSParav Pandit 
842bc0d90eeSParav Pandit 	if (!vdev->mdev)
843bc0d90eeSParav Pandit 		return 0;
844bc0d90eeSParav Pandit 	if (info->idx < info->start_idx) {
845bc0d90eeSParav Pandit 		info->idx++;
846bc0d90eeSParav Pandit 		return 0;
847bc0d90eeSParav Pandit 	}
848bc0d90eeSParav Pandit 	err = vdpa_dev_fill(vdev, info->msg, NETLINK_CB(info->cb->skb).portid,
849bc0d90eeSParav Pandit 			    info->cb->nlh->nlmsg_seq, NLM_F_MULTI, info->cb->extack);
850bc0d90eeSParav Pandit 	if (err)
851bc0d90eeSParav Pandit 		return err;
852bc0d90eeSParav Pandit 
853bc0d90eeSParav Pandit 	info->idx++;
854bc0d90eeSParav Pandit 	return 0;
855bc0d90eeSParav Pandit }
856bc0d90eeSParav Pandit 
vdpa_nl_cmd_dev_get_dumpit(struct sk_buff * msg,struct netlink_callback * cb)857bc0d90eeSParav Pandit static int vdpa_nl_cmd_dev_get_dumpit(struct sk_buff *msg, struct netlink_callback *cb)
858bc0d90eeSParav Pandit {
859bc0d90eeSParav Pandit 	struct vdpa_dev_dump_info info;
860bc0d90eeSParav Pandit 
861bc0d90eeSParav Pandit 	info.msg = msg;
862bc0d90eeSParav Pandit 	info.cb = cb;
863bc0d90eeSParav Pandit 	info.start_idx = cb->args[0];
864bc0d90eeSParav Pandit 	info.idx = 0;
865bc0d90eeSParav Pandit 
8660078ad90SEli Cohen 	down_read(&vdpa_dev_lock);
867bc0d90eeSParav Pandit 	bus_for_each_dev(&vdpa_bus, NULL, &info, vdpa_dev_dump);
8680078ad90SEli Cohen 	up_read(&vdpa_dev_lock);
869bc0d90eeSParav Pandit 	cb->args[0] = info.idx;
870bc0d90eeSParav Pandit 	return msg->len;
871bc0d90eeSParav Pandit }
872bc0d90eeSParav Pandit 
vdpa_dev_net_mq_config_fill(struct sk_buff * msg,u64 features,const struct virtio_net_config * config)8738a505711SZhu Lingshan static int vdpa_dev_net_mq_config_fill(struct sk_buff *msg, u64 features,
874ad69dd0bSParav Pandit 				       const struct virtio_net_config *config)
875ad69dd0bSParav Pandit {
876ad69dd0bSParav Pandit 	u16 val_u16;
877ad69dd0bSParav Pandit 
8788a505711SZhu Lingshan 	if ((features & BIT_ULL(VIRTIO_NET_F_MQ)) == 0 &&
8798a505711SZhu Lingshan 	    (features & BIT_ULL(VIRTIO_NET_F_RSS)) == 0)
880ad69dd0bSParav Pandit 		return 0;
881ad69dd0bSParav Pandit 
88235b37c33SZhu Lingshan 	val_u16 = __virtio16_to_cpu(true, config->max_virtqueue_pairs);
88335b37c33SZhu Lingshan 
884ad69dd0bSParav Pandit 	return nla_put_u16(msg, VDPA_ATTR_DEV_NET_CFG_MAX_VQP, val_u16);
885ad69dd0bSParav Pandit }
886ad69dd0bSParav Pandit 
vdpa_dev_net_mtu_config_fill(struct sk_buff * msg,u64 features,const struct virtio_net_config * config)88741a2ad92SZhu Lingshan static int vdpa_dev_net_mtu_config_fill(struct sk_buff *msg, u64 features,
88841a2ad92SZhu Lingshan 					const struct virtio_net_config *config)
88941a2ad92SZhu Lingshan {
89041a2ad92SZhu Lingshan 	u16 val_u16;
89141a2ad92SZhu Lingshan 
89241a2ad92SZhu Lingshan 	if ((features & BIT_ULL(VIRTIO_NET_F_MTU)) == 0)
89341a2ad92SZhu Lingshan 		return 0;
89441a2ad92SZhu Lingshan 
89541a2ad92SZhu Lingshan 	val_u16 = __virtio16_to_cpu(true, config->mtu);
89641a2ad92SZhu Lingshan 
89741a2ad92SZhu Lingshan 	return nla_put_u16(msg, VDPA_ATTR_DEV_NET_CFG_MTU, val_u16);
89841a2ad92SZhu Lingshan }
89941a2ad92SZhu Lingshan 
vdpa_dev_net_mac_config_fill(struct sk_buff * msg,u64 features,const struct virtio_net_config * config)90041a2ad92SZhu Lingshan static int vdpa_dev_net_mac_config_fill(struct sk_buff *msg, u64 features,
90141a2ad92SZhu Lingshan 					const struct virtio_net_config *config)
90241a2ad92SZhu Lingshan {
90341a2ad92SZhu Lingshan 	if ((features & BIT_ULL(VIRTIO_NET_F_MAC)) == 0)
90441a2ad92SZhu Lingshan 		return 0;
90541a2ad92SZhu Lingshan 
90641a2ad92SZhu Lingshan 	return  nla_put(msg, VDPA_ATTR_DEV_NET_CFG_MACADDR,
90741a2ad92SZhu Lingshan 			sizeof(config->mac), config->mac);
90841a2ad92SZhu Lingshan }
90941a2ad92SZhu Lingshan 
vdpa_dev_net_status_config_fill(struct sk_buff * msg,u64 features,const struct virtio_net_config * config)9106e6d3983SSi-Wei Liu static int vdpa_dev_net_status_config_fill(struct sk_buff *msg, u64 features,
9116e6d3983SSi-Wei Liu 					   const struct virtio_net_config *config)
9126e6d3983SSi-Wei Liu {
9136e6d3983SSi-Wei Liu 	u16 val_u16;
9146e6d3983SSi-Wei Liu 
9156e6d3983SSi-Wei Liu 	if ((features & BIT_ULL(VIRTIO_NET_F_STATUS)) == 0)
9166e6d3983SSi-Wei Liu 		return 0;
9176e6d3983SSi-Wei Liu 
9186e6d3983SSi-Wei Liu 	val_u16 = __virtio16_to_cpu(true, config->status);
9196e6d3983SSi-Wei Liu 	return nla_put_u16(msg, VDPA_ATTR_DEV_NET_STATUS, val_u16);
9206e6d3983SSi-Wei Liu }
9216e6d3983SSi-Wei Liu 
vdpa_dev_net_config_fill(struct vdpa_device * vdev,struct sk_buff * msg)922ad69dd0bSParav Pandit static int vdpa_dev_net_config_fill(struct vdpa_device *vdev, struct sk_buff *msg)
923ad69dd0bSParav Pandit {
924ad69dd0bSParav Pandit 	struct virtio_net_config config = {};
925c6dac2ecSZhu Lingshan 	u64 features_device;
926ad69dd0bSParav Pandit 
92722856510SZhu Lingshan 	vdev->config->get_config(vdev, 0, &config, sizeof(config));
928ad69dd0bSParav Pandit 
92922856510SZhu Lingshan 	features_device = vdev->config->get_device_features(vdev);
930ad69dd0bSParav Pandit 
931b9e05399SSi-Wei Liu 	if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_FEATURES, features_device,
932612f330eSEli Cohen 			      VDPA_ATTR_PAD))
933612f330eSEli Cohen 		return -EMSGSIZE;
934ad69dd0bSParav Pandit 
93541a2ad92SZhu Lingshan 	if (vdpa_dev_net_mtu_config_fill(msg, features_device, &config))
93641a2ad92SZhu Lingshan 		return -EMSGSIZE;
93741a2ad92SZhu Lingshan 
93841a2ad92SZhu Lingshan 	if (vdpa_dev_net_mac_config_fill(msg, features_device, &config))
93941a2ad92SZhu Lingshan 		return -EMSGSIZE;
94041a2ad92SZhu Lingshan 
9416e6d3983SSi-Wei Liu 	if (vdpa_dev_net_status_config_fill(msg, features_device, &config))
9426e6d3983SSi-Wei Liu 		return -EMSGSIZE;
9436e6d3983SSi-Wei Liu 
9449d97aa12SZhu Lingshan 	return vdpa_dev_net_mq_config_fill(msg, features_device, &config);
945ad69dd0bSParav Pandit }
946ad69dd0bSParav Pandit 
947ad69dd0bSParav Pandit static int
vdpa_dev_config_fill(struct vdpa_device * vdev,struct sk_buff * msg,u32 portid,u32 seq,int flags,struct netlink_ext_ack * extack)948ad69dd0bSParav Pandit vdpa_dev_config_fill(struct vdpa_device *vdev, struct sk_buff *msg, u32 portid, u32 seq,
949ad69dd0bSParav Pandit 		     int flags, struct netlink_ext_ack *extack)
950ad69dd0bSParav Pandit {
951c6dac2ecSZhu Lingshan 	u64 features_driver;
952c6dac2ecSZhu Lingshan 	u8 status = 0;
953ad69dd0bSParav Pandit 	u32 device_id;
954ad69dd0bSParav Pandit 	void *hdr;
955ad69dd0bSParav Pandit 	int err;
956ad69dd0bSParav Pandit 
957a6a51adcSEli Cohen 	down_read(&vdev->cf_lock);
958ad69dd0bSParav Pandit 	hdr = genlmsg_put(msg, portid, seq, &vdpa_nl_family, flags,
959ad69dd0bSParav Pandit 			  VDPA_CMD_DEV_CONFIG_GET);
96030ef7a8aSEli Cohen 	if (!hdr) {
96130ef7a8aSEli Cohen 		err = -EMSGSIZE;
96230ef7a8aSEli Cohen 		goto out;
96330ef7a8aSEli Cohen 	}
964ad69dd0bSParav Pandit 
965ad69dd0bSParav Pandit 	if (nla_put_string(msg, VDPA_ATTR_DEV_NAME, dev_name(&vdev->dev))) {
966ad69dd0bSParav Pandit 		err = -EMSGSIZE;
967ad69dd0bSParav Pandit 		goto msg_err;
968ad69dd0bSParav Pandit 	}
969ad69dd0bSParav Pandit 
970ad69dd0bSParav Pandit 	device_id = vdev->config->get_device_id(vdev);
971ad69dd0bSParav Pandit 	if (nla_put_u32(msg, VDPA_ATTR_DEV_ID, device_id)) {
972ad69dd0bSParav Pandit 		err = -EMSGSIZE;
973ad69dd0bSParav Pandit 		goto msg_err;
974ad69dd0bSParav Pandit 	}
975ad69dd0bSParav Pandit 
976c6dac2ecSZhu Lingshan 	/* only read driver features after the feature negotiation is done */
977c6dac2ecSZhu Lingshan 	status = vdev->config->get_status(vdev);
978c6dac2ecSZhu Lingshan 	if (status & VIRTIO_CONFIG_S_FEATURES_OK) {
979c6dac2ecSZhu Lingshan 		features_driver = vdev->config->get_driver_features(vdev);
980c6dac2ecSZhu Lingshan 		if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_NEGOTIATED_FEATURES, features_driver,
981c6dac2ecSZhu Lingshan 				      VDPA_ATTR_PAD)) {
982c6dac2ecSZhu Lingshan 			err = -EMSGSIZE;
983c6dac2ecSZhu Lingshan 			goto msg_err;
984c6dac2ecSZhu Lingshan 		}
985c6dac2ecSZhu Lingshan 	}
986c6dac2ecSZhu Lingshan 
987ad69dd0bSParav Pandit 	switch (device_id) {
988ad69dd0bSParav Pandit 	case VIRTIO_ID_NET:
989ad69dd0bSParav Pandit 		err = vdpa_dev_net_config_fill(vdev, msg);
990ad69dd0bSParav Pandit 		break;
991ad69dd0bSParav Pandit 	default:
992ad69dd0bSParav Pandit 		err = -EOPNOTSUPP;
993ad69dd0bSParav Pandit 		break;
994ad69dd0bSParav Pandit 	}
995ad69dd0bSParav Pandit 	if (err)
996ad69dd0bSParav Pandit 		goto msg_err;
997ad69dd0bSParav Pandit 
998a6a51adcSEli Cohen 	up_read(&vdev->cf_lock);
999ad69dd0bSParav Pandit 	genlmsg_end(msg, hdr);
1000ad69dd0bSParav Pandit 	return 0;
1001ad69dd0bSParav Pandit 
1002ad69dd0bSParav Pandit msg_err:
1003ad69dd0bSParav Pandit 	genlmsg_cancel(msg, hdr);
100430ef7a8aSEli Cohen out:
1005a6a51adcSEli Cohen 	up_read(&vdev->cf_lock);
1006ad69dd0bSParav Pandit 	return err;
1007ad69dd0bSParav Pandit }
1008ad69dd0bSParav Pandit 
vdpa_fill_stats_rec(struct vdpa_device * vdev,struct sk_buff * msg,struct genl_info * info,u32 index)100913b00b13SEli Cohen static int vdpa_fill_stats_rec(struct vdpa_device *vdev, struct sk_buff *msg,
101013b00b13SEli Cohen 			       struct genl_info *info, u32 index)
101113b00b13SEli Cohen {
101213b00b13SEli Cohen 	struct virtio_net_config config = {};
101313b00b13SEli Cohen 	u64 features;
101413b00b13SEli Cohen 	u8 status;
101513b00b13SEli Cohen 	int err;
101613b00b13SEli Cohen 
101713b00b13SEli Cohen 	status = vdev->config->get_status(vdev);
101813b00b13SEli Cohen 	if (!(status & VIRTIO_CONFIG_S_FEATURES_OK)) {
101913b00b13SEli Cohen 		NL_SET_ERR_MSG_MOD(info->extack, "feature negotiation not complete");
102013b00b13SEli Cohen 		return -EAGAIN;
102113b00b13SEli Cohen 	}
102213b00b13SEli Cohen 	vdpa_get_config_unlocked(vdev, 0, &config, sizeof(config));
102313b00b13SEli Cohen 
102413b00b13SEli Cohen 	features = vdev->config->get_driver_features(vdev);
102513b00b13SEli Cohen 	if (nla_put_u64_64bit(msg, VDPA_ATTR_DEV_NEGOTIATED_FEATURES,
102613b00b13SEli Cohen 			      features, VDPA_ATTR_PAD))
102713b00b13SEli Cohen 		return -EMSGSIZE;
102813b00b13SEli Cohen 
10291c96d545SJason Wang 	err = vdpa_dev_net_mq_config_fill(msg, features, &config);
10301c96d545SJason Wang 	if (err)
10311c96d545SJason Wang 		return err;
10321c96d545SJason Wang 
103313b00b13SEli Cohen 	if (nla_put_u32(msg, VDPA_ATTR_DEV_QUEUE_INDEX, index))
103413b00b13SEli Cohen 		return -EMSGSIZE;
103513b00b13SEli Cohen 
103613b00b13SEli Cohen 	err = vdev->config->get_vendor_vq_stats(vdev, index, msg, info->extack);
103713b00b13SEli Cohen 	if (err)
103813b00b13SEli Cohen 		return err;
103913b00b13SEli Cohen 
104013b00b13SEli Cohen 	return 0;
104113b00b13SEli Cohen }
104213b00b13SEli Cohen 
vendor_stats_fill(struct vdpa_device * vdev,struct sk_buff * msg,struct genl_info * info,u32 index)104313b00b13SEli Cohen static int vendor_stats_fill(struct vdpa_device *vdev, struct sk_buff *msg,
104413b00b13SEli Cohen 			     struct genl_info *info, u32 index)
104513b00b13SEli Cohen {
104613b00b13SEli Cohen 	int err;
104713b00b13SEli Cohen 
1048a6a51adcSEli Cohen 	down_read(&vdev->cf_lock);
104913b00b13SEli Cohen 	if (!vdev->config->get_vendor_vq_stats) {
105013b00b13SEli Cohen 		err = -EOPNOTSUPP;
105113b00b13SEli Cohen 		goto out;
105213b00b13SEli Cohen 	}
105313b00b13SEli Cohen 
105413b00b13SEli Cohen 	err = vdpa_fill_stats_rec(vdev, msg, info, index);
105513b00b13SEli Cohen out:
1056a6a51adcSEli Cohen 	up_read(&vdev->cf_lock);
105713b00b13SEli Cohen 	return err;
105813b00b13SEli Cohen }
105913b00b13SEli Cohen 
vdpa_dev_vendor_stats_fill(struct vdpa_device * vdev,struct sk_buff * msg,struct genl_info * info,u32 index)106013b00b13SEli Cohen static int vdpa_dev_vendor_stats_fill(struct vdpa_device *vdev,
106113b00b13SEli Cohen 				      struct sk_buff *msg,
106213b00b13SEli Cohen 				      struct genl_info *info, u32 index)
106313b00b13SEli Cohen {
106413b00b13SEli Cohen 	u32 device_id;
106513b00b13SEli Cohen 	void *hdr;
106613b00b13SEli Cohen 	int err;
106713b00b13SEli Cohen 	u32 portid = info->snd_portid;
106813b00b13SEli Cohen 	u32 seq = info->snd_seq;
106913b00b13SEli Cohen 	u32 flags = 0;
107013b00b13SEli Cohen 
107113b00b13SEli Cohen 	hdr = genlmsg_put(msg, portid, seq, &vdpa_nl_family, flags,
107213b00b13SEli Cohen 			  VDPA_CMD_DEV_VSTATS_GET);
107313b00b13SEli Cohen 	if (!hdr)
107413b00b13SEli Cohen 		return -EMSGSIZE;
107513b00b13SEli Cohen 
107613b00b13SEli Cohen 	if (nla_put_string(msg, VDPA_ATTR_DEV_NAME, dev_name(&vdev->dev))) {
107713b00b13SEli Cohen 		err = -EMSGSIZE;
107813b00b13SEli Cohen 		goto undo_msg;
107913b00b13SEli Cohen 	}
108013b00b13SEli Cohen 
108113b00b13SEli Cohen 	device_id = vdev->config->get_device_id(vdev);
108213b00b13SEli Cohen 	if (nla_put_u32(msg, VDPA_ATTR_DEV_ID, device_id)) {
108313b00b13SEli Cohen 		err = -EMSGSIZE;
108413b00b13SEli Cohen 		goto undo_msg;
108513b00b13SEli Cohen 	}
108613b00b13SEli Cohen 
108713b00b13SEli Cohen 	switch (device_id) {
108813b00b13SEli Cohen 	case VIRTIO_ID_NET:
108913b00b13SEli Cohen 		if (index > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX) {
1090699209fcSColin Ian King 			NL_SET_ERR_MSG_MOD(info->extack, "queue index exceeds max value");
109113b00b13SEli Cohen 			err = -ERANGE;
109213b00b13SEli Cohen 			break;
109313b00b13SEli Cohen 		}
109413b00b13SEli Cohen 
109513b00b13SEli Cohen 		err = vendor_stats_fill(vdev, msg, info, index);
109613b00b13SEli Cohen 		break;
109713b00b13SEli Cohen 	default:
109813b00b13SEli Cohen 		err = -EOPNOTSUPP;
109913b00b13SEli Cohen 		break;
110013b00b13SEli Cohen 	}
110113b00b13SEli Cohen 	genlmsg_end(msg, hdr);
110213b00b13SEli Cohen 
110313b00b13SEli Cohen 	return err;
110413b00b13SEli Cohen 
110513b00b13SEli Cohen undo_msg:
110613b00b13SEli Cohen 	genlmsg_cancel(msg, hdr);
110713b00b13SEli Cohen 	return err;
110813b00b13SEli Cohen }
110913b00b13SEli Cohen 
vdpa_nl_cmd_dev_config_get_doit(struct sk_buff * skb,struct genl_info * info)1110ad69dd0bSParav Pandit static int vdpa_nl_cmd_dev_config_get_doit(struct sk_buff *skb, struct genl_info *info)
1111ad69dd0bSParav Pandit {
1112ad69dd0bSParav Pandit 	struct vdpa_device *vdev;
1113ad69dd0bSParav Pandit 	struct sk_buff *msg;
1114ad69dd0bSParav Pandit 	const char *devname;
1115ad69dd0bSParav Pandit 	struct device *dev;
1116ad69dd0bSParav Pandit 	int err;
1117ad69dd0bSParav Pandit 
1118ad69dd0bSParav Pandit 	if (!info->attrs[VDPA_ATTR_DEV_NAME])
1119ad69dd0bSParav Pandit 		return -EINVAL;
1120ad69dd0bSParav Pandit 	devname = nla_data(info->attrs[VDPA_ATTR_DEV_NAME]);
1121ad69dd0bSParav Pandit 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
1122ad69dd0bSParav Pandit 	if (!msg)
1123ad69dd0bSParav Pandit 		return -ENOMEM;
1124ad69dd0bSParav Pandit 
11250078ad90SEli Cohen 	down_read(&vdpa_dev_lock);
1126ad69dd0bSParav Pandit 	dev = bus_find_device(&vdpa_bus, NULL, devname, vdpa_name_match);
1127ad69dd0bSParav Pandit 	if (!dev) {
1128ad69dd0bSParav Pandit 		NL_SET_ERR_MSG_MOD(info->extack, "device not found");
1129ad69dd0bSParav Pandit 		err = -ENODEV;
1130ad69dd0bSParav Pandit 		goto dev_err;
1131ad69dd0bSParav Pandit 	}
1132ad69dd0bSParav Pandit 	vdev = container_of(dev, struct vdpa_device, dev);
1133ad69dd0bSParav Pandit 	if (!vdev->mdev) {
1134ad69dd0bSParav Pandit 		NL_SET_ERR_MSG_MOD(info->extack, "unmanaged vdpa device");
1135ad69dd0bSParav Pandit 		err = -EINVAL;
1136ad69dd0bSParav Pandit 		goto mdev_err;
1137ad69dd0bSParav Pandit 	}
1138ad69dd0bSParav Pandit 	err = vdpa_dev_config_fill(vdev, msg, info->snd_portid, info->snd_seq,
1139ad69dd0bSParav Pandit 				   0, info->extack);
1140ad69dd0bSParav Pandit 	if (!err)
1141ad69dd0bSParav Pandit 		err = genlmsg_reply(msg, info);
1142ad69dd0bSParav Pandit 
1143ad69dd0bSParav Pandit mdev_err:
1144ad69dd0bSParav Pandit 	put_device(dev);
1145ad69dd0bSParav Pandit dev_err:
11460078ad90SEli Cohen 	up_read(&vdpa_dev_lock);
1147ad69dd0bSParav Pandit 	if (err)
1148ad69dd0bSParav Pandit 		nlmsg_free(msg);
1149ad69dd0bSParav Pandit 	return err;
1150ad69dd0bSParav Pandit }
1151ad69dd0bSParav Pandit 
vdpa_dev_config_dump(struct device * dev,void * data)1152ad69dd0bSParav Pandit static int vdpa_dev_config_dump(struct device *dev, void *data)
1153ad69dd0bSParav Pandit {
1154ad69dd0bSParav Pandit 	struct vdpa_device *vdev = container_of(dev, struct vdpa_device, dev);
1155ad69dd0bSParav Pandit 	struct vdpa_dev_dump_info *info = data;
1156ad69dd0bSParav Pandit 	int err;
1157ad69dd0bSParav Pandit 
1158ad69dd0bSParav Pandit 	if (!vdev->mdev)
1159ad69dd0bSParav Pandit 		return 0;
1160ad69dd0bSParav Pandit 	if (info->idx < info->start_idx) {
1161ad69dd0bSParav Pandit 		info->idx++;
1162ad69dd0bSParav Pandit 		return 0;
1163ad69dd0bSParav Pandit 	}
1164ad69dd0bSParav Pandit 	err = vdpa_dev_config_fill(vdev, info->msg, NETLINK_CB(info->cb->skb).portid,
1165ad69dd0bSParav Pandit 				   info->cb->nlh->nlmsg_seq, NLM_F_MULTI,
1166ad69dd0bSParav Pandit 				   info->cb->extack);
1167ad69dd0bSParav Pandit 	if (err)
1168ad69dd0bSParav Pandit 		return err;
1169ad69dd0bSParav Pandit 
1170ad69dd0bSParav Pandit 	info->idx++;
1171ad69dd0bSParav Pandit 	return 0;
1172ad69dd0bSParav Pandit }
1173ad69dd0bSParav Pandit 
1174ad69dd0bSParav Pandit static int
vdpa_nl_cmd_dev_config_get_dumpit(struct sk_buff * msg,struct netlink_callback * cb)1175ad69dd0bSParav Pandit vdpa_nl_cmd_dev_config_get_dumpit(struct sk_buff *msg, struct netlink_callback *cb)
1176ad69dd0bSParav Pandit {
1177ad69dd0bSParav Pandit 	struct vdpa_dev_dump_info info;
1178ad69dd0bSParav Pandit 
1179ad69dd0bSParav Pandit 	info.msg = msg;
1180ad69dd0bSParav Pandit 	info.cb = cb;
1181ad69dd0bSParav Pandit 	info.start_idx = cb->args[0];
1182ad69dd0bSParav Pandit 	info.idx = 0;
1183ad69dd0bSParav Pandit 
11840078ad90SEli Cohen 	down_read(&vdpa_dev_lock);
1185ad69dd0bSParav Pandit 	bus_for_each_dev(&vdpa_bus, NULL, &info, vdpa_dev_config_dump);
11860078ad90SEli Cohen 	up_read(&vdpa_dev_lock);
1187ad69dd0bSParav Pandit 	cb->args[0] = info.idx;
1188ad69dd0bSParav Pandit 	return msg->len;
1189ad69dd0bSParav Pandit }
1190ad69dd0bSParav Pandit 
vdpa_nl_cmd_dev_stats_get_doit(struct sk_buff * skb,struct genl_info * info)119113b00b13SEli Cohen static int vdpa_nl_cmd_dev_stats_get_doit(struct sk_buff *skb,
119213b00b13SEli Cohen 					  struct genl_info *info)
119313b00b13SEli Cohen {
119413b00b13SEli Cohen 	struct vdpa_device *vdev;
119513b00b13SEli Cohen 	struct sk_buff *msg;
119613b00b13SEli Cohen 	const char *devname;
119713b00b13SEli Cohen 	struct device *dev;
119813b00b13SEli Cohen 	u32 index;
119913b00b13SEli Cohen 	int err;
120013b00b13SEli Cohen 
120113b00b13SEli Cohen 	if (!info->attrs[VDPA_ATTR_DEV_NAME])
120213b00b13SEli Cohen 		return -EINVAL;
120313b00b13SEli Cohen 
120413b00b13SEli Cohen 	if (!info->attrs[VDPA_ATTR_DEV_QUEUE_INDEX])
120513b00b13SEli Cohen 		return -EINVAL;
120613b00b13SEli Cohen 
120713b00b13SEli Cohen 	devname = nla_data(info->attrs[VDPA_ATTR_DEV_NAME]);
120813b00b13SEli Cohen 	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
120913b00b13SEli Cohen 	if (!msg)
121013b00b13SEli Cohen 		return -ENOMEM;
121113b00b13SEli Cohen 
121213b00b13SEli Cohen 	index = nla_get_u32(info->attrs[VDPA_ATTR_DEV_QUEUE_INDEX]);
12130078ad90SEli Cohen 	down_read(&vdpa_dev_lock);
121413b00b13SEli Cohen 	dev = bus_find_device(&vdpa_bus, NULL, devname, vdpa_name_match);
121513b00b13SEli Cohen 	if (!dev) {
121613b00b13SEli Cohen 		NL_SET_ERR_MSG_MOD(info->extack, "device not found");
121713b00b13SEli Cohen 		err = -ENODEV;
121813b00b13SEli Cohen 		goto dev_err;
121913b00b13SEli Cohen 	}
122013b00b13SEli Cohen 	vdev = container_of(dev, struct vdpa_device, dev);
122113b00b13SEli Cohen 	if (!vdev->mdev) {
122213b00b13SEli Cohen 		NL_SET_ERR_MSG_MOD(info->extack, "unmanaged vdpa device");
122313b00b13SEli Cohen 		err = -EINVAL;
122413b00b13SEli Cohen 		goto mdev_err;
122513b00b13SEli Cohen 	}
122613b00b13SEli Cohen 	err = vdpa_dev_vendor_stats_fill(vdev, msg, info, index);
122713b00b13SEli Cohen 	if (err)
122813b00b13SEli Cohen 		goto mdev_err;
122913b00b13SEli Cohen 
123013b00b13SEli Cohen 	err = genlmsg_reply(msg, info);
123113b00b13SEli Cohen 
123213b00b13SEli Cohen 	put_device(dev);
12330078ad90SEli Cohen 	up_read(&vdpa_dev_lock);
123413b00b13SEli Cohen 
123513b00b13SEli Cohen 	return err;
123613b00b13SEli Cohen 
123713b00b13SEli Cohen mdev_err:
123813b00b13SEli Cohen 	put_device(dev);
123913b00b13SEli Cohen dev_err:
124013b00b13SEli Cohen 	nlmsg_free(msg);
12410078ad90SEli Cohen 	up_read(&vdpa_dev_lock);
124213b00b13SEli Cohen 	return err;
124313b00b13SEli Cohen }
124413b00b13SEli Cohen 
124533b34750SParav Pandit static const struct nla_policy vdpa_nl_policy[VDPA_ATTR_MAX + 1] = {
124633b34750SParav Pandit 	[VDPA_ATTR_MGMTDEV_BUS_NAME] = { .type = NLA_NUL_STRING },
124733b34750SParav Pandit 	[VDPA_ATTR_MGMTDEV_DEV_NAME] = { .type = NLA_STRING },
1248903f7bcaSParav Pandit 	[VDPA_ATTR_DEV_NAME] = { .type = NLA_STRING },
1249d8ca2fa5SParav Pandit 	[VDPA_ATTR_DEV_NET_CFG_MACADDR] = NLA_POLICY_ETH_ADDR,
1250*5d6ba607SLin Ma 	[VDPA_ATTR_DEV_NET_CFG_MAX_VQP] = { .type = NLA_U16 },
1251d8ca2fa5SParav Pandit 	/* virtio spec 1.1 section 5.1.4.1 for valid MTU range */
1252d8ca2fa5SParav Pandit 	[VDPA_ATTR_DEV_NET_CFG_MTU] = NLA_POLICY_MIN(NLA_U16, 68),
1253b3003e1bSLin Ma 	[VDPA_ATTR_DEV_QUEUE_INDEX] = { .type = NLA_U32 },
125479c86515SLin Ma 	[VDPA_ATTR_DEV_FEATURES] = { .type = NLA_U64 },
125533b34750SParav Pandit };
125633b34750SParav Pandit 
125733b34750SParav Pandit static const struct genl_ops vdpa_nl_ops[] = {
125833b34750SParav Pandit 	{
125933b34750SParav Pandit 		.cmd = VDPA_CMD_MGMTDEV_GET,
126033b34750SParav Pandit 		.doit = vdpa_nl_cmd_mgmtdev_get_doit,
126133b34750SParav Pandit 		.dumpit = vdpa_nl_cmd_mgmtdev_get_dumpit,
126233b34750SParav Pandit 	},
1263903f7bcaSParav Pandit 	{
1264903f7bcaSParav Pandit 		.cmd = VDPA_CMD_DEV_NEW,
1265903f7bcaSParav Pandit 		.doit = vdpa_nl_cmd_dev_add_set_doit,
1266903f7bcaSParav Pandit 		.flags = GENL_ADMIN_PERM,
1267903f7bcaSParav Pandit 	},
1268903f7bcaSParav Pandit 	{
1269903f7bcaSParav Pandit 		.cmd = VDPA_CMD_DEV_DEL,
1270903f7bcaSParav Pandit 		.doit = vdpa_nl_cmd_dev_del_set_doit,
1271903f7bcaSParav Pandit 		.flags = GENL_ADMIN_PERM,
1272903f7bcaSParav Pandit 	},
1273bc0d90eeSParav Pandit 	{
1274bc0d90eeSParav Pandit 		.cmd = VDPA_CMD_DEV_GET,
1275bc0d90eeSParav Pandit 		.doit = vdpa_nl_cmd_dev_get_doit,
1276bc0d90eeSParav Pandit 		.dumpit = vdpa_nl_cmd_dev_get_dumpit,
1277bc0d90eeSParav Pandit 	},
1278ad69dd0bSParav Pandit 	{
1279ad69dd0bSParav Pandit 		.cmd = VDPA_CMD_DEV_CONFIG_GET,
1280ad69dd0bSParav Pandit 		.doit = vdpa_nl_cmd_dev_config_get_doit,
1281ad69dd0bSParav Pandit 		.dumpit = vdpa_nl_cmd_dev_config_get_dumpit,
1282ad69dd0bSParav Pandit 	},
128313b00b13SEli Cohen 	{
128413b00b13SEli Cohen 		.cmd = VDPA_CMD_DEV_VSTATS_GET,
128513b00b13SEli Cohen 		.doit = vdpa_nl_cmd_dev_stats_get_doit,
128613b00b13SEli Cohen 		.flags = GENL_ADMIN_PERM,
128713b00b13SEli Cohen 	},
128833b34750SParav Pandit };
128933b34750SParav Pandit 
129033b34750SParav Pandit static struct genl_family vdpa_nl_family __ro_after_init = {
129133b34750SParav Pandit 	.name = VDPA_GENL_NAME,
129233b34750SParav Pandit 	.version = VDPA_GENL_VERSION,
129333b34750SParav Pandit 	.maxattr = VDPA_ATTR_MAX,
129433b34750SParav Pandit 	.policy = vdpa_nl_policy,
129533b34750SParav Pandit 	.netnsok = false,
129633b34750SParav Pandit 	.module = THIS_MODULE,
129733b34750SParav Pandit 	.ops = vdpa_nl_ops,
129833b34750SParav Pandit 	.n_ops = ARRAY_SIZE(vdpa_nl_ops),
12999c5d03d3SJakub Kicinski 	.resv_start_op = VDPA_CMD_DEV_VSTATS_GET + 1,
130033b34750SParav Pandit };
130133b34750SParav Pandit 
vdpa_init(void)1302c9b9f5f8SMichael S. Tsirkin static int vdpa_init(void)
1303c9b9f5f8SMichael S. Tsirkin {
130433b34750SParav Pandit 	int err;
130533b34750SParav Pandit 
130633b34750SParav Pandit 	err = bus_register(&vdpa_bus);
130733b34750SParav Pandit 	if (err)
130833b34750SParav Pandit 		return err;
130933b34750SParav Pandit 	err = genl_register_family(&vdpa_nl_family);
131033b34750SParav Pandit 	if (err)
131133b34750SParav Pandit 		goto err;
131233b34750SParav Pandit 	return 0;
131333b34750SParav Pandit 
131433b34750SParav Pandit err:
131533b34750SParav Pandit 	bus_unregister(&vdpa_bus);
131633b34750SParav Pandit 	return err;
1317c9b9f5f8SMichael S. Tsirkin }
1318c9b9f5f8SMichael S. Tsirkin 
vdpa_exit(void)1319c9b9f5f8SMichael S. Tsirkin static void __exit vdpa_exit(void)
1320c9b9f5f8SMichael S. Tsirkin {
132133b34750SParav Pandit 	genl_unregister_family(&vdpa_nl_family);
1322c9b9f5f8SMichael S. Tsirkin 	bus_unregister(&vdpa_bus);
1323c9b9f5f8SMichael S. Tsirkin 	ida_destroy(&vdpa_index_ida);
1324c9b9f5f8SMichael S. Tsirkin }
1325c9b9f5f8SMichael S. Tsirkin core_initcall(vdpa_init);
1326c9b9f5f8SMichael S. Tsirkin module_exit(vdpa_exit);
1327c9b9f5f8SMichael S. Tsirkin 
1328c9b9f5f8SMichael S. Tsirkin MODULE_AUTHOR("Jason Wang <jasowang@redhat.com>");
1329c9b9f5f8SMichael S. Tsirkin MODULE_LICENSE("GPL v2");
1330