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 ¶ms); 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, ¶ms); 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, ¶ms); 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 ¶ms); 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, ¶ms); 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, ¶ms); 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