Merge tag 'driver-core-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 21 Mar 2024 20:34:15 +0000 (13:34 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 21 Mar 2024 20:34:15 +0000 (13:34 -0700)
Pull driver core updates from Greg KH:
 "Here is the "big" set of driver core and kernfs changes for 6.9-rc1.

  Nothing all that crazy here, just some good updates that include:

   - automatic attribute group hiding from Dan Williams (he fixed up my
     horrible attempt at doing this.)

   - kobject lock contention fixes from Eric Dumazet

   - driver core cleanups from Andy

   - kernfs rcu work from Tejun

   - fw_devlink changes to resolve some reported issues

   - other minor changes, all details in the shortlog

  All of these have been in linux-next for a long time with no reported
  issues"

* tag 'driver-core-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (28 commits)
  device: core: Log warning for devices pending deferred probe on timeout
  driver: core: Use dev_* instead of pr_* so device metadata is added
  driver: core: Log probe failure as error and with device metadata
  of: property: fw_devlink: Add support for "post-init-providers" property
  driver core: Add FWLINK_FLAG_IGNORE to completely ignore a fwnode link
  driver core: Adds flags param to fwnode_link_add()
  debugfs: fix wait/cancellation handling during remove
  device property: Don't use "proxy" headers
  device property: Move enum dev_dma_attr to fwnode.h
  driver core: Move fw_devlink stuff to where it belongs
  driver core: Drop unneeded 'extern' keyword in fwnode.h
  firmware_loader: Suppress warning on FW_OPT_NO_WARN flag
  sysfs:Addresses documentation in sysfs_merge_group and sysfs_unmerge_group.
  firmware_loader: introduce __free() cleanup hanler
  platform-msi: Remove usage of the deprecated ida_simple_xx() API
  sysfs: Introduce DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE()
  sysfs: Document new "group visible" helpers
  sysfs: Fix crash on empty group attributes array
  sysfs: Introduce a mechanism to hide static attribute_groups
  sysfs: Introduce a mechanism to hide static attribute_groups
  ...

25 files changed:
drivers/base/component.c
drivers/base/core.c
drivers/base/cpu.c
drivers/base/dd.c
drivers/base/firmware_loader/main.c
drivers/base/platform-msi.c
drivers/base/property.c
drivers/base/swnode.c
drivers/firmware/efi/sysfb_efi.c
drivers/of/property.c
fs/debugfs/inode.c
fs/kernfs/dir.c
fs/kernfs/file.c
fs/kernfs/kernfs-internal.h
fs/sysfs/group.c
include/linux/cpu.h
include/linux/firmware.h
include/linux/fwnode.h
include/linux/kernfs.h
include/linux/kobject.h
include/linux/property.h
include/linux/sysfs.h
kernel/ksysfs.c
kernel/workqueue.c
lib/kobject_uevent.c

index 7dbf14a1d915779e0237b996f495b49faaa3e5a6..741497324d78ae02afb124f50fa33acc857126d2 100644 (file)
@@ -751,7 +751,7 @@ static int __component_add(struct device *dev, const struct component_ops *ops,
  * component_bind_all(). See also &struct component_ops.
  *
  * @subcomponent must be nonzero and is used to differentiate between multiple
- * components registerd on the same device @dev. These components are match
+ * components registered on the same device @dev. These components are match
  * using component_match_add_typed().
  *
  * The component needs to be unregistered at driver unload/disconnect by
@@ -781,7 +781,7 @@ EXPORT_SYMBOL_GPL(component_add_typed);
  * The component needs to be unregistered at driver unload/disconnect by
  * calling component_del().
  *
- * See also component_add_typed() for a variant that allows multipled different
+ * See also component_add_typed() for a variant that allows multiple different
  * components on the same device.
  */
 int component_add(struct device *dev, const struct component_ops *ops)
index 9828da9b933cb7511756d15ec8be2ebbd14f9e44..b93f3c5716aeeaa001651962f0af2c73bdfd5685 100644 (file)
@@ -92,12 +92,13 @@ static int __fwnode_link_add(struct fwnode_handle *con,
        return 0;
 }
 
-int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup)
+int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup,
+                   u8 flags)
 {
        int ret;
 
        mutex_lock(&fwnode_link_lock);
-       ret = __fwnode_link_add(con, sup, 0);
+       ret = __fwnode_link_add(con, sup, flags);
        mutex_unlock(&fwnode_link_lock);
        return ret;
 }
@@ -1011,7 +1012,8 @@ static struct fwnode_handle *fwnode_links_check_suppliers(
                return NULL;
 
        list_for_each_entry(link, &fwnode->suppliers, c_hook)
-               if (!(link->flags & FWLINK_FLAG_CYCLE))
+               if (!(link->flags &
+                     (FWLINK_FLAG_CYCLE | FWLINK_FLAG_IGNORE)))
                        return link->supplier;
 
        return NULL;
@@ -1871,6 +1873,7 @@ static void fw_devlink_unblock_consumers(struct device *dev)
        device_links_write_unlock();
 }
 
+#define get_dev_from_fwnode(fwnode)    get_device((fwnode)->dev)
 
 static bool fwnode_init_without_drv(struct fwnode_handle *fwnode)
 {
@@ -1901,6 +1904,63 @@ static bool fwnode_ancestor_init_without_drv(struct fwnode_handle *fwnode)
        return false;
 }
 
+/**
+ * fwnode_is_ancestor_of - Test if @ancestor is ancestor of @child
+ * @ancestor: Firmware which is tested for being an ancestor
+ * @child: Firmware which is tested for being the child
+ *
+ * A node is considered an ancestor of itself too.
+ *
+ * Return: true if @ancestor is an ancestor of @child. Otherwise, returns false.
+ */
+static bool fwnode_is_ancestor_of(const struct fwnode_handle *ancestor,
+                                 const struct fwnode_handle *child)
+{
+       struct fwnode_handle *parent;
+
+       if (IS_ERR_OR_NULL(ancestor))
+               return false;
+
+       if (child == ancestor)
+               return true;
+
+       fwnode_for_each_parent_node(child, parent) {
+               if (parent == ancestor) {
+                       fwnode_handle_put(parent);
+                       return true;
+               }
+       }
+       return false;
+}
+
+/**
+ * fwnode_get_next_parent_dev - Find device of closest ancestor fwnode
+ * @fwnode: firmware node
+ *
+ * Given a firmware node (@fwnode), this function finds its closest ancestor
+ * firmware node that has a corresponding struct device and returns that struct
+ * device.
+ *
+ * The caller is responsible for calling put_device() on the returned device
+ * pointer.
+ *
+ * Return: a pointer to the device of the @fwnode's closest ancestor.
+ */
+static struct device *fwnode_get_next_parent_dev(const struct fwnode_handle *fwnode)
+{
+       struct fwnode_handle *parent;
+       struct device *dev;
+
+       fwnode_for_each_parent_node(fwnode, parent) {
+               dev = get_dev_from_fwnode(parent);
+               if (dev) {
+                       fwnode_handle_put(parent);
+                       return dev;
+               }
+       }
+       return NULL;
+}
+
 /**
  * __fw_devlink_relax_cycles - Relax and mark dependency cycles.
  * @con: Potential consumer device.
@@ -1962,6 +2022,9 @@ static bool __fw_devlink_relax_cycles(struct device *con,
        }
 
        list_for_each_entry(link, &sup_handle->suppliers, c_hook) {
+               if (link->flags & FWLINK_FLAG_IGNORE)
+                       continue;
+
                if (__fw_devlink_relax_cycles(con, link->supplier)) {
                        __fwnode_link_cycle(link);
                        ret = true;
@@ -2040,6 +2103,9 @@ static int fw_devlink_create_devlink(struct device *con,
        int ret = 0;
        u32 flags;
 
+       if (link->flags & FWLINK_FLAG_IGNORE)
+               return 0;
+
        if (con->fwnode == link->consumer)
                flags = fw_devlink_get_flags(link->flags);
        else
index f5a6bffce5188090a6f0be2775e74ffd85ffdf59..56fba44ba391adb432ca0b85e552b3ee95ddbad6 100644 (file)
@@ -366,7 +366,7 @@ static int cpu_uevent(const struct device *dev, struct kobj_uevent_env *env)
 }
 #endif
 
-struct bus_type cpu_subsys = {
+const struct bus_type cpu_subsys = {
        .name = "cpu",
        .dev_name = "cpu",
        .match = cpu_subsys_match,
index 85152537dbf12d005236cf18c721c51f62a5ebd8..83d352394fdf48756180cdf1bb25cf1db9bc5113 100644 (file)
@@ -313,7 +313,7 @@ static void deferred_probe_timeout_work_func(struct work_struct *work)
 
        mutex_lock(&deferred_probe_mutex);
        list_for_each_entry(p, &deferred_probe_pending_list, deferred_probe)
-               dev_info(p->device, "deferred probe pending: %s", p->deferred_probe_reason ?: "(reason unknown)\n");
+               dev_warn(p->device, "deferred probe pending: %s", p->deferred_probe_reason ?: "(reason unknown)\n");
        mutex_unlock(&deferred_probe_mutex);
 
        fw_devlink_probing_done();
@@ -397,13 +397,12 @@ bool device_is_bound(struct device *dev)
 static void driver_bound(struct device *dev)
 {
        if (device_is_bound(dev)) {
-               pr_warn("%s: device %s already bound\n",
-                       __func__, kobject_name(&dev->kobj));
+               dev_warn(dev, "%s: device already bound\n", __func__);
                return;
        }
 
-       pr_debug("driver: '%s': %s: bound to device '%s'\n", dev->driver->name,
-                __func__, dev_name(dev));
+       dev_dbg(dev, "driver: '%s': %s: bound to device\n", dev->driver->name,
+               __func__);
 
        klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
        device_links_driver_bound(dev);
@@ -587,13 +586,13 @@ static int call_driver_probe(struct device *dev, struct device_driver *drv)
                break;
        case -ENODEV:
        case -ENXIO:
-               pr_debug("%s: probe of %s rejects match %d\n",
-                        drv->name, dev_name(dev), ret);
+               dev_dbg(dev, "probe with driver %s rejects match %d\n",
+                       drv->name, ret);
                break;
        default:
                /* driver matched but the probe failed */
-               pr_warn("%s: probe of %s failed with error %d\n",
-                       drv->name, dev_name(dev), ret);
+               dev_err(dev, "probe with driver %s failed with error %d\n",
+                       drv->name, ret);
                break;
        }
 
@@ -620,8 +619,8 @@ static int really_probe(struct device *dev, struct device_driver *drv)
        if (link_ret == -EPROBE_DEFER)
                return link_ret;
 
-       pr_debug("bus: '%s': %s: probing driver %s with device %s\n",
-                drv->bus->name, __func__, drv->name, dev_name(dev));
+       dev_dbg(dev, "bus: '%s': %s: probing driver %s with device\n",
+               drv->bus->name, __func__, drv->name);
        if (!list_empty(&dev->devres_head)) {
                dev_crit(dev, "Resources present before probing\n");
                ret = -EBUSY;
@@ -644,8 +643,7 @@ re_probe:
 
        ret = driver_sysfs_add(dev);
        if (ret) {
-               pr_err("%s: driver_sysfs_add(%s) failed\n",
-                      __func__, dev_name(dev));
+               dev_err(dev, "%s: driver_sysfs_add failed\n", __func__);
                goto sysfs_failed;
        }
 
@@ -706,8 +704,8 @@ re_probe:
                dev->pm_domain->sync(dev);
 
        driver_bound(dev);
-       pr_debug("bus: '%s': %s: bound device %s to driver %s\n",
-                drv->bus->name, __func__, dev_name(dev), drv->name);
+       dev_dbg(dev, "bus: '%s': %s: bound device to driver %s\n",
+               drv->bus->name, __func__, drv->name);
        goto done;
 
 dev_sysfs_state_synced_failed:
@@ -786,8 +784,8 @@ static int __driver_probe_device(struct device_driver *drv, struct device *dev)
                return -EBUSY;
 
        dev->can_match = true;
-       pr_debug("bus: '%s': %s: matched device %s with driver %s\n",
-                drv->bus->name, __func__, dev_name(dev), drv->name);
+       dev_dbg(dev, "bus: '%s': %s: matched device with driver %s\n",
+               drv->bus->name, __func__, drv->name);
 
        pm_runtime_get_suppliers(dev);
        if (dev->parent)
index ea28102d421ebf2d3d611d77547816e48368ab5c..da8ca01d011c3f3694a381e1c98d0b3a8301663b 100644 (file)
@@ -551,12 +551,16 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
                                                       file_size_ptr,
                                                       READING_FIRMWARE);
                if (rc < 0) {
-                       if (rc != -ENOENT)
-                               dev_warn(device, "loading %s failed with error %d\n",
-                                        path, rc);
-                       else
-                               dev_dbg(device, "loading %s failed for no such file or directory.\n",
-                                        path);
+                       if (!(fw_priv->opt_flags & FW_OPT_NO_WARN)) {
+                               if (rc != -ENOENT)
+                                       dev_warn(device,
+                                                "loading %s failed with error %d\n",
+                                                path, rc);
+                               else
+                                       dev_dbg(device,
+                                               "loading %s failed for no such file or directory.\n",
+                                               path);
+                       }
                        continue;
                }
                size = rc;
index 0d01890160f3f4d0bc9bae5af2c49d44e522954c..11f5fdf65b9ef604ab574e476a1a09f89af24552 100644 (file)
@@ -174,8 +174,8 @@ static int platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec,
        if (!datap)
                return -ENOMEM;
 
-       datap->devid = ida_simple_get(&platform_msi_devid_ida,
-                                     0, 1 << DEV_ID_SHIFT, GFP_KERNEL);
+       datap->devid = ida_alloc_max(&platform_msi_devid_ida,
+                                    (1 << DEV_ID_SHIFT) - 1, GFP_KERNEL);
        if (datap->devid < 0) {
                err = datap->devid;
                kfree(datap);
@@ -193,7 +193,7 @@ static void platform_msi_free_priv_data(struct device *dev)
        struct platform_msi_priv_data *data = dev->msi.data->platform_data;
 
        dev->msi.data->platform_data = NULL;
-       ida_simple_remove(&platform_msi_devid_ida, data->devid);
+       ida_free(&platform_msi_devid_ida, data->devid);
        kfree(data);
 }
 
index a1b01ab4205280df05759c5139e49b34f6f64a82..7324a704a9a118322df02b061380fdb0fa3cdc21 100644 (file)
@@ -7,15 +7,16 @@
  *          Mika Westerberg <mika.westerberg@linux.intel.com>
  */
 
-#include <linux/acpi.h>
+#include <linux/device.h>
+#include <linux/err.h>
 #include <linux/export.h>
-#include <linux/kernel.h>
+#include <linux/kconfig.h>
 #include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_graph.h>
-#include <linux/of_irq.h>
 #include <linux/property.h>
 #include <linux/phy.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/types.h>
 
 struct fwnode_handle *__dev_fwnode(struct device *dev)
 {
@@ -699,34 +700,6 @@ struct fwnode_handle *fwnode_get_next_parent(struct fwnode_handle *fwnode)
 }
 EXPORT_SYMBOL_GPL(fwnode_get_next_parent);
 
-/**
- * fwnode_get_next_parent_dev - Find device of closest ancestor fwnode
- * @fwnode: firmware node
- *
- * Given a firmware node (@fwnode), this function finds its closest ancestor
- * firmware node that has a corresponding struct device and returns that struct
- * device.
- *
- * The caller is responsible for calling put_device() on the returned device
- * pointer.
- *
- * Return: a pointer to the device of the @fwnode's closest ancestor.
- */
-struct device *fwnode_get_next_parent_dev(const struct fwnode_handle *fwnode)
-{
-       struct fwnode_handle *parent;
-       struct device *dev;
-
-       fwnode_for_each_parent_node(fwnode, parent) {
-               dev = get_dev_from_fwnode(parent);
-               if (dev) {
-                       fwnode_handle_put(parent);
-                       return dev;
-               }
-       }
-       return NULL;
-}
-
 /**
  * fwnode_count_parents - Return the number of parents a node has
  * @fwnode: The node the parents of which are to be counted
@@ -773,34 +746,6 @@ struct fwnode_handle *fwnode_get_nth_parent(struct fwnode_handle *fwnode,
 }
 EXPORT_SYMBOL_GPL(fwnode_get_nth_parent);
 
-/**
- * fwnode_is_ancestor_of - Test if @ancestor is ancestor of @child
- * @ancestor: Firmware which is tested for being an ancestor
- * @child: Firmware which is tested for being the child
- *
- * A node is considered an ancestor of itself too.
- *
- * Return: true if @ancestor is an ancestor of @child. Otherwise, returns false.
- */
-bool fwnode_is_ancestor_of(const struct fwnode_handle *ancestor, const struct fwnode_handle *child)
-{
-       struct fwnode_handle *parent;
-
-       if (IS_ERR_OR_NULL(ancestor))
-               return false;
-
-       if (child == ancestor)
-               return true;
-
-       fwnode_for_each_parent_node(child, parent) {
-               if (parent == ancestor) {
-                       fwnode_handle_put(parent);
-                       return true;
-               }
-       }
-       return false;
-}
-
 /**
  * fwnode_get_next_child_node - Return the next child node handle for a node
  * @fwnode: Firmware node to find the next child node for.
index 36512fb75a201c1a1ab5aa68b694667b56b96036..eb6eb25b343bafaa9c03f881b98a528305fa6093 100644 (file)
@@ -6,10 +6,21 @@
  * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
  */
 
+#include <linux/container_of.h>
 #include <linux/device.h>
-#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/export.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/kobject.h>
+#include <linux/kstrtox.h>
+#include <linux/list.h>
 #include <linux/property.h>
 #include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
 
 #include "base.h"
 
index 456d0e5eaf78b595a66c622103b719e0fd2d3a69..cc807ed35aedf7737a3585b3b5153408364d2765 100644 (file)
@@ -336,7 +336,7 @@ static int efifb_add_links(struct fwnode_handle *fwnode)
        if (!sup_np)
                return 0;
 
-       fwnode_link_add(fwnode, of_fwnode_handle(sup_np));
+       fwnode_link_add(fwnode, of_fwnode_handle(sup_np), 0);
        of_node_put(sup_np);
 
        return 0;
index f61de622f870b70d4bf1525a813245b5a16f7c98..a6358ee99b74b99a90df38c63e08943b628a7b80 100644 (file)
@@ -1072,7 +1072,8 @@ of_fwnode_device_get_match_data(const struct fwnode_handle *fwnode,
 }
 
 static void of_link_to_phandle(struct device_node *con_np,
-                             struct device_node *sup_np)
+                             struct device_node *sup_np,
+                             u8 flags)
 {
        struct device_node *tmp_np = of_node_get(sup_np);
 
@@ -1091,7 +1092,7 @@ static void of_link_to_phandle(struct device_node *con_np,
                tmp_np = of_get_next_parent(tmp_np);
        }
 
-       fwnode_link_add(of_fwnode_handle(con_np), of_fwnode_handle(sup_np));
+       fwnode_link_add(of_fwnode_handle(con_np), of_fwnode_handle(sup_np), flags);
 }
 
 /**
@@ -1204,6 +1205,8 @@ static struct device_node *parse_##fname(struct device_node *np,       \
  *              to a struct device, implement this ops so fw_devlink can use it
  *              to find the true consumer.
  * @optional: Describes whether a supplier is mandatory or not
+ * @fwlink_flags: Optional fwnode link flags to use when creating a fwnode link
+ *               for this property.
  *
  * Returns:
  * parse_prop() return values are
@@ -1216,6 +1219,7 @@ struct supplier_bindings {
                                          const char *prop_name, int index);
        struct device_node *(*get_con_dev)(struct device_node *np);
        bool optional;
+       u8 fwlink_flags;
 };
 
 DEFINE_SIMPLE_PROP(clocks, "clocks", "#clock-cells")
@@ -1247,6 +1251,7 @@ DEFINE_SIMPLE_PROP(leds, "leds", NULL)
 DEFINE_SIMPLE_PROP(backlight, "backlight", NULL)
 DEFINE_SIMPLE_PROP(panel, "panel", NULL)
 DEFINE_SIMPLE_PROP(msi_parent, "msi-parent", "#msi-cells")
+DEFINE_SIMPLE_PROP(post_init_providers, "post-init-providers", NULL)
 DEFINE_SUFFIX_PROP(regulators, "-supply", NULL)
 DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells")
 
@@ -1357,6 +1362,10 @@ static const struct supplier_bindings of_supplier_bindings[] = {
        { .parse_prop = parse_regulators, },
        { .parse_prop = parse_gpio, },
        { .parse_prop = parse_gpios, },
+       {
+               .parse_prop = parse_post_init_providers,
+               .fwlink_flags = FWLINK_FLAG_IGNORE,
+       },
        {}
 };
 
@@ -1401,7 +1410,7 @@ static int of_link_property(struct device_node *con_np, const char *prop_name)
                                        : of_node_get(con_np);
                        matched = true;
                        i++;
-                       of_link_to_phandle(con_dev_np, phandle);
+                       of_link_to_phandle(con_dev_np, phandle, s->fwlink_flags);
                        of_node_put(phandle);
                        of_node_put(con_dev_np);
                }
index 034a617cb1a5e777d5e254bd3aa81b368c566ed2..a40da006543361c890aa5038a266e0300baaf4ec 100644 (file)
@@ -751,13 +751,28 @@ static void __debugfs_file_removed(struct dentry *dentry)
        if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)
                return;
 
-       /* if we hit zero, just wait for all to finish */
-       if (!refcount_dec_and_test(&fsd->active_users)) {
-               wait_for_completion(&fsd->active_users_drained);
+       /* if this was the last reference, we're done */
+       if (refcount_dec_and_test(&fsd->active_users))
                return;
-       }
 
-       /* if we didn't hit zero, try to cancel any we can */
+       /*
+        * If there's still a reference, the code that obtained it can
+        * be in different states:
+        *  - The common case of not using cancellations, or already
+        *    after debugfs_leave_cancellation(), where we just need
+        *    to wait for debugfs_file_put() which signals the completion;
+        *  - inside a cancellation section, i.e. between
+        *    debugfs_enter_cancellation() and debugfs_leave_cancellation(),
+        *    in which case we need to trigger the ->cancel() function,
+        *    and then wait for debugfs_file_put() just like in the
+        *    previous case;
+        *  - before debugfs_enter_cancellation() (but obviously after
+        *    debugfs_file_get()), in which case we may not see the
+        *    cancellation in the list on the first round of the loop,
+        *    but debugfs_enter_cancellation() signals the completion
+        *    after adding it, so this code gets woken up to call the
+        *    ->cancel() function.
+        */
        while (refcount_read(&fsd->active_users)) {
                struct debugfs_cancellation *c;
 
index bce1d7ac95caaa6ae48ba62c094d43c9da27298e..458519e416fe75e97cc454a716d29c06b3586b56 100644 (file)
@@ -529,6 +529,20 @@ void kernfs_get(struct kernfs_node *kn)
 }
 EXPORT_SYMBOL_GPL(kernfs_get);
 
+static void kernfs_free_rcu(struct rcu_head *rcu)
+{
+       struct kernfs_node *kn = container_of(rcu, struct kernfs_node, rcu);
+
+       kfree_const(kn->name);
+
+       if (kn->iattr) {
+               simple_xattrs_free(&kn->iattr->xattrs, NULL);
+               kmem_cache_free(kernfs_iattrs_cache, kn->iattr);
+       }
+
+       kmem_cache_free(kernfs_node_cache, kn);
+}
+
 /**
  * kernfs_put - put a reference count on a kernfs_node
  * @kn: the target kernfs_node
@@ -557,16 +571,11 @@ void kernfs_put(struct kernfs_node *kn)
        if (kernfs_type(kn) == KERNFS_LINK)
                kernfs_put(kn->symlink.target_kn);
 
-       kfree_const(kn->name);
-
-       if (kn->iattr) {
-               simple_xattrs_free(&kn->iattr->xattrs, NULL);
-               kmem_cache_free(kernfs_iattrs_cache, kn->iattr);
-       }
        spin_lock(&kernfs_idr_lock);
        idr_remove(&root->ino_idr, (u32)kernfs_ino(kn));
        spin_unlock(&kernfs_idr_lock);
-       kmem_cache_free(kernfs_node_cache, kn);
+
+       call_rcu(&kn->rcu, kernfs_free_rcu);
 
        kn = parent;
        if (kn) {
@@ -575,7 +584,7 @@ void kernfs_put(struct kernfs_node *kn)
        } else {
                /* just released the root kn, free @root too */
                idr_destroy(&root->ino_idr);
-               kfree(root);
+               kfree_rcu(root, rcu);
        }
 }
 EXPORT_SYMBOL_GPL(kernfs_put);
@@ -715,7 +724,7 @@ struct kernfs_node *kernfs_find_and_get_node_by_id(struct kernfs_root *root,
        ino_t ino = kernfs_id_ino(id);
        u32 gen = kernfs_id_gen(id);
 
-       spin_lock(&kernfs_idr_lock);
+       rcu_read_lock();
 
        kn = idr_find(&root->ino_idr, (u32)ino);
        if (!kn)
@@ -739,10 +748,10 @@ struct kernfs_node *kernfs_find_and_get_node_by_id(struct kernfs_root *root,
        if (unlikely(!__kernfs_active(kn) || !atomic_inc_not_zero(&kn->count)))
                goto err_unlock;
 
-       spin_unlock(&kernfs_idr_lock);
+       rcu_read_unlock();
        return kn;
 err_unlock:
-       spin_unlock(&kernfs_idr_lock);
+       rcu_read_unlock();
        return NULL;
 }
 
index ffa4565c275a7a1f9dfeb5075ccc82261c971c29..e9df2f87072c687073abe9625e66886934497a02 100644 (file)
@@ -483,9 +483,11 @@ static int kernfs_fop_mmap(struct file *file, struct vm_area_struct *vma)
                goto out_put;
 
        rc = 0;
-       of->mmapped = true;
-       of_on(of)->nr_mmapped++;
-       of->vm_ops = vma->vm_ops;
+       if (!of->mmapped) {
+               of->mmapped = true;
+               of_on(of)->nr_mmapped++;
+               of->vm_ops = vma->vm_ops;
+       }
        vma->vm_ops = &kernfs_vm_ops;
 out_put:
        kernfs_put_active(of->kn);
index 237f2764b9412d65f2db6be01329d5565169a327..b42ee6547cdc1cfb1be7bb30ff890ce2d91e0777 100644 (file)
@@ -49,6 +49,8 @@ struct kernfs_root {
        struct rw_semaphore     kernfs_rwsem;
        struct rw_semaphore     kernfs_iattr_rwsem;
        struct rw_semaphore     kernfs_supers_rwsem;
+
+       struct rcu_head         rcu;
 };
 
 /* +1 to avoid triggering overflow warning when negating it */
index 138676463336c7db9398748a2a516033afa141cc..d22ad67a0f3291f4702f494939528d5d13c31fae 100644 (file)
@@ -31,6 +31,17 @@ static void remove_files(struct kernfs_node *parent,
                        kernfs_remove_by_name(parent, (*bin_attr)->attr.name);
 }
 
+static umode_t __first_visible(const struct attribute_group *grp, struct kobject *kobj)
+{
+       if (grp->attrs && grp->attrs[0] && grp->is_visible)
+               return grp->is_visible(kobj, grp->attrs[0], 0);
+
+       if (grp->bin_attrs && grp->bin_attrs[0] && grp->is_bin_visible)
+               return grp->is_bin_visible(kobj, grp->bin_attrs[0], 0);
+
+       return 0;
+}
+
 static int create_files(struct kernfs_node *parent, struct kobject *kobj,
                        kuid_t uid, kgid_t gid,
                        const struct attribute_group *grp, int update)
@@ -52,6 +63,7 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj,
                                kernfs_remove_by_name(parent, (*attr)->name);
                        if (grp->is_visible) {
                                mode = grp->is_visible(kobj, *attr, i);
+                               mode &= ~SYSFS_GROUP_INVISIBLE;
                                if (!mode)
                                        continue;
                        }
@@ -81,6 +93,7 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj,
                                                (*bin_attr)->attr.name);
                        if (grp->is_bin_visible) {
                                mode = grp->is_bin_visible(kobj, *bin_attr, i);
+                               mode &= ~SYSFS_GROUP_INVISIBLE;
                                if (!mode)
                                        continue;
                        }
@@ -127,16 +140,31 @@ static int internal_create_group(struct kobject *kobj, int update,
 
        kobject_get_ownership(kobj, &uid, &gid);
        if (grp->name) {
+               umode_t mode = __first_visible(grp, kobj);
+
+               if (mode & SYSFS_GROUP_INVISIBLE)
+                       mode = 0;
+               else
+                       mode = S_IRWXU | S_IRUGO | S_IXUGO;
+
                if (update) {
                        kn = kernfs_find_and_get(kobj->sd, grp->name);
                        if (!kn) {
-                               pr_warn("Can't update unknown attr grp name: %s/%s\n",
-                                       kobj->name, grp->name);
-                               return -EINVAL;
+                               pr_debug("attr grp %s/%s not created yet\n",
+                                        kobj->name, grp->name);
+                               /* may have been invisible prior to this update */
+                               update = 0;
+                       } else if (!mode) {
+                               sysfs_remove_group(kobj, grp);
+                               kernfs_put(kn);
+                               return 0;
                        }
-               } else {
-                       kn = kernfs_create_dir_ns(kobj->sd, grp->name,
-                                                 S_IRWXU | S_IRUGO | S_IXUGO,
+               }
+
+               if (!update) {
+                       if (!mode)
+                               return 0;
+                       kn = kernfs_create_dir_ns(kobj->sd, grp->name, mode,
                                                  uid, gid, kobj, NULL);
                        if (IS_ERR(kn)) {
                                if (PTR_ERR(kn) == -EEXIST)
@@ -279,9 +307,8 @@ void sysfs_remove_group(struct kobject *kobj,
        if (grp->name) {
                kn = kernfs_find_and_get(parent, grp->name);
                if (!kn) {
-                       WARN(!kn, KERN_WARNING
-                            "sysfs group '%s' not found for kobject '%s'\n",
-                            grp->name, kobject_name(kobj));
+                       pr_debug("sysfs group '%s' not found for kobject '%s'\n",
+                                grp->name, kobject_name(kobj));
                        return;
                }
        } else {
@@ -318,13 +345,13 @@ void sysfs_remove_groups(struct kobject *kobj,
 EXPORT_SYMBOL_GPL(sysfs_remove_groups);
 
 /**
- * sysfs_merge_group - merge files into a pre-existing attribute group.
+ * sysfs_merge_group - merge files into a pre-existing named attribute group.
  * @kobj:      The kobject containing the group.
  * @grp:       The files to create and the attribute group they belong to.
  *
- * This function returns an error if the group doesn't exist or any of the
- * files already exist in that group, in which case none of the new files
- * are created.
+ * This function returns an error if the group doesn't exist, the .name field is
+ * NULL or any of the files already exist in that group, in which case none of
+ * the new files are created.
  */
 int sysfs_merge_group(struct kobject *kobj,
                       const struct attribute_group *grp)
@@ -356,7 +383,7 @@ int sysfs_merge_group(struct kobject *kobj,
 EXPORT_SYMBOL_GPL(sysfs_merge_group);
 
 /**
- * sysfs_unmerge_group - remove files from a pre-existing attribute group.
+ * sysfs_unmerge_group - remove files from a pre-existing named attribute group.
  * @kobj:      The kobject containing the group.
  * @grp:       The files to remove and the attribute group they belong to.
  */
index ae5a20cf2f9c16c6bb05b3db79d7e76a825f8be3..272e4e79e15c48db487feba3b7e6103e71b21e8d 100644 (file)
@@ -130,7 +130,7 @@ static inline void cpu_maps_update_done(void)
 static inline int add_cpu(unsigned int cpu) { return 0;}
 
 #endif /* CONFIG_SMP */
-extern struct bus_type cpu_subsys;
+extern const struct bus_type cpu_subsys;
 
 extern int lockdep_is_cpus_held(void);
 
index 0311858b46cef2d0b966e7420159f580187f95bc..f026f8926d79255553f4bdabea9b49539c8ed499 100644 (file)
@@ -4,6 +4,7 @@
 
 #include <linux/types.h>
 #include <linux/compiler.h>
+#include <linux/cleanup.h>
 #include <linux/gfp.h>
 
 #define FW_ACTION_NOUEVENT 0
@@ -198,4 +199,6 @@ static inline void firmware_upload_unregister(struct fw_upload *fw_upload)
 
 int firmware_request_cache(struct device *device, const char *name);
 
+DEFINE_FREE(firmware, struct firmware *, release_firmware(_T))
+
 #endif
index 2a72f55d26eb89ffde17e0cd9ec345cdf1dcb1c7..0d79070c5a70f21e9d73751941f28cefb244950d 100644 (file)
@@ -9,10 +9,16 @@
 #ifndef _LINUX_FWNODE_H_
 #define _LINUX_FWNODE_H_
 
-#include <linux/types.h>
-#include <linux/list.h>
 #include <linux/bits.h>
 #include <linux/err.h>
+#include <linux/list.h>
+#include <linux/types.h>
+
+enum dev_dma_attr {
+       DEV_DMA_NOT_SUPPORTED,
+       DEV_DMA_NON_COHERENT,
+       DEV_DMA_COHERENT,
+};
 
 struct fwnode_operations;
 struct device;
@@ -53,8 +59,10 @@ struct fwnode_handle {
  * fwnode link flags
  *
  * CYCLE:      The fwnode link is part of a cycle. Don't defer probe.
+ * IGNORE:     Completely ignore this link, even during cycle detection.
  */
 #define FWLINK_FLAG_CYCLE                      BIT(0)
+#define FWLINK_FLAG_IGNORE                     BIT(1)
 
 struct fwnode_link {
        struct fwnode_handle *supplier;
@@ -187,7 +195,6 @@ struct fwnode_operations {
                if (fwnode_has_op(fwnode, op))                          \
                        (fwnode)->ops->op(fwnode, ## __VA_ARGS__);      \
        } while (false)
-#define get_dev_from_fwnode(fwnode)    get_device((fwnode)->dev)
 
 static inline void fwnode_init(struct fwnode_handle *fwnode,
                               const struct fwnode_operations *ops)
@@ -209,9 +216,10 @@ static inline void fwnode_dev_initialized(struct fwnode_handle *fwnode,
                fwnode->flags &= ~FWNODE_FLAG_INITIALIZED;
 }
 
-extern bool fw_devlink_is_strict(void);
-int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup);
+int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup,
+                   u8 flags);
 void fwnode_links_purge(struct fwnode_handle *fwnode);
 void fw_devlink_purge_absent_suppliers(struct fwnode_handle *fwnode);
+bool fw_devlink_is_strict(void);
 
 #endif
index 99aaa050ccb767954ebcbb0fc47dd522987582e5..87c79d076d6d7396c6da4efc5873a49a5feac873 100644 (file)
@@ -206,23 +206,25 @@ struct kernfs_node {
 
        const void              *ns;    /* namespace tag */
        unsigned int            hash;   /* ns + name hash */
+       unsigned short          flags;
+       umode_t                 mode;
+
        union {
                struct kernfs_elem_dir          dir;
                struct kernfs_elem_symlink      symlink;
                struct kernfs_elem_attr         attr;
        };
 
-       void                    *priv;
-
        /*
         * 64bit unique ID.  On 64bit ino setups, id is the ino.  On 32bit,
         * the low 32bits are ino and upper generation.
         */
        u64                     id;
 
-       unsigned short          flags;
-       umode_t                 mode;
+       void                    *priv;
        struct kernfs_iattrs    *iattr;
+
+       struct rcu_head         rcu;
 };
 
 /*
index c30affcc43b444cc17cb894b83b17b52e41f8ebc..c8219505a79f98bc370e52997efc8af51833cfda 100644 (file)
@@ -38,7 +38,7 @@ extern char uevent_helper[];
 #endif
 
 /* counter to tag the uevent, read only except for the kobject core */
-extern u64 uevent_seqnum;
+extern atomic64_t uevent_seqnum;
 
 /*
  * The actions here must match the index to the string array
index e6516d0b7d52ad0570eb345955701be5856455c0..3a1045eb786c4cf30d8d0bda8845a2ef2993a14b 100644 (file)
@@ -11,6 +11,7 @@
 #define _LINUX_PROPERTY_H_
 
 #include <linux/args.h>
+#include <linux/array_size.h>
 #include <linux/bits.h>
 #include <linux/fwnode.h>
 #include <linux/stddef.h>
@@ -27,12 +28,6 @@ enum dev_prop_type {
        DEV_PROP_REF,
 };
 
-enum dev_dma_attr {
-       DEV_DMA_NOT_SUPPORTED,
-       DEV_DMA_NON_COHERENT,
-       DEV_DMA_COHERENT,
-};
-
 const struct fwnode_handle *__dev_fwnode_const(const struct device *dev);
 struct fwnode_handle *__dev_fwnode(struct device *dev);
 #define dev_fwnode(dev)                                                        \
@@ -156,11 +151,9 @@ struct fwnode_handle *fwnode_get_next_parent(struct fwnode_handle *fwnode);
        for (parent = fwnode_get_parent(fwnode); parent;        \
             parent = fwnode_get_next_parent(parent))
 
-struct device *fwnode_get_next_parent_dev(const struct fwnode_handle *fwnode);
 unsigned int fwnode_count_parents(const struct fwnode_handle *fwn);
 struct fwnode_handle *fwnode_get_nth_parent(struct fwnode_handle *fwn,
                                            unsigned int depth);
-bool fwnode_is_ancestor_of(const struct fwnode_handle *ancestor, const struct fwnode_handle *child);
 struct fwnode_handle *fwnode_get_next_child_node(
        const struct fwnode_handle *fwnode, struct fwnode_handle *child);
 struct fwnode_handle *fwnode_get_next_available_child_node(
index b717a70219f65186907c036f2e8b7e38e8e3083e..326341c623857e2afbb9b0c20a5f9b9ae4a54293 100644 (file)
@@ -61,22 +61,32 @@ do {                                                        \
 /**
  * struct attribute_group - data structure used to declare an attribute group.
  * @name:      Optional: Attribute group name
- *             If specified, the attribute group will be created in
- *             a new subdirectory with this name.
+ *             If specified, the attribute group will be created in a
+ *             new subdirectory with this name. Additionally when a
+ *             group is named, @is_visible and @is_bin_visible may
+ *             return SYSFS_GROUP_INVISIBLE to control visibility of
+ *             the directory itself.
  * @is_visible:        Optional: Function to return permissions associated with an
- *             attribute of the group. Will be called repeatedly for each
- *             non-binary attribute in the group. Only read/write
+ *             attribute of the group. Will be called repeatedly for
+ *             each non-binary attribute in the group. Only read/write
  *             permissions as well as SYSFS_PREALLOC are accepted. Must
- *             return 0 if an attribute is not visible. The returned value
- *             will replace static permissions defined in struct attribute.
+ *             return 0 if an attribute is not visible. The returned
+ *             value will replace static permissions defined in struct
+ *             attribute. Use SYSFS_GROUP_VISIBLE() when assigning this
+ *             callback to specify separate _group_visible() and
+ *             _attr_visible() handlers.
  * @is_bin_visible:
  *             Optional: Function to return permissions associated with a
  *             binary attribute of the group. Will be called repeatedly
  *             for each binary attribute in the group. Only read/write
- *             permissions as well as SYSFS_PREALLOC are accepted. Must
- *             return 0 if a binary attribute is not visible. The returned
- *             value will replace static permissions defined in
- *             struct bin_attribute.
+ *             permissions as well as SYSFS_PREALLOC (and the
+ *             visibility flags for named groups) are accepted. Must
+ *             return 0 if a binary attribute is not visible. The
+ *             returned value will replace static permissions defined
+ *             in struct bin_attribute. If @is_visible is not set, Use
+ *             SYSFS_GROUP_VISIBLE() when assigning this callback to
+ *             specify separate _group_visible() and _attr_visible()
+ *             handlers.
  * @attrs:     Pointer to NULL terminated list of attributes.
  * @bin_attrs: Pointer to NULL terminated list of binary attributes.
  *             Either attrs or bin_attrs or both must be provided.
@@ -91,13 +101,121 @@ struct attribute_group {
        struct bin_attribute    **bin_attrs;
 };
 
+#define SYSFS_PREALLOC         010000
+#define SYSFS_GROUP_INVISIBLE  020000
+
+/*
+ * DEFINE_SYSFS_GROUP_VISIBLE(name):
+ *     A helper macro to pair with the assignment of ".is_visible =
+ *     SYSFS_GROUP_VISIBLE(name)", that arranges for the directory
+ *     associated with a named attribute_group to optionally be hidden.
+ *     This allows for static declaration of attribute_groups, and the
+ *     simplification of attribute visibility lifetime that implies,
+ *     without polluting sysfs with empty attribute directories.
+ * Ex.
+ *
+ * static umode_t example_attr_visible(struct kobject *kobj,
+ *                                   struct attribute *attr, int n)
+ * {
+ *       if (example_attr_condition)
+ *               return 0;
+ *       else if (ro_attr_condition)
+ *               return 0444;
+ *       return a->mode;
+ * }
+ *
+ * static bool example_group_visible(struct kobject *kobj)
+ * {
+ *       if (example_group_condition)
+ *               return false;
+ *       return true;
+ * }
+ *
+ * DEFINE_SYSFS_GROUP_VISIBLE(example);
+ *
+ * static struct attribute_group example_group = {
+ *       .name = "example",
+ *       .is_visible = SYSFS_GROUP_VISIBLE(example),
+ *       .attrs = &example_attrs,
+ * };
+ *
+ * Note that it expects <name>_attr_visible and <name>_group_visible to
+ * be defined. For cases where individual attributes do not need
+ * separate visibility consideration, only entire group visibility at
+ * once, see DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE().
+ */
+#define DEFINE_SYSFS_GROUP_VISIBLE(name)                             \
+       static inline umode_t sysfs_group_visible_##name(            \
+               struct kobject *kobj, struct attribute *attr, int n) \
+       {                                                            \
+               if (n == 0 && !name##_group_visible(kobj))           \
+                       return SYSFS_GROUP_INVISIBLE;                \
+               return name##_attr_visible(kobj, attr, n);           \
+       }
+
+/*
+ * DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(name):
+ *     A helper macro to pair with SYSFS_GROUP_VISIBLE() that like
+ *     DEFINE_SYSFS_GROUP_VISIBLE() controls group visibility, but does
+ *     not require the implementation of a per-attribute visibility
+ *     callback.
+ * Ex.
+ *
+ * static bool example_group_visible(struct kobject *kobj)
+ * {
+ *       if (example_group_condition)
+ *               return false;
+ *       return true;
+ * }
+ *
+ * DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(example);
+ *
+ * static struct attribute_group example_group = {
+ *       .name = "example",
+ *       .is_visible = SYSFS_GROUP_VISIBLE(example),
+ *       .attrs = &example_attrs,
+ * };
+ */
+#define DEFINE_SIMPLE_SYSFS_GROUP_VISIBLE(name)                   \
+       static inline umode_t sysfs_group_visible_##name(         \
+               struct kobject *kobj, struct attribute *a, int n) \
+       {                                                         \
+               if (n == 0 && !name##_group_visible(kobj))        \
+                       return SYSFS_GROUP_INVISIBLE;             \
+               return a->mode;                                   \
+       }
+
+/*
+ * Same as DEFINE_SYSFS_GROUP_VISIBLE, but for groups with only binary
+ * attributes. If an attribute_group defines both text and binary
+ * attributes, the group visibility is determined by the function
+ * specified to is_visible() not is_bin_visible()
+ */
+#define DEFINE_SYSFS_BIN_GROUP_VISIBLE(name)                             \
+       static inline umode_t sysfs_group_visible_##name(                \
+               struct kobject *kobj, struct bin_attribute *attr, int n) \
+       {                                                                \
+               if (n == 0 && !name##_group_visible(kobj))               \
+                       return SYSFS_GROUP_INVISIBLE;                    \
+               return name##_attr_visible(kobj, attr, n);               \
+       }
+
+#define DEFINE_SIMPLE_SYSFS_BIN_GROUP_VISIBLE(name)                   \
+       static inline umode_t sysfs_group_visible_##name(             \
+               struct kobject *kobj, struct bin_attribute *a, int n) \
+       {                                                             \
+               if (n == 0 && !name##_group_visible(kobj))            \
+                       return SYSFS_GROUP_INVISIBLE;                 \
+               return a->mode;                                       \
+       }
+
+#define SYSFS_GROUP_VISIBLE(fn) sysfs_group_visible_##fn
+
 /*
  * Use these macros to make defining attributes easier.
  * See include/linux/device.h for examples..
  */
 
-#define SYSFS_PREALLOC 010000
-
 #define __ATTR(_name, _mode, _show, _store) {                          \
        .attr = {.name = __stringify(_name),                            \
                 .mode = VERIFY_OCTAL_PERMISSIONS(_mode) },             \
index fe7a517fc4abbfd62570692bafb6bb88e5a19da1..495b69a71a5d7d21b44cbf4b9a08ec4bd8388625 100644 (file)
@@ -39,7 +39,7 @@ static struct kobj_attribute _name##_attr = __ATTR_RW(_name)
 static ssize_t uevent_seqnum_show(struct kobject *kobj,
                                  struct kobj_attribute *attr, char *buf)
 {
-       return sysfs_emit(buf, "%llu\n", (unsigned long long)uevent_seqnum);
+       return sysfs_emit(buf, "%llu\n", (u64)atomic64_read(&uevent_seqnum));
 }
 KERNEL_ATTR_RO(uevent_seqnum);
 
index bf2bdac46843dc945a7daa37a124e42eeaea489e..0066c8f6c15442641889c87995410ac82d078423 100644 (file)
@@ -7080,7 +7080,7 @@ static struct device_attribute wq_sysfs_unbound_attrs[] = {
        __ATTR_NULL,
 };
 
-static struct bus_type wq_subsys = {
+static const struct bus_type wq_subsys = {
        .name                           = "workqueue",
        .dev_groups                     = wq_sysfs_groups,
 };
index fb9a2f06dd1e79db0e5db17362c88152790e2b36..03b427e2707e357ab12abeb9da234432c4bc0fb3 100644 (file)
@@ -30,7 +30,7 @@
 #include <net/net_namespace.h>
 
 
-u64 uevent_seqnum;
+atomic64_t uevent_seqnum;
 #ifdef CONFIG_UEVENT_HELPER
 char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
 #endif
@@ -42,10 +42,9 @@ struct uevent_sock {
 
 #ifdef CONFIG_NET
 static LIST_HEAD(uevent_sock_list);
-#endif
-
-/* This lock protects uevent_seqnum and uevent_sock_list */
+/* This lock protects uevent_sock_list */
 static DEFINE_MUTEX(uevent_sock_mutex);
+#endif
 
 /* the strings here must match the enum in include/linux/kobject.h */
 static const char *kobject_actions[] = {
@@ -315,6 +314,7 @@ static int uevent_net_broadcast_untagged(struct kobj_uevent_env *env,
        int retval = 0;
 
        /* send netlink message */
+       mutex_lock(&uevent_sock_mutex);
        list_for_each_entry(ue_sk, &uevent_sock_list, list) {
                struct sock *uevent_sock = ue_sk->sk;
 
@@ -334,6 +334,7 @@ static int uevent_net_broadcast_untagged(struct kobj_uevent_env *env,
                if (retval == -ENOBUFS || retval == -ESRCH)
                        retval = 0;
        }
+       mutex_unlock(&uevent_sock_mutex);
        consume_skb(skb);
 
        return retval;
@@ -583,16 +584,14 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
                break;
        }
 
-       mutex_lock(&uevent_sock_mutex);
        /* we will send an event, so request a new sequence number */
-       retval = add_uevent_var(env, "SEQNUM=%llu", ++uevent_seqnum);
-       if (retval) {
-               mutex_unlock(&uevent_sock_mutex);
+       retval = add_uevent_var(env, "SEQNUM=%llu",
+                               atomic64_inc_return(&uevent_seqnum));
+       if (retval)
                goto exit;
-       }
+
        retval = kobject_uevent_net_broadcast(kobj, env, action_string,
                                              devpath);
-       mutex_unlock(&uevent_sock_mutex);
 
 #ifdef CONFIG_UEVENT_HELPER
        /* call uevent_helper, usually only enabled during early boot */
@@ -688,7 +687,8 @@ static int uevent_net_broadcast(struct sock *usk, struct sk_buff *skb,
        int ret;
 
        /* bump and prepare sequence number */
-       ret = snprintf(buf, sizeof(buf), "SEQNUM=%llu", ++uevent_seqnum);
+       ret = snprintf(buf, sizeof(buf), "SEQNUM=%llu",
+                      atomic64_inc_return(&uevent_seqnum));
        if (ret < 0 || (size_t)ret >= sizeof(buf))
                return -ENOMEM;
        ret++;
@@ -742,9 +742,7 @@ static int uevent_net_rcv_skb(struct sk_buff *skb, struct nlmsghdr *nlh,
                return -EPERM;
        }
 
-       mutex_lock(&uevent_sock_mutex);
        ret = uevent_net_broadcast(net->uevent_sock->sk, skb, extack);
-       mutex_unlock(&uevent_sock_mutex);
 
        return ret;
 }