The current probe time ownership check for Soft Reserved memory based solely on CXL window intersection is insufficient. dax_hmem probing is not always guaranteed to run after CXL enumeration and region assembly, which can lead to incorrect ownership decisions before the CXL stack has finished publishing windows and assembling committed regions. Introduce deferred ownership handling for Soft Reserved ranges that intersect CXL windows. When such a range is encountered during the initial dax_hmem probe, schedule deferred work to wait for the CXL stack to complete enumeration and region assembly before deciding ownership. Once the deferred work runs, evaluate each Soft Reserved range individually: if a CXL region fully contains the range, skip it and let dax_cxl bind. Otherwise, register it with dax_hmem. This per-range ownership model avoids the need for CXL region teardown and alloc_dax_region() resource exclusion prevents double claiming. Introduce a boolean flag dax_hmem_initial_probe to live inside device.c so it survives module reload. Ensure dax_cxl defers driver registration until dax_hmem has completed ownership resolution. dax_cxl calls dax_hmem_flush_work() before cxl_driver_register(), which both waits for the deferred work to complete and creates a module symbol dependency that forces dax_hmem.ko to load before dax_cxl. Co-developed-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: Smita Koralahalli <Smita.KoralahalliChannabasappa@amd.com> Reviewed-by: Dave Jiang <dave.jiang@intel.com> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com> Link: https://patch.msgid.link/20260322195343.206900-9-Smita.KoralahalliChannabasappa@amd.com Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: Dave Jiang <dave.jiang@intel.com>
97 lines
2.1 KiB
C
97 lines
2.1 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
#include <linux/platform_device.h>
|
|
#include <linux/memregion.h>
|
|
#include <linux/module.h>
|
|
#include <linux/dax.h>
|
|
#include <linux/mm.h>
|
|
|
|
static bool nohmem;
|
|
module_param_named(disable, nohmem, bool, 0444);
|
|
|
|
bool dax_hmem_initial_probe;
|
|
EXPORT_SYMBOL_GPL(dax_hmem_initial_probe);
|
|
|
|
static bool platform_initialized;
|
|
static DEFINE_MUTEX(hmem_resource_lock);
|
|
static struct resource hmem_active = {
|
|
.name = "HMEM devices",
|
|
.start = 0,
|
|
.end = -1,
|
|
.flags = IORESOURCE_MEM,
|
|
};
|
|
|
|
int walk_hmem_resources(struct device *host, walk_hmem_fn fn)
|
|
{
|
|
struct resource *res;
|
|
int rc = 0;
|
|
|
|
mutex_lock(&hmem_resource_lock);
|
|
for (res = hmem_active.child; res; res = res->sibling) {
|
|
rc = fn(host, (int) res->desc, res);
|
|
if (rc)
|
|
break;
|
|
}
|
|
mutex_unlock(&hmem_resource_lock);
|
|
return rc;
|
|
}
|
|
EXPORT_SYMBOL_GPL(walk_hmem_resources);
|
|
|
|
static void __hmem_register_resource(int target_nid, struct resource *res)
|
|
{
|
|
struct platform_device *pdev;
|
|
struct resource *new;
|
|
int rc;
|
|
|
|
new = __request_region(&hmem_active, res->start, resource_size(res), "",
|
|
0);
|
|
if (!new) {
|
|
pr_debug("hmem range %pr already active\n", res);
|
|
return;
|
|
}
|
|
|
|
new->desc = target_nid;
|
|
|
|
if (platform_initialized)
|
|
return;
|
|
|
|
pdev = platform_device_alloc("hmem_platform", 0);
|
|
if (!pdev) {
|
|
pr_err_once("failed to register device-dax hmem_platform device\n");
|
|
return;
|
|
}
|
|
|
|
rc = platform_device_add(pdev);
|
|
if (rc)
|
|
platform_device_put(pdev);
|
|
else
|
|
platform_initialized = true;
|
|
}
|
|
|
|
void hmem_register_resource(int target_nid, struct resource *res)
|
|
{
|
|
if (nohmem)
|
|
return;
|
|
|
|
mutex_lock(&hmem_resource_lock);
|
|
__hmem_register_resource(target_nid, res);
|
|
mutex_unlock(&hmem_resource_lock);
|
|
}
|
|
|
|
static __init int hmem_register_one(struct resource *res, void *data)
|
|
{
|
|
hmem_register_resource(phys_to_target_node(res->start), res);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static __init int hmem_init(void)
|
|
{
|
|
walk_soft_reserve_res(0, -1, NULL, hmem_register_one);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* As this is a fallback for address ranges unclaimed by the ACPI HMAT
|
|
* parsing it must be at an initcall level greater than hmat_init().
|
|
*/
|
|
device_initcall(hmem_init);
|