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); 206*cbb58368SJacob Keller debugfs_create_u32("fw_update_overwrite_mask", 0600, nsim_dev->ddir, 207*cbb58368SJacob Keller &nsim_dev->fw_update_overwrite_mask); 208150e8f8aSJiri Pirko debugfs_create_u32("max_macs", 0600, nsim_dev->ddir, 209150e8f8aSJiri Pirko &nsim_dev->max_macs); 210150e8f8aSJiri Pirko debugfs_create_bool("test1", 0600, nsim_dev->ddir, 211150e8f8aSJiri Pirko &nsim_dev->test1); 2128526ad96STaehee Yoo nsim_dev->take_snapshot = debugfs_create_file("take_snapshot", 2138526ad96STaehee Yoo 0200, 2148526ad96STaehee Yoo nsim_dev->ddir, 2158526ad96STaehee Yoo nsim_dev, 2164418f862SJiri Pirko &nsim_dev_take_snapshot_fops); 217155ddfc5SJiri Pirko debugfs_create_bool("dont_allow_reload", 0600, nsim_dev->ddir, 218155ddfc5SJiri Pirko &nsim_dev->dont_allow_reload); 219155ddfc5SJiri Pirko debugfs_create_bool("fail_reload", 0600, nsim_dev->ddir, 220155ddfc5SJiri Pirko &nsim_dev->fail_reload); 221d3cbb907SJiri Pirko debugfs_create_file("trap_flow_action_cookie", 0600, nsim_dev->ddir, 222d3cbb907SJiri Pirko nsim_dev, &nsim_dev_trap_fa_cookie_fops); 2230dc8249aSIdo Schimmel debugfs_create_bool("fail_trap_group_set", 0600, 2240dc8249aSIdo Schimmel nsim_dev->ddir, 2250dc8249aSIdo Schimmel &nsim_dev->fail_trap_group_set); 226ad188458SIdo Schimmel debugfs_create_bool("fail_trap_policer_set", 0600, 227ad188458SIdo Schimmel nsim_dev->ddir, 228ad188458SIdo Schimmel &nsim_dev->fail_trap_policer_set); 229ad188458SIdo Schimmel debugfs_create_bool("fail_trap_policer_counter_get", 0600, 230ad188458SIdo Schimmel nsim_dev->ddir, 231ad188458SIdo Schimmel &nsim_dev->fail_trap_policer_counter_get); 232424be63aSJakub Kicinski nsim_udp_tunnels_debugfs_create(nsim_dev); 233d514f41eSJiri Pirko return 0; 234d514f41eSJiri Pirko } 235d514f41eSJiri Pirko 236d514f41eSJiri Pirko static void nsim_dev_debugfs_exit(struct nsim_dev *nsim_dev) 237d514f41eSJiri Pirko { 238ab1d0cc0SJiri Pirko debugfs_remove_recursive(nsim_dev->ports_ddir); 239d514f41eSJiri Pirko debugfs_remove_recursive(nsim_dev->ddir); 240d514f41eSJiri Pirko } 241d514f41eSJiri Pirko 2428320d145SJiri Pirko static int nsim_dev_port_debugfs_init(struct nsim_dev *nsim_dev, 2438320d145SJiri Pirko struct nsim_dev_port *nsim_dev_port) 2448320d145SJiri Pirko { 2458320d145SJiri Pirko char port_ddir_name[16]; 2468320d145SJiri Pirko char dev_link_name[32]; 2478320d145SJiri Pirko 2488320d145SJiri Pirko sprintf(port_ddir_name, "%u", nsim_dev_port->port_index); 2498320d145SJiri Pirko nsim_dev_port->ddir = debugfs_create_dir(port_ddir_name, 2508320d145SJiri Pirko nsim_dev->ports_ddir); 2516556ff32STaehee Yoo if (IS_ERR(nsim_dev_port->ddir)) 2526556ff32STaehee Yoo return PTR_ERR(nsim_dev_port->ddir); 2538320d145SJiri Pirko 2548320d145SJiri Pirko sprintf(dev_link_name, "../../../" DRV_NAME "%u", 2558320d145SJiri Pirko nsim_dev->nsim_bus_dev->dev.id); 2568320d145SJiri Pirko debugfs_create_symlink("dev", nsim_dev_port->ddir, dev_link_name); 2578320d145SJiri Pirko 2588320d145SJiri Pirko return 0; 2598320d145SJiri Pirko } 2608320d145SJiri Pirko 2618320d145SJiri Pirko static void nsim_dev_port_debugfs_exit(struct nsim_dev_port *nsim_dev_port) 2628320d145SJiri Pirko { 2638320d145SJiri Pirko debugfs_remove_recursive(nsim_dev_port->ddir); 2648320d145SJiri Pirko } 2658320d145SJiri Pirko 2668fb4bc6fSJiri Pirko static int nsim_dev_resources_register(struct devlink *devlink) 2678fb4bc6fSJiri Pirko { 2688fb4bc6fSJiri Pirko struct devlink_resource_size_params params = { 2698fb4bc6fSJiri Pirko .size_max = (u64)-1, 2708fb4bc6fSJiri Pirko .size_granularity = 1, 2718fb4bc6fSJiri Pirko .unit = DEVLINK_RESOURCE_UNIT_ENTRY 2728fb4bc6fSJiri Pirko }; 2738fb4bc6fSJiri Pirko int err; 2748fb4bc6fSJiri Pirko 2758fb4bc6fSJiri Pirko /* Resources for IPv4 */ 2768fb4bc6fSJiri Pirko err = devlink_resource_register(devlink, "IPv4", (u64)-1, 2778fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4, 2788fb4bc6fSJiri Pirko DEVLINK_RESOURCE_ID_PARENT_TOP, 2798fb4bc6fSJiri Pirko ¶ms); 2808fb4bc6fSJiri Pirko if (err) { 2818fb4bc6fSJiri Pirko pr_err("Failed to register IPv4 top resource\n"); 2828fb4bc6fSJiri Pirko goto out; 2838fb4bc6fSJiri Pirko } 2848fb4bc6fSJiri Pirko 285a5facc4cSJiri Pirko err = devlink_resource_register(devlink, "fib", (u64)-1, 2868fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4_FIB, 2878fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4, ¶ms); 2888fb4bc6fSJiri Pirko if (err) { 2898fb4bc6fSJiri Pirko pr_err("Failed to register IPv4 FIB resource\n"); 2908fb4bc6fSJiri Pirko return err; 2918fb4bc6fSJiri Pirko } 2928fb4bc6fSJiri Pirko 293a5facc4cSJiri Pirko err = devlink_resource_register(devlink, "fib-rules", (u64)-1, 2948fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4_FIB_RULES, 2958fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV4, ¶ms); 2968fb4bc6fSJiri Pirko if (err) { 2978fb4bc6fSJiri Pirko pr_err("Failed to register IPv4 FIB rules resource\n"); 2988fb4bc6fSJiri Pirko return err; 2998fb4bc6fSJiri Pirko } 3008fb4bc6fSJiri Pirko 3018fb4bc6fSJiri Pirko /* Resources for IPv6 */ 3028fb4bc6fSJiri Pirko err = devlink_resource_register(devlink, "IPv6", (u64)-1, 3038fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6, 3048fb4bc6fSJiri Pirko DEVLINK_RESOURCE_ID_PARENT_TOP, 3058fb4bc6fSJiri Pirko ¶ms); 3068fb4bc6fSJiri Pirko if (err) { 3078fb4bc6fSJiri Pirko pr_err("Failed to register IPv6 top resource\n"); 3088fb4bc6fSJiri Pirko goto out; 3098fb4bc6fSJiri Pirko } 3108fb4bc6fSJiri Pirko 311a5facc4cSJiri Pirko err = devlink_resource_register(devlink, "fib", (u64)-1, 3128fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6_FIB, 3138fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6, ¶ms); 3148fb4bc6fSJiri Pirko if (err) { 3158fb4bc6fSJiri Pirko pr_err("Failed to register IPv6 FIB resource\n"); 3168fb4bc6fSJiri Pirko return err; 3178fb4bc6fSJiri Pirko } 3188fb4bc6fSJiri Pirko 319a5facc4cSJiri Pirko err = devlink_resource_register(devlink, "fib-rules", (u64)-1, 3208fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6_FIB_RULES, 3218fb4bc6fSJiri Pirko NSIM_RESOURCE_IPV6, ¶ms); 3228fb4bc6fSJiri Pirko if (err) { 3238fb4bc6fSJiri Pirko pr_err("Failed to register IPv6 FIB rules resource\n"); 3248fb4bc6fSJiri Pirko return err; 3258fb4bc6fSJiri Pirko } 3268fb4bc6fSJiri Pirko 3278fb4bc6fSJiri Pirko out: 3288fb4bc6fSJiri Pirko return err; 3298fb4bc6fSJiri Pirko } 3308fb4bc6fSJiri Pirko 331150e8f8aSJiri Pirko enum nsim_devlink_param_id { 332150e8f8aSJiri Pirko NSIM_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, 333150e8f8aSJiri Pirko NSIM_DEVLINK_PARAM_ID_TEST1, 334150e8f8aSJiri Pirko }; 335150e8f8aSJiri Pirko 336150e8f8aSJiri Pirko static const struct devlink_param nsim_devlink_params[] = { 337150e8f8aSJiri Pirko DEVLINK_PARAM_GENERIC(MAX_MACS, 338150e8f8aSJiri Pirko BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), 339150e8f8aSJiri Pirko NULL, NULL, NULL), 340150e8f8aSJiri Pirko DEVLINK_PARAM_DRIVER(NSIM_DEVLINK_PARAM_ID_TEST1, 341150e8f8aSJiri Pirko "test1", DEVLINK_PARAM_TYPE_BOOL, 342150e8f8aSJiri Pirko BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), 343150e8f8aSJiri Pirko NULL, NULL, NULL), 344150e8f8aSJiri Pirko }; 345150e8f8aSJiri Pirko 346150e8f8aSJiri Pirko static void nsim_devlink_set_params_init_values(struct nsim_dev *nsim_dev, 347150e8f8aSJiri Pirko struct devlink *devlink) 348150e8f8aSJiri Pirko { 349150e8f8aSJiri Pirko union devlink_param_value value; 350150e8f8aSJiri Pirko 351150e8f8aSJiri Pirko value.vu32 = nsim_dev->max_macs; 352150e8f8aSJiri Pirko devlink_param_driverinit_value_set(devlink, 353150e8f8aSJiri Pirko DEVLINK_PARAM_GENERIC_ID_MAX_MACS, 354150e8f8aSJiri Pirko value); 355150e8f8aSJiri Pirko value.vbool = nsim_dev->test1; 356150e8f8aSJiri Pirko devlink_param_driverinit_value_set(devlink, 357150e8f8aSJiri Pirko NSIM_DEVLINK_PARAM_ID_TEST1, 358150e8f8aSJiri Pirko value); 359150e8f8aSJiri Pirko } 360150e8f8aSJiri Pirko 361150e8f8aSJiri Pirko static void nsim_devlink_param_load_driverinit_values(struct devlink *devlink) 362150e8f8aSJiri Pirko { 363150e8f8aSJiri Pirko struct nsim_dev *nsim_dev = devlink_priv(devlink); 364150e8f8aSJiri Pirko union devlink_param_value saved_value; 365150e8f8aSJiri Pirko int err; 366150e8f8aSJiri Pirko 367150e8f8aSJiri Pirko err = devlink_param_driverinit_value_get(devlink, 368150e8f8aSJiri Pirko DEVLINK_PARAM_GENERIC_ID_MAX_MACS, 369150e8f8aSJiri Pirko &saved_value); 370150e8f8aSJiri Pirko if (!err) 371150e8f8aSJiri Pirko nsim_dev->max_macs = saved_value.vu32; 372150e8f8aSJiri Pirko err = devlink_param_driverinit_value_get(devlink, 373150e8f8aSJiri Pirko NSIM_DEVLINK_PARAM_ID_TEST1, 374150e8f8aSJiri Pirko &saved_value); 375150e8f8aSJiri Pirko if (!err) 376150e8f8aSJiri Pirko nsim_dev->test1 = saved_value.vbool; 377150e8f8aSJiri Pirko } 378150e8f8aSJiri Pirko 3794418f862SJiri Pirko #define NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX 16 3804418f862SJiri Pirko 381e8937681SJacob Keller static const struct devlink_region_ops dummy_region_ops = { 382e8937681SJacob Keller .name = "dummy", 383a0a09f6bSJacob Keller .destructor = &kfree, 3843fe0fd53SJacob Keller .snapshot = nsim_dev_take_snapshot, 385e8937681SJacob Keller }; 386e8937681SJacob Keller 3874418f862SJiri Pirko static int nsim_dev_dummy_region_init(struct nsim_dev *nsim_dev, 3884418f862SJiri Pirko struct devlink *devlink) 3894418f862SJiri Pirko { 3904418f862SJiri Pirko nsim_dev->dummy_region = 391e8937681SJacob Keller devlink_region_create(devlink, &dummy_region_ops, 3924418f862SJiri Pirko NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX, 3934418f862SJiri Pirko NSIM_DEV_DUMMY_REGION_SIZE); 3944418f862SJiri Pirko return PTR_ERR_OR_ZERO(nsim_dev->dummy_region); 3954418f862SJiri Pirko } 3964418f862SJiri Pirko 3974418f862SJiri Pirko static void nsim_dev_dummy_region_exit(struct nsim_dev *nsim_dev) 3984418f862SJiri Pirko { 3994418f862SJiri Pirko devlink_region_destroy(nsim_dev->dummy_region); 4004418f862SJiri Pirko } 4014418f862SJiri Pirko 402da58f90fSIdo Schimmel struct nsim_trap_item { 403da58f90fSIdo Schimmel void *trap_ctx; 404da58f90fSIdo Schimmel enum devlink_trap_action action; 405da58f90fSIdo Schimmel }; 406da58f90fSIdo Schimmel 407da58f90fSIdo Schimmel struct nsim_trap_data { 408da58f90fSIdo Schimmel struct delayed_work trap_report_dw; 409da58f90fSIdo Schimmel struct nsim_trap_item *trap_items_arr; 410ad188458SIdo Schimmel u64 *trap_policers_cnt_arr; 411da58f90fSIdo Schimmel struct nsim_dev *nsim_dev; 412da58f90fSIdo Schimmel spinlock_t trap_lock; /* Protects trap_items_arr */ 413da58f90fSIdo Schimmel }; 414da58f90fSIdo Schimmel 4159e087457SIdo Schimmel /* All driver-specific traps must be documented in 41604e4272cSJacob Keller * Documentation/networking/devlink/netdevsim.rst 4179e087457SIdo Schimmel */ 418da58f90fSIdo Schimmel enum { 419da58f90fSIdo Schimmel NSIM_TRAP_ID_BASE = DEVLINK_TRAP_GENERIC_ID_MAX, 420da58f90fSIdo Schimmel NSIM_TRAP_ID_FID_MISS, 421da58f90fSIdo Schimmel }; 422da58f90fSIdo Schimmel 423da58f90fSIdo Schimmel #define NSIM_TRAP_NAME_FID_MISS "fid_miss" 424da58f90fSIdo Schimmel 425da58f90fSIdo Schimmel #define NSIM_TRAP_METADATA DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT 426da58f90fSIdo Schimmel 427da58f90fSIdo Schimmel #define NSIM_TRAP_DROP(_id, _group_id) \ 428da58f90fSIdo Schimmel DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \ 429107f1678SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 430da58f90fSIdo Schimmel NSIM_TRAP_METADATA) 431d3cbb907SJiri Pirko #define NSIM_TRAP_DROP_EXT(_id, _group_id, _metadata) \ 432d3cbb907SJiri Pirko DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \ 433107f1678SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 434d3cbb907SJiri Pirko NSIM_TRAP_METADATA | (_metadata)) 435da58f90fSIdo Schimmel #define NSIM_TRAP_EXCEPTION(_id, _group_id) \ 436da58f90fSIdo Schimmel DEVLINK_TRAP_GENERIC(EXCEPTION, TRAP, _id, \ 437107f1678SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 438da58f90fSIdo Schimmel NSIM_TRAP_METADATA) 43918979367SIdo Schimmel #define NSIM_TRAP_CONTROL(_id, _group_id, _action) \ 44018979367SIdo Schimmel DEVLINK_TRAP_GENERIC(CONTROL, _action, _id, \ 44118979367SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 44218979367SIdo Schimmel NSIM_TRAP_METADATA) 443da58f90fSIdo Schimmel #define NSIM_TRAP_DRIVER_EXCEPTION(_id, _group_id) \ 444da58f90fSIdo Schimmel DEVLINK_TRAP_DRIVER(EXCEPTION, TRAP, NSIM_TRAP_ID_##_id, \ 445da58f90fSIdo Schimmel NSIM_TRAP_NAME_##_id, \ 446107f1678SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 447da58f90fSIdo Schimmel NSIM_TRAP_METADATA) 448da58f90fSIdo Schimmel 449ad188458SIdo Schimmel #define NSIM_DEV_TRAP_POLICER_MIN_RATE 1 450ad188458SIdo Schimmel #define NSIM_DEV_TRAP_POLICER_MAX_RATE 8000 451ad188458SIdo Schimmel #define NSIM_DEV_TRAP_POLICER_MIN_BURST 8 452ad188458SIdo Schimmel #define NSIM_DEV_TRAP_POLICER_MAX_BURST 65536 453ad188458SIdo Schimmel 454ad188458SIdo Schimmel #define NSIM_TRAP_POLICER(_id, _rate, _burst) \ 455ad188458SIdo Schimmel DEVLINK_TRAP_POLICER(_id, _rate, _burst, \ 456ad188458SIdo Schimmel NSIM_DEV_TRAP_POLICER_MAX_RATE, \ 457ad188458SIdo Schimmel NSIM_DEV_TRAP_POLICER_MIN_RATE, \ 458ad188458SIdo Schimmel NSIM_DEV_TRAP_POLICER_MAX_BURST, \ 459ad188458SIdo Schimmel NSIM_DEV_TRAP_POLICER_MIN_BURST) 460ad188458SIdo Schimmel 461ad188458SIdo Schimmel static const struct devlink_trap_policer nsim_trap_policers_arr[] = { 462ad188458SIdo Schimmel NSIM_TRAP_POLICER(1, 1000, 128), 463ad188458SIdo Schimmel NSIM_TRAP_POLICER(2, 2000, 256), 464ad188458SIdo Schimmel NSIM_TRAP_POLICER(3, 3000, 512), 465ad188458SIdo Schimmel }; 466ad188458SIdo Schimmel 467b29545d8SIdo Schimmel static const struct devlink_trap_group nsim_trap_groups_arr[] = { 468f9f54392SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS, 0), 469f9f54392SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(L3_DROPS, 1), 47085176f19SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(L3_EXCEPTIONS, 1), 471f9f54392SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(BUFFER_DROPS, 2), 472f9f54392SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(ACL_DROPS, 3), 47318979367SIdo Schimmel DEVLINK_TRAP_GROUP_GENERIC(MC_SNOOPING, 3), 474b29545d8SIdo Schimmel }; 475b29545d8SIdo Schimmel 476da58f90fSIdo Schimmel static const struct devlink_trap nsim_traps_arr[] = { 477da58f90fSIdo Schimmel NSIM_TRAP_DROP(SMAC_MC, L2_DROPS), 478da58f90fSIdo Schimmel NSIM_TRAP_DROP(VLAN_TAG_MISMATCH, L2_DROPS), 479da58f90fSIdo Schimmel NSIM_TRAP_DROP(INGRESS_VLAN_FILTER, L2_DROPS), 480da58f90fSIdo Schimmel NSIM_TRAP_DROP(INGRESS_STP_FILTER, L2_DROPS), 481da58f90fSIdo Schimmel NSIM_TRAP_DROP(EMPTY_TX_LIST, L2_DROPS), 482da58f90fSIdo Schimmel NSIM_TRAP_DROP(PORT_LOOPBACK_FILTER, L2_DROPS), 483da58f90fSIdo Schimmel NSIM_TRAP_DRIVER_EXCEPTION(FID_MISS, L2_DROPS), 484da58f90fSIdo Schimmel NSIM_TRAP_DROP(BLACKHOLE_ROUTE, L3_DROPS), 48585176f19SIdo Schimmel NSIM_TRAP_EXCEPTION(TTL_ERROR, L3_EXCEPTIONS), 486da58f90fSIdo Schimmel NSIM_TRAP_DROP(TAIL_DROP, BUFFER_DROPS), 487d3cbb907SJiri Pirko NSIM_TRAP_DROP_EXT(INGRESS_FLOW_ACTION_DROP, ACL_DROPS, 488d3cbb907SJiri Pirko DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE), 489d3cbb907SJiri Pirko NSIM_TRAP_DROP_EXT(EGRESS_FLOW_ACTION_DROP, ACL_DROPS, 490d3cbb907SJiri Pirko DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE), 49118979367SIdo Schimmel NSIM_TRAP_CONTROL(IGMP_QUERY, MC_SNOOPING, MIRROR), 49218979367SIdo Schimmel NSIM_TRAP_CONTROL(IGMP_V1_REPORT, MC_SNOOPING, TRAP), 493da58f90fSIdo Schimmel }; 494da58f90fSIdo Schimmel 495da58f90fSIdo Schimmel #define NSIM_TRAP_L4_DATA_LEN 100 496da58f90fSIdo Schimmel 497da58f90fSIdo Schimmel static struct sk_buff *nsim_dev_trap_skb_build(void) 498da58f90fSIdo Schimmel { 499da58f90fSIdo Schimmel int tot_len, data_len = NSIM_TRAP_L4_DATA_LEN; 500da58f90fSIdo Schimmel struct sk_buff *skb; 501da58f90fSIdo Schimmel struct udphdr *udph; 502da58f90fSIdo Schimmel struct ethhdr *eth; 503da58f90fSIdo Schimmel struct iphdr *iph; 504da58f90fSIdo Schimmel 505da58f90fSIdo Schimmel skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); 506da58f90fSIdo Schimmel if (!skb) 507da58f90fSIdo Schimmel return NULL; 508da58f90fSIdo Schimmel tot_len = sizeof(struct iphdr) + sizeof(struct udphdr) + data_len; 509da58f90fSIdo Schimmel 51058a406deSIdo Schimmel skb_reset_mac_header(skb); 511da58f90fSIdo Schimmel eth = skb_put(skb, sizeof(struct ethhdr)); 512da58f90fSIdo Schimmel eth_random_addr(eth->h_dest); 513da58f90fSIdo Schimmel eth_random_addr(eth->h_source); 514da58f90fSIdo Schimmel eth->h_proto = htons(ETH_P_IP); 515da58f90fSIdo Schimmel skb->protocol = htons(ETH_P_IP); 516da58f90fSIdo Schimmel 51758a406deSIdo Schimmel skb_set_network_header(skb, skb->len); 518da58f90fSIdo Schimmel iph = skb_put(skb, sizeof(struct iphdr)); 519da58f90fSIdo Schimmel iph->protocol = IPPROTO_UDP; 520da58f90fSIdo Schimmel iph->saddr = in_aton("192.0.2.1"); 521da58f90fSIdo Schimmel iph->daddr = in_aton("198.51.100.1"); 522da58f90fSIdo Schimmel iph->version = 0x4; 523da58f90fSIdo Schimmel iph->frag_off = 0; 524da58f90fSIdo Schimmel iph->ihl = 0x5; 525da58f90fSIdo Schimmel iph->tot_len = htons(tot_len); 526da58f90fSIdo Schimmel iph->ttl = 100; 527d9bd6d27SYueHaibing iph->check = 0; 528d9bd6d27SYueHaibing iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); 529da58f90fSIdo Schimmel 53058a406deSIdo Schimmel skb_set_transport_header(skb, skb->len); 531da58f90fSIdo Schimmel udph = skb_put_zero(skb, sizeof(struct udphdr) + data_len); 532da58f90fSIdo Schimmel get_random_bytes(&udph->source, sizeof(u16)); 533da58f90fSIdo Schimmel get_random_bytes(&udph->dest, sizeof(u16)); 534da58f90fSIdo Schimmel udph->len = htons(sizeof(struct udphdr) + data_len); 535da58f90fSIdo Schimmel 536da58f90fSIdo Schimmel return skb; 537da58f90fSIdo Schimmel } 538da58f90fSIdo Schimmel 539da58f90fSIdo Schimmel static void nsim_dev_trap_report(struct nsim_dev_port *nsim_dev_port) 540da58f90fSIdo Schimmel { 541da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = nsim_dev_port->ns->nsim_dev; 542da58f90fSIdo Schimmel struct devlink *devlink = priv_to_devlink(nsim_dev); 543da58f90fSIdo Schimmel struct nsim_trap_data *nsim_trap_data; 544da58f90fSIdo Schimmel int i; 545da58f90fSIdo Schimmel 546da58f90fSIdo Schimmel nsim_trap_data = nsim_dev->trap_data; 547da58f90fSIdo Schimmel 548da58f90fSIdo Schimmel spin_lock(&nsim_trap_data->trap_lock); 549da58f90fSIdo Schimmel for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) { 550d3cbb907SJiri Pirko struct flow_action_cookie *fa_cookie = NULL; 551da58f90fSIdo Schimmel struct nsim_trap_item *nsim_trap_item; 552da58f90fSIdo Schimmel struct sk_buff *skb; 553d3cbb907SJiri Pirko bool has_fa_cookie; 554d3cbb907SJiri Pirko 555d3cbb907SJiri Pirko has_fa_cookie = nsim_traps_arr[i].metadata_cap & 556d3cbb907SJiri Pirko DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE; 557da58f90fSIdo Schimmel 558da58f90fSIdo Schimmel nsim_trap_item = &nsim_trap_data->trap_items_arr[i]; 559da58f90fSIdo Schimmel if (nsim_trap_item->action == DEVLINK_TRAP_ACTION_DROP) 560da58f90fSIdo Schimmel continue; 561da58f90fSIdo Schimmel 562da58f90fSIdo Schimmel skb = nsim_dev_trap_skb_build(); 563da58f90fSIdo Schimmel if (!skb) 564da58f90fSIdo Schimmel continue; 565da58f90fSIdo Schimmel skb->dev = nsim_dev_port->ns->netdev; 566da58f90fSIdo Schimmel 567da58f90fSIdo Schimmel /* Trapped packets are usually passed to devlink in softIRQ, 568da58f90fSIdo Schimmel * but in this case they are generated in a workqueue. Disable 569da58f90fSIdo Schimmel * softIRQs to prevent lockdep from complaining about 570da58f90fSIdo Schimmel * "incosistent lock state". 571da58f90fSIdo Schimmel */ 572d3cbb907SJiri Pirko 573d3cbb907SJiri Pirko spin_lock_bh(&nsim_dev->fa_cookie_lock); 574d3cbb907SJiri Pirko fa_cookie = has_fa_cookie ? nsim_dev->fa_cookie : NULL; 575da58f90fSIdo Schimmel devlink_trap_report(devlink, skb, nsim_trap_item->trap_ctx, 576d3cbb907SJiri Pirko &nsim_dev_port->devlink_port, fa_cookie); 577d3cbb907SJiri Pirko spin_unlock_bh(&nsim_dev->fa_cookie_lock); 578da58f90fSIdo Schimmel consume_skb(skb); 579da58f90fSIdo Schimmel } 580da58f90fSIdo Schimmel spin_unlock(&nsim_trap_data->trap_lock); 581da58f90fSIdo Schimmel } 582da58f90fSIdo Schimmel 583da58f90fSIdo Schimmel #define NSIM_TRAP_REPORT_INTERVAL_MS 100 584da58f90fSIdo Schimmel 585da58f90fSIdo Schimmel static void nsim_dev_trap_report_work(struct work_struct *work) 586da58f90fSIdo Schimmel { 587da58f90fSIdo Schimmel struct nsim_trap_data *nsim_trap_data; 588da58f90fSIdo Schimmel struct nsim_dev_port *nsim_dev_port; 589da58f90fSIdo Schimmel struct nsim_dev *nsim_dev; 590da58f90fSIdo Schimmel 591da58f90fSIdo Schimmel nsim_trap_data = container_of(work, struct nsim_trap_data, 592da58f90fSIdo Schimmel trap_report_dw.work); 593da58f90fSIdo Schimmel nsim_dev = nsim_trap_data->nsim_dev; 594da58f90fSIdo Schimmel 595da58f90fSIdo Schimmel /* For each running port and enabled packet trap, generate a UDP 596da58f90fSIdo Schimmel * packet with a random 5-tuple and report it. 597da58f90fSIdo Schimmel */ 598da58f90fSIdo Schimmel mutex_lock(&nsim_dev->port_list_lock); 599da58f90fSIdo Schimmel list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) { 600da58f90fSIdo Schimmel if (!netif_running(nsim_dev_port->ns->netdev)) 601da58f90fSIdo Schimmel continue; 602da58f90fSIdo Schimmel 603da58f90fSIdo Schimmel nsim_dev_trap_report(nsim_dev_port); 604da58f90fSIdo Schimmel } 605da58f90fSIdo Schimmel mutex_unlock(&nsim_dev->port_list_lock); 606da58f90fSIdo Schimmel 607da58f90fSIdo Schimmel schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, 608da58f90fSIdo Schimmel msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS)); 609da58f90fSIdo Schimmel } 610da58f90fSIdo Schimmel 611da58f90fSIdo Schimmel static int nsim_dev_traps_init(struct devlink *devlink) 612da58f90fSIdo Schimmel { 613ad188458SIdo Schimmel size_t policers_count = ARRAY_SIZE(nsim_trap_policers_arr); 614da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 615da58f90fSIdo Schimmel struct nsim_trap_data *nsim_trap_data; 616da58f90fSIdo Schimmel int err; 617da58f90fSIdo Schimmel 618da58f90fSIdo Schimmel nsim_trap_data = kzalloc(sizeof(*nsim_trap_data), GFP_KERNEL); 619da58f90fSIdo Schimmel if (!nsim_trap_data) 620da58f90fSIdo Schimmel return -ENOMEM; 621da58f90fSIdo Schimmel 622da58f90fSIdo Schimmel nsim_trap_data->trap_items_arr = kcalloc(ARRAY_SIZE(nsim_traps_arr), 623da58f90fSIdo Schimmel sizeof(struct nsim_trap_item), 624da58f90fSIdo Schimmel GFP_KERNEL); 625da58f90fSIdo Schimmel if (!nsim_trap_data->trap_items_arr) { 626da58f90fSIdo Schimmel err = -ENOMEM; 627da58f90fSIdo Schimmel goto err_trap_data_free; 628da58f90fSIdo Schimmel } 629da58f90fSIdo Schimmel 630ad188458SIdo Schimmel nsim_trap_data->trap_policers_cnt_arr = kcalloc(policers_count, 631ad188458SIdo Schimmel sizeof(u64), 632ad188458SIdo Schimmel GFP_KERNEL); 633ad188458SIdo Schimmel if (!nsim_trap_data->trap_policers_cnt_arr) { 634ad188458SIdo Schimmel err = -ENOMEM; 635ad188458SIdo Schimmel goto err_trap_items_free; 636ad188458SIdo Schimmel } 637ad188458SIdo Schimmel 638da58f90fSIdo Schimmel /* The lock is used to protect the action state of the registered 639da58f90fSIdo Schimmel * traps. The value is written by user and read in delayed work when 640da58f90fSIdo Schimmel * iterating over all the traps. 641da58f90fSIdo Schimmel */ 642da58f90fSIdo Schimmel spin_lock_init(&nsim_trap_data->trap_lock); 643da58f90fSIdo Schimmel nsim_trap_data->nsim_dev = nsim_dev; 644da58f90fSIdo Schimmel nsim_dev->trap_data = nsim_trap_data; 645da58f90fSIdo Schimmel 646ad188458SIdo Schimmel err = devlink_trap_policers_register(devlink, nsim_trap_policers_arr, 647ad188458SIdo Schimmel policers_count); 648ad188458SIdo Schimmel if (err) 649ad188458SIdo Schimmel goto err_trap_policers_cnt_free; 650ad188458SIdo Schimmel 651b29545d8SIdo Schimmel err = devlink_trap_groups_register(devlink, nsim_trap_groups_arr, 652b29545d8SIdo Schimmel ARRAY_SIZE(nsim_trap_groups_arr)); 653b29545d8SIdo Schimmel if (err) 654ad188458SIdo Schimmel goto err_trap_policers_unregister; 655b29545d8SIdo Schimmel 656da58f90fSIdo Schimmel err = devlink_traps_register(devlink, nsim_traps_arr, 657da58f90fSIdo Schimmel ARRAY_SIZE(nsim_traps_arr), NULL); 658da58f90fSIdo Schimmel if (err) 659b29545d8SIdo Schimmel goto err_trap_groups_unregister; 660da58f90fSIdo Schimmel 661da58f90fSIdo Schimmel INIT_DELAYED_WORK(&nsim_dev->trap_data->trap_report_dw, 662da58f90fSIdo Schimmel nsim_dev_trap_report_work); 663da58f90fSIdo Schimmel schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, 664da58f90fSIdo Schimmel msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS)); 665da58f90fSIdo Schimmel 666da58f90fSIdo Schimmel return 0; 667da58f90fSIdo Schimmel 668b29545d8SIdo Schimmel err_trap_groups_unregister: 669b29545d8SIdo Schimmel devlink_trap_groups_unregister(devlink, nsim_trap_groups_arr, 670b29545d8SIdo Schimmel ARRAY_SIZE(nsim_trap_groups_arr)); 671ad188458SIdo Schimmel err_trap_policers_unregister: 672ad188458SIdo Schimmel devlink_trap_policers_unregister(devlink, nsim_trap_policers_arr, 673ad188458SIdo Schimmel ARRAY_SIZE(nsim_trap_policers_arr)); 674ad188458SIdo Schimmel err_trap_policers_cnt_free: 675ad188458SIdo Schimmel kfree(nsim_trap_data->trap_policers_cnt_arr); 676da58f90fSIdo Schimmel err_trap_items_free: 677da58f90fSIdo Schimmel kfree(nsim_trap_data->trap_items_arr); 678da58f90fSIdo Schimmel err_trap_data_free: 679da58f90fSIdo Schimmel kfree(nsim_trap_data); 680da58f90fSIdo Schimmel return err; 681da58f90fSIdo Schimmel } 682da58f90fSIdo Schimmel 683da58f90fSIdo Schimmel static void nsim_dev_traps_exit(struct devlink *devlink) 684da58f90fSIdo Schimmel { 685da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 686da58f90fSIdo Schimmel 687da58f90fSIdo Schimmel cancel_delayed_work_sync(&nsim_dev->trap_data->trap_report_dw); 688da58f90fSIdo Schimmel devlink_traps_unregister(devlink, nsim_traps_arr, 689da58f90fSIdo Schimmel ARRAY_SIZE(nsim_traps_arr)); 690b29545d8SIdo Schimmel devlink_trap_groups_unregister(devlink, nsim_trap_groups_arr, 691b29545d8SIdo Schimmel ARRAY_SIZE(nsim_trap_groups_arr)); 692ad188458SIdo Schimmel devlink_trap_policers_unregister(devlink, nsim_trap_policers_arr, 693ad188458SIdo Schimmel ARRAY_SIZE(nsim_trap_policers_arr)); 694ad188458SIdo Schimmel kfree(nsim_dev->trap_data->trap_policers_cnt_arr); 695da58f90fSIdo Schimmel kfree(nsim_dev->trap_data->trap_items_arr); 696da58f90fSIdo Schimmel kfree(nsim_dev->trap_data); 697da58f90fSIdo Schimmel } 698da58f90fSIdo Schimmel 69975ba029fSJiri Pirko static int nsim_dev_reload_create(struct nsim_dev *nsim_dev, 70075ba029fSJiri Pirko struct netlink_ext_ack *extack); 70175ba029fSJiri Pirko static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev); 70275ba029fSJiri Pirko 703070c63f2SJiri Pirko static int nsim_dev_reload_down(struct devlink *devlink, bool netns_change, 70497691069SJiri Pirko struct netlink_ext_ack *extack) 70597691069SJiri Pirko { 70675ba029fSJiri Pirko struct nsim_dev *nsim_dev = devlink_priv(devlink); 70775ba029fSJiri Pirko 708155ddfc5SJiri Pirko if (nsim_dev->dont_allow_reload) { 709155ddfc5SJiri Pirko /* For testing purposes, user set debugfs dont_allow_reload 710155ddfc5SJiri Pirko * value to true. So forbid it. 711155ddfc5SJiri Pirko */ 712f9867b51SColin Ian King NL_SET_ERR_MSG_MOD(extack, "User forbid the reload for testing purposes"); 713155ddfc5SJiri Pirko return -EOPNOTSUPP; 714155ddfc5SJiri Pirko } 715155ddfc5SJiri Pirko 71675ba029fSJiri Pirko nsim_dev_reload_destroy(nsim_dev); 71797691069SJiri Pirko return 0; 71897691069SJiri Pirko } 71997691069SJiri Pirko 72097691069SJiri Pirko static int nsim_dev_reload_up(struct devlink *devlink, 7218fb4bc6fSJiri Pirko struct netlink_ext_ack *extack) 7228fb4bc6fSJiri Pirko { 723a5facc4cSJiri Pirko struct nsim_dev *nsim_dev = devlink_priv(devlink); 7248fb4bc6fSJiri Pirko 725155ddfc5SJiri Pirko if (nsim_dev->fail_reload) { 726155ddfc5SJiri Pirko /* For testing purposes, user set debugfs fail_reload 727155ddfc5SJiri Pirko * value to true. Fail right away. 728155ddfc5SJiri Pirko */ 729155ddfc5SJiri Pirko NL_SET_ERR_MSG_MOD(extack, "User setup the reload to fail for testing purposes"); 730155ddfc5SJiri Pirko return -EINVAL; 731155ddfc5SJiri Pirko } 732155ddfc5SJiri Pirko 73375ba029fSJiri Pirko return nsim_dev_reload_create(nsim_dev, extack); 7348fb4bc6fSJiri Pirko } 7358fb4bc6fSJiri Pirko 7368e23cc03SJiri Pirko static int nsim_dev_info_get(struct devlink *devlink, 7378e23cc03SJiri Pirko struct devlink_info_req *req, 7388e23cc03SJiri Pirko struct netlink_ext_ack *extack) 7398e23cc03SJiri Pirko { 7408e23cc03SJiri Pirko return devlink_info_driver_name_put(req, DRV_NAME); 7418e23cc03SJiri Pirko } 7428e23cc03SJiri Pirko 743fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_SIZE 500000 744fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_CHUNK_SIZE 1000 745fa4dfc4aSJiri Pirko #define NSIM_DEV_FLASH_CHUNK_TIME_MS 10 746fa4dfc4aSJiri Pirko 747bc75c054SJacob Keller static int nsim_dev_flash_update(struct devlink *devlink, 748bc75c054SJacob Keller struct devlink_flash_update_params *params, 749fa4dfc4aSJiri Pirko struct netlink_ext_ack *extack) 750fa4dfc4aSJiri Pirko { 751fa4dfc4aSJiri Pirko struct nsim_dev *nsim_dev = devlink_priv(devlink); 752fa4dfc4aSJiri Pirko int i; 753fa4dfc4aSJiri Pirko 754*cbb58368SJacob Keller if ((params->overwrite_mask & ~nsim_dev->fw_update_overwrite_mask) != 0) 755*cbb58368SJacob Keller return -EOPNOTSUPP; 756*cbb58368SJacob Keller 757fa4dfc4aSJiri Pirko if (nsim_dev->fw_update_status) { 758fa4dfc4aSJiri Pirko devlink_flash_update_begin_notify(devlink); 759fa4dfc4aSJiri Pirko devlink_flash_update_status_notify(devlink, 760fa4dfc4aSJiri Pirko "Preparing to flash", 761bc75c054SJacob Keller params->component, 0, 0); 762fa4dfc4aSJiri Pirko } 763fa4dfc4aSJiri Pirko 764fa4dfc4aSJiri Pirko for (i = 0; i < NSIM_DEV_FLASH_SIZE / NSIM_DEV_FLASH_CHUNK_SIZE; i++) { 765fa4dfc4aSJiri Pirko if (nsim_dev->fw_update_status) 766fa4dfc4aSJiri Pirko devlink_flash_update_status_notify(devlink, "Flashing", 767bc75c054SJacob Keller params->component, 768fa4dfc4aSJiri Pirko i * NSIM_DEV_FLASH_CHUNK_SIZE, 769fa4dfc4aSJiri Pirko NSIM_DEV_FLASH_SIZE); 770fa4dfc4aSJiri Pirko msleep(NSIM_DEV_FLASH_CHUNK_TIME_MS); 771fa4dfc4aSJiri Pirko } 772fa4dfc4aSJiri Pirko 773fa4dfc4aSJiri Pirko if (nsim_dev->fw_update_status) { 774fa4dfc4aSJiri Pirko devlink_flash_update_status_notify(devlink, "Flashing", 775bc75c054SJacob Keller params->component, 776fa4dfc4aSJiri Pirko NSIM_DEV_FLASH_SIZE, 777fa4dfc4aSJiri Pirko NSIM_DEV_FLASH_SIZE); 778b311b001SShannon Nelson devlink_flash_update_timeout_notify(devlink, "Flash select", 779bc75c054SJacob Keller params->component, 81); 780fa4dfc4aSJiri Pirko devlink_flash_update_status_notify(devlink, "Flashing done", 781bc75c054SJacob Keller params->component, 0, 0); 782fa4dfc4aSJiri Pirko devlink_flash_update_end_notify(devlink); 783fa4dfc4aSJiri Pirko } 784fa4dfc4aSJiri Pirko 785fa4dfc4aSJiri Pirko return 0; 786fa4dfc4aSJiri Pirko } 787fa4dfc4aSJiri Pirko 788da58f90fSIdo Schimmel static struct nsim_trap_item * 789da58f90fSIdo Schimmel nsim_dev_trap_item_lookup(struct nsim_dev *nsim_dev, u16 trap_id) 790da58f90fSIdo Schimmel { 791da58f90fSIdo Schimmel struct nsim_trap_data *nsim_trap_data = nsim_dev->trap_data; 792da58f90fSIdo Schimmel int i; 793da58f90fSIdo Schimmel 794da58f90fSIdo Schimmel for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) { 795da58f90fSIdo Schimmel if (nsim_traps_arr[i].id == trap_id) 796da58f90fSIdo Schimmel return &nsim_trap_data->trap_items_arr[i]; 797da58f90fSIdo Schimmel } 798da58f90fSIdo Schimmel 799da58f90fSIdo Schimmel return NULL; 800da58f90fSIdo Schimmel } 801da58f90fSIdo Schimmel 802da58f90fSIdo Schimmel static int nsim_dev_devlink_trap_init(struct devlink *devlink, 803da58f90fSIdo Schimmel const struct devlink_trap *trap, 804da58f90fSIdo Schimmel void *trap_ctx) 805da58f90fSIdo Schimmel { 806da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 807da58f90fSIdo Schimmel struct nsim_trap_item *nsim_trap_item; 808da58f90fSIdo Schimmel 809da58f90fSIdo Schimmel nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id); 810da58f90fSIdo Schimmel if (WARN_ON(!nsim_trap_item)) 811da58f90fSIdo Schimmel return -ENOENT; 812da58f90fSIdo Schimmel 813da58f90fSIdo Schimmel nsim_trap_item->trap_ctx = trap_ctx; 814da58f90fSIdo Schimmel nsim_trap_item->action = trap->init_action; 815da58f90fSIdo Schimmel 816da58f90fSIdo Schimmel return 0; 817da58f90fSIdo Schimmel } 818da58f90fSIdo Schimmel 819da58f90fSIdo Schimmel static int 820da58f90fSIdo Schimmel nsim_dev_devlink_trap_action_set(struct devlink *devlink, 821da58f90fSIdo Schimmel const struct devlink_trap *trap, 822c88e11e0SIdo Schimmel enum devlink_trap_action action, 823c88e11e0SIdo Schimmel struct netlink_ext_ack *extack) 824da58f90fSIdo Schimmel { 825da58f90fSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 826da58f90fSIdo Schimmel struct nsim_trap_item *nsim_trap_item; 827da58f90fSIdo Schimmel 828da58f90fSIdo Schimmel nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id); 829da58f90fSIdo Schimmel if (WARN_ON(!nsim_trap_item)) 830da58f90fSIdo Schimmel return -ENOENT; 831da58f90fSIdo Schimmel 832da58f90fSIdo Schimmel spin_lock(&nsim_dev->trap_data->trap_lock); 833da58f90fSIdo Schimmel nsim_trap_item->action = action; 834da58f90fSIdo Schimmel spin_unlock(&nsim_dev->trap_data->trap_lock); 835da58f90fSIdo Schimmel 836da58f90fSIdo Schimmel return 0; 837da58f90fSIdo Schimmel } 838da58f90fSIdo Schimmel 839ad188458SIdo Schimmel static int 8400dc8249aSIdo Schimmel nsim_dev_devlink_trap_group_set(struct devlink *devlink, 8410dc8249aSIdo Schimmel const struct devlink_trap_group *group, 842c88e11e0SIdo Schimmel const struct devlink_trap_policer *policer, 843c88e11e0SIdo Schimmel struct netlink_ext_ack *extack) 8440dc8249aSIdo Schimmel { 8450dc8249aSIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 8460dc8249aSIdo Schimmel 8470dc8249aSIdo Schimmel if (nsim_dev->fail_trap_group_set) 8480dc8249aSIdo Schimmel return -EINVAL; 8490dc8249aSIdo Schimmel 8500dc8249aSIdo Schimmel return 0; 8510dc8249aSIdo Schimmel } 8520dc8249aSIdo Schimmel 8530dc8249aSIdo Schimmel static int 854ad188458SIdo Schimmel nsim_dev_devlink_trap_policer_set(struct devlink *devlink, 855ad188458SIdo Schimmel const struct devlink_trap_policer *policer, 856ad188458SIdo Schimmel u64 rate, u64 burst, 857ad188458SIdo Schimmel struct netlink_ext_ack *extack) 858ad188458SIdo Schimmel { 859ad188458SIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 860ad188458SIdo Schimmel 861ad188458SIdo Schimmel if (nsim_dev->fail_trap_policer_set) { 862ad188458SIdo Schimmel NL_SET_ERR_MSG_MOD(extack, "User setup the operation to fail for testing purposes"); 863ad188458SIdo Schimmel return -EINVAL; 864ad188458SIdo Schimmel } 865ad188458SIdo Schimmel 866ad188458SIdo Schimmel return 0; 867ad188458SIdo Schimmel } 868ad188458SIdo Schimmel 869ad188458SIdo Schimmel static int 870ad188458SIdo Schimmel nsim_dev_devlink_trap_policer_counter_get(struct devlink *devlink, 871ad188458SIdo Schimmel const struct devlink_trap_policer *policer, 872ad188458SIdo Schimmel u64 *p_drops) 873ad188458SIdo Schimmel { 874ad188458SIdo Schimmel struct nsim_dev *nsim_dev = devlink_priv(devlink); 875ad188458SIdo Schimmel u64 *cnt; 876ad188458SIdo Schimmel 877ad188458SIdo Schimmel if (nsim_dev->fail_trap_policer_counter_get) 878ad188458SIdo Schimmel return -EINVAL; 879ad188458SIdo Schimmel 880ad188458SIdo Schimmel cnt = &nsim_dev->trap_data->trap_policers_cnt_arr[policer->id - 1]; 881be43224fSIdo Schimmel *p_drops = (*cnt)++; 882ad188458SIdo Schimmel 883ad188458SIdo Schimmel return 0; 884ad188458SIdo Schimmel } 885ad188458SIdo Schimmel 8868fb4bc6fSJiri Pirko static const struct devlink_ops nsim_dev_devlink_ops = { 887*cbb58368SJacob Keller .supported_flash_update_params = DEVLINK_SUPPORT_FLASH_UPDATE_COMPONENT | 888*cbb58368SJacob Keller DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK, 88997691069SJiri Pirko .reload_down = nsim_dev_reload_down, 89097691069SJiri Pirko .reload_up = nsim_dev_reload_up, 8918e23cc03SJiri Pirko .info_get = nsim_dev_info_get, 892fa4dfc4aSJiri Pirko .flash_update = nsim_dev_flash_update, 893da58f90fSIdo Schimmel .trap_init = nsim_dev_devlink_trap_init, 894da58f90fSIdo Schimmel .trap_action_set = nsim_dev_devlink_trap_action_set, 8950dc8249aSIdo Schimmel .trap_group_set = nsim_dev_devlink_trap_group_set, 896ad188458SIdo Schimmel .trap_policer_set = nsim_dev_devlink_trap_policer_set, 897ad188458SIdo Schimmel .trap_policer_counter_get = nsim_dev_devlink_trap_policer_counter_get, 8988fb4bc6fSJiri Pirko }; 8998fb4bc6fSJiri Pirko 900150e8f8aSJiri Pirko #define NSIM_DEV_MAX_MACS_DEFAULT 32 901150e8f8aSJiri Pirko #define NSIM_DEV_TEST1_DEFAULT true 902150e8f8aSJiri Pirko 903794b2c05SJiri Pirko static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, 904794b2c05SJiri Pirko unsigned int port_index) 9058320d145SJiri Pirko { 90671ad8d55SDanielle Ratson struct devlink_port_attrs attrs = {}; 9078320d145SJiri Pirko struct nsim_dev_port *nsim_dev_port; 9088320d145SJiri Pirko struct devlink_port *devlink_port; 9098320d145SJiri Pirko int err; 9108320d145SJiri Pirko 9118320d145SJiri Pirko nsim_dev_port = kzalloc(sizeof(*nsim_dev_port), GFP_KERNEL); 9128320d145SJiri Pirko if (!nsim_dev_port) 9138320d145SJiri Pirko return -ENOMEM; 9148320d145SJiri Pirko nsim_dev_port->port_index = port_index; 9158320d145SJiri Pirko 9168320d145SJiri Pirko devlink_port = &nsim_dev_port->devlink_port; 91771ad8d55SDanielle Ratson attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; 91871ad8d55SDanielle Ratson attrs.phys.port_number = port_index + 1; 91971ad8d55SDanielle Ratson memcpy(attrs.switch_id.id, nsim_dev->switch_id.id, nsim_dev->switch_id.id_len); 92071ad8d55SDanielle Ratson attrs.switch_id.id_len = nsim_dev->switch_id.id_len; 92171ad8d55SDanielle Ratson devlink_port_attrs_set(devlink_port, &attrs); 9228320d145SJiri Pirko err = devlink_port_register(priv_to_devlink(nsim_dev), devlink_port, 9238320d145SJiri Pirko port_index); 9248320d145SJiri Pirko if (err) 9258320d145SJiri Pirko goto err_port_free; 9268320d145SJiri Pirko 9278320d145SJiri Pirko err = nsim_dev_port_debugfs_init(nsim_dev, nsim_dev_port); 9288320d145SJiri Pirko if (err) 9298320d145SJiri Pirko goto err_dl_port_unregister; 9308320d145SJiri Pirko 931e05b2d14SJiri Pirko nsim_dev_port->ns = nsim_create(nsim_dev, nsim_dev_port); 932e05b2d14SJiri Pirko if (IS_ERR(nsim_dev_port->ns)) { 933e05b2d14SJiri Pirko err = PTR_ERR(nsim_dev_port->ns); 934e05b2d14SJiri Pirko goto err_port_debugfs_exit; 935e05b2d14SJiri Pirko } 936e05b2d14SJiri Pirko 937e05b2d14SJiri Pirko devlink_port_type_eth_set(devlink_port, nsim_dev_port->ns->netdev); 9388320d145SJiri Pirko list_add(&nsim_dev_port->list, &nsim_dev->port_list); 9398320d145SJiri Pirko 9408320d145SJiri Pirko return 0; 9418320d145SJiri Pirko 942e05b2d14SJiri Pirko err_port_debugfs_exit: 943e05b2d14SJiri Pirko nsim_dev_port_debugfs_exit(nsim_dev_port); 9448320d145SJiri Pirko err_dl_port_unregister: 9458320d145SJiri Pirko devlink_port_unregister(devlink_port); 9468320d145SJiri Pirko err_port_free: 9478320d145SJiri Pirko kfree(nsim_dev_port); 9488320d145SJiri Pirko return err; 9498320d145SJiri Pirko } 9508320d145SJiri Pirko 951794b2c05SJiri Pirko static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port) 9528320d145SJiri Pirko { 9538320d145SJiri Pirko struct devlink_port *devlink_port = &nsim_dev_port->devlink_port; 9548320d145SJiri Pirko 9558320d145SJiri Pirko list_del(&nsim_dev_port->list); 956e05b2d14SJiri Pirko devlink_port_type_clear(devlink_port); 957e05b2d14SJiri Pirko nsim_destroy(nsim_dev_port->ns); 9588320d145SJiri Pirko nsim_dev_port_debugfs_exit(nsim_dev_port); 9598320d145SJiri Pirko devlink_port_unregister(devlink_port); 9608320d145SJiri Pirko kfree(nsim_dev_port); 9618320d145SJiri Pirko } 9628320d145SJiri Pirko 9638320d145SJiri Pirko static void nsim_dev_port_del_all(struct nsim_dev *nsim_dev) 9648320d145SJiri Pirko { 9658320d145SJiri Pirko struct nsim_dev_port *nsim_dev_port, *tmp; 9668320d145SJiri Pirko 9676d6f0383SIdo Schimmel mutex_lock(&nsim_dev->port_list_lock); 9688320d145SJiri Pirko list_for_each_entry_safe(nsim_dev_port, tmp, 9698320d145SJiri Pirko &nsim_dev->port_list, list) 970794b2c05SJiri Pirko __nsim_dev_port_del(nsim_dev_port); 9716d6f0383SIdo Schimmel mutex_unlock(&nsim_dev->port_list_lock); 9728320d145SJiri Pirko } 9738320d145SJiri Pirko 9747f36a77aSJiri Pirko static int nsim_dev_port_add_all(struct nsim_dev *nsim_dev, 9757f36a77aSJiri Pirko unsigned int port_count) 9768320d145SJiri Pirko { 9777f36a77aSJiri Pirko int i, err; 9788320d145SJiri Pirko 9797f36a77aSJiri Pirko for (i = 0; i < port_count; i++) { 980794b2c05SJiri Pirko err = __nsim_dev_port_add(nsim_dev, i); 9818320d145SJiri Pirko if (err) 9828320d145SJiri Pirko goto err_port_del_all; 9838320d145SJiri Pirko } 9848320d145SJiri Pirko return 0; 9858320d145SJiri Pirko 9868320d145SJiri Pirko err_port_del_all: 9878320d145SJiri Pirko nsim_dev_port_del_all(nsim_dev); 9888320d145SJiri Pirko return err; 9898320d145SJiri Pirko } 9908320d145SJiri Pirko 99175ba029fSJiri Pirko static int nsim_dev_reload_create(struct nsim_dev *nsim_dev, 99275ba029fSJiri Pirko struct netlink_ext_ack *extack) 99375ba029fSJiri Pirko { 99475ba029fSJiri Pirko struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev; 99575ba029fSJiri Pirko struct devlink *devlink; 99675ba029fSJiri Pirko int err; 99775ba029fSJiri Pirko 99875ba029fSJiri Pirko devlink = priv_to_devlink(nsim_dev); 99975ba029fSJiri Pirko nsim_dev = devlink_priv(devlink); 100075ba029fSJiri Pirko INIT_LIST_HEAD(&nsim_dev->port_list); 100175ba029fSJiri Pirko mutex_init(&nsim_dev->port_list_lock); 100275ba029fSJiri Pirko nsim_dev->fw_update_status = true; 1003*cbb58368SJacob Keller nsim_dev->fw_update_overwrite_mask = 0; 100475ba029fSJiri Pirko 100575ba029fSJiri Pirko nsim_dev->fib_data = nsim_fib_create(devlink, extack); 100675ba029fSJiri Pirko if (IS_ERR(nsim_dev->fib_data)) 100775ba029fSJiri Pirko return PTR_ERR(nsim_dev->fib_data); 100875ba029fSJiri Pirko 100975ba029fSJiri Pirko nsim_devlink_param_load_driverinit_values(devlink); 101075ba029fSJiri Pirko 101175ba029fSJiri Pirko err = nsim_dev_dummy_region_init(nsim_dev, devlink); 101275ba029fSJiri Pirko if (err) 101375ba029fSJiri Pirko goto err_fib_destroy; 101475ba029fSJiri Pirko 101575ba029fSJiri Pirko err = nsim_dev_traps_init(devlink); 101675ba029fSJiri Pirko if (err) 101775ba029fSJiri Pirko goto err_dummy_region_exit; 101875ba029fSJiri Pirko 101982c93a87SJiri Pirko err = nsim_dev_health_init(nsim_dev, devlink); 102075ba029fSJiri Pirko if (err) 102175ba029fSJiri Pirko goto err_traps_exit; 102275ba029fSJiri Pirko 102382c93a87SJiri Pirko err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count); 102482c93a87SJiri Pirko if (err) 102582c93a87SJiri Pirko goto err_health_exit; 102682c93a87SJiri Pirko 10278526ad96STaehee Yoo nsim_dev->take_snapshot = debugfs_create_file("take_snapshot", 10288526ad96STaehee Yoo 0200, 10298526ad96STaehee Yoo nsim_dev->ddir, 10308526ad96STaehee Yoo nsim_dev, 10318526ad96STaehee Yoo &nsim_dev_take_snapshot_fops); 103275ba029fSJiri Pirko return 0; 103375ba029fSJiri Pirko 103482c93a87SJiri Pirko err_health_exit: 103582c93a87SJiri Pirko nsim_dev_health_exit(nsim_dev); 103675ba029fSJiri Pirko err_traps_exit: 103775ba029fSJiri Pirko nsim_dev_traps_exit(devlink); 103875ba029fSJiri Pirko err_dummy_region_exit: 103975ba029fSJiri Pirko nsim_dev_dummy_region_exit(nsim_dev); 104075ba029fSJiri Pirko err_fib_destroy: 104175ba029fSJiri Pirko nsim_fib_destroy(devlink, nsim_dev->fib_data); 104275ba029fSJiri Pirko return err; 104375ba029fSJiri Pirko } 104475ba029fSJiri Pirko 1045bfcccfe7SJakub Kicinski int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev) 10467f36a77aSJiri Pirko { 10477f36a77aSJiri Pirko struct nsim_dev *nsim_dev; 10487f36a77aSJiri Pirko struct devlink *devlink; 10497f36a77aSJiri Pirko int err; 10507f36a77aSJiri Pirko 10517f36a77aSJiri Pirko devlink = devlink_alloc(&nsim_dev_devlink_ops, sizeof(*nsim_dev)); 10527f36a77aSJiri Pirko if (!devlink) 1053bfcccfe7SJakub Kicinski return -ENOMEM; 10547b60027bSJiri Pirko devlink_net_set(devlink, nsim_bus_dev->initial_net); 10557f36a77aSJiri Pirko nsim_dev = devlink_priv(devlink); 10567f36a77aSJiri Pirko nsim_dev->nsim_bus_dev = nsim_bus_dev; 10577f36a77aSJiri Pirko nsim_dev->switch_id.id_len = sizeof(nsim_dev->switch_id.id); 10587f36a77aSJiri Pirko get_random_bytes(nsim_dev->switch_id.id, nsim_dev->switch_id.id_len); 10597f36a77aSJiri Pirko INIT_LIST_HEAD(&nsim_dev->port_list); 10607f36a77aSJiri Pirko mutex_init(&nsim_dev->port_list_lock); 10617f36a77aSJiri Pirko nsim_dev->fw_update_status = true; 1062*cbb58368SJacob Keller nsim_dev->fw_update_overwrite_mask = 0; 10637f36a77aSJiri Pirko nsim_dev->max_macs = NSIM_DEV_MAX_MACS_DEFAULT; 10647f36a77aSJiri Pirko nsim_dev->test1 = NSIM_DEV_TEST1_DEFAULT; 1065d3cbb907SJiri Pirko spin_lock_init(&nsim_dev->fa_cookie_lock); 10667f36a77aSJiri Pirko 1067bfcccfe7SJakub Kicinski dev_set_drvdata(&nsim_bus_dev->dev, nsim_dev); 1068bfcccfe7SJakub Kicinski 10697f36a77aSJiri Pirko err = nsim_dev_resources_register(devlink); 10707f36a77aSJiri Pirko if (err) 10717f36a77aSJiri Pirko goto err_devlink_free; 10727f36a77aSJiri Pirko 107375ba029fSJiri Pirko nsim_dev->fib_data = nsim_fib_create(devlink, NULL); 10747f36a77aSJiri Pirko if (IS_ERR(nsim_dev->fib_data)) { 10757f36a77aSJiri Pirko err = PTR_ERR(nsim_dev->fib_data); 10767f36a77aSJiri Pirko goto err_resources_unregister; 10777f36a77aSJiri Pirko } 10787f36a77aSJiri Pirko 10797f36a77aSJiri Pirko err = devlink_register(devlink, &nsim_bus_dev->dev); 10807f36a77aSJiri Pirko if (err) 10817f36a77aSJiri Pirko goto err_fib_destroy; 10827f36a77aSJiri Pirko 10837f36a77aSJiri Pirko err = devlink_params_register(devlink, nsim_devlink_params, 10847f36a77aSJiri Pirko ARRAY_SIZE(nsim_devlink_params)); 10857f36a77aSJiri Pirko if (err) 10867f36a77aSJiri Pirko goto err_dl_unregister; 10877f36a77aSJiri Pirko nsim_devlink_set_params_init_values(nsim_dev, devlink); 10887f36a77aSJiri Pirko 10897f36a77aSJiri Pirko err = nsim_dev_dummy_region_init(nsim_dev, devlink); 10907f36a77aSJiri Pirko if (err) 10917f36a77aSJiri Pirko goto err_params_unregister; 10927f36a77aSJiri Pirko 10937f36a77aSJiri Pirko err = nsim_dev_traps_init(devlink); 10947f36a77aSJiri Pirko if (err) 10957f36a77aSJiri Pirko goto err_dummy_region_exit; 10967f36a77aSJiri Pirko 10977f36a77aSJiri Pirko err = nsim_dev_debugfs_init(nsim_dev); 10987f36a77aSJiri Pirko if (err) 10997f36a77aSJiri Pirko goto err_traps_exit; 11007f36a77aSJiri Pirko 110182c93a87SJiri Pirko err = nsim_dev_health_init(nsim_dev, devlink); 11027f36a77aSJiri Pirko if (err) 11037f36a77aSJiri Pirko goto err_debugfs_exit; 11047f36a77aSJiri Pirko 110582c93a87SJiri Pirko err = nsim_bpf_dev_init(nsim_dev); 110682c93a87SJiri Pirko if (err) 110782c93a87SJiri Pirko goto err_health_exit; 110882c93a87SJiri Pirko 11097f36a77aSJiri Pirko err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count); 11107f36a77aSJiri Pirko if (err) 11117f36a77aSJiri Pirko goto err_bpf_dev_exit; 11127f36a77aSJiri Pirko 11137f36a77aSJiri Pirko devlink_params_publish(devlink); 1114a0c76345SJiri Pirko devlink_reload_enable(devlink); 1115bfcccfe7SJakub Kicinski return 0; 11167f36a77aSJiri Pirko 11177f36a77aSJiri Pirko err_bpf_dev_exit: 11187f36a77aSJiri Pirko nsim_bpf_dev_exit(nsim_dev); 111982c93a87SJiri Pirko err_health_exit: 112082c93a87SJiri Pirko nsim_dev_health_exit(nsim_dev); 11217f36a77aSJiri Pirko err_debugfs_exit: 11227f36a77aSJiri Pirko nsim_dev_debugfs_exit(nsim_dev); 11237f36a77aSJiri Pirko err_traps_exit: 11247f36a77aSJiri Pirko nsim_dev_traps_exit(devlink); 11257f36a77aSJiri Pirko err_dummy_region_exit: 11267f36a77aSJiri Pirko nsim_dev_dummy_region_exit(nsim_dev); 11277f36a77aSJiri Pirko err_params_unregister: 11287f36a77aSJiri Pirko devlink_params_unregister(devlink, nsim_devlink_params, 11297f36a77aSJiri Pirko ARRAY_SIZE(nsim_devlink_params)); 11307f36a77aSJiri Pirko err_dl_unregister: 11317f36a77aSJiri Pirko devlink_unregister(devlink); 11327f36a77aSJiri Pirko err_fib_destroy: 11337f36a77aSJiri Pirko nsim_fib_destroy(devlink, nsim_dev->fib_data); 11347f36a77aSJiri Pirko err_resources_unregister: 11357f36a77aSJiri Pirko devlink_resources_unregister(devlink, NULL); 11367f36a77aSJiri Pirko err_devlink_free: 11377f36a77aSJiri Pirko devlink_free(devlink); 1138bfcccfe7SJakub Kicinski return err; 11397f36a77aSJiri Pirko } 11407f36a77aSJiri Pirko 114175ba029fSJiri Pirko static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev) 114275ba029fSJiri Pirko { 114375ba029fSJiri Pirko struct devlink *devlink = priv_to_devlink(nsim_dev); 114475ba029fSJiri Pirko 114575ba029fSJiri Pirko if (devlink_is_reload_failed(devlink)) 114675ba029fSJiri Pirko return; 11478526ad96STaehee Yoo debugfs_remove(nsim_dev->take_snapshot); 114875ba029fSJiri Pirko nsim_dev_port_del_all(nsim_dev); 114982c93a87SJiri Pirko nsim_dev_health_exit(nsim_dev); 115075ba029fSJiri Pirko nsim_dev_traps_exit(devlink); 115175ba029fSJiri Pirko nsim_dev_dummy_region_exit(nsim_dev); 115275ba029fSJiri Pirko mutex_destroy(&nsim_dev->port_list_lock); 115375ba029fSJiri Pirko nsim_fib_destroy(devlink, nsim_dev->fib_data); 115475ba029fSJiri Pirko } 115575ba029fSJiri Pirko 1156bfcccfe7SJakub Kicinski void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev) 11577f36a77aSJiri Pirko { 1158bfcccfe7SJakub Kicinski struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); 11597f36a77aSJiri Pirko struct devlink *devlink = priv_to_devlink(nsim_dev); 11607f36a77aSJiri Pirko 1161a0c76345SJiri Pirko devlink_reload_disable(devlink); 1162a0c76345SJiri Pirko 116375ba029fSJiri Pirko nsim_dev_reload_destroy(nsim_dev); 116475ba029fSJiri Pirko 11657f36a77aSJiri Pirko nsim_bpf_dev_exit(nsim_dev); 11667f36a77aSJiri Pirko nsim_dev_debugfs_exit(nsim_dev); 11677f36a77aSJiri Pirko devlink_params_unregister(devlink, nsim_devlink_params, 11687f36a77aSJiri Pirko ARRAY_SIZE(nsim_devlink_params)); 11697f36a77aSJiri Pirko devlink_unregister(devlink); 11707f36a77aSJiri Pirko devlink_resources_unregister(devlink, NULL); 11717f36a77aSJiri Pirko devlink_free(devlink); 11727f36a77aSJiri Pirko } 11737f36a77aSJiri Pirko 1174794b2c05SJiri Pirko static struct nsim_dev_port * 1175794b2c05SJiri Pirko __nsim_dev_port_lookup(struct nsim_dev *nsim_dev, unsigned int port_index) 1176794b2c05SJiri Pirko { 1177794b2c05SJiri Pirko struct nsim_dev_port *nsim_dev_port; 1178794b2c05SJiri Pirko 1179794b2c05SJiri Pirko list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) 1180794b2c05SJiri Pirko if (nsim_dev_port->port_index == port_index) 1181794b2c05SJiri Pirko return nsim_dev_port; 1182794b2c05SJiri Pirko return NULL; 1183794b2c05SJiri Pirko } 1184794b2c05SJiri Pirko 1185794b2c05SJiri Pirko int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev, 1186794b2c05SJiri Pirko unsigned int port_index) 1187794b2c05SJiri Pirko { 1188794b2c05SJiri Pirko struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); 1189794b2c05SJiri Pirko int err; 1190794b2c05SJiri Pirko 1191794b2c05SJiri Pirko mutex_lock(&nsim_dev->port_list_lock); 1192794b2c05SJiri Pirko if (__nsim_dev_port_lookup(nsim_dev, port_index)) 1193794b2c05SJiri Pirko err = -EEXIST; 1194794b2c05SJiri Pirko else 1195794b2c05SJiri Pirko err = __nsim_dev_port_add(nsim_dev, port_index); 1196794b2c05SJiri Pirko mutex_unlock(&nsim_dev->port_list_lock); 1197794b2c05SJiri Pirko return err; 1198794b2c05SJiri Pirko } 1199794b2c05SJiri Pirko 1200794b2c05SJiri Pirko int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev, 1201794b2c05SJiri Pirko unsigned int port_index) 1202794b2c05SJiri Pirko { 1203794b2c05SJiri Pirko struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); 1204794b2c05SJiri Pirko struct nsim_dev_port *nsim_dev_port; 1205794b2c05SJiri Pirko int err = 0; 1206794b2c05SJiri Pirko 1207794b2c05SJiri Pirko mutex_lock(&nsim_dev->port_list_lock); 1208794b2c05SJiri Pirko nsim_dev_port = __nsim_dev_port_lookup(nsim_dev, port_index); 1209794b2c05SJiri Pirko if (!nsim_dev_port) 1210794b2c05SJiri Pirko err = -ENOENT; 1211794b2c05SJiri Pirko else 1212794b2c05SJiri Pirko __nsim_dev_port_del(nsim_dev_port); 1213794b2c05SJiri Pirko mutex_unlock(&nsim_dev->port_list_lock); 1214794b2c05SJiri Pirko return err; 1215794b2c05SJiri Pirko } 1216794b2c05SJiri Pirko 1217d514f41eSJiri Pirko int nsim_dev_init(void) 1218d514f41eSJiri Pirko { 1219ab1d0cc0SJiri Pirko nsim_dev_ddir = debugfs_create_dir(DRV_NAME, NULL); 122034611e69Skbuild test robot return PTR_ERR_OR_ZERO(nsim_dev_ddir); 1221d514f41eSJiri Pirko } 1222d514f41eSJiri Pirko 1223d514f41eSJiri Pirko void nsim_dev_exit(void) 1224d514f41eSJiri Pirko { 1225d514f41eSJiri Pirko debugfs_remove_recursive(nsim_dev_ddir); 1226d514f41eSJiri Pirko } 1227