xref: /linux-6.15/drivers/net/netdevsim/dev.c (revision 155ddfc5)
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);
93*155ddfc5SJiri Pirko 	debugfs_create_bool("dont_allow_reload", 0600, nsim_dev->ddir,
94*155ddfc5SJiri Pirko 			    &nsim_dev->dont_allow_reload);
95*155ddfc5SJiri Pirko 	debugfs_create_bool("fail_reload", 0600, nsim_dev->ddir,
96*155ddfc5SJiri 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 
485*155ddfc5SJiri Pirko 	if (nsim_dev->dont_allow_reload) {
486*155ddfc5SJiri Pirko 		/* For testing purposes, user set debugfs dont_allow_reload
487*155ddfc5SJiri Pirko 		 * value to true. So forbid it.
488*155ddfc5SJiri Pirko 		 */
489*155ddfc5SJiri Pirko 		NL_SET_ERR_MSG_MOD(extack, "User forbidded reload for testing purposes");
490*155ddfc5SJiri Pirko 		return -EOPNOTSUPP;
491*155ddfc5SJiri Pirko 	}
492*155ddfc5SJiri 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 
502*155ddfc5SJiri Pirko 	if (nsim_dev->fail_reload) {
503*155ddfc5SJiri Pirko 		/* For testing purposes, user set debugfs fail_reload
504*155ddfc5SJiri Pirko 		 * value to true. Fail right away.
505*155ddfc5SJiri Pirko 		 */
506*155ddfc5SJiri Pirko 		NL_SET_ERR_MSG_MOD(extack, "User setup the reload to fail for testing purposes");
507*155ddfc5SJiri Pirko 		return -EINVAL;
508*155ddfc5SJiri Pirko 	}
509*155ddfc5SJiri Pirko 
51075ba029fSJiri Pirko 	return nsim_dev_reload_create(nsim_dev, extack);
5118fb4bc6fSJiri Pirko }
5128fb4bc6fSJiri Pirko 
513fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_SIZE 500000
514fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_CHUNK_SIZE 1000
515fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_CHUNK_TIME_MS 10
516fa4dfc4aSJiri Pirko 
517fa4dfc4aSJiri Pirko static int nsim_dev_flash_update(struct devlink *devlink, const char *file_name,
518fa4dfc4aSJiri Pirko 				 const char *component,
519fa4dfc4aSJiri Pirko 				 struct netlink_ext_ack *extack)
520fa4dfc4aSJiri Pirko {
521fa4dfc4aSJiri Pirko 	struct nsim_dev *nsim_dev = devlink_priv(devlink);
522fa4dfc4aSJiri Pirko 	int i;
523fa4dfc4aSJiri Pirko 
524fa4dfc4aSJiri Pirko 	if (nsim_dev->fw_update_status) {
525fa4dfc4aSJiri Pirko 		devlink_flash_update_begin_notify(devlink);
526fa4dfc4aSJiri Pirko 		devlink_flash_update_status_notify(devlink,
527fa4dfc4aSJiri Pirko 						   "Preparing to flash",
528fa4dfc4aSJiri Pirko 						   component, 0, 0);
529fa4dfc4aSJiri Pirko 	}
530fa4dfc4aSJiri Pirko 
531fa4dfc4aSJiri Pirko 	for (i = 0; i < NSIM_DEV_FLASH_SIZE / NSIM_DEV_FLASH_CHUNK_SIZE; i++) {
532fa4dfc4aSJiri Pirko 		if (nsim_dev->fw_update_status)
533fa4dfc4aSJiri Pirko 			devlink_flash_update_status_notify(devlink, "Flashing",
534fa4dfc4aSJiri Pirko 							   component,
535fa4dfc4aSJiri Pirko 							   i * NSIM_DEV_FLASH_CHUNK_SIZE,
536fa4dfc4aSJiri Pirko 							   NSIM_DEV_FLASH_SIZE);
537fa4dfc4aSJiri Pirko 		msleep(NSIM_DEV_FLASH_CHUNK_TIME_MS);
538fa4dfc4aSJiri Pirko 	}
539fa4dfc4aSJiri Pirko 
540fa4dfc4aSJiri Pirko 	if (nsim_dev->fw_update_status) {
541fa4dfc4aSJiri Pirko 		devlink_flash_update_status_notify(devlink, "Flashing",
542fa4dfc4aSJiri Pirko 						   component,
543fa4dfc4aSJiri Pirko 						   NSIM_DEV_FLASH_SIZE,
544fa4dfc4aSJiri Pirko 						   NSIM_DEV_FLASH_SIZE);
545fa4dfc4aSJiri Pirko 		devlink_flash_update_status_notify(devlink, "Flashing done",
546fa4dfc4aSJiri Pirko 						   component, 0, 0);
547fa4dfc4aSJiri Pirko 		devlink_flash_update_end_notify(devlink);
548fa4dfc4aSJiri Pirko 	}
549fa4dfc4aSJiri Pirko 
550fa4dfc4aSJiri Pirko 	return 0;
551fa4dfc4aSJiri Pirko }
552fa4dfc4aSJiri Pirko 
553da58f90fSIdo Schimmel static struct nsim_trap_item *
554da58f90fSIdo Schimmel nsim_dev_trap_item_lookup(struct nsim_dev *nsim_dev, u16 trap_id)
555da58f90fSIdo Schimmel {
556da58f90fSIdo Schimmel 	struct nsim_trap_data *nsim_trap_data = nsim_dev->trap_data;
557da58f90fSIdo Schimmel 	int i;
558da58f90fSIdo Schimmel 
559da58f90fSIdo Schimmel 	for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) {
560da58f90fSIdo Schimmel 		if (nsim_traps_arr[i].id == trap_id)
561da58f90fSIdo Schimmel 			return &nsim_trap_data->trap_items_arr[i];
562da58f90fSIdo Schimmel 	}
563da58f90fSIdo Schimmel 
564da58f90fSIdo Schimmel 	return NULL;
565da58f90fSIdo Schimmel }
566da58f90fSIdo Schimmel 
567da58f90fSIdo Schimmel static int nsim_dev_devlink_trap_init(struct devlink *devlink,
568da58f90fSIdo Schimmel 				      const struct devlink_trap *trap,
569da58f90fSIdo Schimmel 				      void *trap_ctx)
570da58f90fSIdo Schimmel {
571da58f90fSIdo Schimmel 	struct nsim_dev *nsim_dev = devlink_priv(devlink);
572da58f90fSIdo Schimmel 	struct nsim_trap_item *nsim_trap_item;
573da58f90fSIdo Schimmel 
574da58f90fSIdo Schimmel 	nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id);
575da58f90fSIdo Schimmel 	if (WARN_ON(!nsim_trap_item))
576da58f90fSIdo Schimmel 		return -ENOENT;
577da58f90fSIdo Schimmel 
578da58f90fSIdo Schimmel 	nsim_trap_item->trap_ctx = trap_ctx;
579da58f90fSIdo Schimmel 	nsim_trap_item->action = trap->init_action;
580da58f90fSIdo Schimmel 
581da58f90fSIdo Schimmel 	return 0;
582da58f90fSIdo Schimmel }
583da58f90fSIdo Schimmel 
584da58f90fSIdo Schimmel static int
585da58f90fSIdo Schimmel nsim_dev_devlink_trap_action_set(struct devlink *devlink,
586da58f90fSIdo Schimmel 				 const struct devlink_trap *trap,
587da58f90fSIdo Schimmel 				 enum devlink_trap_action action)
588da58f90fSIdo Schimmel {
589da58f90fSIdo Schimmel 	struct nsim_dev *nsim_dev = devlink_priv(devlink);
590da58f90fSIdo Schimmel 	struct nsim_trap_item *nsim_trap_item;
591da58f90fSIdo Schimmel 
592da58f90fSIdo Schimmel 	nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id);
593da58f90fSIdo Schimmel 	if (WARN_ON(!nsim_trap_item))
594da58f90fSIdo Schimmel 		return -ENOENT;
595da58f90fSIdo Schimmel 
596da58f90fSIdo Schimmel 	spin_lock(&nsim_dev->trap_data->trap_lock);
597da58f90fSIdo Schimmel 	nsim_trap_item->action = action;
598da58f90fSIdo Schimmel 	spin_unlock(&nsim_dev->trap_data->trap_lock);
599da58f90fSIdo Schimmel 
600da58f90fSIdo Schimmel 	return 0;
601da58f90fSIdo Schimmel }
602da58f90fSIdo Schimmel 
6038fb4bc6fSJiri Pirko static const struct devlink_ops nsim_dev_devlink_ops = {
60497691069SJiri Pirko 	.reload_down = nsim_dev_reload_down,
60597691069SJiri Pirko 	.reload_up = nsim_dev_reload_up,
606fa4dfc4aSJiri Pirko 	.flash_update = nsim_dev_flash_update,
607da58f90fSIdo Schimmel 	.trap_init = nsim_dev_devlink_trap_init,
608da58f90fSIdo Schimmel 	.trap_action_set = nsim_dev_devlink_trap_action_set,
6098fb4bc6fSJiri Pirko };
6108fb4bc6fSJiri Pirko 
611150e8f8aSJiri Pirko #define NSIM_DEV_MAX_MACS_DEFAULT 32
612150e8f8aSJiri Pirko #define NSIM_DEV_TEST1_DEFAULT true
613150e8f8aSJiri Pirko 
614794b2c05SJiri Pirko static int __nsim_dev_port_add(struct nsim_dev *nsim_dev,
615794b2c05SJiri Pirko 			       unsigned int port_index)
6168320d145SJiri Pirko {
6178320d145SJiri Pirko 	struct nsim_dev_port *nsim_dev_port;
6188320d145SJiri Pirko 	struct devlink_port *devlink_port;
6198320d145SJiri Pirko 	int err;
6208320d145SJiri Pirko 
6218320d145SJiri Pirko 	nsim_dev_port = kzalloc(sizeof(*nsim_dev_port), GFP_KERNEL);
6228320d145SJiri Pirko 	if (!nsim_dev_port)
6238320d145SJiri Pirko 		return -ENOMEM;
6248320d145SJiri Pirko 	nsim_dev_port->port_index = port_index;
6258320d145SJiri Pirko 
6268320d145SJiri Pirko 	devlink_port = &nsim_dev_port->devlink_port;
6278320d145SJiri Pirko 	devlink_port_attrs_set(devlink_port, DEVLINK_PORT_FLAVOUR_PHYSICAL,
6288320d145SJiri Pirko 			       port_index + 1, 0, 0,
6298320d145SJiri Pirko 			       nsim_dev->switch_id.id,
6308320d145SJiri Pirko 			       nsim_dev->switch_id.id_len);
6318320d145SJiri Pirko 	err = devlink_port_register(priv_to_devlink(nsim_dev), devlink_port,
6328320d145SJiri Pirko 				    port_index);
6338320d145SJiri Pirko 	if (err)
6348320d145SJiri Pirko 		goto err_port_free;
6358320d145SJiri Pirko 
6368320d145SJiri Pirko 	err = nsim_dev_port_debugfs_init(nsim_dev, nsim_dev_port);
6378320d145SJiri Pirko 	if (err)
6388320d145SJiri Pirko 		goto err_dl_port_unregister;
6398320d145SJiri Pirko 
640e05b2d14SJiri Pirko 	nsim_dev_port->ns = nsim_create(nsim_dev, nsim_dev_port);
641e05b2d14SJiri Pirko 	if (IS_ERR(nsim_dev_port->ns)) {
642e05b2d14SJiri Pirko 		err = PTR_ERR(nsim_dev_port->ns);
643e05b2d14SJiri Pirko 		goto err_port_debugfs_exit;
644e05b2d14SJiri Pirko 	}
645e05b2d14SJiri Pirko 
646e05b2d14SJiri Pirko 	devlink_port_type_eth_set(devlink_port, nsim_dev_port->ns->netdev);
6478320d145SJiri Pirko 	list_add(&nsim_dev_port->list, &nsim_dev->port_list);
6488320d145SJiri Pirko 
6498320d145SJiri Pirko 	return 0;
6508320d145SJiri Pirko 
651e05b2d14SJiri Pirko err_port_debugfs_exit:
652e05b2d14SJiri Pirko 	nsim_dev_port_debugfs_exit(nsim_dev_port);
6538320d145SJiri Pirko err_dl_port_unregister:
6548320d145SJiri Pirko 	devlink_port_unregister(devlink_port);
6558320d145SJiri Pirko err_port_free:
6568320d145SJiri Pirko 	kfree(nsim_dev_port);
6578320d145SJiri Pirko 	return err;
6588320d145SJiri Pirko }
6598320d145SJiri Pirko 
660794b2c05SJiri Pirko static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port)
6618320d145SJiri Pirko {
6628320d145SJiri Pirko 	struct devlink_port *devlink_port = &nsim_dev_port->devlink_port;
6638320d145SJiri Pirko 
6648320d145SJiri Pirko 	list_del(&nsim_dev_port->list);
665e05b2d14SJiri Pirko 	devlink_port_type_clear(devlink_port);
666e05b2d14SJiri Pirko 	nsim_destroy(nsim_dev_port->ns);
6678320d145SJiri Pirko 	nsim_dev_port_debugfs_exit(nsim_dev_port);
6688320d145SJiri Pirko 	devlink_port_unregister(devlink_port);
6698320d145SJiri Pirko 	kfree(nsim_dev_port);
6708320d145SJiri Pirko }
6718320d145SJiri Pirko 
6728320d145SJiri Pirko static void nsim_dev_port_del_all(struct nsim_dev *nsim_dev)
6738320d145SJiri Pirko {
6748320d145SJiri Pirko 	struct nsim_dev_port *nsim_dev_port, *tmp;
6758320d145SJiri Pirko 
6768320d145SJiri Pirko 	list_for_each_entry_safe(nsim_dev_port, tmp,
6778320d145SJiri Pirko 				 &nsim_dev->port_list, list)
678794b2c05SJiri Pirko 		__nsim_dev_port_del(nsim_dev_port);
6798320d145SJiri Pirko }
6808320d145SJiri Pirko 
6817f36a77aSJiri Pirko static int nsim_dev_port_add_all(struct nsim_dev *nsim_dev,
6827f36a77aSJiri Pirko 				 unsigned int port_count)
6838320d145SJiri Pirko {
6847f36a77aSJiri Pirko 	int i, err;
6858320d145SJiri Pirko 
6867f36a77aSJiri Pirko 	for (i = 0; i < port_count; i++) {
687794b2c05SJiri Pirko 		err = __nsim_dev_port_add(nsim_dev, i);
6888320d145SJiri Pirko 		if (err)
6898320d145SJiri Pirko 			goto err_port_del_all;
6908320d145SJiri Pirko 	}
6918320d145SJiri Pirko 	return 0;
6928320d145SJiri Pirko 
6938320d145SJiri Pirko err_port_del_all:
6948320d145SJiri Pirko 	nsim_dev_port_del_all(nsim_dev);
6958320d145SJiri Pirko 	return err;
6968320d145SJiri Pirko }
6978320d145SJiri Pirko 
69875ba029fSJiri Pirko static int nsim_dev_reload_create(struct nsim_dev *nsim_dev,
69975ba029fSJiri Pirko 				  struct netlink_ext_ack *extack)
70075ba029fSJiri Pirko {
70175ba029fSJiri Pirko 	struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev;
70275ba029fSJiri Pirko 	struct devlink *devlink;
70375ba029fSJiri Pirko 	int err;
70475ba029fSJiri Pirko 
70575ba029fSJiri Pirko 	devlink = priv_to_devlink(nsim_dev);
70675ba029fSJiri Pirko 	nsim_dev = devlink_priv(devlink);
70775ba029fSJiri Pirko 	INIT_LIST_HEAD(&nsim_dev->port_list);
70875ba029fSJiri Pirko 	mutex_init(&nsim_dev->port_list_lock);
70975ba029fSJiri Pirko 	nsim_dev->fw_update_status = true;
71075ba029fSJiri Pirko 
71175ba029fSJiri Pirko 	nsim_dev->fib_data = nsim_fib_create(devlink, extack);
71275ba029fSJiri Pirko 	if (IS_ERR(nsim_dev->fib_data))
71375ba029fSJiri Pirko 		return PTR_ERR(nsim_dev->fib_data);
71475ba029fSJiri Pirko 
71575ba029fSJiri Pirko 	nsim_devlink_param_load_driverinit_values(devlink);
71675ba029fSJiri Pirko 
71775ba029fSJiri Pirko 	err = nsim_dev_dummy_region_init(nsim_dev, devlink);
71875ba029fSJiri Pirko 	if (err)
71975ba029fSJiri Pirko 		goto err_fib_destroy;
72075ba029fSJiri Pirko 
72175ba029fSJiri Pirko 	err = nsim_dev_traps_init(devlink);
72275ba029fSJiri Pirko 	if (err)
72375ba029fSJiri Pirko 		goto err_dummy_region_exit;
72475ba029fSJiri Pirko 
72575ba029fSJiri Pirko 	err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count);
72675ba029fSJiri Pirko 	if (err)
72775ba029fSJiri Pirko 		goto err_traps_exit;
72875ba029fSJiri Pirko 
72975ba029fSJiri Pirko 	return 0;
73075ba029fSJiri Pirko 
73175ba029fSJiri Pirko err_traps_exit:
73275ba029fSJiri Pirko 	nsim_dev_traps_exit(devlink);
73375ba029fSJiri Pirko err_dummy_region_exit:
73475ba029fSJiri Pirko 	nsim_dev_dummy_region_exit(nsim_dev);
73575ba029fSJiri Pirko err_fib_destroy:
73675ba029fSJiri Pirko 	nsim_fib_destroy(devlink, nsim_dev->fib_data);
73775ba029fSJiri Pirko 	return err;
73875ba029fSJiri Pirko }
73975ba029fSJiri Pirko 
74075ba029fSJiri Pirko static struct nsim_dev *nsim_dev_create(struct nsim_bus_dev *nsim_bus_dev)
7417f36a77aSJiri Pirko {
7427f36a77aSJiri Pirko 	struct nsim_dev *nsim_dev;
7437f36a77aSJiri Pirko 	struct devlink *devlink;
7447f36a77aSJiri Pirko 	int err;
7457f36a77aSJiri Pirko 
7467f36a77aSJiri Pirko 	devlink = devlink_alloc(&nsim_dev_devlink_ops, sizeof(*nsim_dev));
7477f36a77aSJiri Pirko 	if (!devlink)
7487f36a77aSJiri Pirko 		return ERR_PTR(-ENOMEM);
7497b60027bSJiri Pirko 	devlink_net_set(devlink, nsim_bus_dev->initial_net);
7507f36a77aSJiri Pirko 	nsim_dev = devlink_priv(devlink);
7517f36a77aSJiri Pirko 	nsim_dev->nsim_bus_dev = nsim_bus_dev;
7527f36a77aSJiri Pirko 	nsim_dev->switch_id.id_len = sizeof(nsim_dev->switch_id.id);
7537f36a77aSJiri Pirko 	get_random_bytes(nsim_dev->switch_id.id, nsim_dev->switch_id.id_len);
7547f36a77aSJiri Pirko 	INIT_LIST_HEAD(&nsim_dev->port_list);
7557f36a77aSJiri Pirko 	mutex_init(&nsim_dev->port_list_lock);
7567f36a77aSJiri Pirko 	nsim_dev->fw_update_status = true;
7577f36a77aSJiri Pirko 	nsim_dev->max_macs = NSIM_DEV_MAX_MACS_DEFAULT;
7587f36a77aSJiri Pirko 	nsim_dev->test1 = NSIM_DEV_TEST1_DEFAULT;
7597f36a77aSJiri Pirko 
7607f36a77aSJiri Pirko 	err = nsim_dev_resources_register(devlink);
7617f36a77aSJiri Pirko 	if (err)
7627f36a77aSJiri Pirko 		goto err_devlink_free;
7637f36a77aSJiri Pirko 
76475ba029fSJiri Pirko 	nsim_dev->fib_data = nsim_fib_create(devlink, NULL);
7657f36a77aSJiri Pirko 	if (IS_ERR(nsim_dev->fib_data)) {
7667f36a77aSJiri Pirko 		err = PTR_ERR(nsim_dev->fib_data);
7677f36a77aSJiri Pirko 		goto err_resources_unregister;
7687f36a77aSJiri Pirko 	}
7697f36a77aSJiri Pirko 
7707f36a77aSJiri Pirko 	err = devlink_register(devlink, &nsim_bus_dev->dev);
7717f36a77aSJiri Pirko 	if (err)
7727f36a77aSJiri Pirko 		goto err_fib_destroy;
7737f36a77aSJiri Pirko 
7747f36a77aSJiri Pirko 	err = devlink_params_register(devlink, nsim_devlink_params,
7757f36a77aSJiri Pirko 				      ARRAY_SIZE(nsim_devlink_params));
7767f36a77aSJiri Pirko 	if (err)
7777f36a77aSJiri Pirko 		goto err_dl_unregister;
7787f36a77aSJiri Pirko 	nsim_devlink_set_params_init_values(nsim_dev, devlink);
7797f36a77aSJiri Pirko 
7807f36a77aSJiri Pirko 	err = nsim_dev_dummy_region_init(nsim_dev, devlink);
7817f36a77aSJiri Pirko 	if (err)
7827f36a77aSJiri Pirko 		goto err_params_unregister;
7837f36a77aSJiri Pirko 
7847f36a77aSJiri Pirko 	err = nsim_dev_traps_init(devlink);
7857f36a77aSJiri Pirko 	if (err)
7867f36a77aSJiri Pirko 		goto err_dummy_region_exit;
7877f36a77aSJiri Pirko 
7887f36a77aSJiri Pirko 	err = nsim_dev_debugfs_init(nsim_dev);
7897f36a77aSJiri Pirko 	if (err)
7907f36a77aSJiri Pirko 		goto err_traps_exit;
7917f36a77aSJiri Pirko 
7927f36a77aSJiri Pirko 	err = nsim_bpf_dev_init(nsim_dev);
7937f36a77aSJiri Pirko 	if (err)
7947f36a77aSJiri Pirko 		goto err_debugfs_exit;
7957f36a77aSJiri Pirko 
7967f36a77aSJiri Pirko 	err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count);
7977f36a77aSJiri Pirko 	if (err)
7987f36a77aSJiri Pirko 		goto err_bpf_dev_exit;
7997f36a77aSJiri Pirko 
8007f36a77aSJiri Pirko 	devlink_params_publish(devlink);
8017f36a77aSJiri Pirko 	return nsim_dev;
8027f36a77aSJiri Pirko 
8037f36a77aSJiri Pirko err_bpf_dev_exit:
8047f36a77aSJiri Pirko 	nsim_bpf_dev_exit(nsim_dev);
8057f36a77aSJiri Pirko err_debugfs_exit:
8067f36a77aSJiri Pirko 	nsim_dev_debugfs_exit(nsim_dev);
8077f36a77aSJiri Pirko err_traps_exit:
8087f36a77aSJiri Pirko 	nsim_dev_traps_exit(devlink);
8097f36a77aSJiri Pirko err_dummy_region_exit:
8107f36a77aSJiri Pirko 	nsim_dev_dummy_region_exit(nsim_dev);
8117f36a77aSJiri Pirko err_params_unregister:
8127f36a77aSJiri Pirko 	devlink_params_unregister(devlink, nsim_devlink_params,
8137f36a77aSJiri Pirko 				  ARRAY_SIZE(nsim_devlink_params));
8147f36a77aSJiri Pirko err_dl_unregister:
8157f36a77aSJiri Pirko 	devlink_unregister(devlink);
8167f36a77aSJiri Pirko err_fib_destroy:
8177f36a77aSJiri Pirko 	nsim_fib_destroy(devlink, nsim_dev->fib_data);
8187f36a77aSJiri Pirko err_resources_unregister:
8197f36a77aSJiri Pirko 	devlink_resources_unregister(devlink, NULL);
8207f36a77aSJiri Pirko err_devlink_free:
8217f36a77aSJiri Pirko 	devlink_free(devlink);
8227f36a77aSJiri Pirko 	return ERR_PTR(err);
8237f36a77aSJiri Pirko }
8247f36a77aSJiri Pirko 
82575ba029fSJiri Pirko static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev)
82675ba029fSJiri Pirko {
82775ba029fSJiri Pirko 	struct devlink *devlink = priv_to_devlink(nsim_dev);
82875ba029fSJiri Pirko 
82975ba029fSJiri Pirko 	if (devlink_is_reload_failed(devlink))
83075ba029fSJiri Pirko 		return;
83175ba029fSJiri Pirko 	nsim_dev_port_del_all(nsim_dev);
83275ba029fSJiri Pirko 	nsim_dev_traps_exit(devlink);
83375ba029fSJiri Pirko 	nsim_dev_dummy_region_exit(nsim_dev);
83475ba029fSJiri Pirko 	mutex_destroy(&nsim_dev->port_list_lock);
83575ba029fSJiri Pirko 	nsim_fib_destroy(devlink, nsim_dev->fib_data);
83675ba029fSJiri Pirko }
83775ba029fSJiri Pirko 
8387f36a77aSJiri Pirko static void nsim_dev_destroy(struct nsim_dev *nsim_dev)
8397f36a77aSJiri Pirko {
8407f36a77aSJiri Pirko 	struct devlink *devlink = priv_to_devlink(nsim_dev);
8417f36a77aSJiri Pirko 
84275ba029fSJiri Pirko 	nsim_dev_reload_destroy(nsim_dev);
84375ba029fSJiri Pirko 
8447f36a77aSJiri Pirko 	nsim_bpf_dev_exit(nsim_dev);
8457f36a77aSJiri Pirko 	nsim_dev_debugfs_exit(nsim_dev);
8467f36a77aSJiri Pirko 	devlink_params_unregister(devlink, nsim_devlink_params,
8477f36a77aSJiri Pirko 				  ARRAY_SIZE(nsim_devlink_params));
8487f36a77aSJiri Pirko 	devlink_unregister(devlink);
8497f36a77aSJiri Pirko 	devlink_resources_unregister(devlink, NULL);
8507f36a77aSJiri Pirko 	devlink_free(devlink);
8517f36a77aSJiri Pirko }
8527f36a77aSJiri Pirko 
8537f36a77aSJiri Pirko int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev)
8547f36a77aSJiri Pirko {
8557f36a77aSJiri Pirko 	struct nsim_dev *nsim_dev;
8567f36a77aSJiri Pirko 
8577f36a77aSJiri Pirko 	nsim_dev = nsim_dev_create(nsim_bus_dev);
8587f36a77aSJiri Pirko 	if (IS_ERR(nsim_dev))
8597f36a77aSJiri Pirko 		return PTR_ERR(nsim_dev);
8607f36a77aSJiri Pirko 	dev_set_drvdata(&nsim_bus_dev->dev, nsim_dev);
8617f36a77aSJiri Pirko 
8627f36a77aSJiri Pirko 	return 0;
8637f36a77aSJiri Pirko }
8647f36a77aSJiri Pirko 
8658320d145SJiri Pirko void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev)
8668320d145SJiri Pirko {
8678320d145SJiri Pirko 	struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
8688320d145SJiri Pirko 
8698320d145SJiri Pirko 	nsim_dev_destroy(nsim_dev);
8708320d145SJiri Pirko }
8718320d145SJiri Pirko 
872794b2c05SJiri Pirko static struct nsim_dev_port *
873794b2c05SJiri Pirko __nsim_dev_port_lookup(struct nsim_dev *nsim_dev, unsigned int port_index)
874794b2c05SJiri Pirko {
875794b2c05SJiri Pirko 	struct nsim_dev_port *nsim_dev_port;
876794b2c05SJiri Pirko 
877794b2c05SJiri Pirko 	list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list)
878794b2c05SJiri Pirko 		if (nsim_dev_port->port_index == port_index)
879794b2c05SJiri Pirko 			return nsim_dev_port;
880794b2c05SJiri Pirko 	return NULL;
881794b2c05SJiri Pirko }
882794b2c05SJiri Pirko 
883794b2c05SJiri Pirko int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev,
884794b2c05SJiri Pirko 		      unsigned int port_index)
885794b2c05SJiri Pirko {
886794b2c05SJiri Pirko 	struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
887794b2c05SJiri Pirko 	int err;
888794b2c05SJiri Pirko 
889794b2c05SJiri Pirko 	mutex_lock(&nsim_dev->port_list_lock);
890794b2c05SJiri Pirko 	if (__nsim_dev_port_lookup(nsim_dev, port_index))
891794b2c05SJiri Pirko 		err = -EEXIST;
892794b2c05SJiri Pirko 	else
893794b2c05SJiri Pirko 		err = __nsim_dev_port_add(nsim_dev, port_index);
894794b2c05SJiri Pirko 	mutex_unlock(&nsim_dev->port_list_lock);
895794b2c05SJiri Pirko 	return err;
896794b2c05SJiri Pirko }
897794b2c05SJiri Pirko 
898794b2c05SJiri Pirko int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev,
899794b2c05SJiri Pirko 		      unsigned int port_index)
900794b2c05SJiri Pirko {
901794b2c05SJiri Pirko 	struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev);
902794b2c05SJiri Pirko 	struct nsim_dev_port *nsim_dev_port;
903794b2c05SJiri Pirko 	int err = 0;
904794b2c05SJiri Pirko 
905794b2c05SJiri Pirko 	mutex_lock(&nsim_dev->port_list_lock);
906794b2c05SJiri Pirko 	nsim_dev_port = __nsim_dev_port_lookup(nsim_dev, port_index);
907794b2c05SJiri Pirko 	if (!nsim_dev_port)
908794b2c05SJiri Pirko 		err = -ENOENT;
909794b2c05SJiri Pirko 	else
910794b2c05SJiri Pirko 		__nsim_dev_port_del(nsim_dev_port);
911794b2c05SJiri Pirko 	mutex_unlock(&nsim_dev->port_list_lock);
912794b2c05SJiri Pirko 	return err;
913794b2c05SJiri Pirko }
914794b2c05SJiri Pirko 
915d514f41eSJiri Pirko int nsim_dev_init(void)
916d514f41eSJiri Pirko {
917ab1d0cc0SJiri Pirko 	nsim_dev_ddir = debugfs_create_dir(DRV_NAME, NULL);
918d514f41eSJiri Pirko 	if (IS_ERR_OR_NULL(nsim_dev_ddir))
919d514f41eSJiri Pirko 		return -ENOMEM;
920d514f41eSJiri Pirko 	return 0;
921d514f41eSJiri Pirko }
922d514f41eSJiri Pirko 
923d514f41eSJiri Pirko void nsim_dev_exit(void)
924d514f41eSJiri Pirko {
925d514f41eSJiri Pirko 	debugfs_remove_recursive(nsim_dev_ddir);
926d514f41eSJiri Pirko }
927