Skip to content

Commit 7cf28b3

Browse files
Yicong Yangrafaeljw
authored andcommitted
ACPI: scan: Use async schedule function in acpi_scan_clear_dep_fn()
The device object rescan in acpi_scan_clear_dep_fn() is scheduled on a system workqueue which is not guaranteed to be finished before entering userspace. This may cause some key devices to be missing when userspace init task tries to find them. Two issues observed on RISCV platforms: - Kernel panic due to userspace init cannot have an opened console. The console device scanning is queued by acpi_scan_clear_dep_queue() and not finished by the time userspace init process running, thus by the time userspace init runs, no console is present. - Entering rescue shell due to the lack of root devices (PCIe nvme in our case). Same reason as above, the PCIe host bridge scanning is queued on a system workqueue and finished after init process runs. The reason is because both devices (console, PCIe host bridge) depend on riscv-aplic irqchip to serve their interrupts (console's wired interrupt and PCI's INTx interrupts). In order to keep the dependency, these devices are scanned and created after initializing riscv-aplic. The riscv-aplic is initialized in device_initcall() and a device scan work is queued via acpi_scan_clear_dep_queue(), which is close to the time userspace init process is run. Since system_dfl_wq is used in acpi_scan_clear_dep_queue() with no synchronization, the issues will happen if userspace init runs before these devices are ready. The solution is to wait for the queued work to complete before entering userspace init. One possible way would be to use a dedicated workqueue instead of system_dfl_wq, and explicitly flush it somewhere in the initcall stage before entering userspace. Another way is to use async_schedule_dev_nocall() for scanning these devices. It's designed for asynchronous initialization and will work in the same way as before because it's using a dedicated unbound workqueue as well, but the kernel init code calls async_synchronize_full() right before entering userspace init which will wait for the work to complete. Compared to a dedicated workqueue, the second approach is simpler because the async schedule framework takes care of all of the details. The ACPI code only needs to focus on its job. A dedicated workqueue for this could also be redundant because some platforms don't need acpi_scan_clear_dep_queue() for their device scanning. Signed-off-by: Yicong Yang <yang.yicong@picoheart.com> [ rjw: Subject adjustment, changelog edits ] Link: https://patch.msgid.link/20260128132848.93638-1-yang.yicong@picoheart.com Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
1 parent 8567b57 commit 7cf28b3

1 file changed

Lines changed: 15 additions & 26 deletions

File tree

drivers/acpi/scan.c

Lines changed: 15 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
#define pr_fmt(fmt) "ACPI: " fmt
77

8+
#include <linux/async.h>
89
#include <linux/module.h>
910
#include <linux/init.h>
1011
#include <linux/slab.h>
@@ -2388,46 +2389,34 @@ static int acpi_dev_get_next_consumer_dev_cb(struct acpi_dep_data *dep, void *da
23882389
return 0;
23892390
}
23902391

2391-
struct acpi_scan_clear_dep_work {
2392-
struct work_struct work;
2393-
struct acpi_device *adev;
2394-
};
2395-
2396-
static void acpi_scan_clear_dep_fn(struct work_struct *work)
2392+
static void acpi_scan_clear_dep_fn(void *dev, async_cookie_t cookie)
23972393
{
2398-
struct acpi_scan_clear_dep_work *cdw;
2399-
2400-
cdw = container_of(work, struct acpi_scan_clear_dep_work, work);
2394+
struct acpi_device *adev = to_acpi_device(dev);
24012395

24022396
acpi_scan_lock_acquire();
2403-
acpi_bus_attach(cdw->adev, (void *)true);
2397+
acpi_bus_attach(adev, (void *)true);
24042398
acpi_scan_lock_release();
24052399

2406-
acpi_dev_put(cdw->adev);
2407-
kfree(cdw);
2400+
acpi_dev_put(adev);
24082401
}
24092402

24102403
static bool acpi_scan_clear_dep_queue(struct acpi_device *adev)
24112404
{
2412-
struct acpi_scan_clear_dep_work *cdw;
2413-
24142405
if (adev->dep_unmet)
24152406
return false;
24162407

2417-
cdw = kmalloc(sizeof(*cdw), GFP_KERNEL);
2418-
if (!cdw)
2419-
return false;
2420-
2421-
cdw->adev = adev;
2422-
INIT_WORK(&cdw->work, acpi_scan_clear_dep_fn);
24232408
/*
2424-
* Since the work function may block on the lock until the entire
2425-
* initial enumeration of devices is complete, put it into the unbound
2426-
* workqueue.
2409+
* Async schedule the deferred acpi_scan_clear_dep_fn() since:
2410+
* - acpi_bus_attach() needs to hold acpi_scan_lock which cannot
2411+
* be acquired under acpi_dep_list_lock (held here)
2412+
* - the deferred work at boot stage is ensured to be finished
2413+
* before userspace init task by the async_synchronize_full()
2414+
* barrier
2415+
*
2416+
* Use _nocall variant since it'll return on failure instead of
2417+
* run the function synchronously.
24272418
*/
2428-
queue_work(system_dfl_wq, &cdw->work);
2429-
2430-
return true;
2419+
return async_schedule_dev_nocall(acpi_scan_clear_dep_fn, &adev->dev);
24312420
}
24322421

24332422
static void acpi_scan_delete_dep_data(struct acpi_dep_data *dep)

0 commit comments

Comments
 (0)