Commit 362f533a authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull CXL (Compute Express Link) updates from Dan Williams:
 "The highlight is initial support for CXL memory hotplug. The static
  NUMA node (ACPI SRAT Physical Address to Proximity Domain) information
  known to platform firmware is extended to support the potential
  performance-class / memory-target nodes dynamically created from
  available CXL memory device capacity.

  New unit test infrastructure is added for validating health
  information payloads.

  Fixes to module reload stress and stack usage from exposure in -next
  are included. A symbol rename and some other miscellaneous fixups are
  included as well.

  Summary:

   - Rework ACPI sub-table infrastructure to optionally be used outside
     of __init scenarios and use it for CEDT.CFMWS sub-table parsing.

   - Add support for extending num_possible_nodes by the potential
     hotplug CXL memory ranges

   - Extend tools/testing/cxl with mock memory device health information

   - Fix a module-reload workqueue race

   - Fix excessive stack-frame usage

   - Rename the driver context data structure from "cxl_mem" since that
     name collides with a proposed driver name

   - Use EXPORT_SYMBOL_NS_GPL instead of -DDEFAULT_SYMBOL_NAMESPACE at
     build time"

* tag 'cxl-for-5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl:
  cxl/core: Remove cxld_const_init in cxl_decoder_alloc()
  cxl/pmem: Fix module reload vs workqueue state
  ACPI: NUMA: Add a node and memblk for each CFMWS not in SRAT
  cxl/test: Mock acpi_table_parse_cedt()
  cxl/acpi: Convert CFMWS parsing to ACPI sub-table helpers
  ACPI: Add a context argument for table parsing handlers
  ACPI: Teach ACPI table parsing about the CEDT header format
  ACPI: Keep sub-table parsing infrastructure available for modules
  tools/testing/cxl: add mock output for the GET_HEALTH_INFO command
  cxl/memdev: Remove unused cxlmd field
  cxl/core: Convert to EXPORT_SYMBOL_NS_GPL
  cxl/memdev: Change cxl_mem to a more descriptive name
  cxl/mbox: Remove bad comment
  cxl/pmem: Fix reference counting for delayed work
parents 3acbdbf4 be185c29
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -59,6 +59,9 @@ config ACPI_SYSTEM_POWER_STATES_SUPPORT
config ACPI_CCA_REQUIRED
	bool

config ACPI_TABLE_LIB
	bool

config ACPI_DEBUGGER
	bool "AML debugger interface"
	select ACPI_DEBUG
+58 −1
Original line number Diff line number Diff line
@@ -297,6 +297,47 @@ acpi_numa_memory_affinity_init(struct acpi_srat_mem_affinity *ma)
out_err:
	return -EINVAL;
}

static int __init acpi_parse_cfmws(union acpi_subtable_headers *header,
				   void *arg, const unsigned long table_end)
{
	struct acpi_cedt_cfmws *cfmws;
	int *fake_pxm = arg;
	u64 start, end;
	int node;

	cfmws = (struct acpi_cedt_cfmws *)header;
	start = cfmws->base_hpa;
	end = cfmws->base_hpa + cfmws->window_size;

	/* Skip if the SRAT already described the NUMA details for this HPA */
	node = phys_to_target_node(start);
	if (node != NUMA_NO_NODE)
		return 0;

	node = acpi_map_pxm_to_node(*fake_pxm);

	if (node == NUMA_NO_NODE) {
		pr_err("ACPI NUMA: Too many proximity domains while processing CFMWS.\n");
		return -EINVAL;
	}

	if (numa_add_memblk(node, start, end) < 0) {
		/* CXL driver must handle the NUMA_NO_NODE case */
		pr_warn("ACPI NUMA: Failed to add memblk for CFMWS node %d [mem %#llx-%#llx]\n",
			node, start, end);
	}

	/* Set the next available fake_pxm value */
	(*fake_pxm)++;
	return 0;
}
#else
static int __init acpi_parse_cfmws(union acpi_subtable_headers *header,
				   void *arg, const unsigned long table_end)
{
	return 0;
}
#endif /* defined(CONFIG_X86) || defined (CONFIG_ARM64) */

static int __init acpi_parse_slit(struct acpi_table_header *table)
@@ -441,7 +482,7 @@ acpi_table_parse_srat(enum acpi_srat_type id,

int __init acpi_numa_init(void)
{
	int cnt = 0;
	int i, fake_pxm, cnt = 0;

	if (acpi_disabled)
		return -EINVAL;
@@ -477,6 +518,22 @@ int __init acpi_numa_init(void)
	/* SLIT: System Locality Information Table */
	acpi_table_parse(ACPI_SIG_SLIT, acpi_parse_slit);

	/*
	 * CXL Fixed Memory Window Structures (CFMWS) must be parsed
	 * after the SRAT. Create NUMA Nodes for CXL memory ranges that
	 * are defined in the CFMWS and not already defined in the SRAT.
	 * Initialize a fake_pxm as the first available PXM to emulate.
	 */

	/* fake_pxm is the next unused PXM value after SRAT parsing */
	for (i = 0, fake_pxm = -1; i < MAX_NUMNODES - 1; i++) {
		if (node_to_pxm_map[i] > fake_pxm)
			fake_pxm = node_to_pxm_map[i];
	}
	fake_pxm++;
	acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, acpi_parse_cfmws,
			      &fake_pxm);

	if (cnt < 0)
		return cnt;
	else if (!parsed_numa_memblks)
+66 −21
Original line number Diff line number Diff line
@@ -35,12 +35,13 @@ static char *mps_inti_flags_trigger[] = { "dfl", "edge", "res", "level" };

static struct acpi_table_desc initial_tables[ACPI_MAX_TABLES] __initdata;

static int acpi_apic_instance __initdata;
static int acpi_apic_instance __initdata_or_acpilib;

enum acpi_subtable_type {
	ACPI_SUBTABLE_COMMON,
	ACPI_SUBTABLE_HMAT,
	ACPI_SUBTABLE_PRMT,
	ACPI_SUBTABLE_CEDT,
};

struct acpi_subtable_entry {
@@ -52,7 +53,7 @@ struct acpi_subtable_entry {
 * Disable table checksum verification for the early stage due to the size
 * limitation of the current x86 early mapping implementation.
 */
static bool acpi_verify_table_checksum __initdata = false;
static bool acpi_verify_table_checksum __initdata_or_acpilib = false;

void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
{
@@ -216,7 +217,7 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
	}
}

static unsigned long __init
static unsigned long __init_or_acpilib
acpi_get_entry_type(struct acpi_subtable_entry *entry)
{
	switch (entry->type) {
@@ -226,11 +227,13 @@ acpi_get_entry_type(struct acpi_subtable_entry *entry)
		return entry->hdr->hmat.type;
	case ACPI_SUBTABLE_PRMT:
		return 0;
	case ACPI_SUBTABLE_CEDT:
		return entry->hdr->cedt.type;
	}
	return 0;
}

static unsigned long __init
static unsigned long __init_or_acpilib
acpi_get_entry_length(struct acpi_subtable_entry *entry)
{
	switch (entry->type) {
@@ -240,11 +243,13 @@ acpi_get_entry_length(struct acpi_subtable_entry *entry)
		return entry->hdr->hmat.length;
	case ACPI_SUBTABLE_PRMT:
		return entry->hdr->prmt.length;
	case ACPI_SUBTABLE_CEDT:
		return entry->hdr->cedt.length;
	}
	return 0;
}

static unsigned long __init
static unsigned long __init_or_acpilib
acpi_get_subtable_header_length(struct acpi_subtable_entry *entry)
{
	switch (entry->type) {
@@ -254,20 +259,40 @@ acpi_get_subtable_header_length(struct acpi_subtable_entry *entry)
		return sizeof(entry->hdr->hmat);
	case ACPI_SUBTABLE_PRMT:
		return sizeof(entry->hdr->prmt);
	case ACPI_SUBTABLE_CEDT:
		return sizeof(entry->hdr->cedt);
	}
	return 0;
}

static enum acpi_subtable_type __init
static enum acpi_subtable_type __init_or_acpilib
acpi_get_subtable_type(char *id)
{
	if (strncmp(id, ACPI_SIG_HMAT, 4) == 0)
		return ACPI_SUBTABLE_HMAT;
	if (strncmp(id, ACPI_SIG_PRMT, 4) == 0)
		return ACPI_SUBTABLE_PRMT;
	if (strncmp(id, ACPI_SIG_CEDT, 4) == 0)
		return ACPI_SUBTABLE_CEDT;
	return ACPI_SUBTABLE_COMMON;
}

static __init_or_acpilib bool has_handler(struct acpi_subtable_proc *proc)
{
	return proc->handler || proc->handler_arg;
}

static __init_or_acpilib int call_handler(struct acpi_subtable_proc *proc,
					  union acpi_subtable_headers *hdr,
					  unsigned long end)
{
	if (proc->handler)
		return proc->handler(hdr, end);
	if (proc->handler_arg)
		return proc->handler_arg(hdr, proc->arg, end);
	return -EINVAL;
}

/**
 * acpi_parse_entries_array - for each proc_num find a suitable subtable
 *
@@ -291,10 +316,10 @@ acpi_get_subtable_type(char *id)
 * On success returns sum of all matching entries for all proc handlers.
 * Otherwise, -ENODEV or -EINVAL is returned.
 */
static int __init acpi_parse_entries_array(char *id, unsigned long table_size,
		struct acpi_table_header *table_header,
		struct acpi_subtable_proc *proc, int proc_num,
		unsigned int max_entries)
static int __init_or_acpilib acpi_parse_entries_array(
	char *id, unsigned long table_size,
	struct acpi_table_header *table_header, struct acpi_subtable_proc *proc,
	int proc_num, unsigned int max_entries)
{
	struct acpi_subtable_entry entry;
	unsigned long table_end, subtable_len, entry_len;
@@ -318,8 +343,9 @@ static int __init acpi_parse_entries_array(char *id, unsigned long table_size,
		for (i = 0; i < proc_num; i++) {
			if (acpi_get_entry_type(&entry) != proc[i].id)
				continue;
			if (!proc[i].handler ||
			     (!errs && proc[i].handler(entry.hdr, table_end))) {
			if (!has_handler(&proc[i]) ||
			    (!errs &&
			     call_handler(&proc[i], entry.hdr, table_end))) {
				errs++;
				continue;
			}
@@ -352,10 +378,9 @@ static int __init acpi_parse_entries_array(char *id, unsigned long table_size,
	return errs ? -EINVAL : count;
}

int __init acpi_table_parse_entries_array(char *id,
			 unsigned long table_size,
			 struct acpi_subtable_proc *proc, int proc_num,
			 unsigned int max_entries)
int __init_or_acpilib acpi_table_parse_entries_array(
	char *id, unsigned long table_size, struct acpi_subtable_proc *proc,
	int proc_num, unsigned int max_entries)
{
	struct acpi_table_header *table_header = NULL;
	int count;
@@ -386,21 +411,41 @@ int __init acpi_table_parse_entries_array(char *id,
	return count;
}

int __init acpi_table_parse_entries(char *id,
			unsigned long table_size,
			int entry_id,
			acpi_tbl_entry_handler handler,
			unsigned int max_entries)
static int __init_or_acpilib __acpi_table_parse_entries(
	char *id, unsigned long table_size, int entry_id,
	acpi_tbl_entry_handler handler, acpi_tbl_entry_handler_arg handler_arg,
	void *arg, unsigned int max_entries)
{
	struct acpi_subtable_proc proc = {
		.id		= entry_id,
		.handler	= handler,
		.handler_arg	= handler_arg,
		.arg		= arg,
	};

	return acpi_table_parse_entries_array(id, table_size, &proc, 1,
						max_entries);
}

int __init_or_acpilib
acpi_table_parse_cedt(enum acpi_cedt_type id,
		      acpi_tbl_entry_handler_arg handler_arg, void *arg)
{
	return __acpi_table_parse_entries(ACPI_SIG_CEDT,
					  sizeof(struct acpi_table_cedt), id,
					  NULL, handler_arg, arg, 0);
}
EXPORT_SYMBOL_ACPI_LIB(acpi_table_parse_cedt);

int __init acpi_table_parse_entries(char *id, unsigned long table_size,
				    int entry_id,
				    acpi_tbl_entry_handler handler,
				    unsigned int max_entries)
{
	return __acpi_table_parse_entries(id, table_size, entry_id, handler,
					  NULL, NULL, max_entries);
}

int __init acpi_table_parse_madt(enum acpi_madt_type id,
		      acpi_tbl_entry_handler handler, unsigned int max_entries)
{
+1 −0
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ config CXL_ACPI
	tristate "CXL ACPI: Platform Support"
	depends on ACPI
	default CXL_BUS
	select ACPI_TABLE_LIB
	help
	  Enable support for host managed device memory (HDM) resources
	  published by a platform's ACPI CXL memory layout description.  See
+90 −147
Original line number Diff line number Diff line
@@ -8,8 +8,6 @@
#include <linux/pci.h>
#include "cxl.h"

static struct acpi_table_header *acpi_cedt;

/* Encode defined in CXL 2.0 8.2.5.12.7 HDM Decoder Control Register */
#define CFMWS_INTERLEAVE_WAYS(x)	(1 << (x)->interleave_ways)
#define CFMWS_INTERLEAVE_GRANULARITY(x)	((x)->granularity + 8)
@@ -74,54 +72,38 @@ static int cxl_acpi_cfmws_verify(struct device *dev,
	return 0;
}

static void cxl_add_cfmws_decoders(struct device *dev,
				   struct cxl_port *root_port)
struct cxl_cfmws_context {
	struct device *dev;
	struct cxl_port *root_port;
};

static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
			   const unsigned long end)
{
	int target_map[CXL_DECODER_MAX_INTERLEAVE];
	struct cxl_cfmws_context *ctx = arg;
	struct cxl_port *root_port = ctx->root_port;
	struct device *dev = ctx->dev;
	struct acpi_cedt_cfmws *cfmws;
	struct cxl_decoder *cxld;
	acpi_size len, cur = 0;
	void *cedt_subtable;
	int rc;

	len = acpi_cedt->length - sizeof(*acpi_cedt);
	cedt_subtable = acpi_cedt + 1;

	while (cur < len) {
		struct acpi_cedt_header *c = cedt_subtable + cur;
		int i;

		if (c->type != ACPI_CEDT_TYPE_CFMWS) {
			cur += c->length;
			continue;
		}
	int rc, i;

		cfmws = cedt_subtable + cur;

		if (cfmws->header.length < sizeof(*cfmws)) {
			dev_warn_once(dev,
				      "CFMWS entry skipped:invalid length:%u\n",
				      cfmws->header.length);
			cur += c->length;
			continue;
		}
	cfmws = (struct acpi_cedt_cfmws *) header;

	rc = cxl_acpi_cfmws_verify(dev, cfmws);
	if (rc) {
		dev_err(dev, "CFMWS range %#llx-%#llx not registered\n",
				cfmws->base_hpa, cfmws->base_hpa +
				cfmws->window_size - 1);
			cur += c->length;
			continue;
			cfmws->base_hpa,
			cfmws->base_hpa + cfmws->window_size - 1);
		return 0;
	}

	for (i = 0; i < CFMWS_INTERLEAVE_WAYS(cfmws); i++)
		target_map[i] = cfmws->interleave_targets[i];

		cxld = cxl_decoder_alloc(root_port,
					 CFMWS_INTERLEAVE_WAYS(cfmws));
	cxld = cxl_decoder_alloc(root_port, CFMWS_INTERLEAVE_WAYS(cfmws));
	if (IS_ERR(cxld))
			goto next;
		return 0;

	cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
	cxld->target_type = CXL_DECODER_EXPANDER;
@@ -130,8 +112,7 @@ static void cxl_add_cfmws_decoders(struct device *dev,
		.end = cfmws->base_hpa + cfmws->window_size - 1,
	};
	cxld->interleave_ways = CFMWS_INTERLEAVE_WAYS(cfmws);
		cxld->interleave_granularity =
			CFMWS_INTERLEAVE_GRANULARITY(cfmws);
	cxld->interleave_granularity = CFMWS_INTERLEAVE_GRANULARITY(cfmws);

	rc = cxl_decoder_add(cxld, target_map);
	if (rc)
@@ -140,68 +121,15 @@ static void cxl_add_cfmws_decoders(struct device *dev,
		rc = cxl_decoder_autoremove(dev, cxld);
	if (rc) {
		dev_err(dev, "Failed to add decoder for %#llx-%#llx\n",
				cfmws->base_hpa, cfmws->base_hpa +
				cfmws->window_size - 1);
			goto next;
		}
		dev_dbg(dev, "add: %s range %#llx-%#llx\n",
			dev_name(&cxld->dev), cfmws->base_hpa,
			cfmws->base_hpa,
			cfmws->base_hpa + cfmws->window_size - 1);
next:
		cur += c->length;
	}
}

static struct acpi_cedt_chbs *cxl_acpi_match_chbs(struct device *dev, u32 uid)
{
	struct acpi_cedt_chbs *chbs, *chbs_match = NULL;
	acpi_size len, cur = 0;
	void *cedt_subtable;

	len = acpi_cedt->length - sizeof(*acpi_cedt);
	cedt_subtable = acpi_cedt + 1;

	while (cur < len) {
		struct acpi_cedt_header *c = cedt_subtable + cur;

		if (c->type != ACPI_CEDT_TYPE_CHBS) {
			cur += c->length;
			continue;
		}

		chbs = cedt_subtable + cur;

		if (chbs->header.length < sizeof(*chbs)) {
			dev_warn_once(dev,
				      "CHBS entry skipped: invalid length:%u\n",
				      chbs->header.length);
			cur += c->length;
			continue;
		}

		if (chbs->uid != uid) {
			cur += c->length;
			continue;
		}

		if (chbs_match) {
			dev_warn_once(dev,
				      "CHBS entry skipped: duplicate UID:%u\n",
				      uid);
			cur += c->length;
			continue;
		}

		chbs_match = chbs;
		cur += c->length;
	}

	return chbs_match ? chbs_match : ERR_PTR(-ENODEV);
		return 0;
	}
	dev_dbg(dev, "add: %s node: %d range %#llx-%#llx\n",
		dev_name(&cxld->dev), phys_to_target_node(cxld->range.start),
		cfmws->base_hpa, cfmws->base_hpa + cfmws->window_size - 1);

static resource_size_t get_chbcr(struct acpi_cedt_chbs *chbs)
{
	return IS_ERR(chbs) ? CXL_RESOURCE_NONE : chbs->base;
	return 0;
}

__mock int match_add_root_ports(struct pci_dev *pdev, void *data)
@@ -355,12 +283,36 @@ static int add_host_bridge_uport(struct device *match, void *arg)
	return rc;
}

struct cxl_chbs_context {
	struct device *dev;
	unsigned long long uid;
	resource_size_t chbcr;
};

static int cxl_get_chbcr(union acpi_subtable_headers *header, void *arg,
			 const unsigned long end)
{
	struct cxl_chbs_context *ctx = arg;
	struct acpi_cedt_chbs *chbs;

	if (ctx->chbcr)
		return 0;

	chbs = (struct acpi_cedt_chbs *) header;

	if (ctx->uid != chbs->uid)
		return 0;
	ctx->chbcr = chbs->base;

	return 0;
}

static int add_host_bridge_dport(struct device *match, void *arg)
{
	int rc;
	acpi_status status;
	unsigned long long uid;
	struct acpi_cedt_chbs *chbs;
	struct cxl_chbs_context ctx;
	struct cxl_port *root_port = arg;
	struct device *host = root_port->dev.parent;
	struct acpi_device *bridge = to_cxl_host_bridge(host, match);
@@ -376,14 +328,19 @@ static int add_host_bridge_dport(struct device *match, void *arg)
		return -ENODEV;
	}

	chbs = cxl_acpi_match_chbs(host, uid);
	if (IS_ERR(chbs)) {
	ctx = (struct cxl_chbs_context) {
		.dev = host,
		.uid = uid,
	};
	acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbcr, &ctx);

	if (ctx.chbcr == 0) {
		dev_warn(host, "No CHBS found for Host Bridge: %s\n",
			 dev_name(match));
		return 0;
	}

	rc = cxl_add_dport(root_port, match, uid, get_chbcr(chbs));
	rc = cxl_add_dport(root_port, match, uid, ctx.chbcr);
	if (rc) {
		dev_err(host, "failed to add downstream port: %s\n",
			dev_name(match));
@@ -417,40 +374,29 @@ static int add_root_nvdimm_bridge(struct device *match, void *data)
	return 1;
}

static u32 cedt_instance(struct platform_device *pdev)
{
	const bool *native_acpi0017 = acpi_device_get_match_data(&pdev->dev);

	if (native_acpi0017 && *native_acpi0017)
		return 0;

	/* for cxl_test request a non-canonical instance */
	return U32_MAX;
}

static int cxl_acpi_probe(struct platform_device *pdev)
{
	int rc;
	acpi_status status;
	struct cxl_port *root_port;
	struct device *host = &pdev->dev;
	struct acpi_device *adev = ACPI_COMPANION(host);
	struct cxl_cfmws_context ctx;

	root_port = devm_cxl_add_port(host, host, CXL_RESOURCE_NONE, NULL);
	if (IS_ERR(root_port))
		return PTR_ERR(root_port);
	dev_dbg(host, "add: %s\n", dev_name(&root_port->dev));

	status = acpi_get_table(ACPI_SIG_CEDT, cedt_instance(pdev), &acpi_cedt);
	if (ACPI_FAILURE(status))
		return -ENXIO;

	rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
			      add_host_bridge_dport);
	if (rc)
		goto out;
	if (rc < 0)
		return rc;

	cxl_add_cfmws_decoders(host, root_port);
	ctx = (struct cxl_cfmws_context) {
		.dev = host,
		.root_port = root_port,
	};
	acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, cxl_parse_cfmws, &ctx);

	/*
	 * Root level scanned with host-bridge as dports, now scan host-bridges
@@ -458,24 +404,20 @@ static int cxl_acpi_probe(struct platform_device *pdev)
	 */
	rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
			      add_host_bridge_uport);
	if (rc)
		goto out;
	if (rc < 0)
		return rc;

	if (IS_ENABLED(CONFIG_CXL_PMEM))
		rc = device_for_each_child(&root_port->dev, root_port,
					   add_root_nvdimm_bridge);

out:
	acpi_put_table(acpi_cedt);
	if (rc < 0)
		return rc;

	return 0;
}

static bool native_acpi0017 = true;

static const struct acpi_device_id cxl_acpi_ids[] = {
	{ "ACPI0017", (unsigned long) &native_acpi0017 },
	{ "ACPI0017" },
	{ },
};
MODULE_DEVICE_TABLE(acpi, cxl_acpi_ids);
@@ -491,3 +433,4 @@ static struct platform_driver cxl_acpi_driver = {
module_platform_driver(cxl_acpi_driver);
MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS(CXL);
MODULE_IMPORT_NS(ACPI);
Loading