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

Reply via email to