diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2020-08-05 11:52:17 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2020-08-05 11:52:17 -0700 |
commit | dd27111e32572fdd3aaae98ba2817df6d202357a (patch) | |
tree | 87891d5216486697d3eb0a8f8edc8cc3c67092c6 | |
parent | 1785d116124fc33f2c265243f3f59da3dc2a2576 (diff) | |
parent | 76acb5ee76b1ae5870cac9c8125ca09e9106d5b2 (diff) | |
download | linux-dd27111e32572fdd3aaae98ba2817df6d202357a.tar.bz2 |
Merge tag 'driver-core-5.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 changes to the driver core, and some drivers
using the changes, for 5.9-rc1.
"Biggest" thing in here is the device link exposure in sysfs, to help
to tame the madness that is SoC device tree representations and driver
interactions with it.
Other stuff in here that is interesting is:
- device probe log helper so that drivers can report problems in a
unified way easier.
- devres functions added
- DEVICE_ATTR_ADMIN_* macro added to make it harder to write
incorrect sysfs file permissions
- documentation cleanups
- ability for debugfs to be present in the kernel, yet not exposed to
userspace. Needed for systems that want it enabled, but do not
trust users, so they can still use some kernel functions that were
otherwise disabled.
- other minor fixes and cleanups
The patches outside of drivers/base/ all have acks from the respective
subsystem maintainers to go through this tree instead of theirs.
All of these have been in linux-next with no reported issues"
* tag 'driver-core-5.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (39 commits)
drm/bridge: lvds-codec: simplify error handling
drm/bridge/sii8620: fix resource acquisition error handling
driver core: add deferring probe reason to devices_deferred property
driver core: add device probe log helper
driver core: Avoid binding drivers to dead devices
Revert "test_firmware: Test platform fw loading on non-EFI systems"
firmware_loader: EFI firmware loader must handle pre-allocated buffer
selftest/firmware: Add selftest timeout in settings
test_firmware: Test platform fw loading on non-EFI systems
driver core: Change delimiter in devlink device's name to "--"
debugfs: Add access restriction option
tracefs: Remove unnecessary debug_fs checks.
driver core: Fix probe_count imbalance in really_probe()
kobject: remove unused KOBJ_MAX action
driver core: Fix sleeping in invalid context during device link deletion
driver core: Add waiting_for_supplier sysfs file for devices
driver core: Add state_synced sysfs file for devices that support it
driver core: Expose device link details in sysfs
driver core: Drop mention of obsolete bus rwsem from kernel-doc
debugfs: file: Remove unnecessary cast in kfree()
...
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) |