18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * vDPA bus. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2020, Red Hat. All rights reserved. 68c2ecf20Sopenharmony_ci * Author: Jason Wang <jasowang@redhat.com> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/module.h> 118c2ecf20Sopenharmony_ci#include <linux/idr.h> 128c2ecf20Sopenharmony_ci#include <linux/slab.h> 138c2ecf20Sopenharmony_ci#include <linux/vdpa.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_cistatic DEFINE_IDA(vdpa_index_ida); 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic int vdpa_dev_probe(struct device *d) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci struct vdpa_device *vdev = dev_to_vdpa(d); 208c2ecf20Sopenharmony_ci struct vdpa_driver *drv = drv_to_vdpa(vdev->dev.driver); 218c2ecf20Sopenharmony_ci int ret = 0; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci if (drv && drv->probe) 248c2ecf20Sopenharmony_ci ret = drv->probe(vdev); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci return ret; 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int vdpa_dev_remove(struct device *d) 308c2ecf20Sopenharmony_ci{ 318c2ecf20Sopenharmony_ci struct vdpa_device *vdev = dev_to_vdpa(d); 328c2ecf20Sopenharmony_ci struct vdpa_driver *drv = drv_to_vdpa(vdev->dev.driver); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci if (drv && drv->remove) 358c2ecf20Sopenharmony_ci drv->remove(vdev); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci return 0; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic struct bus_type vdpa_bus = { 418c2ecf20Sopenharmony_ci .name = "vdpa", 428c2ecf20Sopenharmony_ci .probe = vdpa_dev_probe, 438c2ecf20Sopenharmony_ci .remove = vdpa_dev_remove, 448c2ecf20Sopenharmony_ci}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistatic void vdpa_release_dev(struct device *d) 478c2ecf20Sopenharmony_ci{ 488c2ecf20Sopenharmony_ci struct vdpa_device *vdev = dev_to_vdpa(d); 498c2ecf20Sopenharmony_ci const struct vdpa_config_ops *ops = vdev->config; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci if (ops->free) 528c2ecf20Sopenharmony_ci ops->free(vdev); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci ida_simple_remove(&vdpa_index_ida, vdev->index); 558c2ecf20Sopenharmony_ci kfree(vdev); 568c2ecf20Sopenharmony_ci} 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci/** 598c2ecf20Sopenharmony_ci * __vdpa_alloc_device - allocate and initilaize a vDPA device 608c2ecf20Sopenharmony_ci * This allows driver to some prepartion after device is 618c2ecf20Sopenharmony_ci * initialized but before registered. 628c2ecf20Sopenharmony_ci * @parent: the parent device 638c2ecf20Sopenharmony_ci * @config: the bus operations that is supported by this device 648c2ecf20Sopenharmony_ci * @nvqs: number of virtqueues supported by this device 658c2ecf20Sopenharmony_ci * @size: size of the parent structure that contains private data 668c2ecf20Sopenharmony_ci * 678c2ecf20Sopenharmony_ci * Driver should use vdpa_alloc_device() wrapper macro instead of 688c2ecf20Sopenharmony_ci * using this directly. 698c2ecf20Sopenharmony_ci * 708c2ecf20Sopenharmony_ci * Returns an error when parent/config/dma_dev is not set or fail to get 718c2ecf20Sopenharmony_ci * ida. 728c2ecf20Sopenharmony_ci */ 738c2ecf20Sopenharmony_cistruct vdpa_device *__vdpa_alloc_device(struct device *parent, 748c2ecf20Sopenharmony_ci const struct vdpa_config_ops *config, 758c2ecf20Sopenharmony_ci int nvqs, 768c2ecf20Sopenharmony_ci size_t size) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci struct vdpa_device *vdev; 798c2ecf20Sopenharmony_ci int err = -EINVAL; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci if (!config) 828c2ecf20Sopenharmony_ci goto err; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (!!config->dma_map != !!config->dma_unmap) 858c2ecf20Sopenharmony_ci goto err; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci err = -ENOMEM; 888c2ecf20Sopenharmony_ci vdev = kzalloc(size, GFP_KERNEL); 898c2ecf20Sopenharmony_ci if (!vdev) 908c2ecf20Sopenharmony_ci goto err; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci err = ida_simple_get(&vdpa_index_ida, 0, 0, GFP_KERNEL); 938c2ecf20Sopenharmony_ci if (err < 0) 948c2ecf20Sopenharmony_ci goto err_ida; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci vdev->dev.bus = &vdpa_bus; 978c2ecf20Sopenharmony_ci vdev->dev.parent = parent; 988c2ecf20Sopenharmony_ci vdev->dev.release = vdpa_release_dev; 998c2ecf20Sopenharmony_ci vdev->index = err; 1008c2ecf20Sopenharmony_ci vdev->config = config; 1018c2ecf20Sopenharmony_ci vdev->features_valid = false; 1028c2ecf20Sopenharmony_ci vdev->nvqs = nvqs; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci err = dev_set_name(&vdev->dev, "vdpa%u", vdev->index); 1058c2ecf20Sopenharmony_ci if (err) 1068c2ecf20Sopenharmony_ci goto err_name; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci device_initialize(&vdev->dev); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci return vdev; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cierr_name: 1138c2ecf20Sopenharmony_ci ida_simple_remove(&vdpa_index_ida, vdev->index); 1148c2ecf20Sopenharmony_cierr_ida: 1158c2ecf20Sopenharmony_ci kfree(vdev); 1168c2ecf20Sopenharmony_cierr: 1178c2ecf20Sopenharmony_ci return ERR_PTR(err); 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__vdpa_alloc_device); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci/** 1228c2ecf20Sopenharmony_ci * vdpa_register_device - register a vDPA device 1238c2ecf20Sopenharmony_ci * Callers must have a succeed call of vdpa_alloc_device() before. 1248c2ecf20Sopenharmony_ci * @vdev: the vdpa device to be registered to vDPA bus 1258c2ecf20Sopenharmony_ci * 1268c2ecf20Sopenharmony_ci * Returns an error when fail to add to vDPA bus 1278c2ecf20Sopenharmony_ci */ 1288c2ecf20Sopenharmony_ciint vdpa_register_device(struct vdpa_device *vdev) 1298c2ecf20Sopenharmony_ci{ 1308c2ecf20Sopenharmony_ci return device_add(&vdev->dev); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vdpa_register_device); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci/** 1358c2ecf20Sopenharmony_ci * vdpa_unregister_device - unregister a vDPA device 1368c2ecf20Sopenharmony_ci * @vdev: the vdpa device to be unregisted from vDPA bus 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_civoid vdpa_unregister_device(struct vdpa_device *vdev) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci device_unregister(&vdev->dev); 1418c2ecf20Sopenharmony_ci} 1428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vdpa_unregister_device); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci/** 1458c2ecf20Sopenharmony_ci * __vdpa_register_driver - register a vDPA device driver 1468c2ecf20Sopenharmony_ci * @drv: the vdpa device driver to be registered 1478c2ecf20Sopenharmony_ci * @owner: module owner of the driver 1488c2ecf20Sopenharmony_ci * 1498c2ecf20Sopenharmony_ci * Returns an err when fail to do the registration 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_ciint __vdpa_register_driver(struct vdpa_driver *drv, struct module *owner) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci drv->driver.bus = &vdpa_bus; 1548c2ecf20Sopenharmony_ci drv->driver.owner = owner; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci return driver_register(&drv->driver); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__vdpa_register_driver); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci/** 1618c2ecf20Sopenharmony_ci * vdpa_unregister_driver - unregister a vDPA device driver 1628c2ecf20Sopenharmony_ci * @drv: the vdpa device driver to be unregistered 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_civoid vdpa_unregister_driver(struct vdpa_driver *drv) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci driver_unregister(&drv->driver); 1678c2ecf20Sopenharmony_ci} 1688c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(vdpa_unregister_driver); 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic int vdpa_init(void) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci return bus_register(&vdpa_bus); 1738c2ecf20Sopenharmony_ci} 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_cistatic void __exit vdpa_exit(void) 1768c2ecf20Sopenharmony_ci{ 1778c2ecf20Sopenharmony_ci bus_unregister(&vdpa_bus); 1788c2ecf20Sopenharmony_ci ida_destroy(&vdpa_index_ida); 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_cicore_initcall(vdpa_init); 1818c2ecf20Sopenharmony_cimodule_exit(vdpa_exit); 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jason Wang <jasowang@redhat.com>"); 1848c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 185