xref: /linux-6.15/drivers/net/netdevsim/dev.c (revision 97691069)
18fb4bc6fSJiri Pirko /*
28fb4bc6fSJiri Pirko  * Copyright (c) 2018 Cumulus Networks. All rights reserved.
38fb4bc6fSJiri Pirko  * Copyright (c) 2018 David Ahern <[email protected]>
48fb4bc6fSJiri Pirko  * Copyright (c) 2019 Mellanox Technologies. All rights reserved.
58fb4bc6fSJiri Pirko  *
68fb4bc6fSJiri Pirko  * This software is licensed under the GNU General License Version 2,
78fb4bc6fSJiri Pirko  * June 1991 as shown in the file COPYING in the top-level directory of this
88fb4bc6fSJiri Pirko  * source tree.
98fb4bc6fSJiri Pirko  *
108fb4bc6fSJiri Pirko  * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS"
118fb4bc6fSJiri Pirko  * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
128fb4bc6fSJiri Pirko  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
138fb4bc6fSJiri Pirko  * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE
148fb4bc6fSJiri Pirko  * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME
158fb4bc6fSJiri Pirko  * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
168fb4bc6fSJiri Pirko  */
178fb4bc6fSJiri Pirko 
18d514f41eSJiri Pirko #include <linux/debugfs.h>
198fb4bc6fSJiri Pirko #include <linux/device.h>
20da58f90fSIdo Schimmel #include <linux/etherdevice.h>
21da58f90fSIdo Schimmel #include <linux/inet.h>
22da58f90fSIdo Schimmel #include <linux/jiffies.h>
23da58f90fSIdo Schimmel #include <linux/kernel.h>
248320d145SJiri Pirko #include <linux/list.h>
25794b2c05SJiri Pirko #include <linux/mutex.h>
26514cf64cSJiri Pirko #include <linux/random.h>
278fb4bc6fSJiri Pirko #include <linux/rtnetlink.h>
28da58f90fSIdo Schimmel #include <linux/workqueue.h>
298fb4bc6fSJiri Pirko #include <net/devlink.h>
30da58f90fSIdo Schimmel #include <net/ip.h>
31da58f90fSIdo Schimmel #include <uapi/linux/devlink.h>
32da58f90fSIdo Schimmel #include <uapi/linux/ip.h>
33da58f90fSIdo Schimmel #include <uapi/linux/udp.h>
348fb4bc6fSJiri Pirko 
358fb4bc6fSJiri Pirko #include "netdevsim.h"
368fb4bc6fSJiri Pirko 
37d514f41eSJiri Pirko static struct dentry *nsim_dev_ddir;
38d514f41eSJiri Pirko 
394418f862SJiri Pirko #define NSIM_DEV_DUMMY_REGION_SIZE (1024 * 32)
404418f862SJiri Pirko 
414418f862SJiri Pirko static ssize_t nsim_dev_take_snapshot_write(struct file *file,
424418f862SJiri Pirko 					    const char __user *data,
434418f862SJiri Pirko 					    size_t count, loff_t *ppos)
444418f862SJiri Pirko {
454418f862SJiri Pirko 	struct nsim_dev *nsim_dev = file->private_data;
464418f862SJiri Pirko 	void *dummy_data;
474418f862SJiri Pirko 	int err;
484418f862SJiri Pirko 	u32 id;
494418f862SJiri Pirko 
504418f862SJiri Pirko 	dummy_data = kmalloc(NSIM_DEV_DUMMY_REGION_SIZE, GFP_KERNEL);
514418f862SJiri Pirko 	if (!dummy_data)
524418f862SJiri Pirko 		return -ENOMEM;
534418f862SJiri Pirko 
544418f862SJiri Pirko 	get_random_bytes(dummy_data, NSIM_DEV_DUMMY_REGION_SIZE);
554418f862SJiri Pirko 
564418f862SJiri Pirko 	id = devlink_region_shapshot_id_get(priv_to_devlink(nsim_dev));
574418f862SJiri Pirko 	err = devlink_region_snapshot_create(nsim_dev->dummy_region,
584418f862SJiri Pirko 					     dummy_data, id, kfree);
594418f862SJiri Pirko 	if (err) {
604418f862SJiri Pirko 		pr_err("Failed to create region snapshot\n");
614418f862SJiri Pirko 		kfree(dummy_data);
624418f862SJiri Pirko 		return err;
634418f862SJiri Pirko 	}
644418f862SJiri Pirko 
654418f862SJiri Pirko 	return count;
664418f862SJiri Pirko }
674418f862SJiri Pirko 
684418f862SJiri Pirko static const struct file_operations nsim_dev_take_snapshot_fops = {
694418f862SJiri Pirko 	.open = simple_open,
704418f862SJiri Pirko 	.write = nsim_dev_take_snapshot_write,
714418f862SJiri Pirko 	.llseek = generic_file_llseek,
724418f862SJiri Pirko };
734418f862SJiri Pirko 
74d514f41eSJiri Pirko static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev)
75d514f41eSJiri Pirko {
76ab1d0cc0SJiri Pirko 	char dev_ddir_name[16];
77d514f41eSJiri Pirko 
78ab1d0cc0SJiri Pirko 	sprintf(dev_ddir_name, DRV_NAME "%u", nsim_dev->nsim_bus_dev->dev.id);
79d514f41eSJiri Pirko 	nsim_dev->ddir = debugfs_create_dir(dev_ddir_name, nsim_dev_ddir);
80d514f41eSJiri Pirko 	if (IS_ERR_OR_NULL(nsim_dev->ddir))
81d514f41eSJiri Pirko 		return PTR_ERR_OR_ZERO(nsim_dev->ddir) ?: -EINVAL;
82ab1d0cc0SJiri Pirko 	nsim_dev->ports_ddir = debugfs_create_dir("ports", nsim_dev->ddir);
83ab1d0cc0SJiri Pirko 	if (IS_ERR_OR_NULL(nsim_dev->ports_ddir))
84ab1d0cc0SJiri Pirko 		return PTR_ERR_OR_ZERO(nsim_dev->ports_ddir) ?: -EINVAL;
85fa4dfc4aSJiri Pirko 	debugfs_create_bool("fw_update_status", 0600, nsim_dev->ddir,
86fa4dfc4aSJiri Pirko 			    &nsim_dev->fw_update_status);
87150e8f8aSJiri Pirko 	debugfs_create_u32("max_macs", 0600, nsim_dev->ddir,
88150e8f8aSJiri Pirko 			   &nsim_dev->max_macs);
89150e8f8aSJiri Pirko 	debugfs_create_bool("test1", 0600, nsim_dev->ddir,
90150e8f8aSJiri Pirko 			    &nsim_dev->test1);
914418f862SJiri Pirko 	debugfs_create_file("take_snapshot", 0200, nsim_dev->ddir, nsim_dev,
924418f862SJiri Pirko 			    &nsim_dev_take_snapshot_fops);
93d514f41eSJiri Pirko 	return 0;
94d514f41eSJiri Pirko }
95d514f41eSJiri Pirko 
96d514f41eSJiri Pirko static void nsim_dev_debugfs_exit(struct nsim_dev *nsim_dev)
97d514f41eSJiri Pirko {
98ab1d0cc0SJiri Pirko 	debugfs_remove_recursive(nsim_dev->ports_ddir);
99d514f41eSJiri Pirko 	debugfs_remove_recursive(nsim_dev->ddir);
100d514f41eSJiri Pirko }
101d514f41eSJiri Pirko 
1028320d145SJiri Pirko static int nsim_dev_port_debugfs_init(struct nsim_dev *nsim_dev,
1038320d145SJiri Pirko 				      struct nsim_dev_port *nsim_dev_port)
1048320d145SJiri Pirko {
1058320d145SJiri Pirko 	char port_ddir_name[16];
1068320d145SJiri Pirko 	char dev_link_name[32];
1078320d145SJiri Pirko 
1088320d145SJiri Pirko 	sprintf(port_ddir_name, "%u", nsim_dev_port->port_index);
1098320d145SJiri Pirko 	nsim_dev_port->ddir = debugfs_create_dir(port_ddir_name,
1108320d145SJiri Pirko 						 nsim_dev->ports_ddir);
1118320d145SJiri Pirko 	if (IS_ERR_OR_NULL(nsim_dev_port->ddir))
1128320d145SJiri Pirko 		return -ENOMEM;
1138320d145SJiri Pirko 
1148320d145SJiri Pirko 	sprintf(dev_link_name, "../../../" DRV_NAME "%u",
1158320d145SJiri Pirko 		nsim_dev->nsim_bus_dev->dev.id);
1168320d145SJiri Pirko 	debugfs_create_symlink("dev", nsim_dev_port->ddir, dev_link_name);
1178320d145SJiri Pirko 
1188320d145SJiri Pirko 	return 0;
1198320d145SJiri Pirko }
1208320d145SJiri Pirko 
1218320d145SJiri Pirko static void nsim_dev_port_debugfs_exit(struct nsim_dev_port *nsim_dev_port)
1228320d145SJiri Pirko {
1238320d145SJiri Pirko 	debugfs_remove_recursive(nsim_dev_port->ddir);
1248320d145SJiri Pirko }
1258320d145SJiri Pirko 
12659c84b9fSDavid Ahern static struct net *nsim_devlink_net(struct devlink *devlink)
12759c84b9fSDavid Ahern {
12859c84b9fSDavid Ahern 	return &init_net;
12959c84b9fSDavid Ahern }
13059c84b9fSDavid Ahern 
1318fb4bc6fSJiri Pirko static u64 nsim_dev_ipv4_fib_resource_occ_get(void *priv)
1328fb4bc6fSJiri Pirko {
13359c84b9fSDavid Ahern 	struct net *net = priv;
1348fb4bc6fSJiri Pirko 
13559c84b9fSDavid Ahern 	return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, false);
1368fb4bc6fSJiri Pirko }
1378fb4bc6fSJiri Pirko 
1388fb4bc6fSJiri Pirko static u64 nsim_dev_ipv4_fib_rules_res_occ_get(void *priv)
1398fb4bc6fSJiri Pirko {
14059c84b9fSDavid Ahern 	struct net *net = priv;
1418fb4bc6fSJiri Pirko 
14259c84b9fSDavid Ahern 	return nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, false);
1438fb4bc6fSJiri Pirko }
1448fb4bc6fSJiri Pirko 
1458fb4bc6fSJiri Pirko static u64 nsim_dev_ipv6_fib_resource_occ_get(void *priv)
1468fb4bc6fSJiri Pirko {
14759c84b9fSDavid Ahern 	struct net *net = priv;
1488fb4bc6fSJiri Pirko 
14959c84b9fSDavid Ahern 	return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, false);
1508fb4bc6fSJiri Pirko }
1518fb4bc6fSJiri Pirko 
1528fb4bc6fSJiri Pirko static u64 nsim_dev_ipv6_fib_rules_res_occ_get(void *priv)
1538fb4bc6fSJiri Pirko {
15459c84b9fSDavid Ahern 	struct net *net = priv;
1558fb4bc6fSJiri Pirko 
15659c84b9fSDavid Ahern 	return nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, false);
1578fb4bc6fSJiri Pirko }
1588fb4bc6fSJiri Pirko 
1598fb4bc6fSJiri Pirko static int nsim_dev_resources_register(struct devlink *devlink)
1608fb4bc6fSJiri Pirko {
1618fb4bc6fSJiri Pirko 	struct devlink_resource_size_params params = {
1628fb4bc6fSJiri Pirko 		.size_max = (u64)-1,
1638fb4bc6fSJiri Pirko 		.size_granularity = 1,
1648fb4bc6fSJiri Pirko 		.unit = DEVLINK_RESOURCE_UNIT_ENTRY
1658fb4bc6fSJiri Pirko 	};
16659c84b9fSDavid Ahern 	struct net *net = nsim_devlink_net(devlink);
1678fb4bc6fSJiri Pirko 	int err;
1688fb4bc6fSJiri Pirko 	u64 n;
1698fb4bc6fSJiri Pirko 
1708fb4bc6fSJiri Pirko 	/* Resources for IPv4 */
1718fb4bc6fSJiri Pirko 	err = devlink_resource_register(devlink, "IPv4", (u64)-1,
1728fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV4,
1738fb4bc6fSJiri Pirko 					DEVLINK_RESOURCE_ID_PARENT_TOP,
1748fb4bc6fSJiri Pirko 					&params);
1758fb4bc6fSJiri Pirko 	if (err) {
1768fb4bc6fSJiri Pirko 		pr_err("Failed to register IPv4 top resource\n");
1778fb4bc6fSJiri Pirko 		goto out;
1788fb4bc6fSJiri Pirko 	}
1798fb4bc6fSJiri Pirko 
18059c84b9fSDavid Ahern 	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB, true);
1818fb4bc6fSJiri Pirko 	err = devlink_resource_register(devlink, "fib", n,
1828fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV4_FIB,
1838fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV4, &params);
1848fb4bc6fSJiri Pirko 	if (err) {
1858fb4bc6fSJiri Pirko 		pr_err("Failed to register IPv4 FIB resource\n");
1868fb4bc6fSJiri Pirko 		return err;
1878fb4bc6fSJiri Pirko 	}
1888fb4bc6fSJiri Pirko 
18959c84b9fSDavid Ahern 	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV4_FIB_RULES, true);
1908fb4bc6fSJiri Pirko 	err = devlink_resource_register(devlink, "fib-rules", n,
1918fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV4_FIB_RULES,
1928fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV4, &params);
1938fb4bc6fSJiri Pirko 	if (err) {
1948fb4bc6fSJiri Pirko 		pr_err("Failed to register IPv4 FIB rules resource\n");
1958fb4bc6fSJiri Pirko 		return err;
1968fb4bc6fSJiri Pirko 	}
1978fb4bc6fSJiri Pirko 
1988fb4bc6fSJiri Pirko 	/* Resources for IPv6 */
1998fb4bc6fSJiri Pirko 	err = devlink_resource_register(devlink, "IPv6", (u64)-1,
2008fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV6,
2018fb4bc6fSJiri Pirko 					DEVLINK_RESOURCE_ID_PARENT_TOP,
2028fb4bc6fSJiri Pirko 					&params);
2038fb4bc6fSJiri Pirko 	if (err) {
2048fb4bc6fSJiri Pirko 		pr_err("Failed to register IPv6 top resource\n");
2058fb4bc6fSJiri Pirko 		goto out;
2068fb4bc6fSJiri Pirko 	}
2078fb4bc6fSJiri Pirko 
20859c84b9fSDavid Ahern 	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB, true);
2098fb4bc6fSJiri Pirko 	err = devlink_resource_register(devlink, "fib", n,
2108fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV6_FIB,
2118fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV6, &params);
2128fb4bc6fSJiri Pirko 	if (err) {
2138fb4bc6fSJiri Pirko 		pr_err("Failed to register IPv6 FIB resource\n");
2148fb4bc6fSJiri Pirko 		return err;
2158fb4bc6fSJiri Pirko 	}
2168fb4bc6fSJiri Pirko 
21759c84b9fSDavid Ahern 	n = nsim_fib_get_val(net, NSIM_RESOURCE_IPV6_FIB_RULES, true);
2188fb4bc6fSJiri Pirko 	err = devlink_resource_register(devlink, "fib-rules", n,
2198fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV6_FIB_RULES,
2208fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV6, &params);
2218fb4bc6fSJiri Pirko 	if (err) {
2228fb4bc6fSJiri Pirko 		pr_err("Failed to register IPv6 FIB rules resource\n");
2238fb4bc6fSJiri Pirko 		return err;
2248fb4bc6fSJiri Pirko 	}
2258fb4bc6fSJiri Pirko 
2268fb4bc6fSJiri Pirko 	devlink_resource_occ_get_register(devlink,
2278fb4bc6fSJiri Pirko 					  NSIM_RESOURCE_IPV4_FIB,
2288fb4bc6fSJiri Pirko 					  nsim_dev_ipv4_fib_resource_occ_get,
22959c84b9fSDavid Ahern 					  net);
2308fb4bc6fSJiri Pirko 	devlink_resource_occ_get_register(devlink,
2318fb4bc6fSJiri Pirko 					  NSIM_RESOURCE_IPV4_FIB_RULES,
2328fb4bc6fSJiri Pirko 					  nsim_dev_ipv4_fib_rules_res_occ_get,
23359c84b9fSDavid Ahern 					  net);
2348fb4bc6fSJiri Pirko 	devlink_resource_occ_get_register(devlink,
2358fb4bc6fSJiri Pirko 					  NSIM_RESOURCE_IPV6_FIB,
2368fb4bc6fSJiri Pirko 					  nsim_dev_ipv6_fib_resource_occ_get,
23759c84b9fSDavid Ahern 					  net);
2388fb4bc6fSJiri Pirko 	devlink_resource_occ_get_register(devlink,
2398fb4bc6fSJiri Pirko 					  NSIM_RESOURCE_IPV6_FIB_RULES,
2408fb4bc6fSJiri Pirko 					  nsim_dev_ipv6_fib_rules_res_occ_get,
24159c84b9fSDavid Ahern 					  net);
2428fb4bc6fSJiri Pirko out:
2438fb4bc6fSJiri Pirko 	return err;
2448fb4bc6fSJiri Pirko }
2458fb4bc6fSJiri Pirko 
246150e8f8aSJiri Pirko enum nsim_devlink_param_id {
247150e8f8aSJiri Pirko 	NSIM_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
248150e8f8aSJiri Pirko 	NSIM_DEVLINK_PARAM_ID_TEST1,
249150e8f8aSJiri Pirko };
250150e8f8aSJiri Pirko 
251150e8f8aSJiri Pirko static const struct devlink_param nsim_devlink_params[] = {
252150e8f8aSJiri Pirko 	DEVLINK_PARAM_GENERIC(MAX_MACS,
253150e8f8aSJiri Pirko 			      BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
254150e8f8aSJiri Pirko 			      NULL, NULL, NULL),
255150e8f8aSJiri Pirko 	DEVLINK_PARAM_DRIVER(NSIM_DEVLINK_PARAM_ID_TEST1,
256150e8f8aSJiri Pirko 			     "test1", DEVLINK_PARAM_TYPE_BOOL,
257150e8f8aSJiri Pirko 			     BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
258150e8f8aSJiri Pirko 			     NULL, NULL, NULL),
259150e8f8aSJiri Pirko };
260150e8f8aSJiri Pirko 
261150e8f8aSJiri Pirko static void nsim_devlink_set_params_init_values(struct nsim_dev *nsim_dev,
262150e8f8aSJiri Pirko 						struct devlink *devlink)
263150e8f8aSJiri Pirko {
264150e8f8aSJiri Pirko 	union devlink_param_value value;
265150e8f8aSJiri Pirko 
266150e8f8aSJiri Pirko 	value.vu32 = nsim_dev->max_macs;
267150e8f8aSJiri Pirko 	devlink_param_driverinit_value_set(devlink,
268150e8f8aSJiri Pirko 					   DEVLINK_PARAM_GENERIC_ID_MAX_MACS,
269150e8f8aSJiri Pirko 					   value);
270150e8f8aSJiri Pirko 	value.vbool = nsim_dev->test1;
271150e8f8aSJiri Pirko 	devlink_param_driverinit_value_set(devlink,
272150e8f8aSJiri Pirko 					   NSIM_DEVLINK_PARAM_ID_TEST1,
273150e8f8aSJiri Pirko 					   value);
274150e8f8aSJiri Pirko }
275150e8f8aSJiri Pirko 
276150e8f8aSJiri Pirko static void nsim_devlink_param_load_driverinit_values(struct devlink *devlink)
277150e8f8aSJiri Pirko {
278150e8f8aSJiri Pirko 	struct nsim_dev *nsim_dev = devlink_priv(devlink);
279150e8f8aSJiri Pirko 	union devlink_param_value saved_value;
280150e8f8aSJiri Pirko 	int err;
281150e8f8aSJiri Pirko 
282150e8f8aSJiri Pirko 	err = devlink_param_driverinit_value_get(devlink,
283150e8f8aSJiri Pirko 						 DEVLINK_PARAM_GENERIC_ID_MAX_MACS,
284150e8f8aSJiri Pirko 						 &saved_value);
285150e8f8aSJiri Pirko 	if (!err)
286150e8f8aSJiri Pirko 		nsim_dev->max_macs = saved_value.vu32;
287150e8f8aSJiri Pirko 	err = devlink_param_driverinit_value_get(devlink,
288150e8f8aSJiri Pirko 						 NSIM_DEVLINK_PARAM_ID_TEST1,
289150e8f8aSJiri Pirko 						 &saved_value);
290150e8f8aSJiri Pirko 	if (!err)
291150e8f8aSJiri Pirko 		nsim_dev->test1 = saved_value.vbool;
292150e8f8aSJiri Pirko }
293150e8f8aSJiri Pirko 
2944418f862SJiri Pirko #define NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX 16
2954418f862SJiri Pirko 
2964418f862SJiri Pirko static int nsim_dev_dummy_region_init(struct nsim_dev *nsim_dev,
2974418f862SJiri Pirko 				      struct devlink *devlink)
2984418f862SJiri Pirko {
2994418f862SJiri Pirko 	nsim_dev->dummy_region =
3004418f862SJiri Pirko 		devlink_region_create(devlink, "dummy",
3014418f862SJiri Pirko 				      NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX,
3024418f862SJiri Pirko 				      NSIM_DEV_DUMMY_REGION_SIZE);
3034418f862SJiri Pirko 	return PTR_ERR_OR_ZERO(nsim_dev->dummy_region);
3044418f862SJiri Pirko }
3054418f862SJiri Pirko 
3064418f862SJiri Pirko static void nsim_dev_dummy_region_exit(struct nsim_dev *nsim_dev)
3074418f862SJiri Pirko {
3084418f862SJiri Pirko 	devlink_region_destroy(nsim_dev->dummy_region);
3094418f862SJiri Pirko }
3104418f862SJiri Pirko 
311da58f90fSIdo Schimmel struct nsim_trap_item {
312da58f90fSIdo Schimmel 	void *trap_ctx;
313da58f90fSIdo Schimmel 	enum devlink_trap_action action;
314da58f90fSIdo Schimmel };
315da58f90fSIdo Schimmel 
316da58f90fSIdo Schimmel struct nsim_trap_data {
317da58f90fSIdo Schimmel 	struct delayed_work trap_report_dw;
318da58f90fSIdo Schimmel 	struct nsim_trap_item *trap_items_arr;
319da58f90fSIdo Schimmel 	struct nsim_dev *nsim_dev;
320da58f90fSIdo Schimmel 	spinlock_t trap_lock;	/* Protects trap_items_arr */
321da58f90fSIdo Schimmel };
322da58f90fSIdo Schimmel 
3239e087457SIdo Schimmel /* All driver-specific traps must be documented in
3249e087457SIdo Schimmel  * Documentation/networking/devlink-trap-netdevsim.rst
3259e087457SIdo Schimmel  */
326da58f90fSIdo Schimmel enum {
327da58f90fSIdo Schimmel 	NSIM_TRAP_ID_BASE = DEVLINK_TRAP_GENERIC_ID_MAX,
328da58f90fSIdo Schimmel 	NSIM_TRAP_ID_FID_MISS,
329da58f90fSIdo Schimmel };
330da58f90fSIdo Schimmel 
331da58f90fSIdo Schimmel #define NSIM_TRAP_NAME_FID_MISS "fid_miss"
332da58f90fSIdo Schimmel 
333da58f90fSIdo Schimmel #define NSIM_TRAP_METADATA DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT
334da58f90fSIdo Schimmel 
335da58f90fSIdo Schimmel #define NSIM_TRAP_DROP(_id, _group_id)					      \
336da58f90fSIdo Schimmel 	DEVLINK_TRAP_GENERIC(DROP, DROP, _id,				      \
337da58f90fSIdo Schimmel 			     DEVLINK_TRAP_GROUP_GENERIC(_group_id),	      \
338da58f90fSIdo Schimmel 			     NSIM_TRAP_METADATA)
339da58f90fSIdo Schimmel #define NSIM_TRAP_EXCEPTION(_id, _group_id)				      \
340da58f90fSIdo Schimmel 	DEVLINK_TRAP_GENERIC(EXCEPTION, TRAP, _id,			      \
341da58f90fSIdo Schimmel 			     DEVLINK_TRAP_GROUP_GENERIC(_group_id),	      \
342da58f90fSIdo Schimmel 			     NSIM_TRAP_METADATA)
343da58f90fSIdo Schimmel #define NSIM_TRAP_DRIVER_EXCEPTION(_id, _group_id)			      \
344da58f90fSIdo Schimmel 	DEVLINK_TRAP_DRIVER(EXCEPTION, TRAP, NSIM_TRAP_ID_##_id,	      \
345da58f90fSIdo Schimmel 			    NSIM_TRAP_NAME_##_id,			      \
346da58f90fSIdo Schimmel 			    DEVLINK_TRAP_GROUP_GENERIC(_group_id),	      \
347da58f90fSIdo Schimmel 			    NSIM_TRAP_METADATA)
348da58f90fSIdo Schimmel 
349da58f90fSIdo Schimmel static const struct devlink_trap nsim_traps_arr[] = {
350da58f90fSIdo Schimmel 	NSIM_TRAP_DROP(SMAC_MC, L2_DROPS),
351da58f90fSIdo Schimmel 	NSIM_TRAP_DROP(VLAN_TAG_MISMATCH, L2_DROPS),
352da58f90fSIdo Schimmel 	NSIM_TRAP_DROP(INGRESS_VLAN_FILTER, L2_DROPS),
353da58f90fSIdo Schimmel 	NSIM_TRAP_DROP(INGRESS_STP_FILTER, L2_DROPS),
354da58f90fSIdo Schimmel 	NSIM_TRAP_DROP(EMPTY_TX_LIST, L2_DROPS),
355da58f90fSIdo Schimmel 	NSIM_TRAP_DROP(PORT_LOOPBACK_FILTER, L2_DROPS),
356da58f90fSIdo Schimmel 	NSIM_TRAP_DRIVER_EXCEPTION(FID_MISS, L2_DROPS),
357da58f90fSIdo Schimmel 	NSIM_TRAP_DROP(BLACKHOLE_ROUTE, L3_DROPS),
358da58f90fSIdo Schimmel 	NSIM_TRAP_EXCEPTION(TTL_ERROR, L3_DROPS),
359da58f90fSIdo Schimmel 	NSIM_TRAP_DROP(TAIL_DROP, BUFFER_DROPS),
360da58f90fSIdo Schimmel };
361da58f90fSIdo Schimmel 
362da58f90fSIdo Schimmel #define NSIM_TRAP_L4_DATA_LEN 100
363da58f90fSIdo Schimmel 
364da58f90fSIdo Schimmel static struct sk_buff *nsim_dev_trap_skb_build(void)
365da58f90fSIdo Schimmel {
366da58f90fSIdo Schimmel 	int tot_len, data_len = NSIM_TRAP_L4_DATA_LEN;
367da58f90fSIdo Schimmel 	struct sk_buff *skb;
368da58f90fSIdo Schimmel 	struct udphdr *udph;
369da58f90fSIdo Schimmel 	struct ethhdr *eth;
370da58f90fSIdo Schimmel 	struct iphdr *iph;
371da58f90fSIdo Schimmel 
372da58f90fSIdo Schimmel 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
373da58f90fSIdo Schimmel 	if (!skb)
374da58f90fSIdo Schimmel 		return NULL;
375da58f90fSIdo Schimmel 	tot_len = sizeof(struct iphdr) + sizeof(struct udphdr) + data_len;
376da58f90fSIdo Schimmel 
377da58f90fSIdo Schimmel 	eth = skb_put(skb, sizeof(struct ethhdr));
378da58f90fSIdo Schimmel 	eth_random_addr(eth->h_dest);
379da58f90fSIdo Schimmel 	eth_random_addr(eth->h_source);
380da58f90fSIdo Schimmel 	eth->h_proto = htons(ETH_P_IP);
381da58f90fSIdo Schimmel 	skb->protocol = htons(ETH_P_IP);
382da58f90fSIdo Schimmel 
383da58f90fSIdo Schimmel 	iph = skb_put(skb, sizeof(struct iphdr));
384da58f90fSIdo Schimmel 	iph->protocol = IPPROTO_UDP;
385da58f90fSIdo Schimmel 	iph->saddr = in_aton("192.0.2.1");
386da58f90fSIdo Schimmel 	iph->daddr = in_aton("198.51.100.1");
387da58f90fSIdo Schimmel 	iph->version = 0x4;
388da58f90fSIdo Schimmel 	iph->frag_off = 0;
389da58f90fSIdo Schimmel 	iph->ihl = 0x5;
390da58f90fSIdo Schimmel 	iph->tot_len = htons(tot_len);
391da58f90fSIdo Schimmel 	iph->ttl = 100;
392d9bd6d27SYueHaibing 	iph->check = 0;
393d9bd6d27SYueHaibing 	iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
394da58f90fSIdo Schimmel 
395da58f90fSIdo Schimmel 	udph = skb_put_zero(skb, sizeof(struct udphdr) + data_len);
396da58f90fSIdo Schimmel 	get_random_bytes(&udph->source, sizeof(u16));
397da58f90fSIdo Schimmel 	get_random_bytes(&udph->dest, sizeof(u16));
398da58f90fSIdo Schimmel 	udph->len = htons(sizeof(struct udphdr) + data_len);
399da58f90fSIdo Schimmel 
400da58f90fSIdo Schimmel 	return skb;
401da58f90fSIdo Schimmel }
402da58f90fSIdo Schimmel 
403da58f90fSIdo Schimmel static void nsim_dev_trap_report(struct nsim_dev_port *nsim_dev_port)
404da58f90fSIdo Schimmel {
405da58f90fSIdo Schimmel 	struct nsim_dev *nsim_dev = nsim_dev_port->ns->nsim_dev;
406da58f90fSIdo Schimmel 	struct devlink *devlink = priv_to_devlink(nsim_dev);
407da58f90fSIdo Schimmel 	struct nsim_trap_data *nsim_trap_data;
408da58f90fSIdo Schimmel 	int i;
409da58f90fSIdo Schimmel 
410da58f90fSIdo Schimmel 	nsim_trap_data = nsim_dev->trap_data;
411da58f90fSIdo Schimmel 
412da58f90fSIdo Schimmel 	spin_lock(&nsim_trap_data->trap_lock);
413da58f90fSIdo Schimmel 	for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) {
414da58f90fSIdo Schimmel 		struct nsim_trap_item *nsim_trap_item;
415da58f90fSIdo Schimmel 		struct sk_buff *skb;
416da58f90fSIdo Schimmel 
417da58f90fSIdo Schimmel 		nsim_trap_item = &nsim_trap_data->trap_items_arr[i];
418da58f90fSIdo Schimmel 		if (nsim_trap_item->action == DEVLINK_TRAP_ACTION_DROP)
419da58f90fSIdo Schimmel 			continue;
420da58f90fSIdo Schimmel 
421da58f90fSIdo Schimmel 		skb = nsim_dev_trap_skb_build();
422da58f90fSIdo Schimmel 		if (!skb)
423da58f90fSIdo Schimmel 			continue;
424da58f90fSIdo Schimmel 		skb->dev = nsim_dev_port->ns->netdev;
425da58f90fSIdo Schimmel 
426da58f90fSIdo Schimmel 		/* Trapped packets are usually passed to devlink in softIRQ,
427da58f90fSIdo Schimmel 		 * but in this case they are generated in a workqueue. Disable
428da58f90fSIdo Schimmel 		 * softIRQs to prevent lockdep from complaining about
429da58f90fSIdo Schimmel 		 * "incosistent lock state".
430da58f90fSIdo Schimmel 		 */
431da58f90fSIdo Schimmel 		local_bh_disable();
432da58f90fSIdo Schimmel 		devlink_trap_report(devlink, skb, nsim_trap_item->trap_ctx,
433da58f90fSIdo Schimmel 				    &nsim_dev_port->devlink_port);
434da58f90fSIdo Schimmel 		local_bh_enable();
435da58f90fSIdo Schimmel 		consume_skb(skb);
436da58f90fSIdo Schimmel 	}
437da58f90fSIdo Schimmel 	spin_unlock(&nsim_trap_data->trap_lock);
438da58f90fSIdo Schimmel }
439da58f90fSIdo Schimmel 
440da58f90fSIdo Schimmel #define NSIM_TRAP_REPORT_INTERVAL_MS	100
441da58f90fSIdo Schimmel 
442da58f90fSIdo Schimmel static void nsim_dev_trap_report_work(struct work_struct *work)
443da58f90fSIdo Schimmel {
444da58f90fSIdo Schimmel 	struct nsim_trap_data *nsim_trap_data;
445da58f90fSIdo Schimmel 	struct nsim_dev_port *nsim_dev_port;
446da58f90fSIdo Schimmel 	struct nsim_dev *nsim_dev;
447da58f90fSIdo Schimmel 
448da58f90fSIdo Schimmel 	nsim_trap_data = container_of(work, struct nsim_trap_data,
449da58f90fSIdo Schimmel 				      trap_report_dw.work);
450da58f90fSIdo Schimmel 	nsim_dev = nsim_trap_data->nsim_dev;
451da58f90fSIdo Schimmel 
452da58f90fSIdo Schimmel 	/* For each running port and enabled packet trap, generate a UDP
453da58f90fSIdo Schimmel 	 * packet with a random 5-tuple and report it.
454da58f90fSIdo Schimmel 	 */
455da58f90fSIdo Schimmel 	mutex_lock(&nsim_dev->port_list_lock);
456da58f90fSIdo Schimmel 	list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) {
457da58f90fSIdo Schimmel 		if (!netif_running(nsim_dev_port->ns->netdev))
458da58f90fSIdo Schimmel 			continue;
459da58f90fSIdo Schimmel 
460da58f90fSIdo Schimmel 		nsim_dev_trap_report(nsim_dev_port);
461da58f90fSIdo Schimmel 	}
462da58f90fSIdo Schimmel 	mutex_unlock(&nsim_dev->port_list_lock);
463da58f90fSIdo Schimmel 
464da58f90fSIdo Schimmel 	schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw,
465da58f90fSIdo Schimmel 			      msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS));
466da58f90fSIdo Schimmel }
467da58f90fSIdo Schimmel 
468da58f90fSIdo Schimmel static int nsim_dev_traps_init(struct devlink *devlink)
469da58f90fSIdo Schimmel {
470da58f90fSIdo Schimmel 	struct nsim_dev *nsim_dev = devlink_priv(devlink);
471da58f90fSIdo Schimmel 	struct nsim_trap_data *nsim_trap_data;
472da58f90fSIdo Schimmel 	int err;
473da58f90fSIdo Schimmel 
474da58f90fSIdo Schimmel 	nsim_trap_data = kzalloc(sizeof(*nsim_trap_data), GFP_KERNEL);
475da58f90fSIdo Schimmel 	if (!nsim_trap_data)
476da58f90fSIdo Schimmel 		return -ENOMEM;
477da58f90fSIdo Schimmel 
478da58f90fSIdo Schimmel 	nsim_trap_data->trap_items_arr = kcalloc(ARRAY_SIZE(nsim_traps_arr),
479da58f90fSIdo Schimmel 						 sizeof(struct nsim_trap_item),
480da58f90fSIdo Schimmel 						 GFP_KERNEL);
481da58f90fSIdo Schimmel 	if (!nsim_trap_data->trap_items_arr) {
482da58f90fSIdo Schimmel 		err = -ENOMEM;
483da58f90fSIdo Schimmel 		goto err_trap_data_free;
484da58f90fSIdo Schimmel 	}
485da58f90fSIdo Schimmel 
486da58f90fSIdo Schimmel 	/* The lock is used to protect the action state of the registered
487da58f90fSIdo Schimmel 	 * traps. The value is written by user and read in delayed work when
488da58f90fSIdo Schimmel 	 * iterating over all the traps.
489da58f90fSIdo Schimmel 	 */
490da58f90fSIdo Schimmel 	spin_lock_init(&nsim_trap_data->trap_lock);
491da58f90fSIdo Schimmel 	nsim_trap_data->nsim_dev = nsim_dev;
492da58f90fSIdo Schimmel 	nsim_dev->trap_data = nsim_trap_data;
493da58f90fSIdo Schimmel 
494da58f90fSIdo Schimmel 	err = devlink_traps_register(devlink, nsim_traps_arr,
495da58f90fSIdo Schimmel 				     ARRAY_SIZE(nsim_traps_arr), NULL);
496da58f90fSIdo Schimmel 	if (err)
497da58f90fSIdo Schimmel 		goto err_trap_items_free;
498da58f90fSIdo Schimmel 
499da58f90fSIdo Schimmel 	INIT_DELAYED_WORK(&nsim_dev->trap_data->trap_report_dw,
500da58f90fSIdo Schimmel 			  nsim_dev_trap_report_work);
501da58f90fSIdo Schimmel 	schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw,
502da58f90fSIdo Schimmel 			      msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS));
503da58f90fSIdo Schimmel 
504da58f90fSIdo Schimmel 	return 0;
505da58f90fSIdo Schimmel 
506da58f90fSIdo Schimmel err_trap_items_free:
507da58f90fSIdo Schimmel 	kfree(nsim_trap_data->trap_items_arr);
508da58f90fSIdo Schimmel err_trap_data_free:
509da58f90fSIdo Schimmel 	kfree(nsim_trap_data);
510da58f90fSIdo Schimmel 	return err;
511da58f90fSIdo Schimmel }
512da58f90fSIdo Schimmel 
513da58f90fSIdo Schimmel static void nsim_dev_traps_exit(struct devlink *devlink)
514da58f90fSIdo Schimmel {
515da58f90fSIdo Schimmel 	struct nsim_dev *nsim_dev = devlink_priv(devlink);
516da58f90fSIdo Schimmel 
517da58f90fSIdo Schimmel 	cancel_delayed_work_sync(&nsim_dev->trap_data->trap_report_dw);
518da58f90fSIdo Schimmel 	devlink_traps_unregister(devlink, nsim_traps_arr,
519da58f90fSIdo Schimmel 				 ARRAY_SIZE(nsim_traps_arr));
520da58f90fSIdo Schimmel 	kfree(nsim_dev->trap_data->trap_items_arr);
521da58f90fSIdo Schimmel 	kfree(nsim_dev->trap_data);
522da58f90fSIdo Schimmel }
523da58f90fSIdo Schimmel 
524*97691069SJiri Pirko static int nsim_dev_reload_down(struct devlink *devlink,
525*97691069SJiri Pirko 				struct netlink_ext_ack *extack)
526*97691069SJiri Pirko {
527*97691069SJiri Pirko 	return 0;
528*97691069SJiri Pirko }
529*97691069SJiri Pirko 
530*97691069SJiri Pirko static int nsim_dev_reload_up(struct devlink *devlink,
5318fb4bc6fSJiri Pirko 			      struct netlink_ext_ack *extack)
5328fb4bc6fSJiri Pirko {
5338fb4bc6fSJiri Pirko 	enum nsim_resource_id res_ids[] = {
5348fb4bc6fSJiri Pirko 		NSIM_RESOURCE_IPV4_FIB, NSIM_RESOURCE_IPV4_FIB_RULES,
5358fb4bc6fSJiri Pirko 		NSIM_RESOURCE_IPV6_FIB, NSIM_RESOURCE_IPV6_FIB_RULES
5368fb4bc6fSJiri Pirko 	};
53759c84b9fSDavid Ahern 	struct net *net = nsim_devlink_net(devlink);
5388fb4bc6fSJiri Pirko 	int i;
5398fb4bc6fSJiri Pirko 
5408fb4bc6fSJiri Pirko 	for (i = 0; i < ARRAY_SIZE(res_ids); ++i) {
5418fb4bc6fSJiri Pirko 		int err;
5428fb4bc6fSJiri Pirko 		u64 val;
5438fb4bc6fSJiri Pirko 
5448fb4bc6fSJiri Pirko 		err = devlink_resource_size_get(devlink, res_ids[i], &val);
5458fb4bc6fSJiri Pirko 		if (!err) {
54659c84b9fSDavid Ahern 			err = nsim_fib_set_max(net, res_ids[i], val, extack);
5478fb4bc6fSJiri Pirko 			if (err)
5488fb4bc6fSJiri Pirko 				return err;
5498fb4bc6fSJiri Pirko 		}
5508fb4bc6fSJiri Pirko 	}
551150e8f8aSJiri Pirko 	nsim_devlink_param_load_driverinit_values(devlink);
5528fb4bc6fSJiri Pirko 
5538fb4bc6fSJiri Pirko 	return 0;
5548fb4bc6fSJiri Pirko }
5558fb4bc6fSJiri Pirko 
556fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_SIZE 500000
557fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_CHUNK_SIZE 1000
558fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_CHUNK_TIME_MS 10
559fa4dfc4aSJiri Pirko 
560fa4dfc4aSJiri Pirko static int nsim_dev_flash_update(struct devlink *devlink, const char *file_name,
561fa4dfc4aSJiri Pirko 				 const char *component,
562fa4dfc4aSJiri Pirko 				 struct netlink_ext_ack *extack)
563fa4dfc4aSJiri Pirko {
564fa4dfc4aSJiri Pirko 	struct nsim_dev *nsim_dev = devlink_priv(devlink);
565fa4dfc4aSJiri Pirko 	int i;
566fa4dfc4aSJiri Pirko 
567fa4dfc4aSJiri Pirko 	if (nsim_dev->fw_update_status) {
568fa4dfc4aSJiri Pirko 		devlink_flash_update_begin_notify(devlink);
569fa4dfc4aSJiri Pirko 		devlink_flash_update_status_notify(devlink,
570fa4dfc4aSJiri Pirko 						   "Preparing to flash",
571fa4dfc4aSJiri Pirko 						   component, 0, 0);
572fa4dfc4aSJiri Pirko 	}
573fa4dfc4aSJiri Pirko 
574fa4dfc4aSJiri Pirko 	for (i = 0; i < NSIM_DEV_FLASH_SIZE / NSIM_DEV_FLASH_CHUNK_SIZE; i++) {
575fa4dfc4aSJiri Pirko 		if (nsim_dev->fw_update_status)
576fa4dfc4aSJiri Pirko 			devlink_flash_update_status_notify(devlink, "Flashing",
577fa4dfc4aSJiri Pirko 							   component,
578fa4dfc4aSJiri Pirko 							   i * NSIM_DEV_FLASH_CHUNK_SIZE,
579fa4dfc4aSJiri Pirko 							   NSIM_DEV_FLASH_SIZE);
580fa4dfc4aSJiri Pirko 		msleep(NSIM_DEV_FLASH_CHUNK_TIME_MS);
581fa4dfc4aSJiri Pirko 	}
582fa4dfc4aSJiri Pirko 
583fa4dfc4aSJiri Pirko 	if (nsim_dev->fw_update_status) {
584fa4dfc4aSJiri Pirko 		devlink_flash_update_status_notify(devlink, "Flashing",
585fa4dfc4aSJiri Pirko 						   component,
586fa4dfc4aSJiri Pirko 						   NSIM_DEV_FLASH_SIZE,
587fa4dfc4aSJiri Pirko 						   NSIM_DEV_FLASH_SIZE);
588fa4dfc4aSJiri Pirko 		devlink_flash_update_status_notify(devlink, "Flashing done",
589fa4dfc4aSJiri Pirko 						   component, 0, 0);
590fa4dfc4aSJiri Pirko 		devlink_flash_update_end_notify(devlink);
591fa4dfc4aSJiri Pirko 	}
592fa4dfc4aSJiri Pirko 
593fa4dfc4aSJiri Pirko 	return 0;
594fa4dfc4aSJiri Pirko }
595fa4dfc4aSJiri Pirko 
596da58f90fSIdo Schimmel static struct nsim_trap_item *
597da58f90fSIdo Schimmel nsim_dev_trap_item_lookup(struct nsim_dev *nsim_dev, u16 trap_id)
598da58f90fSIdo Schimmel {
599da58f90fSIdo Schimmel 	struct nsim_trap_data *nsim_trap_data = nsim_dev->trap_data;
600da58f90fSIdo Schimmel 	int i;
601da58f90fSIdo Schimmel 
602da58f90fSIdo Schimmel 	for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) {
603da58f90fSIdo Schimmel 		if (nsim_traps_arr[i].id == trap_id)
604da58f90fSIdo Schimmel 			return &nsim_trap_data->trap_items_arr[i];
605da58f90fSIdo Schimmel 	}
606da58f90fSIdo Schimmel 
607da58f90fSIdo Schimmel 	return NULL;
608da58f90fSIdo Schimmel }
609da58f90fSIdo Schimmel 
610da58f90fSIdo Schimmel static int nsim_dev_devlink_trap_init(struct devlink *devlink,
611da58f90fSIdo Schimmel 				      const struct devlink_trap *trap,
612da58f90fSIdo Schimmel 				      void *trap_ctx)
613da58f90fSIdo Schimmel {
614da58f90fSIdo Schimmel 	struct nsim_dev *nsim_dev = devlink_priv(devlink);
615da58f90fSIdo Schimmel 	struct nsim_trap_item *nsim_trap_item;
616da58f90fSIdo Schimmel 
617da58f90fSIdo Schimmel 	nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id);
618da58f90fSIdo Schimmel 	if (WARN_ON(!nsim_trap_item))
619da58f90fSIdo Schimmel 		return -ENOENT;
620da58f90fSIdo Schimmel 
621da58f90fSIdo Schimmel 	nsim_trap_item->trap_ctx = trap_ctx;
622da58f90fSIdo Schimmel 	nsim_trap_item->action = trap->init_action;
623da58f90fSIdo Schimmel 
624da58f90fSIdo Schimmel 	return 0;
625da58f90fSIdo Schimmel }
626da58f90fSIdo Schimmel 
627da58f90fSIdo Schimmel static int
628da58f90fSIdo Schimmel nsim_dev_devlink_trap_action_set(struct devlink *devlink,
629da58f90fSIdo Schimmel 				 const struct devlink_trap *trap,
630da58f90fSIdo Schimmel 				 enum devlink_trap_action action)
631da58f90fSIdo Schimmel {
632da58f90fSIdo Schimmel 	struct nsim_dev *nsim_dev = devlink_priv(devlink);
633da58f90fSIdo Schimmel 	struct nsim_trap_item *nsim_trap_item;
634da58f90fSIdo Schimmel 
635da58f90fSIdo Schimmel 	nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id);
636da58f90fSIdo Schimmel 	if (WARN_ON(!nsim_trap_item))
637da58f90fSIdo Schimmel 		return -ENOENT;
638da58f90fSIdo Schimmel 
639da58f90fSIdo Schimmel 	spin_lock(&nsim_dev->trap_data->trap_lock);
640da58f90fSIdo Schimmel 	nsim_trap_item->action = action;
641da58f90fSIdo Schimmel 	spin_unlock(&nsim_dev->trap_data->trap_lock);
642da58f90fSIdo Schimmel 
643da58f90fSIdo Schimmel 	return 0;
644da58f90fSIdo Schimmel }
645da58f90fSIdo Schimmel 
6468fb4bc6fSJiri Pirko static const struct devlink_ops nsim_dev_devlink_ops = {
647*97691069SJiri Pirko 	.reload_down = nsim_dev_reload_down,
648*97691069SJiri Pirko 	.reload_up = nsim_dev_reload_up,
649fa4dfc4aSJiri Pirko 	.flash_update = nsim_dev_flash_update,
650da58f90fSIdo Schimmel 	.trap_init = nsim_dev_devlink_trap_init,
651da58f90fSIdo Schimmel 	.trap_action_set = nsim_dev_devlink_trap_action_set,
6528fb4bc6fSJiri Pirko };
6538fb4bc6fSJiri Pirko 
654150e8f8aSJiri Pirko #define NSIM_DEV_MAX_MACS_DEFAULT 32
655150e8f8aSJiri Pirko #define NSIM_DEV_TEST1_DEFAULT true
656150e8f8aSJiri Pirko 
6578320d145SJiri Pirko static struct nsim_dev *
6588320d145SJiri Pirko nsim_dev_create(struct nsim_bus_dev *nsim_bus_dev, unsigned int port_count)
6598fb4bc6fSJiri Pirko {
6608fb4bc6fSJiri Pirko 	struct nsim_dev *nsim_dev;
6618fb4bc6fSJiri Pirko 	struct devlink *devlink;
6628fb4bc6fSJiri Pirko 	int err;
6638fb4bc6fSJiri Pirko 
6648fb4bc6fSJiri Pirko 	devlink = devlink_alloc(&nsim_dev_devlink_ops, sizeof(*nsim_dev));
6658fb4bc6fSJiri Pirko 	if (!devlink)
666a60f9e48SJiri Pirko 		return ERR_PTR(-ENOMEM);
6678fb4bc6fSJiri Pirko 	nsim_dev = devlink_priv(devlink);
668d514f41eSJiri Pirko 	nsim_dev->nsim_bus_dev = nsim_bus_dev;
669514cf64cSJiri Pirko 	nsim_dev->switch_id.id_len = sizeof(nsim_dev->switch_id.id);
670514cf64cSJiri Pirko 	get_random_bytes(nsim_dev->switch_id.id, nsim_dev->switch_id.id_len);
6718320d145SJiri Pirko 	INIT_LIST_HEAD(&nsim_dev->port_list);
672794b2c05SJiri Pirko 	mutex_init(&nsim_dev->port_list_lock);
673fa4dfc4aSJiri Pirko 	nsim_dev->fw_update_status = true;
674150e8f8aSJiri Pirko 	nsim_dev->max_macs = NSIM_DEV_MAX_MACS_DEFAULT;
675150e8f8aSJiri Pirko 	nsim_dev->test1 = NSIM_DEV_TEST1_DEFAULT;
6768fb4bc6fSJiri Pirko 
6778fb4bc6fSJiri Pirko 	err = nsim_dev_resources_register(devlink);
6788fb4bc6fSJiri Pirko 	if (err)
67959c84b9fSDavid Ahern 		goto err_devlink_free;
6808fb4bc6fSJiri Pirko 
681a60f9e48SJiri Pirko 	err = devlink_register(devlink, &nsim_bus_dev->dev);
6828fb4bc6fSJiri Pirko 	if (err)
6838fb4bc6fSJiri Pirko 		goto err_resources_unregister;
6848fb4bc6fSJiri Pirko 
685150e8f8aSJiri Pirko 	err = devlink_params_register(devlink, nsim_devlink_params,
686150e8f8aSJiri Pirko 				      ARRAY_SIZE(nsim_devlink_params));
687d514f41eSJiri Pirko 	if (err)
688d514f41eSJiri Pirko 		goto err_dl_unregister;
689150e8f8aSJiri Pirko 	nsim_devlink_set_params_init_values(nsim_dev, devlink);
690150e8f8aSJiri Pirko 
6914418f862SJiri Pirko 	err = nsim_dev_dummy_region_init(nsim_dev, devlink);
692150e8f8aSJiri Pirko 	if (err)
693150e8f8aSJiri Pirko 		goto err_params_unregister;
694d514f41eSJiri Pirko 
695da58f90fSIdo Schimmel 	err = nsim_dev_traps_init(devlink);
6964418f862SJiri Pirko 	if (err)
6974418f862SJiri Pirko 		goto err_dummy_region_exit;
6984418f862SJiri Pirko 
699da58f90fSIdo Schimmel 	err = nsim_dev_debugfs_init(nsim_dev);
700da58f90fSIdo Schimmel 	if (err)
701da58f90fSIdo Schimmel 		goto err_traps_exit;
702da58f90fSIdo Schimmel 
703d514f41eSJiri Pirko 	err = nsim_bpf_dev_init(nsim_dev);
704d514f41eSJiri Pirko 	if (err)
705d514f41eSJiri Pirko 		goto err_debugfs_exit;
706d514f41eSJiri Pirko 
707150e8f8aSJiri Pirko 	devlink_params_publish(devlink);
708a60f9e48SJiri Pirko 	return nsim_dev;
7098fb4bc6fSJiri Pirko 
710d514f41eSJiri Pirko err_debugfs_exit:
711d514f41eSJiri Pirko 	nsim_dev_debugfs_exit(nsim_dev);
712da58f90fSIdo Schimmel err_traps_exit:
713da58f90fSIdo Schimmel 	nsim_dev_traps_exit(devlink);
7144418f862SJiri Pirko err_dummy_region_exit:
7154418f862SJiri Pirko 	nsim_dev_dummy_region_exit(nsim_dev);
716150e8f8aSJiri Pirko err_params_unregister:
717150e8f8aSJiri Pirko 	devlink_params_unregister(devlink, nsim_devlink_params,
718150e8f8aSJiri Pirko 				  ARRAY_SIZE(nsim_devlink_params));
719d514f41eSJiri Pirko err_dl_unregister:
720d514f41eSJiri Pirko 	devlink_unregister(devlink);
7218fb4bc6fSJiri Pirko err_resources_unregister:
7228fb4bc6fSJiri Pirko 	devlink_resources_unregister(devlink, NULL);
7238fb4bc6fSJiri Pirko err_devlink_free:
7248fb4bc6fSJiri Pirko 	devlink_free(devlink);
725a60f9e48SJiri Pirko 	return ERR_PTR(err);
7268fb4bc6fSJiri Pirko }
7278fb4bc6fSJiri Pirko 
728e05b2d14SJiri Pirko static void nsim_dev_destroy(struct nsim_dev *nsim_dev)
7298fb4bc6fSJiri Pirko {
730a60f9e48SJiri Pirko 	struct devlink *devlink = priv_to_devlink(nsim_dev);
7318fb4bc6fSJiri Pirko 
732d514f41eSJiri Pirko 	nsim_bpf_dev_exit(nsim_dev);
733d514f41eSJiri Pirko 	nsim_dev_debugfs_exit(nsim_dev);
734da58f90fSIdo Schimmel 	nsim_dev_traps_exit(devlink);
7354418f862SJiri Pirko 	nsim_dev_dummy_region_exit(nsim_dev);
736150e8f8aSJiri Pirko 	devlink_params_unregister(devlink, nsim_devlink_params,
737150e8f8aSJiri Pirko 				  ARRAY_SIZE(nsim_devlink_params));
7388fb4bc6fSJiri Pirko 	devlink_unregister(devlink);
7398fb4bc6fSJiri Pirko 	devlink_resources_unregister(devlink, NULL);
740794b2c05SJiri Pirko 	mutex_destroy(&nsim_dev->port_list_lock);
7418fb4bc6fSJiri Pirko 	devlink_free(devlink);
7428fb4bc6fSJiri Pirko }
743d514f41eSJiri Pirko 
744794b2c05SJiri Pirko static int __nsim_dev_port_add(struct nsim_dev *nsim_dev,
745794b2c05SJiri Pirko 			       unsigned int port_index)
7468320d145SJiri Pirko {
7478320d145SJiri Pirko 	struct nsim_dev_port *nsim_dev_port;
7488320d145SJiri Pirko 	struct devlink_port *devlink_port;
7498320d145SJiri Pirko 	int err;
7508320d145SJiri Pirko 
7518320d145SJiri Pirko 	nsim_dev_port = kzalloc(sizeof(*nsim_dev_port), GFP_KERNEL);
7528320d145SJiri Pirko 	if (!nsim_dev_port)
7538320d145SJiri Pirko 		return -ENOMEM;
7548320d145SJiri Pirko 	nsim_dev_port->port_index = port_index;
7558320d145SJiri Pirko 
7568320d145SJiri Pirko 	devlink_port = &nsim_dev_port->devlink_port;
7578320d145SJiri Pirko 	devlink_port_attrs_set(devlink_port, DEVLINK_PORT_FLAVOUR_PHYSICAL,
7588320d145SJiri Pirko 			       port_index + 1, 0, 0,
7598320d145SJiri Pirko 			       nsim_dev->switch_id.id,
7608320d145SJiri Pirko 			       nsim_dev->switch_id.id_len);
7618320d145SJiri Pirko 	err = devlink_port_register(priv_to_devlink(nsim_dev), devlink_port,
7628320d145SJiri Pirko 				    port_index);
7638320d145SJiri Pirko 	if (err)
7648320d145SJiri Pirko 		goto err_port_free;
7658320d145SJiri Pirko 
7668320d145SJiri Pirko 	err = nsim_dev_port_debugfs_init(nsim_dev, nsim_dev_port);
7678320d145SJiri Pirko 	if (err)
7688320d145SJiri Pirko 		goto err_dl_port_unregister;
7698320d145SJiri Pirko 
770e05b2d14SJiri Pirko 	nsim_dev_port->ns = nsim_create(nsim_dev, nsim_dev_port);
771e05b2d14SJiri Pirko 	if (IS_ERR(nsim_dev_port->ns)) {
772e05b2d14SJiri Pirko 		err = PTR_ERR(nsim_dev_port->ns);
773e05b2d14SJiri Pirko 		goto err_port_debugfs_exit;
774e05b2d14SJiri Pirko 	}
775e05b2d14SJiri Pirko 
776e05b2d14SJiri Pirko 	devlink_port_type_eth_set(devlink_port, nsim_dev_port->ns->netdev);
7778320d145SJiri Pirko 	list_add(&nsim_dev_port->list, &nsim_dev->port_list);
7788320d145SJiri Pirko 
7798320d145SJiri Pirko 	return 0;
7808320d145SJiri Pirko 
781e05b2d14SJiri Pirko err_port_debugfs_exit:
782e05b2d14SJiri Pirko 	nsim_dev_port_debugfs_exit(nsim_dev_port);
7838320d145SJiri Pirko err_dl_port_unregister:
7848320d145SJiri Pirko 	devlink_port_unregister(devlink_port);
7858320d145SJiri Pirko err_port_free:
7868320d145SJiri Pirko 	kfree(nsim_dev_port);
7878320d145SJiri Pirko 	return err;
7888320d145SJiri Pirko }
7898320d145SJiri Pirko 
790794b2c05SJiri Pirko static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port)
7918320d145SJiri Pirko {
7928320d145SJiri Pirko 	struct devlink_port *devlink_port = &nsim_dev_port->devlink_port;
7938320d145SJiri Pirko 
7948320d145SJiri Pirko 	list_del(&nsim_dev_port->list);
795e05b2d14SJiri Pirko 	devlink_port_type_clear(devlink_port);
796e05b2d14SJiri Pirko 	nsim_destroy(nsim_dev_port->ns);
7978320d145SJiri Pirko 	nsim_dev_port_debugfs_exit(nsim_dev_port);
7988320d145SJiri Pirko 	devlink_port_unregister(devlink_port);
7998320d145SJiri Pirko 	kfree(nsim_dev_port);
8008320d145SJiri Pirko }
8018320d145SJiri Pirko 
8028320d145SJiri Pirko static void nsim_dev_port_del_all(struct nsim_dev *nsim_dev)
8038320d145SJiri Pirko {
8048320d145SJiri Pirko 	struct nsim_dev_port *nsim_dev_port, *tmp;
8058320d145SJiri Pirko 
8068320d145SJiri Pirko 	list_for_each_entry_safe(nsim_dev_port, tmp,
8078320d145SJiri Pirko 				 &nsim_dev->port_list, list)
808794b2c05SJiri Pirko 		__nsim_dev_port_del(nsim_dev_port);
8098320d145SJiri Pirko }
8108320d145SJiri Pirko 
8118320d145SJiri Pirko int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev)
8128320d145SJiri Pirko {
8138320d145SJiri Pirko 	struct nsim_dev *nsim_dev;
8148320d145SJiri Pirko 	int i;
8158320d145SJiri Pirko 	int err;
8168320d145SJiri Pirko 
8178320d145SJiri Pirko 	nsim_dev = nsim_dev_create(nsim_bus_dev, nsim_bus_dev->port_count);
8188320d145SJiri Pirko 	if (IS_ERR(nsim_dev))
8198320d145SJiri Pirko 		return PTR_ERR(nsim_dev);
8208320d145SJiri Pirko 	dev_set_drvdata(&nsim_bus_dev->dev, nsim_dev);
8218320d145SJiri Pirko 
8228320d145SJiri Pirko 	for (i = 0; i < nsim_bus_dev->port_count; i++) {
823794b2c05SJiri Pirko 		err = __nsim_dev_port_add(nsim_dev, i);
8248320d145SJiri Pirko 		if (err)
8258320d145SJiri Pirko 			goto err_port_del_all;
8268320d145SJiri Pirko 	}
8278320d145SJiri Pirko 	return 0;
8288320d145SJiri Pirko 
8298320d145SJiri Pirko err_port_del_all:
8308320d145SJiri Pirko 	nsim_dev_port_del_all(nsim_dev);
8318320d145SJiri Pirko 	nsim_dev_destroy(nsim_dev);
8328320d145SJiri Pirko 	return err;
8338320d145SJiri Pirko }
8348320d145SJiri Pirko 
8358320d145SJiri Pirko void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev)
8368320d145SJiri Pirko {
8378320d145SJiri Pirko 	struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
8388320d145SJiri Pirko 
8398320d145SJiri Pirko 	nsim_dev_port_del_all(nsim_dev);
8408320d145SJiri Pirko 	nsim_dev_destroy(nsim_dev);
8418320d145SJiri Pirko }
8428320d145SJiri Pirko 
843794b2c05SJiri Pirko static struct nsim_dev_port *
844794b2c05SJiri Pirko __nsim_dev_port_lookup(struct nsim_dev *nsim_dev, unsigned int port_index)
845794b2c05SJiri Pirko {
846794b2c05SJiri Pirko 	struct nsim_dev_port *nsim_dev_port;
847794b2c05SJiri Pirko 
848794b2c05SJiri Pirko 	list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list)
849794b2c05SJiri Pirko 		if (nsim_dev_port->port_index == port_index)
850794b2c05SJiri Pirko 			return nsim_dev_port;
851794b2c05SJiri Pirko 	return NULL;
852794b2c05SJiri Pirko }
853794b2c05SJiri Pirko 
854794b2c05SJiri Pirko int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev,
855794b2c05SJiri Pirko 		      unsigned int port_index)
856794b2c05SJiri Pirko {
857794b2c05SJiri Pirko 	struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
858794b2c05SJiri Pirko 	int err;
859794b2c05SJiri Pirko 
860794b2c05SJiri Pirko 	mutex_lock(&nsim_dev->port_list_lock);
861794b2c05SJiri Pirko 	if (__nsim_dev_port_lookup(nsim_dev, port_index))
862794b2c05SJiri Pirko 		err = -EEXIST;
863794b2c05SJiri Pirko 	else
864794b2c05SJiri Pirko 		err = __nsim_dev_port_add(nsim_dev, port_index);
865794b2c05SJiri Pirko 	mutex_unlock(&nsim_dev->port_list_lock);
866794b2c05SJiri Pirko 	return err;
867794b2c05SJiri Pirko }
868794b2c05SJiri Pirko 
869794b2c05SJiri Pirko int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev,
870794b2c05SJiri Pirko 		      unsigned int port_index)
871794b2c05SJiri Pirko {
872794b2c05SJiri Pirko 	struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
873794b2c05SJiri Pirko 	struct nsim_dev_port *nsim_dev_port;
874794b2c05SJiri Pirko 	int err = 0;
875794b2c05SJiri Pirko 
876794b2c05SJiri Pirko 	mutex_lock(&nsim_dev->port_list_lock);
877794b2c05SJiri Pirko 	nsim_dev_port = __nsim_dev_port_lookup(nsim_dev, port_index);
878794b2c05SJiri Pirko 	if (!nsim_dev_port)
879794b2c05SJiri Pirko 		err = -ENOENT;
880794b2c05SJiri Pirko 	else
881794b2c05SJiri Pirko 		__nsim_dev_port_del(nsim_dev_port);
882794b2c05SJiri Pirko 	mutex_unlock(&nsim_dev->port_list_lock);
883794b2c05SJiri Pirko 	return err;
884794b2c05SJiri Pirko }
885794b2c05SJiri Pirko 
886d514f41eSJiri Pirko int nsim_dev_init(void)
887d514f41eSJiri Pirko {
888ab1d0cc0SJiri Pirko 	nsim_dev_ddir = debugfs_create_dir(DRV_NAME, NULL);
889d514f41eSJiri Pirko 	if (IS_ERR_OR_NULL(nsim_dev_ddir))
890d514f41eSJiri Pirko 		return -ENOMEM;
891d514f41eSJiri Pirko 	return 0;
892d514f41eSJiri Pirko }
893d514f41eSJiri Pirko 
894d514f41eSJiri Pirko void nsim_dev_exit(void)
895d514f41eSJiri Pirko {
896d514f41eSJiri Pirko 	debugfs_remove_recursive(nsim_dev_ddir);
897d514f41eSJiri Pirko }
898