summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/cxl/core/hdm.c58
-rw-r--r--drivers/cxl/core/mbox.c3
-rw-r--r--drivers/cxl/core/region.c45
-rw-r--r--drivers/cxl/cxl.h10
-rw-r--r--drivers/cxl/port.c3
5 files changed, 86 insertions, 33 deletions
diff --git a/drivers/cxl/core/hdm.c b/drivers/cxl/core/hdm.c
index 0c80b76a5f9b..39fe283cbc74 100644
--- a/drivers/cxl/core/hdm.c
+++ b/drivers/cxl/core/hdm.c
@@ -240,6 +240,18 @@ static resource_size_t __adjust_skip(struct cxl_dev_state *cxlds,
}
#define release_skip(c, b, l) __adjust_skip((c), (b), (l), NULL)
+static void cxl_dpa_release_region(struct resource *parent,
+ struct resource *res)
+{
+ /* zero sized decoders are not tracked in the resource tree */
+ if (resource_size(res) == 0) {
+ kfree(res);
+ return;
+ }
+
+ __release_region(parent, res->start, resource_size(res));
+}
+
/*
* Must be called in a context that synchronizes against this decoder's
* port ->remove() callback (like an endpoint decoder sysfs attribute)
@@ -256,7 +268,7 @@ static void __cxl_dpa_release(struct cxl_endpoint_decoder *cxled)
/* save @skip_start, before @res is released */
skip_start = res->start - cxled->skip;
- __release_region(&cxlds->dpa_res, res->start, resource_size(res));
+ cxl_dpa_release_region(&cxlds->dpa_res, res);
if (cxled->skip)
release_skip(cxlds, skip_start, cxled->skip);
cxled->skip = 0;
@@ -336,6 +348,27 @@ static int request_skip(struct cxl_dev_state *cxlds,
return -EBUSY;
}
+static struct resource *cxl_dpa_request_region(struct resource *parent,
+ resource_size_t start,
+ resource_size_t n,
+ const char *name)
+{
+ struct resource *res;
+
+ if (!n) {
+ res = kmalloc_obj(*res);
+ if (!res)
+ return ERR_PTR(-ENOMEM);
+
+ *res = DEFINE_RES_NAMED(start, 0, name, IORESOURCE_MEM);
+
+ return res;
+ }
+
+ res = __request_region(parent, start, n, name, 0);
+ return res ?: ERR_PTR(-EBUSY);
+}
+
static int __cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
resource_size_t base, resource_size_t len,
resource_size_t skipped)
@@ -349,12 +382,6 @@ static int __cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
lockdep_assert_held_write(&cxl_rwsem.dpa);
- if (!len) {
- dev_warn(dev, "decoder%d.%d: empty reservation attempted\n",
- port->id, cxled->cxld.id);
- return -EINVAL;
- }
-
if (cxled->dpa_res) {
dev_dbg(dev, "decoder%d.%d: existing allocation %pr assigned\n",
port->id, cxled->cxld.id, cxled->dpa_res);
@@ -378,14 +405,14 @@ static int __cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
if (rc)
return rc;
}
- res = __request_region(&cxlds->dpa_res, base, len,
- dev_name(&cxled->cxld.dev), 0);
- if (!res) {
+ res = cxl_dpa_request_region(&cxlds->dpa_res, base, len,
+ dev_name(&cxled->cxld.dev));
+ if (IS_ERR(res)) {
dev_dbg(dev, "decoder%d.%d: failed to reserve allocation\n",
port->id, cxled->cxld.id);
if (skipped)
release_skip(cxlds, base - skipped, skipped);
- return -EBUSY;
+ return PTR_ERR(res);
}
cxled->dpa_res = res;
cxled->skip = skipped;
@@ -402,7 +429,8 @@ static int __cxl_dpa_reserve(struct cxl_endpoint_decoder *cxled,
break;
}
- if (cxled->part < 0)
+ /* Empty decoders may not be contained by a partition boundary */
+ if (cxled->part < 0 && resource_size(res))
dev_warn(dev, "decoder%d.%d: %pr does not map any partition\n",
port->id, cxled->cxld.id, res);
@@ -1031,12 +1059,6 @@ static int init_hdm_decoder(struct cxl_port *port, struct cxl_decoder *cxld,
return -ENXIO;
}
- if (size == 0) {
- dev_warn(&port->dev,
- "decoder%d.%d: Committed with zero size\n",
- port->id, cxld->id);
- return -ENXIO;
- }
port->commit_end = cxld->id;
} else {
if (cxled) {
diff --git a/drivers/cxl/core/mbox.c b/drivers/cxl/core/mbox.c
index 55828a836c01..1a2553332801 100644
--- a/drivers/cxl/core/mbox.c
+++ b/drivers/cxl/core/mbox.c
@@ -1386,6 +1386,9 @@ int cxl_mem_get_poison(struct cxl_memdev *cxlmd, u64 offset, u64 len,
int nr_records = 0;
int rc;
+ if (!len)
+ return 0;
+
ACQUIRE(mutex_intr, lock)(&mds->poison.mutex);
if ((rc = ACQUIRE_ERR(mutex_intr, &lock)))
return rc;
diff --git a/drivers/cxl/core/region.c b/drivers/cxl/core/region.c
index 10abc073103b..bb51e67f25f5 100644
--- a/drivers/cxl/core/region.c
+++ b/drivers/cxl/core/region.c
@@ -2112,7 +2112,7 @@ static int cxl_region_attach(struct cxl_region *cxlr,
return -ENXIO;
}
- if (!cxled->dpa_res) {
+ if (cxled_empty(cxled)) {
dev_dbg(&cxlr->dev, "%s:%s: missing DPA allocation.\n",
dev_name(&cxlmd->dev), dev_name(&cxled->cxld.dev));
return -ENXIO;
@@ -2966,24 +2966,31 @@ static int poison_by_decoder(struct device *dev, void *arg)
if (!cxled->dpa_res)
return 0;
- cxlmd = cxled_to_memdev(cxled);
- cxlds = cxlmd->cxlds;
- mode = cxlds->part[cxled->part].mode;
+ /*
+ * Handle the degenerate case of a device with only empty decoders. An
+ * empty decoder can still map a non-zero skip range, so advance the
+ * walk to commit_end either way.
+ */
+ if (cxled->part >= 0) {
+ cxlmd = cxled_to_memdev(cxled);
+ cxlds = cxlmd->cxlds;
+ mode = cxlds->part[cxled->part].mode;
- if (cxled->skip) {
- offset = cxled->dpa_res->start - cxled->skip;
- length = cxled->skip;
- rc = cxl_mem_get_poison(cxlmd, offset, length, NULL);
+ if (cxled->skip) {
+ offset = cxled->dpa_res->start - cxled->skip;
+ length = cxled->skip;
+ rc = cxl_mem_get_poison(cxlmd, offset, length, NULL);
+ if (rc && !poison_efault_forgiven(rc, mode))
+ return rc;
+ }
+
+ offset = cxled->dpa_res->start;
+ length = cxled->dpa_res->end - offset + 1;
+ rc = cxl_mem_get_poison(cxlmd, offset, length, cxled->cxld.region);
if (rc && !poison_efault_forgiven(rc, mode))
return rc;
}
- offset = cxled->dpa_res->start;
- length = cxled->dpa_res->end - offset + 1;
- rc = cxl_mem_get_poison(cxlmd, offset, length, cxled->cxld.region);
- if (rc && !poison_efault_forgiven(rc, mode))
- return rc;
-
/* Iterate until commit_end is reached */
if (cxled->cxld.id == ctx->port->commit_end) {
ctx->offset = cxled->dpa_res->end + 1;
@@ -3005,9 +3012,17 @@ int cxl_get_poison_by_endpoint(struct cxl_port *port)
};
rc = device_for_each_child(&port->dev, &ctx, poison_by_decoder);
- if (rc == 1)
+ if (rc == 1) {
+ /*
+ * No decoder with a sized DPA reservation was walked
+ * (every committed decoder is zero-size): scan all
+ * partitions in full.
+ */
+ if (ctx.part < 0)
+ ctx.part = 0;
rc = cxl_get_poison_unmapped(to_cxl_memdev(port->uport_dev),
&ctx);
+ }
return rc;
}
diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
index cab8ce39f465..3ef0810ab86b 100644
--- a/drivers/cxl/cxl.h
+++ b/drivers/cxl/cxl.h
@@ -336,6 +336,16 @@ struct cxl_endpoint_decoder {
int pos;
};
+/*
+ * The common case is decoders with no reservation, but also handle
+ * decoders with a zero-sized reservation that firmware may install for
+ * security lockdown purposes.
+ */
+static inline bool cxled_empty(struct cxl_endpoint_decoder *cxled)
+{
+ return !cxled->dpa_res || !resource_size(cxled->dpa_res);
+}
+
/**
* struct cxl_switch_decoder - Switch specific CXL HDM Decoder
* @cxld: base cxl_decoder object
diff --git a/drivers/cxl/port.c b/drivers/cxl/port.c
index 99cf77b6b699..c12fd0b89883 100644
--- a/drivers/cxl/port.c
+++ b/drivers/cxl/port.c
@@ -46,6 +46,9 @@ static int discover_region(struct device *dev, void *unused)
if (cxled->state != CXL_DECODER_STATE_AUTO)
return 0;
+ if (cxled_empty(cxled))
+ return 0;
+
/*
* Region enumeration is opportunistic, if this add-event fails,
* continue to the next endpoint decoder.