summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/testing/sysfs-bus-platform10
-rw-r--r--Documentation/ABI/testing/sysfs-class-devlink126
-rw-r--r--Documentation/ABI/testing/sysfs-devices-consumer8
-rw-r--r--Documentation/ABI/testing/sysfs-devices-state_synced24
-rw-r--r--Documentation/ABI/testing/sysfs-devices-supplier8
-rw-r--r--Documentation/ABI/testing/sysfs-devices-waiting_for_supplier17
-rw-r--r--Documentation/admin-guide/kernel-parameters.txt15
-rw-r--r--Documentation/driver-api/driver-model/platform.rst2
-rw-r--r--drivers/base/base.h3
-rw-r--r--drivers/base/core.c324
-rw-r--r--drivers/base/dd.c63
-rw-r--r--drivers/base/devres.c25
-rw-r--r--drivers/base/driver.c4
-rw-r--r--drivers/base/firmware_loader/fallback_platform.c5
-rw-r--r--drivers/base/firmware_loader/main.c12
-rw-r--r--drivers/base/memory.c15
-rw-r--r--drivers/base/platform.c28
-rw-r--r--drivers/base/swnode.c8
-rw-r--r--drivers/base/topology.c2
-rw-r--r--drivers/gpu/drm/bridge/lvds-codec.c10
-rw-r--r--drivers/gpu/drm/bridge/sil-sii8620.c21
-rw-r--r--fs/debugfs/file.c2
-rw-r--r--fs/debugfs/inode.c39
-rw-r--r--fs/debugfs/internal.h14
-rw-r--r--include/linux/device.h288
-rw-r--r--include/linux/kobject.h1
-rw-r--r--include/linux/sysfs.h7
-rw-r--r--kernel/trace/trace.c4
-rw-r--r--lib/Kconfig.debug40
-rw-r--r--lib/devres.c17
-rw-r--r--lib/kobject.c33
-rw-r--r--scripts/coccinelle/misc/add_namespace.cocci8
-rw-r--r--scripts/nsdeps2
-rw-r--r--tools/testing/selftests/firmware/settings8
-rw-r--r--tools/testing/selftests/kselftest/runner.sh6
35 files changed, 945 insertions, 254 deletions
diff --git a/Documentation/ABI/testing/sysfs-bus-platform b/Documentation/ABI/testing/sysfs-bus-platform
index 5172a6124b27..194ca700e962 100644
--- a/Documentation/ABI/testing/sysfs-bus-platform
+++ b/Documentation/ABI/testing/sysfs-bus-platform
@@ -18,3 +18,13 @@ Description:
devices to opt-out of driver binding using a driver_override
name such as "none". Only a single driver may be specified in
the override, there is no support for parsing delimiters.
+
+What: /sys/bus/platform/devices/.../numa_node
+Date: June 2020
+Contact: Barry Song <song.bao.hua@hisilicon.com>
+Description:
+ This file contains the NUMA node to which the platform device
+ is attached. It won't be visible if the node is unknown. The
+ value comes from an ACPI _PXM method or a similar firmware
+ source. Initial users for this file would be devices like
+ arm smmu which are populated by arm64 acpi_iort.
diff --git a/Documentation/ABI/testing/sysfs-class-devlink b/Documentation/ABI/testing/sysfs-class-devlink
new file mode 100644
index 000000000000..64791b65c9a3
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-devlink
@@ -0,0 +1,126 @@
+What: /sys/class/devlink/.../
+Date: May 2020
+Contact: Saravana Kannan <saravanak@google.com>
+Description:
+ Provide a place in sysfs for the device link objects in the
+ kernel at any given time. The name of a device link directory,
+ denoted as ... above, is of the form <supplier>--<consumer>
+ where <supplier> is the supplier device name and <consumer> is
+ the consumer device name.
+
+What: /sys/class/devlink/.../auto_remove_on
+Date: May 2020
+Contact: Saravana Kannan <saravanak@google.com>
+Description:
+ This file indicates if the device link will ever be
+ automatically removed by the driver core when the consumer and
+ supplier devices themselves are still present.
+
+ This will be one of the following strings:
+
+ 'consumer unbind'
+ 'supplier unbind'
+ 'never'
+
+ 'consumer unbind' means the device link will be removed when
+ the consumer's driver is unbound from the consumer device.
+
+ 'supplier unbind' means the device link will be removed when
+ the supplier's driver is unbound from the supplier device.
+
+ 'never' means the device link will not be automatically removed
+ when as long as the supplier and consumer devices themselves
+ are still present.
+
+What: /sys/class/devlink/.../consumer
+Date: May 2020
+Contact: Saravana Kannan <saravanak@google.com>
+Description:
+ This file is a symlink to the consumer device's sysfs directory.
+
+What: /sys/class/devlink/.../runtime_pm
+Date: May 2020
+Contact: Saravana Kannan <saravanak@google.com>
+Description:
+ This file indicates if the device link has any impact on the
+ runtime power management behavior of the consumer and supplier
+ devices. For example: Making sure the supplier doesn't enter
+ runtime suspend while the consumer is active.
+
+ This will be one of the following strings:
+
+ '0' - Does not affect runtime power management
+ '1' - Affects runtime power management
+
+What: /sys/class/devlink/.../status
+Date: May 2020
+Contact: Saravana Kannan <saravanak@google.com>
+Description:
+ This file indicates the status of the device link. The status
+ of a device link is affected by whether the supplier and
+ consumer devices have been bound to their corresponding
+ drivers. The status of a device link also affects the binding
+ and unbinding of the supplier and consumer devices with their
+ drivers and also affects whether the software state of the
+ supplier device is synced with the hardware state of the
+ supplier device after boot up.
+ See also: sysfs-devices-state_synced.
+
+ This will be one of the following strings:
+
+ 'not tracked'
+ 'dormant'
+ 'available'
+ 'consumer probing'
+ 'active'
+ 'supplier unbinding'
+ 'unknown'
+
+ 'not tracked' means this device link does not track the status
+ and has no impact on the binding, unbinding and syncing the
+ hardware and software device state.
+
+ 'dormant' means the supplier and the consumer devices have not
+ bound to their driver.
+
+ 'available' means the supplier has bound to its driver and is
+ available to supply resources to the consumer device.
+
+ 'consumer probing' means the consumer device is currently
+ trying to bind to its driver.
+
+ 'active' means the supplier and consumer devices have both
+ bound successfully to their drivers.
+
+ 'supplier unbinding' means the supplier devices is currently in
+ the process of unbinding from its driver.
+
+ 'unknown' means the state of the device link is not any of the
+ above. If this is ever the value, there's a bug in the kernel.
+
+What: /sys/class/devlink/.../supplier
+Date: May 2020
+Contact: Saravana Kannan <saravanak@google.com>
+Description:
+ This file is a symlink to the supplier device's sysfs directory.
+
+What: /sys/class/devlink/.../sync_state_only
+Date: May 2020
+Contact: Saravana Kannan <saravanak@google.com>
+Description:
+ This file indicates if the device link is limited to only
+ affecting the syncing of the hardware and software state of the
+ supplier device.
+
+ This will be one of the following strings:
+
+ '0'
+ '1' - Affects runtime power management
+
+ '0' means the device link can affect other device behaviors
+ like binding/unbinding, suspend/resume, runtime power
+ management, etc.
+
+ '1' means the device link will only affect the syncing of
+ hardware and software state of the supplier device after boot
+ up and doesn't not affect other behaviors of the devices.
diff --git a/Documentation/ABI/testing/sysfs-devices-consumer b/Documentation/ABI/testing/sysfs-devices-consumer
new file mode 100644
index 000000000000..1f06d74d1c3c
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-consumer
@@ -0,0 +1,8 @@
+What: /sys/devices/.../consumer:<consumer>
+Date: May 2020
+Contact: Saravana Kannan <saravanak@google.com>
+Description:
+ The /sys/devices/.../consumer:<consumer> are symlinks to device
+ links where this device is the supplier. <consumer> denotes the
+ name of the consumer in that device link. There can be zero or
+ more of these symlinks for a given device.
diff --git a/Documentation/ABI/testing/sysfs-devices-state_synced b/Documentation/ABI/testing/sysfs-devices-state_synced
new file mode 100644
index 000000000000..0c922d7d02fc
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-state_synced
@@ -0,0 +1,24 @@
+What: /sys/devices/.../state_synced
+Date: May 2020
+Contact: Saravana Kannan <saravanak@google.com>
+Description:
+ The /sys/devices/.../state_synced attribute is only present for
+ devices whose bus types or driver provides the .sync_state()
+ callback. The number read from it (0 or 1) reflects the value
+ of the device's 'state_synced' field. A value of 0 means the
+ .sync_state() callback hasn't been called yet. A value of 1
+ means the .sync_state() callback has been called.
+
+ Generally, if a device has sync_state() support and has some of
+ the resources it provides enabled at the time the kernel starts
+ (Eg: enabled by hardware reset or bootloader or anything that
+ run before the kernel starts), then it'll keep those resources
+ enabled and in a state that's compatible with the state they
+ were in at the start of the kernel. The device will stop doing
+ this only when the sync_state() callback has been called --
+ which happens only when all its consumer devices are registered
+ and have probed successfully. Resources that were left disabled
+ at the time the kernel starts are not affected or limited in
+ any way by sync_state() callbacks.
+
+
diff --git a/Documentation/ABI/testing/sysfs-devices-supplier b/Documentation/ABI/testing/sysfs-devices-supplier
new file mode 100644
index 000000000000..a919e0db5e90
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-supplier
@@ -0,0 +1,8 @@
+What: /sys/devices/.../supplier:<supplier>
+Date: May 2020
+Contact: Saravana Kannan <saravanak@google.com>
+Description:
+ The /sys/devices/.../supplier:<supplier> are symlinks to device
+ links where this device is the consumer. <supplier> denotes the
+ name of the supplier in that device link. There can be zero or
+ more of these symlinks for a given device.
diff --git a/Documentation/ABI/testing/sysfs-devices-waiting_for_supplier b/Documentation/ABI/testing/sysfs-devices-waiting_for_supplier
new file mode 100644
index 000000000000..59d073d20db6
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-devices-waiting_for_supplier
@@ -0,0 +1,17 @@
+What: /sys/devices/.../waiting_for_supplier
+Date: May 2020
+Contact: Saravana Kannan <saravanak@google.com>
+Description:
+ The /sys/devices/.../waiting_for_supplier attribute is only
+ present when fw_devlink kernel command line option is enabled
+ and is set to something stricter than "permissive". It is
+ removed once a device probes successfully (because the
+ information is no longer relevant). The number read from it (0
+ or 1) reflects whether the device is waiting for one or more
+ suppliers to be added and then linked to using device links
+ before the device can probe.
+
+ A value of 0 means the device is not waiting for any suppliers
+ to be added before it can probe. A value of 1 means the device
+ is waiting for one or more suppliers to be added before it can
+ probe.
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index ff5018b39ed0..254bfafdcbcd 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -832,6 +832,21 @@
useful to also enable the page_owner functionality.
on: enable the feature
+ debugfs= [KNL] This parameter enables what is exposed to userspace
+ and debugfs internal clients.
+ Format: { on, no-mount, off }
+ on: All functions are enabled.
+ no-mount:
+ Filesystem is not registered but kernel clients can
+ access APIs and a crashkernel can be used to read
+ its content. There is nothing to mount.
+ off: Filesystem is not registered and clients
+ get a -EPERM as result when trying to register files
+ or directories within debugfs.
+ This is equivalent of the runtime functionality if
+ debugfs was not enabled in the kernel at all.
+ Default value is set in build-time with a kernel configuration.
+
debugpat [X86] Enable PAT debugging
decnet.addr= [HW,NET]
diff --git a/Documentation/driver-api/driver-model/platform.rst b/Documentation/driver-api/driver-model/platform.rst
index 334dd4071ae4..1fe5c6c6199c 100644
--- a/Documentation/driver-api/driver-model/platform.rst
+++ b/Documentation/driver-api/driver-model/platform.rst
@@ -108,7 +108,7 @@ field to hold additional information.
Embedded systems frequently need one or more clocks for platform devices,
which are normally kept off until they're actively needed (to save power).
-System setup also associates those clocks with the device, so that that
+System setup also associates those clocks with the device, so that
calls to clk_get(&pdev->dev, clock_name) return them as needed.
diff --git a/drivers/base/base.h b/drivers/base/base.h
index 40fb069a8a7e..91cfb8405abd 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -93,6 +93,7 @@ struct device_private {
struct klist_node knode_class;
struct list_head deferred_probe;
struct device_driver *async_driver;
+ char *deferred_probe_reason;
struct device *device;
u8 dead:1;
};
@@ -134,6 +135,8 @@ extern void device_release_driver_internal(struct device *dev,
extern void driver_detach(struct device_driver *drv);
extern int driver_probe_device(struct device_driver *drv, struct device *dev);
extern void driver_deferred_probe_del(struct device *dev);
+extern void device_set_deferred_probe_reason(const struct device *dev,
+ struct va_format *vaf);
static inline int driver_match_device(struct device_driver *drv,
struct device *dev)
{
diff --git a/drivers/base/core.c b/drivers/base/core.c
index 05d414e9e8a4..778c7a435cbd 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -236,6 +236,210 @@ void device_pm_move_to_tail(struct device *dev)
device_links_read_unlock(idx);
}
+#define to_devlink(dev) container_of((dev), struct device_link, link_dev)
+
+static ssize_t status_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ char *status;
+
+ switch (to_devlink(dev)->status) {
+ case DL_STATE_NONE:
+ status = "not tracked"; break;
+ case DL_STATE_DORMANT:
+ status = "dormant"; break;
+ case DL_STATE_AVAILABLE:
+ status = "available"; break;
+ case DL_STATE_CONSUMER_PROBE:
+ status = "consumer probing"; break;
+ case DL_STATE_ACTIVE:
+ status = "active"; break;
+ case DL_STATE_SUPPLIER_UNBIND:
+ status = "supplier unbinding"; break;
+ default:
+ status = "unknown"; break;
+ }
+ return sprintf(buf, "%s\n", status);
+}
+static DEVICE_ATTR_RO(status);
+
+static ssize_t auto_remove_on_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct device_link *link = to_devlink(dev);
+ char *str;
+
+ if (link->flags & DL_FLAG_AUTOREMOVE_SUPPLIER)
+ str = "supplier unbind";
+ else if (link->flags & DL_FLAG_AUTOREMOVE_CONSUMER)
+ str = "consumer unbind";
+ else
+ str = "never";
+
+ return sprintf(buf, "%s\n", str);
+}
+static DEVICE_ATTR_RO(auto_remove_on);
+
+static ssize_t runtime_pm_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct device_link *link = to_devlink(dev);
+
+ return sprintf(buf, "%d\n", !!(link->flags & DL_FLAG_PM_RUNTIME));
+}
+static DEVICE_ATTR_RO(runtime_pm);
+
+static ssize_t sync_state_only_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct device_link *link = to_devlink(dev);
+
+ return sprintf(buf, "%d\n", !!(link->flags & DL_FLAG_SYNC_STATE_ONLY));
+}
+static DEVICE_ATTR_RO(sync_state_only);
+
+static struct attribute *devlink_attrs[] = {
+ &dev_attr_status.attr,
+ &dev_attr_auto_remove_on.attr,
+ &dev_attr_runtime_pm.attr,
+ &dev_attr_sync_state_only.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(devlink);
+
+static void device_link_free(struct device_link *link)
+{
+ while (refcount_dec_not_one(&link->rpm_active))
+ pm_runtime_put(link->supplier);
+
+ put_device(link->consumer);
+ put_device(link->supplier);
+ kfree(link);
+}
+
+#ifdef CONFIG_SRCU
+static void __device_link_free_srcu(struct rcu_head *rhead)
+{
+ device_link_free(container_of(rhead, struct device_link, rcu_head));
+}
+
+static void devlink_dev_release(struct device *dev)
+{
+ struct device_link *link = to_devlink(dev);
+
+ call_srcu(&device_links_srcu, &link->rcu_head, __device_link_free_srcu);
+}
+#else
+static void devlink_dev_release(struct device *dev)
+{
+ device_link_free(to_devlink(dev));
+}
+#endif
+
+static struct class devlink_class = {
+ .name = "devlink",
+ .owner = THIS_MODULE,
+ .dev_groups = devlink_groups,
+ .dev_release = devlink_dev_release,
+};
+
+static int devlink_add_symlinks(struct device *dev,
+ struct class_interface *class_intf)
+{
+ int ret;
+ size_t len;
+ struct device_link *link = to_devlink(dev);
+ struct device *sup = link->supplier;
+ struct device *con = link->consumer;
+ char *buf;
+
+ len = max(strlen(dev_name(sup)), strlen(dev_name(con)));
+ len += strlen("supplier:") + 1;
+ buf = kzalloc(len, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = sysfs_create_link(&link->link_dev.kobj, &sup->kobj, "supplier");
+ if (ret)
+ goto out;
+
+ ret = sysfs_create_link(&link->link_dev.kobj, &con->kobj, "consumer");
+ if (ret)
+ goto err_con;
+
+ snprintf(buf, len, "consumer:%s", dev_name(con));
+ ret = sysfs_create_link(&sup->kobj, &link->link_dev.kobj, buf);
+ if (ret)
+ goto err_con_dev;
+
+ snprintf(buf, len, "supplier:%s", dev_name(sup));
+ ret = sysfs_create_link(&con->kobj, &link->link_dev.kobj, buf);
+ if (ret)
+ goto err_sup_dev;
+
+ goto out;
+
+err_sup_dev:
+ snprintf(buf, len, "consumer:%s", dev_name(con));
+ sysfs_remove_link(&sup->kobj, buf);
+err_con_dev:
+ sysfs_remove_link(&link->link_dev.kobj, "consumer");
+err_con:
+ sysfs_remove_link(&link->link_dev.kobj, "supplier");
+out:
+ kfree(buf);
+ return ret;
+}
+
+static void devlink_remove_symlinks(struct device *dev,
+ struct class_interface *class_intf)
+{
+ struct device_link *link = to_devlink(dev);
+ size_t len;
+ struct device *sup = link->supplier;
+ struct device *con = link->consumer;
+ char *buf;
+
+ sysfs_remove_link(&link->link_dev.kobj, "consumer");
+ sysfs_remove_link(&link->link_dev.kobj, "supplier");
+
+ len = max(strlen(dev_name(sup)), strlen(dev_name(con)));
+ len += strlen("supplier:") + 1;
+ buf = kzalloc(len, GFP_KERNEL);
+ if (!buf) {
+ WARN(1, "Unable to properly free device link symlinks!\n");
+ return;
+ }
+
+ snprintf(buf, len, "supplier:%s", dev_name(sup));
+ sysfs_remove_link(&con->kobj, buf);
+ snprintf(buf, len, "consumer:%s", dev_name(con));
+ sysfs_remove_link(&sup->kobj, buf);
+ kfree(buf);
+}
+
+static struct class_interface devlink_class_intf = {
+ .class = &devlink_class,
+ .add_dev = devlink_add_symlinks,
+ .remove_dev = devlink_remove_symlinks,
+};
+
+static int __init devlink_class_init(void)
+{
+ int ret;
+
+ ret = class_register(&devlink_class);
+ if (ret)
+ return ret;
+
+ ret = class_interface_register(&devlink_class_intf);
+ if (ret)
+ class_unregister(&devlink_class);
+
+ return ret;
+}
+postcore_initcall(devlink_class_init);
+
#define DL_MANAGED_LINK_FLAGS (DL_FLAG_AUTOREMOVE_CONSUMER | \
DL_FLAG_AUTOREMOVE_SUPPLIER | \
DL_FLAG_AUTOPROBE_CONSUMER | \
@@ -408,13 +612,6 @@ struct device_link *device_link_add(struct device *consumer,
refcount_set(&link->rpm_active, 1);
- if (flags & DL_FLAG_PM_RUNTIME) {
- if (flags & DL_FLAG_RPM_ACTIVE)
- refcount_inc(&link->rpm_active);
-
- pm_runtime_new_link(consumer);
- }
-
get_device(supplier);
link->supplier = supplier;
INIT_LIST_HEAD(&link->s_node);
@@ -424,6 +621,25 @@ struct device_link *device_link_add(struct device *consumer,
link->flags = flags;
kref_init(&link->kref);
+ link->link_dev.class = &devlink_class;
+ device_set_pm_not_required(&link->link_dev);
+ dev_set_name(&link->link_dev, "%s--%s",
+ dev_name(supplier), dev_name(consumer));
+ if (device_register(&link->link_dev)) {
+ put_device(consumer);
+ put_device(supplier);
+ kfree(link);
+ link = NULL;
+ goto out;
+ }
+
+ if (flags & DL_FLAG_PM_RUNTIME) {
+ if (flags & DL_FLAG_RPM_ACTIVE)
+ refcount_inc(&link->rpm_active);
+
+ pm_runtime_new_link(consumer);
+ }
+
/* Determine the initial link state. */
if (flags & DL_FLAG_STATELESS)
link->status = DL_STATE_NONE;
@@ -539,22 +755,7 @@ static void device_link_add_missing_supplier_links(void)
mutex_unlock(&wfs_lock);
}
-static void device_link_free(struct device_link *link)
-{
- while (refcount_dec_not_one(&link->rpm_active))
- pm_runtime_put(link->supplier);
-
- put_device(link->consumer);
- put_device(link->supplier);
- kfree(link);
-}
-
#ifdef CONFIG_SRCU
-static void __device_link_free_srcu(struct rcu_head *rhead)
-{
- device_link_free(container_of(rhead, struct device_link, rcu_head));
-}
-
static void __device_link_del(struct kref *kref)
{
struct device_link *link = container_of(kref, struct device_link, kref);
@@ -567,7 +768,7 @@ static void __device_link_del(struct kref *kref)
list_del_rcu(&link->s_node);
list_del_rcu(&link->c_node);
- call_srcu(&device_links_srcu, &link->rcu_head, __device_link_free_srcu);
+ device_unregister(&link->link_dev);
}
#else /* !CONFIG_SRCU */
static void __device_link_del(struct kref *kref)
@@ -582,7 +783,7 @@ static void __device_link_del(struct kref *kref)
list_del(&link->s_node);
list_del(&link->c_node);
- device_link_free(link);
+ device_unregister(&link->link_dev);
}
#endif /* !CONFIG_SRCU */
@@ -850,6 +1051,22 @@ static void device_link_drop_managed(struct device_link *link)
kref_put(&link->kref, __device_link_del);
}
+static ssize_t waiting_for_supplier_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ bool val;
+
+ device_lock(dev);
+ mutex_lock(&wfs_lock);
+ val = !list_empty(&dev->links.needs_suppliers)
+ && dev->links.need_for_probe;
+ mutex_unlock(&wfs_lock);
+ device_unlock(dev);
+ return sprintf(buf, "%u\n", val);
+}
+static DEVICE_ATTR_RO(waiting_for_supplier);
+
/**
* device_links_driver_bound - Update device links after probing its driver.
* @dev: Device to update the links for.
@@ -874,6 +1091,7 @@ void device_links_driver_bound(struct device *dev)
mutex_lock(&wfs_lock);
list_del_init(&dev->links.needs_suppliers);
mutex_unlock(&wfs_lock);
+ device_remove_file(dev, &dev_attr_waiting_for_supplier);
device_links_write_lock();
@@ -1160,6 +1378,9 @@ static void device_links_purge(struct device *dev)
{
struct device_link *link, *ln;
+ if (dev->class == &devlink_class)
+ return;
+
mutex_lock(&wfs_lock);
list_del(&dev->links.needs_suppliers);
mutex_unlock(&wfs_lock);
@@ -1969,8 +2190,16 @@ static int device_add_attrs(struct device *dev)
goto err_remove_dev_groups;
}
+ if (fw_devlink_flags && !fw_devlink_is_permissive()) {
+ error = device_create_file(dev, &dev_attr_waiting_for_supplier);
+ if (error)
+ goto err_remove_dev_online;
+ }
+
return 0;
+ err_remove_dev_online:
+ device_remove_file(dev, &dev_attr_online);
err_remove_dev_groups:
device_remove_groups(dev, dev->groups);
err_remove_type_groups:
@@ -1988,6 +2217,7 @@ static void device_remove_attrs(struct device *dev)
struct class *class = dev->class;
const struct device_type *type = dev->type;
+ device_remove_file(dev, &dev_attr_waiting_for_supplier);
device_remove_file(dev, &dev_attr_online);
device_remove_groups(dev, dev->groups);
@@ -3973,6 +4203,52 @@ define_dev_printk_level(_dev_info, KERN_INFO);
#endif
+/**
+ * dev_err_probe - probe error check and log helper
+ * @dev: the pointer to the struct device
+ * @err: error value to test
+ * @fmt: printf-style format string
+ * @...: arguments as specified in the format string
+ *
+ * This helper implements common pattern present in probe functions for error
+ * checking: print debug or error message depending if the error value is
+ * -EPROBE_DEFER and propagate error upwards.
+ * In case of -EPROBE_DEFER it sets also defer probe reason, which can be
+ * checked later by reading devices_deferred debugfs attribute.
+ * It replaces code sequence:
+ * if (err != -EPROBE_DEFER)
+ * dev_err(dev, ...);
+ * else
+ * dev_dbg(dev, ...);
+ * return err;
+ * with
+ * return dev_err_probe(dev, err, ...);
+ *
+ * Returns @err.
+ *
+ */
+int dev_err_probe(const struct device *dev, int err, const char *fmt, ...)
+{
+ struct va_format vaf;
+ va_list args;
+
+ va_start(args, fmt);
+ vaf.fmt = fmt;
+ vaf.va = &args;
+
+ if (err != -EPROBE_DEFER) {
+ dev_err(dev, "error %d: %pV", err, &vaf);
+ } else {
+ device_set_deferred_probe_reason(dev, &vaf);
+ dev_dbg(dev, "error %d: %pV", err, &vaf);
+ }
+
+ va_end(args);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(dev_err_probe);
+
static inline bool fwnode_is_primary(struct fwnode_handle *fwnode)
{
return fwnode && !IS_ERR(fwnode->secondary);
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 48ca81cb8ebc..857b0a928e8d 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -27,6 +27,7 @@
#include <linux/async.h>
#include <linux/pm_runtime.h>
#include <linux/pinctrl/devinfo.h>
+#include <linux/slab.h>
#include "base.h"
#include "power/power.h"
@@ -136,6 +137,8 @@ void driver_deferred_probe_del(struct device *dev)
if (!list_empty(&dev->p->deferred_probe)) {
dev_dbg(dev, "Removed from deferred list\n");
list_del_init(&dev->p->deferred_probe);
+ kfree(dev->p->deferred_probe_reason);
+ dev->p->deferred_probe_reason = NULL;
}
mutex_unlock(&deferred_probe_mutex);
}
@@ -206,6 +209,23 @@ void device_unblock_probing(void)
driver_deferred_probe_trigger();
}
+/**
+ * device_set_deferred_probe_reason() - Set defer probe reason message for device
+ * @dev: the pointer to the struct device
+ * @vaf: the pointer to va_format structure with message
+ */
+void device_set_deferred_probe_reason(const struct device *dev, struct va_format *vaf)
+{
+ const char *drv = dev_driver_string(dev);
+
+ mutex_lock(&deferred_probe_mutex);
+
+ kfree(dev->p->deferred_probe_reason);
+ dev->p->deferred_probe_reason = kasprintf(GFP_KERNEL, "%s: %pV", drv, vaf);
+
+ mutex_unlock(&deferred_probe_mutex);
+}
+
/*
* deferred_devs_show() - Show the devices in the deferred probe pending list.
*/
@@ -216,7 +236,8 @@ static int deferred_devs_show(struct seq_file *s, void *data)
mutex_lock(&deferred_probe_mutex);
list_for_each_entry(curr, &deferred_probe_pending_list, deferred_probe)
- seq_printf(s, "%s\n", dev_name(curr->device));
+ seq_printf(s, "%s\t%s", dev_name(curr->device),
+ curr->device->p->deferred_probe_reason ?: "\n");
mutex_unlock(&deferred_probe_mutex);
@@ -276,7 +297,7 @@ static void deferred_probe_timeout_work_func(struct work_struct *work)
list_for_each_entry_safe(private, p, &deferred_probe_pending_list, deferred_probe)
dev_info(private->device, "deferred probe pending\n");
- wake_up(&probe_timeout_waitqueue);
+ wake_up_all(&probe_timeout_waitqueue);
}
static DECLARE_DELAYED_WORK(deferred_probe_timeout_work, deferred_probe_timeout_work_func);
@@ -425,10 +446,9 @@ static void driver_sysfs_remove(struct device *dev)
* Allow manual attachment of a driver to a device.
* Caller must have already set @dev->driver.
*
- * Note that this does not modify the bus reference count
- * nor take the bus's rwsem. Please verify those are accounted
- * for before calling this. (It is ok to call with no other effort
- * from a driver's probe() method.)
+ * Note that this does not modify the bus reference count.
+ * Please verify that is accounted for before calling this.
+ * (It is ok to call with no other effort from a driver's probe() method.)
*
* This function must be called with the device lock held.
*/
@@ -458,6 +478,18 @@ static void driver_deferred_probe_add_trigger(struct device *dev,
driver_deferred_probe_trigger();
}
+static ssize_t state_synced_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ bool val;
+
+ device_lock(dev);
+ val = dev->state_synced;
+ device_unlock(dev);
+ return sprintf(buf, "%u\n", val);
+}
+static DEVICE_ATTR_RO(state_synced);
+
static int really_probe(struct device *dev, struct device_driver *drv)
{
int ret = -EPROBE_DEFER;
@@ -487,7 +519,8 @@ static int really_probe(struct device *dev, struct device_driver *drv)
drv->bus->name, __func__, drv->name, dev_name(dev));
if (!list_empty(&dev->devres_head)) {
dev_crit(dev, "Resources present before probing\n");
- return -EBUSY;
+ ret = -EBUSY;
+ goto done;
}
re_probe:
@@ -531,9 +564,16 @@ re_probe:
goto dev_groups_failed;
}
+ if (dev_has_sync_state(dev) &&
+ device_create_file(dev, &dev_attr_state_synced)) {
+ dev_err(dev, "state_synced sysfs add failed\n");
+ goto dev_sysfs_state_synced_failed;
+ }
+
if (test_remove) {
test_remove = false;
+ device_remove_file(dev, &dev_attr_state_synced);
device_remove_groups(dev, drv->dev_groups);
if (dev->bus->remove)
@@ -563,6 +603,8 @@ re_probe:
drv->bus->name, __func__, dev_name(dev), drv->name);
goto done;
+dev_sysfs_state_synced_failed:
+ device_remove_groups(dev, drv->dev_groups);
dev_groups_failed:
if (dev->bus->remove)
dev->bus->remove(dev);
@@ -607,7 +649,7 @@ pinctrl_bind_failed:
ret = 0;
done:
atomic_dec(&probe_count);
- wake_up(&probe_waitqueue);
+ wake_up_all(&probe_waitqueue);
return ret;
}
@@ -843,7 +885,9 @@ static int __device_attach(struct device *dev, bool allow_async)
int ret = 0;
device_lock(dev);
- if (dev->driver) {
+ if (dev->p->dead) {
+ goto out_unlock;
+ } else if (dev->driver) {
if (device_is_bound(dev)) {
ret = 1;
goto out_unlock;
@@ -1100,6 +1144,7 @@ static void __device_release_driver(struct device *dev, struct device *parent)
pm_runtime_put_sync(dev);
+ device_remove_file(dev, &dev_attr_state_synced);
device_remove_groups(dev, drv->dev_groups);
if (dev->bus && dev->bus->remove)
diff --git a/drivers/base/devres.c b/drivers/base/devres.c
index 0bbb328bd17f..ed615d3b9cf1 100644
--- a/drivers/base/devres.c
+++ b/drivers/base/devres.c
@@ -89,15 +89,23 @@ static struct devres_group * node_to_group(struct devres_node *node)
return NULL;
}
+static bool check_dr_size(size_t size, size_t *tot_size)
+{
+ /* We must catch any near-SIZE_MAX cases that could overflow. */
+ if (unlikely(check_add_overflow(sizeof(struct devres),
+ size, tot_size)))
+ return false;
+
+ return true;
+}
+
static __always_inline struct devres * alloc_dr(dr_release_t release,
size_t size, gfp_t gfp, int nid)
{
size_t tot_size;
struct devres *dr;
- /* We must catch any near-SIZE_MAX cases that could overflow. */
- if (unlikely(check_add_overflow(sizeof(struct devres), size,
- &tot_size)))
+ if (!check_dr_size(size, &tot_size))
return NULL;
dr = kmalloc_node_track_caller(tot_size, gfp, nid);
@@ -807,10 +815,13 @@ static int devm_kmalloc_match(struct device *dev, void *res, void *data)
* RETURNS:
* Pointer to allocated memory on success, NULL on failure.
*/
-void * devm_kmalloc(struct device *dev, size_t size, gfp_t gfp)
+void *devm_kmalloc(struct device *dev, size_t size, gfp_t gfp)
{
struct devres *dr;
+ if (unlikely(!size))
+ return ZERO_SIZE_PTR;
+
/* use raw alloc_dr for kmalloc caller tracing */
dr = alloc_dr(devm_kmalloc_release, size, gfp, dev_to_node(dev));
if (unlikely(!dr))
@@ -942,10 +953,10 @@ void devm_kfree(struct device *dev, const void *p)
int rc;
/*
- * Special case: pointer to a string in .rodata returned by
- * devm_kstrdup_const().
+ * Special cases: pointer to a string in .rodata returned by
+ * devm_kstrdup_const() or NULL/ZERO ptr.
*/
- if (unlikely(is_kernel_rodata((unsigned long)p)))
+ if (unlikely(is_kernel_rodata((unsigned long)p) || ZERO_OR_NULL_PTR(p)))
return;
rc = devres_destroy(dev, devm_kmalloc_release,
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index 57c68769e157..8c0d33e182fd 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -158,12 +158,12 @@ int driver_register(struct device_driver *drv)
if ((drv->bus->probe && drv->probe) ||
(drv->bus->remove && drv->remove) ||
(drv->bus->shutdown && drv->shutdown))
- printk(KERN_WARNING "Driver '%s' needs updating - please use "
+ pr_warn("Driver '%s' needs updating - please use "
"bus_type methods\n", drv->name);
other = driver_find(drv->name, drv->bus);
if (other) {
- printk(KERN_ERR "Error: Driver '%s' is already registered, "
+ pr_err("Error: Driver '%s' is already registered, "
"aborting...\n", drv->name);
return -EBUSY;
}
diff --git a/drivers/base/firmware_loader/fallback_platform.c b/drivers/base/firmware_loader/fallback_platform.c
index cdd2c9a9f38a..685edb7dd05a 100644
--- a/drivers/base/firmware_loader/fallback_platform.c
+++ b/drivers/base/firmware_loader/fallback_platform.c
@@ -25,7 +25,10 @@ int firmware_fallback_platform(struct fw_priv *fw_priv, u32 opt_flags)
if (rc)
return rc; /* rc == -ENOENT when the fw was not found */
- fw_priv->data = vmalloc(size);
+ if (fw_priv->data && size > fw_priv->allocated_size)
+ return -ENOMEM;
+ if (!fw_priv->data)
+ fw_priv->data = vmalloc(size);
if (!fw_priv->data)
return -ENOMEM;
diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c
index ca871b13524e..9da0c9d5f538 100644
--- a/drivers/base/firmware_loader/main.c
+++ b/drivers/base/firmware_loader/main.c
@@ -838,12 +838,12 @@ EXPORT_SYMBOL(request_firmware);
* @name: name of firmware file
* @device: device for which firmware is being loaded
*
- * This function is similar in behaviour to request_firmware(), except
- * it doesn't produce warning messages when the file is not found.
- * The sysfs fallback mechanism is enabled if direct filesystem lookup fails,
- * however, however failures to find the firmware file with it are still
- * suppressed. It is therefore up to the driver to check for the return value
- * of this call and to decide when to inform the users of errors.
+ * This function is similar in behaviour to request_firmware(), except it
+ * doesn't produce warning messages when the file is not found. The sysfs
+ * fallback mechanism is enabled if direct filesystem lookup fails. However,
+ * failures to find the firmware file with it are still suppressed. It is
+ * therefore up to the driver to check for the return value of this call and to
+ * decide when to inform the users of errors.
**/
int firmware_request_nowarn(const struct firmware **firmware, const char *name,
struct device *device)
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index 2b09b68b9f78..4db3c660de83 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -50,14 +50,14 @@ int memhp_online_type_from_str(const char *str)
static int sections_per_block;
-static inline unsigned long base_memory_block_id(unsigned long section_nr)
+static inline unsigned long memory_block_id(unsigned long section_nr)
{
return section_nr / sections_per_block;
}
static inline unsigned long pfn_to_block_id(unsigned long pfn)
{
- return base_memory_block_id(pfn_to_section_nr(pfn));
+ return memory_block_id(pfn_to_section_nr(pfn));
}
static inline unsigned long phys_to_block_id(unsigned long phys)
@@ -517,7 +517,7 @@ static struct memory_block *find_memory_block_by_id(unsigned long block_id)
*/
struct memory_block *find_memory_block(struct mem_section *section)
{
- unsigned long block_id = base_memory_block_id(__section_nr(section));
+ unsigned long block_id = memory_block_id(__section_nr(section));
return find_memory_block_by_id(block_id);
}
@@ -570,8 +570,7 @@ int register_memory(struct memory_block *memory)
return ret;
}
-static int init_memory_block(struct memory_block **memory,
- unsigned long block_id, unsigned long state)
+static int init_memory_block(unsigned long block_id, unsigned long state)
{
struct memory_block *mem;
unsigned long start_pfn;
@@ -594,14 +593,12 @@ static int init_memory_block(struct memory_block **memory,
ret = register_memory(mem);
- *memory = mem;
return ret;
}
static int add_memory_block(unsigned long base_section_nr)
{
int section_count = 0;
- struct memory_block *mem;
unsigned long nr;
for (nr = base_section_nr; nr < base_section_nr + sections_per_block;
@@ -611,7 +608,7 @@ static int add_memory_block(unsigned long base_section_nr)
if (section_count == 0)
return 0;
- return init_memory_block(&mem, base_memory_block_id(base_section_nr),
+ return init_memory_block(memory_block_id(base_section_nr),
MEM_ONLINE);
}
@@ -647,7 +644,7 @@ int create_memory_block_devices(unsigned long start, unsigned long size)
return -EINVAL;
for (block_id = start_block_id; block_id != end_block_id; block_id++) {
- ret = init_memory_block(&mem, block_id, MEM_OFFLINE);
+ ret = init_memory_block(block_id, MEM_OFFLINE);
if (ret)
break;
}
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index c0d0a5490ac6..e5d8a0503b4f 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -1019,7 +1019,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
if (len != -ENODEV)
return len;
- len = acpi_device_modalias(dev, buf, PAGE_SIZE -1);
+ len = acpi_device_modalias(dev, buf, PAGE_SIZE - 1);
if (len != -ENODEV)
return len;
@@ -1076,13 +1076,37 @@ static ssize_t driver_override_show(struct device *dev,
}
static DEVICE_ATTR_RW(driver_override);
+static ssize_t numa_node_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", dev_to_node(dev));
+}
+static DEVICE_ATTR_RO(numa_node);
+
+static umode_t platform_dev_attrs_visible(struct kobject *kobj, struct attribute *a,
+ int n)
+{
+ struct device *dev = container_of(kobj, typeof(*dev), kobj);
+
+ if (a == &dev_attr_numa_node.attr &&
+ dev_to_node(dev) == NUMA_NO_NODE)
+ return 0;
+
+ return a->mode;
+}
static struct attribute *platform_dev_attrs[] = {
&dev_attr_modalias.attr,
+ &dev_attr_numa_node.attr,
&dev_attr_driver_override.attr,
NULL,
};
-ATTRIBUTE_GROUPS(platform_dev);
+
+static struct attribute_group platform_dev_group = {
+ .attrs = platform_dev_attrs,
+ .is_visible = platform_dev_attrs_visible,
+};
+__ATTRIBUTE_GROUPS(platform_dev);
static int platform_uevent(struct device *dev, struct kobj_uevent_env *env)
{
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index e5eb27375416..010828fc785b 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -761,17 +761,13 @@ EXPORT_SYMBOL_GPL(software_node_register_node_group);
*/
void software_node_unregister_node_group(const struct software_node **node_group)
{
- struct swnode *swnode;
unsigned int i;
if (!node_group)
return;
- for (i = 0; node_group[i]; i++) {
- swnode = software_node_to_swnode(node_group[i]);
- if (swnode)
- fwnode_remove_software_node(&swnode->fwnode);
- }
+ for (i = 0; node_group[i]; i++)
+ software_node_unregister(node_group[i]);
}
EXPORT_SYMBOL_GPL(software_node_unregister_node_group);
diff --git a/drivers/base/topology.c b/drivers/base/topology.c
index 4e033d4cc0dc..ad8d33c6077b 100644
--- a/drivers/base/topology.c
+++ b/drivers/base/topology.c
@@ -133,7 +133,7 @@ static int topology_remove_dev(unsigned int cpu)
return 0;
}
-static int topology_sysfs_init(void)
+static int __init topology_sysfs_init(void)
{
return cpuhp_setup_state(CPUHP_TOPOLOGY_PREPARE,
"base/topology:prepare", topology_add_dev,
diff --git a/drivers/gpu/drm/bridge/lvds-codec.c b/drivers/gpu/drm/bridge/lvds-codec.c
index 24fb1befdfa2..f19d9f7a5db2 100644
--- a/drivers/gpu/drm/bridge/lvds-codec.c
+++ b/drivers/gpu/drm/bridge/lvds-codec.c
@@ -71,13 +71,9 @@ static int lvds_codec_probe(struct platform_device *pdev)
lvds_codec->connector_type = (uintptr_t)of_device_get_match_data(dev);
lvds_codec->powerdown_gpio = devm_gpiod_get_optional(dev, "powerdown",
GPIOD_OUT_HIGH);
- if (IS_ERR(lvds_codec->powerdown_gpio)) {
- int err = PTR_ERR(lvds_codec->powerdown_gpio);
-
- if (err != -EPROBE_DEFER)
- dev_err(dev, "powerdown GPIO failure: %d\n", err);
- return err;
- }
+ if (IS_ERR(lvds_codec->powerdown_gpio))
+ return dev_err_probe(dev, PTR_ERR(lvds_codec->powerdown_gpio),
+ "powerdown GPIO failure\n");
/* Locate the panel DT node. */
panel_node = of_graph_get_remote_node(dev->of_node, 1, 0);
diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c
index 6cd8e012de5d..a282dc962199 100644
--- a/drivers/gpu/drm/bridge/sil-sii8620.c
+++ b/drivers/gpu/drm/bridge/sil-sii8620.c
@@ -2299,10 +2299,9 @@ static int sii8620_probe(struct i2c_client *client,
INIT_LIST_HEAD(&ctx->mt_queue);
ctx->clk_xtal = devm_clk_get(dev, "xtal");
- if (IS_ERR(ctx->clk_xtal)) {
- dev_err(dev, "failed to get xtal clock from DT\n");
- return PTR_ERR(ctx->clk_xtal);
- }
+ if (IS_ERR(ctx->clk_xtal))
+ return dev_err_probe(dev, PTR_ERR(ctx->clk_xtal),
+ "failed to get xtal clock from DT\n");
if (!client->irq) {
dev_err(dev, "no irq provided\n");
@@ -2313,16 +2312,14 @@ static int sii8620_probe(struct i2c_client *client,
sii8620_irq_thread,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
"sii8620", ctx);
- if (ret < 0) {
- dev_err(dev, "failed to install IRQ handler\n");
- return ret;
- }
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "failed to install IRQ handler\n");
ctx->gpio_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
- if (IS_ERR(ctx->gpio_reset)) {
- dev_err(dev, "failed to get reset gpio from DT\n");
- return PTR_ERR(ctx->gpio_reset);
- }
+ if (IS_ERR(ctx->gpio_reset))
+ return dev_err_probe(dev, PTR_ERR(ctx->gpio_reset),
+ "failed to get reset gpio from DT\n");
ctx->supplies[0].supply = "cvcc10";
ctx->supplies[1].supply = "iovcc18";
diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index ae49a55bda00..3753c4c484fc 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -273,7 +273,7 @@ static int full_proxy_release(struct inode *inode, struct file *filp)
r = real_fops->release(inode, filp);
replace_fops(filp, d_inode(dentry)->i_fop);
- kfree((void *)proxy_fops);
+ kfree(proxy_fops);
fops_put(real_fops);
return r;
}
diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c
index b7f2e971ecbc..2fcf66473436 100644
--- a/fs/debugfs/inode.c
+++ b/fs/debugfs/inode.c
@@ -35,6 +35,7 @@
static struct vfsmount *debugfs_mount;
static int debugfs_mount_count;
static bool debugfs_registered;
+static unsigned int debugfs_allow = DEFAULT_DEBUGFS_ALLOW_BITS;
/*
* Don't allow access attributes to be changed whilst the kernel is locked down
@@ -266,6 +267,9 @@ static struct dentry *debug_mount(struct file_system_type *fs_type,
int flags, const char *dev_name,
void *data)
{
+ if (!(debugfs_allow & DEBUGFS_ALLOW_API))
+ return ERR_PTR(-EPERM);
+
return mount_single(fs_type, flags, data, debug_fill_super);
}
@@ -311,6 +315,9 @@ static struct dentry *start_creating(const char *name, struct dentry *parent)
struct dentry *dentry;
int error;
+ if (!(debugfs_allow & DEBUGFS_ALLOW_API))
+ return ERR_PTR(-EPERM);
+
pr_debug("creating file '%s'\n", name);
if (IS_ERR(parent))
@@ -385,6 +392,11 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
if (IS_ERR(dentry))
return dentry;
+ if (!(debugfs_allow & DEBUGFS_ALLOW_API)) {
+ failed_creating(dentry);
+ return ERR_PTR(-EPERM);
+ }
+
inode = debugfs_get_inode(dentry->d_sb);
if (unlikely(!inode)) {
pr_err("out of free dentries, can not create file '%s'\n",
@@ -541,6 +553,11 @@ struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
if (IS_ERR(dentry))
return dentry;
+ if (!(debugfs_allow & DEBUGFS_ALLOW_API)) {
+ failed_creating(dentry);
+ return ERR_PTR(-EPERM);
+ }
+
inode = debugfs_get_inode(dentry->d_sb);
if (unlikely(!inode)) {
pr_err("out of free dentries, can not create directory '%s'\n",
@@ -583,6 +600,11 @@ struct dentry *debugfs_create_automount(const char *name,
if (IS_ERR(dentry))
return dentry;
+ if (!(debugfs_allow & DEBUGFS_ALLOW_API)) {
+ failed_creating(dentry);
+ return ERR_PTR(-EPERM);
+ }
+
inode = debugfs_get_inode(dentry->d_sb);
if (unlikely(!inode)) {
pr_err("out of free dentries, can not create automount '%s'\n",
@@ -786,10 +808,27 @@ bool debugfs_initialized(void)
}
EXPORT_SYMBOL_GPL(debugfs_initialized);
+static int __init debugfs_kernel(char *str)
+{
+ if (str) {
+ if (!strcmp(str, "on"))
+ debugfs_allow = DEBUGFS_ALLOW_API | DEBUGFS_ALLOW_MOUNT;
+ else if (!strcmp(str, "no-mount"))
+ debugfs_allow = DEBUGFS_ALLOW_API;
+ else if (!strcmp(str, "off"))
+ debugfs_allow = 0;
+ }
+
+ return 0;
+}
+early_param("debugfs", debugfs_kernel);
static int __init debugfs_init(void)
{
int retval;
+ if (!(debugfs_allow & DEBUGFS_ALLOW_MOUNT))
+ return -EPERM;
+
retval = sysfs_create_mount_point(kernel_kobj, "debug");
if (retval)
return retval;
diff --git a/fs/debugfs/internal.h b/fs/debugfs/internal.h
index 034e6973cead..92af8ae31313 100644
--- a/fs/debugfs/internal.h
+++ b/fs/debugfs/internal.h
@@ -29,4 +29,18 @@ struct debugfs_fsdata {
*/
#define DEBUGFS_FSDATA_IS_REAL_FOPS_BIT BIT(0)
+/* Access BITS */
+#define DEBUGFS_ALLOW_API BIT(0)
+#define DEBUGFS_ALLOW_MOUNT BIT(1)
+
+#ifdef CONFIG_DEBUG_FS_ALLOW_ALL
+#define DEFAULT_DEBUGFS_ALLOW_BITS (DEBUGFS_ALLOW_MOUNT | DEBUGFS_ALLOW_API)
+#endif
+#ifdef CONFIG_DEBUG_FS_DISALLOW_MOUNT
+#define DEFAULT_DEBUGFS_ALLOW_BITS (DEBUGFS_ALLOW_API)
+#endif
+#ifdef CONFIG_DEBUG_FS_ALLOW_NONE
+#define DEFAULT_DEBUGFS_ALLOW_BITS (0)
+#endif
+
#endif /* _DEBUGFS_INTERNAL_H_ */
diff --git a/include/linux/device.h b/include/linux/device.h
index 06d3b7622082..9abc5d5062e0 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -129,8 +129,12 @@ ssize_t device_store_bool(struct device *dev, struct device_attribute *attr,
__ATTR_PREALLOC(_name, _mode, _show, _store)
#define DEVICE_ATTR_RW(_name) \
struct device_attribute dev_attr_##_name = __ATTR_RW(_name)
+#define DEVICE_ATTR_ADMIN_RW(_name) \
+ struct device_attribute dev_attr_##_name = __ATTR_RW_MODE(_name, 0600)
#define DEVICE_ATTR_RO(_name) \
struct device_attribute dev_attr_##_name = __ATTR_RO(_name)
+#define DEVICE_ATTR_ADMIN_RO(_name) \
+ struct device_attribute dev_attr_##_name = __ATTR_RO_MODE(_name, 0400)
#define DEVICE_ATTR_WO(_name) \
struct device_attribute dev_attr_##_name = __ATTR_WO(_name)
#define DEVICE_ULONG_ATTR(_name, _mode, _var) \
@@ -146,68 +150,66 @@ ssize_t device_store_bool(struct device *dev, struct device_attribute *attr,
struct device_attribute dev_attr_##_name = \
__ATTR_IGNORE_LOCKDEP(_name, _mode, _show, _store)
-extern int device_create_file(struct device *device,
- const struct device_attribute *entry);
-extern void device_remove_file(struct device *dev,
- const struct device_attribute *attr);
-extern bool device_remove_file_self(struct device *dev,
- const struct device_attribute *attr);
-extern int __must_check device_create_bin_file(struct device *dev,
+int device_create_file(struct device *device,
+ const struct device_attribute *entry);
+void device_remove_file(struct device *dev,
+ const struct device_attribute *attr);
+bool device_remove_file_self(struct device *dev,
+ const struct device_attribute *attr);
+int __must_check device_create_bin_file(struct device *dev,
const struct bin_attribute *attr);
-extern void device_remove_bin_file(struct device *dev,
- const struct bin_attribute *attr);
+void device_remove_bin_file(struct device *dev,
+ const struct bin_attribute *attr);
/* device resource management */
typedef void (*dr_release_t)(struct device *dev, void *res);
typedef int (*dr_match_t)(struct device *dev, void *res, void *match_data);
#ifdef CONFIG_DEBUG_DEVRES
-extern void *__devres_alloc_node(dr_release_t release, size_t size, gfp_t gfp,
- int nid, const char *name) __malloc;
+void *__devres_alloc_node(dr_release_t release, size_t size, gfp_t gfp,
+ int nid, const char *name) __malloc;
#define devres_alloc(release, size, gfp) \
__devres_alloc_node(release, size, gfp, NUMA_NO_NODE, #release)
#define devres_alloc_node(release, size, gfp, nid) \
__devres_alloc_node(release, size, gfp, nid, #release)
#else
-extern void *devres_alloc_node(dr_release_t release, size_t size, gfp_t gfp,
- int nid) __malloc;
+void *devres_alloc_node(dr_release_t release, size_t size,
+ gfp_t gfp, int nid) __malloc;
static inline void *devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
{
return devres_alloc_node(release, size, gfp, NUMA_NO_NODE);
}
#endif
-extern void devres_for_each_res(struct device *dev, dr_release_t release,
- dr_match_t match, void *match_data,
- void (*fn)(struct device *, void *, void *),
- void *data);
-extern void devres_free(void *res);
-extern void devres_add(struct device *dev, void *res);
-extern void *devres_find(struct device *dev, dr_release_t release,
- dr_match_t match, void *match_data);
-extern void *devres_get(struct device *dev, void *new_res,
- dr_match_t match, void *match_data);
-extern void *devres_remove(struct device *dev, dr_release_t release,
- dr_match_t match, void *match_data);
-extern int devres_destroy(struct device *dev, dr_release_t release,
- dr_match_t match, void *match_data);
-extern int devres_release(struct device *dev, dr_release_t release,
- dr_match_t match, void *match_data);
+void devres_for_each_res(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data,
+ void (*fn)(struct device *, void *, void *),
+ void *data);
+void devres_free(void *res);
+void devres_add(struct device *dev, void *res);
+void *devres_find(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data);
+void *devres_get(struct device *dev, void *new_res,
+ dr_match_t match, void *match_data);
+void *devres_remove(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data);
+int devres_destroy(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data);
+int devres_release(struct device *dev, dr_release_t release,
+ dr_match_t match, void *match_data);
/* devres group */
-extern void * __must_check devres_open_group(struct device *dev, void *id,
- gfp_t gfp);
-extern void devres_close_group(struct device *dev, void *id);
-extern void devres_remove_group(struct device *dev, void *id);
-extern int devres_release_group(struct device *dev, void *id);
+void * __must_check devres_open_group(struct device *dev, void *id, gfp_t gfp);
+void devres_close_group(struct device *dev, void *id);
+void devres_remove_group(struct device *dev, void *id);
+int devres_release_group(struct device *dev, void *id);
/* managed devm_k.alloc/kfree for device drivers */
-extern void *devm_kmalloc(struct device *dev, size_t size, gfp_t gfp) __malloc;
-extern __printf(3, 0)
-char *devm_kvasprintf(struct device *dev, gfp_t gfp, const char *fmt,
- va_list ap) __malloc;
-extern __printf(3, 4)
-char *devm_kasprintf(struct device *dev, gfp_t gfp, const char *fmt, ...) __malloc;
+void *devm_kmalloc(struct device *dev, size_t size, gfp_t gfp) __malloc;
+__printf(3, 0) char *devm_kvasprintf(struct device *dev, gfp_t gfp,
+ const char *fmt, va_list ap) __malloc;
+__printf(3, 4) char *devm_kasprintf(struct device *dev, gfp_t gfp,
+ const char *fmt, ...) __malloc;
static inline void *devm_kzalloc(struct device *dev, size_t size, gfp_t gfp)
{
return devm_kmalloc(dev, size, gfp | __GFP_ZERO);
@@ -227,16 +229,14 @@ static inline void *devm_kcalloc(struct device *dev,
{
return devm_kmalloc_array(dev, n, size, flags | __GFP_ZERO);
}
-extern void devm_kfree(struct device *dev, const void *p);
-extern char *devm_kstrdup(struct device *dev, const char *s, gfp_t gfp) __malloc;
-extern const char *devm_kstrdup_const(struct device *dev,
- const char *s, gfp_t gfp);
-extern void *devm_kmemdup(struct device *dev, const void *src, size_t len,
- gfp_t gfp);
+void devm_kfree(struct device *dev, const void *p);
+char *devm_kstrdup(struct device *dev, const char *s, gfp_t gfp) __malloc;
+const char *devm_kstrdup_const(struct device *dev, const char *s, gfp_t gfp);
+void *devm_kmemdup(struct device *dev, const void *src, size_t len, gfp_t gfp);
-extern unsigned long devm_get_free_pages(struct device *dev,
- gfp_t gfp_mask, unsigned int order);
-extern void devm_free_pages(struct device *dev, unsigned long addr);
+unsigned long devm_get_free_pages(struct device *dev,
+ gfp_t gfp_mask, unsigned int order);
+void devm_free_pages(struct device *dev, unsigned long addr);
void __iomem *devm_ioremap_resource(struct device *dev,
const struct resource *res);
@@ -388,34 +388,6 @@ enum device_link_state {
#define DL_FLAG_SYNC_STATE_ONLY BIT(7)
/**
- * struct device_link - Device link representation.
- * @supplier: The device on the supplier end of the link.
- * @s_node: Hook to the supplier device's list of links to consumers.
- * @consumer: The device on the consumer end of the link.
- * @c_node: Hook to the consumer device's list of links to suppliers.
- * @status: The state of the link (with respect to the presence of drivers).
- * @flags: Link flags.
- * @rpm_active: Whether or not the consumer device is runtime-PM-active.
- * @kref: Count repeated addition of the same link.
- * @rcu_head: An RCU head to use for deferred execution of SRCU callbacks.
- * @supplier_preactivated: Supplier has been made active before consumer probe.
- */
-struct device_link {
- struct device *supplier;
- struct list_head s_node;
- struct device *consumer;
- struct list_head c_node;
- enum device_link_state status;
- u32 flags;
- refcount_t rpm_active;
- struct kref kref;
-#ifdef CONFIG_SRCU
- struct rcu_head rcu_head;
-#endif
- bool supplier_preactivated; /* Owned by consumer probe. */
-};
-
-/**
* enum dl_dev_state - Device driver presence tracking information.
* @DL_DEV_NO_DRIVER: There is no driver attached to the device.
* @DL_DEV_PROBING: A driver is probing.
@@ -639,6 +611,36 @@ struct device {
#endif
};
+/**
+ * struct device_link - Device link representation.
+ * @supplier: The device on the supplier end of the link.
+ * @s_node: Hook to the supplier device's list of links to consumers.
+ * @consumer: The device on the consumer end of the link.
+ * @c_node: Hook to the consumer device's list of links to suppliers.
+ * @link_dev: device used to expose link details in sysfs
+ * @status: The state of the link (with respect to the presence of drivers).
+ * @flags: Link flags.
+ * @rpm_active: Whether or not the consumer device is runtime-PM-active.
+ * @kref: Count repeated addition of the same link.
+ * @rcu_head: An RCU head to use for deferred execution of SRCU callbacks.
+ * @supplier_preactivated: Supplier has been made active before consumer probe.
+ */
+struct device_link {
+ struct device *supplier;
+ struct list_head s_node;
+ struct device *consumer;
+ struct list_head c_node;
+ struct device link_dev;
+ enum device_link_state status;
+ u32 flags;
+ refcount_t rpm_active;
+ struct kref kref;
+#ifdef CONFIG_SRCU
+ struct rcu_head rcu_head;
+#endif
+ bool supplier_preactivated; /* Owned by consumer probe. */
+};
+
static inline struct device *kobj_to_dev(struct kobject *kobj)
{
return container_of(kobj, struct device, kobj);
@@ -666,8 +668,7 @@ static inline const char *dev_name(const struct device *dev)
return kobject_name(&dev->kobj);
}
-extern __printf(2, 3)
-int dev_set_name(struct device *dev, const char *name, ...);
+__printf(2, 3) int dev_set_name(struct device *dev, const char *name, ...);
#ifdef CONFIG_NUMA
static inline int dev_to_node(struct device *dev)
@@ -824,39 +825,38 @@ static inline bool dev_has_sync_state(struct device *dev)
/*
* High level routines for use by the bus drivers
*/
-extern int __must_check device_register(struct device *dev);
-extern void device_unregister(struct device *dev);
-extern void device_initialize(struct device *dev);
-extern int __must_check device_add(struct device *dev);
-extern void device_del(struct device *dev);
-extern int device_for_each_child(struct device *dev, void *data,
- int (*fn)(struct device *dev, void *data));
-extern int device_for_each_child_reverse(struct device *dev, void *data,
- int (*fn)(struct device *dev, void *data));
-extern struct device *device_find_child(struct device *dev, void *data,
- int (*match)(struct device *dev, void *data));
-extern struct device *device_find_child_by_name(struct device *parent,
- const char *name);
-extern int device_rename(struct device *dev, const char *new_name);
-extern int device_move(struct device *dev, struct device *new_parent,
- enum dpm_order dpm_order);
-extern int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid);
-extern const char *device_get_devnode(struct device *dev,
- umode_t *mode, kuid_t *uid, kgid_t *gid,
- const char **tmp);
+int __must_check device_register(struct device *dev);
+void device_unregister(struct device *dev);
+void device_initialize(struct device *dev);
+int __must_check device_add(struct device *dev);
+void device_del(struct device *dev);
+int device_for_each_child(struct device *dev, void *data,
+ int (*fn)(struct device *dev, void *data));
+int device_for_each_child_reverse(struct device *dev, void *data,
+ int (*fn)(struct device *dev, void *data));
+struct device *device_find_child(struct device *dev, void *data,
+ int (*match)(struct device *dev, void *data));
+struct device *device_find_child_by_name(struct device *parent,
+ const char *name);
+int device_rename(struct device *dev, const char *new_name);
+int device_move(struct device *dev, struct device *new_parent,
+ enum dpm_order dpm_order);
+int device_change_owner(struct device *dev, kuid_t kuid, kgid_t kgid);
+const char *device_get_devnode(struct device *dev, umode_t *mode, kuid_t *uid,
+ kgid_t *gid, const char **tmp);
static inline bool device_supports_offline(struct device *dev)
{
return dev->bus && dev->bus->offline && dev->bus->online;
}
-extern void lock_device_hotplug(void);
-extern void unlock_device_hotplug(void);
-extern int lock_device_hotplug_sysfs(void);
-extern int device_offline(struct device *dev);
-extern int device_online(struct device *dev);
-extern void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode);
-extern void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode);
+void lock_device_hotplug(void);
+void unlock_device_hotplug(void);
+int lock_device_hotplug_sysfs(void);
+int device_offline(struct device *dev);
+int device_online(struct device *dev);
+void set_primary_fwnode(struct device *dev, struct fwnode_handle *fwnode);
+void set_secondary_fwnode(struct device *dev, struct fwnode_handle *fwnode);
void device_set_of_node_from_dev(struct device *dev, const struct device *dev2);
static inline int dev_num_vf(struct device *dev)
@@ -869,14 +869,13 @@ static inline int dev_num_vf(struct device *dev)
/*
* Root device objects for grouping under /sys/devices
*/
-extern struct device *__root_device_register(const char *name,
- struct module *owner);
+struct device *__root_device_register(const char *name, struct module *owner);
/* This is a macro to avoid include problems with THIS_MODULE */
#define root_device_register(name) \
__root_device_register(name, THIS_MODULE)
-extern void root_device_unregister(struct device *root);
+void root_device_unregister(struct device *root);
static inline void *dev_get_platdata(const struct device *dev)
{
@@ -887,33 +886,31 @@ static inline void *dev_get_platdata(const struct device *dev)
* Manual binding of a device to driver. See drivers/base/bus.c
* for information on use.
*/
-extern int __must_check device_bind_driver(struct device *dev);
-extern void device_release_driver(struct device *dev);
-extern int __must_check device_attach(struct device *dev);
-extern int __must_check driver_attach(struct device_driver *drv);
-extern void device_initial_probe(struct device *dev);
-extern int __must_check device_reprobe(struct device *dev);
+int __must_check device_bind_driver(struct device *dev);
+void device_release_driver(struct device *dev);
+int __must_check device_attach(struct device *dev);
+int __must_check driver_attach(struct device_driver *drv);
+void device_initial_probe(struct device *dev);
+int __must_check device_reprobe(struct device *dev);
-extern bool device_is_bound(struct device *dev);
+bool device_is_bound(struct device *dev);
/*
* Easy functions for dynamically creating devices on the fly
*/
-extern __printf(5, 6)
-struct device *device_create(struct class *cls, struct device *parent,
- dev_t devt, void *drvdata,
- const char *fmt, ...);
-extern __printf(6, 7)
-struct device *device_create_with_groups(struct class *cls,
- struct device *parent, dev_t devt, void *drvdata,
- const struct attribute_group **groups,
- const char *fmt, ...);
-extern void device_destroy(struct class *cls, dev_t devt);
-
-extern int __must_check device_add_groups(struct device *dev,
- const struct attribute_group **groups);
-extern void device_remove_groups(struct device *dev,
- const struct attribute_group **groups);
+__printf(5, 6) struct device *
+device_create(struct class *cls, struct device *parent, dev_t devt,
+ void *drvdata, const char *fmt, ...);
+__printf(6, 7) struct device *
+device_create_with_groups(struct class *cls, struct device *parent, dev_t devt,
+ void *drvdata, const struct attribute_group **groups,
+ const char *fmt, ...);
+void device_destroy(struct class *cls, dev_t devt);
+
+int __must_check device_add_groups(struct device *dev,
+ const struct attribute_group **groups);
+void device_remove_groups(struct device *dev,
+ const struct attribute_group **groups);
static inline int __must_check device_add_group(struct device *dev,
const struct attribute_group *grp)
@@ -931,14 +928,14 @@ static inline void device_remove_group(struct device *dev,
return device_remove_groups(dev, groups);
}
-extern int __must_check devm_device_add_groups(struct device *dev,
+int __must_check devm_device_add_groups(struct device *dev,
const struct attribute_group **groups);
-extern void devm_device_remove_groups(struct device *dev,
- const struct attribute_group **groups);
-extern int __must_check devm_device_add_group(struct device *dev,
- const struct attribute_group *grp);
-extern void devm_device_remove_group(struct device *dev,
- const struct attribute_group *grp);
+void devm_device_remove_groups(struct device *dev,
+ const struct attribute_group **groups);
+int __must_check devm_device_add_group(struct device *dev,
+ const struct attribute_group *grp);
+void devm_device_remove_group(struct device *dev,
+ const struct attribute_group *grp);
/*
* Platform "fixup" functions - allow the platform to have their say
@@ -955,21 +952,21 @@ extern int (*platform_notify_remove)(struct device *dev);
* get_device - atomically increment the reference count for the device.
*
*/
-extern struct device *get_device(struct device *dev);
-extern void put_device(struct device *dev);
-extern bool kill_device(struct device *dev);
+struct device *get_device(struct device *dev);
+void put_device(struct device *dev);
+bool kill_device(struct device *dev);
#ifdef CONFIG_DEVTMPFS
-extern int devtmpfs_mount(void);
+int devtmpfs_mount(void);
#else
static inline int devtmpfs_mount(void) { return 0; }
#endif
/* drivers/base/power/shutdown.c */
-extern void device_shutdown(void);
+void device_shutdown(void);
/* debugging and troubleshooting/diagnostic helpers. */
-extern const char *dev_driver_string(const struct device *dev);
+const char *dev_driver_string(const struct device *dev);
/* Device links interface. */
struct device_link *device_link_add(struct device *consumer,
@@ -979,6 +976,9 @@ void device_link_remove(void *consumer, struct device *supplier);
void device_links_supplier_sync_state_pause(void);
void device_links_supplier_sync_state_resume(void);
+extern __printf(3, 4)
+int dev_err_probe(const struct device *dev, int err, const char *fmt, ...);
+
/* Create alias, so I can be autoloaded. */
#define MODULE_ALIAS_CHARDEV(major,minor) \
MODULE_ALIAS("char-major-" __stringify(major) "-" __stringify(minor))
diff --git a/include/linux/kobject.h b/include/linux/kobject.h
index 6cba088bee24..ea30529fba08 100644
--- a/include/linux/kobject.h
+++ b/include/linux/kobject.h
@@ -59,7 +59,6 @@ enum kobject_action {
KOBJ_OFFLINE,
KOBJ_BIND,
KOBJ_UNBIND,
- KOBJ_MAX
};
struct kobject {
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h
index 86067dbe7745..34e84122f635 100644
--- a/include/linux/sysfs.h
+++ b/include/linux/sysfs.h
@@ -123,6 +123,13 @@ struct attribute_group {
.show = _name##_show, \
}
+#define __ATTR_RW_MODE(_name, _mode) { \
+ .attr = { .name = __stringify(_name), \
+ .mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
+ .show = _name##_show, \
+ .store = _name##_store, \
+}
+
#define __ATTR_WO(_name) { \
.attr = { .name = __stringify(_name), .mode = 0200 }, \
.store = _name##_store, \
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index bb62269724d5..848f67a5f16d 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -8945,9 +8945,7 @@ struct dentry *tracing_init_dentry(void)
if (tr->dir)
return NULL;
- if (WARN_ON(!tracefs_initialized()) ||
- (IS_ENABLED(CONFIG_DEBUG_FS) &&
- WARN_ON(!debugfs_initialized())))
+ if (WARN_ON(!tracefs_initialized()))
return ERR_PTR(-ENODEV);
/*
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 3e64a8a809f9..a164785c3b48 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -476,6 +476,38 @@ config DEBUG_FS
If unsure, say N.
+choice
+ prompt "Debugfs default access"
+ depends on DEBUG_FS
+ default DEBUG_FS_ALLOW_ALL
+ help
+ This selects the default access restrictions for debugfs.
+ It can be overridden with kernel command line option
+ debugfs=[on,no-mount,off]. The restrictions apply for API access
+ and filesystem registration.
+
+config DEBUG_FS_ALLOW_ALL
+ bool "Access normal"
+ help
+ No restrictions apply. Both API and filesystem registration
+ is on. This is the normal default operation.
+
+config DEBUG_FS_DISALLOW_MOUNT
+ bool "Do not register debugfs as filesystem"
+ help
+ The API is open but filesystem is not loaded. Clients can still do
+ their work and read with debug tools that do not need
+ debugfs filesystem.
+
+config DEBUG_FS_ALLOW_NONE
+ bool "No access"
+ help
+ Access is off. Clients get -PERM when trying to create nodes in
+ debugfs tree and debugfs is not registered as a filesystem.
+ Client can then back-off or continue without debugfs access.
+
+endchoice
+
source "lib/Kconfig.kgdb"
source "lib/Kconfig.ubsan"
@@ -844,10 +876,10 @@ config DEBUG_SHIRQ
bool "Debug shared IRQ handlers"
depends on DEBUG_KERNEL
help
- Enable this to generate a spurious interrupt as soon as a shared
- interrupt handler is registered, and just before one is deregistered.
- Drivers ought to be able to handle interrupts coming in at those
- points; some don't and need to be caught.
+ Enable this to generate a spurious interrupt just before a shared
+ interrupt handler is deregistered (generating one when registering
+ is currently disabled). Drivers need to handle this correctly. Some
+ don't and need to be caught.
menu "Debug Oops, Lockups and Hangs"
diff --git a/lib/devres.c b/lib/devres.c
index 6ef51f159c54..ebb1573d9ae3 100644
--- a/lib/devres.c
+++ b/lib/devres.c
@@ -119,6 +119,7 @@ __devm_ioremap_resource(struct device *dev, const struct resource *res,
{
resource_size_t size;
void __iomem *dest_ptr;
+ char *pretty_name;
BUG_ON(!dev);
@@ -129,7 +130,15 @@ __devm_ioremap_resource(struct device *dev, const struct resource *res,
size = resource_size(res);
- if (!devm_request_mem_region(dev, res->start, size, dev_name(dev))) {
+ if (res->name)
+ pretty_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s",
+ dev_name(dev), res->name);
+ else
+ pretty_name = devm_kstrdup(dev, dev_name(dev), GFP_KERNEL);
+ if (!pretty_name)
+ return IOMEM_ERR_PTR(-ENOMEM);
+
+ if (!devm_request_mem_region(dev, res->start, size, pretty_name)) {
dev_err(dev, "can't request region for resource %pR\n", res);
return IOMEM_ERR_PTR(-EBUSY);
}
@@ -204,6 +213,12 @@ void __iomem *devm_ioremap_resource_wc(struct device *dev,
* base = devm_of_iomap(&pdev->dev, node, 0, NULL);
* if (IS_ERR(base))
* return PTR_ERR(base);
+ *
+ * Please Note: This is not a one-to-one replacement for of_iomap() because the
+ * of_iomap() function does not track whether the region is already mapped. If
+ * two drivers try to map the same memory, the of_iomap() function will succeed
+ * but the the devm_of_iomap() function will return -EBUSY.
+ *
*/
void __iomem *devm_of_iomap(struct device *dev, struct device_node *node, int index,
resource_size_t *size)
diff --git a/lib/kobject.c b/lib/kobject.c
index 1e4b7382a88e..3afb939f2a1c 100644
--- a/lib/kobject.c
+++ b/lib/kobject.c
@@ -599,14 +599,7 @@ out:
}
EXPORT_SYMBOL_GPL(kobject_move);
-/**
- * kobject_del() - Unlink kobject from hierarchy.
- * @kobj: object.
- *
- * This is the function that should be called to delete an object
- * successfully added via kobject_add().
- */
-void kobject_del(struct kobject *kobj)
+static void __kobject_del(struct kobject *kobj)
{
struct kernfs_node *sd;
const struct kobj_type *ktype;
@@ -632,9 +625,23 @@ void kobject_del(struct kobject *kobj)
kobj->state_in_sysfs = 0;
kobj_kset_leave(kobj);
- kobject_put(kobj->parent);
kobj->parent = NULL;
}
+
+/**
+ * kobject_del() - Unlink kobject from hierarchy.
+ * @kobj: object.
+ *
+ * This is the function that should be called to delete an object
+ * successfully added via kobject_add().
+ */
+void kobject_del(struct kobject *kobj)
+{
+ struct kobject *parent = kobj->parent;
+
+ __kobject_del(kobj);
+ kobject_put(parent);
+}
EXPORT_SYMBOL(kobject_del);
/**
@@ -670,6 +677,7 @@ EXPORT_SYMBOL(kobject_get_unless_zero);
*/
static void kobject_cleanup(struct kobject *kobj)
{
+ struct kobject *parent = kobj->parent;
struct kobj_type *t = get_ktype(kobj);
const char *name = kobj->name;
@@ -684,7 +692,10 @@ static void kobject_cleanup(struct kobject *kobj)
if (kobj->state_in_sysfs) {
pr_debug("kobject: '%s' (%p): auto cleanup kobject_del\n",
kobject_name(kobj), kobj);
- kobject_del(kobj);
+ __kobject_del(kobj);
+ } else {
+ /* avoid dropping the parent reference unnecessarily */
+ parent = NULL;
}
if (t && t->release) {
@@ -698,6 +709,8 @@ static void kobject_cleanup(struct kobject *kobj)
pr_debug("kobject: '%s': free name\n", name);
kfree_const(name);
}
+
+ kobject_put(parent);
}
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
diff --git a/scripts/coccinelle/misc/add_namespace.cocci b/scripts/coccinelle/misc/add_namespace.cocci
index 99e93a6c2e24..cbf1614163cb 100644
--- a/scripts/coccinelle/misc/add_namespace.cocci
+++ b/scripts/coccinelle/misc/add_namespace.cocci
@@ -6,6 +6,7 @@
/// add a missing namespace tag to a module source file.
///
+virtual nsdeps
virtual report
@has_ns_import@
@@ -16,10 +17,15 @@ MODULE_IMPORT_NS(ns);
// Add missing imports, but only adjacent to a MODULE_LICENSE statement.
// That ensures we are adding it only to the main module source file.
-@do_import depends on !has_ns_import@
+@do_import depends on !has_ns_import && nsdeps@
declarer name MODULE_LICENSE;
expression license;
identifier virtual.ns;
@@
MODULE_LICENSE(license);
+ MODULE_IMPORT_NS(ns);
+
+// Dummy rule for report mode that would otherwise be empty and make spatch
+// fail ("No rules apply.")
+@script:python depends on report@
+@@
diff --git a/scripts/nsdeps b/scripts/nsdeps
index 03a8e7cbe6c7..dab4c1a0e27d 100644
--- a/scripts/nsdeps
+++ b/scripts/nsdeps
@@ -29,7 +29,7 @@ fi
generate_deps_for_ns() {
$SPATCH --very-quiet --in-place --sp-file \
- $srctree/scripts/coccinelle/misc/add_namespace.cocci -D ns=$1 $2
+ $srctree/scripts/coccinelle/misc/add_namespace.cocci -D nsdeps -D ns=$1 $2
}
generate_deps() {
diff --git a/tools/testing/selftests/firmware/settings b/tools/testing/selftests/firmware/settings
new file mode 100644
index 000000000000..085e664ee093
--- /dev/null
+++ b/tools/testing/selftests/firmware/settings
@@ -0,0 +1,8 @@
+# The async firmware timeout is set to 1 second (but ends up being effectively
+# 2 seconds). There are 3 test configs, each done with and without firmware
+# present, each with 2 "nowait" functions tested 5 times. Expected time for a
+# normal execution should be 2 * 3 * 2 * 2 * 5 = 120 seconds for those alone.
+# Additionally, fw_fallback may take 5 seconds for internal timeouts in each
+# of the 3 configs, so at least another 15 seconds are needed. Add another
+# 10 seconds for each testing config: 120 + 15 + 30
+timeout=165
diff --git a/tools/testing/selftests/kselftest/runner.sh b/tools/testing/selftests/kselftest/runner.sh
index f4815cbcd60f..cc9c846585f0 100644
--- a/tools/testing/selftests/kselftest/runner.sh
+++ b/tools/testing/selftests/kselftest/runner.sh
@@ -53,6 +53,10 @@ run_one()
settings="$BASE_DIR/$DIR/settings"
if [ -r "$settings" ] ; then
while read line ; do
+ # Skip comments.
+ if echo "$line" | grep -q '^#'; then
+ continue
+ fi
field=$(echo "$line" | cut -d= -f1)
value=$(echo "$line" | cut -d= -f2-)
eval "kselftest_$field"="$value"
@@ -80,7 +84,7 @@ run_one()
echo "ok $test_num $TEST_HDR_MSG # SKIP"
elif [ $rc -eq $timeout_rc ]; then \
echo "#"
- echo "not ok $test_num $TEST_HDR_MSG # TIMEOUT"
+ echo "not ok $test_num $TEST_HDR_MSG # TIMEOUT $kselftest_timeout seconds"
else
echo "not ok $test_num $TEST_HDR_MSG # exit=$rc"
fi)