mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "Jinhui Guo" <guojinhui.liam@bytedance.com>
To: <dakr@kernel.org>, <alexanderduyck@fb.com>, <bhelgaas@google.com>,
	 <bvanassche@acm.org>, <dan.j.williams@intel.com>,
	 <gregkh@linuxfoundation.org>, <helgaas@kernel.org>,
	<rafael@kernel.org>,  <tj@kernel.org>, <frederic@kernel.org>
Cc: <guojinhui.liam@bytedance.com>, <linux-kernel@vger.kernel.org>,
	 <linux-pci@vger.kernel.org>
Subject: [PATCH v2 2/3] driver core: Add NUMA-node awareness to the synchronous probe path
Date: Thu, 22 Jan 2026 22:52:07 +0800	[thread overview]
Message-ID: <20260122145208.1013-3-guojinhui.liam@bytedance.com> (raw)
In-Reply-To: <20260122145208.1013-1-guojinhui.liam@bytedance.com>

Introduce NUMA-node-aware synchronous probing: drivers
can initialize and allocate memory on the device’s local
node without scattering kmalloc_node() calls throughout
the code.

NUMA-aware probing was first added to PCI drivers by
commit d42c69972b85 ("[PATCH] PCI: Run PCI driver
initialization on local node") in 2005 and has benefited
PCI drivers ever since.

The asynchronous probe path already supports NUMA-node-aware
probing via async_schedule_dev() in the driver core. Since
NUMA affinity is orthogonal to sync/async probing, this
patch adds NUMA-node-aware support to the synchronous
probe path.

Signed-off-by: Jinhui Guo <guojinhui.liam@bytedance.com>
---
 drivers/base/dd.c | 76 +++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 70 insertions(+), 6 deletions(-)

diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index b6be95871d3d..a8d560034abe 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -810,10 +810,56 @@ static int __driver_probe_device(const struct device_driver *drv, struct device
 	return ret;
 }
 
+/* Context for NUMA execution */
+struct numa_work_ctx {
+	struct work_struct work;
+	const struct device_driver *drv;
+	struct device *dev;
+	int result;
+};
+
+/* Worker function running on the target node */
+static void __driver_probe_device_node_helper(struct work_struct *work)
+{
+	struct numa_work_ctx *ctx = container_of(work, struct numa_work_ctx, work);
+
+	ctx->result = __driver_probe_device(ctx->drv, ctx->dev);
+}
+
+/*
+ * __driver_probe_device_node - execute __driver_probe_device on a specific NUMA node synchronously
+ * @drv: driver to bind a device to
+ * @dev: device to try to bind to the driver
+ *
+ * Returns the result of the function execution, or -ENODEV if initialization fails.
+ * If the node is invalid or offline, it falls back to local execution.
+ */
+static int __driver_probe_device_node(const struct device_driver *drv, struct device *dev)
+{
+	struct numa_work_ctx ctx;
+	int node = dev_to_node(dev);
+
+	if (node < 0 || node >= MAX_NUMNODES || !node_online(node))
+		return __driver_probe_device(drv, dev);
+
+	ctx.drv = drv;
+	ctx.dev = dev;
+	ctx.result = -ENODEV;
+	INIT_WORK_ONSTACK(&ctx.work, __driver_probe_device_node_helper);
+
+	/* Use system_dfl_wq to allow execution on the specific node. */
+	queue_work_node(node, system_dfl_wq, &ctx.work);
+	flush_work(&ctx.work);
+	destroy_work_on_stack(&ctx.work);
+
+	return ctx.result;
+}
+
 /**
  * driver_probe_device - attempt to bind device & driver together
  * @drv: driver to bind a device to
  * @dev: device to try to bind to the driver
+ * @in_async: true if the caller is running in an asynchronous worker context
  *
  * This function returns -ENODEV if the device is not registered, -EBUSY if it
  * already has a driver, 0 if the device is bound successfully and a positive
@@ -824,13 +870,22 @@ static int __driver_probe_device(const struct device_driver *drv, struct device
  *
  * If the device has a parent, runtime-resume the parent before driver probing.
  */
-static int driver_probe_device(const struct device_driver *drv, struct device *dev)
+static int driver_probe_device(const struct device_driver *drv, struct device *dev, bool in_async)
 {
 	int trigger_count = atomic_read(&deferred_trigger_count);
 	int ret;
 
 	atomic_inc(&probe_count);
-	ret = __driver_probe_device(drv, dev);
+	/*
+	 * If we are already in an asynchronous worker, invoke __driver_probe_device()
+	 * directly to avoid the overhead of an additional workqueue scheduling.
+	 * The async subsystem manages its own concurrency and placement.
+	 */
+	if (in_async)
+		ret = __driver_probe_device(drv, dev);
+	else
+		ret = __driver_probe_device_node(drv, dev);
+
 	if (ret == -EPROBE_DEFER || ret == EPROBE_DEFER) {
 		driver_deferred_probe_add(dev);
 
@@ -919,6 +974,13 @@ struct device_attach_data {
 	 * driver, we'll encounter one that requests asynchronous probing.
 	 */
 	bool have_async;
+
+	/*
+	 * True when running inside an asynchronous worker context scheduled
+	 * by async_schedule_dev() with callback function
+	 * __device_attach_async_helper().
+	 */
+	bool in_async;
 };
 
 static int __device_attach_driver(struct device_driver *drv, void *_data)
@@ -958,7 +1020,7 @@ static int __device_attach_driver(struct device_driver *drv, void *_data)
 	 * Ignore errors returned by ->probe so that the next driver can try
 	 * its luck.
 	 */
-	ret = driver_probe_device(drv, dev);
+	ret = driver_probe_device(drv, dev, data->in_async);
 	if (ret < 0)
 		return ret;
 	return ret == 0;
@@ -1009,6 +1071,7 @@ static void __device_attach_async_helper(void *_dev, async_cookie_t cookie)
 		.dev		= dev,
 		.check_async	= true,
 		.want_async	= true,
+		.in_async	= true,
 	};
 
 	device_lock(dev);
@@ -1055,6 +1118,7 @@ static int __device_attach(struct device *dev, bool allow_async)
 			.dev = dev,
 			.check_async = allow_async,
 			.want_async = false,
+			.in_async = false,
 		};
 
 		ret = __device_attach_driver_scan(&data, &async);
@@ -1144,7 +1208,7 @@ int device_driver_attach(const struct device_driver *drv, struct device *dev)
 	int ret;
 
 	__device_driver_lock(dev, dev->parent);
-	ret = __driver_probe_device(drv, dev);
+	ret = __driver_probe_device_node(drv, dev);
 	__device_driver_unlock(dev, dev->parent);
 
 	/* also return probe errors as normal negative errnos */
@@ -1165,7 +1229,7 @@ static void __driver_attach_async_helper(void *_dev, async_cookie_t cookie)
 	__device_driver_lock(dev, dev->parent);
 	drv = dev->p->async_driver;
 	dev->p->async_driver = NULL;
-	ret = driver_probe_device(drv, dev);
+	ret = driver_probe_device(drv, dev, true);
 	__device_driver_unlock(dev, dev->parent);
 
 	dev_dbg(dev, "driver %s async attach completed: %d\n", drv->name, ret);
@@ -1233,7 +1297,7 @@ static int __driver_attach(struct device *dev, void *data)
 	}
 
 	__device_driver_lock(dev, dev->parent);
-	driver_probe_device(drv, dev);
+	driver_probe_device(drv, dev, false);
 	__device_driver_unlock(dev, dev->parent);
 
 	return 0;
-- 
2.20.1

  parent reply	other threads:[~2026-01-22 14:53 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-22 14:52 [PATCH v2 0/3] Add NUMA-node-aware synchronous probing to driver core Jinhui Guo
2026-01-22 14:52 ` [PATCH v2 1/3] driver core: Introduce helper function __device_attach_driver_scan() Jinhui Guo
2026-01-22 14:52 ` Jinhui Guo [this message]
2026-01-22 14:52 ` [PATCH v2 3/3] PCI: Clean up NUMA-node awareness in pci_bus_type probe Jinhui Guo
2026-01-24  1:04 ` [PATCH v2 0/3] Add NUMA-node-aware synchronous probing to driver core dan.j.williams
2026-01-26  9:17   ` Jinhui Guo
2026-01-27  2:32     ` Jinhui Guo
2026-01-27  3:37     ` dan.j.williams

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260122145208.1013-3-guojinhui.liam@bytedance.com \
    --to=guojinhui.liam@bytedance.com \
    --cc=alexanderduyck@fb.com \
    --cc=bhelgaas@google.com \
    --cc=bvanassche@acm.org \
    --cc=dakr@kernel.org \
    --cc=dan.j.williams@intel.com \
    --cc=frederic@kernel.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=helgaas@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=rafael@kernel.org \
    --cc=tj@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®