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> 31*d3cbb907SJiri Pirko #include <net/flow_offload.h> 32da58f90fSIdo Schimmel #include <uapi/linux/devlink.h> 33da58f90fSIdo Schimmel #include <uapi/linux/ip.h> 34da58f90fSIdo Schimmel #include <uapi/linux/udp.h> 358fb4bc6fSJiri Pirko 368fb4bc6fSJiri Pirko #include "netdevsim.h" 378fb4bc6fSJiri Pirko 38d514f41eSJiri Pirko static struct dentry *nsim_dev_ddir; 39d514f41eSJiri Pirko 404418f862SJiri Pirko #define NSIM_DEV_DUMMY_REGION_SIZE (1024 * 32) 414418f862SJiri Pirko 424418f862SJiri Pirko static ssize_t nsim_dev_take_snapshot_write(struct file *file, 434418f862SJiri Pirko const char __user *data, 444418f862SJiri Pirko size_t count, loff_t *ppos) 454418f862SJiri Pirko { 464418f862SJiri Pirko struct nsim_dev *nsim_dev = file->private_data; 474418f862SJiri Pirko void *dummy_data; 484418f862SJiri Pirko int err; 494418f862SJiri Pirko u32 id; 504418f862SJiri Pirko 514418f862SJiri Pirko dummy_data = kmalloc(NSIM_DEV_DUMMY_REGION_SIZE, GFP_KERNEL); 524418f862SJiri Pirko if (!dummy_data) 534418f862SJiri Pirko return -ENOMEM; 544418f862SJiri Pirko 554418f862SJiri Pirko get_random_bytes(dummy_data, NSIM_DEV_DUMMY_REGION_SIZE); 564418f862SJiri Pirko 57b0efcae5SJacob Keller id = devlink_region_snapshot_id_get(priv_to_devlink(nsim_dev)); 584418f862SJiri Pirko err = devlink_region_snapshot_create(nsim_dev->dummy_region, 594418f862SJiri Pirko dummy_data, id, kfree); 604418f862SJiri Pirko if (err) { 614418f862SJiri Pirko pr_err("Failed to create region snapshot\n"); 624418f862SJiri Pirko kfree(dummy_data); 634418f862SJiri Pirko return err; 644418f862SJiri Pirko } 654418f862SJiri Pirko 664418f862SJiri Pirko return count; 674418f862SJiri Pirko } 684418f862SJiri Pirko 694418f862SJiri Pirko static const struct file_operations nsim_dev_take_snapshot_fops = { 704418f862SJiri Pirko .open = simple_open, 714418f862SJiri Pirko .write = nsim_dev_take_snapshot_write, 724418f862SJiri Pirko .llseek = generic_file_llseek, 734418f862SJiri Pirko }; 744418f862SJiri Pirko 75*d3cbb907SJiri Pirko static ssize_t nsim_dev_trap_fa_cookie_read(struct file *file, 76*d3cbb907SJiri Pirko char __user *data, 77*d3cbb907SJiri Pirko size_t count, loff_t *ppos) 78*d3cbb907SJiri Pirko { 79*d3cbb907SJiri Pirko struct nsim_dev *nsim_dev = file->private_data; 80*d3cbb907SJiri Pirko struct flow_action_cookie *fa_cookie; 81*d3cbb907SJiri Pirko unsigned int buf_len; 82*d3cbb907SJiri Pirko ssize_t ret; 83*d3cbb907SJiri Pirko char *buf; 84*d3cbb907SJiri Pirko 85*d3cbb907SJiri Pirko spin_lock(&nsim_dev->fa_cookie_lock); 86*d3cbb907SJiri Pirko fa_cookie = nsim_dev->fa_cookie; 87*d3cbb907SJiri Pirko if (!fa_cookie) { 88*d3cbb907SJiri Pirko ret = -EINVAL; 89*d3cbb907SJiri Pirko goto errout; 90*d3cbb907SJiri Pirko } 91*d3cbb907SJiri Pirko buf_len = fa_cookie->cookie_len * 2; 92*d3cbb907SJiri Pirko buf = kmalloc(buf_len, GFP_ATOMIC); 93*d3cbb907SJiri Pirko if (!buf) { 94*d3cbb907SJiri Pirko ret = -ENOMEM; 95*d3cbb907SJiri Pirko goto errout; 96*d3cbb907SJiri Pirko } 97*d3cbb907SJiri Pirko bin2hex(buf, fa_cookie->cookie, fa_cookie->cookie_len); 98*d3cbb907SJiri Pirko spin_unlock(&nsim_dev->fa_cookie_lock); 99*d3cbb907SJiri Pirko 100*d3cbb907SJiri Pirko ret = simple_read_from_buffer(data, count, ppos, buf, buf_len); 101*d3cbb907SJiri Pirko 102*d3cbb907SJiri Pirko kfree(buf); 103*d3cbb907SJiri Pirko return ret; 104*d3cbb907SJiri Pirko 105*d3cbb907SJiri Pirko errout: 106*d3cbb907SJiri Pirko spin_unlock(&nsim_dev->fa_cookie_lock); 107*d3cbb907SJiri Pirko return ret; 108*d3cbb907SJiri Pirko } 109*d3cbb907SJiri Pirko 110*d3cbb907SJiri Pirko static ssize_t nsim_dev_trap_fa_cookie_write(struct file *file, 111*d3cbb907SJiri Pirko const char __user *data, 112*d3cbb907SJiri Pirko size_t count, loff_t *ppos) 113*d3cbb907SJiri Pirko { 114*d3cbb907SJiri Pirko struct nsim_dev *nsim_dev = file->private_data; 115*d3cbb907SJiri Pirko struct flow_action_cookie *fa_cookie; 116*d3cbb907SJiri Pirko size_t cookie_len; 117*d3cbb907SJiri Pirko ssize_t ret; 118*d3cbb907SJiri Pirko char *buf; 119*d3cbb907SJiri Pirko 120*d3cbb907SJiri Pirko if (*ppos != 0) 121*d3cbb907SJiri Pirko return -EINVAL; 122*d3cbb907SJiri Pirko cookie_len = (count - 1) / 2; 123*d3cbb907SJiri Pirko if ((count - 1) % 2) 124*d3cbb907SJiri Pirko return -EINVAL; 125*d3cbb907SJiri Pirko buf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); 126*d3cbb907SJiri Pirko if (!buf) 127*d3cbb907SJiri Pirko return -ENOMEM; 128*d3cbb907SJiri Pirko 129*d3cbb907SJiri Pirko ret = simple_write_to_buffer(buf, count, ppos, data, count); 130*d3cbb907SJiri Pirko if (ret < 0) 131*d3cbb907SJiri Pirko goto free_buf; 132*d3cbb907SJiri Pirko 133*d3cbb907SJiri Pirko fa_cookie = kmalloc(sizeof(*fa_cookie) + cookie_len, 134*d3cbb907SJiri Pirko GFP_KERNEL | __GFP_NOWARN); 135*d3cbb907SJiri Pirko if (!fa_cookie) { 136*d3cbb907SJiri Pirko ret = -ENOMEM; 137*d3cbb907SJiri Pirko goto free_buf; 138*d3cbb907SJiri Pirko } 139*d3cbb907SJiri Pirko 140*d3cbb907SJiri Pirko fa_cookie->cookie_len = cookie_len; 141*d3cbb907SJiri Pirko ret = hex2bin(fa_cookie->cookie, buf, cookie_len); 142*d3cbb907SJiri Pirko if (ret) 143*d3cbb907SJiri Pirko goto free_fa_cookie; 144*d3cbb907SJiri Pirko kfree(buf); 145*d3cbb907SJiri Pirko 146*d3cbb907SJiri Pirko spin_lock(&nsim_dev->fa_cookie_lock); 147*d3cbb907SJiri Pirko kfree(nsim_dev->fa_cookie); 148*d3cbb907SJiri Pirko nsim_dev->fa_cookie = fa_cookie; 149*d3cbb907SJiri Pirko spin_unlock(&nsim_dev->fa_cookie_lock); 150*d3cbb907SJiri Pirko 151*d3cbb907SJiri Pirko return count; 152*d3cbb907SJiri Pirko 153*d3cbb907SJiri Pirko free_fa_cookie: 154*d3cbb907SJiri Pirko kfree(fa_cookie); 155*d3cbb907SJiri Pirko free_buf: 156*d3cbb907SJiri Pirko kfree(buf); 157*d3cbb907SJiri Pirko return ret; 158*d3cbb907SJiri Pirko } 159*d3cbb907SJiri Pirko 160*d3cbb907SJiri Pirko static const struct file_operations nsim_dev_trap_fa_cookie_fops = { 161*d3cbb907SJiri Pirko .open = simple_open, 162*d3cbb907SJiri Pirko .read = nsim_dev_trap_fa_cookie_read, 163*d3cbb907SJiri Pirko .write = nsim_dev_trap_fa_cookie_write, 164*d3cbb907SJiri Pirko .llseek = generic_file_llseek, 165*d3cbb907SJiri Pirko }; 166*d3cbb907SJiri Pirko 167d514f41eSJiri Pirko static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev) 168d514f41eSJiri Pirko { 1696fb8852bSTaehee Yoo char dev_ddir_name[sizeof(DRV_NAME) + 10]; 170d514f41eSJiri Pirko 171ab1d0cc0SJiri Pirko sprintf(dev_ddir_name, DRV_NAME "%u", nsim_dev->nsim_bus_dev->dev.id); 172d514f41eSJiri Pirko nsim_dev->ddir = debugfs_create_dir(dev_ddir_name, nsim_dev_ddir); 1736556ff32STaehee Yoo if (IS_ERR(nsim_dev->ddir)) 1746556ff32STaehee Yoo return PTR_ERR(nsim_dev->ddir); 175ab1d0cc0SJiri Pirko nsim_dev->ports_ddir = debugfs_create_dir("ports", nsim_dev->ddir); 1766556ff32STaehee Yoo if (IS_ERR(nsim_dev->ports_ddir)) 1776556ff32STaehee Yoo return PTR_ERR(nsim_dev->ports_ddir); 178fa4dfc4aSJiri Pirko debugfs_create_bool("fw_update_status", 0600, nsim_dev->ddir, 179fa4dfc4aSJiri Pirko &nsim_dev->fw_update_status); 180150e8f8aSJiri Pirko debugfs_create_u32("max_macs", 0600, nsim_dev->ddir, 181150e8f8aSJiri Pirko &nsim_dev->max_macs); 182150e8f8aSJiri Pirko debugfs_create_bool("test1", 0600, nsim_dev->ddir, 183150e8f8aSJiri Pirko &nsim_dev->test1); 1848526ad96STaehee Yoo nsim_dev->take_snapshot = debugfs_create_file("take_snapshot", 1858526ad96STaehee Yoo 0200, 1868526ad96STaehee Yoo nsim_dev->ddir, 1878526ad96STaehee Yoo nsim_dev, 1884418f862SJiri Pirko &nsim_dev_take_snapshot_fops); 189155ddfc5SJiri Pirko debugfs_create_bool("dont_allow_reload", 0600, nsim_dev->ddir, 190155ddfc5SJiri Pirko &nsim_dev->dont_allow_reload); 191155ddfc5SJiri Pirko debugfs_create_bool("fail_reload", 0600, nsim_dev->ddir, 192155ddfc5SJiri Pirko &nsim_dev->fail_reload); 193*d3cbb907SJiri Pirko debugfs_create_file("trap_flow_action_cookie", 0600, nsim_dev->ddir, 194*d3cbb907SJiri Pirko nsim_dev, &nsim_dev_trap_fa_cookie_fops); 195d514f41eSJiri Pirko return 0; 196d514f41eSJiri Pirko } 197d514f41eSJiri Pirko 198d514f41eSJiri Pirko static void nsim_dev_debugfs_exit(struct nsim_dev *nsim_dev) 199d514f41eSJiri Pirko { 200ab1d0cc0SJiri Pirko debugfs_remove_recursive(nsim_dev->ports_ddir); 201d514f41eSJiri Pirko debugfs_remove_recursive(nsim_dev->ddir); 202d514f41eSJiri Pirko } 203d514f41eSJiri Pirko 2048320d145SJiri Pirko static int nsim_dev_port_debugfs_init(struct nsim_dev *nsim_dev, 2058320d145SJiri Pirko struct nsim_dev_port *nsim_dev_port) 2068320d145SJiri Pirko { 2078320d145SJiri Pirko char port_ddir_name[16]; 2088320d145SJiri Pirko char dev_link_name[32]; 2098320d145SJiri Pirko 2108320d145SJiri Pirko sprintf(port_ddir_name, "%u", nsim_dev_port->port_index); 2118320d145SJiri Pirko nsim_dev_port->ddir = debugfs_create_dir(port_ddir_name, 2128320d145SJiri Pirko nsim_dev->ports_ddir); 2136556ff32STaehee Yoo if (IS_ERR(nsim_dev_port->ddir)) 2146556ff32STaehee Yoo return PTR_ERR(nsim_dev_port->ddir); 2158320d145SJiri Pirko 2168320d145SJiri Pirko sprintf(dev_link_name, "../../../" DRV_NAME "%u", 2178320d145SJiri Pirko nsim_dev->nsim_bus_dev->dev.id); 2188320d145SJiri Pirko debugfs_create_symlink("dev", nsim_dev_port->ddir, dev_link_name); 2198320d145SJiri Pirko 2208320d145SJiri Pirko return 0; 2218320d145SJiri Pirko } 2228320d145SJiri Pirko 2238320d145SJiri Pirko static void nsim_dev_port_debugfs_exit(struct nsim_dev_port *nsim_dev_port) 2248320d145SJiri Pirko { 2258320d145SJiri Pirko debugfs_remove_recursive(nsim_dev_port->ddir); 2268320d145SJiri Pirko } 2278320d145SJiri Pirko 2288fb4bc6fSJiri Pirko static int nsim_dev_resources_register(struct devlink *devlink) 2298fb4bc6fSJiri Pirko { 2308fb4bc6fSJiri Pirko struct devlink_resource_size_params params = { 2318fb4bc6fSJiri Pirko .size_max = (u64)-1, 2328fb4bc6fSJiri Pirko .size_granularity = 1, 2338fb4bc6fSJiri Pirko .unit = DEVLINK_RESOURCE_UNIT_ENTRY 2348fb4bc6fSJiri Pirko }; 2358fb4bc6fSJiri Pirko int err; 2368fb4bc6fSJiri Pirko 2378fb4bc6fSJiri Pirko /* Resources for IPv4 */ 2388fb4bc6fSJiri Pirko err = devlink_resource_register(devlink, "IPv4", (u64)-1, 2398fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4, 2408fb4bc6fSJiri Pirko DEVLINK_RESOURCE_ID_PARENT_TOP, 2418fb4bc6fSJiri Pirko ¶ms); 2428fb4bc6fSJiri Pirko if (err) { 2438fb4bc6fSJiri Pirko pr_err("Failed to register IPv4 top resource\n"); 2448fb4bc6fSJiri Pirko goto out; 2458fb4bc6fSJiri Pirko } 2468fb4bc6fSJiri Pirko 247a5facc4cSJiri Pirko err = devlink_resource_register(devlink, "fib", (u64)-1, 2488fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4_FIB, 2498fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4, ¶ms); 2508fb4bc6fSJiri Pirko if (err) { 2518fb4bc6fSJiri Pirko pr_err("Failed to register IPv4 FIB resource\n"); 2528fb4bc6fSJiri Pirko return err; 2538fb4bc6fSJiri Pirko } 2548fb4bc6fSJiri Pirko 255a5facc4cSJiri Pirko err = devlink_resource_register(devlink, "fib-rules", (u64)-1, 2568fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4_FIB_RULES, 2578fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4, ¶ms); 2588fb4bc6fSJiri Pirko if (err) { 2598fb4bc6fSJiri Pirko pr_err("Failed to register IPv4 FIB rules resource\n"); 2608fb4bc6fSJiri Pirko return err; 2618fb4bc6fSJiri Pirko } 2628fb4bc6fSJiri Pirko 2638fb4bc6fSJiri Pirko /* Resources for IPv6 */ 2648fb4bc6fSJiri Pirko err = devlink_resource_register(devlink, "IPv6", (u64)-1, 2658fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6, 2668fb4bc6fSJiri Pirko DEVLINK_RESOURCE_ID_PARENT_TOP, 2678fb4bc6fSJiri Pirko ¶ms); 2688fb4bc6fSJiri Pirko if (err) { 2698fb4bc6fSJiri Pirko pr_err("Failed to register IPv6 top resource\n"); 2708fb4bc6fSJiri Pirko goto out; 2718fb4bc6fSJiri Pirko } 2728fb4bc6fSJiri Pirko 273a5facc4cSJiri Pirko err = devlink_resource_register(devlink, "fib", (u64)-1, 2748fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6_FIB, 2758fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6, ¶ms); 2768fb4bc6fSJiri Pirko if (err) { 2778fb4bc6fSJiri Pirko pr_err("Failed to register IPv6 FIB resource\n"); 2788fb4bc6fSJiri Pirko return err; 2798fb4bc6fSJiri Pirko } 2808fb4bc6fSJiri Pirko 281a5facc4cSJiri Pirko err = devlink_resource_register(devlink, "fib-rules", (u64)-1, 2828fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6_FIB_RULES, 2838fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6, ¶ms); 2848fb4bc6fSJiri Pirko if (err) { 2858fb4bc6fSJiri Pirko pr_err("Failed to register IPv6 FIB rules resource\n"); 2868fb4bc6fSJiri Pirko return err; 2878fb4bc6fSJiri Pirko } 2888fb4bc6fSJiri Pirko 2898fb4bc6fSJiri Pirko out: 2908fb4bc6fSJiri Pirko return err; 2918fb4bc6fSJiri Pirko } 2928fb4bc6fSJiri Pirko 293150e8f8aSJiri Pirko enum nsim_devlink_param_id { 294150e8f8aSJiri Pirko NSIM_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, 295150e8f8aSJiri Pirko NSIM_DEVLINK_PARAM_ID_TEST1, 296150e8f8aSJiri Pirko }; 297150e8f8aSJiri Pirko 298150e8f8aSJiri Pirko static const struct devlink_param nsim_devlink_params[] = { 299150e8f8aSJiri Pirko DEVLINK_PARAM_GENERIC(MAX_MACS, 300150e8f8aSJiri Pirko BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), 301150e8f8aSJiri Pirko NULL, NULL, NULL), 302150e8f8aSJiri Pirko DEVLINK_PARAM_DRIVER(NSIM_DEVLINK_PARAM_ID_TEST1, 303150e8f8aSJiri Pirko "test1", DEVLINK_PARAM_TYPE_BOOL, 304150e8f8aSJiri Pirko BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), 305150e8f8aSJiri Pirko NULL, NULL, NULL), 306150e8f8aSJiri Pirko }; 307150e8f8aSJiri Pirko 308150e8f8aSJiri Pirko static void nsim_devlink_set_params_init_values(struct nsim_dev *nsim_dev, 309150e8f8aSJiri Pirko struct devlink *devlink) 310150e8f8aSJiri Pirko { 311150e8f8aSJiri Pirko union devlink_param_value value; 312150e8f8aSJiri Pirko 313150e8f8aSJiri Pirko value.vu32 = nsim_dev->max_macs; 314150e8f8aSJiri Pirko devlink_param_driverinit_value_set(devlink, 315150e8f8aSJiri Pirko DEVLINK_PARAM_GENERIC_ID_MAX_MACS, 316150e8f8aSJiri Pirko value); 317150e8f8aSJiri Pirko value.vbool = nsim_dev->test1; 318150e8f8aSJiri Pirko devlink_param_driverinit_value_set(devlink, 319150e8f8aSJiri Pirko NSIM_DEVLINK_PARAM_ID_TEST1, 320150e8f8aSJiri Pirko value); 321150e8f8aSJiri Pirko } 322150e8f8aSJiri Pirko 323150e8f8aSJiri Pirko static void nsim_devlink_param_load_driverinit_values(struct devlink *devlink) 324150e8f8aSJiri Pirko { 325150e8f8aSJiri Pirko struct nsim_dev *nsim_dev = devlink_priv(devlink); 326150e8f8aSJiri Pirko union devlink_param_value saved_value; 327150e8f8aSJiri Pirko int err; 328150e8f8aSJiri Pirko 329150e8f8aSJiri Pirko err = devlink_param_driverinit_value_get(devlink, 330150e8f8aSJiri Pirko DEVLINK_PARAM_GENERIC_ID_MAX_MACS, 331150e8f8aSJiri Pirko &saved_value); 332150e8f8aSJiri Pirko if (!err) 333150e8f8aSJiri Pirko nsim_dev->max_macs = saved_value.vu32; 334150e8f8aSJiri Pirko err = devlink_param_driverinit_value_get(devlink, 335150e8f8aSJiri Pirko NSIM_DEVLINK_PARAM_ID_TEST1, 336150e8f8aSJiri Pirko &saved_value); 337150e8f8aSJiri Pirko if (!err) 338150e8f8aSJiri Pirko nsim_dev->test1 = saved_value.vbool; 339150e8f8aSJiri Pirko } 340150e8f8aSJiri Pirko 3414418f862SJiri Pirko #define NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX 16 3424418f862SJiri Pirko 3434418f862SJiri Pirko static int nsim_dev_dummy_region_init(struct nsim_dev *nsim_dev, 3444418f862SJiri Pirko struct devlink *devlink) 3454418f862SJiri Pirko { 3464418f862SJiri Pirko nsim_dev->dummy_region = 3474418f862SJiri Pirko devlink_region_create(devlink, "dummy", 3484418f862SJiri Pirko NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX, 3494418f862SJiri Pirko NSIM_DEV_DUMMY_REGION_SIZE); 3504418f862SJiri Pirko return PTR_ERR_OR_ZERO(nsim_dev->dummy_region); 3514418f862SJiri Pirko } 3524418f862SJiri Pirko 3534418f862SJiri Pirko static void nsim_dev_dummy_region_exit(struct nsim_dev *nsim_dev) 3544418f862SJiri Pirko { 3554418f862SJiri Pirko devlink_region_destroy(nsim_dev->dummy_region); 3564418f862SJiri Pirko } 3574418f862SJiri Pirko 358da58f90fSIdo Schimmel struct nsim_trap_item { 359da58f90fSIdo Schimmel void *trap_ctx; 360da58f90fSIdo Schimmel enum devlink_trap_action action; 361da58f90fSIdo Schimmel }; 362da58f90fSIdo Schimmel 363da58f90fSIdo Schimmel struct nsim_trap_data { 364da58f90fSIdo Schimmel struct delayed_work trap_report_dw; 365da58f90fSIdo Schimmel struct nsim_trap_item *trap_items_arr; 366da58f90fSIdo Schimmel struct nsim_dev *nsim_dev; 367da58f90fSIdo Schimmel spinlock_t trap_lock; /* Protects trap_items_arr */ 368da58f90fSIdo Schimmel }; 369da58f90fSIdo Schimmel 3709e087457SIdo Schimmel /* All driver-specific traps must be documented in 37104e4272cSJacob Keller * Documentation/networking/devlink/netdevsim.rst 3729e087457SIdo Schimmel */ 373da58f90fSIdo Schimmel enum { 374da58f90fSIdo Schimmel NSIM_TRAP_ID_BASE = DEVLINK_TRAP_GENERIC_ID_MAX, 375da58f90fSIdo Schimmel NSIM_TRAP_ID_FID_MISS, 376da58f90fSIdo Schimmel }; 377da58f90fSIdo Schimmel 378da58f90fSIdo Schimmel #define NSIM_TRAP_NAME_FID_MISS "fid_miss" 379da58f90fSIdo Schimmel 380da58f90fSIdo Schimmel #define NSIM_TRAP_METADATA DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT 381da58f90fSIdo Schimmel 382da58f90fSIdo Schimmel #define NSIM_TRAP_DROP(_id, _group_id) \ 383da58f90fSIdo Schimmel DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \ 384da58f90fSIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(_group_id), \ 385da58f90fSIdo Schimmel NSIM_TRAP_METADATA) 386*d3cbb907SJiri Pirko #define NSIM_TRAP_DROP_EXT(_id, _group_id, _metadata) \ 387*d3cbb907SJiri Pirko DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \ 388*d3cbb907SJiri Pirko DEVLINK_TRAP_GROUP_GENERIC(_group_id), \ 389*d3cbb907SJiri Pirko NSIM_TRAP_METADATA | (_metadata)) 390da58f90fSIdo Schimmel #define NSIM_TRAP_EXCEPTION(_id, _group_id) \ 391da58f90fSIdo Schimmel DEVLINK_TRAP_GENERIC(EXCEPTION, TRAP, _id, \ 392da58f90fSIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(_group_id), \ 393da58f90fSIdo Schimmel NSIM_TRAP_METADATA) 394da58f90fSIdo Schimmel #define NSIM_TRAP_DRIVER_EXCEPTION(_id, _group_id) \ 395da58f90fSIdo Schimmel DEVLINK_TRAP_DRIVER(EXCEPTION, TRAP, NSIM_TRAP_ID_##_id, \ 396da58f90fSIdo Schimmel NSIM_TRAP_NAME_##_id, \ 397da58f90fSIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(_group_id), \ 398da58f90fSIdo Schimmel NSIM_TRAP_METADATA) 399da58f90fSIdo Schimmel 400da58f90fSIdo Schimmel static const struct devlink_trap nsim_traps_arr[] = { 401da58f90fSIdo Schimmel NSIM_TRAP_DROP(SMAC_MC, L2_DROPS), 402da58f90fSIdo Schimmel NSIM_TRAP_DROP(VLAN_TAG_MISMATCH, L2_DROPS), 403da58f90fSIdo Schimmel NSIM_TRAP_DROP(INGRESS_VLAN_FILTER, L2_DROPS), 404da58f90fSIdo Schimmel NSIM_TRAP_DROP(INGRESS_STP_FILTER, L2_DROPS), 405da58f90fSIdo Schimmel NSIM_TRAP_DROP(EMPTY_TX_LIST, L2_DROPS), 406da58f90fSIdo Schimmel NSIM_TRAP_DROP(PORT_LOOPBACK_FILTER, L2_DROPS), 407da58f90fSIdo Schimmel NSIM_TRAP_DRIVER_EXCEPTION(FID_MISS, L2_DROPS), 408da58f90fSIdo Schimmel NSIM_TRAP_DROP(BLACKHOLE_ROUTE, L3_DROPS), 409da58f90fSIdo Schimmel NSIM_TRAP_EXCEPTION(TTL_ERROR, L3_DROPS), 410da58f90fSIdo Schimmel NSIM_TRAP_DROP(TAIL_DROP, BUFFER_DROPS), 411*d3cbb907SJiri Pirko NSIM_TRAP_DROP_EXT(INGRESS_FLOW_ACTION_DROP, ACL_DROPS, 412*d3cbb907SJiri Pirko DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE), 413*d3cbb907SJiri Pirko NSIM_TRAP_DROP_EXT(EGRESS_FLOW_ACTION_DROP, ACL_DROPS, 414*d3cbb907SJiri Pirko DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE), 415da58f90fSIdo Schimmel }; 416da58f90fSIdo Schimmel 417da58f90fSIdo Schimmel #define NSIM_TRAP_L4_DATA_LEN 100 418da58f90fSIdo Schimmel 419da58f90fSIdo Schimmel static struct sk_buff *nsim_dev_trap_skb_build(void) 420da58f90fSIdo Schimmel { 421da58f90fSIdo Schimmel int tot_len, data_len = NSIM_TRAP_L4_DATA_LEN; 422da58f90fSIdo Schimmel struct sk_buff *skb; 423da58f90fSIdo Schimmel struct udphdr *udph; 424da58f90fSIdo Schimmel struct ethhdr *eth; 425da58f90fSIdo Schimmel struct iphdr *iph; 426da58f90fSIdo Schimmel 427da58f90fSIdo Schimmel skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); 428da58f90fSIdo Schimmel if (!skb) 429da58f90fSIdo Schimmel return NULL; 430da58f90fSIdo Schimmel tot_len = sizeof(struct iphdr) + sizeof(struct udphdr) + data_len; 431da58f90fSIdo Schimmel 43258a406deSIdo Schimmel skb_reset_mac_header(skb); 433da58f90fSIdo Schimmel eth = skb_put(skb, sizeof(struct ethhdr)); 434da58f90fSIdo Schimmel eth_random_addr(eth->h_dest); 435da58f90fSIdo Schimmel eth_random_addr(eth->h_source); 436da58f90fSIdo Schimmel eth->h_proto = htons(ETH_P_IP); 437da58f90fSIdo Schimmel skb->protocol = htons(ETH_P_IP); 438da58f90fSIdo Schimmel 43958a406deSIdo Schimmel skb_set_network_header(skb, skb->len); 440da58f90fSIdo Schimmel iph = skb_put(skb, sizeof(struct iphdr)); 441da58f90fSIdo Schimmel iph->protocol = IPPROTO_UDP; 442da58f90fSIdo Schimmel iph->saddr = in_aton("192.0.2.1"); 443da58f90fSIdo Schimmel iph->daddr = in_aton("198.51.100.1"); 444da58f90fSIdo Schimmel iph->version = 0x4; 445da58f90fSIdo Schimmel iph->frag_off = 0; 446da58f90fSIdo Schimmel iph->ihl = 0x5; 447da58f90fSIdo Schimmel iph->tot_len = htons(tot_len); 448da58f90fSIdo Schimmel iph->ttl = 100; 449d9bd6d27SYueHaibing iph->check = 0; 450d9bd6d27SYueHaibing iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); 451da58f90fSIdo Schimmel 45258a406deSIdo Schimmel skb_set_transport_header(skb, skb->len); 453da58f90fSIdo Schimmel udph = skb_put_zero(skb, sizeof(struct udphdr) + data_len); 454da58f90fSIdo Schimmel get_random_bytes(&udph->source, sizeof(u16)); 455da58f90fSIdo Schimmel get_random_bytes(&udph->dest, sizeof(u16)); 456da58f90fSIdo Schimmel udph->len = htons(sizeof(struct udphdr) + data_len); 457da58f90fSIdo Schimmel 458da58f90fSIdo Schimmel return skb; 459da58f90fSIdo Schimmel } 460da58f90fSIdo Schimmel 461da58f90fSIdo Schimmel static void nsim_dev_trap_report(struct nsim_dev_port *nsim_dev_port) 462da58f90fSIdo Schimmel { 463da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = nsim_dev_port->ns->nsim_dev; 464da58f90fSIdo Schimmel struct devlink *devlink = priv_to_devlink(nsim_dev); 465da58f90fSIdo Schimmel struct nsim_trap_data *nsim_trap_data; 466da58f90fSIdo Schimmel int i; 467da58f90fSIdo Schimmel 468da58f90fSIdo Schimmel nsim_trap_data = nsim_dev->trap_data; 469da58f90fSIdo Schimmel 470da58f90fSIdo Schimmel spin_lock(&nsim_trap_data->trap_lock); 471da58f90fSIdo Schimmel for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) { 472*d3cbb907SJiri Pirko struct flow_action_cookie *fa_cookie = NULL; 473da58f90fSIdo Schimmel struct nsim_trap_item *nsim_trap_item; 474da58f90fSIdo Schimmel struct sk_buff *skb; 475*d3cbb907SJiri Pirko bool has_fa_cookie; 476*d3cbb907SJiri Pirko 477*d3cbb907SJiri Pirko has_fa_cookie = nsim_traps_arr[i].metadata_cap & 478*d3cbb907SJiri Pirko DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE; 479da58f90fSIdo Schimmel 480da58f90fSIdo Schimmel nsim_trap_item = &nsim_trap_data->trap_items_arr[i]; 481da58f90fSIdo Schimmel if (nsim_trap_item->action == DEVLINK_TRAP_ACTION_DROP) 482da58f90fSIdo Schimmel continue; 483da58f90fSIdo Schimmel 484da58f90fSIdo Schimmel skb = nsim_dev_trap_skb_build(); 485da58f90fSIdo Schimmel if (!skb) 486da58f90fSIdo Schimmel continue; 487da58f90fSIdo Schimmel skb->dev = nsim_dev_port->ns->netdev; 488da58f90fSIdo Schimmel 489da58f90fSIdo Schimmel /* Trapped packets are usually passed to devlink in softIRQ, 490da58f90fSIdo Schimmel * but in this case they are generated in a workqueue. Disable 491da58f90fSIdo Schimmel * softIRQs to prevent lockdep from complaining about 492da58f90fSIdo Schimmel * "incosistent lock state". 493da58f90fSIdo Schimmel */ 494*d3cbb907SJiri Pirko 495*d3cbb907SJiri Pirko spin_lock_bh(&nsim_dev->fa_cookie_lock); 496*d3cbb907SJiri Pirko fa_cookie = has_fa_cookie ? nsim_dev->fa_cookie : NULL; 497da58f90fSIdo Schimmel devlink_trap_report(devlink, skb, nsim_trap_item->trap_ctx, 498*d3cbb907SJiri Pirko &nsim_dev_port->devlink_port, fa_cookie); 499*d3cbb907SJiri Pirko spin_unlock_bh(&nsim_dev->fa_cookie_lock); 500da58f90fSIdo Schimmel consume_skb(skb); 501da58f90fSIdo Schimmel } 502da58f90fSIdo Schimmel spin_unlock(&nsim_trap_data->trap_lock); 503da58f90fSIdo Schimmel } 504da58f90fSIdo Schimmel 505da58f90fSIdo Schimmel #define NSIM_TRAP_REPORT_INTERVAL_MS 100 506da58f90fSIdo Schimmel 507da58f90fSIdo Schimmel static void nsim_dev_trap_report_work(struct work_struct *work) 508da58f90fSIdo Schimmel { 509da58f90fSIdo Schimmel struct nsim_trap_data *nsim_trap_data; 510da58f90fSIdo Schimmel struct nsim_dev_port *nsim_dev_port; 511da58f90fSIdo Schimmel struct nsim_dev *nsim_dev; 512da58f90fSIdo Schimmel 513da58f90fSIdo Schimmel nsim_trap_data = container_of(work, struct nsim_trap_data, 514da58f90fSIdo Schimmel trap_report_dw.work); 515da58f90fSIdo Schimmel nsim_dev = nsim_trap_data->nsim_dev; 516da58f90fSIdo Schimmel 517da58f90fSIdo Schimmel /* For each running port and enabled packet trap, generate a UDP 518da58f90fSIdo Schimmel * packet with a random 5-tuple and report it. 519da58f90fSIdo Schimmel */ 520da58f90fSIdo Schimmel mutex_lock(&nsim_dev->port_list_lock); 521da58f90fSIdo Schimmel list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) { 522da58f90fSIdo Schimmel if (!netif_running(nsim_dev_port->ns->netdev)) 523da58f90fSIdo Schimmel continue; 524da58f90fSIdo Schimmel 525da58f90fSIdo Schimmel nsim_dev_trap_report(nsim_dev_port); 526da58f90fSIdo Schimmel } 527da58f90fSIdo Schimmel mutex_unlock(&nsim_dev->port_list_lock); 528da58f90fSIdo Schimmel 529da58f90fSIdo Schimmel schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, 530da58f90fSIdo Schimmel msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS)); 531da58f90fSIdo Schimmel } 532da58f90fSIdo Schimmel 533da58f90fSIdo Schimmel static int nsim_dev_traps_init(struct devlink *devlink) 534da58f90fSIdo Schimmel { 535da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 536da58f90fSIdo Schimmel struct nsim_trap_data *nsim_trap_data; 537da58f90fSIdo Schimmel int err; 538da58f90fSIdo Schimmel 539da58f90fSIdo Schimmel nsim_trap_data = kzalloc(sizeof(*nsim_trap_data), GFP_KERNEL); 540da58f90fSIdo Schimmel if (!nsim_trap_data) 541da58f90fSIdo Schimmel return -ENOMEM; 542da58f90fSIdo Schimmel 543da58f90fSIdo Schimmel nsim_trap_data->trap_items_arr = kcalloc(ARRAY_SIZE(nsim_traps_arr), 544da58f90fSIdo Schimmel sizeof(struct nsim_trap_item), 545da58f90fSIdo Schimmel GFP_KERNEL); 546da58f90fSIdo Schimmel if (!nsim_trap_data->trap_items_arr) { 547da58f90fSIdo Schimmel err = -ENOMEM; 548da58f90fSIdo Schimmel goto err_trap_data_free; 549da58f90fSIdo Schimmel } 550da58f90fSIdo Schimmel 551da58f90fSIdo Schimmel /* The lock is used to protect the action state of the registered 552da58f90fSIdo Schimmel * traps. The value is written by user and read in delayed work when 553da58f90fSIdo Schimmel * iterating over all the traps. 554da58f90fSIdo Schimmel */ 555da58f90fSIdo Schimmel spin_lock_init(&nsim_trap_data->trap_lock); 556da58f90fSIdo Schimmel nsim_trap_data->nsim_dev = nsim_dev; 557da58f90fSIdo Schimmel nsim_dev->trap_data = nsim_trap_data; 558da58f90fSIdo Schimmel 559da58f90fSIdo Schimmel err = devlink_traps_register(devlink, nsim_traps_arr, 560da58f90fSIdo Schimmel ARRAY_SIZE(nsim_traps_arr), NULL); 561da58f90fSIdo Schimmel if (err) 562da58f90fSIdo Schimmel goto err_trap_items_free; 563da58f90fSIdo Schimmel 564da58f90fSIdo Schimmel INIT_DELAYED_WORK(&nsim_dev->trap_data->trap_report_dw, 565da58f90fSIdo Schimmel nsim_dev_trap_report_work); 566da58f90fSIdo Schimmel schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, 567da58f90fSIdo Schimmel msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS)); 568da58f90fSIdo Schimmel 569da58f90fSIdo Schimmel return 0; 570da58f90fSIdo Schimmel 571da58f90fSIdo Schimmel err_trap_items_free: 572da58f90fSIdo Schimmel kfree(nsim_trap_data->trap_items_arr); 573da58f90fSIdo Schimmel err_trap_data_free: 574da58f90fSIdo Schimmel kfree(nsim_trap_data); 575da58f90fSIdo Schimmel return err; 576da58f90fSIdo Schimmel } 577da58f90fSIdo Schimmel 578da58f90fSIdo Schimmel static void nsim_dev_traps_exit(struct devlink *devlink) 579da58f90fSIdo Schimmel { 580da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 581da58f90fSIdo Schimmel 582da58f90fSIdo Schimmel cancel_delayed_work_sync(&nsim_dev->trap_data->trap_report_dw); 583da58f90fSIdo Schimmel devlink_traps_unregister(devlink, nsim_traps_arr, 584da58f90fSIdo Schimmel ARRAY_SIZE(nsim_traps_arr)); 585da58f90fSIdo Schimmel kfree(nsim_dev->trap_data->trap_items_arr); 586da58f90fSIdo Schimmel kfree(nsim_dev->trap_data); 587da58f90fSIdo Schimmel } 588da58f90fSIdo Schimmel 58975ba029fSJiri Pirko static int nsim_dev_reload_create(struct nsim_dev *nsim_dev, 59075ba029fSJiri Pirko struct netlink_ext_ack *extack); 59175ba029fSJiri Pirko static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev); 59275ba029fSJiri Pirko 593070c63f2SJiri Pirko static int nsim_dev_reload_down(struct devlink *devlink, bool netns_change, 59497691069SJiri Pirko struct netlink_ext_ack *extack) 59597691069SJiri Pirko { 59675ba029fSJiri Pirko struct nsim_dev *nsim_dev = devlink_priv(devlink); 59775ba029fSJiri Pirko 598155ddfc5SJiri Pirko if (nsim_dev->dont_allow_reload) { 599155ddfc5SJiri Pirko /* For testing purposes, user set debugfs dont_allow_reload 600155ddfc5SJiri Pirko * value to true. So forbid it. 601155ddfc5SJiri Pirko */ 602f9867b51SColin Ian King NL_SET_ERR_MSG_MOD(extack, "User forbid the reload for testing purposes"); 603155ddfc5SJiri Pirko return -EOPNOTSUPP; 604155ddfc5SJiri Pirko } 605155ddfc5SJiri Pirko 60675ba029fSJiri Pirko nsim_dev_reload_destroy(nsim_dev); 60797691069SJiri Pirko return 0; 60897691069SJiri Pirko } 60997691069SJiri Pirko 61097691069SJiri Pirko static int nsim_dev_reload_up(struct devlink *devlink, 6118fb4bc6fSJiri Pirko struct netlink_ext_ack *extack) 6128fb4bc6fSJiri Pirko { 613a5facc4cSJiri Pirko struct nsim_dev *nsim_dev = devlink_priv(devlink); 6148fb4bc6fSJiri Pirko 615155ddfc5SJiri Pirko if (nsim_dev->fail_reload) { 616155ddfc5SJiri Pirko /* For testing purposes, user set debugfs fail_reload 617155ddfc5SJiri Pirko * value to true. Fail right away. 618155ddfc5SJiri Pirko */ 619155ddfc5SJiri Pirko NL_SET_ERR_MSG_MOD(extack, "User setup the reload to fail for testing purposes"); 620155ddfc5SJiri Pirko return -EINVAL; 621155ddfc5SJiri Pirko } 622155ddfc5SJiri Pirko 62375ba029fSJiri Pirko return nsim_dev_reload_create(nsim_dev, extack); 6248fb4bc6fSJiri Pirko } 6258fb4bc6fSJiri Pirko 6268e23cc03SJiri Pirko static int nsim_dev_info_get(struct devlink *devlink, 6278e23cc03SJiri Pirko struct devlink_info_req *req, 6288e23cc03SJiri Pirko struct netlink_ext_ack *extack) 6298e23cc03SJiri Pirko { 6308e23cc03SJiri Pirko return devlink_info_driver_name_put(req, DRV_NAME); 6318e23cc03SJiri Pirko } 6328e23cc03SJiri Pirko 633fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_SIZE 500000 634fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_CHUNK_SIZE 1000 635fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_CHUNK_TIME_MS 10 636fa4dfc4aSJiri Pirko 637fa4dfc4aSJiri Pirko static int nsim_dev_flash_update(struct devlink *devlink, const char *file_name, 638fa4dfc4aSJiri Pirko const char *component, 639fa4dfc4aSJiri Pirko struct netlink_ext_ack *extack) 640fa4dfc4aSJiri Pirko { 641fa4dfc4aSJiri Pirko struct nsim_dev *nsim_dev = devlink_priv(devlink); 642fa4dfc4aSJiri Pirko int i; 643fa4dfc4aSJiri Pirko 644fa4dfc4aSJiri Pirko if (nsim_dev->fw_update_status) { 645fa4dfc4aSJiri Pirko devlink_flash_update_begin_notify(devlink); 646fa4dfc4aSJiri Pirko devlink_flash_update_status_notify(devlink, 647fa4dfc4aSJiri Pirko "Preparing to flash", 648fa4dfc4aSJiri Pirko component, 0, 0); 649fa4dfc4aSJiri Pirko } 650fa4dfc4aSJiri Pirko 651fa4dfc4aSJiri Pirko for (i = 0; i < NSIM_DEV_FLASH_SIZE / NSIM_DEV_FLASH_CHUNK_SIZE; i++) { 652fa4dfc4aSJiri Pirko if (nsim_dev->fw_update_status) 653fa4dfc4aSJiri Pirko devlink_flash_update_status_notify(devlink, "Flashing", 654fa4dfc4aSJiri Pirko component, 655fa4dfc4aSJiri Pirko i * NSIM_DEV_FLASH_CHUNK_SIZE, 656fa4dfc4aSJiri Pirko NSIM_DEV_FLASH_SIZE); 657fa4dfc4aSJiri Pirko msleep(NSIM_DEV_FLASH_CHUNK_TIME_MS); 658fa4dfc4aSJiri Pirko } 659fa4dfc4aSJiri Pirko 660fa4dfc4aSJiri Pirko if (nsim_dev->fw_update_status) { 661fa4dfc4aSJiri Pirko devlink_flash_update_status_notify(devlink, "Flashing", 662fa4dfc4aSJiri Pirko component, 663fa4dfc4aSJiri Pirko NSIM_DEV_FLASH_SIZE, 664fa4dfc4aSJiri Pirko NSIM_DEV_FLASH_SIZE); 665fa4dfc4aSJiri Pirko devlink_flash_update_status_notify(devlink, "Flashing done", 666fa4dfc4aSJiri Pirko component, 0, 0); 667fa4dfc4aSJiri Pirko devlink_flash_update_end_notify(devlink); 668fa4dfc4aSJiri Pirko } 669fa4dfc4aSJiri Pirko 670fa4dfc4aSJiri Pirko return 0; 671fa4dfc4aSJiri Pirko } 672fa4dfc4aSJiri Pirko 673da58f90fSIdo Schimmel static struct nsim_trap_item * 674da58f90fSIdo Schimmel nsim_dev_trap_item_lookup(struct nsim_dev *nsim_dev, u16 trap_id) 675da58f90fSIdo Schimmel { 676da58f90fSIdo Schimmel struct nsim_trap_data *nsim_trap_data = nsim_dev->trap_data; 677da58f90fSIdo Schimmel int i; 678da58f90fSIdo Schimmel 679da58f90fSIdo Schimmel for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) { 680da58f90fSIdo Schimmel if (nsim_traps_arr[i].id == trap_id) 681da58f90fSIdo Schimmel return &nsim_trap_data->trap_items_arr[i]; 682da58f90fSIdo Schimmel } 683da58f90fSIdo Schimmel 684da58f90fSIdo Schimmel return NULL; 685da58f90fSIdo Schimmel } 686da58f90fSIdo Schimmel 687da58f90fSIdo Schimmel static int nsim_dev_devlink_trap_init(struct devlink *devlink, 688da58f90fSIdo Schimmel const struct devlink_trap *trap, 689da58f90fSIdo Schimmel void *trap_ctx) 690da58f90fSIdo Schimmel { 691da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 692da58f90fSIdo Schimmel struct nsim_trap_item *nsim_trap_item; 693da58f90fSIdo Schimmel 694da58f90fSIdo Schimmel nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id); 695da58f90fSIdo Schimmel if (WARN_ON(!nsim_trap_item)) 696da58f90fSIdo Schimmel return -ENOENT; 697da58f90fSIdo Schimmel 698da58f90fSIdo Schimmel nsim_trap_item->trap_ctx = trap_ctx; 699da58f90fSIdo Schimmel nsim_trap_item->action = trap->init_action; 700da58f90fSIdo Schimmel 701da58f90fSIdo Schimmel return 0; 702da58f90fSIdo Schimmel } 703da58f90fSIdo Schimmel 704da58f90fSIdo Schimmel static int 705da58f90fSIdo Schimmel nsim_dev_devlink_trap_action_set(struct devlink *devlink, 706da58f90fSIdo Schimmel const struct devlink_trap *trap, 707da58f90fSIdo Schimmel enum devlink_trap_action action) 708da58f90fSIdo Schimmel { 709da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 710da58f90fSIdo Schimmel struct nsim_trap_item *nsim_trap_item; 711da58f90fSIdo Schimmel 712da58f90fSIdo Schimmel nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id); 713da58f90fSIdo Schimmel if (WARN_ON(!nsim_trap_item)) 714da58f90fSIdo Schimmel return -ENOENT; 715da58f90fSIdo Schimmel 716da58f90fSIdo Schimmel spin_lock(&nsim_dev->trap_data->trap_lock); 717da58f90fSIdo Schimmel nsim_trap_item->action = action; 718da58f90fSIdo Schimmel spin_unlock(&nsim_dev->trap_data->trap_lock); 719da58f90fSIdo Schimmel 720da58f90fSIdo Schimmel return 0; 721da58f90fSIdo Schimmel } 722da58f90fSIdo Schimmel 7238fb4bc6fSJiri Pirko static const struct devlink_ops nsim_dev_devlink_ops = { 72497691069SJiri Pirko .reload_down = nsim_dev_reload_down, 72597691069SJiri Pirko .reload_up = nsim_dev_reload_up, 7268e23cc03SJiri Pirko .info_get = nsim_dev_info_get, 727fa4dfc4aSJiri Pirko .flash_update = nsim_dev_flash_update, 728da58f90fSIdo Schimmel .trap_init = nsim_dev_devlink_trap_init, 729da58f90fSIdo Schimmel .trap_action_set = nsim_dev_devlink_trap_action_set, 7308fb4bc6fSJiri Pirko }; 7318fb4bc6fSJiri Pirko 732150e8f8aSJiri Pirko #define NSIM_DEV_MAX_MACS_DEFAULT 32 733150e8f8aSJiri Pirko #define NSIM_DEV_TEST1_DEFAULT true 734150e8f8aSJiri Pirko 735794b2c05SJiri Pirko static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, 736794b2c05SJiri Pirko unsigned int port_index) 7378320d145SJiri Pirko { 7388320d145SJiri Pirko struct nsim_dev_port *nsim_dev_port; 7398320d145SJiri Pirko struct devlink_port *devlink_port; 7408320d145SJiri Pirko int err; 7418320d145SJiri Pirko 7428320d145SJiri Pirko nsim_dev_port = kzalloc(sizeof(*nsim_dev_port), GFP_KERNEL); 7438320d145SJiri Pirko if (!nsim_dev_port) 7448320d145SJiri Pirko return -ENOMEM; 7458320d145SJiri Pirko nsim_dev_port->port_index = port_index; 7468320d145SJiri Pirko 7478320d145SJiri Pirko devlink_port = &nsim_dev_port->devlink_port; 7488320d145SJiri Pirko devlink_port_attrs_set(devlink_port, DEVLINK_PORT_FLAVOUR_PHYSICAL, 7498320d145SJiri Pirko port_index + 1, 0, 0, 7508320d145SJiri Pirko nsim_dev->switch_id.id, 7518320d145SJiri Pirko nsim_dev->switch_id.id_len); 7528320d145SJiri Pirko err = devlink_port_register(priv_to_devlink(nsim_dev), devlink_port, 7538320d145SJiri Pirko port_index); 7548320d145SJiri Pirko if (err) 7558320d145SJiri Pirko goto err_port_free; 7568320d145SJiri Pirko 7578320d145SJiri Pirko err = nsim_dev_port_debugfs_init(nsim_dev, nsim_dev_port); 7588320d145SJiri Pirko if (err) 7598320d145SJiri Pirko goto err_dl_port_unregister; 7608320d145SJiri Pirko 761e05b2d14SJiri Pirko nsim_dev_port->ns = nsim_create(nsim_dev, nsim_dev_port); 762e05b2d14SJiri Pirko if (IS_ERR(nsim_dev_port->ns)) { 763e05b2d14SJiri Pirko err = PTR_ERR(nsim_dev_port->ns); 764e05b2d14SJiri Pirko goto err_port_debugfs_exit; 765e05b2d14SJiri Pirko } 766e05b2d14SJiri Pirko 767e05b2d14SJiri Pirko devlink_port_type_eth_set(devlink_port, nsim_dev_port->ns->netdev); 7688320d145SJiri Pirko list_add(&nsim_dev_port->list, &nsim_dev->port_list); 7698320d145SJiri Pirko 7708320d145SJiri Pirko return 0; 7718320d145SJiri Pirko 772e05b2d14SJiri Pirko err_port_debugfs_exit: 773e05b2d14SJiri Pirko nsim_dev_port_debugfs_exit(nsim_dev_port); 7748320d145SJiri Pirko err_dl_port_unregister: 7758320d145SJiri Pirko devlink_port_unregister(devlink_port); 7768320d145SJiri Pirko err_port_free: 7778320d145SJiri Pirko kfree(nsim_dev_port); 7788320d145SJiri Pirko return err; 7798320d145SJiri Pirko } 7808320d145SJiri Pirko 781794b2c05SJiri Pirko static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port) 7828320d145SJiri Pirko { 7838320d145SJiri Pirko struct devlink_port *devlink_port = &nsim_dev_port->devlink_port; 7848320d145SJiri Pirko 7858320d145SJiri Pirko list_del(&nsim_dev_port->list); 786e05b2d14SJiri Pirko devlink_port_type_clear(devlink_port); 787e05b2d14SJiri Pirko nsim_destroy(nsim_dev_port->ns); 7888320d145SJiri Pirko nsim_dev_port_debugfs_exit(nsim_dev_port); 7898320d145SJiri Pirko devlink_port_unregister(devlink_port); 7908320d145SJiri Pirko kfree(nsim_dev_port); 7918320d145SJiri Pirko } 7928320d145SJiri Pirko 7938320d145SJiri Pirko static void nsim_dev_port_del_all(struct nsim_dev *nsim_dev) 7948320d145SJiri Pirko { 7958320d145SJiri Pirko struct nsim_dev_port *nsim_dev_port, *tmp; 7968320d145SJiri Pirko 7976d6f0383SIdo Schimmel mutex_lock(&nsim_dev->port_list_lock); 7988320d145SJiri Pirko list_for_each_entry_safe(nsim_dev_port, tmp, 7998320d145SJiri Pirko &nsim_dev->port_list, list) 800794b2c05SJiri Pirko __nsim_dev_port_del(nsim_dev_port); 8016d6f0383SIdo Schimmel mutex_unlock(&nsim_dev->port_list_lock); 8028320d145SJiri Pirko } 8038320d145SJiri Pirko 8047f36a77aSJiri Pirko static int nsim_dev_port_add_all(struct nsim_dev *nsim_dev, 8057f36a77aSJiri Pirko unsigned int port_count) 8068320d145SJiri Pirko { 8077f36a77aSJiri Pirko int i, err; 8088320d145SJiri Pirko 8097f36a77aSJiri Pirko for (i = 0; i < port_count; i++) { 810794b2c05SJiri Pirko err = __nsim_dev_port_add(nsim_dev, i); 8118320d145SJiri Pirko if (err) 8128320d145SJiri Pirko goto err_port_del_all; 8138320d145SJiri Pirko } 8148320d145SJiri Pirko return 0; 8158320d145SJiri Pirko 8168320d145SJiri Pirko err_port_del_all: 8178320d145SJiri Pirko nsim_dev_port_del_all(nsim_dev); 8188320d145SJiri Pirko return err; 8198320d145SJiri Pirko } 8208320d145SJiri Pirko 82175ba029fSJiri Pirko static int nsim_dev_reload_create(struct nsim_dev *nsim_dev, 82275ba029fSJiri Pirko struct netlink_ext_ack *extack) 82375ba029fSJiri Pirko { 82475ba029fSJiri Pirko struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev; 82575ba029fSJiri Pirko struct devlink *devlink; 82675ba029fSJiri Pirko int err; 82775ba029fSJiri Pirko 82875ba029fSJiri Pirko devlink = priv_to_devlink(nsim_dev); 82975ba029fSJiri Pirko nsim_dev = devlink_priv(devlink); 83075ba029fSJiri Pirko INIT_LIST_HEAD(&nsim_dev->port_list); 83175ba029fSJiri Pirko mutex_init(&nsim_dev->port_list_lock); 83275ba029fSJiri Pirko nsim_dev->fw_update_status = true; 83375ba029fSJiri Pirko 83475ba029fSJiri Pirko nsim_dev->fib_data = nsim_fib_create(devlink, extack); 83575ba029fSJiri Pirko if (IS_ERR(nsim_dev->fib_data)) 83675ba029fSJiri Pirko return PTR_ERR(nsim_dev->fib_data); 83775ba029fSJiri Pirko 83875ba029fSJiri Pirko nsim_devlink_param_load_driverinit_values(devlink); 83975ba029fSJiri Pirko 84075ba029fSJiri Pirko err = nsim_dev_dummy_region_init(nsim_dev, devlink); 84175ba029fSJiri Pirko if (err) 84275ba029fSJiri Pirko goto err_fib_destroy; 84375ba029fSJiri Pirko 84475ba029fSJiri Pirko err = nsim_dev_traps_init(devlink); 84575ba029fSJiri Pirko if (err) 84675ba029fSJiri Pirko goto err_dummy_region_exit; 84775ba029fSJiri Pirko 84882c93a87SJiri Pirko err = nsim_dev_health_init(nsim_dev, devlink); 84975ba029fSJiri Pirko if (err) 85075ba029fSJiri Pirko goto err_traps_exit; 85175ba029fSJiri Pirko 85282c93a87SJiri Pirko err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count); 85382c93a87SJiri Pirko if (err) 85482c93a87SJiri Pirko goto err_health_exit; 85582c93a87SJiri Pirko 8568526ad96STaehee Yoo nsim_dev->take_snapshot = debugfs_create_file("take_snapshot", 8578526ad96STaehee Yoo 0200, 8588526ad96STaehee Yoo nsim_dev->ddir, 8598526ad96STaehee Yoo nsim_dev, 8608526ad96STaehee Yoo &nsim_dev_take_snapshot_fops); 86175ba029fSJiri Pirko return 0; 86275ba029fSJiri Pirko 86382c93a87SJiri Pirko err_health_exit: 86482c93a87SJiri Pirko nsim_dev_health_exit(nsim_dev); 86575ba029fSJiri Pirko err_traps_exit: 86675ba029fSJiri Pirko nsim_dev_traps_exit(devlink); 86775ba029fSJiri Pirko err_dummy_region_exit: 86875ba029fSJiri Pirko nsim_dev_dummy_region_exit(nsim_dev); 86975ba029fSJiri Pirko err_fib_destroy: 87075ba029fSJiri Pirko nsim_fib_destroy(devlink, nsim_dev->fib_data); 87175ba029fSJiri Pirko return err; 87275ba029fSJiri Pirko } 87375ba029fSJiri Pirko 874bfcccfe7SJakub Kicinski int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev) 8757f36a77aSJiri Pirko { 8767f36a77aSJiri Pirko struct nsim_dev *nsim_dev; 8777f36a77aSJiri Pirko struct devlink *devlink; 8787f36a77aSJiri Pirko int err; 8797f36a77aSJiri Pirko 8807f36a77aSJiri Pirko devlink = devlink_alloc(&nsim_dev_devlink_ops, sizeof(*nsim_dev)); 8817f36a77aSJiri Pirko if (!devlink) 882bfcccfe7SJakub Kicinski return -ENOMEM; 8837b60027bSJiri Pirko devlink_net_set(devlink, nsim_bus_dev->initial_net); 8847f36a77aSJiri Pirko nsim_dev = devlink_priv(devlink); 8857f36a77aSJiri Pirko nsim_dev->nsim_bus_dev = nsim_bus_dev; 8867f36a77aSJiri Pirko nsim_dev->switch_id.id_len = sizeof(nsim_dev->switch_id.id); 8877f36a77aSJiri Pirko get_random_bytes(nsim_dev->switch_id.id, nsim_dev->switch_id.id_len); 8887f36a77aSJiri Pirko INIT_LIST_HEAD(&nsim_dev->port_list); 8897f36a77aSJiri Pirko mutex_init(&nsim_dev->port_list_lock); 8907f36a77aSJiri Pirko nsim_dev->fw_update_status = true; 8917f36a77aSJiri Pirko nsim_dev->max_macs = NSIM_DEV_MAX_MACS_DEFAULT; 8927f36a77aSJiri Pirko nsim_dev->test1 = NSIM_DEV_TEST1_DEFAULT; 893*d3cbb907SJiri Pirko spin_lock_init(&nsim_dev->fa_cookie_lock); 8947f36a77aSJiri Pirko 895bfcccfe7SJakub Kicinski dev_set_drvdata(&nsim_bus_dev->dev, nsim_dev); 896bfcccfe7SJakub Kicinski 8977f36a77aSJiri Pirko err = nsim_dev_resources_register(devlink); 8987f36a77aSJiri Pirko if (err) 8997f36a77aSJiri Pirko goto err_devlink_free; 9007f36a77aSJiri Pirko 90175ba029fSJiri Pirko nsim_dev->fib_data = nsim_fib_create(devlink, NULL); 9027f36a77aSJiri Pirko if (IS_ERR(nsim_dev->fib_data)) { 9037f36a77aSJiri Pirko err = PTR_ERR(nsim_dev->fib_data); 9047f36a77aSJiri Pirko goto err_resources_unregister; 9057f36a77aSJiri Pirko } 9067f36a77aSJiri Pirko 9077f36a77aSJiri Pirko err = devlink_register(devlink, &nsim_bus_dev->dev); 9087f36a77aSJiri Pirko if (err) 9097f36a77aSJiri Pirko goto err_fib_destroy; 9107f36a77aSJiri Pirko 9117f36a77aSJiri Pirko err = devlink_params_register(devlink, nsim_devlink_params, 9127f36a77aSJiri Pirko ARRAY_SIZE(nsim_devlink_params)); 9137f36a77aSJiri Pirko if (err) 9147f36a77aSJiri Pirko goto err_dl_unregister; 9157f36a77aSJiri Pirko nsim_devlink_set_params_init_values(nsim_dev, devlink); 9167f36a77aSJiri Pirko 9177f36a77aSJiri Pirko err = nsim_dev_dummy_region_init(nsim_dev, devlink); 9187f36a77aSJiri Pirko if (err) 9197f36a77aSJiri Pirko goto err_params_unregister; 9207f36a77aSJiri Pirko 9217f36a77aSJiri Pirko err = nsim_dev_traps_init(devlink); 9227f36a77aSJiri Pirko if (err) 9237f36a77aSJiri Pirko goto err_dummy_region_exit; 9247f36a77aSJiri Pirko 9257f36a77aSJiri Pirko err = nsim_dev_debugfs_init(nsim_dev); 9267f36a77aSJiri Pirko if (err) 9277f36a77aSJiri Pirko goto err_traps_exit; 9287f36a77aSJiri Pirko 92982c93a87SJiri Pirko err = nsim_dev_health_init(nsim_dev, devlink); 9307f36a77aSJiri Pirko if (err) 9317f36a77aSJiri Pirko goto err_debugfs_exit; 9327f36a77aSJiri Pirko 93382c93a87SJiri Pirko err = nsim_bpf_dev_init(nsim_dev); 93482c93a87SJiri Pirko if (err) 93582c93a87SJiri Pirko goto err_health_exit; 93682c93a87SJiri Pirko 9377f36a77aSJiri Pirko err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count); 9387f36a77aSJiri Pirko if (err) 9397f36a77aSJiri Pirko goto err_bpf_dev_exit; 9407f36a77aSJiri Pirko 9417f36a77aSJiri Pirko devlink_params_publish(devlink); 942a0c76345SJiri Pirko devlink_reload_enable(devlink); 943bfcccfe7SJakub Kicinski return 0; 9447f36a77aSJiri Pirko 9457f36a77aSJiri Pirko err_bpf_dev_exit: 9467f36a77aSJiri Pirko nsim_bpf_dev_exit(nsim_dev); 94782c93a87SJiri Pirko err_health_exit: 94882c93a87SJiri Pirko nsim_dev_health_exit(nsim_dev); 9497f36a77aSJiri Pirko err_debugfs_exit: 9507f36a77aSJiri Pirko nsim_dev_debugfs_exit(nsim_dev); 9517f36a77aSJiri Pirko err_traps_exit: 9527f36a77aSJiri Pirko nsim_dev_traps_exit(devlink); 9537f36a77aSJiri Pirko err_dummy_region_exit: 9547f36a77aSJiri Pirko nsim_dev_dummy_region_exit(nsim_dev); 9557f36a77aSJiri Pirko err_params_unregister: 9567f36a77aSJiri Pirko devlink_params_unregister(devlink, nsim_devlink_params, 9577f36a77aSJiri Pirko ARRAY_SIZE(nsim_devlink_params)); 9587f36a77aSJiri Pirko err_dl_unregister: 9597f36a77aSJiri Pirko devlink_unregister(devlink); 9607f36a77aSJiri Pirko err_fib_destroy: 9617f36a77aSJiri Pirko nsim_fib_destroy(devlink, nsim_dev->fib_data); 9627f36a77aSJiri Pirko err_resources_unregister: 9637f36a77aSJiri Pirko devlink_resources_unregister(devlink, NULL); 9647f36a77aSJiri Pirko err_devlink_free: 9657f36a77aSJiri Pirko devlink_free(devlink); 966bfcccfe7SJakub Kicinski return err; 9677f36a77aSJiri Pirko } 9687f36a77aSJiri Pirko 96975ba029fSJiri Pirko static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev) 97075ba029fSJiri Pirko { 97175ba029fSJiri Pirko struct devlink *devlink = priv_to_devlink(nsim_dev); 97275ba029fSJiri Pirko 97375ba029fSJiri Pirko if (devlink_is_reload_failed(devlink)) 97475ba029fSJiri Pirko return; 9758526ad96STaehee Yoo debugfs_remove(nsim_dev->take_snapshot); 97675ba029fSJiri Pirko nsim_dev_port_del_all(nsim_dev); 97782c93a87SJiri Pirko nsim_dev_health_exit(nsim_dev); 97875ba029fSJiri Pirko nsim_dev_traps_exit(devlink); 97975ba029fSJiri Pirko nsim_dev_dummy_region_exit(nsim_dev); 98075ba029fSJiri Pirko mutex_destroy(&nsim_dev->port_list_lock); 98175ba029fSJiri Pirko nsim_fib_destroy(devlink, nsim_dev->fib_data); 98275ba029fSJiri Pirko } 98375ba029fSJiri Pirko 984bfcccfe7SJakub Kicinski void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev) 9857f36a77aSJiri Pirko { 986bfcccfe7SJakub Kicinski struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); 9877f36a77aSJiri Pirko struct devlink *devlink = priv_to_devlink(nsim_dev); 9887f36a77aSJiri Pirko 989a0c76345SJiri Pirko devlink_reload_disable(devlink); 990a0c76345SJiri Pirko 99175ba029fSJiri Pirko nsim_dev_reload_destroy(nsim_dev); 99275ba029fSJiri Pirko 9937f36a77aSJiri Pirko nsim_bpf_dev_exit(nsim_dev); 9947f36a77aSJiri Pirko nsim_dev_debugfs_exit(nsim_dev); 9957f36a77aSJiri Pirko devlink_params_unregister(devlink, nsim_devlink_params, 9967f36a77aSJiri Pirko ARRAY_SIZE(nsim_devlink_params)); 9977f36a77aSJiri Pirko devlink_unregister(devlink); 9987f36a77aSJiri Pirko devlink_resources_unregister(devlink, NULL); 9997f36a77aSJiri Pirko devlink_free(devlink); 10007f36a77aSJiri Pirko } 10017f36a77aSJiri Pirko 1002794b2c05SJiri Pirko static struct nsim_dev_port * 1003794b2c05SJiri Pirko __nsim_dev_port_lookup(struct nsim_dev *nsim_dev, unsigned int port_index) 1004794b2c05SJiri Pirko { 1005794b2c05SJiri Pirko struct nsim_dev_port *nsim_dev_port; 1006794b2c05SJiri Pirko 1007794b2c05SJiri Pirko list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) 1008794b2c05SJiri Pirko if (nsim_dev_port->port_index == port_index) 1009794b2c05SJiri Pirko return nsim_dev_port; 1010794b2c05SJiri Pirko return NULL; 1011794b2c05SJiri Pirko } 1012794b2c05SJiri Pirko 1013794b2c05SJiri Pirko int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev, 1014794b2c05SJiri Pirko unsigned int port_index) 1015794b2c05SJiri Pirko { 1016794b2c05SJiri Pirko struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); 1017794b2c05SJiri Pirko int err; 1018794b2c05SJiri Pirko 1019794b2c05SJiri Pirko mutex_lock(&nsim_dev->port_list_lock); 1020794b2c05SJiri Pirko if (__nsim_dev_port_lookup(nsim_dev, port_index)) 1021794b2c05SJiri Pirko err = -EEXIST; 1022794b2c05SJiri Pirko else 1023794b2c05SJiri Pirko err = __nsim_dev_port_add(nsim_dev, port_index); 1024794b2c05SJiri Pirko mutex_unlock(&nsim_dev->port_list_lock); 1025794b2c05SJiri Pirko return err; 1026794b2c05SJiri Pirko } 1027794b2c05SJiri Pirko 1028794b2c05SJiri Pirko int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev, 1029794b2c05SJiri Pirko unsigned int port_index) 1030794b2c05SJiri Pirko { 1031794b2c05SJiri Pirko struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); 1032794b2c05SJiri Pirko struct nsim_dev_port *nsim_dev_port; 1033794b2c05SJiri Pirko int err = 0; 1034794b2c05SJiri Pirko 1035794b2c05SJiri Pirko mutex_lock(&nsim_dev->port_list_lock); 1036794b2c05SJiri Pirko nsim_dev_port = __nsim_dev_port_lookup(nsim_dev, port_index); 1037794b2c05SJiri Pirko if (!nsim_dev_port) 1038794b2c05SJiri Pirko err = -ENOENT; 1039794b2c05SJiri Pirko else 1040794b2c05SJiri Pirko __nsim_dev_port_del(nsim_dev_port); 1041794b2c05SJiri Pirko mutex_unlock(&nsim_dev->port_list_lock); 1042794b2c05SJiri Pirko return err; 1043794b2c05SJiri Pirko } 1044794b2c05SJiri Pirko 1045d514f41eSJiri Pirko int nsim_dev_init(void) 1046d514f41eSJiri Pirko { 1047ab1d0cc0SJiri Pirko nsim_dev_ddir = debugfs_create_dir(DRV_NAME, NULL); 104834611e69Skbuild test robot return PTR_ERR_OR_ZERO(nsim_dev_ddir); 1049d514f41eSJiri Pirko } 1050d514f41eSJiri Pirko 1051d514f41eSJiri Pirko void nsim_dev_exit(void) 1052d514f41eSJiri Pirko { 1053d514f41eSJiri Pirko debugfs_remove_recursive(nsim_dev_ddir); 1054d514f41eSJiri Pirko } 1055