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> 31d3cbb907SJiri 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 423fe0fd53SJacob Keller static int 43d4602a9fSAndrew Lunn nsim_dev_take_snapshot(struct devlink *devlink, 44d4602a9fSAndrew Lunn const struct devlink_region_ops *ops, 45d4602a9fSAndrew Lunn struct netlink_ext_ack *extack, 463fe0fd53SJacob Keller u8 **data) 474418f862SJiri Pirko { 484418f862SJiri Pirko void *dummy_data; 4912102436SJacob Keller 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 563fe0fd53SJacob Keller *data = dummy_data; 573fe0fd53SJacob Keller 583fe0fd53SJacob Keller return 0; 593fe0fd53SJacob Keller } 603fe0fd53SJacob Keller 613fe0fd53SJacob Keller static ssize_t nsim_dev_take_snapshot_write(struct file *file, 623fe0fd53SJacob Keller const char __user *data, 633fe0fd53SJacob Keller size_t count, loff_t *ppos) 643fe0fd53SJacob Keller { 653fe0fd53SJacob Keller struct nsim_dev *nsim_dev = file->private_data; 663fe0fd53SJacob Keller struct devlink *devlink; 673fe0fd53SJacob Keller u8 *dummy_data; 683fe0fd53SJacob Keller int err; 693fe0fd53SJacob Keller u32 id; 703fe0fd53SJacob Keller 713fe0fd53SJacob Keller devlink = priv_to_devlink(nsim_dev); 723fe0fd53SJacob Keller 73d4602a9fSAndrew Lunn err = nsim_dev_take_snapshot(devlink, NULL, NULL, &dummy_data); 743fe0fd53SJacob Keller if (err) 753fe0fd53SJacob Keller return err; 763fe0fd53SJacob Keller 7712102436SJacob Keller err = devlink_region_snapshot_id_get(devlink, &id); 787ef19d3bSJacob Keller if (err) { 797ef19d3bSJacob Keller pr_err("Failed to get snapshot id\n"); 803902baf9SGustavo A. R. Silva kfree(dummy_data); 817ef19d3bSJacob Keller return err; 827ef19d3bSJacob Keller } 834418f862SJiri Pirko err = devlink_region_snapshot_create(nsim_dev->dummy_region, 84a0a09f6bSJacob Keller dummy_data, id); 8512102436SJacob Keller devlink_region_snapshot_id_put(devlink, id); 864418f862SJiri Pirko if (err) { 874418f862SJiri Pirko pr_err("Failed to create region snapshot\n"); 884418f862SJiri Pirko kfree(dummy_data); 894418f862SJiri Pirko return err; 904418f862SJiri Pirko } 914418f862SJiri Pirko 924418f862SJiri Pirko return count; 934418f862SJiri Pirko } 944418f862SJiri Pirko 954418f862SJiri Pirko static const struct file_operations nsim_dev_take_snapshot_fops = { 964418f862SJiri Pirko .open = simple_open, 974418f862SJiri Pirko .write = nsim_dev_take_snapshot_write, 984418f862SJiri Pirko .llseek = generic_file_llseek, 99*a5bbcbf2STaehee Yoo .owner = THIS_MODULE, 1004418f862SJiri Pirko }; 1014418f862SJiri Pirko 102d3cbb907SJiri Pirko static ssize_t nsim_dev_trap_fa_cookie_read(struct file *file, 103d3cbb907SJiri Pirko char __user *data, 104d3cbb907SJiri Pirko size_t count, loff_t *ppos) 105d3cbb907SJiri Pirko { 106d3cbb907SJiri Pirko struct nsim_dev *nsim_dev = file->private_data; 107d3cbb907SJiri Pirko struct flow_action_cookie *fa_cookie; 108d3cbb907SJiri Pirko unsigned int buf_len; 109d3cbb907SJiri Pirko ssize_t ret; 110d3cbb907SJiri Pirko char *buf; 111d3cbb907SJiri Pirko 112d3cbb907SJiri Pirko spin_lock(&nsim_dev->fa_cookie_lock); 113d3cbb907SJiri Pirko fa_cookie = nsim_dev->fa_cookie; 114d3cbb907SJiri Pirko if (!fa_cookie) { 115d3cbb907SJiri Pirko ret = -EINVAL; 116d3cbb907SJiri Pirko goto errout; 117d3cbb907SJiri Pirko } 118d3cbb907SJiri Pirko buf_len = fa_cookie->cookie_len * 2; 119d3cbb907SJiri Pirko buf = kmalloc(buf_len, GFP_ATOMIC); 120d3cbb907SJiri Pirko if (!buf) { 121d3cbb907SJiri Pirko ret = -ENOMEM; 122d3cbb907SJiri Pirko goto errout; 123d3cbb907SJiri Pirko } 124d3cbb907SJiri Pirko bin2hex(buf, fa_cookie->cookie, fa_cookie->cookie_len); 125d3cbb907SJiri Pirko spin_unlock(&nsim_dev->fa_cookie_lock); 126d3cbb907SJiri Pirko 127d3cbb907SJiri Pirko ret = simple_read_from_buffer(data, count, ppos, buf, buf_len); 128d3cbb907SJiri Pirko 129d3cbb907SJiri Pirko kfree(buf); 130d3cbb907SJiri Pirko return ret; 131d3cbb907SJiri Pirko 132d3cbb907SJiri Pirko errout: 133d3cbb907SJiri Pirko spin_unlock(&nsim_dev->fa_cookie_lock); 134d3cbb907SJiri Pirko return ret; 135d3cbb907SJiri Pirko } 136d3cbb907SJiri Pirko 137d3cbb907SJiri Pirko static ssize_t nsim_dev_trap_fa_cookie_write(struct file *file, 138d3cbb907SJiri Pirko const char __user *data, 139d3cbb907SJiri Pirko size_t count, loff_t *ppos) 140d3cbb907SJiri Pirko { 141d3cbb907SJiri Pirko struct nsim_dev *nsim_dev = file->private_data; 142d3cbb907SJiri Pirko struct flow_action_cookie *fa_cookie; 143d3cbb907SJiri Pirko size_t cookie_len; 144d3cbb907SJiri Pirko ssize_t ret; 145d3cbb907SJiri Pirko char *buf; 146d3cbb907SJiri Pirko 147d3cbb907SJiri Pirko if (*ppos != 0) 148d3cbb907SJiri Pirko return -EINVAL; 149d3cbb907SJiri Pirko cookie_len = (count - 1) / 2; 150d3cbb907SJiri Pirko if ((count - 1) % 2) 151d3cbb907SJiri Pirko return -EINVAL; 152d3cbb907SJiri Pirko buf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); 153d3cbb907SJiri Pirko if (!buf) 154d3cbb907SJiri Pirko return -ENOMEM; 155d3cbb907SJiri Pirko 156d3cbb907SJiri Pirko ret = simple_write_to_buffer(buf, count, ppos, data, count); 157d3cbb907SJiri Pirko if (ret < 0) 158d3cbb907SJiri Pirko goto free_buf; 159d3cbb907SJiri Pirko 160d3cbb907SJiri Pirko fa_cookie = kmalloc(sizeof(*fa_cookie) + cookie_len, 161d3cbb907SJiri Pirko GFP_KERNEL | __GFP_NOWARN); 162d3cbb907SJiri Pirko if (!fa_cookie) { 163d3cbb907SJiri Pirko ret = -ENOMEM; 164d3cbb907SJiri Pirko goto free_buf; 165d3cbb907SJiri Pirko } 166d3cbb907SJiri Pirko 167d3cbb907SJiri Pirko fa_cookie->cookie_len = cookie_len; 168d3cbb907SJiri Pirko ret = hex2bin(fa_cookie->cookie, buf, cookie_len); 169d3cbb907SJiri Pirko if (ret) 170d3cbb907SJiri Pirko goto free_fa_cookie; 171d3cbb907SJiri Pirko kfree(buf); 172d3cbb907SJiri Pirko 173d3cbb907SJiri Pirko spin_lock(&nsim_dev->fa_cookie_lock); 174d3cbb907SJiri Pirko kfree(nsim_dev->fa_cookie); 175d3cbb907SJiri Pirko nsim_dev->fa_cookie = fa_cookie; 176d3cbb907SJiri Pirko spin_unlock(&nsim_dev->fa_cookie_lock); 177d3cbb907SJiri Pirko 178d3cbb907SJiri Pirko return count; 179d3cbb907SJiri Pirko 180d3cbb907SJiri Pirko free_fa_cookie: 181d3cbb907SJiri Pirko kfree(fa_cookie); 182d3cbb907SJiri Pirko free_buf: 183d3cbb907SJiri Pirko kfree(buf); 184d3cbb907SJiri Pirko return ret; 185d3cbb907SJiri Pirko } 186d3cbb907SJiri Pirko 187d3cbb907SJiri Pirko static const struct file_operations nsim_dev_trap_fa_cookie_fops = { 188d3cbb907SJiri Pirko .open = simple_open, 189d3cbb907SJiri Pirko .read = nsim_dev_trap_fa_cookie_read, 190d3cbb907SJiri Pirko .write = nsim_dev_trap_fa_cookie_write, 191d3cbb907SJiri Pirko .llseek = generic_file_llseek, 192*a5bbcbf2STaehee Yoo .owner = THIS_MODULE, 193d3cbb907SJiri Pirko }; 194d3cbb907SJiri Pirko 195d514f41eSJiri Pirko static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev) 196d514f41eSJiri Pirko { 1976fb8852bSTaehee Yoo char dev_ddir_name[sizeof(DRV_NAME) + 10]; 198d514f41eSJiri Pirko 199ab1d0cc0SJiri Pirko sprintf(dev_ddir_name, DRV_NAME "%u", nsim_dev->nsim_bus_dev->dev.id); 200d514f41eSJiri Pirko nsim_dev->ddir = debugfs_create_dir(dev_ddir_name, nsim_dev_ddir); 2016556ff32STaehee Yoo if (IS_ERR(nsim_dev->ddir)) 2026556ff32STaehee Yoo return PTR_ERR(nsim_dev->ddir); 203ab1d0cc0SJiri Pirko nsim_dev->ports_ddir = debugfs_create_dir("ports", nsim_dev->ddir); 2046556ff32STaehee Yoo if (IS_ERR(nsim_dev->ports_ddir)) 2056556ff32STaehee Yoo return PTR_ERR(nsim_dev->ports_ddir); 206fa4dfc4aSJiri Pirko debugfs_create_bool("fw_update_status", 0600, nsim_dev->ddir, 207fa4dfc4aSJiri Pirko &nsim_dev->fw_update_status); 208cbb58368SJacob Keller debugfs_create_u32("fw_update_overwrite_mask", 0600, nsim_dev->ddir, 209cbb58368SJacob Keller &nsim_dev->fw_update_overwrite_mask); 210150e8f8aSJiri Pirko debugfs_create_u32("max_macs", 0600, nsim_dev->ddir, 211150e8f8aSJiri Pirko &nsim_dev->max_macs); 212150e8f8aSJiri Pirko debugfs_create_bool("test1", 0600, nsim_dev->ddir, 213150e8f8aSJiri Pirko &nsim_dev->test1); 2148526ad96STaehee Yoo nsim_dev->take_snapshot = debugfs_create_file("take_snapshot", 2158526ad96STaehee Yoo 0200, 2168526ad96STaehee Yoo nsim_dev->ddir, 2178526ad96STaehee Yoo nsim_dev, 2184418f862SJiri Pirko &nsim_dev_take_snapshot_fops); 219155ddfc5SJiri Pirko debugfs_create_bool("dont_allow_reload", 0600, nsim_dev->ddir, 220155ddfc5SJiri Pirko &nsim_dev->dont_allow_reload); 221155ddfc5SJiri Pirko debugfs_create_bool("fail_reload", 0600, nsim_dev->ddir, 222155ddfc5SJiri Pirko &nsim_dev->fail_reload); 223d3cbb907SJiri Pirko debugfs_create_file("trap_flow_action_cookie", 0600, nsim_dev->ddir, 224d3cbb907SJiri Pirko nsim_dev, &nsim_dev_trap_fa_cookie_fops); 2250dc8249aSIdo Schimmel debugfs_create_bool("fail_trap_group_set", 0600, 2260dc8249aSIdo Schimmel nsim_dev->ddir, 2270dc8249aSIdo Schimmel &nsim_dev->fail_trap_group_set); 228ad188458SIdo Schimmel debugfs_create_bool("fail_trap_policer_set", 0600, 229ad188458SIdo Schimmel nsim_dev->ddir, 230ad188458SIdo Schimmel &nsim_dev->fail_trap_policer_set); 231ad188458SIdo Schimmel debugfs_create_bool("fail_trap_policer_counter_get", 0600, 232ad188458SIdo Schimmel nsim_dev->ddir, 233ad188458SIdo Schimmel &nsim_dev->fail_trap_policer_counter_get); 234424be63aSJakub Kicinski nsim_udp_tunnels_debugfs_create(nsim_dev); 235d514f41eSJiri Pirko return 0; 236d514f41eSJiri Pirko } 237d514f41eSJiri Pirko 238d514f41eSJiri Pirko static void nsim_dev_debugfs_exit(struct nsim_dev *nsim_dev) 239d514f41eSJiri Pirko { 240ab1d0cc0SJiri Pirko debugfs_remove_recursive(nsim_dev->ports_ddir); 241d514f41eSJiri Pirko debugfs_remove_recursive(nsim_dev->ddir); 242d514f41eSJiri Pirko } 243d514f41eSJiri Pirko 2448320d145SJiri Pirko static int nsim_dev_port_debugfs_init(struct nsim_dev *nsim_dev, 2458320d145SJiri Pirko struct nsim_dev_port *nsim_dev_port) 2468320d145SJiri Pirko { 2478320d145SJiri Pirko char port_ddir_name[16]; 2488320d145SJiri Pirko char dev_link_name[32]; 2498320d145SJiri Pirko 2508320d145SJiri Pirko sprintf(port_ddir_name, "%u", nsim_dev_port->port_index); 2518320d145SJiri Pirko nsim_dev_port->ddir = debugfs_create_dir(port_ddir_name, 2528320d145SJiri Pirko nsim_dev->ports_ddir); 2536556ff32STaehee Yoo if (IS_ERR(nsim_dev_port->ddir)) 2546556ff32STaehee Yoo return PTR_ERR(nsim_dev_port->ddir); 2558320d145SJiri Pirko 2568320d145SJiri Pirko sprintf(dev_link_name, "../../../" DRV_NAME "%u", 2578320d145SJiri Pirko nsim_dev->nsim_bus_dev->dev.id); 2588320d145SJiri Pirko debugfs_create_symlink("dev", nsim_dev_port->ddir, dev_link_name); 2598320d145SJiri Pirko 2608320d145SJiri Pirko return 0; 2618320d145SJiri Pirko } 2628320d145SJiri Pirko 2638320d145SJiri Pirko static void nsim_dev_port_debugfs_exit(struct nsim_dev_port *nsim_dev_port) 2648320d145SJiri Pirko { 2658320d145SJiri Pirko debugfs_remove_recursive(nsim_dev_port->ddir); 2668320d145SJiri Pirko } 2678320d145SJiri Pirko 2688fb4bc6fSJiri Pirko static int nsim_dev_resources_register(struct devlink *devlink) 2698fb4bc6fSJiri Pirko { 2708fb4bc6fSJiri Pirko struct devlink_resource_size_params params = { 2718fb4bc6fSJiri Pirko .size_max = (u64)-1, 2728fb4bc6fSJiri Pirko .size_granularity = 1, 2738fb4bc6fSJiri Pirko .unit = DEVLINK_RESOURCE_UNIT_ENTRY 2748fb4bc6fSJiri Pirko }; 2758fb4bc6fSJiri Pirko int err; 2768fb4bc6fSJiri Pirko 2778fb4bc6fSJiri Pirko /* Resources for IPv4 */ 2788fb4bc6fSJiri Pirko err = devlink_resource_register(devlink, "IPv4", (u64)-1, 2798fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4, 2808fb4bc6fSJiri Pirko DEVLINK_RESOURCE_ID_PARENT_TOP, 2818fb4bc6fSJiri Pirko ¶ms); 2828fb4bc6fSJiri Pirko if (err) { 2838fb4bc6fSJiri Pirko pr_err("Failed to register IPv4 top resource\n"); 2848fb4bc6fSJiri Pirko goto out; 2858fb4bc6fSJiri Pirko } 2868fb4bc6fSJiri Pirko 287a5facc4cSJiri Pirko err = devlink_resource_register(devlink, "fib", (u64)-1, 2888fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4_FIB, 2898fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4, ¶ms); 2908fb4bc6fSJiri Pirko if (err) { 2918fb4bc6fSJiri Pirko pr_err("Failed to register IPv4 FIB resource\n"); 2928fb4bc6fSJiri Pirko return err; 2938fb4bc6fSJiri Pirko } 2948fb4bc6fSJiri Pirko 295a5facc4cSJiri Pirko err = devlink_resource_register(devlink, "fib-rules", (u64)-1, 2968fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4_FIB_RULES, 2978fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4, ¶ms); 2988fb4bc6fSJiri Pirko if (err) { 2998fb4bc6fSJiri Pirko pr_err("Failed to register IPv4 FIB rules resource\n"); 3008fb4bc6fSJiri Pirko return err; 3018fb4bc6fSJiri Pirko } 3028fb4bc6fSJiri Pirko 3038fb4bc6fSJiri Pirko /* Resources for IPv6 */ 3048fb4bc6fSJiri Pirko err = devlink_resource_register(devlink, "IPv6", (u64)-1, 3058fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6, 3068fb4bc6fSJiri Pirko DEVLINK_RESOURCE_ID_PARENT_TOP, 3078fb4bc6fSJiri Pirko ¶ms); 3088fb4bc6fSJiri Pirko if (err) { 3098fb4bc6fSJiri Pirko pr_err("Failed to register IPv6 top resource\n"); 3108fb4bc6fSJiri Pirko goto out; 3118fb4bc6fSJiri Pirko } 3128fb4bc6fSJiri Pirko 313a5facc4cSJiri Pirko err = devlink_resource_register(devlink, "fib", (u64)-1, 3148fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6_FIB, 3158fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6, ¶ms); 3168fb4bc6fSJiri Pirko if (err) { 3178fb4bc6fSJiri Pirko pr_err("Failed to register IPv6 FIB resource\n"); 3188fb4bc6fSJiri Pirko return err; 3198fb4bc6fSJiri Pirko } 3208fb4bc6fSJiri Pirko 321a5facc4cSJiri Pirko err = devlink_resource_register(devlink, "fib-rules", (u64)-1, 3228fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6_FIB_RULES, 3238fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6, ¶ms); 3248fb4bc6fSJiri Pirko if (err) { 3258fb4bc6fSJiri Pirko pr_err("Failed to register IPv6 FIB rules resource\n"); 3268fb4bc6fSJiri Pirko return err; 3278fb4bc6fSJiri Pirko } 3288fb4bc6fSJiri Pirko 3298fb4bc6fSJiri Pirko out: 3308fb4bc6fSJiri Pirko return err; 3318fb4bc6fSJiri Pirko } 3328fb4bc6fSJiri Pirko 333150e8f8aSJiri Pirko enum nsim_devlink_param_id { 334150e8f8aSJiri Pirko NSIM_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, 335150e8f8aSJiri Pirko NSIM_DEVLINK_PARAM_ID_TEST1, 336150e8f8aSJiri Pirko }; 337150e8f8aSJiri Pirko 338150e8f8aSJiri Pirko static const struct devlink_param nsim_devlink_params[] = { 339150e8f8aSJiri Pirko DEVLINK_PARAM_GENERIC(MAX_MACS, 340150e8f8aSJiri Pirko BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), 341150e8f8aSJiri Pirko NULL, NULL, NULL), 342150e8f8aSJiri Pirko DEVLINK_PARAM_DRIVER(NSIM_DEVLINK_PARAM_ID_TEST1, 343150e8f8aSJiri Pirko "test1", DEVLINK_PARAM_TYPE_BOOL, 344150e8f8aSJiri Pirko BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), 345150e8f8aSJiri Pirko NULL, NULL, NULL), 346150e8f8aSJiri Pirko }; 347150e8f8aSJiri Pirko 348150e8f8aSJiri Pirko static void nsim_devlink_set_params_init_values(struct nsim_dev *nsim_dev, 349150e8f8aSJiri Pirko struct devlink *devlink) 350150e8f8aSJiri Pirko { 351150e8f8aSJiri Pirko union devlink_param_value value; 352150e8f8aSJiri Pirko 353150e8f8aSJiri Pirko value.vu32 = nsim_dev->max_macs; 354150e8f8aSJiri Pirko devlink_param_driverinit_value_set(devlink, 355150e8f8aSJiri Pirko DEVLINK_PARAM_GENERIC_ID_MAX_MACS, 356150e8f8aSJiri Pirko value); 357150e8f8aSJiri Pirko value.vbool = nsim_dev->test1; 358150e8f8aSJiri Pirko devlink_param_driverinit_value_set(devlink, 359150e8f8aSJiri Pirko NSIM_DEVLINK_PARAM_ID_TEST1, 360150e8f8aSJiri Pirko value); 361150e8f8aSJiri Pirko } 362150e8f8aSJiri Pirko 363150e8f8aSJiri Pirko static void nsim_devlink_param_load_driverinit_values(struct devlink *devlink) 364150e8f8aSJiri Pirko { 365150e8f8aSJiri Pirko struct nsim_dev *nsim_dev = devlink_priv(devlink); 366150e8f8aSJiri Pirko union devlink_param_value saved_value; 367150e8f8aSJiri Pirko int err; 368150e8f8aSJiri Pirko 369150e8f8aSJiri Pirko err = devlink_param_driverinit_value_get(devlink, 370150e8f8aSJiri Pirko DEVLINK_PARAM_GENERIC_ID_MAX_MACS, 371150e8f8aSJiri Pirko &saved_value); 372150e8f8aSJiri Pirko if (!err) 373150e8f8aSJiri Pirko nsim_dev->max_macs = saved_value.vu32; 374150e8f8aSJiri Pirko err = devlink_param_driverinit_value_get(devlink, 375150e8f8aSJiri Pirko NSIM_DEVLINK_PARAM_ID_TEST1, 376150e8f8aSJiri Pirko &saved_value); 377150e8f8aSJiri Pirko if (!err) 378150e8f8aSJiri Pirko nsim_dev->test1 = saved_value.vbool; 379150e8f8aSJiri Pirko } 380150e8f8aSJiri Pirko 3814418f862SJiri Pirko #define NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX 16 3824418f862SJiri Pirko 383e8937681SJacob Keller static const struct devlink_region_ops dummy_region_ops = { 384e8937681SJacob Keller .name = "dummy", 385a0a09f6bSJacob Keller .destructor = &kfree, 3863fe0fd53SJacob Keller .snapshot = nsim_dev_take_snapshot, 387e8937681SJacob Keller }; 388e8937681SJacob Keller 3894418f862SJiri Pirko static int nsim_dev_dummy_region_init(struct nsim_dev *nsim_dev, 3904418f862SJiri Pirko struct devlink *devlink) 3914418f862SJiri Pirko { 3924418f862SJiri Pirko nsim_dev->dummy_region = 393e8937681SJacob Keller devlink_region_create(devlink, &dummy_region_ops, 3944418f862SJiri Pirko NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX, 3954418f862SJiri Pirko NSIM_DEV_DUMMY_REGION_SIZE); 3964418f862SJiri Pirko return PTR_ERR_OR_ZERO(nsim_dev->dummy_region); 3974418f862SJiri Pirko } 3984418f862SJiri Pirko 3994418f862SJiri Pirko static void nsim_dev_dummy_region_exit(struct nsim_dev *nsim_dev) 4004418f862SJiri Pirko { 4014418f862SJiri Pirko devlink_region_destroy(nsim_dev->dummy_region); 4024418f862SJiri Pirko } 4034418f862SJiri Pirko 404da58f90fSIdo Schimmel struct nsim_trap_item { 405da58f90fSIdo Schimmel void *trap_ctx; 406da58f90fSIdo Schimmel enum devlink_trap_action action; 407da58f90fSIdo Schimmel }; 408da58f90fSIdo Schimmel 409da58f90fSIdo Schimmel struct nsim_trap_data { 410da58f90fSIdo Schimmel struct delayed_work trap_report_dw; 411da58f90fSIdo Schimmel struct nsim_trap_item *trap_items_arr; 412ad188458SIdo Schimmel u64 *trap_policers_cnt_arr; 413da58f90fSIdo Schimmel struct nsim_dev *nsim_dev; 414da58f90fSIdo Schimmel spinlock_t trap_lock; /* Protects trap_items_arr */ 415da58f90fSIdo Schimmel }; 416da58f90fSIdo Schimmel 4179e087457SIdo Schimmel /* All driver-specific traps must be documented in 41804e4272cSJacob Keller * Documentation/networking/devlink/netdevsim.rst 4199e087457SIdo Schimmel */ 420da58f90fSIdo Schimmel enum { 421da58f90fSIdo Schimmel NSIM_TRAP_ID_BASE = DEVLINK_TRAP_GENERIC_ID_MAX, 422da58f90fSIdo Schimmel NSIM_TRAP_ID_FID_MISS, 423da58f90fSIdo Schimmel }; 424da58f90fSIdo Schimmel 425da58f90fSIdo Schimmel #define NSIM_TRAP_NAME_FID_MISS "fid_miss" 426da58f90fSIdo Schimmel 427da58f90fSIdo Schimmel #define NSIM_TRAP_METADATA DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT 428da58f90fSIdo Schimmel 429da58f90fSIdo Schimmel #define NSIM_TRAP_DROP(_id, _group_id) \ 430da58f90fSIdo Schimmel DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \ 431107f1678SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 432da58f90fSIdo Schimmel NSIM_TRAP_METADATA) 433d3cbb907SJiri Pirko #define NSIM_TRAP_DROP_EXT(_id, _group_id, _metadata) \ 434d3cbb907SJiri Pirko DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \ 435107f1678SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 436d3cbb907SJiri Pirko NSIM_TRAP_METADATA | (_metadata)) 437da58f90fSIdo Schimmel #define NSIM_TRAP_EXCEPTION(_id, _group_id) \ 438da58f90fSIdo Schimmel DEVLINK_TRAP_GENERIC(EXCEPTION, TRAP, _id, \ 439107f1678SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 440da58f90fSIdo Schimmel NSIM_TRAP_METADATA) 44118979367SIdo Schimmel #define NSIM_TRAP_CONTROL(_id, _group_id, _action) \ 44218979367SIdo Schimmel DEVLINK_TRAP_GENERIC(CONTROL, _action, _id, \ 44318979367SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 44418979367SIdo Schimmel NSIM_TRAP_METADATA) 445da58f90fSIdo Schimmel #define NSIM_TRAP_DRIVER_EXCEPTION(_id, _group_id) \ 446da58f90fSIdo Schimmel DEVLINK_TRAP_DRIVER(EXCEPTION, TRAP, NSIM_TRAP_ID_##_id, \ 447da58f90fSIdo Schimmel NSIM_TRAP_NAME_##_id, \ 448107f1678SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 449da58f90fSIdo Schimmel NSIM_TRAP_METADATA) 450da58f90fSIdo Schimmel 451ad188458SIdo Schimmel #define NSIM_DEV_TRAP_POLICER_MIN_RATE 1 452ad188458SIdo Schimmel #define NSIM_DEV_TRAP_POLICER_MAX_RATE 8000 453ad188458SIdo Schimmel #define NSIM_DEV_TRAP_POLICER_MIN_BURST 8 454ad188458SIdo Schimmel #define NSIM_DEV_TRAP_POLICER_MAX_BURST 65536 455ad188458SIdo Schimmel 456ad188458SIdo Schimmel #define NSIM_TRAP_POLICER(_id, _rate, _burst) \ 457ad188458SIdo Schimmel DEVLINK_TRAP_POLICER(_id, _rate, _burst, \ 458ad188458SIdo Schimmel NSIM_DEV_TRAP_POLICER_MAX_RATE, \ 459ad188458SIdo Schimmel NSIM_DEV_TRAP_POLICER_MIN_RATE, \ 460ad188458SIdo Schimmel NSIM_DEV_TRAP_POLICER_MAX_BURST, \ 461ad188458SIdo Schimmel NSIM_DEV_TRAP_POLICER_MIN_BURST) 462ad188458SIdo Schimmel 463ad188458SIdo Schimmel static const struct devlink_trap_policer nsim_trap_policers_arr[] = { 464ad188458SIdo Schimmel NSIM_TRAP_POLICER(1, 1000, 128), 465ad188458SIdo Schimmel NSIM_TRAP_POLICER(2, 2000, 256), 466ad188458SIdo Schimmel NSIM_TRAP_POLICER(3, 3000, 512), 467ad188458SIdo Schimmel }; 468ad188458SIdo Schimmel 469b29545d8SIdo Schimmel static const struct devlink_trap_group nsim_trap_groups_arr[] = { 470f9f54392SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS, 0), 471f9f54392SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(L3_DROPS, 1), 47285176f19SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(L3_EXCEPTIONS, 1), 473f9f54392SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(BUFFER_DROPS, 2), 474f9f54392SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(ACL_DROPS, 3), 47518979367SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(MC_SNOOPING, 3), 476b29545d8SIdo Schimmel }; 477b29545d8SIdo Schimmel 478da58f90fSIdo Schimmel static const struct devlink_trap nsim_traps_arr[] = { 479da58f90fSIdo Schimmel NSIM_TRAP_DROP(SMAC_MC, L2_DROPS), 480da58f90fSIdo Schimmel NSIM_TRAP_DROP(VLAN_TAG_MISMATCH, L2_DROPS), 481da58f90fSIdo Schimmel NSIM_TRAP_DROP(INGRESS_VLAN_FILTER, L2_DROPS), 482da58f90fSIdo Schimmel NSIM_TRAP_DROP(INGRESS_STP_FILTER, L2_DROPS), 483da58f90fSIdo Schimmel NSIM_TRAP_DROP(EMPTY_TX_LIST, L2_DROPS), 484da58f90fSIdo Schimmel NSIM_TRAP_DROP(PORT_LOOPBACK_FILTER, L2_DROPS), 485da58f90fSIdo Schimmel NSIM_TRAP_DRIVER_EXCEPTION(FID_MISS, L2_DROPS), 486da58f90fSIdo Schimmel NSIM_TRAP_DROP(BLACKHOLE_ROUTE, L3_DROPS), 48785176f19SIdo Schimmel NSIM_TRAP_EXCEPTION(TTL_ERROR, L3_EXCEPTIONS), 488da58f90fSIdo Schimmel NSIM_TRAP_DROP(TAIL_DROP, BUFFER_DROPS), 489d3cbb907SJiri Pirko NSIM_TRAP_DROP_EXT(INGRESS_FLOW_ACTION_DROP, ACL_DROPS, 490d3cbb907SJiri Pirko DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE), 491d3cbb907SJiri Pirko NSIM_TRAP_DROP_EXT(EGRESS_FLOW_ACTION_DROP, ACL_DROPS, 492d3cbb907SJiri Pirko DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE), 49318979367SIdo Schimmel NSIM_TRAP_CONTROL(IGMP_QUERY, MC_SNOOPING, MIRROR), 49418979367SIdo Schimmel NSIM_TRAP_CONTROL(IGMP_V1_REPORT, MC_SNOOPING, TRAP), 495da58f90fSIdo Schimmel }; 496da58f90fSIdo Schimmel 497da58f90fSIdo Schimmel #define NSIM_TRAP_L4_DATA_LEN 100 498da58f90fSIdo Schimmel 499da58f90fSIdo Schimmel static struct sk_buff *nsim_dev_trap_skb_build(void) 500da58f90fSIdo Schimmel { 501da58f90fSIdo Schimmel int tot_len, data_len = NSIM_TRAP_L4_DATA_LEN; 502da58f90fSIdo Schimmel struct sk_buff *skb; 503da58f90fSIdo Schimmel struct udphdr *udph; 504da58f90fSIdo Schimmel struct ethhdr *eth; 505da58f90fSIdo Schimmel struct iphdr *iph; 506da58f90fSIdo Schimmel 507da58f90fSIdo Schimmel skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); 508da58f90fSIdo Schimmel if (!skb) 509da58f90fSIdo Schimmel return NULL; 510da58f90fSIdo Schimmel tot_len = sizeof(struct iphdr) + sizeof(struct udphdr) + data_len; 511da58f90fSIdo Schimmel 51258a406deSIdo Schimmel skb_reset_mac_header(skb); 513da58f90fSIdo Schimmel eth = skb_put(skb, sizeof(struct ethhdr)); 514da58f90fSIdo Schimmel eth_random_addr(eth->h_dest); 515da58f90fSIdo Schimmel eth_random_addr(eth->h_source); 516da58f90fSIdo Schimmel eth->h_proto = htons(ETH_P_IP); 517da58f90fSIdo Schimmel skb->protocol = htons(ETH_P_IP); 518da58f90fSIdo Schimmel 51958a406deSIdo Schimmel skb_set_network_header(skb, skb->len); 520da58f90fSIdo Schimmel iph = skb_put(skb, sizeof(struct iphdr)); 521da58f90fSIdo Schimmel iph->protocol = IPPROTO_UDP; 522da58f90fSIdo Schimmel iph->saddr = in_aton("192.0.2.1"); 523da58f90fSIdo Schimmel iph->daddr = in_aton("198.51.100.1"); 524da58f90fSIdo Schimmel iph->version = 0x4; 525da58f90fSIdo Schimmel iph->frag_off = 0; 526da58f90fSIdo Schimmel iph->ihl = 0x5; 527da58f90fSIdo Schimmel iph->tot_len = htons(tot_len); 528da58f90fSIdo Schimmel iph->ttl = 100; 529d9bd6d27SYueHaibing iph->check = 0; 530d9bd6d27SYueHaibing iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); 531da58f90fSIdo Schimmel 53258a406deSIdo Schimmel skb_set_transport_header(skb, skb->len); 533da58f90fSIdo Schimmel udph = skb_put_zero(skb, sizeof(struct udphdr) + data_len); 534da58f90fSIdo Schimmel get_random_bytes(&udph->source, sizeof(u16)); 535da58f90fSIdo Schimmel get_random_bytes(&udph->dest, sizeof(u16)); 536da58f90fSIdo Schimmel udph->len = htons(sizeof(struct udphdr) + data_len); 537da58f90fSIdo Schimmel 538da58f90fSIdo Schimmel return skb; 539da58f90fSIdo Schimmel } 540da58f90fSIdo Schimmel 541da58f90fSIdo Schimmel static void nsim_dev_trap_report(struct nsim_dev_port *nsim_dev_port) 542da58f90fSIdo Schimmel { 543da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = nsim_dev_port->ns->nsim_dev; 544da58f90fSIdo Schimmel struct devlink *devlink = priv_to_devlink(nsim_dev); 545da58f90fSIdo Schimmel struct nsim_trap_data *nsim_trap_data; 546da58f90fSIdo Schimmel int i; 547da58f90fSIdo Schimmel 548da58f90fSIdo Schimmel nsim_trap_data = nsim_dev->trap_data; 549da58f90fSIdo Schimmel 550da58f90fSIdo Schimmel spin_lock(&nsim_trap_data->trap_lock); 551da58f90fSIdo Schimmel for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) { 552d3cbb907SJiri Pirko struct flow_action_cookie *fa_cookie = NULL; 553da58f90fSIdo Schimmel struct nsim_trap_item *nsim_trap_item; 554da58f90fSIdo Schimmel struct sk_buff *skb; 555d3cbb907SJiri Pirko bool has_fa_cookie; 556d3cbb907SJiri Pirko 557d3cbb907SJiri Pirko has_fa_cookie = nsim_traps_arr[i].metadata_cap & 558d3cbb907SJiri Pirko DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE; 559da58f90fSIdo Schimmel 560da58f90fSIdo Schimmel nsim_trap_item = &nsim_trap_data->trap_items_arr[i]; 561da58f90fSIdo Schimmel if (nsim_trap_item->action == DEVLINK_TRAP_ACTION_DROP) 562da58f90fSIdo Schimmel continue; 563da58f90fSIdo Schimmel 564da58f90fSIdo Schimmel skb = nsim_dev_trap_skb_build(); 565da58f90fSIdo Schimmel if (!skb) 566da58f90fSIdo Schimmel continue; 567da58f90fSIdo Schimmel skb->dev = nsim_dev_port->ns->netdev; 568da58f90fSIdo Schimmel 569da58f90fSIdo Schimmel /* Trapped packets are usually passed to devlink in softIRQ, 570da58f90fSIdo Schimmel * but in this case they are generated in a workqueue. Disable 571da58f90fSIdo Schimmel * softIRQs to prevent lockdep from complaining about 572da58f90fSIdo Schimmel * "incosistent lock state". 573da58f90fSIdo Schimmel */ 574d3cbb907SJiri Pirko 575d3cbb907SJiri Pirko spin_lock_bh(&nsim_dev->fa_cookie_lock); 576d3cbb907SJiri Pirko fa_cookie = has_fa_cookie ? nsim_dev->fa_cookie : NULL; 577da58f90fSIdo Schimmel devlink_trap_report(devlink, skb, nsim_trap_item->trap_ctx, 578d3cbb907SJiri Pirko &nsim_dev_port->devlink_port, fa_cookie); 579d3cbb907SJiri Pirko spin_unlock_bh(&nsim_dev->fa_cookie_lock); 580da58f90fSIdo Schimmel consume_skb(skb); 581da58f90fSIdo Schimmel } 582da58f90fSIdo Schimmel spin_unlock(&nsim_trap_data->trap_lock); 583da58f90fSIdo Schimmel } 584da58f90fSIdo Schimmel 585da58f90fSIdo Schimmel #define NSIM_TRAP_REPORT_INTERVAL_MS 100 586da58f90fSIdo Schimmel 587da58f90fSIdo Schimmel static void nsim_dev_trap_report_work(struct work_struct *work) 588da58f90fSIdo Schimmel { 589da58f90fSIdo Schimmel struct nsim_trap_data *nsim_trap_data; 590da58f90fSIdo Schimmel struct nsim_dev_port *nsim_dev_port; 591da58f90fSIdo Schimmel struct nsim_dev *nsim_dev; 592da58f90fSIdo Schimmel 593da58f90fSIdo Schimmel nsim_trap_data = container_of(work, struct nsim_trap_data, 594da58f90fSIdo Schimmel trap_report_dw.work); 595da58f90fSIdo Schimmel nsim_dev = nsim_trap_data->nsim_dev; 596da58f90fSIdo Schimmel 597da58f90fSIdo Schimmel /* For each running port and enabled packet trap, generate a UDP 598da58f90fSIdo Schimmel * packet with a random 5-tuple and report it. 599da58f90fSIdo Schimmel */ 600da58f90fSIdo Schimmel mutex_lock(&nsim_dev->port_list_lock); 601da58f90fSIdo Schimmel list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) { 602da58f90fSIdo Schimmel if (!netif_running(nsim_dev_port->ns->netdev)) 603da58f90fSIdo Schimmel continue; 604da58f90fSIdo Schimmel 605da58f90fSIdo Schimmel nsim_dev_trap_report(nsim_dev_port); 606da58f90fSIdo Schimmel } 607da58f90fSIdo Schimmel mutex_unlock(&nsim_dev->port_list_lock); 608da58f90fSIdo Schimmel 609da58f90fSIdo Schimmel schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, 610da58f90fSIdo Schimmel msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS)); 611da58f90fSIdo Schimmel } 612da58f90fSIdo Schimmel 613da58f90fSIdo Schimmel static int nsim_dev_traps_init(struct devlink *devlink) 614da58f90fSIdo Schimmel { 615ad188458SIdo Schimmel size_t policers_count = ARRAY_SIZE(nsim_trap_policers_arr); 616da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 617da58f90fSIdo Schimmel struct nsim_trap_data *nsim_trap_data; 618da58f90fSIdo Schimmel int err; 619da58f90fSIdo Schimmel 620da58f90fSIdo Schimmel nsim_trap_data = kzalloc(sizeof(*nsim_trap_data), GFP_KERNEL); 621da58f90fSIdo Schimmel if (!nsim_trap_data) 622da58f90fSIdo Schimmel return -ENOMEM; 623da58f90fSIdo Schimmel 624da58f90fSIdo Schimmel nsim_trap_data->trap_items_arr = kcalloc(ARRAY_SIZE(nsim_traps_arr), 625da58f90fSIdo Schimmel sizeof(struct nsim_trap_item), 626da58f90fSIdo Schimmel GFP_KERNEL); 627da58f90fSIdo Schimmel if (!nsim_trap_data->trap_items_arr) { 628da58f90fSIdo Schimmel err = -ENOMEM; 629da58f90fSIdo Schimmel goto err_trap_data_free; 630da58f90fSIdo Schimmel } 631da58f90fSIdo Schimmel 632ad188458SIdo Schimmel nsim_trap_data->trap_policers_cnt_arr = kcalloc(policers_count, 633ad188458SIdo Schimmel sizeof(u64), 634ad188458SIdo Schimmel GFP_KERNEL); 635ad188458SIdo Schimmel if (!nsim_trap_data->trap_policers_cnt_arr) { 636ad188458SIdo Schimmel err = -ENOMEM; 637ad188458SIdo Schimmel goto err_trap_items_free; 638ad188458SIdo Schimmel } 639ad188458SIdo Schimmel 640da58f90fSIdo Schimmel /* The lock is used to protect the action state of the registered 641da58f90fSIdo Schimmel * traps. The value is written by user and read in delayed work when 642da58f90fSIdo Schimmel * iterating over all the traps. 643da58f90fSIdo Schimmel */ 644da58f90fSIdo Schimmel spin_lock_init(&nsim_trap_data->trap_lock); 645da58f90fSIdo Schimmel nsim_trap_data->nsim_dev = nsim_dev; 646da58f90fSIdo Schimmel nsim_dev->trap_data = nsim_trap_data; 647da58f90fSIdo Schimmel 648ad188458SIdo Schimmel err = devlink_trap_policers_register(devlink, nsim_trap_policers_arr, 649ad188458SIdo Schimmel policers_count); 650ad188458SIdo Schimmel if (err) 651ad188458SIdo Schimmel goto err_trap_policers_cnt_free; 652ad188458SIdo Schimmel 653b29545d8SIdo Schimmel err = devlink_trap_groups_register(devlink, nsim_trap_groups_arr, 654b29545d8SIdo Schimmel ARRAY_SIZE(nsim_trap_groups_arr)); 655b29545d8SIdo Schimmel if (err) 656ad188458SIdo Schimmel goto err_trap_policers_unregister; 657b29545d8SIdo Schimmel 658da58f90fSIdo Schimmel err = devlink_traps_register(devlink, nsim_traps_arr, 659da58f90fSIdo Schimmel ARRAY_SIZE(nsim_traps_arr), NULL); 660da58f90fSIdo Schimmel if (err) 661b29545d8SIdo Schimmel goto err_trap_groups_unregister; 662da58f90fSIdo Schimmel 663da58f90fSIdo Schimmel INIT_DELAYED_WORK(&nsim_dev->trap_data->trap_report_dw, 664da58f90fSIdo Schimmel nsim_dev_trap_report_work); 665da58f90fSIdo Schimmel schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, 666da58f90fSIdo Schimmel msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS)); 667da58f90fSIdo Schimmel 668da58f90fSIdo Schimmel return 0; 669da58f90fSIdo Schimmel 670b29545d8SIdo Schimmel err_trap_groups_unregister: 671b29545d8SIdo Schimmel devlink_trap_groups_unregister(devlink, nsim_trap_groups_arr, 672b29545d8SIdo Schimmel ARRAY_SIZE(nsim_trap_groups_arr)); 673ad188458SIdo Schimmel err_trap_policers_unregister: 674ad188458SIdo Schimmel devlink_trap_policers_unregister(devlink, nsim_trap_policers_arr, 675ad188458SIdo Schimmel ARRAY_SIZE(nsim_trap_policers_arr)); 676ad188458SIdo Schimmel err_trap_policers_cnt_free: 677ad188458SIdo Schimmel kfree(nsim_trap_data->trap_policers_cnt_arr); 678da58f90fSIdo Schimmel err_trap_items_free: 679da58f90fSIdo Schimmel kfree(nsim_trap_data->trap_items_arr); 680da58f90fSIdo Schimmel err_trap_data_free: 681da58f90fSIdo Schimmel kfree(nsim_trap_data); 682da58f90fSIdo Schimmel return err; 683da58f90fSIdo Schimmel } 684da58f90fSIdo Schimmel 685da58f90fSIdo Schimmel static void nsim_dev_traps_exit(struct devlink *devlink) 686da58f90fSIdo Schimmel { 687da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 688da58f90fSIdo Schimmel 689da58f90fSIdo Schimmel cancel_delayed_work_sync(&nsim_dev->trap_data->trap_report_dw); 690da58f90fSIdo Schimmel devlink_traps_unregister(devlink, nsim_traps_arr, 691da58f90fSIdo Schimmel ARRAY_SIZE(nsim_traps_arr)); 692b29545d8SIdo Schimmel devlink_trap_groups_unregister(devlink, nsim_trap_groups_arr, 693b29545d8SIdo Schimmel ARRAY_SIZE(nsim_trap_groups_arr)); 694ad188458SIdo Schimmel devlink_trap_policers_unregister(devlink, nsim_trap_policers_arr, 695ad188458SIdo Schimmel ARRAY_SIZE(nsim_trap_policers_arr)); 696ad188458SIdo Schimmel kfree(nsim_dev->trap_data->trap_policers_cnt_arr); 697da58f90fSIdo Schimmel kfree(nsim_dev->trap_data->trap_items_arr); 698da58f90fSIdo Schimmel kfree(nsim_dev->trap_data); 699da58f90fSIdo Schimmel } 700da58f90fSIdo Schimmel 70175ba029fSJiri Pirko static int nsim_dev_reload_create(struct nsim_dev *nsim_dev, 70275ba029fSJiri Pirko struct netlink_ext_ack *extack); 70375ba029fSJiri Pirko static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev); 70475ba029fSJiri Pirko 705070c63f2SJiri Pirko static int nsim_dev_reload_down(struct devlink *devlink, bool netns_change, 706dc64cc7cSMoshe Shemesh enum devlink_reload_action action, enum devlink_reload_limit limit, 707dc64cc7cSMoshe Shemesh struct netlink_ext_ack *extack) 70897691069SJiri Pirko { 70975ba029fSJiri Pirko struct nsim_dev *nsim_dev = devlink_priv(devlink); 71075ba029fSJiri Pirko 711155ddfc5SJiri Pirko if (nsim_dev->dont_allow_reload) { 712155ddfc5SJiri Pirko /* For testing purposes, user set debugfs dont_allow_reload 713155ddfc5SJiri Pirko * value to true. So forbid it. 714155ddfc5SJiri Pirko */ 715f9867b51SColin Ian King NL_SET_ERR_MSG_MOD(extack, "User forbid the reload for testing purposes"); 716155ddfc5SJiri Pirko return -EOPNOTSUPP; 717155ddfc5SJiri Pirko } 718155ddfc5SJiri Pirko 71975ba029fSJiri Pirko nsim_dev_reload_destroy(nsim_dev); 72097691069SJiri Pirko return 0; 72197691069SJiri Pirko } 72297691069SJiri Pirko 723ccdf0721SMoshe Shemesh static int nsim_dev_reload_up(struct devlink *devlink, enum devlink_reload_action action, 724dc64cc7cSMoshe Shemesh enum devlink_reload_limit limit, u32 *actions_performed, 725dc64cc7cSMoshe Shemesh struct netlink_ext_ack *extack) 7268fb4bc6fSJiri Pirko { 727a5facc4cSJiri Pirko struct nsim_dev *nsim_dev = devlink_priv(devlink); 7288fb4bc6fSJiri Pirko 729155ddfc5SJiri Pirko if (nsim_dev->fail_reload) { 730155ddfc5SJiri Pirko /* For testing purposes, user set debugfs fail_reload 731155ddfc5SJiri Pirko * value to true. Fail right away. 732155ddfc5SJiri Pirko */ 733155ddfc5SJiri Pirko NL_SET_ERR_MSG_MOD(extack, "User setup the reload to fail for testing purposes"); 734155ddfc5SJiri Pirko return -EINVAL; 735155ddfc5SJiri Pirko } 736155ddfc5SJiri Pirko 737ccdf0721SMoshe Shemesh *actions_performed = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT); 73875ba029fSJiri Pirko return nsim_dev_reload_create(nsim_dev, extack); 7398fb4bc6fSJiri Pirko } 7408fb4bc6fSJiri Pirko 7418e23cc03SJiri Pirko static int nsim_dev_info_get(struct devlink *devlink, 7428e23cc03SJiri Pirko struct devlink_info_req *req, 7438e23cc03SJiri Pirko struct netlink_ext_ack *extack) 7448e23cc03SJiri Pirko { 7458e23cc03SJiri Pirko return devlink_info_driver_name_put(req, DRV_NAME); 7468e23cc03SJiri Pirko } 7478e23cc03SJiri Pirko 748fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_SIZE 500000 749fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_CHUNK_SIZE 1000 750fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_CHUNK_TIME_MS 10 751fa4dfc4aSJiri Pirko 752bc75c054SJacob Keller static int nsim_dev_flash_update(struct devlink *devlink, 753bc75c054SJacob Keller struct devlink_flash_update_params *params, 754fa4dfc4aSJiri Pirko struct netlink_ext_ack *extack) 755fa4dfc4aSJiri Pirko { 756fa4dfc4aSJiri Pirko struct nsim_dev *nsim_dev = devlink_priv(devlink); 757fa4dfc4aSJiri Pirko int i; 758fa4dfc4aSJiri Pirko 759cbb58368SJacob Keller if ((params->overwrite_mask & ~nsim_dev->fw_update_overwrite_mask) != 0) 760cbb58368SJacob Keller return -EOPNOTSUPP; 761cbb58368SJacob Keller 762fa4dfc4aSJiri Pirko if (nsim_dev->fw_update_status) { 763fa4dfc4aSJiri Pirko devlink_flash_update_begin_notify(devlink); 764fa4dfc4aSJiri Pirko devlink_flash_update_status_notify(devlink, 765fa4dfc4aSJiri Pirko "Preparing to flash", 766bc75c054SJacob Keller params->component, 0, 0); 767fa4dfc4aSJiri Pirko } 768fa4dfc4aSJiri Pirko 769fa4dfc4aSJiri Pirko for (i = 0; i < NSIM_DEV_FLASH_SIZE / NSIM_DEV_FLASH_CHUNK_SIZE; i++) { 770fa4dfc4aSJiri Pirko if (nsim_dev->fw_update_status) 771fa4dfc4aSJiri Pirko devlink_flash_update_status_notify(devlink, "Flashing", 772bc75c054SJacob Keller params->component, 773fa4dfc4aSJiri Pirko i * NSIM_DEV_FLASH_CHUNK_SIZE, 774fa4dfc4aSJiri Pirko NSIM_DEV_FLASH_SIZE); 775fa4dfc4aSJiri Pirko msleep(NSIM_DEV_FLASH_CHUNK_TIME_MS); 776fa4dfc4aSJiri Pirko } 777fa4dfc4aSJiri Pirko 778fa4dfc4aSJiri Pirko if (nsim_dev->fw_update_status) { 779fa4dfc4aSJiri Pirko devlink_flash_update_status_notify(devlink, "Flashing", 780bc75c054SJacob Keller params->component, 781fa4dfc4aSJiri Pirko NSIM_DEV_FLASH_SIZE, 782fa4dfc4aSJiri Pirko NSIM_DEV_FLASH_SIZE); 783b311b001SShannon Nelson devlink_flash_update_timeout_notify(devlink, "Flash select", 784bc75c054SJacob Keller params->component, 81); 785fa4dfc4aSJiri Pirko devlink_flash_update_status_notify(devlink, "Flashing done", 786bc75c054SJacob Keller params->component, 0, 0); 787fa4dfc4aSJiri Pirko devlink_flash_update_end_notify(devlink); 788fa4dfc4aSJiri Pirko } 789fa4dfc4aSJiri Pirko 790fa4dfc4aSJiri Pirko return 0; 791fa4dfc4aSJiri Pirko } 792fa4dfc4aSJiri Pirko 793da58f90fSIdo Schimmel static struct nsim_trap_item * 794da58f90fSIdo Schimmel nsim_dev_trap_item_lookup(struct nsim_dev *nsim_dev, u16 trap_id) 795da58f90fSIdo Schimmel { 796da58f90fSIdo Schimmel struct nsim_trap_data *nsim_trap_data = nsim_dev->trap_data; 797da58f90fSIdo Schimmel int i; 798da58f90fSIdo Schimmel 799da58f90fSIdo Schimmel for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) { 800da58f90fSIdo Schimmel if (nsim_traps_arr[i].id == trap_id) 801da58f90fSIdo Schimmel return &nsim_trap_data->trap_items_arr[i]; 802da58f90fSIdo Schimmel } 803da58f90fSIdo Schimmel 804da58f90fSIdo Schimmel return NULL; 805da58f90fSIdo Schimmel } 806da58f90fSIdo Schimmel 807da58f90fSIdo Schimmel static int nsim_dev_devlink_trap_init(struct devlink *devlink, 808da58f90fSIdo Schimmel const struct devlink_trap *trap, 809da58f90fSIdo Schimmel void *trap_ctx) 810da58f90fSIdo Schimmel { 811da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 812da58f90fSIdo Schimmel struct nsim_trap_item *nsim_trap_item; 813da58f90fSIdo Schimmel 814da58f90fSIdo Schimmel nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id); 815da58f90fSIdo Schimmel if (WARN_ON(!nsim_trap_item)) 816da58f90fSIdo Schimmel return -ENOENT; 817da58f90fSIdo Schimmel 818da58f90fSIdo Schimmel nsim_trap_item->trap_ctx = trap_ctx; 819da58f90fSIdo Schimmel nsim_trap_item->action = trap->init_action; 820da58f90fSIdo Schimmel 821da58f90fSIdo Schimmel return 0; 822da58f90fSIdo Schimmel } 823da58f90fSIdo Schimmel 824da58f90fSIdo Schimmel static int 825da58f90fSIdo Schimmel nsim_dev_devlink_trap_action_set(struct devlink *devlink, 826da58f90fSIdo Schimmel const struct devlink_trap *trap, 827c88e11e0SIdo Schimmel enum devlink_trap_action action, 828c88e11e0SIdo Schimmel struct netlink_ext_ack *extack) 829da58f90fSIdo Schimmel { 830da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 831da58f90fSIdo Schimmel struct nsim_trap_item *nsim_trap_item; 832da58f90fSIdo Schimmel 833da58f90fSIdo Schimmel nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id); 834da58f90fSIdo Schimmel if (WARN_ON(!nsim_trap_item)) 835da58f90fSIdo Schimmel return -ENOENT; 836da58f90fSIdo Schimmel 837da58f90fSIdo Schimmel spin_lock(&nsim_dev->trap_data->trap_lock); 838da58f90fSIdo Schimmel nsim_trap_item->action = action; 839da58f90fSIdo Schimmel spin_unlock(&nsim_dev->trap_data->trap_lock); 840da58f90fSIdo Schimmel 841da58f90fSIdo Schimmel return 0; 842da58f90fSIdo Schimmel } 843da58f90fSIdo Schimmel 844ad188458SIdo Schimmel static int 8450dc8249aSIdo Schimmel nsim_dev_devlink_trap_group_set(struct devlink *devlink, 8460dc8249aSIdo Schimmel const struct devlink_trap_group *group, 847c88e11e0SIdo Schimmel const struct devlink_trap_policer *policer, 848c88e11e0SIdo Schimmel struct netlink_ext_ack *extack) 8490dc8249aSIdo Schimmel { 8500dc8249aSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 8510dc8249aSIdo Schimmel 8520dc8249aSIdo Schimmel if (nsim_dev->fail_trap_group_set) 8530dc8249aSIdo Schimmel return -EINVAL; 8540dc8249aSIdo Schimmel 8550dc8249aSIdo Schimmel return 0; 8560dc8249aSIdo Schimmel } 8570dc8249aSIdo Schimmel 8580dc8249aSIdo Schimmel static int 859ad188458SIdo Schimmel nsim_dev_devlink_trap_policer_set(struct devlink *devlink, 860ad188458SIdo Schimmel const struct devlink_trap_policer *policer, 861ad188458SIdo Schimmel u64 rate, u64 burst, 862ad188458SIdo Schimmel struct netlink_ext_ack *extack) 863ad188458SIdo Schimmel { 864ad188458SIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 865ad188458SIdo Schimmel 866ad188458SIdo Schimmel if (nsim_dev->fail_trap_policer_set) { 867ad188458SIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "User setup the operation to fail for testing purposes"); 868ad188458SIdo Schimmel return -EINVAL; 869ad188458SIdo Schimmel } 870ad188458SIdo Schimmel 871ad188458SIdo Schimmel return 0; 872ad188458SIdo Schimmel } 873ad188458SIdo Schimmel 874ad188458SIdo Schimmel static int 875ad188458SIdo Schimmel nsim_dev_devlink_trap_policer_counter_get(struct devlink *devlink, 876ad188458SIdo Schimmel const struct devlink_trap_policer *policer, 877ad188458SIdo Schimmel u64 *p_drops) 878ad188458SIdo Schimmel { 879ad188458SIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 880ad188458SIdo Schimmel u64 *cnt; 881ad188458SIdo Schimmel 882ad188458SIdo Schimmel if (nsim_dev->fail_trap_policer_counter_get) 883ad188458SIdo Schimmel return -EINVAL; 884ad188458SIdo Schimmel 885ad188458SIdo Schimmel cnt = &nsim_dev->trap_data->trap_policers_cnt_arr[policer->id - 1]; 886be43224fSIdo Schimmel *p_drops = (*cnt)++; 887ad188458SIdo Schimmel 888ad188458SIdo Schimmel return 0; 889ad188458SIdo Schimmel } 890ad188458SIdo Schimmel 8918fb4bc6fSJiri Pirko static const struct devlink_ops nsim_dev_devlink_ops = { 892cbb58368SJacob Keller .supported_flash_update_params = DEVLINK_SUPPORT_FLASH_UPDATE_COMPONENT | 893cbb58368SJacob Keller DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK, 894ccdf0721SMoshe Shemesh .reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT), 89597691069SJiri Pirko .reload_down = nsim_dev_reload_down, 89697691069SJiri Pirko .reload_up = nsim_dev_reload_up, 8978e23cc03SJiri Pirko .info_get = nsim_dev_info_get, 898fa4dfc4aSJiri Pirko .flash_update = nsim_dev_flash_update, 899da58f90fSIdo Schimmel .trap_init = nsim_dev_devlink_trap_init, 900da58f90fSIdo Schimmel .trap_action_set = nsim_dev_devlink_trap_action_set, 9010dc8249aSIdo Schimmel .trap_group_set = nsim_dev_devlink_trap_group_set, 902ad188458SIdo Schimmel .trap_policer_set = nsim_dev_devlink_trap_policer_set, 903ad188458SIdo Schimmel .trap_policer_counter_get = nsim_dev_devlink_trap_policer_counter_get, 9048fb4bc6fSJiri Pirko }; 9058fb4bc6fSJiri Pirko 906150e8f8aSJiri Pirko #define NSIM_DEV_MAX_MACS_DEFAULT 32 907150e8f8aSJiri Pirko #define NSIM_DEV_TEST1_DEFAULT true 908150e8f8aSJiri Pirko 909794b2c05SJiri Pirko static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, 910794b2c05SJiri Pirko unsigned int port_index) 9118320d145SJiri Pirko { 91271ad8d55SDanielle Ratson struct devlink_port_attrs attrs = {}; 9138320d145SJiri Pirko struct nsim_dev_port *nsim_dev_port; 9148320d145SJiri Pirko struct devlink_port *devlink_port; 9158320d145SJiri Pirko int err; 9168320d145SJiri Pirko 9178320d145SJiri Pirko nsim_dev_port = kzalloc(sizeof(*nsim_dev_port), GFP_KERNEL); 9188320d145SJiri Pirko if (!nsim_dev_port) 9198320d145SJiri Pirko return -ENOMEM; 9208320d145SJiri Pirko nsim_dev_port->port_index = port_index; 9218320d145SJiri Pirko 9228320d145SJiri Pirko devlink_port = &nsim_dev_port->devlink_port; 92371ad8d55SDanielle Ratson attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; 92471ad8d55SDanielle Ratson attrs.phys.port_number = port_index + 1; 92571ad8d55SDanielle Ratson memcpy(attrs.switch_id.id, nsim_dev->switch_id.id, nsim_dev->switch_id.id_len); 92671ad8d55SDanielle Ratson attrs.switch_id.id_len = nsim_dev->switch_id.id_len; 92771ad8d55SDanielle Ratson devlink_port_attrs_set(devlink_port, &attrs); 9288320d145SJiri Pirko err = devlink_port_register(priv_to_devlink(nsim_dev), devlink_port, 9298320d145SJiri Pirko port_index); 9308320d145SJiri Pirko if (err) 9318320d145SJiri Pirko goto err_port_free; 9328320d145SJiri Pirko 9338320d145SJiri Pirko err = nsim_dev_port_debugfs_init(nsim_dev, nsim_dev_port); 9348320d145SJiri Pirko if (err) 9358320d145SJiri Pirko goto err_dl_port_unregister; 9368320d145SJiri Pirko 937e05b2d14SJiri Pirko nsim_dev_port->ns = nsim_create(nsim_dev, nsim_dev_port); 938e05b2d14SJiri Pirko if (IS_ERR(nsim_dev_port->ns)) { 939e05b2d14SJiri Pirko err = PTR_ERR(nsim_dev_port->ns); 940e05b2d14SJiri Pirko goto err_port_debugfs_exit; 941e05b2d14SJiri Pirko } 942e05b2d14SJiri Pirko 943e05b2d14SJiri Pirko devlink_port_type_eth_set(devlink_port, nsim_dev_port->ns->netdev); 9448320d145SJiri Pirko list_add(&nsim_dev_port->list, &nsim_dev->port_list); 9458320d145SJiri Pirko 9468320d145SJiri Pirko return 0; 9478320d145SJiri Pirko 948e05b2d14SJiri Pirko err_port_debugfs_exit: 949e05b2d14SJiri Pirko nsim_dev_port_debugfs_exit(nsim_dev_port); 9508320d145SJiri Pirko err_dl_port_unregister: 9518320d145SJiri Pirko devlink_port_unregister(devlink_port); 9528320d145SJiri Pirko err_port_free: 9538320d145SJiri Pirko kfree(nsim_dev_port); 9548320d145SJiri Pirko return err; 9558320d145SJiri Pirko } 9568320d145SJiri Pirko 957794b2c05SJiri Pirko static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port) 9588320d145SJiri Pirko { 9598320d145SJiri Pirko struct devlink_port *devlink_port = &nsim_dev_port->devlink_port; 9608320d145SJiri Pirko 9618320d145SJiri Pirko list_del(&nsim_dev_port->list); 962e05b2d14SJiri Pirko devlink_port_type_clear(devlink_port); 963e05b2d14SJiri Pirko nsim_destroy(nsim_dev_port->ns); 9648320d145SJiri Pirko nsim_dev_port_debugfs_exit(nsim_dev_port); 9658320d145SJiri Pirko devlink_port_unregister(devlink_port); 9668320d145SJiri Pirko kfree(nsim_dev_port); 9678320d145SJiri Pirko } 9688320d145SJiri Pirko 9698320d145SJiri Pirko static void nsim_dev_port_del_all(struct nsim_dev *nsim_dev) 9708320d145SJiri Pirko { 9718320d145SJiri Pirko struct nsim_dev_port *nsim_dev_port, *tmp; 9728320d145SJiri Pirko 9736d6f0383SIdo Schimmel mutex_lock(&nsim_dev->port_list_lock); 9748320d145SJiri Pirko list_for_each_entry_safe(nsim_dev_port, tmp, 9758320d145SJiri Pirko &nsim_dev->port_list, list) 976794b2c05SJiri Pirko __nsim_dev_port_del(nsim_dev_port); 9776d6f0383SIdo Schimmel mutex_unlock(&nsim_dev->port_list_lock); 9788320d145SJiri Pirko } 9798320d145SJiri Pirko 9807f36a77aSJiri Pirko static int nsim_dev_port_add_all(struct nsim_dev *nsim_dev, 9817f36a77aSJiri Pirko unsigned int port_count) 9828320d145SJiri Pirko { 9837f36a77aSJiri Pirko int i, err; 9848320d145SJiri Pirko 9857f36a77aSJiri Pirko for (i = 0; i < port_count; i++) { 986794b2c05SJiri Pirko err = __nsim_dev_port_add(nsim_dev, i); 9878320d145SJiri Pirko if (err) 9888320d145SJiri Pirko goto err_port_del_all; 9898320d145SJiri Pirko } 9908320d145SJiri Pirko return 0; 9918320d145SJiri Pirko 9928320d145SJiri Pirko err_port_del_all: 9938320d145SJiri Pirko nsim_dev_port_del_all(nsim_dev); 9948320d145SJiri Pirko return err; 9958320d145SJiri Pirko } 9968320d145SJiri Pirko 99775ba029fSJiri Pirko static int nsim_dev_reload_create(struct nsim_dev *nsim_dev, 99875ba029fSJiri Pirko struct netlink_ext_ack *extack) 99975ba029fSJiri Pirko { 100075ba029fSJiri Pirko struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev; 100175ba029fSJiri Pirko struct devlink *devlink; 100275ba029fSJiri Pirko int err; 100375ba029fSJiri Pirko 100475ba029fSJiri Pirko devlink = priv_to_devlink(nsim_dev); 100575ba029fSJiri Pirko nsim_dev = devlink_priv(devlink); 100675ba029fSJiri Pirko INIT_LIST_HEAD(&nsim_dev->port_list); 100775ba029fSJiri Pirko mutex_init(&nsim_dev->port_list_lock); 100875ba029fSJiri Pirko nsim_dev->fw_update_status = true; 1009cbb58368SJacob Keller nsim_dev->fw_update_overwrite_mask = 0; 101075ba029fSJiri Pirko 101175ba029fSJiri Pirko nsim_dev->fib_data = nsim_fib_create(devlink, extack); 101275ba029fSJiri Pirko if (IS_ERR(nsim_dev->fib_data)) 101375ba029fSJiri Pirko return PTR_ERR(nsim_dev->fib_data); 101475ba029fSJiri Pirko 101575ba029fSJiri Pirko nsim_devlink_param_load_driverinit_values(devlink); 101675ba029fSJiri Pirko 101775ba029fSJiri Pirko err = nsim_dev_dummy_region_init(nsim_dev, devlink); 101875ba029fSJiri Pirko if (err) 101975ba029fSJiri Pirko goto err_fib_destroy; 102075ba029fSJiri Pirko 102175ba029fSJiri Pirko err = nsim_dev_traps_init(devlink); 102275ba029fSJiri Pirko if (err) 102375ba029fSJiri Pirko goto err_dummy_region_exit; 102475ba029fSJiri Pirko 102582c93a87SJiri Pirko err = nsim_dev_health_init(nsim_dev, devlink); 102675ba029fSJiri Pirko if (err) 102775ba029fSJiri Pirko goto err_traps_exit; 102875ba029fSJiri Pirko 102982c93a87SJiri Pirko err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count); 103082c93a87SJiri Pirko if (err) 103182c93a87SJiri Pirko goto err_health_exit; 103282c93a87SJiri Pirko 10338526ad96STaehee Yoo nsim_dev->take_snapshot = debugfs_create_file("take_snapshot", 10348526ad96STaehee Yoo 0200, 10358526ad96STaehee Yoo nsim_dev->ddir, 10368526ad96STaehee Yoo nsim_dev, 10378526ad96STaehee Yoo &nsim_dev_take_snapshot_fops); 103875ba029fSJiri Pirko return 0; 103975ba029fSJiri Pirko 104082c93a87SJiri Pirko err_health_exit: 104182c93a87SJiri Pirko nsim_dev_health_exit(nsim_dev); 104275ba029fSJiri Pirko err_traps_exit: 104375ba029fSJiri Pirko nsim_dev_traps_exit(devlink); 104475ba029fSJiri Pirko err_dummy_region_exit: 104575ba029fSJiri Pirko nsim_dev_dummy_region_exit(nsim_dev); 104675ba029fSJiri Pirko err_fib_destroy: 104775ba029fSJiri Pirko nsim_fib_destroy(devlink, nsim_dev->fib_data); 104875ba029fSJiri Pirko return err; 104975ba029fSJiri Pirko } 105075ba029fSJiri Pirko 1051bfcccfe7SJakub Kicinski int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev) 10527f36a77aSJiri Pirko { 10537f36a77aSJiri Pirko struct nsim_dev *nsim_dev; 10547f36a77aSJiri Pirko struct devlink *devlink; 10557f36a77aSJiri Pirko int err; 10567f36a77aSJiri Pirko 10577f36a77aSJiri Pirko devlink = devlink_alloc(&nsim_dev_devlink_ops, sizeof(*nsim_dev)); 10587f36a77aSJiri Pirko if (!devlink) 1059bfcccfe7SJakub Kicinski return -ENOMEM; 10607b60027bSJiri Pirko devlink_net_set(devlink, nsim_bus_dev->initial_net); 10617f36a77aSJiri Pirko nsim_dev = devlink_priv(devlink); 10627f36a77aSJiri Pirko nsim_dev->nsim_bus_dev = nsim_bus_dev; 10637f36a77aSJiri Pirko nsim_dev->switch_id.id_len = sizeof(nsim_dev->switch_id.id); 10647f36a77aSJiri Pirko get_random_bytes(nsim_dev->switch_id.id, nsim_dev->switch_id.id_len); 10657f36a77aSJiri Pirko INIT_LIST_HEAD(&nsim_dev->port_list); 10667f36a77aSJiri Pirko mutex_init(&nsim_dev->port_list_lock); 10677f36a77aSJiri Pirko nsim_dev->fw_update_status = true; 1068cbb58368SJacob Keller nsim_dev->fw_update_overwrite_mask = 0; 10697f36a77aSJiri Pirko nsim_dev->max_macs = NSIM_DEV_MAX_MACS_DEFAULT; 10707f36a77aSJiri Pirko nsim_dev->test1 = NSIM_DEV_TEST1_DEFAULT; 1071d3cbb907SJiri Pirko spin_lock_init(&nsim_dev->fa_cookie_lock); 10727f36a77aSJiri Pirko 1073bfcccfe7SJakub Kicinski dev_set_drvdata(&nsim_bus_dev->dev, nsim_dev); 1074bfcccfe7SJakub Kicinski 10757f36a77aSJiri Pirko err = nsim_dev_resources_register(devlink); 10767f36a77aSJiri Pirko if (err) 10777f36a77aSJiri Pirko goto err_devlink_free; 10787f36a77aSJiri Pirko 107975ba029fSJiri Pirko nsim_dev->fib_data = nsim_fib_create(devlink, NULL); 10807f36a77aSJiri Pirko if (IS_ERR(nsim_dev->fib_data)) { 10817f36a77aSJiri Pirko err = PTR_ERR(nsim_dev->fib_data); 10827f36a77aSJiri Pirko goto err_resources_unregister; 10837f36a77aSJiri Pirko } 10847f36a77aSJiri Pirko 10857f36a77aSJiri Pirko err = devlink_register(devlink, &nsim_bus_dev->dev); 10867f36a77aSJiri Pirko if (err) 10877f36a77aSJiri Pirko goto err_fib_destroy; 10887f36a77aSJiri Pirko 10897f36a77aSJiri Pirko err = devlink_params_register(devlink, nsim_devlink_params, 10907f36a77aSJiri Pirko ARRAY_SIZE(nsim_devlink_params)); 10917f36a77aSJiri Pirko if (err) 10927f36a77aSJiri Pirko goto err_dl_unregister; 10937f36a77aSJiri Pirko nsim_devlink_set_params_init_values(nsim_dev, devlink); 10947f36a77aSJiri Pirko 10957f36a77aSJiri Pirko err = nsim_dev_dummy_region_init(nsim_dev, devlink); 10967f36a77aSJiri Pirko if (err) 10977f36a77aSJiri Pirko goto err_params_unregister; 10987f36a77aSJiri Pirko 10997f36a77aSJiri Pirko err = nsim_dev_traps_init(devlink); 11007f36a77aSJiri Pirko if (err) 11017f36a77aSJiri Pirko goto err_dummy_region_exit; 11027f36a77aSJiri Pirko 11037f36a77aSJiri Pirko err = nsim_dev_debugfs_init(nsim_dev); 11047f36a77aSJiri Pirko if (err) 11057f36a77aSJiri Pirko goto err_traps_exit; 11067f36a77aSJiri Pirko 110782c93a87SJiri Pirko err = nsim_dev_health_init(nsim_dev, devlink); 11087f36a77aSJiri Pirko if (err) 11097f36a77aSJiri Pirko goto err_debugfs_exit; 11107f36a77aSJiri Pirko 111182c93a87SJiri Pirko err = nsim_bpf_dev_init(nsim_dev); 111282c93a87SJiri Pirko if (err) 111382c93a87SJiri Pirko goto err_health_exit; 111482c93a87SJiri Pirko 11157f36a77aSJiri Pirko err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count); 11167f36a77aSJiri Pirko if (err) 11177f36a77aSJiri Pirko goto err_bpf_dev_exit; 11187f36a77aSJiri Pirko 11197f36a77aSJiri Pirko devlink_params_publish(devlink); 1120a0c76345SJiri Pirko devlink_reload_enable(devlink); 1121bfcccfe7SJakub Kicinski return 0; 11227f36a77aSJiri Pirko 11237f36a77aSJiri Pirko err_bpf_dev_exit: 11247f36a77aSJiri Pirko nsim_bpf_dev_exit(nsim_dev); 112582c93a87SJiri Pirko err_health_exit: 112682c93a87SJiri Pirko nsim_dev_health_exit(nsim_dev); 11277f36a77aSJiri Pirko err_debugfs_exit: 11287f36a77aSJiri Pirko nsim_dev_debugfs_exit(nsim_dev); 11297f36a77aSJiri Pirko err_traps_exit: 11307f36a77aSJiri Pirko nsim_dev_traps_exit(devlink); 11317f36a77aSJiri Pirko err_dummy_region_exit: 11327f36a77aSJiri Pirko nsim_dev_dummy_region_exit(nsim_dev); 11337f36a77aSJiri Pirko err_params_unregister: 11347f36a77aSJiri Pirko devlink_params_unregister(devlink, nsim_devlink_params, 11357f36a77aSJiri Pirko ARRAY_SIZE(nsim_devlink_params)); 11367f36a77aSJiri Pirko err_dl_unregister: 11377f36a77aSJiri Pirko devlink_unregister(devlink); 11387f36a77aSJiri Pirko err_fib_destroy: 11397f36a77aSJiri Pirko nsim_fib_destroy(devlink, nsim_dev->fib_data); 11407f36a77aSJiri Pirko err_resources_unregister: 11417f36a77aSJiri Pirko devlink_resources_unregister(devlink, NULL); 11427f36a77aSJiri Pirko err_devlink_free: 11437f36a77aSJiri Pirko devlink_free(devlink); 1144bfcccfe7SJakub Kicinski return err; 11457f36a77aSJiri Pirko } 11467f36a77aSJiri Pirko 114775ba029fSJiri Pirko static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev) 114875ba029fSJiri Pirko { 114975ba029fSJiri Pirko struct devlink *devlink = priv_to_devlink(nsim_dev); 115075ba029fSJiri Pirko 115175ba029fSJiri Pirko if (devlink_is_reload_failed(devlink)) 115275ba029fSJiri Pirko return; 11538526ad96STaehee Yoo debugfs_remove(nsim_dev->take_snapshot); 115475ba029fSJiri Pirko nsim_dev_port_del_all(nsim_dev); 115582c93a87SJiri Pirko nsim_dev_health_exit(nsim_dev); 115675ba029fSJiri Pirko nsim_dev_traps_exit(devlink); 115775ba029fSJiri Pirko nsim_dev_dummy_region_exit(nsim_dev); 115875ba029fSJiri Pirko mutex_destroy(&nsim_dev->port_list_lock); 115975ba029fSJiri Pirko nsim_fib_destroy(devlink, nsim_dev->fib_data); 116075ba029fSJiri Pirko } 116175ba029fSJiri Pirko 1162bfcccfe7SJakub Kicinski void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev) 11637f36a77aSJiri Pirko { 1164bfcccfe7SJakub Kicinski struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); 11657f36a77aSJiri Pirko struct devlink *devlink = priv_to_devlink(nsim_dev); 11667f36a77aSJiri Pirko 1167a0c76345SJiri Pirko devlink_reload_disable(devlink); 1168a0c76345SJiri Pirko 116975ba029fSJiri Pirko nsim_dev_reload_destroy(nsim_dev); 117075ba029fSJiri Pirko 11717f36a77aSJiri Pirko nsim_bpf_dev_exit(nsim_dev); 11727f36a77aSJiri Pirko nsim_dev_debugfs_exit(nsim_dev); 11737f36a77aSJiri Pirko devlink_params_unregister(devlink, nsim_devlink_params, 11747f36a77aSJiri Pirko ARRAY_SIZE(nsim_devlink_params)); 11757f36a77aSJiri Pirko devlink_unregister(devlink); 11767f36a77aSJiri Pirko devlink_resources_unregister(devlink, NULL); 11777f36a77aSJiri Pirko devlink_free(devlink); 11787f36a77aSJiri Pirko } 11797f36a77aSJiri Pirko 1180794b2c05SJiri Pirko static struct nsim_dev_port * 1181794b2c05SJiri Pirko __nsim_dev_port_lookup(struct nsim_dev *nsim_dev, unsigned int port_index) 1182794b2c05SJiri Pirko { 1183794b2c05SJiri Pirko struct nsim_dev_port *nsim_dev_port; 1184794b2c05SJiri Pirko 1185794b2c05SJiri Pirko list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) 1186794b2c05SJiri Pirko if (nsim_dev_port->port_index == port_index) 1187794b2c05SJiri Pirko return nsim_dev_port; 1188794b2c05SJiri Pirko return NULL; 1189794b2c05SJiri Pirko } 1190794b2c05SJiri Pirko 1191794b2c05SJiri Pirko int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev, 1192794b2c05SJiri Pirko unsigned int port_index) 1193794b2c05SJiri Pirko { 1194794b2c05SJiri Pirko struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); 1195794b2c05SJiri Pirko int err; 1196794b2c05SJiri Pirko 1197794b2c05SJiri Pirko mutex_lock(&nsim_dev->port_list_lock); 1198794b2c05SJiri Pirko if (__nsim_dev_port_lookup(nsim_dev, port_index)) 1199794b2c05SJiri Pirko err = -EEXIST; 1200794b2c05SJiri Pirko else 1201794b2c05SJiri Pirko err = __nsim_dev_port_add(nsim_dev, port_index); 1202794b2c05SJiri Pirko mutex_unlock(&nsim_dev->port_list_lock); 1203794b2c05SJiri Pirko return err; 1204794b2c05SJiri Pirko } 1205794b2c05SJiri Pirko 1206794b2c05SJiri Pirko int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev, 1207794b2c05SJiri Pirko unsigned int port_index) 1208794b2c05SJiri Pirko { 1209794b2c05SJiri Pirko struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); 1210794b2c05SJiri Pirko struct nsim_dev_port *nsim_dev_port; 1211794b2c05SJiri Pirko int err = 0; 1212794b2c05SJiri Pirko 1213794b2c05SJiri Pirko mutex_lock(&nsim_dev->port_list_lock); 1214794b2c05SJiri Pirko nsim_dev_port = __nsim_dev_port_lookup(nsim_dev, port_index); 1215794b2c05SJiri Pirko if (!nsim_dev_port) 1216794b2c05SJiri Pirko err = -ENOENT; 1217794b2c05SJiri Pirko else 1218794b2c05SJiri Pirko __nsim_dev_port_del(nsim_dev_port); 1219794b2c05SJiri Pirko mutex_unlock(&nsim_dev->port_list_lock); 1220794b2c05SJiri Pirko return err; 1221794b2c05SJiri Pirko } 1222794b2c05SJiri Pirko 1223d514f41eSJiri Pirko int nsim_dev_init(void) 1224d514f41eSJiri Pirko { 1225ab1d0cc0SJiri Pirko nsim_dev_ddir = debugfs_create_dir(DRV_NAME, NULL); 122634611e69Skbuild test robot return PTR_ERR_OR_ZERO(nsim_dev_ddir); 1227d514f41eSJiri Pirko } 1228d514f41eSJiri Pirko 1229d514f41eSJiri Pirko void nsim_dev_exit(void) 1230d514f41eSJiri Pirko { 1231d514f41eSJiri Pirko debugfs_remove_recursive(nsim_dev_ddir); 1232d514f41eSJiri Pirko } 1233