From fe6193a3187a1a7482ccfbc904bc527e7730a36c Mon Sep 17 00:00:00 2001 From: Kaushlendra Kumar Date: Thu, 25 Sep 2025 11:11:29 +0530 Subject: firmware_loader: Only call cancel when upload is active The cancel_store() function currently calls the firmware upload cancel operation even when no upload is in progress (i.e., when progress is FW_UPLOAD_PROG_IDLE). Update cancel_store() to only invoke the cancel operation when an upload is active. If the upload is idle, return -ENODEV without calling cancel. This change improves safety and correctness by ensuring driver operations are only called in valid states. Signed-off-by: Kaushlendra Kumar Link: https://patch.msgid.link/20250925054129.2199157-1-kaushlendra.kumar@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_loader/sysfs_upload.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/firmware_loader/sysfs_upload.c b/drivers/base/firmware_loader/sysfs_upload.c index 829270067d16..c3797b93c5f5 100644 --- a/drivers/base/firmware_loader/sysfs_upload.c +++ b/drivers/base/firmware_loader/sysfs_upload.c @@ -100,8 +100,10 @@ static ssize_t cancel_store(struct device *dev, struct device_attribute *attr, return -EINVAL; mutex_lock(&fwlp->lock); - if (fwlp->progress == FW_UPLOAD_PROG_IDLE) - ret = -ENODEV; + if (fwlp->progress == FW_UPLOAD_PROG_IDLE) { + mutex_unlock(&fwlp->lock); + return -ENODEV; + } fwlp->ops->cancel(fwlp->fw_upload); mutex_unlock(&fwlp->lock); -- cgit From b811e8a01d8db2ea605e6158063aac0bf00c77b8 Mon Sep 17 00:00:00 2001 From: Kaushlendra Kumar Date: Thu, 25 Sep 2025 12:08:12 +0530 Subject: firmware_loader: Replace simple_strtol() with kstrtoint() Replace deprecated simple_strtol() calls with kstrtoint() in timeout_store() and firmware_loading_store() functions to improve input validation and error handling. The simple_strtol() function does not provide proper error checking for invalid input, while kstrtoint() returns an error for malformed strings. This change adds proper validation for user input from sysfs attributes, returning -EINVAL for invalid numeric strings instead of silently accepting potentially malformed input. The behavior for valid numeric input remains unchanged. The simple_strtol() function is deprecated in favor of kstrtoint() family functions which provide better error handling and are recommended for new code and replacements. Signed-off-by: Kaushlendra Kumar Link: https://patch.msgid.link/20250925063812.2269501-1-kaushlendra.kumar@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_loader/sysfs.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/firmware_loader/sysfs.c b/drivers/base/firmware_loader/sysfs.c index add0b9b75edd..92e91050f96a 100644 --- a/drivers/base/firmware_loader/sysfs.c +++ b/drivers/base/firmware_loader/sysfs.c @@ -47,7 +47,10 @@ static ssize_t timeout_show(const struct class *class, const struct class_attrib static ssize_t timeout_store(const struct class *class, const struct class_attribute *attr, const char *buf, size_t count) { - int tmp_loading_timeout = simple_strtol(buf, NULL, 10); + int tmp_loading_timeout; + + if (kstrtoint(buf, 10, &tmp_loading_timeout)) + return -EINVAL; if (tmp_loading_timeout < 0) tmp_loading_timeout = 0; @@ -157,7 +160,10 @@ static ssize_t firmware_loading_store(struct device *dev, struct fw_sysfs *fw_sysfs = to_fw_sysfs(dev); struct fw_priv *fw_priv; ssize_t written = count; - int loading = simple_strtol(buf, NULL, 10); + int loading; + + if (kstrtoint(buf, 10, &loading)) + return -EINVAL; mutex_lock(&fw_lock); fw_priv = fw_sysfs->fw_priv; -- cgit From 6d0ef68955d30be1e218caf160ec32eec23ebc6e Mon Sep 17 00:00:00 2001 From: Yunhui Cui Date: Tue, 23 Sep 2025 09:54:09 +0800 Subject: arch_topology: move parse_acpi_topology() to common code Currently, RISC-V lacks arch-specific registers for CPU topology properties and must get them from ACPI. Thus, parse_acpi_topology() is moved from arm64/ to drivers/ for RISC-V reuse. Signed-off-by: Yunhui Cui Reviewed-by: Sudeep Holla Link: https://patch.msgid.link/20250923015409.15983-2-cuiyunhui@bytedance.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/arch_topology.c | 96 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index 1037169abb45..1ccb1eda4ce8 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -823,12 +823,106 @@ void remove_cpu_topology(unsigned int cpu) clear_cpu_topology(cpu); } +#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV) +struct cpu_smt_info { + unsigned int thread_num; + int core_id; +}; + +static bool __init acpi_cpu_is_threaded(int cpu) +{ + int is_threaded = acpi_pptt_cpu_is_thread(cpu); + + /* + * if the PPTT doesn't have thread information, check for architecture + * specific fallback if available + */ + if (is_threaded < 0) + is_threaded = arch_cpu_is_threaded(); + + return !!is_threaded; +} + +/* + * Propagate the topology information of the processor_topology_node tree to the + * cpu_topology array. + */ __weak int __init parse_acpi_topology(void) { + unsigned int max_smt_thread_num = 1; + struct cpu_smt_info *entry; + struct xarray hetero_cpu; + unsigned long hetero_id; + int cpu, topology_id; + + if (acpi_disabled) + return 0; + + xa_init(&hetero_cpu); + + for_each_possible_cpu(cpu) { + topology_id = find_acpi_cpu_topology(cpu, 0); + if (topology_id < 0) + return topology_id; + + if (acpi_cpu_is_threaded(cpu)) { + cpu_topology[cpu].thread_id = topology_id; + topology_id = find_acpi_cpu_topology(cpu, 1); + cpu_topology[cpu].core_id = topology_id; + + /* + * In the PPTT, CPUs below a node with the 'identical + * implementation' flag have the same number of threads. + * Count the number of threads for only one CPU (i.e. + * one core_id) among those with the same hetero_id. + * See the comment of find_acpi_cpu_topology_hetero_id() + * for more details. + * + * One entry is created for each node having: + * - the 'identical implementation' flag + * - its parent not having the flag + */ + hetero_id = find_acpi_cpu_topology_hetero_id(cpu); + entry = xa_load(&hetero_cpu, hetero_id); + if (!entry) { + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + WARN_ON_ONCE(!entry); + + if (entry) { + entry->core_id = topology_id; + entry->thread_num = 1; + xa_store(&hetero_cpu, hetero_id, + entry, GFP_KERNEL); + } + } else if (entry->core_id == topology_id) { + entry->thread_num++; + } + } else { + cpu_topology[cpu].thread_id = -1; + cpu_topology[cpu].core_id = topology_id; + } + topology_id = find_acpi_cpu_topology_cluster(cpu); + cpu_topology[cpu].cluster_id = topology_id; + topology_id = find_acpi_cpu_topology_package(cpu); + cpu_topology[cpu].package_id = topology_id; + } + + /* + * This is a short loop since the number of XArray elements is the + * number of heterogeneous CPU clusters. On a homogeneous system + * there's only one entry in the XArray. + */ + xa_for_each(&hetero_cpu, hetero_id, entry) { + max_smt_thread_num = max(max_smt_thread_num, entry->thread_num); + xa_erase(&hetero_cpu, hetero_id); + kfree(entry); + } + + cpu_smt_set_num_threads(max_smt_thread_num, max_smt_thread_num); + xa_destroy(&hetero_cpu); return 0; } -#if defined(CONFIG_ARM64) || defined(CONFIG_RISCV) void __init init_cpu_topology(void) { int cpu, ret; -- cgit From 6f61a2637abe4f89877da3280775565baedb60e0 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 21 Oct 2025 00:34:24 +0200 Subject: rust: device: introduce Device::drvdata() In C dev_get_drvdata() has specific requirements under which it is valid to access the returned pointer. That is, drivers have to ensure that (1) for the duration the returned pointer is accessed the driver is bound and remains to be bound to the corresponding device, (2) the returned void * is treated according to the driver's private data type, i.e. according to what has been passed to dev_set_drvdata(). In Rust, (1) can be ensured by simply requiring the Bound device context, i.e. provide the drvdata() method for Device only. For (2) we would usually make the device type generic over the driver type, e.g. Device, where ::Data is the type of the driver's private data. However, a device does not have a driver type known at compile time and may be bound to multiple drivers throughout its lifetime. Hence, in order to be able to provide a safe accessor for the driver's device private data, we have to do the type check on runtime. This is achieved by letting a driver assert the expected type, which is then compared to a type hash stored in struct device_private when dev_set_drvdata() is called. Example: // `dev` is a `&Device`. let data = dev.drvdata::()?; There are two aspects to note: (1) Technically, the same check could be achieved by comparing the struct device_driver pointer of struct device with the struct device_driver pointer of the driver struct (e.g. struct pci_driver). However, this would - in addition the pointer comparison - require to tie back the private driver data type to the struct device_driver pointer of the driver struct to prove correctness. Besides that, accessing the driver struct (stored in the module structure) isn't trivial and would result into horrible code and API ergonomics. (2) Having a direct accessor to the driver's private data is not commonly required (at least in Rust): Bus callback methods already provide access to the driver's device private data through a &self argument, while other driver entry points such as IRQs, workqueues, timers, IOCTLs, etc. have their own private data with separate ownership and lifetime. In other words, a driver's device private data is only relevant for driver model contexts (such a file private is only relevant for file contexts). Having that said, the motivation for accessing the driver's device private data with Device::drvdata() are interactions between drivers. For instance, when an auxiliary driver calls back into its parent, the parent has to be capable to derive its private data from the corresponding device (i.e. the parent of the auxiliary device). Reviewed-by: Alice Ryhl Reviewed-by: Greg Kroah-Hartman [ * Remove unnecessary `const _: ()` block, * rename type_id_{store,match}() to {set,match}_type_id(), * assert size_of::() >= size_of::(), * add missing check in case Device::drvdata() is called from probe(). - Danilo ] Signed-off-by: Danilo Krummrich --- drivers/base/base.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/base.h b/drivers/base/base.h index 86fa7fbb3548..430cbefbc97f 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -85,6 +85,18 @@ struct driver_private { }; #define to_driver(obj) container_of(obj, struct driver_private, kobj) +#ifdef CONFIG_RUST +/** + * struct driver_type - Representation of a Rust driver type. + */ +struct driver_type { + /** + * @id: Representation of core::any::TypeId. + */ + u8 id[16]; +} __packed; +#endif + /** * struct device_private - structure to hold the private to the driver core portions of the device structure. * @@ -100,6 +112,7 @@ struct driver_private { * @async_driver - pointer to device driver awaiting probe via async_probe * @device - pointer back to the struct device that this structure is * associated with. + * @driver_type - The type of the bound Rust driver. * @dead - This device is currently either in the process of or has been * removed from the system. Any asynchronous events scheduled for this * device should exit without taking any action. @@ -116,6 +129,9 @@ struct device_private { const struct device_driver *async_driver; char *deferred_probe_reason; struct device *device; +#ifdef CONFIG_RUST + struct driver_type driver_type; +#endif u8 dead:1; }; #define to_device_private_parent(obj) \ -- cgit From 4d24145a7833c14a6521dfab57c5f10076a0110f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 11 Nov 2025 15:49:45 +0100 Subject: devres: Remove unused devm_free_percpu() Remove unused devm_free_percpu(). By the way, it was never used in the drivers/ from day 1. Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20251111145046.997309-2-andriy.shevchenko@linux.intel.com Signed-off-by: Danilo Krummrich --- drivers/base/devres.c | 25 ------------------------- 1 file changed, 25 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/devres.c b/drivers/base/devres.c index c948c88d3956..f54db6d138ab 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -1222,13 +1222,6 @@ static void devm_percpu_release(struct device *dev, void *pdata) free_percpu(p); } -static int devm_percpu_match(struct device *dev, void *data, void *p) -{ - struct devres *devr = container_of(data, struct devres, data); - - return *(void **)devr->data == p; -} - /** * __devm_alloc_percpu - Resource-managed alloc_percpu * @dev: Device to allocate per-cpu memory for @@ -1264,21 +1257,3 @@ void __percpu *__devm_alloc_percpu(struct device *dev, size_t size, return pcpu; } EXPORT_SYMBOL_GPL(__devm_alloc_percpu); - -/** - * devm_free_percpu - Resource-managed free_percpu - * @dev: Device this memory belongs to - * @pdata: Per-cpu memory to free - * - * Free memory allocated with devm_alloc_percpu(). - */ -void devm_free_percpu(struct device *dev, void __percpu *pdata) -{ - /* - * Use devres_release() to prevent memory leakage as - * devm_free_pages() does. - */ - WARN_ON(devres_release(dev, devm_percpu_release, devm_percpu_match, - (void *)(__force unsigned long)pdata)); -} -EXPORT_SYMBOL_GPL(devm_free_percpu); -- cgit From 76987bac71d5349a62f312ca1cd92de73778a652 Mon Sep 17 00:00:00 2001 From: Aaron Tomlin Date: Fri, 10 Oct 2025 21:18:29 -0400 Subject: sysfs/cpu: Use DEVICE_ATTR_RO for nohz_full attribute The /sys/devices/system/cpu/nohz_full file is a read-only attribute that reports the CPUs configured for tickless operation (CONFIG_NO_HZ_FULL=y). The current definition uses the generic DEVICE_ATTR macro, which unnecessarily requires specifying the file mode (0444) and a NULL store operation pointer. This patch converts the definition to use the dedicated DEVICE_ATTR_RO macro. This correctly expresses the read-only nature of the attribute, removes the redundant mode field, and simplifies the code. As a related cleanup, rename the show function from print_cpus_nohz_full() to the standard nohz_full_show() for consistency with common sysfs attribute naming conventions. Signed-off-by: Aaron Tomlin Link: https://patch.msgid.link/20251011011830.6670-2-atomlin@atomlin.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/cpu.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index fa0a2eef93ac..c792ec66462d 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -301,12 +301,13 @@ static ssize_t print_cpus_isolated(struct device *dev, static DEVICE_ATTR(isolated, 0444, print_cpus_isolated, NULL); #ifdef CONFIG_NO_HZ_FULL -static ssize_t print_cpus_nohz_full(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t nohz_full_show(struct device *dev, + struct device_attribute *attr, + char *buf) { return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(tick_nohz_full_mask)); } -static DEVICE_ATTR(nohz_full, 0444, print_cpus_nohz_full, NULL); +static DEVICE_ATTR_RO(nohz_full); #endif #ifdef CONFIG_CRASH_HOTPLUG -- cgit From f10c23fa159c5481dfe0025e619dc5ef844f6ce1 Mon Sep 17 00:00:00 2001 From: Aaron Tomlin Date: Fri, 10 Oct 2025 21:18:30 -0400 Subject: tick/nohz: avoid showing '(null)' if nohz_full= not set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the context of CONFIG_NO_HZ_FULL=y, tick_nohz_full_mask (of type cpumask_var_t) is initialised to 0. Memory is only allocated to the cpumask data structure, in tick_nohz_full_setup(), when Linux kernel boot-time parameter "nohz_full=" is correctly specified (see housekeeping_setup()). If "nohz_full=" is not set and an attempt is made to read /sys/devices/system/cpu/nohz_full, '(null)' can be displayed: ❯ cat /sys/devices/system/cpu/nohz_full (null) This patch changes the output to print a newline (or 0x0A) instead of '(null)', making it consistent with print_cpus_isolated() behaviour. Signed-off-by: Aaron Tomlin Link: https://patch.msgid.link/20251011011830.6670-3-atomlin@atomlin.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/cpu.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index c792ec66462d..e3cb47eae982 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -305,7 +305,10 @@ static ssize_t nohz_full_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(tick_nohz_full_mask)); + if (cpumask_available(tick_nohz_full_mask)) + return sysfs_emit(buf, "%*pbl\n", + cpumask_pr_args(tick_nohz_full_mask)); + return sysfs_emit(buf, "\n"); } static DEVICE_ATTR_RO(nohz_full); #endif -- cgit From cd22926af45400093738c758b6749de8035ed5a8 Mon Sep 17 00:00:00 2001 From: Aaron Tomlin Date: Fri, 10 Oct 2025 21:28:53 -0400 Subject: tick/nohz: Expose housekeeping CPUs in sysfs Expose the current system-defined list of housekeeping CPUs in a new sysfs file: /sys/devices/system/cpu/housekeeping. This provides userspace performance tuning tools and resource managers with a canonical, reliable method to accurately identify the cores responsible for essential kernel maintenance workloads (RCU, timer callbacks, and unbound workqueues). Currently, tooling must manually calculate the housekeeping set by parsing complex kernel boot parameters (like isolcpus= and nohz_full=) and system topology, which is prone to error. This dedicated file simplifies the configuration of low-latency workloads. Signed-off-by: Aaron Tomlin Link: https://patch.msgid.link/20251011012853.7539-2-atomlin@atomlin.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/cpu.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers/base') diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index e3cb47eae982..c6c57b6f61c6 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -300,6 +300,19 @@ static ssize_t print_cpus_isolated(struct device *dev, } static DEVICE_ATTR(isolated, 0444, print_cpus_isolated, NULL); +static ssize_t housekeeping_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + const struct cpumask *hk_mask; + + hk_mask = housekeeping_cpumask(HK_TYPE_KERNEL_NOISE); + + if (housekeeping_enabled(HK_TYPE_KERNEL_NOISE)) + return sysfs_emit(buf, "%*pbl\n", cpumask_pr_args(hk_mask)); + return sysfs_emit(buf, "\n"); +} +static DEVICE_ATTR_RO(housekeeping); + #ifdef CONFIG_NO_HZ_FULL static ssize_t nohz_full_show(struct device *dev, struct device_attribute *attr, @@ -509,6 +522,7 @@ static struct attribute *cpu_root_attrs[] = { &dev_attr_offline.attr, &dev_attr_enabled.attr, &dev_attr_isolated.attr, + &dev_attr_housekeeping.attr, #ifdef CONFIG_NO_HZ_FULL &dev_attr_nohz_full.attr, #endif -- cgit From e40ad215cea21464d516790f263dc3df69174efe Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Fri, 14 Nov 2025 15:16:17 +0100 Subject: driver core: replace use of system_unbound_wq with system_dfl_wq Currently if a user enqueue a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND. This lack of consistentcy cannot be addressed without refactoring the API. This continues the effort to refactor workqueue APIs, which began with the introduction of new workqueues and a new alloc_workqueue flag in: commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag") Switch to using system_dfl_wq because system_unbound_wq is going away as part of a workqueue restructuring. Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari Link: https://patch.msgid.link/20251114141618.172154-2-marco.crivellari@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 13ab98e033ea..4938a132a085 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -193,7 +193,7 @@ void driver_deferred_probe_trigger(void) * Kick the re-probe thread. It may already be scheduled, but it is * safe to kick it again. */ - queue_work(system_unbound_wq, &deferred_probe_work); + queue_work(system_dfl_wq, &deferred_probe_work); } /** -- cgit From ac1ab906d7a98e34be95ef63b81ff828cc432346 Mon Sep 17 00:00:00 2001 From: Marco Crivellari Date: Fri, 14 Nov 2025 15:16:18 +0100 Subject: driver core: WQ_PERCPU added to alloc_workqueue users MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently if a user enqueue a work item using schedule_delayed_work() the used wq is "system_wq" (per-cpu wq) while queue_delayed_work() use WORK_CPU_UNBOUND (used when a cpu is not specified). The same applies to schedule_work() that is using system_wq and queue_work(), that makes use again of WORK_CPU_UNBOUND. This lack of consistency cannot be addressed without refactoring the API. alloc_workqueue() treats all queues as per-CPU by default, while unbound workqueues must opt-in via WQ_UNBOUND. This default is suboptimal: most workloads benefit from unbound queues, allowing the scheduler to place worker threads where they’re needed and reducing noise when CPUs are isolated. This continues the effort to refactor workqueue APIs, which began with the introduction of new workqueues and a new alloc_workqueue flag in: commit 128ea9f6ccfb ("workqueue: Add system_percpu_wq and system_dfl_wq") commit 930c2ea566af ("workqueue: Add new WQ_PERCPU flag") This change adds a new WQ_PERCPU flag to explicitly request alloc_workqueue() to be per-cpu when WQ_UNBOUND has not been specified. With the introduction of the WQ_PERCPU flag (equivalent to !WQ_UNBOUND), any alloc_workqueue() caller that doesn’t explicitly specify WQ_UNBOUND must now use WQ_PERCPU. Once migration is complete, WQ_UNBOUND can be removed and unbound will become the implicit default. Suggested-by: Tejun Heo Signed-off-by: Marco Crivellari Link: https://patch.msgid.link/20251114141618.172154-3-marco.crivellari@suse.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/base') diff --git a/drivers/base/core.c b/drivers/base/core.c index f69dc9c85954..40de2f51a1b1 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -4138,7 +4138,7 @@ int __init devices_init(void) sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj); if (!sysfs_dev_char_kobj) goto char_kobj_err; - device_link_wq = alloc_workqueue("device_link_wq", 0, 0); + device_link_wq = alloc_workqueue("device_link_wq", WQ_PERCPU, 0); if (!device_link_wq) goto wq_err; -- cgit From ea34511aaf755349999a1067b2984a541bee1492 Mon Sep 17 00:00:00 2001 From: Vincent Liu Date: Wed, 22 Oct 2025 13:07:40 +0100 Subject: driver core: Check drivers_autoprobe for all added devices When a device is hot-plugged, the drivers_autoprobe sysfs attribute is not checked (at least for PCI devices). This means that drivers_autoprobe is not working as intended, e.g. hot-plugged PCI devices will still be autoprobed and bound to drivers even with drivers_autoprobe disabled. The problem likely started when device_add() was removed from pci_bus_add_device() in commit 4f535093cf8f ("PCI: Put pci_dev in device tree as early as possible") which means that the check for drivers_autoprobe which used to happen in bus_probe_device() is no longer present (previously bus_add_device() calls bus_probe_device()). Conveniently, in commit 91703041697c ("PCI: Allow built-in drivers to use async initial probing") device_attach() was replaced with device_initial_probe() which faciliates this change to push the check for drivers_autoprobe into device_initial_probe(). Make sure all devices check drivers_autoprobe by pushing the drivers_autoprobe check into device_initial_probe(). This will only affect devices on the PCI bus for now as device_initial_probe() is only called by pci_bus_add_device() and bus_probe_device(), but bus_probe_device() already checks for autoprobe, so callers of bus_probe_device() should not observe changes on autoprobing. Note also that pushing this check into device_initial_probe() rather than device_attach() makes it only affect automatic probing of drivers (e.g. when a device is hot-plugged), userspace can still choose to manually bind a driver by writing to drivers_probe sysfs attribute, even with autoprobe disabled. Any future callers of device_initial_probe() will respect the drivers_autoprobe sysfs attribute, which is the intended purpose of drivers_autoprobe. Signed-off-by: Vincent Liu Link: https://patch.msgid.link/20251022120740.2476482-1-vincent.liu@nutanix.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 3 +-- drivers/base/dd.c | 10 +++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'drivers/base') diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 5e75e1bce551..320e155c6be7 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -533,8 +533,7 @@ void bus_probe_device(struct device *dev) if (!sp) return; - if (sp->drivers_autoprobe) - device_initial_probe(dev); + device_initial_probe(dev); mutex_lock(&sp->mutex); list_for_each_entry(sif, &sp->interfaces, node) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 4938a132a085..349f31bedfa1 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -1077,7 +1077,15 @@ EXPORT_SYMBOL_GPL(device_attach); void device_initial_probe(struct device *dev) { - __device_attach(dev, true); + struct subsys_private *sp = bus_to_subsys(dev->bus); + + if (!sp) + return; + + if (sp->drivers_autoprobe) + __device_attach(dev, true); + + subsys_put(sp); } /* -- cgit