Commit 2e925002 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull CXL updates from Dave Jiang:

 - Three CXL mailbox passthrough commands are added to support the
   populating and clearing of vendor debug logs:
     - Get Log Capabilities
     - Get Supported Log Sub-List Commands
     - Clear Log

 - Add support of Device Phyiscal Address (DPA) to Host Physical Address
   (HPA) translation for CXL events of cxl_dram and cxl_general media.

   This allows user space to figure out which CXL region the event
   occured via trace event.

 - Connect CXL to CPER reporting.

   If a device is configured for firmware first, CXL event records are
   not sent directly to the host. Those records are reported through EFI
   Common Platform Error Records (CPER). Add support to route the CPER
   records through the CXL sub-system in order to provide DPA to HPA
   translation and also event decoding and tracing. This is useful for
   users to determine which system issues may correspond to specific
   hardware events.

 - A number of misc cleanups and fixes:
     - Fix for compile warning of cxl_security_ops
     - Add debug message for invalid interleave granularity
     - Enhancement to cxl-test event testing
     - Add dev_warn() on unsupported mixed mode decoder
     - Fix use of phys_to_target_node() for x86
     - Use helper function for decoder enum instead of open coding
     - Include missing headers for cxl-event
     - Fix MAINTAINERS file entry
     - Fix cxlr_pmem memory leak
     - Cleanup __cxl_parse_cfmws via scope-based resource menagement
     - Convert cxl_pmem_region_alloc() to scope-based resource management

* tag 'cxl-for-6.10' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl: (21 commits)
  cxl/cper: Remove duplicated GUID defines
  cxl/cper: Fix non-ACPI-APEI-GHES build
  cxl/pci: Process CPER events
  acpi/ghes: Process CXL Component Events
  cxl/region: Convert cxl_pmem_region_alloc to scope-based resource management
  cxl/acpi: Cleanup __cxl_parse_cfmws()
  cxl/region: Fix cxlr_pmem leaks
  cxl/core: Add region info to cxl_general_media and cxl_dram events
  cxl/region: Move cxl_trace_hpa() work to the region driver
  cxl/region: Move cxl_dpa_to_region() work to the region driver
  cxl/trace: Correct DPA field masks for general_media & dram events
  MAINTAINERS: repair file entry in COMPUTE EXPRESS LINK
  cxl/cxl-event: include missing <linux/types.h> and <linux/uuid.h>
  cxl/hdm: Debug, use decoder name function
  cxl: Fix use of phys_to_target_node() for x86
  cxl/hdm: dev_warn() on unsupported mixed mode decoder
  cxl/test: Enhance event testing
  cxl/hdm: Add debug message for invalid interleave granularity
  cxl: Fix compile warning for cxl_security_ops extern
  cxl/mbox: Add Clear Log mailbox command
  ...
parents c405aa3e d99f1384
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -5407,7 +5407,7 @@ M: Dan Williams <dan.j.williams@intel.com>
L:	linux-cxl@vger.kernel.org
S:	Maintained
F:	drivers/cxl/
F:	include/linux/cxl-einj.h
F:	include/linux/einj-cxl.h
F:	include/linux/cxl-event.h
F:	include/uapi/linux/cxl_mem.h
F:	tools/testing/cxl/
+84 −0
Original line number Diff line number Diff line
@@ -26,6 +26,8 @@
#include <linux/interrupt.h>
#include <linux/timer.h>
#include <linux/cper.h>
#include <linux/cleanup.h>
#include <linux/cxl-event.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
#include <linux/ratelimit.h>
@@ -33,6 +35,7 @@
#include <linux/irq_work.h>
#include <linux/llist.h>
#include <linux/genalloc.h>
#include <linux/kfifo.h>
#include <linux/pci.h>
#include <linux/pfn.h>
#include <linux/aer.h>
@@ -673,6 +676,75 @@ static void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata,
	schedule_work(&entry->work);
}

/* Room for 8 entries for each of the 4 event log queues */
#define CXL_CPER_FIFO_DEPTH 32
DEFINE_KFIFO(cxl_cper_fifo, struct cxl_cper_work_data, CXL_CPER_FIFO_DEPTH);

/* Synchronize schedule_work() with cxl_cper_work changes */
static DEFINE_SPINLOCK(cxl_cper_work_lock);
struct work_struct *cxl_cper_work;

static void cxl_cper_post_event(enum cxl_event_type event_type,
				struct cxl_cper_event_rec *rec)
{
	struct cxl_cper_work_data wd;

	if (rec->hdr.length <= sizeof(rec->hdr) ||
	    rec->hdr.length > sizeof(*rec)) {
		pr_err(FW_WARN "CXL CPER Invalid section length (%u)\n",
		       rec->hdr.length);
		return;
	}

	if (!(rec->hdr.validation_bits & CPER_CXL_COMP_EVENT_LOG_VALID)) {
		pr_err(FW_WARN "CXL CPER invalid event\n");
		return;
	}

	guard(spinlock_irqsave)(&cxl_cper_work_lock);

	if (!cxl_cper_work)
		return;

	wd.event_type = event_type;
	memcpy(&wd.rec, rec, sizeof(wd.rec));

	if (!kfifo_put(&cxl_cper_fifo, wd)) {
		pr_err_ratelimited("CXL CPER kfifo overflow\n");
		return;
	}

	schedule_work(cxl_cper_work);
}

int cxl_cper_register_work(struct work_struct *work)
{
	if (cxl_cper_work)
		return -EINVAL;

	guard(spinlock)(&cxl_cper_work_lock);
	cxl_cper_work = work;
	return 0;
}
EXPORT_SYMBOL_NS_GPL(cxl_cper_register_work, CXL);

int cxl_cper_unregister_work(struct work_struct *work)
{
	if (cxl_cper_work != work)
		return -EINVAL;

	guard(spinlock)(&cxl_cper_work_lock);
	cxl_cper_work = NULL;
	return 0;
}
EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_work, CXL);

int cxl_cper_kfifo_get(struct cxl_cper_work_data *wd)
{
	return kfifo_get(&cxl_cper_fifo, wd);
}
EXPORT_SYMBOL_NS_GPL(cxl_cper_kfifo_get, CXL);

static bool ghes_do_proc(struct ghes *ghes,
			 const struct acpi_hest_generic_status *estatus)
{
@@ -707,6 +779,18 @@ static bool ghes_do_proc(struct ghes *ghes,
		}
		else if (guid_equal(sec_type, &CPER_SEC_PROC_ARM)) {
			queued = ghes_handle_arm_hw_error(gdata, sev, sync);
		} else if (guid_equal(sec_type, &CPER_SEC_CXL_GEN_MEDIA_GUID)) {
			struct cxl_cper_event_rec *rec = acpi_hest_get_payload(gdata);

			cxl_cper_post_event(CXL_CPER_EVENT_GEN_MEDIA, rec);
		} else if (guid_equal(sec_type, &CPER_SEC_CXL_DRAM_GUID)) {
			struct cxl_cper_event_rec *rec = acpi_hest_get_payload(gdata);

			cxl_cper_post_event(CXL_CPER_EVENT_DRAM, rec);
		} else if (guid_equal(sec_type, &CPER_SEC_CXL_MEM_MODULE_GUID)) {
			struct cxl_cper_event_rec *rec = acpi_hest_get_payload(gdata);

			cxl_cper_post_event(CXL_CPER_EVENT_MEM_MODULE, rec);
		} else {
			void *err = acpi_hest_get_payload(gdata);

+1 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ menuconfig CXL_BUS
	select FW_UPLOAD
	select PCI_DOE
	select FIRMWARE_TABLE
	select NUMA_KEEP_MEMINFO if (NUMA && X86)
	help
	  CXL is a bus that is electrically compatible with PCI Express, but
	  layers three protocols on that signalling (CXL.io, CXL.cache, and
+51 −42
Original line number Diff line number Diff line
@@ -316,28 +316,59 @@ static const struct cxl_root_ops acpi_root_ops = {
	.qos_class = cxl_acpi_qos_class,
};

static void del_cxl_resource(struct resource *res)
{
	if (!res)
		return;
	kfree(res->name);
	kfree(res);
}

static struct resource *alloc_cxl_resource(resource_size_t base,
					   resource_size_t n, int id)
{
	struct resource *res __free(kfree) = kzalloc(sizeof(*res), GFP_KERNEL);

	if (!res)
		return NULL;

	res->start = base;
	res->end = base + n - 1;
	res->flags = IORESOURCE_MEM;
	res->name = kasprintf(GFP_KERNEL, "CXL Window %d", id);
	if (!res->name)
		return NULL;

	return no_free_ptr(res);
}

static int add_or_reset_cxl_resource(struct resource *parent, struct resource *res)
{
	int rc = insert_resource(parent, res);

	if (rc)
		del_cxl_resource(res);
	return rc;
}

DEFINE_FREE(put_cxlrd, struct cxl_root_decoder *,
	    if (!IS_ERR_OR_NULL(_T)) put_device(&_T->cxlsd.cxld.dev))
DEFINE_FREE(del_cxl_resource, struct resource *, if (_T) del_cxl_resource(_T))
static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws,
			     struct cxl_cfmws_context *ctx)
{
	int target_map[CXL_DECODER_MAX_INTERLEAVE];
	struct cxl_port *root_port = ctx->root_port;
	struct resource *cxl_res = ctx->cxl_res;
	struct cxl_cxims_context cxims_ctx;
	struct cxl_root_decoder *cxlrd;
	struct device *dev = ctx->dev;
	cxl_calc_hb_fn cxl_calc_hb;
	struct cxl_decoder *cxld;
	unsigned int ways, i, ig;
	struct resource *res;
	int rc;

	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);
	if (rc)
		return rc;
	}

	rc = eiw_to_ways(cfmws->interleave_ways, &ways);
	if (rc)
@@ -348,29 +379,23 @@ static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws,
	for (i = 0; i < ways; i++)
		target_map[i] = cfmws->interleave_targets[i];

	res = kzalloc(sizeof(*res), GFP_KERNEL);
	struct resource *res __free(del_cxl_resource) = alloc_cxl_resource(
		cfmws->base_hpa, cfmws->window_size, ctx->id++);
	if (!res)
		return -ENOMEM;

	res->name = kasprintf(GFP_KERNEL, "CXL Window %d", ctx->id++);
	if (!res->name)
		goto err_name;

	res->start = cfmws->base_hpa;
	res->end = cfmws->base_hpa + cfmws->window_size - 1;
	res->flags = IORESOURCE_MEM;

	/* add to the local resource tracking to establish a sort order */
	rc = insert_resource(cxl_res, res);
	rc = add_or_reset_cxl_resource(ctx->cxl_res, no_free_ptr(res));
	if (rc)
		goto err_insert;
		return rc;

	if (cfmws->interleave_arithmetic == ACPI_CEDT_CFMWS_ARITHMETIC_MODULO)
		cxl_calc_hb = cxl_hb_modulo;
	else
		cxl_calc_hb = cxl_hb_xor;

	cxlrd = cxl_root_decoder_alloc(root_port, ways, cxl_calc_hb);
	struct cxl_root_decoder *cxlrd __free(put_cxlrd) =
		cxl_root_decoder_alloc(root_port, ways, cxl_calc_hb);
	if (IS_ERR(cxlrd))
		return PTR_ERR(cxlrd);

@@ -378,8 +403,8 @@ static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws,
	cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
	cxld->target_type = CXL_DECODER_HOSTONLYMEM;
	cxld->hpa_range = (struct range) {
		.start = res->start,
		.end = res->end,
		.start = cfmws->base_hpa,
		.end = cfmws->base_hpa + cfmws->window_size - 1,
	};
	cxld->interleave_ways = ways;
	/*
@@ -399,11 +424,10 @@ static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws,
			rc = acpi_table_parse_cedt(ACPI_CEDT_TYPE_CXIMS,
						   cxl_parse_cxims, &cxims_ctx);
			if (rc < 0)
				goto err_xormap;
				return rc;
			if (!cxlrd->platform_data) {
				dev_err(dev, "No CXIMS for HBIG %u\n", ig);
				rc = -EINVAL;
				goto err_xormap;
				return -EINVAL;
			}
		}
	}
@@ -411,18 +435,9 @@ static int __cxl_parse_cfmws(struct acpi_cedt_cfmws *cfmws,
	cxlrd->qos_class = cfmws->qtg_id;

	rc = cxl_decoder_add(cxld, target_map);
err_xormap:
	if (rc)
		put_device(&cxld->dev);
	else
		rc = cxl_decoder_autoremove(dev, cxld);
		return rc;

err_insert:
	kfree(res->name);
err_name:
	kfree(res);
	return -ENOMEM;
	return cxl_root_decoder_autoremove(dev, no_free_ptr(cxlrd));
}

static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
@@ -683,12 +698,6 @@ static void cxl_acpi_lock_reset_class(void *dev)
	device_lock_reset_class(dev);
}

static void del_cxl_resource(struct resource *res)
{
	kfree(res->name);
	kfree(res);
}

static void cxl_set_public_resource(struct resource *priv, struct resource *pub)
{
	priv->desc = (unsigned long) pub;
+14 −0
Original line number Diff line number Diff line
@@ -27,7 +27,21 @@ void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled);
int cxl_region_init(void);
void cxl_region_exit(void);
int cxl_get_poison_by_endpoint(struct cxl_port *port);
struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa);
u64 cxl_trace_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd,
		  u64 dpa);

#else
static inline u64
cxl_trace_hpa(struct cxl_region *cxlr, const struct cxl_memdev *cxlmd, u64 dpa)
{
	return ULLONG_MAX;
}
static inline
struct cxl_region *cxl_dpa_to_region(const struct cxl_memdev *cxlmd, u64 dpa)
{
	return NULL;
}
static inline int cxl_get_poison_by_endpoint(struct cxl_port *port)
{
	return 0;
Loading