Linux CXL
 help / color / mirror / Atom feed
From: Jonathan Cameron <Jonathan.Cameron@Huawei.com>
To: Dan Williams <dan.j.williams@intel.com>
Cc: <linux-cxl@vger.kernel.org>,
	Ben Widawsky <ben.widawsky@intel.com>,
	"Vishal Verma" <vishal.l.verma@intel.com>,
	<nvdimm@lists.linux.dev>, <alison.schofield@intel.com>,
	<ira.weiny@intel.com>
Subject: Re: [PATCH v4 17/21] tools/testing/cxl: Introduce a mocked-up CXL port hierarchy
Date: Fri, 10 Sep 2021 10:53:13 +0100	[thread overview]
Message-ID: <20210910105313.00006408@Huawei.com> (raw)
In-Reply-To: <163116438489.2460985.12407123882806203553.stgit@dwillia2-desk3.amr.corp.intel.com>

On Wed, 8 Sep 2021 22:13:04 -0700
Dan Williams <dan.j.williams@intel.com> wrote:

> Create an environment for CXL plumbing unit tests. Especially when it
> comes to an algorithm for HDM Decoder (Host-managed Device Memory
> Decoder) programming, the availability of an in-kernel-tree emulation
> environment for CXL configuration complexity and corner cases speeds
> development and deters regressions.
> 
> The approach taken mirrors what was done for tools/testing/nvdimm/. I.e.
> an external module, cxl_test.ko built out of the tools/testing/cxl/
> directory, provides mock implementations of kernel APIs and kernel
> objects to simulate a real world device hierarchy.
> 
> One feedback for the tools/testing/nvdimm/ proposal was "why not do this
> in QEMU?". In fact, the CXL development community has developed a QEMU
> model for CXL [1]. However, there are a few blocking issues that keep
> QEMU from being a tight fit for topology + provisioning unit tests:
> 
> 1/ The QEMU community has yet to show interest in merging any of this
>    support that has had patches on the list since November 2020. So,
>    testing CXL to date involves building custom QEMU with out-of-tree
>    patches.
> 
> 2/ CXL mechanisms like cross-host-bridge interleave do not have a clear
>    path to be emulated by QEMU without major infrastructure work. This
>    is easier to achieve with the alloc_mock_res() approach taken in this
>    patch to shortcut-define emulated system physical address ranges with
>    interleave behavior.
> 
> The QEMU enabling has been critical to get the driver off the ground,
> and may still move forward, but it does not address the ongoing needs of
> a regression testing environment and test driven development.
> 
> This patch adds an ACPI CXL Platform definition with emulated CXL
> multi-ported host-bridges. A follow on patch adds emulated memory
> expander devices.
> 
> Acked-by: Ben Widawsky <ben.widawsky@intel.com>
> Reported-by: Vishal Verma <vishal.l.verma@intel.com>
> Link: https://lore.kernel.org/r/20210202005948.241655-1-ben.widawsky@intel.com [1]
> Link: https://lore.kernel.org/r/162982125348.1124374.17808192318402734926.stgit@dwillia2-desk3.amr.corp.intel.com
> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
A trivial comment below, but I'm fine with leave that one change in here
as it is only a very small amount of noise.

Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>



> ---
>  drivers/cxl/acpi.c               |   40 ++-
>  drivers/cxl/cxl.h                |   16 +
>  tools/testing/cxl/Kbuild         |   36 +++
>  tools/testing/cxl/config_check.c |   13 +
>  tools/testing/cxl/mock_acpi.c    |  109 ++++++++
>  tools/testing/cxl/test/Kbuild    |    6 
>  tools/testing/cxl/test/cxl.c     |  509 ++++++++++++++++++++++++++++++++++++++
>  tools/testing/cxl/test/mock.c    |  171 +++++++++++++
>  tools/testing/cxl/test/mock.h    |   27 ++
>  9 files changed, 911 insertions(+), 16 deletions(-)
>  create mode 100644 tools/testing/cxl/Kbuild
>  create mode 100644 tools/testing/cxl/config_check.c
>  create mode 100644 tools/testing/cxl/mock_acpi.c
>  create mode 100644 tools/testing/cxl/test/Kbuild
>  create mode 100644 tools/testing/cxl/test/cxl.c
>  create mode 100644 tools/testing/cxl/test/mock.c
>  create mode 100644 tools/testing/cxl/test/mock.h
> 
> diff --git a/drivers/cxl/acpi.c b/drivers/cxl/acpi.c
> index 54e9d4d2cf5f..d31a97218593 100644
> --- a/drivers/cxl/acpi.c
> +++ b/drivers/cxl/acpi.c
> @@ -182,15 +182,7 @@ static resource_size_t get_chbcr(struct acpi_cedt_chbs *chbs)
>  	return IS_ERR(chbs) ? CXL_RESOURCE_NONE : chbs->base;
>  }
>  
> -struct cxl_walk_context {
> -	struct device *dev;
> -	struct pci_bus *root;
> -	struct cxl_port *port;
> -	int error;
> -	int count;
> -};
> -
> -static int match_add_root_ports(struct pci_dev *pdev, void *data)
> +__mock int match_add_root_ports(struct pci_dev *pdev, void *data)
>  {
>  	struct cxl_walk_context *ctx = data;
>  	struct pci_bus *root_bus = ctx->root;
> @@ -239,15 +231,18 @@ static struct cxl_dport *find_dport_by_dev(struct cxl_port *port, struct device
>  	return NULL;
>  }
>  
> -static struct acpi_device *to_cxl_host_bridge(struct device *dev)
> +__mock struct acpi_device *to_cxl_host_bridge(struct device *host,
> +					      struct device *dev)
>  {
>  	struct acpi_device *adev = to_acpi_device(dev);
>  
>  	if (!acpi_pci_find_root(adev->handle))
>  		return NULL;
>  
> -	if (strcmp(acpi_device_hid(adev), "ACPI0016") == 0)
> +	if (strcmp(acpi_device_hid(adev), "ACPI0016") == 0) {
> +		dev_dbg(host, "found host bridge %s\n", dev_name(&adev->dev));

I didn't call it out in the previous review, but technically unrelated to the
rest of the patch even if useful.

>  		return adev;
> +	}
>  	return NULL;
>  }
>  
> @@ -257,9 +252,9 @@ static struct acpi_device *to_cxl_host_bridge(struct device *dev)
>   */
>  static int add_host_bridge_uport(struct device *match, void *arg)
>  {
> -	struct acpi_device *bridge = to_cxl_host_bridge(match);
>  	struct cxl_port *root_port = arg;
>  	struct device *host = root_port->dev.parent;
> +	struct acpi_device *bridge = to_cxl_host_bridge(host, match);
>  	struct acpi_pci_root *pci_root;
>  	struct cxl_walk_context ctx;
>  	struct cxl_decoder *cxld;
> @@ -323,7 +318,7 @@ static int add_host_bridge_dport(struct device *match, void *arg)
>  	struct acpi_cedt_chbs *chbs;
>  	struct cxl_port *root_port = arg;
>  	struct device *host = root_port->dev.parent;
> -	struct acpi_device *bridge = to_cxl_host_bridge(match);
> +	struct acpi_device *bridge = to_cxl_host_bridge(host, match);
>  
>  	if (!bridge)
>  		return 0;
> @@ -375,6 +370,17 @@ static int add_root_nvdimm_bridge(struct device *match, void *data)
>  	return 1;
>  }
>  
> +static u32 cedt_instance(struct platform_device *pdev)
> +{
> +	const bool *native_acpi0017 = acpi_device_get_match_data(&pdev->dev);
> +
> +	if (native_acpi0017 && *native_acpi0017)
> +		return 0;
> +
> +	/* for cxl_test request a non-canonical instance */
> +	return U32_MAX;
> +}
> +
>  static int cxl_acpi_probe(struct platform_device *pdev)
>  {
>  	int rc;
> @@ -388,7 +394,7 @@ static int cxl_acpi_probe(struct platform_device *pdev)
>  		return PTR_ERR(root_port);
>  	dev_dbg(host, "add: %s\n", dev_name(&root_port->dev));
>  
> -	status = acpi_get_table(ACPI_SIG_CEDT, 0, &acpi_cedt);
> +	status = acpi_get_table(ACPI_SIG_CEDT, cedt_instance(pdev), &acpi_cedt);
>  	if (ACPI_FAILURE(status))
>  		return -ENXIO;
>  
> @@ -419,9 +425,11 @@ static int cxl_acpi_probe(struct platform_device *pdev)
>  	return 0;
>  }
>  
> +static bool native_acpi0017 = true;
> +
>  static const struct acpi_device_id cxl_acpi_ids[] = {
> -	{ "ACPI0017", 0 },
> -	{ "", 0 },
> +	{ "ACPI0017", (unsigned long) &native_acpi0017 },
> +	{ },
>  };
>  MODULE_DEVICE_TABLE(acpi, cxl_acpi_ids);
>  
> diff --git a/drivers/cxl/cxl.h b/drivers/cxl/cxl.h
> index 1b2e816e061e..c5152718267e 100644
> --- a/drivers/cxl/cxl.h
> +++ b/drivers/cxl/cxl.h
> @@ -226,6 +226,14 @@ struct cxl_nvdimm {
>  	struct nvdimm *nvdimm;
>  };
>  
> +struct cxl_walk_context {
> +	struct device *dev;
> +	struct pci_bus *root;
> +	struct cxl_port *port;
> +	int error;
> +	int count;
> +};
> +
>  /**
>   * struct cxl_port - logical collection of upstream port devices and
>   *		     downstream port devices to construct a CXL memory
> @@ -325,4 +333,12 @@ struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev);
>  bool is_cxl_nvdimm(struct device *dev);
>  int devm_cxl_add_nvdimm(struct device *host, struct cxl_memdev *cxlmd);
>  struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(void);
> +
> +/*
> + * Unit test builds overrides this to __weak, find the 'strong' version
> + * of these symbols in tools/testing/cxl/.
> + */
> +#ifndef __mock
> +#define __mock static
> +#endif
>  #endif /* __CXL_H__ */
> diff --git a/tools/testing/cxl/Kbuild b/tools/testing/cxl/Kbuild
> new file mode 100644
> index 000000000000..63a4a07e71c4
> --- /dev/null
> +++ b/tools/testing/cxl/Kbuild
> @@ -0,0 +1,36 @@
> +# SPDX-License-Identifier: GPL-2.0
> +ldflags-y += --wrap=is_acpi_device_node
> +ldflags-y += --wrap=acpi_get_table
> +ldflags-y += --wrap=acpi_put_table
> +ldflags-y += --wrap=acpi_evaluate_integer
> +ldflags-y += --wrap=acpi_pci_find_root
> +ldflags-y += --wrap=pci_walk_bus
> +ldflags-y += --wrap=nvdimm_bus_register
> +
> +DRIVERS := ../../../drivers
> +CXL_SRC := $(DRIVERS)/cxl
> +CXL_CORE_SRC := $(DRIVERS)/cxl/core
> +ccflags-y := -I$(srctree)/drivers/cxl/
> +ccflags-y += -D__mock=__weak
> +
> +obj-m += cxl_acpi.o
> +
> +cxl_acpi-y := $(CXL_SRC)/acpi.o
> +cxl_acpi-y += mock_acpi.o
> +cxl_acpi-y += config_check.o
> +
> +obj-m += cxl_pmem.o
> +
> +cxl_pmem-y := $(CXL_SRC)/pmem.o
> +cxl_pmem-y += config_check.o
> +
> +obj-m += cxl_core.o
> +
> +cxl_core-y := $(CXL_CORE_SRC)/bus.o
> +cxl_core-y += $(CXL_CORE_SRC)/pmem.o
> +cxl_core-y += $(CXL_CORE_SRC)/regs.o
> +cxl_core-y += $(CXL_CORE_SRC)/memdev.o
> +cxl_core-y += $(CXL_CORE_SRC)/mbox.o
> +cxl_core-y += config_check.o
> +
> +obj-m += test/
> diff --git a/tools/testing/cxl/config_check.c b/tools/testing/cxl/config_check.c
> new file mode 100644
> index 000000000000..de5e5b3652fd
> --- /dev/null
> +++ b/tools/testing/cxl/config_check.c
> @@ -0,0 +1,13 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/bug.h>
> +
> +void check(void)
> +{
> +	/*
> +	 * These kconfig symbols must be set to "m" for cxl_test to load
> +	 * and operate.
> +	 */
> +	BUILD_BUG_ON(!IS_MODULE(CONFIG_CXL_BUS));
> +	BUILD_BUG_ON(!IS_MODULE(CONFIG_CXL_ACPI));
> +	BUILD_BUG_ON(!IS_MODULE(CONFIG_CXL_PMEM));
> +}
> diff --git a/tools/testing/cxl/mock_acpi.c b/tools/testing/cxl/mock_acpi.c
> new file mode 100644
> index 000000000000..4c8a493ace56
> --- /dev/null
> +++ b/tools/testing/cxl/mock_acpi.c
> @@ -0,0 +1,109 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/* Copyright(c) 2021 Intel Corporation. All rights reserved. */
> +
> +#include <linux/platform_device.h>
> +#include <linux/device.h>
> +#include <linux/acpi.h>
> +#include <linux/pci.h>
> +#include <cxl.h>
> +#include "test/mock.h"
> +
> +struct acpi_device *to_cxl_host_bridge(struct device *host, struct device *dev)
> +{
> +	int index;
> +	struct acpi_device *adev, *found = NULL;
> +	struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
> +
> +	if (ops && ops->is_mock_bridge(dev)) {
> +		found = ACPI_COMPANION(dev);
> +		goto out;
> +	}
> +
> +	if (dev->bus == &platform_bus_type)
> +		goto out;
> +
> +	adev = to_acpi_device(dev);
> +	if (!acpi_pci_find_root(adev->handle))
> +		goto out;
> +
> +	if (strcmp(acpi_device_hid(adev), "ACPI0016") == 0) {
> +		found = adev;
> +		dev_dbg(host, "found host bridge %s\n", dev_name(&adev->dev));
> +	}
> +out:
> +	put_cxl_mock_ops(index);
> +	return found;
> +}
> +
> +static int match_add_root_port(struct pci_dev *pdev, void *data)
> +{
> +	struct cxl_walk_context *ctx = data;
> +	struct pci_bus *root_bus = ctx->root;
> +	struct cxl_port *port = ctx->port;
> +	int type = pci_pcie_type(pdev);
> +	struct device *dev = ctx->dev;
> +	u32 lnkcap, port_num;
> +	int rc;
> +
> +	if (pdev->bus != root_bus)
> +		return 0;
> +	if (!pci_is_pcie(pdev))
> +		return 0;
> +	if (type != PCI_EXP_TYPE_ROOT_PORT)
> +		return 0;
> +	if (pci_read_config_dword(pdev, pci_pcie_cap(pdev) + PCI_EXP_LNKCAP,
> +				  &lnkcap) != PCIBIOS_SUCCESSFUL)
> +		return 0;
> +
> +	/* TODO walk DVSEC to find component register base */
> +	port_num = FIELD_GET(PCI_EXP_LNKCAP_PN, lnkcap);
> +	rc = cxl_add_dport(port, &pdev->dev, port_num, CXL_RESOURCE_NONE);
> +	if (rc) {
> +		dev_err(dev, "failed to add dport: %s (%d)\n",
> +			dev_name(&pdev->dev), rc);
> +		ctx->error = rc;
> +		return rc;
> +	}
> +	ctx->count++;
> +
> +	dev_dbg(dev, "add dport%d: %s\n", port_num, dev_name(&pdev->dev));
> +
> +	return 0;
> +}
> +
> +static int mock_add_root_port(struct platform_device *pdev, void *data)
> +{
> +	struct cxl_walk_context *ctx = data;
> +	struct cxl_port *port = ctx->port;
> +	struct device *dev = ctx->dev;
> +	int rc;
> +
> +	rc = cxl_add_dport(port, &pdev->dev, pdev->id, CXL_RESOURCE_NONE);
> +	if (rc) {
> +		dev_err(dev, "failed to add dport: %s (%d)\n",
> +			dev_name(&pdev->dev), rc);
> +		ctx->error = rc;
> +		return rc;
> +	}
> +	ctx->count++;
> +
> +	dev_dbg(dev, "add dport%d: %s\n", pdev->id, dev_name(&pdev->dev));
> +
> +	return 0;
> +}
> +
> +int match_add_root_ports(struct pci_dev *dev, void *data)
> +{
> +	int index, rc;
> +	struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
> +	struct platform_device *pdev = (struct platform_device *) dev;
> +
> +	if (ops && ops->is_mock_port(pdev))
> +		rc = mock_add_root_port(pdev, data);
> +	else
> +		rc = match_add_root_port(dev, data);
> +
> +	put_cxl_mock_ops(index);
> +
> +	return rc;
> +}
> diff --git a/tools/testing/cxl/test/Kbuild b/tools/testing/cxl/test/Kbuild
> new file mode 100644
> index 000000000000..7de4ddecfd21
> --- /dev/null
> +++ b/tools/testing/cxl/test/Kbuild
> @@ -0,0 +1,6 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-m += cxl_test.o
> +obj-m += cxl_mock.o
> +
> +cxl_test-y := cxl.o
> +cxl_mock-y := mock.o
> diff --git a/tools/testing/cxl/test/cxl.c b/tools/testing/cxl/test/cxl.c
> new file mode 100644
> index 000000000000..1c47b34244a4
> --- /dev/null
> +++ b/tools/testing/cxl/test/cxl.c
> @@ -0,0 +1,509 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +// Copyright(c) 2021 Intel Corporation. All rights reserved.
> +
> +#include <linux/platform_device.h>
> +#include <linux/genalloc.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +#include <linux/acpi.h>
> +#include <linux/pci.h>
> +#include <linux/mm.h>
> +#include "mock.h"
> +
> +#define NR_CXL_HOST_BRIDGES 4
> +#define NR_CXL_ROOT_PORTS 2
> +
> +static struct platform_device *cxl_acpi;
> +static struct platform_device *cxl_host_bridge[NR_CXL_HOST_BRIDGES];
> +static struct platform_device
> +	*cxl_root_port[NR_CXL_HOST_BRIDGES * NR_CXL_ROOT_PORTS];
> +
> +static struct acpi_device acpi0017_mock;
> +static struct acpi_device host_bridge[NR_CXL_HOST_BRIDGES] = {
> +	[0] = {
> +		.handle = &host_bridge[0],
> +	},
> +	[1] = {
> +		.handle = &host_bridge[1],
> +	},
> +	[2] = {
> +		.handle = &host_bridge[2],
> +	},
> +	[3] = {
> +		.handle = &host_bridge[3],
> +	},
> +};
> +
> +static bool is_mock_dev(struct device *dev)
> +{
> +	if (dev == &cxl_acpi->dev)
> +		return true;
> +	return false;
> +}
> +
> +static bool is_mock_adev(struct acpi_device *adev)
> +{
> +	int i;
> +
> +	if (adev == &acpi0017_mock)
> +		return true;
> +
> +	for (i = 0; i < ARRAY_SIZE(host_bridge); i++)
> +		if (adev == &host_bridge[i])
> +			return true;
> +
> +	return false;
> +}
> +
> +static struct {
> +	struct acpi_table_cedt cedt;
> +	struct acpi_cedt_chbs chbs[NR_CXL_HOST_BRIDGES];
> +	struct {
> +		struct acpi_cedt_cfmws cfmws;
> +		u32 target[1];
> +	} cfmws0;
> +	struct {
> +		struct acpi_cedt_cfmws cfmws;
> +		u32 target[4];
> +	} cfmws1;
> +	struct {
> +		struct acpi_cedt_cfmws cfmws;
> +		u32 target[1];
> +	} cfmws2;
> +	struct {
> +		struct acpi_cedt_cfmws cfmws;
> +		u32 target[4];
> +	} cfmws3;
> +} __packed mock_cedt = {
> +	.cedt = {
> +		.header = {
> +			.signature = "CEDT",
> +			.length = sizeof(mock_cedt),
> +			.revision = 1,
> +		},
> +	},
> +	.chbs[0] = {
> +		.header = {
> +			.type = ACPI_CEDT_TYPE_CHBS,
> +			.length = sizeof(mock_cedt.chbs[0]),
> +		},
> +		.uid = 0,
> +		.cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20,
> +	},
> +	.chbs[1] = {
> +		.header = {
> +			.type = ACPI_CEDT_TYPE_CHBS,
> +			.length = sizeof(mock_cedt.chbs[0]),
> +		},
> +		.uid = 1,
> +		.cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20,
> +	},
> +	.chbs[2] = {
> +		.header = {
> +			.type = ACPI_CEDT_TYPE_CHBS,
> +			.length = sizeof(mock_cedt.chbs[0]),
> +		},
> +		.uid = 2,
> +		.cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20,
> +	},
> +	.chbs[3] = {
> +		.header = {
> +			.type = ACPI_CEDT_TYPE_CHBS,
> +			.length = sizeof(mock_cedt.chbs[0]),
> +		},
> +		.uid = 3,
> +		.cxl_version = ACPI_CEDT_CHBS_VERSION_CXL20,
> +	},
> +	.cfmws0 = {
> +		.cfmws = {
> +			.header = {
> +				.type = ACPI_CEDT_TYPE_CFMWS,
> +				.length = sizeof(mock_cedt.cfmws0),
> +			},
> +			.interleave_ways = 0,
> +			.granularity = 4,
> +			.restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
> +					ACPI_CEDT_CFMWS_RESTRICT_VOLATILE,
> +			.qtg_id = 0,
> +			.window_size = SZ_256M,
> +		},
> +		.target = { 0 },
> +	},
> +	.cfmws1 = {
> +		.cfmws = {
> +			.header = {
> +				.type = ACPI_CEDT_TYPE_CFMWS,
> +				.length = sizeof(mock_cedt.cfmws1),
> +			},
> +			.interleave_ways = 2,
> +			.granularity = 4,
> +			.restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
> +					ACPI_CEDT_CFMWS_RESTRICT_VOLATILE,
> +			.qtg_id = 1,
> +			.window_size = SZ_256M * 4,
> +		},
> +		.target = { 0, 1, 2, 3 },
> +	},
> +	.cfmws2 = {
> +		.cfmws = {
> +			.header = {
> +				.type = ACPI_CEDT_TYPE_CFMWS,
> +				.length = sizeof(mock_cedt.cfmws2),
> +			},
> +			.interleave_ways = 0,
> +			.granularity = 4,
> +			.restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
> +					ACPI_CEDT_CFMWS_RESTRICT_PMEM,
> +			.qtg_id = 2,
> +			.window_size = SZ_256M,
> +		},
> +		.target = { 0 },
> +	},
> +	.cfmws3 = {
> +		.cfmws = {
> +			.header = {
> +				.type = ACPI_CEDT_TYPE_CFMWS,
> +				.length = sizeof(mock_cedt.cfmws3),
> +			},
> +			.interleave_ways = 2,
> +			.granularity = 4,
> +			.restrictions = ACPI_CEDT_CFMWS_RESTRICT_TYPE3 |
> +					ACPI_CEDT_CFMWS_RESTRICT_PMEM,
> +			.qtg_id = 3,
> +			.window_size = SZ_256M * 4,
> +		},
> +		.target = { 0, 1, 2, 3 },
> +	},
> +};
> +
> +struct cxl_mock_res {
> +	struct list_head list;
> +	struct range range;
> +};
> +
> +static LIST_HEAD(mock_res);
> +static DEFINE_MUTEX(mock_res_lock);
> +static struct gen_pool *cxl_mock_pool;
> +
> +static void depopulate_all_mock_resources(void)
> +{
> +	struct cxl_mock_res *res, *_res;
> +
> +	mutex_lock(&mock_res_lock);
> +	list_for_each_entry_safe(res, _res, &mock_res, list) {
> +		gen_pool_free(cxl_mock_pool, res->range.start,
> +			      range_len(&res->range));
> +		list_del(&res->list);
> +		kfree(res);
> +	}
> +	mutex_unlock(&mock_res_lock);
> +}
> +
> +static struct cxl_mock_res *alloc_mock_res(resource_size_t size)
> +{
> +	struct cxl_mock_res *res = kzalloc(sizeof(*res), GFP_KERNEL);
> +	struct genpool_data_align data = {
> +		.align = SZ_256M,
> +	};
> +	unsigned long phys;
> +
> +	INIT_LIST_HEAD(&res->list);
> +	phys = gen_pool_alloc_algo(cxl_mock_pool, size,
> +				   gen_pool_first_fit_align, &data);
> +	if (!phys)
> +		return NULL;
> +
> +	res->range = (struct range) {
> +		.start = phys,
> +		.end = phys + size - 1,
> +	};
> +	mutex_lock(&mock_res_lock);
> +	list_add(&res->list, &mock_res);
> +	mutex_unlock(&mock_res_lock);
> +
> +	return res;
> +}
> +
> +static int populate_cedt(void)
> +{
> +	struct acpi_cedt_cfmws *cfmws[4] = {
> +		[0] = &mock_cedt.cfmws0.cfmws,
> +		[1] = &mock_cedt.cfmws1.cfmws,
> +		[2] = &mock_cedt.cfmws2.cfmws,
> +		[3] = &mock_cedt.cfmws3.cfmws,
> +	};
> +	struct cxl_mock_res *res;
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(mock_cedt.chbs); i++) {
> +		struct acpi_cedt_chbs *chbs = &mock_cedt.chbs[i];
> +		resource_size_t size;
> +
> +		if (chbs->cxl_version == ACPI_CEDT_CHBS_VERSION_CXL20)
> +			size = ACPI_CEDT_CHBS_LENGTH_CXL20;
> +		else
> +			size = ACPI_CEDT_CHBS_LENGTH_CXL11;
> +
> +		res = alloc_mock_res(size);
> +		if (!res)
> +			return -ENOMEM;
> +		chbs->base = res->range.start;
> +		chbs->length = size;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(cfmws); i++) {
> +		struct acpi_cedt_cfmws *window = cfmws[i];
> +
> +		res = alloc_mock_res(window->window_size);
> +		if (!res)
> +			return -ENOMEM;
> +		window->base_hpa = res->range.start;
> +	}
> +
> +	return 0;
> +}
> +
> +static acpi_status mock_acpi_get_table(char *signature, u32 instance,
> +				       struct acpi_table_header **out_table)
> +{
> +	if (instance < U32_MAX || strcmp(signature, ACPI_SIG_CEDT) != 0)
> +		return acpi_get_table(signature, instance, out_table);
> +
> +	*out_table = (struct acpi_table_header *) &mock_cedt;
> +	return AE_OK;
> +}
> +
> +static void mock_acpi_put_table(struct acpi_table_header *table)
> +{
> +	if (table == (struct acpi_table_header *) &mock_cedt)
> +		return;
> +	acpi_put_table(table);
> +}
> +
> +static bool is_mock_bridge(struct device *dev)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++)
> +		if (dev == &cxl_host_bridge[i]->dev)
> +			return true;
> +
> +	return false;
> +}
> +
> +static int host_bridge_index(struct acpi_device *adev)
> +{
> +	return adev - host_bridge;
> +}
> +
> +static struct acpi_device *find_host_bridge(acpi_handle handle)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(host_bridge); i++)
> +		if (handle == host_bridge[i].handle)
> +			return &host_bridge[i];
> +	return NULL;
> +}
> +
> +static acpi_status
> +mock_acpi_evaluate_integer(acpi_handle handle, acpi_string pathname,
> +			   struct acpi_object_list *arguments,
> +			   unsigned long long *data)
> +{
> +	struct acpi_device *adev = find_host_bridge(handle);
> +
> +	if (!adev || strcmp(pathname, METHOD_NAME__UID) != 0)
> +		return acpi_evaluate_integer(handle, pathname, arguments, data);
> +
> +	*data = host_bridge_index(adev);
> +	return AE_OK;
> +}
> +
> +static struct pci_bus mock_pci_bus[NR_CXL_HOST_BRIDGES];
> +static struct acpi_pci_root mock_pci_root[NR_CXL_HOST_BRIDGES] = {
> +	[0] = {
> +		.bus = &mock_pci_bus[0],
> +	},
> +	[1] = {
> +		.bus = &mock_pci_bus[1],
> +	},
> +	[2] = {
> +		.bus = &mock_pci_bus[2],
> +	},
> +	[3] = {
> +		.bus = &mock_pci_bus[3],
> +	},
> +};
> +
> +static struct platform_device *mock_cxl_root_port(struct pci_bus *bus, int index)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(mock_pci_bus); i++)
> +		if (bus == &mock_pci_bus[i])
> +			return cxl_root_port[index + i * NR_CXL_ROOT_PORTS];
> +	return NULL;
> +}
> +
> +static bool is_mock_port(struct platform_device *pdev)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(cxl_root_port); i++)
> +		if (pdev == cxl_root_port[i])
> +			return true;
> +	return false;
> +}
> +
> +static bool is_mock_bus(struct pci_bus *bus)
> +{
> +	int i;
> +
> +	for (i = 0; i < ARRAY_SIZE(mock_pci_bus); i++)
> +		if (bus == &mock_pci_bus[i])
> +			return true;
> +	return false;
> +}
> +
> +static struct acpi_pci_root *mock_acpi_pci_find_root(acpi_handle handle)
> +{
> +	struct acpi_device *adev = find_host_bridge(handle);
> +
> +	if (!adev)
> +		return acpi_pci_find_root(handle);
> +	return &mock_pci_root[host_bridge_index(adev)];
> +}
> +
> +static struct cxl_mock_ops cxl_mock_ops = {
> +	.is_mock_adev = is_mock_adev,
> +	.is_mock_bridge = is_mock_bridge,
> +	.is_mock_bus = is_mock_bus,
> +	.is_mock_port = is_mock_port,
> +	.is_mock_dev = is_mock_dev,
> +	.mock_port = mock_cxl_root_port,
> +	.acpi_get_table = mock_acpi_get_table,
> +	.acpi_put_table = mock_acpi_put_table,
> +	.acpi_evaluate_integer = mock_acpi_evaluate_integer,
> +	.acpi_pci_find_root = mock_acpi_pci_find_root,
> +	.list = LIST_HEAD_INIT(cxl_mock_ops.list),
> +};
> +
> +static void mock_companion(struct acpi_device *adev, struct device *dev)
> +{
> +	device_initialize(&adev->dev);
> +	fwnode_init(&adev->fwnode, NULL);
> +	dev->fwnode = &adev->fwnode;
> +	adev->fwnode.dev = dev;
> +}
> +
> +#ifndef SZ_64G
> +#define SZ_64G (SZ_32G * 2)
> +#endif
> +
> +#ifndef SZ_512G
> +#define SZ_512G (SZ_64G * 8)
> +#endif
> +
> +static __init int cxl_test_init(void)
> +{
> +	int rc, i;
> +
> +	register_cxl_mock_ops(&cxl_mock_ops);
> +
> +	cxl_mock_pool = gen_pool_create(ilog2(SZ_2M), NUMA_NO_NODE);
> +	if (!cxl_mock_pool) {
> +		rc = -ENOMEM;
> +		goto err_gen_pool_create;
> +	}
> +
> +	rc = gen_pool_add(cxl_mock_pool, SZ_512G, SZ_64G, NUMA_NO_NODE);
> +	if (rc)
> +		goto err_gen_pool_add;
> +
> +	rc = populate_cedt();
> +	if (rc)
> +		goto err_populate;
> +
> +	for (i = 0; i < ARRAY_SIZE(cxl_host_bridge); i++) {
> +		struct acpi_device *adev = &host_bridge[i];
> +		struct platform_device *pdev;
> +
> +		pdev = platform_device_alloc("cxl_host_bridge", i);
> +		if (!pdev)
> +			goto err_bridge;
> +
> +		mock_companion(adev, &pdev->dev);
> +		rc = platform_device_add(pdev);
> +		if (rc) {
> +			platform_device_put(pdev);
> +			goto err_bridge;
> +		}
> +		cxl_host_bridge[i] = pdev;
> +	}
> +
> +	for (i = 0; i < ARRAY_SIZE(cxl_root_port); i++) {
> +		struct platform_device *bridge =
> +			cxl_host_bridge[i / NR_CXL_ROOT_PORTS];
> +		struct platform_device *pdev;
> +
> +		pdev = platform_device_alloc("cxl_root_port", i);
> +		if (!pdev)
> +			goto err_port;
> +		pdev->dev.parent = &bridge->dev;
> +
> +		rc = platform_device_add(pdev);
> +		if (rc) {
> +			platform_device_put(pdev);
> +			goto err_port;
> +		}
> +		cxl_root_port[i] = pdev;
> +	}
> +
> +	cxl_acpi = platform_device_alloc("cxl_acpi", 0);
> +	if (!cxl_acpi)
> +		goto err_port;
> +
> +	mock_companion(&acpi0017_mock, &cxl_acpi->dev);
> +	acpi0017_mock.dev.bus = &platform_bus_type;
> +
> +	rc = platform_device_add(cxl_acpi);
> +	if (rc)
> +		goto err_add;
> +
> +	return 0;
> +
> +err_add:
> +	platform_device_put(cxl_acpi);
> +err_port:
> +	for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--)
> +		platform_device_unregister(cxl_root_port[i]);
> +err_bridge:
> +	for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--)
> +		platform_device_unregister(cxl_host_bridge[i]);
> +err_populate:
> +	depopulate_all_mock_resources();
> +err_gen_pool_add:
> +	gen_pool_destroy(cxl_mock_pool);
> +err_gen_pool_create:
> +	unregister_cxl_mock_ops(&cxl_mock_ops);
> +	return rc;
> +}
> +
> +static __exit void cxl_test_exit(void)
> +{
> +	int i;
> +
> +	platform_device_unregister(cxl_acpi);
> +	for (i = ARRAY_SIZE(cxl_root_port) - 1; i >= 0; i--)
> +		platform_device_unregister(cxl_root_port[i]);
> +	for (i = ARRAY_SIZE(cxl_host_bridge) - 1; i >= 0; i--)
> +		platform_device_unregister(cxl_host_bridge[i]);
> +	depopulate_all_mock_resources();
> +	gen_pool_destroy(cxl_mock_pool);
> +	unregister_cxl_mock_ops(&cxl_mock_ops);
> +}
> +
> +module_init(cxl_test_init);
> +module_exit(cxl_test_exit);
> +MODULE_LICENSE("GPL v2");
> diff --git a/tools/testing/cxl/test/mock.c b/tools/testing/cxl/test/mock.c
> new file mode 100644
> index 000000000000..b8c108abcf07
> --- /dev/null
> +++ b/tools/testing/cxl/test/mock.c
> @@ -0,0 +1,171 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +//Copyright(c) 2021 Intel Corporation. All rights reserved.
> +
> +#include <linux/libnvdimm.h>
> +#include <linux/rculist.h>
> +#include <linux/device.h>
> +#include <linux/export.h>
> +#include <linux/acpi.h>
> +#include <linux/pci.h>
> +#include "mock.h"
> +
> +static LIST_HEAD(mock);
> +
> +void register_cxl_mock_ops(struct cxl_mock_ops *ops)
> +{
> +	list_add_rcu(&ops->list, &mock);
> +}
> +EXPORT_SYMBOL_GPL(register_cxl_mock_ops);
> +
> +static DEFINE_SRCU(cxl_mock_srcu);
> +
> +void unregister_cxl_mock_ops(struct cxl_mock_ops *ops)
> +{
> +	list_del_rcu(&ops->list);
> +	synchronize_srcu(&cxl_mock_srcu);
> +}
> +EXPORT_SYMBOL_GPL(unregister_cxl_mock_ops);
> +
> +struct cxl_mock_ops *get_cxl_mock_ops(int *index)
> +{
> +	*index = srcu_read_lock(&cxl_mock_srcu);
> +	return list_first_or_null_rcu(&mock, struct cxl_mock_ops, list);
> +}
> +EXPORT_SYMBOL_GPL(get_cxl_mock_ops);
> +
> +void put_cxl_mock_ops(int index)
> +{
> +	srcu_read_unlock(&cxl_mock_srcu, index);
> +}
> +EXPORT_SYMBOL_GPL(put_cxl_mock_ops);
> +
> +bool __wrap_is_acpi_device_node(const struct fwnode_handle *fwnode)
> +{
> +	struct acpi_device *adev =
> +		container_of(fwnode, struct acpi_device, fwnode);
> +	int index;
> +	struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
> +	bool retval = false;
> +
> +	if (ops)
> +		retval = ops->is_mock_adev(adev);
> +
> +	if (!retval)
> +		retval = is_acpi_device_node(fwnode);
> +
> +	put_cxl_mock_ops(index);
> +	return retval;
> +}
> +EXPORT_SYMBOL(__wrap_is_acpi_device_node);
> +
> +acpi_status __wrap_acpi_get_table(char *signature, u32 instance,
> +				  struct acpi_table_header **out_table)
> +{
> +	int index;
> +	struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
> +	acpi_status status;
> +
> +	if (ops)
> +		status = ops->acpi_get_table(signature, instance, out_table);
> +	else
> +		status = acpi_get_table(signature, instance, out_table);
> +
> +	put_cxl_mock_ops(index);
> +
> +	return status;
> +}
> +EXPORT_SYMBOL(__wrap_acpi_get_table);
> +
> +void __wrap_acpi_put_table(struct acpi_table_header *table)
> +{
> +	int index;
> +	struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
> +
> +	if (ops)
> +		ops->acpi_put_table(table);
> +	else
> +		acpi_put_table(table);
> +	put_cxl_mock_ops(index);
> +}
> +EXPORT_SYMBOL(__wrap_acpi_put_table);
> +
> +acpi_status __wrap_acpi_evaluate_integer(acpi_handle handle,
> +					 acpi_string pathname,
> +					 struct acpi_object_list *arguments,
> +					 unsigned long long *data)
> +{
> +	int index;
> +	struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
> +	acpi_status status;
> +
> +	if (ops)
> +		status = ops->acpi_evaluate_integer(handle, pathname, arguments,
> +						    data);
> +	else
> +		status = acpi_evaluate_integer(handle, pathname, arguments,
> +					       data);
> +	put_cxl_mock_ops(index);
> +
> +	return status;
> +}
> +EXPORT_SYMBOL(__wrap_acpi_evaluate_integer);
> +
> +struct acpi_pci_root *__wrap_acpi_pci_find_root(acpi_handle handle)
> +{
> +	int index;
> +	struct acpi_pci_root *root;
> +	struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
> +
> +	if (ops)
> +		root = ops->acpi_pci_find_root(handle);
> +	else
> +		root = acpi_pci_find_root(handle);
> +
> +	put_cxl_mock_ops(index);
> +
> +	return root;
> +}
> +EXPORT_SYMBOL_GPL(__wrap_acpi_pci_find_root);
> +
> +void __wrap_pci_walk_bus(struct pci_bus *bus,
> +			 int (*cb)(struct pci_dev *, void *), void *userdata)
> +{
> +	int index;
> +	struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
> +
> +	if (ops && ops->is_mock_bus(bus)) {
> +		int rc, i;
> +
> +		/*
> +		 * Simulate 2 root ports per host-bridge and no
> +		 * depth recursion.
> +		 */
> +		for (i = 0; i < 2; i++) {
> +			rc = cb((struct pci_dev *) ops->mock_port(bus, i),
> +				userdata);
> +			if (rc)
> +				break;
> +		}
> +	} else
> +		pci_walk_bus(bus, cb, userdata);
> +
> +	put_cxl_mock_ops(index);
> +}
> +EXPORT_SYMBOL_GPL(__wrap_pci_walk_bus);
> +
> +struct nvdimm_bus *
> +__wrap_nvdimm_bus_register(struct device *dev,
> +			   struct nvdimm_bus_descriptor *nd_desc)
> +{
> +	int index;
> +	struct cxl_mock_ops *ops = get_cxl_mock_ops(&index);
> +
> +	if (ops && ops->is_mock_dev(dev->parent->parent))
> +		nd_desc->provider_name = "cxl_test";
> +	put_cxl_mock_ops(index);
> +
> +	return nvdimm_bus_register(dev, nd_desc);
> +}
> +EXPORT_SYMBOL_GPL(__wrap_nvdimm_bus_register);
> +
> +MODULE_LICENSE("GPL v2");
> diff --git a/tools/testing/cxl/test/mock.h b/tools/testing/cxl/test/mock.h
> new file mode 100644
> index 000000000000..805a94cb3fbe
> --- /dev/null
> +++ b/tools/testing/cxl/test/mock.h
> @@ -0,0 +1,27 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +
> +#include <linux/list.h>
> +#include <linux/acpi.h>
> +
> +struct cxl_mock_ops {
> +	struct list_head list;
> +	bool (*is_mock_adev)(struct acpi_device *dev);
> +	acpi_status (*acpi_get_table)(char *signature, u32 instance,
> +				      struct acpi_table_header **out_table);
> +	void (*acpi_put_table)(struct acpi_table_header *table);
> +	bool (*is_mock_bridge)(struct device *dev);
> +	acpi_status (*acpi_evaluate_integer)(acpi_handle handle,
> +					     acpi_string pathname,
> +					     struct acpi_object_list *arguments,
> +					     unsigned long long *data);
> +	struct acpi_pci_root *(*acpi_pci_find_root)(acpi_handle handle);
> +	struct platform_device *(*mock_port)(struct pci_bus *bus, int index);
> +	bool (*is_mock_bus)(struct pci_bus *bus);
> +	bool (*is_mock_port)(struct platform_device *pdev);
> +	bool (*is_mock_dev)(struct device *dev);
> +};
> +
> +void register_cxl_mock_ops(struct cxl_mock_ops *ops);
> +void unregister_cxl_mock_ops(struct cxl_mock_ops *ops);
> +struct cxl_mock_ops *get_cxl_mock_ops(int *index);
> +void put_cxl_mock_ops(int index);
> 


  reply	other threads:[~2021-09-10  9:53 UTC|newest]

Thread overview: 78+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-09-09  5:11 [PATCH v4 00/21] cxl_test: Enable CXL Topology and UAPI regression tests Dan Williams
2021-09-09  5:11 ` [PATCH v4 01/21] libnvdimm/labels: Add uuid helpers Dan Williams
2021-09-09  5:11 ` [PATCH v4 02/21] libnvdimm/label: Add a helper for nlabel validation Dan Williams
2021-09-09  5:11 ` [PATCH v4 03/21] libnvdimm/labels: Introduce the concept of multi-range namespace labels Dan Williams
2021-09-09 13:09   ` Jonathan Cameron
2021-09-09 15:16     ` Dan Williams
2021-09-09  5:11 ` [PATCH v4 04/21] libnvdimm/labels: Fix kernel-doc for label.h Dan Williams
2021-09-10  8:38   ` Jonathan Cameron
2021-09-09  5:11 ` [PATCH v4 05/21] libnvdimm/label: Define CXL region labels Dan Williams
2021-09-09 15:58   ` Ben Widawsky
2021-09-09 18:38     ` Dan Williams
2021-09-09  5:12 ` [PATCH v4 06/21] libnvdimm/labels: Introduce CXL labels Dan Williams
2021-09-09  5:12 ` [PATCH v4 07/21] cxl/pci: Make 'struct cxl_mem' device type generic Dan Williams
2021-09-09 16:12   ` Ben Widawsky
2021-09-10  8:43   ` Jonathan Cameron
2021-09-09  5:12 ` [PATCH v4 08/21] cxl/pci: Clean up cxl_mem_get_partition_info() Dan Williams
2021-09-09 16:20   ` Ben Widawsky
2021-09-09 18:06     ` Dan Williams
2021-09-09 21:05       ` Ben Widawsky
2021-09-09 21:10         ` Dan Williams
2021-09-10  8:56         ` Jonathan Cameron
2021-09-13 22:19   ` [PATCH v5 " Dan Williams
2021-09-13 22:21     ` Dan Williams
2021-09-13 22:24   ` [PATCH v6 " Dan Williams
2021-09-09  5:12 ` [PATCH v4 09/21] cxl/mbox: Introduce the mbox_send operation Dan Williams
2021-09-09 16:34   ` Ben Widawsky
2021-09-10  8:58   ` Jonathan Cameron
2021-09-09  5:12 ` [PATCH v4 10/21] cxl/pci: Drop idr.h Dan Williams
2021-09-09 16:34   ` Ben Widawsky
2021-09-10  8:46     ` Jonathan Cameron
2021-09-09  5:12 ` [PATCH v4 11/21] cxl/mbox: Move mailbox and other non-PCI specific infrastructure to the core Dan Williams
2021-09-09 16:41   ` Ben Widawsky
2021-09-09 18:50     ` Dan Williams
2021-09-09 20:35       ` Ben Widawsky
2021-09-09 21:05         ` Dan Williams
2021-09-10  9:13   ` Jonathan Cameron
2021-09-09  5:12 ` [PATCH v4 12/21] cxl/pci: Use module_pci_driver Dan Williams
2021-09-09  5:12 ` [PATCH v4 13/21] cxl/mbox: Convert 'enabled_cmds' to DECLARE_BITMAP Dan Williams
2021-09-09  5:12 ` [PATCH v4 14/21] cxl/mbox: Add exclusive kernel command support Dan Williams
2021-09-09 17:02   ` Ben Widawsky
2021-09-10  9:33   ` Jonathan Cameron
2021-09-13 23:46     ` Dan Williams
2021-09-14  9:01       ` Jonathan Cameron
2021-09-14 12:22       ` Konstantin Ryabitsev
2021-09-14 14:39         ` Dan Williams
2021-09-14 15:51           ` Konstantin Ryabitsev
2021-09-14 19:03   ` [PATCH v5 " Dan Williams
2021-09-09  5:12 ` [PATCH v4 15/21] cxl/pmem: Translate NVDIMM label commands to CXL label commands Dan Williams
2021-09-09 17:22   ` Ben Widawsky
2021-09-09 19:03     ` Dan Williams
2021-09-09 20:32       ` Ben Widawsky
2021-09-10  9:39         ` Jonathan Cameron
2021-09-09 22:08   ` [PATCH v5 " Dan Williams
2021-09-10  9:40     ` Jonathan Cameron
2021-09-14 19:06   ` Dan Williams
2021-09-09  5:12 ` [PATCH v4 16/21] cxl/pmem: Add support for multiple nvdimm-bridge objects Dan Williams
2021-09-09 22:03   ` Dan Williams
2021-09-14 19:08   ` [PATCH v5 " Dan Williams
2021-09-09  5:13 ` [PATCH v4 17/21] tools/testing/cxl: Introduce a mocked-up CXL port hierarchy Dan Williams
2021-09-10  9:53   ` Jonathan Cameron [this message]
2021-09-10 18:46     ` Dan Williams
2021-09-14 19:14   ` [PATCH v5 " Dan Williams
2021-09-09  5:13 ` [PATCH v4 18/21] cxl/bus: Populate the target list at decoder create Dan Williams
2021-09-10  9:57   ` Jonathan Cameron
2021-09-09  5:13 ` [PATCH v4 19/21] cxl/mbox: Move command definitions to common location Dan Williams
2021-09-09  5:13 ` [PATCH v4 20/21] tools/testing/cxl: Introduce a mock memory device + driver Dan Williams
2021-09-10 10:09   ` Jonathan Cameron
2021-09-09  5:13 ` [PATCH v4 21/21] cxl/core: Split decoder setup into alloc + add Dan Williams
2021-09-10 10:33   ` Jonathan Cameron
2021-09-10 18:36     ` Dan Williams
2021-09-11 17:15       ` Ben Widawsky
2021-09-11 20:20         ` Dan Williams
2021-09-14 19:31   ` [PATCH v5 " Dan Williams
2021-09-21 14:24     ` Ben Widawsky
2021-09-21 16:18       ` Dan Williams
2021-09-21 19:22     ` [PATCH v6 " Dan Williams
2021-12-10 19:38       ` Nathan Chancellor
2021-12-10 19:41         ` Dan Williams

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=20210910105313.00006408@Huawei.com \
    --to=jonathan.cameron@huawei.com \
    --cc=alison.schofield@intel.com \
    --cc=ben.widawsky@intel.com \
    --cc=dan.j.williams@intel.com \
    --cc=ira.weiny@intel.com \
    --cc=linux-cxl@vger.kernel.org \
    --cc=nvdimm@lists.linux.dev \
    --cc=vishal.l.verma@intel.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