From: Jiri Pirko <[email protected]>

Allow devlink_alloc_ns() to be called with dev=NULL to support
device-less devlink instances. When dev is NULL, the instance is
identified over netlink using "devlink_index" as bus_name and
the decimal index value as dev_name.

Signed-off-by: Jiri Pirko <[email protected]>
---
v1->v2:
- moved DEVLINK_INDEX_BUS_NAME definition to patch #5
- added comment to dev arg that it can be NULL
- fixed the index sprintf for dev-less
---
 net/devlink/core.c          | 25 +++++++++++++++++++------
 net/devlink/dev.c           | 11 +++++++----
 net/devlink/devl_internal.h |  4 ++--
 net/devlink/port.c          | 14 +++++++++-----
 4 files changed, 37 insertions(+), 17 deletions(-)

diff --git a/net/devlink/core.c b/net/devlink/core.c
index 85ea5856d523..e931e66aa3a2 100644
--- a/net/devlink/core.c
+++ b/net/devlink/core.c
@@ -331,7 +331,10 @@ static void devlink_release(struct work_struct *work)
 
        mutex_destroy(&devlink->lock);
        lockdep_unregister_key(&devlink->lock_key);
-       put_device(devlink->dev);
+       if (devlink->dev)
+               put_device(devlink->dev);
+       else
+               kfree(devlink->dev_name);
        kvfree(devlink);
 }
 
@@ -433,7 +436,7 @@ EXPORT_SYMBOL_GPL(devlink_unregister);
  *     @ops: ops
  *     @priv_size: size of user private data
  *     @net: net namespace
- *     @dev: parent device
+ *     @dev: parent device, or NULL for a device-less devlink instance
  *
  *     Allocate new devlink instance resources, including devlink index
  *     and name.
@@ -446,7 +449,7 @@ struct devlink *devlink_alloc_ns(const struct devlink_ops 
*ops,
        static u32 last_id;
        int ret;
 
-       WARN_ON(!ops || !dev);
+       WARN_ON(!ops);
        if (!devlink_reload_actions_valid(ops))
                return NULL;
 
@@ -459,9 +462,17 @@ struct devlink *devlink_alloc_ns(const struct devlink_ops 
*ops,
        if (ret < 0)
                goto err_xa_alloc;
 
-       devlink->dev = get_device(dev);
-       devlink->bus_name = dev->bus->name;
-       devlink->dev_name = dev_name(dev);
+       if (dev) {
+               devlink->dev = get_device(dev);
+               devlink->bus_name = dev->bus->name;
+               devlink->dev_name = dev_name(dev);
+       } else {
+               devlink->bus_name = DEVLINK_INDEX_BUS_NAME;
+               devlink->dev_name = kasprintf(GFP_KERNEL, "%u", devlink->index);
+               if (!devlink->dev_name)
+                       goto err_kasprintf;
+       }
+
        devlink->ops = ops;
        xa_init_flags(&devlink->ports, XA_FLAGS_ALLOC);
        xa_init_flags(&devlink->params, XA_FLAGS_ALLOC);
@@ -486,6 +497,8 @@ struct devlink *devlink_alloc_ns(const struct devlink_ops 
*ops,
 
        return devlink;
 
+err_kasprintf:
+       xa_erase(&devlinks, devlink->index);
 err_xa_alloc:
        kvfree(devlink);
        return NULL;
diff --git a/net/devlink/dev.c b/net/devlink/dev.c
index e3a36de4f4ae..b63597312bbd 100644
--- a/net/devlink/dev.c
+++ b/net/devlink/dev.c
@@ -453,7 +453,8 @@ int devlink_reload(struct devlink *devlink, struct net 
*dest_net,
         * (e.g., PCI reset) and to close possible races between these
         * operations and probe/remove.
         */
-       device_lock_assert(devlink->dev);
+       if (devlink->dev)
+               device_lock_assert(devlink->dev);
 
        memcpy(remote_reload_stats, devlink->stats.remote_reload_stats,
               sizeof(remote_reload_stats));
@@ -892,9 +893,11 @@ devlink_nl_info_fill(struct sk_buff *msg, struct devlink 
*devlink,
                        goto err_cancel_msg;
        }
 
-       err = devlink_nl_driver_info_get(dev->driver, &req);
-       if (err)
-               goto err_cancel_msg;
+       if (dev) {
+               err = devlink_nl_driver_info_get(dev->driver, &req);
+               if (err)
+                       goto err_cancel_msg;
+       }
 
        genlmsg_end(msg, hdr);
        return 0;
diff --git a/net/devlink/devl_internal.h b/net/devlink/devl_internal.h
index 67425c5d8cfc..89d08fd511cb 100644
--- a/net/devlink/devl_internal.h
+++ b/net/devlink/devl_internal.h
@@ -107,7 +107,7 @@ static inline bool devl_is_registered(struct devlink 
*devlink)
 
 static inline void devl_dev_lock(struct devlink *devlink, bool dev_lock)
 {
-       if (dev_lock)
+       if (dev_lock && devlink->dev)
                device_lock(devlink->dev);
        devl_lock(devlink);
 }
@@ -115,7 +115,7 @@ static inline void devl_dev_lock(struct devlink *devlink, 
bool dev_lock)
 static inline void devl_dev_unlock(struct devlink *devlink, bool dev_lock)
 {
        devl_unlock(devlink);
-       if (dev_lock)
+       if (dev_lock && devlink->dev)
                device_unlock(devlink->dev);
 }
 
diff --git a/net/devlink/port.c b/net/devlink/port.c
index 1d4a79c6d4d3..f19b690ebe7e 100644
--- a/net/devlink/port.c
+++ b/net/devlink/port.c
@@ -976,7 +976,9 @@ static void devlink_port_type_warn(struct work_struct *work)
        struct devlink_port *port = container_of(to_delayed_work(work),
                                                 struct devlink_port,
                                                 type_warn_dw);
-       dev_warn(port->devlink->dev, "Type was not set for devlink port.");
+       if (port->devlink->dev)
+               dev_warn(port->devlink->dev,
+                        "Type was not set for devlink port.");
 }
 
 static bool devlink_port_type_should_warn(struct devlink_port *devlink_port)
@@ -1242,9 +1244,10 @@ static void __devlink_port_type_set(struct devlink_port 
*devlink_port,
  */
 void devlink_port_type_eth_set(struct devlink_port *devlink_port)
 {
-       dev_warn(devlink_port->devlink->dev,
-                "devlink port type for port %d set to Ethernet without a 
software interface reference, device type not supported by the kernel?\n",
-                devlink_port->index);
+       if (devlink_port->devlink->dev)
+               dev_warn(devlink_port->devlink->dev,
+                        "devlink port type for port %d set to Ethernet without 
a software interface reference, device type not supported by the kernel?\n",
+                        devlink_port->index);
        __devlink_port_type_set(devlink_port, DEVLINK_PORT_TYPE_ETH, NULL);
 }
 EXPORT_SYMBOL_GPL(devlink_port_type_eth_set);
@@ -1272,7 +1275,8 @@ EXPORT_SYMBOL_GPL(devlink_port_type_ib_set);
  */
 void devlink_port_type_clear(struct devlink_port *devlink_port)
 {
-       if (devlink_port->type == DEVLINK_PORT_TYPE_ETH)
+       if (devlink_port->type == DEVLINK_PORT_TYPE_ETH &&
+           devlink_port->devlink->dev)
                dev_warn(devlink_port->devlink->dev,
                         "devlink port type for port %d cleared without a 
software interface reference, device type not supported by the kernel?\n",
                         devlink_port->index);
-- 
2.51.1


Reply via email to