diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-04-26 11:05:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-04-26 11:05:36 -0700 |
commit | c01c0716ccf5db2086d9693033472f37de96a699 (patch) | |
tree | 330fbb4c8a386073a25fb17ee29be15e21266ab3 | |
parent | 8e3a3249502d8ff92d73d827fb41dd44c5a16f76 (diff) | |
parent | a943d76352dbb4707a5e5537bbe696c00f5ddd36 (diff) | |
download | linux-stable-c01c0716ccf5db2086d9693033472f37de96a699.tar.gz linux-stable-c01c0716ccf5db2086d9693033472f37de96a699.tar.bz2 linux-stable-c01c0716ccf5db2086d9693033472f37de96a699.zip |
Merge tag 'driver-core-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core
Pull driver core updates from Greg KH:
"Here is the "big" set of driver core changes for 5.13-rc1.
Nothing major, just lots of little core changes and cleanups, notable
things are:
- finally set 'fw_devlink=on' by default.
All reported issues with this have been shaken out over the past 9
months or so, but we will be paying attention to any fallout here
in case we need to revert this as the default boot value (symptoms
of problems are a simple lack of booting)
- fixes found to be needed by fw_devlink=on value in some subsystems
(like clock).
- delayed work initialization cleanup
- driver core cleanups and minor updates
- software node cleanups and tweaks
- devtmpfs cleanups
- minor debugfs cleanups
All of these have been in linux-next for a while with no reported
issues"
* tag 'driver-core-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (53 commits)
devm-helpers: Fix devm_delayed_work_autocancel() kerneldoc
PM / wakeup: use dev_set_name() directly
software node: Allow node addition to already existing device
kunit: software node: adhear to KUNIT formatting standard
node: fix device cleanups in error handling code
kobject_uevent: remove warning in init_uevent_argv()
debugfs: Make debugfs_allow RO after init
Revert "driver core: platform: Make platform_get_irq_optional() optional"
media: ipu3-cio2: Switch to use SOFTWARE_NODE_REFERENCE()
software node: Introduce SOFTWARE_NODE_REFERENCE() helper macro
software node: Imply kobj_to_swnode() to be no-op
software node: Deduplicate code in fwnode_create_software_node()
software node: Introduce software_node_alloc()/software_node_free()
software node: Free resources explicitly when swnode_register() fails
debugfs: drop pointless nul-termination in debugfs_read_file_bool()
driver core: add helper for deferred probe reason setting
driver core: Improve fw_devlink & deferred_probe_timeout interaction
of: property: fw_devlink: Add support for remote-endpoint
driver core: platform: Make platform_get_irq_optional() optional
driver core: Replace printf() specifier and drop unneeded casting
...
42 files changed, 481 insertions, 327 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index 56defe56d421..502a7d570d11 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5197,6 +5197,12 @@ M: Torben Mathiasen <device@lanana.org> S: Maintained W: http://lanana.org/docs/device-list/index.html +DEVICE RESOURCE MANAGEMENT HELPERS +M: Hans de Goede <hdegoede@redhat.com> +R: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com> +S: Maintained +F: include/linux/devm-helpers.h + DEVICE-MAPPER (LVM) M: Alasdair Kergon <agk@redhat.com> M: Mike Snitzer <snitzer@redhat.com> diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c index f7bd0f4db13d..9c00d203d61e 100644 --- a/drivers/base/attribute_container.c +++ b/drivers/base/attribute_container.c @@ -461,6 +461,10 @@ attribute_container_add_class_device(struct device *classdev) /** * attribute_container_add_class_device_adapter - simple adapter for triggers * + * @cont: the container to register. + * @dev: the generic device to activate the trigger for + * @classdev: the class device to add + * * This function is identical to attribute_container_add_class_device except * that it is designed to be called from the triggers */ diff --git a/drivers/base/auxiliary.c b/drivers/base/auxiliary.c index d8b314e7d0fd..adc199dfba3c 100644 --- a/drivers/base/auxiliary.c +++ b/drivers/base/auxiliary.c @@ -265,8 +265,3 @@ void __init auxiliary_bus_init(void) { WARN_ON(bus_register(&auxiliary_bus_type)); } - -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("Auxiliary Bus"); -MODULE_AUTHOR("David Ertman <david.m.ertman@intel.com>"); -MODULE_AUTHOR("Kiran Patil <kiran.patil@intel.com>"); diff --git a/drivers/base/base.h b/drivers/base/base.h index 52b3d7b75c27..e5f9b7e656c3 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -185,11 +185,13 @@ extern int device_links_read_lock(void); extern void device_links_read_unlock(int idx); extern int device_links_read_lock_held(void); extern int device_links_check_suppliers(struct device *dev); +extern void device_links_force_bind(struct device *dev); extern void device_links_driver_bound(struct device *dev); extern void device_links_driver_cleanup(struct device *dev); extern void device_links_no_driver(struct device *dev); extern bool device_links_busy(struct device *dev); extern void device_links_unbind_consumers(struct device *dev); +extern void fw_devlink_drivers_done(void); /* device pm support */ void device_pm_move_to_tail(struct device *dev); diff --git a/drivers/base/component.c b/drivers/base/component.c index dcfbe7251dc4..272ba42392f0 100644 --- a/drivers/base/component.c +++ b/drivers/base/component.c @@ -65,7 +65,6 @@ struct master { const struct component_master_ops *ops; struct device *dev; struct component_match *match; - struct dentry *dentry; }; struct component { @@ -125,15 +124,13 @@ core_initcall(component_debug_init); static void component_master_debugfs_add(struct master *m) { - m->dentry = debugfs_create_file(dev_name(m->dev), 0444, - component_debugfs_dir, - m, &component_devices_fops); + debugfs_create_file(dev_name(m->dev), 0444, component_debugfs_dir, m, + &component_devices_fops); } static void component_master_debugfs_del(struct master *m) { - debugfs_remove(m->dentry); - m->dentry = NULL; + debugfs_remove(debugfs_lookup(dev_name(m->dev), component_debugfs_dir)); } #else diff --git a/drivers/base/core.c b/drivers/base/core.c index f29839382f81..4a8bf8cda52b 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -51,6 +51,7 @@ static LIST_HEAD(deferred_sync); static unsigned int defer_sync_state_count = 1; static DEFINE_MUTEX(fwnode_link_lock); static bool fw_devlink_is_permissive(void); +static bool fw_devlink_drv_reg_done; /** * fwnode_link_add - Create a link between two fwnode_handles. @@ -1154,6 +1155,41 @@ static ssize_t waiting_for_supplier_show(struct device *dev, static DEVICE_ATTR_RO(waiting_for_supplier); /** + * device_links_force_bind - Prepares device to be force bound + * @dev: Consumer device. + * + * device_bind_driver() force binds a device to a driver without calling any + * driver probe functions. So the consumer really isn't going to wait for any + * supplier before it's bound to the driver. We still want the device link + * states to be sensible when this happens. + * + * In preparation for device_bind_driver(), this function goes through each + * supplier device links and checks if the supplier is bound. If it is, then + * the device link status is set to CONSUMER_PROBE. Otherwise, the device link + * is dropped. Links without the DL_FLAG_MANAGED flag set are ignored. + */ +void device_links_force_bind(struct device *dev) +{ + struct device_link *link, *ln; + + device_links_write_lock(); + + list_for_each_entry_safe(link, ln, &dev->links.suppliers, c_node) { + if (!(link->flags & DL_FLAG_MANAGED)) + continue; + + if (link->status != DL_STATE_AVAILABLE) { + device_link_drop_managed(link); + continue; + } + WRITE_ONCE(link->status, DL_STATE_CONSUMER_PROBE); + } + dev->links.status = DL_DEV_PROBING; + + device_links_write_unlock(); +} + +/** * device_links_driver_bound - Update device links after probing its driver. * @dev: Device to update the links for. * @@ -1503,7 +1539,7 @@ static void device_links_purge(struct device *dev) #define FW_DEVLINK_FLAGS_RPM (FW_DEVLINK_FLAGS_ON | \ DL_FLAG_PM_RUNTIME) -static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_PERMISSIVE; +static u32 fw_devlink_flags = FW_DEVLINK_FLAGS_ON; static int __init fw_devlink_setup(char *arg) { if (!arg) @@ -1563,6 +1599,52 @@ static void fw_devlink_parse_fwtree(struct fwnode_handle *fwnode) fw_devlink_parse_fwtree(child); } +static void fw_devlink_relax_link(struct device_link *link) +{ + if (!(link->flags & DL_FLAG_INFERRED)) + return; + + if (link->flags == (DL_FLAG_MANAGED | FW_DEVLINK_FLAGS_PERMISSIVE)) + return; + + pm_runtime_drop_link(link); + link->flags = DL_FLAG_MANAGED | FW_DEVLINK_FLAGS_PERMISSIVE; + dev_dbg(link->consumer, "Relaxing link with %s\n", + dev_name(link->supplier)); +} + +static int fw_devlink_no_driver(struct device *dev, void *data) +{ + struct device_link *link = to_devlink(dev); + + if (!link->supplier->can_match) + fw_devlink_relax_link(link); + + return 0; +} + +void fw_devlink_drivers_done(void) +{ + fw_devlink_drv_reg_done = true; + device_links_write_lock(); + class_for_each_device(&devlink_class, NULL, NULL, + fw_devlink_no_driver); + device_links_write_unlock(); +} + +static void fw_devlink_unblock_consumers(struct device *dev) +{ + struct device_link *link; + + if (!fw_devlink_flags || fw_devlink_is_permissive()) + return; + + device_links_write_lock(); + list_for_each_entry(link, &dev->links.consumers, s_node) + fw_devlink_relax_link(link); + device_links_write_unlock(); +} + /** * fw_devlink_relax_cycle - Convert cyclic links to SYNC_STATE_ONLY links * @con: Device to check dependencies for. @@ -1599,21 +1681,16 @@ static int fw_devlink_relax_cycle(struct device *con, void *sup) ret = 1; - if (!(link->flags & DL_FLAG_INFERRED)) - continue; - - pm_runtime_drop_link(link); - link->flags = DL_FLAG_MANAGED | FW_DEVLINK_FLAGS_PERMISSIVE; - dev_dbg(link->consumer, "Relaxing link with %s\n", - dev_name(link->supplier)); + fw_devlink_relax_link(link); } return ret; } /** * fw_devlink_create_devlink - Create a device link from a consumer to fwnode - * @con - Consumer device for the device link - * @sup_handle - fwnode handle of supplier + * @con: consumer device for the device link + * @sup_handle: fwnode handle of supplier + * @flags: devlink flags * * This function will try to create a device link between the consumer device * @con and the supplier device represented by @sup_handle. @@ -1709,7 +1786,7 @@ out: /** * __fw_devlink_link_to_consumers - Create device links to consumers of a device - * @dev - Device that needs to be linked to its consumers + * @dev: Device that needs to be linked to its consumers * * This function looks at all the consumer fwnodes of @dev and creates device * links between the consumer device and @dev (supplier). @@ -1779,8 +1856,8 @@ static void __fw_devlink_link_to_consumers(struct device *dev) /** * __fw_devlink_link_to_suppliers - Create device links to suppliers of a device - * @dev - The consumer device that needs to be linked to its suppliers - * @fwnode - Root of the fwnode tree that is used to create device links + * @dev: The consumer device that needs to be linked to its suppliers + * @fwnode: Root of the fwnode tree that is used to create device links * * This function looks at all the supplier fwnodes of fwnode tree rooted at * @fwnode and creates device links between @dev (consumer) and all the @@ -3240,6 +3317,15 @@ int device_add(struct device *dev) } bus_probe_device(dev); + + /* + * If all driver registration is done and a newly added device doesn't + * match with any driver, don't block its consumers from probing in + * case the consumer device is able to operate without this supplier. + */ + if (dev->fwnode && fw_devlink_drv_reg_done && !dev->can_match) + fw_devlink_unblock_consumers(dev); + if (parent) klist_add_tail(&dev->p->knode_parent, &parent->p->klist_children); diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 8f1d6569564c..2b9e41377a07 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -409,13 +409,11 @@ __cpu_device_create(struct device *parent, void *drvdata, const char *fmt, va_list args) { struct device *dev = NULL; - int retval = -ENODEV; + int retval = -ENOMEM; dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - retval = -ENOMEM; + if (!dev) goto error; - } device_initialize(dev); dev->parent = parent; diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 37a5e5f8b221..ecd7cf848daf 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -55,7 +55,6 @@ static DEFINE_MUTEX(deferred_probe_mutex); static LIST_HEAD(deferred_probe_pending_list); static LIST_HEAD(deferred_probe_active_list); static atomic_t deferred_trigger_count = ATOMIC_INIT(0); -static struct dentry *deferred_devices; static bool initcalls_done; /* Save the async probe drivers' name from kernel cmdline */ @@ -69,6 +68,12 @@ static char async_probe_drv_names[ASYNC_DRV_NAMES_MAX_LEN]; */ static bool defer_all_probes; +static void __device_set_deferred_probe_reason(const struct device *dev, char *reason) +{ + kfree(dev->p->deferred_probe_reason); + dev->p->deferred_probe_reason = reason; +} + /* * deferred_probe_work_func() - Retry probing devices in the active list. */ @@ -97,8 +102,7 @@ static void deferred_probe_work_func(struct work_struct *work) get_device(dev); - kfree(dev->p->deferred_probe_reason); - dev->p->deferred_probe_reason = NULL; + __device_set_deferred_probe_reason(dev, NULL); /* * Drop the mutex while probing each device; the probe path may @@ -126,6 +130,9 @@ static DECLARE_WORK(deferred_probe_work, deferred_probe_work_func); void driver_deferred_probe_add(struct device *dev) { + if (!dev->can_match) + return; + mutex_lock(&deferred_probe_mutex); if (list_empty(&dev->p->deferred_probe)) { dev_dbg(dev, "Added to deferred list\n"); @@ -140,8 +147,7 @@ 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; + __device_set_deferred_probe_reason(dev, NULL); } mutex_unlock(&deferred_probe_mutex); } @@ -185,7 +191,7 @@ static void driver_deferred_probe_trigger(void) * Kick the re-probe thread. It may already be scheduled, but it is * safe to kick it again. */ - schedule_work(&deferred_probe_work); + queue_work(system_unbound_wq, &deferred_probe_work); } /** @@ -220,11 +226,12 @@ void device_unblock_probing(void) void device_set_deferred_probe_reason(const struct device *dev, struct va_format *vaf) { const char *drv = dev_driver_string(dev); + char *reason; mutex_lock(&deferred_probe_mutex); - kfree(dev->p->deferred_probe_reason); - dev->p->deferred_probe_reason = kasprintf(GFP_KERNEL, "%s: %pV", drv, vaf); + reason = kasprintf(GFP_KERNEL, "%s: %pV", drv, vaf); + __device_set_deferred_probe_reason(dev, reason); mutex_unlock(&deferred_probe_mutex); } @@ -294,6 +301,8 @@ static void deferred_probe_timeout_work_func(struct work_struct *work) { struct device_private *p; + fw_devlink_drivers_done(); + driver_deferred_probe_timeout = 0; driver_deferred_probe_trigger(); flush_work(&deferred_probe_work); @@ -315,8 +324,8 @@ static DECLARE_DELAYED_WORK(deferred_probe_timeout_work, deferred_probe_timeout_ */ static int deferred_probe_initcall(void) { - deferred_devices = debugfs_create_file("devices_deferred", 0444, NULL, - NULL, &deferred_devs_fops); + debugfs_create_file("devices_deferred", 0444, NULL, NULL, + &deferred_devs_fops); driver_deferred_probe_enable = true; driver_deferred_probe_trigger(); @@ -324,6 +333,9 @@ static int deferred_probe_initcall(void) flush_work(&deferred_probe_work); initcalls_done = true; + if (!IS_ENABLED(CONFIG_MODULES)) + fw_devlink_drivers_done(); + /* * Trigger deferred probe again, this time we won't defer anything * that is optional @@ -341,7 +353,7 @@ late_initcall(deferred_probe_initcall); static void __exit deferred_probe_exit(void) { - debugfs_remove_recursive(deferred_devices); + debugfs_remove_recursive(debugfs_lookup("devices_deferred", NULL)); } __exitcall(deferred_probe_exit); @@ -418,8 +430,11 @@ static int driver_sysfs_add(struct device *dev) if (ret) goto rm_dev; - if (!IS_ENABLED(CONFIG_DEV_COREDUMP) || !dev->driver->coredump || - !device_create_file(dev, &dev_attr_coredump)) + if (!IS_ENABLED(CONFIG_DEV_COREDUMP) || !dev->driver->coredump) + return 0; + + ret = device_create_file(dev, &dev_attr_coredump); + if (!ret) return 0; sysfs_remove_link(&dev->kobj, "driver"); @@ -462,8 +477,10 @@ int device_bind_driver(struct device *dev) int ret; ret = driver_sysfs_add(dev); - if (!ret) + if (!ret) { + device_links_force_bind(dev); driver_bound(dev); + } else if (dev->bus) blocking_notifier_call_chain(&dev->bus->p->bus_notifier, BUS_NOTIFY_DRIVER_NOT_BOUND, dev); @@ -731,6 +748,7 @@ static int driver_probe_device(struct device_driver *drv, struct device *dev) if (!device_is_registered(dev)) return -ENODEV; + dev->can_match = true; pr_debug("bus: '%s': %s: matched device %s with driver %s\n", drv->bus->name, __func__, dev_name(dev), drv->name); @@ -834,6 +852,7 @@ static int __device_attach_driver(struct device_driver *drv, void *_data) return 0; } else if (ret == -EPROBE_DEFER) { dev_dbg(dev, "Device match requests probe deferral\n"); + dev->can_match = true; driver_deferred_probe_add(dev); } else if (ret < 0) { dev_dbg(dev, "Bus failed to match device: %d\n", ret); @@ -1069,6 +1088,7 @@ static int __driver_attach(struct device *dev, void *data) return 0; } else if (ret == -EPROBE_DEFER) { dev_dbg(dev, "Device match requests probe deferral\n"); + dev->can_match = true; driver_deferred_probe_add(dev); } else if (ret < 0) { dev_dbg(dev, "Bus failed to match device: %d\n", ret); diff --git a/drivers/base/devcoredump.c b/drivers/base/devcoredump.c index 9243468e2c99..8eec0e0ddff7 100644 --- a/drivers/base/devcoredump.c +++ b/drivers/base/devcoredump.c @@ -202,7 +202,7 @@ static int devcd_match_failing(struct device *dev, const void *failing) * NOTE: if two tables allocated with devcd_alloc_sgtable and then chained * using the sg_chain function then that function should be called only once * on the chained table - * @table: pointer to sg_table to free + * @data: pointer to sg_table to free */ static void devcd_free_sgtable(void *data) { @@ -210,7 +210,7 @@ static void devcd_free_sgtable(void *data) } /** - * devcd_read_from_table - copy data from sg_table to a given buffer + * devcd_read_from_sgtable - copy data from sg_table to a given buffer * and return the number of bytes read * @buffer: the buffer to copy the data to it * @buf_len: the length of the buffer @@ -292,13 +292,16 @@ void dev_coredumpm(struct device *dev, struct module *owner, if (device_add(&devcd->devcd_dev)) goto put_device; + /* + * These should normally not fail, but there is no problem + * continuing without the links, so just warn instead of + * failing. + */ if (sysfs_create_link(&devcd->devcd_dev.kobj, &dev->kobj, - "failing_device")) - /* nothing - symlink will be missing */; - - if (sysfs_create_link(&dev->kobj, &devcd->devcd_dev.kobj, - "devcoredump")) - /* nothing - symlink will be missing */; + "failing_device") || + sysfs_create_link(&dev->kobj, &devcd->devcd_dev.kobj, + "devcoredump")) + dev_warn(dev, "devcoredump create_link failed\n"); INIT_DELAYED_WORK(&devcd->del_wk, devcd_del); schedule_delayed_work(&devcd->del_wk, DEVCD_TIMEOUT); diff --git a/drivers/base/devres.c b/drivers/base/devres.c index fb9d5289a620..8746f2212781 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -58,8 +58,8 @@ static void devres_log(struct device *dev, struct devres_node *node, const char *op) { if (unlikely(log_devres)) - dev_err(dev, "DEVRES %3s %p %s (%lu bytes)\n", - op, node, node->name, (unsigned long)node->size); + dev_err(dev, "DEVRES %3s %p %s (%zu bytes)\n", + op, node, node->name, node->size); } #else /* CONFIG_DEBUG_DEVRES */ #define set_node_dbginfo(node, n, s) do {} while (0) @@ -1228,6 +1228,6 @@ EXPORT_SYMBOL_GPL(__devm_alloc_percpu); void devm_free_percpu(struct device *dev, void __percpu *pdata) { WARN_ON(devres_destroy(dev, devm_percpu_release, devm_percpu_match, - (void *)pdata)); + (__force void *)pdata)); } EXPORT_SYMBOL_GPL(devm_free_percpu); diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 653c8c6ac7a7..8be352ab4ddb 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -371,7 +371,7 @@ int __init devtmpfs_mount(void) return err; } -static DECLARE_COMPLETION(setup_done); +static __initdata DECLARE_COMPLETION(setup_done); static int handle(const char *name, umode_t mode, kuid_t uid, kgid_t gid, struct device *dev) @@ -405,7 +405,7 @@ static void __noreturn devtmpfs_work_loop(void) } } -static int __init devtmpfs_setup(void *p) +static noinline int __init devtmpfs_setup(void *p) { int err; @@ -419,7 +419,6 @@ static int __init devtmpfs_setup(void *p) init_chroot("."); out: *(int *)p = err; - complete(&setup_done); return err; } @@ -432,6 +431,7 @@ static int __ref devtmpfsd(void *p) { int err = devtmpfs_setup(p); + complete(&setup_done); if (err) return err; devtmpfs_work_loop(); diff --git a/drivers/base/node.c b/drivers/base/node.c index f449dbb2c746..2c36f61d30bc 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -268,21 +268,20 @@ static void node_init_cache_dev(struct node *node) if (!dev) return; + device_initialize(dev); dev->parent = &node->dev; dev->release = node_cache_release; if (dev_set_name(dev, "memory_side_cache")) - goto free_dev; + goto put_device; - if (device_register(dev)) - goto free_name; + if (device_add(dev)) + goto put_device; pm_runtime_no_callbacks(dev); node->cache_dev = dev; return; -free_name: - kfree_const(dev->kobj.name); -free_dev: - kfree(dev); +put_device: + put_device(dev); } /** @@ -319,25 +318,24 @@ void node_add_cache(unsigned int nid, struct node_cache_attrs *cache_attrs) return; dev = &info->dev; + device_initialize(dev); dev->parent = node->cache_dev; dev->release = node_cacheinfo_release; dev->groups = cache_groups; if (dev_set_name(dev, "index%d", cache_attrs->level)) - goto free_cache; + goto put_device; info->cache_attrs = *cache_attrs; - if (device_register(dev)) { + if (device_add(dev)) { dev_warn(&node->dev, "failed to add cache level:%d\n", cache_attrs->level); - goto free_name; + goto put_device; } pm_runtime_no_callbacks(dev); list_add_tail(&info->node, &node->cache_attrs); return; -free_name: - kfree_const(dev->kobj.name); -free_cache: - kfree(info); +put_device: + put_device(dev); } static void node_remove_caches(struct node *node) diff --git a/drivers/base/platform-msi.c b/drivers/base/platform-msi.c index 2c1e2e0c1a59..0b72b134a304 100644 --- a/drivers/base/platform-msi.c +++ b/drivers/base/platform-msi.c @@ -316,10 +316,11 @@ void *platform_msi_get_host_data(struct irq_domain *domain) } /** - * platform_msi_create_device_domain - Create a platform-msi domain + * __platform_msi_create_device_domain - Create a platform-msi domain * * @dev: The device generating the MSIs * @nvec: The number of MSIs that need to be allocated + * @is_tree: flag to indicate tree hierarchy * @write_msi_msg: Callback to write an interrupt message for @dev * @ops: The hierarchy domain operations to use * @host_data: Private data associated to this domain diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 6e1f8e0b661c..9cd34def2237 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -192,7 +192,7 @@ int platform_get_irq_optional(struct platform_device *dev, unsigned int num) #ifdef CONFIG_SPARC /* sparc does not have irqs represented as IORESOURCE_IRQ resources */ if (!dev || num >= dev->archdata.num_irqs) - return -ENXIO; + goto out_not_found; ret = dev->archdata.irqs[num]; goto out; #else @@ -223,10 +223,8 @@ int platform_get_irq_optional(struct platform_device *dev, unsigned int num) struct irq_data *irqd; irqd = irq_get_irq_data(r->start); - if (!irqd) { - ret = -ENXIO; - goto out; - } + if (!irqd) + goto out_not_found; irqd_set_trigger_type(irqd, r->flags & IORESOURCE_BITS); } @@ -249,8 +247,9 @@ int platform_get_irq_optional(struct platform_device *dev, unsigned int num) goto out; } - ret = -ENXIO; #endif +out_not_found: + ret = -ENXIO; out: WARN(ret == 0, "0 is an invalid IRQ number\n"); return ret; diff --git a/drivers/base/power/wakeup_stats.c b/drivers/base/power/wakeup_stats.c index d638259b829a..5ade7539ac02 100644 --- a/drivers/base/power/wakeup_stats.c +++ b/drivers/base/power/wakeup_stats.c @@ -154,7 +154,7 @@ static struct device *wakeup_source_device_create(struct device *parent, dev_set_drvdata(dev, ws); device_set_pm_not_required(dev); - retval = kobject_set_name(&dev->kobj, "wakeup%d", ws->id); + retval = dev_set_name(dev, "wakeup%d", ws->id); if (retval) goto error; diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index fa3719ef80e4..3cc11b813f28 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -12,10 +12,10 @@ #include <linux/slab.h> struct swnode { - int id; struct kobject kobj; struct fwnode_handle fwnode; const struct software_node *node; + int id; /* hierarchy */ struct ida child_ids; @@ -720,19 +720,30 @@ software_node_find_by_name(const struct software_node *parent, const char *name) } EXPORT_SYMBOL_GPL(software_node_find_by_name); -static int -software_node_register_properties(struct software_node *node, - const struct property_entry *properties) +static struct software_node *software_node_alloc(const struct property_entry *properties) { struct property_entry *props; + struct software_node *node; props = property_entries_dup(properties); if (IS_ERR(props)) - return PTR_ERR(props); + return ERR_CAST(props); + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) { + property_entries_free(props); + return ERR_PTR(-ENOMEM); + } node->properties = props; - return 0; + return node; +} + +static void software_node_free(const struct software_node *node) +{ + property_entries_free(node->properties); + kfree(node); } static void software_node_release(struct kobject *kobj) @@ -746,10 +757,9 @@ static void software_node_release(struct kobject *kobj) ida_simple_remove(&swnode_root_ids, swnode->id); } - if (swnode->allocated) { - property_entries_free(swnode->node->properties); - kfree(swnode->node); - } + if (swnode->allocated) + software_node_free(swnode->node); + ida_destroy(&swnode->child_ids); kfree(swnode); } @@ -767,22 +777,19 @@ swnode_register(const struct software_node *node, struct swnode *parent, int ret; swnode = kzalloc(sizeof(*swnode), GFP_KERNEL); - if (!swnode) { - ret = -ENOMEM; - goto out_err; - } + if (!swnode) + return ERR_PTR(-ENOMEM); ret = ida_simple_get(parent ? &parent->child_ids : &swnode_root_ids, 0, 0, GFP_KERNEL); if (ret < 0) { kfree(swnode); - goto out_err; + return ERR_PTR(ret); } swnode->id = ret; swnode->node = node; swnode->parent = parent; - swnode->allocated = allocated; swnode->kobj.kset = swnode_kset; fwnode_init(&swnode->fwnode, &software_node_ops); @@ -803,16 +810,17 @@ swnode_register(const struct software_node *node, struct swnode *parent, return ERR_PTR(ret); } + /* + * Assign the flag only in the successful case, so + * the above kobject_put() won't mess up with properties. + */ + swnode->allocated = allocated; + if (parent) list_add_tail(&swnode->entry, &parent->children); kobject_uevent(&swnode->kobj, KOBJ_ADD); return &swnode->fwnode; - -out_err: - if (allocated) - property_entries_free(node->properties); - return ERR_PTR(ret); } /** @@ -880,7 +888,11 @@ EXPORT_SYMBOL_GPL(software_node_unregister_nodes); * software_node_register_node_group - Register a group of software nodes * @node_group: NULL terminated array of software node pointers to be registered * - * Register multiple software nodes at once. + * Register multiple software nodes at once. If any node in the array + * has its .parent pointer set (which can only be to another software_node), + * then its parent **must** have been registered before it is; either outside + * of this function or by ordering the array such that parent comes before + * child. */ int software_node_register_node_group(const struct software_node **node_group) { @@ -906,10 +918,14 @@ EXPORT_SYMBOL_GPL(software_node_register_node_group); * software_node_unregister_node_group - Unregister a group of software nodes * @node_group: NULL terminated array of software node pointers to be unregistered * - * Unregister multiple software nodes at once. The array will be unwound in - * reverse order (i.e. last entry first) and thus if any members of the array are - * children of another member then the children must appear later in the list such - * that they are unregistered first. + * Unregister multiple software nodes at once. If parent pointers are set up + * in any of the software nodes then the array **must** be ordered such that + * parents come before their children. + * + * NOTE: If you are uncertain whether the array is ordered such that + * parents will be unregistered before their children, it is wiser to + * remove the nodes individually, in the correct order (child before + * parent). */ void software_node_unregister_node_group( const struct software_node **node_group) @@ -963,31 +979,28 @@ struct fwnode_handle * fwnode_create_software_node(const struct property_entry *properties, const struct fwnode_handle *parent) { + struct fwnode_handle *fwnode; struct software_node *node; - struct swnode *p = NULL; - int ret; + struct swnode *p; - if (parent) { - if (IS_ERR(parent)) - return ERR_CAST(parent); - if (!is_software_node(parent)) - return ERR_PTR(-EINVAL); - p = to_swnode(parent); - } + if (IS_ERR(parent)) + return ERR_CAST(parent); - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (!node) - return ERR_PTR(-ENOMEM); + p = to_swnode(parent); + if (parent && !p) + return ERR_PTR(-EINVAL); - ret = software_node_register_properties(node, properties); - if (ret) { - kfree(node); - return ERR_PTR(ret); - } + node = software_node_alloc(properties); + if (IS_ERR(node)) + return ERR_CAST(node); node->parent = p ? p->node : NULL; - return swnode_register(node, p, 1); + fwnode = swnode_register(node, p, 1); + if (IS_ERR(fwnode)) + software_node_free(node); + + return fwnode; } EXPORT_SYMBOL_GPL(fwnode_create_software_node); @@ -1032,6 +1045,7 @@ int device_add_software_node(struct device *dev, const struct software_node *nod } set_secondary_fwnode(dev, &swnode->fwnode); + software_node_notify(dev, KOBJ_ADD); return 0; } @@ -1105,8 +1119,8 @@ int software_node_notify(struct device *dev, unsigned long action) switch (action) { case KOBJ_ADD: - ret = sysfs_create_link(&dev->kobj, &swnode->kobj, - "software_node"); + ret = sysfs_create_link_nowarn(&dev->kobj, &swnode->kobj, + "software_node"); if (ret) break; diff --git a/drivers/base/test/Kconfig b/drivers/base/test/Kconfig index ba225eb1b761..2f3fa31a948e 100644 --- a/drivers/base/test/Kconfig +++ b/drivers/base/test/Kconfig @@ -8,7 +8,7 @@ config TEST_ASYNC_DRIVER_PROBE The module name will be test_async_driver_probe.ko If unsure say N. -config KUNIT_DRIVER_PE_TEST +config DRIVER_PE_KUNIT_TEST bool "KUnit Tests for property entry API" if !KUNIT_ALL_TESTS depends on KUNIT=y default KUNIT_ALL_TESTS diff --git a/drivers/base/test/Makefile b/drivers/base/test/Makefile index 2f15fae8625f..64b2f3d744d5 100644 --- a/drivers/base/test/Makefile +++ b/drivers/base/test/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TEST_ASYNC_DRIVER_PROBE) += test_async_driver_probe.o -obj-$(CONFIG_KUNIT_DRIVER_PE_TEST) += property-entry-test.o +obj-$(CONFIG_DRIVER_PE_KUNIT_TEST) += property-entry-test.o CFLAGS_REMOVE_property-entry-test.o += -fplugin-arg-structleak_plugin-byref -fplugin-arg-structleak_plugin-byref-all diff --git a/drivers/base/test/property-entry-test.c b/drivers/base/test/property-entry-test.c index abe03315180f..1106fedcceed 100644 --- a/drivers/base/test/property-entry-test.c +++ b/drivers/base/test/property-entry-test.c @@ -27,6 +27,9 @@ static void pe_test_uints(struct kunit *test) node = fwnode_create_software_node(entries, NULL); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, node); + error = fwnode_property_count_u8(node, "prop-u8"); + KUNIT_EXPECT_EQ(test, error, 1); + error = fwnode_property_read_u8(node, "prop-u8", &val_u8); KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)val_u8, 8); @@ -48,6 +51,9 @@ static void pe_test_uints(struct kunit *test) KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)val_u16, 16); + error = fwnode_property_count_u16(node, "prop-u16"); + KUNIT_EXPECT_EQ(test, error, 1); + error = fwnode_property_read_u16_array(node, "prop-u16", array_u16, 1); KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)array_u16[0], 16); @@ -65,6 +71,9 @@ static void pe_test_uints(struct kunit *test) KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)val_u32, 32); + error = fwnode_property_count_u32(node, "prop-u32"); + KUNIT_EXPECT_EQ(test, error, 1); + error = fwnode_property_read_u32_array(node, "prop-u32", array_u32, 1); KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)array_u32[0], 32); @@ -82,6 +91,9 @@ static void pe_test_uints(struct kunit *test) KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)val_u64, 64); + error = fwnode_property_count_u64(node, "prop-u64"); + KUNIT_EXPECT_EQ(test, error, 1); + error = fwnode_property_read_u64_array(node, "prop-u64", array_u64, 1); KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)array_u64[0], 64); @@ -95,15 +107,19 @@ static void pe_test_uints(struct kunit *test) error = fwnode_property_read_u64_array(node, "no-prop-u64", array_u64, 1); KUNIT_EXPECT_NE(test, error, 0); + /* Count 64-bit values as 16-bit */ + error = fwnode_property_count_u16(node, "prop-u64"); + KUNIT_EXPECT_EQ(test, error, 4); + fwnode_remove_software_node(node); } static void pe_test_uint_arrays(struct kunit *test) { - static const u8 a_u8[16] = { 8, 9 }; - static const u16 a_u16[16] = { 16, 17 }; - static const u32 a_u32[16] = { 32, 33 }; - static const u64 a_u64[16] = { 64, 65 }; + static const u8 a_u8[10] = { 8, 9 }; + static const u16 a_u16[10] = { 16, 17 }; + static const u32 a_u32[10] = { 32, 33 }; + static const u64 a_u64[10] = { 64, 65 }; static const struct property_entry entries[] = { PROPERTY_ENTRY_U8_ARRAY("prop-u8", a_u8), PROPERTY_ENTRY_U16_ARRAY("prop-u16", a_u16), @@ -126,6 +142,9 @@ static void pe_test_uint_arrays(struct kunit *test) KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)val_u8, 8); + error = fwnode_property_count_u8(node, "prop-u8"); + KUNIT_EXPECT_EQ(test, error, 10); + error = fwnode_property_read_u8_array(node, "prop-u8", array_u8, 1); KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)array_u8[0], 8); @@ -148,6 +167,9 @@ static void pe_test_uint_arrays(struct kunit *test) KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)val_u16, 16); + error = fwnode_property_count_u16(node, "prop-u16"); + KUNIT_EXPECT_EQ(test, error, 10); + error = fwnode_property_read_u16_array(node, "prop-u16", array_u16, 1); KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)array_u16[0], 16); @@ -170,6 +192,9 @@ static void pe_test_uint_arrays(struct kunit *test) KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)val_u32, 32); + error = fwnode_property_count_u32(node, "prop-u32"); + KUNIT_EXPECT_EQ(test, error, 10); + error = fwnode_property_read_u32_array(node, "prop-u32", array_u32, 1); KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)array_u32[0], 32); @@ -192,6 +217,9 @@ static void pe_test_uint_arrays(struct kunit *test) KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)val_u64, 64); + error = fwnode_property_count_u64(node, "prop-u64"); + KUNIT_EXPECT_EQ(test, error, 10); + error = fwnode_property_read_u64_array(node, "prop-u64", array_u64, 1); KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_EQ(test, (int)array_u64[0], 64); @@ -210,6 +238,14 @@ static void pe_test_uint_arrays(struct kunit *test) error = fwnode_property_read_u64_array(node, "no-prop-u64", array_u64, 1); KUNIT_EXPECT_NE(test, error, 0); + /* Count 64-bit values as 16-bit */ + error = fwnode_property_count_u16(node, "prop-u64"); + KUNIT_EXPECT_EQ(test, error, 40); + + /* Other way around */ + error = fwnode_property_count_u64(node, "prop-u16"); + KUNIT_EXPECT_EQ(test, error, 2); + fwnode_remove_software_node(node); } @@ -239,6 +275,9 @@ static void pe_test_strings(struct kunit *test) KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_STREQ(test, str, "single"); + error = fwnode_property_string_array_count(node, "str"); + KUNIT_EXPECT_EQ(test, error, 1); + error = fwnode_property_read_string_array(node, "str", strs, 1); KUNIT_EXPECT_EQ(test, error, 1); KUNIT_EXPECT_STREQ(test, strs[0], "single"); @@ -258,6 +297,9 @@ static void pe_test_strings(struct kunit *test) KUNIT_EXPECT_EQ(test, error, 0); KUNIT_EXPECT_STREQ(test, str, ""); + error = fwnode_property_string_array_count(node, "strs"); + KUNIT_EXPECT_EQ(test, error, 2); + error = fwnode_property_read_string_array(node, "strs", strs, 3); KUNIT_EXPECT_EQ(test, error, 2); KUNIT_EXPECT_STREQ(test, strs[0], "string-a"); @@ -370,15 +412,8 @@ static void pe_test_reference(struct kunit *test) }; static const struct software_node_ref_args refs[] = { - { - .node = &nodes[0], - .nargs = 0, - }, - { - .node = &nodes[1], - .nargs = 2, - .args = { 3, 4 }, - }, + SOFTWARE_NODE_REFERENCE(&nodes[0]), + SOFTWARE_NODE_REFERENCE(&nodes[1], 3, 4), }; const struct property_entry entries[] = { diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 39cfc6c6a8d2..a3b30f7de2ef 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -4610,6 +4610,8 @@ int of_clk_add_hw_provider(struct device_node *np, if (ret < 0) of_clk_del_provider(np); + fwnode_dev_initialized(&np->fwnode, true); + return ret; } EXPORT_SYMBOL_GPL(of_clk_add_hw_provider); diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index c211222f5d0c..4105df74f2b0 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -9,6 +9,7 @@ * (originally switch class is supported) */ +#include <linux/devm-helpers.h> #include <linux/extcon-provider.h> #include <linux/gpio/consumer.h> #include <linux/init.h> @@ -112,7 +113,9 @@ static int gpio_extcon_probe(struct platform_device *pdev) if (ret < 0) return ret; - INIT_DELAYED_WORK(&data->work, gpio_extcon_work); + ret = devm_delayed_work_autocancel(dev, &data->work, gpio_extcon_work); + if (ret) + return ret; /* * Request the interrupt of gpio to detect whether external connector @@ -131,15 +134,6 @@ static int gpio_extcon_probe(struct platform_device *pdev) return 0; } -static int gpio_extcon_remove(struct platform_device *pdev) -{ - struct gpio_extcon_data *data = platform_get_drvdata(pdev); - - cancel_delayed_work_sync(&data->work); - - return 0; -} - #ifdef CONFIG_PM_SLEEP static int gpio_extcon_resume(struct device *dev) { @@ -158,7 +152,6 @@ static SIMPLE_DEV_PM_OPS(gpio_extcon_pm_ops, NULL, gpio_extcon_resume); static struct platform_driver gpio_extcon_driver = { .probe = gpio_extcon_probe, - .remove = gpio_extcon_remove, .driver = { .name = "extcon-gpio", .pm = &gpio_extcon_pm_ops, diff --git a/drivers/extcon/extcon-intel-int3496.c b/drivers/extcon/extcon-intel-int3496.c index 80c9abcc3f97..fb527c23639e 100644 --- a/drivers/extcon/extcon-intel-int3496.c +++ b/drivers/extcon/extcon-intel-int3496.c @@ -11,6 +11,7 @@ */ #include <linux/acpi.h> +#include <linux/devm-helpers.h> #include <linux/extcon-provider.h> #include <linux/gpio/consumer.h> #include <linux/interrupt.h> @@ -101,7 +102,9 @@ static int int3496_probe(struct platform_device *pdev) return -ENOMEM; data->dev = dev; - INIT_DELAYED_WORK(&data->work, int3496_do_usb_id); + ret = devm_delayed_work_autocancel(dev, &data->work, int3496_do_usb_id); + if (ret) + return ret; data->gpio_usb_id = devm_gpiod_get(dev, "id", GPIOD_IN); if (IS_ERR(data->gpio_usb_id)) { @@ -155,16 +158,6 @@ static int int3496_probe(struct platform_device *pdev) return 0; } -static int int3496_remove(struct platform_device *pdev) -{ - struct int3496_data *data = platform_get_drvdata(pdev); - - devm_free_irq(&pdev->dev, data->usb_id_irq, data); - cancel_delayed_work_sync(&data->work); - - return 0; -} - static const struct acpi_device_id int3496_acpi_match[] = { { "INT3496" }, { } @@ -177,7 +170,6 @@ static struct platform_driver int3496_driver = { .acpi_match_table = int3496_acpi_match, }, .probe = int3496_probe, - .remove = int3496_remove, }; module_platform_driver(int3496_driver); diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index a2852bcc5f0d..d2c1a8b89c08 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c @@ -9,6 +9,7 @@ * Author: Hema HK <hemahk@ti.com> */ +#include <linux/devm-helpers.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/platform_device.h> @@ -237,7 +238,11 @@ static int palmas_usb_probe(struct platform_device *pdev) palmas_usb->sw_debounce_jiffies = msecs_to_jiffies(debounce); } - INIT_DELAYED_WORK(&palmas_usb->wq_detectid, palmas_gpio_id_detect); + status = devm_delayed_work_autocancel(&pdev->dev, + &palmas_usb->wq_detectid, + palmas_gpio_id_detect); + if (status) + return status; palmas->usb = palmas_usb; palmas_usb->palmas = palmas; @@ -359,15 +364,6 @@ static int palmas_usb_probe(struct platform_device *pdev) return 0; } -static int palmas_usb_remove(struct platform_device *pdev) -{ - struct palmas_usb *palmas_usb = platform_get_drvdata(pdev); - - cancel_delayed_work_sync(&palmas_usb->wq_detectid); - - return 0; -} - #ifdef CONFIG_PM_SLEEP static int palmas_usb_suspend(struct device *dev) { @@ -422,7 +418,6 @@ static const struct of_device_id of_palmas_match_tbl[] = { static struct platform_driver palmas_usb_driver = { .probe = palmas_usb_probe, - .remove = palmas_usb_remove, .driver = { .name = "palmas-usb", .of_match_table = of_palmas_match_tbl, diff --git a/drivers/extcon/extcon-qcom-spmi-misc.c b/drivers/extcon/extcon-qcom-spmi-misc.c index 9e8ccfbea026..eb02cb962b5e 100644 --- a/drivers/extcon/extcon-qcom-spmi-misc.c +++ b/drivers/extcon/extcon-qcom-spmi-misc.c @@ -7,6 +7,7 @@ * Stephen Boyd <stephen.boyd@linaro.org> */ +#include <linux/devm-helpers.h> #include <linux/extcon-provider.h> #include <linux/init.h> #include <linux/interrupt.h> @@ -116,7 +117,11 @@ static int qcom_usb_extcon_probe(struct platform_device *pdev) } info->debounce_jiffies = msecs_to_jiffies(USB_ID_DEBOUNCE_MS); - INIT_DELAYED_WORK(&info->wq_detcable, qcom_usb_extcon_detect_cable); + + ret = devm_delayed_work_autocancel(dev, &info->wq_detcable, + qcom_usb_extcon_detect_cable); + if (ret) + return ret; info->id_irq = platform_get_irq_byname(pdev, "usb_id"); if (info->id_irq > 0) { @@ -158,15 +163,6 @@ static int qcom_usb_extcon_probe(struct platform_device *pdev) return 0; } -static int qcom_usb_extcon_remove(struct platform_device *pdev) -{ - struct qcom_usb_extcon_info *info = platform_get_drvdata(pdev); - - cancel_delayed_work_sync(&info->wq_detcable); - - return 0; -} - #ifdef CONFIG_PM_SLEEP static int qcom_usb_extcon_suspend(struct device *dev) { @@ -210,7 +206,6 @@ MODULE_DEVICE_TABLE(of, qcom_usb_extcon_dt_match); static struct platform_driver qcom_usb_extcon_driver = { .probe = qcom_usb_extcon_probe, - .remove = qcom_usb_extcon_remove, .driver = { .name = "extcon-pm8941-misc", .pm = &qcom_usb_extcon_pm_ops, diff --git a/drivers/hwmon/raspberrypi-hwmon.c b/drivers/hwmon/raspberrypi-hwmon.c index d3a64a35f7a9..805d396aa81b 100644 --- a/drivers/hwmon/raspberrypi-hwmon.c +++ b/drivers/hwmon/raspberrypi-hwmon.c @@ -7,6 +7,7 @@ * Copyright (C) 2018 Stefan Wahren <stefan.wahren@i2se.com> */ #include <linux/device.h> +#include <linux/devm-helpers.h> #include <linux/err.h> #include <linux/hwmon.h> #include <linux/module.h> @@ -106,6 +107,7 @@ static int rpi_hwmon_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rpi_hwmon_data *data; + int ret; data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) @@ -119,7 +121,10 @@ static int rpi_hwmon_probe(struct platform_device *pdev) &rpi_chip_info, NULL); - INIT_DELAYED_WORK(&data->get_values_poll_work, get_values_poll); + ret = devm_delayed_work_autocancel(dev, &data->get_values_poll_work, + get_values_poll); + if (ret) + return ret; platform_set_drvdata(pdev, data); if (!PTR_ERR_OR_ZERO(data->hwmon_dev)) @@ -128,18 +133,8 @@ static int rpi_hwmon_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(data->hwmon_dev); } -static int rpi_hwmon_remove(struct platform_device *pdev) -{ - struct rpi_hwmon_data *data = platform_get_drvdata(pdev); - - cancel_delayed_work_sync(&data->get_values_poll_work); - - return 0; -} - static struct platform_driver rpi_hwmon_driver = { .probe = rpi_hwmon_probe, - .remove = rpi_hwmon_remove, .driver = { .name = "raspberrypi-hwmon", }, diff --git a/drivers/media/pci/intel/ipu3/cio2-bridge.c b/drivers/media/pci/intel/ipu3/cio2-bridge.c index c2199042d3db..e8511787c1e4 100644 --- a/drivers/media/pci/intel/ipu3/cio2-bridge.c +++ b/drivers/media/pci/intel/ipu3/cio2-bridge.c @@ -79,8 +79,8 @@ static void cio2_bridge_create_fwnode_properties( { sensor->prop_names = prop_names; - sensor->local_ref[0].node = &sensor->swnodes[SWNODE_CIO2_ENDPOINT]; - sensor->remote_ref[0].node = &sensor->swnodes[SWNODE_SENSOR_ENDPOINT]; + sensor->local_ref[0] = SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_CIO2_ENDPOINT]); + sensor->remote_ref[0] = SOFTWARE_NODE_REFERENCE(&sensor->swnodes[SWNODE_SENSOR_ENDPOINT]); sensor->dev_properties[0] = PROPERTY_ENTRY_U32( sensor->prop_names.clock_frequency, diff --git a/drivers/of/property.c b/drivers/of/property.c index 78427c85bef3..aab6383f0219 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1038,6 +1038,25 @@ static bool of_is_ancestor_of(struct device_node *test_ancestor, return false; } +static struct device_node *of_get_compat_node(struct device_node *np) +{ + of_node_get(np); + + while (np) { + if (!of_device_is_available(np)) { + of_node_put(np); + np = NULL; + } + + if (of_find_property(np, "compatible", NULL)) + break; + + np = of_get_next_parent(np); + } + + return np; +} + /** * of_link_to_phandle - Add fwnode link to supplier from supplier phandle * @con_np: consumer device tree node @@ -1061,25 +1080,11 @@ static int of_link_to_phandle(struct device_node *con_np, struct device *sup_dev; struct device_node *tmp_np = sup_np; - of_node_get(sup_np); /* * Find the device node that contains the supplier phandle. It may be * @sup_np or it may be an ancestor of @sup_np. */ - while (sup_np) { - - /* Don't allow linking to a disabled supplier */ - if (!of_device_is_available(sup_np)) { - of_node_put(sup_np); - sup_np = NULL; - } - - if (of_find_property(sup_np, "compatible", NULL)) - break; - - sup_np = of_get_next_parent(sup_np); - } - + sup_np = of_get_compat_node(sup_np); if (!sup_np) { pr_debug("Not linking %pOFP to %pOFP - No device\n", con_np, tmp_np); @@ -1225,6 +1230,8 @@ static struct device_node *parse_##fname(struct device_node *np, \ * @parse_prop.prop_name: Name of property holding a phandle value * @parse_prop.index: For properties holding a list of phandles, this is the * index into the list + * @optional: The property can be an optional dependency. + * @node_not_dev: The consumer node containing the property is never a device. * * Returns: * parse_prop() return values are @@ -1236,6 +1243,7 @@ struct supplier_bindings { struct device_node *(*parse_prop)(struct device_node *np, const char *prop_name, int index); bool optional; + bool node_not_dev; }; DEFINE_SIMPLE_PROP(clocks, "clocks", "#clock-cells") @@ -1260,6 +1268,7 @@ DEFINE_SIMPLE_PROP(pinctrl5, "pinctrl-5", NULL) DEFINE_SIMPLE_PROP(pinctrl6, "pinctrl-6", NULL) DEFINE_SIMPLE_PROP(pinctrl7, "pinctrl-7", NULL) DEFINE_SIMPLE_PROP(pinctrl8, "pinctrl-8", NULL) +DEFINE_SIMPLE_PROP(remote_endpoint, "remote-endpoint", NULL) DEFINE_SUFFIX_PROP(regulators, "-supply", NULL) DEFINE_SUFFIX_PROP(gpio, "-gpio", "#gpio-cells") @@ -1343,6 +1352,7 @@ static const struct supplier_bindings of_supplier_bindings[] = { { .parse_prop = parse_pinctrl6, }, { .parse_prop = parse_pinctrl7, }, { .parse_prop = parse_pinctrl8, }, + { .parse_prop = parse_remote_endpoint, .node_not_dev = true, }, { .parse_prop = parse_gpio_compat, }, { .parse_prop = parse_interrupts, }, { .parse_prop = parse_regulators, }, @@ -1387,10 +1397,16 @@ static int of_link_property(struct device_node *con_np, const char *prop_name) } while ((phandle = s->parse_prop(con_np, prop_name, i))) { + struct device_node *con_dev_np; + + con_dev_np = s->node_not_dev + ? of_get_compat_node(con_np) + : of_node_get(con_np); matched = true; i++; - of_link_to_phandle(con_np, phandle); + of_link_to_phandle(con_dev_np, phandle); of_node_put(phandle); + of_node_put(con_dev_np); } s++; } diff --git a/drivers/platform/x86/gpd-pocket-fan.c b/drivers/platform/x86/gpd-pocket-fan.c index 5b516e4c2bfb..7a20f68ae206 100644 --- a/drivers/platform/x86/gpd-pocket-fan.c +++ b/drivers/platform/x86/gpd-pocket-fan.c @@ -6,6 +6,7 @@ */ #include <linux/acpi.h> +#include <linux/devm-helpers.h> #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/moduleparam.h> @@ -124,7 +125,7 @@ static void gpd_pocket_fan_force_update(struct gpd_pocket_fan_data *fan) static int gpd_pocket_fan_probe(struct platform_device *pdev) { struct gpd_pocket_fan_data *fan; - int i; + int i, ret; for (i = 0; i < ARRAY_SIZE(temp_limits); i++) { if (temp_limits[i] < 20000 || temp_limits[i] > 90000) { @@ -152,7 +153,10 @@ static int gpd_pocket_fan_probe(struct platform_device *pdev) return -ENOMEM; fan->dev = &pdev->dev; - INIT_DELAYED_WORK(&fan->work, gpd_pocket_fan_worker); + ret = devm_delayed_work_autocancel(&pdev->dev, &fan->work, + gpd_pocket_fan_worker); + if (ret) + return ret; /* Note this returns a "weak" reference which we don't need to free */ fan->dts0 = thermal_zone_get_zone_by_name("soc_dts0"); @@ -177,14 +181,6 @@ static int gpd_pocket_fan_probe(struct platform_device *pdev) return 0; } -static int gpd_pocket_fan_remove(struct platform_device *pdev) -{ - struct gpd_pocket_fan_data *fan = platform_get_drvdata(pdev); - - cancel_delayed_work_sync(&fan->work); - return 0; -} - #ifdef CONFIG_PM_SLEEP static int gpd_pocket_fan_suspend(struct device *dev) { @@ -215,7 +211,6 @@ MODULE_DEVICE_TABLE(acpi, gpd_pocket_fan_acpi_match); static struct platform_driver gpd_pocket_fan_driver = { .probe = gpd_pocket_fan_probe, - .remove = gpd_pocket_fan_remove, .driver = { .name = "gpd_pocket_fan", .acpi_match_table = gpd_pocket_fan_acpi_match, diff --git a/drivers/power/supply/axp20x_usb_power.c b/drivers/power/supply/axp20x_usb_power.c index 8933ae26c3d6..e954970b50e6 100644 --- a/drivers/power/supply/axp20x_usb_power.c +++ b/drivers/power/supply/axp20x_usb_power.c @@ -8,6 +8,7 @@ #include <linux/bitops.h> #include <linux/device.h> +#include <linux/devm-helpers.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/kernel.h> @@ -593,7 +594,11 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) power->axp20x_id = axp_data->axp20x_id; power->regmap = axp20x->regmap; power->num_irqs = axp_data->num_irq_names; - INIT_DELAYED_WORK(&power->vbus_detect, axp20x_usb_power_poll_vbus); + + ret = devm_delayed_work_autocancel(&pdev->dev, &power->vbus_detect, + axp20x_usb_power_poll_vbus); + if (ret) + return ret; if (power->axp20x_id == AXP202_ID) { /* Enable vbus valid checking */ @@ -652,15 +657,6 @@ static int axp20x_usb_power_probe(struct platform_device *pdev) return 0; } -static int axp20x_usb_power_remove(struct platform_device *pdev) -{ - struct axp20x_usb_power *power = platform_get_drvdata(pdev); - - cancel_delayed_work_sync(&power->vbus_detect); - - return 0; -} - static const struct of_device_id axp20x_usb_power_match[] = { { .compatible = "x-powers,axp202-usb-power-supply", @@ -680,7 +676,6 @@ MODULE_DEVICE_TABLE(of, axp20x_usb_power_match); static struct platform_driver axp20x_usb_power_driver = { .probe = axp20x_usb_power_probe, - .remove = axp20x_usb_power_remove, .driver = { .name = DRVNAME, .of_match_table = axp20x_usb_power_match, diff --git a/drivers/power/supply/bq24735-charger.c b/drivers/power/supply/bq24735-charger.c index ab2f4bf8f603..b5d619db79f6 100644 --- a/drivers/power/supply/bq24735-charger.c +++ b/drivers/power/supply/bq24735-charger.c @@ -17,6 +17,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include <linux/devm-helpers.h> #include <linux/err.h> #include <linux/i2c.h> #include <linux/init.h> @@ -473,7 +474,11 @@ static int bq24735_charger_probe(struct i2c_client *client, if (!charger->poll_interval) return 0; - INIT_DELAYED_WORK(&charger->poll, bq24735_poll); + ret = devm_delayed_work_autocancel(&client->dev, &charger->poll, + bq24735_poll); + if (ret) + return ret; + schedule_delayed_work(&charger->poll, msecs_to_jiffies(charger->poll_interval)); } @@ -481,16 +486,6 @@ static int bq24735_charger_probe(struct i2c_client *client, return 0; } -static int bq24735_charger_remove(struct i2c_client *client) -{ - struct bq24735 *charger = i2c_get_clientdata(client); - - if (charger->poll_interval) - cancel_delayed_work_sync(&charger->poll); - - return 0; -} - static const struct i2c_device_id bq24735_charger_id[] = { { "bq24735-charger", 0 }, {} @@ -509,7 +504,6 @@ static struct i2c_driver bq24735_charger_driver = { .of_match_table = bq24735_match_ids, }, .probe = bq24735_charger_probe, - .remove = bq24735_charger_remove, .id_table = bq24735_charger_id, }; diff --git a/drivers/power/supply/ltc2941-battery-gauge.c b/drivers/power/supply/ltc2941-battery-gauge.c index 10cd617516ec..09f3e78af4e0 100644 --- a/drivers/power/supply/ltc2941-battery-gauge.c +++ b/drivers/power/supply/ltc2941-battery-gauge.c @@ -8,6 +8,7 @@ * Author: Auryn Verwegen * Author: Mike Looijmans */ +#include <linux/devm-helpers.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of_device.h> @@ -445,15 +446,6 @@ static enum power_supply_property ltc294x_properties[] = { POWER_SUPPLY_PROP_CURRENT_NOW, }; -static int ltc294x_i2c_remove(struct i2c_client *client) -{ - struct ltc294x_info *info = i2c_get_clientdata(client); - - cancel_delayed_work_sync(&info->work); - power_supply_unregister(info->supply); - return 0; -} - static int ltc294x_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -547,7 +539,10 @@ static int ltc294x_i2c_probe(struct i2c_client *client, psy_cfg.drv_data = info; - INIT_DELAYED_WORK(&info->work, ltc294x_work); + ret = devm_delayed_work_autocancel(&client->dev, &info->work, + ltc294x_work); + if (ret) + return ret; ret = ltc294x_reset(info, prescaler_exp); if (ret < 0) { @@ -555,8 +550,8 @@ static int ltc294x_i2c_probe(struct i2c_client *client, return ret; } - info->supply = power_supply_register(&client->dev, &info->supply_desc, - &psy_cfg); + info->supply = devm_power_supply_register(&client->dev, + &info->supply_desc, &psy_cfg); if (IS_ERR(info->supply)) { dev_err(&client->dev, "failed to register ltc2941\n"); return PTR_ERR(info->supply); @@ -655,7 +650,6 @@ static struct i2c_driver ltc294x_driver = { .pm = LTC294X_PM_OPS, }, .probe = ltc294x_i2c_probe, - .remove = ltc294x_i2c_remove, .shutdown = ltc294x_i2c_shutdown, .id_table = ltc294x_i2c_id, }; diff --git a/drivers/power/supply/sbs-battery.c b/drivers/power/supply/sbs-battery.c index b6a538ebb378..70ea404b2a36 100644 --- a/drivers/power/supply/sbs-battery.c +++ b/drivers/power/supply/sbs-battery.c @@ -7,6 +7,7 @@ #include <linux/bits.h> #include <linux/delay.h> +#include <linux/devm-helpers.h> #include <linux/err.h> #include <linux/gpio/consumer.h> #include <linux/i2c.h> @@ -1165,7 +1166,10 @@ skip_gpio: } } - INIT_DELAYED_WORK(&chip->work, sbs_delayed_work); + rc = devm_delayed_work_autocancel(&client->dev, &chip->work, + sbs_delayed_work); + if (rc) + return rc; chip->power_supply = devm_power_supply_register(&client->dev, sbs_desc, &psy_cfg); @@ -1185,15 +1189,6 @@ exit_psupply: return rc; } -static int sbs_remove(struct i2c_client *client) -{ - struct sbs_info *chip = i2c_get_clientdata(client); - - cancel_delayed_work_sync(&chip->work); - - return 0; -} - #if defined CONFIG_PM_SLEEP static int sbs_suspend(struct device *dev) @@ -1248,7 +1243,6 @@ MODULE_DEVICE_TABLE(of, sbs_dt_ids); static struct i2c_driver sbs_battery_driver = { .probe_new = sbs_probe, - .remove = sbs_remove, .alert = sbs_alert, .id_table = sbs_id, .driver = { diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c index e62e1d72d943..c2442d7798ab 100644 --- a/drivers/regulator/qcom_spmi-regulator.c +++ b/drivers/regulator/qcom_spmi-regulator.c @@ -5,6 +5,7 @@ #include <linux/module.h> #include <linux/delay.h> +#include <linux/devm-helpers.h> #include <linux/err.h> #include <linux/kernel.h> #include <linux/interrupt.h> @@ -1842,7 +1843,10 @@ static int spmi_regulator_of_parse(struct device_node *node, return ret; } - INIT_DELAYED_WORK(&vreg->ocp_work, spmi_regulator_vs_ocp_work); + ret = devm_delayed_work_autocancel(dev, &vreg->ocp_work, + spmi_regulator_vs_ocp_work); + if (ret) + return ret; } return 0; @@ -2157,10 +2161,8 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev) vreg->regmap = regmap; if (reg->ocp) { vreg->ocp_irq = platform_get_irq_byname(pdev, reg->ocp); - if (vreg->ocp_irq < 0) { - ret = vreg->ocp_irq; - goto err; - } + if (vreg->ocp_irq < 0) + return vreg->ocp_irq; } vreg->desc.id = -1; vreg->desc.owner = THIS_MODULE; @@ -2203,8 +2205,7 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev) rdev = devm_regulator_register(dev, &vreg->desc, &config); if (IS_ERR(rdev)) { dev_err(dev, "failed to register %s\n", name); - ret = PTR_ERR(rdev); - goto err; + return PTR_ERR(rdev); } INIT_LIST_HEAD(&vreg->node); @@ -2212,24 +2213,6 @@ static int qcom_spmi_regulator_probe(struct platform_device *pdev) } return 0; - -err: - list_for_each_entry(vreg, vreg_list, node) - if (vreg->ocp_irq) - cancel_delayed_work_sync(&vreg->ocp_work); - return ret; -} - -static int qcom_spmi_regulator_remove(struct platform_device *pdev) -{ - struct spmi_regulator *vreg; - struct list_head *vreg_list = platform_get_drvdata(pdev); - - list_for_each_entry(vreg, vreg_list, node) - if (vreg->ocp_irq) - cancel_delayed_work_sync(&vreg->ocp_work); - - return 0; } static struct platform_driver qcom_spmi_regulator_driver = { @@ -2238,7 +2221,6 @@ static struct platform_driver qcom_spmi_regulator_driver = { .of_match_table = qcom_spmi_regulator_match, }, .probe = qcom_spmi_regulator_probe, - .remove = qcom_spmi_regulator_remove, }; module_platform_driver(qcom_spmi_regulator_driver); diff --git a/drivers/watchdog/retu_wdt.c b/drivers/watchdog/retu_wdt.c index 258dfcf9cbda..2b9017e1cd91 100644 --- a/drivers/watchdog/retu_wdt.c +++ b/drivers/watchdog/retu_wdt.c @@ -8,6 +8,7 @@ * Rewritten by Aaro Koskinen. */ +#include <linux/devm-helpers.h> #include <linux/slab.h> #include <linux/errno.h> #include <linux/device.h> @@ -127,9 +128,12 @@ static int retu_wdt_probe(struct platform_device *pdev) wdev->rdev = rdev; wdev->dev = &pdev->dev; - INIT_DELAYED_WORK(&wdev->ping_work, retu_wdt_ping_work); + ret = devm_delayed_work_autocancel(&pdev->dev, &wdev->ping_work, + retu_wdt_ping_work); + if (ret) + return ret; - ret = watchdog_register_device(retu_wdt); + ret = devm_watchdog_register_device(&pdev->dev, retu_wdt); if (ret < 0) return ret; @@ -138,25 +142,11 @@ static int retu_wdt_probe(struct platform_device *pdev) else retu_wdt_ping_enable(wdev); - platform_set_drvdata(pdev, retu_wdt); - - return 0; -} - -static int retu_wdt_remove(struct platform_device *pdev) -{ - struct watchdog_device *wdog = platform_get_drvdata(pdev); - struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog); - - watchdog_unregister_device(wdog); - cancel_delayed_work_sync(&wdev->ping_work); - return 0; } static struct platform_driver retu_wdt_driver = { .probe = retu_wdt_probe, - .remove = retu_wdt_remove, .driver = { .name = "retu-wdt", }, diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c index 686e0ad28788..9979d981e9be 100644 --- a/fs/debugfs/file.c +++ b/fs/debugfs/file.c @@ -773,7 +773,7 @@ EXPORT_SYMBOL_GPL(debugfs_create_atomic_t); ssize_t debugfs_read_file_bool(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - char buf[3]; + char buf[2]; bool val; int r; struct dentry *dentry = F_DENTRY(file); @@ -789,7 +789,6 @@ ssize_t debugfs_read_file_bool(struct file *file, char __user *user_buf, else buf[0] = 'N'; buf[1] = '\n'; - buf[2] = 0x00; return simple_read_from_buffer(user_buf, count, ppos, buf, 2); } EXPORT_SYMBOL_GPL(debugfs_read_file_bool); diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index 22e86ae4dd5a..1d252164d97b 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -35,7 +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; +static unsigned int debugfs_allow __ro_after_init = DEFAULT_DEBUGFS_ALLOW_BITS; /* * Don't allow access attributes to be changed whilst the kernel is locked down diff --git a/include/linux/device.h b/include/linux/device.h index ba660731bd25..38a2071cf776 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -49,7 +49,7 @@ struct dev_iommu; /** * struct subsys_interface - interfaces to device functions * @name: name of the device function - * @subsys: subsytem of the devices to attach to + * @subsys: subsystem of the devices to attach to * @node: the list of functions registered at the subsystem * @add_dev: device hookup to device function handler * @remove_dev: device hookup to device function handler @@ -439,6 +439,9 @@ struct dev_links_info { * @state_synced: The hardware state of this device has been synced to match * the software state of this device by calling the driver/bus * sync_state() callback. + * @can_match: The device has matched with a driver at least once or it is in + * a bus (like AMBA) which can't check for matching drivers until + * other devices probe successfully. * @dma_coherent: this particular device is dma coherent, even if the * architecture supports non-coherent devices. * @dma_ops_bypass: If set to %true then the dma_ops are bypassed for the @@ -545,6 +548,7 @@ struct device { bool offline:1; bool of_node_reused:1; bool state_synced:1; + bool can_match:1; #if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) diff --git a/include/linux/devm-helpers.h b/include/linux/devm-helpers.h new file mode 100644 index 000000000000..f40f77717a24 --- /dev/null +++ b/include/linux/devm-helpers.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef __LINUX_DEVM_HELPERS_H +#define __LINUX_DEVM_HELPERS_H + +/* + * Functions which do automatically cancel operations or release resources upon + * driver detach. + * + * These should be helpful to avoid mixing the manual and devm-based resource + * management which can be source of annoying, rarely occurring, + * hard-to-reproduce bugs. + * + * Please take into account that devm based cancellation may be performed some + * time after the remove() is ran. + * + * Thus mixing devm and manual resource management can easily cause problems + * when unwinding operations with dependencies. IRQ scheduling a work in a queue + * is typical example where IRQs are often devm-managed and WQs are manually + * cleaned at remove(). If IRQs are not manually freed at remove() (and this is + * often the case when we use devm for IRQs) we have a period of time after + * remove() - and before devm managed IRQs are freed - where new IRQ may fire + * and schedule a work item which won't be cancelled because remove() was + * already ran. + */ + +#include <linux/device.h> +#include <linux/workqueue.h> + +static inline void devm_delayed_work_drop(void *res) +{ + cancel_delayed_work_sync(res); +} + +/** + * devm_delayed_work_autocancel - Resource-managed delayed work allocation + * @dev: Device which lifetime work is bound to + * @w: Work item to be queued + * @worker: Worker function + * + * Initialize delayed work which is automatically cancelled when driver is + * detached. A few drivers need delayed work which must be cancelled before + * driver is detached to avoid accessing removed resources. + * devm_delayed_work_autocancel() can be used to omit the explicit + * cancelleation when driver is detached. + */ +static inline int devm_delayed_work_autocancel(struct device *dev, + struct delayed_work *w, + work_func_t worker) +{ + INIT_DELAYED_WORK(w, worker); + return devm_add_action(dev, devm_delayed_work_drop, w); +} + +#endif diff --git a/include/linux/platform_device.h b/include/linux/platform_device.h index 3f23f6e430bf..cd81e060863c 100644 --- a/include/linux/platform_device.h +++ b/include/linux/platform_device.h @@ -359,4 +359,7 @@ static inline int is_sh_early_platform_device(struct platform_device *pdev) } #endif /* CONFIG_SUPERH */ +/* For now only SuperH uses it */ +void early_platform_cleanup(void); + #endif /* _PLATFORM_DEVICE_H_ */ diff --git a/include/linux/property.h b/include/linux/property.h index dd4687b56239..0d876316e61d 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -254,6 +254,13 @@ struct software_node_ref_args { u64 args[NR_FWNODE_REFERENCE_ARGS]; }; +#define SOFTWARE_NODE_REFERENCE(_ref_, ...) \ +(const struct software_node_ref_args) { \ + .node = _ref_, \ + .nargs = ARRAY_SIZE(((u64[]){ 0, ##__VA_ARGS__ })) - 1, \ + .args = { __VA_ARGS__ }, \ +} + /** * struct property_entry - "Built-in" device property representation. * @name: Name of the property. @@ -362,11 +369,7 @@ struct property_entry { .name = _name_, \ .length = sizeof(struct software_node_ref_args), \ .type = DEV_PROP_REF, \ - { .pointer = &(const struct software_node_ref_args) { \ - .node = _ref_, \ - .nargs = ARRAY_SIZE(((u64[]){ 0, ##__VA_ARGS__ })) - 1, \ - .args = { __VA_ARGS__ }, \ - } }, \ + { .pointer = &SOFTWARE_NODE_REFERENCE(_ref_, ##__VA_ARGS__), }, \ } struct property_entry * diff --git a/lib/kobject_uevent.c b/lib/kobject_uevent.c index 7998affa45d4..c87d5b6a8a55 100644 --- a/lib/kobject_uevent.c +++ b/lib/kobject_uevent.c @@ -251,12 +251,13 @@ static int kobj_usermode_filter(struct kobject *kobj) static int init_uevent_argv(struct kobj_uevent_env *env, const char *subsystem) { + int buffer_size = sizeof(env->buf) - env->buflen; int len; - len = strlcpy(&env->buf[env->buflen], subsystem, - sizeof(env->buf) - env->buflen); - if (len >= (sizeof(env->buf) - env->buflen)) { - WARN(1, KERN_ERR "init_uevent_argv: buffer size too small\n"); + len = strlcpy(&env->buf[env->buflen], subsystem, buffer_size); + if (len >= buffer_size) { + pr_warn("init_uevent_argv: buffer size of %d too small, needed %d\n", + buffer_size, len); return -ENOMEM; } diff --git a/tools/testing/selftests/firmware/fw_namespace.c b/tools/testing/selftests/firmware/fw_namespace.c index 5ebc1aec7923..0e393cb5f42d 100644 --- a/tools/testing/selftests/firmware/fw_namespace.c +++ b/tools/testing/selftests/firmware/fw_namespace.c @@ -95,7 +95,7 @@ static bool test_fw_in_ns(const char *fw_name, const char *sys_path, bool block_ } if (block_fw_in_parent_ns) umount("/lib/firmware"); - return WEXITSTATUS(status) == EXIT_SUCCESS ? true : false; + return WEXITSTATUS(status) == EXIT_SUCCESS; } if (unshare(CLONE_NEWNS) != 0) { |