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