Commit 624eda92 authored by Dan Williams's avatar Dan Williams

Merge branch 'for-6.7/cxl-qtg' into cxl/next

Merge some prep-work for CXL QOS class support. This cycle saw large
collisions with mm on this topic, so the bulk of this topic needs to
wait.
parents 7f946e6d a103f466
......@@ -369,6 +369,21 @@ Description:
provided it is currently idle / not bound to a driver.
What: /sys/bus/cxl/devices/decoderX.Y/qos_class
Date: May, 2023
KernelVersion: v6.5
Contact: linux-cxl@vger.kernel.org
Description:
(RO) For CXL host platforms that support "QoS Telemmetry" this
root-decoder-only attribute conveys a platform specific cookie
that identifies a QoS performance class for the CXL Window.
This class-id can be compared against a similar "qos_class"
published for each memory-type that an endpoint supports. While
it is not required that endpoints map their local memory-class
to a matching platform class, mismatches are not recommended and
there are platform specific side-effects that may result.
What: /sys/bus/cxl/devices/regionZ/uuid
Date: May, 2022
KernelVersion: v6.0
......
......@@ -294,6 +294,8 @@ F: drivers/pnp/pnpacpi/
F: include/acpi/
F: include/linux/acpi.h
F: include/linux/fwnode.h
F: include/linux/fw_table.h
F: lib/fw_table.c
F: tools/power/acpi/
ACPI APEI
......
......@@ -12,6 +12,7 @@ menuconfig ACPI
select PNP
select NLS
select CRC32
select FIRMWARE_TABLE
default y if X86
help
Advanced Configuration and Power Interface (ACPI) support for
......
......@@ -37,18 +37,6 @@ static struct acpi_table_desc initial_tables[ACPI_MAX_TABLES] __initdata;
static int acpi_apic_instance __initdata_or_acpilib;
enum acpi_subtable_type {
ACPI_SUBTABLE_COMMON,
ACPI_SUBTABLE_HMAT,
ACPI_SUBTABLE_PRMT,
ACPI_SUBTABLE_CEDT,
};
struct acpi_subtable_entry {
union acpi_subtable_headers *hdr;
enum acpi_subtable_type type;
};
/*
* Disable table checksum verification for the early stage due to the size
* limitation of the current x86 early mapping implementation.
......@@ -237,167 +225,6 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
}
}
static unsigned long __init_or_acpilib
acpi_get_entry_type(struct acpi_subtable_entry *entry)
{
switch (entry->type) {
case ACPI_SUBTABLE_COMMON:
return entry->hdr->common.type;
case ACPI_SUBTABLE_HMAT:
return entry->hdr->hmat.type;
case ACPI_SUBTABLE_PRMT:
return 0;
case ACPI_SUBTABLE_CEDT:
return entry->hdr->cedt.type;
}
return 0;
}
static unsigned long __init_or_acpilib
acpi_get_entry_length(struct acpi_subtable_entry *entry)
{
switch (entry->type) {
case ACPI_SUBTABLE_COMMON:
return entry->hdr->common.length;
case ACPI_SUBTABLE_HMAT:
return entry->hdr->hmat.length;
case ACPI_SUBTABLE_PRMT:
return entry->hdr->prmt.length;
case ACPI_SUBTABLE_CEDT:
return entry->hdr->cedt.length;
}
return 0;
}
static unsigned long __init_or_acpilib
acpi_get_subtable_header_length(struct acpi_subtable_entry *entry)
{
switch (entry->type) {
case ACPI_SUBTABLE_COMMON:
return sizeof(entry->hdr->common);
case ACPI_SUBTABLE_HMAT:
return sizeof(entry->hdr->hmat);
case ACPI_SUBTABLE_PRMT:
return sizeof(entry->hdr->prmt);
case ACPI_SUBTABLE_CEDT:
return sizeof(entry->hdr->cedt);
}
return 0;
}
static enum acpi_subtable_type __init_or_acpilib
acpi_get_subtable_type(char *id)
{
if (strncmp(id, ACPI_SIG_HMAT, 4) == 0)
return ACPI_SUBTABLE_HMAT;
if (strncmp(id, ACPI_SIG_PRMT, 4) == 0)
return ACPI_SUBTABLE_PRMT;
if (strncmp(id, ACPI_SIG_CEDT, 4) == 0)
return ACPI_SUBTABLE_CEDT;
return ACPI_SUBTABLE_COMMON;
}
static __init_or_acpilib bool has_handler(struct acpi_subtable_proc *proc)
{
return proc->handler || proc->handler_arg;
}
static __init_or_acpilib int call_handler(struct acpi_subtable_proc *proc,
union acpi_subtable_headers *hdr,
unsigned long end)
{
if (proc->handler)
return proc->handler(hdr, end);
if (proc->handler_arg)
return proc->handler_arg(hdr, proc->arg, end);
return -EINVAL;
}
/**
* acpi_parse_entries_array - for each proc_num find a suitable subtable
*
* @id: table id (for debugging purposes)
* @table_size: size of the root table
* @table_header: where does the table start?
* @proc: array of acpi_subtable_proc struct containing entry id
* and associated handler with it
* @proc_num: how big proc is?
* @max_entries: how many entries can we process?
*
* For each proc_num find a subtable with proc->id and run proc->handler
* on it. Assumption is that there's only single handler for particular
* entry id.
*
* The table_size is not the size of the complete ACPI table (the length
* field in the header struct), but only the size of the root table; i.e.,
* the offset from the very first byte of the complete ACPI table, to the
* first byte of the very first subtable.
*
* On success returns sum of all matching entries for all proc handlers.
* Otherwise, -ENODEV or -EINVAL is returned.
*/
static int __init_or_acpilib acpi_parse_entries_array(
char *id, unsigned long table_size,
struct acpi_table_header *table_header, struct acpi_subtable_proc *proc,
int proc_num, unsigned int max_entries)
{
struct acpi_subtable_entry entry;
unsigned long table_end, subtable_len, entry_len;
int count = 0;
int errs = 0;
int i;
table_end = (unsigned long)table_header + table_header->length;
/* Parse all entries looking for a match. */
entry.type = acpi_get_subtable_type(id);
entry.hdr = (union acpi_subtable_headers *)
((unsigned long)table_header + table_size);
subtable_len = acpi_get_subtable_header_length(&entry);
while (((unsigned long)entry.hdr) + subtable_len < table_end) {
if (max_entries && count >= max_entries)
break;
for (i = 0; i < proc_num; i++) {
if (acpi_get_entry_type(&entry) != proc[i].id)
continue;
if (!has_handler(&proc[i]) ||
(!errs &&
call_handler(&proc[i], entry.hdr, table_end))) {
errs++;
continue;
}
proc[i].count++;
break;
}
if (i != proc_num)
count++;
/*
* If entry->length is 0, break from this loop to avoid
* infinite loop.
*/
entry_len = acpi_get_entry_length(&entry);
if (entry_len == 0) {
pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, proc->id);
return -EINVAL;
}
entry.hdr = (union acpi_subtable_headers *)
((unsigned long)entry.hdr + entry_len);
}
if (max_entries && count > max_entries) {
pr_warn("[%4.4s:0x%02x] found the maximum %i entries\n",
id, proc->id, count);
}
return errs ? -EINVAL : count;
}
int __init_or_acpilib acpi_table_parse_entries_array(
char *id, unsigned long table_size, struct acpi_subtable_proc *proc,
int proc_num, unsigned int max_entries)
......
......@@ -289,6 +289,9 @@ static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
}
}
}
cxlrd->qos_class = cfmws->qtg_id;
rc = cxl_decoder_add(cxld, target_map);
err_xormap:
if (rc)
......
......@@ -596,6 +596,16 @@ static int cxl_cdat_read_table(struct device *dev,
return 0;
}
static unsigned char cdat_checksum(void *buf, size_t size)
{
unsigned char sum, *data = buf;
size_t i;
for (sum = 0, i = 0; i < size; i++)
sum += data[i];
return sum;
}
/**
* read_cdat_data - Read the CDAT data on this port
* @port: Port to read data from
......@@ -604,18 +614,30 @@ static int cxl_cdat_read_table(struct device *dev,
*/
void read_cdat_data(struct cxl_port *port)
{
struct cxl_memdev *cxlmd = to_cxl_memdev(port->uport_dev);
struct device *host = cxlmd->dev.parent;
struct device *uport = port->uport_dev;
struct device *dev = &port->dev;
struct pci_doe_mb *cdat_doe;
struct pci_dev *pdev = NULL;
struct cxl_memdev *cxlmd;
size_t cdat_length;
void *cdat_table;
int rc;
if (!dev_is_pci(host))
if (is_cxl_memdev(uport)) {
struct device *host;
cxlmd = to_cxl_memdev(uport);
host = cxlmd->dev.parent;
if (dev_is_pci(host))
pdev = to_pci_dev(host);
} else if (dev_is_pci(uport)) {
pdev = to_pci_dev(uport);
}
if (!pdev)
return;
cdat_doe = pci_find_doe_mailbox(to_pci_dev(host),
PCI_DVSEC_VENDOR_ID_CXL,
cdat_doe = pci_find_doe_mailbox(pdev, PCI_DVSEC_VENDOR_ID_CXL,
CXL_DOE_PROTOCOL_TABLE_ACCESS);
if (!cdat_doe) {
dev_dbg(dev, "No CDAT mailbox\n");
......@@ -635,15 +657,21 @@ void read_cdat_data(struct cxl_port *port)
return;
rc = cxl_cdat_read_table(dev, cdat_doe, cdat_table, &cdat_length);
if (rc) {
/* Don't leave table data allocated on error */
devm_kfree(dev, cdat_table);
dev_err(dev, "CDAT data read error\n");
return;
}
if (rc)
goto err;
cdat_table = cdat_table + sizeof(__le32);
if (cdat_checksum(cdat_table, cdat_length))
goto err;
port->cdat.table = cdat_table + sizeof(__le32);
port->cdat.table = cdat_table;
port->cdat.length = cdat_length;
return;
err:
/* Don't leave table data allocated on error */
devm_kfree(dev, cdat_table);
dev_err(dev, "Failed to read/validate CDAT.\n");
}
EXPORT_SYMBOL_NS_GPL(read_cdat_data, CXL);
......
......@@ -284,6 +284,15 @@ static ssize_t interleave_ways_show(struct device *dev,
static DEVICE_ATTR_RO(interleave_ways);
static ssize_t qos_class_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct cxl_root_decoder *cxlrd = to_cxl_root_decoder(dev);
return sysfs_emit(buf, "%d\n", cxlrd->qos_class);
}
static DEVICE_ATTR_RO(qos_class);
static struct attribute *cxl_decoder_base_attrs[] = {
&dev_attr_start.attr,
&dev_attr_size.attr,
......@@ -303,6 +312,7 @@ static struct attribute *cxl_decoder_root_attrs[] = {
&dev_attr_cap_type2.attr,
&dev_attr_cap_type3.attr,
&dev_attr_target_list.attr,
&dev_attr_qos_class.attr,
SET_CXL_REGION_ATTR(create_pmem_region)
SET_CXL_REGION_ATTR(create_ram_region)
SET_CXL_REGION_ATTR(delete_region)
......@@ -1735,6 +1745,7 @@ struct cxl_root_decoder *cxl_root_decoder_alloc(struct cxl_port *port,
}
atomic_set(&cxlrd->region_id, rc);
cxlrd->qos_class = CXL_QOS_CLASS_INVALID;
return cxlrd;
}
EXPORT_SYMBOL_NS_GPL(cxl_root_decoder_alloc, CXL);
......
......@@ -328,6 +328,7 @@ enum cxl_decoder_type {
*/
#define CXL_DECODER_MAX_INTERLEAVE 16
#define CXL_QOS_CLASS_INVALID -1
/**
* struct cxl_decoder - Common CXL HDM Decoder Attributes
......@@ -439,6 +440,7 @@ typedef struct cxl_dport *(*cxl_calc_hb_fn)(struct cxl_root_decoder *cxlrd,
* @calc_hb: which host bridge covers the n'th position by granularity
* @platform_data: platform specific configuration data
* @range_lock: sync region autodiscovery by address range
* @qos_class: QoS performance class cookie
* @cxlsd: base cxl switch decoder
*/
struct cxl_root_decoder {
......@@ -447,6 +449,7 @@ struct cxl_root_decoder {
cxl_calc_hb_fn calc_hb;
void *platform_data;
struct mutex range_lock;
int qos_class;
struct cxl_switch_decoder cxlsd;
};
......
......@@ -62,6 +62,9 @@ static int cxl_switch_port_probe(struct cxl_port *port)
struct cxl_hdm *cxlhdm;
int rc;
/* Cache the data early to ensure is_visible() works */
read_cdat_data(port);
rc = devm_cxl_port_enumerate_dports(port);
if (rc < 0)
return rc;
......
......@@ -465,6 +465,9 @@ struct acpi_cdat_sslbe {
u16 reserved;
};
#define ACPI_CDAT_SSLBIS_US_PORT 0x0100
#define ACPI_CDAT_SSLBIS_ANY_PORT 0xffff
/*******************************************************************************
*
* CEDT - CXL Early Discovery Table
......
......@@ -15,6 +15,7 @@
#include <linux/mod_devicetable.h>
#include <linux/property.h>
#include <linux/uuid.h>
#include <linux/fw_table.h>
struct irq_domain;
struct irq_domain_ops;
......@@ -24,6 +25,16 @@ struct irq_domain_ops;
#endif
#include <acpi/acpi.h>
#ifdef CONFIG_ACPI_TABLE_LIB
#define EXPORT_SYMBOL_ACPI_LIB(x) EXPORT_SYMBOL_NS_GPL(x, ACPI)
#define __init_or_acpilib
#define __initdata_or_acpilib
#else
#define EXPORT_SYMBOL_ACPI_LIB(x)
#define __init_or_acpilib __init
#define __initdata_or_acpilib __initdata
#endif
#ifdef CONFIG_ACPI
#include <linux/list.h>
......@@ -119,21 +130,8 @@ enum acpi_address_range_id {
/* Table Handlers */
union acpi_subtable_headers {
struct acpi_subtable_header common;
struct acpi_hmat_structure hmat;
struct acpi_prmt_module_header prmt;
struct acpi_cedt_header cedt;
};
typedef int (*acpi_tbl_table_handler)(struct acpi_table_header *table);
typedef int (*acpi_tbl_entry_handler)(union acpi_subtable_headers *header,
const unsigned long end);
typedef int (*acpi_tbl_entry_handler_arg)(union acpi_subtable_headers *header,
void *arg, const unsigned long end);
/* Debugger support */
struct acpi_debugger_ops {
......@@ -207,14 +205,6 @@ static inline int acpi_debugger_notify_command_complete(void)
(!entry) || (unsigned long)entry + sizeof(*entry) > end || \
((struct acpi_subtable_header *)entry)->length < sizeof(*entry))
struct acpi_subtable_proc {
int id;
acpi_tbl_entry_handler handler;
acpi_tbl_entry_handler_arg handler_arg;
void *arg;
int count;
};
void __iomem *__acpi_map_table(unsigned long phys, unsigned long size);
void __acpi_unmap_table(void __iomem *map, unsigned long size);
int early_acpi_boot_init(void);
......@@ -229,16 +219,6 @@ void acpi_reserve_initial_tables (void);
void acpi_table_init_complete (void);
int acpi_table_init (void);
#ifdef CONFIG_ACPI_TABLE_LIB
#define EXPORT_SYMBOL_ACPI_LIB(x) EXPORT_SYMBOL_NS_GPL(x, ACPI)
#define __init_or_acpilib
#define __initdata_or_acpilib
#else
#define EXPORT_SYMBOL_ACPI_LIB(x)
#define __init_or_acpilib __init
#define __initdata_or_acpilib __initdata
#endif
int acpi_table_parse(char *id, acpi_tbl_table_handler handler);
int __init_or_acpilib acpi_table_parse_entries(char *id,
unsigned long table_size, int entry_id,
......
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* fw_tables.h - Parsing support for ACPI and ACPI-like tables provided by
* platform or device firmware
*
* Copyright (C) 2001 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
* Copyright (C) 2023 Intel Corp.
*/
#ifndef _FW_TABLE_H_
#define _FW_TABLE_H_
union acpi_subtable_headers;
typedef int (*acpi_tbl_entry_handler)(union acpi_subtable_headers *header,
const unsigned long end);
typedef int (*acpi_tbl_entry_handler_arg)(union acpi_subtable_headers *header,
void *arg, const unsigned long end);
struct acpi_subtable_proc {
int id;
acpi_tbl_entry_handler handler;
acpi_tbl_entry_handler_arg handler_arg;
void *arg;
int count;
};
#include <linux/acpi.h>
#include <acpi/acpi.h>
union acpi_subtable_headers {
struct acpi_subtable_header common;
struct acpi_hmat_structure hmat;
struct acpi_prmt_module_header prmt;
struct acpi_cedt_header cedt;
};
int acpi_parse_entries_array(char *id, unsigned long table_size,
struct acpi_table_header *table_header,
struct acpi_subtable_proc *proc,
int proc_num, unsigned int max_entries);
#endif
......@@ -764,3 +764,6 @@ config ASN1_ENCODER
config POLYNOMIAL
tristate
config FIRMWARE_TABLE
bool
......@@ -405,6 +405,8 @@ obj-$(CONFIG_SIPHASH_KUNIT_TEST) += siphash_kunit.o
obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o
obj-$(CONFIG_FIRMWARE_TABLE) += fw_table.o
# FORTIFY_SOURCE compile-time behavior tests
TEST_FORTIFY_SRCS = $(wildcard $(srctree)/$(src)/test_fortify/*-*.c)
TEST_FORTIFY_LOGS = $(patsubst $(srctree)/$(src)/%.c, %.log, $(TEST_FORTIFY_SRCS))
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* fw_tables.c - Parsing support for ACPI and ACPI-like tables provided by
* platform or device firmware
*
* Copyright (C) 2001 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
* Copyright (C) 2023 Intel Corp.
*/
#include <linux/errno.h>
#include <linux/fw_table.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/types.h>
enum acpi_subtable_type {
ACPI_SUBTABLE_COMMON,
ACPI_SUBTABLE_HMAT,
ACPI_SUBTABLE_PRMT,
ACPI_SUBTABLE_CEDT,
};
struct acpi_subtable_entry {
union acpi_subtable_headers *hdr;
enum acpi_subtable_type type;
};
static unsigned long __init_or_acpilib
acpi_get_entry_type(struct acpi_subtable_entry *entry)
{
switch (entry->type) {
case ACPI_SUBTABLE_COMMON:
return entry->hdr->common.type;
case ACPI_SUBTABLE_HMAT:
return entry->hdr->hmat.type;
case ACPI_SUBTABLE_PRMT:
return 0;
case ACPI_SUBTABLE_CEDT:
return entry->hdr->cedt.type;
}
return 0;
}
static unsigned long __init_or_acpilib
acpi_get_entry_length(struct acpi_subtable_entry *entry)
{
switch (entry->type) {
case ACPI_SUBTABLE_COMMON:
return entry->hdr->common.length;
case ACPI_SUBTABLE_HMAT:
return entry->hdr->hmat.length;
case ACPI_SUBTABLE_PRMT:
return entry->hdr->prmt.length;
case ACPI_SUBTABLE_CEDT:
return entry->hdr->cedt.length;
}
return 0;
}
static unsigned long __init_or_acpilib
acpi_get_subtable_header_length(struct acpi_subtable_entry *entry)
{
switch (entry->type) {
case ACPI_SUBTABLE_COMMON:
return sizeof(entry->hdr->common);
case ACPI_SUBTABLE_HMAT:
return sizeof(entry->hdr->hmat);
case ACPI_SUBTABLE_PRMT:
return sizeof(entry->hdr->prmt);
case ACPI_SUBTABLE_CEDT:
return sizeof(entry->hdr->cedt);
}
return 0;
}
static enum acpi_subtable_type __init_or_acpilib
acpi_get_subtable_type(char *id)
{
if (strncmp(id, ACPI_SIG_HMAT, 4) == 0)
return ACPI_SUBTABLE_HMAT;
if (strncmp(id, ACPI_SIG_PRMT, 4) == 0)
return ACPI_SUBTABLE_PRMT;
if (strncmp(id, ACPI_SIG_CEDT, 4) == 0)
return ACPI_SUBTABLE_CEDT;
return ACPI_SUBTABLE_COMMON;
}
static __init_or_acpilib bool has_handler(struct acpi_subtable_proc *proc)
{
return proc->handler || proc->handler_arg;
}
static __init_or_acpilib int call_handler(struct acpi_subtable_proc *proc,
union acpi_subtable_headers *hdr,
unsigned long end)
{
if (proc->handler)
return proc->handler(hdr, end);
if (proc->handler_arg)
return proc->handler_arg(hdr, proc->arg, end);
return -EINVAL;
}
/**
* acpi_parse_entries_array - for each proc_num find a suitable subtable
*
* @id: table id (for debugging purposes)
* @table_size: size of the root table
* @table_header: where does the table start?
* @proc: array of acpi_subtable_proc struct containing entry id
* and associated handler with it
* @proc_num: how big proc is?
* @max_entries: how many entries can we process?
*
* For each proc_num find a subtable with proc->id and run proc->handler
* on it. Assumption is that there's only single handler for particular
* entry id.
*
* The table_size is not the size of the complete ACPI table (the length
* field in the header struct), but only the size of the root table; i.e.,
* the offset from the very first byte of the complete ACPI table, to the
* first byte of the very first subtable.
*
* On success returns sum of all matching entries for all proc handlers.
* Otherwise, -ENODEV or -EINVAL is returned.
*/
int __init_or_acpilib
acpi_parse_entries_array(char *id, unsigned long table_size,
struct acpi_table_header *table_header,
struct acpi_subtable_proc *proc,
int proc_num, unsigned int max_entries)
{
unsigned long table_end, subtable_len, entry_len;
struct acpi_subtable_entry entry;
int count = 0;
int errs = 0;
int i;
table_end = (unsigned long)table_header + table_header->length;
/* Parse all entries looking for a match. */
entry.type = acpi_get_subtable_type(id);
entry.hdr = (union acpi_subtable_headers *)
((unsigned long)table_header + table_size);
subtable_len = acpi_get_subtable_header_length(&entry);
while (((unsigned long)entry.hdr) + subtable_len < table_end) {
if (max_entries && count >= max_entries)
break;
for (i = 0; i < proc_num; i++) {
if (acpi_get_entry_type(&entry) != proc[i].id)
continue;
if (!has_handler(&proc[i]) ||
(!errs &&
call_handler(&proc[i], entry.hdr, table_end))) {
errs++;
continue;
}
proc[i].count++;
break;
}
if (i != proc_num)
count++;
/*
* If entry->length is 0, break from this loop to avoid
* infinite loop.
*/
entry_len = acpi_get_entry_length(&entry);
if (entry_len == 0) {
pr_err("[%4.4s:0x%02x] Invalid zero length\n", id, proc->id);
return -EINVAL;
}
entry.hdr = (union acpi_subtable_headers *)
((unsigned long)entry.hdr + entry_len);
}
if (max_entries && count > max_entries) {
pr_warn("[%4.4s:0x%02x] found the maximum %i entries\n",
id, proc->id, count);
}
return errs ? -EINVAL : count;
}
EXPORT_SYMBOL_GPL(acpi_parse_entries_array);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment