Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1/*
2 * Copyright (c) 2018 Cumulus Networks. All rights reserved.
3 * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com>
4 * Copyright (c) 2019 Mellanox Technologies. All rights reserved.
5 *
6 * This software is licensed under the GNU General License Version 2,
7 * June 1991 as shown in the file COPYING in the top-level directory of this
8 * source tree.
9 *
10 * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
11 * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
12 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
13 * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
14 * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
15 * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16 */
17
18#include <linux/debugfs.h>
19#include <linux/device.h>
20#include <linux/list.h>
21#include <linux/mutex.h>
22#include <linux/random.h>
23#include <linux/rtnetlink.h>
24#include <net/devlink.h>
25
26#include "netdevsim.h"
27
28static struct dentry *nsim_dev_ddir;
29
30static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev)
31{
32 char dev_ddir_name[16];
33
34 sprintf(dev_ddir_name, DRV_NAME "%u", nsim_dev->nsim_bus_dev->dev.id);
35 nsim_dev->ddir = debugfs_create_dir(dev_ddir_name, nsim_dev_ddir);
36 if (IS_ERR_OR_NULL(nsim_dev->ddir))
37 return PTR_ERR_OR_ZERO(nsim_dev->ddir) ?: -EINVAL;
38 nsim_dev->ports_ddir = debugfs_create_dir("ports", nsim_dev->ddir);
39 if (IS_ERR_OR_NULL(nsim_dev->ports_ddir))
40 return PTR_ERR_OR_ZERO(nsim_dev->ports_ddir) ?: -EINVAL;
41 debugfs_create_bool("fw_update_status", 0600, nsim_dev->ddir,
42 &nsim_dev->fw_update_status);
43 return 0;
44}
45
46static void nsim_dev_debugfs_exit(struct nsim_dev *nsim_dev)
47{
48 debugfs_remove_recursive(nsim_dev->ports_ddir);
49 debugfs_remove_recursive(nsim_dev->ddir);
50}
51
52static int nsim_dev_port_debugfs_init(struct nsim_dev *nsim_dev,
53 struct nsim_dev_port *nsim_dev_port)
54{
55 char port_ddir_name[16];
56 char dev_link_name[32];
57
58 sprintf(port_ddir_name, "%u", nsim_dev_port->port_index);
59 nsim_dev_port->ddir = debugfs_create_dir(port_ddir_name,
60 nsim_dev->ports_ddir);
61 if (IS_ERR_OR_NULL(nsim_dev_port->ddir))
62 return -ENOMEM;
63
64 sprintf(dev_link_name, "../../../" DRV_NAME "%u",
65 nsim_dev->nsim_bus_dev->dev.id);
66 debugfs_create_symlink("dev", nsim_dev_port->ddir, dev_link_name);
67
68 return 0;
69}
70
71static void nsim_dev_port_debugfs_exit(struct nsim_dev_port *nsim_dev_port)
72{
73 debugfs_remove_recursive(nsim_dev_port->ddir);
74}
75
76static u64 nsim_dev_ipv4_fib_resource_occ_get(void *priv)
77{
78 struct nsim_dev *nsim_dev = priv;
79
80 return nsim_fib_get_val(nsim_dev->fib_data,
81 NSIM_RESOURCE_IPV4_FIB, false);
82}
83
84static u64 nsim_dev_ipv4_fib_rules_res_occ_get(void *priv)
85{
86 struct nsim_dev *nsim_dev = priv;
87
88 return nsim_fib_get_val(nsim_dev->fib_data,
89 NSIM_RESOURCE_IPV4_FIB_RULES, false);
90}
91
92static u64 nsim_dev_ipv6_fib_resource_occ_get(void *priv)
93{
94 struct nsim_dev *nsim_dev = priv;
95
96 return nsim_fib_get_val(nsim_dev->fib_data,
97 NSIM_RESOURCE_IPV6_FIB, false);
98}
99
100static u64 nsim_dev_ipv6_fib_rules_res_occ_get(void *priv)
101{
102 struct nsim_dev *nsim_dev = priv;
103
104 return nsim_fib_get_val(nsim_dev->fib_data,
105 NSIM_RESOURCE_IPV6_FIB_RULES, false);
106}
107
108static int nsim_dev_resources_register(struct devlink *devlink)
109{
110 struct nsim_dev *nsim_dev = devlink_priv(devlink);
111 struct devlink_resource_size_params params = {
112 .size_max = (u64)-1,
113 .size_granularity = 1,
114 .unit = DEVLINK_RESOURCE_UNIT_ENTRY
115 };
116 int err;
117 u64 n;
118
119 /* Resources for IPv4 */
120 err = devlink_resource_register(devlink, "IPv4", (u64)-1,
121 NSIM_RESOURCE_IPV4,
122 DEVLINK_RESOURCE_ID_PARENT_TOP,
123 ¶ms);
124 if (err) {
125 pr_err("Failed to register IPv4 top resource\n");
126 goto out;
127 }
128
129 n = nsim_fib_get_val(nsim_dev->fib_data,
130 NSIM_RESOURCE_IPV4_FIB, true);
131 err = devlink_resource_register(devlink, "fib", n,
132 NSIM_RESOURCE_IPV4_FIB,
133 NSIM_RESOURCE_IPV4, ¶ms);
134 if (err) {
135 pr_err("Failed to register IPv4 FIB resource\n");
136 return err;
137 }
138
139 n = nsim_fib_get_val(nsim_dev->fib_data,
140 NSIM_RESOURCE_IPV4_FIB_RULES, true);
141 err = devlink_resource_register(devlink, "fib-rules", n,
142 NSIM_RESOURCE_IPV4_FIB_RULES,
143 NSIM_RESOURCE_IPV4, ¶ms);
144 if (err) {
145 pr_err("Failed to register IPv4 FIB rules resource\n");
146 return err;
147 }
148
149 /* Resources for IPv6 */
150 err = devlink_resource_register(devlink, "IPv6", (u64)-1,
151 NSIM_RESOURCE_IPV6,
152 DEVLINK_RESOURCE_ID_PARENT_TOP,
153 ¶ms);
154 if (err) {
155 pr_err("Failed to register IPv6 top resource\n");
156 goto out;
157 }
158
159 n = nsim_fib_get_val(nsim_dev->fib_data,
160 NSIM_RESOURCE_IPV6_FIB, true);
161 err = devlink_resource_register(devlink, "fib", n,
162 NSIM_RESOURCE_IPV6_FIB,
163 NSIM_RESOURCE_IPV6, ¶ms);
164 if (err) {
165 pr_err("Failed to register IPv6 FIB resource\n");
166 return err;
167 }
168
169 n = nsim_fib_get_val(nsim_dev->fib_data,
170 NSIM_RESOURCE_IPV6_FIB_RULES, true);
171 err = devlink_resource_register(devlink, "fib-rules", n,
172 NSIM_RESOURCE_IPV6_FIB_RULES,
173 NSIM_RESOURCE_IPV6, ¶ms);
174 if (err) {
175 pr_err("Failed to register IPv6 FIB rules resource\n");
176 return err;
177 }
178
179 devlink_resource_occ_get_register(devlink,
180 NSIM_RESOURCE_IPV4_FIB,
181 nsim_dev_ipv4_fib_resource_occ_get,
182 nsim_dev);
183 devlink_resource_occ_get_register(devlink,
184 NSIM_RESOURCE_IPV4_FIB_RULES,
185 nsim_dev_ipv4_fib_rules_res_occ_get,
186 nsim_dev);
187 devlink_resource_occ_get_register(devlink,
188 NSIM_RESOURCE_IPV6_FIB,
189 nsim_dev_ipv6_fib_resource_occ_get,
190 nsim_dev);
191 devlink_resource_occ_get_register(devlink,
192 NSIM_RESOURCE_IPV6_FIB_RULES,
193 nsim_dev_ipv6_fib_rules_res_occ_get,
194 nsim_dev);
195out:
196 return err;
197}
198
199static int nsim_dev_reload(struct devlink *devlink,
200 struct netlink_ext_ack *extack)
201{
202 struct nsim_dev *nsim_dev = devlink_priv(devlink);
203 enum nsim_resource_id res_ids[] = {
204 NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
205 NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
206 };
207 int i;
208
209 for (i = 0; i < ARRAY_SIZE(res_ids); ++i) {
210 int err;
211 u64 val;
212
213 err = devlink_resource_size_get(devlink, res_ids[i], &val);
214 if (!err) {
215 err = nsim_fib_set_max(nsim_dev->fib_data,
216 res_ids[i], val, extack);
217 if (err)
218 return err;
219 }
220 }
221
222 return 0;
223}
224
225#define NSIM_DEV_FLASH_SIZE 500000
226#define NSIM_DEV_FLASH_CHUNK_SIZE 1000
227#define NSIM_DEV_FLASH_CHUNK_TIME_MS 10
228
229static int nsim_dev_flash_update(struct devlink *devlink, const char *file_name,
230 const char *component,
231 struct netlink_ext_ack *extack)
232{
233 struct nsim_dev *nsim_dev = devlink_priv(devlink);
234 int i;
235
236 if (nsim_dev->fw_update_status) {
237 devlink_flash_update_begin_notify(devlink);
238 devlink_flash_update_status_notify(devlink,
239 "Preparing to flash",
240 component, 0, 0);
241 }
242
243 for (i = 0; i < NSIM_DEV_FLASH_SIZE / NSIM_DEV_FLASH_CHUNK_SIZE; i++) {
244 if (nsim_dev->fw_update_status)
245 devlink_flash_update_status_notify(devlink, "Flashing",
246 component,
247 i * NSIM_DEV_FLASH_CHUNK_SIZE,
248 NSIM_DEV_FLASH_SIZE);
249 msleep(NSIM_DEV_FLASH_CHUNK_TIME_MS);
250 }
251
252 if (nsim_dev->fw_update_status) {
253 devlink_flash_update_status_notify(devlink, "Flashing",
254 component,
255 NSIM_DEV_FLASH_SIZE,
256 NSIM_DEV_FLASH_SIZE);
257 devlink_flash_update_status_notify(devlink, "Flashing done",
258 component, 0, 0);
259 devlink_flash_update_end_notify(devlink);
260 }
261
262 return 0;
263}
264
265static const struct devlink_ops nsim_dev_devlink_ops = {
266 .reload = nsim_dev_reload,
267 .flash_update = nsim_dev_flash_update,
268};
269
270static struct nsim_dev *
271nsim_dev_create(struct nsim_bus_dev *nsim_bus_dev, unsigned int port_count)
272{
273 struct nsim_dev *nsim_dev;
274 struct devlink *devlink;
275 int err;
276
277 devlink = devlink_alloc(&nsim_dev_devlink_ops, sizeof(*nsim_dev));
278 if (!devlink)
279 return ERR_PTR(-ENOMEM);
280 nsim_dev = devlink_priv(devlink);
281 nsim_dev->nsim_bus_dev = nsim_bus_dev;
282 nsim_dev->switch_id.id_len = sizeof(nsim_dev->switch_id.id);
283 get_random_bytes(nsim_dev->switch_id.id, nsim_dev->switch_id.id_len);
284 INIT_LIST_HEAD(&nsim_dev->port_list);
285 mutex_init(&nsim_dev->port_list_lock);
286 nsim_dev->fw_update_status = true;
287
288 nsim_dev->fib_data = nsim_fib_create();
289 if (IS_ERR(nsim_dev->fib_data)) {
290 err = PTR_ERR(nsim_dev->fib_data);
291 goto err_devlink_free;
292 }
293
294 err = nsim_dev_resources_register(devlink);
295 if (err)
296 goto err_fib_destroy;
297
298 err = devlink_register(devlink, &nsim_bus_dev->dev);
299 if (err)
300 goto err_resources_unregister;
301
302 err = nsim_dev_debugfs_init(nsim_dev);
303 if (err)
304 goto err_dl_unregister;
305
306 err = nsim_bpf_dev_init(nsim_dev);
307 if (err)
308 goto err_debugfs_exit;
309
310 return nsim_dev;
311
312err_debugfs_exit:
313 nsim_dev_debugfs_exit(nsim_dev);
314err_dl_unregister:
315 devlink_unregister(devlink);
316err_resources_unregister:
317 devlink_resources_unregister(devlink, NULL);
318err_fib_destroy:
319 nsim_fib_destroy(nsim_dev->fib_data);
320err_devlink_free:
321 devlink_free(devlink);
322 return ERR_PTR(err);
323}
324
325static void nsim_dev_destroy(struct nsim_dev *nsim_dev)
326{
327 struct devlink *devlink = priv_to_devlink(nsim_dev);
328
329 nsim_bpf_dev_exit(nsim_dev);
330 nsim_dev_debugfs_exit(nsim_dev);
331 devlink_unregister(devlink);
332 devlink_resources_unregister(devlink, NULL);
333 nsim_fib_destroy(nsim_dev->fib_data);
334 mutex_destroy(&nsim_dev->port_list_lock);
335 devlink_free(devlink);
336}
337
338static int __nsim_dev_port_add(struct nsim_dev *nsim_dev,
339 unsigned int port_index)
340{
341 struct nsim_dev_port *nsim_dev_port;
342 struct devlink_port *devlink_port;
343 int err;
344
345 nsim_dev_port = kzalloc(sizeof(*nsim_dev_port), GFP_KERNEL);
346 if (!nsim_dev_port)
347 return -ENOMEM;
348 nsim_dev_port->port_index = port_index;
349
350 devlink_port = &nsim_dev_port->devlink_port;
351 devlink_port_attrs_set(devlink_port, DEVLINK_PORT_FLAVOUR_PHYSICAL,
352 port_index + 1, 0, 0,
353 nsim_dev->switch_id.id,
354 nsim_dev->switch_id.id_len);
355 err = devlink_port_register(priv_to_devlink(nsim_dev), devlink_port,
356 port_index);
357 if (err)
358 goto err_port_free;
359
360 err = nsim_dev_port_debugfs_init(nsim_dev, nsim_dev_port);
361 if (err)
362 goto err_dl_port_unregister;
363
364 nsim_dev_port->ns = nsim_create(nsim_dev, nsim_dev_port);
365 if (IS_ERR(nsim_dev_port->ns)) {
366 err = PTR_ERR(nsim_dev_port->ns);
367 goto err_port_debugfs_exit;
368 }
369
370 devlink_port_type_eth_set(devlink_port, nsim_dev_port->ns->netdev);
371 list_add(&nsim_dev_port->list, &nsim_dev->port_list);
372
373 return 0;
374
375err_port_debugfs_exit:
376 nsim_dev_port_debugfs_exit(nsim_dev_port);
377err_dl_port_unregister:
378 devlink_port_unregister(devlink_port);
379err_port_free:
380 kfree(nsim_dev_port);
381 return err;
382}
383
384static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port)
385{
386 struct devlink_port *devlink_port = &nsim_dev_port->devlink_port;
387
388 list_del(&nsim_dev_port->list);
389 devlink_port_type_clear(devlink_port);
390 nsim_destroy(nsim_dev_port->ns);
391 nsim_dev_port_debugfs_exit(nsim_dev_port);
392 devlink_port_unregister(devlink_port);
393 kfree(nsim_dev_port);
394}
395
396static void nsim_dev_port_del_all(struct nsim_dev *nsim_dev)
397{
398 struct nsim_dev_port *nsim_dev_port, *tmp;
399
400 list_for_each_entry_safe(nsim_dev_port, tmp,
401 &nsim_dev->port_list, list)
402 __nsim_dev_port_del(nsim_dev_port);
403}
404
405int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev)
406{
407 struct nsim_dev *nsim_dev;
408 int i;
409 int err;
410
411 nsim_dev = nsim_dev_create(nsim_bus_dev, nsim_bus_dev->port_count);
412 if (IS_ERR(nsim_dev))
413 return PTR_ERR(nsim_dev);
414 dev_set_drvdata(&nsim_bus_dev->dev, nsim_dev);
415
416 for (i = 0; i < nsim_bus_dev->port_count; i++) {
417 err = __nsim_dev_port_add(nsim_dev, i);
418 if (err)
419 goto err_port_del_all;
420 }
421 return 0;
422
423err_port_del_all:
424 nsim_dev_port_del_all(nsim_dev);
425 nsim_dev_destroy(nsim_dev);
426 return err;
427}
428
429void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev)
430{
431 struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
432
433 nsim_dev_port_del_all(nsim_dev);
434 nsim_dev_destroy(nsim_dev);
435}
436
437static struct nsim_dev_port *
438__nsim_dev_port_lookup(struct nsim_dev *nsim_dev, unsigned int port_index)
439{
440 struct nsim_dev_port *nsim_dev_port;
441
442 list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list)
443 if (nsim_dev_port->port_index == port_index)
444 return nsim_dev_port;
445 return NULL;
446}
447
448int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev,
449 unsigned int port_index)
450{
451 struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
452 int err;
453
454 mutex_lock(&nsim_dev->port_list_lock);
455 if (__nsim_dev_port_lookup(nsim_dev, port_index))
456 err = -EEXIST;
457 else
458 err = __nsim_dev_port_add(nsim_dev, port_index);
459 mutex_unlock(&nsim_dev->port_list_lock);
460 return err;
461}
462
463int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev,
464 unsigned int port_index)
465{
466 struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
467 struct nsim_dev_port *nsim_dev_port;
468 int err = 0;
469
470 mutex_lock(&nsim_dev->port_list_lock);
471 nsim_dev_port = __nsim_dev_port_lookup(nsim_dev, port_index);
472 if (!nsim_dev_port)
473 err = -ENOENT;
474 else
475 __nsim_dev_port_del(nsim_dev_port);
476 mutex_unlock(&nsim_dev->port_list_lock);
477 return err;
478}
479
480int nsim_dev_init(void)
481{
482 nsim_dev_ddir = debugfs_create_dir(DRV_NAME, NULL);
483 if (IS_ERR_OR_NULL(nsim_dev_ddir))
484 return -ENOMEM;
485 return 0;
486}
487
488void nsim_dev_exit(void)
489{
490 debugfs_remove_recursive(nsim_dev_ddir);
491}