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 ¶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 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