blob: 9bbecf4d159b45a771b1cd0ecc4aa10bc638ea02 [file] [log] [blame]
David Brazdil0f672f62019-12-10 10:32:29 +00001/*
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/etherdevice.h>
21#include <linux/inet.h>
22#include <linux/jiffies.h>
23#include <linux/kernel.h>
24#include <linux/list.h>
25#include <linux/mutex.h>
26#include <linux/random.h>
27#include <linux/rtnetlink.h>
28#include <linux/workqueue.h>
29#include <net/devlink.h>
30#include <net/ip.h>
Olivier Deprez157378f2022-04-04 15:47:50 +020031#include <net/flow_offload.h>
David Brazdil0f672f62019-12-10 10:32:29 +000032#include <uapi/linux/devlink.h>
33#include <uapi/linux/ip.h>
34#include <uapi/linux/udp.h>
35
36#include "netdevsim.h"
37
38static struct dentry *nsim_dev_ddir;
39
40#define NSIM_DEV_DUMMY_REGION_SIZE (1024 * 32)
41
Olivier Deprez157378f2022-04-04 15:47:50 +020042static int
43nsim_dev_take_snapshot(struct devlink *devlink,
44 const struct devlink_region_ops *ops,
45 struct netlink_ext_ack *extack,
46 u8 **data)
David Brazdil0f672f62019-12-10 10:32:29 +000047{
David Brazdil0f672f62019-12-10 10:32:29 +000048 void *dummy_data;
David Brazdil0f672f62019-12-10 10:32:29 +000049
50 dummy_data = kmalloc(NSIM_DEV_DUMMY_REGION_SIZE, GFP_KERNEL);
51 if (!dummy_data)
52 return -ENOMEM;
53
54 get_random_bytes(dummy_data, NSIM_DEV_DUMMY_REGION_SIZE);
55
Olivier Deprez157378f2022-04-04 15:47:50 +020056 *data = dummy_data;
57
58 return 0;
59}
60
61static ssize_t nsim_dev_take_snapshot_write(struct file *file,
62 const char __user *data,
63 size_t count, loff_t *ppos)
64{
65 struct nsim_dev *nsim_dev = file->private_data;
66 struct devlink *devlink;
67 u8 *dummy_data;
68 int err;
69 u32 id;
70
71 devlink = priv_to_devlink(nsim_dev);
72
73 err = nsim_dev_take_snapshot(devlink, NULL, NULL, &dummy_data);
74 if (err)
75 return err;
76
77 err = devlink_region_snapshot_id_get(devlink, &id);
78 if (err) {
79 pr_err("Failed to get snapshot id\n");
80 kfree(dummy_data);
81 return err;
82 }
David Brazdil0f672f62019-12-10 10:32:29 +000083 err = devlink_region_snapshot_create(nsim_dev->dummy_region,
Olivier Deprez157378f2022-04-04 15:47:50 +020084 dummy_data, id);
85 devlink_region_snapshot_id_put(devlink, id);
David Brazdil0f672f62019-12-10 10:32:29 +000086 if (err) {
87 pr_err("Failed to create region snapshot\n");
88 kfree(dummy_data);
89 return err;
90 }
91
92 return count;
93}
94
95static const struct file_operations nsim_dev_take_snapshot_fops = {
96 .open = simple_open,
97 .write = nsim_dev_take_snapshot_write,
98 .llseek = generic_file_llseek,
Olivier Deprez157378f2022-04-04 15:47:50 +020099 .owner = THIS_MODULE,
100};
101
102static ssize_t nsim_dev_trap_fa_cookie_read(struct file *file,
103 char __user *data,
104 size_t count, loff_t *ppos)
105{
106 struct nsim_dev *nsim_dev = file->private_data;
107 struct flow_action_cookie *fa_cookie;
108 unsigned int buf_len;
109 ssize_t ret;
110 char *buf;
111
112 spin_lock(&nsim_dev->fa_cookie_lock);
113 fa_cookie = nsim_dev->fa_cookie;
114 if (!fa_cookie) {
115 ret = -EINVAL;
116 goto errout;
117 }
118 buf_len = fa_cookie->cookie_len * 2;
119 buf = kmalloc(buf_len, GFP_ATOMIC);
120 if (!buf) {
121 ret = -ENOMEM;
122 goto errout;
123 }
124 bin2hex(buf, fa_cookie->cookie, fa_cookie->cookie_len);
125 spin_unlock(&nsim_dev->fa_cookie_lock);
126
127 ret = simple_read_from_buffer(data, count, ppos, buf, buf_len);
128
129 kfree(buf);
130 return ret;
131
132errout:
133 spin_unlock(&nsim_dev->fa_cookie_lock);
134 return ret;
135}
136
137static ssize_t nsim_dev_trap_fa_cookie_write(struct file *file,
138 const char __user *data,
139 size_t count, loff_t *ppos)
140{
141 struct nsim_dev *nsim_dev = file->private_data;
142 struct flow_action_cookie *fa_cookie;
143 size_t cookie_len;
144 ssize_t ret;
145 char *buf;
146
147 if (*ppos != 0)
148 return -EINVAL;
149 cookie_len = (count - 1) / 2;
150 if ((count - 1) % 2)
151 return -EINVAL;
152 buf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN);
153 if (!buf)
154 return -ENOMEM;
155
156 ret = simple_write_to_buffer(buf, count, ppos, data, count);
157 if (ret < 0)
158 goto free_buf;
159
160 fa_cookie = kmalloc(sizeof(*fa_cookie) + cookie_len,
161 GFP_KERNEL | __GFP_NOWARN);
162 if (!fa_cookie) {
163 ret = -ENOMEM;
164 goto free_buf;
165 }
166
167 fa_cookie->cookie_len = cookie_len;
168 ret = hex2bin(fa_cookie->cookie, buf, cookie_len);
169 if (ret)
170 goto free_fa_cookie;
171 kfree(buf);
172
173 spin_lock(&nsim_dev->fa_cookie_lock);
174 kfree(nsim_dev->fa_cookie);
175 nsim_dev->fa_cookie = fa_cookie;
176 spin_unlock(&nsim_dev->fa_cookie_lock);
177
178 return count;
179
180free_fa_cookie:
181 kfree(fa_cookie);
182free_buf:
183 kfree(buf);
184 return ret;
185}
186
187static const struct file_operations nsim_dev_trap_fa_cookie_fops = {
188 .open = simple_open,
189 .read = nsim_dev_trap_fa_cookie_read,
190 .write = nsim_dev_trap_fa_cookie_write,
191 .llseek = generic_file_llseek,
192 .owner = THIS_MODULE,
David Brazdil0f672f62019-12-10 10:32:29 +0000193};
194
195static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev)
196{
Olivier Deprez0e641232021-09-23 10:07:05 +0200197 char dev_ddir_name[sizeof(DRV_NAME) + 10];
David Brazdil0f672f62019-12-10 10:32:29 +0000198
199 sprintf(dev_ddir_name, DRV_NAME "%u", nsim_dev->nsim_bus_dev->dev.id);
200 nsim_dev->ddir = debugfs_create_dir(dev_ddir_name, nsim_dev_ddir);
Olivier Deprez157378f2022-04-04 15:47:50 +0200201 if (IS_ERR(nsim_dev->ddir))
202 return PTR_ERR(nsim_dev->ddir);
David Brazdil0f672f62019-12-10 10:32:29 +0000203 nsim_dev->ports_ddir = debugfs_create_dir("ports", nsim_dev->ddir);
Olivier Deprez157378f2022-04-04 15:47:50 +0200204 if (IS_ERR(nsim_dev->ports_ddir))
205 return PTR_ERR(nsim_dev->ports_ddir);
David Brazdil0f672f62019-12-10 10:32:29 +0000206 debugfs_create_bool("fw_update_status", 0600, nsim_dev->ddir,
207 &nsim_dev->fw_update_status);
Olivier Deprez157378f2022-04-04 15:47:50 +0200208 debugfs_create_u32("fw_update_overwrite_mask", 0600, nsim_dev->ddir,
209 &nsim_dev->fw_update_overwrite_mask);
David Brazdil0f672f62019-12-10 10:32:29 +0000210 debugfs_create_u32("max_macs", 0600, nsim_dev->ddir,
211 &nsim_dev->max_macs);
212 debugfs_create_bool("test1", 0600, nsim_dev->ddir,
213 &nsim_dev->test1);
Olivier Deprez157378f2022-04-04 15:47:50 +0200214 nsim_dev->take_snapshot = debugfs_create_file("take_snapshot",
215 0200,
216 nsim_dev->ddir,
217 nsim_dev,
218 &nsim_dev_take_snapshot_fops);
219 debugfs_create_bool("dont_allow_reload", 0600, nsim_dev->ddir,
220 &nsim_dev->dont_allow_reload);
221 debugfs_create_bool("fail_reload", 0600, nsim_dev->ddir,
222 &nsim_dev->fail_reload);
223 debugfs_create_file("trap_flow_action_cookie", 0600, nsim_dev->ddir,
224 nsim_dev, &nsim_dev_trap_fa_cookie_fops);
225 debugfs_create_bool("fail_trap_group_set", 0600,
226 nsim_dev->ddir,
227 &nsim_dev->fail_trap_group_set);
228 debugfs_create_bool("fail_trap_policer_set", 0600,
229 nsim_dev->ddir,
230 &nsim_dev->fail_trap_policer_set);
231 debugfs_create_bool("fail_trap_policer_counter_get", 0600,
232 nsim_dev->ddir,
233 &nsim_dev->fail_trap_policer_counter_get);
234 nsim_udp_tunnels_debugfs_create(nsim_dev);
David Brazdil0f672f62019-12-10 10:32:29 +0000235 return 0;
236}
237
238static void nsim_dev_debugfs_exit(struct nsim_dev *nsim_dev)
239{
240 debugfs_remove_recursive(nsim_dev->ports_ddir);
241 debugfs_remove_recursive(nsim_dev->ddir);
242}
243
244static int nsim_dev_port_debugfs_init(struct nsim_dev *nsim_dev,
245 struct nsim_dev_port *nsim_dev_port)
246{
247 char port_ddir_name[16];
248 char dev_link_name[32];
249
250 sprintf(port_ddir_name, "%u", nsim_dev_port->port_index);
251 nsim_dev_port->ddir = debugfs_create_dir(port_ddir_name,
252 nsim_dev->ports_ddir);
Olivier Deprez157378f2022-04-04 15:47:50 +0200253 if (IS_ERR(nsim_dev_port->ddir))
254 return PTR_ERR(nsim_dev_port->ddir);
David Brazdil0f672f62019-12-10 10:32:29 +0000255
256 sprintf(dev_link_name, "../../../" DRV_NAME "%u",
257 nsim_dev->nsim_bus_dev->dev.id);
258 debugfs_create_symlink("dev", nsim_dev_port->ddir, dev_link_name);
259
260 return 0;
261}
262
263static void nsim_dev_port_debugfs_exit(struct nsim_dev_port *nsim_dev_port)
264{
265 debugfs_remove_recursive(nsim_dev_port->ddir);
266}
267
David Brazdil0f672f62019-12-10 10:32:29 +0000268static int nsim_dev_resources_register(struct devlink *devlink)
269{
270 struct devlink_resource_size_params params = {
271 .size_max = (u64)-1,
272 .size_granularity = 1,
273 .unit = DEVLINK_RESOURCE_UNIT_ENTRY
274 };
David Brazdil0f672f62019-12-10 10:32:29 +0000275 int err;
David Brazdil0f672f62019-12-10 10:32:29 +0000276
277 /* Resources for IPv4 */
278 err = devlink_resource_register(devlink, "IPv4", (u64)-1,
279 NSIM_RESOURCE_IPV4,
280 DEVLINK_RESOURCE_ID_PARENT_TOP,
281 &params);
282 if (err) {
283 pr_err("Failed to register IPv4 top resource\n");
284 goto out;
285 }
286
Olivier Deprez157378f2022-04-04 15:47:50 +0200287 err = devlink_resource_register(devlink, "fib", (u64)-1,
David Brazdil0f672f62019-12-10 10:32:29 +0000288 NSIM_RESOURCE_IPV4_FIB,
289 NSIM_RESOURCE_IPV4, &params);
290 if (err) {
291 pr_err("Failed to register IPv4 FIB resource\n");
292 return err;
293 }
294
Olivier Deprez157378f2022-04-04 15:47:50 +0200295 err = devlink_resource_register(devlink, "fib-rules", (u64)-1,
David Brazdil0f672f62019-12-10 10:32:29 +0000296 NSIM_RESOURCE_IPV4_FIB_RULES,
297 NSIM_RESOURCE_IPV4, &params);
298 if (err) {
299 pr_err("Failed to register IPv4 FIB rules resource\n");
300 return err;
301 }
302
303 /* Resources for IPv6 */
304 err = devlink_resource_register(devlink, "IPv6", (u64)-1,
305 NSIM_RESOURCE_IPV6,
306 DEVLINK_RESOURCE_ID_PARENT_TOP,
307 &params);
308 if (err) {
309 pr_err("Failed to register IPv6 top resource\n");
310 goto out;
311 }
312
Olivier Deprez157378f2022-04-04 15:47:50 +0200313 err = devlink_resource_register(devlink, "fib", (u64)-1,
David Brazdil0f672f62019-12-10 10:32:29 +0000314 NSIM_RESOURCE_IPV6_FIB,
315 NSIM_RESOURCE_IPV6, &params);
316 if (err) {
317 pr_err("Failed to register IPv6 FIB resource\n");
318 return err;
319 }
320
Olivier Deprez157378f2022-04-04 15:47:50 +0200321 err = devlink_resource_register(devlink, "fib-rules", (u64)-1,
David Brazdil0f672f62019-12-10 10:32:29 +0000322 NSIM_RESOURCE_IPV6_FIB_RULES,
323 NSIM_RESOURCE_IPV6, &params);
324 if (err) {
325 pr_err("Failed to register IPv6 FIB rules resource\n");
326 return err;
327 }
328
David Brazdil0f672f62019-12-10 10:32:29 +0000329out:
330 return err;
331}
332
333enum nsim_devlink_param_id {
334 NSIM_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
335 NSIM_DEVLINK_PARAM_ID_TEST1,
336};
337
338static const struct devlink_param nsim_devlink_params[] = {
339 DEVLINK_PARAM_GENERIC(MAX_MACS,
340 BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
341 NULL, NULL, NULL),
342 DEVLINK_PARAM_DRIVER(NSIM_DEVLINK_PARAM_ID_TEST1,
343 "test1", DEVLINK_PARAM_TYPE_BOOL,
344 BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
345 NULL, NULL, NULL),
346};
347
348static void nsim_devlink_set_params_init_values(struct nsim_dev *nsim_dev,
349 struct devlink *devlink)
350{
351 union devlink_param_value value;
352
353 value.vu32 = nsim_dev->max_macs;
354 devlink_param_driverinit_value_set(devlink,
355 DEVLINK_PARAM_GENERIC_ID_MAX_MACS,
356 value);
357 value.vbool = nsim_dev->test1;
358 devlink_param_driverinit_value_set(devlink,
359 NSIM_DEVLINK_PARAM_ID_TEST1,
360 value);
361}
362
363static void nsim_devlink_param_load_driverinit_values(struct devlink *devlink)
364{
365 struct nsim_dev *nsim_dev = devlink_priv(devlink);
366 union devlink_param_value saved_value;
367 int err;
368
369 err = devlink_param_driverinit_value_get(devlink,
370 DEVLINK_PARAM_GENERIC_ID_MAX_MACS,
371 &saved_value);
372 if (!err)
373 nsim_dev->max_macs = saved_value.vu32;
374 err = devlink_param_driverinit_value_get(devlink,
375 NSIM_DEVLINK_PARAM_ID_TEST1,
376 &saved_value);
377 if (!err)
378 nsim_dev->test1 = saved_value.vbool;
379}
380
381#define NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX 16
382
Olivier Deprez157378f2022-04-04 15:47:50 +0200383static const struct devlink_region_ops dummy_region_ops = {
384 .name = "dummy",
385 .destructor = &kfree,
386 .snapshot = nsim_dev_take_snapshot,
387};
388
David Brazdil0f672f62019-12-10 10:32:29 +0000389static int nsim_dev_dummy_region_init(struct nsim_dev *nsim_dev,
390 struct devlink *devlink)
391{
392 nsim_dev->dummy_region =
Olivier Deprez157378f2022-04-04 15:47:50 +0200393 devlink_region_create(devlink, &dummy_region_ops,
David Brazdil0f672f62019-12-10 10:32:29 +0000394 NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX,
395 NSIM_DEV_DUMMY_REGION_SIZE);
396 return PTR_ERR_OR_ZERO(nsim_dev->dummy_region);
397}
398
399static void nsim_dev_dummy_region_exit(struct nsim_dev *nsim_dev)
400{
401 devlink_region_destroy(nsim_dev->dummy_region);
402}
403
404struct nsim_trap_item {
405 void *trap_ctx;
406 enum devlink_trap_action action;
407};
408
409struct nsim_trap_data {
410 struct delayed_work trap_report_dw;
411 struct nsim_trap_item *trap_items_arr;
Olivier Deprez157378f2022-04-04 15:47:50 +0200412 u64 *trap_policers_cnt_arr;
David Brazdil0f672f62019-12-10 10:32:29 +0000413 struct nsim_dev *nsim_dev;
414 spinlock_t trap_lock; /* Protects trap_items_arr */
415};
416
417/* All driver-specific traps must be documented in
Olivier Deprez157378f2022-04-04 15:47:50 +0200418 * Documentation/networking/devlink/netdevsim.rst
David Brazdil0f672f62019-12-10 10:32:29 +0000419 */
420enum {
421 NSIM_TRAP_ID_BASE = DEVLINK_TRAP_GENERIC_ID_MAX,
422 NSIM_TRAP_ID_FID_MISS,
423};
424
425#define NSIM_TRAP_NAME_FID_MISS "fid_miss"
426
427#define NSIM_TRAP_METADATA DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT
428
429#define NSIM_TRAP_DROP(_id, _group_id) \
430 DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \
Olivier Deprez157378f2022-04-04 15:47:50 +0200431 DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \
David Brazdil0f672f62019-12-10 10:32:29 +0000432 NSIM_TRAP_METADATA)
Olivier Deprez157378f2022-04-04 15:47:50 +0200433#define NSIM_TRAP_DROP_EXT(_id, _group_id, _metadata) \
434 DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \
435 DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \
436 NSIM_TRAP_METADATA | (_metadata))
David Brazdil0f672f62019-12-10 10:32:29 +0000437#define NSIM_TRAP_EXCEPTION(_id, _group_id) \
438 DEVLINK_TRAP_GENERIC(EXCEPTION, TRAP, _id, \
Olivier Deprez157378f2022-04-04 15:47:50 +0200439 DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \
440 NSIM_TRAP_METADATA)
441#define NSIM_TRAP_CONTROL(_id, _group_id, _action) \
442 DEVLINK_TRAP_GENERIC(CONTROL, _action, _id, \
443 DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \
David Brazdil0f672f62019-12-10 10:32:29 +0000444 NSIM_TRAP_METADATA)
445#define NSIM_TRAP_DRIVER_EXCEPTION(_id, _group_id) \
446 DEVLINK_TRAP_DRIVER(EXCEPTION, TRAP, NSIM_TRAP_ID_##_id, \
447 NSIM_TRAP_NAME_##_id, \
Olivier Deprez157378f2022-04-04 15:47:50 +0200448 DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \
David Brazdil0f672f62019-12-10 10:32:29 +0000449 NSIM_TRAP_METADATA)
450
Olivier Deprez157378f2022-04-04 15:47:50 +0200451#define NSIM_DEV_TRAP_POLICER_MIN_RATE 1
452#define NSIM_DEV_TRAP_POLICER_MAX_RATE 8000
453#define NSIM_DEV_TRAP_POLICER_MIN_BURST 8
454#define NSIM_DEV_TRAP_POLICER_MAX_BURST 65536
455
456#define NSIM_TRAP_POLICER(_id, _rate, _burst) \
457 DEVLINK_TRAP_POLICER(_id, _rate, _burst, \
458 NSIM_DEV_TRAP_POLICER_MAX_RATE, \
459 NSIM_DEV_TRAP_POLICER_MIN_RATE, \
460 NSIM_DEV_TRAP_POLICER_MAX_BURST, \
461 NSIM_DEV_TRAP_POLICER_MIN_BURST)
462
463static const struct devlink_trap_policer nsim_trap_policers_arr[] = {
464 NSIM_TRAP_POLICER(1, 1000, 128),
465 NSIM_TRAP_POLICER(2, 2000, 256),
466 NSIM_TRAP_POLICER(3, 3000, 512),
467};
468
469static const struct devlink_trap_group nsim_trap_groups_arr[] = {
470 DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS, 0),
471 DEVLINK_TRAP_GROUP_GENERIC(L3_DROPS, 1),
472 DEVLINK_TRAP_GROUP_GENERIC(L3_EXCEPTIONS, 1),
473 DEVLINK_TRAP_GROUP_GENERIC(BUFFER_DROPS, 2),
474 DEVLINK_TRAP_GROUP_GENERIC(ACL_DROPS, 3),
475 DEVLINK_TRAP_GROUP_GENERIC(MC_SNOOPING, 3),
476};
477
David Brazdil0f672f62019-12-10 10:32:29 +0000478static const struct devlink_trap nsim_traps_arr[] = {
479 NSIM_TRAP_DROP(SMAC_MC, L2_DROPS),
480 NSIM_TRAP_DROP(VLAN_TAG_MISMATCH, L2_DROPS),
481 NSIM_TRAP_DROP(INGRESS_VLAN_FILTER, L2_DROPS),
482 NSIM_TRAP_DROP(INGRESS_STP_FILTER, L2_DROPS),
483 NSIM_TRAP_DROP(EMPTY_TX_LIST, L2_DROPS),
484 NSIM_TRAP_DROP(PORT_LOOPBACK_FILTER, L2_DROPS),
485 NSIM_TRAP_DRIVER_EXCEPTION(FID_MISS, L2_DROPS),
486 NSIM_TRAP_DROP(BLACKHOLE_ROUTE, L3_DROPS),
Olivier Deprez157378f2022-04-04 15:47:50 +0200487 NSIM_TRAP_EXCEPTION(TTL_ERROR, L3_EXCEPTIONS),
David Brazdil0f672f62019-12-10 10:32:29 +0000488 NSIM_TRAP_DROP(TAIL_DROP, BUFFER_DROPS),
Olivier Deprez157378f2022-04-04 15:47:50 +0200489 NSIM_TRAP_DROP_EXT(INGRESS_FLOW_ACTION_DROP, ACL_DROPS,
490 DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE),
491 NSIM_TRAP_DROP_EXT(EGRESS_FLOW_ACTION_DROP, ACL_DROPS,
492 DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE),
493 NSIM_TRAP_CONTROL(IGMP_QUERY, MC_SNOOPING, MIRROR),
494 NSIM_TRAP_CONTROL(IGMP_V1_REPORT, MC_SNOOPING, TRAP),
David Brazdil0f672f62019-12-10 10:32:29 +0000495};
496
497#define NSIM_TRAP_L4_DATA_LEN 100
498
499static struct sk_buff *nsim_dev_trap_skb_build(void)
500{
501 int tot_len, data_len = NSIM_TRAP_L4_DATA_LEN;
502 struct sk_buff *skb;
503 struct udphdr *udph;
504 struct ethhdr *eth;
505 struct iphdr *iph;
506
507 skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
508 if (!skb)
509 return NULL;
510 tot_len = sizeof(struct iphdr) + sizeof(struct udphdr) + data_len;
511
512 skb_reset_mac_header(skb);
513 eth = skb_put(skb, sizeof(struct ethhdr));
514 eth_random_addr(eth->h_dest);
515 eth_random_addr(eth->h_source);
516 eth->h_proto = htons(ETH_P_IP);
517 skb->protocol = htons(ETH_P_IP);
518
519 skb_set_network_header(skb, skb->len);
520 iph = skb_put(skb, sizeof(struct iphdr));
521 iph->protocol = IPPROTO_UDP;
522 iph->saddr = in_aton("192.0.2.1");
523 iph->daddr = in_aton("198.51.100.1");
524 iph->version = 0x4;
525 iph->frag_off = 0;
526 iph->ihl = 0x5;
527 iph->tot_len = htons(tot_len);
528 iph->ttl = 100;
529 iph->check = 0;
530 iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
531
532 skb_set_transport_header(skb, skb->len);
533 udph = skb_put_zero(skb, sizeof(struct udphdr) + data_len);
534 get_random_bytes(&udph->source, sizeof(u16));
535 get_random_bytes(&udph->dest, sizeof(u16));
536 udph->len = htons(sizeof(struct udphdr) + data_len);
537
538 return skb;
539}
540
541static void nsim_dev_trap_report(struct nsim_dev_port *nsim_dev_port)
542{
543 struct nsim_dev *nsim_dev = nsim_dev_port->ns->nsim_dev;
544 struct devlink *devlink = priv_to_devlink(nsim_dev);
545 struct nsim_trap_data *nsim_trap_data;
546 int i;
547
548 nsim_trap_data = nsim_dev->trap_data;
549
550 spin_lock(&nsim_trap_data->trap_lock);
551 for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) {
Olivier Deprez157378f2022-04-04 15:47:50 +0200552 struct flow_action_cookie *fa_cookie = NULL;
David Brazdil0f672f62019-12-10 10:32:29 +0000553 struct nsim_trap_item *nsim_trap_item;
554 struct sk_buff *skb;
Olivier Deprez157378f2022-04-04 15:47:50 +0200555 bool has_fa_cookie;
556
557 has_fa_cookie = nsim_traps_arr[i].metadata_cap &
558 DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE;
David Brazdil0f672f62019-12-10 10:32:29 +0000559
560 nsim_trap_item = &nsim_trap_data->trap_items_arr[i];
561 if (nsim_trap_item->action == DEVLINK_TRAP_ACTION_DROP)
562 continue;
563
564 skb = nsim_dev_trap_skb_build();
565 if (!skb)
566 continue;
567 skb->dev = nsim_dev_port->ns->netdev;
568
569 /* Trapped packets are usually passed to devlink in softIRQ,
570 * but in this case they are generated in a workqueue. Disable
571 * softIRQs to prevent lockdep from complaining about
572 * "incosistent lock state".
573 */
Olivier Deprez157378f2022-04-04 15:47:50 +0200574
575 spin_lock_bh(&nsim_dev->fa_cookie_lock);
576 fa_cookie = has_fa_cookie ? nsim_dev->fa_cookie : NULL;
David Brazdil0f672f62019-12-10 10:32:29 +0000577 devlink_trap_report(devlink, skb, nsim_trap_item->trap_ctx,
Olivier Deprez157378f2022-04-04 15:47:50 +0200578 &nsim_dev_port->devlink_port, fa_cookie);
579 spin_unlock_bh(&nsim_dev->fa_cookie_lock);
David Brazdil0f672f62019-12-10 10:32:29 +0000580 consume_skb(skb);
581 }
582 spin_unlock(&nsim_trap_data->trap_lock);
583}
584
585#define NSIM_TRAP_REPORT_INTERVAL_MS 100
586
587static void nsim_dev_trap_report_work(struct work_struct *work)
588{
589 struct nsim_trap_data *nsim_trap_data;
590 struct nsim_dev_port *nsim_dev_port;
591 struct nsim_dev *nsim_dev;
592
593 nsim_trap_data = container_of(work, struct nsim_trap_data,
594 trap_report_dw.work);
595 nsim_dev = nsim_trap_data->nsim_dev;
596
597 /* For each running port and enabled packet trap, generate a UDP
598 * packet with a random 5-tuple and report it.
599 */
600 mutex_lock(&nsim_dev->port_list_lock);
601 list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) {
602 if (!netif_running(nsim_dev_port->ns->netdev))
603 continue;
604
605 nsim_dev_trap_report(nsim_dev_port);
606 }
607 mutex_unlock(&nsim_dev->port_list_lock);
608
609 schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw,
610 msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS));
611}
612
613static int nsim_dev_traps_init(struct devlink *devlink)
614{
Olivier Deprez157378f2022-04-04 15:47:50 +0200615 size_t policers_count = ARRAY_SIZE(nsim_trap_policers_arr);
David Brazdil0f672f62019-12-10 10:32:29 +0000616 struct nsim_dev *nsim_dev = devlink_priv(devlink);
617 struct nsim_trap_data *nsim_trap_data;
618 int err;
619
620 nsim_trap_data = kzalloc(sizeof(*nsim_trap_data), GFP_KERNEL);
621 if (!nsim_trap_data)
622 return -ENOMEM;
623
624 nsim_trap_data->trap_items_arr = kcalloc(ARRAY_SIZE(nsim_traps_arr),
625 sizeof(struct nsim_trap_item),
626 GFP_KERNEL);
627 if (!nsim_trap_data->trap_items_arr) {
628 err = -ENOMEM;
629 goto err_trap_data_free;
630 }
631
Olivier Deprez157378f2022-04-04 15:47:50 +0200632 nsim_trap_data->trap_policers_cnt_arr = kcalloc(policers_count,
633 sizeof(u64),
634 GFP_KERNEL);
635 if (!nsim_trap_data->trap_policers_cnt_arr) {
636 err = -ENOMEM;
637 goto err_trap_items_free;
638 }
639
David Brazdil0f672f62019-12-10 10:32:29 +0000640 /* The lock is used to protect the action state of the registered
641 * traps. The value is written by user and read in delayed work when
642 * iterating over all the traps.
643 */
644 spin_lock_init(&nsim_trap_data->trap_lock);
645 nsim_trap_data->nsim_dev = nsim_dev;
646 nsim_dev->trap_data = nsim_trap_data;
647
Olivier Deprez157378f2022-04-04 15:47:50 +0200648 err = devlink_trap_policers_register(devlink, nsim_trap_policers_arr,
649 policers_count);
650 if (err)
651 goto err_trap_policers_cnt_free;
652
653 err = devlink_trap_groups_register(devlink, nsim_trap_groups_arr,
654 ARRAY_SIZE(nsim_trap_groups_arr));
655 if (err)
656 goto err_trap_policers_unregister;
657
David Brazdil0f672f62019-12-10 10:32:29 +0000658 err = devlink_traps_register(devlink, nsim_traps_arr,
659 ARRAY_SIZE(nsim_traps_arr), NULL);
660 if (err)
Olivier Deprez157378f2022-04-04 15:47:50 +0200661 goto err_trap_groups_unregister;
David Brazdil0f672f62019-12-10 10:32:29 +0000662
663 INIT_DELAYED_WORK(&nsim_dev->trap_data->trap_report_dw,
664 nsim_dev_trap_report_work);
665 schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw,
666 msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS));
667
668 return 0;
669
Olivier Deprez157378f2022-04-04 15:47:50 +0200670err_trap_groups_unregister:
671 devlink_trap_groups_unregister(devlink, nsim_trap_groups_arr,
672 ARRAY_SIZE(nsim_trap_groups_arr));
673err_trap_policers_unregister:
674 devlink_trap_policers_unregister(devlink, nsim_trap_policers_arr,
675 ARRAY_SIZE(nsim_trap_policers_arr));
676err_trap_policers_cnt_free:
677 kfree(nsim_trap_data->trap_policers_cnt_arr);
David Brazdil0f672f62019-12-10 10:32:29 +0000678err_trap_items_free:
679 kfree(nsim_trap_data->trap_items_arr);
680err_trap_data_free:
681 kfree(nsim_trap_data);
682 return err;
683}
684
685static void nsim_dev_traps_exit(struct devlink *devlink)
686{
687 struct nsim_dev *nsim_dev = devlink_priv(devlink);
688
689 cancel_delayed_work_sync(&nsim_dev->trap_data->trap_report_dw);
690 devlink_traps_unregister(devlink, nsim_traps_arr,
691 ARRAY_SIZE(nsim_traps_arr));
Olivier Deprez157378f2022-04-04 15:47:50 +0200692 devlink_trap_groups_unregister(devlink, nsim_trap_groups_arr,
693 ARRAY_SIZE(nsim_trap_groups_arr));
694 devlink_trap_policers_unregister(devlink, nsim_trap_policers_arr,
695 ARRAY_SIZE(nsim_trap_policers_arr));
696 kfree(nsim_dev->trap_data->trap_policers_cnt_arr);
David Brazdil0f672f62019-12-10 10:32:29 +0000697 kfree(nsim_dev->trap_data->trap_items_arr);
698 kfree(nsim_dev->trap_data);
699}
700
Olivier Deprez157378f2022-04-04 15:47:50 +0200701static int nsim_dev_reload_create(struct nsim_dev *nsim_dev,
702 struct netlink_ext_ack *extack);
703static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev);
704
705static int nsim_dev_reload_down(struct devlink *devlink, bool netns_change,
706 enum devlink_reload_action action, enum devlink_reload_limit limit,
David Brazdil0f672f62019-12-10 10:32:29 +0000707 struct netlink_ext_ack *extack)
708{
Olivier Deprez157378f2022-04-04 15:47:50 +0200709 struct nsim_dev *nsim_dev = devlink_priv(devlink);
710
711 if (nsim_dev->dont_allow_reload) {
712 /* For testing purposes, user set debugfs dont_allow_reload
713 * value to true. So forbid it.
714 */
715 NL_SET_ERR_MSG_MOD(extack, "User forbid the reload for testing purposes");
716 return -EOPNOTSUPP;
717 }
718
719 nsim_dev_reload_destroy(nsim_dev);
David Brazdil0f672f62019-12-10 10:32:29 +0000720 return 0;
721}
722
Olivier Deprez157378f2022-04-04 15:47:50 +0200723static int nsim_dev_reload_up(struct devlink *devlink, enum devlink_reload_action action,
724 enum devlink_reload_limit limit, u32 *actions_performed,
David Brazdil0f672f62019-12-10 10:32:29 +0000725 struct netlink_ext_ack *extack)
726{
Olivier Deprez157378f2022-04-04 15:47:50 +0200727 struct nsim_dev *nsim_dev = devlink_priv(devlink);
David Brazdil0f672f62019-12-10 10:32:29 +0000728
Olivier Deprez157378f2022-04-04 15:47:50 +0200729 if (nsim_dev->fail_reload) {
730 /* For testing purposes, user set debugfs fail_reload
731 * value to true. Fail right away.
732 */
733 NL_SET_ERR_MSG_MOD(extack, "User setup the reload to fail for testing purposes");
734 return -EINVAL;
David Brazdil0f672f62019-12-10 10:32:29 +0000735 }
David Brazdil0f672f62019-12-10 10:32:29 +0000736
Olivier Deprez157378f2022-04-04 15:47:50 +0200737 *actions_performed = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT);
738 return nsim_dev_reload_create(nsim_dev, extack);
739}
740
741static int nsim_dev_info_get(struct devlink *devlink,
742 struct devlink_info_req *req,
743 struct netlink_ext_ack *extack)
744{
745 return devlink_info_driver_name_put(req, DRV_NAME);
David Brazdil0f672f62019-12-10 10:32:29 +0000746}
747
748#define NSIM_DEV_FLASH_SIZE 500000
749#define NSIM_DEV_FLASH_CHUNK_SIZE 1000
750#define NSIM_DEV_FLASH_CHUNK_TIME_MS 10
751
Olivier Deprez157378f2022-04-04 15:47:50 +0200752static int nsim_dev_flash_update(struct devlink *devlink,
753 struct devlink_flash_update_params *params,
David Brazdil0f672f62019-12-10 10:32:29 +0000754 struct netlink_ext_ack *extack)
755{
756 struct nsim_dev *nsim_dev = devlink_priv(devlink);
757 int i;
758
Olivier Deprez157378f2022-04-04 15:47:50 +0200759 if ((params->overwrite_mask & ~nsim_dev->fw_update_overwrite_mask) != 0)
760 return -EOPNOTSUPP;
761
David Brazdil0f672f62019-12-10 10:32:29 +0000762 if (nsim_dev->fw_update_status) {
763 devlink_flash_update_begin_notify(devlink);
764 devlink_flash_update_status_notify(devlink,
765 "Preparing to flash",
Olivier Deprez157378f2022-04-04 15:47:50 +0200766 params->component, 0, 0);
David Brazdil0f672f62019-12-10 10:32:29 +0000767 }
768
769 for (i = 0; i < NSIM_DEV_FLASH_SIZE / NSIM_DEV_FLASH_CHUNK_SIZE; i++) {
770 if (nsim_dev->fw_update_status)
771 devlink_flash_update_status_notify(devlink, "Flashing",
Olivier Deprez157378f2022-04-04 15:47:50 +0200772 params->component,
David Brazdil0f672f62019-12-10 10:32:29 +0000773 i * NSIM_DEV_FLASH_CHUNK_SIZE,
774 NSIM_DEV_FLASH_SIZE);
775 msleep(NSIM_DEV_FLASH_CHUNK_TIME_MS);
776 }
777
778 if (nsim_dev->fw_update_status) {
779 devlink_flash_update_status_notify(devlink, "Flashing",
Olivier Deprez157378f2022-04-04 15:47:50 +0200780 params->component,
David Brazdil0f672f62019-12-10 10:32:29 +0000781 NSIM_DEV_FLASH_SIZE,
782 NSIM_DEV_FLASH_SIZE);
Olivier Deprez157378f2022-04-04 15:47:50 +0200783 devlink_flash_update_timeout_notify(devlink, "Flash select",
784 params->component, 81);
David Brazdil0f672f62019-12-10 10:32:29 +0000785 devlink_flash_update_status_notify(devlink, "Flashing done",
Olivier Deprez157378f2022-04-04 15:47:50 +0200786 params->component, 0, 0);
David Brazdil0f672f62019-12-10 10:32:29 +0000787 devlink_flash_update_end_notify(devlink);
788 }
789
790 return 0;
791}
792
793static struct nsim_trap_item *
794nsim_dev_trap_item_lookup(struct nsim_dev *nsim_dev, u16 trap_id)
795{
796 struct nsim_trap_data *nsim_trap_data = nsim_dev->trap_data;
797 int i;
798
799 for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) {
800 if (nsim_traps_arr[i].id == trap_id)
801 return &nsim_trap_data->trap_items_arr[i];
802 }
803
804 return NULL;
805}
806
807static int nsim_dev_devlink_trap_init(struct devlink *devlink,
808 const struct devlink_trap *trap,
809 void *trap_ctx)
810{
811 struct nsim_dev *nsim_dev = devlink_priv(devlink);
812 struct nsim_trap_item *nsim_trap_item;
813
814 nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id);
815 if (WARN_ON(!nsim_trap_item))
816 return -ENOENT;
817
818 nsim_trap_item->trap_ctx = trap_ctx;
819 nsim_trap_item->action = trap->init_action;
820
821 return 0;
822}
823
824static int
825nsim_dev_devlink_trap_action_set(struct devlink *devlink,
826 const struct devlink_trap *trap,
Olivier Deprez157378f2022-04-04 15:47:50 +0200827 enum devlink_trap_action action,
828 struct netlink_ext_ack *extack)
David Brazdil0f672f62019-12-10 10:32:29 +0000829{
830 struct nsim_dev *nsim_dev = devlink_priv(devlink);
831 struct nsim_trap_item *nsim_trap_item;
832
833 nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id);
834 if (WARN_ON(!nsim_trap_item))
835 return -ENOENT;
836
837 spin_lock(&nsim_dev->trap_data->trap_lock);
838 nsim_trap_item->action = action;
839 spin_unlock(&nsim_dev->trap_data->trap_lock);
840
841 return 0;
842}
843
Olivier Deprez157378f2022-04-04 15:47:50 +0200844static int
845nsim_dev_devlink_trap_group_set(struct devlink *devlink,
846 const struct devlink_trap_group *group,
847 const struct devlink_trap_policer *policer,
848 struct netlink_ext_ack *extack)
849{
850 struct nsim_dev *nsim_dev = devlink_priv(devlink);
851
852 if (nsim_dev->fail_trap_group_set)
853 return -EINVAL;
854
855 return 0;
856}
857
858static int
859nsim_dev_devlink_trap_policer_set(struct devlink *devlink,
860 const struct devlink_trap_policer *policer,
861 u64 rate, u64 burst,
862 struct netlink_ext_ack *extack)
863{
864 struct nsim_dev *nsim_dev = devlink_priv(devlink);
865
866 if (nsim_dev->fail_trap_policer_set) {
867 NL_SET_ERR_MSG_MOD(extack, "User setup the operation to fail for testing purposes");
868 return -EINVAL;
869 }
870
871 return 0;
872}
873
874static int
875nsim_dev_devlink_trap_policer_counter_get(struct devlink *devlink,
876 const struct devlink_trap_policer *policer,
877 u64 *p_drops)
878{
879 struct nsim_dev *nsim_dev = devlink_priv(devlink);
880 u64 *cnt;
881
882 if (nsim_dev->fail_trap_policer_counter_get)
883 return -EINVAL;
884
885 cnt = &nsim_dev->trap_data->trap_policers_cnt_arr[policer->id - 1];
886 *p_drops = (*cnt)++;
887
888 return 0;
889}
890
David Brazdil0f672f62019-12-10 10:32:29 +0000891static const struct devlink_ops nsim_dev_devlink_ops = {
Olivier Deprez157378f2022-04-04 15:47:50 +0200892 .supported_flash_update_params = DEVLINK_SUPPORT_FLASH_UPDATE_COMPONENT |
893 DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK,
894 .reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT),
David Brazdil0f672f62019-12-10 10:32:29 +0000895 .reload_down = nsim_dev_reload_down,
896 .reload_up = nsim_dev_reload_up,
Olivier Deprez157378f2022-04-04 15:47:50 +0200897 .info_get = nsim_dev_info_get,
David Brazdil0f672f62019-12-10 10:32:29 +0000898 .flash_update = nsim_dev_flash_update,
899 .trap_init = nsim_dev_devlink_trap_init,
900 .trap_action_set = nsim_dev_devlink_trap_action_set,
Olivier Deprez157378f2022-04-04 15:47:50 +0200901 .trap_group_set = nsim_dev_devlink_trap_group_set,
902 .trap_policer_set = nsim_dev_devlink_trap_policer_set,
903 .trap_policer_counter_get = nsim_dev_devlink_trap_policer_counter_get,
David Brazdil0f672f62019-12-10 10:32:29 +0000904};
905
906#define NSIM_DEV_MAX_MACS_DEFAULT 32
907#define NSIM_DEV_TEST1_DEFAULT true
908
David Brazdil0f672f62019-12-10 10:32:29 +0000909static int __nsim_dev_port_add(struct nsim_dev *nsim_dev,
910 unsigned int port_index)
911{
Olivier Deprez157378f2022-04-04 15:47:50 +0200912 struct devlink_port_attrs attrs = {};
David Brazdil0f672f62019-12-10 10:32:29 +0000913 struct nsim_dev_port *nsim_dev_port;
914 struct devlink_port *devlink_port;
915 int err;
916
917 nsim_dev_port = kzalloc(sizeof(*nsim_dev_port), GFP_KERNEL);
918 if (!nsim_dev_port)
919 return -ENOMEM;
920 nsim_dev_port->port_index = port_index;
921
922 devlink_port = &nsim_dev_port->devlink_port;
Olivier Deprez157378f2022-04-04 15:47:50 +0200923 attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL;
924 attrs.phys.port_number = port_index + 1;
925 memcpy(attrs.switch_id.id, nsim_dev->switch_id.id, nsim_dev->switch_id.id_len);
926 attrs.switch_id.id_len = nsim_dev->switch_id.id_len;
927 devlink_port_attrs_set(devlink_port, &attrs);
David Brazdil0f672f62019-12-10 10:32:29 +0000928 err = devlink_port_register(priv_to_devlink(nsim_dev), devlink_port,
929 port_index);
930 if (err)
931 goto err_port_free;
932
933 err = nsim_dev_port_debugfs_init(nsim_dev, nsim_dev_port);
934 if (err)
935 goto err_dl_port_unregister;
936
937 nsim_dev_port->ns = nsim_create(nsim_dev, nsim_dev_port);
938 if (IS_ERR(nsim_dev_port->ns)) {
939 err = PTR_ERR(nsim_dev_port->ns);
940 goto err_port_debugfs_exit;
941 }
942
943 devlink_port_type_eth_set(devlink_port, nsim_dev_port->ns->netdev);
944 list_add(&nsim_dev_port->list, &nsim_dev->port_list);
945
946 return 0;
947
948err_port_debugfs_exit:
949 nsim_dev_port_debugfs_exit(nsim_dev_port);
950err_dl_port_unregister:
951 devlink_port_unregister(devlink_port);
952err_port_free:
953 kfree(nsim_dev_port);
954 return err;
955}
956
957static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port)
958{
959 struct devlink_port *devlink_port = &nsim_dev_port->devlink_port;
960
961 list_del(&nsim_dev_port->list);
962 devlink_port_type_clear(devlink_port);
963 nsim_destroy(nsim_dev_port->ns);
964 nsim_dev_port_debugfs_exit(nsim_dev_port);
965 devlink_port_unregister(devlink_port);
966 kfree(nsim_dev_port);
967}
968
969static void nsim_dev_port_del_all(struct nsim_dev *nsim_dev)
970{
971 struct nsim_dev_port *nsim_dev_port, *tmp;
972
973 mutex_lock(&nsim_dev->port_list_lock);
974 list_for_each_entry_safe(nsim_dev_port, tmp,
975 &nsim_dev->port_list, list)
976 __nsim_dev_port_del(nsim_dev_port);
977 mutex_unlock(&nsim_dev->port_list_lock);
978}
979
Olivier Deprez157378f2022-04-04 15:47:50 +0200980static int nsim_dev_port_add_all(struct nsim_dev *nsim_dev,
981 unsigned int port_count)
David Brazdil0f672f62019-12-10 10:32:29 +0000982{
Olivier Deprez157378f2022-04-04 15:47:50 +0200983 int i, err;
David Brazdil0f672f62019-12-10 10:32:29 +0000984
Olivier Deprez157378f2022-04-04 15:47:50 +0200985 for (i = 0; i < port_count; i++) {
David Brazdil0f672f62019-12-10 10:32:29 +0000986 err = __nsim_dev_port_add(nsim_dev, i);
987 if (err)
988 goto err_port_del_all;
989 }
David Brazdil0f672f62019-12-10 10:32:29 +0000990 return 0;
991
992err_port_del_all:
David Brazdil0f672f62019-12-10 10:32:29 +0000993 nsim_dev_port_del_all(nsim_dev);
David Brazdil0f672f62019-12-10 10:32:29 +0000994 return err;
995}
996
Olivier Deprez157378f2022-04-04 15:47:50 +0200997static int nsim_dev_reload_create(struct nsim_dev *nsim_dev,
998 struct netlink_ext_ack *extack)
999{
1000 struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev;
1001 struct devlink *devlink;
1002 int err;
1003
1004 devlink = priv_to_devlink(nsim_dev);
1005 nsim_dev = devlink_priv(devlink);
1006 INIT_LIST_HEAD(&nsim_dev->port_list);
1007 mutex_init(&nsim_dev->port_list_lock);
1008 nsim_dev->fw_update_status = true;
1009 nsim_dev->fw_update_overwrite_mask = 0;
1010
1011 nsim_devlink_param_load_driverinit_values(devlink);
1012
1013 err = nsim_dev_dummy_region_init(nsim_dev, devlink);
1014 if (err)
1015 return err;
1016
1017 err = nsim_dev_traps_init(devlink);
1018 if (err)
1019 goto err_dummy_region_exit;
1020
1021 nsim_dev->fib_data = nsim_fib_create(devlink, extack);
1022 if (IS_ERR(nsim_dev->fib_data)) {
1023 err = PTR_ERR(nsim_dev->fib_data);
1024 goto err_traps_exit;
1025 }
1026
1027 err = nsim_dev_health_init(nsim_dev, devlink);
1028 if (err)
1029 goto err_fib_destroy;
1030
1031 err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count);
1032 if (err)
1033 goto err_health_exit;
1034
1035 nsim_dev->take_snapshot = debugfs_create_file("take_snapshot",
1036 0200,
1037 nsim_dev->ddir,
1038 nsim_dev,
1039 &nsim_dev_take_snapshot_fops);
1040 return 0;
1041
1042err_health_exit:
1043 nsim_dev_health_exit(nsim_dev);
1044err_fib_destroy:
1045 nsim_fib_destroy(devlink, nsim_dev->fib_data);
1046err_traps_exit:
1047 nsim_dev_traps_exit(devlink);
1048err_dummy_region_exit:
1049 nsim_dev_dummy_region_exit(nsim_dev);
1050 return err;
1051}
1052
1053int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev)
1054{
1055 struct nsim_dev *nsim_dev;
1056 struct devlink *devlink;
1057 int err;
1058
1059 devlink = devlink_alloc(&nsim_dev_devlink_ops, sizeof(*nsim_dev));
1060 if (!devlink)
1061 return -ENOMEM;
1062 devlink_net_set(devlink, nsim_bus_dev->initial_net);
1063 nsim_dev = devlink_priv(devlink);
1064 nsim_dev->nsim_bus_dev = nsim_bus_dev;
1065 nsim_dev->switch_id.id_len = sizeof(nsim_dev->switch_id.id);
1066 get_random_bytes(nsim_dev->switch_id.id, nsim_dev->switch_id.id_len);
1067 INIT_LIST_HEAD(&nsim_dev->port_list);
1068 mutex_init(&nsim_dev->port_list_lock);
1069 nsim_dev->fw_update_status = true;
1070 nsim_dev->fw_update_overwrite_mask = 0;
1071 nsim_dev->max_macs = NSIM_DEV_MAX_MACS_DEFAULT;
1072 nsim_dev->test1 = NSIM_DEV_TEST1_DEFAULT;
1073 spin_lock_init(&nsim_dev->fa_cookie_lock);
1074
1075 dev_set_drvdata(&nsim_bus_dev->dev, nsim_dev);
1076
1077 err = nsim_dev_resources_register(devlink);
1078 if (err)
1079 goto err_devlink_free;
1080
1081 err = devlink_register(devlink, &nsim_bus_dev->dev);
1082 if (err)
1083 goto err_resources_unregister;
1084
1085 err = devlink_params_register(devlink, nsim_devlink_params,
1086 ARRAY_SIZE(nsim_devlink_params));
1087 if (err)
1088 goto err_dl_unregister;
1089 nsim_devlink_set_params_init_values(nsim_dev, devlink);
1090
1091 err = nsim_dev_dummy_region_init(nsim_dev, devlink);
1092 if (err)
1093 goto err_params_unregister;
1094
1095 err = nsim_dev_traps_init(devlink);
1096 if (err)
1097 goto err_dummy_region_exit;
1098
1099 err = nsim_dev_debugfs_init(nsim_dev);
1100 if (err)
1101 goto err_traps_exit;
1102
1103 nsim_dev->fib_data = nsim_fib_create(devlink, NULL);
1104 if (IS_ERR(nsim_dev->fib_data)) {
1105 err = PTR_ERR(nsim_dev->fib_data);
1106 goto err_debugfs_exit;
1107 }
1108
1109 err = nsim_dev_health_init(nsim_dev, devlink);
1110 if (err)
1111 goto err_fib_destroy;
1112
1113 err = nsim_bpf_dev_init(nsim_dev);
1114 if (err)
1115 goto err_health_exit;
1116
1117 err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count);
1118 if (err)
1119 goto err_bpf_dev_exit;
1120
1121 devlink_params_publish(devlink);
1122 devlink_reload_enable(devlink);
1123 return 0;
1124
1125err_bpf_dev_exit:
1126 nsim_bpf_dev_exit(nsim_dev);
1127err_health_exit:
1128 nsim_dev_health_exit(nsim_dev);
1129err_fib_destroy:
1130 nsim_fib_destroy(devlink, nsim_dev->fib_data);
1131err_debugfs_exit:
1132 nsim_dev_debugfs_exit(nsim_dev);
1133err_traps_exit:
1134 nsim_dev_traps_exit(devlink);
1135err_dummy_region_exit:
1136 nsim_dev_dummy_region_exit(nsim_dev);
1137err_params_unregister:
1138 devlink_params_unregister(devlink, nsim_devlink_params,
1139 ARRAY_SIZE(nsim_devlink_params));
1140err_dl_unregister:
1141 devlink_unregister(devlink);
1142err_resources_unregister:
1143 devlink_resources_unregister(devlink, NULL);
1144err_devlink_free:
1145 devlink_free(devlink);
1146 return err;
1147}
1148
1149static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev)
1150{
1151 struct devlink *devlink = priv_to_devlink(nsim_dev);
1152
1153 if (devlink_is_reload_failed(devlink))
1154 return;
1155 debugfs_remove(nsim_dev->take_snapshot);
1156 nsim_dev_port_del_all(nsim_dev);
1157 nsim_dev_health_exit(nsim_dev);
1158 nsim_fib_destroy(devlink, nsim_dev->fib_data);
1159 nsim_dev_traps_exit(devlink);
1160 nsim_dev_dummy_region_exit(nsim_dev);
1161 mutex_destroy(&nsim_dev->port_list_lock);
1162}
1163
David Brazdil0f672f62019-12-10 10:32:29 +00001164void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev)
1165{
1166 struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
Olivier Deprez157378f2022-04-04 15:47:50 +02001167 struct devlink *devlink = priv_to_devlink(nsim_dev);
David Brazdil0f672f62019-12-10 10:32:29 +00001168
Olivier Deprez157378f2022-04-04 15:47:50 +02001169 devlink_reload_disable(devlink);
1170
1171 nsim_dev_reload_destroy(nsim_dev);
1172
1173 nsim_bpf_dev_exit(nsim_dev);
1174 nsim_dev_debugfs_exit(nsim_dev);
1175 devlink_params_unregister(devlink, nsim_devlink_params,
1176 ARRAY_SIZE(nsim_devlink_params));
1177 devlink_unregister(devlink);
1178 devlink_resources_unregister(devlink, NULL);
1179 devlink_free(devlink);
David Brazdil0f672f62019-12-10 10:32:29 +00001180}
1181
1182static struct nsim_dev_port *
1183__nsim_dev_port_lookup(struct nsim_dev *nsim_dev, unsigned int port_index)
1184{
1185 struct nsim_dev_port *nsim_dev_port;
1186
1187 list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list)
1188 if (nsim_dev_port->port_index == port_index)
1189 return nsim_dev_port;
1190 return NULL;
1191}
1192
1193int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev,
1194 unsigned int port_index)
1195{
1196 struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
1197 int err;
1198
1199 mutex_lock(&nsim_dev->port_list_lock);
1200 if (__nsim_dev_port_lookup(nsim_dev, port_index))
1201 err = -EEXIST;
1202 else
1203 err = __nsim_dev_port_add(nsim_dev, port_index);
1204 mutex_unlock(&nsim_dev->port_list_lock);
1205 return err;
1206}
1207
1208int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev,
1209 unsigned int port_index)
1210{
1211 struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
1212 struct nsim_dev_port *nsim_dev_port;
1213 int err = 0;
1214
1215 mutex_lock(&nsim_dev->port_list_lock);
1216 nsim_dev_port = __nsim_dev_port_lookup(nsim_dev, port_index);
1217 if (!nsim_dev_port)
1218 err = -ENOENT;
1219 else
1220 __nsim_dev_port_del(nsim_dev_port);
1221 mutex_unlock(&nsim_dev->port_list_lock);
1222 return err;
1223}
1224
1225int nsim_dev_init(void)
1226{
1227 nsim_dev_ddir = debugfs_create_dir(DRV_NAME, NULL);
Olivier Deprez157378f2022-04-04 15:47:50 +02001228 return PTR_ERR_OR_ZERO(nsim_dev_ddir);
David Brazdil0f672f62019-12-10 10:32:29 +00001229}
1230
1231void nsim_dev_exit(void)
1232{
1233 debugfs_remove_recursive(nsim_dev_ddir);
1234}