Commit 241590e5 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'driver-core-6.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core

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
  ...
parents bb41fe35 6aeb8850
...@@ -751,7 +751,7 @@ static int __component_add(struct device *dev, const struct component_ops *ops, ...@@ -751,7 +751,7 @@ static int __component_add(struct device *dev, const struct component_ops *ops,
* component_bind_all(). See also &struct component_ops. * component_bind_all(). See also &struct component_ops.
* *
* @subcomponent must be nonzero and is used to differentiate between multiple * @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(). * using component_match_add_typed().
* *
* The component needs to be unregistered at driver unload/disconnect by * The component needs to be unregistered at driver unload/disconnect by
...@@ -781,7 +781,7 @@ EXPORT_SYMBOL_GPL(component_add_typed); ...@@ -781,7 +781,7 @@ EXPORT_SYMBOL_GPL(component_add_typed);
* The component needs to be unregistered at driver unload/disconnect by * The component needs to be unregistered at driver unload/disconnect by
* calling component_del(). * 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. * components on the same device.
*/ */
int component_add(struct device *dev, const struct component_ops *ops) int component_add(struct device *dev, const struct component_ops *ops)
......
...@@ -92,12 +92,13 @@ static int __fwnode_link_add(struct fwnode_handle *con, ...@@ -92,12 +92,13 @@ static int __fwnode_link_add(struct fwnode_handle *con,
return 0; 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; int ret;
mutex_lock(&fwnode_link_lock); mutex_lock(&fwnode_link_lock);
ret = __fwnode_link_add(con, sup, 0); ret = __fwnode_link_add(con, sup, flags);
mutex_unlock(&fwnode_link_lock); mutex_unlock(&fwnode_link_lock);
return ret; return ret;
} }
...@@ -1011,7 +1012,8 @@ static struct fwnode_handle *fwnode_links_check_suppliers( ...@@ -1011,7 +1012,8 @@ static struct fwnode_handle *fwnode_links_check_suppliers(
return NULL; return NULL;
list_for_each_entry(link, &fwnode->suppliers, c_hook) 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 link->supplier;
return NULL; return NULL;
...@@ -1871,6 +1873,7 @@ static void fw_devlink_unblock_consumers(struct device *dev) ...@@ -1871,6 +1873,7 @@ static void fw_devlink_unblock_consumers(struct device *dev)
device_links_write_unlock(); device_links_write_unlock();
} }
#define get_dev_from_fwnode(fwnode) get_device((fwnode)->dev)
static bool fwnode_init_without_drv(struct fwnode_handle *fwnode) 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) ...@@ -1901,6 +1904,63 @@ static bool fwnode_ancestor_init_without_drv(struct fwnode_handle *fwnode)
return false; 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. * __fw_devlink_relax_cycles - Relax and mark dependency cycles.
* @con: Potential consumer device. * @con: Potential consumer device.
...@@ -1962,6 +2022,9 @@ static bool __fw_devlink_relax_cycles(struct device *con, ...@@ -1962,6 +2022,9 @@ static bool __fw_devlink_relax_cycles(struct device *con,
} }
list_for_each_entry(link, &sup_handle->suppliers, c_hook) { 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)) { if (__fw_devlink_relax_cycles(con, link->supplier)) {
__fwnode_link_cycle(link); __fwnode_link_cycle(link);
ret = true; ret = true;
...@@ -2040,6 +2103,9 @@ static int fw_devlink_create_devlink(struct device *con, ...@@ -2040,6 +2103,9 @@ static int fw_devlink_create_devlink(struct device *con,
int ret = 0; int ret = 0;
u32 flags; u32 flags;
if (link->flags & FWLINK_FLAG_IGNORE)
return 0;
if (con->fwnode == link->consumer) if (con->fwnode == link->consumer)
flags = fw_devlink_get_flags(link->flags); flags = fw_devlink_get_flags(link->flags);
else else
......
...@@ -366,7 +366,7 @@ static int cpu_uevent(const struct device *dev, struct kobj_uevent_env *env) ...@@ -366,7 +366,7 @@ static int cpu_uevent(const struct device *dev, struct kobj_uevent_env *env)
} }
#endif #endif
struct bus_type cpu_subsys = { const struct bus_type cpu_subsys = {
.name = "cpu", .name = "cpu",
.dev_name = "cpu", .dev_name = "cpu",
.match = cpu_subsys_match, .match = cpu_subsys_match,
......
...@@ -313,7 +313,7 @@ static void deferred_probe_timeout_work_func(struct work_struct *work) ...@@ -313,7 +313,7 @@ static void deferred_probe_timeout_work_func(struct work_struct *work)
mutex_lock(&deferred_probe_mutex); mutex_lock(&deferred_probe_mutex);
list_for_each_entry(p, &deferred_probe_pending_list, deferred_probe) 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); mutex_unlock(&deferred_probe_mutex);
fw_devlink_probing_done(); fw_devlink_probing_done();
...@@ -397,13 +397,12 @@ bool device_is_bound(struct device *dev) ...@@ -397,13 +397,12 @@ bool device_is_bound(struct device *dev)
static void driver_bound(struct device *dev) static void driver_bound(struct device *dev)
{ {
if (device_is_bound(dev)) { if (device_is_bound(dev)) {
pr_warn("%s: device %s already bound\n", dev_warn(dev, "%s: device already bound\n", __func__);
__func__, kobject_name(&dev->kobj));
return; return;
} }
pr_debug("driver: '%s': %s: bound to device '%s'\n", dev->driver->name, dev_dbg(dev, "driver: '%s': %s: bound to device\n", dev->driver->name,
__func__, dev_name(dev)); __func__);
klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices); klist_add_tail(&dev->p->knode_driver, &dev->driver->p->klist_devices);
device_links_driver_bound(dev); device_links_driver_bound(dev);
...@@ -587,13 +586,13 @@ static int call_driver_probe(struct device *dev, struct device_driver *drv) ...@@ -587,13 +586,13 @@ static int call_driver_probe(struct device *dev, struct device_driver *drv)
break; break;
case -ENODEV: case -ENODEV:
case -ENXIO: case -ENXIO:
pr_debug("%s: probe of %s rejects match %d\n", dev_dbg(dev, "probe with driver %s rejects match %d\n",
drv->name, dev_name(dev), ret); drv->name, ret);
break; break;
default: default:
/* driver matched but the probe failed */ /* driver matched but the probe failed */
pr_warn("%s: probe of %s failed with error %d\n", dev_err(dev, "probe with driver %s failed with error %d\n",
drv->name, dev_name(dev), ret); drv->name, ret);
break; break;
} }
...@@ -620,8 +619,8 @@ static int really_probe(struct device *dev, struct device_driver *drv) ...@@ -620,8 +619,8 @@ static int really_probe(struct device *dev, struct device_driver *drv)
if (link_ret == -EPROBE_DEFER) if (link_ret == -EPROBE_DEFER)
return link_ret; return link_ret;
pr_debug("bus: '%s': %s: probing driver %s with device %s\n", dev_dbg(dev, "bus: '%s': %s: probing driver %s with device\n",
drv->bus->name, __func__, drv->name, dev_name(dev)); drv->bus->name, __func__, drv->name);
if (!list_empty(&dev->devres_head)) { if (!list_empty(&dev->devres_head)) {
dev_crit(dev, "Resources present before probing\n"); dev_crit(dev, "Resources present before probing\n");
ret = -EBUSY; ret = -EBUSY;
...@@ -644,8 +643,7 @@ static int really_probe(struct device *dev, struct device_driver *drv) ...@@ -644,8 +643,7 @@ static int really_probe(struct device *dev, struct device_driver *drv)
ret = driver_sysfs_add(dev); ret = driver_sysfs_add(dev);
if (ret) { if (ret) {
pr_err("%s: driver_sysfs_add(%s) failed\n", dev_err(dev, "%s: driver_sysfs_add failed\n", __func__);
__func__, dev_name(dev));
goto sysfs_failed; goto sysfs_failed;
} }
...@@ -706,8 +704,8 @@ static int really_probe(struct device *dev, struct device_driver *drv) ...@@ -706,8 +704,8 @@ static int really_probe(struct device *dev, struct device_driver *drv)
dev->pm_domain->sync(dev); dev->pm_domain->sync(dev);
driver_bound(dev); driver_bound(dev);
pr_debug("bus: '%s': %s: bound device %s to driver %s\n", dev_dbg(dev, "bus: '%s': %s: bound device to driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name); drv->bus->name, __func__, drv->name);
goto done; goto done;
dev_sysfs_state_synced_failed: dev_sysfs_state_synced_failed:
...@@ -786,8 +784,8 @@ static int __driver_probe_device(struct device_driver *drv, struct device *dev) ...@@ -786,8 +784,8 @@ static int __driver_probe_device(struct device_driver *drv, struct device *dev)
return -EBUSY; return -EBUSY;
dev->can_match = true; dev->can_match = true;
pr_debug("bus: '%s': %s: matched device %s with driver %s\n", dev_dbg(dev, "bus: '%s': %s: matched device with driver %s\n",
drv->bus->name, __func__, dev_name(dev), drv->name); drv->bus->name, __func__, drv->name);
pm_runtime_get_suppliers(dev); pm_runtime_get_suppliers(dev);
if (dev->parent) if (dev->parent)
......
...@@ -551,12 +551,16 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv, ...@@ -551,12 +551,16 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
file_size_ptr, file_size_ptr,
READING_FIRMWARE); READING_FIRMWARE);
if (rc < 0) { if (rc < 0) {
if (!(fw_priv->opt_flags & FW_OPT_NO_WARN)) {
if (rc != -ENOENT) if (rc != -ENOENT)
dev_warn(device, "loading %s failed with error %d\n", dev_warn(device,
"loading %s failed with error %d\n",
path, rc); path, rc);
else else
dev_dbg(device, "loading %s failed for no such file or directory.\n", dev_dbg(device,
"loading %s failed for no such file or directory.\n",
path); path);
}
continue; continue;
} }
size = rc; size = rc;
......
...@@ -174,8 +174,8 @@ static int platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec, ...@@ -174,8 +174,8 @@ static int platform_msi_alloc_priv_data(struct device *dev, unsigned int nvec,
if (!datap) if (!datap)
return -ENOMEM; return -ENOMEM;
datap->devid = ida_simple_get(&platform_msi_devid_ida, datap->devid = ida_alloc_max(&platform_msi_devid_ida,
0, 1 << DEV_ID_SHIFT, GFP_KERNEL); (1 << DEV_ID_SHIFT) - 1, GFP_KERNEL);
if (datap->devid < 0) { if (datap->devid < 0) {
err = datap->devid; err = datap->devid;
kfree(datap); kfree(datap);
...@@ -193,7 +193,7 @@ static void platform_msi_free_priv_data(struct device *dev) ...@@ -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; struct platform_msi_priv_data *data = dev->msi.data->platform_data;
dev->msi.data->platform_data = NULL; 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); kfree(data);
} }
......
...@@ -7,15 +7,16 @@ ...@@ -7,15 +7,16 @@
* Mika Westerberg <mika.westerberg@linux.intel.com> * 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/export.h>
#include <linux/kernel.h> #include <linux/kconfig.h>
#include <linux/of.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/property.h>
#include <linux/phy.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) struct fwnode_handle *__dev_fwnode(struct device *dev)
{ {
...@@ -699,34 +700,6 @@ struct fwnode_handle *fwnode_get_next_parent(struct fwnode_handle *fwnode) ...@@ -699,34 +700,6 @@ struct fwnode_handle *fwnode_get_next_parent(struct fwnode_handle *fwnode)
} }
EXPORT_SYMBOL_GPL(fwnode_get_next_parent); 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_count_parents - Return the number of parents a node has
* @fwnode: The node the parents of which are to be counted * @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, ...@@ -773,34 +746,6 @@ struct fwnode_handle *fwnode_get_nth_parent(struct fwnode_handle *fwnode,
} }
EXPORT_SYMBOL_GPL(fwnode_get_nth_parent); 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_get_next_child_node - Return the next child node handle for a node
* @fwnode: Firmware node to find the next child node for. * @fwnode: Firmware node to find the next child node for.
......
...@@ -6,10 +6,21 @@ ...@@ -6,10 +6,21 @@
* Author: Heikki Krogerus <heikki.krogerus@linux.intel.com> * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
*/ */
#include <linux/container_of.h>
#include <linux/device.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/property.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/types.h>
#include "base.h" #include "base.h"
......
...@@ -336,7 +336,7 @@ static int efifb_add_links(struct fwnode_handle *fwnode) ...@@ -336,7 +336,7 @@ static int efifb_add_links(struct fwnode_handle *fwnode)
if (!sup_np) if (!sup_np)
return 0; 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); of_node_put(sup_np);
return 0; return 0;
......
...@@ -1072,7 +1072,8 @@ of_fwnode_device_get_match_data(const struct fwnode_handle *fwnode, ...@@ -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, 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); 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, ...@@ -1091,7 +1092,7 @@ static void of_link_to_phandle(struct device_node *con_np,
tmp_np = of_get_next_parent(tmp_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, \ ...@@ -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 a struct device, implement this ops so fw_devlink can use it
* to find the true consumer. * to find the true consumer.
* @optional: Describes whether a supplier is mandatory or not * @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: * Returns:
* parse_prop() return values are * parse_prop() return values are
...@@ -1216,6 +1219,7 @@ struct supplier_bindings { ...@@ -1216,6 +1219,7 @@ struct supplier_bindings {
const char *prop_name, int index); const char *prop_name, int index);
struct device_node *(*get_con_dev)(struct device_node *np); struct device_node *(*get_con_dev)(struct device_node *np);
bool optional; bool optional;
u8 fwlink_flags;
}; };
DEFINE_SIMPLE_PROP(clocks, "clocks", "#clock-cells") DEFINE_SIMPLE_PROP(clocks, "clocks", "#clock-cells")
...@@ -1247,6 +1251,7 @@ DEFINE_SIMPLE_PROP(leds, "leds", NULL) ...@@ -1247,6 +1251,7 @@ DEFINE_SIMPLE_PROP(leds, "leds", NULL)
DEFINE_SIMPLE_PROP(backlight, "backlight", NULL) DEFINE_SIMPLE_PROP(backlight, "backlight", NULL)
DEFINE_SIMPLE_PROP(panel, "panel", NULL) DEFINE_SIMPLE_PROP(panel, "panel", NULL)
DEFINE_SIMPLE_PROP(msi_parent, "msi-parent", "#msi-cells") 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(regulators, "-supply", NULL)
DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells") DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells")
...@@ -1357,6 +1362,10 @@ static const struct supplier_bindings of_supplier_bindings[] = { ...@@ -1357,6 +1362,10 @@ static const struct supplier_bindings of_supplier_bindings[] = {
{ .parse_prop = parse_regulators, }, { .parse_prop = parse_regulators, },
{ .parse_prop = parse_gpio, }, { .parse_prop = parse_gpio, },
{ .parse_prop = parse_gpios, }, { .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) ...@@ -1401,7 +1410,7 @@ static int of_link_property(struct device_node *con_np, const char *prop_name)
: of_node_get(con_np); : of_node_get(con_np);
matched = true; matched = true;
i++; 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(phandle);
of_node_put(con_dev_np); of_node_put(con_dev_np);
} }
......
...@@ -751,13 +751,28 @@ static void __debugfs_file_removed(struct dentry *dentry) ...@@ -751,13 +751,28 @@ static void __debugfs_file_removed(struct dentry *dentry)
if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT) if ((unsigned long)fsd & DEBUGFS_FSDATA_IS_REAL_FOPS_BIT)
return; return;
/* if we hit zero, just wait for all to finish */ /* if this was the last reference, we're done */
if (!refcount_dec_and_test(&fsd->active_users)) { if (refcount_dec_and_test(&fsd->active_users))
wait_for_completion(&fsd->active_users_drained);
return; 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)) { while (refcount_read(&fsd->active_users)) {
struct debugfs_cancellation *c; struct debugfs_cancellation *c;
......
...@@ -529,6 +529,20 @@ void kernfs_get(struct kernfs_node *kn) ...@@ -529,6 +529,20 @@ void kernfs_get(struct kernfs_node *kn)
} }
EXPORT_SYMBOL_GPL(kernfs_get); 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 * kernfs_put - put a reference count on a kernfs_node
* @kn: the target kernfs_node * @kn: the target kernfs_node
...@@ -557,16 +571,11 @@ void kernfs_put(struct kernfs_node *kn) ...@@ -557,16 +571,11 @@ void kernfs_put(struct kernfs_node *kn)
if (kernfs_type(kn) == KERNFS_LINK) if (kernfs_type(kn) == KERNFS_LINK)
kernfs_put(kn->symlink.target_kn); 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); spin_lock(&kernfs_idr_lock);
idr_remove(&root->ino_idr, (u32)kernfs_ino(kn)); idr_remove(&root->ino_idr, (u32)kernfs_ino(kn));
spin_unlock(&kernfs_idr_lock); spin_unlock(&kernfs_idr_lock);
kmem_cache_free(kernfs_node_cache, kn);
call_rcu(&kn->rcu, kernfs_free_rcu);
kn = parent; kn = parent;
if (kn) { if (kn) {
...@@ -575,7 +584,7 @@ void kernfs_put(struct kernfs_node *kn) ...@@ -575,7 +584,7 @@ void kernfs_put(struct kernfs_node *kn)
} else { } else {
/* just released the root kn, free @root too */ /* just released the root kn, free @root too */
idr_destroy(&root->ino_idr); idr_destroy(&root->ino_idr);
kfree(root); kfree_rcu(root, rcu);
} }
} }
EXPORT_SYMBOL_GPL(kernfs_put); EXPORT_SYMBOL_GPL(kernfs_put);
...@@ -715,7 +724,7 @@ struct kernfs_node *kernfs_find_and_get_node_by_id(struct kernfs_root *root, ...@@ -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); ino_t ino = kernfs_id_ino(id);
u32 gen = kernfs_id_gen(id); u32 gen = kernfs_id_gen(id);
spin_lock(&kernfs_idr_lock); rcu_read_lock();
kn = idr_find(&root->ino_idr, (u32)ino); kn = idr_find(&root->ino_idr, (u32)ino);
if (!kn) if (!kn)
...@@ -739,10 +748,10 @@ struct kernfs_node *kernfs_find_and_get_node_by_id(struct kernfs_root *root, ...@@ -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))) if (unlikely(!__kernfs_active(kn) || !atomic_inc_not_zero(&kn->count)))
goto err_unlock; goto err_unlock;
spin_unlock(&kernfs_idr_lock); rcu_read_unlock();
return kn; return kn;
err_unlock: err_unlock:
spin_unlock(&kernfs_idr_lock); rcu_read_unlock();
return NULL; return NULL;
} }
......
...@@ -483,9 +483,11 @@ static int kernfs_fop_mmap(struct file *file, struct vm_area_struct *vma) ...@@ -483,9 +483,11 @@ static int kernfs_fop_mmap(struct file *file, struct vm_area_struct *vma)
goto out_put; goto out_put;
rc = 0; rc = 0;
if (!of->mmapped) {
of->mmapped = true; of->mmapped = true;
of_on(of)->nr_mmapped++; of_on(of)->nr_mmapped++;
of->vm_ops = vma->vm_ops; of->vm_ops = vma->vm_ops;
}
vma->vm_ops = &kernfs_vm_ops; vma->vm_ops = &kernfs_vm_ops;
out_put: out_put:
kernfs_put_active(of->kn); kernfs_put_active(of->kn);
......
...@@ -49,6 +49,8 @@ struct kernfs_root { ...@@ -49,6 +49,8 @@ struct kernfs_root {
struct rw_semaphore kernfs_rwsem; struct rw_semaphore kernfs_rwsem;
struct rw_semaphore kernfs_iattr_rwsem; struct rw_semaphore kernfs_iattr_rwsem;
struct rw_semaphore kernfs_supers_rwsem; struct rw_semaphore kernfs_supers_rwsem;
struct rcu_head rcu;
}; };
/* +1 to avoid triggering overflow warning when negating it */ /* +1 to avoid triggering overflow warning when negating it */
......
...@@ -31,6 +31,17 @@ static void remove_files(struct kernfs_node *parent, ...@@ -31,6 +31,17 @@ static void remove_files(struct kernfs_node *parent,
kernfs_remove_by_name(parent, (*bin_attr)->attr.name); 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, static int create_files(struct kernfs_node *parent, struct kobject *kobj,
kuid_t uid, kgid_t gid, kuid_t uid, kgid_t gid,
const struct attribute_group *grp, int update) const struct attribute_group *grp, int update)
...@@ -52,6 +63,7 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj, ...@@ -52,6 +63,7 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj,
kernfs_remove_by_name(parent, (*attr)->name); kernfs_remove_by_name(parent, (*attr)->name);
if (grp->is_visible) { if (grp->is_visible) {
mode = grp->is_visible(kobj, *attr, i); mode = grp->is_visible(kobj, *attr, i);
mode &= ~SYSFS_GROUP_INVISIBLE;
if (!mode) if (!mode)
continue; continue;
} }
...@@ -81,6 +93,7 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj, ...@@ -81,6 +93,7 @@ static int create_files(struct kernfs_node *parent, struct kobject *kobj,
(*bin_attr)->attr.name); (*bin_attr)->attr.name);
if (grp->is_bin_visible) { if (grp->is_bin_visible) {
mode = grp->is_bin_visible(kobj, *bin_attr, i); mode = grp->is_bin_visible(kobj, *bin_attr, i);
mode &= ~SYSFS_GROUP_INVISIBLE;
if (!mode) if (!mode)
continue; continue;
} }
...@@ -127,16 +140,31 @@ static int internal_create_group(struct kobject *kobj, int update, ...@@ -127,16 +140,31 @@ static int internal_create_group(struct kobject *kobj, int update,
kobject_get_ownership(kobj, &uid, &gid); kobject_get_ownership(kobj, &uid, &gid);
if (grp->name) { 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) { if (update) {
kn = kernfs_find_and_get(kobj->sd, grp->name); kn = kernfs_find_and_get(kobj->sd, grp->name);
if (!kn) { if (!kn) {
pr_warn("Can't update unknown attr grp name: %s/%s\n", pr_debug("attr grp %s/%s not created yet\n",
kobj->name, grp->name); kobj->name, grp->name);
return -EINVAL; /* 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); uid, gid, kobj, NULL);
if (IS_ERR(kn)) { if (IS_ERR(kn)) {
if (PTR_ERR(kn) == -EEXIST) if (PTR_ERR(kn) == -EEXIST)
...@@ -279,8 +307,7 @@ void sysfs_remove_group(struct kobject *kobj, ...@@ -279,8 +307,7 @@ void sysfs_remove_group(struct kobject *kobj,
if (grp->name) { if (grp->name) {
kn = kernfs_find_and_get(parent, grp->name); kn = kernfs_find_and_get(parent, grp->name);
if (!kn) { if (!kn) {
WARN(!kn, KERN_WARNING pr_debug("sysfs group '%s' not found for kobject '%s'\n",
"sysfs group '%s' not found for kobject '%s'\n",
grp->name, kobject_name(kobj)); grp->name, kobject_name(kobj));
return; return;
} }
...@@ -318,13 +345,13 @@ void sysfs_remove_groups(struct kobject *kobj, ...@@ -318,13 +345,13 @@ void sysfs_remove_groups(struct kobject *kobj,
EXPORT_SYMBOL_GPL(sysfs_remove_groups); 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. * @kobj: The kobject containing the group.
* @grp: The files to create and the attribute group they belong to. * @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 * This function returns an error if the group doesn't exist, the .name field is
* files already exist in that group, in which case none of the new files * NULL or any of the files already exist in that group, in which case none of
* are created. * the new files are created.
*/ */
int sysfs_merge_group(struct kobject *kobj, int sysfs_merge_group(struct kobject *kobj,
const struct attribute_group *grp) const struct attribute_group *grp)
...@@ -356,7 +383,7 @@ int sysfs_merge_group(struct kobject *kobj, ...@@ -356,7 +383,7 @@ int sysfs_merge_group(struct kobject *kobj,
EXPORT_SYMBOL_GPL(sysfs_merge_group); 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. * @kobj: The kobject containing the group.
* @grp: The files to remove and the attribute group they belong to. * @grp: The files to remove and the attribute group they belong to.
*/ */
......
...@@ -130,7 +130,7 @@ static inline void cpu_maps_update_done(void) ...@@ -130,7 +130,7 @@ static inline void cpu_maps_update_done(void)
static inline int add_cpu(unsigned int cpu) { return 0;} static inline int add_cpu(unsigned int cpu) { return 0;}
#endif /* CONFIG_SMP */ #endif /* CONFIG_SMP */
extern struct bus_type cpu_subsys; extern const struct bus_type cpu_subsys;
extern int lockdep_is_cpus_held(void); extern int lockdep_is_cpus_held(void);
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/cleanup.h>
#include <linux/gfp.h> #include <linux/gfp.h>
#define FW_ACTION_NOUEVENT 0 #define FW_ACTION_NOUEVENT 0
...@@ -198,4 +199,6 @@ static inline void firmware_upload_unregister(struct fw_upload *fw_upload) ...@@ -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); int firmware_request_cache(struct device *device, const char *name);
DEFINE_FREE(firmware, struct firmware *, release_firmware(_T))
#endif #endif
...@@ -9,10 +9,16 @@ ...@@ -9,10 +9,16 @@
#ifndef _LINUX_FWNODE_H_ #ifndef _LINUX_FWNODE_H_
#define _LINUX_FWNODE_H_ #define _LINUX_FWNODE_H_
#include <linux/types.h>
#include <linux/list.h>
#include <linux/bits.h> #include <linux/bits.h>
#include <linux/err.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 fwnode_operations;
struct device; struct device;
...@@ -53,8 +59,10 @@ struct fwnode_handle { ...@@ -53,8 +59,10 @@ struct fwnode_handle {
* fwnode link flags * fwnode link flags
* *
* CYCLE: The fwnode link is part of a cycle. Don't defer probe. * 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_CYCLE BIT(0)
#define FWLINK_FLAG_IGNORE BIT(1)
struct fwnode_link { struct fwnode_link {
struct fwnode_handle *supplier; struct fwnode_handle *supplier;
...@@ -187,7 +195,6 @@ struct fwnode_operations { ...@@ -187,7 +195,6 @@ struct fwnode_operations {
if (fwnode_has_op(fwnode, op)) \ if (fwnode_has_op(fwnode, op)) \
(fwnode)->ops->op(fwnode, ## __VA_ARGS__); \ (fwnode)->ops->op(fwnode, ## __VA_ARGS__); \
} while (false) } while (false)
#define get_dev_from_fwnode(fwnode) get_device((fwnode)->dev)
static inline void fwnode_init(struct fwnode_handle *fwnode, static inline void fwnode_init(struct fwnode_handle *fwnode,
const struct fwnode_operations *ops) const struct fwnode_operations *ops)
...@@ -209,9 +216,10 @@ static inline void fwnode_dev_initialized(struct fwnode_handle *fwnode, ...@@ -209,9 +216,10 @@ static inline void fwnode_dev_initialized(struct fwnode_handle *fwnode,
fwnode->flags &= ~FWNODE_FLAG_INITIALIZED; 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 fwnode_links_purge(struct fwnode_handle *fwnode);
void fw_devlink_purge_absent_suppliers(struct fwnode_handle *fwnode); void fw_devlink_purge_absent_suppliers(struct fwnode_handle *fwnode);
bool fw_devlink_is_strict(void);
#endif #endif
...@@ -206,23 +206,25 @@ struct kernfs_node { ...@@ -206,23 +206,25 @@ struct kernfs_node {
const void *ns; /* namespace tag */ const void *ns; /* namespace tag */
unsigned int hash; /* ns + name hash */ unsigned int hash; /* ns + name hash */
unsigned short flags;
umode_t mode;
union { union {
struct kernfs_elem_dir dir; struct kernfs_elem_dir dir;
struct kernfs_elem_symlink symlink; struct kernfs_elem_symlink symlink;
struct kernfs_elem_attr attr; struct kernfs_elem_attr attr;
}; };
void *priv;
/* /*
* 64bit unique ID. On 64bit ino setups, id is the ino. On 32bit, * 64bit unique ID. On 64bit ino setups, id is the ino. On 32bit,
* the low 32bits are ino and upper generation. * the low 32bits are ino and upper generation.
*/ */
u64 id; u64 id;
unsigned short flags; void *priv;
umode_t mode;
struct kernfs_iattrs *iattr; struct kernfs_iattrs *iattr;
struct rcu_head rcu;
}; };
/* /*
......
...@@ -38,7 +38,7 @@ extern char uevent_helper[]; ...@@ -38,7 +38,7 @@ extern char uevent_helper[];
#endif #endif
/* counter to tag the uevent, read only except for the kobject core */ /* 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 * The actions here must match the index to the string array
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#define _LINUX_PROPERTY_H_ #define _LINUX_PROPERTY_H_
#include <linux/args.h> #include <linux/args.h>
#include <linux/array_size.h>
#include <linux/bits.h> #include <linux/bits.h>
#include <linux/fwnode.h> #include <linux/fwnode.h>
#include <linux/stddef.h> #include <linux/stddef.h>
...@@ -27,12 +28,6 @@ enum dev_prop_type { ...@@ -27,12 +28,6 @@ enum dev_prop_type {
DEV_PROP_REF, 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); const struct fwnode_handle *__dev_fwnode_const(const struct device *dev);
struct fwnode_handle *__dev_fwnode(struct device *dev); struct fwnode_handle *__dev_fwnode(struct device *dev);
#define dev_fwnode(dev) \ #define dev_fwnode(dev) \
...@@ -156,11 +151,9 @@ struct fwnode_handle *fwnode_get_next_parent(struct fwnode_handle *fwnode); ...@@ -156,11 +151,9 @@ struct fwnode_handle *fwnode_get_next_parent(struct fwnode_handle *fwnode);
for (parent = fwnode_get_parent(fwnode); parent; \ for (parent = fwnode_get_parent(fwnode); parent; \
parent = fwnode_get_next_parent(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); unsigned int fwnode_count_parents(const struct fwnode_handle *fwn);
struct fwnode_handle *fwnode_get_nth_parent(struct fwnode_handle *fwn, struct fwnode_handle *fwnode_get_nth_parent(struct fwnode_handle *fwn,
unsigned int depth); 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( struct fwnode_handle *fwnode_get_next_child_node(
const struct fwnode_handle *fwnode, struct fwnode_handle *child); const struct fwnode_handle *fwnode, struct fwnode_handle *child);
struct fwnode_handle *fwnode_get_next_available_child_node( struct fwnode_handle *fwnode_get_next_available_child_node(
......
...@@ -61,22 +61,32 @@ do { \ ...@@ -61,22 +61,32 @@ do { \
/** /**
* struct attribute_group - data structure used to declare an attribute group. * struct attribute_group - data structure used to declare an attribute group.
* @name: Optional: Attribute group name * @name: Optional: Attribute group name
* If specified, the attribute group will be created in * If specified, the attribute group will be created in a
* a new subdirectory with this name. * 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 * @is_visible: Optional: Function to return permissions associated with an
* attribute of the group. Will be called repeatedly for each * attribute of the group. Will be called repeatedly for
* non-binary attribute in the group. Only read/write * each non-binary attribute in the group. Only read/write
* permissions as well as SYSFS_PREALLOC are accepted. Must * permissions as well as SYSFS_PREALLOC are accepted. Must
* return 0 if an attribute is not visible. The returned value * return 0 if an attribute is not visible. The returned
* will replace static permissions defined in struct attribute. * 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: * @is_bin_visible:
* Optional: Function to return permissions associated with a * Optional: Function to return permissions associated with a
* binary attribute of the group. Will be called repeatedly * binary attribute of the group. Will be called repeatedly
* for each binary attribute in the group. Only read/write * for each binary attribute in the group. Only read/write
* permissions as well as SYSFS_PREALLOC are accepted. Must * permissions as well as SYSFS_PREALLOC (and the
* return 0 if a binary attribute is not visible. The returned * visibility flags for named groups) are accepted. Must
* value will replace static permissions defined in * return 0 if a binary attribute is not visible. The
* struct bin_attribute. * 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. * @attrs: Pointer to NULL terminated list of attributes.
* @bin_attrs: Pointer to NULL terminated list of binary attributes. * @bin_attrs: Pointer to NULL terminated list of binary attributes.
* Either attrs or bin_attrs or both must be provided. * Either attrs or bin_attrs or both must be provided.
...@@ -91,13 +101,121 @@ struct attribute_group { ...@@ -91,13 +101,121 @@ struct attribute_group {
struct bin_attribute **bin_attrs; 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. * Use these macros to make defining attributes easier.
* See include/linux/device.h for examples.. * See include/linux/device.h for examples..
*/ */
#define SYSFS_PREALLOC 010000
#define __ATTR(_name, _mode, _show, _store) { \ #define __ATTR(_name, _mode, _show, _store) { \
.attr = {.name = __stringify(_name), \ .attr = {.name = __stringify(_name), \
.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \ .mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
......
...@@ -39,7 +39,7 @@ static struct kobj_attribute _name##_attr = __ATTR_RW(_name) ...@@ -39,7 +39,7 @@ static struct kobj_attribute _name##_attr = __ATTR_RW(_name)
static ssize_t uevent_seqnum_show(struct kobject *kobj, static ssize_t uevent_seqnum_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf) 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); KERNEL_ATTR_RO(uevent_seqnum);
......
...@@ -7080,7 +7080,7 @@ static struct device_attribute wq_sysfs_unbound_attrs[] = { ...@@ -7080,7 +7080,7 @@ static struct device_attribute wq_sysfs_unbound_attrs[] = {
__ATTR_NULL, __ATTR_NULL,
}; };
static struct bus_type wq_subsys = { static const struct bus_type wq_subsys = {
.name = "workqueue", .name = "workqueue",
.dev_groups = wq_sysfs_groups, .dev_groups = wq_sysfs_groups,
}; };
......
...@@ -30,7 +30,7 @@ ...@@ -30,7 +30,7 @@
#include <net/net_namespace.h> #include <net/net_namespace.h>
u64 uevent_seqnum; atomic64_t uevent_seqnum;
#ifdef CONFIG_UEVENT_HELPER #ifdef CONFIG_UEVENT_HELPER
char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH; char uevent_helper[UEVENT_HELPER_PATH_LEN] = CONFIG_UEVENT_HELPER_PATH;
#endif #endif
...@@ -42,10 +42,9 @@ struct uevent_sock { ...@@ -42,10 +42,9 @@ struct uevent_sock {
#ifdef CONFIG_NET #ifdef CONFIG_NET
static LIST_HEAD(uevent_sock_list); static LIST_HEAD(uevent_sock_list);
#endif /* This lock protects uevent_sock_list */
/* This lock protects uevent_seqnum and uevent_sock_list */
static DEFINE_MUTEX(uevent_sock_mutex); static DEFINE_MUTEX(uevent_sock_mutex);
#endif
/* the strings here must match the enum in include/linux/kobject.h */ /* the strings here must match the enum in include/linux/kobject.h */
static const char *kobject_actions[] = { static const char *kobject_actions[] = {
...@@ -315,6 +314,7 @@ static int uevent_net_broadcast_untagged(struct kobj_uevent_env *env, ...@@ -315,6 +314,7 @@ static int uevent_net_broadcast_untagged(struct kobj_uevent_env *env,
int retval = 0; int retval = 0;
/* send netlink message */ /* send netlink message */
mutex_lock(&uevent_sock_mutex);
list_for_each_entry(ue_sk, &uevent_sock_list, list) { list_for_each_entry(ue_sk, &uevent_sock_list, list) {
struct sock *uevent_sock = ue_sk->sk; struct sock *uevent_sock = ue_sk->sk;
...@@ -334,6 +334,7 @@ static int uevent_net_broadcast_untagged(struct kobj_uevent_env *env, ...@@ -334,6 +334,7 @@ static int uevent_net_broadcast_untagged(struct kobj_uevent_env *env,
if (retval == -ENOBUFS || retval == -ESRCH) if (retval == -ENOBUFS || retval == -ESRCH)
retval = 0; retval = 0;
} }
mutex_unlock(&uevent_sock_mutex);
consume_skb(skb); consume_skb(skb);
return retval; return retval;
...@@ -583,16 +584,14 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, ...@@ -583,16 +584,14 @@ int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
break; break;
} }
mutex_lock(&uevent_sock_mutex);
/* we will send an event, so request a new sequence number */ /* we will send an event, so request a new sequence number */
retval = add_uevent_var(env, "SEQNUM=%llu", ++uevent_seqnum); retval = add_uevent_var(env, "SEQNUM=%llu",
if (retval) { atomic64_inc_return(&uevent_seqnum));
mutex_unlock(&uevent_sock_mutex); if (retval)
goto exit; goto exit;
}
retval = kobject_uevent_net_broadcast(kobj, env, action_string, retval = kobject_uevent_net_broadcast(kobj, env, action_string,
devpath); devpath);
mutex_unlock(&uevent_sock_mutex);
#ifdef CONFIG_UEVENT_HELPER #ifdef CONFIG_UEVENT_HELPER
/* call uevent_helper, usually only enabled during early boot */ /* 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, ...@@ -688,7 +687,8 @@ static int uevent_net_broadcast(struct sock *usk, struct sk_buff *skb,
int ret; int ret;
/* bump and prepare sequence number */ /* 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)) if (ret < 0 || (size_t)ret >= sizeof(buf))
return -ENOMEM; return -ENOMEM;
ret++; ret++;
...@@ -742,9 +742,7 @@ static int uevent_net_rcv_skb(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -742,9 +742,7 @@ static int uevent_net_rcv_skb(struct sk_buff *skb, struct nlmsghdr *nlh,
return -EPERM; return -EPERM;
} }
mutex_lock(&uevent_sock_mutex);
ret = uevent_net_broadcast(net->uevent_sock->sk, skb, extack); ret = uevent_net_broadcast(net->uevent_sock->sk, skb, extack);
mutex_unlock(&uevent_sock_mutex);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment