devlink: expose instance locking and add locked port registering
authorJakub Kicinski <kuba@kernel.org>
Tue, 15 Mar 2022 06:00:04 +0000 (23:00 -0700)
committerJakub Kicinski <kuba@kernel.org>
Wed, 16 Mar 2022 19:56:31 +0000 (12:56 -0700)
It should be familiar and beneficial to expose devlink instance
lock to the drivers. This way drivers can block devlink from
calling them during critical sections without breakneck locking.

Add port helpers, port splitting callbacks will be the first
target.

Use 'devl_' prefix for "explicitly locked" API. Initial RFC used
'__devlink' but that's too much typing.

devl_lock_is_held() is not defined without lockdep, which is
the same behavior as lockdep_is_held() itself.

Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Documentation/networking/devlink/index.rst
include/net/devlink.h
net/core/devlink.c

index 443123772f44d570ef53a87bdb91f11f4fa3fd17..c17cdb0796112cbb2fe77294a0fd0a64be25fda9 100644 (file)
@@ -4,6 +4,22 @@ Linux Devlink Documentation
 devlink is an API to expose device information and resources not directly
 related to any device class, such as chip-wide/switch-ASIC-wide configuration.
 
+Locking
+-------
+
+Driver facing APIs are currently transitioning to allow more explicit
+locking. Drivers can use the existing ``devlink_*`` set of APIs, or
+new APIs prefixed by ``devl_*``. The older APIs handle all the locking
+in devlink core, but don't allow registration of most sub-objects once
+the main devlink object is itself registered. The newer ``devl_*`` APIs assume
+the devlink instance lock is already held. Drivers can take the instance
+lock by calling ``devl_lock()``. It is also held in most of the callbacks.
+Eventually all callbacks will be invoked under the devlink instance lock,
+refer to the use of the ``DEVLINK_NL_FLAG_NO_LOCK`` flag in devlink core
+to find out which callbacks are not converted, yet.
+
+Drivers are encouraged to use the devlink instance lock for their own needs.
+
 Interface documentation
 -----------------------
 
index 8d5349d2fb68b0ef83531c091def02c10ae08c10..9de0d091aee95351251a454329d8fa892f9e93c0 100644 (file)
@@ -1479,6 +1479,17 @@ void *devlink_priv(struct devlink *devlink);
 struct devlink *priv_to_devlink(void *priv);
 struct device *devlink_to_dev(const struct devlink *devlink);
 
+/* Devlink instance explicit locking */
+void devl_lock(struct devlink *devlink);
+void devl_unlock(struct devlink *devlink);
+void devl_assert_locked(struct devlink *devlink);
+bool devl_lock_is_held(struct devlink *devlink);
+
+int devl_port_register(struct devlink *devlink,
+                      struct devlink_port *devlink_port,
+                      unsigned int port_index);
+void devl_port_unregister(struct devlink_port *devlink_port);
+
 struct ib_device;
 
 struct net *devlink_net(const struct devlink *devlink);
index fcd9f6d85cf1792eeb16222bde8cc11d5026b0d4..769e5f7fa219a186c08c14e96bf19eba49c1e9ea 100644 (file)
@@ -225,6 +225,33 @@ struct devlink *__must_check devlink_try_get(struct devlink *devlink)
        return NULL;
 }
 
+void devl_assert_locked(struct devlink *devlink)
+{
+       lockdep_assert_held(&devlink->lock);
+}
+EXPORT_SYMBOL_GPL(devl_assert_locked);
+
+#ifdef CONFIG_LOCKDEP
+/* For use in conjunction with LOCKDEP only e.g. rcu_dereference_protected() */
+bool devl_lock_is_held(struct devlink *devlink)
+{
+       return lockdep_is_held(&devlink->lock);
+}
+EXPORT_SYMBOL_GPL(devl_lock_is_held);
+#endif
+
+void devl_lock(struct devlink *devlink)
+{
+       mutex_lock(&devlink->lock);
+}
+EXPORT_SYMBOL_GPL(devl_lock);
+
+void devl_unlock(struct devlink *devlink)
+{
+       mutex_unlock(&devlink->lock);
+}
+EXPORT_SYMBOL_GPL(devl_unlock);
+
 static struct devlink *devlink_get_from_attrs(struct net *net,
                                              struct nlattr **attrs)
 {
@@ -9249,6 +9276,32 @@ static void devlink_port_type_warn_cancel(struct devlink_port *devlink_port)
        cancel_delayed_work_sync(&devlink_port->type_warn_dw);
 }
 
+int devl_port_register(struct devlink *devlink,
+                      struct devlink_port *devlink_port,
+                      unsigned int port_index)
+{
+       lockdep_assert_held(&devlink->lock);
+
+       if (devlink_port_index_exists(devlink, port_index))
+               return -EEXIST;
+
+       WARN_ON(devlink_port->devlink);
+       devlink_port->devlink = devlink;
+       devlink_port->index = port_index;
+       spin_lock_init(&devlink_port->type_lock);
+       INIT_LIST_HEAD(&devlink_port->reporter_list);
+       mutex_init(&devlink_port->reporters_lock);
+       list_add_tail(&devlink_port->list, &devlink->port_list);
+       INIT_LIST_HEAD(&devlink_port->param_list);
+       INIT_LIST_HEAD(&devlink_port->region_list);
+
+       INIT_DELAYED_WORK(&devlink_port->type_warn_dw, &devlink_port_type_warn);
+       devlink_port_type_warn_schedule(devlink_port);
+       devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(devl_port_register);
+
 /**
  *     devlink_port_register - Register devlink port
  *
@@ -9266,29 +9319,28 @@ int devlink_port_register(struct devlink *devlink,
                          struct devlink_port *devlink_port,
                          unsigned int port_index)
 {
-       mutex_lock(&devlink->lock);
-       if (devlink_port_index_exists(devlink, port_index)) {
-               mutex_unlock(&devlink->lock);
-               return -EEXIST;
-       }
+       int err;
 
-       WARN_ON(devlink_port->devlink);
-       devlink_port->devlink = devlink;
-       devlink_port->index = port_index;
-       spin_lock_init(&devlink_port->type_lock);
-       INIT_LIST_HEAD(&devlink_port->reporter_list);
-       mutex_init(&devlink_port->reporters_lock);
-       list_add_tail(&devlink_port->list, &devlink->port_list);
-       INIT_LIST_HEAD(&devlink_port->param_list);
-       INIT_LIST_HEAD(&devlink_port->region_list);
+       mutex_lock(&devlink->lock);
+       err = devl_port_register(devlink, devlink_port, port_index);
        mutex_unlock(&devlink->lock);
-       INIT_DELAYED_WORK(&devlink_port->type_warn_dw, &devlink_port_type_warn);
-       devlink_port_type_warn_schedule(devlink_port);
-       devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_NEW);
-       return 0;
+       return err;
 }
 EXPORT_SYMBOL_GPL(devlink_port_register);
 
+void devl_port_unregister(struct devlink_port *devlink_port)
+{
+       lockdep_assert_held(&devlink_port->devlink->lock);
+
+       devlink_port_type_warn_cancel(devlink_port);
+       devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL);
+       list_del(&devlink_port->list);
+       WARN_ON(!list_empty(&devlink_port->reporter_list));
+       WARN_ON(!list_empty(&devlink_port->region_list));
+       mutex_destroy(&devlink_port->reporters_lock);
+}
+EXPORT_SYMBOL_GPL(devl_port_unregister);
+
 /**
  *     devlink_port_unregister - Unregister devlink port
  *
@@ -9298,14 +9350,9 @@ void devlink_port_unregister(struct devlink_port *devlink_port)
 {
        struct devlink *devlink = devlink_port->devlink;
 
-       devlink_port_type_warn_cancel(devlink_port);
-       devlink_port_notify(devlink_port, DEVLINK_CMD_PORT_DEL);
        mutex_lock(&devlink->lock);
-       list_del(&devlink_port->list);
+       devl_port_unregister(devlink_port);
        mutex_unlock(&devlink->lock);
-       WARN_ON(!list_empty(&devlink_port->reporter_list));
-       WARN_ON(!list_empty(&devlink_port->region_list));
-       mutex_destroy(&devlink_port->reporters_lock);
 }
 EXPORT_SYMBOL_GPL(devlink_port_unregister);