xref: /linux-6.15/drivers/net/netdevsim/dev.c (revision bfcccfe7)
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);
93155ddfc5SJiri Pirko 	debugfs_create_bool("dont_allow_reload", 0600, nsim_dev->ddir,
94155ddfc5SJiri Pirko 			    &nsim_dev->dont_allow_reload);
95155ddfc5SJiri Pirko 	debugfs_create_bool("fail_reload", 0600, nsim_dev->ddir,
96155ddfc5SJiri Pirko 			    &nsim_dev->fail_reload);
97d514f41eSJiri Pirko 	return 0;
98d514f41eSJiri Pirko }
99d514f41eSJiri Pirko 
100d514f41eSJiri Pirko static void nsim_dev_debugfs_exit(struct nsim_dev *nsim_dev)
101d514f41eSJiri Pirko {
102ab1d0cc0SJiri Pirko 	debugfs_remove_recursive(nsim_dev->ports_ddir);
103d514f41eSJiri Pirko 	debugfs_remove_recursive(nsim_dev->ddir);
104d514f41eSJiri Pirko }
105d514f41eSJiri Pirko 
1068320d145SJiri Pirko static int nsim_dev_port_debugfs_init(struct nsim_dev *nsim_dev,
1078320d145SJiri Pirko 				      struct nsim_dev_port *nsim_dev_port)
1088320d145SJiri Pirko {
1098320d145SJiri Pirko 	char port_ddir_name[16];
1108320d145SJiri Pirko 	char dev_link_name[32];
1118320d145SJiri Pirko 
1128320d145SJiri Pirko 	sprintf(port_ddir_name, "%u", nsim_dev_port->port_index);
1138320d145SJiri Pirko 	nsim_dev_port->ddir = debugfs_create_dir(port_ddir_name,
1148320d145SJiri Pirko 						 nsim_dev->ports_ddir);
1158320d145SJiri Pirko 	if (IS_ERR_OR_NULL(nsim_dev_port->ddir))
1168320d145SJiri Pirko 		return -ENOMEM;
1178320d145SJiri Pirko 
1188320d145SJiri Pirko 	sprintf(dev_link_name, "../../../" DRV_NAME "%u",
1198320d145SJiri Pirko 		nsim_dev->nsim_bus_dev->dev.id);
1208320d145SJiri Pirko 	debugfs_create_symlink("dev", nsim_dev_port->ddir, dev_link_name);
1218320d145SJiri Pirko 
1228320d145SJiri Pirko 	return 0;
1238320d145SJiri Pirko }
1248320d145SJiri Pirko 
1258320d145SJiri Pirko static void nsim_dev_port_debugfs_exit(struct nsim_dev_port *nsim_dev_port)
1268320d145SJiri Pirko {
1278320d145SJiri Pirko 	debugfs_remove_recursive(nsim_dev_port->ddir);
1288320d145SJiri Pirko }
1298320d145SJiri Pirko 
1308fb4bc6fSJiri Pirko static int nsim_dev_resources_register(struct devlink *devlink)
1318fb4bc6fSJiri Pirko {
1328fb4bc6fSJiri Pirko 	struct devlink_resource_size_params params = {
1338fb4bc6fSJiri Pirko 		.size_max = (u64)-1,
1348fb4bc6fSJiri Pirko 		.size_granularity = 1,
1358fb4bc6fSJiri Pirko 		.unit = DEVLINK_RESOURCE_UNIT_ENTRY
1368fb4bc6fSJiri Pirko 	};
1378fb4bc6fSJiri Pirko 	int err;
1388fb4bc6fSJiri Pirko 
1398fb4bc6fSJiri Pirko 	/* Resources for IPv4 */
1408fb4bc6fSJiri Pirko 	err = devlink_resource_register(devlink, "IPv4", (u64)-1,
1418fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV4,
1428fb4bc6fSJiri Pirko 					DEVLINK_RESOURCE_ID_PARENT_TOP,
1438fb4bc6fSJiri Pirko 					&params);
1448fb4bc6fSJiri Pirko 	if (err) {
1458fb4bc6fSJiri Pirko 		pr_err("Failed to register IPv4 top resource\n");
1468fb4bc6fSJiri Pirko 		goto out;
1478fb4bc6fSJiri Pirko 	}
1488fb4bc6fSJiri Pirko 
149a5facc4cSJiri Pirko 	err = devlink_resource_register(devlink, "fib", (u64)-1,
1508fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV4_FIB,
1518fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV4, &params);
1528fb4bc6fSJiri Pirko 	if (err) {
1538fb4bc6fSJiri Pirko 		pr_err("Failed to register IPv4 FIB resource\n");
1548fb4bc6fSJiri Pirko 		return err;
1558fb4bc6fSJiri Pirko 	}
1568fb4bc6fSJiri Pirko 
157a5facc4cSJiri Pirko 	err = devlink_resource_register(devlink, "fib-rules", (u64)-1,
1588fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV4_FIB_RULES,
1598fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV4, &params);
1608fb4bc6fSJiri Pirko 	if (err) {
1618fb4bc6fSJiri Pirko 		pr_err("Failed to register IPv4 FIB rules resource\n");
1628fb4bc6fSJiri Pirko 		return err;
1638fb4bc6fSJiri Pirko 	}
1648fb4bc6fSJiri Pirko 
1658fb4bc6fSJiri Pirko 	/* Resources for IPv6 */
1668fb4bc6fSJiri Pirko 	err = devlink_resource_register(devlink, "IPv6", (u64)-1,
1678fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV6,
1688fb4bc6fSJiri Pirko 					DEVLINK_RESOURCE_ID_PARENT_TOP,
1698fb4bc6fSJiri Pirko 					&params);
1708fb4bc6fSJiri Pirko 	if (err) {
1718fb4bc6fSJiri Pirko 		pr_err("Failed to register IPv6 top resource\n");
1728fb4bc6fSJiri Pirko 		goto out;
1738fb4bc6fSJiri Pirko 	}
1748fb4bc6fSJiri Pirko 
175a5facc4cSJiri Pirko 	err = devlink_resource_register(devlink, "fib", (u64)-1,
1768fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV6_FIB,
1778fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV6, &params);
1788fb4bc6fSJiri Pirko 	if (err) {
1798fb4bc6fSJiri Pirko 		pr_err("Failed to register IPv6 FIB resource\n");
1808fb4bc6fSJiri Pirko 		return err;
1818fb4bc6fSJiri Pirko 	}
1828fb4bc6fSJiri Pirko 
183a5facc4cSJiri Pirko 	err = devlink_resource_register(devlink, "fib-rules", (u64)-1,
1848fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV6_FIB_RULES,
1858fb4bc6fSJiri Pirko 					NSIM_RESOURCE_IPV6, &params);
1868fb4bc6fSJiri Pirko 	if (err) {
1878fb4bc6fSJiri Pirko 		pr_err("Failed to register IPv6 FIB rules resource\n");
1888fb4bc6fSJiri Pirko 		return err;
1898fb4bc6fSJiri Pirko 	}
1908fb4bc6fSJiri Pirko 
1918fb4bc6fSJiri Pirko out:
1928fb4bc6fSJiri Pirko 	return err;
1938fb4bc6fSJiri Pirko }
1948fb4bc6fSJiri Pirko 
195150e8f8aSJiri Pirko enum nsim_devlink_param_id {
196150e8f8aSJiri Pirko 	NSIM_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
197150e8f8aSJiri Pirko 	NSIM_DEVLINK_PARAM_ID_TEST1,
198150e8f8aSJiri Pirko };
199150e8f8aSJiri Pirko 
200150e8f8aSJiri Pirko static const struct devlink_param nsim_devlink_params[] = {
201150e8f8aSJiri Pirko 	DEVLINK_PARAM_GENERIC(MAX_MACS,
202150e8f8aSJiri Pirko 			      BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
203150e8f8aSJiri Pirko 			      NULL, NULL, NULL),
204150e8f8aSJiri Pirko 	DEVLINK_PARAM_DRIVER(NSIM_DEVLINK_PARAM_ID_TEST1,
205150e8f8aSJiri Pirko 			     "test1", DEVLINK_PARAM_TYPE_BOOL,
206150e8f8aSJiri Pirko 			     BIT(DEVLINK_PARAM_CMODE_DRIVERINIT),
207150e8f8aSJiri Pirko 			     NULL, NULL, NULL),
208150e8f8aSJiri Pirko };
209150e8f8aSJiri Pirko 
210150e8f8aSJiri Pirko static void nsim_devlink_set_params_init_values(struct nsim_dev *nsim_dev,
211150e8f8aSJiri Pirko 						struct devlink *devlink)
212150e8f8aSJiri Pirko {
213150e8f8aSJiri Pirko 	union devlink_param_value value;
214150e8f8aSJiri Pirko 
215150e8f8aSJiri Pirko 	value.vu32 = nsim_dev->max_macs;
216150e8f8aSJiri Pirko 	devlink_param_driverinit_value_set(devlink,
217150e8f8aSJiri Pirko 					   DEVLINK_PARAM_GENERIC_ID_MAX_MACS,
218150e8f8aSJiri Pirko 					   value);
219150e8f8aSJiri Pirko 	value.vbool = nsim_dev->test1;
220150e8f8aSJiri Pirko 	devlink_param_driverinit_value_set(devlink,
221150e8f8aSJiri Pirko 					   NSIM_DEVLINK_PARAM_ID_TEST1,
222150e8f8aSJiri Pirko 					   value);
223150e8f8aSJiri Pirko }
224150e8f8aSJiri Pirko 
225150e8f8aSJiri Pirko static void nsim_devlink_param_load_driverinit_values(struct devlink *devlink)
226150e8f8aSJiri Pirko {
227150e8f8aSJiri Pirko 	struct nsim_dev *nsim_dev = devlink_priv(devlink);
228150e8f8aSJiri Pirko 	union devlink_param_value saved_value;
229150e8f8aSJiri Pirko 	int err;
230150e8f8aSJiri Pirko 
231150e8f8aSJiri Pirko 	err = devlink_param_driverinit_value_get(devlink,
232150e8f8aSJiri Pirko 						 DEVLINK_PARAM_GENERIC_ID_MAX_MACS,
233150e8f8aSJiri Pirko 						 &saved_value);
234150e8f8aSJiri Pirko 	if (!err)
235150e8f8aSJiri Pirko 		nsim_dev->max_macs = saved_value.vu32;
236150e8f8aSJiri Pirko 	err = devlink_param_driverinit_value_get(devlink,
237150e8f8aSJiri Pirko 						 NSIM_DEVLINK_PARAM_ID_TEST1,
238150e8f8aSJiri Pirko 						 &saved_value);
239150e8f8aSJiri Pirko 	if (!err)
240150e8f8aSJiri Pirko 		nsim_dev->test1 = saved_value.vbool;
241150e8f8aSJiri Pirko }
242150e8f8aSJiri Pirko 
2434418f862SJiri Pirko #define NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX 16
2444418f862SJiri Pirko 
2454418f862SJiri Pirko static int nsim_dev_dummy_region_init(struct nsim_dev *nsim_dev,
2464418f862SJiri Pirko 				      struct devlink *devlink)
2474418f862SJiri Pirko {
2484418f862SJiri Pirko 	nsim_dev->dummy_region =
2494418f862SJiri Pirko 		devlink_region_create(devlink, "dummy",
2504418f862SJiri Pirko 				      NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX,
2514418f862SJiri Pirko 				      NSIM_DEV_DUMMY_REGION_SIZE);
2524418f862SJiri Pirko 	return PTR_ERR_OR_ZERO(nsim_dev->dummy_region);
2534418f862SJiri Pirko }
2544418f862SJiri Pirko 
2554418f862SJiri Pirko static void nsim_dev_dummy_region_exit(struct nsim_dev *nsim_dev)
2564418f862SJiri Pirko {
2574418f862SJiri Pirko 	devlink_region_destroy(nsim_dev->dummy_region);
2584418f862SJiri Pirko }
2594418f862SJiri Pirko 
260da58f90fSIdo Schimmel struct nsim_trap_item {
261da58f90fSIdo Schimmel 	void *trap_ctx;
262da58f90fSIdo Schimmel 	enum devlink_trap_action action;
263da58f90fSIdo Schimmel };
264da58f90fSIdo Schimmel 
265da58f90fSIdo Schimmel struct nsim_trap_data {
266da58f90fSIdo Schimmel 	struct delayed_work trap_report_dw;
267da58f90fSIdo Schimmel 	struct nsim_trap_item *trap_items_arr;
268da58f90fSIdo Schimmel 	struct nsim_dev *nsim_dev;
269da58f90fSIdo Schimmel 	spinlock_t trap_lock;	/* Protects trap_items_arr */
270da58f90fSIdo Schimmel };
271da58f90fSIdo Schimmel 
2729e087457SIdo Schimmel /* All driver-specific traps must be documented in
2739e087457SIdo Schimmel  * Documentation/networking/devlink-trap-netdevsim.rst
2749e087457SIdo Schimmel  */
275da58f90fSIdo Schimmel enum {
276da58f90fSIdo Schimmel 	NSIM_TRAP_ID_BASE = DEVLINK_TRAP_GENERIC_ID_MAX,
277da58f90fSIdo Schimmel 	NSIM_TRAP_ID_FID_MISS,
278da58f90fSIdo Schimmel };
279da58f90fSIdo Schimmel 
280da58f90fSIdo Schimmel #define NSIM_TRAP_NAME_FID_MISS "fid_miss"
281da58f90fSIdo Schimmel 
282da58f90fSIdo Schimmel #define NSIM_TRAP_METADATA DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT
283da58f90fSIdo Schimmel 
284da58f90fSIdo Schimmel #define NSIM_TRAP_DROP(_id, _group_id)					      \
285da58f90fSIdo Schimmel 	DEVLINK_TRAP_GENERIC(DROP, DROP, _id,				      \
286da58f90fSIdo Schimmel 			     DEVLINK_TRAP_GROUP_GENERIC(_group_id),	      \
287da58f90fSIdo Schimmel 			     NSIM_TRAP_METADATA)
288da58f90fSIdo Schimmel #define NSIM_TRAP_EXCEPTION(_id, _group_id)				      \
289da58f90fSIdo Schimmel 	DEVLINK_TRAP_GENERIC(EXCEPTION, TRAP, _id,			      \
290da58f90fSIdo Schimmel 			     DEVLINK_TRAP_GROUP_GENERIC(_group_id),	      \
291da58f90fSIdo Schimmel 			     NSIM_TRAP_METADATA)
292da58f90fSIdo Schimmel #define NSIM_TRAP_DRIVER_EXCEPTION(_id, _group_id)			      \
293da58f90fSIdo Schimmel 	DEVLINK_TRAP_DRIVER(EXCEPTION, TRAP, NSIM_TRAP_ID_##_id,	      \
294da58f90fSIdo Schimmel 			    NSIM_TRAP_NAME_##_id,			      \
295da58f90fSIdo Schimmel 			    DEVLINK_TRAP_GROUP_GENERIC(_group_id),	      \
296da58f90fSIdo Schimmel 			    NSIM_TRAP_METADATA)
297da58f90fSIdo Schimmel 
298da58f90fSIdo Schimmel static const struct devlink_trap nsim_traps_arr[] = {
299da58f90fSIdo Schimmel 	NSIM_TRAP_DROP(SMAC_MC, L2_DROPS),
300da58f90fSIdo Schimmel 	NSIM_TRAP_DROP(VLAN_TAG_MISMATCH, L2_DROPS),
301da58f90fSIdo Schimmel 	NSIM_TRAP_DROP(INGRESS_VLAN_FILTER, L2_DROPS),
302da58f90fSIdo Schimmel 	NSIM_TRAP_DROP(INGRESS_STP_FILTER, L2_DROPS),
303da58f90fSIdo Schimmel 	NSIM_TRAP_DROP(EMPTY_TX_LIST, L2_DROPS),
304da58f90fSIdo Schimmel 	NSIM_TRAP_DROP(PORT_LOOPBACK_FILTER, L2_DROPS),
305da58f90fSIdo Schimmel 	NSIM_TRAP_DRIVER_EXCEPTION(FID_MISS, L2_DROPS),
306da58f90fSIdo Schimmel 	NSIM_TRAP_DROP(BLACKHOLE_ROUTE, L3_DROPS),
307da58f90fSIdo Schimmel 	NSIM_TRAP_EXCEPTION(TTL_ERROR, L3_DROPS),
308da58f90fSIdo Schimmel 	NSIM_TRAP_DROP(TAIL_DROP, BUFFER_DROPS),
309da58f90fSIdo Schimmel };
310da58f90fSIdo Schimmel 
311da58f90fSIdo Schimmel #define NSIM_TRAP_L4_DATA_LEN 100
312da58f90fSIdo Schimmel 
313da58f90fSIdo Schimmel static struct sk_buff *nsim_dev_trap_skb_build(void)
314da58f90fSIdo Schimmel {
315da58f90fSIdo Schimmel 	int tot_len, data_len = NSIM_TRAP_L4_DATA_LEN;
316da58f90fSIdo Schimmel 	struct sk_buff *skb;
317da58f90fSIdo Schimmel 	struct udphdr *udph;
318da58f90fSIdo Schimmel 	struct ethhdr *eth;
319da58f90fSIdo Schimmel 	struct iphdr *iph;
320da58f90fSIdo Schimmel 
321da58f90fSIdo Schimmel 	skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC);
322da58f90fSIdo Schimmel 	if (!skb)
323da58f90fSIdo Schimmel 		return NULL;
324da58f90fSIdo Schimmel 	tot_len = sizeof(struct iphdr) + sizeof(struct udphdr) + data_len;
325da58f90fSIdo Schimmel 
32658a406deSIdo Schimmel 	skb_reset_mac_header(skb);
327da58f90fSIdo Schimmel 	eth = skb_put(skb, sizeof(struct ethhdr));
328da58f90fSIdo Schimmel 	eth_random_addr(eth->h_dest);
329da58f90fSIdo Schimmel 	eth_random_addr(eth->h_source);
330da58f90fSIdo Schimmel 	eth->h_proto = htons(ETH_P_IP);
331da58f90fSIdo Schimmel 	skb->protocol = htons(ETH_P_IP);
332da58f90fSIdo Schimmel 
33358a406deSIdo Schimmel 	skb_set_network_header(skb, skb->len);
334da58f90fSIdo Schimmel 	iph = skb_put(skb, sizeof(struct iphdr));
335da58f90fSIdo Schimmel 	iph->protocol = IPPROTO_UDP;
336da58f90fSIdo Schimmel 	iph->saddr = in_aton("192.0.2.1");
337da58f90fSIdo Schimmel 	iph->daddr = in_aton("198.51.100.1");
338da58f90fSIdo Schimmel 	iph->version = 0x4;
339da58f90fSIdo Schimmel 	iph->frag_off = 0;
340da58f90fSIdo Schimmel 	iph->ihl = 0x5;
341da58f90fSIdo Schimmel 	iph->tot_len = htons(tot_len);
342da58f90fSIdo Schimmel 	iph->ttl = 100;
343d9bd6d27SYueHaibing 	iph->check = 0;
344d9bd6d27SYueHaibing 	iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
345da58f90fSIdo Schimmel 
34658a406deSIdo Schimmel 	skb_set_transport_header(skb, skb->len);
347da58f90fSIdo Schimmel 	udph = skb_put_zero(skb, sizeof(struct udphdr) + data_len);
348da58f90fSIdo Schimmel 	get_random_bytes(&udph->source, sizeof(u16));
349da58f90fSIdo Schimmel 	get_random_bytes(&udph->dest, sizeof(u16));
350da58f90fSIdo Schimmel 	udph->len = htons(sizeof(struct udphdr) + data_len);
351da58f90fSIdo Schimmel 
352da58f90fSIdo Schimmel 	return skb;
353da58f90fSIdo Schimmel }
354da58f90fSIdo Schimmel 
355da58f90fSIdo Schimmel static void nsim_dev_trap_report(struct nsim_dev_port *nsim_dev_port)
356da58f90fSIdo Schimmel {
357da58f90fSIdo Schimmel 	struct nsim_dev *nsim_dev = nsim_dev_port->ns->nsim_dev;
358da58f90fSIdo Schimmel 	struct devlink *devlink = priv_to_devlink(nsim_dev);
359da58f90fSIdo Schimmel 	struct nsim_trap_data *nsim_trap_data;
360da58f90fSIdo Schimmel 	int i;
361da58f90fSIdo Schimmel 
362da58f90fSIdo Schimmel 	nsim_trap_data = nsim_dev->trap_data;
363da58f90fSIdo Schimmel 
364da58f90fSIdo Schimmel 	spin_lock(&nsim_trap_data->trap_lock);
365da58f90fSIdo Schimmel 	for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) {
366da58f90fSIdo Schimmel 		struct nsim_trap_item *nsim_trap_item;
367da58f90fSIdo Schimmel 		struct sk_buff *skb;
368da58f90fSIdo Schimmel 
369da58f90fSIdo Schimmel 		nsim_trap_item = &nsim_trap_data->trap_items_arr[i];
370da58f90fSIdo Schimmel 		if (nsim_trap_item->action == DEVLINK_TRAP_ACTION_DROP)
371da58f90fSIdo Schimmel 			continue;
372da58f90fSIdo Schimmel 
373da58f90fSIdo Schimmel 		skb = nsim_dev_trap_skb_build();
374da58f90fSIdo Schimmel 		if (!skb)
375da58f90fSIdo Schimmel 			continue;
376da58f90fSIdo Schimmel 		skb->dev = nsim_dev_port->ns->netdev;
377da58f90fSIdo Schimmel 
378da58f90fSIdo Schimmel 		/* Trapped packets are usually passed to devlink in softIRQ,
379da58f90fSIdo Schimmel 		 * but in this case they are generated in a workqueue. Disable
380da58f90fSIdo Schimmel 		 * softIRQs to prevent lockdep from complaining about
381da58f90fSIdo Schimmel 		 * "incosistent lock state".
382da58f90fSIdo Schimmel 		 */
383da58f90fSIdo Schimmel 		local_bh_disable();
384da58f90fSIdo Schimmel 		devlink_trap_report(devlink, skb, nsim_trap_item->trap_ctx,
385da58f90fSIdo Schimmel 				    &nsim_dev_port->devlink_port);
386da58f90fSIdo Schimmel 		local_bh_enable();
387da58f90fSIdo Schimmel 		consume_skb(skb);
388da58f90fSIdo Schimmel 	}
389da58f90fSIdo Schimmel 	spin_unlock(&nsim_trap_data->trap_lock);
390da58f90fSIdo Schimmel }
391da58f90fSIdo Schimmel 
392da58f90fSIdo Schimmel #define NSIM_TRAP_REPORT_INTERVAL_MS	100
393da58f90fSIdo Schimmel 
394da58f90fSIdo Schimmel static void nsim_dev_trap_report_work(struct work_struct *work)
395da58f90fSIdo Schimmel {
396da58f90fSIdo Schimmel 	struct nsim_trap_data *nsim_trap_data;
397da58f90fSIdo Schimmel 	struct nsim_dev_port *nsim_dev_port;
398da58f90fSIdo Schimmel 	struct nsim_dev *nsim_dev;
399da58f90fSIdo Schimmel 
400da58f90fSIdo Schimmel 	nsim_trap_data = container_of(work, struct nsim_trap_data,
401da58f90fSIdo Schimmel 				      trap_report_dw.work);
402da58f90fSIdo Schimmel 	nsim_dev = nsim_trap_data->nsim_dev;
403da58f90fSIdo Schimmel 
404da58f90fSIdo Schimmel 	/* For each running port and enabled packet trap, generate a UDP
405da58f90fSIdo Schimmel 	 * packet with a random 5-tuple and report it.
406da58f90fSIdo Schimmel 	 */
407da58f90fSIdo Schimmel 	mutex_lock(&nsim_dev->port_list_lock);
408da58f90fSIdo Schimmel 	list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) {
409da58f90fSIdo Schimmel 		if (!netif_running(nsim_dev_port->ns->netdev))
410da58f90fSIdo Schimmel 			continue;
411da58f90fSIdo Schimmel 
412da58f90fSIdo Schimmel 		nsim_dev_trap_report(nsim_dev_port);
413da58f90fSIdo Schimmel 	}
414da58f90fSIdo Schimmel 	mutex_unlock(&nsim_dev->port_list_lock);
415da58f90fSIdo Schimmel 
416da58f90fSIdo Schimmel 	schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw,
417da58f90fSIdo Schimmel 			      msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS));
418da58f90fSIdo Schimmel }
419da58f90fSIdo Schimmel 
420da58f90fSIdo Schimmel static int nsim_dev_traps_init(struct devlink *devlink)
421da58f90fSIdo Schimmel {
422da58f90fSIdo Schimmel 	struct nsim_dev *nsim_dev = devlink_priv(devlink);
423da58f90fSIdo Schimmel 	struct nsim_trap_data *nsim_trap_data;
424da58f90fSIdo Schimmel 	int err;
425da58f90fSIdo Schimmel 
426da58f90fSIdo Schimmel 	nsim_trap_data = kzalloc(sizeof(*nsim_trap_data), GFP_KERNEL);
427da58f90fSIdo Schimmel 	if (!nsim_trap_data)
428da58f90fSIdo Schimmel 		return -ENOMEM;
429da58f90fSIdo Schimmel 
430da58f90fSIdo Schimmel 	nsim_trap_data->trap_items_arr = kcalloc(ARRAY_SIZE(nsim_traps_arr),
431da58f90fSIdo Schimmel 						 sizeof(struct nsim_trap_item),
432da58f90fSIdo Schimmel 						 GFP_KERNEL);
433da58f90fSIdo Schimmel 	if (!nsim_trap_data->trap_items_arr) {
434da58f90fSIdo Schimmel 		err = -ENOMEM;
435da58f90fSIdo Schimmel 		goto err_trap_data_free;
436da58f90fSIdo Schimmel 	}
437da58f90fSIdo Schimmel 
438da58f90fSIdo Schimmel 	/* The lock is used to protect the action state of the registered
439da58f90fSIdo Schimmel 	 * traps. The value is written by user and read in delayed work when
440da58f90fSIdo Schimmel 	 * iterating over all the traps.
441da58f90fSIdo Schimmel 	 */
442da58f90fSIdo Schimmel 	spin_lock_init(&nsim_trap_data->trap_lock);
443da58f90fSIdo Schimmel 	nsim_trap_data->nsim_dev = nsim_dev;
444da58f90fSIdo Schimmel 	nsim_dev->trap_data = nsim_trap_data;
445da58f90fSIdo Schimmel 
446da58f90fSIdo Schimmel 	err = devlink_traps_register(devlink, nsim_traps_arr,
447da58f90fSIdo Schimmel 				     ARRAY_SIZE(nsim_traps_arr), NULL);
448da58f90fSIdo Schimmel 	if (err)
449da58f90fSIdo Schimmel 		goto err_trap_items_free;
450da58f90fSIdo Schimmel 
451da58f90fSIdo Schimmel 	INIT_DELAYED_WORK(&nsim_dev->trap_data->trap_report_dw,
452da58f90fSIdo Schimmel 			  nsim_dev_trap_report_work);
453da58f90fSIdo Schimmel 	schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw,
454da58f90fSIdo Schimmel 			      msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS));
455da58f90fSIdo Schimmel 
456da58f90fSIdo Schimmel 	return 0;
457da58f90fSIdo Schimmel 
458da58f90fSIdo Schimmel err_trap_items_free:
459da58f90fSIdo Schimmel 	kfree(nsim_trap_data->trap_items_arr);
460da58f90fSIdo Schimmel err_trap_data_free:
461da58f90fSIdo Schimmel 	kfree(nsim_trap_data);
462da58f90fSIdo Schimmel 	return err;
463da58f90fSIdo Schimmel }
464da58f90fSIdo Schimmel 
465da58f90fSIdo Schimmel static void nsim_dev_traps_exit(struct devlink *devlink)
466da58f90fSIdo Schimmel {
467da58f90fSIdo Schimmel 	struct nsim_dev *nsim_dev = devlink_priv(devlink);
468da58f90fSIdo Schimmel 
469da58f90fSIdo Schimmel 	cancel_delayed_work_sync(&nsim_dev->trap_data->trap_report_dw);
470da58f90fSIdo Schimmel 	devlink_traps_unregister(devlink, nsim_traps_arr,
471da58f90fSIdo Schimmel 				 ARRAY_SIZE(nsim_traps_arr));
472da58f90fSIdo Schimmel 	kfree(nsim_dev->trap_data->trap_items_arr);
473da58f90fSIdo Schimmel 	kfree(nsim_dev->trap_data);
474da58f90fSIdo Schimmel }
475da58f90fSIdo Schimmel 
47675ba029fSJiri Pirko static int nsim_dev_reload_create(struct nsim_dev *nsim_dev,
47775ba029fSJiri Pirko 				  struct netlink_ext_ack *extack);
47875ba029fSJiri Pirko static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev);
47975ba029fSJiri Pirko 
480070c63f2SJiri Pirko static int nsim_dev_reload_down(struct devlink *devlink, bool netns_change,
48197691069SJiri Pirko 				struct netlink_ext_ack *extack)
48297691069SJiri Pirko {
48375ba029fSJiri Pirko 	struct nsim_dev *nsim_dev = devlink_priv(devlink);
48475ba029fSJiri Pirko 
485155ddfc5SJiri Pirko 	if (nsim_dev->dont_allow_reload) {
486155ddfc5SJiri Pirko 		/* For testing purposes, user set debugfs dont_allow_reload
487155ddfc5SJiri Pirko 		 * value to true. So forbid it.
488155ddfc5SJiri Pirko 		 */
489f9867b51SColin Ian King 		NL_SET_ERR_MSG_MOD(extack, "User forbid the reload for testing purposes");
490155ddfc5SJiri Pirko 		return -EOPNOTSUPP;
491155ddfc5SJiri Pirko 	}
492155ddfc5SJiri Pirko 
49375ba029fSJiri Pirko 	nsim_dev_reload_destroy(nsim_dev);
49497691069SJiri Pirko 	return 0;
49597691069SJiri Pirko }
49697691069SJiri Pirko 
49797691069SJiri Pirko static int nsim_dev_reload_up(struct devlink *devlink,
4988fb4bc6fSJiri Pirko 			      struct netlink_ext_ack *extack)
4998fb4bc6fSJiri Pirko {
500a5facc4cSJiri Pirko 	struct nsim_dev *nsim_dev = devlink_priv(devlink);
5018fb4bc6fSJiri Pirko 
502155ddfc5SJiri Pirko 	if (nsim_dev->fail_reload) {
503155ddfc5SJiri Pirko 		/* For testing purposes, user set debugfs fail_reload
504155ddfc5SJiri Pirko 		 * value to true. Fail right away.
505155ddfc5SJiri Pirko 		 */
506155ddfc5SJiri Pirko 		NL_SET_ERR_MSG_MOD(extack, "User setup the reload to fail for testing purposes");
507155ddfc5SJiri Pirko 		return -EINVAL;
508155ddfc5SJiri Pirko 	}
509155ddfc5SJiri Pirko 
51075ba029fSJiri Pirko 	return nsim_dev_reload_create(nsim_dev, extack);
5118fb4bc6fSJiri Pirko }
5128fb4bc6fSJiri Pirko 
5138e23cc03SJiri Pirko static int nsim_dev_info_get(struct devlink *devlink,
5148e23cc03SJiri Pirko 			     struct devlink_info_req *req,
5158e23cc03SJiri Pirko 			     struct netlink_ext_ack *extack)
5168e23cc03SJiri Pirko {
5178e23cc03SJiri Pirko 	return devlink_info_driver_name_put(req, DRV_NAME);
5188e23cc03SJiri Pirko }
5198e23cc03SJiri Pirko 
520fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_SIZE 500000
521fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_CHUNK_SIZE 1000
522fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_CHUNK_TIME_MS 10
523fa4dfc4aSJiri Pirko 
524fa4dfc4aSJiri Pirko static int nsim_dev_flash_update(struct devlink *devlink, const char *file_name,
525fa4dfc4aSJiri Pirko 				 const char *component,
526fa4dfc4aSJiri Pirko 				 struct netlink_ext_ack *extack)
527fa4dfc4aSJiri Pirko {
528fa4dfc4aSJiri Pirko 	struct nsim_dev *nsim_dev = devlink_priv(devlink);
529fa4dfc4aSJiri Pirko 	int i;
530fa4dfc4aSJiri Pirko 
531fa4dfc4aSJiri Pirko 	if (nsim_dev->fw_update_status) {
532fa4dfc4aSJiri Pirko 		devlink_flash_update_begin_notify(devlink);
533fa4dfc4aSJiri Pirko 		devlink_flash_update_status_notify(devlink,
534fa4dfc4aSJiri Pirko 						   "Preparing to flash",
535fa4dfc4aSJiri Pirko 						   component, 0, 0);
536fa4dfc4aSJiri Pirko 	}
537fa4dfc4aSJiri Pirko 
538fa4dfc4aSJiri Pirko 	for (i = 0; i < NSIM_DEV_FLASH_SIZE / NSIM_DEV_FLASH_CHUNK_SIZE; i++) {
539fa4dfc4aSJiri Pirko 		if (nsim_dev->fw_update_status)
540fa4dfc4aSJiri Pirko 			devlink_flash_update_status_notify(devlink, "Flashing",
541fa4dfc4aSJiri Pirko 							   component,
542fa4dfc4aSJiri Pirko 							   i * NSIM_DEV_FLASH_CHUNK_SIZE,
543fa4dfc4aSJiri Pirko 							   NSIM_DEV_FLASH_SIZE);
544fa4dfc4aSJiri Pirko 		msleep(NSIM_DEV_FLASH_CHUNK_TIME_MS);
545fa4dfc4aSJiri Pirko 	}
546fa4dfc4aSJiri Pirko 
547fa4dfc4aSJiri Pirko 	if (nsim_dev->fw_update_status) {
548fa4dfc4aSJiri Pirko 		devlink_flash_update_status_notify(devlink, "Flashing",
549fa4dfc4aSJiri Pirko 						   component,
550fa4dfc4aSJiri Pirko 						   NSIM_DEV_FLASH_SIZE,
551fa4dfc4aSJiri Pirko 						   NSIM_DEV_FLASH_SIZE);
552fa4dfc4aSJiri Pirko 		devlink_flash_update_status_notify(devlink, "Flashing done",
553fa4dfc4aSJiri Pirko 						   component, 0, 0);
554fa4dfc4aSJiri Pirko 		devlink_flash_update_end_notify(devlink);
555fa4dfc4aSJiri Pirko 	}
556fa4dfc4aSJiri Pirko 
557fa4dfc4aSJiri Pirko 	return 0;
558fa4dfc4aSJiri Pirko }
559fa4dfc4aSJiri Pirko 
560da58f90fSIdo Schimmel static struct nsim_trap_item *
561da58f90fSIdo Schimmel nsim_dev_trap_item_lookup(struct nsim_dev *nsim_dev, u16 trap_id)
562da58f90fSIdo Schimmel {
563da58f90fSIdo Schimmel 	struct nsim_trap_data *nsim_trap_data = nsim_dev->trap_data;
564da58f90fSIdo Schimmel 	int i;
565da58f90fSIdo Schimmel 
566da58f90fSIdo Schimmel 	for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) {
567da58f90fSIdo Schimmel 		if (nsim_traps_arr[i].id == trap_id)
568da58f90fSIdo Schimmel 			return &nsim_trap_data->trap_items_arr[i];
569da58f90fSIdo Schimmel 	}
570da58f90fSIdo Schimmel 
571da58f90fSIdo Schimmel 	return NULL;
572da58f90fSIdo Schimmel }
573da58f90fSIdo Schimmel 
574da58f90fSIdo Schimmel static int nsim_dev_devlink_trap_init(struct devlink *devlink,
575da58f90fSIdo Schimmel 				      const struct devlink_trap *trap,
576da58f90fSIdo Schimmel 				      void *trap_ctx)
577da58f90fSIdo Schimmel {
578da58f90fSIdo Schimmel 	struct nsim_dev *nsim_dev = devlink_priv(devlink);
579da58f90fSIdo Schimmel 	struct nsim_trap_item *nsim_trap_item;
580da58f90fSIdo Schimmel 
581da58f90fSIdo Schimmel 	nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id);
582da58f90fSIdo Schimmel 	if (WARN_ON(!nsim_trap_item))
583da58f90fSIdo Schimmel 		return -ENOENT;
584da58f90fSIdo Schimmel 
585da58f90fSIdo Schimmel 	nsim_trap_item->trap_ctx = trap_ctx;
586da58f90fSIdo Schimmel 	nsim_trap_item->action = trap->init_action;
587da58f90fSIdo Schimmel 
588da58f90fSIdo Schimmel 	return 0;
589da58f90fSIdo Schimmel }
590da58f90fSIdo Schimmel 
591da58f90fSIdo Schimmel static int
592da58f90fSIdo Schimmel nsim_dev_devlink_trap_action_set(struct devlink *devlink,
593da58f90fSIdo Schimmel 				 const struct devlink_trap *trap,
594da58f90fSIdo Schimmel 				 enum devlink_trap_action action)
595da58f90fSIdo Schimmel {
596da58f90fSIdo Schimmel 	struct nsim_dev *nsim_dev = devlink_priv(devlink);
597da58f90fSIdo Schimmel 	struct nsim_trap_item *nsim_trap_item;
598da58f90fSIdo Schimmel 
599da58f90fSIdo Schimmel 	nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id);
600da58f90fSIdo Schimmel 	if (WARN_ON(!nsim_trap_item))
601da58f90fSIdo Schimmel 		return -ENOENT;
602da58f90fSIdo Schimmel 
603da58f90fSIdo Schimmel 	spin_lock(&nsim_dev->trap_data->trap_lock);
604da58f90fSIdo Schimmel 	nsim_trap_item->action = action;
605da58f90fSIdo Schimmel 	spin_unlock(&nsim_dev->trap_data->trap_lock);
606da58f90fSIdo Schimmel 
607da58f90fSIdo Schimmel 	return 0;
608da58f90fSIdo Schimmel }
609da58f90fSIdo Schimmel 
6108fb4bc6fSJiri Pirko static const struct devlink_ops nsim_dev_devlink_ops = {
61197691069SJiri Pirko 	.reload_down = nsim_dev_reload_down,
61297691069SJiri Pirko 	.reload_up = nsim_dev_reload_up,
6138e23cc03SJiri Pirko 	.info_get = nsim_dev_info_get,
614fa4dfc4aSJiri Pirko 	.flash_update = nsim_dev_flash_update,
615da58f90fSIdo Schimmel 	.trap_init = nsim_dev_devlink_trap_init,
616da58f90fSIdo Schimmel 	.trap_action_set = nsim_dev_devlink_trap_action_set,
6178fb4bc6fSJiri Pirko };
6188fb4bc6fSJiri Pirko 
619150e8f8aSJiri Pirko #define NSIM_DEV_MAX_MACS_DEFAULT 32
620150e8f8aSJiri Pirko #define NSIM_DEV_TEST1_DEFAULT true
621150e8f8aSJiri Pirko 
622794b2c05SJiri Pirko static int __nsim_dev_port_add(struct nsim_dev *nsim_dev,
623794b2c05SJiri Pirko 			       unsigned int port_index)
6248320d145SJiri Pirko {
6258320d145SJiri Pirko 	struct nsim_dev_port *nsim_dev_port;
6268320d145SJiri Pirko 	struct devlink_port *devlink_port;
6278320d145SJiri Pirko 	int err;
6288320d145SJiri Pirko 
6298320d145SJiri Pirko 	nsim_dev_port = kzalloc(sizeof(*nsim_dev_port), GFP_KERNEL);
6308320d145SJiri Pirko 	if (!nsim_dev_port)
6318320d145SJiri Pirko 		return -ENOMEM;
6328320d145SJiri Pirko 	nsim_dev_port->port_index = port_index;
6338320d145SJiri Pirko 
6348320d145SJiri Pirko 	devlink_port = &nsim_dev_port->devlink_port;
6358320d145SJiri Pirko 	devlink_port_attrs_set(devlink_port, DEVLINK_PORT_FLAVOUR_PHYSICAL,
6368320d145SJiri Pirko 			       port_index + 1, 0, 0,
6378320d145SJiri Pirko 			       nsim_dev->switch_id.id,
6388320d145SJiri Pirko 			       nsim_dev->switch_id.id_len);
6398320d145SJiri Pirko 	err = devlink_port_register(priv_to_devlink(nsim_dev), devlink_port,
6408320d145SJiri Pirko 				    port_index);
6418320d145SJiri Pirko 	if (err)
6428320d145SJiri Pirko 		goto err_port_free;
6438320d145SJiri Pirko 
6448320d145SJiri Pirko 	err = nsim_dev_port_debugfs_init(nsim_dev, nsim_dev_port);
6458320d145SJiri Pirko 	if (err)
6468320d145SJiri Pirko 		goto err_dl_port_unregister;
6478320d145SJiri Pirko 
648e05b2d14SJiri Pirko 	nsim_dev_port->ns = nsim_create(nsim_dev, nsim_dev_port);
649e05b2d14SJiri Pirko 	if (IS_ERR(nsim_dev_port->ns)) {
650e05b2d14SJiri Pirko 		err = PTR_ERR(nsim_dev_port->ns);
651e05b2d14SJiri Pirko 		goto err_port_debugfs_exit;
652e05b2d14SJiri Pirko 	}
653e05b2d14SJiri Pirko 
654e05b2d14SJiri Pirko 	devlink_port_type_eth_set(devlink_port, nsim_dev_port->ns->netdev);
6558320d145SJiri Pirko 	list_add(&nsim_dev_port->list, &nsim_dev->port_list);
6568320d145SJiri Pirko 
6578320d145SJiri Pirko 	return 0;
6588320d145SJiri Pirko 
659e05b2d14SJiri Pirko err_port_debugfs_exit:
660e05b2d14SJiri Pirko 	nsim_dev_port_debugfs_exit(nsim_dev_port);
6618320d145SJiri Pirko err_dl_port_unregister:
6628320d145SJiri Pirko 	devlink_port_unregister(devlink_port);
6638320d145SJiri Pirko err_port_free:
6648320d145SJiri Pirko 	kfree(nsim_dev_port);
6658320d145SJiri Pirko 	return err;
6668320d145SJiri Pirko }
6678320d145SJiri Pirko 
668794b2c05SJiri Pirko static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port)
6698320d145SJiri Pirko {
6708320d145SJiri Pirko 	struct devlink_port *devlink_port = &nsim_dev_port->devlink_port;
6718320d145SJiri Pirko 
6728320d145SJiri Pirko 	list_del(&nsim_dev_port->list);
673e05b2d14SJiri Pirko 	devlink_port_type_clear(devlink_port);
674e05b2d14SJiri Pirko 	nsim_destroy(nsim_dev_port->ns);
6758320d145SJiri Pirko 	nsim_dev_port_debugfs_exit(nsim_dev_port);
6768320d145SJiri Pirko 	devlink_port_unregister(devlink_port);
6778320d145SJiri Pirko 	kfree(nsim_dev_port);
6788320d145SJiri Pirko }
6798320d145SJiri Pirko 
6808320d145SJiri Pirko static void nsim_dev_port_del_all(struct nsim_dev *nsim_dev)
6818320d145SJiri Pirko {
6828320d145SJiri Pirko 	struct nsim_dev_port *nsim_dev_port, *tmp;
6838320d145SJiri Pirko 
6846d6f0383SIdo Schimmel 	mutex_lock(&nsim_dev->port_list_lock);
6858320d145SJiri Pirko 	list_for_each_entry_safe(nsim_dev_port, tmp,
6868320d145SJiri Pirko 				 &nsim_dev->port_list, list)
687794b2c05SJiri Pirko 		__nsim_dev_port_del(nsim_dev_port);
6886d6f0383SIdo Schimmel 	mutex_unlock(&nsim_dev->port_list_lock);
6898320d145SJiri Pirko }
6908320d145SJiri Pirko 
6917f36a77aSJiri Pirko static int nsim_dev_port_add_all(struct nsim_dev *nsim_dev,
6927f36a77aSJiri Pirko 				 unsigned int port_count)
6938320d145SJiri Pirko {
6947f36a77aSJiri Pirko 	int i, err;
6958320d145SJiri Pirko 
6967f36a77aSJiri Pirko 	for (i = 0; i < port_count; i++) {
697794b2c05SJiri Pirko 		err = __nsim_dev_port_add(nsim_dev, i);
6988320d145SJiri Pirko 		if (err)
6998320d145SJiri Pirko 			goto err_port_del_all;
7008320d145SJiri Pirko 	}
7018320d145SJiri Pirko 	return 0;
7028320d145SJiri Pirko 
7038320d145SJiri Pirko err_port_del_all:
7048320d145SJiri Pirko 	nsim_dev_port_del_all(nsim_dev);
7058320d145SJiri Pirko 	return err;
7068320d145SJiri Pirko }
7078320d145SJiri Pirko 
70875ba029fSJiri Pirko static int nsim_dev_reload_create(struct nsim_dev *nsim_dev,
70975ba029fSJiri Pirko 				  struct netlink_ext_ack *extack)
71075ba029fSJiri Pirko {
71175ba029fSJiri Pirko 	struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev;
71275ba029fSJiri Pirko 	struct devlink *devlink;
71375ba029fSJiri Pirko 	int err;
71475ba029fSJiri Pirko 
71575ba029fSJiri Pirko 	devlink = priv_to_devlink(nsim_dev);
71675ba029fSJiri Pirko 	nsim_dev = devlink_priv(devlink);
71775ba029fSJiri Pirko 	INIT_LIST_HEAD(&nsim_dev->port_list);
71875ba029fSJiri Pirko 	mutex_init(&nsim_dev->port_list_lock);
71975ba029fSJiri Pirko 	nsim_dev->fw_update_status = true;
72075ba029fSJiri Pirko 
72175ba029fSJiri Pirko 	nsim_dev->fib_data = nsim_fib_create(devlink, extack);
72275ba029fSJiri Pirko 	if (IS_ERR(nsim_dev->fib_data))
72375ba029fSJiri Pirko 		return PTR_ERR(nsim_dev->fib_data);
72475ba029fSJiri Pirko 
72575ba029fSJiri Pirko 	nsim_devlink_param_load_driverinit_values(devlink);
72675ba029fSJiri Pirko 
72775ba029fSJiri Pirko 	err = nsim_dev_dummy_region_init(nsim_dev, devlink);
72875ba029fSJiri Pirko 	if (err)
72975ba029fSJiri Pirko 		goto err_fib_destroy;
73075ba029fSJiri Pirko 
73175ba029fSJiri Pirko 	err = nsim_dev_traps_init(devlink);
73275ba029fSJiri Pirko 	if (err)
73375ba029fSJiri Pirko 		goto err_dummy_region_exit;
73475ba029fSJiri Pirko 
73582c93a87SJiri Pirko 	err = nsim_dev_health_init(nsim_dev, devlink);
73675ba029fSJiri Pirko 	if (err)
73775ba029fSJiri Pirko 		goto err_traps_exit;
73875ba029fSJiri Pirko 
73982c93a87SJiri Pirko 	err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count);
74082c93a87SJiri Pirko 	if (err)
74182c93a87SJiri Pirko 		goto err_health_exit;
74282c93a87SJiri Pirko 
74375ba029fSJiri Pirko 	return 0;
74475ba029fSJiri Pirko 
74582c93a87SJiri Pirko err_health_exit:
74682c93a87SJiri Pirko 	nsim_dev_health_exit(nsim_dev);
74775ba029fSJiri Pirko err_traps_exit:
74875ba029fSJiri Pirko 	nsim_dev_traps_exit(devlink);
74975ba029fSJiri Pirko err_dummy_region_exit:
75075ba029fSJiri Pirko 	nsim_dev_dummy_region_exit(nsim_dev);
75175ba029fSJiri Pirko err_fib_destroy:
75275ba029fSJiri Pirko 	nsim_fib_destroy(devlink, nsim_dev->fib_data);
75375ba029fSJiri Pirko 	return err;
75475ba029fSJiri Pirko }
75575ba029fSJiri Pirko 
756*bfcccfe7SJakub Kicinski int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev)
7577f36a77aSJiri Pirko {
7587f36a77aSJiri Pirko 	struct nsim_dev *nsim_dev;
7597f36a77aSJiri Pirko 	struct devlink *devlink;
7607f36a77aSJiri Pirko 	int err;
7617f36a77aSJiri Pirko 
7627f36a77aSJiri Pirko 	devlink = devlink_alloc(&nsim_dev_devlink_ops, sizeof(*nsim_dev));
7637f36a77aSJiri Pirko 	if (!devlink)
764*bfcccfe7SJakub Kicinski 		return -ENOMEM;
7657b60027bSJiri Pirko 	devlink_net_set(devlink, nsim_bus_dev->initial_net);
7667f36a77aSJiri Pirko 	nsim_dev = devlink_priv(devlink);
7677f36a77aSJiri Pirko 	nsim_dev->nsim_bus_dev = nsim_bus_dev;
7687f36a77aSJiri Pirko 	nsim_dev->switch_id.id_len = sizeof(nsim_dev->switch_id.id);
7697f36a77aSJiri Pirko 	get_random_bytes(nsim_dev->switch_id.id, nsim_dev->switch_id.id_len);
7707f36a77aSJiri Pirko 	INIT_LIST_HEAD(&nsim_dev->port_list);
7717f36a77aSJiri Pirko 	mutex_init(&nsim_dev->port_list_lock);
7727f36a77aSJiri Pirko 	nsim_dev->fw_update_status = true;
7737f36a77aSJiri Pirko 	nsim_dev->max_macs = NSIM_DEV_MAX_MACS_DEFAULT;
7747f36a77aSJiri Pirko 	nsim_dev->test1 = NSIM_DEV_TEST1_DEFAULT;
7757f36a77aSJiri Pirko 
776*bfcccfe7SJakub Kicinski 	dev_set_drvdata(&nsim_bus_dev->dev, nsim_dev);
777*bfcccfe7SJakub Kicinski 
7787f36a77aSJiri Pirko 	err = nsim_dev_resources_register(devlink);
7797f36a77aSJiri Pirko 	if (err)
7807f36a77aSJiri Pirko 		goto err_devlink_free;
7817f36a77aSJiri Pirko 
78275ba029fSJiri Pirko 	nsim_dev->fib_data = nsim_fib_create(devlink, NULL);
7837f36a77aSJiri Pirko 	if (IS_ERR(nsim_dev->fib_data)) {
7847f36a77aSJiri Pirko 		err = PTR_ERR(nsim_dev->fib_data);
7857f36a77aSJiri Pirko 		goto err_resources_unregister;
7867f36a77aSJiri Pirko 	}
7877f36a77aSJiri Pirko 
7887f36a77aSJiri Pirko 	err = devlink_register(devlink, &nsim_bus_dev->dev);
7897f36a77aSJiri Pirko 	if (err)
7907f36a77aSJiri Pirko 		goto err_fib_destroy;
7917f36a77aSJiri Pirko 
7927f36a77aSJiri Pirko 	err = devlink_params_register(devlink, nsim_devlink_params,
7937f36a77aSJiri Pirko 				      ARRAY_SIZE(nsim_devlink_params));
7947f36a77aSJiri Pirko 	if (err)
7957f36a77aSJiri Pirko 		goto err_dl_unregister;
7967f36a77aSJiri Pirko 	nsim_devlink_set_params_init_values(nsim_dev, devlink);
7977f36a77aSJiri Pirko 
7987f36a77aSJiri Pirko 	err = nsim_dev_dummy_region_init(nsim_dev, devlink);
7997f36a77aSJiri Pirko 	if (err)
8007f36a77aSJiri Pirko 		goto err_params_unregister;
8017f36a77aSJiri Pirko 
8027f36a77aSJiri Pirko 	err = nsim_dev_traps_init(devlink);
8037f36a77aSJiri Pirko 	if (err)
8047f36a77aSJiri Pirko 		goto err_dummy_region_exit;
8057f36a77aSJiri Pirko 
8067f36a77aSJiri Pirko 	err = nsim_dev_debugfs_init(nsim_dev);
8077f36a77aSJiri Pirko 	if (err)
8087f36a77aSJiri Pirko 		goto err_traps_exit;
8097f36a77aSJiri Pirko 
81082c93a87SJiri Pirko 	err = nsim_dev_health_init(nsim_dev, devlink);
8117f36a77aSJiri Pirko 	if (err)
8127f36a77aSJiri Pirko 		goto err_debugfs_exit;
8137f36a77aSJiri Pirko 
81482c93a87SJiri Pirko 	err = nsim_bpf_dev_init(nsim_dev);
81582c93a87SJiri Pirko 	if (err)
81682c93a87SJiri Pirko 		goto err_health_exit;
81782c93a87SJiri Pirko 
8187f36a77aSJiri Pirko 	err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count);
8197f36a77aSJiri Pirko 	if (err)
8207f36a77aSJiri Pirko 		goto err_bpf_dev_exit;
8217f36a77aSJiri Pirko 
8227f36a77aSJiri Pirko 	devlink_params_publish(devlink);
823*bfcccfe7SJakub Kicinski 	return 0;
8247f36a77aSJiri Pirko 
8257f36a77aSJiri Pirko err_bpf_dev_exit:
8267f36a77aSJiri Pirko 	nsim_bpf_dev_exit(nsim_dev);
82782c93a87SJiri Pirko err_health_exit:
82882c93a87SJiri Pirko 	nsim_dev_health_exit(nsim_dev);
8297f36a77aSJiri Pirko err_debugfs_exit:
8307f36a77aSJiri Pirko 	nsim_dev_debugfs_exit(nsim_dev);
8317f36a77aSJiri Pirko err_traps_exit:
8327f36a77aSJiri Pirko 	nsim_dev_traps_exit(devlink);
8337f36a77aSJiri Pirko err_dummy_region_exit:
8347f36a77aSJiri Pirko 	nsim_dev_dummy_region_exit(nsim_dev);
8357f36a77aSJiri Pirko err_params_unregister:
8367f36a77aSJiri Pirko 	devlink_params_unregister(devlink, nsim_devlink_params,
8377f36a77aSJiri Pirko 				  ARRAY_SIZE(nsim_devlink_params));
8387f36a77aSJiri Pirko err_dl_unregister:
8397f36a77aSJiri Pirko 	devlink_unregister(devlink);
8407f36a77aSJiri Pirko err_fib_destroy:
8417f36a77aSJiri Pirko 	nsim_fib_destroy(devlink, nsim_dev->fib_data);
8427f36a77aSJiri Pirko err_resources_unregister:
8437f36a77aSJiri Pirko 	devlink_resources_unregister(devlink, NULL);
8447f36a77aSJiri Pirko err_devlink_free:
8457f36a77aSJiri Pirko 	devlink_free(devlink);
846*bfcccfe7SJakub Kicinski 	return err;
8477f36a77aSJiri Pirko }
8487f36a77aSJiri Pirko 
84975ba029fSJiri Pirko static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev)
85075ba029fSJiri Pirko {
85175ba029fSJiri Pirko 	struct devlink *devlink = priv_to_devlink(nsim_dev);
85275ba029fSJiri Pirko 
85375ba029fSJiri Pirko 	if (devlink_is_reload_failed(devlink))
85475ba029fSJiri Pirko 		return;
85575ba029fSJiri Pirko 	nsim_dev_port_del_all(nsim_dev);
85682c93a87SJiri Pirko 	nsim_dev_health_exit(nsim_dev);
85775ba029fSJiri Pirko 	nsim_dev_traps_exit(devlink);
85875ba029fSJiri Pirko 	nsim_dev_dummy_region_exit(nsim_dev);
85975ba029fSJiri Pirko 	mutex_destroy(&nsim_dev->port_list_lock);
86075ba029fSJiri Pirko 	nsim_fib_destroy(devlink, nsim_dev->fib_data);
86175ba029fSJiri Pirko }
86275ba029fSJiri Pirko 
863*bfcccfe7SJakub Kicinski void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev)
8647f36a77aSJiri Pirko {
865*bfcccfe7SJakub Kicinski 	struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
8667f36a77aSJiri Pirko 	struct devlink *devlink = priv_to_devlink(nsim_dev);
8677f36a77aSJiri Pirko 
86875ba029fSJiri Pirko 	nsim_dev_reload_destroy(nsim_dev);
86975ba029fSJiri Pirko 
8707f36a77aSJiri Pirko 	nsim_bpf_dev_exit(nsim_dev);
8717f36a77aSJiri Pirko 	nsim_dev_debugfs_exit(nsim_dev);
8727f36a77aSJiri Pirko 	devlink_params_unregister(devlink, nsim_devlink_params,
8737f36a77aSJiri Pirko 				  ARRAY_SIZE(nsim_devlink_params));
8747f36a77aSJiri Pirko 	devlink_unregister(devlink);
8757f36a77aSJiri Pirko 	devlink_resources_unregister(devlink, NULL);
8767f36a77aSJiri Pirko 	devlink_free(devlink);
8777f36a77aSJiri Pirko }
8787f36a77aSJiri Pirko 
879794b2c05SJiri Pirko static struct nsim_dev_port *
880794b2c05SJiri Pirko __nsim_dev_port_lookup(struct nsim_dev *nsim_dev, unsigned int port_index)
881794b2c05SJiri Pirko {
882794b2c05SJiri Pirko 	struct nsim_dev_port *nsim_dev_port;
883794b2c05SJiri Pirko 
884794b2c05SJiri Pirko 	list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list)
885794b2c05SJiri Pirko 		if (nsim_dev_port->port_index == port_index)
886794b2c05SJiri Pirko 			return nsim_dev_port;
887794b2c05SJiri Pirko 	return NULL;
888794b2c05SJiri Pirko }
889794b2c05SJiri Pirko 
890794b2c05SJiri Pirko int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev,
891794b2c05SJiri Pirko 		      unsigned int port_index)
892794b2c05SJiri Pirko {
893794b2c05SJiri Pirko 	struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
894794b2c05SJiri Pirko 	int err;
895794b2c05SJiri Pirko 
896794b2c05SJiri Pirko 	mutex_lock(&nsim_dev->port_list_lock);
897794b2c05SJiri Pirko 	if (__nsim_dev_port_lookup(nsim_dev, port_index))
898794b2c05SJiri Pirko 		err = -EEXIST;
899794b2c05SJiri Pirko 	else
900794b2c05SJiri Pirko 		err = __nsim_dev_port_add(nsim_dev, port_index);
901794b2c05SJiri Pirko 	mutex_unlock(&nsim_dev->port_list_lock);
902794b2c05SJiri Pirko 	return err;
903794b2c05SJiri Pirko }
904794b2c05SJiri Pirko 
905794b2c05SJiri Pirko int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev,
906794b2c05SJiri Pirko 		      unsigned int port_index)
907794b2c05SJiri Pirko {
908794b2c05SJiri Pirko 	struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
909794b2c05SJiri Pirko 	struct nsim_dev_port *nsim_dev_port;
910794b2c05SJiri Pirko 	int err = 0;
911794b2c05SJiri Pirko 
912794b2c05SJiri Pirko 	mutex_lock(&nsim_dev->port_list_lock);
913794b2c05SJiri Pirko 	nsim_dev_port = __nsim_dev_port_lookup(nsim_dev, port_index);
914794b2c05SJiri Pirko 	if (!nsim_dev_port)
915794b2c05SJiri Pirko 		err = -ENOENT;
916794b2c05SJiri Pirko 	else
917794b2c05SJiri Pirko 		__nsim_dev_port_del(nsim_dev_port);
918794b2c05SJiri Pirko 	mutex_unlock(&nsim_dev->port_list_lock);
919794b2c05SJiri Pirko 	return err;
920794b2c05SJiri Pirko }
921794b2c05SJiri Pirko 
922d514f41eSJiri Pirko int nsim_dev_init(void)
923d514f41eSJiri Pirko {
924ab1d0cc0SJiri Pirko 	nsim_dev_ddir = debugfs_create_dir(DRV_NAME, NULL);
925d514f41eSJiri Pirko 	if (IS_ERR_OR_NULL(nsim_dev_ddir))
926d514f41eSJiri Pirko 		return -ENOMEM;
927d514f41eSJiri Pirko 	return 0;
928d514f41eSJiri Pirko }
929d514f41eSJiri Pirko 
930d514f41eSJiri Pirko void nsim_dev_exit(void)
931d514f41eSJiri Pirko {
932d514f41eSJiri Pirko 	debugfs_remove_recursive(nsim_dev_ddir);
933d514f41eSJiri Pirko }
934