]>
Commit | Line | Data |
---|---|---|
961e9c84 JW |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * vDPA bus. | |
4 | * | |
5 | * Copyright (c) 2020, Red Hat. All rights reserved. | |
6 | * Author: Jason Wang <jasowang@redhat.com> | |
7 | * | |
8 | */ | |
9 | ||
10 | #include <linux/module.h> | |
11 | #include <linux/idr.h> | |
12 | #include <linux/slab.h> | |
13 | #include <linux/vdpa.h> | |
14 | ||
15 | static DEFINE_IDA(vdpa_index_ida); | |
16 | ||
17 | static int vdpa_dev_probe(struct device *d) | |
18 | { | |
19 | struct vdpa_device *vdev = dev_to_vdpa(d); | |
20 | struct vdpa_driver *drv = drv_to_vdpa(vdev->dev.driver); | |
21 | int ret = 0; | |
22 | ||
23 | if (drv && drv->probe) | |
24 | ret = drv->probe(vdev); | |
25 | ||
26 | return ret; | |
27 | } | |
28 | ||
29 | static int vdpa_dev_remove(struct device *d) | |
30 | { | |
31 | struct vdpa_device *vdev = dev_to_vdpa(d); | |
32 | struct vdpa_driver *drv = drv_to_vdpa(vdev->dev.driver); | |
33 | ||
34 | if (drv && drv->remove) | |
35 | drv->remove(vdev); | |
36 | ||
37 | return 0; | |
38 | } | |
39 | ||
40 | static struct bus_type vdpa_bus = { | |
41 | .name = "vdpa", | |
42 | .probe = vdpa_dev_probe, | |
43 | .remove = vdpa_dev_remove, | |
44 | }; | |
45 | ||
46 | static void vdpa_release_dev(struct device *d) | |
47 | { | |
48 | struct vdpa_device *vdev = dev_to_vdpa(d); | |
49 | const struct vdpa_config_ops *ops = vdev->config; | |
50 | ||
51 | if (ops->free) | |
52 | ops->free(vdev); | |
53 | ||
54 | ida_simple_remove(&vdpa_index_ida, vdev->index); | |
55 | kfree(vdev); | |
56 | } | |
57 | ||
58 | /** | |
59 | * __vdpa_alloc_device - allocate and initilaize a vDPA device | |
60 | * This allows driver to some prepartion after device is | |
61 | * initialized but before registered. | |
62 | * @parent: the parent device | |
63 | * @config: the bus operations that is supported by this device | |
a9974489 | 64 | * @nvqs: number of virtqueues supported by this device |
961e9c84 JW |
65 | * @size: size of the parent structure that contains private data |
66 | * | |
24eae8eb | 67 | * Driver should use vdpa_alloc_device() wrapper macro instead of |
961e9c84 JW |
68 | * using this directly. |
69 | * | |
70 | * Returns an error when parent/config/dma_dev is not set or fail to get | |
71 | * ida. | |
72 | */ | |
73 | struct vdpa_device *__vdpa_alloc_device(struct device *parent, | |
74 | const struct vdpa_config_ops *config, | |
a9974489 | 75 | int nvqs, |
961e9c84 JW |
76 | size_t size) |
77 | { | |
78 | struct vdpa_device *vdev; | |
79 | int err = -EINVAL; | |
80 | ||
81 | if (!config) | |
82 | goto err; | |
83 | ||
84 | if (!!config->dma_map != !!config->dma_unmap) | |
85 | goto err; | |
86 | ||
87 | err = -ENOMEM; | |
88 | vdev = kzalloc(size, GFP_KERNEL); | |
89 | if (!vdev) | |
90 | goto err; | |
91 | ||
418eddef | 92 | err = ida_alloc(&vdpa_index_ida, GFP_KERNEL); |
961e9c84 JW |
93 | if (err < 0) |
94 | goto err_ida; | |
95 | ||
96 | vdev->dev.bus = &vdpa_bus; | |
97 | vdev->dev.parent = parent; | |
98 | vdev->dev.release = vdpa_release_dev; | |
99 | vdev->index = err; | |
100 | vdev->config = config; | |
452639a6 | 101 | vdev->features_valid = false; |
a9974489 | 102 | vdev->nvqs = nvqs; |
961e9c84 JW |
103 | |
104 | err = dev_set_name(&vdev->dev, "vdpa%u", vdev->index); | |
105 | if (err) | |
106 | goto err_name; | |
107 | ||
108 | device_initialize(&vdev->dev); | |
109 | ||
110 | return vdev; | |
111 | ||
112 | err_name: | |
113 | ida_simple_remove(&vdpa_index_ida, vdev->index); | |
114 | err_ida: | |
115 | kfree(vdev); | |
116 | err: | |
117 | return ERR_PTR(err); | |
118 | } | |
119 | EXPORT_SYMBOL_GPL(__vdpa_alloc_device); | |
120 | ||
121 | /** | |
122 | * vdpa_register_device - register a vDPA device | |
ac8b85f9 | 123 | * Callers must have a succeed call of vdpa_alloc_device() before. |
961e9c84 JW |
124 | * @vdev: the vdpa device to be registered to vDPA bus |
125 | * | |
126 | * Returns an error when fail to add to vDPA bus | |
127 | */ | |
128 | int vdpa_register_device(struct vdpa_device *vdev) | |
129 | { | |
130 | return device_add(&vdev->dev); | |
131 | } | |
132 | EXPORT_SYMBOL_GPL(vdpa_register_device); | |
133 | ||
134 | /** | |
135 | * vdpa_unregister_device - unregister a vDPA device | |
136 | * @vdev: the vdpa device to be unregisted from vDPA bus | |
137 | */ | |
138 | void vdpa_unregister_device(struct vdpa_device *vdev) | |
139 | { | |
140 | device_unregister(&vdev->dev); | |
141 | } | |
142 | EXPORT_SYMBOL_GPL(vdpa_unregister_device); | |
143 | ||
144 | /** | |
145 | * __vdpa_register_driver - register a vDPA device driver | |
146 | * @drv: the vdpa device driver to be registered | |
147 | * @owner: module owner of the driver | |
148 | * | |
149 | * Returns an err when fail to do the registration | |
150 | */ | |
151 | int __vdpa_register_driver(struct vdpa_driver *drv, struct module *owner) | |
152 | { | |
153 | drv->driver.bus = &vdpa_bus; | |
154 | drv->driver.owner = owner; | |
155 | ||
156 | return driver_register(&drv->driver); | |
157 | } | |
158 | EXPORT_SYMBOL_GPL(__vdpa_register_driver); | |
159 | ||
160 | /** | |
161 | * vdpa_unregister_driver - unregister a vDPA device driver | |
162 | * @drv: the vdpa device driver to be unregistered | |
163 | */ | |
164 | void vdpa_unregister_driver(struct vdpa_driver *drv) | |
165 | { | |
166 | driver_unregister(&drv->driver); | |
167 | } | |
168 | EXPORT_SYMBOL_GPL(vdpa_unregister_driver); | |
169 | ||
170 | static int vdpa_init(void) | |
171 | { | |
172 | return bus_register(&vdpa_bus); | |
173 | } | |
174 | ||
175 | static void __exit vdpa_exit(void) | |
176 | { | |
177 | bus_unregister(&vdpa_bus); | |
178 | ida_destroy(&vdpa_index_ida); | |
179 | } | |
180 | core_initcall(vdpa_init); | |
181 | module_exit(vdpa_exit); | |
182 | ||
183 | MODULE_AUTHOR("Jason Wang <jasowang@redhat.com>"); | |
184 | MODULE_LICENSE("GPL v2"); |