2022-02-01 12:24:30 -08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
/* Copyright(c) 2022 Intel Corporation. All rights reserved. */
|
2022-05-26 12:15:25 -07:00
|
|
|
#include <linux/seq_file.h>
|
2022-02-01 12:24:30 -08:00
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/delay.h>
|
|
|
|
|
|
|
|
#include "cxlmem.h"
|
|
|
|
#include "core.h"
|
|
|
|
|
|
|
|
/**
|
|
|
|
* DOC: cxl core hdm
|
|
|
|
*
|
|
|
|
* Compute Express Link Host Managed Device Memory, starting with the
|
|
|
|
* CXL 2.0 specification, is managed by an array of HDM Decoder register
|
|
|
|
* instances per CXL port and per CXL endpoint. Define common helpers
|
|
|
|
* for enumerating these registers and capabilities.
|
|
|
|
*/
|
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
struct cxl_rwsem cxl_rwsem = {
|
|
|
|
.region = __RWSEM_INITIALIZER(cxl_rwsem.region),
|
|
|
|
.dpa = __RWSEM_INITIALIZER(cxl_rwsem.dpa),
|
|
|
|
};
|
2022-05-26 12:15:25 -07:00
|
|
|
|
2022-02-01 12:24:30 -08:00
|
|
|
static int add_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
|
|
|
|
int *target_map)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
rc = cxl_decoder_add_locked(cxld, target_map);
|
|
|
|
if (rc) {
|
|
|
|
put_device(&cxld->dev);
|
|
|
|
dev_err(&port->dev, "Failed to add decoder\n");
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = cxl_decoder_autoremove(&port->dev, cxld);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
2025-05-09 17:06:59 +02:00
|
|
|
dev_dbg(port->uport_dev, "%s added to %s\n",
|
|
|
|
dev_name(&cxld->dev), dev_name(&port->dev));
|
2022-02-01 12:24:30 -08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Per the CXL specification (8.2.5.12 CXL HDM Decoder Capability Structure)
|
|
|
|
* single ported host-bridges need not publish a decoder capability when a
|
|
|
|
* passthrough decode can be assumed, i.e. all transactions that the uport sees
|
|
|
|
* are claimed and passed to the single dport. Disable the range until the first
|
|
|
|
* CXL region is enumerated / activated.
|
|
|
|
*/
|
2022-02-01 13:23:14 -08:00
|
|
|
int devm_cxl_add_passthrough_decoder(struct cxl_port *port)
|
2022-02-01 12:24:30 -08:00
|
|
|
{
|
2022-05-18 17:52:23 -07:00
|
|
|
struct cxl_switch_decoder *cxlsd;
|
2022-05-27 20:51:19 -07:00
|
|
|
struct cxl_dport *dport = NULL;
|
2022-02-01 12:24:30 -08:00
|
|
|
int single_port_map[1];
|
2022-05-27 20:51:19 -07:00
|
|
|
unsigned long index;
|
cxl/region: check interleave capability
Since interleave capability is not verified, if the interleave
capability of a target does not match the region need, committing decoder
should have failed at the device end.
In order to checkout this error as quickly as possible, driver needs
to check the interleave capability of target during attaching it to
region.
Per CXL specification r3.1(8.2.4.20.1 CXL HDM Decoder Capability Register),
bits 11 and 12 indicate the capability to establish interleaving in 3, 6,
12 and 16 ways. If these bits are not set, the target cannot be attached to
a region utilizing such interleave ways.
Additionally, bits 8 and 9 represent the capability of the bits used for
interleaving in the address, Linux tracks this in the cxl_port
interleave_mask.
Per CXL specification r3.1(8.2.4.20.13 Decoder Protection):
eIW means encoded Interleave Ways.
eIG means encoded Interleave Granularity.
in HPA:
if eIW is 0 or 8 (interleave ways: 1, 3), all the bits of HPA are used,
the interleave bits are none, the following check is ignored.
if eIW is less than 8 (interleave ways: 2, 4, 8, 16), the interleave bits
start at bit position eIG + 8 and end at eIG + eIW + 8 - 1.
if eIW is greater than 8 (interleave ways: 6, 12), the interleave bits
start at bit position eIG + 8 and end at eIG + eIW - 1.
if the interleave mask is insufficient to cover the required interleave
bits, the target cannot be attached to the region.
Fixes: 384e624bb211 ("cxl/region: Attach endpoint decoders")
Signed-off-by: Yao Xingtao <yaoxt.fnst@fujitsu.com>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Link: https://patch.msgid.link/20240614084755.59503-2-yaoxt.fnst@fujitsu.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2024-06-14 04:47:54 -04:00
|
|
|
struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Capability checks are moot for passthrough decoders, support
|
|
|
|
* any and all possibilities.
|
|
|
|
*/
|
|
|
|
cxlhdm->interleave_mask = ~0U;
|
|
|
|
cxlhdm->iw_cap_mask = ~0UL;
|
2022-02-01 12:24:30 -08:00
|
|
|
|
2022-05-18 17:52:23 -07:00
|
|
|
cxlsd = cxl_switch_decoder_alloc(port, 1);
|
|
|
|
if (IS_ERR(cxlsd))
|
|
|
|
return PTR_ERR(cxlsd);
|
2022-02-01 12:24:30 -08:00
|
|
|
|
|
|
|
device_lock_assert(&port->dev);
|
|
|
|
|
2022-05-27 20:51:19 -07:00
|
|
|
xa_for_each(&port->dports, index, dport)
|
|
|
|
break;
|
2022-02-01 12:24:30 -08:00
|
|
|
single_port_map[0] = dport->port_id;
|
|
|
|
|
2022-05-18 17:52:23 -07:00
|
|
|
return add_hdm_decoder(port, &cxlsd->cxld, single_port_map);
|
2022-02-01 12:24:30 -08:00
|
|
|
}
|
module: Convert symbol namespace to string literal
Clean up the existing export namespace code along the same lines of
commit 33def8498fdd ("treewide: Convert macro and uses of __section(foo)
to __section("foo")") and for the same reason, it is not desired for the
namespace argument to be a macro expansion itself.
Scripted using
git grep -l -e MODULE_IMPORT_NS -e EXPORT_SYMBOL_NS | while read file;
do
awk -i inplace '
/^#define EXPORT_SYMBOL_NS/ {
gsub(/__stringify\(ns\)/, "ns");
print;
next;
}
/^#define MODULE_IMPORT_NS/ {
gsub(/__stringify\(ns\)/, "ns");
print;
next;
}
/MODULE_IMPORT_NS/ {
$0 = gensub(/MODULE_IMPORT_NS\(([^)]*)\)/, "MODULE_IMPORT_NS(\"\\1\")", "g");
}
/EXPORT_SYMBOL_NS/ {
if ($0 ~ /(EXPORT_SYMBOL_NS[^(]*)\(([^,]+),/) {
if ($0 !~ /(EXPORT_SYMBOL_NS[^(]*)\(([^,]+), ([^)]+)\)/ &&
$0 !~ /(EXPORT_SYMBOL_NS[^(]*)\(\)/ &&
$0 !~ /^my/) {
getline line;
gsub(/[[:space:]]*\\$/, "");
gsub(/[[:space:]]/, "", line);
$0 = $0 " " line;
}
$0 = gensub(/(EXPORT_SYMBOL_NS[^(]*)\(([^,]+), ([^)]+)\)/,
"\\1(\\2, \"\\3\")", "g");
}
}
{ print }' $file;
done
Requested-by: Masahiro Yamada <masahiroy@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://mail.google.com/mail/u/2/#inbox/FMfcgzQXKWgMmjdFwwdsfgxzKpVHWPlc
Acked-by: Greg KH <gregkh@linuxfoundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2024-12-02 15:59:47 +01:00
|
|
|
EXPORT_SYMBOL_NS_GPL(devm_cxl_add_passthrough_decoder, "CXL");
|
2022-02-01 12:24:30 -08:00
|
|
|
|
|
|
|
static void parse_hdm_decoder_caps(struct cxl_hdm *cxlhdm)
|
|
|
|
{
|
|
|
|
u32 hdm_cap;
|
|
|
|
|
|
|
|
hdm_cap = readl(cxlhdm->regs.hdm_decoder + CXL_HDM_DECODER_CAP_OFFSET);
|
|
|
|
cxlhdm->decoder_count = cxl_hdm_decoder_count(hdm_cap);
|
|
|
|
cxlhdm->target_count =
|
|
|
|
FIELD_GET(CXL_HDM_DECODER_TARGET_COUNT_MASK, hdm_cap);
|
|
|
|
if (FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_11_8, hdm_cap))
|
|
|
|
cxlhdm->interleave_mask |= GENMASK(11, 8);
|
|
|
|
if (FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_14_12, hdm_cap))
|
|
|
|
cxlhdm->interleave_mask |= GENMASK(14, 12);
|
cxl/region: check interleave capability
Since interleave capability is not verified, if the interleave
capability of a target does not match the region need, committing decoder
should have failed at the device end.
In order to checkout this error as quickly as possible, driver needs
to check the interleave capability of target during attaching it to
region.
Per CXL specification r3.1(8.2.4.20.1 CXL HDM Decoder Capability Register),
bits 11 and 12 indicate the capability to establish interleaving in 3, 6,
12 and 16 ways. If these bits are not set, the target cannot be attached to
a region utilizing such interleave ways.
Additionally, bits 8 and 9 represent the capability of the bits used for
interleaving in the address, Linux tracks this in the cxl_port
interleave_mask.
Per CXL specification r3.1(8.2.4.20.13 Decoder Protection):
eIW means encoded Interleave Ways.
eIG means encoded Interleave Granularity.
in HPA:
if eIW is 0 or 8 (interleave ways: 1, 3), all the bits of HPA are used,
the interleave bits are none, the following check is ignored.
if eIW is less than 8 (interleave ways: 2, 4, 8, 16), the interleave bits
start at bit position eIG + 8 and end at eIG + eIW + 8 - 1.
if eIW is greater than 8 (interleave ways: 6, 12), the interleave bits
start at bit position eIG + 8 and end at eIG + eIW - 1.
if the interleave mask is insufficient to cover the required interleave
bits, the target cannot be attached to the region.
Fixes: 384e624bb211 ("cxl/region: Attach endpoint decoders")
Signed-off-by: Yao Xingtao <yaoxt.fnst@fujitsu.com>
Reviewed-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Link: https://patch.msgid.link/20240614084755.59503-2-yaoxt.fnst@fujitsu.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2024-06-14 04:47:54 -04:00
|
|
|
cxlhdm->iw_cap_mask = BIT(1) | BIT(2) | BIT(4) | BIT(8);
|
|
|
|
if (FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_3_6_12_WAY, hdm_cap))
|
|
|
|
cxlhdm->iw_cap_mask |= BIT(3) | BIT(6) | BIT(12);
|
|
|
|
if (FIELD_GET(CXL_HDM_DECODER_INTERLEAVE_16_WAY, hdm_cap))
|
|
|
|
cxlhdm->iw_cap_mask |= BIT(16);
|
2022-02-01 12:24:30 -08:00
|
|
|
}
|
|
|
|
|
2023-04-03 15:13:37 -07:00
|
|
|
static bool should_emulate_decoders(struct cxl_endpoint_dvsec_info *info)
|
|
|
|
{
|
|
|
|
struct cxl_hdm *cxlhdm;
|
|
|
|
void __iomem *hdm;
|
|
|
|
u32 ctrl;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!info)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
cxlhdm = dev_get_drvdata(&info->port->dev);
|
|
|
|
hdm = cxlhdm->regs.hdm_decoder;
|
|
|
|
|
|
|
|
if (!hdm)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If HDM decoders are present and the driver is in control of
|
|
|
|
* Mem_Enable skip DVSEC based emulation
|
|
|
|
*/
|
|
|
|
if (!info->mem_enabled)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If any decoders are committed already, there should not be any
|
|
|
|
* emulated DVSEC decoders.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < cxlhdm->decoder_count; i++) {
|
|
|
|
ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(i));
|
2023-04-14 11:54:16 -07:00
|
|
|
dev_dbg(&info->port->dev,
|
|
|
|
"decoder%d.%d: committed: %ld base: %#x_%.8x size: %#x_%.8x\n",
|
|
|
|
info->port->id, i,
|
|
|
|
FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMITTED, ctrl),
|
|
|
|
readl(hdm + CXL_HDM_DECODER0_BASE_HIGH_OFFSET(i)),
|
|
|
|
readl(hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(i)),
|
|
|
|
readl(hdm + CXL_HDM_DECODER0_SIZE_HIGH_OFFSET(i)),
|
|
|
|
readl(hdm + CXL_HDM_DECODER0_SIZE_LOW_OFFSET(i)));
|
2023-04-03 15:13:37 -07:00
|
|
|
if (FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMITTED, ctrl))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2022-02-01 12:24:30 -08:00
|
|
|
/**
|
|
|
|
* devm_cxl_setup_hdm - map HDM decoder component registers
|
|
|
|
* @port: cxl_port to map
|
2023-02-14 11:41:30 -08:00
|
|
|
* @info: cached DVSEC range register info
|
2022-02-01 12:24:30 -08:00
|
|
|
*/
|
2023-02-14 11:41:30 -08:00
|
|
|
struct cxl_hdm *devm_cxl_setup_hdm(struct cxl_port *port,
|
|
|
|
struct cxl_endpoint_dvsec_info *info)
|
2022-02-01 12:24:30 -08:00
|
|
|
{
|
2023-10-18 19:17:00 +02:00
|
|
|
struct cxl_register_map *reg_map = &port->reg_map;
|
2022-02-01 12:24:30 -08:00
|
|
|
struct device *dev = &port->dev;
|
|
|
|
struct cxl_hdm *cxlhdm;
|
2022-11-29 10:48:36 -07:00
|
|
|
int rc;
|
2022-02-01 12:24:30 -08:00
|
|
|
|
2022-02-01 13:23:14 -08:00
|
|
|
cxlhdm = devm_kzalloc(dev, sizeof(*cxlhdm), GFP_KERNEL);
|
2022-02-01 12:24:30 -08:00
|
|
|
if (!cxlhdm)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
cxlhdm->port = port;
|
2023-04-04 15:34:12 -07:00
|
|
|
dev_set_drvdata(dev, cxlhdm);
|
2023-02-14 11:41:30 -08:00
|
|
|
|
2023-10-18 19:17:00 +02:00
|
|
|
/* Memory devices can configure device HDM using DVSEC range regs. */
|
|
|
|
if (reg_map->resource == CXL_RESOURCE_NONE) {
|
2023-10-31 12:53:52 +03:00
|
|
|
if (!info || !info->mem_enabled) {
|
2023-10-18 19:17:00 +02:00
|
|
|
dev_err(dev, "No component registers mapped\n");
|
|
|
|
return ERR_PTR(-ENXIO);
|
|
|
|
}
|
|
|
|
|
2023-04-04 15:34:12 -07:00
|
|
|
cxlhdm->decoder_count = info->ranges;
|
|
|
|
return cxlhdm;
|
2022-02-01 12:24:30 -08:00
|
|
|
}
|
|
|
|
|
2023-10-18 19:17:00 +02:00
|
|
|
if (!reg_map->component_map.hdm_decoder.valid) {
|
|
|
|
dev_dbg(&port->dev, "HDM decoder registers not implemented\n");
|
|
|
|
/* unique error code to indicate no HDM decoder capability */
|
|
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = cxl_map_component_regs(reg_map, &cxlhdm->regs,
|
|
|
|
BIT(CXL_CM_CAP_CAP_ID_HDM));
|
|
|
|
if (rc) {
|
|
|
|
dev_err(dev, "Failed to map HDM capability.\n");
|
2022-11-29 10:48:36 -07:00
|
|
|
return ERR_PTR(rc);
|
2023-10-18 19:17:00 +02:00
|
|
|
}
|
2022-02-01 12:24:30 -08:00
|
|
|
|
|
|
|
parse_hdm_decoder_caps(cxlhdm);
|
|
|
|
if (cxlhdm->decoder_count == 0) {
|
|
|
|
dev_err(dev, "Spec violation. Caps invalid\n");
|
|
|
|
return ERR_PTR(-ENXIO);
|
|
|
|
}
|
|
|
|
|
2023-04-03 15:13:37 -07:00
|
|
|
/*
|
|
|
|
* Now that the hdm capability is parsed, decide if range
|
|
|
|
* register emulation is needed and fixup cxlhdm accordingly.
|
|
|
|
*/
|
|
|
|
if (should_emulate_decoders(info)) {
|
|
|
|
dev_dbg(dev, "Fallback map %d range register%s\n", info->ranges,
|
|
|
|
info->ranges > 1 ? "s" : "");
|
|
|
|
cxlhdm->decoder_count = info->ranges;
|
|
|
|
}
|
|
|
|
|
2022-02-01 12:24:30 -08:00
|
|
|
return cxlhdm;
|
|
|
|
}
|
module: Convert symbol namespace to string literal
Clean up the existing export namespace code along the same lines of
commit 33def8498fdd ("treewide: Convert macro and uses of __section(foo)
to __section("foo")") and for the same reason, it is not desired for the
namespace argument to be a macro expansion itself.
Scripted using
git grep -l -e MODULE_IMPORT_NS -e EXPORT_SYMBOL_NS | while read file;
do
awk -i inplace '
/^#define EXPORT_SYMBOL_NS/ {
gsub(/__stringify\(ns\)/, "ns");
print;
next;
}
/^#define MODULE_IMPORT_NS/ {
gsub(/__stringify\(ns\)/, "ns");
print;
next;
}
/MODULE_IMPORT_NS/ {
$0 = gensub(/MODULE_IMPORT_NS\(([^)]*)\)/, "MODULE_IMPORT_NS(\"\\1\")", "g");
}
/EXPORT_SYMBOL_NS/ {
if ($0 ~ /(EXPORT_SYMBOL_NS[^(]*)\(([^,]+),/) {
if ($0 !~ /(EXPORT_SYMBOL_NS[^(]*)\(([^,]+), ([^)]+)\)/ &&
$0 !~ /(EXPORT_SYMBOL_NS[^(]*)\(\)/ &&
$0 !~ /^my/) {
getline line;
gsub(/[[:space:]]*\\$/, "");
gsub(/[[:space:]]/, "", line);
$0 = $0 " " line;
}
$0 = gensub(/(EXPORT_SYMBOL_NS[^(]*)\(([^,]+), ([^)]+)\)/,
"\\1(\\2, \"\\3\")", "g");
}
}
{ print }' $file;
done
Requested-by: Masahiro Yamada <masahiroy@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://mail.google.com/mail/u/2/#inbox/FMfcgzQXKWgMmjdFwwdsfgxzKpVHWPlc
Acked-by: Greg KH <gregkh@linuxfoundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2024-12-02 15:59:47 +01:00
|
|
|
EXPORT_SYMBOL_NS_GPL(devm_cxl_setup_hdm, "CXL");
|
2022-02-01 12:24:30 -08:00
|
|
|
|
2022-05-26 12:15:25 -07:00
|
|
|
static void __cxl_dpa_debug(struct seq_file *file, struct resource *r, int depth)
|
|
|
|
{
|
|
|
|
unsigned long long start = r->start, end = r->end;
|
|
|
|
|
|
|
|
seq_printf(file, "%*s%08llx-%08llx : %s\n", depth * 2, "", start, end,
|
|
|
|
r->name);
|
|
|
|
}
|
|
|
|
|
|
|
|
void cxl_dpa_debug(struct seq_file *file, struct cxl_dev_state *cxlds)
|
|
|
|
{
|
|
|
|
struct resource *p1, *p2;
|
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
guard(rwsem_read)(&cxl_rwsem.dpa);
|
2022-05-26 12:15:25 -07:00
|
|
|
for (p1 = cxlds->dpa_res.child; p1; p1 = p1->sibling) {
|
|
|
|
__cxl_dpa_debug(file, p1, 0);
|
|
|
|
for (p2 = p1->child; p2; p2 = p2->sibling)
|
|
|
|
__cxl_dpa_debug(file, p2, 1);
|
|
|
|
}
|
|
|
|
}
|
module: Convert symbol namespace to string literal
Clean up the existing export namespace code along the same lines of
commit 33def8498fdd ("treewide: Convert macro and uses of __section(foo)
to __section("foo")") and for the same reason, it is not desired for the
namespace argument to be a macro expansion itself.
Scripted using
git grep -l -e MODULE_IMPORT_NS -e EXPORT_SYMBOL_NS | while read file;
do
awk -i inplace '
/^#define EXPORT_SYMBOL_NS/ {
gsub(/__stringify\(ns\)/, "ns");
print;
next;
}
/^#define MODULE_IMPORT_NS/ {
gsub(/__stringify\(ns\)/, "ns");
print;
next;
}
/MODULE_IMPORT_NS/ {
$0 = gensub(/MODULE_IMPORT_NS\(([^)]*)\)/, "MODULE_IMPORT_NS(\"\\1\")", "g");
}
/EXPORT_SYMBOL_NS/ {
if ($0 ~ /(EXPORT_SYMBOL_NS[^(]*)\(([^,]+),/) {
if ($0 !~ /(EXPORT_SYMBOL_NS[^(]*)\(([^,]+), ([^)]+)\)/ &&
$0 !~ /(EXPORT_SYMBOL_NS[^(]*)\(\)/ &&
$0 !~ /^my/) {
getline line;
gsub(/[[:space:]]*\\$/, "");
gsub(/[[:space:]]/, "", line);
$0 = $0 " " line;
}
$0 = gensub(/(EXPORT_SYMBOL_NS[^(]*)\(([^,]+), ([^)]+)\)/,
"\\1(\\2, \"\\3\")", "g");
}
}
{ print }' $file;
done
Requested-by: Masahiro Yamada <masahiroy@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://mail.google.com/mail/u/2/#inbox/FMfcgzQXKWgMmjdFwwdsfgxzKpVHWPlc
Acked-by: Greg KH <gregkh@linuxfoundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2024-12-02 15:59:47 +01:00
|
|
|
EXPORT_SYMBOL_NS_GPL(cxl_dpa_debug, "CXL");
|
2022-05-26 12:15:25 -07:00
|
|
|
|
2025-02-03 20:24:24 -08:00
|
|
|
/* See request_skip() kernel-doc */
|
|
|
|
static resource_size_t __adjust_skip(struct cxl_dev_state *cxlds,
|
|
|
|
const resource_size_t skip_base,
|
|
|
|
const resource_size_t skip_len,
|
|
|
|
const char *requester)
|
|
|
|
{
|
|
|
|
const resource_size_t skip_end = skip_base + skip_len - 1;
|
|
|
|
|
|
|
|
for (int i = 0; i < cxlds->nr_partitions; i++) {
|
|
|
|
const struct resource *part_res = &cxlds->part[i].res;
|
|
|
|
resource_size_t adjust_start, adjust_end, size;
|
|
|
|
|
|
|
|
adjust_start = max(skip_base, part_res->start);
|
|
|
|
adjust_end = min(skip_end, part_res->end);
|
|
|
|
|
|
|
|
if (adjust_end < adjust_start)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
size = adjust_end - adjust_start + 1;
|
|
|
|
|
|
|
|
if (!requester)
|
|
|
|
__release_region(&cxlds->dpa_res, adjust_start, size);
|
|
|
|
else if (!__request_region(&cxlds->dpa_res, adjust_start, size,
|
|
|
|
requester, 0))
|
|
|
|
return adjust_start - skip_base;
|
|
|
|
}
|
|
|
|
|
|
|
|
return skip_len;
|
|
|
|
}
|
|
|
|
#define release_skip(c, b, l) __adjust_skip((c), (b), (l), NULL)
|
|
|
|
|
2022-07-21 17:19:12 -07:00
|
|
|
/*
|
|
|
|
* Must be called in a context that synchronizes against this decoder's
|
|
|
|
* port ->remove() callback (like an endpoint decoder sysfs attribute)
|
|
|
|
*/
|
|
|
|
static void __cxl_dpa_release(struct cxl_endpoint_decoder *cxled)
|
|
|
|
{
|
|
|
|
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
|
2022-05-24 12:04:58 -07:00
|
|
|
struct cxl_port *port = cxled_to_port(cxled);
|
2022-07-21 17:19:12 -07:00
|
|
|
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
|
|
|
struct resource *res = cxled->dpa_res;
|
|
|
|
resource_size_t skip_start;
|
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
lockdep_assert_held_write(&cxl_rwsem.dpa);
|
2022-07-21 17:19:12 -07:00
|
|
|
|
|
|
|
/* save @skip_start, before @res is released */
|
|
|
|
skip_start = res->start - cxled->skip;
|
|
|
|
__release_region(&cxlds->dpa_res, res->start, resource_size(res));
|
|
|
|
if (cxled->skip)
|
2025-02-03 20:24:24 -08:00
|
|
|
release_skip(cxlds, skip_start, cxled->skip);
|
2022-07-21 17:19:12 -07:00
|
|
|
cxled->skip = 0;
|
|
|
|
cxled->dpa_res = NULL;
|
2022-07-27 15:16:46 -07:00
|
|
|
put_device(&cxled->cxld.dev);
|
2022-05-24 12:04:58 -07:00
|
|
|
port->hdm_end--;
|
2022-07-21 17:19:12 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void cxl_dpa_release(void *cxled)
|
|
|
|
{
|
2025-07-11 16:49:32 -07:00
|
|
|
guard(rwsem_write)(&cxl_rwsem.dpa);
|
2022-07-21 17:19:12 -07:00
|
|
|
__cxl_dpa_release(cxled);
|
|
|
|
}
|
|
|
|
|
2022-05-23 18:02:30 -07:00
|
|
|
/*
|
|
|
|
* Must be called from context that will not race port device
|
|
|
|
* unregistration, like decoder sysfs attribute methods
|
|
|
|
*/
|
|
|
|
static void devm_cxl_dpa_release(struct cxl_endpoint_decoder *cxled)
|
|
|
|
{
|
|
|
|
struct cxl_port *port = cxled_to_port(cxled);
|
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
lockdep_assert_held_write(&cxl_rwsem.dpa);
|
2022-05-23 18:02:30 -07:00
|
|
|
devm_remove_action(&port->dev, cxl_dpa_release, cxled);
|
|
|
|
__cxl_dpa_release(cxled);
|
|
|
|
}
|
|
|
|
|
2025-02-03 20:24:24 -08:00
|
|
|
/**
|
|
|
|
* request_skip() - Track DPA 'skip' in @cxlds->dpa_res resource tree
|
|
|
|
* @cxlds: CXL.mem device context that parents @cxled
|
|
|
|
* @cxled: Endpoint decoder establishing new allocation that skips lower DPA
|
|
|
|
* @skip_base: DPA < start of new DPA allocation (DPAnew)
|
|
|
|
* @skip_len: @skip_base + @skip_len == DPAnew
|
|
|
|
*
|
|
|
|
* DPA 'skip' arises from out-of-sequence DPA allocation events relative
|
|
|
|
* to free capacity across multiple partitions. It is a wasteful event
|
|
|
|
* as usable DPA gets thrown away, but if a deployment has, for example,
|
|
|
|
* a dual RAM+PMEM device, wants to use PMEM, and has unallocated RAM
|
|
|
|
* DPA, the free RAM DPA must be sacrificed to start allocating PMEM.
|
|
|
|
* See third "Implementation Note" in CXL 3.1 8.2.4.19.13 "Decoder
|
|
|
|
* Protection" for more details.
|
|
|
|
*
|
|
|
|
* A 'skip' always covers the last allocated DPA in a previous partition
|
|
|
|
* to the start of the current partition to allocate. Allocations never
|
|
|
|
* start in the middle of a partition, and allocations are always
|
|
|
|
* de-allocated in reverse order (see cxl_dpa_free(), or natural devm
|
|
|
|
* unwind order from forced in-order allocation).
|
|
|
|
*
|
|
|
|
* If @cxlds->nr_partitions was guaranteed to be <= 2 then the 'skip'
|
|
|
|
* would always be contained to a single partition. Given
|
|
|
|
* @cxlds->nr_partitions may be > 2 it results in cases where the 'skip'
|
|
|
|
* might span "tail capacity of partition[0], all of partition[1], ...,
|
|
|
|
* all of partition[N-1]" to support allocating from partition[N]. That
|
|
|
|
* in turn interacts with the partition 'struct resource' boundaries
|
|
|
|
* within @cxlds->dpa_res whereby 'skip' requests need to be divided by
|
|
|
|
* partition. I.e. this is a quirk of using a 'struct resource' tree to
|
|
|
|
* detect range conflicts while also tracking partition boundaries in
|
|
|
|
* @cxlds->dpa_res.
|
|
|
|
*/
|
|
|
|
static int request_skip(struct cxl_dev_state *cxlds,
|
|
|
|
struct cxl_endpoint_decoder *cxled,
|
|
|
|
const resource_size_t skip_base,
|
|
|
|
const resource_size_t skip_len)
|
|
|
|
{
|
|
|
|
resource_size_t skipped = __adjust_skip(cxlds, skip_base, skip_len,
|
|
|
|
dev_name(&cxled->cxld.dev));
|
|
|
|
|
|
|
|
if (skipped == skip_len)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
dev_dbg(cxlds->dev,
|
|
|
|
"%s: failed to reserve skipped space (%pa %pa %pa)\n",
|
|
|
|
dev_name(&cxled->cxld.dev), &skip_base, &skip_len, &skipped);
|
|
|
|
|
|
|
|
release_skip(cxlds, skip_base, skipped);
|
|
|
|
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2022-07-21 17:19:12 -07:00
|
|
|
static int __cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
|
|
|
|
resource_size_t base, resource_size_t len,
|
|
|
|
resource_size_t skipped)
|
|
|
|
{
|
|
|
|
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
|
|
|
|
struct cxl_port *port = cxled_to_port(cxled);
|
|
|
|
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
|
|
|
struct device *dev = &port->dev;
|
|
|
|
struct resource *res;
|
2025-02-03 20:24:24 -08:00
|
|
|
int rc;
|
2022-07-21 17:19:12 -07:00
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
lockdep_assert_held_write(&cxl_rwsem.dpa);
|
2022-07-21 17:19:12 -07:00
|
|
|
|
2023-04-14 11:53:55 -07:00
|
|
|
if (!len) {
|
|
|
|
dev_warn(dev, "decoder%d.%d: empty reservation attempted\n",
|
|
|
|
port->id, cxled->cxld.id);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2022-07-21 17:19:12 -07:00
|
|
|
|
|
|
|
if (cxled->dpa_res) {
|
|
|
|
dev_dbg(dev, "decoder%d.%d: existing allocation %pr assigned\n",
|
|
|
|
port->id, cxled->cxld.id, cxled->dpa_res);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2022-05-24 12:04:58 -07:00
|
|
|
if (port->hdm_end + 1 != cxled->cxld.id) {
|
|
|
|
/*
|
|
|
|
* Assumes alloc and commit order is always in hardware instance
|
|
|
|
* order per expectations from 8.2.5.12.20 Committing Decoder
|
|
|
|
* Programming that enforce decoder[m] committed before
|
|
|
|
* decoder[m+1] commit start.
|
|
|
|
*/
|
|
|
|
dev_dbg(dev, "decoder%d.%d: expected decoder%d.%d\n", port->id,
|
|
|
|
cxled->cxld.id, port->id, port->hdm_end + 1);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2022-07-21 17:19:12 -07:00
|
|
|
if (skipped) {
|
2025-02-03 20:24:24 -08:00
|
|
|
rc = request_skip(cxlds, cxled, base - skipped, skipped);
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
2022-07-21 17:19:12 -07:00
|
|
|
}
|
|
|
|
res = __request_region(&cxlds->dpa_res, base, len,
|
|
|
|
dev_name(&cxled->cxld.dev), 0);
|
|
|
|
if (!res) {
|
|
|
|
dev_dbg(dev, "decoder%d.%d: failed to reserve allocation\n",
|
|
|
|
port->id, cxled->cxld.id);
|
|
|
|
if (skipped)
|
2025-02-03 20:24:24 -08:00
|
|
|
release_skip(cxlds, base - skipped, skipped);
|
2022-07-21 17:19:12 -07:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
cxled->dpa_res = res;
|
|
|
|
cxled->skip = skipped;
|
|
|
|
|
2025-02-03 20:24:29 -08:00
|
|
|
/*
|
|
|
|
* When allocating new capacity, ->part is already set, when
|
|
|
|
* discovering decoder settings at initial enumeration, ->part
|
|
|
|
* is not set.
|
|
|
|
*/
|
|
|
|
if (cxled->part < 0)
|
|
|
|
for (int i = 0; cxlds->nr_partitions; i++)
|
|
|
|
if (resource_contains(&cxlds->part[i].res, res)) {
|
|
|
|
cxled->part = i;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cxled->part < 0)
|
2025-02-03 20:24:06 -08:00
|
|
|
dev_warn(dev, "decoder%d.%d: %pr does not map any partition\n",
|
|
|
|
port->id, cxled->cxld.id, res);
|
2022-05-23 12:15:35 -07:00
|
|
|
|
2022-07-27 15:16:46 -07:00
|
|
|
port->hdm_end++;
|
|
|
|
get_device(&cxled->cxld.dev);
|
2022-07-21 17:19:12 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
cxl: Introduce 'struct cxl_dpa_partition' and 'struct cxl_range_info'
The pending efforts to add CXL Accelerator (type-2) device [1], and
Dynamic Capacity (DCD) support [2], tripped on the
no-longer-fit-for-purpose design in the CXL subsystem for tracking
device-physical-address (DPA) metadata. Trip hazards include:
- CXL Memory Devices need to consider a PMEM partition, but Accelerator
devices with CXL.mem likely do not in the common case.
- CXL Memory Devices enumerate DPA through Memory Device mailbox
commands like Partition Info, Accelerators devices do not.
- CXL Memory Devices that support DCD support more than 2 partitions.
Some of the driver algorithms are awkward to expand to > 2 partition
cases.
- DPA performance data is a general capability that can be shared with
accelerators, so tracking it in 'struct cxl_memdev_state' is no longer
suitable.
- Hardcoded assumptions around the PMEM partition always being index-1
if RAM is zero-sized or PMEM is zero sized.
- 'enum cxl_decoder_mode' is sometimes a partition id and sometimes a
memory property, it should be phased in favor of a partition id and
the memory property comes from the partition info.
Towards cleaning up those issues and allowing a smoother landing for the
aforementioned pending efforts, introduce a 'struct cxl_dpa_partition'
array to 'struct cxl_dev_state', and 'struct cxl_range_info' as a shared
way for Memory Devices and Accelerators to initialize the DPA information
in 'struct cxl_dev_state'.
For now, split a new cxl_dpa_setup() from cxl_mem_create_range_info() to
get the new data structure initialized, and cleanup some qos_class init.
Follow on patches will go further to use the new data structure to
cleanup algorithms that are better suited to loop over all possible
partitions.
cxl_dpa_setup() follows the locking expectations of mutating the device
DPA map, and is suitable for Accelerator drivers to use. Accelerators
likely only have one hardcoded 'ram' partition to convey to the
cxl_core.
Link: http://lore.kernel.org/20241230214445.27602-1-alejandro.lucero-palau@amd.com [1]
Link: http://lore.kernel.org/20241210-dcd-type2-upstream-v8-0-812852504400@intel.com [2]
Reviewed-by: Ira Weiny <ira.weiny@intel.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Alejandro Lucero <alucerop@amd.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Tested-by: Alejandro Lucero <alucerop@amd.com>
Link: https://patch.msgid.link/173864305827.668823.13978794102080021276.stgit@dwillia2-xfh.jf.intel.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-02-03 20:24:18 -08:00
|
|
|
static int add_dpa_res(struct device *dev, struct resource *parent,
|
|
|
|
struct resource *res, resource_size_t start,
|
|
|
|
resource_size_t size, const char *type)
|
|
|
|
{
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
*res = (struct resource) {
|
|
|
|
.name = type,
|
|
|
|
.start = start,
|
|
|
|
.end = start + size - 1,
|
|
|
|
.flags = IORESOURCE_MEM,
|
|
|
|
};
|
|
|
|
if (resource_size(res) == 0) {
|
|
|
|
dev_dbg(dev, "DPA(%s): no capacity\n", res->name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
rc = request_resource(parent, res);
|
|
|
|
if (rc) {
|
|
|
|
dev_err(dev, "DPA(%s): failed to track %pr (%d)\n", res->name,
|
|
|
|
res, rc);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_dbg(dev, "DPA(%s): %pr\n", res->name, res);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *cxl_mode_name(enum cxl_partition_mode mode)
|
|
|
|
{
|
|
|
|
switch (mode) {
|
|
|
|
case CXL_PARTMODE_RAM:
|
|
|
|
return "ram";
|
|
|
|
case CXL_PARTMODE_PMEM:
|
|
|
|
return "pmem";
|
|
|
|
default:
|
|
|
|
return "";
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if this fails the caller must destroy @cxlds, there is no recovery */
|
|
|
|
int cxl_dpa_setup(struct cxl_dev_state *cxlds, const struct cxl_dpa_info *info)
|
|
|
|
{
|
|
|
|
struct device *dev = cxlds->dev;
|
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
guard(rwsem_write)(&cxl_rwsem.dpa);
|
cxl: Introduce 'struct cxl_dpa_partition' and 'struct cxl_range_info'
The pending efforts to add CXL Accelerator (type-2) device [1], and
Dynamic Capacity (DCD) support [2], tripped on the
no-longer-fit-for-purpose design in the CXL subsystem for tracking
device-physical-address (DPA) metadata. Trip hazards include:
- CXL Memory Devices need to consider a PMEM partition, but Accelerator
devices with CXL.mem likely do not in the common case.
- CXL Memory Devices enumerate DPA through Memory Device mailbox
commands like Partition Info, Accelerators devices do not.
- CXL Memory Devices that support DCD support more than 2 partitions.
Some of the driver algorithms are awkward to expand to > 2 partition
cases.
- DPA performance data is a general capability that can be shared with
accelerators, so tracking it in 'struct cxl_memdev_state' is no longer
suitable.
- Hardcoded assumptions around the PMEM partition always being index-1
if RAM is zero-sized or PMEM is zero sized.
- 'enum cxl_decoder_mode' is sometimes a partition id and sometimes a
memory property, it should be phased in favor of a partition id and
the memory property comes from the partition info.
Towards cleaning up those issues and allowing a smoother landing for the
aforementioned pending efforts, introduce a 'struct cxl_dpa_partition'
array to 'struct cxl_dev_state', and 'struct cxl_range_info' as a shared
way for Memory Devices and Accelerators to initialize the DPA information
in 'struct cxl_dev_state'.
For now, split a new cxl_dpa_setup() from cxl_mem_create_range_info() to
get the new data structure initialized, and cleanup some qos_class init.
Follow on patches will go further to use the new data structure to
cleanup algorithms that are better suited to loop over all possible
partitions.
cxl_dpa_setup() follows the locking expectations of mutating the device
DPA map, and is suitable for Accelerator drivers to use. Accelerators
likely only have one hardcoded 'ram' partition to convey to the
cxl_core.
Link: http://lore.kernel.org/20241230214445.27602-1-alejandro.lucero-palau@amd.com [1]
Link: http://lore.kernel.org/20241210-dcd-type2-upstream-v8-0-812852504400@intel.com [2]
Reviewed-by: Ira Weiny <ira.weiny@intel.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Alejandro Lucero <alucerop@amd.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Tested-by: Alejandro Lucero <alucerop@amd.com>
Link: https://patch.msgid.link/173864305827.668823.13978794102080021276.stgit@dwillia2-xfh.jf.intel.com
Signed-off-by: Dave Jiang <dave.jiang@intel.com>
2025-02-03 20:24:18 -08:00
|
|
|
|
|
|
|
if (cxlds->nr_partitions)
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
if (!info->size || !info->nr_partitions) {
|
|
|
|
cxlds->dpa_res = DEFINE_RES_MEM(0, 0);
|
|
|
|
cxlds->nr_partitions = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
cxlds->dpa_res = DEFINE_RES_MEM(0, info->size);
|
|
|
|
|
|
|
|
for (int i = 0; i < info->nr_partitions; i++) {
|
|
|
|
const struct cxl_dpa_part_info *part = &info->part[i];
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
cxlds->part[i].perf.qos_class = CXL_QOS_CLASS_INVALID;
|
|
|
|
cxlds->part[i].mode = part->mode;
|
|
|
|
|
|
|
|
/* Require ordered + contiguous partitions */
|
|
|
|
if (i) {
|
|
|
|
const struct cxl_dpa_part_info *prev = &info->part[i - 1];
|
|
|
|
|
|
|
|
if (prev->range.end + 1 != part->range.start)
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
rc = add_dpa_res(dev, &cxlds->dpa_res, &cxlds->part[i].res,
|
|
|
|
part->range.start, range_len(&part->range),
|
|
|
|
cxl_mode_name(part->mode));
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
cxlds->nr_partitions++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(cxl_dpa_setup);
|
|
|
|
|
2023-02-10 01:06:45 -08:00
|
|
|
int devm_cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
|
2022-07-21 17:19:12 -07:00
|
|
|
resource_size_t base, resource_size_t len,
|
|
|
|
resource_size_t skipped)
|
|
|
|
{
|
|
|
|
struct cxl_port *port = cxled_to_port(cxled);
|
|
|
|
int rc;
|
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
scoped_guard(rwsem_write, &cxl_rwsem.dpa)
|
|
|
|
rc = __cxl_dpa_reserve(cxled, base, len, skipped);
|
2022-07-21 17:19:12 -07:00
|
|
|
|
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
return devm_add_action_or_reset(&port->dev, cxl_dpa_release, cxled);
|
|
|
|
}
|
module: Convert symbol namespace to string literal
Clean up the existing export namespace code along the same lines of
commit 33def8498fdd ("treewide: Convert macro and uses of __section(foo)
to __section("foo")") and for the same reason, it is not desired for the
namespace argument to be a macro expansion itself.
Scripted using
git grep -l -e MODULE_IMPORT_NS -e EXPORT_SYMBOL_NS | while read file;
do
awk -i inplace '
/^#define EXPORT_SYMBOL_NS/ {
gsub(/__stringify\(ns\)/, "ns");
print;
next;
}
/^#define MODULE_IMPORT_NS/ {
gsub(/__stringify\(ns\)/, "ns");
print;
next;
}
/MODULE_IMPORT_NS/ {
$0 = gensub(/MODULE_IMPORT_NS\(([^)]*)\)/, "MODULE_IMPORT_NS(\"\\1\")", "g");
}
/EXPORT_SYMBOL_NS/ {
if ($0 ~ /(EXPORT_SYMBOL_NS[^(]*)\(([^,]+),/) {
if ($0 !~ /(EXPORT_SYMBOL_NS[^(]*)\(([^,]+), ([^)]+)\)/ &&
$0 !~ /(EXPORT_SYMBOL_NS[^(]*)\(\)/ &&
$0 !~ /^my/) {
getline line;
gsub(/[[:space:]]*\\$/, "");
gsub(/[[:space:]]/, "", line);
$0 = $0 " " line;
}
$0 = gensub(/(EXPORT_SYMBOL_NS[^(]*)\(([^,]+), ([^)]+)\)/,
"\\1(\\2, \"\\3\")", "g");
}
}
{ print }' $file;
done
Requested-by: Masahiro Yamada <masahiroy@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://mail.google.com/mail/u/2/#inbox/FMfcgzQXKWgMmjdFwwdsfgxzKpVHWPlc
Acked-by: Greg KH <gregkh@linuxfoundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2024-12-02 15:59:47 +01:00
|
|
|
EXPORT_SYMBOL_NS_GPL(devm_cxl_dpa_reserve, "CXL");
|
2022-07-21 17:19:12 -07:00
|
|
|
|
2022-05-23 18:02:30 -07:00
|
|
|
resource_size_t cxl_dpa_size(struct cxl_endpoint_decoder *cxled)
|
|
|
|
{
|
2025-07-11 16:49:32 -07:00
|
|
|
guard(rwsem_read)(&cxl_rwsem.dpa);
|
2022-05-23 18:02:30 -07:00
|
|
|
if (cxled->dpa_res)
|
2025-02-21 09:24:47 +08:00
|
|
|
return resource_size(cxled->dpa_res);
|
2022-05-23 18:02:30 -07:00
|
|
|
|
2025-02-21 09:24:47 +08:00
|
|
|
return 0;
|
2022-05-23 18:02:30 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
resource_size_t cxl_dpa_resource_start(struct cxl_endpoint_decoder *cxled)
|
|
|
|
{
|
|
|
|
resource_size_t base = -1;
|
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
lockdep_assert_held(&cxl_rwsem.dpa);
|
2022-05-23 18:02:30 -07:00
|
|
|
if (cxled->dpa_res)
|
|
|
|
base = cxled->dpa_res->start;
|
|
|
|
|
|
|
|
return base;
|
|
|
|
}
|
|
|
|
|
2025-07-11 11:23:55 +08:00
|
|
|
bool cxl_resource_contains_addr(const struct resource *res, const resource_size_t addr)
|
|
|
|
{
|
|
|
|
struct resource _addr = DEFINE_RES_MEM(addr, 1);
|
|
|
|
|
|
|
|
return resource_contains(res, &_addr);
|
|
|
|
}
|
|
|
|
|
2022-05-23 18:02:30 -07:00
|
|
|
int cxl_dpa_free(struct cxl_endpoint_decoder *cxled)
|
|
|
|
{
|
|
|
|
struct cxl_port *port = cxled_to_port(cxled);
|
|
|
|
struct device *dev = &cxled->cxld.dev;
|
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
guard(rwsem_write)(&cxl_rwsem.dpa);
|
2025-02-21 09:24:50 +08:00
|
|
|
if (!cxled->dpa_res)
|
|
|
|
return 0;
|
2022-06-04 15:49:53 -07:00
|
|
|
if (cxled->cxld.region) {
|
|
|
|
dev_dbg(dev, "decoder assigned to: %s\n",
|
|
|
|
dev_name(&cxled->cxld.region->dev));
|
2025-02-21 09:24:50 +08:00
|
|
|
return -EBUSY;
|
2022-06-04 15:49:53 -07:00
|
|
|
}
|
2022-05-23 18:02:30 -07:00
|
|
|
if (cxled->cxld.flags & CXL_DECODER_F_ENABLE) {
|
|
|
|
dev_dbg(dev, "decoder enabled\n");
|
2025-02-21 09:24:50 +08:00
|
|
|
return -EBUSY;
|
2022-05-23 18:02:30 -07:00
|
|
|
}
|
|
|
|
if (cxled->cxld.id != port->hdm_end) {
|
|
|
|
dev_dbg(dev, "expected decoder%d.%d\n", port->id,
|
|
|
|
port->hdm_end);
|
2025-02-21 09:24:50 +08:00
|
|
|
return -EBUSY;
|
2022-05-23 18:02:30 -07:00
|
|
|
}
|
2025-02-21 09:24:50 +08:00
|
|
|
|
2022-05-23 18:02:30 -07:00
|
|
|
devm_cxl_dpa_release(cxled);
|
2025-02-21 09:24:50 +08:00
|
|
|
return 0;
|
2022-05-23 18:02:30 -07:00
|
|
|
}
|
|
|
|
|
2025-02-03 20:24:29 -08:00
|
|
|
int cxl_dpa_set_part(struct cxl_endpoint_decoder *cxled,
|
|
|
|
enum cxl_partition_mode mode)
|
2022-05-23 18:02:30 -07:00
|
|
|
{
|
|
|
|
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
|
|
|
|
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
|
|
|
struct device *dev = &cxled->cxld.dev;
|
2025-02-03 20:24:29 -08:00
|
|
|
int part;
|
2022-05-23 18:02:30 -07:00
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
guard(rwsem_write)(&cxl_rwsem.dpa);
|
2024-11-07 14:58:23 -06:00
|
|
|
if (cxled->cxld.flags & CXL_DECODER_F_ENABLE)
|
|
|
|
return -EBUSY;
|
2022-05-23 18:02:30 -07:00
|
|
|
|
2025-02-03 20:24:29 -08:00
|
|
|
for (part = 0; part < cxlds->nr_partitions; part++)
|
|
|
|
if (cxlds->part[part].mode == mode)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (part >= cxlds->nr_partitions) {
|
|
|
|
dev_dbg(dev, "unsupported mode: %d\n", mode);
|
|
|
|
return -EINVAL;
|
2022-05-23 18:02:30 -07:00
|
|
|
}
|
2025-02-03 20:24:29 -08:00
|
|
|
|
|
|
|
if (!resource_size(&cxlds->part[part].res)) {
|
|
|
|
dev_dbg(dev, "no available capacity for mode: %d\n", mode);
|
2024-11-07 14:58:23 -06:00
|
|
|
return -ENXIO;
|
2022-05-23 18:02:30 -07:00
|
|
|
}
|
|
|
|
|
2025-02-03 20:24:29 -08:00
|
|
|
cxled->part = part;
|
2024-11-07 14:58:23 -06:00
|
|
|
return 0;
|
2022-05-23 18:02:30 -07:00
|
|
|
}
|
|
|
|
|
2024-02-22 09:14:02 +03:00
|
|
|
static int __cxl_dpa_alloc(struct cxl_endpoint_decoder *cxled, u64 size)
|
2022-05-23 18:02:30 -07:00
|
|
|
{
|
|
|
|
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
|
|
|
|
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
|
|
|
struct device *dev = &cxled->cxld.dev;
|
2025-02-03 20:24:24 -08:00
|
|
|
struct resource *res, *prev = NULL;
|
|
|
|
resource_size_t start, avail, skip, skip_start;
|
2022-05-23 18:02:30 -07:00
|
|
|
struct resource *p, *last;
|
2025-03-14 15:26:23 -07:00
|
|
|
int part;
|
2022-05-23 18:02:30 -07:00
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
guard(rwsem_write)(&cxl_rwsem.dpa);
|
2022-06-04 15:49:53 -07:00
|
|
|
if (cxled->cxld.region) {
|
|
|
|
dev_dbg(dev, "decoder attached to %s\n",
|
|
|
|
dev_name(&cxled->cxld.region->dev));
|
2025-02-21 09:24:51 +08:00
|
|
|
return -EBUSY;
|
2022-06-04 15:49:53 -07:00
|
|
|
}
|
|
|
|
|
2022-05-23 18:02:30 -07:00
|
|
|
if (cxled->cxld.flags & CXL_DECODER_F_ENABLE) {
|
|
|
|
dev_dbg(dev, "decoder enabled\n");
|
2025-02-21 09:24:51 +08:00
|
|
|
return -EBUSY;
|
2022-05-23 18:02:30 -07:00
|
|
|
}
|
|
|
|
|
2025-02-03 20:24:29 -08:00
|
|
|
part = cxled->part;
|
2025-02-03 20:24:24 -08:00
|
|
|
if (part < 0) {
|
2025-02-03 20:24:29 -08:00
|
|
|
dev_dbg(dev, "partition not set\n");
|
2025-03-14 15:26:23 -07:00
|
|
|
return -EBUSY;
|
2025-02-03 20:24:24 -08:00
|
|
|
}
|
2022-05-23 18:02:30 -07:00
|
|
|
|
2025-02-03 20:24:24 -08:00
|
|
|
res = &cxlds->part[part].res;
|
|
|
|
for (p = res->child, last = NULL; p; p = p->sibling)
|
2022-05-23 18:02:30 -07:00
|
|
|
last = p;
|
|
|
|
if (last)
|
2025-02-03 20:24:24 -08:00
|
|
|
start = last->end + 1;
|
2022-05-23 18:02:30 -07:00
|
|
|
else
|
2025-02-03 20:24:24 -08:00
|
|
|
start = res->start;
|
2022-08-05 15:12:27 -07:00
|
|
|
|
2025-02-03 20:24:24 -08:00
|
|
|
/*
|
|
|
|
* To allocate at partition N, a skip needs to be calculated for all
|
|
|
|
* unallocated space at lower partitions indices.
|
|
|
|
*
|
|
|
|
* If a partition has any allocations, the search can end because a
|
|
|
|
* previous cxl_dpa_alloc() invocation is assumed to have accounted for
|
|
|
|
* all previous partitions.
|
|
|
|
*/
|
|
|
|
skip_start = CXL_RESOURCE_NONE;
|
|
|
|
for (int i = part; i; i--) {
|
|
|
|
prev = &cxlds->part[i - 1].res;
|
|
|
|
for (p = prev->child, last = NULL; p; p = p->sibling)
|
|
|
|
last = p;
|
|
|
|
if (last) {
|
|
|
|
skip_start = last->end + 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
skip_start = prev->start;
|
2022-05-23 18:02:30 -07:00
|
|
|
}
|
|
|
|
|
2025-02-03 20:24:24 -08:00
|
|
|
avail = res->end - start + 1;
|
|
|
|
if (skip_start == CXL_RESOURCE_NONE)
|
|
|
|
skip = 0;
|
|
|
|
else
|
|
|
|
skip = res->start - skip_start;
|
|
|
|
|
2022-05-23 18:02:30 -07:00
|
|
|
if (size > avail) {
|
2024-02-22 09:14:02 +03:00
|
|
|
dev_dbg(dev, "%llu exceeds available %s capacity: %llu\n", size,
|
|
|
|
res->name, (u64)avail);
|
2025-02-21 09:24:51 +08:00
|
|
|
return -ENOSPC;
|
2022-05-23 18:02:30 -07:00
|
|
|
}
|
|
|
|
|
2025-02-21 09:24:51 +08:00
|
|
|
return __cxl_dpa_reserve(cxled, start, size, skip);
|
|
|
|
}
|
|
|
|
|
2024-02-22 09:14:02 +03:00
|
|
|
int cxl_dpa_alloc(struct cxl_endpoint_decoder *cxled, u64 size)
|
2025-02-21 09:24:51 +08:00
|
|
|
{
|
|
|
|
struct cxl_port *port = cxled_to_port(cxled);
|
|
|
|
int rc;
|
2022-05-23 18:02:30 -07:00
|
|
|
|
2025-02-21 09:24:51 +08:00
|
|
|
rc = __cxl_dpa_alloc(cxled, size);
|
2022-05-23 18:02:30 -07:00
|
|
|
if (rc)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
return devm_add_action_or_reset(&port->dev, cxl_dpa_release, cxled);
|
|
|
|
}
|
|
|
|
|
2022-06-08 22:56:37 -07:00
|
|
|
static void cxld_set_interleave(struct cxl_decoder *cxld, u32 *ctrl)
|
|
|
|
{
|
|
|
|
u16 eig;
|
|
|
|
u8 eiw;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Input validation ensures these warns never fire, but otherwise
|
|
|
|
* suppress unititalized variable usage warnings.
|
|
|
|
*/
|
2022-12-05 14:20:01 -07:00
|
|
|
if (WARN_ONCE(ways_to_eiw(cxld->interleave_ways, &eiw),
|
2022-06-08 22:56:37 -07:00
|
|
|
"invalid interleave_ways: %d\n", cxld->interleave_ways))
|
|
|
|
return;
|
2022-12-05 14:16:07 -07:00
|
|
|
if (WARN_ONCE(granularity_to_eig(cxld->interleave_granularity, &eig),
|
2022-06-08 22:56:37 -07:00
|
|
|
"invalid interleave_granularity: %d\n",
|
|
|
|
cxld->interleave_granularity))
|
|
|
|
return;
|
|
|
|
|
|
|
|
u32p_replace_bits(ctrl, eig, CXL_HDM_DECODER0_CTRL_IG_MASK);
|
|
|
|
u32p_replace_bits(ctrl, eiw, CXL_HDM_DECODER0_CTRL_IW_MASK);
|
|
|
|
*ctrl |= CXL_HDM_DECODER0_CTRL_COMMIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void cxld_set_type(struct cxl_decoder *cxld, u32 *ctrl)
|
|
|
|
{
|
2023-06-14 18:30:13 -07:00
|
|
|
u32p_replace_bits(ctrl,
|
|
|
|
!!(cxld->target_type == CXL_DECODER_HOSTONLYMEM),
|
2023-06-14 18:30:19 -07:00
|
|
|
CXL_HDM_DECODER0_CTRL_HOSTONLY);
|
2022-06-08 22:56:37 -07:00
|
|
|
}
|
|
|
|
|
2023-10-31 14:09:19 -07:00
|
|
|
static void cxlsd_set_targets(struct cxl_switch_decoder *cxlsd, u64 *tgt)
|
2022-06-08 22:56:37 -07:00
|
|
|
{
|
|
|
|
struct cxl_dport **t = &cxlsd->target[0];
|
|
|
|
int ways = cxlsd->cxld.interleave_ways;
|
|
|
|
|
|
|
|
*tgt = FIELD_PREP(GENMASK(7, 0), t[0]->port_id);
|
|
|
|
if (ways > 1)
|
|
|
|
*tgt |= FIELD_PREP(GENMASK(15, 8), t[1]->port_id);
|
|
|
|
if (ways > 2)
|
|
|
|
*tgt |= FIELD_PREP(GENMASK(23, 16), t[2]->port_id);
|
|
|
|
if (ways > 3)
|
|
|
|
*tgt |= FIELD_PREP(GENMASK(31, 24), t[3]->port_id);
|
|
|
|
if (ways > 4)
|
|
|
|
*tgt |= FIELD_PREP(GENMASK_ULL(39, 32), t[4]->port_id);
|
|
|
|
if (ways > 5)
|
|
|
|
*tgt |= FIELD_PREP(GENMASK_ULL(47, 40), t[5]->port_id);
|
|
|
|
if (ways > 6)
|
|
|
|
*tgt |= FIELD_PREP(GENMASK_ULL(55, 48), t[6]->port_id);
|
|
|
|
if (ways > 7)
|
|
|
|
*tgt |= FIELD_PREP(GENMASK_ULL(63, 56), t[7]->port_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Per CXL 2.0 8.2.5.12.20 Committing Decoder Programming, hardware must set
|
|
|
|
* committed or error within 10ms, but just be generous with 20ms to account for
|
|
|
|
* clock skew and other marginal behavior
|
|
|
|
*/
|
|
|
|
#define COMMIT_TIMEOUT_MS 20
|
|
|
|
static int cxld_await_commit(void __iomem *hdm, int id)
|
|
|
|
{
|
|
|
|
u32 ctrl;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < COMMIT_TIMEOUT_MS; i++) {
|
|
|
|
ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id));
|
|
|
|
if (FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMIT_ERROR, ctrl)) {
|
|
|
|
ctrl &= ~CXL_HDM_DECODER0_CTRL_COMMIT;
|
|
|
|
writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id));
|
|
|
|
return -EIO;
|
|
|
|
}
|
|
|
|
if (FIELD_GET(CXL_HDM_DECODER0_CTRL_COMMITTED, ctrl))
|
|
|
|
return 0;
|
|
|
|
fsleep(1000);
|
|
|
|
}
|
|
|
|
|
|
|
|
return -ETIMEDOUT;
|
|
|
|
}
|
|
|
|
|
2025-07-11 16:49:27 -07:00
|
|
|
static void setup_hw_decoder(struct cxl_decoder *cxld, void __iomem *hdm)
|
2022-06-08 22:56:37 -07:00
|
|
|
{
|
2025-07-11 16:49:27 -07:00
|
|
|
int id = cxld->id;
|
2022-06-08 22:56:37 -07:00
|
|
|
u64 base, size;
|
|
|
|
u32 ctrl;
|
|
|
|
|
|
|
|
/* common decoder settings */
|
|
|
|
ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(cxld->id));
|
|
|
|
cxld_set_interleave(cxld, &ctrl);
|
|
|
|
cxld_set_type(cxld, &ctrl);
|
2022-08-05 13:27:40 -07:00
|
|
|
base = cxld->hpa_range.start;
|
|
|
|
size = range_len(&cxld->hpa_range);
|
2022-06-08 22:56:37 -07:00
|
|
|
|
|
|
|
writel(upper_32_bits(base), hdm + CXL_HDM_DECODER0_BASE_HIGH_OFFSET(id));
|
|
|
|
writel(lower_32_bits(base), hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(id));
|
|
|
|
writel(upper_32_bits(size), hdm + CXL_HDM_DECODER0_SIZE_HIGH_OFFSET(id));
|
|
|
|
writel(lower_32_bits(size), hdm + CXL_HDM_DECODER0_SIZE_LOW_OFFSET(id));
|
|
|
|
|
|
|
|
if (is_switch_decoder(&cxld->dev)) {
|
|
|
|
struct cxl_switch_decoder *cxlsd =
|
|
|
|
to_cxl_switch_decoder(&cxld->dev);
|
|
|
|
void __iomem *tl_hi = hdm + CXL_HDM_DECODER0_TL_HIGH(id);
|
|
|
|
void __iomem *tl_lo = hdm + CXL_HDM_DECODER0_TL_LOW(id);
|
|
|
|
u64 targets;
|
|
|
|
|
2023-10-31 14:09:19 -07:00
|
|
|
cxlsd_set_targets(cxlsd, &targets);
|
2022-06-08 22:56:37 -07:00
|
|
|
writel(upper_32_bits(targets), tl_hi);
|
|
|
|
writel(lower_32_bits(targets), tl_lo);
|
|
|
|
} else {
|
|
|
|
struct cxl_endpoint_decoder *cxled =
|
|
|
|
to_cxl_endpoint_decoder(&cxld->dev);
|
|
|
|
void __iomem *sk_hi = hdm + CXL_HDM_DECODER0_SKIP_HIGH(id);
|
|
|
|
void __iomem *sk_lo = hdm + CXL_HDM_DECODER0_SKIP_LOW(id);
|
|
|
|
|
|
|
|
writel(upper_32_bits(cxled->skip), sk_hi);
|
|
|
|
writel(lower_32_bits(cxled->skip), sk_lo);
|
|
|
|
}
|
|
|
|
|
|
|
|
writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id));
|
2025-07-11 16:49:27 -07:00
|
|
|
}
|
|
|
|
|
2022-06-08 22:56:37 -07:00
|
|
|
static int cxl_decoder_commit(struct cxl_decoder *cxld)
|
|
|
|
{
|
|
|
|
struct cxl_port *port = to_cxl_port(cxld->dev.parent);
|
|
|
|
struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev);
|
|
|
|
void __iomem *hdm = cxlhdm->regs.hdm_decoder;
|
|
|
|
int id = cxld->id, rc;
|
|
|
|
|
|
|
|
if (cxld->flags & CXL_DECODER_F_ENABLE)
|
|
|
|
return 0;
|
|
|
|
|
2023-10-16 10:57:48 -07:00
|
|
|
if (cxl_num_decoders_committed(port) != id) {
|
2022-06-08 22:56:37 -07:00
|
|
|
dev_dbg(&port->dev,
|
|
|
|
"%s: out of order commit, expected decoder%d.%d\n",
|
2023-10-16 10:57:48 -07:00
|
|
|
dev_name(&cxld->dev), port->id,
|
|
|
|
cxl_num_decoders_committed(port));
|
2022-06-08 22:56:37 -07:00
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
|
2023-10-04 18:35:01 -07:00
|
|
|
/*
|
|
|
|
* For endpoint decoders hosted on CXL memory devices that
|
|
|
|
* support the sanitize operation, make sure sanitize is not in-flight.
|
|
|
|
*/
|
|
|
|
if (is_endpoint_decoder(&cxld->dev)) {
|
|
|
|
struct cxl_endpoint_decoder *cxled =
|
|
|
|
to_cxl_endpoint_decoder(&cxld->dev);
|
|
|
|
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
|
|
|
|
struct cxl_memdev_state *mds =
|
|
|
|
to_cxl_memdev_state(cxlmd->cxlds);
|
|
|
|
|
|
|
|
if (mds && mds->security.sanitize_active) {
|
|
|
|
dev_dbg(&cxlmd->dev,
|
|
|
|
"attempted to commit %s during sanitize\n",
|
|
|
|
dev_name(&cxld->dev));
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
scoped_guard(rwsem_read, &cxl_rwsem.dpa)
|
|
|
|
setup_hw_decoder(cxld, hdm);
|
2022-06-08 22:56:37 -07:00
|
|
|
|
|
|
|
port->commit_end++;
|
|
|
|
rc = cxld_await_commit(hdm, cxld->id);
|
|
|
|
if (rc) {
|
|
|
|
dev_dbg(&port->dev, "%s: error %d committing decoder\n",
|
|
|
|
dev_name(&cxld->dev), rc);
|
|
|
|
cxld->reset(cxld);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
cxld->flags |= CXL_DECODER_F_ENABLE;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-01-05 16:34:07 +08:00
|
|
|
static int commit_reap(struct device *dev, void *data)
|
cxl/port: Fix use-after-free, permit out-of-order decoder shutdown
In support of investigating an initialization failure report [1],
cxl_test was updated to register mock memory-devices after the mock
root-port/bus device had been registered. That led to cxl_test crashing
with a use-after-free bug with the following signature:
cxl_port_attach_region: cxl region3: cxl_host_bridge.0:port3 decoder3.0 add: mem0:decoder7.0 @ 0 next: cxl_switch_uport.0 nr_eps: 1 nr_targets: 1
cxl_port_attach_region: cxl region3: cxl_host_bridge.0:port3 decoder3.0 add: mem4:decoder14.0 @ 1 next: cxl_switch_uport.0 nr_eps: 2 nr_targets: 1
cxl_port_setup_targets: cxl region3: cxl_switch_uport.0:port6 target[0] = cxl_switch_dport.0 for mem0:decoder7.0 @ 0
1) cxl_port_setup_targets: cxl region3: cxl_switch_uport.0:port6 target[1] = cxl_switch_dport.4 for mem4:decoder14.0 @ 1
[..]
cxld_unregister: cxl decoder14.0:
cxl_region_decode_reset: cxl_region region3:
mock_decoder_reset: cxl_port port3: decoder3.0 reset
2) mock_decoder_reset: cxl_port port3: decoder3.0: out of order reset, expected decoder3.1
cxl_endpoint_decoder_release: cxl decoder14.0:
[..]
cxld_unregister: cxl decoder7.0:
3) cxl_region_decode_reset: cxl_region region3:
Oops: general protection fault, probably for non-canonical address 0x6b6b6b6b6b6b6bc3: 0000 [#1] PREEMPT SMP PTI
[..]
RIP: 0010:to_cxl_port+0x8/0x60 [cxl_core]
[..]
Call Trace:
<TASK>
cxl_region_decode_reset+0x69/0x190 [cxl_core]
cxl_region_detach+0xe8/0x210 [cxl_core]
cxl_decoder_kill_region+0x27/0x40 [cxl_core]
cxld_unregister+0x5d/0x60 [cxl_core]
At 1) a region has been established with 2 endpoint decoders (7.0 and
14.0). Those endpoints share a common switch-decoder in the topology
(3.0). At teardown, 2), decoder14.0 is the first to be removed and hits
the "out of order reset case" in the switch decoder. The effect though
is that region3 cleanup is aborted leaving it in-tact and
referencing decoder14.0. At 3) the second attempt to teardown region3
trips over the stale decoder14.0 object which has long since been
deleted.
The fix here is to recognize that the CXL specification places no
mandate on in-order shutdown of switch-decoders, the driver enforces
in-order allocation, and hardware enforces in-order commit. So, rather
than fail and leave objects dangling, always remove them.
In support of making cxl_region_decode_reset() always succeed,
cxl_region_invalidate_memregion() failures are turned into warnings.
Crashing the kernel is ok there since system integrity is at risk if
caches cannot be managed around physical address mutation events like
CXL region destruction.
A new device_for_each_child_reverse_from() is added to cleanup
port->commit_end after all dependent decoders have been disabled. In
other words if decoders are allocated 0->1->2 and disabled 1->2->0 then
port->commit_end only decrements from 2 after 2 has been disabled, and
it decrements all the way to zero since 1 was disabled previously.
Link: http://lore.kernel.org/20241004212504.1246-1-gourry@gourry.net [1]
Cc: stable@vger.kernel.org
Fixes: 176baefb2eb5 ("cxl/hdm: Commit decoder state to hardware")
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Alison Schofield <alison.schofield@intel.com>
Cc: Ira Weiny <ira.weiny@intel.com>
Cc: Zijun Hu <quic_zijuhu@quicinc.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Ira Weiny <ira.weiny@intel.com>
Link: https://patch.msgid.link/172964782781.81806.17902885593105284330.stgit@dwillia2-xfh.jf.intel.com
Signed-off-by: Ira Weiny <ira.weiny@intel.com>
2024-10-22 18:43:49 -07:00
|
|
|
{
|
|
|
|
struct cxl_port *port = to_cxl_port(dev->parent);
|
|
|
|
struct cxl_decoder *cxld;
|
|
|
|
|
|
|
|
if (!is_switch_decoder(dev) && !is_endpoint_decoder(dev))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cxld = to_cxl_decoder(dev);
|
|
|
|
if (port->commit_end == cxld->id &&
|
|
|
|
((cxld->flags & CXL_DECODER_F_ENABLE) == 0)) {
|
|
|
|
port->commit_end--;
|
|
|
|
dev_dbg(&port->dev, "reap: %s commit_end: %d\n",
|
|
|
|
dev_name(&cxld->dev), port->commit_end);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cxl_port_commit_reap(struct cxl_decoder *cxld)
|
|
|
|
{
|
|
|
|
struct cxl_port *port = to_cxl_port(cxld->dev.parent);
|
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
lockdep_assert_held_write(&cxl_rwsem.region);
|
cxl/port: Fix use-after-free, permit out-of-order decoder shutdown
In support of investigating an initialization failure report [1],
cxl_test was updated to register mock memory-devices after the mock
root-port/bus device had been registered. That led to cxl_test crashing
with a use-after-free bug with the following signature:
cxl_port_attach_region: cxl region3: cxl_host_bridge.0:port3 decoder3.0 add: mem0:decoder7.0 @ 0 next: cxl_switch_uport.0 nr_eps: 1 nr_targets: 1
cxl_port_attach_region: cxl region3: cxl_host_bridge.0:port3 decoder3.0 add: mem4:decoder14.0 @ 1 next: cxl_switch_uport.0 nr_eps: 2 nr_targets: 1
cxl_port_setup_targets: cxl region3: cxl_switch_uport.0:port6 target[0] = cxl_switch_dport.0 for mem0:decoder7.0 @ 0
1) cxl_port_setup_targets: cxl region3: cxl_switch_uport.0:port6 target[1] = cxl_switch_dport.4 for mem4:decoder14.0 @ 1
[..]
cxld_unregister: cxl decoder14.0:
cxl_region_decode_reset: cxl_region region3:
mock_decoder_reset: cxl_port port3: decoder3.0 reset
2) mock_decoder_reset: cxl_port port3: decoder3.0: out of order reset, expected decoder3.1
cxl_endpoint_decoder_release: cxl decoder14.0:
[..]
cxld_unregister: cxl decoder7.0:
3) cxl_region_decode_reset: cxl_region region3:
Oops: general protection fault, probably for non-canonical address 0x6b6b6b6b6b6b6bc3: 0000 [#1] PREEMPT SMP PTI
[..]
RIP: 0010:to_cxl_port+0x8/0x60 [cxl_core]
[..]
Call Trace:
<TASK>
cxl_region_decode_reset+0x69/0x190 [cxl_core]
cxl_region_detach+0xe8/0x210 [cxl_core]
cxl_decoder_kill_region+0x27/0x40 [cxl_core]
cxld_unregister+0x5d/0x60 [cxl_core]
At 1) a region has been established with 2 endpoint decoders (7.0 and
14.0). Those endpoints share a common switch-decoder in the topology
(3.0). At teardown, 2), decoder14.0 is the first to be removed and hits
the "out of order reset case" in the switch decoder. The effect though
is that region3 cleanup is aborted leaving it in-tact and
referencing decoder14.0. At 3) the second attempt to teardown region3
trips over the stale decoder14.0 object which has long since been
deleted.
The fix here is to recognize that the CXL specification places no
mandate on in-order shutdown of switch-decoders, the driver enforces
in-order allocation, and hardware enforces in-order commit. So, rather
than fail and leave objects dangling, always remove them.
In support of making cxl_region_decode_reset() always succeed,
cxl_region_invalidate_memregion() failures are turned into warnings.
Crashing the kernel is ok there since system integrity is at risk if
caches cannot be managed around physical address mutation events like
CXL region destruction.
A new device_for_each_child_reverse_from() is added to cleanup
port->commit_end after all dependent decoders have been disabled. In
other words if decoders are allocated 0->1->2 and disabled 1->2->0 then
port->commit_end only decrements from 2 after 2 has been disabled, and
it decrements all the way to zero since 1 was disabled previously.
Link: http://lore.kernel.org/20241004212504.1246-1-gourry@gourry.net [1]
Cc: stable@vger.kernel.org
Fixes: 176baefb2eb5 ("cxl/hdm: Commit decoder state to hardware")
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Alison Schofield <alison.schofield@intel.com>
Cc: Ira Weiny <ira.weiny@intel.com>
Cc: Zijun Hu <quic_zijuhu@quicinc.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Ira Weiny <ira.weiny@intel.com>
Link: https://patch.msgid.link/172964782781.81806.17902885593105284330.stgit@dwillia2-xfh.jf.intel.com
Signed-off-by: Ira Weiny <ira.weiny@intel.com>
2024-10-22 18:43:49 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Once the highest committed decoder is disabled, free any other
|
|
|
|
* decoders that were pinned allocated by out-of-order release.
|
|
|
|
*/
|
|
|
|
port->commit_end--;
|
|
|
|
dev_dbg(&port->dev, "reap: %s commit_end: %d\n", dev_name(&cxld->dev),
|
|
|
|
port->commit_end);
|
|
|
|
device_for_each_child_reverse_from(&port->dev, &cxld->dev, NULL,
|
|
|
|
commit_reap);
|
|
|
|
}
|
module: Convert symbol namespace to string literal
Clean up the existing export namespace code along the same lines of
commit 33def8498fdd ("treewide: Convert macro and uses of __section(foo)
to __section("foo")") and for the same reason, it is not desired for the
namespace argument to be a macro expansion itself.
Scripted using
git grep -l -e MODULE_IMPORT_NS -e EXPORT_SYMBOL_NS | while read file;
do
awk -i inplace '
/^#define EXPORT_SYMBOL_NS/ {
gsub(/__stringify\(ns\)/, "ns");
print;
next;
}
/^#define MODULE_IMPORT_NS/ {
gsub(/__stringify\(ns\)/, "ns");
print;
next;
}
/MODULE_IMPORT_NS/ {
$0 = gensub(/MODULE_IMPORT_NS\(([^)]*)\)/, "MODULE_IMPORT_NS(\"\\1\")", "g");
}
/EXPORT_SYMBOL_NS/ {
if ($0 ~ /(EXPORT_SYMBOL_NS[^(]*)\(([^,]+),/) {
if ($0 !~ /(EXPORT_SYMBOL_NS[^(]*)\(([^,]+), ([^)]+)\)/ &&
$0 !~ /(EXPORT_SYMBOL_NS[^(]*)\(\)/ &&
$0 !~ /^my/) {
getline line;
gsub(/[[:space:]]*\\$/, "");
gsub(/[[:space:]]/, "", line);
$0 = $0 " " line;
}
$0 = gensub(/(EXPORT_SYMBOL_NS[^(]*)\(([^,]+), ([^)]+)\)/,
"\\1(\\2, \"\\3\")", "g");
}
}
{ print }' $file;
done
Requested-by: Masahiro Yamada <masahiroy@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://mail.google.com/mail/u/2/#inbox/FMfcgzQXKWgMmjdFwwdsfgxzKpVHWPlc
Acked-by: Greg KH <gregkh@linuxfoundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2024-12-02 15:59:47 +01:00
|
|
|
EXPORT_SYMBOL_NS_GPL(cxl_port_commit_reap, "CXL");
|
cxl/port: Fix use-after-free, permit out-of-order decoder shutdown
In support of investigating an initialization failure report [1],
cxl_test was updated to register mock memory-devices after the mock
root-port/bus device had been registered. That led to cxl_test crashing
with a use-after-free bug with the following signature:
cxl_port_attach_region: cxl region3: cxl_host_bridge.0:port3 decoder3.0 add: mem0:decoder7.0 @ 0 next: cxl_switch_uport.0 nr_eps: 1 nr_targets: 1
cxl_port_attach_region: cxl region3: cxl_host_bridge.0:port3 decoder3.0 add: mem4:decoder14.0 @ 1 next: cxl_switch_uport.0 nr_eps: 2 nr_targets: 1
cxl_port_setup_targets: cxl region3: cxl_switch_uport.0:port6 target[0] = cxl_switch_dport.0 for mem0:decoder7.0 @ 0
1) cxl_port_setup_targets: cxl region3: cxl_switch_uport.0:port6 target[1] = cxl_switch_dport.4 for mem4:decoder14.0 @ 1
[..]
cxld_unregister: cxl decoder14.0:
cxl_region_decode_reset: cxl_region region3:
mock_decoder_reset: cxl_port port3: decoder3.0 reset
2) mock_decoder_reset: cxl_port port3: decoder3.0: out of order reset, expected decoder3.1
cxl_endpoint_decoder_release: cxl decoder14.0:
[..]
cxld_unregister: cxl decoder7.0:
3) cxl_region_decode_reset: cxl_region region3:
Oops: general protection fault, probably for non-canonical address 0x6b6b6b6b6b6b6bc3: 0000 [#1] PREEMPT SMP PTI
[..]
RIP: 0010:to_cxl_port+0x8/0x60 [cxl_core]
[..]
Call Trace:
<TASK>
cxl_region_decode_reset+0x69/0x190 [cxl_core]
cxl_region_detach+0xe8/0x210 [cxl_core]
cxl_decoder_kill_region+0x27/0x40 [cxl_core]
cxld_unregister+0x5d/0x60 [cxl_core]
At 1) a region has been established with 2 endpoint decoders (7.0 and
14.0). Those endpoints share a common switch-decoder in the topology
(3.0). At teardown, 2), decoder14.0 is the first to be removed and hits
the "out of order reset case" in the switch decoder. The effect though
is that region3 cleanup is aborted leaving it in-tact and
referencing decoder14.0. At 3) the second attempt to teardown region3
trips over the stale decoder14.0 object which has long since been
deleted.
The fix here is to recognize that the CXL specification places no
mandate on in-order shutdown of switch-decoders, the driver enforces
in-order allocation, and hardware enforces in-order commit. So, rather
than fail and leave objects dangling, always remove them.
In support of making cxl_region_decode_reset() always succeed,
cxl_region_invalidate_memregion() failures are turned into warnings.
Crashing the kernel is ok there since system integrity is at risk if
caches cannot be managed around physical address mutation events like
CXL region destruction.
A new device_for_each_child_reverse_from() is added to cleanup
port->commit_end after all dependent decoders have been disabled. In
other words if decoders are allocated 0->1->2 and disabled 1->2->0 then
port->commit_end only decrements from 2 after 2 has been disabled, and
it decrements all the way to zero since 1 was disabled previously.
Link: http://lore.kernel.org/20241004212504.1246-1-gourry@gourry.net [1]
Cc: stable@vger.kernel.org
Fixes: 176baefb2eb5 ("cxl/hdm: Commit decoder state to hardware")
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Alison Schofield <alison.schofield@intel.com>
Cc: Ira Weiny <ira.weiny@intel.com>
Cc: Zijun Hu <quic_zijuhu@quicinc.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Ira Weiny <ira.weiny@intel.com>
Link: https://patch.msgid.link/172964782781.81806.17902885593105284330.stgit@dwillia2-xfh.jf.intel.com
Signed-off-by: Ira Weiny <ira.weiny@intel.com>
2024-10-22 18:43:49 -07:00
|
|
|
|
|
|
|
static void cxl_decoder_reset(struct cxl_decoder *cxld)
|
2022-06-08 22:56:37 -07:00
|
|
|
{
|
|
|
|
struct cxl_port *port = to_cxl_port(cxld->dev.parent);
|
|
|
|
struct cxl_hdm *cxlhdm = dev_get_drvdata(&port->dev);
|
|
|
|
void __iomem *hdm = cxlhdm->regs.hdm_decoder;
|
|
|
|
int id = cxld->id;
|
|
|
|
u32 ctrl;
|
|
|
|
|
|
|
|
if ((cxld->flags & CXL_DECODER_F_ENABLE) == 0)
|
cxl/port: Fix use-after-free, permit out-of-order decoder shutdown
In support of investigating an initialization failure report [1],
cxl_test was updated to register mock memory-devices after the mock
root-port/bus device had been registered. That led to cxl_test crashing
with a use-after-free bug with the following signature:
cxl_port_attach_region: cxl region3: cxl_host_bridge.0:port3 decoder3.0 add: mem0:decoder7.0 @ 0 next: cxl_switch_uport.0 nr_eps: 1 nr_targets: 1
cxl_port_attach_region: cxl region3: cxl_host_bridge.0:port3 decoder3.0 add: mem4:decoder14.0 @ 1 next: cxl_switch_uport.0 nr_eps: 2 nr_targets: 1
cxl_port_setup_targets: cxl region3: cxl_switch_uport.0:port6 target[0] = cxl_switch_dport.0 for mem0:decoder7.0 @ 0
1) cxl_port_setup_targets: cxl region3: cxl_switch_uport.0:port6 target[1] = cxl_switch_dport.4 for mem4:decoder14.0 @ 1
[..]
cxld_unregister: cxl decoder14.0:
cxl_region_decode_reset: cxl_region region3:
mock_decoder_reset: cxl_port port3: decoder3.0 reset
2) mock_decoder_reset: cxl_port port3: decoder3.0: out of order reset, expected decoder3.1
cxl_endpoint_decoder_release: cxl decoder14.0:
[..]
cxld_unregister: cxl decoder7.0:
3) cxl_region_decode_reset: cxl_region region3:
Oops: general protection fault, probably for non-canonical address 0x6b6b6b6b6b6b6bc3: 0000 [#1] PREEMPT SMP PTI
[..]
RIP: 0010:to_cxl_port+0x8/0x60 [cxl_core]
[..]
Call Trace:
<TASK>
cxl_region_decode_reset+0x69/0x190 [cxl_core]
cxl_region_detach+0xe8/0x210 [cxl_core]
cxl_decoder_kill_region+0x27/0x40 [cxl_core]
cxld_unregister+0x5d/0x60 [cxl_core]
At 1) a region has been established with 2 endpoint decoders (7.0 and
14.0). Those endpoints share a common switch-decoder in the topology
(3.0). At teardown, 2), decoder14.0 is the first to be removed and hits
the "out of order reset case" in the switch decoder. The effect though
is that region3 cleanup is aborted leaving it in-tact and
referencing decoder14.0. At 3) the second attempt to teardown region3
trips over the stale decoder14.0 object which has long since been
deleted.
The fix here is to recognize that the CXL specification places no
mandate on in-order shutdown of switch-decoders, the driver enforces
in-order allocation, and hardware enforces in-order commit. So, rather
than fail and leave objects dangling, always remove them.
In support of making cxl_region_decode_reset() always succeed,
cxl_region_invalidate_memregion() failures are turned into warnings.
Crashing the kernel is ok there since system integrity is at risk if
caches cannot be managed around physical address mutation events like
CXL region destruction.
A new device_for_each_child_reverse_from() is added to cleanup
port->commit_end after all dependent decoders have been disabled. In
other words if decoders are allocated 0->1->2 and disabled 1->2->0 then
port->commit_end only decrements from 2 after 2 has been disabled, and
it decrements all the way to zero since 1 was disabled previously.
Link: http://lore.kernel.org/20241004212504.1246-1-gourry@gourry.net [1]
Cc: stable@vger.kernel.org
Fixes: 176baefb2eb5 ("cxl/hdm: Commit decoder state to hardware")
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Alison Schofield <alison.schofield@intel.com>
Cc: Ira Weiny <ira.weiny@intel.com>
Cc: Zijun Hu <quic_zijuhu@quicinc.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Ira Weiny <ira.weiny@intel.com>
Link: https://patch.msgid.link/172964782781.81806.17902885593105284330.stgit@dwillia2-xfh.jf.intel.com
Signed-off-by: Ira Weiny <ira.weiny@intel.com>
2024-10-22 18:43:49 -07:00
|
|
|
return;
|
2022-06-08 22:56:37 -07:00
|
|
|
|
cxl/port: Fix use-after-free, permit out-of-order decoder shutdown
In support of investigating an initialization failure report [1],
cxl_test was updated to register mock memory-devices after the mock
root-port/bus device had been registered. That led to cxl_test crashing
with a use-after-free bug with the following signature:
cxl_port_attach_region: cxl region3: cxl_host_bridge.0:port3 decoder3.0 add: mem0:decoder7.0 @ 0 next: cxl_switch_uport.0 nr_eps: 1 nr_targets: 1
cxl_port_attach_region: cxl region3: cxl_host_bridge.0:port3 decoder3.0 add: mem4:decoder14.0 @ 1 next: cxl_switch_uport.0 nr_eps: 2 nr_targets: 1
cxl_port_setup_targets: cxl region3: cxl_switch_uport.0:port6 target[0] = cxl_switch_dport.0 for mem0:decoder7.0 @ 0
1) cxl_port_setup_targets: cxl region3: cxl_switch_uport.0:port6 target[1] = cxl_switch_dport.4 for mem4:decoder14.0 @ 1
[..]
cxld_unregister: cxl decoder14.0:
cxl_region_decode_reset: cxl_region region3:
mock_decoder_reset: cxl_port port3: decoder3.0 reset
2) mock_decoder_reset: cxl_port port3: decoder3.0: out of order reset, expected decoder3.1
cxl_endpoint_decoder_release: cxl decoder14.0:
[..]
cxld_unregister: cxl decoder7.0:
3) cxl_region_decode_reset: cxl_region region3:
Oops: general protection fault, probably for non-canonical address 0x6b6b6b6b6b6b6bc3: 0000 [#1] PREEMPT SMP PTI
[..]
RIP: 0010:to_cxl_port+0x8/0x60 [cxl_core]
[..]
Call Trace:
<TASK>
cxl_region_decode_reset+0x69/0x190 [cxl_core]
cxl_region_detach+0xe8/0x210 [cxl_core]
cxl_decoder_kill_region+0x27/0x40 [cxl_core]
cxld_unregister+0x5d/0x60 [cxl_core]
At 1) a region has been established with 2 endpoint decoders (7.0 and
14.0). Those endpoints share a common switch-decoder in the topology
(3.0). At teardown, 2), decoder14.0 is the first to be removed and hits
the "out of order reset case" in the switch decoder. The effect though
is that region3 cleanup is aborted leaving it in-tact and
referencing decoder14.0. At 3) the second attempt to teardown region3
trips over the stale decoder14.0 object which has long since been
deleted.
The fix here is to recognize that the CXL specification places no
mandate on in-order shutdown of switch-decoders, the driver enforces
in-order allocation, and hardware enforces in-order commit. So, rather
than fail and leave objects dangling, always remove them.
In support of making cxl_region_decode_reset() always succeed,
cxl_region_invalidate_memregion() failures are turned into warnings.
Crashing the kernel is ok there since system integrity is at risk if
caches cannot be managed around physical address mutation events like
CXL region destruction.
A new device_for_each_child_reverse_from() is added to cleanup
port->commit_end after all dependent decoders have been disabled. In
other words if decoders are allocated 0->1->2 and disabled 1->2->0 then
port->commit_end only decrements from 2 after 2 has been disabled, and
it decrements all the way to zero since 1 was disabled previously.
Link: http://lore.kernel.org/20241004212504.1246-1-gourry@gourry.net [1]
Cc: stable@vger.kernel.org
Fixes: 176baefb2eb5 ("cxl/hdm: Commit decoder state to hardware")
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Davidlohr Bueso <dave@stgolabs.net>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Alison Schofield <alison.schofield@intel.com>
Cc: Ira Weiny <ira.weiny@intel.com>
Cc: Zijun Hu <quic_zijuhu@quicinc.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
Reviewed-by: Ira Weiny <ira.weiny@intel.com>
Link: https://patch.msgid.link/172964782781.81806.17902885593105284330.stgit@dwillia2-xfh.jf.intel.com
Signed-off-by: Ira Weiny <ira.weiny@intel.com>
2024-10-22 18:43:49 -07:00
|
|
|
if (port->commit_end == id)
|
|
|
|
cxl_port_commit_reap(cxld);
|
|
|
|
else
|
2022-06-08 22:56:37 -07:00
|
|
|
dev_dbg(&port->dev,
|
|
|
|
"%s: out of order reset, expected decoder%d.%d\n",
|
|
|
|
dev_name(&cxld->dev), port->id, port->commit_end);
|
|
|
|
|
|
|
|
ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id));
|
|
|
|
ctrl &= ~CXL_HDM_DECODER0_CTRL_COMMIT;
|
|
|
|
writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(id));
|
|
|
|
|
|
|
|
writel(0, hdm + CXL_HDM_DECODER0_SIZE_HIGH_OFFSET(id));
|
|
|
|
writel(0, hdm + CXL_HDM_DECODER0_SIZE_LOW_OFFSET(id));
|
|
|
|
writel(0, hdm + CXL_HDM_DECODER0_BASE_HIGH_OFFSET(id));
|
|
|
|
writel(0, hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(id));
|
|
|
|
|
|
|
|
cxld->flags &= ~CXL_DECODER_F_ENABLE;
|
|
|
|
|
2023-02-10 17:31:17 -08:00
|
|
|
/* Userspace is now responsible for reconfiguring this decoder */
|
|
|
|
if (is_endpoint_decoder(&cxld->dev)) {
|
|
|
|
struct cxl_endpoint_decoder *cxled;
|
|
|
|
|
|
|
|
cxled = to_cxl_endpoint_decoder(&cxld->dev);
|
|
|
|
cxled->state = CXL_DECODER_STATE_MANUAL;
|
|
|
|
}
|
2022-06-08 22:56:37 -07:00
|
|
|
}
|
|
|
|
|
2023-04-03 16:01:32 -07:00
|
|
|
static int cxl_setup_hdm_decoder_from_dvsec(
|
|
|
|
struct cxl_port *port, struct cxl_decoder *cxld, u64 *dpa_base,
|
|
|
|
int which, struct cxl_endpoint_dvsec_info *info)
|
2023-02-14 11:41:24 -08:00
|
|
|
{
|
2023-04-03 16:01:32 -07:00
|
|
|
struct cxl_endpoint_decoder *cxled;
|
|
|
|
u64 len;
|
|
|
|
int rc;
|
|
|
|
|
2023-02-14 11:41:24 -08:00
|
|
|
if (!is_cxl_endpoint(port))
|
|
|
|
return -EOPNOTSUPP;
|
|
|
|
|
2023-04-03 16:01:32 -07:00
|
|
|
cxled = to_cxl_endpoint_decoder(&cxld->dev);
|
|
|
|
len = range_len(&info->dvsec_range[which]);
|
|
|
|
if (!len)
|
2023-02-14 11:41:24 -08:00
|
|
|
return -ENOENT;
|
|
|
|
|
2023-06-14 18:30:13 -07:00
|
|
|
cxld->target_type = CXL_DECODER_HOSTONLYMEM;
|
2023-02-14 11:41:24 -08:00
|
|
|
cxld->commit = NULL;
|
|
|
|
cxld->reset = NULL;
|
|
|
|
cxld->hpa_range = info->dvsec_range[which];
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the emulated decoder as locked pending additional support to
|
|
|
|
* change the range registers at run time.
|
|
|
|
*/
|
|
|
|
cxld->flags |= CXL_DECODER_F_ENABLE | CXL_DECODER_F_LOCK;
|
|
|
|
port->commit_end = cxld->id;
|
|
|
|
|
2023-04-03 16:01:32 -07:00
|
|
|
rc = devm_cxl_dpa_reserve(cxled, *dpa_base, len, 0);
|
|
|
|
if (rc) {
|
|
|
|
dev_err(&port->dev,
|
|
|
|
"decoder%d.%d: Failed to reserve DPA range %#llx - %#llx\n (%d)",
|
|
|
|
port->id, cxld->id, *dpa_base, *dpa_base + len - 1, rc);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
*dpa_base += len;
|
|
|
|
cxled->state = CXL_DECODER_STATE_AUTO;
|
|
|
|
|
2023-02-14 11:41:24 -08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-01-25 21:24:09 -08:00
|
|
|
static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
|
2022-07-21 17:19:12 -07:00
|
|
|
int *target_map, void __iomem *hdm, int which,
|
2023-02-14 11:41:24 -08:00
|
|
|
u64 *dpa_base, struct cxl_endpoint_dvsec_info *info)
|
2022-02-01 12:24:30 -08:00
|
|
|
{
|
2023-06-14 18:30:19 -07:00
|
|
|
struct cxl_endpoint_decoder *cxled = NULL;
|
2023-04-14 11:54:00 -07:00
|
|
|
u64 size, base, skip, dpa_size, lo, hi;
|
2022-07-21 17:19:12 -07:00
|
|
|
bool committed;
|
|
|
|
u32 remainder;
|
2022-05-22 17:04:27 -07:00
|
|
|
int i, rc;
|
2022-02-01 12:24:30 -08:00
|
|
|
u32 ctrl;
|
|
|
|
union {
|
|
|
|
u64 value;
|
|
|
|
unsigned char target_id[8];
|
|
|
|
} target_list;
|
|
|
|
|
2023-04-03 14:33:48 -07:00
|
|
|
if (should_emulate_decoders(info))
|
2023-04-03 16:01:32 -07:00
|
|
|
return cxl_setup_hdm_decoder_from_dvsec(port, cxld, dpa_base,
|
|
|
|
which, info);
|
2023-02-14 11:41:35 -08:00
|
|
|
|
2022-02-01 12:24:30 -08:00
|
|
|
ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(which));
|
2023-04-14 11:54:00 -07:00
|
|
|
lo = readl(hdm + CXL_HDM_DECODER0_BASE_LOW_OFFSET(which));
|
|
|
|
hi = readl(hdm + CXL_HDM_DECODER0_BASE_HIGH_OFFSET(which));
|
|
|
|
base = (hi << 32) + lo;
|
|
|
|
lo = readl(hdm + CXL_HDM_DECODER0_SIZE_LOW_OFFSET(which));
|
|
|
|
hi = readl(hdm + CXL_HDM_DECODER0_SIZE_HIGH_OFFSET(which));
|
|
|
|
size = (hi << 32) + lo;
|
2022-07-21 17:19:12 -07:00
|
|
|
committed = !!(ctrl & CXL_HDM_DECODER0_CTRL_COMMITTED);
|
2022-06-08 22:56:37 -07:00
|
|
|
cxld->commit = cxl_decoder_commit;
|
|
|
|
cxld->reset = cxl_decoder_reset;
|
2022-02-01 12:24:30 -08:00
|
|
|
|
2022-07-21 17:19:12 -07:00
|
|
|
if (!committed)
|
2022-02-01 12:24:30 -08:00
|
|
|
size = 0;
|
2022-01-25 21:24:09 -08:00
|
|
|
if (base == U64_MAX || size == U64_MAX) {
|
|
|
|
dev_warn(&port->dev, "decoder%d.%d: Invalid resource range\n",
|
|
|
|
port->id, cxld->id);
|
|
|
|
return -ENXIO;
|
|
|
|
}
|
2022-02-01 12:24:30 -08:00
|
|
|
|
2023-06-14 18:30:19 -07:00
|
|
|
if (info)
|
|
|
|
cxled = to_cxl_endpoint_decoder(&cxld->dev);
|
2022-05-18 17:55:13 -07:00
|
|
|
cxld->hpa_range = (struct range) {
|
2022-02-01 12:24:30 -08:00
|
|
|
.start = base,
|
|
|
|
.end = base + size - 1,
|
|
|
|
};
|
|
|
|
|
2022-07-21 17:19:12 -07:00
|
|
|
/* decoders are enabled if committed */
|
|
|
|
if (committed) {
|
2022-02-01 12:24:30 -08:00
|
|
|
cxld->flags |= CXL_DECODER_F_ENABLE;
|
|
|
|
if (ctrl & CXL_HDM_DECODER0_CTRL_LOCK)
|
|
|
|
cxld->flags |= CXL_DECODER_F_LOCK;
|
2023-06-14 18:30:19 -07:00
|
|
|
if (FIELD_GET(CXL_HDM_DECODER0_CTRL_HOSTONLY, ctrl))
|
2023-06-14 18:30:13 -07:00
|
|
|
cxld->target_type = CXL_DECODER_HOSTONLYMEM;
|
2022-06-07 11:21:40 -07:00
|
|
|
else
|
2023-06-14 18:30:13 -07:00
|
|
|
cxld->target_type = CXL_DECODER_DEVMEM;
|
2023-11-17 13:18:48 -07:00
|
|
|
|
2025-07-11 16:49:32 -07:00
|
|
|
guard(rwsem_write)(&cxl_rwsem.region);
|
2023-10-16 10:57:48 -07:00
|
|
|
if (cxld->id != cxl_num_decoders_committed(port)) {
|
2022-06-08 22:56:37 -07:00
|
|
|
dev_warn(&port->dev,
|
|
|
|
"decoder%d.%d: Committed out of order\n",
|
|
|
|
port->id, cxld->id);
|
|
|
|
return -ENXIO;
|
|
|
|
}
|
2023-04-14 11:53:55 -07:00
|
|
|
|
|
|
|
if (size == 0) {
|
|
|
|
dev_warn(&port->dev,
|
|
|
|
"decoder%d.%d: Committed with zero size\n",
|
|
|
|
port->id, cxld->id);
|
|
|
|
return -ENXIO;
|
|
|
|
}
|
2022-06-08 22:56:37 -07:00
|
|
|
port->commit_end = cxld->id;
|
2022-06-07 11:21:40 -07:00
|
|
|
} else {
|
2023-06-14 18:30:19 -07:00
|
|
|
if (cxled) {
|
|
|
|
struct cxl_memdev *cxlmd = cxled_to_memdev(cxled);
|
|
|
|
struct cxl_dev_state *cxlds = cxlmd->cxlds;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Default by devtype until a device arrives that needs
|
|
|
|
* more precision.
|
|
|
|
*/
|
|
|
|
if (cxlds->type == CXL_DEVTYPE_CLASSMEM)
|
|
|
|
cxld->target_type = CXL_DECODER_HOSTONLYMEM;
|
|
|
|
else
|
|
|
|
cxld->target_type = CXL_DECODER_DEVMEM;
|
|
|
|
} else {
|
|
|
|
/* To be overridden by region type at commit time */
|
|
|
|
cxld->target_type = CXL_DECODER_HOSTONLYMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!FIELD_GET(CXL_HDM_DECODER0_CTRL_HOSTONLY, ctrl) &&
|
|
|
|
cxld->target_type == CXL_DECODER_HOSTONLYMEM) {
|
|
|
|
ctrl |= CXL_HDM_DECODER0_CTRL_HOSTONLY;
|
2022-06-07 11:21:40 -07:00
|
|
|
writel(ctrl, hdm + CXL_HDM_DECODER0_CTRL_OFFSET(which));
|
|
|
|
}
|
2022-02-01 12:24:30 -08:00
|
|
|
}
|
2022-12-05 14:20:01 -07:00
|
|
|
rc = eiw_to_ways(FIELD_GET(CXL_HDM_DECODER0_CTRL_IW_MASK, ctrl),
|
|
|
|
&cxld->interleave_ways);
|
2022-05-22 17:04:27 -07:00
|
|
|
if (rc) {
|
2022-01-25 21:24:09 -08:00
|
|
|
dev_warn(&port->dev,
|
|
|
|
"decoder%d.%d: Invalid interleave ways (ctrl: %#x)\n",
|
|
|
|
port->id, cxld->id, ctrl);
|
2022-05-22 17:04:27 -07:00
|
|
|
return rc;
|
2022-01-25 21:24:09 -08:00
|
|
|
}
|
2022-12-05 14:16:07 -07:00
|
|
|
rc = eig_to_granularity(FIELD_GET(CXL_HDM_DECODER0_CTRL_IG_MASK, ctrl),
|
|
|
|
&cxld->interleave_granularity);
|
2024-04-02 14:10:16 +08:00
|
|
|
if (rc) {
|
|
|
|
dev_warn(&port->dev,
|
|
|
|
"decoder%d.%d: Invalid interleave granularity (ctrl: %#x)\n",
|
|
|
|
port->id, cxld->id, ctrl);
|
2022-05-22 17:04:27 -07:00
|
|
|
return rc;
|
2024-04-02 14:10:16 +08:00
|
|
|
}
|
2022-02-01 12:24:30 -08:00
|
|
|
|
2023-04-14 11:54:16 -07:00
|
|
|
dev_dbg(&port->dev, "decoder%d.%d: range: %#llx-%#llx iw: %d ig: %d\n",
|
|
|
|
port->id, cxld->id, cxld->hpa_range.start, cxld->hpa_range.end,
|
|
|
|
cxld->interleave_ways, cxld->interleave_granularity);
|
|
|
|
|
2023-06-14 18:30:19 -07:00
|
|
|
if (!cxled) {
|
2023-04-14 11:54:00 -07:00
|
|
|
lo = readl(hdm + CXL_HDM_DECODER0_TL_LOW(which));
|
|
|
|
hi = readl(hdm + CXL_HDM_DECODER0_TL_HIGH(which));
|
|
|
|
target_list.value = (hi << 32) + lo;
|
2022-07-21 17:19:12 -07:00
|
|
|
for (i = 0; i < cxld->interleave_ways; i++)
|
|
|
|
target_map[i] = target_list.target_id[i];
|
|
|
|
|
2022-01-25 21:24:09 -08:00
|
|
|
return 0;
|
2022-07-21 17:19:12 -07:00
|
|
|
}
|
2022-02-02 20:02:06 -08:00
|
|
|
|
2022-07-21 17:19:12 -07:00
|
|
|
if (!committed)
|
|
|
|
return 0;
|
2022-01-25 21:24:09 -08:00
|
|
|
|
2022-07-21 17:19:12 -07:00
|
|
|
dpa_size = div_u64_rem(size, cxld->interleave_ways, &remainder);
|
|
|
|
if (remainder) {
|
|
|
|
dev_err(&port->dev,
|
|
|
|
"decoder%d.%d: invalid committed configuration size: %#llx ways: %d\n",
|
|
|
|
port->id, cxld->id, size, cxld->interleave_ways);
|
|
|
|
return -ENXIO;
|
|
|
|
}
|
2023-04-14 11:54:00 -07:00
|
|
|
lo = readl(hdm + CXL_HDM_DECODER0_SKIP_LOW(which));
|
|
|
|
hi = readl(hdm + CXL_HDM_DECODER0_SKIP_HIGH(which));
|
|
|
|
skip = (hi << 32) + lo;
|
2022-07-21 17:19:12 -07:00
|
|
|
rc = devm_cxl_dpa_reserve(cxled, *dpa_base + skip, dpa_size, skip);
|
|
|
|
if (rc) {
|
|
|
|
dev_err(&port->dev,
|
|
|
|
"decoder%d.%d: Failed to reserve DPA range %#llx - %#llx\n (%d)",
|
|
|
|
port->id, cxld->id, *dpa_base,
|
|
|
|
*dpa_base + dpa_size + skip - 1, rc);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
*dpa_base += dpa_size + skip;
|
2023-02-10 17:31:17 -08:00
|
|
|
|
|
|
|
cxled->state = CXL_DECODER_STATE_AUTO;
|
|
|
|
|
2022-01-25 21:24:09 -08:00
|
|
|
return 0;
|
2022-02-01 12:24:30 -08:00
|
|
|
}
|
|
|
|
|
2023-02-14 11:41:30 -08:00
|
|
|
static void cxl_settle_decoders(struct cxl_hdm *cxlhdm)
|
2022-02-01 12:24:30 -08:00
|
|
|
{
|
|
|
|
void __iomem *hdm = cxlhdm->regs.hdm_decoder;
|
2023-02-14 11:41:30 -08:00
|
|
|
int committed, i;
|
2022-02-01 12:24:30 -08:00
|
|
|
u32 ctrl;
|
|
|
|
|
2023-02-14 11:41:30 -08:00
|
|
|
if (!hdm)
|
|
|
|
return;
|
|
|
|
|
2022-02-01 12:24:30 -08:00
|
|
|
/*
|
|
|
|
* Since the register resource was recently claimed via request_region()
|
|
|
|
* be careful about trusting the "not-committed" status until the commit
|
|
|
|
* timeout has elapsed. The commit timeout is 10ms (CXL 2.0
|
|
|
|
* 8.2.5.12.20), but double it to be tolerant of any clock skew between
|
|
|
|
* host and target.
|
|
|
|
*/
|
|
|
|
for (i = 0, committed = 0; i < cxlhdm->decoder_count; i++) {
|
|
|
|
ctrl = readl(hdm + CXL_HDM_DECODER0_CTRL_OFFSET(i));
|
|
|
|
if (ctrl & CXL_HDM_DECODER0_CTRL_COMMITTED)
|
|
|
|
committed++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* ensure that future checks of committed can be trusted */
|
|
|
|
if (committed != cxlhdm->decoder_count)
|
|
|
|
msleep(20);
|
2023-02-14 11:41:30 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* devm_cxl_enumerate_decoders - add decoder objects per HDM register set
|
|
|
|
* @cxlhdm: Structure to populate with HDM capabilities
|
|
|
|
* @info: cached DVSEC range register info
|
|
|
|
*/
|
|
|
|
int devm_cxl_enumerate_decoders(struct cxl_hdm *cxlhdm,
|
|
|
|
struct cxl_endpoint_dvsec_info *info)
|
|
|
|
{
|
|
|
|
void __iomem *hdm = cxlhdm->regs.hdm_decoder;
|
|
|
|
struct cxl_port *port = cxlhdm->port;
|
|
|
|
int i;
|
|
|
|
u64 dpa_base = 0;
|
|
|
|
|
|
|
|
cxl_settle_decoders(cxlhdm);
|
2022-02-01 12:24:30 -08:00
|
|
|
|
2022-04-12 22:18:09 -07:00
|
|
|
for (i = 0; i < cxlhdm->decoder_count; i++) {
|
2022-02-01 12:24:30 -08:00
|
|
|
int target_map[CXL_DECODER_MAX_INTERLEAVE] = { 0 };
|
|
|
|
int rc, target_count = cxlhdm->target_count;
|
|
|
|
struct cxl_decoder *cxld;
|
|
|
|
|
2022-05-18 17:52:23 -07:00
|
|
|
if (is_cxl_endpoint(port)) {
|
2022-05-21 16:24:14 -07:00
|
|
|
struct cxl_endpoint_decoder *cxled;
|
|
|
|
|
|
|
|
cxled = cxl_endpoint_decoder_alloc(port);
|
|
|
|
if (IS_ERR(cxled)) {
|
2022-05-18 17:52:23 -07:00
|
|
|
dev_warn(&port->dev,
|
2023-02-10 01:06:33 -08:00
|
|
|
"Failed to allocate decoder%d.%d\n",
|
|
|
|
port->id, i);
|
2022-05-21 16:24:14 -07:00
|
|
|
return PTR_ERR(cxled);
|
2022-05-18 17:52:23 -07:00
|
|
|
}
|
2022-05-21 16:24:14 -07:00
|
|
|
cxld = &cxled->cxld;
|
2022-05-18 17:52:23 -07:00
|
|
|
} else {
|
|
|
|
struct cxl_switch_decoder *cxlsd;
|
|
|
|
|
|
|
|
cxlsd = cxl_switch_decoder_alloc(port, target_count);
|
|
|
|
if (IS_ERR(cxlsd)) {
|
|
|
|
dev_warn(&port->dev,
|
2023-02-10 01:06:33 -08:00
|
|
|
"Failed to allocate decoder%d.%d\n",
|
|
|
|
port->id, i);
|
2022-05-18 17:52:23 -07:00
|
|
|
return PTR_ERR(cxlsd);
|
|
|
|
}
|
|
|
|
cxld = &cxlsd->cxld;
|
2022-02-01 12:24:30 -08:00
|
|
|
}
|
|
|
|
|
2023-02-14 11:41:24 -08:00
|
|
|
rc = init_hdm_decoder(port, cxld, target_map, hdm, i,
|
|
|
|
&dpa_base, info);
|
2022-01-25 21:24:09 -08:00
|
|
|
if (rc) {
|
2023-02-10 01:06:33 -08:00
|
|
|
dev_warn(&port->dev,
|
|
|
|
"Failed to initialize decoder%d.%d\n",
|
|
|
|
port->id, i);
|
2022-01-25 21:24:09 -08:00
|
|
|
put_device(&cxld->dev);
|
2022-04-12 22:18:09 -07:00
|
|
|
return rc;
|
2022-01-25 21:24:09 -08:00
|
|
|
}
|
2022-02-01 12:24:30 -08:00
|
|
|
rc = add_hdm_decoder(port, cxld, target_map);
|
|
|
|
if (rc) {
|
|
|
|
dev_warn(&port->dev,
|
2023-02-10 01:06:33 -08:00
|
|
|
"Failed to add decoder%d.%d\n", port->id, i);
|
2022-02-01 12:24:30 -08:00
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
module: Convert symbol namespace to string literal
Clean up the existing export namespace code along the same lines of
commit 33def8498fdd ("treewide: Convert macro and uses of __section(foo)
to __section("foo")") and for the same reason, it is not desired for the
namespace argument to be a macro expansion itself.
Scripted using
git grep -l -e MODULE_IMPORT_NS -e EXPORT_SYMBOL_NS | while read file;
do
awk -i inplace '
/^#define EXPORT_SYMBOL_NS/ {
gsub(/__stringify\(ns\)/, "ns");
print;
next;
}
/^#define MODULE_IMPORT_NS/ {
gsub(/__stringify\(ns\)/, "ns");
print;
next;
}
/MODULE_IMPORT_NS/ {
$0 = gensub(/MODULE_IMPORT_NS\(([^)]*)\)/, "MODULE_IMPORT_NS(\"\\1\")", "g");
}
/EXPORT_SYMBOL_NS/ {
if ($0 ~ /(EXPORT_SYMBOL_NS[^(]*)\(([^,]+),/) {
if ($0 !~ /(EXPORT_SYMBOL_NS[^(]*)\(([^,]+), ([^)]+)\)/ &&
$0 !~ /(EXPORT_SYMBOL_NS[^(]*)\(\)/ &&
$0 !~ /^my/) {
getline line;
gsub(/[[:space:]]*\\$/, "");
gsub(/[[:space:]]/, "", line);
$0 = $0 " " line;
}
$0 = gensub(/(EXPORT_SYMBOL_NS[^(]*)\(([^,]+), ([^)]+)\)/,
"\\1(\\2, \"\\3\")", "g");
}
}
{ print }' $file;
done
Requested-by: Masahiro Yamada <masahiroy@kernel.org>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://mail.google.com/mail/u/2/#inbox/FMfcgzQXKWgMmjdFwwdsfgxzKpVHWPlc
Acked-by: Greg KH <gregkh@linuxfoundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2024-12-02 15:59:47 +01:00
|
|
|
EXPORT_SYMBOL_NS_GPL(devm_cxl_enumerate_decoders, "CXL");
|