diff options
Diffstat (limited to 'drivers/net/netdevsim/dev.c')
-rw-r--r-- | drivers/net/netdevsim/dev.c | 404 |
1 files changed, 389 insertions, 15 deletions
diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index 6189a4c0d39e..6348307bfa84 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c @@ -35,6 +35,25 @@ #include "netdevsim.h" +static unsigned int +nsim_dev_port_index(enum nsim_dev_port_type type, unsigned int port_index) +{ + switch (type) { + case NSIM_DEV_PORT_TYPE_VF: + port_index = NSIM_DEV_VF_PORT_INDEX_BASE + port_index; + break; + case NSIM_DEV_PORT_TYPE_PF: + break; + } + + return port_index; +} + +static inline unsigned int nsim_dev_port_index_to_vf_index(unsigned int port_index) +{ + return port_index - NSIM_DEV_VF_PORT_INDEX_BASE; +} + static struct dentry *nsim_dev_ddir; #define NSIM_DEV_DUMMY_REGION_SIZE (1024 * 32) @@ -192,9 +211,18 @@ static const struct file_operations nsim_dev_trap_fa_cookie_fops = { .owner = THIS_MODULE, }; +static const struct file_operations nsim_dev_max_vfs_fops = { + .open = simple_open, + .read = nsim_bus_dev_max_vfs_read, + .write = nsim_bus_dev_max_vfs_write, + .llseek = generic_file_llseek, + .owner = THIS_MODULE, +}; + static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev) { char dev_ddir_name[sizeof(DRV_NAME) + 10]; + int err; sprintf(dev_ddir_name, DRV_NAME "%u", nsim_dev->nsim_bus_dev->dev.id); nsim_dev->ddir = debugfs_create_dir(dev_ddir_name, nsim_dev_ddir); @@ -231,30 +259,84 @@ static int nsim_dev_debugfs_init(struct nsim_dev *nsim_dev) debugfs_create_bool("fail_trap_policer_counter_get", 0600, nsim_dev->ddir, &nsim_dev->fail_trap_policer_counter_get); + nsim_dev->max_vfs = debugfs_create_file("max_vfs", + 0600, + nsim_dev->ddir, + nsim_dev->nsim_bus_dev, + &nsim_dev_max_vfs_fops); + nsim_dev->nodes_ddir = debugfs_create_dir("rate_nodes", nsim_dev->ddir); + if (IS_ERR(nsim_dev->nodes_ddir)) { + err = PTR_ERR(nsim_dev->nodes_ddir); + goto err_out; + } + debugfs_create_bool("fail_trap_drop_counter_get", 0600, + nsim_dev->ddir, + &nsim_dev->fail_trap_drop_counter_get); nsim_udp_tunnels_debugfs_create(nsim_dev); return 0; + +err_out: + debugfs_remove_recursive(nsim_dev->ports_ddir); + debugfs_remove_recursive(nsim_dev->ddir); + return err; } static void nsim_dev_debugfs_exit(struct nsim_dev *nsim_dev) { + debugfs_remove_recursive(nsim_dev->nodes_ddir); debugfs_remove_recursive(nsim_dev->ports_ddir); debugfs_remove_recursive(nsim_dev->ddir); } +static ssize_t nsim_dev_rate_parent_read(struct file *file, + char __user *data, + size_t count, loff_t *ppos) +{ + char **name_ptr = file->private_data; + size_t len; + + if (!*name_ptr) + return 0; + + len = strlen(*name_ptr); + return simple_read_from_buffer(data, count, ppos, *name_ptr, len); +} + +static const struct file_operations nsim_dev_rate_parent_fops = { + .open = simple_open, + .read = nsim_dev_rate_parent_read, + .llseek = generic_file_llseek, + .owner = THIS_MODULE, +}; + static int nsim_dev_port_debugfs_init(struct nsim_dev *nsim_dev, struct nsim_dev_port *nsim_dev_port) { + struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev; + unsigned int port_index = nsim_dev_port->port_index; char port_ddir_name[16]; char dev_link_name[32]; - sprintf(port_ddir_name, "%u", nsim_dev_port->port_index); + sprintf(port_ddir_name, "%u", port_index); nsim_dev_port->ddir = debugfs_create_dir(port_ddir_name, nsim_dev->ports_ddir); if (IS_ERR(nsim_dev_port->ddir)) return PTR_ERR(nsim_dev_port->ddir); - sprintf(dev_link_name, "../../../" DRV_NAME "%u", - nsim_dev->nsim_bus_dev->dev.id); + sprintf(dev_link_name, "../../../" DRV_NAME "%u", nsim_bus_dev->dev.id); + if (nsim_dev_port_is_vf(nsim_dev_port)) { + unsigned int vf_id = nsim_dev_port_index_to_vf_index(port_index); + + debugfs_create_u16("tx_share", 0400, nsim_dev_port->ddir, + &nsim_bus_dev->vfconfigs[vf_id].min_tx_rate); + debugfs_create_u16("tx_max", 0400, nsim_dev_port->ddir, + &nsim_bus_dev->vfconfigs[vf_id].max_tx_rate); + nsim_dev_port->rate_parent = debugfs_create_file("rate_parent", + 0400, + nsim_dev_port->ddir, + &nsim_dev_port->parent_name, + &nsim_dev_rate_parent_fops); + } debugfs_create_symlink("dev", nsim_dev_port->ddir, dev_link_name); return 0; @@ -407,6 +489,74 @@ static void nsim_dev_dummy_region_exit(struct nsim_dev *nsim_dev) devlink_region_destroy(nsim_dev->dummy_region); } +static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port); +int nsim_esw_legacy_enable(struct nsim_dev *nsim_dev, struct netlink_ext_ack *extack) +{ + struct devlink *devlink = priv_to_devlink(nsim_dev); + struct nsim_dev_port *nsim_dev_port, *tmp; + + devlink_rate_nodes_destroy(devlink); + mutex_lock(&nsim_dev->port_list_lock); + list_for_each_entry_safe(nsim_dev_port, tmp, &nsim_dev->port_list, list) + if (nsim_dev_port_is_vf(nsim_dev_port)) + __nsim_dev_port_del(nsim_dev_port); + mutex_unlock(&nsim_dev->port_list_lock); + nsim_dev->esw_mode = DEVLINK_ESWITCH_MODE_LEGACY; + return 0; +} + +int nsim_esw_switchdev_enable(struct nsim_dev *nsim_dev, struct netlink_ext_ack *extack) +{ + struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev; + int i, err; + + for (i = 0; i < nsim_bus_dev->num_vfs; i++) { + err = nsim_dev_port_add(nsim_bus_dev, NSIM_DEV_PORT_TYPE_VF, i); + if (err) { + NL_SET_ERR_MSG_MOD(extack, "Failed to initialize VFs' netdevsim ports"); + pr_err("Failed to initialize VF id=%d. %d.\n", i, err); + goto err_port_add_vfs; + } + } + nsim_dev->esw_mode = DEVLINK_ESWITCH_MODE_SWITCHDEV; + return 0; + +err_port_add_vfs: + for (i--; i >= 0; i--) + nsim_dev_port_del(nsim_bus_dev, NSIM_DEV_PORT_TYPE_VF, i); + return err; +} + +static int nsim_devlink_eswitch_mode_set(struct devlink *devlink, u16 mode, + struct netlink_ext_ack *extack) +{ + struct nsim_dev *nsim_dev = devlink_priv(devlink); + int err = 0; + + mutex_lock(&nsim_dev->nsim_bus_dev->vfs_lock); + if (mode == nsim_dev->esw_mode) + goto unlock; + + if (mode == DEVLINK_ESWITCH_MODE_LEGACY) + err = nsim_esw_legacy_enable(nsim_dev, extack); + else if (mode == DEVLINK_ESWITCH_MODE_SWITCHDEV) + err = nsim_esw_switchdev_enable(nsim_dev, extack); + else + err = -EINVAL; + +unlock: + mutex_unlock(&nsim_dev->nsim_bus_dev->vfs_lock); + return err; +} + +static int nsim_devlink_eswitch_mode_get(struct devlink *devlink, u16 *mode) +{ + struct nsim_dev *nsim_dev = devlink_priv(devlink); + + *mode = nsim_dev->esw_mode; + return 0; +} + struct nsim_trap_item { void *trap_ctx; enum devlink_trap_action action; @@ -416,6 +566,7 @@ struct nsim_trap_data { struct delayed_work trap_report_dw; struct nsim_trap_item *trap_items_arr; u64 *trap_policers_cnt_arr; + u64 trap_pkt_cnt; struct nsim_dev *nsim_dev; spinlock_t trap_lock; /* Protects trap_items_arr */ }; @@ -892,7 +1043,190 @@ nsim_dev_devlink_trap_policer_counter_get(struct devlink *devlink, return 0; } +#define NSIM_LINK_SPEED_MAX 5000 /* Mbps */ +#define NSIM_LINK_SPEED_UNIT 125000 /* 1 Mbps given in bytes/sec to avoid + * u64 overflow during conversion from + * bytes to bits. + */ + +static int nsim_rate_bytes_to_units(char *name, u64 *rate, struct netlink_ext_ack *extack) +{ + u64 val; + u32 rem; + + val = div_u64_rem(*rate, NSIM_LINK_SPEED_UNIT, &rem); + if (rem) { + pr_err("%s rate value %lluBps not in link speed units of 1Mbps.\n", + name, *rate); + NL_SET_ERR_MSG_MOD(extack, "TX rate value not in link speed units of 1Mbps."); + return -EINVAL; + } + + if (val > NSIM_LINK_SPEED_MAX) { + pr_err("%s rate value %lluMbps exceed link maximum speed 5000Mbps.\n", + name, val); + NL_SET_ERR_MSG_MOD(extack, "TX rate value exceed link maximum speed 5000Mbps."); + return -EINVAL; + } + *rate = val; + return 0; +} + +static int nsim_leaf_tx_share_set(struct devlink_rate *devlink_rate, void *priv, + u64 tx_share, struct netlink_ext_ack *extack) +{ + struct nsim_dev_port *nsim_dev_port = priv; + struct nsim_bus_dev *nsim_bus_dev = nsim_dev_port->ns->nsim_bus_dev; + int vf_id = nsim_dev_port_index_to_vf_index(nsim_dev_port->port_index); + int err; + + err = nsim_rate_bytes_to_units("tx_share", &tx_share, extack); + if (err) + return err; + + nsim_bus_dev->vfconfigs[vf_id].min_tx_rate = tx_share; + return 0; +} + +static int nsim_leaf_tx_max_set(struct devlink_rate *devlink_rate, void *priv, + u64 tx_max, struct netlink_ext_ack *extack) +{ + struct nsim_dev_port *nsim_dev_port = priv; + struct nsim_bus_dev *nsim_bus_dev = nsim_dev_port->ns->nsim_bus_dev; + int vf_id = nsim_dev_port_index_to_vf_index(nsim_dev_port->port_index); + int err; + + err = nsim_rate_bytes_to_units("tx_max", &tx_max, extack); + if (err) + return err; + + nsim_bus_dev->vfconfigs[vf_id].max_tx_rate = tx_max; + return 0; +} + +struct nsim_rate_node { + struct dentry *ddir; + struct dentry *rate_parent; + char *parent_name; + u16 tx_share; + u16 tx_max; +}; + +static int nsim_node_tx_share_set(struct devlink_rate *devlink_rate, void *priv, + u64 tx_share, struct netlink_ext_ack *extack) +{ + struct nsim_rate_node *nsim_node = priv; + int err; + + err = nsim_rate_bytes_to_units("tx_share", &tx_share, extack); + if (err) + return err; + + nsim_node->tx_share = tx_share; + return 0; +} + +static int nsim_node_tx_max_set(struct devlink_rate *devlink_rate, void *priv, + u64 tx_max, struct netlink_ext_ack *extack) +{ + struct nsim_rate_node *nsim_node = priv; + int err; + + err = nsim_rate_bytes_to_units("tx_max", &tx_max, extack); + if (err) + return err; + + nsim_node->tx_max = tx_max; + return 0; +} + +static int nsim_rate_node_new(struct devlink_rate *node, void **priv, + struct netlink_ext_ack *extack) +{ + struct nsim_dev *nsim_dev = devlink_priv(node->devlink); + struct nsim_rate_node *nsim_node; + + if (!nsim_esw_mode_is_switchdev(nsim_dev)) { + NL_SET_ERR_MSG_MOD(extack, "Node creation allowed only in switchdev mode."); + return -EOPNOTSUPP; + } + + nsim_node = kzalloc(sizeof(*nsim_node), GFP_KERNEL); + if (!nsim_node) + return -ENOMEM; + + nsim_node->ddir = debugfs_create_dir(node->name, nsim_dev->nodes_ddir); + + debugfs_create_u16("tx_share", 0400, nsim_node->ddir, &nsim_node->tx_share); + debugfs_create_u16("tx_max", 0400, nsim_node->ddir, &nsim_node->tx_max); + nsim_node->rate_parent = debugfs_create_file("rate_parent", 0400, + nsim_node->ddir, + &nsim_node->parent_name, + &nsim_dev_rate_parent_fops); + + *priv = nsim_node; + return 0; +} + +static int nsim_rate_node_del(struct devlink_rate *node, void *priv, + struct netlink_ext_ack *extack) +{ + struct nsim_rate_node *nsim_node = priv; + + debugfs_remove(nsim_node->rate_parent); + debugfs_remove_recursive(nsim_node->ddir); + kfree(nsim_node); + return 0; +} + +static int nsim_rate_leaf_parent_set(struct devlink_rate *child, + struct devlink_rate *parent, + void *priv_child, void *priv_parent, + struct netlink_ext_ack *extack) +{ + struct nsim_dev_port *nsim_dev_port = priv_child; + + if (parent) + nsim_dev_port->parent_name = parent->name; + else + nsim_dev_port->parent_name = NULL; + return 0; +} + +static int nsim_rate_node_parent_set(struct devlink_rate *child, + struct devlink_rate *parent, + void *priv_child, void *priv_parent, + struct netlink_ext_ack *extack) +{ + struct nsim_rate_node *nsim_node = priv_child; + + if (parent) + nsim_node->parent_name = parent->name; + else + nsim_node->parent_name = NULL; + return 0; +} + +static int +nsim_dev_devlink_trap_drop_counter_get(struct devlink *devlink, + const struct devlink_trap *trap, + u64 *p_drops) +{ + struct nsim_dev *nsim_dev = devlink_priv(devlink); + u64 *cnt; + + if (nsim_dev->fail_trap_drop_counter_get) + return -EINVAL; + + cnt = &nsim_dev->trap_data->trap_pkt_cnt; + *p_drops = (*cnt)++; + + return 0; +} + static const struct devlink_ops nsim_dev_devlink_ops = { + .eswitch_mode_set = nsim_devlink_eswitch_mode_set, + .eswitch_mode_get = nsim_devlink_eswitch_mode_get, .supported_flash_update_params = DEVLINK_SUPPORT_FLASH_UPDATE_COMPONENT | DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK, .reload_actions = BIT(DEVLINK_RELOAD_ACTION_DRIVER_REINIT), @@ -905,32 +1239,52 @@ static const struct devlink_ops nsim_dev_devlink_ops = { .trap_group_set = nsim_dev_devlink_trap_group_set, .trap_policer_set = nsim_dev_devlink_trap_policer_set, .trap_policer_counter_get = nsim_dev_devlink_trap_policer_counter_get, + .rate_leaf_tx_share_set = nsim_leaf_tx_share_set, + .rate_leaf_tx_max_set = nsim_leaf_tx_max_set, + .rate_node_tx_share_set = nsim_node_tx_share_set, + .rate_node_tx_max_set = nsim_node_tx_max_set, + .rate_node_new = nsim_rate_node_new, + .rate_node_del = nsim_rate_node_del, + .rate_leaf_parent_set = nsim_rate_leaf_parent_set, + .rate_node_parent_set = nsim_rate_node_parent_set, + .trap_drop_counter_get = nsim_dev_devlink_trap_drop_counter_get, }; #define NSIM_DEV_MAX_MACS_DEFAULT 32 #define NSIM_DEV_TEST1_DEFAULT true -static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, +static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, enum nsim_dev_port_type type, unsigned int port_index) { + struct nsim_bus_dev *nsim_bus_dev = nsim_dev->nsim_bus_dev; struct devlink_port_attrs attrs = {}; struct nsim_dev_port *nsim_dev_port; struct devlink_port *devlink_port; int err; + if (type == NSIM_DEV_PORT_TYPE_VF && !nsim_bus_dev->num_vfs) + return -EINVAL; + nsim_dev_port = kzalloc(sizeof(*nsim_dev_port), GFP_KERNEL); if (!nsim_dev_port) return -ENOMEM; - nsim_dev_port->port_index = port_index; + nsim_dev_port->port_index = nsim_dev_port_index(type, port_index); + nsim_dev_port->port_type = type; devlink_port = &nsim_dev_port->devlink_port; - attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; - attrs.phys.port_number = port_index + 1; + if (nsim_dev_port_is_pf(nsim_dev_port)) { + attrs.flavour = DEVLINK_PORT_FLAVOUR_PHYSICAL; + attrs.phys.port_number = port_index + 1; + } else { + attrs.flavour = DEVLINK_PORT_FLAVOUR_PCI_VF; + attrs.pci_vf.pf = 0; + attrs.pci_vf.vf = port_index; + } memcpy(attrs.switch_id.id, nsim_dev->switch_id.id, nsim_dev->switch_id.id_len); attrs.switch_id.id_len = nsim_dev->switch_id.id_len; devlink_port_attrs_set(devlink_port, &attrs); err = devlink_port_register(priv_to_devlink(nsim_dev), devlink_port, - port_index); + nsim_dev_port->port_index); if (err) goto err_port_free; @@ -944,11 +1298,20 @@ static int __nsim_dev_port_add(struct nsim_dev *nsim_dev, goto err_port_debugfs_exit; } + if (nsim_dev_port_is_vf(nsim_dev_port)) { + err = devlink_rate_leaf_create(&nsim_dev_port->devlink_port, + nsim_dev_port); + if (err) + goto err_nsim_destroy; + } + devlink_port_type_eth_set(devlink_port, nsim_dev_port->ns->netdev); list_add(&nsim_dev_port->list, &nsim_dev->port_list); return 0; +err_nsim_destroy: + nsim_destroy(nsim_dev_port->ns); err_port_debugfs_exit: nsim_dev_port_debugfs_exit(nsim_dev_port); err_dl_port_unregister: @@ -963,6 +1326,8 @@ static void __nsim_dev_port_del(struct nsim_dev_port *nsim_dev_port) struct devlink_port *devlink_port = &nsim_dev_port->devlink_port; list_del(&nsim_dev_port->list); + if (nsim_dev_port_is_vf(nsim_dev_port)) + devlink_rate_leaf_destroy(&nsim_dev_port->devlink_port); devlink_port_type_clear(devlink_port); nsim_destroy(nsim_dev_port->ns); nsim_dev_port_debugfs_exit(nsim_dev_port); @@ -987,7 +1352,7 @@ static int nsim_dev_port_add_all(struct nsim_dev *nsim_dev, int i, err; for (i = 0; i < port_count; i++) { - err = __nsim_dev_port_add(nsim_dev, i); + err = __nsim_dev_port_add(nsim_dev, NSIM_DEV_PORT_TYPE_PF, i); if (err) goto err_port_del_all; } @@ -1134,6 +1499,7 @@ int nsim_dev_probe(struct nsim_bus_dev *nsim_bus_dev) devlink_params_publish(devlink); devlink_reload_enable(devlink); + nsim_dev->esw_mode = DEVLINK_ESWITCH_MODE_LEGACY; return 0; err_psample_exit: @@ -1169,6 +1535,12 @@ static void nsim_dev_reload_destroy(struct nsim_dev *nsim_dev) if (devlink_is_reload_failed(devlink)) return; debugfs_remove(nsim_dev->take_snapshot); + + mutex_lock(&nsim_dev->nsim_bus_dev->vfs_lock); + if (nsim_dev->nsim_bus_dev->num_vfs) + nsim_bus_dev_vfs_disable(nsim_dev->nsim_bus_dev); + mutex_unlock(&nsim_dev->nsim_bus_dev->vfs_lock); + nsim_dev_port_del_all(nsim_dev); nsim_dev_psample_exit(nsim_dev); nsim_dev_health_exit(nsim_dev); @@ -1197,32 +1569,34 @@ void nsim_dev_remove(struct nsim_bus_dev *nsim_bus_dev) } static struct nsim_dev_port * -__nsim_dev_port_lookup(struct nsim_dev *nsim_dev, unsigned int port_index) +__nsim_dev_port_lookup(struct nsim_dev *nsim_dev, enum nsim_dev_port_type type, + unsigned int port_index) { struct nsim_dev_port *nsim_dev_port; + port_index = nsim_dev_port_index(type, port_index); list_for_each_entry(nsim_dev_port, &nsim_dev->port_list, list) if (nsim_dev_port->port_index == port_index) return nsim_dev_port; return NULL; } -int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev, +int nsim_dev_port_add(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type type, unsigned int port_index) { struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); int err; mutex_lock(&nsim_dev->port_list_lock); - if (__nsim_dev_port_lookup(nsim_dev, port_index)) + if (__nsim_dev_port_lookup(nsim_dev, type, port_index)) err = -EEXIST; else - err = __nsim_dev_port_add(nsim_dev, port_index); + err = __nsim_dev_port_add(nsim_dev, type, port_index); mutex_unlock(&nsim_dev->port_list_lock); return err; } -int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev, +int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev, enum nsim_dev_port_type type, unsigned int port_index) { struct nsim_dev *nsim_dev = dev_get_drvdata(&nsim_bus_dev->dev); @@ -1230,7 +1604,7 @@ int nsim_dev_port_del(struct nsim_bus_dev *nsim_bus_dev, int err = 0; mutex_lock(&nsim_dev->port_list_lock); - nsim_dev_port = __nsim_dev_port_lookup(nsim_dev, port_index); + nsim_dev_port = __nsim_dev_port_lookup(nsim_dev, type, port_index); if (!nsim_dev_port) err = -ENOENT; else |