Commit 7acc1372 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull compute express link updates from Dan Williams:
 "DOE support is promoted from drivers/cxl/ to drivers/pci/ with Bjorn's
  blessing, and the CXL core continues to mature its media management
  capabilities with support for listing and injecting media errors. Some
  late fixes that missed v6.3-final are also included:

   - Refactor the DOE infrastructure (Data Object Exchange
     PCI-config-cycle mailbox) to be a facility of the PCI core rather
     than the CXL core.

     This is foundational for upcoming support for PCI
     device-attestation and PCIe / CXL link encryption.

   - Add support for retrieving and injecting poison for CXL memory
     expanders.

     This enabling uses trace-events to convey CXL media error records
     to user tooling. It includes translation of device-local addresses
     (DPA) to system physical addresses (SPA) and their corresponding
     CXL region.

   - Fixes for decoder enumeration that missed v6.3-final

   - Miscellaneous fixups"

* tag 'cxl-for-6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl: (38 commits)
  cxl/test: Add mock test for set_timestamp
  cxl/mbox: Update CMD_RC_TABLE
  tools/testing/cxl: Require CONFIG_DEBUG_FS
  tools/testing/cxl: Add a sysfs attr to test poison inject limits
  tools/testing/cxl: Use injected poison for get poison list
  tools/testing/cxl: Mock the Clear Poison mailbox command
  tools/testing/cxl: Mock the Inject Poison mailbox command
  cxl/mem: Add debugfs attributes for poison inject and clear
  cxl/memdev: Trace inject and clear poison as cxl_poison events
  cxl/memdev: Warn of poison inject or clear to a mapped region
  cxl/memdev: Add support for the Clear Poison mailbox command
  cxl/memdev: Add support for the Inject Poison mailbox command
  tools/testing/cxl: Mock support for Get Poison List
  cxl/trace: Add an HPA to cxl_poison trace events
  cxl/region: Provide region info to the cxl_poison trace event
  cxl/memdev: Add trigger_poison_list sysfs attribute
  cxl/trace: Add TRACE support for CXL media-error records
  cxl/mbox: Add GET_POISON_LIST mailbox command
  cxl/mbox: Initialize the poison state
  cxl/mbox: Restrict poison cmds to debugfs cxl_raw_allow_all
  ...
parents 10de638d fd35fdcb
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -521,7 +521,6 @@ ForEachMacros:
  - 'of_property_for_each_u32'
  - 'pci_bus_for_each_resource'
  - 'pci_dev_for_each_resource'
  - 'pci_doe_for_each_off'
  - 'pcl_for_each_chunk'
  - 'pcl_for_each_segment'
  - 'pcm_for_each_format'
+35 −0
Original line number Diff line number Diff line
What:		/sys/kernel/debug/cxl/memX/inject_poison
Date:		April, 2023
KernelVersion:	v6.4
Contact:	linux-cxl@vger.kernel.org
Description:
		(WO) When a Device Physical Address (DPA) is written to this
		attribute, the memdev driver sends an inject poison command to
		the device for the specified address. The DPA must be 64-byte
		aligned and the length of the injected poison is 64-bytes. If
		successful, the device returns poison when the address is
		accessed through the CXL.mem bus. Injecting poison adds the
		address to the device's Poison List and the error source is set
		to Injected. In addition, the device adds a poison creation
		event to its internal Informational Event log, updates the
		Event Status register, and if configured, interrupts the host.
		It is not an error to inject poison into an address that
		already has poison present and no error is returned. The
		inject_poison attribute is only visible for devices supporting
		the capability.


What:		/sys/kernel/debug/memX/clear_poison
Date:		April, 2023
KernelVersion:	v6.4
Contact:	linux-cxl@vger.kernel.org
Description:
		(WO) When a Device Physical Address (DPA) is written to this
		attribute, the memdev driver sends a clear poison command to
		the device for the specified address. Clearing poison removes
		the address from the device's Poison List and writes 0 (zero)
		for 64 bytes starting at address. It is not an error to clear
		poison from an address that does not have poison set. If the
		device cannot clear poison from the address, -ENXIO is returned.
		The clear_poison attribute is only visible for devices
		supporting the capability.
+14 −0
Original line number Diff line number Diff line
@@ -415,3 +415,17 @@ Description:
		1), and checks that the hardware accepts the commit request.
		Reading this value indicates whether the region is committed or
		not.


What:		/sys/bus/cxl/devices/memX/trigger_poison_list
Date:		April, 2023
KernelVersion:	v6.4
Contact:	linux-cxl@vger.kernel.org
Description:
		(WO) When a boolean 'true' is written to this attribute the
		memdev driver retrieves the poison list from the device. The
		list consists of addresses that are poisoned, or would result
		in poison if accessed, and the source of the poison. This
		attribute is only visible for devices supporting the
		capability. The retrieved errors are logged as kernel
		events when cxl_poison event tracing is enabled.
+11 −0
Original line number Diff line number Diff line
@@ -25,7 +25,12 @@ void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled);
#define CXL_DAX_REGION_TYPE(x) (&cxl_dax_region_type)
int cxl_region_init(void);
void cxl_region_exit(void);
int cxl_get_poison_by_endpoint(struct cxl_port *port);
#else
static inline int cxl_get_poison_by_endpoint(struct cxl_port *port)
{
	return 0;
}
static inline void cxl_decoder_kill_region(struct cxl_endpoint_decoder *cxled)
{
}
@@ -64,4 +69,10 @@ int cxl_memdev_init(void);
void cxl_memdev_exit(void);
void cxl_mbox_init(void);

enum cxl_poison_trace_type {
	CXL_POISON_TRACE_LIST,
	CXL_POISON_TRACE_INJECT,
	CXL_POISON_TRACE_CLEAR,
};

#endif /* __CXL_CORE_H__ */
+40 −12
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0-only
/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
#include <linux/io-64-nonatomic-hi-lo.h>
#include <linux/seq_file.h>
#include <linux/device.h>
#include <linux/delay.h>
@@ -93,8 +92,9 @@ static int map_hdm_decoder_regs(struct cxl_port *port, void __iomem *crb,

	cxl_probe_component_regs(&port->dev, crb, &map.component_map);
	if (!map.component_map.hdm_decoder.valid) {
		dev_err(&port->dev, "HDM decoder registers invalid\n");
		return -ENXIO;
		dev_dbg(&port->dev, "HDM decoder registers not implemented\n");
		/* unique error code to indicate no HDM decoder capability */
		return -ENODEV;
	}

	return cxl_map_component_regs(&port->dev, regs, &map,
@@ -130,6 +130,14 @@ static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
	 */
	for (i = 0; i < cxlhdm->decoder_count; i++) {
		ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(i));
		dev_dbg(&info->port->dev,
			"decoder%d.%d: committed: %ld base: %#x_%.8x size: %#x_%.8x\n",
			info->port->id, i,
			FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMITTED, ctrl),
			readl(hdm + CXL_HDM_DECODER0_BASE_HIGH_OFFSET(i)),
			readl(hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(i)),
			readl(hdm + CXL_HDM_DECODER0_SIZE_HIGH_OFFSET(i)),
			readl(hdm + CXL_HDM_DECODER0_SIZE_LOW_OFFSET(i)));
		if (FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMITTED, ctrl))
			return false;
	}
@@ -269,8 +277,11 @@ static int __cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,

	lockdep_assert_held_write(&cxl_dpa_rwsem);

	if (!len)
		goto success;
	if (!len) {
		dev_warn(dev, "decoder%d.%d: empty reservation attempted\n",
			 port->id, cxled->cxld.id);
		return -EINVAL;
	}

	if (cxled->dpa_res) {
		dev_dbg(dev, "decoder%d.%d: existing allocation %pr assigned\n",
@@ -323,7 +334,6 @@ static int __cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
		cxled->mode = CXL_DECODER_MIXED;
	}

success:
	port->hdm_end++;
	get_device(&cxled->cxld.dev);
	return 0;
@@ -783,8 +793,8 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
			    int *target_map, void __iomem *hdm, int which,
			    u64 *dpa_base, struct cxl_endpoint_dvsec_info *info)
{
	u64 size, base, skip, dpa_size, lo, hi;
	struct cxl_endpoint_decoder *cxled;
	u64 size, base, skip, dpa_size;
	bool committed;
	u32 remainder;
	int i, rc;
@@ -799,8 +809,12 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
							which, info);

	ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(which));
	base = ioread64_hi_lo(hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(which));
	size = ioread64_hi_lo(hdm + CXL_HDM_DECODER0_SIZE_LOW_OFFSET(which));
	lo = readl(hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(which));
	hi = readl(hdm + CXL_HDM_DECODER0_BASE_HIGH_OFFSET(which));
	base = (hi << 32) + lo;
	lo = readl(hdm + CXL_HDM_DECODER0_SIZE_LOW_OFFSET(which));
	hi = readl(hdm + CXL_HDM_DECODER0_SIZE_HIGH_OFFSET(which));
	size = (hi << 32) + lo;
	committed = !!(ctrl & CXL_HDM_DECODER0_CTRL_COMMITTED);
	cxld->commit = cxl_decoder_commit;
	cxld->reset = cxl_decoder_reset;
@@ -833,6 +847,13 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
				 port->id, cxld->id);
			return -ENXIO;
		}

		if (size == 0) {
			dev_warn(&port->dev,
				 "decoder%d.%d: Committed with zero size\n",
				 port->id, cxld->id);
			return -ENXIO;
		}
		port->commit_end = cxld->id;
	} else {
		/* unless / until type-2 drivers arrive, assume type-3 */
@@ -855,9 +876,14 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
	if (rc)
		return rc;

	dev_dbg(&port->dev, "decoder%d.%d: range: %#llx-%#llx iw: %d ig: %d\n",
		port->id, cxld->id, cxld->hpa_range.start, cxld->hpa_range.end,
		cxld->interleave_ways, cxld->interleave_granularity);

	if (!info) {
		target_list.value =
			ioread64_hi_lo(hdm + CXL_HDM_DECODER0_TL_LOW(which));
		lo = readl(hdm + CXL_HDM_DECODER0_TL_LOW(which));
		hi = readl(hdm + CXL_HDM_DECODER0_TL_HIGH(which));
		target_list.value = (hi << 32) + lo;
		for (i = 0; i < cxld->interleave_ways; i++)
			target_map[i] = target_list.target_id[i];

@@ -874,7 +900,9 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
			port->id, cxld->id, size, cxld->interleave_ways);
		return -ENXIO;
	}
	skip = ioread64_hi_lo(hdm + CXL_HDM_DECODER0_SKIP_LOW(which));
	lo = readl(hdm + CXL_HDM_DECODER0_SKIP_LOW(which));
	hi = readl(hdm + CXL_HDM_DECODER0_SKIP_HIGH(which));
	skip = (hi << 32) + lo;
	cxled = to_cxl_endpoint_decoder(&cxld->dev);
	rc = devm_cxl_dpa_reserve(cxled, *dpa_base + skip, dpa_size, skip);
	if (rc) {
Loading