Simulate PCI SF ports. Allow user to create one or more PCI SF ports. Examples:
echo "10 1" > /sys/bus/netdevsim/new_device Add PCI PF port: $ devlink port add netdevsim/netdevsim10 flavour pcipf pfnum 2 netdevsim/netdevsim10/1: type eth netdev eth1 flavour pcipf controller 0 pfnum 2 external false splittable false Add PCI SF port where port index and sfnum are auto assigned by driver. $ devlink port add netdevsim/netdevsim10 flavour pcisf pfnum 2 netdevsim/netdevsim10/2: type eth netdev eth2 flavour pcisf controller 0 pfnum 2 sfnum 0 splittable false Show devlink ports: $ devlink port show netdevsim/netdevsim10/0: type eth netdev eth0 flavour physical port 1 splittable false netdevsim/netdevsim10/1: type eth netdev eth1 flavour pcipf controller 0 pfnum 2 external false splittable false netdevsim/netdevsim10/2: type eth netdev eth2 flavour pcisf controller 0 pfnum 2 sfnum 0 splittable false Create a PCI SF port whose port index and SF number are assigned by the user. $ devlink port add netdevsim/netdevsim10/66 flavour pcisf pfnum 2 sfnum 66 netdevsim/netdevsim10/66: type eth netdev eth3 flavour pcisf controller 0 pfnum 2 sfnum 66 splittable false Delete PCI SF and PF ports: $ devlink port del netdevsim/netdevsim10/66 $ devlink port del netdevsim/netdevsim10/2 $ devlink port del netdevsim/netdevsim10/1 Signed-off-by: Parav Pandit <pa...@nvidia.com> Reviewed-by: Jiri Pirko <j...@nvidia.com> --- drivers/net/netdevsim/netdevsim.h | 1 + drivers/net/netdevsim/port_function.c | 99 ++++++++++++++++++++++++++- 2 files changed, 97 insertions(+), 3 deletions(-) diff --git a/drivers/net/netdevsim/netdevsim.h b/drivers/net/netdevsim/netdevsim.h index 31beddede0f2..efa7c08d842a 100644 --- a/drivers/net/netdevsim/netdevsim.h +++ b/drivers/net/netdevsim/netdevsim.h @@ -233,6 +233,7 @@ struct nsim_dev { struct list_head head; struct ida ida; struct ida pfnum_ida; + struct ida sfnum_ida; struct mutex disable_mutex; /* protects port deletion * by driver unload context */ diff --git a/drivers/net/netdevsim/port_function.c b/drivers/net/netdevsim/port_function.c index a957b754ef92..a2f62a609e9b 100644 --- a/drivers/net/netdevsim/port_function.c +++ b/drivers/net/netdevsim/port_function.c @@ -9,9 +9,12 @@ struct nsim_port_fn { struct devlink_port dl_port; struct net_device *netdev; + struct nsim_port_fn *pf_pfn; struct list_head list; unsigned int port_index; enum devlink_port_flavour flavour; + int refcount; /* Counts how many sf ports are bound attached to this pf port. */ + u32 sfnum; u16 pfnum; }; @@ -91,9 +94,24 @@ nsim_devlink_port_fn_alloc(struct nsim_dev *dev, goto fn_ida_err; port->pfnum = ret; break; + case DEVLINK_PORT_FLAVOUR_PCI_SF: + if (attrs->sfnum_valid) + ret = ida_alloc_range(&dev->port_functions.sfnum_ida, attrs->sfnum, + attrs->sfnum, GFP_KERNEL); + else + ret = ida_alloc(&dev->port_functions.sfnum_ida, GFP_KERNEL); + if (ret < 0) + goto fn_ida_err; + port->sfnum = ret; + port->pfnum = attrs->pfnum; + break; default: break; } + /* refcount_t is not needed as port is protected by port_functions.mutex. + * This count is to keep track of how many SF ports are attached a PF port. + */ + port->refcount = 1; return port; fn_ida_err: @@ -110,6 +128,9 @@ nsim_devlink_port_fn_free(struct nsim_dev *dev, struct nsim_port_fn *port) case DEVLINK_PORT_FLAVOUR_PCI_PF: ida_simple_remove(&dev->port_functions.pfnum_ida, port->pfnum); break; + case DEVLINK_PORT_FLAVOUR_PCI_SF: + ida_simple_remove(&dev->port_functions.sfnum_ida, port->sfnum); + break; default: break; } @@ -139,6 +160,12 @@ nsim_dev_port_port_exists(struct nsim_dev *nsim_dev, tmp->flavour == DEVLINK_PORT_FLAVOUR_PCI_PF && tmp->pfnum == attrs->pfnum) return true; + + if (attrs->flavour == DEVLINK_PORT_FLAVOUR_PCI_SF && + tmp->flavour == DEVLINK_PORT_FLAVOUR_PCI_SF && + attrs->sfnum_valid && + tmp->sfnum == attrs->sfnum && tmp->pfnum == attrs->pfnum) + return true; } return false; } @@ -153,20 +180,72 @@ nsim_dev_devlink_port_index_lookup(const struct nsim_dev *nsim_dev, list_for_each_entry(port, &nsim_dev->port_functions.head, list) { if (port->port_index != port_index) continue; + if (port->refcount > 1) { + NL_SET_ERR_MSG_MOD(extack, "Port is in use"); + return ERR_PTR(-EBUSY); + } return port; } NL_SET_ERR_MSG_MOD(extack, "User created port not found"); return ERR_PTR(-ENOENT); } +static struct nsim_port_fn * +pf_port_get(struct nsim_dev *nsim_dev, struct nsim_port_fn *port) +{ + struct nsim_port_fn *tmp; + + /* PF port addition doesn't need a parent. */ + if (port->flavour == DEVLINK_PORT_FLAVOUR_PCI_PF) + return NULL; + + list_for_each_entry(tmp, &nsim_dev->port_functions.head, list) { + if (tmp->flavour != DEVLINK_PORT_FLAVOUR_PCI_PF || + tmp->pfnum != port->pfnum) + continue; + + if (tmp->refcount + 1 == INT_MAX) + return ERR_PTR(-ENOSPC); + + port->pf_pfn = tmp; + tmp->refcount++; + return tmp; + } + return ERR_PTR(-ENOENT); +} + +static void pf_port_put(struct nsim_port_fn *port) +{ + if (port->pf_pfn) { + port->pf_pfn->refcount--; + WARN_ON(port->pf_pfn->refcount < 0); + } + port->refcount--; + WARN_ON(port->refcount != 0); +} + static int nsim_devlink_port_fn_add(struct devlink *devlink, struct nsim_dev *nsim_dev, struct nsim_port_fn *port, struct netlink_ext_ack *extack) { + struct nsim_port_fn *pf_pfn; int err; - list_add(&port->list, &nsim_dev->port_functions.head); + /* Keep all PF ports at the start, so that when driver is unloaded + * All SF ports from the end of the list can be removed first. + */ + if (port->flavour == DEVLINK_PORT_FLAVOUR_PCI_PF) + list_add(&port->list, &nsim_dev->port_functions.head); + else + list_add_tail(&port->list, &nsim_dev->port_functions.head); + + pf_pfn = pf_port_get(nsim_dev, port); + if (IS_ERR(pf_pfn)) { + NL_SET_ERR_MSG_MOD(extack, "Fail to get pf port"); + err = PTR_ERR(pf_pfn); + goto pf_err; + } err = devlink_port_register(devlink, &port->dl_port, port->port_index); if (err) @@ -183,6 +262,8 @@ static int nsim_devlink_port_fn_add(struct devlink *devlink, devlink_port_type_clear(&port->dl_port); devlink_port_unregister(&port->dl_port); reg_err: + pf_port_put(port); +pf_err: list_del(&port->list); return err; } @@ -194,13 +275,15 @@ static void nsim_devlink_port_fn_del(struct nsim_dev *nsim_dev, unregister_netdev(port->netdev); devlink_port_unregister(&port->dl_port); list_del(&port->list); + pf_port_put(port); } static bool nsim_dev_port_flavour_supported(const struct nsim_dev *nsim_dev, const struct devlink_port_new_attrs *attrs) { - return attrs->flavour == DEVLINK_PORT_FLAVOUR_PCI_PF; + return attrs->flavour == DEVLINK_PORT_FLAVOUR_PCI_PF || + attrs->flavour == DEVLINK_PORT_FLAVOUR_PCI_SF; } int nsim_dev_devlink_port_new(struct devlink *devlink, @@ -245,7 +328,12 @@ int nsim_dev_devlink_port_new(struct devlink *devlink, nsim_dev->switch_id.id_len); port->dl_port.attrs.switch_id.id_len = nsim_dev->switch_id.id_len; - devlink_port_attrs_pci_pf_set(&port->dl_port, 0, port->pfnum, false); + if (attrs->flavour == DEVLINK_PORT_FLAVOUR_PCI_PF) + devlink_port_attrs_pci_pf_set(&port->dl_port, 0, + port->pfnum, false); + else + devlink_port_attrs_pci_sf_set(&port->dl_port, 0, port->pfnum, + port->sfnum); err = nsim_devlink_port_fn_add(devlink, nsim_dev, port, extack); if (err) @@ -300,10 +388,13 @@ void nsim_dev_port_fn_init(struct nsim_dev *nsim_dev) INIT_LIST_HEAD(&nsim_dev->port_functions.head); ida_init(&nsim_dev->port_functions.ida); ida_init(&nsim_dev->port_functions.pfnum_ida); + ida_init(&nsim_dev->port_functions.sfnum_ida); } void nsim_dev_port_fn_exit(struct nsim_dev *nsim_dev) { + WARN_ON(!ida_is_empty(&nsim_dev->port_functions.sfnum_ida)); + ida_destroy(&nsim_dev->port_functions.sfnum_ida); WARN_ON(!ida_is_empty(&nsim_dev->port_functions.pfnum_ida)); ida_destroy(&nsim_dev->port_functions.pfnum_ida); WARN_ON(!ida_is_empty(&nsim_dev->port_functions.ida)); @@ -332,6 +423,8 @@ void nsim_dev_port_fn_disable(struct nsim_dev *nsim_dev) * commands have completed, so it is safe to delete all user created * ports. */ + + /* Remove SF ports first, followed by PF ports. */ list_for_each_entry_safe_reverse(port, tmp, &nsim_dev->port_functions.head, list) { nsim_devlink_port_fn_del(nsim_dev, port); -- 2.26.2