summaryrefslogtreecommitdiff
path: root/drivers/acpi
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/acpi')
-rw-r--r--drivers/acpi/apei/ghes.c89
-rw-r--r--drivers/acpi/numa/hmat.c193
-rw-r--r--drivers/acpi/tables.c5
3 files changed, 256 insertions, 31 deletions
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index ab2a82cb1b0b..7b7c605166e0 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -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);
diff --git a/drivers/acpi/numa/hmat.c b/drivers/acpi/numa/hmat.c
index 9ef5f1bdcfdb..d6b85f0f6082 100644
--- a/drivers/acpi/numa/hmat.c
+++ b/drivers/acpi/numa/hmat.c
@@ -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)
@@ -713,6 +830,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
* to all the memory in a proximity domain leading to
@@ -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);
diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c
index c1516337f668..b07f7d091d13 100644
--- a/drivers/acpi/tables.c
+++ b/drivers/acpi/tables.c
@@ -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;