Simulate PCI SF ports. Allow user to create one or more PCI SF ports.

Examples:

Create a PCI PF and PCI SF port.
$ devlink port add netdevsim/netdevsim10/10 flavour pcipf pfnum 0
$ devlink port add netdevsim/netdevsim10/11 flavour pcisf pfnum 0 sfnum 44
$ devlink port show netdevsim/netdevsim10/11
netdevsim/netdevsim10/11: type eth netdev eni10npf0sf44 flavour pcisf 
controller 0 pfnum 0 sfnum 44 external true splittable false
  function:
    hw_addr 00:00:00:00:00:00 state inactive

$ devlink port function set netdevsim/netdevsim10/11 hw_addr 00:11:22:33:44:55 
state active

$ devlink port show netdevsim/netdevsim10/11 -jp
{
    "port": {
        "netdevsim/netdevsim10/11": {
            "type": "eth",
            "netdev": "eni10npf0sf44",
            "flavour": "pcisf",
            "controller": 0,
            "pfnum": 0,
            "sfnum": 44,
            "external": true,
            "splittable": false,
            "function": {
                "hw_addr": "00:11:22:33:44:55",
                "state": "active"
            }
        }
    }
}

Delete newly added devlink port
$ devlink port add netdevsim/netdevsim10/11

Add devlink port of flavour 'pcisf' where port index and sfnum are
auto assigned by driver.
$ devlink port add netdevsim/netdevsim10 flavour pcisf controller 0 pfnum 0

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 | 95 +++++++++++++++++++++++++--
 2 files changed, 92 insertions(+), 4 deletions(-)

diff --git a/drivers/net/netdevsim/netdevsim.h 
b/drivers/net/netdevsim/netdevsim.h
index 0ea9705eda38..c70782e444d5 100644
--- a/drivers/net/netdevsim/netdevsim.h
+++ b/drivers/net/netdevsim/netdevsim.h
@@ -222,6 +222,7 @@ struct nsim_dev {
                struct list_head head;
                struct ida ida;
                struct ida pfnum_ida;
+               struct ida sfnum_ida;
        } port_functions;
 };
 
diff --git a/drivers/net/netdevsim/port_function.c 
b/drivers/net/netdevsim/port_function.c
index 99581d3d15fe..e1812acd55b4 100644
--- a/drivers/net/netdevsim/port_function.c
+++ b/drivers/net/netdevsim/port_function.c
@@ -13,10 +13,12 @@ struct nsim_port_function {
        unsigned int port_index;
        enum devlink_port_flavour flavour;
        u32 controller;
+       u32 sfnum;
        u16 pfnum;
        struct nsim_port_function *pf_port; /* Valid only for SF port */
        u8 hw_addr[ETH_ALEN];
        u8 state; /* enum devlink_port_function_state */
+       int refcount; /* Counts how many sf ports are bound attached to this pf 
port. */
 };
 
 void nsim_dev_port_function_init(struct nsim_dev *nsim_dev)
@@ -25,10 +27,13 @@ void nsim_dev_port_function_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_function_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));
@@ -119,9 +124,24 @@ nsim_devlink_port_function_alloc(struct nsim_dev *dev, 
const struct devlink_port
                        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:
@@ -137,6 +157,9 @@ static void nsim_devlink_port_function_free(struct nsim_dev 
*dev, struct nsim_po
        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;
        }
@@ -170,6 +193,11 @@ nsim_dev_port_port_exists(struct nsim_dev *nsim_dev, const 
struct devlink_port_n
                if (attrs->flavour == DEVLINK_PORT_FLAVOUR_PCI_PF &&
                    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 &&
+                   tmp->sfnum == attrs->sfnum && tmp->pfnum == attrs->pfnum)
+                       return true;
        }
        return false;
 }
@@ -183,21 +211,71 @@ nsim_dev_devlink_port_index_lookup(const struct nsim_dev 
*nsim_dev, unsigned int
        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_function *
+pf_port_get(struct nsim_dev *nsim_dev, struct nsim_port_function *port)
+{
+       struct nsim_port_function *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_port = tmp;
+               tmp->refcount++;
+               return tmp;
+       }
+       return ERR_PTR(-ENOENT);
+}
+
+static void pf_port_put(struct nsim_port_function *port)
+{
+       if (port->pf_port) {
+               port->pf_port->refcount--;
+               WARN_ON(port->pf_port->refcount < 0);
+       }
+       port->refcount--;
+       WARN_ON(port->refcount != 0);
+}
+
 static int nsim_devlink_port_function_add(struct devlink *devlink, struct 
nsim_dev *nsim_dev,
                                          struct nsim_port_function *port,
                                          struct netlink_ext_ack *extack)
 {
+       struct nsim_port_function *pf_port;
        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_port = pf_port_get(nsim_dev, port);
+       if (IS_ERR(pf_port)) {
+               NL_SET_ERR_MSG_MOD(extack, "Fail to get pf port");
+               err = PTR_ERR(pf_port);
+               goto pf_err;
+       }
 
-       port->state = DEVLINK_PORT_FUNCTION_STATE_INACTIVE;
        err = devlink_port_register(devlink, &port->dl_port, port->port_index);
        if (err)
                goto reg_err;
@@ -213,6 +291,8 @@ static int nsim_devlink_port_function_add(struct devlink 
*devlink, struct nsim_d
        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;
 }
@@ -224,12 +304,14 @@ static void nsim_devlink_port_function_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, const struct 
devlink_port_new_attrs *attrs,
@@ -266,7 +348,11 @@ int nsim_dev_devlink_port_new(struct devlink *devlink, 
const struct devlink_port
               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, port->controller, 
port->pfnum, false);
+       if (attrs->flavour == DEVLINK_PORT_FLAVOUR_PCI_PF)
+               devlink_port_attrs_pci_pf_set(&port->dl_port, port->controller, 
port->pfnum, false);
+       else
+               devlink_port_attrs_pci_sf_set(&port->dl_port, port->controller, 
port->pfnum,
+                                             port->sfnum, false);
 
        err = nsim_devlink_port_function_add(devlink, nsim_dev, port, extack);
        if (err)
@@ -333,6 +419,7 @@ void nsim_dev_port_function_disable(struct nsim_dev 
*nsim_dev)
         * 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_function_del(nsim_dev, port);
                nsim_devlink_port_function_free(nsim_dev, port);
-- 
2.26.2

Reply via email to