mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 21:38:32 +08:00
cxl for 5.17
- Rework ACPI sub-table infrastructure to optionally be used outside of __init scenarios and use it for CEDT.CFMWS sub-table parsing. - Add support for extending num_possible_nodes by the potential hotplug CXL memory ranges - Extend tools/testing/cxl with mock memory device health information - Fix a module-reload workqueue race - Fix excessive stack-frame usage - Rename the driver context data structure from "cxl_mem" since that name collides with a proposed driver name - Use EXPORT_SYMBOL_NS_GPL instead of -DDEFAULT_SYMBOL_NAMESPACE at build time -----BEGIN PGP SIGNATURE----- iHUEABYIAB0WIQSbo+XnGs+rwLz9XGXfioYZHlFsZwUCYd38+QAKCRDfioYZHlFs Z5ijAP9w56E/lf/lQOz3NFG/yCJYMHnIb2jFe2LUqXwRuEdg1QEArMwJo0YDgwmi ACFnIbKTf7qG0PxBkJT83b9CJ2xOsA8= =G5xP -----END PGP SIGNATURE----- Merge tag 'cxl-for-5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl Pull CXL (Compute Express Link) updates from Dan Williams: "The highlight is initial support for CXL memory hotplug. The static NUMA node (ACPI SRAT Physical Address to Proximity Domain) information known to platform firmware is extended to support the potential performance-class / memory-target nodes dynamically created from available CXL memory device capacity. New unit test infrastructure is added for validating health information payloads. Fixes to module reload stress and stack usage from exposure in -next are included. A symbol rename and some other miscellaneous fixups are included as well. Summary: - Rework ACPI sub-table infrastructure to optionally be used outside of __init scenarios and use it for CEDT.CFMWS sub-table parsing. - Add support for extending num_possible_nodes by the potential hotplug CXL memory ranges - Extend tools/testing/cxl with mock memory device health information - Fix a module-reload workqueue race - Fix excessive stack-frame usage - Rename the driver context data structure from "cxl_mem" since that name collides with a proposed driver name - Use EXPORT_SYMBOL_NS_GPL instead of -DDEFAULT_SYMBOL_NAMESPACE at build time" * tag 'cxl-for-5.17' of git://git.kernel.org/pub/scm/linux/kernel/git/cxl/cxl: cxl/core: Remove cxld_const_init in cxl_decoder_alloc() cxl/pmem: Fix module reload vs workqueue state ACPI: NUMA: Add a node and memblk for each CFMWS not in SRAT cxl/test: Mock acpi_table_parse_cedt() cxl/acpi: Convert CFMWS parsing to ACPI sub-table helpers ACPI: Add a context argument for table parsing handlers ACPI: Teach ACPI table parsing about the CEDT header format ACPI: Keep sub-table parsing infrastructure available for modules tools/testing/cxl: add mock output for the GET_HEALTH_INFO command cxl/memdev: Remove unused cxlmd field cxl/core: Convert to EXPORT_SYMBOL_NS_GPL cxl/memdev: Change cxl_mem to a more descriptive name cxl/mbox: Remove bad comment cxl/pmem: Fix reference counting for delayed work
This commit is contained in:
commit
362f533a2a
@ -59,6 +59,9 @@ config ACPI_SYSTEM_POWER_STATES_SUPPORT
|
||||
config ACPI_CCA_REQUIRED
|
||||
bool
|
||||
|
||||
config ACPI_TABLE_LIB
|
||||
bool
|
||||
|
||||
config ACPI_DEBUGGER
|
||||
bool "AML debugger interface"
|
||||
select ACPI_DEBUG
|
||||
|
@ -297,6 +297,47 @@ out_err_bad_srat:
|
||||
out_err:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int __init acpi_parse_cfmws(union acpi_subtable_headers *header,
|
||||
void *arg, const unsigned long table_end)
|
||||
{
|
||||
struct acpi_cedt_cfmws *cfmws;
|
||||
int *fake_pxm = arg;
|
||||
u64 start, end;
|
||||
int node;
|
||||
|
||||
cfmws = (struct acpi_cedt_cfmws *)header;
|
||||
start = cfmws->base_hpa;
|
||||
end = cfmws->base_hpa + cfmws->window_size;
|
||||
|
||||
/* Skip if the SRAT already described the NUMA details for this HPA */
|
||||
node = phys_to_target_node(start);
|
||||
if (node != NUMA_NO_NODE)
|
||||
return 0;
|
||||
|
||||
node = acpi_map_pxm_to_node(*fake_pxm);
|
||||
|
||||
if (node == NUMA_NO_NODE) {
|
||||
pr_err("ACPI NUMA: Too many proximity domains while processing CFMWS.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (numa_add_memblk(node, start, end) < 0) {
|
||||
/* CXL driver must handle the NUMA_NO_NODE case */
|
||||
pr_warn("ACPI NUMA: Failed to add memblk for CFMWS node %d [mem %#llx-%#llx]\n",
|
||||
node, start, end);
|
||||
}
|
||||
|
||||
/* Set the next available fake_pxm value */
|
||||
(*fake_pxm)++;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int __init acpi_parse_cfmws(union acpi_subtable_headers *header,
|
||||
void *arg, const unsigned long table_end)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* defined(CONFIG_X86) || defined (CONFIG_ARM64) */
|
||||
|
||||
static int __init acpi_parse_slit(struct acpi_table_header *table)
|
||||
@ -441,7 +482,7 @@ acpi_table_parse_srat(enum acpi_srat_type id,
|
||||
|
||||
int __init acpi_numa_init(void)
|
||||
{
|
||||
int cnt = 0;
|
||||
int i, fake_pxm, cnt = 0;
|
||||
|
||||
if (acpi_disabled)
|
||||
return -EINVAL;
|
||||
@ -477,6 +518,22 @@ int __init acpi_numa_init(void)
|
||||
/* SLIT: System Locality Information Table */
|
||||
acpi_table_parse(ACPI_SIG_SLIT, acpi_parse_slit);
|
||||
|
||||
/*
|
||||
* CXL Fixed Memory Window Structures (CFMWS) must be parsed
|
||||
* after the SRAT. Create NUMA Nodes for CXL memory ranges that
|
||||
* are defined in the CFMWS and not already defined in the SRAT.
|
||||
* Initialize a fake_pxm as the first available PXM to emulate.
|
||||
*/
|
||||
|
||||
/* fake_pxm is the next unused PXM value after SRAT parsing */
|
||||
for (i = 0, fake_pxm = -1; i < MAX_NUMNODES - 1; i++) {
|
||||
if (node_to_pxm_map[i] > fake_pxm)
|
||||
fake_pxm = node_to_pxm_map[i];
|
||||
}
|
||||
fake_pxm++;
|
||||
acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, acpi_parse_cfmws,
|
||||
&fake_pxm);
|
||||
|
||||
if (cnt < 0)
|
||||
return cnt;
|
||||
else if (!parsed_numa_memblks)
|
||||
|
@ -35,12 +35,13 @@ static char *mps_inti_flags_trigger[] = { "dfl", "edge", "res", "level" };
|
||||
|
||||
static struct acpi_table_desc initial_tables[ACPI_MAX_TABLES] __initdata;
|
||||
|
||||
static int acpi_apic_instance __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 {
|
||||
@ -52,7 +53,7 @@ struct acpi_subtable_entry {
|
||||
* Disable table checksum verification for the early stage due to the size
|
||||
* limitation of the current x86 early mapping implementation.
|
||||
*/
|
||||
static bool acpi_verify_table_checksum __initdata = false;
|
||||
static bool acpi_verify_table_checksum __initdata_or_acpilib = false;
|
||||
|
||||
void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
|
||||
{
|
||||
@ -216,7 +217,7 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
|
||||
}
|
||||
}
|
||||
|
||||
static unsigned long __init
|
||||
static unsigned long __init_or_acpilib
|
||||
acpi_get_entry_type(struct acpi_subtable_entry *entry)
|
||||
{
|
||||
switch (entry->type) {
|
||||
@ -226,11 +227,13 @@ acpi_get_entry_type(struct acpi_subtable_entry *entry)
|
||||
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
|
||||
static unsigned long __init_or_acpilib
|
||||
acpi_get_entry_length(struct acpi_subtable_entry *entry)
|
||||
{
|
||||
switch (entry->type) {
|
||||
@ -240,11 +243,13 @@ acpi_get_entry_length(struct acpi_subtable_entry *entry)
|
||||
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
|
||||
static unsigned long __init_or_acpilib
|
||||
acpi_get_subtable_header_length(struct acpi_subtable_entry *entry)
|
||||
{
|
||||
switch (entry->type) {
|
||||
@ -254,20 +259,40 @@ acpi_get_subtable_header_length(struct acpi_subtable_entry *entry)
|
||||
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
|
||||
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
|
||||
*
|
||||
@ -291,10 +316,10 @@ acpi_get_subtable_type(char *id)
|
||||
* On success returns sum of all matching entries for all proc handlers.
|
||||
* Otherwise, -ENODEV or -EINVAL is returned.
|
||||
*/
|
||||
static int __init 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)
|
||||
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;
|
||||
@ -318,8 +343,9 @@ static int __init acpi_parse_entries_array(char *id, unsigned long table_size,
|
||||
for (i = 0; i < proc_num; i++) {
|
||||
if (acpi_get_entry_type(&entry) != proc[i].id)
|
||||
continue;
|
||||
if (!proc[i].handler ||
|
||||
(!errs && proc[i].handler(entry.hdr, table_end))) {
|
||||
if (!has_handler(&proc[i]) ||
|
||||
(!errs &&
|
||||
call_handler(&proc[i], entry.hdr, table_end))) {
|
||||
errs++;
|
||||
continue;
|
||||
}
|
||||
@ -352,10 +378,9 @@ static int __init acpi_parse_entries_array(char *id, unsigned long table_size,
|
||||
return errs ? -EINVAL : count;
|
||||
}
|
||||
|
||||
int __init acpi_table_parse_entries_array(char *id,
|
||||
unsigned long table_size,
|
||||
struct acpi_subtable_proc *proc, int proc_num,
|
||||
unsigned int max_entries)
|
||||
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)
|
||||
{
|
||||
struct acpi_table_header *table_header = NULL;
|
||||
int count;
|
||||
@ -386,21 +411,41 @@ int __init acpi_table_parse_entries_array(char *id,
|
||||
return count;
|
||||
}
|
||||
|
||||
int __init acpi_table_parse_entries(char *id,
|
||||
unsigned long table_size,
|
||||
int entry_id,
|
||||
acpi_tbl_entry_handler handler,
|
||||
unsigned int max_entries)
|
||||
static int __init_or_acpilib __acpi_table_parse_entries(
|
||||
char *id, unsigned long table_size, int entry_id,
|
||||
acpi_tbl_entry_handler handler, acpi_tbl_entry_handler_arg handler_arg,
|
||||
void *arg, unsigned int max_entries)
|
||||
{
|
||||
struct acpi_subtable_proc proc = {
|
||||
.id = entry_id,
|
||||
.handler = handler,
|
||||
.handler_arg = handler_arg,
|
||||
.arg = arg,
|
||||
};
|
||||
|
||||
return acpi_table_parse_entries_array(id, table_size, &proc, 1,
|
||||
max_entries);
|
||||
}
|
||||
|
||||
int __init_or_acpilib
|
||||
acpi_table_parse_cedt(enum acpi_cedt_type id,
|
||||
acpi_tbl_entry_handler_arg handler_arg, void *arg)
|
||||
{
|
||||
return __acpi_table_parse_entries(ACPI_SIG_CEDT,
|
||||
sizeof(struct acpi_table_cedt), id,
|
||||
NULL, handler_arg, arg, 0);
|
||||
}
|
||||
EXPORT_SYMBOL_ACPI_LIB(acpi_table_parse_cedt);
|
||||
|
||||
int __init acpi_table_parse_entries(char *id, unsigned long table_size,
|
||||
int entry_id,
|
||||
acpi_tbl_entry_handler handler,
|
||||
unsigned int max_entries)
|
||||
{
|
||||
return __acpi_table_parse_entries(id, table_size, entry_id, handler,
|
||||
NULL, NULL, max_entries);
|
||||
}
|
||||
|
||||
int __init acpi_table_parse_madt(enum acpi_madt_type id,
|
||||
acpi_tbl_entry_handler handler, unsigned int max_entries)
|
||||
{
|
||||
|
@ -51,6 +51,7 @@ config CXL_ACPI
|
||||
tristate "CXL ACPI: Platform Support"
|
||||
depends on ACPI
|
||||
default CXL_BUS
|
||||
select ACPI_TABLE_LIB
|
||||
help
|
||||
Enable support for host managed device memory (HDM) resources
|
||||
published by a platform's ACPI CXL memory layout description. See
|
||||
|
@ -8,8 +8,6 @@
|
||||
#include <linux/pci.h>
|
||||
#include "cxl.h"
|
||||
|
||||
static struct acpi_table_header *acpi_cedt;
|
||||
|
||||
/* Encode defined in CXL 2.0 8.2.5.12.7 HDM Decoder Control Register */
|
||||
#define CFMWS_INTERLEAVE_WAYS(x) (1 << (x)->interleave_ways)
|
||||
#define CFMWS_INTERLEAVE_GRANULARITY(x) ((x)->granularity + 8)
|
||||
@ -74,134 +72,64 @@ static int cxl_acpi_cfmws_verify(struct device *dev,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cxl_add_cfmws_decoders(struct device *dev,
|
||||
struct cxl_port *root_port)
|
||||
struct cxl_cfmws_context {
|
||||
struct device *dev;
|
||||
struct cxl_port *root_port;
|
||||
};
|
||||
|
||||
static int cxl_parse_cfmws(union acpi_subtable_headers *header, void *arg,
|
||||
const unsigned long end)
|
||||
{
|
||||
int target_map[CXL_DECODER_MAX_INTERLEAVE];
|
||||
struct cxl_cfmws_context *ctx = arg;
|
||||
struct cxl_port *root_port = ctx->root_port;
|
||||
struct device *dev = ctx->dev;
|
||||
struct acpi_cedt_cfmws *cfmws;
|
||||
struct cxl_decoder *cxld;
|
||||
acpi_size len, cur = 0;
|
||||
void *cedt_subtable;
|
||||
int rc;
|
||||
int rc, i;
|
||||
|
||||
len = acpi_cedt->length - sizeof(*acpi_cedt);
|
||||
cedt_subtable = acpi_cedt + 1;
|
||||
cfmws = (struct acpi_cedt_cfmws *) header;
|
||||
|
||||
while (cur < len) {
|
||||
struct acpi_cedt_header *c = cedt_subtable + cur;
|
||||
int i;
|
||||
|
||||
if (c->type != ACPI_CEDT_TYPE_CFMWS) {
|
||||
cur += c->length;
|
||||
continue;
|
||||
}
|
||||
|
||||
cfmws = cedt_subtable + cur;
|
||||
|
||||
if (cfmws->header.length < sizeof(*cfmws)) {
|
||||
dev_warn_once(dev,
|
||||
"CFMWS entry skipped:invalid length:%u\n",
|
||||
cfmws->header.length);
|
||||
cur += c->length;
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = cxl_acpi_cfmws_verify(dev, cfmws);
|
||||
if (rc) {
|
||||
dev_err(dev, "CFMWS range %#llx-%#llx not registered\n",
|
||||
cfmws->base_hpa, cfmws->base_hpa +
|
||||
cfmws->window_size - 1);
|
||||
cur += c->length;
|
||||
continue;
|
||||
}
|
||||
|
||||
for (i = 0; i < CFMWS_INTERLEAVE_WAYS(cfmws); i++)
|
||||
target_map[i] = cfmws->interleave_targets[i];
|
||||
|
||||
cxld = cxl_decoder_alloc(root_port,
|
||||
CFMWS_INTERLEAVE_WAYS(cfmws));
|
||||
if (IS_ERR(cxld))
|
||||
goto next;
|
||||
|
||||
cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
|
||||
cxld->target_type = CXL_DECODER_EXPANDER;
|
||||
cxld->range = (struct range) {
|
||||
.start = cfmws->base_hpa,
|
||||
.end = cfmws->base_hpa + cfmws->window_size - 1,
|
||||
};
|
||||
cxld->interleave_ways = CFMWS_INTERLEAVE_WAYS(cfmws);
|
||||
cxld->interleave_granularity =
|
||||
CFMWS_INTERLEAVE_GRANULARITY(cfmws);
|
||||
|
||||
rc = cxl_decoder_add(cxld, target_map);
|
||||
if (rc)
|
||||
put_device(&cxld->dev);
|
||||
else
|
||||
rc = cxl_decoder_autoremove(dev, cxld);
|
||||
if (rc) {
|
||||
dev_err(dev, "Failed to add decoder for %#llx-%#llx\n",
|
||||
cfmws->base_hpa, cfmws->base_hpa +
|
||||
cfmws->window_size - 1);
|
||||
goto next;
|
||||
}
|
||||
dev_dbg(dev, "add: %s range %#llx-%#llx\n",
|
||||
dev_name(&cxld->dev), cfmws->base_hpa,
|
||||
rc = cxl_acpi_cfmws_verify(dev, cfmws);
|
||||
if (rc) {
|
||||
dev_err(dev, "CFMWS range %#llx-%#llx not registered\n",
|
||||
cfmws->base_hpa,
|
||||
cfmws->base_hpa + cfmws->window_size - 1);
|
||||
next:
|
||||
cur += c->length;
|
||||
}
|
||||
}
|
||||
|
||||
static struct acpi_cedt_chbs *cxl_acpi_match_chbs(struct device *dev, u32 uid)
|
||||
{
|
||||
struct acpi_cedt_chbs *chbs, *chbs_match = NULL;
|
||||
acpi_size len, cur = 0;
|
||||
void *cedt_subtable;
|
||||
|
||||
len = acpi_cedt->length - sizeof(*acpi_cedt);
|
||||
cedt_subtable = acpi_cedt + 1;
|
||||
|
||||
while (cur < len) {
|
||||
struct acpi_cedt_header *c = cedt_subtable + cur;
|
||||
|
||||
if (c->type != ACPI_CEDT_TYPE_CHBS) {
|
||||
cur += c->length;
|
||||
continue;
|
||||
}
|
||||
|
||||
chbs = cedt_subtable + cur;
|
||||
|
||||
if (chbs->header.length < sizeof(*chbs)) {
|
||||
dev_warn_once(dev,
|
||||
"CHBS entry skipped: invalid length:%u\n",
|
||||
chbs->header.length);
|
||||
cur += c->length;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chbs->uid != uid) {
|
||||
cur += c->length;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (chbs_match) {
|
||||
dev_warn_once(dev,
|
||||
"CHBS entry skipped: duplicate UID:%u\n",
|
||||
uid);
|
||||
cur += c->length;
|
||||
continue;
|
||||
}
|
||||
|
||||
chbs_match = chbs;
|
||||
cur += c->length;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return chbs_match ? chbs_match : ERR_PTR(-ENODEV);
|
||||
}
|
||||
for (i = 0; i < CFMWS_INTERLEAVE_WAYS(cfmws); i++)
|
||||
target_map[i] = cfmws->interleave_targets[i];
|
||||
|
||||
static resource_size_t get_chbcr(struct acpi_cedt_chbs *chbs)
|
||||
{
|
||||
return IS_ERR(chbs) ? CXL_RESOURCE_NONE : chbs->base;
|
||||
cxld = cxl_decoder_alloc(root_port, CFMWS_INTERLEAVE_WAYS(cfmws));
|
||||
if (IS_ERR(cxld))
|
||||
return 0;
|
||||
|
||||
cxld->flags = cfmws_to_decoder_flags(cfmws->restrictions);
|
||||
cxld->target_type = CXL_DECODER_EXPANDER;
|
||||
cxld->range = (struct range){
|
||||
.start = cfmws->base_hpa,
|
||||
.end = cfmws->base_hpa + cfmws->window_size - 1,
|
||||
};
|
||||
cxld->interleave_ways = CFMWS_INTERLEAVE_WAYS(cfmws);
|
||||
cxld->interleave_granularity = CFMWS_INTERLEAVE_GRANULARITY(cfmws);
|
||||
|
||||
rc = cxl_decoder_add(cxld, target_map);
|
||||
if (rc)
|
||||
put_device(&cxld->dev);
|
||||
else
|
||||
rc = cxl_decoder_autoremove(dev, cxld);
|
||||
if (rc) {
|
||||
dev_err(dev, "Failed to add decoder for %#llx-%#llx\n",
|
||||
cfmws->base_hpa,
|
||||
cfmws->base_hpa + cfmws->window_size - 1);
|
||||
return 0;
|
||||
}
|
||||
dev_dbg(dev, "add: %s node: %d range %#llx-%#llx\n",
|
||||
dev_name(&cxld->dev), phys_to_target_node(cxld->range.start),
|
||||
cfmws->base_hpa, cfmws->base_hpa + cfmws->window_size - 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
__mock int match_add_root_ports(struct pci_dev *pdev, void *data)
|
||||
@ -355,12 +283,36 @@ static int add_host_bridge_uport(struct device *match, void *arg)
|
||||
return rc;
|
||||
}
|
||||
|
||||
struct cxl_chbs_context {
|
||||
struct device *dev;
|
||||
unsigned long long uid;
|
||||
resource_size_t chbcr;
|
||||
};
|
||||
|
||||
static int cxl_get_chbcr(union acpi_subtable_headers *header, void *arg,
|
||||
const unsigned long end)
|
||||
{
|
||||
struct cxl_chbs_context *ctx = arg;
|
||||
struct acpi_cedt_chbs *chbs;
|
||||
|
||||
if (ctx->chbcr)
|
||||
return 0;
|
||||
|
||||
chbs = (struct acpi_cedt_chbs *) header;
|
||||
|
||||
if (ctx->uid != chbs->uid)
|
||||
return 0;
|
||||
ctx->chbcr = chbs->base;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int add_host_bridge_dport(struct device *match, void *arg)
|
||||
{
|
||||
int rc;
|
||||
acpi_status status;
|
||||
unsigned long long uid;
|
||||
struct acpi_cedt_chbs *chbs;
|
||||
struct cxl_chbs_context ctx;
|
||||
struct cxl_port *root_port = arg;
|
||||
struct device *host = root_port->dev.parent;
|
||||
struct acpi_device *bridge = to_cxl_host_bridge(host, match);
|
||||
@ -376,14 +328,19 @@ static int add_host_bridge_dport(struct device *match, void *arg)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
chbs = cxl_acpi_match_chbs(host, uid);
|
||||
if (IS_ERR(chbs)) {
|
||||
ctx = (struct cxl_chbs_context) {
|
||||
.dev = host,
|
||||
.uid = uid,
|
||||
};
|
||||
acpi_table_parse_cedt(ACPI_CEDT_TYPE_CHBS, cxl_get_chbcr, &ctx);
|
||||
|
||||
if (ctx.chbcr == 0) {
|
||||
dev_warn(host, "No CHBS found for Host Bridge: %s\n",
|
||||
dev_name(match));
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = cxl_add_dport(root_port, match, uid, get_chbcr(chbs));
|
||||
rc = cxl_add_dport(root_port, match, uid, ctx.chbcr);
|
||||
if (rc) {
|
||||
dev_err(host, "failed to add downstream port: %s\n",
|
||||
dev_name(match));
|
||||
@ -417,40 +374,29 @@ static int add_root_nvdimm_bridge(struct device *match, void *data)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static u32 cedt_instance(struct platform_device *pdev)
|
||||
{
|
||||
const bool *native_acpi0017 = acpi_device_get_match_data(&pdev->dev);
|
||||
|
||||
if (native_acpi0017 && *native_acpi0017)
|
||||
return 0;
|
||||
|
||||
/* for cxl_test request a non-canonical instance */
|
||||
return U32_MAX;
|
||||
}
|
||||
|
||||
static int cxl_acpi_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc;
|
||||
acpi_status status;
|
||||
struct cxl_port *root_port;
|
||||
struct device *host = &pdev->dev;
|
||||
struct acpi_device *adev = ACPI_COMPANION(host);
|
||||
struct cxl_cfmws_context ctx;
|
||||
|
||||
root_port = devm_cxl_add_port(host, host, CXL_RESOURCE_NONE, NULL);
|
||||
if (IS_ERR(root_port))
|
||||
return PTR_ERR(root_port);
|
||||
dev_dbg(host, "add: %s\n", dev_name(&root_port->dev));
|
||||
|
||||
status = acpi_get_table(ACPI_SIG_CEDT, cedt_instance(pdev), &acpi_cedt);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENXIO;
|
||||
|
||||
rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
|
||||
add_host_bridge_dport);
|
||||
if (rc)
|
||||
goto out;
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
cxl_add_cfmws_decoders(host, root_port);
|
||||
ctx = (struct cxl_cfmws_context) {
|
||||
.dev = host,
|
||||
.root_port = root_port,
|
||||
};
|
||||
acpi_table_parse_cedt(ACPI_CEDT_TYPE_CFMWS, cxl_parse_cfmws, &ctx);
|
||||
|
||||
/*
|
||||
* Root level scanned with host-bridge as dports, now scan host-bridges
|
||||
@ -458,24 +404,20 @@ static int cxl_acpi_probe(struct platform_device *pdev)
|
||||
*/
|
||||
rc = bus_for_each_dev(adev->dev.bus, NULL, root_port,
|
||||
add_host_bridge_uport);
|
||||
if (rc)
|
||||
goto out;
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (IS_ENABLED(CONFIG_CXL_PMEM))
|
||||
rc = device_for_each_child(&root_port->dev, root_port,
|
||||
add_root_nvdimm_bridge);
|
||||
|
||||
out:
|
||||
acpi_put_table(acpi_cedt);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool native_acpi0017 = true;
|
||||
|
||||
static const struct acpi_device_id cxl_acpi_ids[] = {
|
||||
{ "ACPI0017", (unsigned long) &native_acpi0017 },
|
||||
{ "ACPI0017" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, cxl_acpi_ids);
|
||||
@ -491,3 +433,4 @@ static struct platform_driver cxl_acpi_driver = {
|
||||
module_platform_driver(cxl_acpi_driver);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_IMPORT_NS(CXL);
|
||||
MODULE_IMPORT_NS(ACPI);
|
||||
|
@ -1,7 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
obj-$(CONFIG_CXL_BUS) += cxl_core.o
|
||||
|
||||
ccflags-y += -DDEFAULT_SYMBOL_NAMESPACE=CXL -I$(srctree)/drivers/cxl
|
||||
ccflags-y += -I$(srctree)/drivers/cxl
|
||||
cxl_core-y := bus.o
|
||||
cxl_core-y += pmem.o
|
||||
cxl_core-y += regs.o
|
||||
|
@ -200,7 +200,7 @@ bool is_root_decoder(struct device *dev)
|
||||
{
|
||||
return dev->type == &cxl_decoder_root_type;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(is_root_decoder);
|
||||
EXPORT_SYMBOL_NS_GPL(is_root_decoder, CXL);
|
||||
|
||||
struct cxl_decoder *to_cxl_decoder(struct device *dev)
|
||||
{
|
||||
@ -209,7 +209,7 @@ struct cxl_decoder *to_cxl_decoder(struct device *dev)
|
||||
return NULL;
|
||||
return container_of(dev, struct cxl_decoder, dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(to_cxl_decoder);
|
||||
EXPORT_SYMBOL_NS_GPL(to_cxl_decoder, CXL);
|
||||
|
||||
static void cxl_dport_release(struct cxl_dport *dport)
|
||||
{
|
||||
@ -376,7 +376,7 @@ err:
|
||||
put_device(dev);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_cxl_add_port);
|
||||
EXPORT_SYMBOL_NS_GPL(devm_cxl_add_port, CXL);
|
||||
|
||||
static struct cxl_dport *find_dport(struct cxl_port *port, int id)
|
||||
{
|
||||
@ -451,7 +451,7 @@ err:
|
||||
cxl_dport_release(dport);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cxl_add_dport);
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_add_dport, CXL);
|
||||
|
||||
static int decoder_populate_targets(struct cxl_decoder *cxld,
|
||||
struct cxl_port *port, int *target_map)
|
||||
@ -485,9 +485,7 @@ out_unlock:
|
||||
|
||||
struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
|
||||
{
|
||||
struct cxl_decoder *cxld, cxld_const_init = {
|
||||
.nr_targets = nr_targets,
|
||||
};
|
||||
struct cxl_decoder *cxld;
|
||||
struct device *dev;
|
||||
int rc = 0;
|
||||
|
||||
@ -497,13 +495,13 @@ struct cxl_decoder *cxl_decoder_alloc(struct cxl_port *port, int nr_targets)
|
||||
cxld = kzalloc(struct_size(cxld, target, nr_targets), GFP_KERNEL);
|
||||
if (!cxld)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
memcpy(cxld, &cxld_const_init, sizeof(cxld_const_init));
|
||||
|
||||
rc = ida_alloc(&port->decoder_ida, GFP_KERNEL);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
|
||||
cxld->id = rc;
|
||||
cxld->nr_targets = nr_targets;
|
||||
dev = &cxld->dev;
|
||||
device_initialize(dev);
|
||||
device_set_pm_not_required(dev);
|
||||
@ -521,7 +519,7 @@ err:
|
||||
kfree(cxld);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cxl_decoder_alloc);
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_decoder_alloc, CXL);
|
||||
|
||||
int cxl_decoder_add(struct cxl_decoder *cxld, int *target_map)
|
||||
{
|
||||
@ -550,7 +548,7 @@ int cxl_decoder_add(struct cxl_decoder *cxld, int *target_map)
|
||||
|
||||
return device_add(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cxl_decoder_add);
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_decoder_add, CXL);
|
||||
|
||||
static void cxld_unregister(void *dev)
|
||||
{
|
||||
@ -561,7 +559,7 @@ int cxl_decoder_autoremove(struct device *host, struct cxl_decoder *cxld)
|
||||
{
|
||||
return devm_add_action_or_reset(host, cxld_unregister, &cxld->dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cxl_decoder_autoremove);
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_decoder_autoremove, CXL);
|
||||
|
||||
/**
|
||||
* __cxl_driver_register - register a driver for the cxl bus
|
||||
@ -594,13 +592,13 @@ int __cxl_driver_register(struct cxl_driver *cxl_drv, struct module *owner,
|
||||
|
||||
return driver_register(&cxl_drv->drv);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__cxl_driver_register);
|
||||
EXPORT_SYMBOL_NS_GPL(__cxl_driver_register, CXL);
|
||||
|
||||
void cxl_driver_unregister(struct cxl_driver *cxl_drv)
|
||||
{
|
||||
driver_unregister(&cxl_drv->drv);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cxl_driver_unregister);
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_driver_unregister, CXL);
|
||||
|
||||
static int cxl_device_id(struct device *dev)
|
||||
{
|
||||
@ -642,7 +640,7 @@ struct bus_type cxl_bus_type = {
|
||||
.probe = cxl_bus_probe,
|
||||
.remove = cxl_bus_remove,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(cxl_bus_type);
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_bus_type, CXL);
|
||||
|
||||
static __init int cxl_core_init(void)
|
||||
{
|
||||
|
@ -128,8 +128,8 @@ static struct cxl_mem_command *cxl_mem_find_command(u16 opcode)
|
||||
}
|
||||
|
||||
/**
|
||||
* cxl_mem_mbox_send_cmd() - Send a mailbox command to a memory device.
|
||||
* @cxlm: The CXL memory device to communicate with.
|
||||
* cxl_mbox_send_cmd() - Send a mailbox command to a device.
|
||||
* @cxlds: The device data for the operation
|
||||
* @opcode: Opcode for the mailbox command.
|
||||
* @in: The input payload for the mailbox command.
|
||||
* @in_size: The length of the input payload
|
||||
@ -148,11 +148,9 @@ static struct cxl_mem_command *cxl_mem_find_command(u16 opcode)
|
||||
* Mailbox commands may execute successfully yet the device itself reported an
|
||||
* error. While this distinction can be useful for commands from userspace, the
|
||||
* kernel will only be able to use results when both are successful.
|
||||
*
|
||||
* See __cxl_mem_mbox_send_cmd()
|
||||
*/
|
||||
int cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, u16 opcode, void *in,
|
||||
size_t in_size, void *out, size_t out_size)
|
||||
int cxl_mbox_send_cmd(struct cxl_dev_state *cxlds, u16 opcode, void *in,
|
||||
size_t in_size, void *out, size_t out_size)
|
||||
{
|
||||
const struct cxl_mem_command *cmd = cxl_mem_find_command(opcode);
|
||||
struct cxl_mbox_cmd mbox_cmd = {
|
||||
@ -164,10 +162,10 @@ int cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, u16 opcode, void *in,
|
||||
};
|
||||
int rc;
|
||||
|
||||
if (out_size > cxlm->payload_size)
|
||||
if (out_size > cxlds->payload_size)
|
||||
return -E2BIG;
|
||||
|
||||
rc = cxlm->mbox_send(cxlm, &mbox_cmd);
|
||||
rc = cxlds->mbox_send(cxlds, &mbox_cmd);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
@ -184,7 +182,7 @@ int cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, u16 opcode, void *in,
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cxl_mem_mbox_send_cmd);
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_mbox_send_cmd, CXL);
|
||||
|
||||
static bool cxl_mem_raw_command_allowed(u16 opcode)
|
||||
{
|
||||
@ -211,7 +209,7 @@ static bool cxl_mem_raw_command_allowed(u16 opcode)
|
||||
|
||||
/**
|
||||
* cxl_validate_cmd_from_user() - Check fields for CXL_MEM_SEND_COMMAND.
|
||||
* @cxlm: &struct cxl_mem device whose mailbox will be used.
|
||||
* @cxlds: The device data for the operation
|
||||
* @send_cmd: &struct cxl_send_command copied in from userspace.
|
||||
* @out_cmd: Sanitized and populated &struct cxl_mem_command.
|
||||
*
|
||||
@ -228,7 +226,7 @@ static bool cxl_mem_raw_command_allowed(u16 opcode)
|
||||
*
|
||||
* See handle_mailbox_cmd_from_user()
|
||||
*/
|
||||
static int cxl_validate_cmd_from_user(struct cxl_mem *cxlm,
|
||||
static int cxl_validate_cmd_from_user(struct cxl_dev_state *cxlds,
|
||||
const struct cxl_send_command *send_cmd,
|
||||
struct cxl_mem_command *out_cmd)
|
||||
{
|
||||
@ -243,7 +241,7 @@ static int cxl_validate_cmd_from_user(struct cxl_mem *cxlm,
|
||||
* supports, but output can be arbitrarily large (simply write out as
|
||||
* much data as the hardware provides).
|
||||
*/
|
||||
if (send_cmd->in.size > cxlm->payload_size)
|
||||
if (send_cmd->in.size > cxlds->payload_size)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
@ -269,7 +267,7 @@ static int cxl_validate_cmd_from_user(struct cxl_mem *cxlm,
|
||||
* gets passed along without further checking, so it must be
|
||||
* validated here.
|
||||
*/
|
||||
if (send_cmd->out.size > cxlm->payload_size)
|
||||
if (send_cmd->out.size > cxlds->payload_size)
|
||||
return -EINVAL;
|
||||
|
||||
if (!cxl_mem_raw_command_allowed(send_cmd->raw.opcode))
|
||||
@ -294,11 +292,11 @@ static int cxl_validate_cmd_from_user(struct cxl_mem *cxlm,
|
||||
info = &c->info;
|
||||
|
||||
/* Check that the command is enabled for hardware */
|
||||
if (!test_bit(info->id, cxlm->enabled_cmds))
|
||||
if (!test_bit(info->id, cxlds->enabled_cmds))
|
||||
return -ENOTTY;
|
||||
|
||||
/* Check that the command is not claimed for exclusive kernel use */
|
||||
if (test_bit(info->id, cxlm->exclusive_cmds))
|
||||
if (test_bit(info->id, cxlds->exclusive_cmds))
|
||||
return -EBUSY;
|
||||
|
||||
/* Check the input buffer is the expected size */
|
||||
@ -356,7 +354,7 @@ int cxl_query_cmd(struct cxl_memdev *cxlmd,
|
||||
|
||||
/**
|
||||
* handle_mailbox_cmd_from_user() - Dispatch a mailbox command for userspace.
|
||||
* @cxlm: The CXL memory device to communicate with.
|
||||
* @cxlds: The device data for the operation
|
||||
* @cmd: The validated command.
|
||||
* @in_payload: Pointer to userspace's input payload.
|
||||
* @out_payload: Pointer to userspace's output payload.
|
||||
@ -379,12 +377,12 @@ int cxl_query_cmd(struct cxl_memdev *cxlmd,
|
||||
*
|
||||
* See cxl_send_cmd().
|
||||
*/
|
||||
static int handle_mailbox_cmd_from_user(struct cxl_mem *cxlm,
|
||||
static int handle_mailbox_cmd_from_user(struct cxl_dev_state *cxlds,
|
||||
const struct cxl_mem_command *cmd,
|
||||
u64 in_payload, u64 out_payload,
|
||||
s32 *size_out, u32 *retval)
|
||||
{
|
||||
struct device *dev = cxlm->dev;
|
||||
struct device *dev = cxlds->dev;
|
||||
struct cxl_mbox_cmd mbox_cmd = {
|
||||
.opcode = cmd->opcode,
|
||||
.size_in = cmd->info.size_in,
|
||||
@ -417,7 +415,7 @@ static int handle_mailbox_cmd_from_user(struct cxl_mem *cxlm,
|
||||
dev_WARN_ONCE(dev, cmd->info.id == CXL_MEM_COMMAND_ID_RAW,
|
||||
"raw command path used\n");
|
||||
|
||||
rc = cxlm->mbox_send(cxlm, &mbox_cmd);
|
||||
rc = cxlds->mbox_send(cxlds, &mbox_cmd);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
@ -447,7 +445,7 @@ out:
|
||||
|
||||
int cxl_send_cmd(struct cxl_memdev *cxlmd, struct cxl_send_command __user *s)
|
||||
{
|
||||
struct cxl_mem *cxlm = cxlmd->cxlm;
|
||||
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
||||
struct device *dev = &cxlmd->dev;
|
||||
struct cxl_send_command send;
|
||||
struct cxl_mem_command c;
|
||||
@ -458,15 +456,15 @@ int cxl_send_cmd(struct cxl_memdev *cxlmd, struct cxl_send_command __user *s)
|
||||
if (copy_from_user(&send, s, sizeof(send)))
|
||||
return -EFAULT;
|
||||
|
||||
rc = cxl_validate_cmd_from_user(cxlmd->cxlm, &send, &c);
|
||||
rc = cxl_validate_cmd_from_user(cxlmd->cxlds, &send, &c);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Prepare to handle a full payload for variable sized output */
|
||||
if (c.info.size_out < 0)
|
||||
c.info.size_out = cxlm->payload_size;
|
||||
c.info.size_out = cxlds->payload_size;
|
||||
|
||||
rc = handle_mailbox_cmd_from_user(cxlm, &c, send.in.payload,
|
||||
rc = handle_mailbox_cmd_from_user(cxlds, &c, send.in.payload,
|
||||
send.out.payload, &send.out.size,
|
||||
&send.retval);
|
||||
if (rc)
|
||||
@ -478,13 +476,13 @@ int cxl_send_cmd(struct cxl_memdev *cxlmd, struct cxl_send_command __user *s)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cxl_xfer_log(struct cxl_mem *cxlm, uuid_t *uuid, u32 size, u8 *out)
|
||||
static int cxl_xfer_log(struct cxl_dev_state *cxlds, uuid_t *uuid, u32 size, u8 *out)
|
||||
{
|
||||
u32 remaining = size;
|
||||
u32 offset = 0;
|
||||
|
||||
while (remaining) {
|
||||
u32 xfer_size = min_t(u32, remaining, cxlm->payload_size);
|
||||
u32 xfer_size = min_t(u32, remaining, cxlds->payload_size);
|
||||
struct cxl_mbox_get_log log = {
|
||||
.uuid = *uuid,
|
||||
.offset = cpu_to_le32(offset),
|
||||
@ -492,8 +490,8 @@ static int cxl_xfer_log(struct cxl_mem *cxlm, uuid_t *uuid, u32 size, u8 *out)
|
||||
};
|
||||
int rc;
|
||||
|
||||
rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_LOG, &log,
|
||||
sizeof(log), out, xfer_size);
|
||||
rc = cxl_mbox_send_cmd(cxlds, CXL_MBOX_OP_GET_LOG, &log, sizeof(log),
|
||||
out, xfer_size);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
@ -507,14 +505,14 @@ static int cxl_xfer_log(struct cxl_mem *cxlm, uuid_t *uuid, u32 size, u8 *out)
|
||||
|
||||
/**
|
||||
* cxl_walk_cel() - Walk through the Command Effects Log.
|
||||
* @cxlm: Device.
|
||||
* @cxlds: The device data for the operation
|
||||
* @size: Length of the Command Effects Log.
|
||||
* @cel: CEL
|
||||
*
|
||||
* Iterate over each entry in the CEL and determine if the driver supports the
|
||||
* command. If so, the command is enabled for the device and can be used later.
|
||||
*/
|
||||
static void cxl_walk_cel(struct cxl_mem *cxlm, size_t size, u8 *cel)
|
||||
static void cxl_walk_cel(struct cxl_dev_state *cxlds, size_t size, u8 *cel)
|
||||
{
|
||||
struct cxl_cel_entry *cel_entry;
|
||||
const int cel_entries = size / sizeof(*cel_entry);
|
||||
@ -527,26 +525,26 @@ static void cxl_walk_cel(struct cxl_mem *cxlm, size_t size, u8 *cel)
|
||||
struct cxl_mem_command *cmd = cxl_mem_find_command(opcode);
|
||||
|
||||
if (!cmd) {
|
||||
dev_dbg(cxlm->dev,
|
||||
dev_dbg(cxlds->dev,
|
||||
"Opcode 0x%04x unsupported by driver", opcode);
|
||||
continue;
|
||||
}
|
||||
|
||||
set_bit(cmd->info.id, cxlm->enabled_cmds);
|
||||
set_bit(cmd->info.id, cxlds->enabled_cmds);
|
||||
}
|
||||
}
|
||||
|
||||
static struct cxl_mbox_get_supported_logs *cxl_get_gsl(struct cxl_mem *cxlm)
|
||||
static struct cxl_mbox_get_supported_logs *cxl_get_gsl(struct cxl_dev_state *cxlds)
|
||||
{
|
||||
struct cxl_mbox_get_supported_logs *ret;
|
||||
int rc;
|
||||
|
||||
ret = kvmalloc(cxlm->payload_size, GFP_KERNEL);
|
||||
ret = kvmalloc(cxlds->payload_size, GFP_KERNEL);
|
||||
if (!ret)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_SUPPORTED_LOGS, NULL,
|
||||
0, ret, cxlm->payload_size);
|
||||
rc = cxl_mbox_send_cmd(cxlds, CXL_MBOX_OP_GET_SUPPORTED_LOGS, NULL, 0, ret,
|
||||
cxlds->payload_size);
|
||||
if (rc < 0) {
|
||||
kvfree(ret);
|
||||
return ERR_PTR(rc);
|
||||
@ -567,23 +565,23 @@ static const uuid_t log_uuid[] = {
|
||||
};
|
||||
|
||||
/**
|
||||
* cxl_mem_enumerate_cmds() - Enumerate commands for a device.
|
||||
* @cxlm: The device.
|
||||
* cxl_enumerate_cmds() - Enumerate commands for a device.
|
||||
* @cxlds: The device data for the operation
|
||||
*
|
||||
* Returns 0 if enumerate completed successfully.
|
||||
*
|
||||
* CXL devices have optional support for certain commands. This function will
|
||||
* determine the set of supported commands for the hardware and update the
|
||||
* enabled_cmds bitmap in the @cxlm.
|
||||
* enabled_cmds bitmap in the @cxlds.
|
||||
*/
|
||||
int cxl_mem_enumerate_cmds(struct cxl_mem *cxlm)
|
||||
int cxl_enumerate_cmds(struct cxl_dev_state *cxlds)
|
||||
{
|
||||
struct cxl_mbox_get_supported_logs *gsl;
|
||||
struct device *dev = cxlm->dev;
|
||||
struct device *dev = cxlds->dev;
|
||||
struct cxl_mem_command *cmd;
|
||||
int i, rc;
|
||||
|
||||
gsl = cxl_get_gsl(cxlm);
|
||||
gsl = cxl_get_gsl(cxlds);
|
||||
if (IS_ERR(gsl))
|
||||
return PTR_ERR(gsl);
|
||||
|
||||
@ -604,19 +602,19 @@ int cxl_mem_enumerate_cmds(struct cxl_mem *cxlm)
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = cxl_xfer_log(cxlm, &uuid, size, log);
|
||||
rc = cxl_xfer_log(cxlds, &uuid, size, log);
|
||||
if (rc) {
|
||||
kvfree(log);
|
||||
goto out;
|
||||
}
|
||||
|
||||
cxl_walk_cel(cxlm, size, log);
|
||||
cxl_walk_cel(cxlds, size, log);
|
||||
kvfree(log);
|
||||
|
||||
/* In case CEL was bogus, enable some default commands. */
|
||||
cxl_for_each_cmd(cmd)
|
||||
if (cmd->flags & CXL_CMD_FLAG_FORCE_ENABLE)
|
||||
set_bit(cmd->info.id, cxlm->enabled_cmds);
|
||||
set_bit(cmd->info.id, cxlds->enabled_cmds);
|
||||
|
||||
/* Found the required CEL */
|
||||
rc = 0;
|
||||
@ -626,11 +624,11 @@ out:
|
||||
kvfree(gsl);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cxl_mem_enumerate_cmds);
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_enumerate_cmds, CXL);
|
||||
|
||||
/**
|
||||
* cxl_mem_get_partition_info - Get partition info
|
||||
* @cxlm: cxl_mem instance to update partition info
|
||||
* @cxlds: The device data for the operation
|
||||
*
|
||||
* Retrieve the current partition info for the device specified. The active
|
||||
* values are the current capacity in bytes. If not 0, the 'next' values are
|
||||
@ -640,7 +638,7 @@ EXPORT_SYMBOL_GPL(cxl_mem_enumerate_cmds);
|
||||
*
|
||||
* See CXL @8.2.9.5.2.1 Get Partition Info
|
||||
*/
|
||||
static int cxl_mem_get_partition_info(struct cxl_mem *cxlm)
|
||||
static int cxl_mem_get_partition_info(struct cxl_dev_state *cxlds)
|
||||
{
|
||||
struct cxl_mbox_get_partition_info {
|
||||
__le64 active_volatile_cap;
|
||||
@ -650,124 +648,124 @@ static int cxl_mem_get_partition_info(struct cxl_mem *cxlm)
|
||||
} __packed pi;
|
||||
int rc;
|
||||
|
||||
rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_PARTITION_INFO,
|
||||
NULL, 0, &pi, sizeof(pi));
|
||||
rc = cxl_mbox_send_cmd(cxlds, CXL_MBOX_OP_GET_PARTITION_INFO, NULL, 0,
|
||||
&pi, sizeof(pi));
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
cxlm->active_volatile_bytes =
|
||||
cxlds->active_volatile_bytes =
|
||||
le64_to_cpu(pi.active_volatile_cap) * CXL_CAPACITY_MULTIPLIER;
|
||||
cxlm->active_persistent_bytes =
|
||||
cxlds->active_persistent_bytes =
|
||||
le64_to_cpu(pi.active_persistent_cap) * CXL_CAPACITY_MULTIPLIER;
|
||||
cxlm->next_volatile_bytes =
|
||||
cxlds->next_volatile_bytes =
|
||||
le64_to_cpu(pi.next_volatile_cap) * CXL_CAPACITY_MULTIPLIER;
|
||||
cxlm->next_persistent_bytes =
|
||||
cxlds->next_persistent_bytes =
|
||||
le64_to_cpu(pi.next_volatile_cap) * CXL_CAPACITY_MULTIPLIER;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* cxl_mem_identify() - Send the IDENTIFY command to the device.
|
||||
* @cxlm: The device to identify.
|
||||
* cxl_dev_state_identify() - Send the IDENTIFY command to the device.
|
||||
* @cxlds: The device data for the operation
|
||||
*
|
||||
* Return: 0 if identify was executed successfully.
|
||||
*
|
||||
* This will dispatch the identify command to the device and on success populate
|
||||
* structures to be exported to sysfs.
|
||||
*/
|
||||
int cxl_mem_identify(struct cxl_mem *cxlm)
|
||||
int cxl_dev_state_identify(struct cxl_dev_state *cxlds)
|
||||
{
|
||||
/* See CXL 2.0 Table 175 Identify Memory Device Output Payload */
|
||||
struct cxl_mbox_identify id;
|
||||
int rc;
|
||||
|
||||
rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_IDENTIFY, NULL, 0, &id,
|
||||
sizeof(id));
|
||||
rc = cxl_mbox_send_cmd(cxlds, CXL_MBOX_OP_IDENTIFY, NULL, 0, &id,
|
||||
sizeof(id));
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
cxlm->total_bytes =
|
||||
cxlds->total_bytes =
|
||||
le64_to_cpu(id.total_capacity) * CXL_CAPACITY_MULTIPLIER;
|
||||
cxlm->volatile_only_bytes =
|
||||
cxlds->volatile_only_bytes =
|
||||
le64_to_cpu(id.volatile_capacity) * CXL_CAPACITY_MULTIPLIER;
|
||||
cxlm->persistent_only_bytes =
|
||||
cxlds->persistent_only_bytes =
|
||||
le64_to_cpu(id.persistent_capacity) * CXL_CAPACITY_MULTIPLIER;
|
||||
cxlm->partition_align_bytes =
|
||||
cxlds->partition_align_bytes =
|
||||
le64_to_cpu(id.partition_align) * CXL_CAPACITY_MULTIPLIER;
|
||||
|
||||
dev_dbg(cxlm->dev,
|
||||
dev_dbg(cxlds->dev,
|
||||
"Identify Memory Device\n"
|
||||
" total_bytes = %#llx\n"
|
||||
" volatile_only_bytes = %#llx\n"
|
||||
" persistent_only_bytes = %#llx\n"
|
||||
" partition_align_bytes = %#llx\n",
|
||||
cxlm->total_bytes, cxlm->volatile_only_bytes,
|
||||
cxlm->persistent_only_bytes, cxlm->partition_align_bytes);
|
||||
cxlds->total_bytes, cxlds->volatile_only_bytes,
|
||||
cxlds->persistent_only_bytes, cxlds->partition_align_bytes);
|
||||
|
||||
cxlm->lsa_size = le32_to_cpu(id.lsa_size);
|
||||
memcpy(cxlm->firmware_version, id.fw_revision, sizeof(id.fw_revision));
|
||||
cxlds->lsa_size = le32_to_cpu(id.lsa_size);
|
||||
memcpy(cxlds->firmware_version, id.fw_revision, sizeof(id.fw_revision));
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cxl_mem_identify);
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_dev_state_identify, CXL);
|
||||
|
||||
int cxl_mem_create_range_info(struct cxl_mem *cxlm)
|
||||
int cxl_mem_create_range_info(struct cxl_dev_state *cxlds)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (cxlm->partition_align_bytes == 0) {
|
||||
cxlm->ram_range.start = 0;
|
||||
cxlm->ram_range.end = cxlm->volatile_only_bytes - 1;
|
||||
cxlm->pmem_range.start = cxlm->volatile_only_bytes;
|
||||
cxlm->pmem_range.end = cxlm->volatile_only_bytes +
|
||||
cxlm->persistent_only_bytes - 1;
|
||||
if (cxlds->partition_align_bytes == 0) {
|
||||
cxlds->ram_range.start = 0;
|
||||
cxlds->ram_range.end = cxlds->volatile_only_bytes - 1;
|
||||
cxlds->pmem_range.start = cxlds->volatile_only_bytes;
|
||||
cxlds->pmem_range.end = cxlds->volatile_only_bytes +
|
||||
cxlds->persistent_only_bytes - 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = cxl_mem_get_partition_info(cxlm);
|
||||
rc = cxl_mem_get_partition_info(cxlds);
|
||||
if (rc) {
|
||||
dev_err(cxlm->dev, "Failed to query partition information\n");
|
||||
dev_err(cxlds->dev, "Failed to query partition information\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
dev_dbg(cxlm->dev,
|
||||
dev_dbg(cxlds->dev,
|
||||
"Get Partition Info\n"
|
||||
" active_volatile_bytes = %#llx\n"
|
||||
" active_persistent_bytes = %#llx\n"
|
||||
" next_volatile_bytes = %#llx\n"
|
||||
" next_persistent_bytes = %#llx\n",
|
||||
cxlm->active_volatile_bytes, cxlm->active_persistent_bytes,
|
||||
cxlm->next_volatile_bytes, cxlm->next_persistent_bytes);
|
||||
cxlds->active_volatile_bytes, cxlds->active_persistent_bytes,
|
||||
cxlds->next_volatile_bytes, cxlds->next_persistent_bytes);
|
||||
|
||||
cxlm->ram_range.start = 0;
|
||||
cxlm->ram_range.end = cxlm->active_volatile_bytes - 1;
|
||||
cxlds->ram_range.start = 0;
|
||||
cxlds->ram_range.end = cxlds->active_volatile_bytes - 1;
|
||||
|
||||
cxlm->pmem_range.start = cxlm->active_volatile_bytes;
|
||||
cxlm->pmem_range.end =
|
||||
cxlm->active_volatile_bytes + cxlm->active_persistent_bytes - 1;
|
||||
cxlds->pmem_range.start = cxlds->active_volatile_bytes;
|
||||
cxlds->pmem_range.end =
|
||||
cxlds->active_volatile_bytes + cxlds->active_persistent_bytes - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cxl_mem_create_range_info);
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_mem_create_range_info, CXL);
|
||||
|
||||
struct cxl_mem *cxl_mem_create(struct device *dev)
|
||||
struct cxl_dev_state *cxl_dev_state_create(struct device *dev)
|
||||
{
|
||||
struct cxl_mem *cxlm;
|
||||
struct cxl_dev_state *cxlds;
|
||||
|
||||
cxlm = devm_kzalloc(dev, sizeof(*cxlm), GFP_KERNEL);
|
||||
if (!cxlm) {
|
||||
cxlds = devm_kzalloc(dev, sizeof(*cxlds), GFP_KERNEL);
|
||||
if (!cxlds) {
|
||||
dev_err(dev, "No memory available\n");
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
mutex_init(&cxlm->mbox_mutex);
|
||||
cxlm->dev = dev;
|
||||
mutex_init(&cxlds->mbox_mutex);
|
||||
cxlds->dev = dev;
|
||||
|
||||
return cxlm;
|
||||
return cxlds;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cxl_mem_create);
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_dev_state_create, CXL);
|
||||
|
||||
static struct dentry *cxl_debugfs;
|
||||
|
||||
|
@ -37,9 +37,9 @@ static ssize_t firmware_version_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
|
||||
struct cxl_mem *cxlm = cxlmd->cxlm;
|
||||
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
||||
|
||||
return sysfs_emit(buf, "%.16s\n", cxlm->firmware_version);
|
||||
return sysfs_emit(buf, "%.16s\n", cxlds->firmware_version);
|
||||
}
|
||||
static DEVICE_ATTR_RO(firmware_version);
|
||||
|
||||
@ -47,9 +47,9 @@ static ssize_t payload_max_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
|
||||
struct cxl_mem *cxlm = cxlmd->cxlm;
|
||||
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
||||
|
||||
return sysfs_emit(buf, "%zu\n", cxlm->payload_size);
|
||||
return sysfs_emit(buf, "%zu\n", cxlds->payload_size);
|
||||
}
|
||||
static DEVICE_ATTR_RO(payload_max);
|
||||
|
||||
@ -57,9 +57,9 @@ static ssize_t label_storage_size_show(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
|
||||
struct cxl_mem *cxlm = cxlmd->cxlm;
|
||||
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
||||
|
||||
return sysfs_emit(buf, "%zu\n", cxlm->lsa_size);
|
||||
return sysfs_emit(buf, "%zu\n", cxlds->lsa_size);
|
||||
}
|
||||
static DEVICE_ATTR_RO(label_storage_size);
|
||||
|
||||
@ -67,8 +67,8 @@ static ssize_t ram_size_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
|
||||
struct cxl_mem *cxlm = cxlmd->cxlm;
|
||||
unsigned long long len = range_len(&cxlm->ram_range);
|
||||
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
||||
unsigned long long len = range_len(&cxlds->ram_range);
|
||||
|
||||
return sysfs_emit(buf, "%#llx\n", len);
|
||||
}
|
||||
@ -80,8 +80,8 @@ static ssize_t pmem_size_show(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
|
||||
struct cxl_mem *cxlm = cxlmd->cxlm;
|
||||
unsigned long long len = range_len(&cxlm->pmem_range);
|
||||
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
||||
unsigned long long len = range_len(&cxlds->pmem_range);
|
||||
|
||||
return sysfs_emit(buf, "%#llx\n", len);
|
||||
}
|
||||
@ -136,42 +136,42 @@ static const struct device_type cxl_memdev_type = {
|
||||
|
||||
/**
|
||||
* set_exclusive_cxl_commands() - atomically disable user cxl commands
|
||||
* @cxlm: cxl_mem instance to modify
|
||||
* @cxlds: The device state to operate on
|
||||
* @cmds: bitmap of commands to mark exclusive
|
||||
*
|
||||
* Grab the cxl_memdev_rwsem in write mode to flush in-flight
|
||||
* invocations of the ioctl path and then disable future execution of
|
||||
* commands with the command ids set in @cmds.
|
||||
*/
|
||||
void set_exclusive_cxl_commands(struct cxl_mem *cxlm, unsigned long *cmds)
|
||||
void set_exclusive_cxl_commands(struct cxl_dev_state *cxlds, unsigned long *cmds)
|
||||
{
|
||||
down_write(&cxl_memdev_rwsem);
|
||||
bitmap_or(cxlm->exclusive_cmds, cxlm->exclusive_cmds, cmds,
|
||||
bitmap_or(cxlds->exclusive_cmds, cxlds->exclusive_cmds, cmds,
|
||||
CXL_MEM_COMMAND_ID_MAX);
|
||||
up_write(&cxl_memdev_rwsem);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(set_exclusive_cxl_commands);
|
||||
EXPORT_SYMBOL_NS_GPL(set_exclusive_cxl_commands, CXL);
|
||||
|
||||
/**
|
||||
* clear_exclusive_cxl_commands() - atomically enable user cxl commands
|
||||
* @cxlm: cxl_mem instance to modify
|
||||
* @cxlds: The device state to modify
|
||||
* @cmds: bitmap of commands to mark available for userspace
|
||||
*/
|
||||
void clear_exclusive_cxl_commands(struct cxl_mem *cxlm, unsigned long *cmds)
|
||||
void clear_exclusive_cxl_commands(struct cxl_dev_state *cxlds, unsigned long *cmds)
|
||||
{
|
||||
down_write(&cxl_memdev_rwsem);
|
||||
bitmap_andnot(cxlm->exclusive_cmds, cxlm->exclusive_cmds, cmds,
|
||||
bitmap_andnot(cxlds->exclusive_cmds, cxlds->exclusive_cmds, cmds,
|
||||
CXL_MEM_COMMAND_ID_MAX);
|
||||
up_write(&cxl_memdev_rwsem);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clear_exclusive_cxl_commands);
|
||||
EXPORT_SYMBOL_NS_GPL(clear_exclusive_cxl_commands, CXL);
|
||||
|
||||
static void cxl_memdev_shutdown(struct device *dev)
|
||||
{
|
||||
struct cxl_memdev *cxlmd = to_cxl_memdev(dev);
|
||||
|
||||
down_write(&cxl_memdev_rwsem);
|
||||
cxlmd->cxlm = NULL;
|
||||
cxlmd->cxlds = NULL;
|
||||
up_write(&cxl_memdev_rwsem);
|
||||
}
|
||||
|
||||
@ -185,7 +185,7 @@ static void cxl_memdev_unregister(void *_cxlmd)
|
||||
put_device(dev);
|
||||
}
|
||||
|
||||
static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm,
|
||||
static struct cxl_memdev *cxl_memdev_alloc(struct cxl_dev_state *cxlds,
|
||||
const struct file_operations *fops)
|
||||
{
|
||||
struct cxl_memdev *cxlmd;
|
||||
@ -204,7 +204,7 @@ static struct cxl_memdev *cxl_memdev_alloc(struct cxl_mem *cxlm,
|
||||
|
||||
dev = &cxlmd->dev;
|
||||
device_initialize(dev);
|
||||
dev->parent = cxlm->dev;
|
||||
dev->parent = cxlds->dev;
|
||||
dev->bus = &cxl_bus_type;
|
||||
dev->devt = MKDEV(cxl_mem_major, cxlmd->id);
|
||||
dev->type = &cxl_memdev_type;
|
||||
@ -239,7 +239,7 @@ static long cxl_memdev_ioctl(struct file *file, unsigned int cmd,
|
||||
int rc = -ENXIO;
|
||||
|
||||
down_read(&cxl_memdev_rwsem);
|
||||
if (cxlmd->cxlm)
|
||||
if (cxlmd->cxlds)
|
||||
rc = __cxl_memdev_ioctl(cxlmd, cmd, arg);
|
||||
up_read(&cxl_memdev_rwsem);
|
||||
|
||||
@ -276,15 +276,14 @@ static const struct file_operations cxl_memdev_fops = {
|
||||
.llseek = noop_llseek,
|
||||
};
|
||||
|
||||
struct cxl_memdev *
|
||||
devm_cxl_add_memdev(struct cxl_mem *cxlm)
|
||||
struct cxl_memdev *devm_cxl_add_memdev(struct cxl_dev_state *cxlds)
|
||||
{
|
||||
struct cxl_memdev *cxlmd;
|
||||
struct device *dev;
|
||||
struct cdev *cdev;
|
||||
int rc;
|
||||
|
||||
cxlmd = cxl_memdev_alloc(cxlm, &cxl_memdev_fops);
|
||||
cxlmd = cxl_memdev_alloc(cxlds, &cxl_memdev_fops);
|
||||
if (IS_ERR(cxlmd))
|
||||
return cxlmd;
|
||||
|
||||
@ -297,14 +296,14 @@ devm_cxl_add_memdev(struct cxl_mem *cxlm)
|
||||
* Activate ioctl operations, no cxl_memdev_rwsem manipulation
|
||||
* needed as this is ordered with cdev_add() publishing the device.
|
||||
*/
|
||||
cxlmd->cxlm = cxlm;
|
||||
cxlmd->cxlds = cxlds;
|
||||
|
||||
cdev = &cxlmd->cdev;
|
||||
rc = cdev_device_add(cdev, dev);
|
||||
if (rc)
|
||||
goto err;
|
||||
|
||||
rc = devm_add_action_or_reset(cxlm->dev, cxl_memdev_unregister, cxlmd);
|
||||
rc = devm_add_action_or_reset(cxlds->dev, cxl_memdev_unregister, cxlmd);
|
||||
if (rc)
|
||||
return ERR_PTR(rc);
|
||||
return cxlmd;
|
||||
@ -318,7 +317,7 @@ err:
|
||||
put_device(dev);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_cxl_add_memdev);
|
||||
EXPORT_SYMBOL_NS_GPL(devm_cxl_add_memdev, CXL);
|
||||
|
||||
__init int cxl_memdev_init(void)
|
||||
{
|
||||
|
@ -49,11 +49,17 @@ struct cxl_nvdimm_bridge *to_cxl_nvdimm_bridge(struct device *dev)
|
||||
return NULL;
|
||||
return container_of(dev, struct cxl_nvdimm_bridge, dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(to_cxl_nvdimm_bridge);
|
||||
EXPORT_SYMBOL_NS_GPL(to_cxl_nvdimm_bridge, CXL);
|
||||
|
||||
bool is_cxl_nvdimm_bridge(struct device *dev)
|
||||
{
|
||||
return dev->type == &cxl_nvdimm_bridge_type;
|
||||
}
|
||||
EXPORT_SYMBOL_NS_GPL(is_cxl_nvdimm_bridge, CXL);
|
||||
|
||||
__mock int match_nvdimm_bridge(struct device *dev, const void *data)
|
||||
{
|
||||
return dev->type == &cxl_nvdimm_bridge_type;
|
||||
return is_cxl_nvdimm_bridge(dev);
|
||||
}
|
||||
|
||||
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_nvdimm *cxl_nvd)
|
||||
@ -65,7 +71,7 @@ struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_nvdimm *cxl_nvd)
|
||||
return NULL;
|
||||
return to_cxl_nvdimm_bridge(dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cxl_find_nvdimm_bridge);
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_find_nvdimm_bridge, CXL);
|
||||
|
||||
static struct cxl_nvdimm_bridge *
|
||||
cxl_nvdimm_bridge_alloc(struct cxl_port *port)
|
||||
@ -167,7 +173,7 @@ err:
|
||||
put_device(dev);
|
||||
return ERR_PTR(rc);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_cxl_add_nvdimm_bridge);
|
||||
EXPORT_SYMBOL_NS_GPL(devm_cxl_add_nvdimm_bridge, CXL);
|
||||
|
||||
static void cxl_nvdimm_release(struct device *dev)
|
||||
{
|
||||
@ -191,7 +197,7 @@ bool is_cxl_nvdimm(struct device *dev)
|
||||
{
|
||||
return dev->type == &cxl_nvdimm_type;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(is_cxl_nvdimm);
|
||||
EXPORT_SYMBOL_NS_GPL(is_cxl_nvdimm, CXL);
|
||||
|
||||
struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev)
|
||||
{
|
||||
@ -200,7 +206,7 @@ struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev)
|
||||
return NULL;
|
||||
return container_of(dev, struct cxl_nvdimm, dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(to_cxl_nvdimm);
|
||||
EXPORT_SYMBOL_NS_GPL(to_cxl_nvdimm, CXL);
|
||||
|
||||
static struct cxl_nvdimm *cxl_nvdimm_alloc(struct cxl_memdev *cxlmd)
|
||||
{
|
||||
@ -262,4 +268,4 @@ err:
|
||||
put_device(dev);
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_cxl_add_nvdimm);
|
||||
EXPORT_SYMBOL_NS_GPL(devm_cxl_add_nvdimm, CXL);
|
||||
|
@ -90,7 +90,7 @@ void cxl_probe_component_regs(struct device *dev, void __iomem *base,
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cxl_probe_component_regs);
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_probe_component_regs, CXL);
|
||||
|
||||
/**
|
||||
* cxl_probe_device_regs() - Detect CXL Device register blocks
|
||||
@ -156,7 +156,7 @@ void cxl_probe_device_regs(struct device *dev, void __iomem *base,
|
||||
}
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cxl_probe_device_regs);
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_probe_device_regs, CXL);
|
||||
|
||||
static void __iomem *devm_cxl_iomap_block(struct device *dev,
|
||||
resource_size_t addr,
|
||||
@ -199,7 +199,7 @@ int cxl_map_component_regs(struct pci_dev *pdev,
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cxl_map_component_regs);
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_map_component_regs, CXL);
|
||||
|
||||
int cxl_map_device_regs(struct pci_dev *pdev,
|
||||
struct cxl_device_regs *regs,
|
||||
@ -246,4 +246,4 @@ int cxl_map_device_regs(struct pci_dev *pdev,
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cxl_map_device_regs);
|
||||
EXPORT_SYMBOL_NS_GPL(cxl_map_device_regs, CXL);
|
||||
|
@ -191,11 +191,18 @@ struct cxl_decoder {
|
||||
int interleave_granularity;
|
||||
enum cxl_decoder_type target_type;
|
||||
unsigned long flags;
|
||||
const int nr_targets;
|
||||
int nr_targets;
|
||||
struct cxl_dport *target[];
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* enum cxl_nvdimm_brige_state - state machine for managing bus rescans
|
||||
* @CXL_NVB_NEW: Set at bridge create and after cxl_pmem_wq is destroyed
|
||||
* @CXL_NVB_DEAD: Set at brige unregistration to preclude async probing
|
||||
* @CXL_NVB_ONLINE: Target state after successful ->probe()
|
||||
* @CXL_NVB_OFFLINE: Target state after ->remove() or failed ->probe()
|
||||
*/
|
||||
enum cxl_nvdimm_brige_state {
|
||||
CXL_NVB_NEW,
|
||||
CXL_NVB_DEAD,
|
||||
@ -308,6 +315,7 @@ struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host,
|
||||
struct cxl_port *port);
|
||||
struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev);
|
||||
bool is_cxl_nvdimm(struct device *dev);
|
||||
bool is_cxl_nvdimm_bridge(struct device *dev);
|
||||
int devm_cxl_add_nvdimm(struct device *host, struct cxl_memdev *cxlmd);
|
||||
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_nvdimm *cxl_nvd);
|
||||
|
||||
|
@ -33,13 +33,13 @@
|
||||
* struct cxl_memdev - CXL bus object representing a Type-3 Memory Device
|
||||
* @dev: driver core device object
|
||||
* @cdev: char dev core object for ioctl operations
|
||||
* @cxlm: pointer to the parent device driver data
|
||||
* @cxlds: The device state backing this device
|
||||
* @id: id number of this memdev instance.
|
||||
*/
|
||||
struct cxl_memdev {
|
||||
struct device dev;
|
||||
struct cdev cdev;
|
||||
struct cxl_mem *cxlm;
|
||||
struct cxl_dev_state *cxlds;
|
||||
int id;
|
||||
};
|
||||
|
||||
@ -48,7 +48,7 @@ static inline struct cxl_memdev *to_cxl_memdev(struct device *dev)
|
||||
return container_of(dev, struct cxl_memdev, dev);
|
||||
}
|
||||
|
||||
struct cxl_memdev *devm_cxl_add_memdev(struct cxl_mem *cxlm);
|
||||
struct cxl_memdev *devm_cxl_add_memdev(struct cxl_dev_state *cxlds);
|
||||
|
||||
/**
|
||||
* struct cxl_mbox_cmd - A command to be submitted to hardware.
|
||||
@ -90,9 +90,13 @@ struct cxl_mbox_cmd {
|
||||
#define CXL_CAPACITY_MULTIPLIER SZ_256M
|
||||
|
||||
/**
|
||||
* struct cxl_mem - A CXL memory device
|
||||
* @dev: The device associated with this CXL device.
|
||||
* @cxlmd: Logical memory device chardev / interface
|
||||
* struct cxl_dev_state - The driver device state
|
||||
*
|
||||
* cxl_dev_state represents the CXL driver/device state. It provides an
|
||||
* interface to mailbox commands as well as some cached data about the device.
|
||||
* Currently only memory devices are represented.
|
||||
*
|
||||
* @dev: The device associated with this CXL state
|
||||
* @regs: Parsed register blocks
|
||||
* @payload_size: Size of space for payload
|
||||
* (CXL 2.0 8.2.8.4.3 Mailbox Capabilities Register)
|
||||
@ -117,9 +121,8 @@ struct cxl_mbox_cmd {
|
||||
* See section 8.2.9.5.2 Capacity Configuration and Label Storage for
|
||||
* details on capacity parameters.
|
||||
*/
|
||||
struct cxl_mem {
|
||||
struct cxl_dev_state {
|
||||
struct device *dev;
|
||||
struct cxl_memdev *cxlmd;
|
||||
|
||||
struct cxl_regs regs;
|
||||
|
||||
@ -142,7 +145,7 @@ struct cxl_mem {
|
||||
u64 next_volatile_bytes;
|
||||
u64 next_persistent_bytes;
|
||||
|
||||
int (*mbox_send)(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd);
|
||||
int (*mbox_send)(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd);
|
||||
};
|
||||
|
||||
enum cxl_opcode {
|
||||
@ -253,12 +256,12 @@ struct cxl_mem_command {
|
||||
#define CXL_CMD_FLAG_FORCE_ENABLE BIT(0)
|
||||
};
|
||||
|
||||
int cxl_mem_mbox_send_cmd(struct cxl_mem *cxlm, u16 opcode, void *in,
|
||||
size_t in_size, void *out, size_t out_size);
|
||||
int cxl_mem_identify(struct cxl_mem *cxlm);
|
||||
int cxl_mem_enumerate_cmds(struct cxl_mem *cxlm);
|
||||
int cxl_mem_create_range_info(struct cxl_mem *cxlm);
|
||||
struct cxl_mem *cxl_mem_create(struct device *dev);
|
||||
void set_exclusive_cxl_commands(struct cxl_mem *cxlm, unsigned long *cmds);
|
||||
void clear_exclusive_cxl_commands(struct cxl_mem *cxlm, unsigned long *cmds);
|
||||
int cxl_mbox_send_cmd(struct cxl_dev_state *cxlds, u16 opcode, void *in,
|
||||
size_t in_size, void *out, size_t out_size);
|
||||
int cxl_dev_state_identify(struct cxl_dev_state *cxlds);
|
||||
int cxl_enumerate_cmds(struct cxl_dev_state *cxlds);
|
||||
int cxl_mem_create_range_info(struct cxl_dev_state *cxlds);
|
||||
struct cxl_dev_state *cxl_dev_state_create(struct device *dev);
|
||||
void set_exclusive_cxl_commands(struct cxl_dev_state *cxlds, unsigned long *cmds);
|
||||
void clear_exclusive_cxl_commands(struct cxl_dev_state *cxlds, unsigned long *cmds);
|
||||
#endif /* __CXL_MEM_H__ */
|
||||
|
@ -28,39 +28,39 @@
|
||||
* - Registers a CXL mailbox with cxl_core.
|
||||
*/
|
||||
|
||||
#define cxl_doorbell_busy(cxlm) \
|
||||
(readl((cxlm)->regs.mbox + CXLDEV_MBOX_CTRL_OFFSET) & \
|
||||
#define cxl_doorbell_busy(cxlds) \
|
||||
(readl((cxlds)->regs.mbox + CXLDEV_MBOX_CTRL_OFFSET) & \
|
||||
CXLDEV_MBOX_CTRL_DOORBELL)
|
||||
|
||||
/* CXL 2.0 - 8.2.8.4 */
|
||||
#define CXL_MAILBOX_TIMEOUT_MS (2 * HZ)
|
||||
|
||||
static int cxl_pci_mbox_wait_for_doorbell(struct cxl_mem *cxlm)
|
||||
static int cxl_pci_mbox_wait_for_doorbell(struct cxl_dev_state *cxlds)
|
||||
{
|
||||
const unsigned long start = jiffies;
|
||||
unsigned long end = start;
|
||||
|
||||
while (cxl_doorbell_busy(cxlm)) {
|
||||
while (cxl_doorbell_busy(cxlds)) {
|
||||
end = jiffies;
|
||||
|
||||
if (time_after(end, start + CXL_MAILBOX_TIMEOUT_MS)) {
|
||||
/* Check again in case preempted before timeout test */
|
||||
if (!cxl_doorbell_busy(cxlm))
|
||||
if (!cxl_doorbell_busy(cxlds))
|
||||
break;
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
dev_dbg(cxlm->dev, "Doorbell wait took %dms",
|
||||
dev_dbg(cxlds->dev, "Doorbell wait took %dms",
|
||||
jiffies_to_msecs(end) - jiffies_to_msecs(start));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cxl_pci_mbox_timeout(struct cxl_mem *cxlm,
|
||||
static void cxl_pci_mbox_timeout(struct cxl_dev_state *cxlds,
|
||||
struct cxl_mbox_cmd *mbox_cmd)
|
||||
{
|
||||
struct device *dev = cxlm->dev;
|
||||
struct device *dev = cxlds->dev;
|
||||
|
||||
dev_dbg(dev, "Mailbox command (opcode: %#x size: %zub) timed out\n",
|
||||
mbox_cmd->opcode, mbox_cmd->size_in);
|
||||
@ -68,7 +68,7 @@ static void cxl_pci_mbox_timeout(struct cxl_mem *cxlm,
|
||||
|
||||
/**
|
||||
* __cxl_pci_mbox_send_cmd() - Execute a mailbox command
|
||||
* @cxlm: The CXL memory device to communicate with.
|
||||
* @cxlds: The device state to communicate with.
|
||||
* @mbox_cmd: Command to send to the memory device.
|
||||
*
|
||||
* Context: Any context. Expects mbox_mutex to be held.
|
||||
@ -88,16 +88,16 @@ static void cxl_pci_mbox_timeout(struct cxl_mem *cxlm,
|
||||
* not need to coordinate with each other. The driver only uses the primary
|
||||
* mailbox.
|
||||
*/
|
||||
static int __cxl_pci_mbox_send_cmd(struct cxl_mem *cxlm,
|
||||
static int __cxl_pci_mbox_send_cmd(struct cxl_dev_state *cxlds,
|
||||
struct cxl_mbox_cmd *mbox_cmd)
|
||||
{
|
||||
void __iomem *payload = cxlm->regs.mbox + CXLDEV_MBOX_PAYLOAD_OFFSET;
|
||||
struct device *dev = cxlm->dev;
|
||||
void __iomem *payload = cxlds->regs.mbox + CXLDEV_MBOX_PAYLOAD_OFFSET;
|
||||
struct device *dev = cxlds->dev;
|
||||
u64 cmd_reg, status_reg;
|
||||
size_t out_len;
|
||||
int rc;
|
||||
|
||||
lockdep_assert_held(&cxlm->mbox_mutex);
|
||||
lockdep_assert_held(&cxlds->mbox_mutex);
|
||||
|
||||
/*
|
||||
* Here are the steps from 8.2.8.4 of the CXL 2.0 spec.
|
||||
@ -117,7 +117,7 @@ static int __cxl_pci_mbox_send_cmd(struct cxl_mem *cxlm,
|
||||
*/
|
||||
|
||||
/* #1 */
|
||||
if (cxl_doorbell_busy(cxlm)) {
|
||||
if (cxl_doorbell_busy(cxlds)) {
|
||||
dev_err_ratelimited(dev, "Mailbox re-busy after acquiring\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
@ -134,22 +134,22 @@ static int __cxl_pci_mbox_send_cmd(struct cxl_mem *cxlm,
|
||||
}
|
||||
|
||||
/* #2, #3 */
|
||||
writeq(cmd_reg, cxlm->regs.mbox + CXLDEV_MBOX_CMD_OFFSET);
|
||||
writeq(cmd_reg, cxlds->regs.mbox + CXLDEV_MBOX_CMD_OFFSET);
|
||||
|
||||
/* #4 */
|
||||
dev_dbg(dev, "Sending command\n");
|
||||
writel(CXLDEV_MBOX_CTRL_DOORBELL,
|
||||
cxlm->regs.mbox + CXLDEV_MBOX_CTRL_OFFSET);
|
||||
cxlds->regs.mbox + CXLDEV_MBOX_CTRL_OFFSET);
|
||||
|
||||
/* #5 */
|
||||
rc = cxl_pci_mbox_wait_for_doorbell(cxlm);
|
||||
rc = cxl_pci_mbox_wait_for_doorbell(cxlds);
|
||||
if (rc == -ETIMEDOUT) {
|
||||
cxl_pci_mbox_timeout(cxlm, mbox_cmd);
|
||||
cxl_pci_mbox_timeout(cxlds, mbox_cmd);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* #6 */
|
||||
status_reg = readq(cxlm->regs.mbox + CXLDEV_MBOX_STATUS_OFFSET);
|
||||
status_reg = readq(cxlds->regs.mbox + CXLDEV_MBOX_STATUS_OFFSET);
|
||||
mbox_cmd->return_code =
|
||||
FIELD_GET(CXLDEV_MBOX_STATUS_RET_CODE_MASK, status_reg);
|
||||
|
||||
@ -159,7 +159,7 @@ static int __cxl_pci_mbox_send_cmd(struct cxl_mem *cxlm,
|
||||
}
|
||||
|
||||
/* #7 */
|
||||
cmd_reg = readq(cxlm->regs.mbox + CXLDEV_MBOX_CMD_OFFSET);
|
||||
cmd_reg = readq(cxlds->regs.mbox + CXLDEV_MBOX_CMD_OFFSET);
|
||||
out_len = FIELD_GET(CXLDEV_MBOX_CMD_PAYLOAD_LENGTH_MASK, cmd_reg);
|
||||
|
||||
/* #8 */
|
||||
@ -171,7 +171,7 @@ static int __cxl_pci_mbox_send_cmd(struct cxl_mem *cxlm,
|
||||
* have requested less data than the hardware supplied even
|
||||
* within spec.
|
||||
*/
|
||||
size_t n = min3(mbox_cmd->size_out, cxlm->payload_size, out_len);
|
||||
size_t n = min3(mbox_cmd->size_out, cxlds->payload_size, out_len);
|
||||
|
||||
memcpy_fromio(mbox_cmd->payload_out, payload, n);
|
||||
mbox_cmd->size_out = n;
|
||||
@ -184,18 +184,18 @@ static int __cxl_pci_mbox_send_cmd(struct cxl_mem *cxlm,
|
||||
|
||||
/**
|
||||
* cxl_pci_mbox_get() - Acquire exclusive access to the mailbox.
|
||||
* @cxlm: The memory device to gain access to.
|
||||
* @cxlds: The device state to gain access to.
|
||||
*
|
||||
* Context: Any context. Takes the mbox_mutex.
|
||||
* Return: 0 if exclusive access was acquired.
|
||||
*/
|
||||
static int cxl_pci_mbox_get(struct cxl_mem *cxlm)
|
||||
static int cxl_pci_mbox_get(struct cxl_dev_state *cxlds)
|
||||
{
|
||||
struct device *dev = cxlm->dev;
|
||||
struct device *dev = cxlds->dev;
|
||||
u64 md_status;
|
||||
int rc;
|
||||
|
||||
mutex_lock_io(&cxlm->mbox_mutex);
|
||||
mutex_lock_io(&cxlds->mbox_mutex);
|
||||
|
||||
/*
|
||||
* XXX: There is some amount of ambiguity in the 2.0 version of the spec
|
||||
@ -214,13 +214,13 @@ static int cxl_pci_mbox_get(struct cxl_mem *cxlm)
|
||||
* Mailbox Interface Ready bit. Therefore, waiting for the doorbell
|
||||
* to be ready is sufficient.
|
||||
*/
|
||||
rc = cxl_pci_mbox_wait_for_doorbell(cxlm);
|
||||
rc = cxl_pci_mbox_wait_for_doorbell(cxlds);
|
||||
if (rc) {
|
||||
dev_warn(dev, "Mailbox interface not ready\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
md_status = readq(cxlm->regs.memdev + CXLMDEV_STATUS_OFFSET);
|
||||
md_status = readq(cxlds->regs.memdev + CXLMDEV_STATUS_OFFSET);
|
||||
if (!(md_status & CXLMDEV_MBOX_IF_READY && CXLMDEV_READY(md_status))) {
|
||||
dev_err(dev, "mbox: reported doorbell ready, but not mbox ready\n");
|
||||
rc = -EBUSY;
|
||||
@ -249,41 +249,41 @@ static int cxl_pci_mbox_get(struct cxl_mem *cxlm)
|
||||
return 0;
|
||||
|
||||
out:
|
||||
mutex_unlock(&cxlm->mbox_mutex);
|
||||
mutex_unlock(&cxlds->mbox_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* cxl_pci_mbox_put() - Release exclusive access to the mailbox.
|
||||
* @cxlm: The CXL memory device to communicate with.
|
||||
* @cxlds: The device state to communicate with.
|
||||
*
|
||||
* Context: Any context. Expects mbox_mutex to be held.
|
||||
*/
|
||||
static void cxl_pci_mbox_put(struct cxl_mem *cxlm)
|
||||
static void cxl_pci_mbox_put(struct cxl_dev_state *cxlds)
|
||||
{
|
||||
mutex_unlock(&cxlm->mbox_mutex);
|
||||
mutex_unlock(&cxlds->mbox_mutex);
|
||||
}
|
||||
|
||||
static int cxl_pci_mbox_send(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd)
|
||||
static int cxl_pci_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = cxl_pci_mbox_get(cxlm);
|
||||
rc = cxl_pci_mbox_get(cxlds);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = __cxl_pci_mbox_send_cmd(cxlm, cmd);
|
||||
cxl_pci_mbox_put(cxlm);
|
||||
rc = __cxl_pci_mbox_send_cmd(cxlds, cmd);
|
||||
cxl_pci_mbox_put(cxlds);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cxl_pci_setup_mailbox(struct cxl_mem *cxlm)
|
||||
static int cxl_pci_setup_mailbox(struct cxl_dev_state *cxlds)
|
||||
{
|
||||
const int cap = readl(cxlm->regs.mbox + CXLDEV_MBOX_CAPS_OFFSET);
|
||||
const int cap = readl(cxlds->regs.mbox + CXLDEV_MBOX_CAPS_OFFSET);
|
||||
|
||||
cxlm->mbox_send = cxl_pci_mbox_send;
|
||||
cxlm->payload_size =
|
||||
cxlds->mbox_send = cxl_pci_mbox_send;
|
||||
cxlds->payload_size =
|
||||
1 << FIELD_GET(CXLDEV_MBOX_CAP_PAYLOAD_SIZE_MASK, cap);
|
||||
|
||||
/*
|
||||
@ -293,15 +293,15 @@ static int cxl_pci_setup_mailbox(struct cxl_mem *cxlm)
|
||||
* there's no point in going forward. If the size is too large, there's
|
||||
* no harm is soft limiting it.
|
||||
*/
|
||||
cxlm->payload_size = min_t(size_t, cxlm->payload_size, SZ_1M);
|
||||
if (cxlm->payload_size < 256) {
|
||||
dev_err(cxlm->dev, "Mailbox is too small (%zub)",
|
||||
cxlm->payload_size);
|
||||
cxlds->payload_size = min_t(size_t, cxlds->payload_size, SZ_1M);
|
||||
if (cxlds->payload_size < 256) {
|
||||
dev_err(cxlds->dev, "Mailbox is too small (%zub)",
|
||||
cxlds->payload_size);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
dev_dbg(cxlm->dev, "Mailbox payload sized %zu",
|
||||
cxlm->payload_size);
|
||||
dev_dbg(cxlds->dev, "Mailbox payload sized %zu",
|
||||
cxlds->payload_size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -379,18 +379,18 @@ static int cxl_probe_regs(struct pci_dev *pdev, struct cxl_register_map *map)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cxl_map_regs(struct cxl_mem *cxlm, struct cxl_register_map *map)
|
||||
static int cxl_map_regs(struct cxl_dev_state *cxlds, struct cxl_register_map *map)
|
||||
{
|
||||
struct device *dev = cxlm->dev;
|
||||
struct device *dev = cxlds->dev;
|
||||
struct pci_dev *pdev = to_pci_dev(dev);
|
||||
|
||||
switch (map->reg_type) {
|
||||
case CXL_REGLOC_RBI_COMPONENT:
|
||||
cxl_map_component_regs(pdev, &cxlm->regs.component, map);
|
||||
cxl_map_component_regs(pdev, &cxlds->regs.component, map);
|
||||
dev_dbg(dev, "Mapping component registers...\n");
|
||||
break;
|
||||
case CXL_REGLOC_RBI_MEMDEV:
|
||||
cxl_map_device_regs(pdev, &cxlm->regs.device_regs, map);
|
||||
cxl_map_device_regs(pdev, &cxlds->regs.device_regs, map);
|
||||
dev_dbg(dev, "Probing device registers...\n");
|
||||
break;
|
||||
default:
|
||||
@ -475,7 +475,7 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct cxl_register_map map;
|
||||
struct cxl_memdev *cxlmd;
|
||||
struct cxl_mem *cxlm;
|
||||
struct cxl_dev_state *cxlds;
|
||||
int rc;
|
||||
|
||||
/*
|
||||
@ -489,39 +489,39 @@ static int cxl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
cxlm = cxl_mem_create(&pdev->dev);
|
||||
if (IS_ERR(cxlm))
|
||||
return PTR_ERR(cxlm);
|
||||
cxlds = cxl_dev_state_create(&pdev->dev);
|
||||
if (IS_ERR(cxlds))
|
||||
return PTR_ERR(cxlds);
|
||||
|
||||
rc = cxl_setup_regs(pdev, CXL_REGLOC_RBI_MEMDEV, &map);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = cxl_map_regs(cxlm, &map);
|
||||
rc = cxl_map_regs(cxlds, &map);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = cxl_pci_setup_mailbox(cxlm);
|
||||
rc = cxl_pci_setup_mailbox(cxlds);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = cxl_mem_enumerate_cmds(cxlm);
|
||||
rc = cxl_enumerate_cmds(cxlds);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = cxl_mem_identify(cxlm);
|
||||
rc = cxl_dev_state_identify(cxlds);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = cxl_mem_create_range_info(cxlm);
|
||||
rc = cxl_mem_create_range_info(cxlds);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
cxlmd = devm_cxl_add_memdev(cxlm);
|
||||
cxlmd = devm_cxl_add_memdev(cxlds);
|
||||
if (IS_ERR(cxlmd))
|
||||
return PTR_ERR(cxlmd);
|
||||
|
||||
if (range_len(&cxlm->pmem_range) && IS_ENABLED(CONFIG_CXL_PMEM))
|
||||
if (range_len(&cxlds->pmem_range) && IS_ENABLED(CONFIG_CXL_PMEM))
|
||||
rc = devm_cxl_add_nvdimm(&pdev->dev, cxlmd);
|
||||
|
||||
return rc;
|
||||
|
@ -19,9 +19,9 @@ static struct workqueue_struct *cxl_pmem_wq;
|
||||
|
||||
static __read_mostly DECLARE_BITMAP(exclusive_cmds, CXL_MEM_COMMAND_ID_MAX);
|
||||
|
||||
static void clear_exclusive(void *cxlm)
|
||||
static void clear_exclusive(void *cxlds)
|
||||
{
|
||||
clear_exclusive_cxl_commands(cxlm, exclusive_cmds);
|
||||
clear_exclusive_cxl_commands(cxlds, exclusive_cmds);
|
||||
}
|
||||
|
||||
static void unregister_nvdimm(void *nvdimm)
|
||||
@ -34,7 +34,7 @@ static int cxl_nvdimm_probe(struct device *dev)
|
||||
struct cxl_nvdimm *cxl_nvd = to_cxl_nvdimm(dev);
|
||||
struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
|
||||
unsigned long flags = 0, cmd_mask = 0;
|
||||
struct cxl_mem *cxlm = cxlmd->cxlm;
|
||||
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
||||
struct cxl_nvdimm_bridge *cxl_nvb;
|
||||
struct nvdimm *nvdimm;
|
||||
int rc;
|
||||
@ -49,8 +49,8 @@ static int cxl_nvdimm_probe(struct device *dev)
|
||||
goto out;
|
||||
}
|
||||
|
||||
set_exclusive_cxl_commands(cxlm, exclusive_cmds);
|
||||
rc = devm_add_action_or_reset(dev, clear_exclusive, cxlm);
|
||||
set_exclusive_cxl_commands(cxlds, exclusive_cmds);
|
||||
rc = devm_add_action_or_reset(dev, clear_exclusive, cxlds);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
@ -80,7 +80,7 @@ static struct cxl_driver cxl_nvdimm_driver = {
|
||||
.id = CXL_DEVICE_NVDIMM,
|
||||
};
|
||||
|
||||
static int cxl_pmem_get_config_size(struct cxl_mem *cxlm,
|
||||
static int cxl_pmem_get_config_size(struct cxl_dev_state *cxlds,
|
||||
struct nd_cmd_get_config_size *cmd,
|
||||
unsigned int buf_len)
|
||||
{
|
||||
@ -88,14 +88,14 @@ static int cxl_pmem_get_config_size(struct cxl_mem *cxlm,
|
||||
return -EINVAL;
|
||||
|
||||
*cmd = (struct nd_cmd_get_config_size) {
|
||||
.config_size = cxlm->lsa_size,
|
||||
.max_xfer = cxlm->payload_size,
|
||||
.config_size = cxlds->lsa_size,
|
||||
.max_xfer = cxlds->payload_size,
|
||||
};
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cxl_pmem_get_config_data(struct cxl_mem *cxlm,
|
||||
static int cxl_pmem_get_config_data(struct cxl_dev_state *cxlds,
|
||||
struct nd_cmd_get_config_data_hdr *cmd,
|
||||
unsigned int buf_len)
|
||||
{
|
||||
@ -112,15 +112,14 @@ static int cxl_pmem_get_config_data(struct cxl_mem *cxlm,
|
||||
.length = cmd->in_length,
|
||||
};
|
||||
|
||||
rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_GET_LSA, &get_lsa,
|
||||
sizeof(get_lsa), cmd->out_buf,
|
||||
cmd->in_length);
|
||||
rc = cxl_mbox_send_cmd(cxlds, CXL_MBOX_OP_GET_LSA, &get_lsa,
|
||||
sizeof(get_lsa), cmd->out_buf, cmd->in_length);
|
||||
cmd->status = 0;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int cxl_pmem_set_config_data(struct cxl_mem *cxlm,
|
||||
static int cxl_pmem_set_config_data(struct cxl_dev_state *cxlds,
|
||||
struct nd_cmd_set_config_hdr *cmd,
|
||||
unsigned int buf_len)
|
||||
{
|
||||
@ -144,9 +143,9 @@ static int cxl_pmem_set_config_data(struct cxl_mem *cxlm,
|
||||
};
|
||||
memcpy(set_lsa->data, cmd->in_buf, cmd->in_length);
|
||||
|
||||
rc = cxl_mem_mbox_send_cmd(cxlm, CXL_MBOX_OP_SET_LSA, set_lsa,
|
||||
struct_size(set_lsa, data, cmd->in_length),
|
||||
NULL, 0);
|
||||
rc = cxl_mbox_send_cmd(cxlds, CXL_MBOX_OP_SET_LSA, set_lsa,
|
||||
struct_size(set_lsa, data, cmd->in_length),
|
||||
NULL, 0);
|
||||
|
||||
/*
|
||||
* Set "firmware" status (4-packed bytes at the end of the input
|
||||
@ -164,18 +163,18 @@ static int cxl_pmem_nvdimm_ctl(struct nvdimm *nvdimm, unsigned int cmd,
|
||||
struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
|
||||
unsigned long cmd_mask = nvdimm_cmd_mask(nvdimm);
|
||||
struct cxl_memdev *cxlmd = cxl_nvd->cxlmd;
|
||||
struct cxl_mem *cxlm = cxlmd->cxlm;
|
||||
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
||||
|
||||
if (!test_bit(cmd, &cmd_mask))
|
||||
return -ENOTTY;
|
||||
|
||||
switch (cmd) {
|
||||
case ND_CMD_GET_CONFIG_SIZE:
|
||||
return cxl_pmem_get_config_size(cxlm, buf, buf_len);
|
||||
return cxl_pmem_get_config_size(cxlds, buf, buf_len);
|
||||
case ND_CMD_GET_CONFIG_DATA:
|
||||
return cxl_pmem_get_config_data(cxlm, buf, buf_len);
|
||||
return cxl_pmem_get_config_data(cxlds, buf, buf_len);
|
||||
case ND_CMD_SET_CONFIG_DATA:
|
||||
return cxl_pmem_set_config_data(cxlm, buf, buf_len);
|
||||
return cxl_pmem_set_config_data(cxlds, buf, buf_len);
|
||||
default:
|
||||
return -ENOTTY;
|
||||
}
|
||||
@ -266,14 +265,24 @@ static void cxl_nvb_update_state(struct work_struct *work)
|
||||
put_device(&cxl_nvb->dev);
|
||||
}
|
||||
|
||||
static void cxl_nvdimm_bridge_state_work(struct cxl_nvdimm_bridge *cxl_nvb)
|
||||
{
|
||||
/*
|
||||
* Take a reference that the workqueue will drop if new work
|
||||
* gets queued.
|
||||
*/
|
||||
get_device(&cxl_nvb->dev);
|
||||
if (!queue_work(cxl_pmem_wq, &cxl_nvb->state_work))
|
||||
put_device(&cxl_nvb->dev);
|
||||
}
|
||||
|
||||
static void cxl_nvdimm_bridge_remove(struct device *dev)
|
||||
{
|
||||
struct cxl_nvdimm_bridge *cxl_nvb = to_cxl_nvdimm_bridge(dev);
|
||||
|
||||
if (cxl_nvb->state == CXL_NVB_ONLINE)
|
||||
cxl_nvb->state = CXL_NVB_OFFLINE;
|
||||
if (queue_work(cxl_pmem_wq, &cxl_nvb->state_work))
|
||||
get_device(&cxl_nvb->dev);
|
||||
cxl_nvdimm_bridge_state_work(cxl_nvb);
|
||||
}
|
||||
|
||||
static int cxl_nvdimm_bridge_probe(struct device *dev)
|
||||
@ -294,8 +303,7 @@ static int cxl_nvdimm_bridge_probe(struct device *dev)
|
||||
}
|
||||
|
||||
cxl_nvb->state = CXL_NVB_ONLINE;
|
||||
if (queue_work(cxl_pmem_wq, &cxl_nvb->state_work))
|
||||
get_device(&cxl_nvb->dev);
|
||||
cxl_nvdimm_bridge_state_work(cxl_nvb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -307,6 +315,31 @@ static struct cxl_driver cxl_nvdimm_bridge_driver = {
|
||||
.id = CXL_DEVICE_NVDIMM_BRIDGE,
|
||||
};
|
||||
|
||||
/*
|
||||
* Return all bridges to the CXL_NVB_NEW state to invalidate any
|
||||
* ->state_work referring to the now destroyed cxl_pmem_wq.
|
||||
*/
|
||||
static int cxl_nvdimm_bridge_reset(struct device *dev, void *data)
|
||||
{
|
||||
struct cxl_nvdimm_bridge *cxl_nvb;
|
||||
|
||||
if (!is_cxl_nvdimm_bridge(dev))
|
||||
return 0;
|
||||
|
||||
cxl_nvb = to_cxl_nvdimm_bridge(dev);
|
||||
device_lock(dev);
|
||||
cxl_nvb->state = CXL_NVB_NEW;
|
||||
device_unlock(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void destroy_cxl_pmem_wq(void)
|
||||
{
|
||||
destroy_workqueue(cxl_pmem_wq);
|
||||
bus_for_each_dev(&cxl_bus_type, NULL, NULL, cxl_nvdimm_bridge_reset);
|
||||
}
|
||||
|
||||
static __init int cxl_pmem_init(void)
|
||||
{
|
||||
int rc;
|
||||
@ -332,7 +365,7 @@ static __init int cxl_pmem_init(void)
|
||||
err_nvdimm:
|
||||
cxl_driver_unregister(&cxl_nvdimm_bridge_driver);
|
||||
err_bridge:
|
||||
destroy_workqueue(cxl_pmem_wq);
|
||||
destroy_cxl_pmem_wq();
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -340,7 +373,7 @@ static __exit void cxl_pmem_exit(void)
|
||||
{
|
||||
cxl_driver_unregister(&cxl_nvdimm_driver);
|
||||
cxl_driver_unregister(&cxl_nvdimm_bridge_driver);
|
||||
destroy_workqueue(cxl_pmem_wq);
|
||||
destroy_cxl_pmem_wq();
|
||||
}
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
@ -133,6 +133,7 @@ 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);
|
||||
@ -140,6 +141,9 @@ 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 {
|
||||
@ -216,6 +220,8 @@ static inline int acpi_debugger_notify_command_complete(void)
|
||||
struct acpi_subtable_proc {
|
||||
int id;
|
||||
acpi_tbl_entry_handler handler;
|
||||
acpi_tbl_entry_handler_arg handler_arg;
|
||||
void *arg;
|
||||
int count;
|
||||
};
|
||||
|
||||
@ -232,17 +238,31 @@ int acpi_locate_initial_tables (void);
|
||||
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 acpi_table_parse_entries(char *id, unsigned long table_size,
|
||||
int entry_id,
|
||||
acpi_tbl_entry_handler handler,
|
||||
unsigned int max_entries);
|
||||
int __init acpi_table_parse_entries_array(char *id, unsigned long table_size,
|
||||
struct acpi_subtable_proc *proc, int proc_num,
|
||||
unsigned int max_entries);
|
||||
int __init_or_acpilib acpi_table_parse_entries(char *id,
|
||||
unsigned long table_size, int entry_id,
|
||||
acpi_tbl_entry_handler handler, unsigned int max_entries);
|
||||
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);
|
||||
int acpi_table_parse_madt(enum acpi_madt_type id,
|
||||
acpi_tbl_entry_handler handler,
|
||||
unsigned int max_entries);
|
||||
int __init_or_acpilib
|
||||
acpi_table_parse_cedt(enum acpi_cedt_type id,
|
||||
acpi_tbl_entry_handler_arg handler_arg, void *arg);
|
||||
|
||||
int acpi_parse_mcfg (struct acpi_table_header *header);
|
||||
void acpi_table_print_madt_entry (struct acpi_subtable_header *madt);
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
ldflags-y += --wrap=acpi_table_parse_cedt
|
||||
ldflags-y += --wrap=is_acpi_device_node
|
||||
ldflags-y += --wrap=acpi_get_table
|
||||
ldflags-y += --wrap=acpi_put_table
|
||||
ldflags-y += --wrap=acpi_evaluate_integer
|
||||
ldflags-y += --wrap=acpi_pci_find_root
|
||||
ldflags-y += --wrap=pci_walk_bus
|
||||
|
@ -182,6 +182,13 @@ static struct {
|
||||
},
|
||||
};
|
||||
|
||||
struct acpi_cedt_cfmws *mock_cfmws[4] = {
|
||||
[0] = &mock_cedt.cfmws0.cfmws,
|
||||
[1] = &mock_cedt.cfmws1.cfmws,
|
||||
[2] = &mock_cedt.cfmws2.cfmws,
|
||||
[3] = &mock_cedt.cfmws3.cfmws,
|
||||
};
|
||||
|
||||
struct cxl_mock_res {
|
||||
struct list_head list;
|
||||
struct range range;
|
||||
@ -232,12 +239,6 @@ static struct cxl_mock_res *alloc_mock_res(resource_size_t size)
|
||||
|
||||
static int populate_cedt(void)
|
||||
{
|
||||
struct acpi_cedt_cfmws *cfmws[4] = {
|
||||
[0] = &mock_cedt.cfmws0.cfmws,
|
||||
[1] = &mock_cedt.cfmws1.cfmws,
|
||||
[2] = &mock_cedt.cfmws2.cfmws,
|
||||
[3] = &mock_cedt.cfmws3.cfmws,
|
||||
};
|
||||
struct cxl_mock_res *res;
|
||||
int i;
|
||||
|
||||
@ -257,8 +258,8 @@ static int populate_cedt(void)
|
||||
chbs->length = size;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(cfmws); i++) {
|
||||
struct acpi_cedt_cfmws *window = cfmws[i];
|
||||
for (i = 0; i < ARRAY_SIZE(mock_cfmws); i++) {
|
||||
struct acpi_cedt_cfmws *window = mock_cfmws[i];
|
||||
|
||||
res = alloc_mock_res(window->window_size);
|
||||
if (!res)
|
||||
@ -269,21 +270,44 @@ static int populate_cedt(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static acpi_status mock_acpi_get_table(char *signature, u32 instance,
|
||||
struct acpi_table_header **out_table)
|
||||
{
|
||||
if (instance < U32_MAX || strcmp(signature, ACPI_SIG_CEDT) != 0)
|
||||
return acpi_get_table(signature, instance, out_table);
|
||||
/*
|
||||
* WARNING, this hack assumes the format of 'struct
|
||||
* cxl_cfmws_context' and 'struct cxl_chbs_context' share the property that
|
||||
* the first struct member is the device being probed by the cxl_acpi
|
||||
* driver.
|
||||
*/
|
||||
struct cxl_cedt_context {
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
*out_table = (struct acpi_table_header *) &mock_cedt;
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static void mock_acpi_put_table(struct acpi_table_header *table)
|
||||
static int mock_acpi_table_parse_cedt(enum acpi_cedt_type id,
|
||||
acpi_tbl_entry_handler_arg handler_arg,
|
||||
void *arg)
|
||||
{
|
||||
if (table == (struct acpi_table_header *) &mock_cedt)
|
||||
return;
|
||||
acpi_put_table(table);
|
||||
struct cxl_cedt_context *ctx = arg;
|
||||
struct device *dev = ctx->dev;
|
||||
union acpi_subtable_headers *h;
|
||||
unsigned long end;
|
||||
int i;
|
||||
|
||||
if (dev != &cxl_acpi->dev)
|
||||
return acpi_table_parse_cedt(id, handler_arg, arg);
|
||||
|
||||
if (id == ACPI_CEDT_TYPE_CHBS)
|
||||
for (i = 0; i < ARRAY_SIZE(mock_cedt.chbs); i++) {
|
||||
h = (union acpi_subtable_headers *)&mock_cedt.chbs[i];
|
||||
end = (unsigned long)&mock_cedt.chbs[i + 1];
|
||||
handler_arg(h, arg, end);
|
||||
}
|
||||
|
||||
if (id == ACPI_CEDT_TYPE_CFMWS)
|
||||
for (i = 0; i < ARRAY_SIZE(mock_cfmws); i++) {
|
||||
h = (union acpi_subtable_headers *) mock_cfmws[i];
|
||||
end = (unsigned long) h + mock_cfmws[i]->header.length;
|
||||
handler_arg(h, arg, end);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool is_mock_bridge(struct device *dev)
|
||||
@ -388,8 +412,7 @@ static struct cxl_mock_ops cxl_mock_ops = {
|
||||
.is_mock_port = is_mock_port,
|
||||
.is_mock_dev = is_mock_dev,
|
||||
.mock_port = mock_cxl_root_port,
|
||||
.acpi_get_table = mock_acpi_get_table,
|
||||
.acpi_put_table = mock_acpi_put_table,
|
||||
.acpi_table_parse_cedt = mock_acpi_table_parse_cedt,
|
||||
.acpi_evaluate_integer = mock_acpi_evaluate_integer,
|
||||
.acpi_pci_find_root = mock_acpi_pci_find_root,
|
||||
.list = LIST_HEAD_INIT(cxl_mock_ops.list),
|
||||
@ -574,3 +597,4 @@ static __exit void cxl_test_exit(void)
|
||||
module_init(cxl_test_init);
|
||||
module_exit(cxl_test_exit);
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_IMPORT_NS(ACPI);
|
||||
|
@ -28,8 +28,24 @@ static struct cxl_cel_entry mock_cel[] = {
|
||||
.opcode = cpu_to_le16(CXL_MBOX_OP_SET_LSA),
|
||||
.effect = cpu_to_le16(EFFECT(1) | EFFECT(2)),
|
||||
},
|
||||
{
|
||||
.opcode = cpu_to_le16(CXL_MBOX_OP_GET_HEALTH_INFO),
|
||||
.effect = cpu_to_le16(0),
|
||||
},
|
||||
};
|
||||
|
||||
/* See CXL 2.0 Table 181 Get Health Info Output Payload */
|
||||
struct cxl_mbox_health_info {
|
||||
u8 health_status;
|
||||
u8 media_status;
|
||||
u8 ext_status;
|
||||
u8 life_used;
|
||||
__le16 temperature;
|
||||
__le32 dirty_shutdowns;
|
||||
__le32 volatile_errors;
|
||||
__le32 pmem_errors;
|
||||
} __packed;
|
||||
|
||||
static struct {
|
||||
struct cxl_mbox_get_supported_logs gsl;
|
||||
struct cxl_gsl_entry entry;
|
||||
@ -54,7 +70,7 @@ static int mock_gsl(struct cxl_mbox_cmd *cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mock_get_log(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd)
|
||||
static int mock_get_log(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
|
||||
{
|
||||
struct cxl_mbox_get_log *gl = cmd->payload_in;
|
||||
u32 offset = le32_to_cpu(gl->offset);
|
||||
@ -64,7 +80,7 @@ static int mock_get_log(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd)
|
||||
|
||||
if (cmd->size_in < sizeof(*gl))
|
||||
return -EINVAL;
|
||||
if (length > cxlm->payload_size)
|
||||
if (length > cxlds->payload_size)
|
||||
return -EINVAL;
|
||||
if (offset + length > sizeof(mock_cel))
|
||||
return -EINVAL;
|
||||
@ -78,9 +94,9 @@ static int mock_get_log(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mock_id(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd)
|
||||
static int mock_id(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(cxlm->dev);
|
||||
struct platform_device *pdev = to_platform_device(cxlds->dev);
|
||||
struct cxl_mbox_identify id = {
|
||||
.fw_revision = { "mock fw v1 " },
|
||||
.lsa_size = cpu_to_le32(LSA_SIZE),
|
||||
@ -120,10 +136,10 @@ static int mock_id(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mock_get_lsa(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd)
|
||||
static int mock_get_lsa(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
|
||||
{
|
||||
struct cxl_mbox_get_lsa *get_lsa = cmd->payload_in;
|
||||
void *lsa = dev_get_drvdata(cxlm->dev);
|
||||
void *lsa = dev_get_drvdata(cxlds->dev);
|
||||
u32 offset, length;
|
||||
|
||||
if (sizeof(*get_lsa) > cmd->size_in)
|
||||
@ -139,10 +155,10 @@ static int mock_get_lsa(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mock_set_lsa(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd)
|
||||
static int mock_set_lsa(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
|
||||
{
|
||||
struct cxl_mbox_set_lsa *set_lsa = cmd->payload_in;
|
||||
void *lsa = dev_get_drvdata(cxlm->dev);
|
||||
void *lsa = dev_get_drvdata(cxlds->dev);
|
||||
u32 offset, length;
|
||||
|
||||
if (sizeof(*set_lsa) > cmd->size_in)
|
||||
@ -156,9 +172,39 @@ static int mock_set_lsa(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cxl_mock_mbox_send(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd)
|
||||
static int mock_health_info(struct cxl_dev_state *cxlds,
|
||||
struct cxl_mbox_cmd *cmd)
|
||||
{
|
||||
struct device *dev = cxlm->dev;
|
||||
struct cxl_mbox_health_info health_info = {
|
||||
/* set flags for maint needed, perf degraded, hw replacement */
|
||||
.health_status = 0x7,
|
||||
/* set media status to "All Data Lost" */
|
||||
.media_status = 0x3,
|
||||
/*
|
||||
* set ext_status flags for:
|
||||
* ext_life_used: normal,
|
||||
* ext_temperature: critical,
|
||||
* ext_corrected_volatile: warning,
|
||||
* ext_corrected_persistent: normal,
|
||||
*/
|
||||
.ext_status = 0x18,
|
||||
.life_used = 15,
|
||||
.temperature = cpu_to_le16(25),
|
||||
.dirty_shutdowns = cpu_to_le32(10),
|
||||
.volatile_errors = cpu_to_le32(20),
|
||||
.pmem_errors = cpu_to_le32(30),
|
||||
};
|
||||
|
||||
if (cmd->size_out < sizeof(health_info))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(cmd->payload_out, &health_info, sizeof(health_info));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cxl_mock_mbox_send(struct cxl_dev_state *cxlds, struct cxl_mbox_cmd *cmd)
|
||||
{
|
||||
struct device *dev = cxlds->dev;
|
||||
int rc = -EIO;
|
||||
|
||||
switch (cmd->opcode) {
|
||||
@ -166,16 +212,19 @@ static int cxl_mock_mbox_send(struct cxl_mem *cxlm, struct cxl_mbox_cmd *cmd)
|
||||
rc = mock_gsl(cmd);
|
||||
break;
|
||||
case CXL_MBOX_OP_GET_LOG:
|
||||
rc = mock_get_log(cxlm, cmd);
|
||||
rc = mock_get_log(cxlds, cmd);
|
||||
break;
|
||||
case CXL_MBOX_OP_IDENTIFY:
|
||||
rc = mock_id(cxlm, cmd);
|
||||
rc = mock_id(cxlds, cmd);
|
||||
break;
|
||||
case CXL_MBOX_OP_GET_LSA:
|
||||
rc = mock_get_lsa(cxlm, cmd);
|
||||
rc = mock_get_lsa(cxlds, cmd);
|
||||
break;
|
||||
case CXL_MBOX_OP_SET_LSA:
|
||||
rc = mock_set_lsa(cxlm, cmd);
|
||||
rc = mock_set_lsa(cxlds, cmd);
|
||||
break;
|
||||
case CXL_MBOX_OP_GET_HEALTH_INFO:
|
||||
rc = mock_health_info(cxlds, cmd);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
@ -196,7 +245,7 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct cxl_memdev *cxlmd;
|
||||
struct cxl_mem *cxlm;
|
||||
struct cxl_dev_state *cxlds;
|
||||
void *lsa;
|
||||
int rc;
|
||||
|
||||
@ -208,30 +257,30 @@ static int cxl_mock_mem_probe(struct platform_device *pdev)
|
||||
return rc;
|
||||
dev_set_drvdata(dev, lsa);
|
||||
|
||||
cxlm = cxl_mem_create(dev);
|
||||
if (IS_ERR(cxlm))
|
||||
return PTR_ERR(cxlm);
|
||||
cxlds = cxl_dev_state_create(dev);
|
||||
if (IS_ERR(cxlds))
|
||||
return PTR_ERR(cxlds);
|
||||
|
||||
cxlm->mbox_send = cxl_mock_mbox_send;
|
||||
cxlm->payload_size = SZ_4K;
|
||||
cxlds->mbox_send = cxl_mock_mbox_send;
|
||||
cxlds->payload_size = SZ_4K;
|
||||
|
||||
rc = cxl_mem_enumerate_cmds(cxlm);
|
||||
rc = cxl_enumerate_cmds(cxlds);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = cxl_mem_identify(cxlm);
|
||||
rc = cxl_dev_state_identify(cxlds);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = cxl_mem_create_range_info(cxlm);
|
||||
rc = cxl_mem_create_range_info(cxlds);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
cxlmd = devm_cxl_add_memdev(cxlm);
|
||||
cxlmd = devm_cxl_add_memdev(cxlds);
|
||||
if (IS_ERR(cxlmd))
|
||||
return PTR_ERR(cxlmd);
|
||||
|
||||
if (range_len(&cxlm->pmem_range) && IS_ENABLED(CONFIG_CXL_PMEM))
|
||||
if (range_len(&cxlds->pmem_range) && IS_ENABLED(CONFIG_CXL_PMEM))
|
||||
rc = devm_cxl_add_nvdimm(dev, cxlmd);
|
||||
|
||||
return 0;
|
||||
|
@ -58,36 +58,23 @@ bool __wrap_is_acpi_device_node(const struct fwnode_handle *fwnode)
|
||||
}
|
||||
EXPORT_SYMBOL(__wrap_is_acpi_device_node);
|
||||
|
||||
acpi_status __wrap_acpi_get_table(char *signature, u32 instance,
|
||||
struct acpi_table_header **out_table)
|
||||
int __wrap_acpi_table_parse_cedt(enum acpi_cedt_type id,
|
||||
acpi_tbl_entry_handler_arg handler_arg,
|
||||
void *arg)
|
||||
{
|
||||
int index;
|
||||
struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
|
||||
acpi_status status;
|
||||
|
||||
if (ops)
|
||||
status = ops->acpi_get_table(signature, instance, out_table);
|
||||
else
|
||||
status = acpi_get_table(signature, instance, out_table);
|
||||
|
||||
put_cxl_mock_ops(index);
|
||||
|
||||
return status;
|
||||
}
|
||||
EXPORT_SYMBOL(__wrap_acpi_get_table);
|
||||
|
||||
void __wrap_acpi_put_table(struct acpi_table_header *table)
|
||||
{
|
||||
int index;
|
||||
int index, rc;
|
||||
struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
|
||||
|
||||
if (ops)
|
||||
ops->acpi_put_table(table);
|
||||
rc = ops->acpi_table_parse_cedt(id, handler_arg, arg);
|
||||
else
|
||||
acpi_put_table(table);
|
||||
rc = acpi_table_parse_cedt(id, handler_arg, arg);
|
||||
|
||||
put_cxl_mock_ops(index);
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL(__wrap_acpi_put_table);
|
||||
EXPORT_SYMBOL_NS_GPL(__wrap_acpi_table_parse_cedt, ACPI);
|
||||
|
||||
acpi_status __wrap_acpi_evaluate_integer(acpi_handle handle,
|
||||
acpi_string pathname,
|
||||
@ -169,3 +156,4 @@ __wrap_nvdimm_bus_register(struct device *dev,
|
||||
EXPORT_SYMBOL_GPL(__wrap_nvdimm_bus_register);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_IMPORT_NS(ACPI);
|
||||
|
@ -6,9 +6,9 @@
|
||||
struct cxl_mock_ops {
|
||||
struct list_head list;
|
||||
bool (*is_mock_adev)(struct acpi_device *dev);
|
||||
acpi_status (*acpi_get_table)(char *signature, u32 instance,
|
||||
struct acpi_table_header **out_table);
|
||||
void (*acpi_put_table)(struct acpi_table_header *table);
|
||||
int (*acpi_table_parse_cedt)(enum acpi_cedt_type id,
|
||||
acpi_tbl_entry_handler_arg handler_arg,
|
||||
void *arg);
|
||||
bool (*is_mock_bridge)(struct device *dev);
|
||||
acpi_status (*acpi_evaluate_integer)(acpi_handle handle,
|
||||
acpi_string pathname,
|
||||
|
Loading…
Reference in New Issue
Block a user