1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* Copyright (C) 2017 Netronome Systems, Inc. |
3 | * Copyright (C) 2019 Mellanox Technologies. All rights reserved |
4 | */ |
5 | |
6 | #include <linux/completion.h> |
7 | #include <linux/device.h> |
8 | #include <linux/idr.h> |
9 | #include <linux/kernel.h> |
10 | #include <linux/list.h> |
11 | #include <linux/mutex.h> |
12 | #include <linux/refcount.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/sysfs.h> |
15 | |
16 | #include "netdevsim.h" |
17 | |
18 | static DEFINE_IDA(nsim_bus_dev_ids); |
19 | static LIST_HEAD(nsim_bus_dev_list); |
20 | static DEFINE_MUTEX(nsim_bus_dev_list_lock); |
21 | static bool nsim_bus_enable; |
22 | static refcount_t nsim_bus_devs; /* Including the bus itself. */ |
23 | static DECLARE_COMPLETION(nsim_bus_devs_released); |
24 | |
25 | static struct nsim_bus_dev *to_nsim_bus_dev(struct device *dev) |
26 | { |
27 | return container_of(dev, struct nsim_bus_dev, dev); |
28 | } |
29 | |
30 | static ssize_t |
31 | nsim_bus_dev_numvfs_store(struct device *dev, struct device_attribute *attr, |
32 | const char *buf, size_t count) |
33 | { |
34 | struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev); |
35 | unsigned int num_vfs; |
36 | int ret; |
37 | |
38 | ret = kstrtouint(s: buf, base: 0, res: &num_vfs); |
39 | if (ret) |
40 | return ret; |
41 | |
42 | device_lock(dev); |
43 | ret = -ENOENT; |
44 | if (dev_get_drvdata(dev)) |
45 | ret = nsim_drv_configure_vfs(nsim_bus_dev, num_vfs); |
46 | device_unlock(dev); |
47 | |
48 | return ret ? ret : count; |
49 | } |
50 | |
51 | static ssize_t |
52 | nsim_bus_dev_numvfs_show(struct device *dev, |
53 | struct device_attribute *attr, char *buf) |
54 | { |
55 | struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev); |
56 | |
57 | return sprintf(buf, fmt: "%u\n" , nsim_bus_dev->num_vfs); |
58 | } |
59 | |
60 | static struct device_attribute nsim_bus_dev_numvfs_attr = |
61 | __ATTR(sriov_numvfs, 0664, nsim_bus_dev_numvfs_show, |
62 | nsim_bus_dev_numvfs_store); |
63 | |
64 | static ssize_t |
65 | new_port_store(struct device *dev, struct device_attribute *attr, |
66 | const char *buf, size_t count) |
67 | { |
68 | struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev); |
69 | unsigned int port_index; |
70 | int ret; |
71 | |
72 | /* Prevent to use nsim_bus_dev before initialization. */ |
73 | if (!smp_load_acquire(&nsim_bus_dev->init)) |
74 | return -EBUSY; |
75 | ret = kstrtouint(s: buf, base: 0, res: &port_index); |
76 | if (ret) |
77 | return ret; |
78 | |
79 | ret = nsim_drv_port_add(nsim_bus_dev, type: NSIM_DEV_PORT_TYPE_PF, port_index); |
80 | return ret ? ret : count; |
81 | } |
82 | |
83 | static struct device_attribute nsim_bus_dev_new_port_attr = __ATTR_WO(new_port); |
84 | |
85 | static ssize_t |
86 | del_port_store(struct device *dev, struct device_attribute *attr, |
87 | const char *buf, size_t count) |
88 | { |
89 | struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev); |
90 | unsigned int port_index; |
91 | int ret; |
92 | |
93 | /* Prevent to use nsim_bus_dev before initialization. */ |
94 | if (!smp_load_acquire(&nsim_bus_dev->init)) |
95 | return -EBUSY; |
96 | ret = kstrtouint(s: buf, base: 0, res: &port_index); |
97 | if (ret) |
98 | return ret; |
99 | |
100 | ret = nsim_drv_port_del(nsim_bus_dev, type: NSIM_DEV_PORT_TYPE_PF, port_index); |
101 | return ret ? ret : count; |
102 | } |
103 | |
104 | static struct device_attribute nsim_bus_dev_del_port_attr = __ATTR_WO(del_port); |
105 | |
106 | static struct attribute *nsim_bus_dev_attrs[] = { |
107 | &nsim_bus_dev_numvfs_attr.attr, |
108 | &nsim_bus_dev_new_port_attr.attr, |
109 | &nsim_bus_dev_del_port_attr.attr, |
110 | NULL, |
111 | }; |
112 | |
113 | static const struct attribute_group nsim_bus_dev_attr_group = { |
114 | .attrs = nsim_bus_dev_attrs, |
115 | }; |
116 | |
117 | static const struct attribute_group *nsim_bus_dev_attr_groups[] = { |
118 | &nsim_bus_dev_attr_group, |
119 | NULL, |
120 | }; |
121 | |
122 | static void nsim_bus_dev_release(struct device *dev) |
123 | { |
124 | struct nsim_bus_dev *nsim_bus_dev; |
125 | |
126 | nsim_bus_dev = container_of(dev, struct nsim_bus_dev, dev); |
127 | kfree(objp: nsim_bus_dev); |
128 | if (refcount_dec_and_test(r: &nsim_bus_devs)) |
129 | complete(&nsim_bus_devs_released); |
130 | } |
131 | |
132 | static const struct device_type nsim_bus_dev_type = { |
133 | .groups = nsim_bus_dev_attr_groups, |
134 | .release = nsim_bus_dev_release, |
135 | }; |
136 | |
137 | static struct nsim_bus_dev * |
138 | nsim_bus_dev_new(unsigned int id, unsigned int port_count, unsigned int num_queues); |
139 | |
140 | static ssize_t |
141 | new_device_store(const struct bus_type *bus, const char *buf, size_t count) |
142 | { |
143 | unsigned int id, port_count, num_queues; |
144 | struct nsim_bus_dev *nsim_bus_dev; |
145 | int err; |
146 | |
147 | err = sscanf(buf, "%u %u %u" , &id, &port_count, &num_queues); |
148 | switch (err) { |
149 | case 1: |
150 | port_count = 1; |
151 | fallthrough; |
152 | case 2: |
153 | num_queues = 1; |
154 | fallthrough; |
155 | case 3: |
156 | if (id > INT_MAX) { |
157 | pr_err("Value of \"id\" is too big.\n" ); |
158 | return -EINVAL; |
159 | } |
160 | break; |
161 | default: |
162 | pr_err("Format for adding new device is \"id port_count num_queues\" (uint uint unit).\n" ); |
163 | return -EINVAL; |
164 | } |
165 | |
166 | mutex_lock(&nsim_bus_dev_list_lock); |
167 | /* Prevent to use resource before initialization. */ |
168 | if (!smp_load_acquire(&nsim_bus_enable)) { |
169 | err = -EBUSY; |
170 | goto err; |
171 | } |
172 | |
173 | nsim_bus_dev = nsim_bus_dev_new(id, port_count, num_queues); |
174 | if (IS_ERR(ptr: nsim_bus_dev)) { |
175 | err = PTR_ERR(ptr: nsim_bus_dev); |
176 | goto err; |
177 | } |
178 | |
179 | refcount_inc(r: &nsim_bus_devs); |
180 | /* Allow using nsim_bus_dev */ |
181 | smp_store_release(&nsim_bus_dev->init, true); |
182 | |
183 | list_add_tail(new: &nsim_bus_dev->list, head: &nsim_bus_dev_list); |
184 | mutex_unlock(lock: &nsim_bus_dev_list_lock); |
185 | |
186 | return count; |
187 | err: |
188 | mutex_unlock(lock: &nsim_bus_dev_list_lock); |
189 | return err; |
190 | } |
191 | static BUS_ATTR_WO(new_device); |
192 | |
193 | static void nsim_bus_dev_del(struct nsim_bus_dev *nsim_bus_dev); |
194 | |
195 | static ssize_t |
196 | del_device_store(const struct bus_type *bus, const char *buf, size_t count) |
197 | { |
198 | struct nsim_bus_dev *nsim_bus_dev, *tmp; |
199 | unsigned int id; |
200 | int err; |
201 | |
202 | err = sscanf(buf, "%u" , &id); |
203 | switch (err) { |
204 | case 1: |
205 | if (id > INT_MAX) { |
206 | pr_err("Value of \"id\" is too big.\n" ); |
207 | return -EINVAL; |
208 | } |
209 | break; |
210 | default: |
211 | pr_err("Format for deleting device is \"id\" (uint).\n" ); |
212 | return -EINVAL; |
213 | } |
214 | |
215 | err = -ENOENT; |
216 | mutex_lock(&nsim_bus_dev_list_lock); |
217 | /* Prevent to use resource before initialization. */ |
218 | if (!smp_load_acquire(&nsim_bus_enable)) { |
219 | mutex_unlock(lock: &nsim_bus_dev_list_lock); |
220 | return -EBUSY; |
221 | } |
222 | list_for_each_entry_safe(nsim_bus_dev, tmp, &nsim_bus_dev_list, list) { |
223 | if (nsim_bus_dev->dev.id != id) |
224 | continue; |
225 | list_del(entry: &nsim_bus_dev->list); |
226 | nsim_bus_dev_del(nsim_bus_dev); |
227 | err = 0; |
228 | break; |
229 | } |
230 | mutex_unlock(lock: &nsim_bus_dev_list_lock); |
231 | return !err ? count : err; |
232 | } |
233 | static BUS_ATTR_WO(del_device); |
234 | |
235 | static ssize_t link_device_store(const struct bus_type *bus, const char *buf, size_t count) |
236 | { |
237 | struct netdevsim *nsim_a, *nsim_b, *peer; |
238 | struct net_device *dev_a, *dev_b; |
239 | unsigned int ifidx_a, ifidx_b; |
240 | int netnsfd_a, netnsfd_b, err; |
241 | struct net *ns_a, *ns_b; |
242 | |
243 | err = sscanf(buf, "%d:%u %d:%u" , &netnsfd_a, &ifidx_a, &netnsfd_b, |
244 | &ifidx_b); |
245 | if (err != 4) { |
246 | pr_err("Format for linking two devices is \"netnsfd_a:ifidx_a netnsfd_b:ifidx_b\" (int uint int uint).\n" ); |
247 | return -EINVAL; |
248 | } |
249 | |
250 | ns_a = get_net_ns_by_fd(fd: netnsfd_a); |
251 | if (IS_ERR(ptr: ns_a)) { |
252 | pr_err("Could not find netns with fd: %d\n" , netnsfd_a); |
253 | return -EINVAL; |
254 | } |
255 | |
256 | ns_b = get_net_ns_by_fd(fd: netnsfd_b); |
257 | if (IS_ERR(ptr: ns_b)) { |
258 | pr_err("Could not find netns with fd: %d\n" , netnsfd_b); |
259 | put_net(net: ns_a); |
260 | return -EINVAL; |
261 | } |
262 | |
263 | err = -EINVAL; |
264 | rtnl_lock(); |
265 | dev_a = __dev_get_by_index(net: ns_a, ifindex: ifidx_a); |
266 | if (!dev_a) { |
267 | pr_err("Could not find device with ifindex %u in netnsfd %d\n" , |
268 | ifidx_a, netnsfd_a); |
269 | goto out_err; |
270 | } |
271 | |
272 | if (!netdev_is_nsim(dev: dev_a)) { |
273 | pr_err("Device with ifindex %u in netnsfd %d is not a netdevsim\n" , |
274 | ifidx_a, netnsfd_a); |
275 | goto out_err; |
276 | } |
277 | |
278 | dev_b = __dev_get_by_index(net: ns_b, ifindex: ifidx_b); |
279 | if (!dev_b) { |
280 | pr_err("Could not find device with ifindex %u in netnsfd %d\n" , |
281 | ifidx_b, netnsfd_b); |
282 | goto out_err; |
283 | } |
284 | |
285 | if (!netdev_is_nsim(dev: dev_b)) { |
286 | pr_err("Device with ifindex %u in netnsfd %d is not a netdevsim\n" , |
287 | ifidx_b, netnsfd_b); |
288 | goto out_err; |
289 | } |
290 | |
291 | if (dev_a == dev_b) { |
292 | pr_err("Cannot link a netdevsim to itself\n" ); |
293 | goto out_err; |
294 | } |
295 | |
296 | err = -EBUSY; |
297 | nsim_a = netdev_priv(dev: dev_a); |
298 | peer = rtnl_dereference(nsim_a->peer); |
299 | if (peer) { |
300 | pr_err("Netdevsim %d:%u is already linked\n" , netnsfd_a, |
301 | ifidx_a); |
302 | goto out_err; |
303 | } |
304 | |
305 | nsim_b = netdev_priv(dev: dev_b); |
306 | peer = rtnl_dereference(nsim_b->peer); |
307 | if (peer) { |
308 | pr_err("Netdevsim %d:%u is already linked\n" , netnsfd_b, |
309 | ifidx_b); |
310 | goto out_err; |
311 | } |
312 | |
313 | err = 0; |
314 | rcu_assign_pointer(nsim_a->peer, nsim_b); |
315 | rcu_assign_pointer(nsim_b->peer, nsim_a); |
316 | |
317 | out_err: |
318 | put_net(net: ns_b); |
319 | put_net(net: ns_a); |
320 | rtnl_unlock(); |
321 | |
322 | return !err ? count : err; |
323 | } |
324 | static BUS_ATTR_WO(link_device); |
325 | |
326 | static ssize_t unlink_device_store(const struct bus_type *bus, const char *buf, size_t count) |
327 | { |
328 | struct netdevsim *nsim, *peer; |
329 | struct net_device *dev; |
330 | unsigned int ifidx; |
331 | int netnsfd, err; |
332 | struct net *ns; |
333 | |
334 | err = sscanf(buf, "%u:%u" , &netnsfd, &ifidx); |
335 | if (err != 2) { |
336 | pr_err("Format for unlinking a device is \"netnsfd:ifidx\" (int uint).\n" ); |
337 | return -EINVAL; |
338 | } |
339 | |
340 | ns = get_net_ns_by_fd(fd: netnsfd); |
341 | if (IS_ERR(ptr: ns)) { |
342 | pr_err("Could not find netns with fd: %d\n" , netnsfd); |
343 | return -EINVAL; |
344 | } |
345 | |
346 | err = -EINVAL; |
347 | rtnl_lock(); |
348 | dev = __dev_get_by_index(net: ns, ifindex: ifidx); |
349 | if (!dev) { |
350 | pr_err("Could not find device with ifindex %u in netnsfd %d\n" , |
351 | ifidx, netnsfd); |
352 | goto out_put_netns; |
353 | } |
354 | |
355 | if (!netdev_is_nsim(dev)) { |
356 | pr_err("Device with ifindex %u in netnsfd %d is not a netdevsim\n" , |
357 | ifidx, netnsfd); |
358 | goto out_put_netns; |
359 | } |
360 | |
361 | nsim = netdev_priv(dev); |
362 | peer = rtnl_dereference(nsim->peer); |
363 | if (!peer) |
364 | goto out_put_netns; |
365 | |
366 | err = 0; |
367 | RCU_INIT_POINTER(nsim->peer, NULL); |
368 | RCU_INIT_POINTER(peer->peer, NULL); |
369 | |
370 | out_put_netns: |
371 | put_net(net: ns); |
372 | rtnl_unlock(); |
373 | |
374 | return !err ? count : err; |
375 | } |
376 | static BUS_ATTR_WO(unlink_device); |
377 | |
378 | static struct attribute *nsim_bus_attrs[] = { |
379 | &bus_attr_new_device.attr, |
380 | &bus_attr_del_device.attr, |
381 | &bus_attr_link_device.attr, |
382 | &bus_attr_unlink_device.attr, |
383 | NULL |
384 | }; |
385 | ATTRIBUTE_GROUPS(nsim_bus); |
386 | |
387 | static int nsim_bus_probe(struct device *dev) |
388 | { |
389 | struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev); |
390 | |
391 | return nsim_drv_probe(nsim_bus_dev); |
392 | } |
393 | |
394 | static void nsim_bus_remove(struct device *dev) |
395 | { |
396 | struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev); |
397 | |
398 | nsim_drv_remove(nsim_bus_dev); |
399 | } |
400 | |
401 | static int nsim_num_vf(struct device *dev) |
402 | { |
403 | struct nsim_bus_dev *nsim_bus_dev = to_nsim_bus_dev(dev); |
404 | |
405 | return nsim_bus_dev->num_vfs; |
406 | } |
407 | |
408 | static const struct bus_type nsim_bus = { |
409 | .name = DRV_NAME, |
410 | .dev_name = DRV_NAME, |
411 | .bus_groups = nsim_bus_groups, |
412 | .probe = nsim_bus_probe, |
413 | .remove = nsim_bus_remove, |
414 | .num_vf = nsim_num_vf, |
415 | }; |
416 | |
417 | #define NSIM_BUS_DEV_MAX_VFS 4 |
418 | |
419 | static struct nsim_bus_dev * |
420 | nsim_bus_dev_new(unsigned int id, unsigned int port_count, unsigned int num_queues) |
421 | { |
422 | struct nsim_bus_dev *nsim_bus_dev; |
423 | int err; |
424 | |
425 | nsim_bus_dev = kzalloc(size: sizeof(*nsim_bus_dev), GFP_KERNEL); |
426 | if (!nsim_bus_dev) |
427 | return ERR_PTR(error: -ENOMEM); |
428 | |
429 | err = ida_alloc_range(&nsim_bus_dev_ids, min: id, max: id, GFP_KERNEL); |
430 | if (err < 0) |
431 | goto err_nsim_bus_dev_free; |
432 | nsim_bus_dev->dev.id = err; |
433 | nsim_bus_dev->dev.bus = &nsim_bus; |
434 | nsim_bus_dev->dev.type = &nsim_bus_dev_type; |
435 | nsim_bus_dev->port_count = port_count; |
436 | nsim_bus_dev->num_queues = num_queues; |
437 | nsim_bus_dev->initial_net = current->nsproxy->net_ns; |
438 | nsim_bus_dev->max_vfs = NSIM_BUS_DEV_MAX_VFS; |
439 | /* Disallow using nsim_bus_dev */ |
440 | smp_store_release(&nsim_bus_dev->init, false); |
441 | |
442 | err = device_register(dev: &nsim_bus_dev->dev); |
443 | if (err) |
444 | goto err_nsim_bus_dev_id_free; |
445 | |
446 | return nsim_bus_dev; |
447 | |
448 | err_nsim_bus_dev_id_free: |
449 | ida_free(&nsim_bus_dev_ids, id: nsim_bus_dev->dev.id); |
450 | put_device(dev: &nsim_bus_dev->dev); |
451 | nsim_bus_dev = NULL; |
452 | err_nsim_bus_dev_free: |
453 | kfree(objp: nsim_bus_dev); |
454 | return ERR_PTR(error: err); |
455 | } |
456 | |
457 | static void nsim_bus_dev_del(struct nsim_bus_dev *nsim_bus_dev) |
458 | { |
459 | /* Disallow using nsim_bus_dev */ |
460 | smp_store_release(&nsim_bus_dev->init, false); |
461 | ida_free(&nsim_bus_dev_ids, id: nsim_bus_dev->dev.id); |
462 | device_unregister(dev: &nsim_bus_dev->dev); |
463 | } |
464 | |
465 | static struct device_driver nsim_driver = { |
466 | .name = DRV_NAME, |
467 | .bus = &nsim_bus, |
468 | .owner = THIS_MODULE, |
469 | }; |
470 | |
471 | int nsim_bus_init(void) |
472 | { |
473 | int err; |
474 | |
475 | err = bus_register(bus: &nsim_bus); |
476 | if (err) |
477 | return err; |
478 | err = driver_register(drv: &nsim_driver); |
479 | if (err) |
480 | goto err_bus_unregister; |
481 | refcount_set(r: &nsim_bus_devs, n: 1); |
482 | /* Allow using resources */ |
483 | smp_store_release(&nsim_bus_enable, true); |
484 | return 0; |
485 | |
486 | err_bus_unregister: |
487 | bus_unregister(bus: &nsim_bus); |
488 | return err; |
489 | } |
490 | |
491 | void nsim_bus_exit(void) |
492 | { |
493 | struct nsim_bus_dev *nsim_bus_dev, *tmp; |
494 | |
495 | /* Disallow using resources */ |
496 | smp_store_release(&nsim_bus_enable, false); |
497 | if (refcount_dec_and_test(r: &nsim_bus_devs)) |
498 | complete(&nsim_bus_devs_released); |
499 | |
500 | mutex_lock(&nsim_bus_dev_list_lock); |
501 | list_for_each_entry_safe(nsim_bus_dev, tmp, &nsim_bus_dev_list, list) { |
502 | list_del(entry: &nsim_bus_dev->list); |
503 | nsim_bus_dev_del(nsim_bus_dev); |
504 | } |
505 | mutex_unlock(lock: &nsim_bus_dev_list_lock); |
506 | |
507 | wait_for_completion(&nsim_bus_devs_released); |
508 | |
509 | driver_unregister(drv: &nsim_driver); |
510 | bus_unregister(bus: &nsim_bus); |
511 | } |
512 | |