From: Alison Schofield <alison.schofield@intel.com>
To: Dan Williams <dan.j.williams@intel.com>
Cc: Davidlohr Bueso <dave@stgolabs.net>,
Jonathan Cameron <jonathan.cameron@huawei.com>,
Dave Jiang <dave.jiang@intel.com>,
Vishal Verma <vishal.l.verma@intel.com>,
Ira Weiny <ira.weiny@intel.com>,
linux-cxl@vger.kernel.org, Wonjae Lee <wj28.lee@samsung.com>
Subject: Re: [RFC PATCH] cxl/region: Allow out of order assembly of autodiscovered regions
Date: Thu, 8 Feb 2024 12:52:04 -0800 [thread overview]
Message-ID: <ZcU+9BxBD8IRsnTu@aschofie-mobl2> (raw)
In-Reply-To: <65a980249f50f_3b8e294a3@dwillia2-xfh.jf.intel.com.notmuch>
On Thu, Jan 18, 2024 at 11:46:44AM -0800, Dan Williams wrote:
- snip to decoder replay patch -
>
> So one of the largest roadblocks for creating arbitrary region assembly
> scenarios with cxl_test is the inability to save and restore decoder
> settings.
>
> The patch below adds support for recording decoder settings and skipping
> the reset of those values when unloading the driver. Then, when the
> driver is reloaded, it simulates the case of BIOS created CXL regions
> prior to OS boot.
>
> We can go after even finer grained cases once the uunit effort settles,
> but in the meantime cxl_test can add auto-assembly regression tests with
> the current topology.
>
> With the below you can simply trigger "cxl {en,dis}able-memdev" in the
> proper order to cause the violation.
>
> -- >8 --
> From 5839392a3dbb64dbbac0630d930b1f48a8ef7ea6 Mon Sep 17 00:00:00 2001
> From: Dan Williams <dan.j.williams@intel.com>
> Date: Wed, 17 Jan 2024 20:56:20 -0800
> Subject: [PATCH] tools/testing/cxl: Add decoder save/restore support
>
> Record decoder values at init and mock_decoder_commit() time, and
> restore them at the next invocation of mock_init_hdm_decoder(). Add 2
> attributes to the cxl_test "cxl_acpi" device to optionally flush the
> cache of topology decoder values, or disable updating the decoder at
> mock_decoder_reset() time.
>
> This enables replaying a saved decoder configuration when re-triggering
> a topology scan by re-binding the cxl_acpi driver to "cxl_acpi.0" (the
> cxl_test emulation of an ACPI0017 instance).
Hi Dan,
Sorry it's taken a while to come back on this. I did find it useful
in testing the auto-assembly order issue as you suggested.
I didn't use this one: &dev_attr_decoder_registry_invalidate.attr,
I just reloaded the cxl-test module to do same.
This I used: &dev_attr_decoder_registry_reset_disable.attr,
with your decoders state fixup to set CXL_DECODER_STATE_AUTO,
and a work-around to avoid pmem_probe failures on pmem region
auto create.
More generally, I'm wondering about the implementation of the
'registry_save'. Here it continuously updates during all
cxl-test usage. Did you consider only creating the registry upon
user request and then at the next mock_init_hmd_decoder() look
for and use that registry if it exists.
Usage would be:
# echo 1 > /sys/bus/platform/devices/cxl_acpi.0/decoder_registry_create
# echo cxl_acpi.0 > /sys/bus/platform/drivers/cxl_acpi/unbind
# echo cxl_acpi.0 > /sys/bus/platform/drivers/cxl_acpi/bind
And then maybe, for folks who like to acpi/unbind,bind, rather
than reload module, we could offer a decoder_registry_remove attr.
Am I missing something regarding the need to keep it updated
on the fly?
Alison
>
> # modprobe cxl_test
> # cxl list -RB -b cxl_test -u
> {
> "bus":"root3",
> "provider":"cxl_test",
> "regions:root3":[
> {
> "region":"region5",
> "resource":"0xf010000000",
> "size":"512.00 MiB (536.87 MB)",
> "type":"ram",
> "interleave_ways":2,
> "interleave_granularity":4096,
> "decode_state":"commit"
> }
> ]
> }
> # echo 1 > /sys/bus/platform/devices/cxl_acpi.0/decoder_registry_reset_disable
> # echo cxl_acpi.0 > /sys/bus/platform/drivers/cxl_acpi/unbind
> # cxl list -RB -b cxl_test -u
> # echo cxl_acpi.0 > /sys/bus/platform/drivers/cxl_acpi/bind
> # cxl list -RB -b cxl_test -u
> {
> "bus":"root3",
> "provider":"cxl_test",
> "regions:root3":[
> {
> "region":"region5",
> "resource":"0xf010000000",
> "size":"512.00 MiB (536.87 MB)",
> "type":"ram",
> "interleave_ways":2,
> "interleave_granularity":4096,
> "decode_state":"commit"
> }
> ]
> }
>
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
> ---
> tools/testing/cxl/test/cxl.c | 268 +++++++++++++++++++++++++++++++++++
> 1 file changed, 268 insertions(+)
>
> diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
> index f4e517a0c774..3f333d6a57be 100644
> --- a/tools/testing/cxl/test/cxl.c
> +++ b/tools/testing/cxl/test/cxl.c
> @@ -44,6 +44,9 @@ struct platform_device *cxl_mem_single[NR_MEM_SINGLE];
> static struct platform_device *cxl_rch[NR_CXL_RCH];
> static struct platform_device *cxl_rcd[NR_CXL_RCH];
>
> +static DEFINE_XARRAY(decoder_registry);
> +static bool decoder_registry_reset_disable;
> +
> static inline bool is_multi_bridge(struct device *dev)
> {
> int i;
> @@ -660,6 +663,153 @@ static int map_targets(struct device *dev, void *data)
> return 0;
> }
>
> +static unsigned long cxld_registry_index(struct cxl_decoder *cxld)
> +{
> + struct cxl_port *port = to_cxl_port(cxld->dev.parent);
> +
> + /*
> + * Upper nibble of a kernel pointer is 0xff, chop that to make
> + * space for a cxl_decoder id which should be less than 128
> + * given decoder count is a 4-bit field.
> + *
> + * While @port is reallocated each enumeration, @port->uport_dev
> + * is stable.
> + */
> + dev_WARN_ONCE(&port->dev, cxld->id >= 128,
> + "decoder id:%d out of range\n", cxld->id);
> + return (((unsigned long) port->uport_dev) << 4) | cxld->id;
> +}
> +
> +struct cxl_test_decoder {
> + union {
> + struct cxl_switch_decoder cxlsd;
> + struct cxl_endpoint_decoder cxled;
> + };
> + union {
> + struct cxl_dport *targets[CXL_DECODER_MAX_INTERLEAVE];
> + struct range dpa_range;
> + };
> +};
> +
> +static struct cxl_test_decoder *cxld_registry_find(struct cxl_decoder *cxld)
> +{
> + return xa_load(&decoder_registry, cxld_registry_index(cxld));
> +}
> +
> +#define dbg_cxld(port, msg, cxld) \
> + do { \
> + struct cxl_decoder *___d = (cxld); \
> + dev_dbg((port)->uport_dev, \
> + "decoder%d: %s range: %#llx-%#llx iw: %d ig: %d flags: %#lx\n", \
> + ___d->id, msg, ___d->hpa_range.start, \
> + ___d->hpa_range.end + 1, ___d->interleave_ways, \
> + ___d->interleave_granularity, ___d->flags); \
> + } while (0)
> +
> +static int mock_decoder_commit(struct cxl_decoder *cxld);
> +static int mock_decoder_reset(struct cxl_decoder *cxld);
> +
> +static void cxld_copy(struct cxl_decoder *a, struct cxl_decoder *b)
> +{
> + a->id = b->id;
> + a->hpa_range = b->hpa_range;
> + a->interleave_ways = b->interleave_ways;
> + a->interleave_granularity = b->interleave_granularity;
> + a->target_type = b->target_type;
> + a->flags = b->flags;
> + a->commit = mock_decoder_commit;
> + a->reset = mock_decoder_reset;
> +}
> +
> +static void cxld_registry_restore(struct cxl_decoder *cxld, struct cxl_test_decoder *td)
> +{
> + struct cxl_port *port = to_cxl_port(cxld->dev.parent);
> +
> + if (is_switch_decoder(&cxld->dev)) {
> + struct cxl_switch_decoder *cxlsd = to_cxl_switch_decoder(&cxld->dev);
> +
> + dbg_cxld(port, "restore", &td->cxlsd.cxld);
> + cxld_copy(cxld, &td->cxlsd.cxld);
> + WARN_ON(cxlsd->nr_targets != td->cxlsd.nr_targets);
> +
> + /* convert saved dport devs to dports */
> + for (int i = 0; i < cxlsd->nr_targets; i++) {
> + struct cxl_dport *dport;
> +
> + if (!td->cxlsd.target[i])
> + continue;
> + dport = cxl_find_dport_by_dev(
> + port, (struct device *)td->cxlsd.target[i]);
> + WARN_ON(!dport);
> + cxlsd->target[i] = dport;
> + }
> + } else {
> + struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(&cxld->dev);
> +
> + dbg_cxld(port, "restore", &td->cxled.cxld);
> + cxld_copy(cxld, &td->cxled.cxld);
> + cxled->state = td->cxled.state;
> + cxled->skip = td->cxled.skip;
> + if (range_len(&td->dpa_range))
> + devm_cxl_dpa_reserve(cxled, td->dpa_range.start,
> + range_len(&td->dpa_range),
> + td->cxled.skip);
> + if (cxld->flags & CXL_DECODER_F_ENABLE)
> + port->commit_end = cxld->id;
> + }
> +}
> +
> +static void __cxld_registry_save(struct cxl_test_decoder *td,
> + struct cxl_decoder *cxld)
> +{
> + if (is_switch_decoder(&cxld->dev)) {
> + struct cxl_switch_decoder *cxlsd = to_cxl_switch_decoder(&cxld->dev);
> +
> + cxld_copy(&td->cxlsd.cxld, cxld);
> + td->cxlsd.nr_targets = cxlsd->nr_targets;
> +
> + /* save dport devs as a stable placeholder for dports */
> + for (int i = 0; i < cxlsd->nr_targets; i++) {
> + if (!cxlsd->target[i])
> + continue;
> + td->cxlsd.target[i] =
> + (struct cxl_dport *)cxlsd->target[i]->dport_dev;
> + }
> + } else {
> + struct cxl_endpoint_decoder *cxled = to_cxl_endpoint_decoder(&cxld->dev);
> +
> + cxld_copy(&td->cxled.cxld, cxld);
> + td->cxled.state = cxled->state;
> + td->cxled.skip = cxled->skip;
> + if (cxled->dpa_res) {
> + td->dpa_range.start = cxled->dpa_res->start;
> + td->dpa_range.end = cxled->dpa_res->end;
> + } else {
> + td->dpa_range.start = 0;
> + td->dpa_range.end = -1;
> + }
> + }
> +}
> +
> +static void cxld_registry_save(struct cxl_test_decoder *td, struct cxl_decoder *cxld)
> +{
> + struct cxl_port *port = to_cxl_port(cxld->dev.parent);
> +
> + dbg_cxld(port, "save", cxld);
> + __cxld_registry_save(td, cxld);
> +}
> +
> +static void cxld_registry_update(struct cxl_decoder *cxld)
> +{
> + struct cxl_port *port = to_cxl_port(cxld->dev.parent);
> + struct cxl_test_decoder *td = cxld_registry_find(cxld);
> +
> + dev_WARN_ONCE(port->uport_dev, !td, "%s failed\n", __func__);
> +
> + dbg_cxld(port, "update", cxld);
> + __cxld_registry_save(td, cxld);
> +}
> +
> static int mock_decoder_commit(struct cxl_decoder *cxld)
> {
> struct cxl_port *port = to_cxl_port(cxld->dev.parent);
> @@ -679,6 +829,7 @@ static int mock_decoder_commit(struct cxl_decoder *cxld)
>
> port->commit_end++;
> cxld->flags |= CXL_DECODER_F_ENABLE;
> + cxld_registry_update(cxld);
>
> return 0;
> }
> @@ -701,10 +852,43 @@ static int mock_decoder_reset(struct cxl_decoder *cxld)
>
> port->commit_end--;
> cxld->flags &= ~CXL_DECODER_F_ENABLE;
> + if (decoder_registry_reset_disable)
> + dev_dbg(port->uport_dev, "decoder%d: skip registry update\n",
> + cxld->id);
> + else
> + cxld_registry_update(cxld);
>
> return 0;
> }
>
> +static void cxld_registry_invalidate(void)
> +{
> + unsigned long index;
> + void *entry;
> +
> + xa_for_each(&decoder_registry, index, entry) {
> + xa_erase(&decoder_registry, index);
> + kfree(entry);
> + }
> +}
> +
> +static struct cxl_test_decoder *cxld_registry_new(struct cxl_decoder *cxld)
> +{
> + struct cxl_test_decoder *td __free(kfree) = kzalloc(sizeof(*td), GFP_KERNEL);
> +
> + if (!td)
> + return NULL;
> +
> + if (xa_insert(&decoder_registry, cxld_registry_index(cxld), td,
> + GFP_KERNEL)) {
> + WARN_ON(1);
> + return NULL;
> + }
> +
> + cxld_registry_save(td, cxld);
> + return no_free_ptr(td);
> +}
> +
> static void default_mock_decoder(struct cxl_decoder *cxld)
> {
> cxld->hpa_range = (struct range){
> @@ -717,6 +901,9 @@ static void default_mock_decoder(struct cxl_decoder *cxld)
> cxld->target_type = CXL_DECODER_HOSTONLYMEM;
> cxld->commit = mock_decoder_commit;
> cxld->reset = mock_decoder_reset;
> +
> + if (!cxld_registry_new(cxld))
> + dev_dbg(&cxld->dev, "failed to add to registry\n");
> }
>
> static int first_decoder(struct device *dev, void *data)
> @@ -738,6 +925,7 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
> struct cxl_endpoint_decoder *cxled;
> struct cxl_switch_decoder *cxlsd;
> struct cxl_port *port, *iter;
> + struct cxl_test_decoder *td;
> const int size = SZ_512M;
> struct cxl_memdev *cxlmd;
> struct cxl_dport *dport;
> @@ -767,6 +955,12 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
> port = cxled_to_port(cxled);
> }
>
> + td = cxld_registry_find(cxld);
> + if (td) {
> + cxld_registry_restore(cxld, td);
> + return;
> + }
> +
> /*
> * The first decoder on the first 2 devices on the first switch
> * attached to host-bridge0 mock a fake / static RAM region. All
> @@ -795,6 +989,8 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
> devm_cxl_dpa_reserve(cxled, 0, size / cxld->interleave_ways, 0);
> cxld->commit = mock_decoder_commit;
> cxld->reset = mock_decoder_reset;
> + if (!cxld_registry_new(cxld))
> + dev_dbg(&cxld->dev, "failed to add to registry\n");
>
> /*
> * Now that endpoint decoder is set up, walk up the hierarchy
> @@ -837,6 +1033,7 @@ static void mock_init_hdm_decoder(struct cxl_decoder *cxld)
> .start = base,
> .end = base + size - 1,
> };
> + cxld_registry_update(cxld);
> put_device(dev);
> }
> }
> @@ -1233,6 +1430,73 @@ static void cxl_single_exit(void)
> }
> }
>
> +static ssize_t decoder_registry_invalidate_show(struct device *dev,
> + struct device_attribute *attr,
> + char *buf)
> +{
> + unsigned long index;
> + bool empty = true;
> + void *entry;
> +
> + xa_for_each(&decoder_registry, index, entry) {
> + empty = false;
> + break;
> + }
> +
> + return sysfs_emit(buf, "%d\n", !empty);
> +}
> +
> +static ssize_t decoder_registry_invalidate_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + bool invalidate;
> + int rc;
> +
> + rc = kstrtobool(buf, &invalidate);
> + if (rc)
> + return rc;
> +
> + guard(device)(dev);
> +
> + if (dev->driver)
> + return -EBUSY;
> +
> + cxld_registry_invalidate();
> + return count;
> +}
> +
> +static DEVICE_ATTR_RW(decoder_registry_invalidate);
> +
> +static ssize_t
> +decoder_registry_reset_disable_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + return sysfs_emit(buf, "%d\n", decoder_registry_reset_disable);
> +}
> +
> +static ssize_t
> +decoder_registry_reset_disable_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t count)
> +{
> + int rc;
> +
> + rc = kstrtobool(buf, &decoder_registry_reset_disable);
> + if (rc)
> + return rc;
> + return count;
> +}
> +
> +static DEVICE_ATTR_RW(decoder_registry_reset_disable);
> +
> +static struct attribute *cxl_acpi_attrs[] = {
> + &dev_attr_decoder_registry_invalidate.attr,
> + &dev_attr_decoder_registry_reset_disable.attr,
> + NULL
> +};
> +ATTRIBUTE_GROUPS(cxl_acpi);
> +
> static __init int cxl_test_init(void)
> {
> int rc, i;
> @@ -1377,6 +1641,7 @@ static __init int cxl_test_init(void)
>
> mock_companion(&acpi0017_mock, &cxl_acpi->dev);
> acpi0017_mock.dev.bus = &platform_bus_type;
> + cxl_acpi->dev.groups = cxl_acpi_groups;
>
> rc = platform_device_add(cxl_acpi);
> if (rc)
> @@ -1446,6 +1711,9 @@ static __exit void cxl_test_exit(void)
> depopulate_all_mock_resources();
> gen_pool_destroy(cxl_mock_pool);
> unregister_cxl_mock_ops(&cxl_mock_ops);
> +
> + cxld_registry_invalidate();
> + xa_destroy(&decoder_registry);
> }
>
> module_param(interleave_arithmetic, int, 0444);
> --
> 2.43.0
next prev parent reply other threads:[~2024-02-08 20:52 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <CGME20240113050447epcas2p2097a5c49c1f0f9318ec4202843e169b8@epcms2p8>
2024-01-13 5:04 ` [RFC PATCH] cxl/region: Allow out of order assembly of autodiscovered regions alison.schofield
2024-01-18 19:46 ` Dan Williams
2024-02-08 20:52 ` Alison Schofield [this message]
2024-02-08 22:57 ` Dan Williams
2024-02-09 0:23 ` Alison Schofield
2024-02-09 5:25 ` Dan Williams
2024-01-26 8:51 ` Wonjae Lee
2024-01-30 4:37 ` Alison Schofield
2024-01-31 1:02 ` Wonjae Lee
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ZcU+9BxBD8IRsnTu@aschofie-mobl2 \
--to=alison.schofield@intel.com \
--cc=dan.j.williams@intel.com \
--cc=dave.jiang@intel.com \
--cc=dave@stgolabs.net \
--cc=ira.weiny@intel.com \
--cc=jonathan.cameron@huawei.com \
--cc=linux-cxl@vger.kernel.org \
--cc=vishal.l.verma@intel.com \
--cc=wj28.lee@samsung.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox