162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2018 Cumulus Networks. All rights reserved. 362306a36Sopenharmony_ci * Copyright (c) 2018 David Ahern <dsa@cumulusnetworks.com> 462306a36Sopenharmony_ci * Copyright (c) 2019 Mellanox Technologies. All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This software is licensed under the GNU General License Version 2, 762306a36Sopenharmony_ci * June 1991 as shown in the file COPYING in the top-level directory of this 862306a36Sopenharmony_ci * source tree. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" 1162306a36Sopenharmony_ci * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 1262306a36Sopenharmony_ci * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 1362306a36Sopenharmony_ci * FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE 1462306a36Sopenharmony_ci * OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME 1562306a36Sopenharmony_ci * THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/debugfs.h> 1962306a36Sopenharmony_ci#include <linux/device.h> 2062306a36Sopenharmony_ci#include <linux/etherdevice.h> 2162306a36Sopenharmony_ci#include <linux/inet.h> 2262306a36Sopenharmony_ci#include <linux/jiffies.h> 2362306a36Sopenharmony_ci#include <linux/kernel.h> 2462306a36Sopenharmony_ci#include <linux/list.h> 2562306a36Sopenharmony_ci#include <linux/mutex.h> 2662306a36Sopenharmony_ci#include <linux/random.h> 2762306a36Sopenharmony_ci#include <linux/rtnetlink.h> 2862306a36Sopenharmony_ci#include <linux/workqueue.h> 2962306a36Sopenharmony_ci#include <net/devlink.h> 3062306a36Sopenharmony_ci#include <net/ip.h> 3162306a36Sopenharmony_ci#include <net/flow_offload.h> 3262306a36Sopenharmony_ci#include <uapi/linux/devlink.h> 3362306a36Sopenharmony_ci#include <uapi/linux/ip.h> 3462306a36Sopenharmony_ci#include <uapi/linux/udp.h> 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#include "netdevsim.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic unsigned int 3962306a36Sopenharmony_cinsim_dev_port_index(enum nsim_dev_port_type type, unsigned int port_index) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci switch (type) { 4262306a36Sopenharmony_ci case NSIM_DEV_PORT_TYPE_VF: 4362306a36Sopenharmony_ci port_index = NSIM_DEV_VF_PORT_INDEX_BASE + port_index; 4462306a36Sopenharmony_ci break; 4562306a36Sopenharmony_ci case NSIM_DEV_PORT_TYPE_PF: 4662306a36Sopenharmony_ci break; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci return port_index; 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic inline unsigned int nsim_dev_port_index_to_vf_index(unsigned int port_index) 5362306a36Sopenharmony_ci{ 5462306a36Sopenharmony_ci return port_index - NSIM_DEV_VF_PORT_INDEX_BASE; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_cistatic struct dentry *nsim_dev_ddir; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ciunsigned int nsim_dev_get_vfs(struct nsim_dev *nsim_dev) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci WARN_ON(!lockdep_rtnl_is_held() && 6262306a36Sopenharmony_ci !devl_lock_is_held(priv_to_devlink(nsim_dev))); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci return nsim_dev->nsim_bus_dev->num_vfs; 6562306a36Sopenharmony_ci} 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistatic void 6862306a36Sopenharmony_cinsim_bus_dev_set_vfs(struct nsim_bus_dev *nsim_bus_dev, unsigned int num_vfs) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci rtnl_lock(); 7162306a36Sopenharmony_ci nsim_bus_dev->num_vfs = num_vfs; 7262306a36Sopenharmony_ci rtnl_unlock(); 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci#define NSIM_DEV_DUMMY_REGION_SIZE (1024 * 32) 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int 7862306a36Sopenharmony_cinsim_dev_take_snapshot(struct devlink *devlink, 7962306a36Sopenharmony_ci const struct devlink_region_ops *ops, 8062306a36Sopenharmony_ci struct netlink_ext_ack *extack, 8162306a36Sopenharmony_ci u8 **data) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci void *dummy_data; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci dummy_data = kmalloc(NSIM_DEV_DUMMY_REGION_SIZE, GFP_KERNEL); 8662306a36Sopenharmony_ci if (!dummy_data) 8762306a36Sopenharmony_ci return -ENOMEM; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci get_random_bytes(dummy_data, NSIM_DEV_DUMMY_REGION_SIZE); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci *data = dummy_data; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci return 0; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic ssize_t nsim_dev_take_snapshot_write(struct file *file, 9762306a36Sopenharmony_ci const char __user *data, 9862306a36Sopenharmony_ci size_t count, loff_t *ppos) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci struct nsim_dev *nsim_dev = file->private_data; 10162306a36Sopenharmony_ci struct devlink *devlink; 10262306a36Sopenharmony_ci u8 *dummy_data; 10362306a36Sopenharmony_ci int err; 10462306a36Sopenharmony_ci u32 id; 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci devlink = priv_to_devlink(nsim_dev); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci err = nsim_dev_take_snapshot(devlink, NULL, NULL, &dummy_data); 10962306a36Sopenharmony_ci if (err) 11062306a36Sopenharmony_ci return err; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci err = devlink_region_snapshot_id_get(devlink, &id); 11362306a36Sopenharmony_ci if (err) { 11462306a36Sopenharmony_ci pr_err("Failed to get snapshot id\n"); 11562306a36Sopenharmony_ci kfree(dummy_data); 11662306a36Sopenharmony_ci return err; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci err = devlink_region_snapshot_create(nsim_dev->dummy_region, 11962306a36Sopenharmony_ci dummy_data, id); 12062306a36Sopenharmony_ci devlink_region_snapshot_id_put(devlink, id); 12162306a36Sopenharmony_ci if (err) { 12262306a36Sopenharmony_ci pr_err("Failed to create region snapshot\n"); 12362306a36Sopenharmony_ci kfree(dummy_data); 12462306a36Sopenharmony_ci return err; 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci return count; 12862306a36Sopenharmony_ci} 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistatic const struct file_operations nsim_dev_take_snapshot_fops = { 13162306a36Sopenharmony_ci .open = simple_open, 13262306a36Sopenharmony_ci .write = nsim_dev_take_snapshot_write, 13362306a36Sopenharmony_ci .llseek = generic_file_llseek, 13462306a36Sopenharmony_ci .owner = THIS_MODULE, 13562306a36Sopenharmony_ci}; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic ssize_t nsim_dev_trap_fa_cookie_read(struct file *file, 13862306a36Sopenharmony_ci char __user *data, 13962306a36Sopenharmony_ci size_t count, loff_t *ppos) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct nsim_dev *nsim_dev = file->private_data; 14262306a36Sopenharmony_ci struct flow_action_cookie *fa_cookie; 14362306a36Sopenharmony_ci unsigned int buf_len; 14462306a36Sopenharmony_ci ssize_t ret; 14562306a36Sopenharmony_ci char *buf; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci spin_lock(&nsim_dev->fa_cookie_lock); 14862306a36Sopenharmony_ci fa_cookie = nsim_dev->fa_cookie; 14962306a36Sopenharmony_ci if (!fa_cookie) { 15062306a36Sopenharmony_ci ret = -EINVAL; 15162306a36Sopenharmony_ci goto errout; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci buf_len = fa_cookie->cookie_len * 2; 15462306a36Sopenharmony_ci buf = kmalloc(buf_len, GFP_ATOMIC); 15562306a36Sopenharmony_ci if (!buf) { 15662306a36Sopenharmony_ci ret = -ENOMEM; 15762306a36Sopenharmony_ci goto errout; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci bin2hex(buf, fa_cookie->cookie, fa_cookie->cookie_len); 16062306a36Sopenharmony_ci spin_unlock(&nsim_dev->fa_cookie_lock); 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci ret = simple_read_from_buffer(data, count, ppos, buf, buf_len); 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci kfree(buf); 16562306a36Sopenharmony_ci return ret; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cierrout: 16862306a36Sopenharmony_ci spin_unlock(&nsim_dev->fa_cookie_lock); 16962306a36Sopenharmony_ci return ret; 17062306a36Sopenharmony_ci} 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_cistatic ssize_t nsim_dev_trap_fa_cookie_write(struct file *file, 17362306a36Sopenharmony_ci const char __user *data, 17462306a36Sopenharmony_ci size_t count, loff_t *ppos) 17562306a36Sopenharmony_ci{ 17662306a36Sopenharmony_ci struct nsim_dev *nsim_dev = file->private_data; 17762306a36Sopenharmony_ci struct flow_action_cookie *fa_cookie; 17862306a36Sopenharmony_ci size_t cookie_len; 17962306a36Sopenharmony_ci ssize_t ret; 18062306a36Sopenharmony_ci char *buf; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (*ppos != 0) 18362306a36Sopenharmony_ci return -EINVAL; 18462306a36Sopenharmony_ci cookie_len = (count - 1) / 2; 18562306a36Sopenharmony_ci if ((count - 1) % 2) 18662306a36Sopenharmony_ci return -EINVAL; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci buf = memdup_user(data, count); 18962306a36Sopenharmony_ci if (IS_ERR(buf)) 19062306a36Sopenharmony_ci return PTR_ERR(buf); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci fa_cookie = kmalloc(sizeof(*fa_cookie) + cookie_len, 19362306a36Sopenharmony_ci GFP_KERNEL | __GFP_NOWARN); 19462306a36Sopenharmony_ci if (!fa_cookie) { 19562306a36Sopenharmony_ci ret = -ENOMEM; 19662306a36Sopenharmony_ci goto free_buf; 19762306a36Sopenharmony_ci } 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci fa_cookie->cookie_len = cookie_len; 20062306a36Sopenharmony_ci ret = hex2bin(fa_cookie->cookie, buf, cookie_len); 20162306a36Sopenharmony_ci if (ret) 20262306a36Sopenharmony_ci goto free_fa_cookie; 20362306a36Sopenharmony_ci kfree(buf); 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci spin_lock(&nsim_dev->fa_cookie_lock); 20662306a36Sopenharmony_ci kfree(nsim_dev->fa_cookie); 20762306a36Sopenharmony_ci nsim_dev->fa_cookie = fa_cookie; 20862306a36Sopenharmony_ci spin_unlock(&nsim_dev->fa_cookie_lock); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci return count; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_cifree_fa_cookie: 21362306a36Sopenharmony_ci kfree(fa_cookie); 21462306a36Sopenharmony_cifree_buf: 21562306a36Sopenharmony_ci kfree(buf); 21662306a36Sopenharmony_ci return ret; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic const struct file_operations nsim_dev_trap_fa_cookie_fops = { 22062306a36Sopenharmony_ci .open = simple_open, 22162306a36Sopenharmony_ci .read = nsim_dev_trap_fa_cookie_read, 22262306a36Sopenharmony_ci .write = nsim_dev_trap_fa_cookie_write, 22362306a36Sopenharmony_ci .llseek = generic_file_llseek, 22462306a36Sopenharmony_ci .owner = THIS_MODULE, 22562306a36Sopenharmony_ci}; 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic ssize_t nsim_bus_dev_max_vfs_read(struct file *file, char __user *data, 22862306a36Sopenharmony_ci size_t count, loff_t *ppos) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct nsim_dev *nsim_dev = file->private_data; 23162306a36Sopenharmony_ci char buf[11]; 23262306a36Sopenharmony_ci ssize_t len; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci len = scnprintf(buf, sizeof(buf), "%u\n", 23562306a36Sopenharmony_ci READ_ONCE(nsim_dev->nsim_bus_dev->max_vfs)); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci return simple_read_from_buffer(data, count, ppos, buf, len); 23862306a36Sopenharmony_ci} 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistatic ssize_t nsim_bus_dev_max_vfs_write(struct file *file, 24162306a36Sopenharmony_ci const char __user *data, 24262306a36Sopenharmony_ci size_t count, loff_t *ppos) 24362306a36Sopenharmony_ci{ 24462306a36Sopenharmony_ci struct nsim_vf_config *vfconfigs; 24562306a36Sopenharmony_ci struct nsim_dev *nsim_dev; 24662306a36Sopenharmony_ci char buf[10]; 24762306a36Sopenharmony_ci ssize_t ret; 24862306a36Sopenharmony_ci u32 val; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci if (*ppos != 0) 25162306a36Sopenharmony_ci return 0; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (count >= sizeof(buf)) 25462306a36Sopenharmony_ci return -ENOSPC; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci ret = copy_from_user(buf, data, count); 25762306a36Sopenharmony_ci if (ret) 25862306a36Sopenharmony_ci return -EFAULT; 25962306a36Sopenharmony_ci buf[count] = '\0'; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci ret = kstrtouint(buf, 10, &val); 26262306a36Sopenharmony_ci if (ret) 26362306a36Sopenharmony_ci return -EINVAL; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci /* max_vfs limited by the maximum number of provided port indexes */ 26662306a36Sopenharmony_ci if (val > NSIM_DEV_VF_PORT_INDEX_MAX - NSIM_DEV_VF_PORT_INDEX_BASE) 26762306a36Sopenharmony_ci return -ERANGE; 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci vfconfigs = kcalloc(val, sizeof(struct nsim_vf_config), 27062306a36Sopenharmony_ci GFP_KERNEL | __GFP_NOWARN); 27162306a36Sopenharmony_ci if (!vfconfigs) 27262306a36Sopenharmony_ci return -ENOMEM; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci nsim_dev = file->private_data; 27562306a36Sopenharmony_ci devl_lock(priv_to_devlink(nsim_dev)); 27662306a36Sopenharmony_ci /* Reject if VFs are configured */ 27762306a36Sopenharmony_ci if (nsim_dev_get_vfs(nsim_dev)) { 27862306a36Sopenharmony_ci ret = -EBUSY; 27962306a36Sopenharmony_ci } else { 28062306a36Sopenharmony_ci swap(nsim_dev->vfconfigs, vfconfigs); 28162306a36Sopenharmony_ci WRITE_ONCE(nsim_dev->nsim_bus_dev->max_vfs, val); 28262306a36Sopenharmony_ci *ppos += count; 28362306a36Sopenharmony_ci ret = count; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci devl_unlock(priv_to_devlink(nsim_dev)); 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci kfree(vfconfigs); 28862306a36Sopenharmony_ci return ret; 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic const struct file_operations nsim_dev_max_vfs_fops = { 29262306a36Sopenharmony_ci .open = simple_open, 29362306a36Sopenharmony_ci .read = nsim_bus_dev_max_vfs_read, 29462306a36Sopenharmony_ci .write = nsim_bus_dev_max_vfs_write, 29562306a36Sopenharmony_ci .llseek = generic_file_llseek, 29662306a36Sopenharmony_ci .owner = THIS_MODULE, 29762306a36Sopenharmony_ci}; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_cistatic int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev) 30062306a36Sopenharmony_ci{ 30162306a36Sopenharmony_ci char dev_ddir_name[sizeof(DRV_NAME) + 10]; 30262306a36Sopenharmony_ci int err; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci sprintf(dev_ddir_name, DRV_NAME "%u", nsim_dev->nsim_bus_dev->dev.id); 30562306a36Sopenharmony_ci nsim_dev->ddir = debugfs_create_dir(dev_ddir_name, nsim_dev_ddir); 30662306a36Sopenharmony_ci if (IS_ERR(nsim_dev->ddir)) 30762306a36Sopenharmony_ci return PTR_ERR(nsim_dev->ddir); 30862306a36Sopenharmony_ci nsim_dev->ports_ddir = debugfs_create_dir("ports", nsim_dev->ddir); 30962306a36Sopenharmony_ci if (IS_ERR(nsim_dev->ports_ddir)) { 31062306a36Sopenharmony_ci err = PTR_ERR(nsim_dev->ports_ddir); 31162306a36Sopenharmony_ci goto err_ddir; 31262306a36Sopenharmony_ci } 31362306a36Sopenharmony_ci debugfs_create_bool("fw_update_status", 0600, nsim_dev->ddir, 31462306a36Sopenharmony_ci &nsim_dev->fw_update_status); 31562306a36Sopenharmony_ci debugfs_create_u32("fw_update_overwrite_mask", 0600, nsim_dev->ddir, 31662306a36Sopenharmony_ci &nsim_dev->fw_update_overwrite_mask); 31762306a36Sopenharmony_ci debugfs_create_u32("max_macs", 0600, nsim_dev->ddir, 31862306a36Sopenharmony_ci &nsim_dev->max_macs); 31962306a36Sopenharmony_ci debugfs_create_bool("test1", 0600, nsim_dev->ddir, 32062306a36Sopenharmony_ci &nsim_dev->test1); 32162306a36Sopenharmony_ci nsim_dev->take_snapshot = debugfs_create_file("take_snapshot", 32262306a36Sopenharmony_ci 0200, 32362306a36Sopenharmony_ci nsim_dev->ddir, 32462306a36Sopenharmony_ci nsim_dev, 32562306a36Sopenharmony_ci &nsim_dev_take_snapshot_fops); 32662306a36Sopenharmony_ci debugfs_create_bool("dont_allow_reload", 0600, nsim_dev->ddir, 32762306a36Sopenharmony_ci &nsim_dev->dont_allow_reload); 32862306a36Sopenharmony_ci debugfs_create_bool("fail_reload", 0600, nsim_dev->ddir, 32962306a36Sopenharmony_ci &nsim_dev->fail_reload); 33062306a36Sopenharmony_ci debugfs_create_file("trap_flow_action_cookie", 0600, nsim_dev->ddir, 33162306a36Sopenharmony_ci nsim_dev, &nsim_dev_trap_fa_cookie_fops); 33262306a36Sopenharmony_ci debugfs_create_bool("fail_trap_group_set", 0600, 33362306a36Sopenharmony_ci nsim_dev->ddir, 33462306a36Sopenharmony_ci &nsim_dev->fail_trap_group_set); 33562306a36Sopenharmony_ci debugfs_create_bool("fail_trap_policer_set", 0600, 33662306a36Sopenharmony_ci nsim_dev->ddir, 33762306a36Sopenharmony_ci &nsim_dev->fail_trap_policer_set); 33862306a36Sopenharmony_ci debugfs_create_bool("fail_trap_policer_counter_get", 0600, 33962306a36Sopenharmony_ci nsim_dev->ddir, 34062306a36Sopenharmony_ci &nsim_dev->fail_trap_policer_counter_get); 34162306a36Sopenharmony_ci /* caution, dev_max_vfs write takes devlink lock */ 34262306a36Sopenharmony_ci debugfs_create_file("max_vfs", 0600, nsim_dev->ddir, 34362306a36Sopenharmony_ci nsim_dev, &nsim_dev_max_vfs_fops); 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci nsim_dev->nodes_ddir = debugfs_create_dir("rate_nodes", nsim_dev->ddir); 34662306a36Sopenharmony_ci if (IS_ERR(nsim_dev->nodes_ddir)) { 34762306a36Sopenharmony_ci err = PTR_ERR(nsim_dev->nodes_ddir); 34862306a36Sopenharmony_ci goto err_ports_ddir; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci debugfs_create_bool("fail_trap_drop_counter_get", 0600, 35162306a36Sopenharmony_ci nsim_dev->ddir, 35262306a36Sopenharmony_ci &nsim_dev->fail_trap_drop_counter_get); 35362306a36Sopenharmony_ci nsim_udp_tunnels_debugfs_create(nsim_dev); 35462306a36Sopenharmony_ci return 0; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_cierr_ports_ddir: 35762306a36Sopenharmony_ci debugfs_remove_recursive(nsim_dev->ports_ddir); 35862306a36Sopenharmony_cierr_ddir: 35962306a36Sopenharmony_ci debugfs_remove_recursive(nsim_dev->ddir); 36062306a36Sopenharmony_ci return err; 36162306a36Sopenharmony_ci} 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_cistatic void nsim_dev_debugfs_exit(struct nsim_dev *nsim_dev) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci debugfs_remove_recursive(nsim_dev->nodes_ddir); 36662306a36Sopenharmony_ci debugfs_remove_recursive(nsim_dev->ports_ddir); 36762306a36Sopenharmony_ci debugfs_remove_recursive(nsim_dev->ddir); 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic ssize_t nsim_dev_rate_parent_read(struct file *file, 37162306a36Sopenharmony_ci char __user *data, 37262306a36Sopenharmony_ci size_t count, loff_t *ppos) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci char **name_ptr = file->private_data; 37562306a36Sopenharmony_ci size_t len; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (!*name_ptr) 37862306a36Sopenharmony_ci return 0; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci len = strlen(*name_ptr); 38162306a36Sopenharmony_ci return simple_read_from_buffer(data, count, ppos, *name_ptr, len); 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_cistatic const struct file_operations nsim_dev_rate_parent_fops = { 38562306a36Sopenharmony_ci .open = simple_open, 38662306a36Sopenharmony_ci .read = nsim_dev_rate_parent_read, 38762306a36Sopenharmony_ci .llseek = generic_file_llseek, 38862306a36Sopenharmony_ci .owner = THIS_MODULE, 38962306a36Sopenharmony_ci}; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic int nsim_dev_port_debugfs_init(struct nsim_dev *nsim_dev, 39262306a36Sopenharmony_ci struct nsim_dev_port *nsim_dev_port) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev; 39562306a36Sopenharmony_ci unsigned int port_index = nsim_dev_port->port_index; 39662306a36Sopenharmony_ci char port_ddir_name[16]; 39762306a36Sopenharmony_ci char dev_link_name[32]; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci sprintf(port_ddir_name, "%u", port_index); 40062306a36Sopenharmony_ci nsim_dev_port->ddir = debugfs_create_dir(port_ddir_name, 40162306a36Sopenharmony_ci nsim_dev->ports_ddir); 40262306a36Sopenharmony_ci if (IS_ERR(nsim_dev_port->ddir)) 40362306a36Sopenharmony_ci return PTR_ERR(nsim_dev_port->ddir); 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci sprintf(dev_link_name, "../../../" DRV_NAME "%u", nsim_bus_dev->dev.id); 40662306a36Sopenharmony_ci if (nsim_dev_port_is_vf(nsim_dev_port)) { 40762306a36Sopenharmony_ci unsigned int vf_id = nsim_dev_port_index_to_vf_index(port_index); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci debugfs_create_u16("tx_share", 0400, nsim_dev_port->ddir, 41062306a36Sopenharmony_ci &nsim_dev->vfconfigs[vf_id].min_tx_rate); 41162306a36Sopenharmony_ci debugfs_create_u16("tx_max", 0400, nsim_dev_port->ddir, 41262306a36Sopenharmony_ci &nsim_dev->vfconfigs[vf_id].max_tx_rate); 41362306a36Sopenharmony_ci nsim_dev_port->rate_parent = debugfs_create_file("rate_parent", 41462306a36Sopenharmony_ci 0400, 41562306a36Sopenharmony_ci nsim_dev_port->ddir, 41662306a36Sopenharmony_ci &nsim_dev_port->parent_name, 41762306a36Sopenharmony_ci &nsim_dev_rate_parent_fops); 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci debugfs_create_symlink("dev", nsim_dev_port->ddir, dev_link_name); 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci return 0; 42262306a36Sopenharmony_ci} 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_cistatic void nsim_dev_port_debugfs_exit(struct nsim_dev_port *nsim_dev_port) 42562306a36Sopenharmony_ci{ 42662306a36Sopenharmony_ci debugfs_remove_recursive(nsim_dev_port->ddir); 42762306a36Sopenharmony_ci} 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cistatic int nsim_dev_resources_register(struct devlink *devlink) 43062306a36Sopenharmony_ci{ 43162306a36Sopenharmony_ci struct devlink_resource_size_params params = { 43262306a36Sopenharmony_ci .size_max = (u64)-1, 43362306a36Sopenharmony_ci .size_granularity = 1, 43462306a36Sopenharmony_ci .unit = DEVLINK_RESOURCE_UNIT_ENTRY 43562306a36Sopenharmony_ci }; 43662306a36Sopenharmony_ci int err; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* Resources for IPv4 */ 43962306a36Sopenharmony_ci err = devl_resource_register(devlink, "IPv4", (u64)-1, 44062306a36Sopenharmony_ci NSIM_RESOURCE_IPV4, 44162306a36Sopenharmony_ci DEVLINK_RESOURCE_ID_PARENT_TOP, 44262306a36Sopenharmony_ci ¶ms); 44362306a36Sopenharmony_ci if (err) { 44462306a36Sopenharmony_ci pr_err("Failed to register IPv4 top resource\n"); 44562306a36Sopenharmony_ci goto err_out; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci err = devl_resource_register(devlink, "fib", (u64)-1, 44962306a36Sopenharmony_ci NSIM_RESOURCE_IPV4_FIB, 45062306a36Sopenharmony_ci NSIM_RESOURCE_IPV4, ¶ms); 45162306a36Sopenharmony_ci if (err) { 45262306a36Sopenharmony_ci pr_err("Failed to register IPv4 FIB resource\n"); 45362306a36Sopenharmony_ci goto err_out; 45462306a36Sopenharmony_ci } 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_ci err = devl_resource_register(devlink, "fib-rules", (u64)-1, 45762306a36Sopenharmony_ci NSIM_RESOURCE_IPV4_FIB_RULES, 45862306a36Sopenharmony_ci NSIM_RESOURCE_IPV4, ¶ms); 45962306a36Sopenharmony_ci if (err) { 46062306a36Sopenharmony_ci pr_err("Failed to register IPv4 FIB rules resource\n"); 46162306a36Sopenharmony_ci goto err_out; 46262306a36Sopenharmony_ci } 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* Resources for IPv6 */ 46562306a36Sopenharmony_ci err = devl_resource_register(devlink, "IPv6", (u64)-1, 46662306a36Sopenharmony_ci NSIM_RESOURCE_IPV6, 46762306a36Sopenharmony_ci DEVLINK_RESOURCE_ID_PARENT_TOP, 46862306a36Sopenharmony_ci ¶ms); 46962306a36Sopenharmony_ci if (err) { 47062306a36Sopenharmony_ci pr_err("Failed to register IPv6 top resource\n"); 47162306a36Sopenharmony_ci goto err_out; 47262306a36Sopenharmony_ci } 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci err = devl_resource_register(devlink, "fib", (u64)-1, 47562306a36Sopenharmony_ci NSIM_RESOURCE_IPV6_FIB, 47662306a36Sopenharmony_ci NSIM_RESOURCE_IPV6, ¶ms); 47762306a36Sopenharmony_ci if (err) { 47862306a36Sopenharmony_ci pr_err("Failed to register IPv6 FIB resource\n"); 47962306a36Sopenharmony_ci goto err_out; 48062306a36Sopenharmony_ci } 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci err = devl_resource_register(devlink, "fib-rules", (u64)-1, 48362306a36Sopenharmony_ci NSIM_RESOURCE_IPV6_FIB_RULES, 48462306a36Sopenharmony_ci NSIM_RESOURCE_IPV6, ¶ms); 48562306a36Sopenharmony_ci if (err) { 48662306a36Sopenharmony_ci pr_err("Failed to register IPv6 FIB rules resource\n"); 48762306a36Sopenharmony_ci goto err_out; 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* Resources for nexthops */ 49162306a36Sopenharmony_ci err = devl_resource_register(devlink, "nexthops", (u64)-1, 49262306a36Sopenharmony_ci NSIM_RESOURCE_NEXTHOPS, 49362306a36Sopenharmony_ci DEVLINK_RESOURCE_ID_PARENT_TOP, 49462306a36Sopenharmony_ci ¶ms); 49562306a36Sopenharmony_ci if (err) { 49662306a36Sopenharmony_ci pr_err("Failed to register NEXTHOPS resource\n"); 49762306a36Sopenharmony_ci goto err_out; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci return 0; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_cierr_out: 50262306a36Sopenharmony_ci devl_resources_unregister(devlink); 50362306a36Sopenharmony_ci return err; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cienum nsim_devlink_param_id { 50762306a36Sopenharmony_ci NSIM_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX, 50862306a36Sopenharmony_ci NSIM_DEVLINK_PARAM_ID_TEST1, 50962306a36Sopenharmony_ci}; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_cistatic const struct devlink_param nsim_devlink_params[] = { 51262306a36Sopenharmony_ci DEVLINK_PARAM_GENERIC(MAX_MACS, 51362306a36Sopenharmony_ci BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), 51462306a36Sopenharmony_ci NULL, NULL, NULL), 51562306a36Sopenharmony_ci DEVLINK_PARAM_DRIVER(NSIM_DEVLINK_PARAM_ID_TEST1, 51662306a36Sopenharmony_ci "test1", DEVLINK_PARAM_TYPE_BOOL, 51762306a36Sopenharmony_ci BIT(DEVLINK_PARAM_CMODE_DRIVERINIT), 51862306a36Sopenharmony_ci NULL, NULL, NULL), 51962306a36Sopenharmony_ci}; 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_cistatic void nsim_devlink_set_params_init_values(struct nsim_dev *nsim_dev, 52262306a36Sopenharmony_ci struct devlink *devlink) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci union devlink_param_value value; 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci value.vu32 = nsim_dev->max_macs; 52762306a36Sopenharmony_ci devl_param_driverinit_value_set(devlink, 52862306a36Sopenharmony_ci DEVLINK_PARAM_GENERIC_ID_MAX_MACS, 52962306a36Sopenharmony_ci value); 53062306a36Sopenharmony_ci value.vbool = nsim_dev->test1; 53162306a36Sopenharmony_ci devl_param_driverinit_value_set(devlink, 53262306a36Sopenharmony_ci NSIM_DEVLINK_PARAM_ID_TEST1, 53362306a36Sopenharmony_ci value); 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic void nsim_devlink_param_load_driverinit_values(struct devlink *devlink) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct nsim_dev *nsim_dev = devlink_priv(devlink); 53962306a36Sopenharmony_ci union devlink_param_value saved_value; 54062306a36Sopenharmony_ci int err; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci err = devl_param_driverinit_value_get(devlink, 54362306a36Sopenharmony_ci DEVLINK_PARAM_GENERIC_ID_MAX_MACS, 54462306a36Sopenharmony_ci &saved_value); 54562306a36Sopenharmony_ci if (!err) 54662306a36Sopenharmony_ci nsim_dev->max_macs = saved_value.vu32; 54762306a36Sopenharmony_ci err = devl_param_driverinit_value_get(devlink, 54862306a36Sopenharmony_ci NSIM_DEVLINK_PARAM_ID_TEST1, 54962306a36Sopenharmony_ci &saved_value); 55062306a36Sopenharmony_ci if (!err) 55162306a36Sopenharmony_ci nsim_dev->test1 = saved_value.vbool; 55262306a36Sopenharmony_ci} 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci#define NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX 16 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_cistatic const struct devlink_region_ops dummy_region_ops = { 55762306a36Sopenharmony_ci .name = "dummy", 55862306a36Sopenharmony_ci .destructor = &kfree, 55962306a36Sopenharmony_ci .snapshot = nsim_dev_take_snapshot, 56062306a36Sopenharmony_ci}; 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_cistatic int nsim_dev_dummy_region_init(struct nsim_dev *nsim_dev, 56362306a36Sopenharmony_ci struct devlink *devlink) 56462306a36Sopenharmony_ci{ 56562306a36Sopenharmony_ci nsim_dev->dummy_region = 56662306a36Sopenharmony_ci devl_region_create(devlink, &dummy_region_ops, 56762306a36Sopenharmony_ci NSIM_DEV_DUMMY_REGION_SNAPSHOT_MAX, 56862306a36Sopenharmony_ci NSIM_DEV_DUMMY_REGION_SIZE); 56962306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(nsim_dev->dummy_region); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_cistatic void nsim_dev_dummy_region_exit(struct nsim_dev *nsim_dev) 57362306a36Sopenharmony_ci{ 57462306a36Sopenharmony_ci devl_region_destroy(nsim_dev->dummy_region); 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic int 57862306a36Sopenharmony_ci__nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_type type, 57962306a36Sopenharmony_ci unsigned int port_index); 58062306a36Sopenharmony_cistatic void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port); 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic int nsim_esw_legacy_enable(struct nsim_dev *nsim_dev, 58362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 58462306a36Sopenharmony_ci{ 58562306a36Sopenharmony_ci struct devlink *devlink = priv_to_devlink(nsim_dev); 58662306a36Sopenharmony_ci struct nsim_dev_port *nsim_dev_port, *tmp; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci devl_rate_nodes_destroy(devlink); 58962306a36Sopenharmony_ci list_for_each_entry_safe(nsim_dev_port, tmp, &nsim_dev->port_list, list) 59062306a36Sopenharmony_ci if (nsim_dev_port_is_vf(nsim_dev_port)) 59162306a36Sopenharmony_ci __nsim_dev_port_del(nsim_dev_port); 59262306a36Sopenharmony_ci nsim_dev->esw_mode = DEVLINK_ESWITCH_MODE_LEGACY; 59362306a36Sopenharmony_ci return 0; 59462306a36Sopenharmony_ci} 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_cistatic int nsim_esw_switchdev_enable(struct nsim_dev *nsim_dev, 59762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci struct nsim_dev_port *nsim_dev_port, *tmp; 60062306a36Sopenharmony_ci int i, err; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci for (i = 0; i < nsim_dev_get_vfs(nsim_dev); i++) { 60362306a36Sopenharmony_ci err = __nsim_dev_port_add(nsim_dev, NSIM_DEV_PORT_TYPE_VF, i); 60462306a36Sopenharmony_ci if (err) { 60562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Failed to initialize VFs' netdevsim ports"); 60662306a36Sopenharmony_ci pr_err("Failed to initialize VF id=%d. %d.\n", i, err); 60762306a36Sopenharmony_ci goto err_port_add_vfs; 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci } 61062306a36Sopenharmony_ci nsim_dev->esw_mode = DEVLINK_ESWITCH_MODE_SWITCHDEV; 61162306a36Sopenharmony_ci return 0; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cierr_port_add_vfs: 61462306a36Sopenharmony_ci list_for_each_entry_safe(nsim_dev_port, tmp, &nsim_dev->port_list, list) 61562306a36Sopenharmony_ci if (nsim_dev_port_is_vf(nsim_dev_port)) 61662306a36Sopenharmony_ci __nsim_dev_port_del(nsim_dev_port); 61762306a36Sopenharmony_ci return err; 61862306a36Sopenharmony_ci} 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic int nsim_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode, 62162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 62262306a36Sopenharmony_ci{ 62362306a36Sopenharmony_ci struct nsim_dev *nsim_dev = devlink_priv(devlink); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci if (mode == nsim_dev->esw_mode) 62662306a36Sopenharmony_ci return 0; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (mode == DEVLINK_ESWITCH_MODE_LEGACY) 62962306a36Sopenharmony_ci return nsim_esw_legacy_enable(nsim_dev, extack); 63062306a36Sopenharmony_ci if (mode == DEVLINK_ESWITCH_MODE_SWITCHDEV) 63162306a36Sopenharmony_ci return nsim_esw_switchdev_enable(nsim_dev, extack); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci return -EINVAL; 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic int nsim_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci struct nsim_dev *nsim_dev = devlink_priv(devlink); 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci *mode = nsim_dev->esw_mode; 64162306a36Sopenharmony_ci return 0; 64262306a36Sopenharmony_ci} 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_cistruct nsim_trap_item { 64562306a36Sopenharmony_ci void *trap_ctx; 64662306a36Sopenharmony_ci enum devlink_trap_action action; 64762306a36Sopenharmony_ci}; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_cistruct nsim_trap_data { 65062306a36Sopenharmony_ci struct delayed_work trap_report_dw; 65162306a36Sopenharmony_ci struct nsim_trap_item *trap_items_arr; 65262306a36Sopenharmony_ci u64 *trap_policers_cnt_arr; 65362306a36Sopenharmony_ci u64 trap_pkt_cnt; 65462306a36Sopenharmony_ci struct nsim_dev *nsim_dev; 65562306a36Sopenharmony_ci spinlock_t trap_lock; /* Protects trap_items_arr */ 65662306a36Sopenharmony_ci}; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci/* All driver-specific traps must be documented in 65962306a36Sopenharmony_ci * Documentation/networking/devlink/netdevsim.rst 66062306a36Sopenharmony_ci */ 66162306a36Sopenharmony_cienum { 66262306a36Sopenharmony_ci NSIM_TRAP_ID_BASE = DEVLINK_TRAP_GENERIC_ID_MAX, 66362306a36Sopenharmony_ci NSIM_TRAP_ID_FID_MISS, 66462306a36Sopenharmony_ci}; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci#define NSIM_TRAP_NAME_FID_MISS "fid_miss" 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci#define NSIM_TRAP_METADATA DEVLINK_TRAP_METADATA_TYPE_F_IN_PORT 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ci#define NSIM_TRAP_DROP(_id, _group_id) \ 67162306a36Sopenharmony_ci DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \ 67262306a36Sopenharmony_ci DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 67362306a36Sopenharmony_ci NSIM_TRAP_METADATA) 67462306a36Sopenharmony_ci#define NSIM_TRAP_DROP_EXT(_id, _group_id, _metadata) \ 67562306a36Sopenharmony_ci DEVLINK_TRAP_GENERIC(DROP, DROP, _id, \ 67662306a36Sopenharmony_ci DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 67762306a36Sopenharmony_ci NSIM_TRAP_METADATA | (_metadata)) 67862306a36Sopenharmony_ci#define NSIM_TRAP_EXCEPTION(_id, _group_id) \ 67962306a36Sopenharmony_ci DEVLINK_TRAP_GENERIC(EXCEPTION, TRAP, _id, \ 68062306a36Sopenharmony_ci DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 68162306a36Sopenharmony_ci NSIM_TRAP_METADATA) 68262306a36Sopenharmony_ci#define NSIM_TRAP_CONTROL(_id, _group_id, _action) \ 68362306a36Sopenharmony_ci DEVLINK_TRAP_GENERIC(CONTROL, _action, _id, \ 68462306a36Sopenharmony_ci DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 68562306a36Sopenharmony_ci NSIM_TRAP_METADATA) 68662306a36Sopenharmony_ci#define NSIM_TRAP_DRIVER_EXCEPTION(_id, _group_id) \ 68762306a36Sopenharmony_ci DEVLINK_TRAP_DRIVER(EXCEPTION, TRAP, NSIM_TRAP_ID_##_id, \ 68862306a36Sopenharmony_ci NSIM_TRAP_NAME_##_id, \ 68962306a36Sopenharmony_ci DEVLINK_TRAP_GROUP_GENERIC_ID_##_group_id, \ 69062306a36Sopenharmony_ci NSIM_TRAP_METADATA) 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci#define NSIM_DEV_TRAP_POLICER_MIN_RATE 1 69362306a36Sopenharmony_ci#define NSIM_DEV_TRAP_POLICER_MAX_RATE 8000 69462306a36Sopenharmony_ci#define NSIM_DEV_TRAP_POLICER_MIN_BURST 8 69562306a36Sopenharmony_ci#define NSIM_DEV_TRAP_POLICER_MAX_BURST 65536 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci#define NSIM_TRAP_POLICER(_id, _rate, _burst) \ 69862306a36Sopenharmony_ci DEVLINK_TRAP_POLICER(_id, _rate, _burst, \ 69962306a36Sopenharmony_ci NSIM_DEV_TRAP_POLICER_MAX_RATE, \ 70062306a36Sopenharmony_ci NSIM_DEV_TRAP_POLICER_MIN_RATE, \ 70162306a36Sopenharmony_ci NSIM_DEV_TRAP_POLICER_MAX_BURST, \ 70262306a36Sopenharmony_ci NSIM_DEV_TRAP_POLICER_MIN_BURST) 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic const struct devlink_trap_policer nsim_trap_policers_arr[] = { 70562306a36Sopenharmony_ci NSIM_TRAP_POLICER(1, 1000, 128), 70662306a36Sopenharmony_ci NSIM_TRAP_POLICER(2, 2000, 256), 70762306a36Sopenharmony_ci NSIM_TRAP_POLICER(3, 3000, 512), 70862306a36Sopenharmony_ci}; 70962306a36Sopenharmony_ci 71062306a36Sopenharmony_cistatic const struct devlink_trap_group nsim_trap_groups_arr[] = { 71162306a36Sopenharmony_ci DEVLINK_TRAP_GROUP_GENERIC(L2_DROPS, 0), 71262306a36Sopenharmony_ci DEVLINK_TRAP_GROUP_GENERIC(L3_DROPS, 1), 71362306a36Sopenharmony_ci DEVLINK_TRAP_GROUP_GENERIC(L3_EXCEPTIONS, 1), 71462306a36Sopenharmony_ci DEVLINK_TRAP_GROUP_GENERIC(BUFFER_DROPS, 2), 71562306a36Sopenharmony_ci DEVLINK_TRAP_GROUP_GENERIC(ACL_DROPS, 3), 71662306a36Sopenharmony_ci DEVLINK_TRAP_GROUP_GENERIC(MC_SNOOPING, 3), 71762306a36Sopenharmony_ci}; 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_cistatic const struct devlink_trap nsim_traps_arr[] = { 72062306a36Sopenharmony_ci NSIM_TRAP_DROP(SMAC_MC, L2_DROPS), 72162306a36Sopenharmony_ci NSIM_TRAP_DROP(VLAN_TAG_MISMATCH, L2_DROPS), 72262306a36Sopenharmony_ci NSIM_TRAP_DROP(INGRESS_VLAN_FILTER, L2_DROPS), 72362306a36Sopenharmony_ci NSIM_TRAP_DROP(INGRESS_STP_FILTER, L2_DROPS), 72462306a36Sopenharmony_ci NSIM_TRAP_DROP(EMPTY_TX_LIST, L2_DROPS), 72562306a36Sopenharmony_ci NSIM_TRAP_DROP(PORT_LOOPBACK_FILTER, L2_DROPS), 72662306a36Sopenharmony_ci NSIM_TRAP_DRIVER_EXCEPTION(FID_MISS, L2_DROPS), 72762306a36Sopenharmony_ci NSIM_TRAP_DROP(BLACKHOLE_ROUTE, L3_DROPS), 72862306a36Sopenharmony_ci NSIM_TRAP_EXCEPTION(TTL_ERROR, L3_EXCEPTIONS), 72962306a36Sopenharmony_ci NSIM_TRAP_DROP(TAIL_DROP, BUFFER_DROPS), 73062306a36Sopenharmony_ci NSIM_TRAP_DROP_EXT(INGRESS_FLOW_ACTION_DROP, ACL_DROPS, 73162306a36Sopenharmony_ci DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE), 73262306a36Sopenharmony_ci NSIM_TRAP_DROP_EXT(EGRESS_FLOW_ACTION_DROP, ACL_DROPS, 73362306a36Sopenharmony_ci DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE), 73462306a36Sopenharmony_ci NSIM_TRAP_CONTROL(IGMP_QUERY, MC_SNOOPING, MIRROR), 73562306a36Sopenharmony_ci NSIM_TRAP_CONTROL(IGMP_V1_REPORT, MC_SNOOPING, TRAP), 73662306a36Sopenharmony_ci}; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci#define NSIM_TRAP_L4_DATA_LEN 100 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_cistatic struct sk_buff *nsim_dev_trap_skb_build(void) 74162306a36Sopenharmony_ci{ 74262306a36Sopenharmony_ci int tot_len, data_len = NSIM_TRAP_L4_DATA_LEN; 74362306a36Sopenharmony_ci struct sk_buff *skb; 74462306a36Sopenharmony_ci struct udphdr *udph; 74562306a36Sopenharmony_ci struct ethhdr *eth; 74662306a36Sopenharmony_ci struct iphdr *iph; 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci skb = alloc_skb(NLMSG_GOODSIZE, GFP_ATOMIC); 74962306a36Sopenharmony_ci if (!skb) 75062306a36Sopenharmony_ci return NULL; 75162306a36Sopenharmony_ci tot_len = sizeof(struct iphdr) + sizeof(struct udphdr) + data_len; 75262306a36Sopenharmony_ci 75362306a36Sopenharmony_ci skb_reset_mac_header(skb); 75462306a36Sopenharmony_ci eth = skb_put(skb, sizeof(struct ethhdr)); 75562306a36Sopenharmony_ci eth_random_addr(eth->h_dest); 75662306a36Sopenharmony_ci eth_random_addr(eth->h_source); 75762306a36Sopenharmony_ci eth->h_proto = htons(ETH_P_IP); 75862306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IP); 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci skb_set_network_header(skb, skb->len); 76162306a36Sopenharmony_ci iph = skb_put(skb, sizeof(struct iphdr)); 76262306a36Sopenharmony_ci iph->protocol = IPPROTO_UDP; 76362306a36Sopenharmony_ci iph->saddr = in_aton("192.0.2.1"); 76462306a36Sopenharmony_ci iph->daddr = in_aton("198.51.100.1"); 76562306a36Sopenharmony_ci iph->version = 0x4; 76662306a36Sopenharmony_ci iph->frag_off = 0; 76762306a36Sopenharmony_ci iph->ihl = 0x5; 76862306a36Sopenharmony_ci iph->tot_len = htons(tot_len); 76962306a36Sopenharmony_ci iph->ttl = 100; 77062306a36Sopenharmony_ci iph->check = 0; 77162306a36Sopenharmony_ci iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci skb_set_transport_header(skb, skb->len); 77462306a36Sopenharmony_ci udph = skb_put_zero(skb, sizeof(struct udphdr) + data_len); 77562306a36Sopenharmony_ci get_random_bytes(&udph->source, sizeof(u16)); 77662306a36Sopenharmony_ci get_random_bytes(&udph->dest, sizeof(u16)); 77762306a36Sopenharmony_ci udph->len = htons(sizeof(struct udphdr) + data_len); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci return skb; 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_cistatic void nsim_dev_trap_report(struct nsim_dev_port *nsim_dev_port) 78362306a36Sopenharmony_ci{ 78462306a36Sopenharmony_ci struct nsim_dev *nsim_dev = nsim_dev_port->ns->nsim_dev; 78562306a36Sopenharmony_ci struct devlink *devlink = priv_to_devlink(nsim_dev); 78662306a36Sopenharmony_ci struct nsim_trap_data *nsim_trap_data; 78762306a36Sopenharmony_ci int i; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci nsim_trap_data = nsim_dev->trap_data; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci spin_lock(&nsim_trap_data->trap_lock); 79262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) { 79362306a36Sopenharmony_ci struct flow_action_cookie *fa_cookie = NULL; 79462306a36Sopenharmony_ci struct nsim_trap_item *nsim_trap_item; 79562306a36Sopenharmony_ci struct sk_buff *skb; 79662306a36Sopenharmony_ci bool has_fa_cookie; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci has_fa_cookie = nsim_traps_arr[i].metadata_cap & 79962306a36Sopenharmony_ci DEVLINK_TRAP_METADATA_TYPE_F_FA_COOKIE; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci nsim_trap_item = &nsim_trap_data->trap_items_arr[i]; 80262306a36Sopenharmony_ci if (nsim_trap_item->action == DEVLINK_TRAP_ACTION_DROP) 80362306a36Sopenharmony_ci continue; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci skb = nsim_dev_trap_skb_build(); 80662306a36Sopenharmony_ci if (!skb) 80762306a36Sopenharmony_ci continue; 80862306a36Sopenharmony_ci skb->dev = nsim_dev_port->ns->netdev; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci /* Trapped packets are usually passed to devlink in softIRQ, 81162306a36Sopenharmony_ci * but in this case they are generated in a workqueue. Disable 81262306a36Sopenharmony_ci * softIRQs to prevent lockdep from complaining about 81362306a36Sopenharmony_ci * "incosistent lock state". 81462306a36Sopenharmony_ci */ 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci spin_lock_bh(&nsim_dev->fa_cookie_lock); 81762306a36Sopenharmony_ci fa_cookie = has_fa_cookie ? nsim_dev->fa_cookie : NULL; 81862306a36Sopenharmony_ci devlink_trap_report(devlink, skb, nsim_trap_item->trap_ctx, 81962306a36Sopenharmony_ci &nsim_dev_port->devlink_port, fa_cookie); 82062306a36Sopenharmony_ci spin_unlock_bh(&nsim_dev->fa_cookie_lock); 82162306a36Sopenharmony_ci consume_skb(skb); 82262306a36Sopenharmony_ci } 82362306a36Sopenharmony_ci spin_unlock(&nsim_trap_data->trap_lock); 82462306a36Sopenharmony_ci} 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci#define NSIM_TRAP_REPORT_INTERVAL_MS 100 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_cistatic void nsim_dev_trap_report_work(struct work_struct *work) 82962306a36Sopenharmony_ci{ 83062306a36Sopenharmony_ci struct nsim_trap_data *nsim_trap_data; 83162306a36Sopenharmony_ci struct nsim_dev_port *nsim_dev_port; 83262306a36Sopenharmony_ci struct nsim_dev *nsim_dev; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci nsim_trap_data = container_of(work, struct nsim_trap_data, 83562306a36Sopenharmony_ci trap_report_dw.work); 83662306a36Sopenharmony_ci nsim_dev = nsim_trap_data->nsim_dev; 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_ci if (!devl_trylock(priv_to_devlink(nsim_dev))) { 83962306a36Sopenharmony_ci schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, 1); 84062306a36Sopenharmony_ci return; 84162306a36Sopenharmony_ci } 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci /* For each running port and enabled packet trap, generate a UDP 84462306a36Sopenharmony_ci * packet with a random 5-tuple and report it. 84562306a36Sopenharmony_ci */ 84662306a36Sopenharmony_ci list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) { 84762306a36Sopenharmony_ci if (!netif_running(nsim_dev_port->ns->netdev)) 84862306a36Sopenharmony_ci continue; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci nsim_dev_trap_report(nsim_dev_port); 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci devl_unlock(priv_to_devlink(nsim_dev)); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, 85562306a36Sopenharmony_ci msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS)); 85662306a36Sopenharmony_ci} 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_cistatic int nsim_dev_traps_init(struct devlink *devlink) 85962306a36Sopenharmony_ci{ 86062306a36Sopenharmony_ci size_t policers_count = ARRAY_SIZE(nsim_trap_policers_arr); 86162306a36Sopenharmony_ci struct nsim_dev *nsim_dev = devlink_priv(devlink); 86262306a36Sopenharmony_ci struct nsim_trap_data *nsim_trap_data; 86362306a36Sopenharmony_ci int err; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci nsim_trap_data = kzalloc(sizeof(*nsim_trap_data), GFP_KERNEL); 86662306a36Sopenharmony_ci if (!nsim_trap_data) 86762306a36Sopenharmony_ci return -ENOMEM; 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci nsim_trap_data->trap_items_arr = kcalloc(ARRAY_SIZE(nsim_traps_arr), 87062306a36Sopenharmony_ci sizeof(struct nsim_trap_item), 87162306a36Sopenharmony_ci GFP_KERNEL); 87262306a36Sopenharmony_ci if (!nsim_trap_data->trap_items_arr) { 87362306a36Sopenharmony_ci err = -ENOMEM; 87462306a36Sopenharmony_ci goto err_trap_data_free; 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci nsim_trap_data->trap_policers_cnt_arr = kcalloc(policers_count, 87862306a36Sopenharmony_ci sizeof(u64), 87962306a36Sopenharmony_ci GFP_KERNEL); 88062306a36Sopenharmony_ci if (!nsim_trap_data->trap_policers_cnt_arr) { 88162306a36Sopenharmony_ci err = -ENOMEM; 88262306a36Sopenharmony_ci goto err_trap_items_free; 88362306a36Sopenharmony_ci } 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci /* The lock is used to protect the action state of the registered 88662306a36Sopenharmony_ci * traps. The value is written by user and read in delayed work when 88762306a36Sopenharmony_ci * iterating over all the traps. 88862306a36Sopenharmony_ci */ 88962306a36Sopenharmony_ci spin_lock_init(&nsim_trap_data->trap_lock); 89062306a36Sopenharmony_ci nsim_trap_data->nsim_dev = nsim_dev; 89162306a36Sopenharmony_ci nsim_dev->trap_data = nsim_trap_data; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci err = devl_trap_policers_register(devlink, nsim_trap_policers_arr, 89462306a36Sopenharmony_ci policers_count); 89562306a36Sopenharmony_ci if (err) 89662306a36Sopenharmony_ci goto err_trap_policers_cnt_free; 89762306a36Sopenharmony_ci 89862306a36Sopenharmony_ci err = devl_trap_groups_register(devlink, nsim_trap_groups_arr, 89962306a36Sopenharmony_ci ARRAY_SIZE(nsim_trap_groups_arr)); 90062306a36Sopenharmony_ci if (err) 90162306a36Sopenharmony_ci goto err_trap_policers_unregister; 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci err = devl_traps_register(devlink, nsim_traps_arr, 90462306a36Sopenharmony_ci ARRAY_SIZE(nsim_traps_arr), NULL); 90562306a36Sopenharmony_ci if (err) 90662306a36Sopenharmony_ci goto err_trap_groups_unregister; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci INIT_DELAYED_WORK(&nsim_dev->trap_data->trap_report_dw, 90962306a36Sopenharmony_ci nsim_dev_trap_report_work); 91062306a36Sopenharmony_ci schedule_delayed_work(&nsim_dev->trap_data->trap_report_dw, 91162306a36Sopenharmony_ci msecs_to_jiffies(NSIM_TRAP_REPORT_INTERVAL_MS)); 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci return 0; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_cierr_trap_groups_unregister: 91662306a36Sopenharmony_ci devl_trap_groups_unregister(devlink, nsim_trap_groups_arr, 91762306a36Sopenharmony_ci ARRAY_SIZE(nsim_trap_groups_arr)); 91862306a36Sopenharmony_cierr_trap_policers_unregister: 91962306a36Sopenharmony_ci devl_trap_policers_unregister(devlink, nsim_trap_policers_arr, 92062306a36Sopenharmony_ci ARRAY_SIZE(nsim_trap_policers_arr)); 92162306a36Sopenharmony_cierr_trap_policers_cnt_free: 92262306a36Sopenharmony_ci kfree(nsim_trap_data->trap_policers_cnt_arr); 92362306a36Sopenharmony_cierr_trap_items_free: 92462306a36Sopenharmony_ci kfree(nsim_trap_data->trap_items_arr); 92562306a36Sopenharmony_cierr_trap_data_free: 92662306a36Sopenharmony_ci kfree(nsim_trap_data); 92762306a36Sopenharmony_ci return err; 92862306a36Sopenharmony_ci} 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_cistatic void nsim_dev_traps_exit(struct devlink *devlink) 93162306a36Sopenharmony_ci{ 93262306a36Sopenharmony_ci struct nsim_dev *nsim_dev = devlink_priv(devlink); 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci /* caution, trap work takes devlink lock */ 93562306a36Sopenharmony_ci cancel_delayed_work_sync(&nsim_dev->trap_data->trap_report_dw); 93662306a36Sopenharmony_ci devl_traps_unregister(devlink, nsim_traps_arr, 93762306a36Sopenharmony_ci ARRAY_SIZE(nsim_traps_arr)); 93862306a36Sopenharmony_ci devl_trap_groups_unregister(devlink, nsim_trap_groups_arr, 93962306a36Sopenharmony_ci ARRAY_SIZE(nsim_trap_groups_arr)); 94062306a36Sopenharmony_ci devl_trap_policers_unregister(devlink, nsim_trap_policers_arr, 94162306a36Sopenharmony_ci ARRAY_SIZE(nsim_trap_policers_arr)); 94262306a36Sopenharmony_ci kfree(nsim_dev->trap_data->trap_policers_cnt_arr); 94362306a36Sopenharmony_ci kfree(nsim_dev->trap_data->trap_items_arr); 94462306a36Sopenharmony_ci kfree(nsim_dev->trap_data); 94562306a36Sopenharmony_ci} 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_cistatic int nsim_dev_reload_create(struct nsim_dev *nsim_dev, 94862306a36Sopenharmony_ci struct netlink_ext_ack *extack); 94962306a36Sopenharmony_cistatic void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic int nsim_dev_reload_down(struct devlink *devlink, bool netns_change, 95262306a36Sopenharmony_ci enum devlink_reload_action action, enum devlink_reload_limit limit, 95362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 95462306a36Sopenharmony_ci{ 95562306a36Sopenharmony_ci struct nsim_dev *nsim_dev = devlink_priv(devlink); 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci if (nsim_dev->dont_allow_reload) { 95862306a36Sopenharmony_ci /* For testing purposes, user set debugfs dont_allow_reload 95962306a36Sopenharmony_ci * value to true. So forbid it. 96062306a36Sopenharmony_ci */ 96162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "User forbid the reload for testing purposes"); 96262306a36Sopenharmony_ci return -EOPNOTSUPP; 96362306a36Sopenharmony_ci } 96462306a36Sopenharmony_ci 96562306a36Sopenharmony_ci nsim_dev_reload_destroy(nsim_dev); 96662306a36Sopenharmony_ci return 0; 96762306a36Sopenharmony_ci} 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_cistatic int nsim_dev_reload_up(struct devlink *devlink, enum devlink_reload_action action, 97062306a36Sopenharmony_ci enum devlink_reload_limit limit, u32 *actions_performed, 97162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 97262306a36Sopenharmony_ci{ 97362306a36Sopenharmony_ci struct nsim_dev *nsim_dev = devlink_priv(devlink); 97462306a36Sopenharmony_ci 97562306a36Sopenharmony_ci if (nsim_dev->fail_reload) { 97662306a36Sopenharmony_ci /* For testing purposes, user set debugfs fail_reload 97762306a36Sopenharmony_ci * value to true. Fail right away. 97862306a36Sopenharmony_ci */ 97962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "User setup the reload to fail for testing purposes"); 98062306a36Sopenharmony_ci return -EINVAL; 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci *actions_performed = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci return nsim_dev_reload_create(nsim_dev, extack); 98662306a36Sopenharmony_ci} 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_cistatic int nsim_dev_info_get(struct devlink *devlink, 98962306a36Sopenharmony_ci struct devlink_info_req *req, 99062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 99162306a36Sopenharmony_ci{ 99262306a36Sopenharmony_ci int err; 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_ci err = devlink_info_version_stored_put_ext(req, "fw.mgmt", "10.20.30", 99562306a36Sopenharmony_ci DEVLINK_INFO_VERSION_TYPE_COMPONENT); 99662306a36Sopenharmony_ci if (err) 99762306a36Sopenharmony_ci return err; 99862306a36Sopenharmony_ci return devlink_info_version_running_put_ext(req, "fw.mgmt", "10.20.30", 99962306a36Sopenharmony_ci DEVLINK_INFO_VERSION_TYPE_COMPONENT); 100062306a36Sopenharmony_ci} 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_ci#define NSIM_DEV_FLASH_SIZE 500000 100362306a36Sopenharmony_ci#define NSIM_DEV_FLASH_CHUNK_SIZE 1000 100462306a36Sopenharmony_ci#define NSIM_DEV_FLASH_CHUNK_TIME_MS 10 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_cistatic int nsim_dev_flash_update(struct devlink *devlink, 100762306a36Sopenharmony_ci struct devlink_flash_update_params *params, 100862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci struct nsim_dev *nsim_dev = devlink_priv(devlink); 101162306a36Sopenharmony_ci int i; 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci if ((params->overwrite_mask & ~nsim_dev->fw_update_overwrite_mask) != 0) 101462306a36Sopenharmony_ci return -EOPNOTSUPP; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci if (nsim_dev->fw_update_status) { 101762306a36Sopenharmony_ci devlink_flash_update_status_notify(devlink, 101862306a36Sopenharmony_ci "Preparing to flash", 101962306a36Sopenharmony_ci params->component, 0, 0); 102062306a36Sopenharmony_ci } 102162306a36Sopenharmony_ci 102262306a36Sopenharmony_ci for (i = 0; i < NSIM_DEV_FLASH_SIZE / NSIM_DEV_FLASH_CHUNK_SIZE; i++) { 102362306a36Sopenharmony_ci if (nsim_dev->fw_update_status) 102462306a36Sopenharmony_ci devlink_flash_update_status_notify(devlink, "Flashing", 102562306a36Sopenharmony_ci params->component, 102662306a36Sopenharmony_ci i * NSIM_DEV_FLASH_CHUNK_SIZE, 102762306a36Sopenharmony_ci NSIM_DEV_FLASH_SIZE); 102862306a36Sopenharmony_ci msleep(NSIM_DEV_FLASH_CHUNK_TIME_MS); 102962306a36Sopenharmony_ci } 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (nsim_dev->fw_update_status) { 103262306a36Sopenharmony_ci devlink_flash_update_status_notify(devlink, "Flashing", 103362306a36Sopenharmony_ci params->component, 103462306a36Sopenharmony_ci NSIM_DEV_FLASH_SIZE, 103562306a36Sopenharmony_ci NSIM_DEV_FLASH_SIZE); 103662306a36Sopenharmony_ci devlink_flash_update_timeout_notify(devlink, "Flash select", 103762306a36Sopenharmony_ci params->component, 81); 103862306a36Sopenharmony_ci devlink_flash_update_status_notify(devlink, "Flashing done", 103962306a36Sopenharmony_ci params->component, 0, 0); 104062306a36Sopenharmony_ci } 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci return 0; 104362306a36Sopenharmony_ci} 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_cistatic struct nsim_trap_item * 104662306a36Sopenharmony_cinsim_dev_trap_item_lookup(struct nsim_dev *nsim_dev, u16 trap_id) 104762306a36Sopenharmony_ci{ 104862306a36Sopenharmony_ci struct nsim_trap_data *nsim_trap_data = nsim_dev->trap_data; 104962306a36Sopenharmony_ci int i; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(nsim_traps_arr); i++) { 105262306a36Sopenharmony_ci if (nsim_traps_arr[i].id == trap_id) 105362306a36Sopenharmony_ci return &nsim_trap_data->trap_items_arr[i]; 105462306a36Sopenharmony_ci } 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci return NULL; 105762306a36Sopenharmony_ci} 105862306a36Sopenharmony_ci 105962306a36Sopenharmony_cistatic int nsim_dev_devlink_trap_init(struct devlink *devlink, 106062306a36Sopenharmony_ci const struct devlink_trap *trap, 106162306a36Sopenharmony_ci void *trap_ctx) 106262306a36Sopenharmony_ci{ 106362306a36Sopenharmony_ci struct nsim_dev *nsim_dev = devlink_priv(devlink); 106462306a36Sopenharmony_ci struct nsim_trap_item *nsim_trap_item; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id); 106762306a36Sopenharmony_ci if (WARN_ON(!nsim_trap_item)) 106862306a36Sopenharmony_ci return -ENOENT; 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci nsim_trap_item->trap_ctx = trap_ctx; 107162306a36Sopenharmony_ci nsim_trap_item->action = trap->init_action; 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_ci return 0; 107462306a36Sopenharmony_ci} 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_cistatic int 107762306a36Sopenharmony_cinsim_dev_devlink_trap_action_set(struct devlink *devlink, 107862306a36Sopenharmony_ci const struct devlink_trap *trap, 107962306a36Sopenharmony_ci enum devlink_trap_action action, 108062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci struct nsim_dev *nsim_dev = devlink_priv(devlink); 108362306a36Sopenharmony_ci struct nsim_trap_item *nsim_trap_item; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci nsim_trap_item = nsim_dev_trap_item_lookup(nsim_dev, trap->id); 108662306a36Sopenharmony_ci if (WARN_ON(!nsim_trap_item)) 108762306a36Sopenharmony_ci return -ENOENT; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci spin_lock(&nsim_dev->trap_data->trap_lock); 109062306a36Sopenharmony_ci nsim_trap_item->action = action; 109162306a36Sopenharmony_ci spin_unlock(&nsim_dev->trap_data->trap_lock); 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci return 0; 109462306a36Sopenharmony_ci} 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_cistatic int 109762306a36Sopenharmony_cinsim_dev_devlink_trap_group_set(struct devlink *devlink, 109862306a36Sopenharmony_ci const struct devlink_trap_group *group, 109962306a36Sopenharmony_ci const struct devlink_trap_policer *policer, 110062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 110162306a36Sopenharmony_ci{ 110262306a36Sopenharmony_ci struct nsim_dev *nsim_dev = devlink_priv(devlink); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci if (nsim_dev->fail_trap_group_set) 110562306a36Sopenharmony_ci return -EINVAL; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci return 0; 110862306a36Sopenharmony_ci} 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_cistatic int 111162306a36Sopenharmony_cinsim_dev_devlink_trap_policer_set(struct devlink *devlink, 111262306a36Sopenharmony_ci const struct devlink_trap_policer *policer, 111362306a36Sopenharmony_ci u64 rate, u64 burst, 111462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 111562306a36Sopenharmony_ci{ 111662306a36Sopenharmony_ci struct nsim_dev *nsim_dev = devlink_priv(devlink); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci if (nsim_dev->fail_trap_policer_set) { 111962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "User setup the operation to fail for testing purposes"); 112062306a36Sopenharmony_ci return -EINVAL; 112162306a36Sopenharmony_ci } 112262306a36Sopenharmony_ci 112362306a36Sopenharmony_ci return 0; 112462306a36Sopenharmony_ci} 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_cistatic int 112762306a36Sopenharmony_cinsim_dev_devlink_trap_policer_counter_get(struct devlink *devlink, 112862306a36Sopenharmony_ci const struct devlink_trap_policer *policer, 112962306a36Sopenharmony_ci u64 *p_drops) 113062306a36Sopenharmony_ci{ 113162306a36Sopenharmony_ci struct nsim_dev *nsim_dev = devlink_priv(devlink); 113262306a36Sopenharmony_ci u64 *cnt; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci if (nsim_dev->fail_trap_policer_counter_get) 113562306a36Sopenharmony_ci return -EINVAL; 113662306a36Sopenharmony_ci 113762306a36Sopenharmony_ci cnt = &nsim_dev->trap_data->trap_policers_cnt_arr[policer->id - 1]; 113862306a36Sopenharmony_ci *p_drops = (*cnt)++; 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci return 0; 114162306a36Sopenharmony_ci} 114262306a36Sopenharmony_ci 114362306a36Sopenharmony_ci#define NSIM_LINK_SPEED_MAX 5000 /* Mbps */ 114462306a36Sopenharmony_ci#define NSIM_LINK_SPEED_UNIT 125000 /* 1 Mbps given in bytes/sec to avoid 114562306a36Sopenharmony_ci * u64 overflow during conversion from 114662306a36Sopenharmony_ci * bytes to bits. 114762306a36Sopenharmony_ci */ 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_cistatic int nsim_rate_bytes_to_units(char *name, u64 *rate, struct netlink_ext_ack *extack) 115062306a36Sopenharmony_ci{ 115162306a36Sopenharmony_ci u64 val; 115262306a36Sopenharmony_ci u32 rem; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci val = div_u64_rem(*rate, NSIM_LINK_SPEED_UNIT, &rem); 115562306a36Sopenharmony_ci if (rem) { 115662306a36Sopenharmony_ci pr_err("%s rate value %lluBps not in link speed units of 1Mbps.\n", 115762306a36Sopenharmony_ci name, *rate); 115862306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "TX rate value not in link speed units of 1Mbps."); 115962306a36Sopenharmony_ci return -EINVAL; 116062306a36Sopenharmony_ci } 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci if (val > NSIM_LINK_SPEED_MAX) { 116362306a36Sopenharmony_ci pr_err("%s rate value %lluMbps exceed link maximum speed 5000Mbps.\n", 116462306a36Sopenharmony_ci name, val); 116562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "TX rate value exceed link maximum speed 5000Mbps."); 116662306a36Sopenharmony_ci return -EINVAL; 116762306a36Sopenharmony_ci } 116862306a36Sopenharmony_ci *rate = val; 116962306a36Sopenharmony_ci return 0; 117062306a36Sopenharmony_ci} 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_cistatic int nsim_leaf_tx_share_set(struct devlink_rate *devlink_rate, void *priv, 117362306a36Sopenharmony_ci u64 tx_share, struct netlink_ext_ack *extack) 117462306a36Sopenharmony_ci{ 117562306a36Sopenharmony_ci struct nsim_dev_port *nsim_dev_port = priv; 117662306a36Sopenharmony_ci struct nsim_dev *nsim_dev = nsim_dev_port->ns->nsim_dev; 117762306a36Sopenharmony_ci int vf_id = nsim_dev_port_index_to_vf_index(nsim_dev_port->port_index); 117862306a36Sopenharmony_ci int err; 117962306a36Sopenharmony_ci 118062306a36Sopenharmony_ci err = nsim_rate_bytes_to_units("tx_share", &tx_share, extack); 118162306a36Sopenharmony_ci if (err) 118262306a36Sopenharmony_ci return err; 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci nsim_dev->vfconfigs[vf_id].min_tx_rate = tx_share; 118562306a36Sopenharmony_ci return 0; 118662306a36Sopenharmony_ci} 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_cistatic int nsim_leaf_tx_max_set(struct devlink_rate *devlink_rate, void *priv, 118962306a36Sopenharmony_ci u64 tx_max, struct netlink_ext_ack *extack) 119062306a36Sopenharmony_ci{ 119162306a36Sopenharmony_ci struct nsim_dev_port *nsim_dev_port = priv; 119262306a36Sopenharmony_ci struct nsim_dev *nsim_dev = nsim_dev_port->ns->nsim_dev; 119362306a36Sopenharmony_ci int vf_id = nsim_dev_port_index_to_vf_index(nsim_dev_port->port_index); 119462306a36Sopenharmony_ci int err; 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci err = nsim_rate_bytes_to_units("tx_max", &tx_max, extack); 119762306a36Sopenharmony_ci if (err) 119862306a36Sopenharmony_ci return err; 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci nsim_dev->vfconfigs[vf_id].max_tx_rate = tx_max; 120162306a36Sopenharmony_ci return 0; 120262306a36Sopenharmony_ci} 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_cistruct nsim_rate_node { 120562306a36Sopenharmony_ci struct dentry *ddir; 120662306a36Sopenharmony_ci struct dentry *rate_parent; 120762306a36Sopenharmony_ci char *parent_name; 120862306a36Sopenharmony_ci u16 tx_share; 120962306a36Sopenharmony_ci u16 tx_max; 121062306a36Sopenharmony_ci}; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_cistatic int nsim_node_tx_share_set(struct devlink_rate *devlink_rate, void *priv, 121362306a36Sopenharmony_ci u64 tx_share, struct netlink_ext_ack *extack) 121462306a36Sopenharmony_ci{ 121562306a36Sopenharmony_ci struct nsim_rate_node *nsim_node = priv; 121662306a36Sopenharmony_ci int err; 121762306a36Sopenharmony_ci 121862306a36Sopenharmony_ci err = nsim_rate_bytes_to_units("tx_share", &tx_share, extack); 121962306a36Sopenharmony_ci if (err) 122062306a36Sopenharmony_ci return err; 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_ci nsim_node->tx_share = tx_share; 122362306a36Sopenharmony_ci return 0; 122462306a36Sopenharmony_ci} 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_cistatic int nsim_node_tx_max_set(struct devlink_rate *devlink_rate, void *priv, 122762306a36Sopenharmony_ci u64 tx_max, struct netlink_ext_ack *extack) 122862306a36Sopenharmony_ci{ 122962306a36Sopenharmony_ci struct nsim_rate_node *nsim_node = priv; 123062306a36Sopenharmony_ci int err; 123162306a36Sopenharmony_ci 123262306a36Sopenharmony_ci err = nsim_rate_bytes_to_units("tx_max", &tx_max, extack); 123362306a36Sopenharmony_ci if (err) 123462306a36Sopenharmony_ci return err; 123562306a36Sopenharmony_ci 123662306a36Sopenharmony_ci nsim_node->tx_max = tx_max; 123762306a36Sopenharmony_ci return 0; 123862306a36Sopenharmony_ci} 123962306a36Sopenharmony_ci 124062306a36Sopenharmony_cistatic int nsim_rate_node_new(struct devlink_rate *node, void **priv, 124162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 124262306a36Sopenharmony_ci{ 124362306a36Sopenharmony_ci struct nsim_dev *nsim_dev = devlink_priv(node->devlink); 124462306a36Sopenharmony_ci struct nsim_rate_node *nsim_node; 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci if (!nsim_esw_mode_is_switchdev(nsim_dev)) { 124762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Node creation allowed only in switchdev mode."); 124862306a36Sopenharmony_ci return -EOPNOTSUPP; 124962306a36Sopenharmony_ci } 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci nsim_node = kzalloc(sizeof(*nsim_node), GFP_KERNEL); 125262306a36Sopenharmony_ci if (!nsim_node) 125362306a36Sopenharmony_ci return -ENOMEM; 125462306a36Sopenharmony_ci 125562306a36Sopenharmony_ci nsim_node->ddir = debugfs_create_dir(node->name, nsim_dev->nodes_ddir); 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci debugfs_create_u16("tx_share", 0400, nsim_node->ddir, &nsim_node->tx_share); 125862306a36Sopenharmony_ci debugfs_create_u16("tx_max", 0400, nsim_node->ddir, &nsim_node->tx_max); 125962306a36Sopenharmony_ci nsim_node->rate_parent = debugfs_create_file("rate_parent", 0400, 126062306a36Sopenharmony_ci nsim_node->ddir, 126162306a36Sopenharmony_ci &nsim_node->parent_name, 126262306a36Sopenharmony_ci &nsim_dev_rate_parent_fops); 126362306a36Sopenharmony_ci 126462306a36Sopenharmony_ci *priv = nsim_node; 126562306a36Sopenharmony_ci return 0; 126662306a36Sopenharmony_ci} 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_cistatic int nsim_rate_node_del(struct devlink_rate *node, void *priv, 126962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 127062306a36Sopenharmony_ci{ 127162306a36Sopenharmony_ci struct nsim_rate_node *nsim_node = priv; 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci debugfs_remove(nsim_node->rate_parent); 127462306a36Sopenharmony_ci debugfs_remove_recursive(nsim_node->ddir); 127562306a36Sopenharmony_ci kfree(nsim_node); 127662306a36Sopenharmony_ci return 0; 127762306a36Sopenharmony_ci} 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_cistatic int nsim_rate_leaf_parent_set(struct devlink_rate *child, 128062306a36Sopenharmony_ci struct devlink_rate *parent, 128162306a36Sopenharmony_ci void *priv_child, void *priv_parent, 128262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 128362306a36Sopenharmony_ci{ 128462306a36Sopenharmony_ci struct nsim_dev_port *nsim_dev_port = priv_child; 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci if (parent) 128762306a36Sopenharmony_ci nsim_dev_port->parent_name = parent->name; 128862306a36Sopenharmony_ci else 128962306a36Sopenharmony_ci nsim_dev_port->parent_name = NULL; 129062306a36Sopenharmony_ci return 0; 129162306a36Sopenharmony_ci} 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_cistatic int nsim_rate_node_parent_set(struct devlink_rate *child, 129462306a36Sopenharmony_ci struct devlink_rate *parent, 129562306a36Sopenharmony_ci void *priv_child, void *priv_parent, 129662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 129762306a36Sopenharmony_ci{ 129862306a36Sopenharmony_ci struct nsim_rate_node *nsim_node = priv_child; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci if (parent) 130162306a36Sopenharmony_ci nsim_node->parent_name = parent->name; 130262306a36Sopenharmony_ci else 130362306a36Sopenharmony_ci nsim_node->parent_name = NULL; 130462306a36Sopenharmony_ci return 0; 130562306a36Sopenharmony_ci} 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_cistatic int 130862306a36Sopenharmony_cinsim_dev_devlink_trap_drop_counter_get(struct devlink *devlink, 130962306a36Sopenharmony_ci const struct devlink_trap *trap, 131062306a36Sopenharmony_ci u64 *p_drops) 131162306a36Sopenharmony_ci{ 131262306a36Sopenharmony_ci struct nsim_dev *nsim_dev = devlink_priv(devlink); 131362306a36Sopenharmony_ci u64 *cnt; 131462306a36Sopenharmony_ci 131562306a36Sopenharmony_ci if (nsim_dev->fail_trap_drop_counter_get) 131662306a36Sopenharmony_ci return -EINVAL; 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci cnt = &nsim_dev->trap_data->trap_pkt_cnt; 131962306a36Sopenharmony_ci *p_drops = (*cnt)++; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci return 0; 132262306a36Sopenharmony_ci} 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_cistatic const struct devlink_ops nsim_dev_devlink_ops = { 132562306a36Sopenharmony_ci .eswitch_mode_set = nsim_devlink_eswitch_mode_set, 132662306a36Sopenharmony_ci .eswitch_mode_get = nsim_devlink_eswitch_mode_get, 132762306a36Sopenharmony_ci .supported_flash_update_params = DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK, 132862306a36Sopenharmony_ci .reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT), 132962306a36Sopenharmony_ci .reload_down = nsim_dev_reload_down, 133062306a36Sopenharmony_ci .reload_up = nsim_dev_reload_up, 133162306a36Sopenharmony_ci .info_get = nsim_dev_info_get, 133262306a36Sopenharmony_ci .flash_update = nsim_dev_flash_update, 133362306a36Sopenharmony_ci .trap_init = nsim_dev_devlink_trap_init, 133462306a36Sopenharmony_ci .trap_action_set = nsim_dev_devlink_trap_action_set, 133562306a36Sopenharmony_ci .trap_group_set = nsim_dev_devlink_trap_group_set, 133662306a36Sopenharmony_ci .trap_policer_set = nsim_dev_devlink_trap_policer_set, 133762306a36Sopenharmony_ci .trap_policer_counter_get = nsim_dev_devlink_trap_policer_counter_get, 133862306a36Sopenharmony_ci .rate_leaf_tx_share_set = nsim_leaf_tx_share_set, 133962306a36Sopenharmony_ci .rate_leaf_tx_max_set = nsim_leaf_tx_max_set, 134062306a36Sopenharmony_ci .rate_node_tx_share_set = nsim_node_tx_share_set, 134162306a36Sopenharmony_ci .rate_node_tx_max_set = nsim_node_tx_max_set, 134262306a36Sopenharmony_ci .rate_node_new = nsim_rate_node_new, 134362306a36Sopenharmony_ci .rate_node_del = nsim_rate_node_del, 134462306a36Sopenharmony_ci .rate_leaf_parent_set = nsim_rate_leaf_parent_set, 134562306a36Sopenharmony_ci .rate_node_parent_set = nsim_rate_node_parent_set, 134662306a36Sopenharmony_ci .trap_drop_counter_get = nsim_dev_devlink_trap_drop_counter_get, 134762306a36Sopenharmony_ci}; 134862306a36Sopenharmony_ci 134962306a36Sopenharmony_ci#define NSIM_DEV_MAX_MACS_DEFAULT 32 135062306a36Sopenharmony_ci#define NSIM_DEV_TEST1_DEFAULT true 135162306a36Sopenharmony_ci 135262306a36Sopenharmony_cistatic int __nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_type type, 135362306a36Sopenharmony_ci unsigned int port_index) 135462306a36Sopenharmony_ci{ 135562306a36Sopenharmony_ci struct devlink_port_attrs attrs = {}; 135662306a36Sopenharmony_ci struct nsim_dev_port *nsim_dev_port; 135762306a36Sopenharmony_ci struct devlink_port *devlink_port; 135862306a36Sopenharmony_ci int err; 135962306a36Sopenharmony_ci 136062306a36Sopenharmony_ci if (type == NSIM_DEV_PORT_TYPE_VF && !nsim_dev_get_vfs(nsim_dev)) 136162306a36Sopenharmony_ci return -EINVAL; 136262306a36Sopenharmony_ci 136362306a36Sopenharmony_ci nsim_dev_port = kzalloc(sizeof(*nsim_dev_port), GFP_KERNEL); 136462306a36Sopenharmony_ci if (!nsim_dev_port) 136562306a36Sopenharmony_ci return -ENOMEM; 136662306a36Sopenharmony_ci nsim_dev_port->port_index = nsim_dev_port_index(type, port_index); 136762306a36Sopenharmony_ci nsim_dev_port->port_type = type; 136862306a36Sopenharmony_ci 136962306a36Sopenharmony_ci devlink_port = &nsim_dev_port->devlink_port; 137062306a36Sopenharmony_ci if (nsim_dev_port_is_pf(nsim_dev_port)) { 137162306a36Sopenharmony_ci attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; 137262306a36Sopenharmony_ci attrs.phys.port_number = port_index + 1; 137362306a36Sopenharmony_ci } else { 137462306a36Sopenharmony_ci attrs.flavour = DEVLINK_PORT_FLAVOUR_PCI_VF; 137562306a36Sopenharmony_ci attrs.pci_vf.pf = 0; 137662306a36Sopenharmony_ci attrs.pci_vf.vf = port_index; 137762306a36Sopenharmony_ci } 137862306a36Sopenharmony_ci memcpy(attrs.switch_id.id, nsim_dev->switch_id.id, nsim_dev->switch_id.id_len); 137962306a36Sopenharmony_ci attrs.switch_id.id_len = nsim_dev->switch_id.id_len; 138062306a36Sopenharmony_ci devlink_port_attrs_set(devlink_port, &attrs); 138162306a36Sopenharmony_ci err = devl_port_register(priv_to_devlink(nsim_dev), devlink_port, 138262306a36Sopenharmony_ci nsim_dev_port->port_index); 138362306a36Sopenharmony_ci if (err) 138462306a36Sopenharmony_ci goto err_port_free; 138562306a36Sopenharmony_ci 138662306a36Sopenharmony_ci err = nsim_dev_port_debugfs_init(nsim_dev, nsim_dev_port); 138762306a36Sopenharmony_ci if (err) 138862306a36Sopenharmony_ci goto err_dl_port_unregister; 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_ci nsim_dev_port->ns = nsim_create(nsim_dev, nsim_dev_port); 139162306a36Sopenharmony_ci if (IS_ERR(nsim_dev_port->ns)) { 139262306a36Sopenharmony_ci err = PTR_ERR(nsim_dev_port->ns); 139362306a36Sopenharmony_ci goto err_port_debugfs_exit; 139462306a36Sopenharmony_ci } 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci if (nsim_dev_port_is_vf(nsim_dev_port)) { 139762306a36Sopenharmony_ci err = devl_rate_leaf_create(&nsim_dev_port->devlink_port, 139862306a36Sopenharmony_ci nsim_dev_port, NULL); 139962306a36Sopenharmony_ci if (err) 140062306a36Sopenharmony_ci goto err_nsim_destroy; 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci list_add(&nsim_dev_port->list, &nsim_dev->port_list); 140462306a36Sopenharmony_ci 140562306a36Sopenharmony_ci return 0; 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_cierr_nsim_destroy: 140862306a36Sopenharmony_ci nsim_destroy(nsim_dev_port->ns); 140962306a36Sopenharmony_cierr_port_debugfs_exit: 141062306a36Sopenharmony_ci nsim_dev_port_debugfs_exit(nsim_dev_port); 141162306a36Sopenharmony_cierr_dl_port_unregister: 141262306a36Sopenharmony_ci devl_port_unregister(devlink_port); 141362306a36Sopenharmony_cierr_port_free: 141462306a36Sopenharmony_ci kfree(nsim_dev_port); 141562306a36Sopenharmony_ci return err; 141662306a36Sopenharmony_ci} 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_cistatic void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port) 141962306a36Sopenharmony_ci{ 142062306a36Sopenharmony_ci struct devlink_port *devlink_port = &nsim_dev_port->devlink_port; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci list_del(&nsim_dev_port->list); 142362306a36Sopenharmony_ci if (nsim_dev_port_is_vf(nsim_dev_port)) 142462306a36Sopenharmony_ci devl_rate_leaf_destroy(&nsim_dev_port->devlink_port); 142562306a36Sopenharmony_ci nsim_destroy(nsim_dev_port->ns); 142662306a36Sopenharmony_ci nsim_dev_port_debugfs_exit(nsim_dev_port); 142762306a36Sopenharmony_ci devl_port_unregister(devlink_port); 142862306a36Sopenharmony_ci kfree(nsim_dev_port); 142962306a36Sopenharmony_ci} 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_cistatic void nsim_dev_port_del_all(struct nsim_dev *nsim_dev) 143262306a36Sopenharmony_ci{ 143362306a36Sopenharmony_ci struct nsim_dev_port *nsim_dev_port, *tmp; 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci list_for_each_entry_safe(nsim_dev_port, tmp, 143662306a36Sopenharmony_ci &nsim_dev->port_list, list) 143762306a36Sopenharmony_ci __nsim_dev_port_del(nsim_dev_port); 143862306a36Sopenharmony_ci} 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_cistatic int nsim_dev_port_add_all(struct nsim_dev *nsim_dev, 144162306a36Sopenharmony_ci unsigned int port_count) 144262306a36Sopenharmony_ci{ 144362306a36Sopenharmony_ci int i, err; 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci for (i = 0; i < port_count; i++) { 144662306a36Sopenharmony_ci err = __nsim_dev_port_add(nsim_dev, NSIM_DEV_PORT_TYPE_PF, i); 144762306a36Sopenharmony_ci if (err) 144862306a36Sopenharmony_ci goto err_port_del_all; 144962306a36Sopenharmony_ci } 145062306a36Sopenharmony_ci return 0; 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_cierr_port_del_all: 145362306a36Sopenharmony_ci nsim_dev_port_del_all(nsim_dev); 145462306a36Sopenharmony_ci return err; 145562306a36Sopenharmony_ci} 145662306a36Sopenharmony_ci 145762306a36Sopenharmony_cistatic int nsim_dev_reload_create(struct nsim_dev *nsim_dev, 145862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 145962306a36Sopenharmony_ci{ 146062306a36Sopenharmony_ci struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev; 146162306a36Sopenharmony_ci struct devlink *devlink; 146262306a36Sopenharmony_ci int err; 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci devlink = priv_to_devlink(nsim_dev); 146562306a36Sopenharmony_ci nsim_dev = devlink_priv(devlink); 146662306a36Sopenharmony_ci INIT_LIST_HEAD(&nsim_dev->port_list); 146762306a36Sopenharmony_ci nsim_dev->fw_update_status = true; 146862306a36Sopenharmony_ci nsim_dev->fw_update_overwrite_mask = 0; 146962306a36Sopenharmony_ci 147062306a36Sopenharmony_ci nsim_devlink_param_load_driverinit_values(devlink); 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci err = nsim_dev_dummy_region_init(nsim_dev, devlink); 147362306a36Sopenharmony_ci if (err) 147462306a36Sopenharmony_ci return err; 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci err = nsim_dev_traps_init(devlink); 147762306a36Sopenharmony_ci if (err) 147862306a36Sopenharmony_ci goto err_dummy_region_exit; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci nsim_dev->fib_data = nsim_fib_create(devlink, extack); 148162306a36Sopenharmony_ci if (IS_ERR(nsim_dev->fib_data)) { 148262306a36Sopenharmony_ci err = PTR_ERR(nsim_dev->fib_data); 148362306a36Sopenharmony_ci goto err_traps_exit; 148462306a36Sopenharmony_ci } 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci err = nsim_dev_health_init(nsim_dev, devlink); 148762306a36Sopenharmony_ci if (err) 148862306a36Sopenharmony_ci goto err_fib_destroy; 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci err = nsim_dev_psample_init(nsim_dev); 149162306a36Sopenharmony_ci if (err) 149262306a36Sopenharmony_ci goto err_health_exit; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci err = nsim_dev_hwstats_init(nsim_dev); 149562306a36Sopenharmony_ci if (err) 149662306a36Sopenharmony_ci goto err_psample_exit; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count); 149962306a36Sopenharmony_ci if (err) 150062306a36Sopenharmony_ci goto err_hwstats_exit; 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_ci nsim_dev->take_snapshot = debugfs_create_file("take_snapshot", 150362306a36Sopenharmony_ci 0200, 150462306a36Sopenharmony_ci nsim_dev->ddir, 150562306a36Sopenharmony_ci nsim_dev, 150662306a36Sopenharmony_ci &nsim_dev_take_snapshot_fops); 150762306a36Sopenharmony_ci return 0; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_cierr_hwstats_exit: 151062306a36Sopenharmony_ci nsim_dev_hwstats_exit(nsim_dev); 151162306a36Sopenharmony_cierr_psample_exit: 151262306a36Sopenharmony_ci nsim_dev_psample_exit(nsim_dev); 151362306a36Sopenharmony_cierr_health_exit: 151462306a36Sopenharmony_ci nsim_dev_health_exit(nsim_dev); 151562306a36Sopenharmony_cierr_fib_destroy: 151662306a36Sopenharmony_ci nsim_fib_destroy(devlink, nsim_dev->fib_data); 151762306a36Sopenharmony_cierr_traps_exit: 151862306a36Sopenharmony_ci nsim_dev_traps_exit(devlink); 151962306a36Sopenharmony_cierr_dummy_region_exit: 152062306a36Sopenharmony_ci nsim_dev_dummy_region_exit(nsim_dev); 152162306a36Sopenharmony_ci return err; 152262306a36Sopenharmony_ci} 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ciint nsim_drv_probe(struct nsim_bus_dev *nsim_bus_dev) 152562306a36Sopenharmony_ci{ 152662306a36Sopenharmony_ci struct nsim_dev *nsim_dev; 152762306a36Sopenharmony_ci struct devlink *devlink; 152862306a36Sopenharmony_ci int err; 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci devlink = devlink_alloc_ns(&nsim_dev_devlink_ops, sizeof(*nsim_dev), 153162306a36Sopenharmony_ci nsim_bus_dev->initial_net, &nsim_bus_dev->dev); 153262306a36Sopenharmony_ci if (!devlink) 153362306a36Sopenharmony_ci return -ENOMEM; 153462306a36Sopenharmony_ci devl_lock(devlink); 153562306a36Sopenharmony_ci nsim_dev = devlink_priv(devlink); 153662306a36Sopenharmony_ci nsim_dev->nsim_bus_dev = nsim_bus_dev; 153762306a36Sopenharmony_ci nsim_dev->switch_id.id_len = sizeof(nsim_dev->switch_id.id); 153862306a36Sopenharmony_ci get_random_bytes(nsim_dev->switch_id.id, nsim_dev->switch_id.id_len); 153962306a36Sopenharmony_ci INIT_LIST_HEAD(&nsim_dev->port_list); 154062306a36Sopenharmony_ci nsim_dev->fw_update_status = true; 154162306a36Sopenharmony_ci nsim_dev->fw_update_overwrite_mask = 0; 154262306a36Sopenharmony_ci nsim_dev->max_macs = NSIM_DEV_MAX_MACS_DEFAULT; 154362306a36Sopenharmony_ci nsim_dev->test1 = NSIM_DEV_TEST1_DEFAULT; 154462306a36Sopenharmony_ci spin_lock_init(&nsim_dev->fa_cookie_lock); 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_ci dev_set_drvdata(&nsim_bus_dev->dev, nsim_dev); 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci nsim_dev->vfconfigs = kcalloc(nsim_bus_dev->max_vfs, 154962306a36Sopenharmony_ci sizeof(struct nsim_vf_config), 155062306a36Sopenharmony_ci GFP_KERNEL | __GFP_NOWARN); 155162306a36Sopenharmony_ci if (!nsim_dev->vfconfigs) { 155262306a36Sopenharmony_ci err = -ENOMEM; 155362306a36Sopenharmony_ci goto err_devlink_unlock; 155462306a36Sopenharmony_ci } 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci err = devl_register(devlink); 155762306a36Sopenharmony_ci if (err) 155862306a36Sopenharmony_ci goto err_vfc_free; 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci err = nsim_dev_resources_register(devlink); 156162306a36Sopenharmony_ci if (err) 156262306a36Sopenharmony_ci goto err_dl_unregister; 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci err = devl_params_register(devlink, nsim_devlink_params, 156562306a36Sopenharmony_ci ARRAY_SIZE(nsim_devlink_params)); 156662306a36Sopenharmony_ci if (err) 156762306a36Sopenharmony_ci goto err_resource_unregister; 156862306a36Sopenharmony_ci nsim_devlink_set_params_init_values(nsim_dev, devlink); 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci err = nsim_dev_dummy_region_init(nsim_dev, devlink); 157162306a36Sopenharmony_ci if (err) 157262306a36Sopenharmony_ci goto err_params_unregister; 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_ci err = nsim_dev_traps_init(devlink); 157562306a36Sopenharmony_ci if (err) 157662306a36Sopenharmony_ci goto err_dummy_region_exit; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci err = nsim_dev_debugfs_init(nsim_dev); 157962306a36Sopenharmony_ci if (err) 158062306a36Sopenharmony_ci goto err_traps_exit; 158162306a36Sopenharmony_ci 158262306a36Sopenharmony_ci nsim_dev->fib_data = nsim_fib_create(devlink, NULL); 158362306a36Sopenharmony_ci if (IS_ERR(nsim_dev->fib_data)) { 158462306a36Sopenharmony_ci err = PTR_ERR(nsim_dev->fib_data); 158562306a36Sopenharmony_ci goto err_debugfs_exit; 158662306a36Sopenharmony_ci } 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci err = nsim_dev_health_init(nsim_dev, devlink); 158962306a36Sopenharmony_ci if (err) 159062306a36Sopenharmony_ci goto err_fib_destroy; 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_ci err = nsim_bpf_dev_init(nsim_dev); 159362306a36Sopenharmony_ci if (err) 159462306a36Sopenharmony_ci goto err_health_exit; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_ci err = nsim_dev_psample_init(nsim_dev); 159762306a36Sopenharmony_ci if (err) 159862306a36Sopenharmony_ci goto err_bpf_dev_exit; 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci err = nsim_dev_hwstats_init(nsim_dev); 160162306a36Sopenharmony_ci if (err) 160262306a36Sopenharmony_ci goto err_psample_exit; 160362306a36Sopenharmony_ci 160462306a36Sopenharmony_ci err = nsim_dev_port_add_all(nsim_dev, nsim_bus_dev->port_count); 160562306a36Sopenharmony_ci if (err) 160662306a36Sopenharmony_ci goto err_hwstats_exit; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci nsim_dev->esw_mode = DEVLINK_ESWITCH_MODE_LEGACY; 160962306a36Sopenharmony_ci devl_unlock(devlink); 161062306a36Sopenharmony_ci return 0; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_cierr_hwstats_exit: 161362306a36Sopenharmony_ci nsim_dev_hwstats_exit(nsim_dev); 161462306a36Sopenharmony_cierr_psample_exit: 161562306a36Sopenharmony_ci nsim_dev_psample_exit(nsim_dev); 161662306a36Sopenharmony_cierr_bpf_dev_exit: 161762306a36Sopenharmony_ci nsim_bpf_dev_exit(nsim_dev); 161862306a36Sopenharmony_cierr_health_exit: 161962306a36Sopenharmony_ci nsim_dev_health_exit(nsim_dev); 162062306a36Sopenharmony_cierr_fib_destroy: 162162306a36Sopenharmony_ci nsim_fib_destroy(devlink, nsim_dev->fib_data); 162262306a36Sopenharmony_cierr_debugfs_exit: 162362306a36Sopenharmony_ci nsim_dev_debugfs_exit(nsim_dev); 162462306a36Sopenharmony_cierr_traps_exit: 162562306a36Sopenharmony_ci nsim_dev_traps_exit(devlink); 162662306a36Sopenharmony_cierr_dummy_region_exit: 162762306a36Sopenharmony_ci nsim_dev_dummy_region_exit(nsim_dev); 162862306a36Sopenharmony_cierr_params_unregister: 162962306a36Sopenharmony_ci devl_params_unregister(devlink, nsim_devlink_params, 163062306a36Sopenharmony_ci ARRAY_SIZE(nsim_devlink_params)); 163162306a36Sopenharmony_cierr_resource_unregister: 163262306a36Sopenharmony_ci devl_resources_unregister(devlink); 163362306a36Sopenharmony_cierr_dl_unregister: 163462306a36Sopenharmony_ci devl_unregister(devlink); 163562306a36Sopenharmony_cierr_vfc_free: 163662306a36Sopenharmony_ci kfree(nsim_dev->vfconfigs); 163762306a36Sopenharmony_cierr_devlink_unlock: 163862306a36Sopenharmony_ci devl_unlock(devlink); 163962306a36Sopenharmony_ci devlink_free(devlink); 164062306a36Sopenharmony_ci dev_set_drvdata(&nsim_bus_dev->dev, NULL); 164162306a36Sopenharmony_ci return err; 164262306a36Sopenharmony_ci} 164362306a36Sopenharmony_ci 164462306a36Sopenharmony_cistatic void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev) 164562306a36Sopenharmony_ci{ 164662306a36Sopenharmony_ci struct devlink *devlink = priv_to_devlink(nsim_dev); 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_ci if (devlink_is_reload_failed(devlink)) 164962306a36Sopenharmony_ci return; 165062306a36Sopenharmony_ci debugfs_remove(nsim_dev->take_snapshot); 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci if (nsim_dev_get_vfs(nsim_dev)) { 165362306a36Sopenharmony_ci nsim_bus_dev_set_vfs(nsim_dev->nsim_bus_dev, 0); 165462306a36Sopenharmony_ci if (nsim_esw_mode_is_switchdev(nsim_dev)) 165562306a36Sopenharmony_ci nsim_esw_legacy_enable(nsim_dev, NULL); 165662306a36Sopenharmony_ci } 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci nsim_dev_port_del_all(nsim_dev); 165962306a36Sopenharmony_ci nsim_dev_hwstats_exit(nsim_dev); 166062306a36Sopenharmony_ci nsim_dev_psample_exit(nsim_dev); 166162306a36Sopenharmony_ci nsim_dev_health_exit(nsim_dev); 166262306a36Sopenharmony_ci nsim_fib_destroy(devlink, nsim_dev->fib_data); 166362306a36Sopenharmony_ci nsim_dev_traps_exit(devlink); 166462306a36Sopenharmony_ci nsim_dev_dummy_region_exit(nsim_dev); 166562306a36Sopenharmony_ci} 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_civoid nsim_drv_remove(struct nsim_bus_dev *nsim_bus_dev) 166862306a36Sopenharmony_ci{ 166962306a36Sopenharmony_ci struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); 167062306a36Sopenharmony_ci struct devlink *devlink = priv_to_devlink(nsim_dev); 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci devl_lock(devlink); 167362306a36Sopenharmony_ci nsim_dev_reload_destroy(nsim_dev); 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci nsim_bpf_dev_exit(nsim_dev); 167662306a36Sopenharmony_ci nsim_dev_debugfs_exit(nsim_dev); 167762306a36Sopenharmony_ci devl_params_unregister(devlink, nsim_devlink_params, 167862306a36Sopenharmony_ci ARRAY_SIZE(nsim_devlink_params)); 167962306a36Sopenharmony_ci devl_resources_unregister(devlink); 168062306a36Sopenharmony_ci devl_unregister(devlink); 168162306a36Sopenharmony_ci kfree(nsim_dev->vfconfigs); 168262306a36Sopenharmony_ci kfree(nsim_dev->fa_cookie); 168362306a36Sopenharmony_ci devl_unlock(devlink); 168462306a36Sopenharmony_ci devlink_free(devlink); 168562306a36Sopenharmony_ci dev_set_drvdata(&nsim_bus_dev->dev, NULL); 168662306a36Sopenharmony_ci} 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_cistatic struct nsim_dev_port * 168962306a36Sopenharmony_ci__nsim_dev_port_lookup(struct nsim_dev *nsim_dev, enum nsim_dev_port_type type, 169062306a36Sopenharmony_ci unsigned int port_index) 169162306a36Sopenharmony_ci{ 169262306a36Sopenharmony_ci struct nsim_dev_port *nsim_dev_port; 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci port_index = nsim_dev_port_index(type, port_index); 169562306a36Sopenharmony_ci list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) 169662306a36Sopenharmony_ci if (nsim_dev_port->port_index == port_index) 169762306a36Sopenharmony_ci return nsim_dev_port; 169862306a36Sopenharmony_ci return NULL; 169962306a36Sopenharmony_ci} 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ciint nsim_drv_port_add(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type type, 170262306a36Sopenharmony_ci unsigned int port_index) 170362306a36Sopenharmony_ci{ 170462306a36Sopenharmony_ci struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); 170562306a36Sopenharmony_ci int err; 170662306a36Sopenharmony_ci 170762306a36Sopenharmony_ci devl_lock(priv_to_devlink(nsim_dev)); 170862306a36Sopenharmony_ci if (__nsim_dev_port_lookup(nsim_dev, type, port_index)) 170962306a36Sopenharmony_ci err = -EEXIST; 171062306a36Sopenharmony_ci else 171162306a36Sopenharmony_ci err = __nsim_dev_port_add(nsim_dev, type, port_index); 171262306a36Sopenharmony_ci devl_unlock(priv_to_devlink(nsim_dev)); 171362306a36Sopenharmony_ci return err; 171462306a36Sopenharmony_ci} 171562306a36Sopenharmony_ci 171662306a36Sopenharmony_ciint nsim_drv_port_del(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type type, 171762306a36Sopenharmony_ci unsigned int port_index) 171862306a36Sopenharmony_ci{ 171962306a36Sopenharmony_ci struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); 172062306a36Sopenharmony_ci struct nsim_dev_port *nsim_dev_port; 172162306a36Sopenharmony_ci int err = 0; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci devl_lock(priv_to_devlink(nsim_dev)); 172462306a36Sopenharmony_ci nsim_dev_port = __nsim_dev_port_lookup(nsim_dev, type, port_index); 172562306a36Sopenharmony_ci if (!nsim_dev_port) 172662306a36Sopenharmony_ci err = -ENOENT; 172762306a36Sopenharmony_ci else 172862306a36Sopenharmony_ci __nsim_dev_port_del(nsim_dev_port); 172962306a36Sopenharmony_ci devl_unlock(priv_to_devlink(nsim_dev)); 173062306a36Sopenharmony_ci return err; 173162306a36Sopenharmony_ci} 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_ciint nsim_drv_configure_vfs(struct nsim_bus_dev *nsim_bus_dev, 173462306a36Sopenharmony_ci unsigned int num_vfs) 173562306a36Sopenharmony_ci{ 173662306a36Sopenharmony_ci struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); 173762306a36Sopenharmony_ci struct devlink *devlink = priv_to_devlink(nsim_dev); 173862306a36Sopenharmony_ci int ret = 0; 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci devl_lock(devlink); 174162306a36Sopenharmony_ci if (nsim_bus_dev->num_vfs == num_vfs) 174262306a36Sopenharmony_ci goto exit_unlock; 174362306a36Sopenharmony_ci if (nsim_bus_dev->num_vfs && num_vfs) { 174462306a36Sopenharmony_ci ret = -EBUSY; 174562306a36Sopenharmony_ci goto exit_unlock; 174662306a36Sopenharmony_ci } 174762306a36Sopenharmony_ci if (nsim_bus_dev->max_vfs < num_vfs) { 174862306a36Sopenharmony_ci ret = -ENOMEM; 174962306a36Sopenharmony_ci goto exit_unlock; 175062306a36Sopenharmony_ci } 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci nsim_bus_dev_set_vfs(nsim_bus_dev, num_vfs); 175362306a36Sopenharmony_ci if (nsim_esw_mode_is_switchdev(nsim_dev)) { 175462306a36Sopenharmony_ci if (num_vfs) { 175562306a36Sopenharmony_ci ret = nsim_esw_switchdev_enable(nsim_dev, NULL); 175662306a36Sopenharmony_ci if (ret) { 175762306a36Sopenharmony_ci nsim_bus_dev_set_vfs(nsim_bus_dev, 0); 175862306a36Sopenharmony_ci goto exit_unlock; 175962306a36Sopenharmony_ci } 176062306a36Sopenharmony_ci } else { 176162306a36Sopenharmony_ci nsim_esw_legacy_enable(nsim_dev, NULL); 176262306a36Sopenharmony_ci } 176362306a36Sopenharmony_ci } 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_ciexit_unlock: 176662306a36Sopenharmony_ci devl_unlock(devlink); 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_ci return ret; 176962306a36Sopenharmony_ci} 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ciint nsim_dev_init(void) 177262306a36Sopenharmony_ci{ 177362306a36Sopenharmony_ci nsim_dev_ddir = debugfs_create_dir(DRV_NAME, NULL); 177462306a36Sopenharmony_ci return PTR_ERR_OR_ZERO(nsim_dev_ddir); 177562306a36Sopenharmony_ci} 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_civoid nsim_dev_exit(void) 177862306a36Sopenharmony_ci{ 177962306a36Sopenharmony_ci debugfs_remove_recursive(nsim_dev_ddir); 178062306a36Sopenharmony_ci} 1781