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, 994418f862SJiri Pirko }; 1004418f862SJiri Pirko 101d3cbb907SJiri Pirko static ssize_t nsim_dev_trap_fa_cookie_read(struct file *file, 102d3cbb907SJiri Pirko char __user *data, 103d3cbb907SJiri Pirko size_t count, loff_t *ppos) 104d3cbb907SJiri Pirko { 105d3cbb907SJiri Pirko struct nsim_dev *nsim_dev = file->private_data; 106d3cbb907SJiri Pirko struct flow_action_cookie *fa_cookie; 107d3cbb907SJiri Pirko unsigned int buf_len; 108d3cbb907SJiri Pirko ssize_t ret; 109d3cbb907SJiri Pirko char *buf; 110d3cbb907SJiri Pirko 111d3cbb907SJiri Pirko spin_lock(&nsim_dev->fa_cookie_lock); 112d3cbb907SJiri Pirko fa_cookie = nsim_dev->fa_cookie; 113d3cbb907SJiri Pirko if (!fa_cookie) { 114d3cbb907SJiri Pirko ret = -EINVAL; 115d3cbb907SJiri Pirko goto errout; 116d3cbb907SJiri Pirko } 117d3cbb907SJiri Pirko buf_len = fa_cookie->cookie_len * 2; 118d3cbb907SJiri Pirko buf = kmalloc(buf_len, GFP_ATOMIC); 119d3cbb907SJiri Pirko if (!buf) { 120d3cbb907SJiri Pirko ret = -ENOMEM; 121d3cbb907SJiri Pirko goto errout; 122d3cbb907SJiri Pirko } 123d3cbb907SJiri Pirko bin2hex(buf, fa_cookie->cookie, fa_cookie->cookie_len); 124d3cbb907SJiri Pirko spin_unlock(&nsim_dev->fa_cookie_lock); 125d3cbb907SJiri Pirko 126d3cbb907SJiri Pirko ret = simple_read_from_buffer(data, count, ppos, buf, buf_len); 127d3cbb907SJiri Pirko 128d3cbb907SJiri Pirko kfree(buf); 129d3cbb907SJiri Pirko return ret; 130d3cbb907SJiri Pirko 131d3cbb907SJiri Pirko errout: 132d3cbb907SJiri Pirko spin_unlock(&nsim_dev->fa_cookie_lock); 133d3cbb907SJiri Pirko return ret; 134d3cbb907SJiri Pirko } 135d3cbb907SJiri Pirko 136d3cbb907SJiri Pirko static ssize_t nsim_dev_trap_fa_cookie_write(struct file *file, 137d3cbb907SJiri Pirko const char __user *data, 138d3cbb907SJiri Pirko size_t count, loff_t *ppos) 139d3cbb907SJiri Pirko { 140d3cbb907SJiri Pirko struct nsim_dev *nsim_dev = file->private_data; 141d3cbb907SJiri Pirko struct flow_action_cookie *fa_cookie; 142d3cbb907SJiri Pirko size_t cookie_len; 143d3cbb907SJiri Pirko ssize_t ret; 144d3cbb907SJiri Pirko char *buf; 145d3cbb907SJiri Pirko 146d3cbb907SJiri Pirko if (*ppos != 0) 147d3cbb907SJiri Pirko return -EINVAL; 148d3cbb907SJiri Pirko cookie_len = (count - 1) / 2; 149d3cbb907SJiri Pirko if ((count - 1) % 2) 150d3cbb907SJiri Pirko return -EINVAL; 151d3cbb907SJiri Pirko buf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); 152d3cbb907SJiri Pirko if (!buf) 153d3cbb907SJiri Pirko return -ENOMEM; 154d3cbb907SJiri Pirko 155d3cbb907SJiri Pirko ret = simple_write_to_buffer(buf, count, ppos, data, count); 156d3cbb907SJiri Pirko if (ret < 0) 157d3cbb907SJiri Pirko goto free_buf; 158d3cbb907SJiri Pirko 159d3cbb907SJiri Pirko fa_cookie = kmalloc(sizeof(*fa_cookie) + cookie_len, 160d3cbb907SJiri Pirko GFP_KERNEL | __GFP_NOWARN); 161d3cbb907SJiri Pirko if (!fa_cookie) { 162d3cbb907SJiri Pirko ret = -ENOMEM; 163d3cbb907SJiri Pirko goto free_buf; 164d3cbb907SJiri Pirko } 165d3cbb907SJiri Pirko 166d3cbb907SJiri Pirko fa_cookie->cookie_len = cookie_len; 167d3cbb907SJiri Pirko ret = hex2bin(fa_cookie->cookie, buf, cookie_len); 168d3cbb907SJiri Pirko if (ret) 169d3cbb907SJiri Pirko goto free_fa_cookie; 170d3cbb907SJiri Pirko kfree(buf); 171d3cbb907SJiri Pirko 172d3cbb907SJiri Pirko spin_lock(&nsim_dev->fa_cookie_lock); 173d3cbb907SJiri Pirko kfree(nsim_dev->fa_cookie); 174d3cbb907SJiri Pirko nsim_dev->fa_cookie = fa_cookie; 175d3cbb907SJiri Pirko spin_unlock(&nsim_dev->fa_cookie_lock); 176d3cbb907SJiri Pirko 177d3cbb907SJiri Pirko return count; 178d3cbb907SJiri Pirko 179d3cbb907SJiri Pirko free_fa_cookie: 180d3cbb907SJiri Pirko kfree(fa_cookie); 181d3cbb907SJiri Pirko free_buf: 182d3cbb907SJiri Pirko kfree(buf); 183d3cbb907SJiri Pirko return ret; 184d3cbb907SJiri Pirko } 185d3cbb907SJiri Pirko 186d3cbb907SJiri Pirko static const struct file_operations nsim_dev_trap_fa_cookie_fops = { 187d3cbb907SJiri Pirko .open = simple_open, 188d3cbb907SJiri Pirko .read = nsim_dev_trap_fa_cookie_read, 189d3cbb907SJiri Pirko .write = nsim_dev_trap_fa_cookie_write, 190d3cbb907SJiri Pirko .llseek = generic_file_llseek, 191d3cbb907SJiri Pirko }; 192d3cbb907SJiri Pirko 193d514f41eSJiri Pirko static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev) 194d514f41eSJiri Pirko { 1956fb8852bSTaehee Yoo char dev_ddir_name[sizeof(DRV_NAME) + 10]; 196d514f41eSJiri Pirko 197ab1d0cc0SJiri Pirko sprintf(dev_ddir_name, DRV_NAME "%u", nsim_dev->nsim_bus_dev->dev.id); 198d514f41eSJiri Pirko nsim_dev->ddir = debugfs_create_dir(dev_ddir_name, nsim_dev_ddir); 1996556ff32STaehee Yoo if (IS_ERR(nsim_dev->ddir)) 2006556ff32STaehee Yoo return PTR_ERR(nsim_dev->ddir); 201ab1d0cc0SJiri Pirko nsim_dev->ports_ddir = debugfs_create_dir("ports", nsim_dev->ddir); 2026556ff32STaehee Yoo if (IS_ERR(nsim_dev->ports_ddir)) 2036556ff32STaehee Yoo return PTR_ERR(nsim_dev->ports_ddir); 204fa4dfc4aSJiri Pirko debugfs_create_bool("fw_update_status", 0600, nsim_dev->ddir, 205fa4dfc4aSJiri Pirko &nsim_dev->fw_update_status); 206150e8f8aSJiri Pirko debugfs_create_u32("max_macs", 0600, nsim_dev->ddir, 207150e8f8aSJiri Pirko &nsim_dev->max_macs); 208150e8f8aSJiri Pirko debugfs_create_bool("test1", 0600, nsim_dev->ddir, 209150e8f8aSJiri Pirko &nsim_dev->test1); 2108526ad96STaehee Yoo nsim_dev->take_snapshot = debugfs_create_file("take_snapshot", 2118526ad96STaehee Yoo 0200, 2128526ad96STaehee Yoo nsim_dev->ddir, 2138526ad96STaehee Yoo nsim_dev, 2144418f862SJiri Pirko &nsim_dev_take_snapshot_fops); 215155ddfc5SJiri Pirko debugfs_create_bool("dont_allow_reload", 0600, nsim_dev->ddir, 216155ddfc5SJiri Pirko &nsim_dev->dont_allow_reload); 217155ddfc5SJiri Pirko debugfs_create_bool("fail_reload", 0600, nsim_dev->ddir, 218155ddfc5SJiri Pirko &nsim_dev->fail_reload); 219d3cbb907SJiri Pirko debugfs_create_file("trap_flow_action_cookie", 0600, nsim_dev->ddir, 220d3cbb907SJiri Pirko nsim_dev, &nsim_dev_trap_fa_cookie_fops); 2210dc8249aSIdo Schimmel debugfs_create_bool("fail_trap_group_set", 0600, 2220dc8249aSIdo Schimmel nsim_dev->ddir, 2230dc8249aSIdo Schimmel &nsim_dev->fail_trap_group_set); 224ad188458SIdo Schimmel debugfs_create_bool("fail_trap_policer_set", 0600, 225ad188458SIdo Schimmel nsim_dev->ddir, 226ad188458SIdo Schimmel &nsim_dev->fail_trap_policer_set); 227ad188458SIdo Schimmel debugfs_create_bool("fail_trap_policer_counter_get", 0600, 228ad188458SIdo Schimmel nsim_dev->ddir, 229ad188458SIdo Schimmel &nsim_dev->fail_trap_policer_counter_get); 230424be63aSJakub Kicinski nsim_udp_tunnels_debugfs_create(nsim_dev); 231d514f41eSJiri Pirko return 0; 232d514f41eSJiri Pirko } 233d514f41eSJiri Pirko 234d514f41eSJiri Pirko static void nsim_dev_debugfs_exit(struct nsim_dev *nsim_dev) 235d514f41eSJiri Pirko { 236ab1d0cc0SJiri Pirko debugfs_remove_recursive(nsim_dev->ports_ddir); 237d514f41eSJiri Pirko debugfs_remove_recursive(nsim_dev->ddir); 238d514f41eSJiri Pirko } 239d514f41eSJiri Pirko 2408320d145SJiri Pirko static int nsim_dev_port_debugfs_init(struct nsim_dev *nsim_dev, 2418320d145SJiri Pirko struct nsim_dev_port *nsim_dev_port) 2428320d145SJiri Pirko { 2438320d145SJiri Pirko char port_ddir_name[16]; 2448320d145SJiri Pirko char dev_link_name[32]; 2458320d145SJiri Pirko 2468320d145SJiri Pirko sprintf(port_ddir_name, "%u", nsim_dev_port->port_index); 2478320d145SJiri Pirko nsim_dev_port->ddir = debugfs_create_dir(port_ddir_name, 2488320d145SJiri Pirko nsim_dev->ports_ddir); 2496556ff32STaehee Yoo if (IS_ERR(nsim_dev_port->ddir)) 2506556ff32STaehee Yoo return PTR_ERR(nsim_dev_port->ddir); 2518320d145SJiri Pirko 2528320d145SJiri Pirko sprintf(dev_link_name, "../../../" DRV_NAME "%u", 2538320d145SJiri Pirko nsim_dev->nsim_bus_dev->dev.id); 2548320d145SJiri Pirko debugfs_create_symlink("dev", nsim_dev_port->ddir, dev_link_name); 2558320d145SJiri Pirko 2568320d145SJiri Pirko return 0; 2578320d145SJiri Pirko } 2588320d145SJiri Pirko 2598320d145SJiri Pirko static void nsim_dev_port_debugfs_exit(struct nsim_dev_port *nsim_dev_port) 2608320d145SJiri Pirko { 2618320d145SJiri Pirko debugfs_remove_recursive(nsim_dev_port->ddir); 2628320d145SJiri Pirko } 2638320d145SJiri Pirko 2648fb4bc6fSJiri Pirko static int nsim_dev_resources_register(struct devlink *devlink) 2658fb4bc6fSJiri Pirko { 2668fb4bc6fSJiri Pirko struct devlink_resource_size_params params = { 2678fb4bc6fSJiri Pirko .size_max = (u64)-1, 2688fb4bc6fSJiri Pirko .size_granularity = 1, 2698fb4bc6fSJiri Pirko .unit = DEVLINK_RESOURCE_UNIT_ENTRY 2708fb4bc6fSJiri Pirko }; 2718fb4bc6fSJiri Pirko int err; 2728fb4bc6fSJiri Pirko 2738fb4bc6fSJiri Pirko /* Resources for IPv4 */ 2748fb4bc6fSJiri Pirko err = devlink_resource_register(devlink, "IPv4", (u64)-1, 2758fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4, 2768fb4bc6fSJiri Pirko DEVLINK_RESOURCE_ID_PARENT_TOP, 2778fb4bc6fSJiri Pirko ¶ms); 2788fb4bc6fSJiri Pirko if (err) { 2798fb4bc6fSJiri Pirko pr_err("Failed to register IPv4 top resource\n"); 2808fb4bc6fSJiri Pirko goto out; 2818fb4bc6fSJiri Pirko } 2828fb4bc6fSJiri Pirko 283a5facc4cSJiri Pirko err = devlink_resource_register(devlink, "fib", (u64)-1, 2848fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4_FIB, 2858fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4, ¶ms); 2868fb4bc6fSJiri Pirko if (err) { 2878fb4bc6fSJiri Pirko pr_err("Failed to register IPv4 FIB resource\n"); 2888fb4bc6fSJiri Pirko return err; 2898fb4bc6fSJiri Pirko } 2908fb4bc6fSJiri Pirko 291a5facc4cSJiri Pirko err = devlink_resource_register(devlink, "fib-rules", (u64)-1, 2928fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4_FIB_RULES, 2938fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4, ¶ms); 2948fb4bc6fSJiri Pirko if (err) { 2958fb4bc6fSJiri Pirko pr_err("Failed to register IPv4 FIB rules resource\n"); 2968fb4bc6fSJiri Pirko return err; 2978fb4bc6fSJiri Pirko } 2988fb4bc6fSJiri Pirko 2998fb4bc6fSJiri Pirko /* Resources for IPv6 */ 3008fb4bc6fSJiri Pirko err = devlink_resource_register(devlink, "IPv6", (u64)-1, 3018fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6, 3028fb4bc6fSJiri Pirko DEVLINK_RESOURCE_ID_PARENT_TOP, 3038fb4bc6fSJiri Pirko ¶ms); 3048fb4bc6fSJiri Pirko if (err) { 3058fb4bc6fSJiri Pirko pr_err("Failed to register IPv6 top resource\n"); 3068fb4bc6fSJiri Pirko goto out; 3078fb4bc6fSJiri Pirko } 3088fb4bc6fSJiri Pirko 309a5facc4cSJiri Pirko err = devlink_resource_register(devlink, "fib", (u64)-1, 3108fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6_FIB, 3118fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6, ¶ms); 3128fb4bc6fSJiri Pirko if (err) { 3138fb4bc6fSJiri Pirko pr_err("Failed to register IPv6 FIB resource\n"); 3148fb4bc6fSJiri Pirko return err; 3158fb4bc6fSJiri Pirko } 3168fb4bc6fSJiri Pirko 317a5facc4cSJiri Pirko err = devlink_resource_register(devlink, "fib-rules", (u64)-1, 3188fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6_FIB_RULES, 3198fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6, ¶ms); 3208fb4bc6fSJiri Pirko if (err) { 3218fb4bc6fSJiri Pirko pr_err("Failed to register IPv6 FIB rules resource\n"); 3228fb4bc6fSJiri Pirko return err; 3238fb4bc6fSJiri Pirko } 3248fb4bc6fSJiri Pirko 3258fb4bc6fSJiri Pirko out: 3268fb4bc6fSJiri Pirko return err; 3278fb4bc6fSJiri Pirko } 3288fb4bc6fSJiri Pirko 329150e8f8aSJiri Pirko enum nsim_devlink_param_id { 330150e8f8aSJiri Pirko NSIM_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, 331150e8f8aSJiri Pirko NSIM_DEVLINK_PARAM_ID_TEST1, 332150e8f8aSJiri Pirko }; 333150e8f8aSJiri Pirko 334150e8f8aSJiri Pirko static const struct devlink_param nsim_devlink_params[] = { 335150e8f8aSJiri Pirko DEVLINK_PARAM_GENERIC(MAX_MACS, 336150e8f8aSJiri Pirko BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), 337150e8f8aSJiri Pirko NULL, NULL, NULL), 338150e8f8aSJiri Pirko DEVLINK_PARAM_DRIVER(NSIM_DEVLINK_PARAM_ID_TEST1, 339150e8f8aSJiri Pirko "test1", DEVLINK_PARAM_TYPE_BOOL, 340150e8f8aSJiri Pirko BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), 341150e8f8aSJiri Pirko NULL, NULL, NULL), 342150e8f8aSJiri Pirko }; 343150e8f8aSJiri Pirko 344150e8f8aSJiri Pirko static void nsim_devlink_set_params_init_values(struct nsim_dev *nsim_dev, 345150e8f8aSJiri Pirko struct devlink *devlink) 346150e8f8aSJiri Pirko { 347150e8f8aSJiri Pirko union devlink_param_value value; 348150e8f8aSJiri Pirko 349150e8f8aSJiri Pirko value.vu32 = nsim_dev->max_macs; 350150e8f8aSJiri Pirko devlink_param_driverinit_value_set(devlink, 351150e8f8aSJiri Pirko DEVLINK_PARAM_GENERIC_ID_MAX_MACS, 352150e8f8aSJiri Pirko value); 353150e8f8aSJiri Pirko value.vbool = nsim_dev->test1; 354150e8f8aSJiri Pirko devlink_param_driverinit_value_set(devlink, 355150e8f8aSJiri Pirko NSIM_DEVLINK_PARAM_ID_TEST1, 356150e8f8aSJiri Pirko value); 357150e8f8aSJiri Pirko } 358150e8f8aSJiri Pirko 359150e8f8aSJiri Pirko static void nsim_devlink_param_load_driverinit_values(struct devlink *devlink) 360150e8f8aSJiri Pirko { 361150e8f8aSJiri Pirko struct nsim_dev *nsim_dev = devlink_priv(devlink); 362150e8f8aSJiri Pirko union devlink_param_value saved_value; 363150e8f8aSJiri Pirko int err; 364150e8f8aSJiri Pirko 365150e8f8aSJiri Pirko err = devlink_param_driverinit_value_get(devlink, 366150e8f8aSJiri Pirko DEVLINK_PARAM_GENERIC_ID_MAX_MACS, 367150e8f8aSJiri Pirko &saved_value); 368150e8f8aSJiri Pirko if (!err) 369150e8f8aSJiri Pirko nsim_dev->max_macs = saved_value.vu32; 370150e8f8aSJiri Pirko err = devlink_param_driverinit_value_get(devlink, 371150e8f8aSJiri Pirko NSIM_DEVLINK_PARAM_ID_TEST1, 372150e8f8aSJiri Pirko &saved_value); 373150e8f8aSJiri Pirko if (!err) 374150e8f8aSJiri Pirko nsim_dev->test1 = saved_value.vbool; 375150e8f8aSJiri Pirko } 376150e8f8aSJiri Pirko 3774418f862SJiri Pirko #define NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX 16 3784418f862SJiri Pirko 379e8937681SJacob Keller static const struct devlink_region_ops dummy_region_ops = { 380e8937681SJacob Keller .name = "dummy", 381a0a09f6bSJacob Keller .destructor = &kfree, 3823fe0fd53SJacob Keller .snapshot = nsim_dev_take_snapshot, 383e8937681SJacob Keller }; 384e8937681SJacob Keller 3854418f862SJiri Pirko static int nsim_dev_dummy_region_init(struct nsim_dev *nsim_dev, 3864418f862SJiri Pirko struct devlink *devlink) 3874418f862SJiri Pirko { 3884418f862SJiri Pirko nsim_dev->dummy_region = 389e8937681SJacob Keller devlink_region_create(devlink, &dummy_region_ops, 3904418f862SJiri Pirko NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX, 3914418f862SJiri Pirko NSIM_DEV_DUMMY_REGION_SIZE); 3924418f862SJiri Pirko return PTR_ERR_OR_ZERO(nsim_dev->dummy_region); 3934418f862SJiri Pirko } 3944418f862SJiri Pirko 3954418f862SJiri Pirko static void nsim_dev_dummy_region_exit(struct nsim_dev *nsim_dev) 3964418f862SJiri Pirko { 3974418f862SJiri Pirko devlink_region_destroy(nsim_dev->dummy_region); 3984418f862SJiri Pirko } 3994418f862SJiri Pirko 400da58f90fSIdo Schimmel struct nsim_trap_item { 401da58f90fSIdo Schimmel void *trap_ctx; 402da58f90fSIdo Schimmel enum devlink_trap_action action; 403da58f90fSIdo Schimmel }; 404da58f90fSIdo Schimmel 405da58f90fSIdo Schimmel struct nsim_trap_data { 406da58f90fSIdo Schimmel struct delayed_work trap_report_dw; 407da58f90fSIdo Schimmel struct nsim_trap_item *trap_items_arr; 408ad188458SIdo Schimmel u64 *trap_policers_cnt_arr; 409da58f90fSIdo Schimmel struct nsim_dev *nsim_dev; 410da58f90fSIdo Schimmel spinlock_t trap_lock; /* Protects trap_items_arr */ 411da58f90fSIdo Schimmel }; 412da58f90fSIdo Schimmel 4139e087457SIdo Schimmel /* All driver-specific traps must be documented in 41404e4272cSJacob Keller * Documentation/networking/devlink/netdevsim.rst 4159e087457SIdo Schimmel */ 416da58f90fSIdo Schimmel enum { 417da58f90fSIdo Schimmel NSIM_TRAP_ID_BASE = DEVLINK_TRAP_GENERIC_ID_MAX, 418da58f90fSIdo Schimmel NSIM_TRAP_ID_FID_MISS, 419da58f90fSIdo Schimmel }; 420da58f90fSIdo Schimmel 421da58f90fSIdo Schimmel #define NSIM_TRAP_NAME_FID_MISS "fid_miss" 422da58f90fSIdo Schimmel 423da58f90fSIdo Schimmel #define NSIM_TRAP_METADATA DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT 424da58f90fSIdo Schimmel 425da58f90fSIdo Schimmel #define NSIM_TRAP_DROP(_id, _group_id) \ 426da58f90fSIdo Schimmel DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \ 427107f1678SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 428da58f90fSIdo Schimmel NSIM_TRAP_METADATA) 429d3cbb907SJiri Pirko #define NSIM_TRAP_DROP_EXT(_id, _group_id, _metadata) \ 430d3cbb907SJiri Pirko DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \ 431107f1678SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 432d3cbb907SJiri Pirko NSIM_TRAP_METADATA | (_metadata)) 433da58f90fSIdo Schimmel #define NSIM_TRAP_EXCEPTION(_id, _group_id) \ 434da58f90fSIdo Schimmel DEVLINK_TRAP_GENERIC(EXCEPTION, TRAP, _id, \ 435107f1678SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 436da58f90fSIdo Schimmel NSIM_TRAP_METADATA) 43718979367SIdo Schimmel #define NSIM_TRAP_CONTROL(_id, _group_id, _action) \ 43818979367SIdo Schimmel DEVLINK_TRAP_GENERIC(CONTROL, _action, _id, \ 43918979367SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 44018979367SIdo Schimmel NSIM_TRAP_METADATA) 441da58f90fSIdo Schimmel #define NSIM_TRAP_DRIVER_EXCEPTION(_id, _group_id) \ 442da58f90fSIdo Schimmel DEVLINK_TRAP_DRIVER(EXCEPTION, TRAP, NSIM_TRAP_ID_##_id, \ 443da58f90fSIdo Schimmel NSIM_TRAP_NAME_##_id, \ 444107f1678SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 445da58f90fSIdo Schimmel NSIM_TRAP_METADATA) 446da58f90fSIdo Schimmel 447ad188458SIdo Schimmel #define NSIM_DEV_TRAP_POLICER_MIN_RATE 1 448ad188458SIdo Schimmel #define NSIM_DEV_TRAP_POLICER_MAX_RATE 8000 449ad188458SIdo Schimmel #define NSIM_DEV_TRAP_POLICER_MIN_BURST 8 450ad188458SIdo Schimmel #define NSIM_DEV_TRAP_POLICER_MAX_BURST 65536 451ad188458SIdo Schimmel 452ad188458SIdo Schimmel #define NSIM_TRAP_POLICER(_id, _rate, _burst) \ 453ad188458SIdo Schimmel DEVLINK_TRAP_POLICER(_id, _rate, _burst, \ 454ad188458SIdo Schimmel NSIM_DEV_TRAP_POLICER_MAX_RATE, \ 455ad188458SIdo Schimmel NSIM_DEV_TRAP_POLICER_MIN_RATE, \ 456ad188458SIdo Schimmel NSIM_DEV_TRAP_POLICER_MAX_BURST, \ 457ad188458SIdo Schimmel NSIM_DEV_TRAP_POLICER_MIN_BURST) 458ad188458SIdo Schimmel 459ad188458SIdo Schimmel static const struct devlink_trap_policer nsim_trap_policers_arr[] = { 460ad188458SIdo Schimmel NSIM_TRAP_POLICER(1, 1000, 128), 461ad188458SIdo Schimmel NSIM_TRAP_POLICER(2, 2000, 256), 462ad188458SIdo Schimmel NSIM_TRAP_POLICER(3, 3000, 512), 463ad188458SIdo Schimmel }; 464ad188458SIdo Schimmel 465b29545d8SIdo Schimmel static const struct devlink_trap_group nsim_trap_groups_arr[] = { 466f9f54392SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS, 0), 467f9f54392SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(L3_DROPS, 1), 46885176f19SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(L3_EXCEPTIONS, 1), 469f9f54392SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(BUFFER_DROPS, 2), 470f9f54392SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(ACL_DROPS, 3), 47118979367SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(MC_SNOOPING, 3), 472b29545d8SIdo Schimmel }; 473b29545d8SIdo Schimmel 474da58f90fSIdo Schimmel static const struct devlink_trap nsim_traps_arr[] = { 475da58f90fSIdo Schimmel NSIM_TRAP_DROP(SMAC_MC, L2_DROPS), 476da58f90fSIdo Schimmel NSIM_TRAP_DROP(VLAN_TAG_MISMATCH, L2_DROPS), 477da58f90fSIdo Schimmel NSIM_TRAP_DROP(INGRESS_VLAN_FILTER, L2_DROPS), 478da58f90fSIdo Schimmel NSIM_TRAP_DROP(INGRESS_STP_FILTER, L2_DROPS), 479da58f90fSIdo Schimmel NSIM_TRAP_DROP(EMPTY_TX_LIST, L2_DROPS), 480da58f90fSIdo Schimmel NSIM_TRAP_DROP(PORT_LOOPBACK_FILTER, L2_DROPS), 481da58f90fSIdo Schimmel NSIM_TRAP_DRIVER_EXCEPTION(FID_MISS, L2_DROPS), 482da58f90fSIdo Schimmel NSIM_TRAP_DROP(BLACKHOLE_ROUTE, L3_DROPS), 48385176f19SIdo Schimmel NSIM_TRAP_EXCEPTION(TTL_ERROR, L3_EXCEPTIONS), 484da58f90fSIdo Schimmel NSIM_TRAP_DROP(TAIL_DROP, BUFFER_DROPS), 485d3cbb907SJiri Pirko NSIM_TRAP_DROP_EXT(INGRESS_FLOW_ACTION_DROP, ACL_DROPS, 486d3cbb907SJiri Pirko DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE), 487d3cbb907SJiri Pirko NSIM_TRAP_DROP_EXT(EGRESS_FLOW_ACTION_DROP, ACL_DROPS, 488d3cbb907SJiri Pirko DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE), 48918979367SIdo Schimmel NSIM_TRAP_CONTROL(IGMP_QUERY, MC_SNOOPING, MIRROR), 49018979367SIdo Schimmel NSIM_TRAP_CONTROL(IGMP_V1_REPORT, MC_SNOOPING, TRAP), 491da58f90fSIdo Schimmel }; 492da58f90fSIdo Schimmel 493da58f90fSIdo Schimmel #define NSIM_TRAP_L4_DATA_LEN 100 494da58f90fSIdo Schimmel 495da58f90fSIdo Schimmel static struct sk_buff *nsim_dev_trap_skb_build(void) 496da58f90fSIdo Schimmel { 497da58f90fSIdo Schimmel int tot_len, data_len = NSIM_TRAP_L4_DATA_LEN; 498da58f90fSIdo Schimmel struct sk_buff *skb; 499da58f90fSIdo Schimmel struct udphdr *udph; 500da58f90fSIdo Schimmel struct ethhdr *eth; 501da58f90fSIdo Schimmel struct iphdr *iph; 502da58f90fSIdo Schimmel 503da58f90fSIdo Schimmel skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); 504da58f90fSIdo Schimmel if (!skb) 505da58f90fSIdo Schimmel return NULL; 506da58f90fSIdo Schimmel tot_len = sizeof(struct iphdr) + sizeof(struct udphdr) + data_len; 507da58f90fSIdo Schimmel 50858a406deSIdo Schimmel skb_reset_mac_header(skb); 509da58f90fSIdo Schimmel eth = skb_put(skb, sizeof(struct ethhdr)); 510da58f90fSIdo Schimmel eth_random_addr(eth->h_dest); 511da58f90fSIdo Schimmel eth_random_addr(eth->h_source); 512da58f90fSIdo Schimmel eth->h_proto = htons(ETH_P_IP); 513da58f90fSIdo Schimmel skb->protocol = htons(ETH_P_IP); 514da58f90fSIdo Schimmel 51558a406deSIdo Schimmel skb_set_network_header(skb, skb->len); 516da58f90fSIdo Schimmel iph = skb_put(skb, sizeof(struct iphdr)); 517da58f90fSIdo Schimmel iph->protocol = IPPROTO_UDP; 518da58f90fSIdo Schimmel iph->saddr = in_aton("192.0.2.1"); 519da58f90fSIdo Schimmel iph->daddr = in_aton("198.51.100.1"); 520da58f90fSIdo Schimmel iph->version = 0x4; 521da58f90fSIdo Schimmel iph->frag_off = 0; 522da58f90fSIdo Schimmel iph->ihl = 0x5; 523da58f90fSIdo Schimmel iph->tot_len = htons(tot_len); 524da58f90fSIdo Schimmel iph->ttl = 100; 525d9bd6d27SYueHaibing iph->check = 0; 526d9bd6d27SYueHaibing iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); 527da58f90fSIdo Schimmel 52858a406deSIdo Schimmel skb_set_transport_header(skb, skb->len); 529da58f90fSIdo Schimmel udph = skb_put_zero(skb, sizeof(struct udphdr) + data_len); 530da58f90fSIdo Schimmel get_random_bytes(&udph->source, sizeof(u16)); 531da58f90fSIdo Schimmel get_random_bytes(&udph->dest, sizeof(u16)); 532da58f90fSIdo Schimmel udph->len = htons(sizeof(struct udphdr) + data_len); 533da58f90fSIdo Schimmel 534da58f90fSIdo Schimmel return skb; 535da58f90fSIdo Schimmel } 536da58f90fSIdo Schimmel 537da58f90fSIdo Schimmel static void nsim_dev_trap_report(struct nsim_dev_port *nsim_dev_port) 538da58f90fSIdo Schimmel { 539da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = nsim_dev_port->ns->nsim_dev; 540da58f90fSIdo Schimmel struct devlink *devlink = priv_to_devlink(nsim_dev); 541da58f90fSIdo Schimmel struct nsim_trap_data *nsim_trap_data; 542da58f90fSIdo Schimmel int i; 543da58f90fSIdo Schimmel 544da58f90fSIdo Schimmel nsim_trap_data = nsim_dev->trap_data; 545da58f90fSIdo Schimmel 546da58f90fSIdo Schimmel spin_lock(&nsim_trap_data->trap_lock); 547da58f90fSIdo Schimmel for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) { 548d3cbb907SJiri Pirko struct flow_action_cookie *fa_cookie = NULL; 549da58f90fSIdo Schimmel struct nsim_trap_item *nsim_trap_item; 550da58f90fSIdo Schimmel struct sk_buff *skb; 551d3cbb907SJiri Pirko bool has_fa_cookie; 552d3cbb907SJiri Pirko 553d3cbb907SJiri Pirko has_fa_cookie = nsim_traps_arr[i].metadata_cap & 554d3cbb907SJiri Pirko DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE; 555da58f90fSIdo Schimmel 556da58f90fSIdo Schimmel nsim_trap_item = &nsim_trap_data->trap_items_arr[i]; 557da58f90fSIdo Schimmel if (nsim_trap_item->action == DEVLINK_TRAP_ACTION_DROP) 558da58f90fSIdo Schimmel continue; 559da58f90fSIdo Schimmel 560da58f90fSIdo Schimmel skb = nsim_dev_trap_skb_build(); 561da58f90fSIdo Schimmel if (!skb) 562da58f90fSIdo Schimmel continue; 563da58f90fSIdo Schimmel skb->dev = nsim_dev_port->ns->netdev; 564da58f90fSIdo Schimmel 565da58f90fSIdo Schimmel /* Trapped packets are usually passed to devlink in softIRQ, 566da58f90fSIdo Schimmel * but in this case they are generated in a workqueue. Disable 567da58f90fSIdo Schimmel * softIRQs to prevent lockdep from complaining about 568da58f90fSIdo Schimmel * "incosistent lock state". 569da58f90fSIdo Schimmel */ 570d3cbb907SJiri Pirko 571d3cbb907SJiri Pirko spin_lock_bh(&nsim_dev->fa_cookie_lock); 572d3cbb907SJiri Pirko fa_cookie = has_fa_cookie ? nsim_dev->fa_cookie : NULL; 573da58f90fSIdo Schimmel devlink_trap_report(devlink, skb, nsim_trap_item->trap_ctx, 574d3cbb907SJiri Pirko &nsim_dev_port->devlink_port, fa_cookie); 575d3cbb907SJiri Pirko spin_unlock_bh(&nsim_dev->fa_cookie_lock); 576da58f90fSIdo Schimmel consume_skb(skb); 577da58f90fSIdo Schimmel } 578da58f90fSIdo Schimmel spin_unlock(&nsim_trap_data->trap_lock); 579da58f90fSIdo Schimmel } 580da58f90fSIdo Schimmel 581da58f90fSIdo Schimmel #define NSIM_TRAP_REPORT_INTERVAL_MS 100 582da58f90fSIdo Schimmel 583da58f90fSIdo Schimmel static void nsim_dev_trap_report_work(struct work_struct *work) 584da58f90fSIdo Schimmel { 585da58f90fSIdo Schimmel struct nsim_trap_data *nsim_trap_data; 586da58f90fSIdo Schimmel struct nsim_dev_port *nsim_dev_port; 587da58f90fSIdo Schimmel struct nsim_dev *nsim_dev; 588da58f90fSIdo Schimmel 589da58f90fSIdo Schimmel nsim_trap_data = container_of(work, struct nsim_trap_data, 590da58f90fSIdo Schimmel trap_report_dw.work); 591da58f90fSIdo Schimmel nsim_dev = nsim_trap_data->nsim_dev; 592da58f90fSIdo Schimmel 593da58f90fSIdo Schimmel /* For each running port and enabled packet trap, generate a UDP 594da58f90fSIdo Schimmel * packet with a random 5-tuple and report it. 595da58f90fSIdo Schimmel */ 596da58f90fSIdo Schimmel mutex_lock(&nsim_dev->port_list_lock); 597da58f90fSIdo Schimmel list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) { 598da58f90fSIdo Schimmel if (!netif_running(nsim_dev_port->ns->netdev)) 599da58f90fSIdo Schimmel continue; 600da58f90fSIdo Schimmel 601da58f90fSIdo Schimmel nsim_dev_trap_report(nsim_dev_port); 602da58f90fSIdo Schimmel } 603da58f90fSIdo Schimmel mutex_unlock(&nsim_dev->port_list_lock); 604da58f90fSIdo Schimmel 605da58f90fSIdo Schimmel schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, 606da58f90fSIdo Schimmel msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS)); 607da58f90fSIdo Schimmel } 608da58f90fSIdo Schimmel 609da58f90fSIdo Schimmel static int nsim_dev_traps_init(struct devlink *devlink) 610da58f90fSIdo Schimmel { 611ad188458SIdo Schimmel size_t policers_count = ARRAY_SIZE(nsim_trap_policers_arr); 612da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 613da58f90fSIdo Schimmel struct nsim_trap_data *nsim_trap_data; 614da58f90fSIdo Schimmel int err; 615da58f90fSIdo Schimmel 616da58f90fSIdo Schimmel nsim_trap_data = kzalloc(sizeof(*nsim_trap_data), GFP_KERNEL); 617da58f90fSIdo Schimmel if (!nsim_trap_data) 618da58f90fSIdo Schimmel return -ENOMEM; 619da58f90fSIdo Schimmel 620da58f90fSIdo Schimmel nsim_trap_data->trap_items_arr = kcalloc(ARRAY_SIZE(nsim_traps_arr), 621da58f90fSIdo Schimmel sizeof(struct nsim_trap_item), 622da58f90fSIdo Schimmel GFP_KERNEL); 623da58f90fSIdo Schimmel if (!nsim_trap_data->trap_items_arr) { 624da58f90fSIdo Schimmel err = -ENOMEM; 625da58f90fSIdo Schimmel goto err_trap_data_free; 626da58f90fSIdo Schimmel } 627da58f90fSIdo Schimmel 628ad188458SIdo Schimmel nsim_trap_data->trap_policers_cnt_arr = kcalloc(policers_count, 629ad188458SIdo Schimmel sizeof(u64), 630ad188458SIdo Schimmel GFP_KERNEL); 631ad188458SIdo Schimmel if (!nsim_trap_data->trap_policers_cnt_arr) { 632ad188458SIdo Schimmel err = -ENOMEM; 633ad188458SIdo Schimmel goto err_trap_items_free; 634ad188458SIdo Schimmel } 635ad188458SIdo Schimmel 636da58f90fSIdo Schimmel /* The lock is used to protect the action state of the registered 637da58f90fSIdo Schimmel * traps. The value is written by user and read in delayed work when 638da58f90fSIdo Schimmel * iterating over all the traps. 639da58f90fSIdo Schimmel */ 640da58f90fSIdo Schimmel spin_lock_init(&nsim_trap_data->trap_lock); 641da58f90fSIdo Schimmel nsim_trap_data->nsim_dev = nsim_dev; 642da58f90fSIdo Schimmel nsim_dev->trap_data = nsim_trap_data; 643da58f90fSIdo Schimmel 644ad188458SIdo Schimmel err = devlink_trap_policers_register(devlink, nsim_trap_policers_arr, 645ad188458SIdo Schimmel policers_count); 646ad188458SIdo Schimmel if (err) 647ad188458SIdo Schimmel goto err_trap_policers_cnt_free; 648ad188458SIdo Schimmel 649b29545d8SIdo Schimmel err = devlink_trap_groups_register(devlink, nsim_trap_groups_arr, 650b29545d8SIdo Schimmel ARRAY_SIZE(nsim_trap_groups_arr)); 651b29545d8SIdo Schimmel if (err) 652ad188458SIdo Schimmel goto err_trap_policers_unregister; 653b29545d8SIdo Schimmel 654da58f90fSIdo Schimmel err = devlink_traps_register(devlink, nsim_traps_arr, 655da58f90fSIdo Schimmel ARRAY_SIZE(nsim_traps_arr), NULL); 656da58f90fSIdo Schimmel if (err) 657b29545d8SIdo Schimmel goto err_trap_groups_unregister; 658da58f90fSIdo Schimmel 659da58f90fSIdo Schimmel INIT_DELAYED_WORK(&nsim_dev->trap_data->trap_report_dw, 660da58f90fSIdo Schimmel nsim_dev_trap_report_work); 661da58f90fSIdo Schimmel schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, 662da58f90fSIdo Schimmel msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS)); 663da58f90fSIdo Schimmel 664da58f90fSIdo Schimmel return 0; 665da58f90fSIdo Schimmel 666b29545d8SIdo Schimmel err_trap_groups_unregister: 667b29545d8SIdo Schimmel devlink_trap_groups_unregister(devlink, nsim_trap_groups_arr, 668b29545d8SIdo Schimmel ARRAY_SIZE(nsim_trap_groups_arr)); 669ad188458SIdo Schimmel err_trap_policers_unregister: 670ad188458SIdo Schimmel devlink_trap_policers_unregister(devlink, nsim_trap_policers_arr, 671ad188458SIdo Schimmel ARRAY_SIZE(nsim_trap_policers_arr)); 672ad188458SIdo Schimmel err_trap_policers_cnt_free: 673ad188458SIdo Schimmel kfree(nsim_trap_data->trap_policers_cnt_arr); 674da58f90fSIdo Schimmel err_trap_items_free: 675da58f90fSIdo Schimmel kfree(nsim_trap_data->trap_items_arr); 676da58f90fSIdo Schimmel err_trap_data_free: 677da58f90fSIdo Schimmel kfree(nsim_trap_data); 678da58f90fSIdo Schimmel return err; 679da58f90fSIdo Schimmel } 680da58f90fSIdo Schimmel 681da58f90fSIdo Schimmel static void nsim_dev_traps_exit(struct devlink *devlink) 682da58f90fSIdo Schimmel { 683da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 684da58f90fSIdo Schimmel 685da58f90fSIdo Schimmel cancel_delayed_work_sync(&nsim_dev->trap_data->trap_report_dw); 686da58f90fSIdo Schimmel devlink_traps_unregister(devlink, nsim_traps_arr, 687da58f90fSIdo Schimmel ARRAY_SIZE(nsim_traps_arr)); 688b29545d8SIdo Schimmel devlink_trap_groups_unregister(devlink, nsim_trap_groups_arr, 689b29545d8SIdo Schimmel ARRAY_SIZE(nsim_trap_groups_arr)); 690ad188458SIdo Schimmel devlink_trap_policers_unregister(devlink, nsim_trap_policers_arr, 691ad188458SIdo Schimmel ARRAY_SIZE(nsim_trap_policers_arr)); 692ad188458SIdo Schimmel kfree(nsim_dev->trap_data->trap_policers_cnt_arr); 693da58f90fSIdo Schimmel kfree(nsim_dev->trap_data->trap_items_arr); 694da58f90fSIdo Schimmel kfree(nsim_dev->trap_data); 695da58f90fSIdo Schimmel } 696da58f90fSIdo Schimmel 69775ba029fSJiri Pirko static int nsim_dev_reload_create(struct nsim_dev *nsim_dev, 69875ba029fSJiri Pirko struct netlink_ext_ack *extack); 69975ba029fSJiri Pirko static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev); 70075ba029fSJiri Pirko 701070c63f2SJiri Pirko static int nsim_dev_reload_down(struct devlink *devlink, bool netns_change, 70297691069SJiri Pirko struct netlink_ext_ack *extack) 70397691069SJiri Pirko { 70475ba029fSJiri Pirko struct nsim_dev *nsim_dev = devlink_priv(devlink); 70575ba029fSJiri Pirko 706155ddfc5SJiri Pirko if (nsim_dev->dont_allow_reload) { 707155ddfc5SJiri Pirko /* For testing purposes, user set debugfs dont_allow_reload 708155ddfc5SJiri Pirko * value to true. So forbid it. 709155ddfc5SJiri Pirko */ 710f9867b51SColin Ian King NL_SET_ERR_MSG_MOD(extack, "User forbid the reload for testing purposes"); 711155ddfc5SJiri Pirko return -EOPNOTSUPP; 712155ddfc5SJiri Pirko } 713155ddfc5SJiri Pirko 71475ba029fSJiri Pirko nsim_dev_reload_destroy(nsim_dev); 71597691069SJiri Pirko return 0; 71697691069SJiri Pirko } 71797691069SJiri Pirko 71897691069SJiri Pirko static int nsim_dev_reload_up(struct devlink *devlink, 7198fb4bc6fSJiri Pirko struct netlink_ext_ack *extack) 7208fb4bc6fSJiri Pirko { 721a5facc4cSJiri Pirko struct nsim_dev *nsim_dev = devlink_priv(devlink); 7228fb4bc6fSJiri Pirko 723155ddfc5SJiri Pirko if (nsim_dev->fail_reload) { 724155ddfc5SJiri Pirko /* For testing purposes, user set debugfs fail_reload 725155ddfc5SJiri Pirko * value to true. Fail right away. 726155ddfc5SJiri Pirko */ 727155ddfc5SJiri Pirko NL_SET_ERR_MSG_MOD(extack, "User setup the reload to fail for testing purposes"); 728155ddfc5SJiri Pirko return -EINVAL; 729155ddfc5SJiri Pirko } 730155ddfc5SJiri Pirko 73175ba029fSJiri Pirko return nsim_dev_reload_create(nsim_dev, extack); 7328fb4bc6fSJiri Pirko } 7338fb4bc6fSJiri Pirko 7348e23cc03SJiri Pirko static int nsim_dev_info_get(struct devlink *devlink, 7358e23cc03SJiri Pirko struct devlink_info_req *req, 7368e23cc03SJiri Pirko struct netlink_ext_ack *extack) 7378e23cc03SJiri Pirko { 7388e23cc03SJiri Pirko return devlink_info_driver_name_put(req, DRV_NAME); 7398e23cc03SJiri Pirko } 7408e23cc03SJiri Pirko 741fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_SIZE 500000 742fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_CHUNK_SIZE 1000 743fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_CHUNK_TIME_MS 10 744fa4dfc4aSJiri Pirko 745*bc75c054SJacob Keller static int nsim_dev_flash_update(struct devlink *devlink, 746*bc75c054SJacob Keller struct devlink_flash_update_params *params, 747fa4dfc4aSJiri Pirko struct netlink_ext_ack *extack) 748fa4dfc4aSJiri Pirko { 749fa4dfc4aSJiri Pirko struct nsim_dev *nsim_dev = devlink_priv(devlink); 750fa4dfc4aSJiri Pirko int i; 751fa4dfc4aSJiri Pirko 752fa4dfc4aSJiri Pirko if (nsim_dev->fw_update_status) { 753fa4dfc4aSJiri Pirko devlink_flash_update_begin_notify(devlink); 754fa4dfc4aSJiri Pirko devlink_flash_update_status_notify(devlink, 755fa4dfc4aSJiri Pirko "Preparing to flash", 756*bc75c054SJacob Keller params->component, 0, 0); 757fa4dfc4aSJiri Pirko } 758fa4dfc4aSJiri Pirko 759fa4dfc4aSJiri Pirko for (i = 0; i < NSIM_DEV_FLASH_SIZE / NSIM_DEV_FLASH_CHUNK_SIZE; i++) { 760fa4dfc4aSJiri Pirko if (nsim_dev->fw_update_status) 761fa4dfc4aSJiri Pirko devlink_flash_update_status_notify(devlink, "Flashing", 762*bc75c054SJacob Keller params->component, 763fa4dfc4aSJiri Pirko i * NSIM_DEV_FLASH_CHUNK_SIZE, 764fa4dfc4aSJiri Pirko NSIM_DEV_FLASH_SIZE); 765fa4dfc4aSJiri Pirko msleep(NSIM_DEV_FLASH_CHUNK_TIME_MS); 766fa4dfc4aSJiri Pirko } 767fa4dfc4aSJiri Pirko 768fa4dfc4aSJiri Pirko if (nsim_dev->fw_update_status) { 769fa4dfc4aSJiri Pirko devlink_flash_update_status_notify(devlink, "Flashing", 770*bc75c054SJacob Keller params->component, 771fa4dfc4aSJiri Pirko NSIM_DEV_FLASH_SIZE, 772fa4dfc4aSJiri Pirko NSIM_DEV_FLASH_SIZE); 773b311b001SShannon Nelson devlink_flash_update_timeout_notify(devlink, "Flash select", 774*bc75c054SJacob Keller params->component, 81); 775fa4dfc4aSJiri Pirko devlink_flash_update_status_notify(devlink, "Flashing done", 776*bc75c054SJacob Keller params->component, 0, 0); 777fa4dfc4aSJiri Pirko devlink_flash_update_end_notify(devlink); 778fa4dfc4aSJiri Pirko } 779fa4dfc4aSJiri Pirko 780fa4dfc4aSJiri Pirko return 0; 781fa4dfc4aSJiri Pirko } 782fa4dfc4aSJiri Pirko 783da58f90fSIdo Schimmel static struct nsim_trap_item * 784da58f90fSIdo Schimmel nsim_dev_trap_item_lookup(struct nsim_dev *nsim_dev, u16 trap_id) 785da58f90fSIdo Schimmel { 786da58f90fSIdo Schimmel struct nsim_trap_data *nsim_trap_data = nsim_dev->trap_data; 787da58f90fSIdo Schimmel int i; 788da58f90fSIdo Schimmel 789da58f90fSIdo Schimmel for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) { 790da58f90fSIdo Schimmel if (nsim_traps_arr[i].id == trap_id) 791da58f90fSIdo Schimmel return &nsim_trap_data->trap_items_arr[i]; 792da58f90fSIdo Schimmel } 793da58f90fSIdo Schimmel 794da58f90fSIdo Schimmel return NULL; 795da58f90fSIdo Schimmel } 796da58f90fSIdo Schimmel 797da58f90fSIdo Schimmel static int nsim_dev_devlink_trap_init(struct devlink *devlink, 798da58f90fSIdo Schimmel const struct devlink_trap *trap, 799da58f90fSIdo Schimmel void *trap_ctx) 800da58f90fSIdo Schimmel { 801da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 802da58f90fSIdo Schimmel struct nsim_trap_item *nsim_trap_item; 803da58f90fSIdo Schimmel 804da58f90fSIdo Schimmel nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id); 805da58f90fSIdo Schimmel if (WARN_ON(!nsim_trap_item)) 806da58f90fSIdo Schimmel return -ENOENT; 807da58f90fSIdo Schimmel 808da58f90fSIdo Schimmel nsim_trap_item->trap_ctx = trap_ctx; 809da58f90fSIdo Schimmel nsim_trap_item->action = trap->init_action; 810da58f90fSIdo Schimmel 811da58f90fSIdo Schimmel return 0; 812da58f90fSIdo Schimmel } 813da58f90fSIdo Schimmel 814da58f90fSIdo Schimmel static int 815da58f90fSIdo Schimmel nsim_dev_devlink_trap_action_set(struct devlink *devlink, 816da58f90fSIdo Schimmel const struct devlink_trap *trap, 817c88e11e0SIdo Schimmel enum devlink_trap_action action, 818c88e11e0SIdo Schimmel struct netlink_ext_ack *extack) 819da58f90fSIdo Schimmel { 820da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 821da58f90fSIdo Schimmel struct nsim_trap_item *nsim_trap_item; 822da58f90fSIdo Schimmel 823da58f90fSIdo Schimmel nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id); 824da58f90fSIdo Schimmel if (WARN_ON(!nsim_trap_item)) 825da58f90fSIdo Schimmel return -ENOENT; 826da58f90fSIdo Schimmel 827da58f90fSIdo Schimmel spin_lock(&nsim_dev->trap_data->trap_lock); 828da58f90fSIdo Schimmel nsim_trap_item->action = action; 829da58f90fSIdo Schimmel spin_unlock(&nsim_dev->trap_data->trap_lock); 830da58f90fSIdo Schimmel 831da58f90fSIdo Schimmel return 0; 832da58f90fSIdo Schimmel } 833da58f90fSIdo Schimmel 834ad188458SIdo Schimmel static int 8350dc8249aSIdo Schimmel nsim_dev_devlink_trap_group_set(struct devlink *devlink, 8360dc8249aSIdo Schimmel const struct devlink_trap_group *group, 837c88e11e0SIdo Schimmel const struct devlink_trap_policer *policer, 838c88e11e0SIdo Schimmel struct netlink_ext_ack *extack) 8390dc8249aSIdo Schimmel { 8400dc8249aSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 8410dc8249aSIdo Schimmel 8420dc8249aSIdo Schimmel if (nsim_dev->fail_trap_group_set) 8430dc8249aSIdo Schimmel return -EINVAL; 8440dc8249aSIdo Schimmel 8450dc8249aSIdo Schimmel return 0; 8460dc8249aSIdo Schimmel } 8470dc8249aSIdo Schimmel 8480dc8249aSIdo Schimmel static int 849ad188458SIdo Schimmel nsim_dev_devlink_trap_policer_set(struct devlink *devlink, 850ad188458SIdo Schimmel const struct devlink_trap_policer *policer, 851ad188458SIdo Schimmel u64 rate, u64 burst, 852ad188458SIdo Schimmel struct netlink_ext_ack *extack) 853ad188458SIdo Schimmel { 854ad188458SIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 855ad188458SIdo Schimmel 856ad188458SIdo Schimmel if (nsim_dev->fail_trap_policer_set) { 857ad188458SIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "User setup the operation to fail for testing purposes"); 858ad188458SIdo Schimmel return -EINVAL; 859ad188458SIdo Schimmel } 860ad188458SIdo Schimmel 861ad188458SIdo Schimmel return 0; 862ad188458SIdo Schimmel } 863ad188458SIdo Schimmel 864ad188458SIdo Schimmel static int 865ad188458SIdo Schimmel nsim_dev_devlink_trap_policer_counter_get(struct devlink *devlink, 866ad188458SIdo Schimmel const struct devlink_trap_policer *policer, 867ad188458SIdo Schimmel u64 *p_drops) 868ad188458SIdo Schimmel { 869ad188458SIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 870ad188458SIdo Schimmel u64 *cnt; 871ad188458SIdo Schimmel 872ad188458SIdo Schimmel if (nsim_dev->fail_trap_policer_counter_get) 873ad188458SIdo Schimmel return -EINVAL; 874ad188458SIdo Schimmel 875ad188458SIdo Schimmel cnt = &nsim_dev->trap_data->trap_policers_cnt_arr[policer->id - 1]; 876be43224fSIdo Schimmel *p_drops = (*cnt)++; 877ad188458SIdo Schimmel 878ad188458SIdo Schimmel return 0; 879ad188458SIdo Schimmel } 880ad188458SIdo Schimmel 8818fb4bc6fSJiri Pirko static const struct devlink_ops nsim_dev_devlink_ops = { 88222ec3d23SJacob Keller .supported_flash_update_params = DEVLINK_SUPPORT_FLASH_UPDATE_COMPONENT, 88397691069SJiri Pirko .reload_down = nsim_dev_reload_down, 88497691069SJiri Pirko .reload_up = nsim_dev_reload_up, 8858e23cc03SJiri Pirko .info_get = nsim_dev_info_get, 886fa4dfc4aSJiri Pirko .flash_update = nsim_dev_flash_update, 887da58f90fSIdo Schimmel .trap_init = nsim_dev_devlink_trap_init, 888da58f90fSIdo Schimmel .trap_action_set = nsim_dev_devlink_trap_action_set, 8890dc8249aSIdo Schimmel .trap_group_set = nsim_dev_devlink_trap_group_set, 890ad188458SIdo Schimmel .trap_policer_set = nsim_dev_devlink_trap_policer_set, 891ad188458SIdo Schimmel .trap_policer_counter_get = nsim_dev_devlink_trap_policer_counter_get, 8928fb4bc6fSJiri Pirko }; 8938fb4bc6fSJiri Pirko 894150e8f8aSJiri Pirko #define NSIM_DEV_MAX_MACS_DEFAULT 32 895150e8f8aSJiri Pirko #define NSIM_DEV_TEST1_DEFAULT true 896150e8f8aSJiri Pirko 897794b2c05SJiri Pirko static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, 898794b2c05SJiri Pirko unsigned int port_index) 8998320d145SJiri Pirko { 90071ad8d55SDanielle Ratson struct devlink_port_attrs attrs = {}; 9018320d145SJiri Pirko struct nsim_dev_port *nsim_dev_port; 9028320d145SJiri Pirko struct devlink_port *devlink_port; 9038320d145SJiri Pirko int err; 9048320d145SJiri Pirko 9058320d145SJiri Pirko nsim_dev_port = kzalloc(sizeof(*nsim_dev_port), GFP_KERNEL); 9068320d145SJiri Pirko if (!nsim_dev_port) 9078320d145SJiri Pirko return -ENOMEM; 9088320d145SJiri Pirko nsim_dev_port->port_index = port_index; 9098320d145SJiri Pirko 9108320d145SJiri Pirko devlink_port = &nsim_dev_port->devlink_port; 91171ad8d55SDanielle Ratson attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; 91271ad8d55SDanielle Ratson attrs.phys.port_number = port_index + 1; 91371ad8d55SDanielle Ratson memcpy(attrs.switch_id.id, nsim_dev->switch_id.id, nsim_dev->switch_id.id_len); 91471ad8d55SDanielle Ratson attrs.switch_id.id_len = nsim_dev->switch_id.id_len; 91571ad8d55SDanielle Ratson devlink_port_attrs_set(devlink_port, &attrs); 9168320d145SJiri Pirko err = devlink_port_register(priv_to_devlink(nsim_dev), devlink_port, 9178320d145SJiri Pirko port_index); 9188320d145SJiri Pirko if (err) 9198320d145SJiri Pirko goto err_port_free; 9208320d145SJiri Pirko 9218320d145SJiri Pirko err = nsim_dev_port_debugfs_init(nsim_dev, nsim_dev_port); 9228320d145SJiri Pirko if (err) 9238320d145SJiri Pirko goto err_dl_port_unregister; 9248320d145SJiri Pirko 925e05b2d14SJiri Pirko nsim_dev_port->ns = nsim_create(nsim_dev, nsim_dev_port); 926e05b2d14SJiri Pirko if (IS_ERR(nsim_dev_port->ns)) { 927e05b2d14SJiri Pirko err = PTR_ERR(nsim_dev_port->ns); 928e05b2d14SJiri Pirko goto err_port_debugfs_exit; 929e05b2d14SJiri Pirko } 930e05b2d14SJiri Pirko 931e05b2d14SJiri Pirko devlink_port_type_eth_set(devlink_port, nsim_dev_port->ns->netdev); 9328320d145SJiri Pirko list_add(&nsim_dev_port->list, &nsim_dev->port_list); 9338320d145SJiri Pirko 9348320d145SJiri Pirko return 0; 9358320d145SJiri Pirko 936e05b2d14SJiri Pirko err_port_debugfs_exit: 937e05b2d14SJiri Pirko nsim_dev_port_debugfs_exit(nsim_dev_port); 9388320d145SJiri Pirko err_dl_port_unregister: 9398320d145SJiri Pirko devlink_port_unregister(devlink_port); 9408320d145SJiri Pirko err_port_free: 9418320d145SJiri Pirko kfree(nsim_dev_port); 9428320d145SJiri Pirko return err; 9438320d145SJiri Pirko } 9448320d145SJiri Pirko 945794b2c05SJiri Pirko static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port) 9468320d145SJiri Pirko { 9478320d145SJiri Pirko struct devlink_port *devlink_port = &nsim_dev_port->devlink_port; 9488320d145SJiri Pirko 9498320d145SJiri Pirko list_del(&nsim_dev_port->list); 950e05b2d14SJiri Pirko devlink_port_type_clear(devlink_port); 951e05b2d14SJiri Pirko nsim_destroy(nsim_dev_port->ns); 9528320d145SJiri Pirko nsim_dev_port_debugfs_exit(nsim_dev_port); 9538320d145SJiri Pirko devlink_port_unregister(devlink_port); 9548320d145SJiri Pirko kfree(nsim_dev_port); 9558320d145SJiri Pirko } 9568320d145SJiri Pirko 9578320d145SJiri Pirko static void nsim_dev_port_del_all(struct nsim_dev *nsim_dev) 9588320d145SJiri Pirko { 9598320d145SJiri Pirko struct nsim_dev_port *nsim_dev_port, *tmp; 9608320d145SJiri Pirko 9616d6f0383SIdo Schimmel mutex_lock(&nsim_dev->port_list_lock); 9628320d145SJiri Pirko list_for_each_entry_safe(nsim_dev_port, tmp, 9638320d145SJiri Pirko &nsim_dev->port_list, list) 964794b2c05SJiri Pirko __nsim_dev_port_del(nsim_dev_port); 9656d6f0383SIdo Schimmel mutex_unlock(&nsim_dev->port_list_lock); 9668320d145SJiri Pirko } 9678320d145SJiri Pirko 9687f36a77aSJiri Pirko static int nsim_dev_port_add_all(struct nsim_dev *nsim_dev, 9697f36a77aSJiri Pirko unsigned int port_count) 9708320d145SJiri Pirko { 9717f36a77aSJiri Pirko int i, err; 9728320d145SJiri Pirko 9737f36a77aSJiri Pirko for (i = 0; i < port_count; i++) { 974794b2c05SJiri Pirko err = __nsim_dev_port_add(nsim_dev, i); 9758320d145SJiri Pirko if (err) 9768320d145SJiri Pirko goto err_port_del_all; 9778320d145SJiri Pirko } 9788320d145SJiri Pirko return 0; 9798320d145SJiri Pirko 9808320d145SJiri Pirko err_port_del_all: 9818320d145SJiri Pirko nsim_dev_port_del_all(nsim_dev); 9828320d145SJiri Pirko return err; 9838320d145SJiri Pirko } 9848320d145SJiri Pirko 98575ba029fSJiri Pirko static int nsim_dev_reload_create(struct nsim_dev *nsim_dev, 98675ba029fSJiri Pirko struct netlink_ext_ack *extack) 98775ba029fSJiri Pirko { 98875ba029fSJiri Pirko struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev; 98975ba029fSJiri Pirko struct devlink *devlink; 99075ba029fSJiri Pirko int err; 99175ba029fSJiri Pirko 99275ba029fSJiri Pirko devlink = priv_to_devlink(nsim_dev); 99375ba029fSJiri Pirko nsim_dev = devlink_priv(devlink); 99475ba029fSJiri Pirko INIT_LIST_HEAD(&nsim_dev->port_list); 99575ba029fSJiri Pirko mutex_init(&nsim_dev->port_list_lock); 99675ba029fSJiri Pirko nsim_dev->fw_update_status = true; 99775ba029fSJiri Pirko 99875ba029fSJiri Pirko nsim_dev->fib_data = nsim_fib_create(devlink, extack); 99975ba029fSJiri Pirko if (IS_ERR(nsim_dev->fib_data)) 100075ba029fSJiri Pirko return PTR_ERR(nsim_dev->fib_data); 100175ba029fSJiri Pirko 100275ba029fSJiri Pirko nsim_devlink_param_load_driverinit_values(devlink); 100375ba029fSJiri Pirko 100475ba029fSJiri Pirko err = nsim_dev_dummy_region_init(nsim_dev, devlink); 100575ba029fSJiri Pirko if (err) 100675ba029fSJiri Pirko goto err_fib_destroy; 100775ba029fSJiri Pirko 100875ba029fSJiri Pirko err = nsim_dev_traps_init(devlink); 100975ba029fSJiri Pirko if (err) 101075ba029fSJiri Pirko goto err_dummy_region_exit; 101175ba029fSJiri Pirko 101282c93a87SJiri Pirko err = nsim_dev_health_init(nsim_dev, devlink); 101375ba029fSJiri Pirko if (err) 101475ba029fSJiri Pirko goto err_traps_exit; 101575ba029fSJiri Pirko 101682c93a87SJiri Pirko err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count); 101782c93a87SJiri Pirko if (err) 101882c93a87SJiri Pirko goto err_health_exit; 101982c93a87SJiri Pirko 10208526ad96STaehee Yoo nsim_dev->take_snapshot = debugfs_create_file("take_snapshot", 10218526ad96STaehee Yoo 0200, 10228526ad96STaehee Yoo nsim_dev->ddir, 10238526ad96STaehee Yoo nsim_dev, 10248526ad96STaehee Yoo &nsim_dev_take_snapshot_fops); 102575ba029fSJiri Pirko return 0; 102675ba029fSJiri Pirko 102782c93a87SJiri Pirko err_health_exit: 102882c93a87SJiri Pirko nsim_dev_health_exit(nsim_dev); 102975ba029fSJiri Pirko err_traps_exit: 103075ba029fSJiri Pirko nsim_dev_traps_exit(devlink); 103175ba029fSJiri Pirko err_dummy_region_exit: 103275ba029fSJiri Pirko nsim_dev_dummy_region_exit(nsim_dev); 103375ba029fSJiri Pirko err_fib_destroy: 103475ba029fSJiri Pirko nsim_fib_destroy(devlink, nsim_dev->fib_data); 103575ba029fSJiri Pirko return err; 103675ba029fSJiri Pirko } 103775ba029fSJiri Pirko 1038bfcccfe7SJakub Kicinski int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev) 10397f36a77aSJiri Pirko { 10407f36a77aSJiri Pirko struct nsim_dev *nsim_dev; 10417f36a77aSJiri Pirko struct devlink *devlink; 10427f36a77aSJiri Pirko int err; 10437f36a77aSJiri Pirko 10447f36a77aSJiri Pirko devlink = devlink_alloc(&nsim_dev_devlink_ops, sizeof(*nsim_dev)); 10457f36a77aSJiri Pirko if (!devlink) 1046bfcccfe7SJakub Kicinski return -ENOMEM; 10477b60027bSJiri Pirko devlink_net_set(devlink, nsim_bus_dev->initial_net); 10487f36a77aSJiri Pirko nsim_dev = devlink_priv(devlink); 10497f36a77aSJiri Pirko nsim_dev->nsim_bus_dev = nsim_bus_dev; 10507f36a77aSJiri Pirko nsim_dev->switch_id.id_len = sizeof(nsim_dev->switch_id.id); 10517f36a77aSJiri Pirko get_random_bytes(nsim_dev->switch_id.id, nsim_dev->switch_id.id_len); 10527f36a77aSJiri Pirko INIT_LIST_HEAD(&nsim_dev->port_list); 10537f36a77aSJiri Pirko mutex_init(&nsim_dev->port_list_lock); 10547f36a77aSJiri Pirko nsim_dev->fw_update_status = true; 10557f36a77aSJiri Pirko nsim_dev->max_macs = NSIM_DEV_MAX_MACS_DEFAULT; 10567f36a77aSJiri Pirko nsim_dev->test1 = NSIM_DEV_TEST1_DEFAULT; 1057d3cbb907SJiri Pirko spin_lock_init(&nsim_dev->fa_cookie_lock); 10587f36a77aSJiri Pirko 1059bfcccfe7SJakub Kicinski dev_set_drvdata(&nsim_bus_dev->dev, nsim_dev); 1060bfcccfe7SJakub Kicinski 10617f36a77aSJiri Pirko err = nsim_dev_resources_register(devlink); 10627f36a77aSJiri Pirko if (err) 10637f36a77aSJiri Pirko goto err_devlink_free; 10647f36a77aSJiri Pirko 106575ba029fSJiri Pirko nsim_dev->fib_data = nsim_fib_create(devlink, NULL); 10667f36a77aSJiri Pirko if (IS_ERR(nsim_dev->fib_data)) { 10677f36a77aSJiri Pirko err = PTR_ERR(nsim_dev->fib_data); 10687f36a77aSJiri Pirko goto err_resources_unregister; 10697f36a77aSJiri Pirko } 10707f36a77aSJiri Pirko 10717f36a77aSJiri Pirko err = devlink_register(devlink, &nsim_bus_dev->dev); 10727f36a77aSJiri Pirko if (err) 10737f36a77aSJiri Pirko goto err_fib_destroy; 10747f36a77aSJiri Pirko 10757f36a77aSJiri Pirko err = devlink_params_register(devlink, nsim_devlink_params, 10767f36a77aSJiri Pirko ARRAY_SIZE(nsim_devlink_params)); 10777f36a77aSJiri Pirko if (err) 10787f36a77aSJiri Pirko goto err_dl_unregister; 10797f36a77aSJiri Pirko nsim_devlink_set_params_init_values(nsim_dev, devlink); 10807f36a77aSJiri Pirko 10817f36a77aSJiri Pirko err = nsim_dev_dummy_region_init(nsim_dev, devlink); 10827f36a77aSJiri Pirko if (err) 10837f36a77aSJiri Pirko goto err_params_unregister; 10847f36a77aSJiri Pirko 10857f36a77aSJiri Pirko err = nsim_dev_traps_init(devlink); 10867f36a77aSJiri Pirko if (err) 10877f36a77aSJiri Pirko goto err_dummy_region_exit; 10887f36a77aSJiri Pirko 10897f36a77aSJiri Pirko err = nsim_dev_debugfs_init(nsim_dev); 10907f36a77aSJiri Pirko if (err) 10917f36a77aSJiri Pirko goto err_traps_exit; 10927f36a77aSJiri Pirko 109382c93a87SJiri Pirko err = nsim_dev_health_init(nsim_dev, devlink); 10947f36a77aSJiri Pirko if (err) 10957f36a77aSJiri Pirko goto err_debugfs_exit; 10967f36a77aSJiri Pirko 109782c93a87SJiri Pirko err = nsim_bpf_dev_init(nsim_dev); 109882c93a87SJiri Pirko if (err) 109982c93a87SJiri Pirko goto err_health_exit; 110082c93a87SJiri Pirko 11017f36a77aSJiri Pirko err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count); 11027f36a77aSJiri Pirko if (err) 11037f36a77aSJiri Pirko goto err_bpf_dev_exit; 11047f36a77aSJiri Pirko 11057f36a77aSJiri Pirko devlink_params_publish(devlink); 1106a0c76345SJiri Pirko devlink_reload_enable(devlink); 1107bfcccfe7SJakub Kicinski return 0; 11087f36a77aSJiri Pirko 11097f36a77aSJiri Pirko err_bpf_dev_exit: 11107f36a77aSJiri Pirko nsim_bpf_dev_exit(nsim_dev); 111182c93a87SJiri Pirko err_health_exit: 111282c93a87SJiri Pirko nsim_dev_health_exit(nsim_dev); 11137f36a77aSJiri Pirko err_debugfs_exit: 11147f36a77aSJiri Pirko nsim_dev_debugfs_exit(nsim_dev); 11157f36a77aSJiri Pirko err_traps_exit: 11167f36a77aSJiri Pirko nsim_dev_traps_exit(devlink); 11177f36a77aSJiri Pirko err_dummy_region_exit: 11187f36a77aSJiri Pirko nsim_dev_dummy_region_exit(nsim_dev); 11197f36a77aSJiri Pirko err_params_unregister: 11207f36a77aSJiri Pirko devlink_params_unregister(devlink, nsim_devlink_params, 11217f36a77aSJiri Pirko ARRAY_SIZE(nsim_devlink_params)); 11227f36a77aSJiri Pirko err_dl_unregister: 11237f36a77aSJiri Pirko devlink_unregister(devlink); 11247f36a77aSJiri Pirko err_fib_destroy: 11257f36a77aSJiri Pirko nsim_fib_destroy(devlink, nsim_dev->fib_data); 11267f36a77aSJiri Pirko err_resources_unregister: 11277f36a77aSJiri Pirko devlink_resources_unregister(devlink, NULL); 11287f36a77aSJiri Pirko err_devlink_free: 11297f36a77aSJiri Pirko devlink_free(devlink); 1130bfcccfe7SJakub Kicinski return err; 11317f36a77aSJiri Pirko } 11327f36a77aSJiri Pirko 113375ba029fSJiri Pirko static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev) 113475ba029fSJiri Pirko { 113575ba029fSJiri Pirko struct devlink *devlink = priv_to_devlink(nsim_dev); 113675ba029fSJiri Pirko 113775ba029fSJiri Pirko if (devlink_is_reload_failed(devlink)) 113875ba029fSJiri Pirko return; 11398526ad96STaehee Yoo debugfs_remove(nsim_dev->take_snapshot); 114075ba029fSJiri Pirko nsim_dev_port_del_all(nsim_dev); 114182c93a87SJiri Pirko nsim_dev_health_exit(nsim_dev); 114275ba029fSJiri Pirko nsim_dev_traps_exit(devlink); 114375ba029fSJiri Pirko nsim_dev_dummy_region_exit(nsim_dev); 114475ba029fSJiri Pirko mutex_destroy(&nsim_dev->port_list_lock); 114575ba029fSJiri Pirko nsim_fib_destroy(devlink, nsim_dev->fib_data); 114675ba029fSJiri Pirko } 114775ba029fSJiri Pirko 1148bfcccfe7SJakub Kicinski void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev) 11497f36a77aSJiri Pirko { 1150bfcccfe7SJakub Kicinski struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); 11517f36a77aSJiri Pirko struct devlink *devlink = priv_to_devlink(nsim_dev); 11527f36a77aSJiri Pirko 1153a0c76345SJiri Pirko devlink_reload_disable(devlink); 1154a0c76345SJiri Pirko 115575ba029fSJiri Pirko nsim_dev_reload_destroy(nsim_dev); 115675ba029fSJiri Pirko 11577f36a77aSJiri Pirko nsim_bpf_dev_exit(nsim_dev); 11587f36a77aSJiri Pirko nsim_dev_debugfs_exit(nsim_dev); 11597f36a77aSJiri Pirko devlink_params_unregister(devlink, nsim_devlink_params, 11607f36a77aSJiri Pirko ARRAY_SIZE(nsim_devlink_params)); 11617f36a77aSJiri Pirko devlink_unregister(devlink); 11627f36a77aSJiri Pirko devlink_resources_unregister(devlink, NULL); 11637f36a77aSJiri Pirko devlink_free(devlink); 11647f36a77aSJiri Pirko } 11657f36a77aSJiri Pirko 1166794b2c05SJiri Pirko static struct nsim_dev_port * 1167794b2c05SJiri Pirko __nsim_dev_port_lookup(struct nsim_dev *nsim_dev, unsigned int port_index) 1168794b2c05SJiri Pirko { 1169794b2c05SJiri Pirko struct nsim_dev_port *nsim_dev_port; 1170794b2c05SJiri Pirko 1171794b2c05SJiri Pirko list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) 1172794b2c05SJiri Pirko if (nsim_dev_port->port_index == port_index) 1173794b2c05SJiri Pirko return nsim_dev_port; 1174794b2c05SJiri Pirko return NULL; 1175794b2c05SJiri Pirko } 1176794b2c05SJiri Pirko 1177794b2c05SJiri Pirko int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev, 1178794b2c05SJiri Pirko unsigned int port_index) 1179794b2c05SJiri Pirko { 1180794b2c05SJiri Pirko struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); 1181794b2c05SJiri Pirko int err; 1182794b2c05SJiri Pirko 1183794b2c05SJiri Pirko mutex_lock(&nsim_dev->port_list_lock); 1184794b2c05SJiri Pirko if (__nsim_dev_port_lookup(nsim_dev, port_index)) 1185794b2c05SJiri Pirko err = -EEXIST; 1186794b2c05SJiri Pirko else 1187794b2c05SJiri Pirko err = __nsim_dev_port_add(nsim_dev, port_index); 1188794b2c05SJiri Pirko mutex_unlock(&nsim_dev->port_list_lock); 1189794b2c05SJiri Pirko return err; 1190794b2c05SJiri Pirko } 1191794b2c05SJiri Pirko 1192794b2c05SJiri Pirko int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev, 1193794b2c05SJiri Pirko unsigned int port_index) 1194794b2c05SJiri Pirko { 1195794b2c05SJiri Pirko struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); 1196794b2c05SJiri Pirko struct nsim_dev_port *nsim_dev_port; 1197794b2c05SJiri Pirko int err = 0; 1198794b2c05SJiri Pirko 1199794b2c05SJiri Pirko mutex_lock(&nsim_dev->port_list_lock); 1200794b2c05SJiri Pirko nsim_dev_port = __nsim_dev_port_lookup(nsim_dev, port_index); 1201794b2c05SJiri Pirko if (!nsim_dev_port) 1202794b2c05SJiri Pirko err = -ENOENT; 1203794b2c05SJiri Pirko else 1204794b2c05SJiri Pirko __nsim_dev_port_del(nsim_dev_port); 1205794b2c05SJiri Pirko mutex_unlock(&nsim_dev->port_list_lock); 1206794b2c05SJiri Pirko return err; 1207794b2c05SJiri Pirko } 1208794b2c05SJiri Pirko 1209d514f41eSJiri Pirko int nsim_dev_init(void) 1210d514f41eSJiri Pirko { 1211ab1d0cc0SJiri Pirko nsim_dev_ddir = debugfs_create_dir(DRV_NAME, NULL); 121234611e69Skbuild test robot return PTR_ERR_OR_ZERO(nsim_dev_ddir); 1213d514f41eSJiri Pirko } 1214d514f41eSJiri Pirko 1215d514f41eSJiri Pirko void nsim_dev_exit(void) 1216d514f41eSJiri Pirko { 1217d514f41eSJiri Pirko debugfs_remove_recursive(nsim_dev_ddir); 1218d514f41eSJiri Pirko } 1219