mirror of
https://github.com/Dasharo/linux.git
synced 2026-03-06 15:25:10 -08:00
Merge tag 'cxl-for-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl
Pull CXL (Compute Express Link) updates from Dan Williams: "The bulk of this update is support for enumerating the performance capabilities of CXL memory targets and connecting that to a platform CXL memory QoS class. Some follow-on work remains to hook up this data into core-mm policy, but that is saved for v6.9. The next significant update is unifying how CXL event records (things like background scrub errors) are processed between so called "firmware first" and native error record retrieval. The CXL driver handler that processes the record retrieved from the device mailbox is now the handler for that same record format coming from an EFI/ACPI notification source. This also contains miscellaneous feature updates, like Get Timestamp, and other fixups. Summary: - Add support for parsing the Coherent Device Attribute Table (CDAT) - Add support for calculating a platform CXL QoS class from CDAT data - Unify the tracing of EFI CXL Events with native CXL Events. - Add Get Timestamp support - Miscellaneous cleanups and fixups" * tag 'cxl-for-6.8' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl: (41 commits) cxl/core: use sysfs_emit() for attr's _show() cxl/pci: Register for and process CPER events PCI: Introduce cleanup helpers for device reference counts and locks acpi/ghes: Process CXL Component Events cxl/events: Create a CXL event union cxl/events: Separate UUID from event structures cxl/events: Remove passing a UUID to known event traces cxl/events: Create common event UUID defines cxl/events: Promote CXL event structures to a core header cxl: Refactor to use __free() for cxl_root allocation in cxl_endpoint_port_probe() cxl: Refactor to use __free() for cxl_root allocation in cxl_find_nvdimm_bridge() cxl: Fix device reference leak in cxl_port_perf_data_calculate() cxl: Convert find_cxl_root() to return a 'struct cxl_root *' cxl: Introduce put_cxl_root() helper cxl/port: Fix missing target list lock cxl/port: Fix decoder initialization when nr_targets > interleave_ways cxl/region: fix x9 interleave typo cxl/trace: Pass UUID explicitly to event traces cxl/region: use %pap format to print resource_size_t cxl/region: Add dev_dbg() detail on failure to allocate HPA space ...
This commit is contained in:
@@ -28,6 +28,23 @@ Description:
|
||||
Payload in the CXL-2.0 specification.
|
||||
|
||||
|
||||
What: /sys/bus/cxl/devices/memX/ram/qos_class
|
||||
Date: May, 2023
|
||||
KernelVersion: v6.8
|
||||
Contact: linux-cxl@vger.kernel.org
|
||||
Description:
|
||||
(RO) For CXL host platforms that support "QoS Telemmetry"
|
||||
this attribute conveys a comma delimited list of platform
|
||||
specific cookies that identifies a QoS performance class
|
||||
for the volatile partition of the CXL mem device. These
|
||||
class-ids can be compared against a similar "qos_class"
|
||||
published for a root decoder. While it is not required
|
||||
that the endpoints map their local memory-class to a
|
||||
matching platform class, mismatches are not recommended
|
||||
and there are platform specific performance related
|
||||
side-effects that may result. First class-id is displayed.
|
||||
|
||||
|
||||
What: /sys/bus/cxl/devices/memX/pmem/size
|
||||
Date: December, 2020
|
||||
KernelVersion: v5.12
|
||||
@@ -38,6 +55,23 @@ Description:
|
||||
Payload in the CXL-2.0 specification.
|
||||
|
||||
|
||||
What: /sys/bus/cxl/devices/memX/pmem/qos_class
|
||||
Date: May, 2023
|
||||
KernelVersion: v6.8
|
||||
Contact: linux-cxl@vger.kernel.org
|
||||
Description:
|
||||
(RO) For CXL host platforms that support "QoS Telemmetry"
|
||||
this attribute conveys a comma delimited list of platform
|
||||
specific cookies that identifies a QoS performance class
|
||||
for the persistent partition of the CXL mem device. These
|
||||
class-ids can be compared against a similar "qos_class"
|
||||
published for a root decoder. While it is not required
|
||||
that the endpoints map their local memory-class to a
|
||||
matching platform class, mismatches are not recommended
|
||||
and there are platform specific performance related
|
||||
side-effects that may result. First class-id is displayed.
|
||||
|
||||
|
||||
What: /sys/bus/cxl/devices/memX/serial
|
||||
Date: January, 2022
|
||||
KernelVersion: v5.18
|
||||
|
||||
@@ -5280,6 +5280,7 @@ M: Dan Williams <dan.j.williams@intel.com>
|
||||
L: linux-cxl@vger.kernel.org
|
||||
S: Maintained
|
||||
F: drivers/cxl/
|
||||
F: include/linux/cxl-event.h
|
||||
F: include/uapi/linux/cxl_mem.h
|
||||
F: tools/testing/cxl/
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/cper.h>
|
||||
#include <linux/cxl-event.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/ratelimit.h>
|
||||
@@ -673,6 +674,78 @@ static void ghes_defer_non_standard_event(struct acpi_hest_generic_data *gdata,
|
||||
schedule_work(&entry->work);
|
||||
}
|
||||
|
||||
/*
|
||||
* Only a single callback can be registered for CXL CPER events.
|
||||
*/
|
||||
static DECLARE_RWSEM(cxl_cper_rw_sem);
|
||||
static cxl_cper_callback cper_callback;
|
||||
|
||||
/* CXL Event record UUIDs are formatted as GUIDs and reported in section type */
|
||||
|
||||
/*
|
||||
* General Media Event Record
|
||||
* CXL rev 3.0 Section 8.2.9.2.1.1; Table 8-43
|
||||
*/
|
||||
#define CPER_SEC_CXL_GEN_MEDIA_GUID \
|
||||
GUID_INIT(0xfbcd0a77, 0xc260, 0x417f, \
|
||||
0x85, 0xa9, 0x08, 0x8b, 0x16, 0x21, 0xeb, 0xa6)
|
||||
|
||||
/*
|
||||
* DRAM Event Record
|
||||
* CXL rev 3.0 section 8.2.9.2.1.2; Table 8-44
|
||||
*/
|
||||
#define CPER_SEC_CXL_DRAM_GUID \
|
||||
GUID_INIT(0x601dcbb3, 0x9c06, 0x4eab, \
|
||||
0xb8, 0xaf, 0x4e, 0x9b, 0xfb, 0x5c, 0x96, 0x24)
|
||||
|
||||
/*
|
||||
* Memory Module Event Record
|
||||
* CXL rev 3.0 section 8.2.9.2.1.3; Table 8-45
|
||||
*/
|
||||
#define CPER_SEC_CXL_MEM_MODULE_GUID \
|
||||
GUID_INIT(0xfe927475, 0xdd59, 0x4339, \
|
||||
0xa5, 0x86, 0x79, 0xba, 0xb1, 0x13, 0xb7, 0x74)
|
||||
|
||||
static void cxl_cper_post_event(enum cxl_event_type event_type,
|
||||
struct cxl_cper_event_rec *rec)
|
||||
{
|
||||
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(rwsem_read)(&cxl_cper_rw_sem);
|
||||
if (cper_callback)
|
||||
cper_callback(event_type, rec);
|
||||
}
|
||||
|
||||
int cxl_cper_register_callback(cxl_cper_callback callback)
|
||||
{
|
||||
guard(rwsem_write)(&cxl_cper_rw_sem);
|
||||
if (cper_callback)
|
||||
return -EINVAL;
|
||||
cper_callback = callback;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_cper_register_callback, CXL);
|
||||
|
||||
int cxl_cper_unregister_callback(cxl_cper_callback callback)
|
||||
{
|
||||
guard(rwsem_write)(&cxl_cper_rw_sem);
|
||||
if (callback != cper_callback)
|
||||
return -EINVAL;
|
||||
cper_callback = NULL;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_cper_unregister_callback, CXL);
|
||||
|
||||
static bool ghes_do_proc(struct ghes *ghes,
|
||||
const struct acpi_hest_generic_status *estatus)
|
||||
{
|
||||
@@ -707,6 +780,22 @@ 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);
|
||||
|
||||
|
||||
@@ -58,14 +58,22 @@ struct target_cache {
|
||||
struct node_cache_attrs cache_attrs;
|
||||
};
|
||||
|
||||
enum {
|
||||
NODE_ACCESS_CLASS_0 = 0,
|
||||
NODE_ACCESS_CLASS_1,
|
||||
NODE_ACCESS_CLASS_GENPORT_SINK,
|
||||
NODE_ACCESS_CLASS_MAX,
|
||||
};
|
||||
|
||||
struct memory_target {
|
||||
struct list_head node;
|
||||
unsigned int memory_pxm;
|
||||
unsigned int processor_pxm;
|
||||
struct resource memregions;
|
||||
struct node_hmem_attrs hmem_attrs[2];
|
||||
struct access_coordinate coord[NODE_ACCESS_CLASS_MAX];
|
||||
struct list_head caches;
|
||||
struct node_cache_attrs cache_attrs;
|
||||
u8 gen_port_device_handle[ACPI_SRAT_DEVICE_HANDLE_SIZE];
|
||||
bool registered;
|
||||
};
|
||||
|
||||
@@ -100,6 +108,47 @@ static struct memory_target *find_mem_target(unsigned int mem_pxm)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct memory_target *acpi_find_genport_target(u32 uid)
|
||||
{
|
||||
struct memory_target *target;
|
||||
u32 target_uid;
|
||||
u8 *uid_ptr;
|
||||
|
||||
list_for_each_entry(target, &targets, node) {
|
||||
uid_ptr = target->gen_port_device_handle + 8;
|
||||
target_uid = *(u32 *)uid_ptr;
|
||||
if (uid == target_uid)
|
||||
return target;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* acpi_get_genport_coordinates - Retrieve the access coordinates for a generic port
|
||||
* @uid: ACPI unique id
|
||||
* @coord: The access coordinates written back out for the generic port
|
||||
*
|
||||
* Return: 0 on success. Errno on failure.
|
||||
*
|
||||
* Only supports device handles that are ACPI. Assume ACPI0016 HID for CXL.
|
||||
*/
|
||||
int acpi_get_genport_coordinates(u32 uid,
|
||||
struct access_coordinate *coord)
|
||||
{
|
||||
struct memory_target *target;
|
||||
|
||||
guard(mutex)(&target_lock);
|
||||
target = acpi_find_genport_target(uid);
|
||||
if (!target)
|
||||
return -ENOENT;
|
||||
|
||||
*coord = target->coord[NODE_ACCESS_CLASS_GENPORT_SINK];
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(acpi_get_genport_coordinates, CXL);
|
||||
|
||||
static __init void alloc_memory_initiator(unsigned int cpu_pxm)
|
||||
{
|
||||
struct memory_initiator *initiator;
|
||||
@@ -120,8 +169,7 @@ static __init void alloc_memory_initiator(unsigned int cpu_pxm)
|
||||
list_add_tail(&initiator->node, &initiators);
|
||||
}
|
||||
|
||||
static __init void alloc_memory_target(unsigned int mem_pxm,
|
||||
resource_size_t start, resource_size_t len)
|
||||
static __init struct memory_target *alloc_target(unsigned int mem_pxm)
|
||||
{
|
||||
struct memory_target *target;
|
||||
|
||||
@@ -129,7 +177,7 @@ static __init void alloc_memory_target(unsigned int mem_pxm,
|
||||
if (!target) {
|
||||
target = kzalloc(sizeof(*target), GFP_KERNEL);
|
||||
if (!target)
|
||||
return;
|
||||
return NULL;
|
||||
target->memory_pxm = mem_pxm;
|
||||
target->processor_pxm = PXM_INVAL;
|
||||
target->memregions = (struct resource) {
|
||||
@@ -142,6 +190,19 @@ static __init void alloc_memory_target(unsigned int mem_pxm,
|
||||
INIT_LIST_HEAD(&target->caches);
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
static __init void alloc_memory_target(unsigned int mem_pxm,
|
||||
resource_size_t start,
|
||||
resource_size_t len)
|
||||
{
|
||||
struct memory_target *target;
|
||||
|
||||
target = alloc_target(mem_pxm);
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
/*
|
||||
* There are potentially multiple ranges per PXM, so record each
|
||||
* in the per-target memregions resource tree.
|
||||
@@ -152,6 +213,18 @@ static __init void alloc_memory_target(unsigned int mem_pxm,
|
||||
start, start + len, mem_pxm);
|
||||
}
|
||||
|
||||
static __init void alloc_genport_target(unsigned int mem_pxm, u8 *handle)
|
||||
{
|
||||
struct memory_target *target;
|
||||
|
||||
target = alloc_target(mem_pxm);
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
memcpy(target->gen_port_device_handle, handle,
|
||||
ACPI_SRAT_DEVICE_HANDLE_SIZE);
|
||||
}
|
||||
|
||||
static __init const char *hmat_data_type(u8 type)
|
||||
{
|
||||
switch (type) {
|
||||
@@ -228,24 +301,24 @@ static void hmat_update_target_access(struct memory_target *target,
|
||||
{
|
||||
switch (type) {
|
||||
case ACPI_HMAT_ACCESS_LATENCY:
|
||||
target->hmem_attrs[access].read_latency = value;
|
||||
target->hmem_attrs[access].write_latency = value;
|
||||
target->coord[access].read_latency = value;
|
||||
target->coord[access].write_latency = value;
|
||||
break;
|
||||
case ACPI_HMAT_READ_LATENCY:
|
||||
target->hmem_attrs[access].read_latency = value;
|
||||
target->coord[access].read_latency = value;
|
||||
break;
|
||||
case ACPI_HMAT_WRITE_LATENCY:
|
||||
target->hmem_attrs[access].write_latency = value;
|
||||
target->coord[access].write_latency = value;
|
||||
break;
|
||||
case ACPI_HMAT_ACCESS_BANDWIDTH:
|
||||
target->hmem_attrs[access].read_bandwidth = value;
|
||||
target->hmem_attrs[access].write_bandwidth = value;
|
||||
target->coord[access].read_bandwidth = value;
|
||||
target->coord[access].write_bandwidth = value;
|
||||
break;
|
||||
case ACPI_HMAT_READ_BANDWIDTH:
|
||||
target->hmem_attrs[access].read_bandwidth = value;
|
||||
target->coord[access].read_bandwidth = value;
|
||||
break;
|
||||
case ACPI_HMAT_WRITE_BANDWIDTH:
|
||||
target->hmem_attrs[access].write_bandwidth = value;
|
||||
target->coord[access].write_bandwidth = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@@ -291,11 +364,28 @@ static __init void hmat_add_locality(struct acpi_hmat_locality *hmat_loc)
|
||||
}
|
||||
}
|
||||
|
||||
static __init void hmat_update_target(unsigned int tgt_pxm, unsigned int init_pxm,
|
||||
u8 mem_hier, u8 type, u32 value)
|
||||
{
|
||||
struct memory_target *target = find_mem_target(tgt_pxm);
|
||||
|
||||
if (mem_hier != ACPI_HMAT_MEMORY)
|
||||
return;
|
||||
|
||||
if (target && target->processor_pxm == init_pxm) {
|
||||
hmat_update_target_access(target, type, value,
|
||||
NODE_ACCESS_CLASS_0);
|
||||
/* If the node has a CPU, update access 1 */
|
||||
if (node_state(pxm_to_node(init_pxm), N_CPU))
|
||||
hmat_update_target_access(target, type, value,
|
||||
NODE_ACCESS_CLASS_1);
|
||||
}
|
||||
}
|
||||
|
||||
static __init int hmat_parse_locality(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_hmat_locality *hmat_loc = (void *)header;
|
||||
struct memory_target *target;
|
||||
unsigned int init, targ, total_size, ipds, tpds;
|
||||
u32 *inits, *targs, value;
|
||||
u16 *entries;
|
||||
@@ -336,15 +426,8 @@ static __init int hmat_parse_locality(union acpi_subtable_headers *header,
|
||||
inits[init], targs[targ], value,
|
||||
hmat_data_type_suffix(type));
|
||||
|
||||
if (mem_hier == ACPI_HMAT_MEMORY) {
|
||||
target = find_mem_target(targs[targ]);
|
||||
if (target && target->processor_pxm == inits[init]) {
|
||||
hmat_update_target_access(target, type, value, 0);
|
||||
/* If the node has a CPU, update access 1 */
|
||||
if (node_state(pxm_to_node(inits[init]), N_CPU))
|
||||
hmat_update_target_access(target, type, value, 1);
|
||||
}
|
||||
}
|
||||
hmat_update_target(targs[targ], inits[init],
|
||||
mem_hier, type, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -491,6 +574,27 @@ static __init int srat_parse_mem_affinity(union acpi_subtable_headers *header,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static __init int srat_parse_genport_affinity(union acpi_subtable_headers *header,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct acpi_srat_generic_affinity *ga = (void *)header;
|
||||
|
||||
if (!ga)
|
||||
return -EINVAL;
|
||||
|
||||
if (!(ga->flags & ACPI_SRAT_GENERIC_AFFINITY_ENABLED))
|
||||
return 0;
|
||||
|
||||
/* Skip PCI device_handle for now */
|
||||
if (ga->device_handle_type != 0)
|
||||
return 0;
|
||||
|
||||
alloc_genport_target(ga->proximity_domain,
|
||||
(u8 *)ga->device_handle);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 hmat_initiator_perf(struct memory_target *target,
|
||||
struct memory_initiator *initiator,
|
||||
struct acpi_hmat_locality *hmat_loc)
|
||||
@@ -592,6 +696,11 @@ static void hmat_update_target_attrs(struct memory_target *target,
|
||||
u32 best = 0;
|
||||
int i;
|
||||
|
||||
/* Don't update for generic port if there's no device handle */
|
||||
if (access == NODE_ACCESS_CLASS_GENPORT_SINK &&
|
||||
!(*(u16 *)target->gen_port_device_handle))
|
||||
return;
|
||||
|
||||
bitmap_zero(p_nodes, MAX_NUMNODES);
|
||||
/*
|
||||
* If the Address Range Structure provides a local processor pxm, set
|
||||
@@ -661,6 +770,14 @@ static void __hmat_register_target_initiators(struct memory_target *target,
|
||||
}
|
||||
}
|
||||
|
||||
static void hmat_register_generic_target_initiators(struct memory_target *target)
|
||||
{
|
||||
static DECLARE_BITMAP(p_nodes, MAX_NUMNODES);
|
||||
|
||||
__hmat_register_target_initiators(target, p_nodes,
|
||||
NODE_ACCESS_CLASS_GENPORT_SINK);
|
||||
}
|
||||
|
||||
static void hmat_register_target_initiators(struct memory_target *target)
|
||||
{
|
||||
static DECLARE_BITMAP(p_nodes, MAX_NUMNODES);
|
||||
@@ -681,7 +798,7 @@ static void hmat_register_target_cache(struct memory_target *target)
|
||||
static void hmat_register_target_perf(struct memory_target *target, int access)
|
||||
{
|
||||
unsigned mem_nid = pxm_to_node(target->memory_pxm);
|
||||
node_set_perf_attrs(mem_nid, &target->hmem_attrs[access], access);
|
||||
node_set_perf_attrs(mem_nid, &target->coord[access], access);
|
||||
}
|
||||
|
||||
static void hmat_register_target_devices(struct memory_target *target)
|
||||
@@ -712,6 +829,17 @@ static void hmat_register_target(struct memory_target *target)
|
||||
*/
|
||||
hmat_register_target_devices(target);
|
||||
|
||||
/*
|
||||
* Register generic port perf numbers. The nid may not be
|
||||
* initialized and is still NUMA_NO_NODE.
|
||||
*/
|
||||
mutex_lock(&target_lock);
|
||||
if (*(u16 *)target->gen_port_device_handle) {
|
||||
hmat_register_generic_target_initiators(target);
|
||||
target->registered = true;
|
||||
}
|
||||
mutex_unlock(&target_lock);
|
||||
|
||||
/*
|
||||
* Skip offline nodes. This can happen when memory
|
||||
* marked EFI_MEMORY_SP, "specific purpose", is applied
|
||||
@@ -726,8 +854,8 @@ static void hmat_register_target(struct memory_target *target)
|
||||
if (!target->registered) {
|
||||
hmat_register_target_initiators(target);
|
||||
hmat_register_target_cache(target);
|
||||
hmat_register_target_perf(target, 0);
|
||||
hmat_register_target_perf(target, 1);
|
||||
hmat_register_target_perf(target, NODE_ACCESS_CLASS_0);
|
||||
hmat_register_target_perf(target, NODE_ACCESS_CLASS_1);
|
||||
target->registered = true;
|
||||
}
|
||||
mutex_unlock(&target_lock);
|
||||
@@ -765,7 +893,7 @@ static int hmat_set_default_dram_perf(void)
|
||||
int rc;
|
||||
int nid, pxm;
|
||||
struct memory_target *target;
|
||||
struct node_hmem_attrs *attrs;
|
||||
struct access_coordinate *attrs;
|
||||
|
||||
if (!default_dram_type)
|
||||
return -EIO;
|
||||
@@ -775,7 +903,7 @@ static int hmat_set_default_dram_perf(void)
|
||||
target = find_mem_target(pxm);
|
||||
if (!target)
|
||||
continue;
|
||||
attrs = &target->hmem_attrs[1];
|
||||
attrs = &target->coord[1];
|
||||
rc = mt_set_default_dram_perf(nid, attrs, "ACPI HMAT");
|
||||
if (rc)
|
||||
return rc;
|
||||
@@ -789,7 +917,7 @@ static int hmat_calculate_adistance(struct notifier_block *self,
|
||||
{
|
||||
static DECLARE_BITMAP(p_nodes, MAX_NUMNODES);
|
||||
struct memory_target *target;
|
||||
struct node_hmem_attrs *perf;
|
||||
struct access_coordinate *perf;
|
||||
int *adist = data;
|
||||
int pxm;
|
||||
|
||||
@@ -802,7 +930,7 @@ static int hmat_calculate_adistance(struct notifier_block *self,
|
||||
hmat_update_target_attrs(target, p_nodes, 1);
|
||||
mutex_unlock(&target_lock);
|
||||
|
||||
perf = &target->hmem_attrs[1];
|
||||
perf = &target->coord[1];
|
||||
|
||||
if (mt_perf_to_adistance(perf, adist))
|
||||
return NOTIFY_OK;
|
||||
@@ -870,6 +998,13 @@ static __init int hmat_init(void)
|
||||
ACPI_SRAT_TYPE_MEMORY_AFFINITY,
|
||||
srat_parse_mem_affinity, 0) < 0)
|
||||
goto out_put;
|
||||
|
||||
if (acpi_table_parse_entries(ACPI_SIG_SRAT,
|
||||
sizeof(struct acpi_table_srat),
|
||||
ACPI_SRAT_TYPE_GENERIC_PORT_AFFINITY,
|
||||
srat_parse_genport_affinity, 0) < 0)
|
||||
goto out_put;
|
||||
|
||||
acpi_put_table(tbl);
|
||||
|
||||
status = acpi_get_table(ACPI_SIG_HMAT, 0, &tbl);
|
||||
|
||||
@@ -251,8 +251,9 @@ int __init_or_acpilib acpi_table_parse_entries_array(
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
count = acpi_parse_entries_array(id, table_size, table_header,
|
||||
proc, proc_num, max_entries);
|
||||
count = acpi_parse_entries_array(id, table_size,
|
||||
(union fw_table_header *)table_header,
|
||||
proc, proc_num, max_entries);
|
||||
|
||||
acpi_put_table(table_header);
|
||||
return count;
|
||||
|
||||
@@ -74,14 +74,14 @@ static BIN_ATTR_RO(cpulist, CPULIST_FILE_MAX_BYTES);
|
||||
* @dev: Device for this memory access class
|
||||
* @list_node: List element in the node's access list
|
||||
* @access: The access class rank
|
||||
* @hmem_attrs: Heterogeneous memory performance attributes
|
||||
* @coord: Heterogeneous memory performance coordinates
|
||||
*/
|
||||
struct node_access_nodes {
|
||||
struct device dev;
|
||||
struct list_head list_node;
|
||||
unsigned int access;
|
||||
#ifdef CONFIG_HMEM_REPORTING
|
||||
struct node_hmem_attrs hmem_attrs;
|
||||
struct access_coordinate coord;
|
||||
#endif
|
||||
};
|
||||
#define to_access_nodes(dev) container_of(dev, struct node_access_nodes, dev)
|
||||
@@ -167,7 +167,7 @@ static ssize_t property##_show(struct device *dev, \
|
||||
char *buf) \
|
||||
{ \
|
||||
return sysfs_emit(buf, "%u\n", \
|
||||
to_access_nodes(dev)->hmem_attrs.property); \
|
||||
to_access_nodes(dev)->coord.property); \
|
||||
} \
|
||||
static DEVICE_ATTR_RO(property)
|
||||
|
||||
@@ -187,10 +187,10 @@ static struct attribute *access_attrs[] = {
|
||||
/**
|
||||
* node_set_perf_attrs - Set the performance values for given access class
|
||||
* @nid: Node identifier to be set
|
||||
* @hmem_attrs: Heterogeneous memory performance attributes
|
||||
* @coord: Heterogeneous memory performance coordinates
|
||||
* @access: The access class the for the given attributes
|
||||
*/
|
||||
void node_set_perf_attrs(unsigned int nid, struct node_hmem_attrs *hmem_attrs,
|
||||
void node_set_perf_attrs(unsigned int nid, struct access_coordinate *coord,
|
||||
unsigned int access)
|
||||
{
|
||||
struct node_access_nodes *c;
|
||||
@@ -205,7 +205,7 @@ void node_set_perf_attrs(unsigned int nid, struct node_hmem_attrs *hmem_attrs,
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
c->hmem_attrs = *hmem_attrs;
|
||||
c->coord = *coord;
|
||||
for (i = 0; access_attrs[i] != NULL; i++) {
|
||||
if (sysfs_add_file_to_group(&c->dev.kobj, access_attrs[i],
|
||||
"initiators")) {
|
||||
|
||||
@@ -5,6 +5,7 @@ menuconfig CXL_BUS
|
||||
select FW_LOADER
|
||||
select FW_UPLOAD
|
||||
select PCI_DOE
|
||||
select FIRMWARE_TABLE
|
||||
help
|
||||
CXL is a bus that is electrically compatible with PCI Express, but
|
||||
layers three protocols on that signalling (CXL.io, CXL.cache, and
|
||||
@@ -54,8 +55,10 @@ config CXL_MEM_RAW_COMMANDS
|
||||
config CXL_ACPI
|
||||
tristate "CXL ACPI: Platform Support"
|
||||
depends on ACPI
|
||||
depends on ACPI_NUMA
|
||||
default CXL_BUS
|
||||
select ACPI_TABLE_LIB
|
||||
select ACPI_HMAT
|
||||
help
|
||||
Enable support for host managed device memory (HDM) resources
|
||||
published by a platform's ACPI CXL memory layout description. See
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/node.h>
|
||||
#include <asm/div64.h>
|
||||
#include "cxlpci.h"
|
||||
#include "cxl.h"
|
||||
@@ -17,6 +18,10 @@ struct cxl_cxims_data {
|
||||
u64 xormaps[] __counted_by(nr_maps);
|
||||
};
|
||||
|
||||
static const guid_t acpi_cxl_qtg_id_guid =
|
||||
GUID_INIT(0xF365F9A6, 0xA7DE, 0x4071,
|
||||
0xA6, 0x6A, 0xB4, 0x0C, 0x0B, 0x4F, 0x8E, 0x52);
|
||||
|
||||
/*
|
||||
* Find a targets entry (n) in the host bridge interleave list.
|
||||
* CXL Specification 3.0 Table 9-22
|
||||
@@ -194,6 +199,123 @@ struct cxl_cfmws_context {
|
||||
int id;
|
||||
};
|
||||
|
||||
/**
|
||||
* cxl_acpi_evaluate_qtg_dsm - Retrieve QTG ids via ACPI _DSM
|
||||
* @handle: ACPI handle
|
||||
* @coord: performance access coordinates
|
||||
* @entries: number of QTG IDs to return
|
||||
* @qos_class: int array provided by caller to return QTG IDs
|
||||
*
|
||||
* Return: number of QTG IDs returned, or -errno for errors
|
||||
*
|
||||
* Issue QTG _DSM with accompanied bandwidth and latency data in order to get
|
||||
* the QTG IDs that are suitable for the performance point in order of most
|
||||
* suitable to least suitable. Write back array of QTG IDs and return the
|
||||
* actual number of QTG IDs written back.
|
||||
*/
|
||||
static int
|
||||
cxl_acpi_evaluate_qtg_dsm(acpi_handle handle, struct access_coordinate *coord,
|
||||
int entries, int *qos_class)
|
||||
{
|
||||
union acpi_object *out_obj, *out_buf, *obj;
|
||||
union acpi_object in_array[4] = {
|
||||
[0].integer = { ACPI_TYPE_INTEGER, coord->read_latency },
|
||||
[1].integer = { ACPI_TYPE_INTEGER, coord->write_latency },
|
||||
[2].integer = { ACPI_TYPE_INTEGER, coord->read_bandwidth },
|
||||
[3].integer = { ACPI_TYPE_INTEGER, coord->write_bandwidth },
|
||||
};
|
||||
union acpi_object in_obj = {
|
||||
.package = {
|
||||
.type = ACPI_TYPE_PACKAGE,
|
||||
.count = 4,
|
||||
.elements = in_array,
|
||||
},
|
||||
};
|
||||
int count, pkg_entries, i;
|
||||
u16 max_qtg;
|
||||
int rc;
|
||||
|
||||
if (!entries)
|
||||
return -EINVAL;
|
||||
|
||||
out_obj = acpi_evaluate_dsm(handle, &acpi_cxl_qtg_id_guid, 1, 1, &in_obj);
|
||||
if (!out_obj)
|
||||
return -ENXIO;
|
||||
|
||||
if (out_obj->type != ACPI_TYPE_PACKAGE) {
|
||||
rc = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Check Max QTG ID */
|
||||
obj = &out_obj->package.elements[0];
|
||||
if (obj->type != ACPI_TYPE_INTEGER) {
|
||||
rc = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
max_qtg = obj->integer.value;
|
||||
|
||||
/* It's legal to have 0 QTG entries */
|
||||
pkg_entries = out_obj->package.count;
|
||||
if (pkg_entries <= 1) {
|
||||
rc = 0;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Retrieve QTG IDs package */
|
||||
obj = &out_obj->package.elements[1];
|
||||
if (obj->type != ACPI_TYPE_PACKAGE) {
|
||||
rc = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pkg_entries = obj->package.count;
|
||||
count = min(entries, pkg_entries);
|
||||
for (i = 0; i < count; i++) {
|
||||
u16 qtg_id;
|
||||
|
||||
out_buf = &obj->package.elements[i];
|
||||
if (out_buf->type != ACPI_TYPE_INTEGER) {
|
||||
rc = -ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
qtg_id = out_buf->integer.value;
|
||||
if (qtg_id > max_qtg)
|
||||
pr_warn("QTG ID %u greater than MAX %u\n",
|
||||
qtg_id, max_qtg);
|
||||
|
||||
qos_class[i] = qtg_id;
|
||||
}
|
||||
rc = count;
|
||||
|
||||
out:
|
||||
ACPI_FREE(out_obj);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cxl_acpi_qos_class(struct cxl_root *cxl_root,
|
||||
struct access_coordinate *coord, int entries,
|
||||
int *qos_class)
|
||||
{
|
||||
struct device *dev = cxl_root->port.uport_dev;
|
||||
acpi_handle handle;
|
||||
|
||||
if (!dev_is_platform(dev))
|
||||
return -ENODEV;
|
||||
|
||||
handle = ACPI_HANDLE(dev);
|
||||
if (!handle)
|
||||
return -ENODEV;
|
||||
|
||||
return cxl_acpi_evaluate_qtg_dsm(handle, coord, entries, qos_class);
|
||||
}
|
||||
|
||||
static const struct cxl_root_ops acpi_root_ops = {
|
||||
.qos_class = cxl_acpi_qos_class,
|
||||
};
|
||||
|
||||
static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
|
||||
const unsigned long end)
|
||||
{
|
||||
@@ -389,8 +511,29 @@ static int cxl_get_chbs(struct device *dev, struct acpi_device *hb,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_genport_coordinates(struct device *dev, struct cxl_dport *dport)
|
||||
{
|
||||
struct acpi_device *hb = to_cxl_host_bridge(NULL, dev);
|
||||
u32 uid;
|
||||
int rc;
|
||||
|
||||
if (kstrtou32(acpi_device_uid(hb), 0, &uid))
|
||||
return -EINVAL;
|
||||
|
||||
rc = acpi_get_genport_coordinates(uid, &dport->hb_coord);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
/* Adjust back to picoseconds from nanoseconds */
|
||||
dport->hb_coord.read_latency *= 1000;
|
||||
dport->hb_coord.write_latency *= 1000;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_host_bridge_dport(struct device *match, void *arg)
|
||||
{
|
||||
int ret;
|
||||
acpi_status rc;
|
||||
struct device *bridge;
|
||||
struct cxl_dport *dport;
|
||||
@@ -440,6 +583,10 @@ static int add_host_bridge_dport(struct device *match, void *arg)
|
||||
if (IS_ERR(dport))
|
||||
return PTR_ERR(dport);
|
||||
|
||||
ret = get_genport_coordinates(match, dport);
|
||||
if (ret)
|
||||
dev_dbg(match, "Failed to get generic port perf coordinates.\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -656,6 +803,7 @@ static int cxl_acpi_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc;
|
||||
struct resource *cxl_res;
|
||||
struct cxl_root *cxl_root;
|
||||
struct cxl_port *root_port;
|
||||
struct device *host = &pdev->dev;
|
||||
struct acpi_device *adev = ACPI_COMPANION(host);
|
||||
@@ -675,9 +823,10 @@ static int cxl_acpi_probe(struct platform_device *pdev)
|
||||
cxl_res->end = -1;
|
||||
cxl_res->flags = IORESOURCE_MEM;
|
||||
|
||||
root_port = devm_cxl_add_port(host, host, CXL_RESOURCE_NONE, NULL);
|
||||
if (IS_ERR(root_port))
|
||||
return PTR_ERR(root_port);
|
||||
cxl_root = devm_cxl_add_root(host, &acpi_root_ops);
|
||||
if (IS_ERR(cxl_root))
|
||||
return PTR_ERR(cxl_root);
|
||||
root_port = &cxl_root->port;
|
||||
|
||||
rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
|
||||
add_host_bridge_dport);
|
||||
|
||||
@@ -13,5 +13,6 @@ cxl_core-y += mbox.o
|
||||
cxl_core-y += pci.o
|
||||
cxl_core-y += hdm.o
|
||||
cxl_core-y += pmu.o
|
||||
cxl_core-y += cdat.o
|
||||
cxl_core-$(CONFIG_TRACING) += trace.o
|
||||
cxl_core-$(CONFIG_CXL_REGION) += region.o
|
||||
|
||||
521
drivers/cxl/core/cdat.c
Normal file
521
drivers/cxl/core/cdat.c
Normal file
File diff suppressed because it is too large
Load Diff
@@ -88,4 +88,6 @@ enum cxl_poison_trace_type {
|
||||
CXL_POISON_TRACE_CLEAR,
|
||||
};
|
||||
|
||||
long cxl_pci_get_latency(struct pci_dev *pdev);
|
||||
|
||||
#endif /* __CXL_CORE_H__ */
|
||||
|
||||
@@ -63,6 +63,7 @@ static struct cxl_mem_command cxl_mem_commands[CXL_MEM_COMMAND_ID_MAX] = {
|
||||
CXL_CMD(GET_SHUTDOWN_STATE, 0, 0x1, 0),
|
||||
CXL_CMD(SET_SHUTDOWN_STATE, 0x1, 0, 0),
|
||||
CXL_CMD(GET_SCAN_MEDIA_CAPS, 0x10, 0x4, 0),
|
||||
CXL_CMD(GET_TIMESTAMP, 0, 0x8, 0),
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -836,54 +837,37 @@ out:
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_enumerate_cmds, CXL);
|
||||
|
||||
/*
|
||||
* General Media Event Record
|
||||
* CXL rev 3.0 Section 8.2.9.2.1.1; Table 8-43
|
||||
*/
|
||||
static const uuid_t gen_media_event_uuid =
|
||||
UUID_INIT(0xfbcd0a77, 0xc260, 0x417f,
|
||||
0x85, 0xa9, 0x08, 0x8b, 0x16, 0x21, 0xeb, 0xa6);
|
||||
|
||||
/*
|
||||
* DRAM Event Record
|
||||
* CXL rev 3.0 section 8.2.9.2.1.2; Table 8-44
|
||||
*/
|
||||
static const uuid_t dram_event_uuid =
|
||||
UUID_INIT(0x601dcbb3, 0x9c06, 0x4eab,
|
||||
0xb8, 0xaf, 0x4e, 0x9b, 0xfb, 0x5c, 0x96, 0x24);
|
||||
|
||||
/*
|
||||
* Memory Module Event Record
|
||||
* CXL rev 3.0 section 8.2.9.2.1.3; Table 8-45
|
||||
*/
|
||||
static const uuid_t mem_mod_event_uuid =
|
||||
UUID_INIT(0xfe927475, 0xdd59, 0x4339,
|
||||
0xa5, 0x86, 0x79, 0xba, 0xb1, 0x13, 0xb7, 0x74);
|
||||
|
||||
static void cxl_event_trace_record(const struct cxl_memdev *cxlmd,
|
||||
enum cxl_event_log_type type,
|
||||
struct cxl_event_record_raw *record)
|
||||
void cxl_event_trace_record(const struct cxl_memdev *cxlmd,
|
||||
enum cxl_event_log_type type,
|
||||
enum cxl_event_type event_type,
|
||||
const uuid_t *uuid, union cxl_event *evt)
|
||||
{
|
||||
uuid_t *id = &record->hdr.id;
|
||||
if (event_type == CXL_CPER_EVENT_GEN_MEDIA)
|
||||
trace_cxl_general_media(cxlmd, type, &evt->gen_media);
|
||||
else if (event_type == CXL_CPER_EVENT_DRAM)
|
||||
trace_cxl_dram(cxlmd, type, &evt->dram);
|
||||
else if (event_type == CXL_CPER_EVENT_MEM_MODULE)
|
||||
trace_cxl_memory_module(cxlmd, type, &evt->mem_module);
|
||||
else
|
||||
trace_cxl_generic_event(cxlmd, type, uuid, &evt->generic);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_event_trace_record, CXL);
|
||||
|
||||
if (uuid_equal(id, &gen_media_event_uuid)) {
|
||||
struct cxl_event_gen_media *rec =
|
||||
(struct cxl_event_gen_media *)record;
|
||||
static void __cxl_event_trace_record(const struct cxl_memdev *cxlmd,
|
||||
enum cxl_event_log_type type,
|
||||
struct cxl_event_record_raw *record)
|
||||
{
|
||||
enum cxl_event_type ev_type = CXL_CPER_EVENT_GENERIC;
|
||||
const uuid_t *uuid = &record->id;
|
||||
|
||||
trace_cxl_general_media(cxlmd, type, rec);
|
||||
} else if (uuid_equal(id, &dram_event_uuid)) {
|
||||
struct cxl_event_dram *rec = (struct cxl_event_dram *)record;
|
||||
if (uuid_equal(uuid, &CXL_EVENT_GEN_MEDIA_UUID))
|
||||
ev_type = CXL_CPER_EVENT_GEN_MEDIA;
|
||||
else if (uuid_equal(uuid, &CXL_EVENT_DRAM_UUID))
|
||||
ev_type = CXL_CPER_EVENT_DRAM;
|
||||
else if (uuid_equal(uuid, &CXL_EVENT_MEM_MODULE_UUID))
|
||||
ev_type = CXL_CPER_EVENT_MEM_MODULE;
|
||||
|
||||
trace_cxl_dram(cxlmd, type, rec);
|
||||
} else if (uuid_equal(id, &mem_mod_event_uuid)) {
|
||||
struct cxl_event_mem_module *rec =
|
||||
(struct cxl_event_mem_module *)record;
|
||||
|
||||
trace_cxl_memory_module(cxlmd, type, rec);
|
||||
} else {
|
||||
/* For unknown record types print just the header */
|
||||
trace_cxl_generic_event(cxlmd, type, record);
|
||||
}
|
||||
cxl_event_trace_record(cxlmd, type, ev_type, uuid, &record->event);
|
||||
}
|
||||
|
||||
static int cxl_clear_event_record(struct cxl_memdev_state *mds,
|
||||
@@ -926,7 +910,10 @@ static int cxl_clear_event_record(struct cxl_memdev_state *mds,
|
||||
*/
|
||||
i = 0;
|
||||
for (cnt = 0; cnt < total; cnt++) {
|
||||
payload->handles[i++] = get_pl->records[cnt].hdr.handle;
|
||||
struct cxl_event_record_raw *raw = &get_pl->records[cnt];
|
||||
struct cxl_event_generic *gen = &raw->event.generic;
|
||||
|
||||
payload->handles[i++] = gen->hdr.handle;
|
||||
dev_dbg(mds->cxlds.dev, "Event log '%d': Clearing %u\n", log,
|
||||
le16_to_cpu(payload->handles[i]));
|
||||
|
||||
@@ -991,8 +978,8 @@ static void cxl_mem_get_records_log(struct cxl_memdev_state *mds,
|
||||
break;
|
||||
|
||||
for (i = 0; i < nr_rec; i++)
|
||||
cxl_event_trace_record(cxlmd, type,
|
||||
&payload->records[i]);
|
||||
__cxl_event_trace_record(cxlmd, type,
|
||||
&payload->records[i]);
|
||||
|
||||
if (payload->flags & CXL_GET_EVENT_FLAG_OVERFLOW)
|
||||
trace_cxl_overflow(cxlmd, type, payload);
|
||||
@@ -1404,6 +1391,8 @@ struct cxl_memdev_state *cxl_memdev_state_create(struct device *dev)
|
||||
mds->cxlds.reg_map.host = dev;
|
||||
mds->cxlds.reg_map.resource = CXL_RESOURCE_NONE;
|
||||
mds->cxlds.type = CXL_DEVTYPE_CLASSMEM;
|
||||
INIT_LIST_HEAD(&mds->ram_perf_list);
|
||||
INIT_LIST_HEAD(&mds->pmem_perf_list);
|
||||
|
||||
return mds;
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ static DEVICE_ATTR_RO(serial);
|
||||
static ssize_t numa_node_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", dev_to_node(dev));
|
||||
return sysfs_emit(buf, "%d\n", dev_to_node(dev));
|
||||
}
|
||||
static DEVICE_ATTR_RO(numa_node);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright(c) 2021 Intel Corporation. All rights reserved. */
|
||||
#include <linux/units.h>
|
||||
#include <linux/io-64-nonatomic-lo-hi.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
@@ -979,3 +980,38 @@ pci_ers_result_t cxl_error_detected(struct pci_dev *pdev,
|
||||
return PCI_ERS_RESULT_NEED_RESET;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_error_detected, CXL);
|
||||
|
||||
static int cxl_flit_size(struct pci_dev *pdev)
|
||||
{
|
||||
if (cxl_pci_flit_256(pdev))
|
||||
return 256;
|
||||
|
||||
return 68;
|
||||
}
|
||||
|
||||
/**
|
||||
* cxl_pci_get_latency - calculate the link latency for the PCIe link
|
||||
* @pdev: PCI device
|
||||
*
|
||||
* return: calculated latency or 0 for no latency
|
||||
*
|
||||
* CXL Memory Device SW Guide v1.0 2.11.4 Link latency calculation
|
||||
* Link latency = LinkPropagationLatency + FlitLatency + RetimerLatency
|
||||
* LinkProgationLatency is negligible, so 0 will be used
|
||||
* RetimerLatency is assumed to be negligible and 0 will be used
|
||||
* FlitLatency = FlitSize / LinkBandwidth
|
||||
* FlitSize is defined by spec. CXL rev3.0 4.2.1.
|
||||
* 68B flit is used up to 32GT/s. >32GT/s, 256B flit size is used.
|
||||
* The FlitLatency is converted to picoseconds.
|
||||
*/
|
||||
long cxl_pci_get_latency(struct pci_dev *pdev)
|
||||
{
|
||||
long bw;
|
||||
|
||||
bw = pcie_link_speed_mbps(pdev);
|
||||
if (bw < 0)
|
||||
return 0;
|
||||
bw /= BITS_PER_BYTE;
|
||||
|
||||
return cxl_flit_size(pdev) * MEGA / bw;
|
||||
}
|
||||
|
||||
@@ -64,14 +64,14 @@ static int match_nvdimm_bridge(struct device *dev, void *data)
|
||||
|
||||
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_memdev *cxlmd)
|
||||
{
|
||||
struct cxl_port *port = find_cxl_root(cxlmd->endpoint);
|
||||
struct cxl_root *cxl_root __free(put_cxl_root) =
|
||||
find_cxl_root(cxlmd->endpoint);
|
||||
struct device *dev;
|
||||
|
||||
if (!port)
|
||||
if (!cxl_root)
|
||||
return NULL;
|
||||
|
||||
dev = device_find_child(&port->dev, NULL, match_nvdimm_bridge);
|
||||
put_device(&port->dev);
|
||||
dev = device_find_child(&cxl_root->port.dev, NULL, match_nvdimm_bridge);
|
||||
|
||||
if (!dev)
|
||||
return NULL;
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <linux/pci.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/idr.h>
|
||||
#include <linux/node.h>
|
||||
#include <cxlmem.h>
|
||||
#include <cxlpci.h>
|
||||
#include <cxl.h>
|
||||
@@ -172,14 +173,10 @@ static ssize_t target_list_show(struct device *dev,
|
||||
{
|
||||
struct cxl_switch_decoder *cxlsd = to_cxl_switch_decoder(dev);
|
||||
ssize_t offset;
|
||||
unsigned int seq;
|
||||
int rc;
|
||||
|
||||
do {
|
||||
seq = read_seqbegin(&cxlsd->target_lock);
|
||||
rc = emit_target_list(cxlsd, buf);
|
||||
} while (read_seqretry(&cxlsd->target_lock, seq));
|
||||
|
||||
guard(rwsem_read)(&cxl_region_rwsem);
|
||||
rc = emit_target_list(cxlsd, buf);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
offset = rc;
|
||||
@@ -541,7 +538,10 @@ static void cxl_port_release(struct device *dev)
|
||||
xa_destroy(&port->dports);
|
||||
xa_destroy(&port->regions);
|
||||
ida_free(&cxl_port_ida, port->id);
|
||||
kfree(port);
|
||||
if (is_cxl_root(port))
|
||||
kfree(to_cxl_root(port));
|
||||
else
|
||||
kfree(port);
|
||||
}
|
||||
|
||||
static ssize_t decoders_committed_show(struct device *dev,
|
||||
@@ -669,17 +669,31 @@ static struct lock_class_key cxl_port_key;
|
||||
static struct cxl_port *cxl_port_alloc(struct device *uport_dev,
|
||||
struct cxl_dport *parent_dport)
|
||||
{
|
||||
struct cxl_port *port;
|
||||
struct cxl_root *cxl_root __free(kfree) = NULL;
|
||||
struct cxl_port *port, *_port __free(kfree) = NULL;
|
||||
struct device *dev;
|
||||
int rc;
|
||||
|
||||
port = kzalloc(sizeof(*port), GFP_KERNEL);
|
||||
if (!port)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
/* No parent_dport, root cxl_port */
|
||||
if (!parent_dport) {
|
||||
cxl_root = kzalloc(sizeof(*cxl_root), GFP_KERNEL);
|
||||
if (!cxl_root)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
} else {
|
||||
_port = kzalloc(sizeof(*port), GFP_KERNEL);
|
||||
if (!_port)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
rc = ida_alloc(&cxl_port_ida, GFP_KERNEL);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
return ERR_PTR(rc);
|
||||
|
||||
if (cxl_root)
|
||||
port = &no_free_ptr(cxl_root)->port;
|
||||
else
|
||||
port = no_free_ptr(_port);
|
||||
|
||||
port->id = rc;
|
||||
port->uport_dev = uport_dev;
|
||||
|
||||
@@ -731,10 +745,6 @@ static struct cxl_port *cxl_port_alloc(struct device *uport_dev,
|
||||
dev->type = &cxl_port_type;
|
||||
|
||||
return port;
|
||||
|
||||
err:
|
||||
kfree(port);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
|
||||
static int cxl_setup_comp_regs(struct device *host, struct cxl_register_map *map,
|
||||
@@ -841,6 +851,9 @@ static struct cxl_port *__devm_cxl_add_port(struct device *host,
|
||||
if (rc)
|
||||
return ERR_PTR(rc);
|
||||
|
||||
if (parent_dport && dev_is_pci(uport_dev))
|
||||
port->pci_latency = cxl_pci_get_latency(to_pci_dev(uport_dev));
|
||||
|
||||
return port;
|
||||
|
||||
err:
|
||||
@@ -884,6 +897,22 @@ struct cxl_port *devm_cxl_add_port(struct device *host,
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(devm_cxl_add_port, CXL);
|
||||
|
||||
struct cxl_root *devm_cxl_add_root(struct device *host,
|
||||
const struct cxl_root_ops *ops)
|
||||
{
|
||||
struct cxl_root *cxl_root;
|
||||
struct cxl_port *port;
|
||||
|
||||
port = devm_cxl_add_port(host, host, CXL_RESOURCE_NONE, NULL);
|
||||
if (IS_ERR(port))
|
||||
return (struct cxl_root *)port;
|
||||
|
||||
cxl_root = to_cxl_root(port);
|
||||
cxl_root->ops = ops;
|
||||
return cxl_root;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(devm_cxl_add_root, CXL);
|
||||
|
||||
struct pci_bus *cxl_port_to_pci_bus(struct cxl_port *port)
|
||||
{
|
||||
/* There is no pci_bus associated with a CXL platform-root port */
|
||||
@@ -939,7 +968,7 @@ static bool dev_is_cxl_root_child(struct device *dev)
|
||||
return false;
|
||||
}
|
||||
|
||||
struct cxl_port *find_cxl_root(struct cxl_port *port)
|
||||
struct cxl_root *find_cxl_root(struct cxl_port *port)
|
||||
{
|
||||
struct cxl_port *iter = port;
|
||||
|
||||
@@ -949,10 +978,19 @@ struct cxl_port *find_cxl_root(struct cxl_port *port)
|
||||
if (!iter)
|
||||
return NULL;
|
||||
get_device(&iter->dev);
|
||||
return iter;
|
||||
return to_cxl_root(iter);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(find_cxl_root, CXL);
|
||||
|
||||
void put_cxl_root(struct cxl_root *cxl_root)
|
||||
{
|
||||
if (!cxl_root)
|
||||
return;
|
||||
|
||||
put_device(&cxl_root->port.dev);
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(put_cxl_root, CXL);
|
||||
|
||||
static struct cxl_dport *find_dport(struct cxl_port *port, int id)
|
||||
{
|
||||
struct cxl_dport *dport;
|
||||
@@ -1108,6 +1146,9 @@ __devm_cxl_add_dport(struct cxl_port *port, struct device *dport_dev,
|
||||
if (rc)
|
||||
return ERR_PTR(rc);
|
||||
|
||||
if (dev_is_pci(dport_dev))
|
||||
dport->link_latency = cxl_pci_get_latency(to_pci_dev(dport_dev));
|
||||
|
||||
return dport;
|
||||
}
|
||||
|
||||
@@ -1633,7 +1674,7 @@ EXPORT_SYMBOL_NS_GPL(cxl_mem_find_port, CXL);
|
||||
static int decoder_populate_targets(struct cxl_switch_decoder *cxlsd,
|
||||
struct cxl_port *port, int *target_map)
|
||||
{
|
||||
int i, rc = 0;
|
||||
int i;
|
||||
|
||||
if (!target_map)
|
||||
return 0;
|
||||
@@ -1643,19 +1684,16 @@ static int decoder_populate_targets(struct cxl_switch_decoder *cxlsd,
|
||||
if (xa_empty(&port->dports))
|
||||
return -EINVAL;
|
||||
|
||||
write_seqlock(&cxlsd->target_lock);
|
||||
for (i = 0; i < cxlsd->nr_targets; i++) {
|
||||
guard(rwsem_write)(&cxl_region_rwsem);
|
||||
for (i = 0; i < cxlsd->cxld.interleave_ways; i++) {
|
||||
struct cxl_dport *dport = find_dport(port, target_map[i]);
|
||||
|
||||
if (!dport) {
|
||||
rc = -ENXIO;
|
||||
break;
|
||||
}
|
||||
if (!dport)
|
||||
return -ENXIO;
|
||||
cxlsd->target[i] = dport;
|
||||
}
|
||||
write_sequnlock(&cxlsd->target_lock);
|
||||
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct cxl_dport *cxl_hb_modulo(struct cxl_root_decoder *cxlrd, int pos)
|
||||
@@ -1725,7 +1763,6 @@ static int cxl_switch_decoder_init(struct cxl_port *port,
|
||||
return -EINVAL;
|
||||
|
||||
cxlsd->nr_targets = nr_targets;
|
||||
seqlock_init(&cxlsd->target_lock);
|
||||
return cxl_decoder_init(port, &cxlsd->cxld);
|
||||
}
|
||||
|
||||
@@ -2059,6 +2096,80 @@ bool schedule_cxl_memdev_detach(struct cxl_memdev *cxlmd)
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(schedule_cxl_memdev_detach, CXL);
|
||||
|
||||
static void combine_coordinates(struct access_coordinate *c1,
|
||||
struct access_coordinate *c2)
|
||||
{
|
||||
if (c2->write_bandwidth)
|
||||
c1->write_bandwidth = min(c1->write_bandwidth,
|
||||
c2->write_bandwidth);
|
||||
c1->write_latency += c2->write_latency;
|
||||
|
||||
if (c2->read_bandwidth)
|
||||
c1->read_bandwidth = min(c1->read_bandwidth,
|
||||
c2->read_bandwidth);
|
||||
c1->read_latency += c2->read_latency;
|
||||
}
|
||||
|
||||
/**
|
||||
* cxl_endpoint_get_perf_coordinates - Retrieve performance numbers stored in dports
|
||||
* of CXL path
|
||||
* @port: endpoint cxl_port
|
||||
* @coord: output performance data
|
||||
*
|
||||
* Return: errno on failure, 0 on success.
|
||||
*/
|
||||
int cxl_endpoint_get_perf_coordinates(struct cxl_port *port,
|
||||
struct access_coordinate *coord)
|
||||
{
|
||||
struct access_coordinate c = {
|
||||
.read_bandwidth = UINT_MAX,
|
||||
.write_bandwidth = UINT_MAX,
|
||||
};
|
||||
struct cxl_port *iter = port;
|
||||
struct cxl_dport *dport;
|
||||
struct pci_dev *pdev;
|
||||
unsigned int bw;
|
||||
|
||||
if (!is_cxl_endpoint(port))
|
||||
return -EINVAL;
|
||||
|
||||
dport = iter->parent_dport;
|
||||
|
||||
/*
|
||||
* Exit the loop when the parent port of the current port is cxl root.
|
||||
* The iterative loop starts at the endpoint and gathers the
|
||||
* latency of the CXL link from the current iter to the next downstream
|
||||
* port each iteration. If the parent is cxl root then there is
|
||||
* nothing to gather.
|
||||
*/
|
||||
while (iter && !is_cxl_root(to_cxl_port(iter->dev.parent))) {
|
||||
combine_coordinates(&c, &dport->sw_coord);
|
||||
c.write_latency += dport->link_latency;
|
||||
c.read_latency += dport->link_latency;
|
||||
|
||||
iter = to_cxl_port(iter->dev.parent);
|
||||
dport = iter->parent_dport;
|
||||
}
|
||||
|
||||
/* Augment with the generic port (host bridge) perf data */
|
||||
combine_coordinates(&c, &dport->hb_coord);
|
||||
|
||||
/* Get the calculated PCI paths bandwidth */
|
||||
pdev = to_pci_dev(port->uport_dev->parent);
|
||||
bw = pcie_bandwidth_available(pdev, NULL, NULL, NULL);
|
||||
if (bw == 0)
|
||||
return -ENXIO;
|
||||
bw /= BITS_PER_BYTE;
|
||||
|
||||
c.write_bandwidth = min(c.write_bandwidth, bw);
|
||||
c.read_bandwidth = min(c.read_bandwidth, bw);
|
||||
|
||||
*coord = c;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_endpoint_get_perf_coordinates, CXL);
|
||||
|
||||
/* for user tooling to ensure port disable work has completed */
|
||||
static ssize_t flush_store(const struct bus_type *bus, const char *buf, size_t count)
|
||||
{
|
||||
|
||||
@@ -397,7 +397,7 @@ static ssize_t interleave_ways_store(struct device *dev,
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* Even for x3, x9, and x12 interleaves the region interleave must be a
|
||||
* Even for x3, x6, and x12 interleaves the region interleave must be a
|
||||
* power of 2 multiple of the host bridge interleave.
|
||||
*/
|
||||
if (!is_power_of_2(val / cxld->interleave_ways) ||
|
||||
@@ -552,8 +552,9 @@ static int alloc_hpa(struct cxl_region *cxlr, resource_size_t size)
|
||||
res = alloc_free_mem_region(cxlrd->res, size, SZ_256M,
|
||||
dev_name(&cxlr->dev));
|
||||
if (IS_ERR(res)) {
|
||||
dev_dbg(&cxlr->dev, "failed to allocate HPA: %ld\n",
|
||||
PTR_ERR(res));
|
||||
dev_dbg(&cxlr->dev,
|
||||
"HPA allocation error (%ld) for size:%pap in %s %pr\n",
|
||||
PTR_ERR(res), &size, cxlrd->res->name, cxlrd->res);
|
||||
return PTR_ERR(res);
|
||||
}
|
||||
|
||||
@@ -2083,13 +2084,13 @@ static struct cxl_region *to_cxl_region(struct device *dev)
|
||||
return container_of(dev, struct cxl_region, dev);
|
||||
}
|
||||
|
||||
static void unregister_region(void *dev)
|
||||
static void unregister_region(void *_cxlr)
|
||||
{
|
||||
struct cxl_region *cxlr = to_cxl_region(dev);
|
||||
struct cxl_region *cxlr = _cxlr;
|
||||
struct cxl_region_params *p = &cxlr->params;
|
||||
int i;
|
||||
|
||||
device_del(dev);
|
||||
device_del(&cxlr->dev);
|
||||
|
||||
/*
|
||||
* Now that region sysfs is shutdown, the parameter block is now
|
||||
@@ -2100,7 +2101,7 @@ static void unregister_region(void *dev)
|
||||
detach_target(cxlr, i);
|
||||
|
||||
cxl_region_iomem_release(cxlr);
|
||||
put_device(dev);
|
||||
put_device(&cxlr->dev);
|
||||
}
|
||||
|
||||
static struct lock_class_key cxl_region_key;
|
||||
|
||||
@@ -181,6 +181,7 @@ TRACE_EVENT(cxl_overflow,
|
||||
* 1) Add CXL_EVT_TP_entry to TP_STRUCT__entry
|
||||
* 2) Use CXL_EVT_TP_fast_assign within TP_fast_assign;
|
||||
* pass the dev, log, and CXL event header
|
||||
* NOTE: The uuid must be assigned by the specific trace event
|
||||
* 3) Use CXL_EVT_TP_printk() instead of TP_printk()
|
||||
*
|
||||
* See the generic_event tracepoint as an example.
|
||||
@@ -203,7 +204,6 @@ TRACE_EVENT(cxl_overflow,
|
||||
__assign_str(host, dev_name((cxlmd)->dev.parent)); \
|
||||
__entry->log = (l); \
|
||||
__entry->serial = (cxlmd)->cxlds->serial; \
|
||||
memcpy(&__entry->hdr_uuid, &(hdr).id, sizeof(uuid_t)); \
|
||||
__entry->hdr_length = (hdr).length; \
|
||||
__entry->hdr_flags = get_unaligned_le24((hdr).flags); \
|
||||
__entry->hdr_handle = le16_to_cpu((hdr).handle); \
|
||||
@@ -225,9 +225,9 @@ TRACE_EVENT(cxl_overflow,
|
||||
TRACE_EVENT(cxl_generic_event,
|
||||
|
||||
TP_PROTO(const struct cxl_memdev *cxlmd, enum cxl_event_log_type log,
|
||||
struct cxl_event_record_raw *rec),
|
||||
const uuid_t *uuid, struct cxl_event_generic *gen_rec),
|
||||
|
||||
TP_ARGS(cxlmd, log, rec),
|
||||
TP_ARGS(cxlmd, log, uuid, gen_rec),
|
||||
|
||||
TP_STRUCT__entry(
|
||||
CXL_EVT_TP_entry
|
||||
@@ -235,8 +235,9 @@ TRACE_EVENT(cxl_generic_event,
|
||||
),
|
||||
|
||||
TP_fast_assign(
|
||||
CXL_EVT_TP_fast_assign(cxlmd, log, rec->hdr);
|
||||
memcpy(__entry->data, &rec->data, CXL_EVENT_RECORD_DATA_LENGTH);
|
||||
CXL_EVT_TP_fast_assign(cxlmd, log, gen_rec->hdr);
|
||||
memcpy(&__entry->hdr_uuid, uuid, sizeof(uuid_t));
|
||||
memcpy(__entry->data, gen_rec->data, CXL_EVENT_RECORD_DATA_LENGTH);
|
||||
),
|
||||
|
||||
CXL_EVT_TP_printk("%s",
|
||||
@@ -337,6 +338,7 @@ TRACE_EVENT(cxl_general_media,
|
||||
|
||||
TP_fast_assign(
|
||||
CXL_EVT_TP_fast_assign(cxlmd, log, rec->hdr);
|
||||
memcpy(&__entry->hdr_uuid, &CXL_EVENT_GEN_MEDIA_UUID, sizeof(uuid_t));
|
||||
|
||||
/* General Media */
|
||||
__entry->dpa = le64_to_cpu(rec->phys_addr);
|
||||
@@ -423,6 +425,7 @@ TRACE_EVENT(cxl_dram,
|
||||
|
||||
TP_fast_assign(
|
||||
CXL_EVT_TP_fast_assign(cxlmd, log, rec->hdr);
|
||||
memcpy(&__entry->hdr_uuid, &CXL_EVENT_DRAM_UUID, sizeof(uuid_t));
|
||||
|
||||
/* DRAM */
|
||||
__entry->dpa = le64_to_cpu(rec->phys_addr);
|
||||
@@ -570,6 +573,7 @@ TRACE_EVENT(cxl_memory_module,
|
||||
|
||||
TP_fast_assign(
|
||||
CXL_EVT_TP_fast_assign(cxlmd, log, rec->hdr);
|
||||
memcpy(&__entry->hdr_uuid, &CXL_EVENT_MEM_MODULE_UUID, sizeof(uuid_t));
|
||||
|
||||
/* Memory Module Event */
|
||||
__entry->event_type = rec->event_type;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/node.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
/**
|
||||
@@ -412,7 +413,6 @@ struct cxl_endpoint_decoder {
|
||||
/**
|
||||
* struct cxl_switch_decoder - Switch specific CXL HDM Decoder
|
||||
* @cxld: base cxl_decoder object
|
||||
* @target_lock: coordinate coherent reads of the target list
|
||||
* @nr_targets: number of elements in @target
|
||||
* @target: active ordered target list in current decoder configuration
|
||||
*
|
||||
@@ -424,7 +424,6 @@ struct cxl_endpoint_decoder {
|
||||
*/
|
||||
struct cxl_switch_decoder {
|
||||
struct cxl_decoder cxld;
|
||||
seqlock_t target_lock;
|
||||
int nr_targets;
|
||||
struct cxl_dport *target[];
|
||||
};
|
||||
@@ -590,6 +589,7 @@ struct cxl_dax_region {
|
||||
* @depth: How deep this port is relative to the root. depth 0 is the root.
|
||||
* @cdat: Cached CDAT data
|
||||
* @cdat_available: Should a CDAT attribute be available in sysfs
|
||||
* @pci_latency: Upstream latency in picoseconds
|
||||
*/
|
||||
struct cxl_port {
|
||||
struct device dev;
|
||||
@@ -612,6 +612,30 @@ struct cxl_port {
|
||||
size_t length;
|
||||
} cdat;
|
||||
bool cdat_available;
|
||||
long pci_latency;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cxl_root - logical collection of root cxl_port items
|
||||
*
|
||||
* @port: cxl_port member
|
||||
* @ops: cxl root operations
|
||||
*/
|
||||
struct cxl_root {
|
||||
struct cxl_port port;
|
||||
const struct cxl_root_ops *ops;
|
||||
};
|
||||
|
||||
static inline struct cxl_root *
|
||||
to_cxl_root(const struct cxl_port *port)
|
||||
{
|
||||
return container_of(port, struct cxl_root, port);
|
||||
}
|
||||
|
||||
struct cxl_root_ops {
|
||||
int (*qos_class)(struct cxl_root *cxl_root,
|
||||
struct access_coordinate *coord, int entries,
|
||||
int *qos_class);
|
||||
};
|
||||
|
||||
static inline struct cxl_dport *
|
||||
@@ -634,6 +658,9 @@ struct cxl_rcrb_info {
|
||||
* @rch: Indicate whether this dport was enumerated in RCH or VH mode
|
||||
* @port: reference to cxl_port that contains this downstream port
|
||||
* @regs: Dport parsed register blocks
|
||||
* @sw_coord: access coordinates (performance) for switch from CDAT
|
||||
* @hb_coord: access coordinates (performance) from ACPI generic port (host bridge)
|
||||
* @link_latency: calculated PCIe downstream latency
|
||||
*/
|
||||
struct cxl_dport {
|
||||
struct device *dport_dev;
|
||||
@@ -643,6 +670,9 @@ struct cxl_dport {
|
||||
bool rch;
|
||||
struct cxl_port *port;
|
||||
struct cxl_regs regs;
|
||||
struct access_coordinate sw_coord;
|
||||
struct access_coordinate hb_coord;
|
||||
long link_latency;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -700,7 +730,12 @@ struct cxl_port *devm_cxl_add_port(struct device *host,
|
||||
struct device *uport_dev,
|
||||
resource_size_t component_reg_phys,
|
||||
struct cxl_dport *parent_dport);
|
||||
struct cxl_port *find_cxl_root(struct cxl_port *port);
|
||||
struct cxl_root *devm_cxl_add_root(struct device *host,
|
||||
const struct cxl_root_ops *ops);
|
||||
struct cxl_root *find_cxl_root(struct cxl_port *port);
|
||||
void put_cxl_root(struct cxl_root *cxl_root);
|
||||
DEFINE_FREE(put_cxl_root, struct cxl_root *, if (_T) put_cxl_root(_T))
|
||||
|
||||
int devm_cxl_enumerate_ports(struct cxl_memdev *cxlmd);
|
||||
void cxl_bus_rescan(void);
|
||||
void cxl_bus_drain(void);
|
||||
@@ -839,6 +874,12 @@ static inline struct cxl_dax_region *to_cxl_dax_region(struct device *dev)
|
||||
}
|
||||
#endif
|
||||
|
||||
void cxl_endpoint_parse_cdat(struct cxl_port *port);
|
||||
void cxl_switch_parse_cdat(struct cxl_port *port);
|
||||
|
||||
int cxl_endpoint_get_perf_coordinates(struct cxl_port *port,
|
||||
struct access_coordinate *coord);
|
||||
|
||||
/*
|
||||
* Unit test builds overrides this to __weak, find the 'strong' version
|
||||
* of these symbols in tools/testing/cxl/.
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/uuid.h>
|
||||
#include <linux/rcuwait.h>
|
||||
#include <linux/cxl-event.h>
|
||||
#include <linux/node.h>
|
||||
#include "cxl.h"
|
||||
|
||||
/* CXL 2.0 8.2.8.5.1.1 Memory Device Status Register */
|
||||
@@ -391,6 +393,20 @@ enum cxl_devtype {
|
||||
CXL_DEVTYPE_CLASSMEM,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cxl_dpa_perf - DPA performance property entry
|
||||
* @list - list entry
|
||||
* @dpa_range - range for DPA address
|
||||
* @coord - QoS performance data (i.e. latency, bandwidth)
|
||||
* @qos_class - QoS Class cookies
|
||||
*/
|
||||
struct cxl_dpa_perf {
|
||||
struct list_head list;
|
||||
struct range dpa_range;
|
||||
struct access_coordinate coord;
|
||||
int qos_class;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cxl_dev_state - The driver device state
|
||||
*
|
||||
@@ -455,6 +471,8 @@ struct cxl_dev_state {
|
||||
* @security: security driver state info
|
||||
* @fw: firmware upload / activation state
|
||||
* @mbox_send: @dev specific transport for transmitting mailbox commands
|
||||
* @ram_perf_list: performance data entries matched to RAM
|
||||
* @pmem_perf_list: performance data entries matched to PMEM
|
||||
*
|
||||
* See CXL 3.0 8.2.9.8.2 Capacity Configuration and Label Storage for
|
||||
* details on capacity parameters.
|
||||
@@ -475,6 +493,10 @@ struct cxl_memdev_state {
|
||||
u64 active_persistent_bytes;
|
||||
u64 next_volatile_bytes;
|
||||
u64 next_persistent_bytes;
|
||||
|
||||
struct list_head ram_perf_list;
|
||||
struct list_head pmem_perf_list;
|
||||
|
||||
struct cxl_event_state event;
|
||||
struct cxl_poison_state poison;
|
||||
struct cxl_security_state security;
|
||||
@@ -503,6 +525,7 @@ enum cxl_opcode {
|
||||
CXL_MBOX_OP_GET_FW_INFO = 0x0200,
|
||||
CXL_MBOX_OP_TRANSFER_FW = 0x0201,
|
||||
CXL_MBOX_OP_ACTIVATE_FW = 0x0202,
|
||||
CXL_MBOX_OP_GET_TIMESTAMP = 0x0300,
|
||||
CXL_MBOX_OP_SET_TIMESTAMP = 0x0301,
|
||||
CXL_MBOX_OP_GET_SUPPORTED_LOGS = 0x0400,
|
||||
CXL_MBOX_OP_GET_LOG = 0x0401,
|
||||
@@ -580,25 +603,28 @@ struct cxl_mbox_identify {
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Common Event Record Format
|
||||
* CXL rev 3.0 section 8.2.9.2.1; Table 8-42
|
||||
* General Media Event Record UUID
|
||||
* CXL rev 3.0 Section 8.2.9.2.1.1; Table 8-43
|
||||
*/
|
||||
struct cxl_event_record_hdr {
|
||||
uuid_t id;
|
||||
u8 length;
|
||||
u8 flags[3];
|
||||
__le16 handle;
|
||||
__le16 related_handle;
|
||||
__le64 timestamp;
|
||||
u8 maint_op_class;
|
||||
u8 reserved[15];
|
||||
} __packed;
|
||||
#define CXL_EVENT_GEN_MEDIA_UUID \
|
||||
UUID_INIT(0xfbcd0a77, 0xc260, 0x417f, 0x85, 0xa9, 0x08, 0x8b, 0x16, \
|
||||
0x21, 0xeb, 0xa6)
|
||||
|
||||
#define CXL_EVENT_RECORD_DATA_LENGTH 0x50
|
||||
struct cxl_event_record_raw {
|
||||
struct cxl_event_record_hdr hdr;
|
||||
u8 data[CXL_EVENT_RECORD_DATA_LENGTH];
|
||||
} __packed;
|
||||
/*
|
||||
* DRAM Event Record UUID
|
||||
* CXL rev 3.0 section 8.2.9.2.1.2; Table 8-44
|
||||
*/
|
||||
#define CXL_EVENT_DRAM_UUID \
|
||||
UUID_INIT(0x601dcbb3, 0x9c06, 0x4eab, 0xb8, 0xaf, 0x4e, 0x9b, 0xfb, \
|
||||
0x5c, 0x96, 0x24)
|
||||
|
||||
/*
|
||||
* Memory Module Event Record UUID
|
||||
* CXL rev 3.0 section 8.2.9.2.1.3; Table 8-45
|
||||
*/
|
||||
#define CXL_EVENT_MEM_MODULE_UUID \
|
||||
UUID_INIT(0xfe927475, 0xdd59, 0x4339, 0xa5, 0x86, 0x79, 0xba, 0xb1, \
|
||||
0x13, 0xb7, 0x74)
|
||||
|
||||
/*
|
||||
* Get Event Records output payload
|
||||
@@ -641,74 +667,6 @@ struct cxl_mbox_clear_event_payload {
|
||||
} __packed;
|
||||
#define CXL_CLEAR_EVENT_MAX_HANDLES U8_MAX
|
||||
|
||||
/*
|
||||
* General Media Event Record
|
||||
* CXL rev 3.0 Section 8.2.9.2.1.1; Table 8-43
|
||||
*/
|
||||
#define CXL_EVENT_GEN_MED_COMP_ID_SIZE 0x10
|
||||
struct cxl_event_gen_media {
|
||||
struct cxl_event_record_hdr hdr;
|
||||
__le64 phys_addr;
|
||||
u8 descriptor;
|
||||
u8 type;
|
||||
u8 transaction_type;
|
||||
u8 validity_flags[2];
|
||||
u8 channel;
|
||||
u8 rank;
|
||||
u8 device[3];
|
||||
u8 component_id[CXL_EVENT_GEN_MED_COMP_ID_SIZE];
|
||||
u8 reserved[46];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* DRAM Event Record - DER
|
||||
* CXL rev 3.0 section 8.2.9.2.1.2; Table 3-44
|
||||
*/
|
||||
#define CXL_EVENT_DER_CORRECTION_MASK_SIZE 0x20
|
||||
struct cxl_event_dram {
|
||||
struct cxl_event_record_hdr hdr;
|
||||
__le64 phys_addr;
|
||||
u8 descriptor;
|
||||
u8 type;
|
||||
u8 transaction_type;
|
||||
u8 validity_flags[2];
|
||||
u8 channel;
|
||||
u8 rank;
|
||||
u8 nibble_mask[3];
|
||||
u8 bank_group;
|
||||
u8 bank;
|
||||
u8 row[3];
|
||||
u8 column[2];
|
||||
u8 correction_mask[CXL_EVENT_DER_CORRECTION_MASK_SIZE];
|
||||
u8 reserved[0x17];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Get Health Info Record
|
||||
* CXL rev 3.0 section 8.2.9.8.3.1; Table 8-100
|
||||
*/
|
||||
struct cxl_get_health_info {
|
||||
u8 health_status;
|
||||
u8 media_status;
|
||||
u8 add_status;
|
||||
u8 life_used;
|
||||
u8 device_temp[2];
|
||||
u8 dirty_shutdown_cnt[4];
|
||||
u8 cor_vol_err_cnt[4];
|
||||
u8 cor_per_err_cnt[4];
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* Memory Module Event Record
|
||||
* CXL rev 3.0 section 8.2.9.2.1.3; Table 8-45
|
||||
*/
|
||||
struct cxl_event_mem_module {
|
||||
struct cxl_event_record_hdr hdr;
|
||||
u8 event_type;
|
||||
struct cxl_get_health_info info;
|
||||
u8 reserved[0x3d];
|
||||
} __packed;
|
||||
|
||||
struct cxl_mbox_get_partition_info {
|
||||
__le64 active_volatile_cap;
|
||||
__le64 active_persistent_cap;
|
||||
@@ -866,6 +824,10 @@ void set_exclusive_cxl_commands(struct cxl_memdev_state *mds,
|
||||
void clear_exclusive_cxl_commands(struct cxl_memdev_state *mds,
|
||||
unsigned long *cmds);
|
||||
void cxl_mem_get_event_records(struct cxl_memdev_state *mds, u32 status);
|
||||
void cxl_event_trace_record(const struct cxl_memdev *cxlmd,
|
||||
enum cxl_event_log_type type,
|
||||
enum cxl_event_type event_type,
|
||||
const uuid_t *uuid, union cxl_event *evt);
|
||||
int cxl_set_timestamp(struct cxl_memdev_state *mds);
|
||||
int cxl_poison_state_init(struct cxl_memdev_state *mds);
|
||||
int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user