linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/10] Add support for OpenCAPI SCM devices
@ 2019-10-25  4:46 Alastair D'Silva
  2019-10-25  4:46 ` [PATCH 01/10] memory_hotplug: Add a bounds check to __add_pages Alastair D'Silva
                   ` (9 more replies)
  0 siblings, 10 replies; 45+ messages in thread
From: Alastair D'Silva @ 2019-10-25  4:46 UTC (permalink / raw)
  To: alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Frederic Barrat, Andrew Donnellan, Arnd Bergmann,
	Greg Kroah-Hartman, Dan Williams, Vishal Verma, Dave Jiang,
	Keith Busch, Ira Weiny, Anton Blanchard, Krzysztof Kozlowski,
	Geert Uytterhoeven, Anju T Sudhakar, Cédric Le Goater,
	Mahesh Salgaonkar, Hari Bathini, Vasant Hegde, Thomas Gleixner,
	Allison Randal, Greg Kurz, Nicholas Piggin, Alexey Kardashevskiy,
	Masahiro Yamada, Andrew Morton, David Hildenbrand,
	Oscar Salvador, Michal Hocko, Pavel Tatashin, Wei Yang, Qian Cai,
	linuxppc-dev, linux-kernel, linux-nvdimm, linux-mm

From: Alastair D'Silva <alastair@d-silva.org>

This series adds support for OpenCAPI SCM devices, exposing
them as nvdimms so that we can make use of the existing
infrastructure.

The first patch (in memory_hotplug) has reviews/acks, but has
not yet made it upstream.

Alastair D'Silva (10):
  memory_hotplug: Add a bounds check to __add_pages
  nvdimm: remove prototypes for nonexistent functions
  powerpc: Add OPAL calls for LPC memory alloc/release
  powerpc: Map & release OpenCAPI LPC memory
  ocxl: Tally up the LPC memory on a link & allow it to be mapped
  ocxl: Add functions to map/unmap LPC memory
  ocxl: Save the device serial number in ocxl_fn
  nvdimm: Add driver for OpenCAPI Storage Class Memory
  powerpc: Enable OpenCAPI Storage Class Memory driver on bare metal
  ocxl: Conditionally bind SCM devices to the generic OCXL driver

 arch/powerpc/configs/powernv_defconfig     |    4 +
 arch/powerpc/include/asm/opal-api.h        |    2 +
 arch/powerpc/include/asm/opal.h            |    3 +
 arch/powerpc/include/asm/pnv-ocxl.h        |    2 +
 arch/powerpc/platforms/powernv/ocxl.c      |   41 +
 arch/powerpc/platforms/powernv/opal-call.c |    2 +
 drivers/misc/ocxl/Kconfig                  |    7 +
 drivers/misc/ocxl/config.c                 |   50 +
 drivers/misc/ocxl/core.c                   |   60 +
 drivers/misc/ocxl/link.c                   |   60 +
 drivers/misc/ocxl/ocxl_internal.h          |   36 +
 drivers/misc/ocxl/pci.c                    |    3 +
 drivers/nvdimm/Kconfig                     |   17 +
 drivers/nvdimm/Makefile                    |    3 +
 drivers/nvdimm/nd-core.h                   |    4 -
 drivers/nvdimm/ocxl-scm.c                  | 2210 ++++++++++++++++++++
 drivers/nvdimm/ocxl-scm_internal.c         |  232 ++
 drivers/nvdimm/ocxl-scm_internal.h         |  331 +++
 drivers/nvdimm/ocxl-scm_sysfs.c            |  219 ++
 include/linux/memory_hotplug.h             |    5 +
 include/misc/ocxl.h                        |   19 +
 include/uapi/linux/ocxl-scm.h              |  128 ++
 mm/memory_hotplug.c                        |   22 +
 23 files changed, 3456 insertions(+), 4 deletions(-)
 create mode 100644 drivers/nvdimm/ocxl-scm.c
 create mode 100644 drivers/nvdimm/ocxl-scm_internal.c
 create mode 100644 drivers/nvdimm/ocxl-scm_internal.h
 create mode 100644 drivers/nvdimm/ocxl-scm_sysfs.c
 create mode 100644 include/uapi/linux/ocxl-scm.h

-- 
2.21.0



^ permalink raw reply	[flat|nested] 45+ messages in thread

* [PATCH 01/10] memory_hotplug: Add a bounds check to __add_pages
  2019-10-25  4:46 [PATCH 00/10] Add support for OpenCAPI SCM devices Alastair D'Silva
@ 2019-10-25  4:46 ` Alastair D'Silva
  2019-10-25  4:46 ` [PATCH 02/10] nvdimm: remove prototypes for nonexistent functions Alastair D'Silva
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 45+ messages in thread
From: Alastair D'Silva @ 2019-10-25  4:46 UTC (permalink / raw)
  To: alastair
  Cc: David Hildenbrand, Michal Hocko, Benjamin Herrenschmidt,
	Paul Mackerras, Michael Ellerman, Frederic Barrat,
	Andrew Donnellan, Arnd Bergmann, Greg Kroah-Hartman,
	Dan Williams, Vishal Verma, Dave Jiang, Keith Busch, Ira Weiny,
	Anton Blanchard, Geert Uytterhoeven, Krzysztof Kozlowski,
	Vasant Hegde, Hari Bathini, Cédric Le Goater,
	Mahesh Salgaonkar, Thomas Gleixner, Greg Kurz, David Gibson,
	Masahiro Yamada, Nicholas Piggin, Alexey Kardashevskiy,
	Andrew Morton, Oscar Salvador, Pavel Tatashin, Wei Yang,
	Qian Cai, linuxppc-dev, linux-kernel, linux-nvdimm, linux-mm

From: Alastair D'Silva <alastair@d-silva.org>

On PowerPC, the address ranges allocated to OpenCAPI LPC memory
are allocated from firmware. These address ranges may be higher
than what older kernels permit, as we increased the maximum
permissable address in commit 4ffe713b7587
("powerpc/mm: Increase the max addressable memory to 2PB"). It is
possible that the addressable range may change again in the
future.

In this scenario, we end up with a bogus section returned from
__section_nr (see the discussion on the thread "mm: Trigger bug on
if a section is not found in __section_nr").

Adding a check here means that we fail early and have an
opportunity to handle the error gracefully, rather than rumbling
on and potentially accessing an incorrect section.

Further discussion is also on the thread ("powerpc: Perform a bounds
check in arch_add_memory")
http://lkml.kernel.org/r/20190827052047.31547-1-alastair@au1.ibm.com

Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
Reviewed-by: David Hildenbrand <david@redhat.com>
Acked-by: Michal Hocko <mhocko@suse.com>
---
 mm/memory_hotplug.c | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index df570e5c71cc..2cecf07b396f 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -278,6 +278,23 @@ static int check_pfn_span(unsigned long pfn, unsigned long nr_pages,
 	return 0;
 }
 
+static int check_hotplug_memory_addressable(unsigned long pfn,
+					    unsigned long nr_pages)
+{
+	const u64 max_addr = PFN_PHYS(pfn + nr_pages) - 1;
+
+	if (max_addr >> MAX_PHYSMEM_BITS) {
+		const u64 max_allowed = (1ull << (MAX_PHYSMEM_BITS + 1)) - 1;
+
+		WARN(1,
+		     "Hotplugged memory exceeds maximum addressable address, range=%#llx-%#llx, maximum=%#llx\n",
+		     PFN_PHYS(pfn), max_addr, max_allowed);
+		return -E2BIG;
+	}
+
+	return 0;
+}
+
 /*
  * Reasonably generic function for adding memory.  It is
  * expected that archs that support memory hotplug will
@@ -291,6 +308,10 @@ int __ref __add_pages(int nid, unsigned long pfn, unsigned long nr_pages,
 	unsigned long nr, start_sec, end_sec;
 	struct vmem_altmap *altmap = restrictions->altmap;
 
+	err = check_hotplug_memory_addressable(pfn, nr_pages);
+	if (err)
+		return err;
+
 	if (altmap) {
 		/*
 		 * Validate altmap is within bounds of the total request
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 02/10] nvdimm: remove prototypes for nonexistent functions
  2019-10-25  4:46 [PATCH 00/10] Add support for OpenCAPI SCM devices Alastair D'Silva
  2019-10-25  4:46 ` [PATCH 01/10] memory_hotplug: Add a bounds check to __add_pages Alastair D'Silva
@ 2019-10-25  4:46 ` Alastair D'Silva
  2019-10-27 23:41   ` Andrew Donnellan
                     ` (2 more replies)
  2019-10-25  4:46 ` [PATCH 03/10] powerpc: Add OPAL calls for LPC memory alloc/release Alastair D'Silva
                   ` (7 subsequent siblings)
  9 siblings, 3 replies; 45+ messages in thread
From: Alastair D'Silva @ 2019-10-25  4:46 UTC (permalink / raw)
  To: alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Frederic Barrat, Andrew Donnellan, Arnd Bergmann,
	Greg Kroah-Hartman, Dan Williams, Vishal Verma, Dave Jiang,
	Keith Busch, Ira Weiny, Krzysztof Kozlowski, Geert Uytterhoeven,
	Anton Blanchard, David Gibson, Hari Bathini, Anju T Sudhakar,
	Thomas Gleixner, Cédric Le Goater, Mahesh Salgaonkar,
	Greg Kurz, Masahiro Yamada, Andrew Morton, David Hildenbrand,
	Oscar Salvador, Michal Hocko, Pavel Tatashin, Wei Yang, Qian Cai,
	linuxppc-dev, linux-kernel, linux-nvdimm, linux-mm

From: Alastair D'Silva <alastair@d-silva.org>

These functions don't exist, so remove the prototypes for them.

Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
---
 drivers/nvdimm/nd-core.h | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h
index 25fa121104d0..9f121a6aeb02 100644
--- a/drivers/nvdimm/nd-core.h
+++ b/drivers/nvdimm/nd-core.h
@@ -124,11 +124,7 @@ void nd_region_create_dax_seed(struct nd_region *nd_region);
 int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus);
 void nvdimm_bus_destroy_ndctl(struct nvdimm_bus *nvdimm_bus);
 void nd_synchronize(void);
-int nvdimm_bus_register_dimms(struct nvdimm_bus *nvdimm_bus);
-int nvdimm_bus_register_regions(struct nvdimm_bus *nvdimm_bus);
-int nvdimm_bus_init_interleave_sets(struct nvdimm_bus *nvdimm_bus);
 void __nd_device_register(struct device *dev);
-int nd_match_dimm(struct device *dev, void *data);
 struct nd_label_id;
 char *nd_label_gen_id(struct nd_label_id *label_id, u8 *uuid, u32 flags);
 bool nd_is_uuid_unique(struct device *dev, u8 *uuid);
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 03/10] powerpc: Add OPAL calls for LPC memory alloc/release
  2019-10-25  4:46 [PATCH 00/10] Add support for OpenCAPI SCM devices Alastair D'Silva
  2019-10-25  4:46 ` [PATCH 01/10] memory_hotplug: Add a bounds check to __add_pages Alastair D'Silva
  2019-10-25  4:46 ` [PATCH 02/10] nvdimm: remove prototypes for nonexistent functions Alastair D'Silva
@ 2019-10-25  4:46 ` Alastair D'Silva
  2019-11-07 17:57   ` Frederic Barrat
  2019-10-25  4:46 ` [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory Alastair D'Silva
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 45+ messages in thread
From: Alastair D'Silva @ 2019-10-25  4:46 UTC (permalink / raw)
  To: alastair
  Cc: Andrew Donnellan, Benjamin Herrenschmidt, Paul Mackerras,
	Michael Ellerman, Frederic Barrat, Arnd Bergmann,
	Greg Kroah-Hartman, Dan Williams, Vishal Verma, Dave Jiang,
	Keith Busch, Ira Weiny, Anton Blanchard, Geert Uytterhoeven,
	Krzysztof Kozlowski, Thomas Gleixner, Vasant Hegde,
	Anju T Sudhakar, Hari Bathini, Mahesh Salgaonkar,
	Cédric Le Goater, Allison Randal, Greg Kurz,
	Nicholas Piggin, David Gibson, Alexey Kardashevskiy,
	Andrew Morton, David Hildenbrand, Oscar Salvador, Michal Hocko,
	Pavel Tatashin, Wei Yang, Qian Cai, linuxppc-dev, linux-kernel,
	linux-nvdimm, linux-mm

From: Alastair D'Silva <alastair@d-silva.org>

Add OPAL calls for LPC memory alloc/release

Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
Acked-by: Andrew Donnellan <ajd@linux.ibm.com>
---
 arch/powerpc/include/asm/opal-api.h        | 2 ++
 arch/powerpc/include/asm/opal.h            | 3 +++
 arch/powerpc/platforms/powernv/opal-call.c | 2 ++
 3 files changed, 7 insertions(+)

diff --git a/arch/powerpc/include/asm/opal-api.h b/arch/powerpc/include/asm/opal-api.h
index 378e3997845a..2c88c02e69ed 100644
--- a/arch/powerpc/include/asm/opal-api.h
+++ b/arch/powerpc/include/asm/opal-api.h
@@ -208,6 +208,8 @@
 #define OPAL_HANDLE_HMI2			166
 #define	OPAL_NX_COPROC_INIT			167
 #define OPAL_XIVE_GET_VP_STATE			170
+#define OPAL_NPU_MEM_ALLOC			171
+#define OPAL_NPU_MEM_RELEASE			172
 #define OPAL_MPIPL_UPDATE			173
 #define OPAL_MPIPL_REGISTER_TAG			174
 #define OPAL_MPIPL_QUERY_TAG			175
diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
index a0cf8fba4d12..4db135fb54ab 100644
--- a/arch/powerpc/include/asm/opal.h
+++ b/arch/powerpc/include/asm/opal.h
@@ -39,6 +39,9 @@ int64_t opal_npu_spa_clear_cache(uint64_t phb_id, uint32_t bdfn,
 				uint64_t PE_handle);
 int64_t opal_npu_tl_set(uint64_t phb_id, uint32_t bdfn, long cap,
 			uint64_t rate_phys, uint32_t size);
+int64_t opal_npu_mem_alloc(uint64_t phb_id, uint32_t bdfn,
+			uint64_t size, uint64_t *bar);
+int64_t opal_npu_mem_release(uint64_t phb_id, uint32_t bdfn);
 
 int64_t opal_console_write(int64_t term_number, __be64 *length,
 			   const uint8_t *buffer);
diff --git a/arch/powerpc/platforms/powernv/opal-call.c b/arch/powerpc/platforms/powernv/opal-call.c
index a2aa5e433ac8..27c4b93c774c 100644
--- a/arch/powerpc/platforms/powernv/opal-call.c
+++ b/arch/powerpc/platforms/powernv/opal-call.c
@@ -287,6 +287,8 @@ OPAL_CALL(opal_pci_set_pbcq_tunnel_bar,		OPAL_PCI_SET_PBCQ_TUNNEL_BAR);
 OPAL_CALL(opal_sensor_read_u64,			OPAL_SENSOR_READ_U64);
 OPAL_CALL(opal_sensor_group_enable,		OPAL_SENSOR_GROUP_ENABLE);
 OPAL_CALL(opal_nx_coproc_init,			OPAL_NX_COPROC_INIT);
+OPAL_CALL(opal_npu_mem_alloc,			OPAL_NPU_MEM_ALLOC);
+OPAL_CALL(opal_npu_mem_release,			OPAL_NPU_MEM_RELEASE);
 OPAL_CALL(opal_mpipl_update,			OPAL_MPIPL_UPDATE);
 OPAL_CALL(opal_mpipl_register_tag,		OPAL_MPIPL_REGISTER_TAG);
 OPAL_CALL(opal_mpipl_query_tag,			OPAL_MPIPL_QUERY_TAG);
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory
  2019-10-25  4:46 [PATCH 00/10] Add support for OpenCAPI SCM devices Alastair D'Silva
                   ` (2 preceding siblings ...)
  2019-10-25  4:46 ` [PATCH 03/10] powerpc: Add OPAL calls for LPC memory alloc/release Alastair D'Silva
@ 2019-10-25  4:46 ` Alastair D'Silva
  2019-10-27 21:24   ` kbuild test robot
                     ` (4 more replies)
  2019-10-25  4:47 ` [PATCH 05/10] ocxl: Tally up the LPC memory on a link & allow it to be mapped Alastair D'Silva
                   ` (5 subsequent siblings)
  9 siblings, 5 replies; 45+ messages in thread
From: Alastair D'Silva @ 2019-10-25  4:46 UTC (permalink / raw)
  To: alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Frederic Barrat, Andrew Donnellan, Arnd Bergmann,
	Greg Kroah-Hartman, Dan Williams, Vishal Verma, Dave Jiang,
	Keith Busch, Ira Weiny, Geert Uytterhoeven, Krzysztof Kozlowski,
	Anton Blanchard, Allison Randal, Cédric Le Goater,
	David Gibson, Vasant Hegde, Thomas Gleixner, Anju T Sudhakar,
	Hari Bathini, Mahesh Salgaonkar, Greg Kurz, Masahiro Yamada,
	Alexey Kardashevskiy, Andrew Morton, David Hildenbrand,
	Oscar Salvador, Michal Hocko, Pavel Tatashin, Wei Yang, Qian Cai,
	linuxppc-dev, linux-kernel, linux-nvdimm, linux-mm

From: Alastair D'Silva <alastair@d-silva.org>

This patch adds platform support to map & release LPC memory.

Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
---
 arch/powerpc/include/asm/pnv-ocxl.h   |  2 ++
 arch/powerpc/platforms/powernv/ocxl.c | 41 +++++++++++++++++++++++++++
 include/linux/memory_hotplug.h        |  5 ++++
 mm/memory_hotplug.c                   |  3 +-
 4 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/arch/powerpc/include/asm/pnv-ocxl.h b/arch/powerpc/include/asm/pnv-ocxl.h
index 7de82647e761..f8f8ffb48aa8 100644
--- a/arch/powerpc/include/asm/pnv-ocxl.h
+++ b/arch/powerpc/include/asm/pnv-ocxl.h
@@ -32,5 +32,7 @@ extern int pnv_ocxl_spa_remove_pe_from_cache(void *platform_data, int pe_handle)
 
 extern int pnv_ocxl_alloc_xive_irq(u32 *irq, u64 *trigger_addr);
 extern void pnv_ocxl_free_xive_irq(u32 irq);
+extern u64 pnv_ocxl_platform_lpc_setup(struct pci_dev *pdev, u64 size);
+extern void pnv_ocxl_platform_lpc_release(struct pci_dev *pdev);
 
 #endif /* _ASM_PNV_OCXL_H */
diff --git a/arch/powerpc/platforms/powernv/ocxl.c b/arch/powerpc/platforms/powernv/ocxl.c
index 8c65aacda9c8..c6d4234e0aba 100644
--- a/arch/powerpc/platforms/powernv/ocxl.c
+++ b/arch/powerpc/platforms/powernv/ocxl.c
@@ -475,6 +475,47 @@ void pnv_ocxl_spa_release(void *platform_data)
 }
 EXPORT_SYMBOL_GPL(pnv_ocxl_spa_release);
 
+u64 pnv_ocxl_platform_lpc_setup(struct pci_dev *pdev, u64 size)
+{
+	struct pci_controller *hose = pci_bus_to_host(pdev->bus);
+	struct pnv_phb *phb = hose->private_data;
+	u32 bdfn = (pdev->bus->number << 8) | pdev->devfn;
+	u64 base_addr = 0;
+	int rc;
+
+	rc = opal_npu_mem_alloc(phb->opal_id, bdfn, size, &base_addr);
+	if (rc) {
+		dev_warn(&pdev->dev,
+			 "OPAL could not allocate LPC memory, rc=%d\n", rc);
+		return 0;
+	}
+
+	base_addr = be64_to_cpu(base_addr);
+
+	rc = check_hotplug_memory_addressable(base_addr >> PAGE_SHIFT,
+					      size >> PAGE_SHIFT);
+	if (rc)
+		return 0;
+
+	return base_addr;
+}
+EXPORT_SYMBOL_GPL(pnv_ocxl_platform_lpc_setup);
+
+void pnv_ocxl_platform_lpc_release(struct pci_dev *pdev)
+{
+	struct pci_controller *hose = pci_bus_to_host(pdev->bus);
+	struct pnv_phb *phb = hose->private_data;
+	u32 bdfn = (pdev->bus->number << 8) | pdev->devfn;
+	int rc;
+
+	rc = opal_npu_mem_release(phb->opal_id, bdfn);
+	if (rc)
+		dev_warn(&pdev->dev,
+			 "OPAL reported rc=%d when releasing LPC memory\n", rc);
+}
+EXPORT_SYMBOL_GPL(pnv_ocxl_platform_lpc_release);
+
+
 int pnv_ocxl_spa_remove_pe_from_cache(void *platform_data, int pe_handle)
 {
 	struct spa_data *data = (struct spa_data *) platform_data;
diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
index f46ea71b4ffd..3f5f1a642abe 100644
--- a/include/linux/memory_hotplug.h
+++ b/include/linux/memory_hotplug.h
@@ -339,6 +339,11 @@ static inline int remove_memory(int nid, u64 start, u64 size)
 static inline void __remove_memory(int nid, u64 start, u64 size) {}
 #endif /* CONFIG_MEMORY_HOTREMOVE */
 
+#if CONFIG_MEMORY_HOTPLUG_SPARSE
+int check_hotplug_memory_addressable(unsigned long pfn,
+		unsigned long nr_pages);
+#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
+
 extern void __ref free_area_init_core_hotplug(int nid);
 extern int __add_memory(int nid, u64 start, u64 size);
 extern int add_memory(int nid, u64 start, u64 size);
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index 2cecf07b396f..b39827dbd071 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -278,7 +278,7 @@ static int check_pfn_span(unsigned long pfn, unsigned long nr_pages,
 	return 0;
 }
 
-static int check_hotplug_memory_addressable(unsigned long pfn,
+int check_hotplug_memory_addressable(unsigned long pfn,
 					    unsigned long nr_pages)
 {
 	const u64 max_addr = PFN_PHYS(pfn + nr_pages) - 1;
@@ -294,6 +294,7 @@ static int check_hotplug_memory_addressable(unsigned long pfn,
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(check_hotplug_memory_addressable);
 
 /*
  * Reasonably generic function for adding memory.  It is
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 05/10] ocxl: Tally up the LPC memory on a link & allow it to be mapped
  2019-10-25  4:46 [PATCH 00/10] Add support for OpenCAPI SCM devices Alastair D'Silva
                   ` (3 preceding siblings ...)
  2019-10-25  4:46 ` [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory Alastair D'Silva
@ 2019-10-25  4:47 ` Alastair D'Silva
  2019-11-07 18:02   ` Frederic Barrat
  2019-10-25  4:47 ` [PATCH 06/10] ocxl: Add functions to map/unmap LPC memory Alastair D'Silva
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 45+ messages in thread
From: Alastair D'Silva @ 2019-10-25  4:47 UTC (permalink / raw)
  To: alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Frederic Barrat, Andrew Donnellan, Arnd Bergmann,
	Greg Kroah-Hartman, Dan Williams, Vishal Verma, Dave Jiang,
	Keith Busch, Ira Weiny, Krzysztof Kozlowski, Anton Blanchard,
	Allison Randal, Madhavan Srinivasan, Mahesh Salgaonkar,
	Hari Bathini, Thomas Gleixner, Cédric Le Goater, Greg Kurz,
	Alexey Kardashevskiy, David Gibson, Masahiro Yamada,
	Andrew Morton, David Hildenbrand, Oscar Salvador, Michal Hocko,
	Pavel Tatashin, Wei Yang, Qian Cai, linuxppc-dev, linux-kernel,
	linux-nvdimm, linux-mm

From: Alastair D'Silva <alastair@d-silva.org>

Tally up the LPC memory on an OpenCAPI link & allow it to be mapped

Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
---
 drivers/misc/ocxl/core.c          | 10 ++++++
 drivers/misc/ocxl/link.c          | 60 +++++++++++++++++++++++++++++++
 drivers/misc/ocxl/ocxl_internal.h | 33 +++++++++++++++++
 3 files changed, 103 insertions(+)

diff --git a/drivers/misc/ocxl/core.c b/drivers/misc/ocxl/core.c
index b7a09b21ab36..2531c6cf19a0 100644
--- a/drivers/misc/ocxl/core.c
+++ b/drivers/misc/ocxl/core.c
@@ -230,8 +230,18 @@ static int configure_afu(struct ocxl_afu *afu, u8 afu_idx, struct pci_dev *dev)
 	if (rc)
 		goto err_free_pasid;
 
+	if (afu->config.lpc_mem_size || afu->config.special_purpose_mem_size) {
+		rc = ocxl_link_add_lpc_mem(afu->fn->link, afu->config.lpc_mem_offset,
+					   afu->config.lpc_mem_size +
+					   afu->config.special_purpose_mem_size);
+		if (rc)
+			goto err_free_mmio;
+	}
+
 	return 0;
 
+err_free_mmio:
+	unmap_mmio_areas(afu);
 err_free_pasid:
 	reclaim_afu_pasid(afu);
 err_free_actag:
diff --git a/drivers/misc/ocxl/link.c b/drivers/misc/ocxl/link.c
index 58d111afd9f6..1d350d0bb860 100644
--- a/drivers/misc/ocxl/link.c
+++ b/drivers/misc/ocxl/link.c
@@ -84,6 +84,11 @@ struct ocxl_link {
 	int dev;
 	atomic_t irq_available;
 	struct spa *spa;
+	struct mutex lpc_mem_lock;
+	u64 lpc_mem_sz; /* Total amount of LPC memory presented on the link */
+	u64 lpc_mem;
+	int lpc_consumers;
+
 	void *platform_data;
 };
 static struct list_head links_list = LIST_HEAD_INIT(links_list);
@@ -396,6 +401,8 @@ static int alloc_link(struct pci_dev *dev, int PE_mask, struct ocxl_link **out_l
 	if (rc)
 		goto err_spa;
 
+	mutex_init(&link->lpc_mem_lock);
+
 	/* platform specific hook */
 	rc = pnv_ocxl_spa_setup(dev, link->spa->spa_mem, PE_mask,
 				&link->platform_data);
@@ -711,3 +718,56 @@ void ocxl_link_free_irq(void *link_handle, int hw_irq)
 	atomic_inc(&link->irq_available);
 }
 EXPORT_SYMBOL_GPL(ocxl_link_free_irq);
+
+int ocxl_link_add_lpc_mem(void *link_handle, u64 offset, u64 size)
+{
+	struct ocxl_link *link = (struct ocxl_link *) link_handle;
+
+	// Check for overflow
+	if (offset > (offset + size))
+		return -EINVAL;
+
+	mutex_lock(&link->lpc_mem_lock);
+	link->lpc_mem_sz = max(link->lpc_mem_sz, offset + size);
+
+	mutex_unlock(&link->lpc_mem_lock);
+
+	return 0;
+}
+
+u64 ocxl_link_lpc_map(void *link_handle, struct pci_dev *pdev)
+{
+	struct ocxl_link *link = (struct ocxl_link *) link_handle;
+	u64 lpc_mem;
+
+	mutex_lock(&link->lpc_mem_lock);
+	if (link->lpc_mem) {
+		lpc_mem = link->lpc_mem;
+
+		link->lpc_consumers++;
+		mutex_unlock(&link->lpc_mem_lock);
+		return lpc_mem;
+	}
+
+	link->lpc_mem = pnv_ocxl_platform_lpc_setup(pdev, link->lpc_mem_sz);
+	if (link->lpc_mem)
+		link->lpc_consumers++;
+	lpc_mem = link->lpc_mem;
+	mutex_unlock(&link->lpc_mem_lock);
+
+	return lpc_mem;
+}
+
+void ocxl_link_lpc_release(void *link_handle, struct pci_dev *pdev)
+{
+	struct ocxl_link *link = (struct ocxl_link *) link_handle;
+
+	mutex_lock(&link->lpc_mem_lock);
+	link->lpc_consumers--;
+	if (link->lpc_consumers == 0) {
+		pnv_ocxl_platform_lpc_release(pdev);
+		link->lpc_mem = 0;
+	}
+
+	mutex_unlock(&link->lpc_mem_lock);
+}
diff --git a/drivers/misc/ocxl/ocxl_internal.h b/drivers/misc/ocxl/ocxl_internal.h
index 97415afd79f3..20b417e00949 100644
--- a/drivers/misc/ocxl/ocxl_internal.h
+++ b/drivers/misc/ocxl/ocxl_internal.h
@@ -141,4 +141,37 @@ int ocxl_irq_offset_to_id(struct ocxl_context *ctx, u64 offset);
 u64 ocxl_irq_id_to_offset(struct ocxl_context *ctx, int irq_id);
 void ocxl_afu_irq_free_all(struct ocxl_context *ctx);
 
+/**
+ * ocxl_link_add_lpc_mem() - Increment the amount of memory required by an OpenCAPI link
+ *
+ * @link_handle: The OpenCAPI link handle
+ * @offset: The offset of the memory to add
+ * @size: The amount of memory to increment by
+ *
+ * Return 0 on success, negative on overflow
+ */
+int ocxl_link_add_lpc_mem(void *link_handle, u64 offset, u64 size);
+
+/**
+ * ocxl_link_lpc_map() - Map the LPC memory for an OpenCAPI device
+ *
+ * Since LPC memory belongs to a link, the whole LPC memory available
+ * on the link bust be mapped in order to make it accessible to a device.
+ *
+ * @link_handle: The OpenCAPI link handle
+ * @pdev: A device that is on the link
+ */
+u64 ocxl_link_lpc_map(void *link_handle, struct pci_dev *pdev);
+
+/**
+ * ocxl_link_lpc_release() - Release the LPC memory device for an OpenCAPI device
+ *
+ * Offlines LPC memory on an OpenCAPI link for a device. If this is the
+ * last device on the link to release the memory, unmap it from the link.
+ *
+ * @link_handle: The OpenCAPI link handle
+ * @pdev: A device that is on the link
+ */
+void ocxl_link_lpc_release(void *link_handle, struct pci_dev *pdev);
+
 #endif /* _OCXL_INTERNAL_H_ */
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 06/10] ocxl: Add functions to map/unmap LPC memory
  2019-10-25  4:46 [PATCH 00/10] Add support for OpenCAPI SCM devices Alastair D'Silva
                   ` (4 preceding siblings ...)
  2019-10-25  4:47 ` [PATCH 05/10] ocxl: Tally up the LPC memory on a link & allow it to be mapped Alastair D'Silva
@ 2019-10-25  4:47 ` Alastair D'Silva
  2019-11-07 18:05   ` Frederic Barrat
  2019-10-25  4:47 ` [PATCH 07/10] ocxl: Save the device serial number in ocxl_fn Alastair D'Silva
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 45+ messages in thread
From: Alastair D'Silva @ 2019-10-25  4:47 UTC (permalink / raw)
  To: alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Frederic Barrat, Andrew Donnellan, Arnd Bergmann,
	Greg Kroah-Hartman, Dan Williams, Vishal Verma, Dave Jiang,
	Keith Busch, Ira Weiny, Krzysztof Kozlowski, Anton Blanchard,
	Geert Uytterhoeven, Anju T Sudhakar, Mahesh Salgaonkar,
	Vasant Hegde, Cédric Le Goater, Thomas Gleixner,
	Hari Bathini, Greg Kurz, Masahiro Yamada, Andrew Morton,
	David Hildenbrand, Oscar Salvador, Michal Hocko, Pavel Tatashin,
	Wei Yang, Qian Cai, linuxppc-dev, linux-kernel, linux-nvdimm,
	linux-mm

From: Alastair D'Silva <alastair@d-silva.org>

Add functions to map/unmap LPC memory

Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
---
 drivers/misc/ocxl/config.c        |  4 +++
 drivers/misc/ocxl/core.c          | 50 +++++++++++++++++++++++++++++++
 drivers/misc/ocxl/ocxl_internal.h |  3 ++
 include/misc/ocxl.h               | 18 +++++++++++
 4 files changed, 75 insertions(+)

diff --git a/drivers/misc/ocxl/config.c b/drivers/misc/ocxl/config.c
index c8e19bfb5ef9..fb0c3b6f8312 100644
--- a/drivers/misc/ocxl/config.c
+++ b/drivers/misc/ocxl/config.c
@@ -568,6 +568,10 @@ static int read_afu_lpc_memory_info(struct pci_dev *dev,
 		afu->special_purpose_mem_size =
 			total_mem_size - lpc_mem_size;
 	}
+
+	dev_info(&dev->dev, "Probed LPC memory of %#llx bytes and special purpose memory of %#llx bytes\n",
+		afu->lpc_mem_size, afu->special_purpose_mem_size);
+
 	return 0;
 }
 
diff --git a/drivers/misc/ocxl/core.c b/drivers/misc/ocxl/core.c
index 2531c6cf19a0..5554f5ce4b9e 100644
--- a/drivers/misc/ocxl/core.c
+++ b/drivers/misc/ocxl/core.c
@@ -210,6 +210,55 @@ static void unmap_mmio_areas(struct ocxl_afu *afu)
 	release_fn_bar(afu->fn, afu->config.global_mmio_bar);
 }
 
+int ocxl_afu_map_lpc_mem(struct ocxl_afu *afu)
+{
+	struct pci_dev *dev = to_pci_dev(afu->fn->dev.parent);
+
+	if ((afu->config.lpc_mem_size + afu->config.special_purpose_mem_size) == 0)
+		return 0;
+
+	afu->lpc_base_addr = ocxl_link_lpc_map(afu->fn->link, dev);
+	if (afu->lpc_base_addr == 0)
+		return -EINVAL;
+
+	if (afu->config.lpc_mem_size) {
+		afu->lpc_res.start = afu->lpc_base_addr + afu->config.lpc_mem_offset;
+		afu->lpc_res.end = afu->lpc_res.start + afu->config.lpc_mem_size - 1;
+	}
+
+	if (afu->config.special_purpose_mem_size) {
+		afu->special_purpose_res.start = afu->lpc_base_addr +
+						 afu->config.special_purpose_mem_offset;
+		afu->special_purpose_res.end = afu->special_purpose_res.start +
+					       afu->config.special_purpose_mem_size - 1;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(ocxl_afu_map_lpc_mem);
+
+struct resource *ocxl_afu_lpc_mem(struct ocxl_afu *afu)
+{
+	return &afu->lpc_res;
+}
+EXPORT_SYMBOL(ocxl_afu_lpc_mem);
+
+static void unmap_lpc_mem(struct ocxl_afu *afu)
+{
+	struct pci_dev *dev = to_pci_dev(afu->fn->dev.parent);
+
+	if (afu->lpc_res.start || afu->special_purpose_res.start) {
+		void *link = afu->fn->link;
+
+		ocxl_link_lpc_release(link, dev);
+
+		afu->lpc_res.start = 0;
+		afu->lpc_res.end = 0;
+		afu->special_purpose_res.start = 0;
+		afu->special_purpose_res.end = 0;
+	}
+}
+
 static int configure_afu(struct ocxl_afu *afu, u8 afu_idx, struct pci_dev *dev)
 {
 	int rc;
@@ -251,6 +300,7 @@ static int configure_afu(struct ocxl_afu *afu, u8 afu_idx, struct pci_dev *dev)
 
 static void deconfigure_afu(struct ocxl_afu *afu)
 {
+	unmap_lpc_mem(afu);
 	unmap_mmio_areas(afu);
 	reclaim_afu_pasid(afu);
 	reclaim_afu_actag(afu);
diff --git a/drivers/misc/ocxl/ocxl_internal.h b/drivers/misc/ocxl/ocxl_internal.h
index 20b417e00949..9f4b47900e62 100644
--- a/drivers/misc/ocxl/ocxl_internal.h
+++ b/drivers/misc/ocxl/ocxl_internal.h
@@ -52,6 +52,9 @@ struct ocxl_afu {
 	void __iomem *global_mmio_ptr;
 	u64 pp_mmio_start;
 	void *private;
+	u64 lpc_base_addr; /* Covers both LPC & special purpose memory */
+	struct resource lpc_res;
+	struct resource special_purpose_res;
 };
 
 enum ocxl_context_status {
diff --git a/include/misc/ocxl.h b/include/misc/ocxl.h
index 06dd5839e438..6f7c02f0d5e3 100644
--- a/include/misc/ocxl.h
+++ b/include/misc/ocxl.h
@@ -212,6 +212,24 @@ int ocxl_irq_set_handler(struct ocxl_context *ctx, int irq_id,
 
 // AFU Metadata
 
+/**
+ * Map the LPC system & special purpose memory for an AFU
+ *
+ * Do not call this during device discovery, as there may me multiple
+ * devices on a link, and the memory is mapped for the whole link, not
+ * just one device. It should only be called after all devices have
+ * registered their memory on the link.
+ *
+ * afu: The AFU that has the LPC memory to map
+ */
+extern int ocxl_afu_map_lpc_mem(struct ocxl_afu *afu);
+
+/**
+ * Get the physical address range of LPC memory for an AFU
+ * afu: The AFU associated with the LPC memory
+ */
+extern struct resource *ocxl_afu_lpc_mem(struct ocxl_afu *afu);
+
 /**
  * Get a pointer to the config for an AFU
  *
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 07/10] ocxl: Save the device serial number in ocxl_fn
  2019-10-25  4:46 [PATCH 00/10] Add support for OpenCAPI SCM devices Alastair D'Silva
                   ` (5 preceding siblings ...)
  2019-10-25  4:47 ` [PATCH 06/10] ocxl: Add functions to map/unmap LPC memory Alastair D'Silva
@ 2019-10-25  4:47 ` Alastair D'Silva
  2019-11-06  3:10   ` Andrew Donnellan
  2019-11-07 18:18   ` Frederic Barrat
  2019-10-25  4:47 ` [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory Alastair D'Silva
                   ` (2 subsequent siblings)
  9 siblings, 2 replies; 45+ messages in thread
From: Alastair D'Silva @ 2019-10-25  4:47 UTC (permalink / raw)
  To: alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Frederic Barrat, Andrew Donnellan, Arnd Bergmann,
	Greg Kroah-Hartman, Dan Williams, Vishal Verma, Dave Jiang,
	Keith Busch, Ira Weiny, Krzysztof Kozlowski, Anton Blanchard,
	Madhavan Srinivasan, Thomas Gleixner, Mahesh Salgaonkar,
	Vasant Hegde, Hari Bathini, Cédric Le Goater, David Gibson,
	Greg Kurz, Masahiro Yamada, Nicholas Piggin, Andrew Morton,
	David Hildenbrand, Oscar Salvador, Michal Hocko, Pavel Tatashin,
	Wei Yang, Qian Cai, linuxppc-dev, linux-kernel, linux-nvdimm,
	linux-mm

From: Alastair D'Silva <alastair@d-silva.org>

This patch retrieves the serial number of the card and makes it available
to consumers of the ocxl driver via the ocxl_fn struct.

Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
---
 drivers/misc/ocxl/config.c | 46 ++++++++++++++++++++++++++++++++++++++
 include/misc/ocxl.h        |  1 +
 2 files changed, 47 insertions(+)

diff --git a/drivers/misc/ocxl/config.c b/drivers/misc/ocxl/config.c
index fb0c3b6f8312..a9203c309365 100644
--- a/drivers/misc/ocxl/config.c
+++ b/drivers/misc/ocxl/config.c
@@ -71,6 +71,51 @@ static int find_dvsec_afu_ctrl(struct pci_dev *dev, u8 afu_idx)
 	return 0;
 }
 
+/**
+ * Find a related PCI device (function 0)
+ * @device: PCI device to match
+ *
+ * Returns a pointer to the related device, or null if not found
+ */
+static struct pci_dev *get_function_0(struct pci_dev *dev)
+{
+	unsigned int devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 0); // Look for function 0
+
+	return pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus),
+					dev->bus->number, devfn);
+}
+
+static void read_serial(struct pci_dev *dev, struct ocxl_fn_config *fn)
+{
+	u32 low, high;
+	int pos;
+
+	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DSN);
+	if (pos) {
+		pci_read_config_dword(dev, pos + 0x04, &low);
+		pci_read_config_dword(dev, pos + 0x08, &high);
+
+		fn->serial = low | ((u64)high) << 32;
+
+		return;
+	}
+
+	if (PCI_FUNC(dev->devfn) != 0) {
+		struct pci_dev *related = get_function_0(dev);
+
+		if (!related) {
+			fn->serial = 0;
+			return;
+		}
+
+		read_serial(related, fn);
+		pci_dev_put(related);
+		return;
+	}
+
+	fn->serial = 0;
+}
+
 static void read_pasid(struct pci_dev *dev, struct ocxl_fn_config *fn)
 {
 	u16 val;
@@ -208,6 +253,7 @@ int ocxl_config_read_function(struct pci_dev *dev, struct ocxl_fn_config *fn)
 	int rc;
 
 	read_pasid(dev, fn);
+	read_serial(dev, fn);
 
 	rc = read_dvsec_tl(dev, fn);
 	if (rc) {
diff --git a/include/misc/ocxl.h b/include/misc/ocxl.h
index 6f7c02f0d5e3..9843051c3c5b 100644
--- a/include/misc/ocxl.h
+++ b/include/misc/ocxl.h
@@ -46,6 +46,7 @@ struct ocxl_fn_config {
 	int dvsec_afu_info_pos; /* offset of the AFU information DVSEC */
 	s8 max_pasid_log;
 	s8 max_afu_index;
+	u64 serial;
 };
 
 enum ocxl_endian {
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory
  2019-10-25  4:46 [PATCH 00/10] Add support for OpenCAPI SCM devices Alastair D'Silva
                   ` (6 preceding siblings ...)
  2019-10-25  4:47 ` [PATCH 07/10] ocxl: Save the device serial number in ocxl_fn Alastair D'Silva
@ 2019-10-25  4:47 ` Alastair D'Silva
  2019-10-27 22:19   ` kbuild test robot
                     ` (3 more replies)
  2019-10-25  4:47 ` [PATCH 09/10] powerpc: Enable OpenCAPI Storage Class Memory driver on bare metal Alastair D'Silva
  2019-10-25  4:47 ` [PATCH 10/10] ocxl: Conditionally bind SCM devices to the generic OCXL driver Alastair D'Silva
  9 siblings, 4 replies; 45+ messages in thread
From: Alastair D'Silva @ 2019-10-25  4:47 UTC (permalink / raw)
  To: alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Frederic Barrat, Andrew Donnellan, Arnd Bergmann,
	Greg Kroah-Hartman, Dan Williams, Vishal Verma, Dave Jiang,
	Keith Busch, Ira Weiny, Anton Blanchard, Krzysztof Kozlowski,
	David Gibson, Cédric Le Goater, Thomas Gleixner,
	Hari Bathini, Mahesh Salgaonkar, Greg Kurz, Masahiro Yamada,
	Alexey Kardashevskiy, Nicholas Piggin, Andrew Morton,
	David Hildenbrand, Oscar Salvador, Michal Hocko, Pavel Tatashin,
	Wei Yang, Qian Cai, linuxppc-dev, linux-kernel, linux-nvdimm,
	linux-mm

From: Alastair D'Silva <alastair@d-silva.org>

This driver exposes LPC memory on OpenCAPI SCM cards
as an NVDIMM, allowing the existing nvram infrastructure
to be used.

Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
---
 drivers/nvdimm/Kconfig             |   17 +
 drivers/nvdimm/Makefile            |    3 +
 drivers/nvdimm/ocxl-scm.c          | 2210 ++++++++++++++++++++++++++++
 drivers/nvdimm/ocxl-scm_internal.c |  232 +++
 drivers/nvdimm/ocxl-scm_internal.h |  331 +++++
 drivers/nvdimm/ocxl-scm_sysfs.c    |  219 +++
 include/uapi/linux/ocxl-scm.h      |  128 ++
 mm/memory_hotplug.c                |    2 +-
 8 files changed, 3141 insertions(+), 1 deletion(-)
 create mode 100644 drivers/nvdimm/ocxl-scm.c
 create mode 100644 drivers/nvdimm/ocxl-scm_internal.c
 create mode 100644 drivers/nvdimm/ocxl-scm_internal.h
 create mode 100644 drivers/nvdimm/ocxl-scm_sysfs.c
 create mode 100644 include/uapi/linux/ocxl-scm.h

diff --git a/drivers/nvdimm/Kconfig b/drivers/nvdimm/Kconfig
index 36af7af6b7cf..e4f7b6b08efd 100644
--- a/drivers/nvdimm/Kconfig
+++ b/drivers/nvdimm/Kconfig
@@ -130,4 +130,21 @@ config NVDIMM_TEST_BUILD
 	  core devm_memremap_pages() implementation and other
 	  infrastructure.
 
+config OCXL_SCM
+	tristate "OpenCAPI Storage Class Memory"
+	depends on LIBNVDIMM
+	select ZONE_DEVICE
+	select OCXL
+	help
+	  Exposes devices that implement the OpenCAPI Storage Class Memory
+	  specification as persistent memory regions.
+
+	  Select N if unsure.
+
+config OCXL_SCM_DEBUG
+	bool "OpenCAPI Storage Class Memory debugging"
+	depends on OCXL_SCM
+	help
+	  Enables low level IOCTLs for OpenCAPI SCM firmware development
+
 endif
diff --git a/drivers/nvdimm/Makefile b/drivers/nvdimm/Makefile
index 29203f3d3069..43d826397bfc 100644
--- a/drivers/nvdimm/Makefile
+++ b/drivers/nvdimm/Makefile
@@ -6,6 +6,9 @@ obj-$(CONFIG_ND_BLK) += nd_blk.o
 obj-$(CONFIG_X86_PMEM_LEGACY) += nd_e820.o
 obj-$(CONFIG_OF_PMEM) += of_pmem.o
 obj-$(CONFIG_VIRTIO_PMEM) += virtio_pmem.o nd_virtio.o
+obj-$(CONFIG_OCXL_SCM) += ocxlscm.o
+
+ocxlscm-y := ocxl-scm.o ocxl-scm_internal.o ocxl-scm_sysfs.o
 
 nd_pmem-y := pmem.o
 
diff --git a/drivers/nvdimm/ocxl-scm.c b/drivers/nvdimm/ocxl-scm.c
new file mode 100644
index 000000000000..f4e6cc022de8
--- /dev/null
+++ b/drivers/nvdimm/ocxl-scm.c
@@ -0,0 +1,2210 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2019 IBM Corp.
+
+/*
+ * A driver for Storage Class Memory, connected via OpenCAPI
+ */
+
+#include <linux/module.h>
+#include <misc/ocxl.h>
+#include <linux/delay.h>
+#include <linux/ndctl.h>
+#include <linux/eventfd.h>
+#include <linux/fs.h>
+#include <linux/mm_types.h>
+#include <linux/memory_hotplug.h>
+#include "ocxl-scm_internal.h"
+
+
+static const struct pci_device_id scm_pci_tbl[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0625), },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(pci, scm_pci_tbl);
+
+#define SCM_NUM_MINORS 256 // Total to reserve
+#define SCM_USABLE_TIMEOUT 120 // seconds
+
+static dev_t scm_dev;
+static struct class *scm_class;
+static struct mutex minors_idr_lock;
+static struct idr minors_idr;
+
+static const struct attribute_group *scm_pmem_attribute_groups[] = {
+	&nvdimm_bus_attribute_group,
+	NULL,
+};
+
+static const struct attribute_group *scm_pmem_region_attribute_groups[] = {
+	&nd_region_attribute_group,
+	&nd_device_attribute_group,
+	&nd_mapping_attribute_group,
+	&nd_numa_attribute_group,
+	NULL,
+};
+
+/**
+ * scm_ndctl_config_write() - Handle a ND_CMD_SET_CONFIG_DATA command from ndctl
+ * @scm_data: the SCM metadata
+ * @command: the incoming data to write
+ * Return: 0 on success, negative on failure
+ */
+static int scm_ndctl_config_write(struct scm_data *scm_data,
+				  struct nd_cmd_set_config_hdr *command)
+{
+	if (command->in_offset + command->in_length > SCM_LABEL_AREA_SIZE)
+		return -EINVAL;
+
+	memcpy_flushcache(scm_data->metadata_addr + command->in_offset, command->in_buf,
+			  command->in_length);
+
+	return 0;
+}
+
+/**
+ * scm_ndctl_config_read() - Handle a ND_CMD_GET_CONFIG_DATA command from ndctl
+ * @scm_data: the SCM metadata
+ * @command: the read request
+ * Return: 0 on success, negative on failure
+ */
+static int scm_ndctl_config_read(struct scm_data *scm_data,
+				 struct nd_cmd_get_config_data_hdr *command)
+{
+	if (command->in_offset + command->in_length > SCM_LABEL_AREA_SIZE)
+		return -EINVAL;
+
+	memcpy(command->out_buf, scm_data->metadata_addr + command->in_offset,
+	       command->in_length);
+
+	return 0;
+}
+
+/**
+ * scm_ndctl_config_size() - Handle a ND_CMD_GET_CONFIG_SIZE command from ndctl
+ * @scm_data: the SCM metadata
+ * @command: the read request
+ * Return: 0 on success, negative on failure
+ */
+static int scm_ndctl_config_size(struct nd_cmd_get_config_size *command)
+{
+	command->status = 0;
+	command->config_size = SCM_LABEL_AREA_SIZE;
+	command->max_xfer = PAGE_SIZE;
+
+	return 0;
+}
+
+static int read_smart_attrib(struct scm_data *scm_data, u16 offset,
+			     struct scm_smart_attribs *attribs)
+{
+	u64 val;
+	int rc;
+	struct scm_smart_attrib *attrib;
+	u8 attrib_id;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, offset, OCXL_LITTLE_ENDIAN,
+				     &val);
+	if (rc)
+		return rc;
+
+	attrib_id = (val >> 56) & 0xff;
+	switch (attrib_id) {
+	case SCM_SMART_ATTR_POWER_ON_HOURS:
+		attrib = &attribs->power_on_hours;
+		break;
+
+	case SCM_SMART_ATTR_TEMPERATURE:
+		attrib = &attribs->temperature;
+		break;
+
+	case SCM_SMART_ATTR_LIFE_REMAINING:
+		attrib = &attribs->life_remaining;
+		break;
+
+	default:
+		dev_err(&scm_data->dev, "Unknown smart attrib '%d'", attrib_id);
+		return -EFAULT;
+	}
+
+	attrib->id = attrib_id;
+	attrib->attribute_flags = (val >> 40) & 0xffff;
+	attrib->current_val = (val >> 32) & 0xff;
+	attrib->threshold_val = (val >> 24) & 0xff;
+	attrib->worst_val = (val >> 16) & 0xff;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, offset + 0x08,
+				     OCXL_LITTLE_ENDIAN, &val);
+	if (rc)
+		return rc;
+
+	attrib->raw_val = val;
+
+	return 0;
+}
+
+static int scm_smart_offset_0x00(struct scm_data *scm_data, u32 *length)
+{
+	int rc;
+	u64 val;
+
+	u16 data_identifier;
+	u32 data_length;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset,
+				     OCXL_LITTLE_ENDIAN, &val);
+	if (rc)
+		return rc;
+
+	data_identifier = val >> 48;
+	data_length = val & 0xFFFFFFFF;
+
+	if (data_identifier != 0x534D) {
+		dev_err(&scm_data->dev,
+			"Bad data identifier for smart data, expected 'SM', got '%-.*s'\n",
+			2, (char *)&data_identifier);
+		return -EFAULT;
+	}
+
+	*length = data_length;
+	return 0;
+}
+
+static int scm_smart_update(struct scm_data *scm_data)
+{
+	u32 length, i;
+	int rc;
+
+	mutex_lock(&scm_data->admin_command.lock);
+
+	rc = scm_admin_command_request(scm_data, ADMIN_COMMAND_SMART);
+	if (rc)
+		goto out;
+
+	rc = scm_admin_command_execute(scm_data);
+	if (rc)
+		goto out;
+
+	rc = scm_admin_command_complete_timeout(scm_data, ADMIN_COMMAND_SMART);
+	if (rc < 0) {
+		dev_err(&scm_data->dev, "SMART timeout\n");
+		goto out;
+	}
+
+	rc = scm_admin_response(scm_data);
+	if (rc < 0)
+		goto out;
+	if (rc != STATUS_SUCCESS) {
+		scm_warn_status(scm_data, "Unexpected status from SMART", rc);
+		goto out;
+	}
+
+	rc = scm_smart_offset_0x00(scm_data, &length);
+	if (rc)
+		goto out;
+
+	length /= 0x10; // Length now contains the number of attributes
+
+	for (i = 0; i < length; i++)
+		read_smart_attrib(scm_data,
+				  scm_data->admin_command.data_offset + 0x08 + i * 0x10,
+				  &scm_data->smart);
+
+	rc = scm_admin_response_handled(scm_data);
+	if (rc)
+		goto out;
+
+	rc = 0;
+	goto out;
+
+out:
+	mutex_unlock(&scm_data->admin_command.lock);
+	return rc;
+}
+
+static int scm_ndctl_smart(struct scm_data *scm_data, void *buf,
+			   unsigned int buf_len)
+{
+	int rc;
+
+	if (buf_len != sizeof(scm_data->smart))
+		return -EINVAL;
+
+	rc = scm_smart_update(scm_data);
+	if (rc)
+		return rc;
+
+	memcpy(buf, &scm_data->smart, buf_len);
+
+	return 0;
+}
+
+
+static int scm_ndctl(struct nvdimm_bus_descriptor *nd_desc,
+		     struct nvdimm *nvdimm,
+		     unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc)
+{
+	struct scm_data *scm_data = container_of(nd_desc, struct scm_data, bus_desc);
+
+	switch (cmd) {
+	case ND_CMD_SMART:
+		*cmd_rc = scm_ndctl_smart(scm_data, buf, buf_len);
+		return 0;
+
+	case ND_CMD_GET_CONFIG_SIZE:
+		*cmd_rc = scm_ndctl_config_size(buf);
+		return 0;
+
+	case ND_CMD_GET_CONFIG_DATA:
+		*cmd_rc = scm_ndctl_config_read(scm_data, buf);
+		return 0;
+
+	case ND_CMD_SET_CONFIG_DATA:
+		*cmd_rc = scm_ndctl_config_write(scm_data, buf);
+		return 0;
+
+	default:
+		return -ENOTTY;
+	}
+}
+
+static ssize_t serial_show(struct device *dev,
+			   struct device_attribute *attr, char *buf)
+{
+	struct nvdimm *nvdimm = to_nvdimm(dev);
+	struct scm_data *scm_data = nvdimm_provider_data(nvdimm);
+	const struct ocxl_fn_config *config = ocxl_function_config(scm_data->ocxl_fn);
+
+	return sprintf(buf, "0x%llx\n", config->serial);
+}
+static DEVICE_ATTR_RO(serial);
+
+static struct attribute *scm_dimm_attributes[] = {
+	&dev_attr_serial.attr,
+	NULL,
+};
+
+static umode_t scm_dimm_attr_visible(struct kobject *kobj,
+				     struct attribute *a, int n)
+{
+	return a->mode;
+}
+
+static const struct attribute_group scm_dimm_attribute_group = {
+	.name = "scm",
+	.attrs = scm_dimm_attributes,
+	.is_visible = scm_dimm_attr_visible,
+};
+
+static const struct attribute_group *scm_dimm_attribute_groups[] = {
+	&nvdimm_attribute_group,
+	&nd_device_attribute_group,
+	&scm_dimm_attribute_group,
+	NULL,
+};
+
+/**
+ * scm_reserve_metadata() - Reserve space for nvdimm metadata
+ * @scm_data: The SCM device data
+ * @lpc_mem: The resource representing the LPC memory of the SCM device
+ */
+static int scm_reserve_metadata(struct scm_data *scm_data,
+				struct resource *lpc_mem)
+{
+	scm_data->metadata_addr = devm_memremap(&scm_data->dev, lpc_mem->start,
+						SCM_LABEL_AREA_SIZE, MEMREMAP_WB);
+	if (IS_ERR(scm_data->metadata_addr))
+		return PTR_ERR(scm_data->metadata_addr);
+
+	return 0;
+}
+
+/**
+ * scm_overwrite() - Overwrite all data on the card
+ * @scm_data: The SCM device data
+ * Return: 0 on success
+ */
+int scm_overwrite(struct scm_data *scm_data)
+{
+	int rc;
+
+	mutex_lock(&scm_data->ns_command.lock);
+
+	rc = scm_ns_command_request(scm_data, NS_COMMAND_SECURE_ERASE);
+	if (rc)
+		goto out;
+
+	rc = scm_ns_command_execute(scm_data);
+	if (rc)
+		goto out;
+
+	scm_data->overwrite_state = SCM_OVERWRITE_BUSY;
+
+	return 0;
+
+out:
+	mutex_unlock(&scm_data->ns_command.lock);
+	return rc;
+}
+
+/**
+ * scm_secop_overwrite() - Overwrite all data on the card
+ * @nvdimm: The nvdimm representation of the SCM device to start the overwrite on
+ * @key_data: Unused (no security key implementation)
+ * Return: 0 on success
+ */
+static int scm_secop_overwrite(struct nvdimm *nvdimm,
+			       const struct nvdimm_key_data *key_data)
+{
+	struct scm_data *scm_data = nvdimm_provider_data(nvdimm);
+
+	return scm_overwrite(scm_data);
+}
+
+/**
+ * scm_secop_query_overwrite() - Get the current overwrite state
+ * @nvdimm: The nvdimm representation of the SCM device to start the overwrite on
+ * Return: 0 if successful or idle, -EBUSY if busy, -EFAULT if failed
+ */
+static int scm_secop_query_overwrite(struct nvdimm *nvdimm)
+{
+	struct scm_data *scm_data = nvdimm_provider_data(nvdimm);
+
+	if (scm_data->overwrite_state == SCM_OVERWRITE_BUSY)
+		return -EBUSY;
+
+	if (scm_data->overwrite_state == SCM_OVERWRITE_FAILED)
+		return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * scm_secop_get_flags() - return the security flags for the SCM device
+ */
+static unsigned long scm_secop_get_flags(struct nvdimm *nvdimm,
+		enum nvdimm_passphrase_type ptype)
+{
+	struct scm_data *scm_data = nvdimm_provider_data(nvdimm);
+
+	if (scm_data->overwrite_state == SCM_OVERWRITE_BUSY)
+		return BIT(NVDIMM_SECURITY_OVERWRITE);
+
+	return BIT(NVDIMM_SECURITY_DISABLED);
+}
+
+static const struct nvdimm_security_ops sec_ops  = {
+	.get_flags = scm_secop_get_flags,
+	.overwrite = scm_secop_overwrite,
+	.query_overwrite = scm_secop_query_overwrite,
+};
+
+/**
+ * scm_register_lpc_mem() - Discover persistent memory on a device and register it with the NVDIMM subsystem
+ * @scm_data: The SCM device data
+ * Return: 0 on success
+ */
+static int scm_register_lpc_mem(struct scm_data *scm_data)
+{
+	struct nd_region_desc region_desc;
+	struct nd_mapping_desc nd_mapping_desc;
+	struct resource *lpc_mem;
+	const struct ocxl_afu_config *config;
+	const struct ocxl_fn_config *fn_config;
+	int rc;
+	unsigned long nvdimm_cmd_mask = 0;
+	unsigned long nvdimm_flags = 0;
+	int target_node;
+	char serial[16+1];
+
+	// Set up the reserved metadata area
+	rc = ocxl_afu_map_lpc_mem(scm_data->ocxl_afu);
+	if (rc < 0)
+		return rc;
+
+	lpc_mem = ocxl_afu_lpc_mem(scm_data->ocxl_afu);
+	if (lpc_mem == NULL)
+		return -EINVAL;
+
+	config = ocxl_afu_config(scm_data->ocxl_afu);
+	fn_config = ocxl_function_config(scm_data->ocxl_fn);
+
+	rc = scm_reserve_metadata(scm_data, lpc_mem);
+	if (rc)
+		return rc;
+
+	scm_data->bus_desc.attr_groups = scm_pmem_attribute_groups;
+	scm_data->bus_desc.provider_name = "scm";
+	scm_data->bus_desc.ndctl = scm_ndctl;
+	scm_data->bus_desc.module = THIS_MODULE;
+
+	scm_data->nvdimm_bus = nvdimm_bus_register(&scm_data->dev,
+			       &scm_data->bus_desc);
+	if (!scm_data->nvdimm_bus)
+		return -EINVAL;
+
+	scm_data->scm_res.start = (u64)lpc_mem->start + SCM_LABEL_AREA_SIZE;
+	scm_data->scm_res.end = (u64)lpc_mem->start + config->lpc_mem_size - 1;
+	scm_data->scm_res.name = "SCM persistent memory";
+
+	set_bit(ND_CMD_GET_CONFIG_SIZE, &nvdimm_cmd_mask);
+	set_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm_cmd_mask);
+	set_bit(ND_CMD_SET_CONFIG_DATA, &nvdimm_cmd_mask);
+	set_bit(ND_CMD_SMART, &nvdimm_cmd_mask);
+
+	set_bit(NDD_ALIASING, &nvdimm_flags);
+
+	snprintf(serial, sizeof(serial), "%llx", fn_config->serial);
+	nd_mapping_desc.nvdimm = __nvdimm_create(scm_data->nvdimm_bus, scm_data,
+				 scm_dimm_attribute_groups,
+				 nvdimm_flags, nvdimm_cmd_mask,
+				 0, NULL, serial, &sec_ops);
+	if (!nd_mapping_desc.nvdimm)
+		return -ENOMEM;
+
+	if (nvdimm_bus_check_dimm_count(scm_data->nvdimm_bus, 1))
+		return -EINVAL;
+
+	nd_mapping_desc.start = scm_data->scm_res.start;
+	nd_mapping_desc.size = resource_size(&scm_data->scm_res);
+	nd_mapping_desc.position = 0;
+
+	scm_data->nd_set.cookie1 = fn_config->serial + 1; // allow for empty serial
+	scm_data->nd_set.cookie2 = fn_config->serial + 1;
+
+	target_node = of_node_to_nid(scm_data->pdev->dev.of_node);
+
+	memset(&region_desc, 0, sizeof(region_desc));
+	region_desc.res = &scm_data->scm_res;
+	region_desc.attr_groups = scm_pmem_region_attribute_groups;
+	region_desc.numa_node = NUMA_NO_NODE;
+	region_desc.target_node = target_node;
+	region_desc.num_mappings = 1;
+	region_desc.mapping = &nd_mapping_desc;
+	region_desc.nd_set = &scm_data->nd_set;
+
+	set_bit(ND_REGION_PAGEMAP, &region_desc.flags);
+	/*
+	 * NB: libnvdimm copies the data from ndr_desc into it's own
+	 * structures so passing a stack pointer is fine.
+	 */
+	scm_data->nd_region = nvdimm_pmem_region_create(scm_data->nvdimm_bus,
+			      &region_desc);
+	if (!scm_data->nd_region)
+		return -EINVAL;
+
+	dev_info(&scm_data->dev,
+		 "Onlining %lluMB of persistent memory\n",
+		 nd_mapping_desc.size / SZ_1M);
+
+	return 0;
+}
+
+/**
+ * scm_is_memory_available() - Does the controller have memory available?
+ * @scm_data: a pointer to the SCM device data
+ * Return: true if the controller has memory available
+ */
+static bool scm_is_memory_available(const struct scm_data *scm_data)
+{
+	u64 val = 0;
+	int rc = scm_chi(scm_data, &val);
+
+	WARN_ON(rc < 0);
+
+	return (val & GLOBAL_MMIO_CHI_MA) != 0;
+}
+
+/**
+ * scm_extract_command_metadata() - Extract command data from MMIO & save it for further use
+ * @scm_data: a pointer to the SCM device data
+ * @offset: The base address of the command data structures (address of CREQO)
+ * @command_metadata: A pointer to the command metadata to populate
+ * Return: 0 on success, negative on failure
+ */
+static int scm_extract_command_metadata(struct scm_data *scm_data, u32 offset,
+					struct command_metadata *command_metadata)
+{
+	int rc;
+	u64 tmp;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, offset, OCXL_LITTLE_ENDIAN,
+				     &tmp);
+	if (rc)
+		return rc;
+
+	command_metadata->request_offset = tmp >> 32;
+	command_metadata->response_offset = tmp & 0xFFFFFFFF;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, offset + 8, OCXL_LITTLE_ENDIAN,
+				     &tmp);
+	if (rc)
+		return rc;
+
+	command_metadata->data_offset = tmp >> 32;
+	command_metadata->data_size = tmp & 0xFFFFFFFF;
+
+	command_metadata->id = 0;
+
+	return 0;
+}
+
+/**
+ * scm_setup_command_metadata() - Set up the command metadata
+ * @scm_data: a pointer to the SCM device data
+ */
+static int scm_setup_command_metadata(struct scm_data *scm_data)
+{
+	int rc;
+
+	rc = scm_extract_command_metadata(scm_data, GLOBAL_MMIO_ACMA_CREQO,
+					  &scm_data->admin_command);
+	if (rc)
+		return rc;
+
+	rc = scm_extract_command_metadata(scm_data, GLOBAL_MMIO_NSCMA_CREQO,
+					  &scm_data->ns_command);
+	if (rc)
+		return rc;
+
+	return 0;
+}
+
+/**
+ * scm_heartbeat() - Issue a heartbeat command to the controller
+ * @scm_data: a pointer to the SCM device data
+ * Return: 0 if the controller responded correctly, negative on error
+ */
+static int scm_heartbeat(struct scm_data *scm_data)
+{
+	int rc;
+
+	mutex_lock(&scm_data->admin_command.lock);
+
+	rc = scm_admin_command_request(scm_data, ADMIN_COMMAND_HEARTBEAT);
+	if (rc)
+		goto out;
+
+	rc = scm_admin_command_execute(scm_data);
+	if (rc)
+		goto out;
+
+	rc = scm_admin_command_complete_timeout(scm_data, ADMIN_COMMAND_HEARTBEAT);
+	if (rc < 0) {
+		dev_err(&scm_data->dev, "Heartbeat timeout\n");
+		goto out;
+	}
+
+	rc = scm_admin_response(scm_data);
+	if (rc < 0)
+		goto out;
+	if (rc != STATUS_SUCCESS)
+		scm_warn_status(scm_data, "Unexpected status from heartbeat", rc);
+
+	rc = scm_admin_response_handled(scm_data);
+
+	goto out;
+
+out:
+	mutex_unlock(&scm_data->admin_command.lock);
+	return rc;
+}
+
+/**
+ * scm_is_usable() - Is a controller usable?
+ * @scm_data: a pointer to the SCM device data
+ * Return: true if the controller is usable
+ */
+static bool scm_is_usable(const struct scm_data *scm_data)
+{
+	if (!scm_controller_is_ready(scm_data)) {
+		dev_err(&scm_data->dev, "SCM controller is not ready.\n");
+		return false;
+	}
+
+	if (!scm_is_memory_available(scm_data)) {
+		dev_err(&scm_data->dev,
+			"SCM controller does not have memory available.\n");
+		return false;
+	}
+
+	return true;
+}
+
+/**
+ * allocate_scm_minor() - Allocate a minor number to use for an SCM device
+ * @scm_data: The SCM device to associate the minor with
+ * Return: the allocated minor number
+ */
+static int allocate_scm_minor(struct scm_data *scm_data)
+{
+	int minor;
+
+	mutex_lock(&minors_idr_lock);
+	minor = idr_alloc(&minors_idr, scm_data, 0, SCM_NUM_MINORS, GFP_KERNEL);
+	mutex_unlock(&minors_idr_lock);
+	return minor;
+}
+
+static void free_scm_minor(struct scm_data *scm_data)
+{
+	mutex_lock(&minors_idr_lock);
+	idr_remove(&minors_idr, MINOR(scm_data->dev.devt));
+	mutex_unlock(&minors_idr_lock);
+}
+
+/**
+ * free_scm() - Free all members of an SCM struct
+ * @scm_data: the SCM metadata to clear
+ */
+static void free_scm(struct scm_data *scm_data)
+{
+	// Disable doorbells
+	(void)ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_CHIEC,
+				     OCXL_LITTLE_ENDIAN,
+				     GLOBAL_MMIO_CHI_ALL);
+
+	free_scm_minor(scm_data);
+
+	if (scm_data->irq_addr[1])
+		iounmap(scm_data->irq_addr[1]);
+
+	if (scm_data->irq_addr[0])
+		iounmap(scm_data->irq_addr[0]);
+
+	if (scm_data->cdev.owner)
+		cdev_del(&scm_data->cdev);
+
+	if (scm_data->metadata_addr)
+		devm_memunmap(&scm_data->dev, scm_data->metadata_addr);
+
+	if (scm_data->ocxl_context)
+		ocxl_context_free(scm_data->ocxl_context);
+
+	if (scm_data->ocxl_afu)
+		ocxl_afu_put(scm_data->ocxl_afu);
+
+	if (scm_data->ocxl_fn)
+		ocxl_function_close(scm_data->ocxl_fn);
+
+	kfree(scm_data);
+}
+
+/**
+ * free_scm_dev - Free an SCM device
+ * @dev: The device struct
+ */
+static void free_scm_dev(struct device *dev)
+{
+	struct scm_data *scm_data = container_of(dev, struct scm_data, dev);
+
+	free_scm(scm_data);
+}
+
+/**
+ * scm_register - Register an SCM device with the kernel
+ * @scm_data: the SCM metadata
+ * Return: 0 on success, negative on failure
+ */
+static int scm_register(struct scm_data *scm_data)
+{
+	int rc;
+	int minor = allocate_scm_minor(scm_data);
+
+	if (minor < 0)
+		return minor;
+
+	scm_data->dev.release = free_scm_dev;
+	rc = dev_set_name(&scm_data->dev, "scm%d", minor);
+	if (rc < 0)
+		return rc;
+
+	scm_data->dev.devt = MKDEV(MAJOR(scm_dev), minor);
+	scm_data->dev.class = scm_class;
+	scm_data->dev.parent = &scm_data->pdev->dev;
+
+	rc = device_register(&scm_data->dev);
+	return rc;
+}
+
+static void scm_put(struct scm_data *scm_data)
+{
+	put_device(&scm_data->dev);
+}
+
+struct scm_data *scm_get(struct scm_data *scm_data)
+{
+	return (get_device(&scm_data->dev) == NULL) ? NULL : scm_data;
+}
+
+static struct scm_data *find_and_get_scm(dev_t devno)
+{
+	struct scm_data *scm_data;
+	int minor = MINOR(devno);
+	/*
+	 * We don't declare an RCU critical section here, as our AFU
+	 * is protected by a reference counter on the device. By the time the
+	 * minor number of a device is removed from the idr, the ref count of
+	 * the device is already at 0, so no user API will access that AFU and
+	 * this function can't return it.
+	 */
+	scm_data = idr_find(&minors_idr, minor);
+	if (scm_data)
+		scm_get(scm_data);
+	return scm_data;
+}
+
+static int scm_file_open(struct inode *inode, struct file *file)
+{
+	struct scm_data *scm_data;
+
+	scm_data = find_and_get_scm(inode->i_rdev);
+	if (!scm_data)
+		return -ENODEV;
+
+	file->private_data = scm_data;
+	return 0;
+}
+
+static int scm_file_release(struct inode *inode, struct file *file)
+{
+	struct scm_data *scm_data = file->private_data;
+
+	if (scm_data->ev_ctx) {
+		eventfd_ctx_put(scm_data->ev_ctx);
+		scm_data->ev_ctx = NULL;
+	}
+
+	scm_put(scm_data);
+	return 0;
+}
+
+static int scm_ioctl_buffer_info(struct scm_data *scm_data,
+				 struct scm_ioctl_buffer_info __user *uarg)
+{
+	struct scm_ioctl_buffer_info args;
+
+	args.admin_command_buffer_size = scm_data->admin_command.data_size;
+	args.near_storage_buffer_size = scm_data->ns_command.data_size;
+
+	if (copy_to_user(uarg, &args, sizeof(args)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int scm_error_log_offset_0x00(struct scm_data *scm_data, u16 *length)
+{
+	int rc;
+	u64 val;
+
+	u16 data_identifier;
+	u32 data_length;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset,
+				     OCXL_LITTLE_ENDIAN, &val);
+	if (rc)
+		return rc;
+
+	data_identifier = val >> 48;
+	data_length = val & 0xFFFF;
+
+	if (data_identifier != 0x454C) {
+		dev_err(&scm_data->dev,
+			"Bad data identifier for error log data, expected 'EL', got '%2s' (%#x), data_length=%u\n",
+			(char *)&data_identifier,
+			(unsigned int)data_identifier, data_length);
+		return -EFAULT;
+	}
+
+	*length = data_length;
+	return 0;
+}
+
+static int scm_error_log_offset_0x08(struct scm_data *scm_data,
+				     u32 *log_identifier, u32 *program_ref_code)
+{
+	int rc;
+	u64 val;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset + 0x08,
+				     OCXL_LITTLE_ENDIAN, &val);
+	if (rc)
+		return rc;
+
+	*log_identifier = val >> 32;
+	*program_ref_code = val & 0xFFFFFFFF;
+
+	return 0;
+}
+
+static int scm_read_error_log(struct scm_data *scm_data,
+			      struct scm_ioctl_error_log *log, bool buf_is_user)
+{
+	u64 val;
+	u16 user_buf_length;
+	u16 buf_length;
+	u16 i;
+	int rc;
+
+	if (log->buf_size % 8)
+		return -EINVAL;
+
+	rc = scm_chi(scm_data, &val);
+	if (rc)
+		goto out;
+
+	if (!(val & GLOBAL_MMIO_CHI_ELA))
+		return -EAGAIN;
+
+	user_buf_length = log->buf_size;
+
+	mutex_lock(&scm_data->admin_command.lock);
+
+	rc = scm_admin_command_request(scm_data, ADMIN_COMMAND_ERRLOG);
+	if (rc)
+		goto out;
+
+	rc = scm_admin_command_execute(scm_data);
+	if (rc)
+		goto out;
+
+	rc = scm_admin_command_complete_timeout(scm_data, ADMIN_COMMAND_ERRLOG);
+	if (rc < 0) {
+		dev_warn(&scm_data->dev, "Read error log timed out\n");
+		goto out;
+	}
+
+	rc = scm_admin_response(scm_data);
+	if (rc < 0)
+		goto out;
+	if (rc != STATUS_SUCCESS) {
+		scm_warn_status(scm_data, "Unexpected status from retrieve error log", rc);
+		goto out;
+	}
+
+
+	rc = scm_error_log_offset_0x00(scm_data, &log->buf_size);
+	if (rc)
+		goto out;
+	// log->buf_size now contains the scm buffer size, not the user size
+
+	rc = scm_error_log_offset_0x08(scm_data, &log->log_identifier,
+				       &log->program_reference_code);
+	if (rc)
+		goto out;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset + 0x10,
+				     OCXL_LITTLE_ENDIAN, &val);
+	if (rc)
+		goto out;
+
+	log->error_log_type = val >> 56;
+	log->action_flags = (log->error_log_type == SCM_ERROR_LOG_TYPE_GENERAL) ?
+			    (val >> 32) & 0xFFFFFF : 0;
+	log->power_on_seconds = val & 0xFFFFFFFF;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset + 0x18,
+				     OCXL_LITTLE_ENDIAN, &log->timestamp);
+	if (rc)
+		goto out;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset + 0x20,
+				     OCXL_HOST_ENDIAN, &log->wwid[0]);
+	if (rc)
+		goto out;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset + 0x28,
+				     OCXL_HOST_ENDIAN, &log->wwid[1]);
+	if (rc)
+		goto out;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset + 0x30,
+				     OCXL_HOST_ENDIAN, (u64 *)log->fw_revision);
+	if (rc)
+		goto out;
+	log->fw_revision[8] = '\0';
+
+	buf_length = (user_buf_length < log->buf_size) ?
+		     user_buf_length : log->buf_size;
+	for (i = 0; i < buf_length + 0x48; i += 8) {
+		u64 val;
+
+		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+					     scm_data->admin_command.data_offset + i,
+					     OCXL_HOST_ENDIAN, &val);
+		if (rc)
+			goto out;
+
+		if (buf_is_user) {
+			if (copy_to_user(&log->buf[i], &val, sizeof(u64))) {
+				rc = -EFAULT;
+				goto out;
+			}
+		} else
+			log->buf[i] = val;
+	}
+
+	rc = scm_admin_response_handled(scm_data);
+	if (rc)
+		goto out;
+
+out:
+	mutex_unlock(&scm_data->admin_command.lock);
+	return rc;
+
+}
+
+static int scm_ioctl_error_log(struct scm_data *scm_data,
+			       struct scm_ioctl_error_log __user *uarg)
+{
+	struct scm_ioctl_error_log args;
+	int rc;
+
+	if (copy_from_user(&args, uarg, sizeof(args)))
+		return -EFAULT;
+
+	rc = scm_read_error_log(scm_data, &args, true);
+	if (rc)
+		return rc;
+
+	if (copy_to_user(uarg, &args, sizeof(args)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int scm_ioctl_controller_dump_data(struct scm_data *scm_data,
+	struct scm_ioctl_controller_dump_data __user *uarg)
+{
+	struct scm_ioctl_controller_dump_data args;
+	u16 i;
+	u64 val;
+	int rc;
+
+	if (copy_from_user(&args, uarg, sizeof(args)))
+		return -EFAULT;
+
+	if (args.buf_size % 8)
+		return -EINVAL;
+
+	if (args.buf_size > scm_data->admin_command.data_size)
+		return -EINVAL;
+
+	mutex_lock(&scm_data->admin_command.lock);
+
+	rc = scm_admin_command_request(scm_data, ADMIN_COMMAND_CONTROLLER_DUMP);
+	if (rc)
+		goto out;
+
+	val = ((u64)args.offset) << 32;
+	val |= args.buf_size;
+	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
+				      scm_data->admin_command.request_offset + 0x08,
+				      OCXL_LITTLE_ENDIAN, val);
+	if (rc)
+		goto out;
+
+	rc = scm_admin_command_execute(scm_data);
+	if (rc)
+		goto out;
+
+	rc = scm_admin_command_complete_timeout(scm_data,
+						ADMIN_COMMAND_CONTROLLER_DUMP);
+	if (rc < 0) {
+		dev_warn(&scm_data->dev, "Controller dump timed out\n");
+		goto out;
+	}
+
+	rc = scm_admin_response(scm_data);
+	if (rc < 0)
+		goto out;
+	if (rc != STATUS_SUCCESS) {
+		scm_warn_status(scm_data,
+				"Unexpected status from retrieve error log",
+				rc);
+		goto out;
+	}
+
+	for (i = 0; i < args.buf_size; i += 8) {
+		u64 val;
+
+		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+					     scm_data->admin_command.data_offset + i,
+					     OCXL_HOST_ENDIAN, &val);
+		if (rc)
+			goto out;
+
+		if (copy_to_user(&args.buf[i], &val, sizeof(u64))) {
+			rc = -EFAULT;
+			goto out;
+		}
+	}
+
+	if (copy_to_user(uarg, &args, sizeof(args))) {
+		rc = -EFAULT;
+		goto out;
+	}
+
+	rc = scm_admin_response_handled(scm_data);
+	if (rc)
+		goto out;
+
+out:
+	mutex_unlock(&scm_data->admin_command.lock);
+	return rc;
+}
+
+int scm_request_controller_dump(struct scm_data *scm_data)
+{
+	int rc;
+	u64 busy = 1;
+
+	rc = ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_CHIC,
+				    OCXL_LITTLE_ENDIAN,
+				    GLOBAL_MMIO_CHI_CDA);
+
+
+	rc = ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_HCI,
+				    OCXL_LITTLE_ENDIAN,
+				    GLOBAL_MMIO_HCI_CONTROLLER_DUMP);
+	if (rc)
+		return rc;
+
+	while (busy) {
+		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+					     GLOBAL_MMIO_HCI,
+					     OCXL_LITTLE_ENDIAN, &busy);
+		if (rc)
+			return rc;
+
+		busy &= GLOBAL_MMIO_HCI_CONTROLLER_DUMP;
+		cond_resched();
+	}
+
+	return 0;
+}
+
+static int scm_ioctl_controller_dump_complete(struct scm_data *scm_data)
+{
+	int rc;
+
+	rc = ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_HCI,
+				    OCXL_LITTLE_ENDIAN,
+				    GLOBAL_MMIO_HCI_CONTROLLER_DUMP_COLLECTED);
+
+	if (rc)
+		return -EFAULT;
+
+	return 0;
+}
+
+static int scm_controller_stats_offset_0x00(struct scm_data *scm_data,
+	u32 *length)
+{
+	int rc;
+	u64 val;
+
+	u16 data_identifier;
+	u32 data_length;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset,
+				     OCXL_LITTLE_ENDIAN, &val);
+	if (rc)
+		return rc;
+
+	data_identifier = val >> 48;
+	data_length = val & 0xFFFFFFFF;
+
+	if (data_identifier != 0x4353) {
+		dev_err(&scm_data->dev,
+			"Bad data identifier for controller stats, expected 'CS', got '%-.*s'\n",
+			2, (char *)&data_identifier);
+		return -EFAULT;
+	}
+
+	*length = data_length;
+	return 0;
+}
+
+static int scm_ioctl_controller_stats(struct scm_data *scm_data,
+				      struct scm_ioctl_controller_stats __user *uarg)
+{
+	struct scm_ioctl_controller_stats args;
+	u32 length;
+	int rc;
+	u64 val;
+
+	memset(&args, '\0', sizeof(args));
+
+	mutex_lock(&scm_data->admin_command.lock);
+
+	rc = scm_admin_command_request(scm_data, ADMIN_COMMAND_CONTROLLER_STATS);
+	if (rc)
+		goto out;
+
+	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
+				      scm_data->admin_command.request_offset + 0x08,
+				      OCXL_LITTLE_ENDIAN, 0);
+	if (rc)
+		goto out;
+
+	rc = scm_admin_command_execute(scm_data);
+	if (rc)
+		goto out;
+
+
+	rc = scm_admin_command_complete_timeout(scm_data,
+						ADMIN_COMMAND_CONTROLLER_STATS);
+	if (rc < 0) {
+		dev_warn(&scm_data->dev, "Controller stats timed out\n");
+		goto out;
+	}
+
+	rc = scm_admin_response(scm_data);
+	if (rc < 0)
+		goto out;
+	if (rc != STATUS_SUCCESS) {
+		scm_warn_status(scm_data,
+				"Unexpected status from controller stats", rc);
+		goto out;
+	}
+
+	rc = scm_controller_stats_offset_0x00(scm_data, &length);
+	if (rc)
+		goto out;
+
+	if (length != 0x140)
+		scm_warn_status(scm_data,
+				"Unexpected length for controller stats data, expected 0x140, got 0x%x",
+				length);
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset + 0x08 + 0x08,
+				     OCXL_LITTLE_ENDIAN, &val);
+	if (rc)
+		goto out;
+
+	args.reset_count = val >> 32;
+	args.reset_uptime = val & 0xFFFFFFFF;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset + 0x08 + 0x10,
+				     OCXL_LITTLE_ENDIAN, &val);
+	if (rc)
+		goto out;
+
+	args.power_on_uptime = val >> 32;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x08,
+				     OCXL_LITTLE_ENDIAN, &args.host_load_count);
+	if (rc)
+		goto out;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x10,
+				     OCXL_LITTLE_ENDIAN, &args.host_store_count);
+	if (rc)
+		goto out;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x18,
+				     OCXL_LITTLE_ENDIAN, &args.media_read_count);
+	if (rc)
+		goto out;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x20,
+				     OCXL_LITTLE_ENDIAN, &args.media_write_count);
+	if (rc)
+		goto out;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x28,
+				     OCXL_LITTLE_ENDIAN, &args.cache_hit_count);
+	if (rc)
+		goto out;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x30,
+				     OCXL_LITTLE_ENDIAN, &args.cache_miss_count);
+	if (rc)
+		goto out;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x38,
+				     OCXL_LITTLE_ENDIAN, &args.media_read_latency);
+	if (rc)
+		goto out;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x40,
+				     OCXL_LITTLE_ENDIAN, &args.media_write_latency);
+	if (rc)
+		goto out;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x48,
+				     OCXL_LITTLE_ENDIAN, &args.cache_read_latency);
+	if (rc)
+		goto out;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x50,
+				     OCXL_LITTLE_ENDIAN, &args.cache_write_latency);
+	if (rc)
+		goto out;
+
+	if (copy_to_user(uarg, &args, sizeof(args))) {
+		rc = -EFAULT;
+		goto out;
+	}
+
+	rc = scm_admin_response_handled(scm_data);
+	if (rc)
+		goto out;
+
+	rc = 0;
+	goto out;
+
+out:
+	mutex_unlock(&scm_data->admin_command.lock);
+	return rc;
+}
+
+static int scm_ioctl_eventfd(struct scm_data *scm_data,
+			     struct scm_ioctl_eventfd __user *uarg)
+{
+	struct scm_ioctl_eventfd args;
+
+	if (copy_from_user(&args, uarg, sizeof(args)))
+		return -EFAULT;
+
+	if (scm_data->ev_ctx)
+		return -EFAULT;
+
+	scm_data->ev_ctx = eventfd_ctx_fdget(args.eventfd);
+	if (!scm_data->ev_ctx)
+		return -EFAULT;
+
+	return 0;
+}
+
+static int scm_ioctl_event_check(struct scm_data *scm_data, u64 __user *uarg)
+{
+	u64 val = 0;
+	int rc;
+	u64 chi = 0;
+
+	rc = scm_chi(scm_data, &chi);
+	if (rc < 0)
+		return -EFAULT;
+
+	if (chi & GLOBAL_MMIO_CHI_ELA)
+		val |= SCM_IOCTL_EVENT_ERROR_LOG_AVAILABLE;
+
+	if (chi & GLOBAL_MMIO_CHI_CDA)
+		val |= SCM_IOCTL_EVENT_CONTROLLER_DUMP_AVAILABLE;
+
+	if (chi & GLOBAL_MMIO_CHI_CFFS)
+		val |= SCM_IOCTL_EVENT_FIRMWARE_FATAL;
+
+	if (chi & GLOBAL_MMIO_CHI_CHFS)
+		val |= SCM_IOCTL_EVENT_HARDWARE_FATAL;
+
+	rc = copy_to_user((u64 __user *) uarg, &val, sizeof(val));
+
+	return rc;
+}
+
+/**
+ * scm_req_controller_health_perf() - Request controller health & performance data
+ * @scm_data: the SCM metadata
+ * Return: 0 on success, negative on failure
+ */
+int scm_req_controller_health_perf(struct scm_data *scm_data)
+{
+	return ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_HCI,
+				      OCXL_LITTLE_ENDIAN,
+				      GLOBAL_MMIO_HCI_REQ_HEALTH_PERF);
+}
+
+#ifdef CONFIG_OCXL_SCM_DEBUG
+/**
+ * scm_enable_fwdebug() - Enable FW debug on the controller
+ * @scm_data: a pointer to the SCM device data
+ * Return: 0 on success, negative on failure
+ */
+static int scm_enable_fwdebug(const struct scm_data *scm_data)
+{
+	return ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_HCI,
+				      OCXL_LITTLE_ENDIAN,
+				      GLOBAL_MMIO_HCI_FW_DEBUG);
+}
+
+/**
+ * scm_disable_fwdebug() - Disable FW debug on the controller
+ * @scm_data: a pointer to the SCM device data
+ * Return: 0 on success, negative on failure
+ */
+static int scm_disable_fwdebug(const struct scm_data *scm_data)
+{
+	return ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_HCIC,
+				      OCXL_LITTLE_ENDIAN,
+				      GLOBAL_MMIO_HCI_FW_DEBUG);
+}
+
+static int scm_ioctl_fwdebug(struct scm_data *scm_data,
+			     struct scm_ioctl_fwdebug __user *uarg)
+{
+	struct scm_ioctl_fwdebug args;
+	u64 val;
+	int i;
+	int rc;
+
+	if (copy_from_user(&args, uarg, sizeof(args)))
+		return -EFAULT;
+
+	// Buffer size must be a multiple of 8
+	if ((args.buf_size & 0x07))
+		return -EINVAL;
+
+	if (args.buf_size > scm_data->admin_command.data_size)
+		return -EINVAL;
+
+	mutex_lock(&scm_data->admin_command.lock);
+
+	rc = scm_enable_fwdebug(scm_data);
+	if (rc)
+		goto out;
+
+	rc = scm_admin_command_request(scm_data, ADMIN_COMMAND_FW_DEBUG);
+	if (rc)
+		goto out;
+
+	// Write DebugAction & FunctionCode
+	val = ((u64)args.debug_action << 56) | ((u64)args.function_code << 40);
+
+	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
+				      scm_data->admin_command.request_offset + 0x08,
+				      OCXL_LITTLE_ENDIAN, val);
+	if (rc)
+		goto out;
+
+	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
+				      scm_data->admin_command.request_offset + 0x10,
+				      OCXL_LITTLE_ENDIAN, args.debug_parameter_1);
+	if (rc)
+		goto out;
+
+	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
+				      scm_data->admin_command.request_offset + 0x18,
+				      OCXL_LITTLE_ENDIAN, args.debug_parameter_2);
+	if (rc)
+		goto out;
+
+	for (i = 0x20; i < 0x38; i += 0x08)
+		rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
+					      scm_data->admin_command.request_offset + i,
+					      OCXL_LITTLE_ENDIAN, 0);
+	if (rc)
+		goto out;
+
+
+	// Populate admin command buffer
+	if (args.buf_size) {
+		for (i = 0; i < args.buf_size; i += sizeof(u64)) {
+			u64 val;
+
+			if (copy_from_user(&val, &args.buf[i], sizeof(u64)))
+				return -EFAULT;
+
+			rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
+						      scm_data->admin_command.data_offset + i,
+						      OCXL_HOST_ENDIAN, val);
+			if (rc)
+				goto out;
+		}
+	}
+
+	rc = scm_admin_command_execute(scm_data);
+	if (rc)
+		goto out;
+
+	rc = scm_admin_command_complete_timeout(scm_data,
+						scm_data->timeouts[ADMIN_COMMAND_FW_DEBUG]);
+	if (rc < 0)
+		goto out;
+
+	rc = scm_admin_response(scm_data);
+	if (rc < 0)
+		goto out;
+	if (rc != STATUS_SUCCESS) {
+		scm_warn_status(scm_data, "Unexpected status from FW Debug", rc);
+		goto out;
+	}
+
+	if (args.buf_size) {
+		for (i = 0; i < args.buf_size; i += sizeof(u64)) {
+			u64 val;
+
+			rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+						     scm_data->admin_command.data_offset + i,
+						     OCXL_HOST_ENDIAN, &val);
+			if (rc)
+				goto out;
+
+			if (copy_to_user(&args.buf[i], &val, sizeof(u64))) {
+				rc = -EFAULT;
+				goto out;
+			}
+		}
+	}
+
+	rc = scm_admin_response_handled(scm_data);
+	if (rc)
+		goto out;
+
+	rc = scm_disable_fwdebug(scm_data);
+	if (rc)
+		goto out;
+
+out:
+	mutex_unlock(&scm_data->admin_command.lock);
+	return rc;
+}
+
+static int scm_ioctl_shutdown(struct scm_data *scm_data)
+{
+	int rc;
+
+	mutex_lock(&scm_data->admin_command.lock);
+
+	rc = scm_admin_command_request(scm_data, ADMIN_COMMAND_SHUTDOWN);
+	if (rc)
+		goto out;
+
+	rc = scm_admin_command_execute(scm_data);
+	if (rc)
+		goto out;
+
+	rc = scm_admin_command_complete_timeout(scm_data, ADMIN_COMMAND_SHUTDOWN);
+	if (rc < 0) {
+		dev_warn(&scm_data->dev, "Shutdown timed out\n");
+		goto out;
+	}
+
+	rc = 0;
+	goto out;
+
+out:
+	mutex_unlock(&scm_data->admin_command.lock);
+	return rc;
+}
+
+static int scm_ioctl_mmio_write(struct scm_data *scm_data,
+				struct scm_ioctl_mmio __user *uarg)
+{
+	struct scm_ioctl_mmio args;
+
+	if (copy_from_user(&args, uarg, sizeof(args)))
+		return -EFAULT;
+
+	return ocxl_global_mmio_write64(scm_data->ocxl_afu, args.address,
+					OCXL_LITTLE_ENDIAN, args.val);
+}
+
+static int scm_ioctl_mmio_read(struct scm_data *scm_data,
+			       struct scm_ioctl_mmio __user *uarg)
+{
+	struct scm_ioctl_mmio args;
+	int rc;
+
+	if (copy_from_user(&args, uarg, sizeof(args)))
+		return -EFAULT;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, args.address,
+				     OCXL_LITTLE_ENDIAN, &args.val);
+	if (rc)
+		return rc;
+
+	if (copy_to_user(uarg, &args, sizeof(args)))
+		return -EFAULT;
+
+	return 0;
+}
+#else
+static int scm_ioctl_fwdebug(struct scm_data *scm_data,
+			     struct scm_ioctl_fwdebug __user *uarg)
+{
+	return -EPERM;
+}
+
+static int scm_ioctl_shutdown(struct scm_data *scm_data)
+{
+	return -EPERM;
+}
+
+static int scm_ioctl_mmio_write(struct scm_data *scm_data,
+				struct scm_ioctl_mmio __user *uarg)
+{
+	return -EPERM;
+}
+
+static int scm_ioctl_mmio_read(struct scm_data *scm_data,
+			       struct scm_ioctl_mmio __user *uarg)
+{
+	return -EPERM;
+}
+#endif
+
+static long scm_file_ioctl(struct file *file, unsigned int cmd,
+			   unsigned long args)
+{
+	struct scm_data *scm_data = file->private_data;
+	int rc = -EINVAL;
+
+	switch (cmd) {
+	case SCM_IOCTL_BUFFER_INFO:
+		rc = scm_ioctl_buffer_info(scm_data,
+					   (struct scm_ioctl_buffer_info __user *)args);
+		break;
+
+	case SCM_IOCTL_ERROR_LOG:
+		rc = scm_ioctl_error_log(scm_data,
+					 (struct scm_ioctl_error_log __user *)args);
+		break;
+
+	case SCM_IOCTL_CONTROLLER_DUMP:
+		rc = scm_request_controller_dump(scm_data);
+		break;
+
+	case SCM_IOCTL_CONTROLLER_DUMP_DATA:
+		rc = scm_ioctl_controller_dump_data(scm_data,
+						    (struct scm_ioctl_controller_dump_data __user *)args);
+		break;
+
+	case SCM_IOCTL_CONTROLLER_DUMP_COMPLETE:
+		rc = scm_ioctl_controller_dump_complete(scm_data);
+		break;
+
+	case SCM_IOCTL_CONTROLLER_STATS:
+		rc = scm_ioctl_controller_stats(scm_data,
+						(struct scm_ioctl_controller_stats __user *)args);
+		break;
+
+	case SCM_IOCTL_EVENTFD:
+		rc = scm_ioctl_eventfd(scm_data,
+				       (struct scm_ioctl_eventfd __user *)args);
+		break;
+
+	case SCM_IOCTL_EVENT_CHECK:
+		rc = scm_ioctl_event_check(scm_data, (u64 __user *)args);
+		break;
+
+	case SCM_IOCTL_REQUEST_HEALTH:
+		rc = scm_req_controller_health_perf(scm_data);
+		break;
+
+	case SCM_IOCTL_FWDEBUG:
+		rc = scm_ioctl_fwdebug(scm_data,
+				       (struct scm_ioctl_fwdebug __user *)args);
+		break;
+
+	case SCM_IOCTL_SHUTDOWN:
+		rc = scm_ioctl_shutdown(scm_data);
+		break;
+
+	case SCM_IOCTL_MMIO_WRITE:
+		rc = scm_ioctl_mmio_write(scm_data,
+					  (struct scm_ioctl_mmio __user *)args);
+		break;
+
+	case SCM_IOCTL_MMIO_READ:
+		rc = scm_ioctl_mmio_read(scm_data,
+					 (struct scm_ioctl_mmio __user *)args);
+		break;
+
+	}
+
+	return rc;
+}
+
+static const struct file_operations scm_fops = {
+	.owner		= THIS_MODULE,
+	.open	   = scm_file_open,
+	.release	= scm_file_release,
+	.unlocked_ioctl = scm_file_ioctl,
+	.compat_ioctl   = scm_file_ioctl,
+};
+
+/**
+ * scm_create_cdev() - Create the chardev in /dev for this scm device
+ * @scm_data: the SCM metadata
+ * Return: 0 on success, negative on failure
+ */
+static int scm_create_cdev(struct scm_data *scm_data)
+{
+	int rc;
+
+	cdev_init(&scm_data->cdev, &scm_fops);
+	rc = cdev_add(&scm_data->cdev, scm_data->dev.devt, 1);
+	if (rc) {
+		dev_err(&scm_data->dev, "Unable to add afu char device: %d\n", rc);
+		return rc;
+	}
+	return 0;
+}
+
+/**
+ * scm_remove() - Free an OpenCAPI Storage Class Memory device
+ * @pdev: the PCI device information struct
+ */
+static void scm_remove(struct pci_dev *pdev)
+{
+	if (PCI_FUNC(pdev->devfn) == 0) {
+		struct scm_function_0 *scm_func_0 = pci_get_drvdata(pdev);
+
+		if (scm_func_0) {
+			ocxl_function_close(scm_func_0->ocxl_fn);
+			scm_func_0->ocxl_fn = NULL;
+		}
+	} else {
+		struct scm_data *scm_data = pci_get_drvdata(pdev);
+
+		if (scm_data) {
+			if (scm_data->nvdimm_bus)
+				nvdimm_bus_unregister(scm_data->nvdimm_bus);
+
+			device_unregister(&scm_data->dev);
+		}
+	}
+}
+
+/**
+ * scm_setup_device_metadata() - Retrieve config information from the AFU and save it for future use
+ * @scm_data: the SCM metadata
+ * Return: 0 on success, negative on failure
+ */
+static int scm_setup_device_metadata(struct scm_data *scm_data)
+{
+	u64 val;
+	int rc;
+	int i;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, GLOBAL_MMIO_CCAP0,
+				     OCXL_LITTLE_ENDIAN, &val);
+	if (rc)
+		return rc;
+
+	scm_data->scm_revision = val & 0xFFFF;
+	scm_data->read_latency = (val >> 32) & 0xFF;
+	scm_data->readiness_timeout = val >> 48;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, GLOBAL_MMIO_CCAP1,
+				     OCXL_LITTLE_ENDIAN, &val);
+	if (rc)
+		return rc;
+
+	scm_data->max_controller_dump_size = val & 0xFFFFFFFF;
+
+	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, GLOBAL_MMIO_FWVER,
+				     OCXL_LITTLE_ENDIAN, &val);
+	if (rc)
+		return rc;
+
+	for (i = 0; i < 8; i++)
+		scm_data->fw_version[i] = (val >> (i * 8)) & 0xff;
+
+	scm_data->fw_version[8] = '\0';
+
+	dev_info(&scm_data->dev,
+		 "Firmware version '%s' SCM revision %d:%d\n", scm_data->fw_version,
+		 scm_data->scm_revision >> 4, scm_data->scm_revision & 0x0F);
+
+	return 0;
+}
+
+static const char *scm_decode_error_log_type(u8 error_log_type)
+{
+	switch (error_log_type) {
+	case 0x00:
+		return "general";
+	case 0x01:
+		return "predictive failure";
+	case 0x02:
+		return "thermal warning";
+	case 0x03:
+		return "data loss";
+	case 0x04:
+		return "health & performance";
+	default:
+		return "unknown";
+	}
+}
+
+static void scm_dump_error_log(struct scm_data *scm_data)
+{
+	struct scm_ioctl_error_log log;
+	u32 buf_size;
+	u8 *buf;
+	int rc;
+
+	if (scm_data->admin_command.data_size == 0)
+		return;
+
+	buf_size = scm_data->admin_command.data_size - 0x48;
+	buf = kzalloc(buf_size, GFP_KERNEL);
+	if (!buf)
+		return;
+
+	log.buf = buf;
+	log.buf_size = buf_size;
+
+	rc = scm_read_error_log(scm_data, &log, false);
+	if (rc < 0)
+		goto out;
+
+	dev_warn(&scm_data->dev,
+		 "SCM Error log: WWID=0x%016llx%016llx LID=0x%x PRC=%x type=0x%x %s, Uptime=%u seconds timestamp=0x%llx\n",
+		 log.wwid[0], log.wwid[1],
+		 log.log_identifier, log.program_reference_code,
+		 log.error_log_type,
+		 scm_decode_error_log_type(log.error_log_type),
+		 log.power_on_seconds, log.timestamp);
+	print_hex_dump(KERN_WARNING, "buf", DUMP_PREFIX_OFFSET, 16, 1, buf,
+		       log.buf_size, false);
+
+out:
+	kfree(buf);
+}
+
+static void scm_handle_nscra_doorbell(struct scm_data *scm_data)
+{
+	int rc;
+
+	if (scm_data->ns_command.op_code == NS_COMMAND_SECURE_ERASE) {
+		u64 success, attempted;
+
+
+		rc = scm_ns_response(scm_data);
+		if (rc < 0) {
+			scm_data->overwrite_state = SCM_OVERWRITE_FAILED;
+			mutex_unlock(&scm_data->ns_command.lock);
+			return;
+		}
+		if (rc != STATUS_SUCCESS)
+			scm_warn_status(scm_data, "Unexpected status from overwrite", rc);
+
+		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+					     scm_data->ns_command.response_offset +
+					     NS_RESPONSE_SECURE_ERASE_ACCESSIBLE_SUCCESS,
+					     OCXL_HOST_ENDIAN, &success);
+		if (rc) {
+			scm_data->overwrite_state = SCM_OVERWRITE_FAILED;
+			mutex_unlock(&scm_data->ns_command.lock);
+			return;
+		}
+
+		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+					     scm_data->ns_command.response_offset +
+					     NS_RESPONSE_SECURE_ERASE_ACCESSIBLE_ATTEMPTED,
+					     OCXL_HOST_ENDIAN, &attempted);
+		if (rc) {
+			scm_data->overwrite_state = SCM_OVERWRITE_FAILED;
+			mutex_unlock(&scm_data->ns_command.lock);
+			return;
+		}
+
+		scm_data->overwrite_state = SCM_OVERWRITE_SUCCESS;
+		if (success != attempted)
+			scm_data->overwrite_state = SCM_OVERWRITE_FAILED;
+
+		dev_info(&scm_data->dev,
+			 "Overwritten %llu/%llu accessible pages", success, attempted);
+
+		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+					     scm_data->ns_command.response_offset +
+					     NS_RESPONSE_SECURE_ERASE_DEFECTIVE_SUCCESS,
+					     OCXL_HOST_ENDIAN, &success);
+		if (rc) {
+			scm_data->overwrite_state = SCM_OVERWRITE_FAILED;
+			mutex_unlock(&scm_data->ns_command.lock);
+			return;
+		}
+
+		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+					     scm_data->ns_command.response_offset +
+					     NS_RESPONSE_SECURE_ERASE_DEFECTIVE_ATTEMPTED,
+					     OCXL_HOST_ENDIAN, &attempted);
+		if (rc) {
+			scm_data->overwrite_state = SCM_OVERWRITE_FAILED;
+			mutex_unlock(&scm_data->ns_command.lock);
+			return;
+		}
+
+		if (success != attempted)
+			scm_data->overwrite_state = SCM_OVERWRITE_FAILED;
+
+		dev_info(&scm_data->dev,
+			 "Overwritten %llu/%llu defective pages", success, attempted);
+
+		scm_ns_response_handled(scm_data);
+
+		mutex_unlock(&scm_data->ns_command.lock);
+		return;
+	}
+}
+
+static irqreturn_t scm_imn0_handler(void *private)
+{
+	struct scm_data *scm_data = private;
+	int rc;
+	u64 chi = 0;
+
+	rc = scm_chi(scm_data, &chi);
+	if (rc < 0)
+		return IRQ_NONE;
+
+	if (chi & GLOBAL_MMIO_CHI_NSCRA)
+		scm_handle_nscra_doorbell(scm_data);
+
+	if (chi & GLOBAL_MMIO_CHI_ELA) {
+		dev_warn(&scm_data->dev, "Error log is available\n");
+
+		if (scm_data->ev_ctx)
+			eventfd_signal(scm_data->ev_ctx, 1);
+	}
+
+	if (chi & GLOBAL_MMIO_CHI_CDA) {
+		dev_warn(&scm_data->dev, "Controller dump is available\n");
+
+		if (scm_data->ev_ctx)
+			eventfd_signal(scm_data->ev_ctx, 1);
+	}
+
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t scm_imn1_handler(void *private)
+{
+	struct scm_data *scm_data = private;
+	u64 chi = 0;
+
+	(void)scm_chi(scm_data, &chi);
+
+	if (chi & (GLOBAL_MMIO_CHI_CFFS | GLOBAL_MMIO_CHI_CHFS)) {
+		dev_err(&scm_data->dev,
+			"Controller status is fatal, chi=0x%llx, going offline\n", chi);
+
+		if (scm_data->nvdimm_bus) {
+			nvdimm_bus_unregister(scm_data->nvdimm_bus);
+			scm_data->nvdimm_bus = NULL;
+		}
+
+		if (scm_data->ev_ctx)
+			eventfd_signal(scm_data->ev_ctx, 1);
+	}
+
+	return IRQ_HANDLED;
+}
+
+
+/**
+ * scm_setup_irq() - Set up the IRQs for the SCM device
+ * @scm_data: the SCM metadata
+ * Return: 0 on success, negative on failure
+ */
+static int scm_setup_irq(struct scm_data *scm_data)
+{
+	int rc;
+	u64 irq_addr;
+
+	rc = ocxl_afu_irq_alloc(scm_data->ocxl_context, &scm_data->irq_id[0]);
+	if (rc)
+		return rc;
+
+	rc = ocxl_irq_set_handler(scm_data->ocxl_context, scm_data->irq_id[0],
+				  scm_imn0_handler, NULL, scm_data);
+
+	irq_addr = ocxl_afu_irq_get_addr(scm_data->ocxl_context, scm_data->irq_id[0]);
+	if (!irq_addr)
+		return -EFAULT;
+
+	scm_data->irq_addr[0] = ioremap(irq_addr, PAGE_SIZE);
+	if (!scm_data->irq_addr[0])
+		return -EINVAL;
+
+	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu, GLOBAL_MMIO_IMA0_OHP,
+				      OCXL_LITTLE_ENDIAN,
+				      (u64)scm_data->irq_addr[0]);
+	if (rc)
+		goto out_irq0;
+
+	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu, GLOBAL_MMIO_IMA0_CFP,
+				      OCXL_LITTLE_ENDIAN, 0);
+	if (rc)
+		goto out_irq0;
+
+	rc = ocxl_afu_irq_alloc(scm_data->ocxl_context, &scm_data->irq_id[1]);
+	if (rc)
+		goto out_irq0;
+
+
+	rc = ocxl_irq_set_handler(scm_data->ocxl_context, scm_data->irq_id[1],
+				  scm_imn1_handler, NULL, scm_data);
+	if (rc)
+		goto out_irq0;
+
+	irq_addr = ocxl_afu_irq_get_addr(scm_data->ocxl_context, scm_data->irq_id[1]);
+	if (!irq_addr)
+		goto out_irq0;
+
+	scm_data->irq_addr[1] = ioremap(irq_addr, PAGE_SIZE);
+	if (!scm_data->irq_addr[1])
+		goto out_irq0;
+
+	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu, GLOBAL_MMIO_IMA1_OHP,
+				      OCXL_LITTLE_ENDIAN,
+				      (u64)scm_data->irq_addr[1]);
+	if (rc)
+		goto out_irq1;
+
+	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu, GLOBAL_MMIO_IMA1_CFP,
+				      OCXL_LITTLE_ENDIAN, 0);
+	if (rc)
+		goto out_irq1;
+
+	// Enable doorbells
+	rc = ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_CHIE,
+				    OCXL_LITTLE_ENDIAN,
+				    GLOBAL_MMIO_CHI_ELA | GLOBAL_MMIO_CHI_CDA |
+				    GLOBAL_MMIO_CHI_CFFS | GLOBAL_MMIO_CHI_CHFS |
+				    GLOBAL_MMIO_CHI_NSCRA);
+	if (rc)
+		goto out_irq1;
+
+	return 0;
+
+out_irq1:
+	iounmap(scm_data->irq_addr[1]);
+	scm_data->irq_addr[1] = NULL;
+
+out_irq0:
+	iounmap(scm_data->irq_addr[0]);
+	scm_data->irq_addr[0] = NULL;
+
+	return rc;
+}
+
+/**
+ * scm_probe_function_0 - Set up function 0 for an OpenCAPI Storage Class Memory device
+ * This is important as it enables higher than 0 across all other functions,
+ * which in turn enables higher bandwidth accesses
+ * @pdev: the PCI device information struct
+ * Return: 0 on success, negative on failure
+ */
+static int scm_probe_function_0(struct pci_dev *pdev)
+{
+	struct scm_function_0 *scm_func_0 = NULL;
+
+	scm_func_0 = kzalloc(sizeof(*scm_func_0), GFP_KERNEL);
+	if (!scm_func_0)
+		return -ENOMEM;
+
+	scm_func_0->pdev = pdev;
+	scm_func_0->ocxl_fn = ocxl_function_open(pdev);
+	if (IS_ERR(scm_func_0->ocxl_fn)) {
+		kfree(scm_func_0);
+		dev_err(&pdev->dev, "failed to open OCXL function\n");
+		return -EFAULT;
+	}
+
+	pci_set_drvdata(pdev, scm_func_0);
+
+	return 0;
+}
+
+/**
+ * scm_probe - Init an OpenCAPI Storage Class Memory device
+ * @pdev: the PCI device information struct
+ * @ent: The entry from scm_pci_tbl
+ * Return: 0 on success, negative on failure
+ */
+static int scm_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	struct scm_data *scm_data = NULL;
+	int elapsed;
+	u64 chi;
+
+	if (PCI_FUNC(pdev->devfn) == 0)
+		return scm_probe_function_0(pdev);
+	else if (PCI_FUNC(pdev->devfn) != 1)
+		return 0;
+
+	scm_data = kzalloc(sizeof(*scm_data), GFP_KERNEL);
+	if (!scm_data)
+		goto err;
+	scm_data->pdev = pdev;
+	mutex_init(&scm_data->admin_command.lock);
+	mutex_init(&scm_data->ns_command.lock);
+
+
+	scm_data->timeouts[ADMIN_COMMAND_ERRLOG] = 2000; // ms
+	scm_data->timeouts[ADMIN_COMMAND_HEARTBEAT] = 100; // ms
+	scm_data->timeouts[ADMIN_COMMAND_SMART] = 100; // ms
+	scm_data->timeouts[ADMIN_COMMAND_CONTROLLER_DUMP] = 1000; // ms
+	scm_data->timeouts[ADMIN_COMMAND_CONTROLLER_STATS] = 100; // ms
+	scm_data->timeouts[ADMIN_COMMAND_SHUTDOWN] = 1000; // ms
+	scm_data->timeouts[ADMIN_COMMAND_FW_UPDATE] = 16000; // ms
+
+	pci_set_drvdata(pdev, scm_data);
+
+	scm_data->ocxl_fn = ocxl_function_open(pdev);
+	if (IS_ERR(scm_data->ocxl_fn)) {
+		kfree(scm_data);
+		scm_data = NULL;
+		pci_set_drvdata(pdev, NULL);
+		dev_err(&pdev->dev, "failed to open OCXL function\n");
+		goto err;
+	}
+
+	scm_data->ocxl_afu = ocxl_function_fetch_afu(scm_data->ocxl_fn, 0);
+	if (scm_data->ocxl_afu == NULL)
+		goto err;
+
+	ocxl_afu_get(scm_data->ocxl_afu);
+
+	if (scm_register(scm_data) < 0)
+		goto err;
+
+	if (ocxl_context_alloc(&scm_data->ocxl_context, scm_data->ocxl_afu, NULL))
+		goto err;
+
+	if (ocxl_context_attach(scm_data->ocxl_context, 0, NULL))
+		goto err;
+
+	if (scm_setup_device_metadata(scm_data))
+		goto err;
+
+	if (scm_setup_irq(scm_data))
+		goto err;
+
+	if (scm_setup_command_metadata(scm_data))
+		goto err;
+
+	if (scm_create_cdev(scm_data))
+		goto err;
+
+	if (scm_sysfs_add(scm_data))
+		goto err;
+
+	if (scm_heartbeat(scm_data))
+		goto err;
+
+	elapsed = 0;
+	while (!scm_is_usable(scm_data)) {
+		if (elapsed++ > SCM_USABLE_TIMEOUT) {
+			dev_warn(&scm_data->dev, "SCM ready timeout.\n");
+			goto err;
+		}
+
+		dev_warn(&scm_data->dev,
+			 "Waiting for SCM to become usable (%d/%d seconds)\n",
+			 elapsed, SCM_USABLE_TIMEOUT);
+		msleep(1000);
+	}
+
+	if (scm_register_lpc_mem(scm_data))
+		goto err;
+
+	return 0;
+
+err:
+	if (scm_data &&
+		    (scm_chi(scm_data, &chi) == 0) &&
+		    (chi & GLOBAL_MMIO_CHI_ELA))
+		scm_dump_error_log(scm_data);
+
+	dev_err(&pdev->dev,
+		"Error detected, will not register storage class memory\n");
+	return -ENXIO;
+}
+
+struct pci_driver scm_pci_driver = {
+	.name = "ocxl-scm",
+	.id_table = scm_pci_tbl,
+	.probe = scm_probe,
+	.remove = scm_remove,
+	.shutdown = scm_remove,
+};
+
+static int scm_file_init(void)
+{
+	int rc;
+
+	mutex_init(&minors_idr_lock);
+	idr_init(&minors_idr);
+
+	rc = alloc_chrdev_region(&scm_dev, 0, SCM_NUM_MINORS, "scm");
+	if (rc) {
+		pr_err("Unable to allocate scm major number: %d\n", rc);
+		return rc;
+	}
+
+	scm_class = class_create(THIS_MODULE, "scm");
+	if (IS_ERR(scm_class)) {
+		pr_err("Unable to create scm class\n");
+		unregister_chrdev_region(scm_dev, SCM_NUM_MINORS);
+		return PTR_ERR(scm_class);
+	}
+
+	return 0;
+}
+
+static void scm_file_exit(void)
+{
+	class_destroy(scm_class);
+	unregister_chrdev_region(scm_dev, SCM_NUM_MINORS);
+	idr_destroy(&minors_idr);
+}
+
+static int __init scm_init(void)
+{
+	int rc = 0;
+
+	rc = scm_file_init();
+	if (rc)
+		return rc;
+
+	rc = pci_register_driver(&scm_pci_driver);
+	if (rc) {
+		scm_file_exit();
+		return rc;
+	}
+
+	return 0;
+}
+
+static void scm_exit(void)
+{
+	pci_unregister_driver(&scm_pci_driver);
+	scm_file_exit();
+}
+
+module_init(scm_init);
+module_exit(scm_exit);
+
+MODULE_DESCRIPTION("Storage Class Memory");
+MODULE_LICENSE("GPL");
diff --git a/drivers/nvdimm/ocxl-scm_internal.c b/drivers/nvdimm/ocxl-scm_internal.c
new file mode 100644
index 000000000000..e7c247835817
--- /dev/null
+++ b/drivers/nvdimm/ocxl-scm_internal.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2019 IBM Corp.
+
+#include <misc/ocxl.h>
+#include <linux/delay.h>
+#include "ocxl-scm_internal.h"
+
+int scm_chi(const struct scm_data *scm_data, u64 *chi)
+{
+	u64 val;
+	int rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, GLOBAL_MMIO_CHI,
+					 OCXL_LITTLE_ENDIAN, &val);
+	if (rc)
+		return rc;
+
+	*chi = val;
+
+	return 0;
+}
+
+bool scm_controller_is_ready(const struct scm_data *scm_data)
+{
+	u64 val = 0;
+	int rc = scm_chi(scm_data, &val);
+
+	WARN_ON(rc < 0);
+
+	return (val & GLOBAL_MMIO_CHI_CRDY) != 0;
+}
+
+static int scm_command_request(const struct scm_data *scm_data,
+			       struct command_metadata *cmd, u8 op_code)
+{
+	u64 val = op_code;
+	int rc;
+	u8 i;
+
+	if (!scm_controller_is_ready(scm_data))
+		return -EIO;
+
+	cmd->op_code = op_code;
+	cmd->id++;
+
+	val |= ((u64)cmd->id) << 16;
+
+	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu, cmd->request_offset,
+				      OCXL_LITTLE_ENDIAN, val);
+	if (rc)
+		return rc;
+
+	for (i = 0x08; i <= 0x38; i += 0x08) {
+		rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
+					      cmd->request_offset + i,
+					      OCXL_LITTLE_ENDIAN, 0);
+		if (rc)
+			return rc;
+	}
+
+	return 0;
+}
+
+int scm_admin_command_request(struct scm_data *scm_data, u8 op_code)
+{
+	return scm_command_request(scm_data, &scm_data->admin_command, op_code);
+}
+
+int scm_command_response(const struct scm_data *scm_data,
+			 const struct command_metadata *cmd)
+{
+	u64 val;
+	u16 id;
+	u8 status;
+	int rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
+					 cmd->response_offset,
+					 OCXL_LITTLE_ENDIAN, &val);
+	if (rc)
+		return rc;
+
+	status = val & 0xff;
+	id = (val >> 16) & 0xffff;
+
+	if (id != cmd->id) {
+		dev_warn(&scm_data->dev,
+			 "Expected response for command %d, but received response for command %d instead.\n",
+			 cmd->id, id);
+	}
+
+	return status;
+}
+
+int scm_admin_response(const struct scm_data *scm_data)
+{
+	return scm_command_response(scm_data, &scm_data->admin_command);
+}
+
+
+int scm_admin_command_execute(const struct scm_data *scm_data)
+{
+	return ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_HCI,
+				      OCXL_LITTLE_ENDIAN, GLOBAL_MMIO_HCI_ACRW);
+}
+
+static bool scm_admin_command_complete(const struct scm_data *scm_data)
+{
+	u64 val = 0;
+
+	int rc = scm_chi(scm_data, &val);
+
+	WARN_ON(rc);
+
+	return (val & GLOBAL_MMIO_CHI_ACRA) != 0;
+}
+
+int scm_admin_command_complete_timeout(const struct scm_data *scm_data,
+				       int command)
+{
+	u32 timeout = scm_data->timeouts[command];
+	timeout++;
+	timeout /= 32;
+	if (!timeout)
+		timeout = SCM_DEFAULT_TIMEOUT / 32;
+
+	while (timeout-- > 0) {
+		if (scm_admin_command_complete(scm_data))
+			return 0;
+		msleep(32);
+	}
+
+	return -EBUSY;
+}
+
+int scm_admin_response_handled(const struct scm_data *scm_data)
+{
+	return ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_CHIC,
+				      OCXL_LITTLE_ENDIAN, GLOBAL_MMIO_CHI_ACRA);
+}
+
+int scm_ns_command_request(struct scm_data *scm_data, u8 op_code)
+{
+	return scm_command_request(scm_data, &scm_data->ns_command, op_code);
+}
+
+int scm_ns_response(const struct scm_data *scm_data)
+{
+	return scm_command_response(scm_data, &scm_data->ns_command);
+}
+
+int scm_ns_command_execute(const struct scm_data *scm_data)
+{
+	return ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_HCI,
+				      OCXL_LITTLE_ENDIAN, GLOBAL_MMIO_HCI_NSCRW);
+}
+
+bool scm_ns_command_complete(const struct scm_data *scm_data)
+{
+	u64 val = 0;
+	int rc = scm_chi(scm_data, &val);
+
+	WARN_ON(rc);
+
+	return (val & GLOBAL_MMIO_CHI_NSCRA) != 0;
+}
+
+int scm_ns_response_handled(const struct scm_data *scm_data)
+{
+	return ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_CHIC,
+				      OCXL_LITTLE_ENDIAN, GLOBAL_MMIO_CHI_NSCRA);
+}
+
+
+void scm_warn_status(const struct scm_data *scm_data, const char *message,
+		     u8 status)
+{
+	const char *text = "Unknown";
+
+	switch (status) {
+	case STATUS_SUCCESS:
+		text = "Success";
+		break;
+
+	case STATUS_MEM_UNAVAILABLE:
+		text = "Persistent memory unavailable";
+		break;
+
+	case STATUS_BAD_OPCODE:
+		text = "Bad opcode";
+		break;
+
+	case STATUS_BAD_REQUEST_PARM:
+		text = "Bad request parameter";
+		break;
+
+	case STATUS_BAD_DATA_PARM:
+		text = "Bad data parameter";
+		break;
+
+	case STATUS_DEBUG_BLOCKED:
+		text = "Debug action blocked";
+		break;
+
+	case STATUS_FAIL:
+		text = "Failed";
+		break;
+	}
+
+	dev_warn(&scm_data->dev, "%s: %s (%x)\n", message, text, status);
+}
+
+void scm_warn_status_fw_update(const struct scm_data *scm_data,
+			       const char *message, u8 status)
+{
+	const char *text;
+
+	switch (status) {
+	case STATUS_FW_UPDATE_BLOCKED:
+		text = "Firmware update is blocked, please try again later";
+		break;
+
+	case STATUS_FW_ARG_INVALID:
+		text = "Internal error in SCM firmware update mechanism";
+		break;
+
+	case STATUS_FW_INVALID:
+		text = "Firmware content is invalid, please verify firmware update file";
+		break;
+
+	default:
+		return scm_warn_status(scm_data, message, status);
+	}
+
+	dev_warn(&scm_data->dev, "%s: %s (%x)\n", message, text, status);
+}
diff --git a/drivers/nvdimm/ocxl-scm_internal.h b/drivers/nvdimm/ocxl-scm_internal.h
new file mode 100644
index 000000000000..c236d8092c6d
--- /dev/null
+++ b/drivers/nvdimm/ocxl-scm_internal.h
@@ -0,0 +1,331 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2019 IBM Corp.
+
+#include <linux/pci.h>
+#include <linux/cdev.h>
+#include <misc/ocxl.h>
+#include <linux/libnvdimm.h>
+#include <uapi/linux/ocxl-scm.h>
+#include <linux/mm.h>
+
+#define SCM_DEFAULT_TIMEOUT 100
+
+#define GLOBAL_MMIO_CHI		0x000
+#define GLOBAL_MMIO_CHIC	0x008
+#define GLOBAL_MMIO_CHIE	0x010
+#define GLOBAL_MMIO_CHIEC	0x018
+#define GLOBAL_MMIO_HCI		0x020
+#define GLOBAL_MMIO_HCIC	0x028
+#define GLOBAL_MMIO_IMA0_OHP	0x040
+#define GLOBAL_MMIO_IMA0_CFP	0x048
+#define GLOBAL_MMIO_IMA1_OHP	0x050
+#define GLOBAL_MMIO_IMA1_CFP	0x058
+#define GLOBAL_MMIO_ACMA_CREQO	0x100
+#define GLOBAL_MMIO_ACMA_CRSPO	0x104
+#define GLOBAL_MMIO_ACMA_CDBO	0x108
+#define GLOBAL_MMIO_ACMA_CDBS	0x10c
+#define GLOBAL_MMIO_NSCMA_CREQO	0x120
+#define GLOBAL_MMIO_NSCMA_CRSPO	0x124
+#define GLOBAL_MMIO_NSCMA_CDBO	0x128
+#define GLOBAL_MMIO_NSCMA_CDBS	0x12c
+#define GLOBAL_MMIO_CSTS	0x140
+#define GLOBAL_MMIO_FWVER	0x148
+#define GLOBAL_MMIO_CCAP0	0x160
+#define GLOBAL_MMIO_CCAP1	0x168
+
+#define GLOBAL_MMIO_CHI_ACRA	BIT_ULL(0)
+#define GLOBAL_MMIO_CHI_NSCRA	BIT_ULL(1)
+#define GLOBAL_MMIO_CHI_CRDY	BIT_ULL(4)
+#define GLOBAL_MMIO_CHI_CFFS	BIT_ULL(5)
+#define GLOBAL_MMIO_CHI_MA	BIT_ULL(6)
+#define GLOBAL_MMIO_CHI_ELA	BIT_ULL(7)
+#define GLOBAL_MMIO_CHI_CDA	BIT_ULL(8)
+#define GLOBAL_MMIO_CHI_CHFS	BIT_ULL(9)
+
+#define GLOBAL_MMIO_CHI_ALL	(GLOBAL_MMIO_CHI_ACRA | \
+				 GLOBAL_MMIO_CHI_NSCRA | \
+				 GLOBAL_MMIO_CHI_CRDY | \
+				 GLOBAL_MMIO_CHI_CFFS | \
+				 GLOBAL_MMIO_CHI_MA | \
+				 GLOBAL_MMIO_CHI_ELA | \
+				 GLOBAL_MMIO_CHI_CDA | \
+				 GLOBAL_MMIO_CHI_CHFS)
+
+#define GLOBAL_MMIO_HCI_ACRW				BIT_ULL(0)
+#define GLOBAL_MMIO_HCI_NSCRW				BIT_ULL(1)
+#define GLOBAL_MMIO_HCI_AFU_RESET			BIT_ULL(2)
+#define GLOBAL_MMIO_HCI_FW_DEBUG			BIT_ULL(3)
+#define GLOBAL_MMIO_HCI_CONTROLLER_DUMP			BIT_ULL(4)
+#define GLOBAL_MMIO_HCI_CONTROLLER_DUMP_COLLECTED	BIT_ULL(5)
+#define GLOBAL_MMIO_HCI_REQ_HEALTH_PERF			BIT_ULL(6)
+
+#define ADMIN_COMMAND_HEARTBEAT		0x00u
+#define ADMIN_COMMAND_SHUTDOWN		0x01u
+#define ADMIN_COMMAND_FW_UPDATE		0x02u
+#define ADMIN_COMMAND_FW_DEBUG		0x03u
+#define ADMIN_COMMAND_ERRLOG		0x04u
+#define ADMIN_COMMAND_SMART		0x05u
+#define ADMIN_COMMAND_CONTROLLER_STATS	0x06u
+#define ADMIN_COMMAND_CONTROLLER_DUMP	0x07u
+#define ADMIN_COMMAND_CMD_CAPS		0x08u
+#define ADMIN_COMMAND_MAX		0x08u
+
+#define NS_COMMAND_SECURE_ERASE	0x20ull
+
+#define NS_RESPONSE_SECURE_ERASE_ACCESSIBLE_SUCCESS 0x20
+#define NS_RESPONSE_SECURE_ERASE_ACCESSIBLE_ATTEMPTED 0x28
+#define NS_RESPONSE_SECURE_ERASE_DEFECTIVE_SUCCESS 0x30
+#define NS_RESPONSE_SECURE_ERASE_DEFECTIVE_ATTEMPTED 0x38
+
+
+
+#define STATUS_SUCCESS		0x00
+#define STATUS_MEM_UNAVAILABLE	0x20
+#define STATUS_BAD_OPCODE	0x50
+#define STATUS_BAD_REQUEST_PARM	0x51
+#define STATUS_BAD_DATA_PARM	0x52
+#define STATUS_DEBUG_BLOCKED	0x70
+#define STATUS_FAIL		0xFF
+
+#define STATUS_FW_UPDATE_BLOCKED 0x21
+#define STATUS_FW_ARG_INVALID	0x51
+#define STATUS_FW_INVALID	0x52
+
+#define SCM_LABEL_AREA_SIZE	(1UL << PA_SECTION_SHIFT)
+
+struct command_metadata {
+	u32 request_offset;
+	u32 response_offset;
+	u32 data_offset;
+	u32 data_size;
+	struct mutex lock;
+	u16 id;
+	u8 op_code;
+};
+
+struct scm_function_0 {
+	struct pci_dev *pdev;
+	struct ocxl_fn *ocxl_fn;
+};
+
+enum overwrite_state {
+	SCM_OVERWRITE_IDLE = 0,
+	SCM_OVERWRITE_BUSY,
+	SCM_OVERWRITE_SUCCESS,
+	SCM_OVERWRITE_FAILED
+};
+
+#define SCM_SMART_ATTR_POWER_ON_HOURS	0x09
+#define SCM_SMART_ATTR_TEMPERATURE	0xC2
+#define SCM_SMART_ATTR_LIFE_REMAINING	0xCA
+
+struct scm_smart_attrib {
+	__u8 id; /* out, See defines above */
+	__u16 attribute_flags;
+	__u8 current_val;
+	__u8 threshold_val;
+	__u8 worst_val;
+	__u8 reserved;
+	__u64 raw_val;
+};
+
+struct scm_smart_attribs {
+	struct scm_smart_attrib power_on_hours;
+	struct scm_smart_attrib temperature;
+	struct scm_smart_attrib life_remaining;
+};
+
+struct scm_data {
+	struct device dev;
+	struct pci_dev *pdev;
+	struct cdev cdev;
+	struct ocxl_fn *ocxl_fn;
+#define SCM_IRQ_COUNT 2
+	int irq_id[SCM_IRQ_COUNT];
+	struct dev_pagemap irq_pgmap[SCM_IRQ_COUNT];
+	void *irq_addr[SCM_IRQ_COUNT];
+	struct nd_interleave_set nd_set;
+	struct nvdimm_bus_descriptor bus_desc;
+	struct nvdimm_bus *nvdimm_bus;
+	struct ocxl_afu *ocxl_afu;
+	struct ocxl_context *ocxl_context;
+	void *metadata_addr;
+	struct scm_global_mmio *global_mmio;
+	struct command_metadata admin_command;
+	struct command_metadata ns_command;
+	enum overwrite_state overwrite_state;
+	struct resource scm_res;
+	struct nd_region *nd_region;
+	struct eventfd_ctx *ev_ctx;
+	struct scm_smart_attribs smart;
+	char fw_version[8+1];
+	u32 timeouts[ADMIN_COMMAND_MAX+1];
+
+	u16 scm_revision; // major/minor
+	u16 readiness_timeout; /* The worst case time (in milliseconds) that the host shall
+				* wait for the controller to become operational following a reset (CHI.CRDY).
+				*/
+	u16 read_latency; /* The nominal measure of latency (in nanoseconds)
+			   * associated with an unassisted read of a memory block.
+			   * This represents the capability of the raw media technology without assistance
+			   */
+	u32 max_controller_dump_size; // bytes
+};
+
+/**
+ * Create sysfs entries for an SCM device
+ * scm_data: The SCM metadata
+ */
+int scm_sysfs_add(struct scm_data *scm_data);
+
+/**
+ * Get the value of the CHI register:
+ * scm_data: The SCM metadata
+ * chi: returns the CHI value
+ *
+ * Returns 0 on success, negative on error
+ */
+int scm_chi(const struct scm_data *scm_data, u64 *chi);
+
+/**
+ * scm_controller_is_ready - Is the controller ready?
+ * @scm_data: a pointer to the SCM device data
+ * Return true if the controller is ready
+ */
+bool scm_controller_is_ready(const struct scm_data *scm_data);
+
+/**
+ * Issue an admin command request
+ *
+ * scm_data: a pointer to the SCM device data
+ * op_code: The op-code for the command
+ *
+ * Returns an identifier for the command, or negative on error
+ */
+int scm_admin_command_request(struct scm_data *scm_data, u8 op_code);
+
+/**
+ * Validate an admin response
+ *
+ * scm_data: a pointer to the SCM device data
+ *
+ * Returns the status code of the command, or negative on error
+ */
+int scm_admin_response(const struct scm_data *scm_data);
+
+/**
+ * Notify the controller to start processing a pending admin command
+ *
+ * scm_data: a pointer to the SCM device data
+ *
+ * Returns 0 on success, negative on error
+ */
+int scm_admin_command_execute(const struct scm_data *scm_data);
+
+/**
+ * Wait for an admin command to finish executing
+ *
+ * scm_data: a pointer to the SCM device data
+ * command: the admin command to wait for completion (determines the timeout)
+ *
+ * Returns 0 on success, -EBUSY on timeout
+ */
+int scm_admin_command_complete_timeout(const struct scm_data *scm_data,
+				       int command);
+
+/**
+ * Notify the controller that the admin response has been handled
+ *
+ * scm_data: a pointer to the SCM device data
+ *
+ * Returns 0 on success, negative on failure
+ */
+int scm_admin_response_handled(const struct scm_data *scm_data);
+
+/**
+ * Issue a near storage command request
+ *
+ * scm_data: a pointer to the SCM device data
+ * op_code: The op-code for the command
+ *
+ * Returns an identifier for the command, or negative on error
+ */
+int scm_ns_command_request(struct scm_data *scm_data, u8 op_code);
+
+/**
+ * Validate a near storage response
+ *
+ * scm_data: a pointer to the SCM device data
+ *
+ * Returns the status code of the command, or negative on error
+ */
+int scm_ns_response(const struct scm_data *scm_data);
+
+/**
+ * Notify the controller to start processing a pending near storage command
+ *
+ * scm_data: a pointer to the SCM device data
+ *
+ * Returns 0 on success, negative on error
+ */
+int scm_ns_command_execute(const struct scm_data *scm_data);
+
+/**
+ * Is a near storage command executing
+ *
+ * scm_data: a pointer to the SCM device data
+ *
+ * Returns true if the previous admin command has completed
+ */
+bool scm_ns_command_complete(const struct scm_data *scm_data);
+
+/**
+ * Notify the controller that the near storage response has been handled
+ *
+ * scm_data: a pointer to the SCM device data
+ *
+ * Returns 0 on success, negative on failure
+ */
+int scm_ns_response_handled(const struct scm_data *scm_data);
+
+/**
+ * Emit a kernel warning showing a command status.
+ *
+ * scm_data: a pointer to the SCM device data
+ * message: A message to accompany the warning
+ * status: The command status
+ */
+void scm_warn_status(const struct scm_data *scm_data, const char *message,
+		     u8 status);
+
+/**
+ * Emit a kernel warning showing a command status.
+ *
+ * scm_data: a pointer to the SCM device data
+ * message: A message to accompany the warning
+ * status: The command status
+ */
+void scm_warn_status_fw_update(const struct scm_data *scm_data,
+			       const char *message, u8 status);
+
+/**
+ * Request a controller dump
+ *
+ * scm_data: a pointer to the SCM device data
+ */
+int scm_request_controller_dump(struct scm_data *scm_data);
+
+/**
+ * Request health & performance data (this will emit error logs with the information)
+ *
+ * scm_data: a pointer to the SCM device data
+ */
+int scm_req_controller_health_perf(struct scm_data *scm_data);
+
+
+/**
+ * scm_overwrite() - Overwrite all data on the card
+ * @scm_data: The SCM device data
+ * Return: 0 on success
+ */
+int scm_overwrite(struct scm_data *scm_data);
diff --git a/drivers/nvdimm/ocxl-scm_sysfs.c b/drivers/nvdimm/ocxl-scm_sysfs.c
new file mode 100644
index 000000000000..080bbdeb0e56
--- /dev/null
+++ b/drivers/nvdimm/ocxl-scm_sysfs.c
@@ -0,0 +1,219 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright 2018 IBM Corp.
+
+#include <linux/sysfs.h>
+#include <linux/capability.h>
+#include <linux/limits.h>
+#include <linux/firmware.h>
+#include "ocxl-scm_internal.h"
+
+static ssize_t admin_command_buffer_size_show(struct device *device,
+	struct device_attribute *attr,
+	char *buf)
+{
+	struct scm_data *scm_data = container_of(device, struct scm_data, dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%d\n", scm_data->admin_command.data_size);
+}
+
+static ssize_t fw_version_show(struct device *device,
+			       struct device_attribute *attr, char *buf)
+{
+	struct scm_data *scm_data = container_of(device, struct scm_data, dev);
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", scm_data->fw_version);
+}
+
+#define SCM_FWUPDATE_BLOCK_SIZE	32768
+
+/**
+ * scm_update_firmware() - Write a 32kB block of data to firmware
+ * The block may be less than 32kB if it is the last one
+ *
+ * scm_data the SCM device metadata
+ * offset: the offset of the start of the block
+ * buf: the block data
+ * size: the size of the block
+ */
+static ssize_t scm_update_firmware(struct scm_data *scm_data, size_t offset,
+				   const char *buf, size_t size)
+{
+	int rc;
+	size_t i;
+	u64 val;
+
+	if (size > SCM_FWUPDATE_BLOCK_SIZE)
+		return -EINVAL;
+
+	rc = scm_admin_command_request(scm_data, ADMIN_COMMAND_FW_UPDATE);
+	if (rc)
+		return rc;
+
+	val = (((u64)offset) << 32) | size;
+	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
+				      scm_data->admin_command.request_offset + 8,
+				      OCXL_LITTLE_ENDIAN, val);
+	if (rc)
+		return rc;
+
+	for (i = 0; i < size; i += 8) {
+		val = *(u64 *)(buf + i);
+		rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
+					      scm_data->admin_command.data_offset + i,
+					      OCXL_HOST_ENDIAN, val);
+		if (rc)
+			return rc;
+	}
+
+	rc = scm_admin_command_execute(scm_data);
+	if (rc)
+		return rc;
+
+	rc = scm_admin_command_complete_timeout(scm_data,
+						ADMIN_COMMAND_FW_UPDATE);
+	if (rc < 0) {
+		dev_err(&scm_data->dev, "Firmware update timeout\n");
+		return rc;
+	}
+
+	rc = scm_admin_response(scm_data);
+	if (rc < 0)
+		return rc;
+	if (rc != STATUS_SUCCESS) {
+		scm_warn_status_fw_update(scm_data, "FW Update", rc);
+		return rc;
+	}
+
+	return 0;
+}
+
+/*
+ * Parse out a firmware filename from sysfs, retrieve it's contents and write it
+ * to the SCM device firmware storage
+ */
+static ssize_t fw_update_filename_store(struct device *device,
+					struct device_attribute *attr,
+					const char *buf, size_t size)
+{
+	char path[NAME_MAX+1];
+	const char *end;
+	const struct firmware *firmware = NULL;
+	size_t offset;
+	int rc;
+	struct scm_data *scm_data = container_of(device, struct scm_data, dev);
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EACCES;
+
+	end = strnchr(buf, size, '\n');
+	if (end == NULL)
+		end = buf + strnlen(buf, size);
+
+	if ((end - buf) > NAME_MAX) {
+		dev_err(device, "Firmware filename '%-.*s' too long\n",
+			(int)(end - buf), buf);
+		return -EIO;
+	}
+
+	memcpy(path, buf, end - buf);
+	path[end - buf] = '\0';
+
+	if (request_firmware(&firmware, path, device)) {
+		dev_err(device, "Firmware file %s not found\n", path);
+		return -EIO;
+	}
+
+	if (firmware->size % 8) {
+		release_firmware(firmware);
+		dev_err(device, "Firmware '%s' should be a multiple of 8 bytes", path);
+		return -EINVAL;
+	}
+
+	mutex_lock(&scm_data->admin_command.lock);
+
+	for (offset = 0; offset < firmware->size; offset += SCM_FWUPDATE_BLOCK_SIZE) {
+		size_t remainder = firmware->size - offset;
+		size_t block_size;
+
+		block_size = (remainder > SCM_FWUPDATE_BLOCK_SIZE) ?
+			      SCM_FWUPDATE_BLOCK_SIZE : remainder;
+		rc = scm_update_firmware(scm_data, offset,
+					 firmware->data + offset, block_size);
+		if (rc) {
+			mutex_unlock(&scm_data->admin_command.lock);
+			return -EFAULT;
+		}
+	}
+
+	mutex_unlock(&scm_data->admin_command.lock);
+
+	return size;
+}
+
+/*
+ * Trigger a controller dump
+ */
+static ssize_t controller_dump_store(struct device *device,
+				     struct device_attribute *attr,
+				     const char *buf, size_t size)
+{
+	struct scm_data *scm_data = container_of(device, struct scm_data, dev);
+
+	scm_request_controller_dump(scm_data);
+
+	return size;
+}
+
+/*
+ * Request health & performance data
+ */
+static ssize_t health_request_store(struct device *device,
+				    struct device_attribute *attr,
+				    const char *buf, size_t size)
+{
+	struct scm_data *scm_data = container_of(device, struct scm_data, dev);
+
+	scm_req_controller_health_perf(scm_data);
+
+	return size;
+}
+
+/*
+ * Overwrite all media
+ */
+static ssize_t overwrite_store(struct device *device,
+			       struct device_attribute *attr,
+			       const char *buf, size_t size)
+{
+	struct scm_data *scm_data = container_of(device, struct scm_data, dev);
+
+	scm_overwrite(scm_data);
+
+	return size;
+}
+
+static struct device_attribute scm_attrs[] = {
+	__ATTR_RO(admin_command_buffer_size),
+	__ATTR_RO(fw_version),
+	__ATTR_WO(fw_update_filename),
+	__ATTR_WO(controller_dump),
+	__ATTR_WO(health_request),
+	__ATTR_WO(overwrite),
+};
+
+int scm_sysfs_add(struct scm_data *scm_data)
+{
+	int i, rc;
+
+	for (i = 0; i < ARRAY_SIZE(scm_attrs); i++) {
+		rc = device_create_file(&scm_data->dev, &scm_attrs[i]);
+		if (rc) {
+			for (; --i >= 0;)
+				device_remove_file(&scm_data->dev, &scm_attrs[i]);
+
+			return rc;
+		}
+	}
+	return 0;
+}
+EXPORT_SYMBOL_GPL(scm_sysfs_add);
diff --git a/include/uapi/linux/ocxl-scm.h b/include/uapi/linux/ocxl-scm.h
new file mode 100644
index 000000000000..6dc7e5196da2
--- /dev/null
+++ b/include/uapi/linux/ocxl-scm.h
@@ -0,0 +1,128 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/* Copyright 2017 IBM Corp. */
+#ifndef _UAPI_OCXL_SCM_H
+#define _UAPI_OCXL_SCM_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+enum scm_fwdebug_action {
+	SCM_FWDEBUG_READ_CONTROLLER_MEMORY = 0x01,
+	SCM_FWDEBUG_WRITE_CONTROLLER_MEMORY = 0x02,
+	SCM_FWDEBUG_ENABLE_FUNCTION = 0x03,
+	SCM_FWDEBUG_DISABLE_FUNCTION = 0x04,
+	SCM_FWDEBUG_GET_PEL = 0x05, // Retrieve Persistent Error Log
+};
+
+struct scm_ioctl_buffer_info {
+	__u32	admin_command_buffer_size; // out
+	__u32	near_storage_buffer_size; // out
+};
+
+struct scm_ioctl_fwdebug { // All args are inputs
+	enum scm_fwdebug_action debug_action;
+	__u16 function_code;
+	__u16 buf_size; // Size of optional data buffer
+	__u64 debug_parameter_1;
+	__u64 debug_parameter_2;
+	__u8 *buf; // Pointer to optional in/out data buffer
+};
+
+#define SCM_ERROR_LOG_ACTION_RESET	(1 << (32-32))
+#define SCM_ERROR_LOG_ACTION_CHKFW	(1 << (53-32))
+#define SCM_ERROR_LOG_ACTION_REPLACE	(1 << (54-32))
+#define SCM_ERROR_LOG_ACTION_DUMP	(1 << (55-32))
+
+#define SCM_ERROR_LOG_TYPE_GENERAL		(0x00)
+#define SCM_ERROR_LOG_TYPE_PREDICTIVE_FAILURE	(0x01)
+#define SCM_ERROR_LOG_TYPE_THERMAL_WARNING	(0x02)
+#define SCM_ERROR_LOG_TYPE_DATA_LOSS		(0x03)
+#define SCM_ERROR_LOG_TYPE_HEALTH_PERFORMANCE	(0x04)
+
+struct scm_ioctl_error_log {
+	__u32 log_identifier; // out
+	__u32 program_reference_code; // out
+	__u32 action_flags; //out, recommended course of action
+	__u32 power_on_seconds; // out, Number of seconds the controller has been on when the error occurred
+	__u64 timestamp; // out, relative time since the current IPL
+	__u64 wwid[2]; // out, the NAA formatted WWID associated with the controller
+	char  fw_revision[8+1]; // out, firmware revision as null terminated text
+	__u16 buf_size; /* in/out, buffer size provided/required.
+			 * If required is greater than provided, the buffer
+			 * will be truncated to the amount provided. If its
+			 * less, then only the required bytes will be populated.
+			 * If it is 0, then there are no more error log entries.
+			 */
+	__u8  error_log_type;
+	__u8  reserved1;
+	__u32 reserved2;
+	__u64 reserved3[2];
+	__u8 *buf; // pointer to output buffer
+};
+
+struct scm_ioctl_controller_dump_data {
+	__u8 *buf; // pointer to output buffer
+	__u16 buf_size; /* in/out, buffer size provided/required.
+			 * If required is greater than provided, the buffer
+			 * will be truncated to the amount provided. If its
+			 * less, then only the required bytes will be populated.
+			 * If it is 0, then there is no more dump data available.
+			 */
+	__u32 offset; // in, Offset within the dump
+	__u64 reserved[8];
+};
+
+struct scm_ioctl_controller_stats {
+	__u32 reset_count;
+	__u32 reset_uptime; // seconds
+	__u32 power_on_uptime; // seconds
+	__u64 host_load_count;
+	__u64 host_store_count;
+	__u64 media_read_count;
+	__u64 media_write_count;
+	__u64 cache_hit_count;
+	__u64 cache_miss_count;
+	__u64 media_read_latency; // nanoseconds
+	__u64 media_write_latency; // nanoseconds
+	__u64 cache_read_latency; // nanoseconds
+	__u64 cache_write_latency; // nanoseconds
+};
+
+struct scm_ioctl_mmio {
+	__u64 address; // Offset in global MMIO space
+	__u64 val; // value to write/was read
+};
+
+struct scm_ioctl_eventfd {
+	__s32 eventfd;
+	__u32 reserved;
+};
+
+#ifndef BIT_ULL
+#define BIT_ULL(nr)	(1ULL << (nr))
+#endif
+
+#define SCM_IOCTL_EVENT_CONTROLLER_DUMP_AVAILABLE	BIT_ULL(0)
+#define SCM_IOCTL_EVENT_ERROR_LOG_AVAILABLE		BIT_ULL(1)
+#define SCM_IOCTL_EVENT_HARDWARE_FATAL			BIT_ULL(2)
+#define SCM_IOCTL_EVENT_FIRMWARE_FATAL			BIT_ULL(3)
+
+/* ioctl numbers */
+#define SCM_MAGIC 0x5C
+/* SCM devices */
+#define SCM_IOCTL_BUFFER_INFO	_IOR(SCM_MAGIC, 0x00, struct scm_ioctl_buffer_info)
+#define SCM_IOCTL_ERROR_LOG	_IOWR(SCM_MAGIC, 0x01, struct scm_ioctl_error_log)
+#define SCM_IOCTL_CONTROLLER_DUMP _IO(SCM_MAGIC, 0x02)
+#define SCM_IOCTL_CONTROLLER_DUMP_DATA _IOWR(SCM_MAGIC, 0x03, struct scm_ioctl_controller_dump_data)
+#define SCM_IOCTL_CONTROLLER_DUMP_COMPLETE _IO(SCM_MAGIC, 0x04)
+#define SCM_IOCTL_CONTROLLER_STATS _IO(SCM_MAGIC, 0x05)
+#define SCM_IOCTL_EVENTFD	_IOW(SCM_MAGIC, 0x06, struct scm_ioctl_eventfd)
+#define SCM_IOCTL_EVENT_CHECK	_IOR(SCM_MAGIC, 0x07, __u64)
+#define SCM_IOCTL_REQUEST_HEALTH _IO(SCM_MAGIC, 0x08)
+
+#define SCM_IOCTL_FWDEBUG	_IOWR(SCM_MAGIC, 0xf0, struct scm_ioctl_fwdebug)
+#define SCM_IOCTL_MMIO_WRITE	_IOW(SCM_MAGIC, 0xf1, struct scm_ioctl_mmio)
+#define SCM_IOCTL_MMIO_READ	_IOWR(SCM_MAGIC, 0xf2, struct scm_ioctl_mmio)
+#define SCM_IOCTL_SHUTDOWN	_IO(SCM_MAGIC, 0xf3)
+
+#endif /* _UAPI_OCXL_SCM_H */
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
index b39827dbd071..376500f4e3a2 100644
--- a/mm/memory_hotplug.c
+++ b/mm/memory_hotplug.c
@@ -279,7 +279,7 @@ static int check_pfn_span(unsigned long pfn, unsigned long nr_pages,
 }
 
 int check_hotplug_memory_addressable(unsigned long pfn,
-					    unsigned long nr_pages)
+				     unsigned long nr_pages)
 {
 	const u64 max_addr = PFN_PHYS(pfn + nr_pages) - 1;
 
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 09/10] powerpc: Enable OpenCAPI Storage Class Memory driver on bare metal
  2019-10-25  4:46 [PATCH 00/10] Add support for OpenCAPI SCM devices Alastair D'Silva
                   ` (7 preceding siblings ...)
  2019-10-25  4:47 ` [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory Alastair D'Silva
@ 2019-10-25  4:47 ` Alastair D'Silva
  2019-10-28  2:43   ` Oliver O'Halloran
  2019-11-08  7:10   ` Frederic Barrat
  2019-10-25  4:47 ` [PATCH 10/10] ocxl: Conditionally bind SCM devices to the generic OCXL driver Alastair D'Silva
  9 siblings, 2 replies; 45+ messages in thread
From: Alastair D'Silva @ 2019-10-25  4:47 UTC (permalink / raw)
  To: alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Frederic Barrat, Andrew Donnellan, Arnd Bergmann,
	Greg Kroah-Hartman, Dan Williams, Vishal Verma, Dave Jiang,
	Keith Busch, Ira Weiny, Anton Blanchard, Geert Uytterhoeven,
	Krzysztof Kozlowski, Madhavan Srinivasan, Thomas Gleixner,
	Anju T Sudhakar, Cédric Le Goater, Vasant Hegde,
	Hari Bathini, Mahesh Salgaonkar, Greg Kurz, Andrew Morton,
	David Hildenbrand, Oscar Salvador, Michal Hocko, Pavel Tatashin,
	Wei Yang, Qian Cai, linuxppc-dev, linux-kernel, linux-nvdimm,
	linux-mm

From: Alastair D'Silva <alastair@d-silva.org>

Enable OpenCAPI Storage Class Memory driver on bare metal

Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
---
 arch/powerpc/configs/powernv_defconfig | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/powerpc/configs/powernv_defconfig b/arch/powerpc/configs/powernv_defconfig
index 6658cceb928c..45c0eff94964 100644
--- a/arch/powerpc/configs/powernv_defconfig
+++ b/arch/powerpc/configs/powernv_defconfig
@@ -352,3 +352,7 @@ CONFIG_KVM_BOOK3S_64=m
 CONFIG_KVM_BOOK3S_64_HV=m
 CONFIG_VHOST_NET=m
 CONFIG_PRINTK_TIME=y
+CONFIG_OCXL_SCM=m
+CONFIG_DEV_DAX=y
+CONFIG_DEV_DAX_PMEM=y
+CONFIG_FS_DAX=y
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 45+ messages in thread

* [PATCH 10/10] ocxl: Conditionally bind SCM devices to the generic OCXL driver
  2019-10-25  4:46 [PATCH 00/10] Add support for OpenCAPI SCM devices Alastair D'Silva
                   ` (8 preceding siblings ...)
  2019-10-25  4:47 ` [PATCH 09/10] powerpc: Enable OpenCAPI Storage Class Memory driver on bare metal Alastair D'Silva
@ 2019-10-25  4:47 ` Alastair D'Silva
  2019-10-26  6:43   ` Christoph Hellwig
                     ` (2 more replies)
  9 siblings, 3 replies; 45+ messages in thread
From: Alastair D'Silva @ 2019-10-25  4:47 UTC (permalink / raw)
  To: alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Frederic Barrat, Andrew Donnellan, Arnd Bergmann,
	Greg Kroah-Hartman, Dan Williams, Vishal Verma, Dave Jiang,
	Keith Busch, Ira Weiny, Geert Uytterhoeven, Anton Blanchard,
	Krzysztof Kozlowski, Vasant Hegde, David Gibson, Hari Bathini,
	Allison Randal, Anju T Sudhakar, Mahesh Salgaonkar,
	Cédric Le Goater, Thomas Gleixner, Greg Kurz,
	Nicholas Piggin, Masahiro Yamada, Alexey Kardashevskiy,
	Andrew Morton, David Hildenbrand, Oscar Salvador, Michal Hocko,
	Pavel Tatashin, Wei Yang, Qian Cai, linuxppc-dev, linux-kernel,
	linux-nvdimm, linux-mm

From: Alastair D'Silva <alastair@d-silva.org>

This patch allows the user to bind OpenCAPI SCM devices to the generic OCXL
driver.

Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
---
 drivers/misc/ocxl/Kconfig | 7 +++++++
 drivers/misc/ocxl/pci.c   | 3 +++
 2 files changed, 10 insertions(+)

diff --git a/drivers/misc/ocxl/Kconfig b/drivers/misc/ocxl/Kconfig
index 1916fa65f2f2..8a683715c97c 100644
--- a/drivers/misc/ocxl/Kconfig
+++ b/drivers/misc/ocxl/Kconfig
@@ -29,3 +29,10 @@ config OCXL
 	  dedicated OpenCAPI link, and don't follow the same protocol.
 
 	  If unsure, say N.
+
+config OCXL_SCM_GENERIC
+	bool "Treat OpenCAPI Storage Class Memory as a generic OpenCAPI device"
+	default n
+	help
+	  Select this option to treat OpenCAPI Storage Class Memory
+	  devices an generic OpenCAPI devices.
diff --git a/drivers/misc/ocxl/pci.c b/drivers/misc/ocxl/pci.c
index cb920aa88d3a..7137055c1883 100644
--- a/drivers/misc/ocxl/pci.c
+++ b/drivers/misc/ocxl/pci.c
@@ -10,6 +10,9 @@
  */
 static const struct pci_device_id ocxl_pci_tbl[] = {
 	{ PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x062B), },
+#ifdef CONFIG_OCXL_SCM_GENERIC
+	{ PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0625), },
+#endif
 	{ }
 };
 MODULE_DEVICE_TABLE(pci, ocxl_pci_tbl);
-- 
2.21.0



^ permalink raw reply related	[flat|nested] 45+ messages in thread

* Re: [PATCH 10/10] ocxl: Conditionally bind SCM devices to the generic OCXL driver
  2019-10-25  4:47 ` [PATCH 10/10] ocxl: Conditionally bind SCM devices to the generic OCXL driver Alastair D'Silva
@ 2019-10-26  6:43   ` Christoph Hellwig
  2019-11-06  3:46   ` Andrew Donnellan
  2019-11-07 18:08   ` Frederic Barrat
  2 siblings, 0 replies; 45+ messages in thread
From: Christoph Hellwig @ 2019-10-26  6:43 UTC (permalink / raw)
  To: Alastair D'Silva
  Cc: alastair, Oscar Salvador, Michal Hocko, Geert Uytterhoeven,
	David Hildenbrand, Alexey Kardashevskiy, Wei Yang, Keith Busch,
	Masahiro Yamada, Paul Mackerras, Ira Weiny, Thomas Gleixner,
	Pavel Tatashin, Dave Jiang, linux-nvdimm, Vishal Verma,
	Krzysztof Kozlowski, Anju T Sudhakar, Hari Bathini,
	Mahesh Salgaonkar, Andrew Donnellan, Arnd Bergmann, Greg Kurz,
	Nicholas Piggin, Qian Cai, Cédric Le Goater, Dan Williams,
	Allison Randal, David Gibson, linux-mm, Greg Kroah-Hartman,
	linux-kernel, Vasant Hegde, Frederic Barrat, Andrew Morton,
	linuxppc-dev

On Fri, Oct 25, 2019 at 03:47:05PM +1100, Alastair D'Silva wrote:
> From: Alastair D'Silva <alastair@d-silva.org>
> 
> This patch allows the user to bind OpenCAPI SCM devices to the generic OCXL
> driver.

This completely misses any explanation of why you'd want that.  The
what is rather obvious from the patch.

> +config OCXL_SCM_GENERIC
> +	bool "Treat OpenCAPI Storage Class Memory as a generic OpenCAPI device"
> +	default n

n is the default default.


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory
  2019-10-25  4:46 ` [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory Alastair D'Silva
@ 2019-10-27 21:24   ` kbuild test robot
  2019-10-27 21:59   ` kbuild test robot
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 45+ messages in thread
From: kbuild test robot @ 2019-10-27 21:24 UTC (permalink / raw)
  To: Alastair D'Silva
  Cc: kbuild-all, alastair, Benjamin Herrenschmidt, Paul Mackerras,
	Michael Ellerman, Frederic Barrat, Andrew Donnellan,
	Arnd Bergmann, Greg Kroah-Hartman, Dan Williams, Vishal Verma,
	Dave Jiang, Keith Busch, Ira Weiny, Geert Uytterhoeven,
	Krzysztof Kozlowski, Anton Blanchard, Allison Randal,
	Cédric Le Goater, David Gibson, Vasant Hegde,
	Thomas Gleixner, Anju T Sudhakar, Hari Bathini,
	Mahesh Salgaonkar, Greg Kurz, Masahiro Yamada,
	Alexey Kardashevskiy, Andrew Morton, David Hildenbrand,
	Oscar Salvador, Michal Hocko, Pavel Tatashin, Wei Yang, Qian Cai,
	linuxppc-dev, linux-kernel, linux-nvdimm, linux-mm

[-- Attachment #1: Type: text/plain, Size: 2494 bytes --]

Hi Alastair,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on char-misc/char-misc-testing]
[also build test WARNING on v5.4-rc5]
[cannot apply to next-20191025]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url:    https://github.com/0day-ci/linux/commits/Alastair-D-Silva/Add-support-for-OpenCAPI-SCM-devices/20191028-043750
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git da80d2e516eb858eb5bcca7fa5f5a13ed86930e4
config: i386-tinyconfig (attached as .config)
compiler: gcc-7 (Debian 7.4.0-14) 7.4.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=i386 

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   In file included from include/linux/mmzone.h:811:0,
                    from include/linux/gfp.h:6,
                    from include/linux/mm.h:10,
                    from include/linux/pid_namespace.h:7,
                    from include/linux/ptrace.h:10,
                    from include/linux/audit.h:13,
                    from security/commoncap.c:6:
>> include/linux/memory_hotplug.h:342:5: warning: "CONFIG_MEMORY_HOTPLUG_SPARSE" is not defined, evaluates to 0 [-Wundef]
    #if CONFIG_MEMORY_HOTPLUG_SPARSE
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
--
   In file included from include/linux/mmzone.h:811:0,
                    from include/linux/gfp.h:6,
                    from include/linux/slab.h:15,
                    from include/linux/crypto.h:19,
                    from arch/x86/kernel/asm-offsets.c:9:
>> include/linux/memory_hotplug.h:342:5: warning: "CONFIG_MEMORY_HOTPLUG_SPARSE" is not defined, evaluates to 0 [-Wundef]
    #if CONFIG_MEMORY_HOTPLUG_SPARSE
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   7 real  4 user  3 sys  112.50% cpu 	make prepare

vim +/CONFIG_MEMORY_HOTPLUG_SPARSE +342 include/linux/memory_hotplug.h

   341	
 > 342	#if CONFIG_MEMORY_HOTPLUG_SPARSE
   343	int check_hotplug_memory_addressable(unsigned long pfn,
   344			unsigned long nr_pages);
   345	#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
   346	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 7151 bytes --]

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory
  2019-10-25  4:46 ` [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory Alastair D'Silva
  2019-10-27 21:24   ` kbuild test robot
@ 2019-10-27 21:59   ` kbuild test robot
  2019-10-28  2:34   ` kbuild test robot
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 45+ messages in thread
From: kbuild test robot @ 2019-10-27 21:59 UTC (permalink / raw)
  To: Alastair D'Silva
  Cc: kbuild-all, alastair, Benjamin Herrenschmidt, Paul Mackerras,
	Michael Ellerman, Frederic Barrat, Andrew Donnellan,
	Arnd Bergmann, Greg Kroah-Hartman, Dan Williams, Vishal Verma,
	Dave Jiang, Keith Busch, Ira Weiny, Geert Uytterhoeven,
	Krzysztof Kozlowski, Anton Blanchard, Allison Randal,
	Cédric Le Goater, David Gibson, Vasant Hegde,
	Thomas Gleixner, Anju T Sudhakar, Hari Bathini,
	Mahesh Salgaonkar, Greg Kurz, Masahiro Yamada,
	Alexey Kardashevskiy, Andrew Morton, David Hildenbrand,
	Oscar Salvador, Michal Hocko, Pavel Tatashin, Wei Yang, Qian Cai,
	linuxppc-dev, linux-kernel, linux-nvdimm, linux-mm

[-- Attachment #1: Type: text/plain, Size: 2368 bytes --]

Hi Alastair,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on char-misc/char-misc-testing]
[cannot apply to v5.4-rc5 next-20191025]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url:    https://github.com/0day-ci/linux/commits/Alastair-D-Silva/Add-support-for-OpenCAPI-SCM-devices/20191028-043750
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git da80d2e516eb858eb5bcca7fa5f5a13ed86930e4
config: mips-allmodconfig (attached as .config)
compiler: mips-linux-gcc (GCC) 7.4.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=7.4.0 make.cross ARCH=mips 

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   In file included from include/linux/mmzone.h:811:0,
                    from include/linux/gfp.h:6,
                    from include/linux/xarray.h:14,
                    from include/linux/radix-tree.h:18,
                    from include/linux/idr.h:15,
                    from include/linux/kernfs.h:13,
                    from include/linux/sysfs.h:16,
                    from include/linux/kobject.h:20,
                    from include/linux/of.h:17,
                    from include/linux/clk-provider.h:9,
                    from arch/mips/generic/init.c:8:
>> include/linux/memory_hotplug.h:342:5: error: "CONFIG_MEMORY_HOTPLUG_SPARSE" is not defined, evaluates to 0 [-Werror=undef]
    #if CONFIG_MEMORY_HOTPLUG_SPARSE
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   cc1: all warnings being treated as errors

vim +/CONFIG_MEMORY_HOTPLUG_SPARSE +342 include/linux/memory_hotplug.h

   341	
 > 342	#if CONFIG_MEMORY_HOTPLUG_SPARSE
   343	int check_hotplug_memory_addressable(unsigned long pfn,
   344			unsigned long nr_pages);
   345	#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
   346	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 62045 bytes --]

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory
  2019-10-25  4:47 ` [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory Alastair D'Silva
@ 2019-10-27 22:19   ` kbuild test robot
  2019-10-27 22:53   ` kbuild test robot
                     ` (2 subsequent siblings)
  3 siblings, 0 replies; 45+ messages in thread
From: kbuild test robot @ 2019-10-27 22:19 UTC (permalink / raw)
  To: Alastair D'Silva
  Cc: kbuild-all, alastair, Benjamin Herrenschmidt, Paul Mackerras,
	Michael Ellerman, Frederic Barrat, Andrew Donnellan,
	Arnd Bergmann, Greg Kroah-Hartman, Dan Williams, Vishal Verma,
	Dave Jiang, Keith Busch, Ira Weiny, Anton Blanchard,
	Krzysztof Kozlowski, David Gibson, Cédric Le Goater,
	Thomas Gleixner, Hari Bathini, Mahesh Salgaonkar, Greg Kurz,
	Masahiro Yamada, Alexey Kardashevskiy, Nicholas Piggin,
	Andrew Morton, David Hildenbrand, Oscar Salvador, Michal Hocko,
	Pavel Tatashin, Wei Yang, Qian Cai, linuxppc-dev, linux-kernel,
	linux-nvdimm, linux-mm

[-- Attachment #1: Type: text/plain, Size: 3499 bytes --]

Hi Alastair,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on char-misc/char-misc-testing]
[cannot apply to v5.4-rc5 next-20191025]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url:    https://github.com/0day-ci/linux/commits/Alastair-D-Silva/Add-support-for-OpenCAPI-SCM-devices/20191028-043750
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git da80d2e516eb858eb5bcca7fa5f5a13ed86930e4
config: s390-allmodconfig (attached as .config)
compiler: s390-linux-gcc (GCC) 7.4.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=7.4.0 make.cross ARCH=s390 

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   drivers/misc/ocxl/main.c: In function 'init_ocxl':
>> drivers/misc/ocxl/main.c:12:7: error: 'tlbie_capable' undeclared (first use in this function); did you mean 'iommu_capable'?
     if (!tlbie_capable)
          ^~~~~~~~~~~~~
          iommu_capable
   drivers/misc/ocxl/main.c:12:7: note: each undeclared identifier is reported only once for each function it appears in
--
   drivers/misc/ocxl/core.c: In function 'ocxl_function_open':
>> drivers/misc/ocxl/core.c:546:7: error: implicit declaration of function 'radix_enabled'; did you mean 'zdev_enabled'? [-Werror=implicit-function-declaration]
     if (!radix_enabled()) {
          ^~~~~~~~~~~~~
          zdev_enabled
   cc1: some warnings being treated as errors

vim +12 drivers/misc/ocxl/main.c

5ef3166e8a32d7 Frederic Barrat 2018-01-23   7  
5ef3166e8a32d7 Frederic Barrat 2018-01-23   8  static int __init init_ocxl(void)
5ef3166e8a32d7 Frederic Barrat 2018-01-23   9  {
5ef3166e8a32d7 Frederic Barrat 2018-01-23  10  	int rc = 0;
5ef3166e8a32d7 Frederic Barrat 2018-01-23  11  
2275d7b5754a57 Nicholas Piggin 2019-09-03 @12  	if (!tlbie_capable)
2275d7b5754a57 Nicholas Piggin 2019-09-03  13  		return -EINVAL;
2275d7b5754a57 Nicholas Piggin 2019-09-03  14  
5ef3166e8a32d7 Frederic Barrat 2018-01-23  15  	rc = ocxl_file_init();
5ef3166e8a32d7 Frederic Barrat 2018-01-23  16  	if (rc)
5ef3166e8a32d7 Frederic Barrat 2018-01-23  17  		return rc;
5ef3166e8a32d7 Frederic Barrat 2018-01-23  18  
5ef3166e8a32d7 Frederic Barrat 2018-01-23  19  	rc = pci_register_driver(&ocxl_pci_driver);
5ef3166e8a32d7 Frederic Barrat 2018-01-23  20  	if (rc) {
5ef3166e8a32d7 Frederic Barrat 2018-01-23  21  		ocxl_file_exit();
5ef3166e8a32d7 Frederic Barrat 2018-01-23  22  		return rc;
5ef3166e8a32d7 Frederic Barrat 2018-01-23  23  	}
5ef3166e8a32d7 Frederic Barrat 2018-01-23  24  	return 0;
5ef3166e8a32d7 Frederic Barrat 2018-01-23  25  }
5ef3166e8a32d7 Frederic Barrat 2018-01-23  26  

:::::: The code at line 12 was first introduced by commit
:::::: 2275d7b5754a573ffb2ca9e40bd0546eeb986696 powerpc/64s/radix: introduce options to disable use of the tlbie instruction

:::::: TO: Nicholas Piggin <npiggin@gmail.com>
:::::: CC: Michael Ellerman <mpe@ellerman.id.au>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 56363 bytes --]

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory
  2019-10-25  4:47 ` [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory Alastair D'Silva
  2019-10-27 22:19   ` kbuild test robot
@ 2019-10-27 22:53   ` kbuild test robot
  2019-10-28  1:12   ` [RFC PATCH] nvdimm: scm_get() can be static kbuild test robot
  2019-11-14 13:41   ` [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory Frederic Barrat
  3 siblings, 0 replies; 45+ messages in thread
From: kbuild test robot @ 2019-10-27 22:53 UTC (permalink / raw)
  To: Alastair D'Silva
  Cc: kbuild-all, alastair, Benjamin Herrenschmidt, Paul Mackerras,
	Michael Ellerman, Frederic Barrat, Andrew Donnellan,
	Arnd Bergmann, Greg Kroah-Hartman, Dan Williams, Vishal Verma,
	Dave Jiang, Keith Busch, Ira Weiny, Anton Blanchard,
	Krzysztof Kozlowski, David Gibson, Cédric Le Goater,
	Thomas Gleixner, Hari Bathini, Mahesh Salgaonkar, Greg Kurz,
	Masahiro Yamada, Alexey Kardashevskiy, Nicholas Piggin,
	Andrew Morton, David Hildenbrand, Oscar Salvador, Michal Hocko,
	Pavel Tatashin, Wei Yang, Qian Cai, linuxppc-dev, linux-kernel,
	linux-nvdimm, linux-mm

[-- Attachment #1: Type: text/plain, Size: 8388 bytes --]

Hi Alastair,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on char-misc/char-misc-testing]
[cannot apply to v5.4-rc5 next-20191025]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url:    https://github.com/0day-ci/linux/commits/Alastair-D-Silva/Add-support-for-OpenCAPI-SCM-devices/20191028-043750
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git da80d2e516eb858eb5bcca7fa5f5a13ed86930e4
config: x86_64-allyesconfig (attached as .config)
compiler: gcc-7 (Debian 7.4.0-14) 7.4.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   drivers/nvdimm/ocxl-scm.c: In function 'scm_register_lpc_mem':
>> drivers/nvdimm/ocxl-scm.c:476:16: error: implicit declaration of function 'of_node_to_nid'; did you mean 'zone_to_nid'? [-Werror=implicit-function-declaration]
     target_node = of_node_to_nid(scm_data->pdev->dev.of_node);
                   ^~~~~~~~~~~~~~
                   zone_to_nid
   cc1: some warnings being treated as errors
--
   drivers/misc/ocxl/main.c: In function 'init_ocxl':
>> drivers/misc/ocxl/main.c:12:7: error: 'tlbie_capable' undeclared (first use in this function); did you mean 'ptracer_capable'?
     if (!tlbie_capable)
          ^~~~~~~~~~~~~
          ptracer_capable
   drivers/misc/ocxl/main.c:12:7: note: each undeclared identifier is reported only once for each function it appears in
--
>> drivers/misc/ocxl/config.c:4:10: fatal error: asm/pnv-ocxl.h: No such file or directory
    #include <asm/pnv-ocxl.h>
             ^~~~~~~~~~~~~~~~
   compilation terminated.
--
>> drivers/misc/ocxl/file.c:9:10: fatal error: asm/reg.h: No such file or directory
    #include <asm/reg.h>
             ^~~~~~~~~~~
   compilation terminated.
--
   drivers/misc/ocxl/mmio.c: In function 'ocxl_global_mmio_read32':
>> drivers/misc/ocxl/mmio.c:20:10: error: implicit declaration of function 'readl_be'; did you mean 'readsb'? [-Werror=implicit-function-declaration]
      *val = readl_be((char *)afu->global_mmio_ptr + offset);
             ^~~~~~~~
             readsb
   drivers/misc/ocxl/mmio.c: In function 'ocxl_global_mmio_read64':
>> drivers/misc/ocxl/mmio.c:45:10: error: implicit declaration of function 'readq_be'; did you mean 'readsb'? [-Werror=implicit-function-declaration]
      *val = readq_be((char *)afu->global_mmio_ptr + offset);
             ^~~~~~~~
             readsb
   drivers/misc/ocxl/mmio.c: In function 'ocxl_global_mmio_write32':
>> drivers/misc/ocxl/mmio.c:70:3: error: implicit declaration of function 'writel_be'; did you mean 'writesb'? [-Werror=implicit-function-declaration]
      writel_be(val, (char *)afu->global_mmio_ptr + offset);
      ^~~~~~~~~
      writesb
   drivers/misc/ocxl/mmio.c: In function 'ocxl_global_mmio_write64':
>> drivers/misc/ocxl/mmio.c:96:3: error: implicit declaration of function 'writeq_be'; did you mean 'writesb'? [-Werror=implicit-function-declaration]
      writeq_be(val, (char *)afu->global_mmio_ptr + offset);
      ^~~~~~~~~
      writesb
   cc1: some warnings being treated as errors
--
>> drivers/misc/ocxl/link.c:7:10: fatal error: asm/copro.h: No such file or directory
    #include <asm/copro.h>
             ^~~~~~~~~~~~~
   compilation terminated.
--
   drivers/misc/ocxl/context.c: In function 'ocxl_context_attach':
>> drivers/misc/ocxl/context.c:82:21: error: 'mm_context_t {aka struct <anonymous>}' has no member named 'id'
      pidr = mm->context.id;
                        ^
--
>> drivers/misc/ocxl/afu_irq.c:4:10: fatal error: asm/pnv-ocxl.h: No such file or directory
    #include <asm/pnv-ocxl.h>
             ^~~~~~~~~~~~~~~~
   compilation terminated.
--
   drivers/misc/ocxl/core.c: In function 'ocxl_function_open':
>> drivers/misc/ocxl/core.c:546:7: error: implicit declaration of function 'radix_enabled'; did you mean 'pat_enabled'? [-Werror=implicit-function-declaration]
     if (!radix_enabled()) {
          ^~~~~~~~~~~~~
          pat_enabled
   cc1: some warnings being treated as errors

vim +476 drivers/nvdimm/ocxl-scm.c

   402	
   403	/**
   404	 * scm_register_lpc_mem() - Discover persistent memory on a device and register it with the NVDIMM subsystem
   405	 * @scm_data: The SCM device data
   406	 * Return: 0 on success
   407	 */
   408	static int scm_register_lpc_mem(struct scm_data *scm_data)
   409	{
   410		struct nd_region_desc region_desc;
   411		struct nd_mapping_desc nd_mapping_desc;
   412		struct resource *lpc_mem;
   413		const struct ocxl_afu_config *config;
   414		const struct ocxl_fn_config *fn_config;
   415		int rc;
   416		unsigned long nvdimm_cmd_mask = 0;
   417		unsigned long nvdimm_flags = 0;
   418		int target_node;
   419		char serial[16+1];
   420	
   421		// Set up the reserved metadata area
   422		rc = ocxl_afu_map_lpc_mem(scm_data->ocxl_afu);
   423		if (rc < 0)
   424			return rc;
   425	
   426		lpc_mem = ocxl_afu_lpc_mem(scm_data->ocxl_afu);
   427		if (lpc_mem == NULL)
   428			return -EINVAL;
   429	
   430		config = ocxl_afu_config(scm_data->ocxl_afu);
   431		fn_config = ocxl_function_config(scm_data->ocxl_fn);
   432	
   433		rc = scm_reserve_metadata(scm_data, lpc_mem);
   434		if (rc)
   435			return rc;
   436	
   437		scm_data->bus_desc.attr_groups = scm_pmem_attribute_groups;
   438		scm_data->bus_desc.provider_name = "scm";
   439		scm_data->bus_desc.ndctl = scm_ndctl;
   440		scm_data->bus_desc.module = THIS_MODULE;
   441	
   442		scm_data->nvdimm_bus = nvdimm_bus_register(&scm_data->dev,
   443				       &scm_data->bus_desc);
   444		if (!scm_data->nvdimm_bus)
   445			return -EINVAL;
   446	
   447		scm_data->scm_res.start = (u64)lpc_mem->start + SCM_LABEL_AREA_SIZE;
   448		scm_data->scm_res.end = (u64)lpc_mem->start + config->lpc_mem_size - 1;
   449		scm_data->scm_res.name = "SCM persistent memory";
   450	
   451		set_bit(ND_CMD_GET_CONFIG_SIZE, &nvdimm_cmd_mask);
   452		set_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm_cmd_mask);
   453		set_bit(ND_CMD_SET_CONFIG_DATA, &nvdimm_cmd_mask);
   454		set_bit(ND_CMD_SMART, &nvdimm_cmd_mask);
   455	
   456		set_bit(NDD_ALIASING, &nvdimm_flags);
   457	
   458		snprintf(serial, sizeof(serial), "%llx", fn_config->serial);
   459		nd_mapping_desc.nvdimm = __nvdimm_create(scm_data->nvdimm_bus, scm_data,
   460					 scm_dimm_attribute_groups,
   461					 nvdimm_flags, nvdimm_cmd_mask,
   462					 0, NULL, serial, &sec_ops);
   463		if (!nd_mapping_desc.nvdimm)
   464			return -ENOMEM;
   465	
   466		if (nvdimm_bus_check_dimm_count(scm_data->nvdimm_bus, 1))
   467			return -EINVAL;
   468	
   469		nd_mapping_desc.start = scm_data->scm_res.start;
   470		nd_mapping_desc.size = resource_size(&scm_data->scm_res);
   471		nd_mapping_desc.position = 0;
   472	
   473		scm_data->nd_set.cookie1 = fn_config->serial + 1; // allow for empty serial
   474		scm_data->nd_set.cookie2 = fn_config->serial + 1;
   475	
 > 476		target_node = of_node_to_nid(scm_data->pdev->dev.of_node);
   477	
   478		memset(&region_desc, 0, sizeof(region_desc));
   479		region_desc.res = &scm_data->scm_res;
   480		region_desc.attr_groups = scm_pmem_region_attribute_groups;
   481		region_desc.numa_node = NUMA_NO_NODE;
   482		region_desc.target_node = target_node;
   483		region_desc.num_mappings = 1;
   484		region_desc.mapping = &nd_mapping_desc;
   485		region_desc.nd_set = &scm_data->nd_set;
   486	
   487		set_bit(ND_REGION_PAGEMAP, &region_desc.flags);
   488		/*
   489		 * NB: libnvdimm copies the data from ndr_desc into it's own
   490		 * structures so passing a stack pointer is fine.
   491		 */
   492		scm_data->nd_region = nvdimm_pmem_region_create(scm_data->nvdimm_bus,
   493				      &region_desc);
   494		if (!scm_data->nd_region)
   495			return -EINVAL;
   496	
   497		dev_info(&scm_data->dev,
   498			 "Onlining %lluMB of persistent memory\n",
   499			 nd_mapping_desc.size / SZ_1M);
   500	
   501		return 0;
   502	}
   503	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 70147 bytes --]

^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 02/10] nvdimm: remove prototypes for nonexistent functions
  2019-10-25  4:46 ` [PATCH 02/10] nvdimm: remove prototypes for nonexistent functions Alastair D'Silva
@ 2019-10-27 23:41   ` Andrew Donnellan
  2019-11-07 17:56   ` Frederic Barrat
  2019-11-15  4:30   ` Dan Williams
  2 siblings, 0 replies; 45+ messages in thread
From: Andrew Donnellan @ 2019-10-27 23:41 UTC (permalink / raw)
  To: Alastair D'Silva, alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Frederic Barrat, Arnd Bergmann, Greg Kroah-Hartman, Dan Williams,
	Vishal Verma, Dave Jiang, Keith Busch, Ira Weiny,
	Krzysztof Kozlowski, Geert Uytterhoeven, Anton Blanchard,
	David Gibson, Hari Bathini, Anju T Sudhakar, Thomas Gleixner,
	Cédric Le Goater, Mahesh Salgaonkar, Greg Kurz,
	Masahiro Yamada, Andrew Morton, David Hildenbrand,
	Oscar Salvador, Michal Hocko, Pavel Tatashin, Wei Yang, Qian Cai,
	linuxppc-dev, linux-kernel, linux-nvdimm, linux-mm

On 25/10/19 3:46 pm, Alastair D'Silva wrote:
> From: Alastair D'Silva <alastair@d-silva.org>
> 
> These functions don't exist, so remove the prototypes for them.
> 
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>

Indeed, they do not.

Reviewed-by: Andrew Donnellan <ajd@linux.ibm.com>

> ---
>   drivers/nvdimm/nd-core.h | 4 ----
>   1 file changed, 4 deletions(-)
> 
> diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h
> index 25fa121104d0..9f121a6aeb02 100644
> --- a/drivers/nvdimm/nd-core.h
> +++ b/drivers/nvdimm/nd-core.h
> @@ -124,11 +124,7 @@ void nd_region_create_dax_seed(struct nd_region *nd_region);
>   int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus);
>   void nvdimm_bus_destroy_ndctl(struct nvdimm_bus *nvdimm_bus);
>   void nd_synchronize(void);
> -int nvdimm_bus_register_dimms(struct nvdimm_bus *nvdimm_bus);
> -int nvdimm_bus_register_regions(struct nvdimm_bus *nvdimm_bus);
> -int nvdimm_bus_init_interleave_sets(struct nvdimm_bus *nvdimm_bus);
>   void __nd_device_register(struct device *dev);
> -int nd_match_dimm(struct device *dev, void *data);
>   struct nd_label_id;
>   char *nd_label_gen_id(struct nd_label_id *label_id, u8 *uuid, u32 flags);
>   bool nd_is_uuid_unique(struct device *dev, u8 *uuid);
> 

-- 
Andrew Donnellan              OzLabs, ADL Canberra
ajd@linux.ibm.com             IBM Australia Limited



^ permalink raw reply	[flat|nested] 45+ messages in thread

* [RFC PATCH] nvdimm: scm_get() can be static
  2019-10-25  4:47 ` [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory Alastair D'Silva
  2019-10-27 22:19   ` kbuild test robot
  2019-10-27 22:53   ` kbuild test robot
@ 2019-10-28  1:12   ` kbuild test robot
  2019-11-14 13:41   ` [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory Frederic Barrat
  3 siblings, 0 replies; 45+ messages in thread
From: kbuild test robot @ 2019-10-28  1:12 UTC (permalink / raw)
  To: Alastair D'Silva
  Cc: kbuild-all, alastair, Benjamin Herrenschmidt, Paul Mackerras,
	Michael Ellerman, Frederic Barrat, Andrew Donnellan,
	Arnd Bergmann, Greg Kroah-Hartman, Dan Williams, Vishal Verma,
	Dave Jiang, Keith Busch, Ira Weiny, Anton Blanchard,
	Krzysztof Kozlowski, David Gibson, Cédric Le Goater,
	Thomas Gleixner, Hari Bathini, Mahesh Salgaonkar, Greg Kurz,
	Masahiro Yamada, Alexey Kardashevskiy, Nicholas Piggin,
	Andrew Morton, David Hildenbrand, Oscar Salvador, Michal Hocko,
	Pavel Tatashin, Wei Yang, Qian Cai, linuxppc-dev, linux-kernel,
	linux-nvdimm, linux-mm


Fixes: 0d40f55b9035 ("nvdimm: Add driver for OpenCAPI Storage Class Memory")
Signed-off-by: kbuild test robot <lkp@intel.com>
---
 ocxl-scm.c          |    4 ++--
 ocxl-scm_internal.c |    4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/nvdimm/ocxl-scm.c b/drivers/nvdimm/ocxl-scm.c
index f4e6cc022de8a..c169cb0bc71d4 100644
--- a/drivers/nvdimm/ocxl-scm.c
+++ b/drivers/nvdimm/ocxl-scm.c
@@ -733,7 +733,7 @@ static void scm_put(struct scm_data *scm_data)
 	put_device(&scm_data->dev);
 }
 
-struct scm_data *scm_get(struct scm_data *scm_data)
+static struct scm_data *scm_get(struct scm_data *scm_data)
 {
 	return (get_device(&scm_data->dev) == NULL) ? NULL : scm_data;
 }
@@ -2142,7 +2142,7 @@ static int scm_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
 	return -ENXIO;
 }
 
-struct pci_driver scm_pci_driver = {
+static struct pci_driver scm_pci_driver = {
 	.name = "ocxl-scm",
 	.id_table = scm_pci_tbl,
 	.probe = scm_probe,
diff --git a/drivers/nvdimm/ocxl-scm_internal.c b/drivers/nvdimm/ocxl-scm_internal.c
index e7c247835817b..ee11fb72e1ecd 100644
--- a/drivers/nvdimm/ocxl-scm_internal.c
+++ b/drivers/nvdimm/ocxl-scm_internal.c
@@ -64,8 +64,8 @@ int scm_admin_command_request(struct scm_data *scm_data, u8 op_code)
 	return scm_command_request(scm_data, &scm_data->admin_command, op_code);
 }
 
-int scm_command_response(const struct scm_data *scm_data,
-			 const struct command_metadata *cmd)
+static int scm_command_response(const struct scm_data *scm_data,
+				const struct command_metadata *cmd)
 {
 	u64 val;
 	u16 id;


^ permalink raw reply related	[flat|nested] 45+ messages in thread

* Re: [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory
  2019-10-25  4:46 ` [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory Alastair D'Silva
  2019-10-27 21:24   ` kbuild test robot
  2019-10-27 21:59   ` kbuild test robot
@ 2019-10-28  2:34   ` kbuild test robot
  2019-10-29  1:49   ` Andrew Donnellan
  2019-11-07 17:59   ` Frederic Barrat
  4 siblings, 0 replies; 45+ messages in thread
From: kbuild test robot @ 2019-10-28  2:34 UTC (permalink / raw)
  To: Alastair D'Silva
  Cc: kbuild-all, alastair, Benjamin Herrenschmidt, Paul Mackerras,
	Michael Ellerman, Frederic Barrat, Andrew Donnellan,
	Arnd Bergmann, Greg Kroah-Hartman, Dan Williams, Vishal Verma,
	Dave Jiang, Keith Busch, Ira Weiny, Geert Uytterhoeven,
	Krzysztof Kozlowski, Anton Blanchard, Allison Randal,
	Cédric Le Goater, David Gibson, Vasant Hegde,
	Thomas Gleixner, Anju T Sudhakar, Hari Bathini,
	Mahesh Salgaonkar, Greg Kurz, Masahiro Yamada,
	Alexey Kardashevskiy, Andrew Morton, David Hildenbrand,
	Oscar Salvador, Michal Hocko, Pavel Tatashin, Wei Yang, Qian Cai,
	linuxppc-dev, linux-kernel, linux-nvdimm, linux-mm

Hi Alastair,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on char-misc/char-misc-testing]
[also build test WARNING on v5.4-rc5]
[cannot apply to next-20191025]
[if your patch is applied to the wrong git tree, please drop us a note to help
improve the system. BTW, we also suggest to use '--base' option to specify the
base tree in git format-patch, please see https://stackoverflow.com/a/37406982]

url:    https://github.com/0day-ci/linux/commits/Alastair-D-Silva/Add-support-for-OpenCAPI-SCM-devices/20191028-043750
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git da80d2e516eb858eb5bcca7fa5f5a13ed86930e4
reproduce:
        # apt-get install sparse
        # sparse version: v0.6.1-dirty
        make ARCH=x86_64 allmodconfig
        make C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__'

If you fix the issue, kindly add following tag
Reported-by: kbuild test robot <lkp@intel.com>


sparse warnings: (new ones prefixed by >>)

>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   include/linux/lightnvm.h:461:35: sparse: sparse: invalid access past the end of 'r' (4 8)
   include/linux/lightnvm.h:454:35: sparse: sparse: invalid access past the end of 'r' (4 8)
   include/linux/lightnvm.h:453:35: sparse: sparse: invalid access past the end of 'r' (4 8)
   include/linux/lightnvm.h:452:35: sparse: sparse: invalid access past the end of 'r' (4 8)
   include/linux/lightnvm.h:490:55: sparse: sparse: invalid access past the end of 'l' (4 8)
   include/linux/lightnvm.h:483:55: sparse: sparse: invalid access past the end of 'l' (4 8)
   include/linux/lightnvm.h:482:54: sparse: sparse: invalid access past the end of 'l' (4 8)
   include/linux/lightnvm.h:481:53: sparse: sparse: invalid access past the end of 'l' (4 8)
   drivers/lightnvm/core.c:704:36: sparse: sparse: invalid access past the end of 'ppa' (4 8)
   include/linux/lightnvm.h:461:35: sparse: sparse: invalid access past the end of 'r' (4 8)
   include/linux/lightnvm.h:454:35: sparse: sparse: invalid access past the end of 'r' (4 8)
   include/linux/lightnvm.h:453:35: sparse: sparse: invalid access past the end of 'r' (4 8)
   include/linux/lightnvm.h:452:35: sparse: sparse: invalid access past the end of 'r' (4 8)
   drivers/lightnvm/core.c:929:36: sparse: sparse: invalid access past the end of 'ppa' (4 8)
   drivers/lightnvm/core.c:928:36: sparse: sparse: invalid access past the end of 'ppa' (4 8)
   drivers/lightnvm/core.c:898:35: sparse: sparse: invalid access past the end of 'ppa' (4 8)
   drivers/lightnvm/core.c:897:34: sparse: sparse: invalid access past the end of 'ppa' (4 8)
   include/linux/lightnvm.h:461:35: sparse: sparse: invalid access past the end of 'r' (4 8)
   include/linux/lightnvm.h:454:35: sparse: sparse: invalid access past the end of 'r' (4 8)
   include/linux/lightnvm.h:453:35: sparse: sparse: invalid access past the end of 'r' (4 8)
   include/linux/lightnvm.h:452:35: sparse: sparse: invalid access past the end of 'r' (4 8)
   include/linux/lightnvm.h:461:35: sparse: sparse: invalid access past the end of 'r' (4 8)
   include/linux/lightnvm.h:454:35: sparse: sparse: invalid access past the end of 'r' (4 8)
   include/linux/lightnvm.h:453:35: sparse: sparse: invalid access past the end of 'r' (4 8)
   include/linux/lightnvm.h:452:35: sparse: sparse: invalid access past the end of 'r' (4 8)
   include/linux/lightnvm.h:490:55: sparse: sparse: invalid access past the end of 'l' (4 8)
   include/linux/lightnvm.h:483:55: sparse: sparse: invalid access past the end of 'l' (4 8)
   include/linux/lightnvm.h:482:54: sparse: sparse: invalid access past the end of 'l' (4 8)
   include/linux/lightnvm.h:481:53: sparse: sparse: invalid access past the end of 'l' (4 8)
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   drivers/pnp/support.c:42:14: sparse: sparse: cast to restricted __be32
   drivers/pnp/support.c:42:14: sparse: sparse: cast to restricted __be32
   drivers/pnp/support.c:42:14: sparse: sparse: cast to restricted __be32
   drivers/pnp/support.c:42:14: sparse: sparse: cast to restricted __be32
   drivers/pnp/support.c:42:14: sparse: sparse: cast to restricted __be32
   drivers/pnp/support.c:42:14: sparse: sparse: cast to restricted __be32
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   drivers/acpi/osl.c:373:17: sparse: sparse: cast removes address space '<asn:2>' of expression
   drivers/acpi/osl.c:698:1: sparse: sparse: context imbalance in 'acpi_os_read_memory' - wrong count at exit
   drivers/acpi/osl.c:731:1: sparse: sparse: context imbalance in 'acpi_os_write_memory' - wrong count at exit
   drivers/acpi/osl.c:1594:16: sparse: sparse: context imbalance in 'acpi_os_acquire_lock' - wrong count at exit
   drivers/acpi/osl.c:1605:6: sparse: sparse: context imbalance in 'acpi_os_release_lock' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   drivers/acpi/nvs.c:138:54: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected void volatile [noderef] <asn:2> *addr @@    got n:2> *addr @@
   drivers/acpi/nvs.c:138:54: sparse:    expected void volatile [noderef] <asn:2> *addr
   drivers/acpi/nvs.c:138:54: sparse:    got void *kaddr
   drivers/acpi/nvs.c:141:66: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected void [noderef] <asn:2> *virt @@    got n:2> *virt @@
   drivers/acpi/nvs.c:141:66: sparse:    expected void [noderef] <asn:2> *virt
   drivers/acpi/nvs.c:141:66: sparse:    got void *kaddr
   drivers/acpi/nvs.c:180:38: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected void *kaddr @@    got void [noderef] <asvoid *kaddr @@
   drivers/acpi/nvs.c:180:38: sparse:    expected void *kaddr
   drivers/acpi/nvs.c:180:38: sparse:    got void [noderef] <asn:2> *
   drivers/acpi/nvs.c:182:46: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected void *kaddr @@    got void [noderef] <asvoid *kaddr @@
   drivers/acpi/nvs.c:182:46: sparse:    expected void *kaddr
   drivers/acpi/nvs.c:182:46: sparse:    got void [noderef] <asn:2> *
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   drivers/acpi/sleep.c:538:46: sparse: sparse: restricted suspend_state_t degrades to integer
   drivers/acpi/sleep.c:640:50: sparse: sparse: restricted suspend_state_t degrades to integer
   drivers/acpi/sleep.c:925:13: sparse: sparse: restricted suspend_state_t degrades to integer
   drivers/acpi/sleep.c:925:33: sparse: sparse: restricted suspend_state_t degrades to integer
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   drivers/acpi/bus.c:37:20: sparse: sparse: symbol 'acpi_root' was not declared. Should it be static?
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   drivers/acpi/sysfs.c:446:14: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected void [noderef] <asn:2> *base @@    got n:2> *base @@
   drivers/acpi/sysfs.c:446:14: sparse:    expected void [noderef] <asn:2> *base
   drivers/acpi/sysfs.c:446:14: sparse:    got void *
   drivers/acpi/sysfs.c:449:59: sparse: sparse: incorrect type in argument 4 (different address spaces) @@    expected void const *from @@    got void [noderevoid const *from @@
   drivers/acpi/sysfs.c:449:59: sparse:    expected void const *from
   drivers/acpi/sysfs.c:449:59: sparse:    got void [noderef] <asn:2> *base
   drivers/acpi/sysfs.c:451:30: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected void *logical_address @@    got void [noderef] <asvoid *logical_address @@
   drivers/acpi/sysfs.c:451:30: sparse:    expected void *logical_address
   drivers/acpi/sysfs.c:451:30: sparse:    got void [noderef] <asn:2> *base
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   drivers/acpi/processor_throttling.c:716:14: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got eref] <asn:3> *__vpp_verify @@
   drivers/acpi/processor_throttling.c:716:14: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   drivers/acpi/processor_throttling.c:716:14: sparse:    got unsigned char *
   drivers/acpi/processor_throttling.c:716:14: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got eref] <asn:3> *__vpp_verify @@
   drivers/acpi/processor_throttling.c:716:14: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   drivers/acpi/processor_throttling.c:716:14: sparse:    got unsigned char *
   drivers/acpi/processor_throttling.c:716:14: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got eref] <asn:3> *__vpp_verify @@
   drivers/acpi/processor_throttling.c:716:14: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   drivers/acpi/processor_throttling.c:716:14: sparse:    got unsigned char *
   drivers/acpi/processor_throttling.c:716:14: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got eref] <asn:3> *__vpp_verify @@
   drivers/acpi/processor_throttling.c:716:14: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   drivers/acpi/processor_throttling.c:716:14: sparse:    got unsigned char *
   drivers/acpi/processor_throttling.c:716:14: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got eref] <asn:3> *__vpp_verify @@
   drivers/acpi/processor_throttling.c:716:14: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   drivers/acpi/processor_throttling.c:716:14: sparse:    got unsigned char *
   drivers/acpi/processor_throttling.c:716:14: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got eref] <asn:3> *__vpp_verify @@
   drivers/acpi/processor_throttling.c:716:14: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   drivers/acpi/processor_throttling.c:716:14: sparse:    got unsigned char *
   drivers/acpi/processor_throttling.c:716:14: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got eref] <asn:3> *__vpp_verify @@
   drivers/acpi/processor_throttling.c:716:14: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   drivers/acpi/processor_throttling.c:716:14: sparse:    got unsigned char *
   drivers/acpi/processor_throttling.c:737:14: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got eref] <asn:3> *__vpp_verify @@
   drivers/acpi/processor_throttling.c:737:14: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   drivers/acpi/processor_throttling.c:737:14: sparse:    got unsigned char *
   drivers/acpi/processor_throttling.c:737:14: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got eref] <asn:3> *__vpp_verify @@
   drivers/acpi/processor_throttling.c:737:14: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   drivers/acpi/processor_throttling.c:737:14: sparse:    got unsigned char *
   drivers/acpi/processor_throttling.c:737:14: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got eref] <asn:3> *__vpp_verify @@
   drivers/acpi/processor_throttling.c:737:14: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   drivers/acpi/processor_throttling.c:737:14: sparse:    got unsigned char *
   drivers/acpi/processor_throttling.c:737:14: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got eref] <asn:3> *__vpp_verify @@
   drivers/acpi/processor_throttling.c:737:14: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   drivers/acpi/processor_throttling.c:737:14: sparse:    got unsigned char *
   drivers/acpi/processor_throttling.c:737:14: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got eref] <asn:3> *__vpp_verify @@
   drivers/acpi/processor_throttling.c:737:14: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   drivers/acpi/processor_throttling.c:737:14: sparse:    got unsigned char *
   drivers/acpi/processor_throttling.c:737:14: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got eref] <asn:3> *__vpp_verify @@
   drivers/acpi/processor_throttling.c:737:14: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   drivers/acpi/processor_throttling.c:737:14: sparse:    got unsigned char *
   drivers/acpi/processor_throttling.c:737:14: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got eref] <asn:3> *__vpp_verify @@
   drivers/acpi/processor_throttling.c:737:14: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   drivers/acpi/processor_throttling.c:737:14: sparse:    got unsigned char *
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   drivers/acpi/acpica/acpredef.h:186:34: sparse: sparse: symbol 'acpi_gbl_predefined_methods' was not declared. Should it be static?
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   include/acpi/acpixf.h:104:1: sparse: sparse: symbol 'acpi_gbl_enable_interpreter_slack' was not declared. Should it be static?
   include/acpi/acpixf.h:113:1: sparse: sparse: symbol 'acpi_gbl_auto_serialize_methods' was not declared. Should it be static?
   include/acpi/acpixf.h:120:1: sparse: sparse: symbol 'acpi_gbl_create_osi_method' was not declared. Should it be static?
   include/acpi/acpixf.h:126:1: sparse: sparse: symbol 'acpi_gbl_use_default_register_widths' was not declared. Should it be static?
   include/acpi/acpixf.h:136:1: sparse: sparse: symbol 'acpi_gbl_enable_table_validation' was not declared. Should it be static?
   include/acpi/acpixf.h:141:1: sparse: sparse: symbol 'acpi_gbl_enable_aml_debug_object' was not declared. Should it be static?
   include/acpi/acpixf.h:149:1: sparse: sparse: symbol 'acpi_gbl_copy_dsdt_locally' was not declared. Should it be static?
   include/acpi/acpixf.h:157:1: sparse: sparse: symbol 'acpi_gbl_do_not_use_xsdt' was not declared. Should it be static?
   include/acpi/acpixf.h:167:1: sparse: sparse: symbol 'acpi_gbl_use32_bit_fadt_addresses' was not declared. Should it be static?
   include/acpi/acpixf.h:176:1: sparse: sparse: symbol 'acpi_gbl_use32_bit_facs_addresses' was not declared. Should it be static?
   include/acpi/acpixf.h:184:1: sparse: sparse: symbol 'acpi_gbl_truncate_io_addresses' was not declared. Should it be static?
   include/acpi/acpixf.h:190:1: sparse: sparse: symbol 'acpi_gbl_disable_auto_repair' was not declared. Should it be static?
   include/acpi/acpixf.h:196:1: sparse: sparse: symbol 'acpi_gbl_disable_ssdt_table_install' was not declared. Should it be static?
   include/acpi/acpixf.h:201:1: sparse: sparse: symbol 'acpi_gbl_runtime_namespace_override' was not declared. Should it be static?
   include/acpi/acpixf.h:207:1: sparse: sparse: symbol 'acpi_gbl_osi_data' was not declared. Should it be static?
   include/acpi/acpixf.h:214:1: sparse: sparse: symbol 'acpi_gbl_reduced_hardware' was not declared. Should it be static?
   include/acpi/acpixf.h:221:1: sparse: sparse: symbol 'acpi_gbl_max_loop_iterations' was not declared. Should it be static?
   include/acpi/acpixf.h:231:1: sparse: sparse: symbol 'acpi_gbl_ignore_package_resolution_errors' was not declared. Should it be static?
   include/acpi/acpixf.h:237:1: sparse: sparse: symbol 'acpi_gbl_trace_flags' was not declared. Should it be static?
   include/acpi/acpixf.h:238:1: sparse: sparse: symbol 'acpi_gbl_trace_method_name' was not declared. Should it be static?
   include/acpi/acpixf.h:239:1: sparse: sparse: symbol 'acpi_gbl_trace_dbg_level' was not declared. Should it be static?
   include/acpi/acpixf.h:240:1: sparse: sparse: symbol 'acpi_gbl_trace_dbg_layer' was not declared. Should it be static?
   include/acpi/acpixf.h:247:1: sparse: sparse: symbol 'acpi_dbg_level' was not declared. Should it be static?
   include/acpi/acpixf.h:248:1: sparse: sparse: symbol 'acpi_dbg_layer' was not declared. Should it be static?
   include/acpi/acpixf.h:252:1: sparse: sparse: symbol 'acpi_gbl_display_debug_timer' was not declared. Should it be static?
   drivers/acpi/acpica/acglobal.h:27:1: sparse: sparse: symbol 'acpi_gbl_dsdt_index' was not declared. Should it be static?
   drivers/acpi/acpica/acglobal.h:28:1: sparse: sparse: symbol 'acpi_gbl_facs_index' was not declared. Should it be static?
   drivers/acpi/acpica/acglobal.h:29:1: sparse: sparse: symbol 'acpi_gbl_xfacs_index' was not declared. Should it be static?
   drivers/acpi/acpica/acglobal.h:30:1: sparse: sparse: symbol 'acpi_gbl_fadt_index' was not declared. Should it be static?
   drivers/acpi/acpica/acglobal.h:114:1: sparse: sparse: symbol 'acpi_gbl_early_initialization' was not declared. Should it be static?
   drivers/acpi/acpica/acglobal.h:134:1: sparse: sparse: symbol 'acpi_gbl_namespace_initialized' was not declared. Should it be static?
   drivers/acpi/acpica/acglobal.h:206:1: sparse: sparse: symbol 'acpi_gbl_current_scope' was not declared. Should it be static?
   drivers/acpi/acpica/acglobal.h:210:1: sparse: sparse: symbol 'acpi_gbl_capture_comments' was not declared. Should it be static?
   drivers/acpi/acpica/acglobal.h:211:1: sparse: sparse: symbol 'acpi_gbl_last_list_head' was not declared. Should it be static?
   drivers/acpi/acpica/acglobal.h:267:1: sparse: sparse: symbol 'acpi_gbl_db_output_flags' was not declared. Should it be static?
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   drivers/acpi/acpica/exfield.c:25:10: sparse: sparse: symbol 'acpi_protocol_lengths' was not declared. Should it be static?
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   drivers/tty/vt/vt.c:232:5: sparse: sparse: symbol 'console_blank_hook' was not declared. Should it be static?
   drivers/tty/vt/vt.c:2875:19: sparse: sparse: symbol 'console_driver' was not declared. Should it be static?
   drivers/tty/vt/vt.c:2916:13: sparse: sparse: context imbalance in 'vt_console_print' - wrong count at exit
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/power/main.c:130:34: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:130:38: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:131:38: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:132:62: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:130:55: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:156:38: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:156:46: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:157:54: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:156:67: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:176:31: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:176:36: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:182:13: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:182:21: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:182:39: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:182:47: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:559:34: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:559:38: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:560:31: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:561:57: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:559:55: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:588:38: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:588:46: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:589:47: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:588:67: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:609:31: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:609:36: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:615:13: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:615:21: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:683:31: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/main.c:683:36: sparse: sparse: restricted suspend_state_t degrades to integer
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/power/suspend.c:90:54: sparse: sparse: incorrect type in argument 2 (different base types) @@    expected int val @@    got restricted suspend_state_t [usertyint val @@
   kernel/power/suspend.c:90:54: sparse:    expected int val
   kernel/power/suspend.c:90:54: sparse:    got restricted suspend_state_t [usertype]
   kernel/power/suspend.c:117:54: sparse: sparse: incorrect type in argument 2 (different base types) @@    expected int val @@    got restricted suspend_state_t [usertyint val @@
   kernel/power/suspend.c:117:54: sparse:    expected int val
   kernel/power/suspend.c:117:54: sparse:    got restricted suspend_state_t [usertype]
   kernel/power/suspend.c:174:19: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:174:47: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:175:19: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:175:51: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:180:26: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:180:65: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:187:42: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:187:51: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:188:38: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:189:51: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:187:72: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:209:34: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:209:73: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:210:27: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:210:59: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:215:34: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:215:69: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:216:21: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:216:42: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:438:33: sparse: sparse: incorrect type in argument 2 (different base types) @@    expected int val @@    got restricted suspend_state_t [usertyint val @@
   kernel/power/suspend.c:438:33: sparse:    expected int val
   kernel/power/suspend.c:438:33: sparse:    got restricted suspend_state_t [usertype] state
   kernel/power/suspend.c:441:33: sparse: sparse: incorrect type in argument 2 (different base types) @@    expected int val @@    got restricted suspend_state_t [usertyint val @@
   kernel/power/suspend.c:441:33: sparse:    expected int val
   kernel/power/suspend.c:441:33: sparse:    got restricted suspend_state_t [usertype] state
   kernel/power/suspend.c:511:53: sparse: sparse: incorrect type in argument 2 (different base types) @@    expected int val @@    got restricted suspend_state_t [usertyint val @@
   kernel/power/suspend.c:511:53: sparse:    expected int val
   kernel/power/suspend.c:511:53: sparse:    got restricted suspend_state_t [usertype] state
   kernel/power/suspend.c:513:53: sparse: sparse: incorrect type in argument 2 (different base types) @@    expected int val @@    got restricted suspend_state_t [usertyint val @@
   kernel/power/suspend.c:513:53: sparse:    expected int val
   kernel/power/suspend.c:513:53: sparse:    got restricted suspend_state_t [usertype] state
   kernel/power/suspend.c:550:52: sparse: sparse: incorrect type in argument 2 (different base types) @@    expected int val @@    got restricted suspend_state_t [usertyint val @@
   kernel/power/suspend.c:550:52: sparse:    expected int val
   kernel/power/suspend.c:550:52: sparse:    got restricted suspend_state_t [usertype] state
   kernel/power/suspend.c:573:9: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:582:52: sparse: sparse: incorrect type in argument 2 (different base types) @@    expected int val @@    got restricted suspend_state_t [usertyint val @@
   kernel/power/suspend.c:582:52: sparse:    expected int val
   kernel/power/suspend.c:582:52: sparse:    got restricted suspend_state_t [usertype] state
   kernel/power/suspend.c:583:9: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:608:13: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:608:22: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:608:39: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:608:48: sparse: sparse: restricted suspend_state_t degrades to integer
   kernel/power/suspend.c:611:9: sparse: sparse: restricted suspend_state_t degrades to integer
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   drivers/base/core.c:63:5: sparse: sparse: context imbalance in 'device_links_read_lock' - wrong count at exit
   include/linux/srcu.h:181:9: sparse: sparse: context imbalance in 'device_links_read_unlock' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   drivers/base/devres.c:1114:9: sparse: sparse: cast removes address space '<asn:3>' of expression
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   drivers/base/power/wakeup.c:1039:13: sparse: sparse: context imbalance in 'wakeup_sources_stats_seq_start' - wrong count at exit
   include/linux/srcu.h:181:9: sparse: sparse: context imbalance in 'wakeup_sources_stats_seq_stop' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   drivers/char/random.c:930:12: sparse: sparse: context imbalance in 'crng_fast_load' - wrong count at exit
   drivers/char/random.c:970:12: sparse: sparse: context imbalance in 'crng_slow_load' - wrong count at exit
   drivers/char/random.c:1309:6: sparse: sparse: context imbalance in 'add_interrupt_randomness' - different lock contexts for basic block
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   lib/radix-tree.c:275:36: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct xa_node *nodes @@    got struct xa_node [nostruct xa_node *nodes @@
   lib/radix-tree.c:275:36: sparse:    expected struct xa_node *nodes
   lib/radix-tree.c:275:36: sparse:    got struct xa_node [noderef] <asn:4> *parent
   lib/radix-tree.c:293:29: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct xa_node [noderef] <asn:4> *parent @@    got deref] <asn:4> *parent @@
   lib/radix-tree.c:293:29: sparse:    expected struct xa_node [noderef] <asn:4> *parent
   lib/radix-tree.c:293:29: sparse:    got struct xa_node *parent
   lib/radix-tree.c:353:38: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct xa_node [noderef] <asn:4> *parent @@    got deref] <asn:4> *parent @@
   lib/radix-tree.c:353:38: sparse:    expected struct xa_node [noderef] <asn:4> *parent
   lib/radix-tree.c:353:38: sparse:    got struct xa_node *nodes
   lib/radix-tree.c:455:54: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct xa_node [noderef] <asn:4> *parent @@    got deref] <asn:4> *parent @@
   lib/radix-tree.c:455:54: sparse:    expected struct xa_node [noderef] <asn:4> *parent
   lib/radix-tree.c:455:54: sparse:    got struct xa_node *node
   lib/radix-tree.c:567:24: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct xa_node *parent @@    got struct xa_node [nostruct xa_node *parent @@
   lib/radix-tree.c:567:24: sparse:    expected struct xa_node *parent
   lib/radix-tree.c:567:24: sparse:    got struct xa_node [noderef] <asn:4> *parent
   lib/radix-tree.c:681:31: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct xa_node *[assigned] child @@    got struct struct xa_node *[assigned] child @@
   lib/radix-tree.c:681:31: sparse:    expected struct xa_node *[assigned] child
   lib/radix-tree.c:681:31: sparse:    got struct xa_node [noderef] <asn:4> *parent
   lib/radix-tree.c:955:22: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct xa_node *node @@    got struct xa_node [nostruct xa_node *node @@
   lib/radix-tree.c:955:22: sparse:    expected struct xa_node *node
   lib/radix-tree.c:955:22: sparse:    got struct xa_node [noderef] <asn:4> *parent
   lib/radix-tree.c:1015:22: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct xa_node *node @@    got struct xa_node [nostruct xa_node *node @@
   lib/radix-tree.c:1015:22: sparse:    expected struct xa_node *node
   lib/radix-tree.c:1015:22: sparse:    got struct xa_node [noderef] <asn:4> *parent
   lib/radix-tree.c:1536:38: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct xa_node *[assigned] node @@    got struct struct xa_node *[assigned] node @@
   lib/radix-tree.c:1536:38: sparse:    expected struct xa_node *[assigned] node
   lib/radix-tree.c:1536:38: sparse:    got struct xa_node [noderef] <asn:4> *parent
   lib/radix-tree.c:1596:28: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct xa_node *nodes @@    got struct xa_node [nostruct xa_node *nodes @@
   lib/radix-tree.c:1596:28: sparse:    expected struct xa_node *nodes
   lib/radix-tree.c:1596:28: sparse:    got struct xa_node [noderef] <asn:4> *parent
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   lib/string.c:1093:6: sparse: sparse: symbol 'fortify_panic' redeclared with different type (originally declared at include/linux/string.h:265) - different modifiers
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   lib/vsprintf.c:1865:23: sparse: sparse: incorrect type in assignment (different base types) @@    expected unsigned long [assigned] flags @@    got resunsigned long [assigned] flags @@
   lib/vsprintf.c:1865:23: sparse:    expected unsigned long [assigned] flags
   lib/vsprintf.c:1865:23: sparse:    got restricted gfp_t [usertype]
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   lib/bitmap.c:62:6: sparse: sparse: symbol '__bitmap_or_equal' redeclared with different type (originally declared at include/linux/bitmap.h:123) - different modifiers
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   lib/generic-radix-tree.c:56:35: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected struct genradix_root *r @@    got struct genradix_rostruct genradix_root *r @@
   lib/generic-radix-tree.c:56:35: sparse:    expected struct genradix_root *r
   lib/generic-radix-tree.c:56:35: sparse:    got struct genradix_root [noderef] <asn:4> *__val
   lib/generic-radix-tree.c:107:35: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected struct genradix_root *v @@    got struct genradix_rostruct genradix_root *v @@
   lib/generic-radix-tree.c:107:35: sparse:    expected struct genradix_root *v
   lib/generic-radix-tree.c:107:35: sparse:    got struct genradix_root [noderef] <asn:4> *__val
   lib/generic-radix-tree.c:131:26: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected struct genradix_root [noderef] <asn:4> *__old @@    got ot [noderef] <asn:4> *__old @@
   lib/generic-radix-tree.c:131:26: sparse:    expected struct genradix_root [noderef] <asn:4> *__old
   lib/generic-radix-tree.c:131:26: sparse:    got struct genradix_root *r
   lib/generic-radix-tree.c:131:26: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected struct genradix_root [noderef] <asn:4> *__new @@    got genradix_root [noderef] <asn:4> *__new @@
   lib/generic-radix-tree.c:131:26: sparse:    expected struct genradix_root [noderef] <asn:4> *__new
   lib/generic-radix-tree.c:131:26: sparse:    got struct genradix_root *[assigned] new_root
   lib/generic-radix-tree.c:131:24: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct genradix_root *v @@    got struct genradix_root [noderefstruct genradix_root *v @@
   lib/generic-radix-tree.c:131:24: sparse:    expected struct genradix_root *v
   lib/generic-radix-tree.c:131:24: sparse:    got struct genradix_root [noderef] <asn:4> *[assigned] __ret
   lib/generic-radix-tree.c:170:11: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct genradix_root *r @@    got struct genradix_rostruct genradix_root *r @@
   lib/generic-radix-tree.c:170:11: sparse:    expected struct genradix_root *r
   lib/generic-radix-tree.c:170:11: sparse:    got struct genradix_root [noderef] <asn:4> *__val
   lib/generic-radix-tree.c:232:35: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected struct genradix_root *r @@    got struct genradix_root [noderefstruct genradix_root *r @@
   lib/generic-radix-tree.c:232:35: sparse:    expected struct genradix_root *r
   lib/generic-radix-tree.c:232:35: sparse:    got struct genradix_root [noderef] <asn:4> *[assigned] __ret
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/notify/inotify/inotify_user.c:544:51: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct fsnotify_mark_connector *conn @@    got struct fsnotify_mastruct fsnotify_mark_connector *conn @@
   fs/notify/inotify/inotify_user.c:544:51: sparse:    expected struct fsnotify_mark_connector *conn
   fs/notify/inotify/inotify_user.c:544:51: sparse:    got struct fsnotify_mark_connector [noderef] <asn:4> *i_fsnotify_marks
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/printk/printk.c:421:1: sparse: sparse: symbol 'log_wait' was not declared. Should it be static?
   kernel/printk/printk.c:2950:23: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected int [noderef] <asn:3> *__p @@    got :3> *__p @@
   kernel/printk/printk.c:2950:23: sparse:    expected int [noderef] <asn:3> *__p
   kernel/printk/printk.c:2950:23: sparse:    got int *
   kernel/printk/printk.c:2950:23: sparse: sparse: dereference of noderef expression
   kernel/printk/printk.c:2950:23: sparse: sparse: dereference of noderef expression
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/lib/iomem.c:39:23: sparse: sparse: cast removes address space '<asn:2>' of expression
   arch/x86/lib/iomem.c:57:19: sparse: sparse: cast removes address space '<asn:2>' of expression
   arch/x86/lib/iomem.c:67:9: sparse: sparse: cast removes address space '<asn:2>' of expression
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   security/commoncap.c:439:31: sparse: sparse: incorrect type in assignment (different base types) @@    expected restricted __le32 [usertype] magic @@    got e] magic @@
   security/commoncap.c:439:31: sparse:    expected restricted __le32 [usertype] magic
   security/commoncap.c:439:31: sparse:    got int
   security/commoncap.c:440:33: sparse: sparse: incorrect type in assignment (different base types) @@    expected restricted __le32 [usertype] nsmagic @@    got icted __le32 [usertype] nsmagic @@
   security/commoncap.c:440:33: sparse:    expected restricted __le32 [usertype] nsmagic
   security/commoncap.c:440:33: sparse:    got unsigned int [usertype]
   security/commoncap.c:441:29: sparse: sparse: restricted __le32 degrades to integer
   security/commoncap.c:442:39: sparse: sparse: invalid assignment: |=
   security/commoncap.c:442:39: sparse:    left side has type restricted __le32
   security/commoncap.c:442:39: sparse:    right side has type int
   security/commoncap.c:444:42: sparse: sparse: cast from restricted __le32
   security/commoncap.c:1260:41: sparse: sparse: dubious: !x | y
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/sched/core.c:4049:17: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/sched/core.c:4049:17: sparse:    struct task_struct [noderef] <asn:4> *
   kernel/sched/core.c:4049:17: sparse:    struct task_struct *
   kernel/sched/core.c:6047:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/sched/core.c:6047:9: sparse:    struct task_struct [noderef] <asn:4> *
   kernel/sched/core.c:6047:9: sparse:    struct task_struct *
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/sched/cputime.c:316:17: sparse: sparse: context imbalance in 'thread_group_cputime' - different lock contexts for basic block
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/sched/fair.c:5073:35: sparse: sparse: marked inline, but without a definition
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/sched/membarrier.c:108:21: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/sched/membarrier.c:108:21: sparse:    struct task_struct [noderef] <asn:4> *
   kernel/sched/membarrier.c:108:21: sparse:    struct task_struct *
   kernel/sched/membarrier.c:177:21: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/sched/membarrier.c:177:21: sparse:    struct task_struct [noderef] <asn:4> *
   kernel/sched/membarrier.c:177:21: sparse:    struct task_struct *
   kernel/sched/membarrier.c:243:21: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/sched/membarrier.c:243:21: sparse:    struct task_struct [noderef] <asn:4> *
   kernel/sched/membarrier.c:243:21: sparse:    struct task_struct *
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/time/posix-timers.c:588:24: sparse: sparse: context imbalance in '__lock_timer' - different lock contexts for basic block
   include/linux/rcupdate.h:651:9: sparse: sparse: context imbalance in 'timer_wait_running' - unexpected unlock
   kernel/time/posix-timers.c:876:12: sparse: sparse: context imbalance in 'do_timer_settime' - different lock contexts for basic block
   kernel/time/posix-timers.c:982:1: sparse: sparse: context imbalance in '__se_sys_timer_delete' - different lock contexts for basic block
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   drivers/firmware/dmi_scan.c:635:27: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected char [noderef] <asn:2> *p @@    got n:2> *p @@
   drivers/firmware/dmi_scan.c:635:27: sparse:    expected char [noderef] <asn:2> *p
   drivers/firmware/dmi_scan.c:635:27: sparse:    got void *
   drivers/firmware/dmi_scan.c:639:41: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected void *addr @@    got char [noderef] <asvoid *addr @@
   drivers/firmware/dmi_scan.c:639:41: sparse:    expected void *addr
   drivers/firmware/dmi_scan.c:639:41: sparse:    got char [noderef] <asn:2> *p
   drivers/firmware/dmi_scan.c:653:19: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected char [noderef] <asn:2> *p @@    got n:2> *p @@
   drivers/firmware/dmi_scan.c:653:19: sparse:    expected char [noderef] <asn:2> *p
   drivers/firmware/dmi_scan.c:653:19: sparse:    got void *
   drivers/firmware/dmi_scan.c:657:33: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected void *addr @@    got char [noderef] <asvoid *addr @@
   drivers/firmware/dmi_scan.c:657:33: sparse:    expected void *addr
   drivers/firmware/dmi_scan.c:657:33: sparse:    got char [noderef] <asn:2> *p
   drivers/firmware/dmi_scan.c:664:19: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected char [noderef] <asn:2> *p @@    got n:2> *p @@
   drivers/firmware/dmi_scan.c:664:19: sparse:    expected char [noderef] <asn:2> *p
   drivers/firmware/dmi_scan.c:664:19: sparse:    got void *
   drivers/firmware/dmi_scan.c:677:49: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected void *addr @@    got char [noderef] <asvoid *addr @@
   drivers/firmware/dmi_scan.c:677:49: sparse:    expected void *addr
   drivers/firmware/dmi_scan.c:677:49: sparse:    got char [noderef] <asn:2> *p
   drivers/firmware/dmi_scan.c:695:49: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected void *addr @@    got char [noderef] <asvoid *addr @@
   drivers/firmware/dmi_scan.c:695:49: sparse:    expected void *addr
   drivers/firmware/dmi_scan.c:695:49: sparse:    got char [noderef] <asn:2> *p
   drivers/firmware/dmi_scan.c:700:33: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected void *addr @@    got char [noderef] <asvoid *addr @@
   drivers/firmware/dmi_scan.c:700:33: sparse:    expected void *addr
   drivers/firmware/dmi_scan.c:700:33: sparse:    got char [noderef] <asn:2> *p
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   block/blk-ioc.c:108:28: sparse: sparse: context imbalance in 'ioc_release_fn' - different lock contexts for basic block
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   init/do_mounts.c:389:30: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected char const [noderef] <asn:1> *dev_name @@    got n:1> *dev_name @@
   init/do_mounts.c:389:30: sparse:    expected char const [noderef] <asn:1> *dev_name
   init/do_mounts.c:389:30: sparse:    got char *name
   init/do_mounts.c:389:36: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected char const [noderef] <asn:1> *dir_name @@    got n:1> *dir_name @@
   init/do_mounts.c:389:36: sparse:    expected char const [noderef] <asn:1> *dir_name
   init/do_mounts.c:389:36: sparse:    got char *
   init/do_mounts.c:389:45: sparse: sparse: incorrect type in argument 3 (different address spaces) @@    expected char const [noderef] <asn:1> *type @@    got n:1> *type @@
   init/do_mounts.c:389:45: sparse:    expected char const [noderef] <asn:1> *type
   init/do_mounts.c:389:45: sparse:    got char *fs
   init/do_mounts.c:389:56: sparse: sparse: incorrect type in argument 5 (different address spaces) @@    expected void [noderef] <asn:1> *data @@    got n:1> *data @@
   init/do_mounts.c:389:56: sparse:    expected void [noderef] <asn:1> *data
   init/do_mounts.c:389:56: sparse:    got void *data
   init/do_mounts.c:393:20: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected char const [noderef] <asn:1> *filename @@    got n:1> *filename @@
   init/do_mounts.c:393:20: sparse:    expected char const [noderef] <asn:1> *filename
   init/do_mounts.c:393:20: sparse:    got char *
   init/do_mounts.h:19:21: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected char const [noderef] <asn:1> *pathname @@    got n:1> *pathname @@
   init/do_mounts.h:19:21: sparse:    expected char const [noderef] <asn:1> *pathname
   init/do_mounts.h:19:21: sparse:    got char *name
   init/do_mounts.h:20:27: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected char const [noderef] <asn:1> *filename @@    got n:1> *filename @@
   init/do_mounts.h:20:27: sparse:    expected char const [noderef] <asn:1> *filename
   init/do_mounts.h:20:27: sparse:    got char *name
   init/do_mounts.c:625:20: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected char const [noderef] <asn:1> *dev_name @@    got n:1> *dev_name @@
   init/do_mounts.c:625:20: sparse:    expected char const [noderef] <asn:1> *dev_name
   init/do_mounts.c:625:20: sparse:    got char *
   init/do_mounts.c:625:25: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected char const [noderef] <asn:1> *dir_name @@    got n:1> *dir_name @@
   init/do_mounts.c:625:25: sparse:    expected char const [noderef] <asn:1> *dir_name
   init/do_mounts.c:625:25: sparse:    got char *
   init/do_mounts.c:626:21: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected char const [noderef] <asn:1> *filename @@    got n:1> *filename @@
   init/do_mounts.c:626:21: sparse:    expected char const [noderef] <asn:1> *filename
   init/do_mounts.c:626:21: sparse:    got char *
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   init/init_task.c:97:28: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected struct task_struct [noderef] <asn:4> *real_parent @@    got  [noderef] <asn:4> *real_parent @@
   init/init_task.c:97:28: sparse:    expected struct task_struct [noderef] <asn:4> *real_parent
   init/init_task.c:97:28: sparse:    got struct task_struct *
   init/init_task.c:98:28: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected struct task_struct [noderef] <asn:4> *parent @@    got  [noderef] <asn:4> *parent @@
   init/init_task.c:98:28: sparse:    expected struct task_struct [noderef] <asn:4> *parent
   init/init_task.c:98:28: sparse:    got struct task_struct *
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/sysctl.c:2048:26: sparse: sparse: non size-preserving pointer to integer cast
   kernel/sysctl.c:2048:26: sparse: sparse: non size-preserving integer to pointer cast
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/ptrace.c:53:22: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/ptrace.c:53:22: sparse:    struct task_struct *
   kernel/ptrace.c:53:22: sparse:    struct task_struct [noderef] <asn:4> *
   kernel/ptrace.c:72:23: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct task_struct [noderef] <asn:4> *parent @@    got  [noderef] <asn:4> *parent @@
   kernel/ptrace.c:72:23: sparse:    expected struct task_struct [noderef] <asn:4> *parent
   kernel/ptrace.c:72:23: sparse:    got struct task_struct *new_parent
   kernel/ptrace.c:73:29: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct cred const [noderef] <asn:4> *ptracer_cred @@    got [noderef] <asn:4> *ptracer_cred @@
   kernel/ptrace.c:73:29: sparse:    expected struct cred const [noderef] <asn:4> *ptracer_cred
   kernel/ptrace.c:73:29: sparse:    got struct cred const *
   kernel/ptrace.c:127:18: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct cred const *old_cred @@    got struct cred const struct cred const *old_cred @@
   kernel/ptrace.c:127:18: sparse:    expected struct cred const *old_cred
   kernel/ptrace.c:127:18: sparse:    got struct cred const [noderef] <asn:4> *ptracer_cred
   kernel/ptrace.c:196:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/ptrace.c:196:9: sparse:    struct task_struct [noderef] <asn:4> *
   kernel/ptrace.c:196:9: sparse:    struct task_struct *
   kernel/ptrace.c:241:44: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/ptrace.c:241:44: sparse:    struct task_struct [noderef] <asn:4> *
   kernel/ptrace.c:241:44: sparse:    struct task_struct *
   kernel/ptrace.c:475:54: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct task_struct *parent @@    got struct task_structstruct task_struct *parent @@
   kernel/ptrace.c:475:54: sparse:    expected struct task_struct *parent
   kernel/ptrace.c:475:54: sparse:    got struct task_struct [noderef] <asn:4> *parent
   kernel/ptrace.c:483:53: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected struct task_struct *new_parent @@    got struct task_structstruct task_struct *new_parent @@
   kernel/ptrace.c:483:53: sparse:    expected struct task_struct *new_parent
   kernel/ptrace.c:483:53: sparse:    got struct task_struct [noderef] <asn:4> *real_parent
   kernel/ptrace.c:531:41: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct task_struct *p1 @@    got struct task_structstruct task_struct *p1 @@
   kernel/ptrace.c:531:41: sparse:    expected struct task_struct *p1
   kernel/ptrace.c:531:41: sparse:    got struct task_struct [noderef] <asn:4> *real_parent
   kernel/ptrace.c:481:38: sparse: sparse: dereference of noderef expression
   kernel/ptrace.c:682:9: sparse: sparse: context imbalance in 'ptrace_getsiginfo' - different lock contexts for basic block
   kernel/ptrace.c:698:9: sparse: sparse: context imbalance in 'ptrace_setsiginfo' - different lock contexts for basic block
   kernel/ptrace.c:854:9: sparse: sparse: context imbalance in 'ptrace_resume' - different lock contexts for basic block
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/user.c:85:19: sparse: sparse: symbol 'uidhash_table' was not declared. Should it be static?
   kernel/user.c:172:17: sparse: sparse: context imbalance in 'free_uid' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/signal.c:1251:29: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected void const volatile [noderef] <asn:1> * @@    got eref] <asn:1> * @@
   kernel/signal.c:1251:29: sparse:    expected void const volatile [noderef] <asn:1> *
   kernel/signal.c:1251:29: sparse:    got unsigned char *
   kernel/signal.c:1370:27: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/signal.c:1370:27: sparse:    struct sighand_struct [noderef] <asn:4> *
   kernel/signal.c:1370:27: sparse:    struct sighand_struct *
   kernel/signal.c:1948:65: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct task_struct *tsk @@    got struct task_structstruct task_struct *tsk @@
   kernel/signal.c:1948:65: sparse:    expected struct task_struct *tsk
   kernel/signal.c:1948:65: sparse:    got struct task_struct [noderef] <asn:4> *parent
   kernel/signal.c:1949:40: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected void const volatile *p @@    got struct cred const [noderef] <asn:4>void const volatile *p @@
   kernel/signal.c:1949:40: sparse:    expected void const volatile *p
   kernel/signal.c:1949:40: sparse:    got struct cred const [noderef] <asn:4> *[noderef] <asn:4> *
   kernel/signal.c:1949:40: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected void const volatile *p @@    got struct cred const [noderef] <asn:4>void const volatile *p @@
   kernel/signal.c:1949:40: sparse:    expected void const volatile *p
   kernel/signal.c:1949:40: sparse:    got struct cred const [noderef] <asn:4> *[noderef] <asn:4> *
   kernel/signal.c:1992:54: sparse: sparse: incorrect type in argument 3 (different address spaces) @@    expected struct task_struct *p @@    got struct task_structstruct task_struct *p @@
   kernel/signal.c:1992:54: sparse:    expected struct task_struct *p
   kernel/signal.c:1992:54: sparse:    got struct task_struct [noderef] <asn:4> *parent
   kernel/signal.c:1993:34: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected struct task_struct *parent @@    got struct task_structstruct task_struct *parent @@
   kernel/signal.c:1993:34: sparse:    expected struct task_struct *parent
   kernel/signal.c:1993:34: sparse:    got struct task_struct [noderef] <asn:4> *parent
   kernel/signal.c:2022:24: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct task_struct *parent @@    got struct task_structstruct task_struct *parent @@
   kernel/signal.c:2022:24: sparse:    expected struct task_struct *parent
   kernel/signal.c:2022:24: sparse:    got struct task_struct [noderef] <asn:4> *parent
   kernel/signal.c:2025:24: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct task_struct *parent @@    got struct task_structstruct task_struct *parent @@
   kernel/signal.c:2025:24: sparse:    expected struct task_struct *parent
   kernel/signal.c:2025:24: sparse:    got struct task_struct [noderef] <asn:4> *real_parent
   include/linux/ptrace.h:99:40: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct task_struct *p1 @@    got struct task_structstruct task_struct *p1 @@
   include/linux/ptrace.h:99:40: sparse:    expected struct task_struct *p1
   include/linux/ptrace.h:99:40: sparse:    got struct task_struct [noderef] <asn:4> *real_parent
   include/linux/ptrace.h:99:60: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected struct task_struct *p2 @@    got struct task_structstruct task_struct *p2 @@
   include/linux/ptrace.h:99:60: sparse:    expected struct task_struct *p2
   include/linux/ptrace.h:99:60: sparse:    got struct task_struct [noderef] <asn:4> *parent
   kernel/signal.c:2506:52: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct task_struct *tsk @@    got struct task_structstruct task_struct *tsk @@
   kernel/signal.c:2506:52: sparse:    expected struct task_struct *tsk
   kernel/signal.c:2506:52: sparse:    got struct task_struct [noderef] <asn:4> *parent
   kernel/signal.c:2508:49: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected void const volatile *p @@    got struct cred const [noderef] <asn:4>void const volatile *p @@
   kernel/signal.c:2508:49: sparse:    expected void const volatile *p
   kernel/signal.c:2508:49: sparse:    got struct cred const [noderef] <asn:4> *[noderef] <asn:4> *
   kernel/signal.c:2508:49: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected void const volatile *p @@    got struct cred const [noderef] <asn:4>void const volatile *p @@
   kernel/signal.c:2508:49: sparse:    expected void const volatile *p
   kernel/signal.c:2508:49: sparse:    got struct cred const [noderef] <asn:4> *[noderef] <asn:4> *
   include/linux/ptrace.h:99:40: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct task_struct *p1 @@    got struct task_structstruct task_struct *p1 @@
   include/linux/ptrace.h:99:40: sparse:    expected struct task_struct *p1
   include/linux/ptrace.h:99:40: sparse:    got struct task_struct [noderef] <asn:4> *real_parent
   include/linux/ptrace.h:99:60: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected struct task_struct *p2 @@    got struct task_structstruct task_struct *p2 @@
   include/linux/ptrace.h:99:60: sparse:    expected struct task_struct *p2
   include/linux/ptrace.h:99:60: sparse:    got struct task_struct [noderef] <asn:4> *parent
   kernel/signal.c:3676:46: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected struct siginfo const [noderef] [usertype] <asn:1> *from @@    got deref] [usertype] <asn:1> *from @@
   kernel/signal.c:3676:46: sparse:    expected struct siginfo const [noderef] [usertype] <asn:1> *from
   kernel/signal.c:3676:46: sparse:    got struct siginfo [usertype] *info
   kernel/signal.c:3736:58: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected struct siginfo [usertype] *info @@    got struct siginfo [nostruct siginfo [usertype] *info @@
   kernel/signal.c:3736:58: sparse:    expected struct siginfo [usertype] *info
   kernel/signal.c:3736:58: sparse:    got struct siginfo [noderef] [usertype] <asn:1> *info
   kernel/signal.c:1289:9: sparse: sparse: context imbalance in 'do_send_sig_info' - different lock contexts for basic block
   include/linux/rcupdate.h:651:9: sparse: sparse: context imbalance in '__lock_task_sighand' - different lock contexts for basic block
   include/linux/rcupdate.h:649:9: sparse: sparse: context imbalance in 'send_sigqueue' - wrong count at exit
   kernel/signal.c:1929:47: sparse: sparse: dereference of noderef expression
   kernel/signal.c:1949:40: sparse: sparse: dereference of noderef expression
   kernel/signal.c:1949:40: sparse: sparse: dereference of noderef expression
   kernel/signal.c:1967:19: sparse: sparse: dereference of noderef expression
   kernel/signal.c:2088:13: sparse: sparse: dereference of noderef expression
   kernel/signal.c:2299:13: sparse: sparse: context imbalance in 'do_signal_stop' - different lock contexts for basic block
   kernel/signal.c:2508:49: sparse: sparse: dereference of noderef expression
   kernel/signal.c:2508:49: sparse: sparse: dereference of noderef expression
   kernel/signal.c:2591:69: sparse: sparse: context imbalance in 'get_signal' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/sys.c:1035:32: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct task_struct *p1 @@    got struct task_structstruct task_struct *p1 @@
   kernel/sys.c:1035:32: sparse:    expected struct task_struct *p1
   kernel/sys.c:1035:32: sparse:    got struct task_struct [noderef] <asn:4> *real_parent
   kernel/sys.c:1862:19: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected struct file [noderef] <asn:4> *__ret @@    got file [noderef] <asn:4> *__ret @@
   kernel/sys.c:1862:19: sparse:    expected struct file [noderef] <asn:4> *__ret
   kernel/sys.c:1862:19: sparse:    got struct file *[assigned] file
   kernel/sys.c:1862:17: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct file *old_exe @@    got struct file [noderef] <asn:4>struct file *old_exe @@
   kernel/sys.c:1862:17: sparse:    expected struct file *old_exe
   kernel/sys.c:1862:17: sparse:    got struct file [noderef] <asn:4> *[assigned] __ret
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/pid.c:375:23: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/pid.c:375:23: sparse:    struct pid [noderef] <asn:4> *
   kernel/pid.c:375:23: sparse:    struct pid *
   kernel/pid.c:434:32: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/pid.c:434:32: sparse:    struct pid [noderef] <asn:4> *
   kernel/pid.c:434:32: sparse:    struct pid *
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/notifier.c:29:20: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
   kernel/notifier.c:29:20: sparse:    expected struct notifier_block **nl
   kernel/notifier.c:29:20: sparse:    got struct notifier_block [noderef] <asn:4> **
   kernel/notifier.c:31:17: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct notifier_block [noderef] <asn:4> *next @@    got ock [noderef] <asn:4> *next @@
   kernel/notifier.c:31:17: sparse:    expected struct notifier_block [noderef] <asn:4> *next
   kernel/notifier.c:31:17: sparse:    got struct notifier_block *
   kernel/notifier.c:32:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/notifier.c:32:9: sparse:    struct notifier_block [noderef] <asn:4> *
   kernel/notifier.c:32:9: sparse:    struct notifier_block *
   kernel/notifier.c:44:20: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
   kernel/notifier.c:44:20: sparse:    expected struct notifier_block **nl
   kernel/notifier.c:44:20: sparse:    got struct notifier_block [noderef] <asn:4> **
   kernel/notifier.c:46:17: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct notifier_block [noderef] <asn:4> *next @@    got ock [noderef] <asn:4> *next @@
   kernel/notifier.c:46:17: sparse:    expected struct notifier_block [noderef] <asn:4> *next
   kernel/notifier.c:46:17: sparse:    got struct notifier_block *
   kernel/notifier.c:47:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/notifier.c:47:9: sparse:    struct notifier_block [noderef] <asn:4> *
   kernel/notifier.c:47:9: sparse:    struct notifier_block *
   kernel/notifier.c:56:25: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/notifier.c:56:25: sparse:    struct notifier_block [noderef] <asn:4> *
   kernel/notifier.c:56:25: sparse:    struct notifier_block *
   kernel/notifier.c:130:40: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
   kernel/notifier.c:152:42: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
   kernel/notifier.c:185:36: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
   kernel/notifier.c:226:49: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
   kernel/notifier.c:229:40: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
   kernel/notifier.c:252:45: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
   kernel/notifier.c:279:51: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
   kernel/notifier.c:282:42: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
   kernel/notifier.c:319:44: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
   kernel/notifier.c:352:41: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
   kernel/notifier.c:369:43: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
   kernel/notifier.c:396:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
   kernel/notifier.c:434:49: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
   kernel/notifier.c:437:40: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
   kernel/notifier.c:464:51: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
   kernel/notifier.c:467:42: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
   kernel/notifier.c:500:36: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct notifier_block **nl @@    got struct notifier_blstruct notifier_block **nl @@
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/cred.c:144:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/cred.c:144:9: sparse:    struct cred *
   kernel/cred.c:144:9: sparse:    struct cred const [noderef] <asn:4> *
   kernel/cred.c:145:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/cred.c:145:9: sparse:    struct cred *
   kernel/cred.c:145:9: sparse:    struct cred const [noderef] <asn:4> *
   kernel/cred.c:161:9: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct atomic_t const [usertype] *v @@    got struct struct atomic_t const [usertype] *v @@
   kernel/cred.c:161:9: sparse:    expected struct atomic_t const [usertype] *v
   kernel/cred.c:161:9: sparse:    got struct atomic_t const [noderef] <asn:4> *
   kernel/cred.c:161:9: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct cred const *cred @@    got struct cred const struct cred const *cred @@
   kernel/cred.c:161:9: sparse:    expected struct cred const *cred
   kernel/cred.c:161:9: sparse:    got struct cred const [noderef] <asn:4> *cred
   kernel/cred.c:165:17: sparse: sparse: cast removes address space '<asn:4>' of expression
   kernel/cred.c:171:17: sparse: sparse: cast removes address space '<asn:4>' of expression
   kernel/cred.c:264:13: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct cred const *old @@    got struct cred const struct cred const *old @@
   kernel/cred.c:264:13: sparse:    expected struct cred const *old
   kernel/cred.c:264:13: sparse:    got struct cred const [noderef] <asn:4> *cred
   kernel/cred.c:345:42: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct cred const *cred @@    got struct cred const struct cred const *cred @@
   kernel/cred.c:345:42: sparse:    expected struct cred const *cred
   kernel/cred.c:345:42: sparse:    got struct cred const [noderef] <asn:4> *cred
   kernel/cred.c:345:30: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct cred const [noderef] <asn:4> *real_cred @@    got [noderef] <asn:4> *real_cred @@
   kernel/cred.c:345:30: sparse:    expected struct cred const [noderef] <asn:4> *real_cred
   kernel/cred.c:345:30: sparse:    got struct cred const *
   kernel/cred.c:346:27: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct cred const *cred @@    got struct cred const struct cred const *cred @@
   kernel/cred.c:346:27: sparse:    expected struct cred const *cred
   kernel/cred.c:346:27: sparse:    got struct cred const [noderef] <asn:4> *cred
   kernel/cred.c:347:41: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct cred const *_cred @@    got struct cred const struct cred const *_cred @@
   kernel/cred.c:347:41: sparse:    expected struct cred const *_cred
   kernel/cred.c:347:41: sparse:    got struct cred const [noderef] <asn:4> *cred
   kernel/cred.c:348:17: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct atomic_t const [usertype] *v @@    got struct struct atomic_t const [usertype] *v @@
   kernel/cred.c:348:17: sparse:    expected struct atomic_t const [usertype] *v
   kernel/cred.c:348:17: sparse:    got struct atomic_t const [noderef] <asn:4> *
   kernel/cred.c:348:17: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct cred const *cred @@    got struct cred const struct cred const *cred @@
   kernel/cred.c:348:17: sparse:    expected struct cred const *cred
   kernel/cred.c:348:17: sparse:    got struct cred const [noderef] <asn:4> *cred
   kernel/cred.c:385:32: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct cred const [noderef] <asn:4> *real_cred @@    got [noderef] <asn:4> *real_cred @@
   kernel/cred.c:385:32: sparse:    expected struct cred const [noderef] <asn:4> *real_cred
   kernel/cred.c:385:32: sparse:    got struct cred const *
   kernel/cred.c:437:38: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected struct cred const *old @@    got struct cred const struct cred const *old @@
   kernel/cred.c:437:38: sparse:    expected struct cred const *old
   kernel/cred.c:437:38: sparse:    got struct cred const [noderef] <asn:4> *real_cred
   kernel/cred.c:443:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/cred.c:443:9: sparse:    struct cred const [noderef] <asn:4> *
   kernel/cred.c:443:9: sparse:    struct cred const *
   kernel/cred.c:543:41: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected struct cred const *old @@    got struct cred const struct cred const *old @@
   kernel/cred.c:543:41: sparse:    expected struct cred const *old
   kernel/cred.c:543:41: sparse:    got struct cred const [noderef] <asn:4> *cred
   kernel/cred.c:584:46: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected struct cred const *override @@    got struct cred const struct cred const *override @@
   kernel/cred.c:584:46: sparse:    expected struct cred const *override
   kernel/cred.c:584:46: sparse:    got struct cred const [noderef] <asn:4> *cred
   kernel/cred.c:351:30: sparse: sparse: dereference of noderef expression
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/futex.c:1559:9: sparse: sparse: context imbalance in 'wake_futex_pi' - unexpected unlock
   kernel/futex.c:1719:33: sparse: sparse: context imbalance in 'futex_wake_op' - different lock contexts for basic block
   kernel/futex.c:2015:39: sparse: sparse: context imbalance in 'futex_requeue' - different lock contexts for basic block
   kernel/futex.c:2491:9: sparse: sparse: context imbalance in 'fixup_pi_state_owner' - unexpected unlock
   kernel/futex.c:2600:13: sparse: sparse: context imbalance in 'futex_wait_queue_me' - unexpected unlock
   kernel/futex.c:2702:1: sparse: sparse: context imbalance in 'futex_wait_setup' - different lock contexts for basic block
   kernel/futex.c:2981:12: sparse: sparse: context imbalance in 'futex_unlock_pi' - different lock contexts for basic block
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in 'futex_wait_requeue_pi' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/pid_namespace.c:56:76: sparse: sparse: Using plain integer as NULL pointer
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/iomem.c:113:22: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected void *[assigned] addr @@    got void [nvoid *[assigned] addr @@
   kernel/iomem.c:113:22: sparse:    expected void *[assigned] addr
   kernel/iomem.c:113:22: sparse:    got void [noderef] <asn:2> *
   kernel/iomem.c:116:22: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected void *[assigned] addr @@    got void [nvoid *[assigned] addr @@
   kernel/iomem.c:116:22: sparse:    expected void *[assigned] addr
   kernel/iomem.c:116:22: sparse:    got void [noderef] <asn:2> *
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/fork.c:1512:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/fork.c:1512:9: sparse:    struct sighand_struct [noderef] <asn:4> *
   kernel/fork.c:1512:9: sparse:    struct sighand_struct *
   kernel/fork.c:1735:17: sparse: sparse: incorrect type in initializer (different base types) @@    expected restricted __poll_t ( *poll )( ... ) @@    got ted __poll_t ( *poll )( ... ) @@
   kernel/fork.c:1735:17: sparse:    expected restricted __poll_t ( *poll )( ... )
   kernel/fork.c:1735:17: sparse:    got unsigned int ( * )( ... )
   kernel/fork.c:2146:32: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct task_struct [noderef] <asn:4> *real_parent @@    got  [noderef] <asn:4> *real_parent @@
   kernel/fork.c:2146:32: sparse:    expected struct task_struct [noderef] <asn:4> *real_parent
   kernel/fork.c:2146:32: sparse:    got struct task_struct *
   include/linux/ptrace.h:218:45: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected struct task_struct *new_parent @@    got struct task_structstruct task_struct *new_parent @@
   include/linux/ptrace.h:218:45: sparse:    expected struct task_struct *new_parent
   include/linux/ptrace.h:218:45: sparse:    got struct task_struct [noderef] <asn:4> *parent
   include/linux/ptrace.h:218:62: sparse: sparse: incorrect type in argument 3 (different address spaces) @@    expected struct cred const *ptracer_cred @@    got struct cred const struct cred const *ptracer_cred @@
   include/linux/ptrace.h:218:62: sparse:    expected struct cred const *ptracer_cred
   include/linux/ptrace.h:218:62: sparse:    got struct cred const [noderef] <asn:4> *ptracer_cred
   kernel/fork.c:2201:54: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected struct list_head *head @@    got struct list_head [struct list_head *head @@
   kernel/fork.c:2201:54: sparse:    expected struct list_head *head
   kernel/fork.c:2201:54: sparse:    got struct list_head [noderef] <asn:4> *
   kernel/fork.c:2639:24: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct task_struct *[assigned] parent @@    got struct struct task_struct *[assigned] parent @@
   kernel/fork.c:2639:24: sparse:    expected struct task_struct *[assigned] parent
   kernel/fork.c:2639:24: sparse:    got struct task_struct [noderef] <asn:4> *real_parent
   kernel/fork.c:1876:27: sparse: sparse: dereference of noderef expression
   kernel/fork.c:1878:22: sparse: sparse: dereference of noderef expression
   kernel/fork.c:2199:59: sparse: sparse: dereference of noderef expression
   kernel/fork.c:2200:59: sparse: sparse: dereference of noderef expression
   kernel/fork.c:2287:22: sparse: sparse: dereference of noderef expression
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/panic.c:167:6: sparse: sparse: symbol 'panic' redeclared with different type (originally declared at include/linux/kernel.h:322) - different modifiers
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/exit.c:100:19: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/exit.c:100:19: sparse:    struct sighand_struct [noderef] <asn:4> *
   kernel/exit.c:100:19: sparse:    struct sighand_struct *
   kernel/exit.c:278:37: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct task_struct *tsk @@    got struct task_structstruct task_struct *tsk @@
   kernel/exit.c:278:37: sparse:    expected struct task_struct *tsk
   kernel/exit.c:278:37: sparse:    got struct task_struct [noderef] <asn:4> *real_parent
   kernel/exit.c:281:32: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct task_struct *task @@    got struct task_structstruct task_struct *task @@
   kernel/exit.c:281:32: sparse:    expected struct task_struct *task
   kernel/exit.c:281:32: sparse:    got struct task_struct [noderef] <asn:4> *real_parent
   kernel/exit.c:282:35: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct task_struct *task @@    got struct task_structstruct task_struct *task @@
   kernel/exit.c:282:35: sparse:    expected struct task_struct *task
   kernel/exit.c:282:35: sparse:    got struct task_struct [noderef] <asn:4> *real_parent
   kernel/exit.c:327:24: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct task_struct *parent @@    got struct task_structstruct task_struct *parent @@
   kernel/exit.c:327:24: sparse:    expected struct task_struct *parent
   kernel/exit.c:327:24: sparse:    got struct task_struct [noderef] <asn:4> *real_parent
   kernel/exit.c:562:29: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct task_struct *reaper @@    got struct task_structstruct task_struct *reaper @@
   kernel/exit.c:562:29: sparse:    expected struct task_struct *reaper
   kernel/exit.c:562:29: sparse:    got struct task_struct [noderef] <asn:4> *real_parent
   kernel/exit.c:564:29: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct task_struct *reaper @@    got struct task_structstruct task_struct *reaper @@
   kernel/exit.c:564:29: sparse:    expected struct task_struct *reaper
   kernel/exit.c:564:29: sparse:    got struct task_struct [noderef] <asn:4> *real_parent
   kernel/exit.c:626:40: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct task_struct [noderef] <asn:4> *real_parent @@    got task_struct [noderef] <asn:4> *real_parent @@
   kernel/exit.c:626:40: sparse:    expected struct task_struct [noderef] <asn:4> *real_parent
   kernel/exit.c:626:40: sparse:    got struct task_struct *[assigned] reaper
   kernel/exit.c:627:25: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/exit.c:627:25: sparse:    struct task_struct [noderef] <asn:4> *
   kernel/exit.c:627:25: sparse:    struct task_struct *
   include/linux/ptrace.h:99:40: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct task_struct *p1 @@    got struct task_structstruct task_struct *p1 @@
   include/linux/ptrace.h:99:40: sparse:    expected struct task_struct *p1
   include/linux/ptrace.h:99:40: sparse:    got struct task_struct [noderef] <asn:4> *real_parent
   include/linux/ptrace.h:99:60: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected struct task_struct *p2 @@    got struct task_structstruct task_struct *p2 @@
   include/linux/ptrace.h:99:60: sparse:    expected struct task_struct *p2
   include/linux/ptrace.h:99:60: sparse:    got struct task_struct [noderef] <asn:4> *parent
   kernel/exit.c:715:17: sparse: sparse: symbol 'do_exit' redeclared with different type (originally declared at include/linux/kernel.h:328) - different modifiers
   kernel/exit.c:879:6: sparse: sparse: symbol 'complete_and_exit' redeclared with different type (originally declared at include/linux/kernel.h:329) - different modifiers
   include/linux/ptrace.h:99:40: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct task_struct *p1 @@    got struct task_structstruct task_struct *p1 @@
   include/linux/ptrace.h:99:40: sparse:    expected struct task_struct *p1
   include/linux/ptrace.h:99:40: sparse:    got struct task_struct [noderef] <asn:4> *real_parent
   include/linux/ptrace.h:99:60: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected struct task_struct *p2 @@    got struct task_structstruct task_struct *p2 @@
   include/linux/ptrace.h:99:60: sparse:    expected struct task_struct *p2
   include/linux/ptrace.h:99:60: sparse:    got struct task_struct [noderef] <asn:4> *parent
   include/linux/ptrace.h:99:40: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct task_struct *p1 @@    got struct task_structstruct task_struct *p1 @@
   include/linux/ptrace.h:99:40: sparse:    expected struct task_struct *p1
   include/linux/ptrace.h:99:40: sparse:    got struct task_struct [noderef] <asn:4> *real_parent
   include/linux/ptrace.h:99:60: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected struct task_struct *p2 @@    got struct task_structstruct task_struct *p2 @@
   include/linux/ptrace.h:99:60: sparse:    expected struct task_struct *p2
   include/linux/ptrace.h:99:60: sparse:    got struct task_struct [noderef] <asn:4> *parent
   kernel/exit.c:1429:59: sparse: sparse: incompatible types in comparison expression (different base types):
   kernel/exit.c:1429:59: sparse:    void *
   kernel/exit.c:1429:59: sparse:    struct task_struct [noderef] <asn:4> *
   kernel/exit.c:1732:13: sparse: sparse: symbol 'abort' was not declared. Should it be static?
   kernel/exit.c:1007:17: sparse: sparse: context imbalance in 'wait_task_zombie' - unexpected unlock
   kernel/exit.c:1199:24: sparse: sparse: context imbalance in 'wait_task_stopped' - unexpected unlock
   include/linux/uidgid.h:168:9: sparse: sparse: context imbalance in 'wait_task_continued' - unexpected unlock
   arch/x86/include/asm/current.h:15:16: sparse: sparse: context imbalance in 'do_wait' - different lock contexts for basic block
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/kernel/tsc.c:52:20: sparse: sparse: symbol 'art_related_clocksource' was not declared. Should it be static?
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/kernel/process.c:606:26: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got  [noderef] <asn:3> *__vpp_verify @@
   arch/x86/kernel/process.c:606:26: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   arch/x86/kernel/process.c:606:26: sparse:    got struct cpuinfo_x86 *
   arch/x86/include/asm/bitops.h:77:37: sparse: sparse: cast truncates bits from constant value (ffffff7f becomes 7f)
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/kernel/umip.c:84:12: sparse: sparse: symbol 'umip_insns' was not declared. Should it be static?
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/kernel/nmi.c:557:13: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned int enum nmi_states [noderef] <asn:3> *__p @@    got nmi_states [noderef] <asn:3> *__p @@
   arch/x86/kernel/nmi.c:557:13: sparse:    expected unsigned int enum nmi_states [noderef] <asn:3> *__p
   arch/x86/kernel/nmi.c:557:13: sparse:    got unsigned int enum nmi_states *
   arch/x86/kernel/nmi.c:312:13: sparse: sparse: context imbalance in 'default_do_nmi' - different lock contexts for basic block
   arch/x86/kernel/nmi.c:557:13: sparse: sparse: dereference of noderef expression
   arch/x86/kernel/nmi.c:557:13: sparse: sparse: dereference of noderef expression
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/kernel/setup.c:180:14: sparse: sparse: symbol 'machine_submodel_id' was not declared. Should it be static?
   arch/x86/kernel/setup.c:181:14: sparse: sparse: symbol 'BIOS_revision' was not declared. Should it be static?
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/kernel/i8259.c:410:19: sparse: sparse: symbol 'default_legacy_pic' was not declared. Should it be static?
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   include/linux/mm.h:1745:21: sparse: sparse: context imbalance in '__text_poke' - different lock contexts for basic block
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/kernel/signal.c:355:16: sparse: sparse: cast removes address space '<asn:1>' of expression
   arch/x86/kernel/signal.c:355:16: sparse: sparse: cast removes address space '<asn:1>' of expression
   arch/x86/kernel/signal.c:355:16: sparse: sparse: cast removes address space '<asn:1>' of expression
   arch/x86/kernel/signal.c:355:16: sparse: sparse: cast removes address space '<asn:1>' of expression
   arch/x86/kernel/signal.c:355:16: sparse: sparse: cast removes address space '<asn:1>' of expression
   arch/x86/kernel/signal.c:355:16: sparse: sparse: cast removes address space '<asn:1>' of expression
   arch/x86/kernel/signal.c:355:16: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected void const volatile [noderef] <asn:1> * @@    got st volatile [noderef] <asn:1> * @@
   arch/x86/kernel/signal.c:355:16: sparse:    expected void const volatile [noderef] <asn:1> *
   arch/x86/kernel/signal.c:355:16: sparse:    got unsigned long long [usertype] *__pu_ptr
   arch/x86/kernel/signal.c:415:17: sparse: sparse: cast removes address space '<asn:1>' of expression
   arch/x86/kernel/signal.c:415:17: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected void const volatile [noderef] <asn:1> * @@    got st volatile [noderef] <asn:1> * @@
   arch/x86/kernel/signal.c:415:17: sparse:    expected void const volatile [noderef] <asn:1> *
   arch/x86/kernel/signal.c:415:17: sparse:    got unsigned long long [usertype] *
   arch/x86/kernel/signal.c:415:17: sparse: sparse: cast removes address space '<asn:1>' of expression
   arch/x86/kernel/signal.c:415:17: sparse: sparse: cast removes address space '<asn:1>' of expression
   arch/x86/kernel/signal.c:415:17: sparse: sparse: cast removes address space '<asn:1>' of expression
   arch/x86/kernel/signal.c:415:17: sparse: sparse: cast removes address space '<asn:1>' of expression
   arch/x86/kernel/signal.c:415:17: sparse: sparse: cast removes address space '<asn:1>' of expression
   arch/x86/kernel/signal.c:415:17: sparse: sparse: cast removes address space '<asn:1>' of expression
   arch/x86/kernel/signal.c:415:17: sparse: sparse: cast removes address space '<asn:1>' of expression
   arch/x86/kernel/signal.c:415:17: sparse: sparse: cast removes address space '<asn:1>' of expression
   arch/x86/kernel/signal.c:415:17: sparse: sparse: cast removes address space '<asn:1>' of expression
   arch/x86/kernel/signal.c:415:17: sparse: sparse: cast removes address space '<asn:1>' of expression
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/mm/tlb.c:105:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned short [noderef] <asn:3> *__p @@    got deref] <asn:3> *__p @@
   arch/x86/mm/tlb.c:105:21: sparse:    expected unsigned short [noderef] <asn:3> *__p
   arch/x86/mm/tlb.c:105:21: sparse:    got unsigned short *
   arch/x86/mm/tlb.c:105:21: sparse: sparse: dereference of noderef expression
   arch/x86/mm/tlb.c:105:21: sparse: sparse: dereference of noderef expression
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   include/linux/highmem.h:118:19: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected int [noderef] <asn:3> *__p @@    got :3> *__p @@
   include/linux/highmem.h:118:19: sparse:    expected int [noderef] <asn:3> *__p
   include/linux/highmem.h:118:19: sparse:    got int *
   include/linux/highmem.h:118:19: sparse: sparse: dereference of noderef expression
   include/linux/highmem.h:118:19: sparse: sparse: dereference of noderef expression
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/mm/pageattr.c:334:6: sparse: sparse: symbol '__cpa_flush_tlb' was not declared. Should it be static?
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in 'poking_init' - different lock contexts for basic block
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/entry/vdso/vdso32/../vclock_gettime.c:70:5: sparse: sparse: symbol '__vdso_clock_gettime64' was not declared. Should it be static?
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/events/intel/core.c:2804:1: sparse: sparse: context imbalance in 'intel_start_scheduling' - different lock contexts for basic block
   arch/x86/events/intel/core.c:2877:9: sparse: sparse: context imbalance in 'intel_stop_scheduling' - unexpected unlock
   arch/x86/events/intel/core.c:3083:25: sparse: sparse: context imbalance in 'intel_put_excl_constraints' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/kernel/acpi/boot.c:111:30: sparse: sparse: incorrect type in return expression (different address spaces) @@    expected void [noderef] <asn:2> * @@    got n:2> * @@
   arch/x86/kernel/acpi/boot.c:111:30: sparse:    expected void [noderef] <asn:2> *
   arch/x86/kernel/acpi/boot.c:111:30: sparse:    got void *
   arch/x86/kernel/acpi/boot.c:119:24: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected void *addr @@    got void [noderef] <asvoid *addr @@
   arch/x86/kernel/acpi/boot.c:119:24: sparse:    expected void *addr
   arch/x86/kernel/acpi/boot.c:119:24: sparse:    got void [noderef] <asn:2> *map
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/kernel/acpi/sleep.c:81:13: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got :3> *__vpp_verify @@
   arch/x86/kernel/acpi/sleep.c:81:13: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   arch/x86/kernel/acpi/sleep.c:81:13: sparse:    got int *
   arch/x86/kernel/acpi/sleep.c:81:13: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got :3> *__vpp_verify @@
   arch/x86/kernel/acpi/sleep.c:81:13: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   arch/x86/kernel/acpi/sleep.c:81:13: sparse:    got int *
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/kernel/cpu/common.c:131:43: sparse: sparse: cast truncates bits from constant value (fffff becomes ffff)
   arch/x86/kernel/cpu/common.c:132:43: sparse: sparse: cast truncates bits from constant value (fffff becomes ffff)
   arch/x86/kernel/cpu/common.c:133:43: sparse: sparse: cast truncates bits from constant value (fffff becomes ffff)
   arch/x86/kernel/cpu/common.c:134:43: sparse: sparse: cast truncates bits from constant value (fffff becomes ffff)
   arch/x86/kernel/cpu/common.c:161:43: sparse: sparse: cast truncates bits from constant value (fffff becomes ffff)
   arch/x86/kernel/cpu/common.c:162:43: sparse: sparse: cast truncates bits from constant value (fffff becomes ffff)
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/kernel/cpu/cacheinfo.c:267:30: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got ref] <asn:3> *__vpp_verify @@
   arch/x86/kernel/cpu/cacheinfo.c:267:30: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   arch/x86/kernel/cpu/cacheinfo.c:267:30: sparse:    got unsigned int *
   arch/x86/kernel/cpu/cacheinfo.c:267:30: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got ref] <asn:3> *__vpp_verify @@
   arch/x86/kernel/cpu/cacheinfo.c:267:30: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   arch/x86/kernel/cpu/cacheinfo.c:267:30: sparse:    got unsigned int *
   arch/x86/kernel/cpu/cacheinfo.c:289:39: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got deref] <asn:3> *__vpp_verify @@
   arch/x86/kernel/cpu/cacheinfo.c:289:39: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   arch/x86/kernel/cpu/cacheinfo.c:289:39: sparse:    got unsigned short *
   arch/x86/kernel/cpu/cacheinfo.c:289:39: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got deref] <asn:3> *__vpp_verify @@
   arch/x86/kernel/cpu/cacheinfo.c:289:39: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   arch/x86/kernel/cpu/cacheinfo.c:289:39: sparse:    got unsigned short *
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   mm/page_alloc.c:1026:1: sparse: sparse: directive in macro's argument list
   mm/page_alloc.c:1028:1: sparse: sparse: directive in macro's argument list
   mm/page_alloc.c:7561:1: sparse: sparse: directive in macro's argument list
   mm/page_alloc.c:7563:1: sparse: sparse: directive in macro's argument list
   mm/page_alloc.c:7571:1: sparse: sparse: directive in macro's argument list
   mm/page_alloc.c:7573:1: sparse: sparse: directive in macro's argument list
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   include/linux/rcupdate.h:651:9: sparse: sparse: context imbalance in 'total_swapcache_pages' - unexpected unlock
   mm/swap_state.c:319:29: sparse: sparse: context imbalance in 'lookup_swap_cache' - unexpected unlock
   mm/swap_state.c:377:43: sparse: sparse: context imbalance in '__read_swap_cache_async' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   mm/swapfile.c:476:35: sparse: sparse: context imbalance in 'swap_do_scheduled_discard' - different lock contexts for basic block
   mm/swapfile.c:647:23: sparse: sparse: context imbalance in 'scan_swap_map_try_ssd_cluster' - different lock contexts for basic block
   mm/swapfile.c:891:20: sparse: sparse: context imbalance in 'scan_swap_map_slots' - unexpected unlock
   mm/swapfile.c:973:23: sparse: sparse: context imbalance in 'swap_free_cluster' - different lock contexts for basic block
   mm/swapfile.c:1151:32: sparse: sparse: context imbalance in 'swap_info_get' - wrong count at exit
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in 'swap_info_get_cont' - unexpected unlock
   mm/swapfile.c:1249:25: sparse: sparse: context imbalance in 'get_swap_device' - different lock contexts for basic block
   mm/swapfile.c:361:40: sparse: sparse: context imbalance in '__swap_entry_free' - different lock contexts for basic block
   mm/swapfile.c:1305:33: sparse: sparse: context imbalance in 'swap_entry_free' - different lock contexts for basic block
   mm/swapfile.c:1360:34: sparse: sparse: context imbalance in 'put_swap_page' - different lock contexts for basic block
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in 'swapcache_free_entries' - unexpected unlock
   mm/swapfile.c:361:40: sparse: sparse: context imbalance in 'page_swapcount' - different lock contexts for basic block
   mm/swapfile.c:1456:35: sparse: sparse: context imbalance in '__swap_count' - unexpected unlock
   mm/swapfile.c:361:40: sparse: sparse: context imbalance in 'swap_swapcount' - different lock contexts for basic block
   mm/swapfile.c:1486:17: sparse: sparse: context imbalance in '__swp_swapcount' - unexpected unlock
   mm/swapfile.c:361:40: sparse: sparse: context imbalance in 'swp_swapcount' - different lock contexts for basic block
   mm/swapfile.c:361:40: sparse: sparse: context imbalance in 'swap_page_trans_huge_swapped' - different lock contexts for basic block
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in 'reuse_swap_page' - unexpected unlock
   include/linux/rcupdate.h:651:9: sparse: sparse: context imbalance in '__swap_duplicate' - unexpected unlock
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in 'add_swap_count_continuation' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   mm/slub.c:2762:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void **[noderef] <asn:3> *__p1 @@    got asn:3> *__p1 @@
   mm/slub.c:2762:21: sparse:    expected void **[noderef] <asn:3> *__p1
   mm/slub.c:2762:21: sparse:    got void ***
   mm/slub.c:2762:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned long [noderef] <asn:3> *__p2 @@    got eref] <asn:3> *__p2 @@
   mm/slub.c:2762:21: sparse:    expected unsigned long [noderef] <asn:3> *__p2
   mm/slub.c:2762:21: sparse:    got unsigned long *
   mm/slub.c:2762:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void **[noderef] <asn:3> *__p1 @@    got asn:3> *__p1 @@
   mm/slub.c:2762:21: sparse:    expected void **[noderef] <asn:3> *__p1
   mm/slub.c:2762:21: sparse:    got void ***
   mm/slub.c:2762:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned long [noderef] <asn:3> *__p2 @@    got eref] <asn:3> *__p2 @@
   mm/slub.c:2762:21: sparse:    expected unsigned long [noderef] <asn:3> *__p2
   mm/slub.c:2762:21: sparse:    got unsigned long *
   mm/slub.c:2762:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void **[noderef] <asn:3> *__p1 @@    got asn:3> *__p1 @@
   mm/slub.c:2762:21: sparse:    expected void **[noderef] <asn:3> *__p1
   mm/slub.c:2762:21: sparse:    got void ***
   mm/slub.c:2762:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned long [noderef] <asn:3> *__p2 @@    got eref] <asn:3> *__p2 @@
   mm/slub.c:2762:21: sparse:    expected unsigned long [noderef] <asn:3> *__p2
   mm/slub.c:2762:21: sparse:    got unsigned long *
   mm/slub.c:3003:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void **[noderef] <asn:3> *__p1 @@    got asn:3> *__p1 @@
   mm/slub.c:3003:21: sparse:    expected void **[noderef] <asn:3> *__p1
   mm/slub.c:3003:21: sparse:    got void ***
   mm/slub.c:3003:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned long [noderef] <asn:3> *__p2 @@    got eref] <asn:3> *__p2 @@
   mm/slub.c:3003:21: sparse:    expected unsigned long [noderef] <asn:3> *__p2
   mm/slub.c:3003:21: sparse:    got unsigned long *
   mm/slub.c:3003:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void **[noderef] <asn:3> *__p1 @@    got asn:3> *__p1 @@
   mm/slub.c:3003:21: sparse:    expected void **[noderef] <asn:3> *__p1
   mm/slub.c:3003:21: sparse:    got void ***
   mm/slub.c:3003:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned long [noderef] <asn:3> *__p2 @@    got eref] <asn:3> *__p2 @@
   mm/slub.c:3003:21: sparse:    expected unsigned long [noderef] <asn:3> *__p2
   mm/slub.c:3003:21: sparse:    got unsigned long *
   mm/slub.c:3003:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void **[noderef] <asn:3> *__p1 @@    got asn:3> *__p1 @@
   mm/slub.c:3003:21: sparse:    expected void **[noderef] <asn:3> *__p1
   mm/slub.c:3003:21: sparse:    got void ***
   mm/slub.c:3003:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned long [noderef] <asn:3> *__p2 @@    got eref] <asn:3> *__p2 @@
   mm/slub.c:3003:21: sparse:    expected unsigned long [noderef] <asn:3> *__p2
   mm/slub.c:3003:21: sparse:    got unsigned long *
   mm/slub.c:3003:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void **[noderef] <asn:3> *__p1 @@    got asn:3> *__p1 @@
   mm/slub.c:3003:21: sparse:    expected void **[noderef] <asn:3> *__p1
   mm/slub.c:3003:21: sparse:    got void ***
   mm/slub.c:3003:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned long [noderef] <asn:3> *__p2 @@    got eref] <asn:3> *__p2 @@
   mm/slub.c:3003:21: sparse:    expected unsigned long [noderef] <asn:3> *__p2
   mm/slub.c:3003:21: sparse:    got unsigned long *
   mm/slub.c:3003:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void **[noderef] <asn:3> *__p1 @@    got asn:3> *__p1 @@
   mm/slub.c:3003:21: sparse:    expected void **[noderef] <asn:3> *__p1
   mm/slub.c:3003:21: sparse:    got void ***
   mm/slub.c:3003:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned long [noderef] <asn:3> *__p2 @@    got eref] <asn:3> *__p2 @@
   mm/slub.c:3003:21: sparse:    expected unsigned long [noderef] <asn:3> *__p2
   mm/slub.c:3003:21: sparse:    got unsigned long *
   mm/slub.c:3003:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void **[noderef] <asn:3> *__p1 @@    got asn:3> *__p1 @@
   mm/slub.c:3003:21: sparse:    expected void **[noderef] <asn:3> *__p1
   mm/slub.c:3003:21: sparse:    got void ***
   mm/slub.c:3003:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned long [noderef] <asn:3> *__p2 @@    got eref] <asn:3> *__p2 @@
   mm/slub.c:3003:21: sparse:    expected unsigned long [noderef] <asn:3> *__p2
   mm/slub.c:3003:21: sparse:    got unsigned long *
   mm/slub.c:2762:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void **[noderef] <asn:3> *__p1 @@    got asn:3> *__p1 @@
   mm/slub.c:2762:21: sparse:    expected void **[noderef] <asn:3> *__p1
   mm/slub.c:2762:21: sparse:    got void ***
   mm/slub.c:2762:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned long [noderef] <asn:3> *__p2 @@    got eref] <asn:3> *__p2 @@
   mm/slub.c:2762:21: sparse:    expected unsigned long [noderef] <asn:3> *__p2
   mm/slub.c:2762:21: sparse:    got unsigned long *
   mm/slub.c:2762:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void **[noderef] <asn:3> *__p1 @@    got asn:3> *__p1 @@
   mm/slub.c:2762:21: sparse:    expected void **[noderef] <asn:3> *__p1
   mm/slub.c:2762:21: sparse:    got void ***
   mm/slub.c:2762:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned long [noderef] <asn:3> *__p2 @@    got eref] <asn:3> *__p2 @@
   mm/slub.c:2762:21: sparse:    expected unsigned long [noderef] <asn:3> *__p2
   mm/slub.c:2762:21: sparse:    got unsigned long *
   mm/slub.c:2762:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void **[noderef] <asn:3> *__p1 @@    got asn:3> *__p1 @@
   mm/slub.c:2762:21: sparse:    expected void **[noderef] <asn:3> *__p1
   mm/slub.c:2762:21: sparse:    got void ***
   mm/slub.c:2762:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned long [noderef] <asn:3> *__p2 @@    got eref] <asn:3> *__p2 @@
   mm/slub.c:2762:21: sparse:    expected unsigned long [noderef] <asn:3> *__p2
   mm/slub.c:2762:21: sparse:    got unsigned long *
   mm/slub.c:3003:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void **[noderef] <asn:3> *__p1 @@    got asn:3> *__p1 @@
   mm/slub.c:3003:21: sparse:    expected void **[noderef] <asn:3> *__p1
   mm/slub.c:3003:21: sparse:    got void ***
   mm/slub.c:3003:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned long [noderef] <asn:3> *__p2 @@    got eref] <asn:3> *__p2 @@
   mm/slub.c:3003:21: sparse:    expected unsigned long [noderef] <asn:3> *__p2
   mm/slub.c:3003:21: sparse:    got unsigned long *
   mm/slub.c:3003:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void **[noderef] <asn:3> *__p1 @@    got asn:3> *__p1 @@
   mm/slub.c:3003:21: sparse:    expected void **[noderef] <asn:3> *__p1
   mm/slub.c:3003:21: sparse:    got void ***
   mm/slub.c:3003:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned long [noderef] <asn:3> *__p2 @@    got eref] <asn:3> *__p2 @@
   mm/slub.c:3003:21: sparse:    expected unsigned long [noderef] <asn:3> *__p2
   mm/slub.c:3003:21: sparse:    got unsigned long *
   mm/slub.c:3003:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void **[noderef] <asn:3> *__p1 @@    got asn:3> *__p1 @@
   mm/slub.c:3003:21: sparse:    expected void **[noderef] <asn:3> *__p1
   mm/slub.c:3003:21: sparse:    got void ***
   mm/slub.c:3003:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned long [noderef] <asn:3> *__p2 @@    got eref] <asn:3> *__p2 @@
   mm/slub.c:3003:21: sparse:    expected unsigned long [noderef] <asn:3> *__p2
   mm/slub.c:3003:21: sparse:    got unsigned long *
   mm/slub.c:2762:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void **[noderef] <asn:3> *__p1 @@    got asn:3> *__p1 @@
   mm/slub.c:2762:21: sparse:    expected void **[noderef] <asn:3> *__p1
   mm/slub.c:2762:21: sparse:    got void ***
   mm/slub.c:2762:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned long [noderef] <asn:3> *__p2 @@    got eref] <asn:3> *__p2 @@
   mm/slub.c:2762:21: sparse:    expected unsigned long [noderef] <asn:3> *__p2
   mm/slub.c:2762:21: sparse:    got unsigned long *
   mm/slub.c:2762:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void **[noderef] <asn:3> *__p1 @@    got asn:3> *__p1 @@
   mm/slub.c:2762:21: sparse:    expected void **[noderef] <asn:3> *__p1
   mm/slub.c:2762:21: sparse:    got void ***
   mm/slub.c:2762:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned long [noderef] <asn:3> *__p2 @@    got eref] <asn:3> *__p2 @@
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   mm/migrate.c:811:9: sparse: sparse: context imbalance in '__buffer_migrate_page' - different lock contexts for basic block
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   include/linux/spinlock.h:393:9: sparse: sparse: context imbalance in 'pagevec_lru_move_fn' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   mm/truncate.c:104:9: sparse: sparse: context imbalance in 'truncate_exceptional_pvec_entries' - different lock contexts for basic block
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/include/asm/irqflags.h:54:9: sparse: sparse: context imbalance in 'check_move_unevictable_pages' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   include/linux/spinlock.h:393:9: sparse: sparse: context imbalance in 'walk_zones_in_node' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   include/linux/gfp.h:318:27: sparse: sparse: restricted gfp_t degrades to integer
   mm/compaction.c:2343:39: sparse: sparse: incorrect type in initializer (different base types) @@    expected int may_perform_io @@    got restricted gint may_perform_io @@
   mm/compaction.c:2343:39: sparse:    expected int may_perform_io
   mm/compaction.c:2343:39: sparse:    got restricted gfp_t
   mm/compaction.c:482:13: sparse: sparse: context imbalance in 'compact_lock_irqsave' - wrong count at exit
   include/linux/spinlock.h:393:9: sparse: sparse: context imbalance in 'compact_unlock_should_abort' - unexpected unlock
   mm/compaction.c:638:39: sparse: sparse: context imbalance in 'isolate_freepages_block' - unexpected unlock
   mm/compaction.c:1047:39: sparse: sparse: context imbalance in 'isolate_migratepages_block' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/include/asm/irqflags.h:54:9: sparse: sparse: context imbalance in 'shadow_lru_isolate' - wrong count at exit
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   mm/memory.c:800:17: sparse: sparse: context imbalance in 'copy_pte_range' - different lock contexts for basic block
   mm/memory.c:1412:16: sparse: sparse: context imbalance in '__get_locked_pte' - different lock contexts for basic block
   mm/memory.c:1801:17: sparse: sparse: context imbalance in 'remap_pte_range' - different lock contexts for basic block
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in 'apply_to_pte_range' - unexpected unlock
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in 'wp_pfn_shared' - unexpected unlock
   mm/memory.c:2547:19: sparse: sparse: context imbalance in 'do_wp_page' - different lock contexts for basic block
   mm/memory.c:3124:19: sparse: sparse: context imbalance in 'pte_alloc_one_map' - different lock contexts for basic block
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in 'finish_fault' - unexpected unlock
   mm/memory.c:3463:9: sparse: sparse: context imbalance in 'do_fault_around' - unexpected unlock
   mm/memory.c:4116:12: sparse: sparse: context imbalance in '__follow_pte_pmd' - different lock contexts for basic block
   mm/memory.c:4195:5: sparse: sparse: context imbalance in 'follow_pte_pmd' - different lock contexts for basic block
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   mm/mincore.c:76:53: sparse: sparse: context imbalance in 'mincore_page' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   include/linux/mm.h:1745:21: sparse: sparse: context imbalance in '__munlock_pagevec_fill' - different lock contexts for basic block
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   include/linux/rcupdate.h:649:9: sparse: sparse: context imbalance in 'find_lock_task_mm' - wrong count at exit
   include/linux/sched/mm.h:166:37: sparse: sparse: dereference of noderef expression
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in 'oom_badness' - unexpected unlock
   mm/oom_kill.c:402:9: sparse: sparse: context imbalance in 'dump_task' - unexpected unlock
   include/linux/rcupdate.h:651:9: sparse: sparse: context imbalance in '__oom_kill_process' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   mm/page_vma_mapped.c:156:29: sparse: sparse: Using plain integer as NULL pointer
   mm/page_vma_mapped.c:16:13: sparse: sparse: context imbalance in 'map_pte' - wrong count at exit
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in 'page_vma_mapped_walk' - unexpected unlock
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in 'page_mapped_in_vma' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in 'page_referenced_one' - unexpected unlock
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in 'try_to_unmap_one' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   mm/vmalloc.c:1097:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected struct vmap_area *[noderef] <asn:3> *__p @@    got [noderef] <asn:3> *__p @@
   mm/vmalloc.c:1097:21: sparse:    expected struct vmap_area *[noderef] <asn:3> *__p
   mm/vmalloc.c:1097:21: sparse:    got struct vmap_area **
   mm/vmalloc.c:958:23: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected struct vmap_area *[noderef] <asn:3> *__p @@    got [noderef] <asn:3> *__p @@
   mm/vmalloc.c:958:23: sparse:    expected struct vmap_area *[noderef] <asn:3> *__p
   mm/vmalloc.c:958:23: sparse:    got struct vmap_area **
   mm/vmalloc.c:1097:21: sparse: sparse: dereference of noderef expression
   mm/vmalloc.c:1097:21: sparse: sparse: dereference of noderef expression
   mm/vmalloc.c:958:23: sparse: sparse: dereference of noderef expression
   mm/vmalloc.c:958:23: sparse: sparse: dereference of noderef expression
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/bpf/hashtab.c:510:19: sparse: sparse: subtraction of functions? Share your drugs
   kernel/bpf/hashtab.c:551:19: sparse: sparse: subtraction of functions? Share your drugs
   kernel/bpf/hashtab.c:1488:19: sparse: sparse: subtraction of functions? Share your drugs
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/bpf/verifier.c:8759:76: sparse: sparse: subtraction of functions? Share your drugs
   kernel/bpf/verifier.c:9112:81: sparse: sparse: subtraction of functions? Share your drugs
   kernel/bpf/verifier.c:9116:81: sparse: sparse: subtraction of functions? Share your drugs
   kernel/bpf/verifier.c:9120:81: sparse: sparse: subtraction of functions? Share your drugs
   kernel/bpf/verifier.c:9124:79: sparse: sparse: subtraction of functions? Share your drugs
   kernel/bpf/verifier.c:9128:78: sparse: sparse: subtraction of functions? Share your drugs
   kernel/bpf/verifier.c:9132:79: sparse: sparse: subtraction of functions? Share your drugs
   kernel/bpf/verifier.c:9151:38: sparse: sparse: subtraction of functions? Share your drugs
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/bpf/core.c:206:49: sparse: sparse: arithmetics on pointers to functions
   kernel/bpf/core.c:1447:43: sparse: sparse: arithmetics on pointers to functions
   kernel/bpf/core.c:1452:48: sparse: sparse: arithmetics on pointers to functions
   kernel/bpf/core.c:1632:77: sparse: sparse: subtraction of functions? Share your drugs
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/events/core.c:571:26: sparse: sparse: function 'perf_pmu_name' with external linkage has definition
   kernel/events/core.c:1035:1: sparse: sparse: symbol 'perf_cgroup_switch' was not declared. Should it be static?
   kernel/events/core.c:1384:15: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:1384:15: sparse:    struct perf_event_context [noderef] <asn:4> *
   kernel/events/core.c:1384:15: sparse:    struct perf_event_context *
   kernel/events/core.c:1397:28: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:1397:28: sparse:    struct perf_event_context [noderef] <asn:4> *
   kernel/events/core.c:1397:28: sparse:    struct perf_event_context *
   kernel/events/core.c:3187:18: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:3187:18: sparse:    struct perf_event_context [noderef] <asn:4> *
   kernel/events/core.c:3187:18: sparse:    struct perf_event_context *
   kernel/events/core.c:3188:23: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:3188:23: sparse:    struct perf_event_context [noderef] <asn:4> *
   kernel/events/core.c:3188:23: sparse:    struct perf_event_context *
   kernel/events/core.c:3219:25: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:3219:25: sparse:    struct perf_event_context [noderef] <asn:4> *
   kernel/events/core.c:3219:25: sparse:    struct perf_event_context *
   kernel/events/core.c:3220:25: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:3220:25: sparse:    struct perf_event_context [noderef] <asn:4> *
   kernel/events/core.c:3220:25: sparse:    struct perf_event_context *
   kernel/events/core.c:3858:21: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected int [noderef] <asn:3> *__p @@    got :3> *__p @@
   kernel/events/core.c:3858:21: sparse:    expected int [noderef] <asn:3> *__p
   kernel/events/core.c:3858:21: sparse:    got int *
   kernel/events/core.c:4294:25: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:4294:25: sparse:    struct perf_event_context [noderef] <asn:4> *
   kernel/events/core.c:4294:25: sparse:    struct perf_event_context *
   kernel/events/core.c:5507:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:5507:9: sparse:    struct ring_buffer [noderef] <asn:4> *
   kernel/events/core.c:5507:9: sparse:    struct ring_buffer *
   kernel/events/core.c:5020:24: sparse: sparse: incorrect type in assignment (different base types) @@    expected restricted __poll_t [usertype] events @@    got e] events @@
   kernel/events/core.c:5020:24: sparse:    expected restricted __poll_t [usertype] events
   kernel/events/core.c:5020:24: sparse:    got int
   kernel/events/core.c:5218:22: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:5218:22: sparse:    struct ring_buffer [noderef] <asn:4> *
   kernel/events/core.c:5218:22: sparse:    struct ring_buffer *
   kernel/events/core.c:5349:14: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:5349:14: sparse:    struct ring_buffer [noderef] <asn:4> *
   kernel/events/core.c:5349:14: sparse:    struct ring_buffer *
   kernel/events/core.c:5382:14: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:5382:14: sparse:    struct ring_buffer [noderef] <asn:4> *
   kernel/events/core.c:5382:14: sparse:    struct ring_buffer *
   kernel/events/core.c:5439:14: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:5439:14: sparse:    struct ring_buffer [noderef] <asn:4> *
   kernel/events/core.c:5439:14: sparse:    struct ring_buffer *
   kernel/events/core.c:5525:14: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:5525:14: sparse:    struct ring_buffer [noderef] <asn:4> *
   kernel/events/core.c:5525:14: sparse:    struct ring_buffer *
   kernel/events/core.c:5538:14: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:5538:14: sparse:    struct ring_buffer [noderef] <asn:4> *
   kernel/events/core.c:5538:14: sparse:    struct ring_buffer *
   kernel/events/internal.h:203:1: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected void const [noderef] <asn:1> *from @@    got f] <asn:1> *from @@
   kernel/events/internal.h:203:1: sparse:    expected void const [noderef] <asn:1> *from
   kernel/events/internal.h:203:1: sparse:    got void const *buf
   kernel/events/core.c:6854:23: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:6854:23: sparse:    struct perf_event_context [noderef] <asn:4> *
   kernel/events/core.c:6854:23: sparse:    struct perf_event_context *
   kernel/events/core.c:6945:13: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:6945:13: sparse:    struct ring_buffer [noderef] <asn:4> *
   kernel/events/core.c:6945:13: sparse:    struct ring_buffer *
   kernel/events/core.c:7633:23: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:7633:23: sparse:    struct perf_event_context [noderef] <asn:4> *
   kernel/events/core.c:7633:23: sparse:    struct perf_event_context *
   kernel/events/core.c:8363:17: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:8363:17: sparse:    struct swevent_hlist [noderef] <asn:4> *
   kernel/events/core.c:8363:17: sparse:    struct swevent_hlist *
   kernel/events/core.c:8383:17: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:8383:17: sparse:    struct swevent_hlist [noderef] <asn:4> *
   kernel/events/core.c:8383:17: sparse:    struct swevent_hlist *
   kernel/events/core.c:8503:16: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:8503:16: sparse:    struct swevent_hlist [noderef] <asn:4> *
   kernel/events/core.c:8503:16: sparse:    struct swevent_hlist *
   kernel/events/core.c:8514:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:8514:9: sparse:    struct swevent_hlist [noderef] <asn:4> *
   kernel/events/core.c:8514:9: sparse:    struct swevent_hlist *
   kernel/events/core.c:8503:16: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:8503:16: sparse:    struct swevent_hlist [noderef] <asn:4> *
   kernel/events/core.c:8503:16: sparse:    struct swevent_hlist *
   kernel/events/core.c:8553:17: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:8553:17: sparse:    struct swevent_hlist [noderef] <asn:4> *
   kernel/events/core.c:8553:17: sparse:    struct swevent_hlist *
   kernel/events/core.c:9905:1: sparse: sparse: symbol 'dev_attr_nr_addr_filters' was not declared. Should it be static?
   kernel/events/core.c:11567:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:11567:9: sparse:    struct perf_event_context [noderef] <asn:4> *
   kernel/events/core.c:11567:9: sparse:    struct perf_event_context *
   kernel/events/core.c:11677:17: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:11677:17: sparse:    struct perf_event_context [noderef] <asn:4> *
   kernel/events/core.c:11677:17: sparse:    struct perf_event_context *
   kernel/events/core.c:8503:16: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:8503:16: sparse:    struct swevent_hlist [noderef] <asn:4> *
   kernel/events/core.c:8503:16: sparse:    struct swevent_hlist *
   kernel/events/core.c:12101:17: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/core.c:12101:17: sparse:    struct swevent_hlist [noderef] <asn:4> *
   kernel/events/core.c:12101:17: sparse:    struct swevent_hlist *
   kernel/events/core.c:155:9: sparse: sparse: context imbalance in 'perf_ctx_lock' - wrong count at exit
   kernel/events/core.c:163:17: sparse: sparse: context imbalance in 'perf_ctx_unlock' - unexpected unlock
   kernel/events/core.c:1404:17: sparse: sparse: context imbalance in 'perf_lock_task_context' - different lock contexts for basic block
   kernel/events/core.c:1431:17: sparse: sparse: context imbalance in 'perf_pin_task_context' - unexpected unlock
   kernel/events/core.c:2636:9: sparse: sparse: context imbalance in '__perf_install_in_context' - wrong count at exit
   kernel/events/core.c:3858:21: sparse: sparse: dereference of noderef expression
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/events/ring_buffer.c:22:39: sparse: sparse: incorrect type in argument 2 (different base types) @@    expected int i @@    got restricted __poll_t [usertyint i @@
   kernel/events/ring_buffer.c:22:39: sparse:    expected int i
   kernel/events/ring_buffer.c:22:39: sparse:    got restricted __poll_t [usertype]
   kernel/events/ring_buffer.c:169:14: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/ring_buffer.c:169:14: sparse:    struct ring_buffer [noderef] <asn:4> *
   kernel/events/ring_buffer.c:169:14: sparse:    struct ring_buffer *
   kernel/events/ring_buffer.c:169:14: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/ring_buffer.c:169:14: sparse:    struct ring_buffer [noderef] <asn:4> *
   kernel/events/ring_buffer.c:169:14: sparse:    struct ring_buffer *
   kernel/events/ring_buffer.c:169:14: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/ring_buffer.c:169:14: sparse:    struct ring_buffer [noderef] <asn:4> *
   kernel/events/ring_buffer.c:169:14: sparse:    struct ring_buffer *
   kernel/events/ring_buffer.c:149:1: sparse: sparse: context imbalance in 'perf_output_begin_forward' - different lock contexts for basic block
   kernel/events/ring_buffer.c:149:1: sparse: sparse: context imbalance in 'perf_output_begin_backward' - different lock contexts for basic block
   kernel/events/ring_buffer.c:149:1: sparse: sparse: context imbalance in 'perf_output_begin' - different lock contexts for basic block
   kernel/events/ring_buffer.c:297:6: sparse: sparse: context imbalance in 'perf_output_end' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/events/callchain.c:66:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/callchain.c:66:9: sparse:    struct callchain_cpus_entries [noderef] <asn:4> *
   kernel/events/callchain.c:66:9: sparse:    struct callchain_cpus_entries *
   kernel/events/callchain.c:96:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/callchain.c:96:9: sparse:    struct callchain_cpus_entries [noderef] <asn:4> *
   kernel/events/callchain.c:96:9: sparse:    struct callchain_cpus_entries *
   kernel/events/callchain.c:161:19: sparse: sparse: incompatible types in comparison expression (different address spaces):
   kernel/events/callchain.c:161:19: sparse:    struct callchain_cpus_entries [noderef] <asn:4> *
   kernel/events/callchain.c:161:19: sparse:    struct callchain_cpus_entries *
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/kernel/cpu/mce/core.c:1416:27: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got  [noderef] <asn:3> *__vpp_verify @@
   arch/x86/kernel/cpu/mce/core.c:1416:27: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   arch/x86/kernel/cpu/mce/core.c:1416:27: sparse:    got struct cpuinfo_x86 *
   arch/x86/kernel/cpu/mce/core.c:2050:34: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got  [noderef] <asn:3> *__vpp_verify @@
   arch/x86/kernel/cpu/mce/core.c:2050:34: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   arch/x86/kernel/cpu/mce/core.c:2050:34: sparse:    got struct cpuinfo_x86 *
   arch/x86/kernel/cpu/mce/core.c:2066:28: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got  [noderef] <asn:3> *__vpp_verify @@
   arch/x86/kernel/cpu/mce/core.c:2066:28: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   arch/x86/kernel/cpu/mce/core.c:2066:28: sparse:    got struct cpuinfo_x86 *
   arch/x86/kernel/cpu/mce/core.c:2083:28: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got  [noderef] <asn:3> *__vpp_verify @@
   arch/x86/kernel/cpu/mce/core.c:2083:28: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   arch/x86/kernel/cpu/mce/core.c:2083:28: sparse:    got struct cpuinfo_x86 *
   arch/x86/kernel/cpu/mce/core.c:2090:28: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got  [noderef] <asn:3> *__vpp_verify @@
   arch/x86/kernel/cpu/mce/core.c:2090:28: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   arch/x86/kernel/cpu/mce/core.c:2090:28: sparse:    got struct cpuinfo_x86 *
   arch/x86/kernel/cpu/mce/core.c:2335:28: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got  [noderef] <asn:3> *__vpp_verify @@
   arch/x86/kernel/cpu/mce/core.c:2335:28: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   arch/x86/kernel/cpu/mce/core.c:2335:28: sparse:    got struct cpuinfo_x86 *
   arch/x86/kernel/cpu/mce/core.c:2349:28: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected void const [noderef] <asn:3> *__vpp_verify @@    got  [noderef] <asn:3> *__vpp_verify @@
   arch/x86/kernel/cpu/mce/core.c:2349:28: sparse:    expected void const [noderef] <asn:3> *__vpp_verify
   arch/x86/kernel/cpu/mce/core.c:2349:28: sparse:    got struct cpuinfo_x86 *
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/direct-io.c:1177:36: sparse: sparse: Using plain integer as NULL pointer
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/signalfd.c:108:32: sparse: sparse: cast removes address space '<asn:1>' of expression
   fs/signalfd.c:125:33: sparse: sparse: cast removes address space '<asn:1>' of expression
   fs/signalfd.c:131:33: sparse: sparse: cast removes address space '<asn:1>' of expression
   fs/signalfd.c:150:32: sparse: sparse: cast removes address space '<asn:1>' of expression
   fs/signalfd.c:154:38: sparse: sparse: cast removes address space '<asn:1>' of expression
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/aio.c:1662:12: sparse: sparse: context imbalance in 'aio_poll_wake' - wrong count at exit
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/io_uring.c:1775:12: sparse: sparse: context imbalance in 'io_poll_wake' - wrong count at exit
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   include/linux/sched/signal.h:668:37: sparse: sparse: context imbalance in 'zap_threads' - different lock contexts for basic block
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/exec.c:417:31: sparse: sparse: incorrect type in return expression (different address spaces) @@    expected char const [noderef] <asn:1> * @@    got n:1> * @@
   fs/exec.c:417:31: sparse:    expected char const [noderef] <asn:1> *
   fs/exec.c:417:31: sparse:    got void *
   fs/exec.c:1175:56: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected struct task_struct *parent @@    got struct task_structstruct task_struct *parent @@
   fs/exec.c:1175:56: sparse:    expected struct task_struct *parent
   fs/exec.c:1175:56: sparse:    got struct task_struct [noderef] <asn:4> *parent
   fs/exec.c:1210:17: sparse: sparse: incompatible types in comparison expression (different address spaces):
   fs/exec.c:1210:17: sparse:    struct sighand_struct [noderef] <asn:4> *
   fs/exec.c:1210:17: sparse:    struct sighand_struct *
   fs/exec.c:1699:70: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct task_struct *tsk @@    got struct task_structstruct task_struct *tsk @@
   fs/exec.c:1699:70: sparse:    expected struct task_struct *tsk
   fs/exec.c:1699:70: sparse:    got struct task_struct [noderef] <asn:4> *parent
   fs/exec.c:1873:52: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected char const [noderef] <asn:1> *const [noderef] <asn:1> *native @@    got n:1> *native @@
   fs/exec.c:1873:52: sparse:    expected char const [noderef] <asn:1> *const [noderef] <asn:1> *native
   fs/exec.c:1873:52: sparse:    got void *__argv
   fs/exec.c:1874:52: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected char const [noderef] <asn:1> *const [noderef] <asn:1> *native @@    got n:1> *native @@
   fs/exec.c:1874:52: sparse:    expected char const [noderef] <asn:1> *const [noderef] <asn:1> *native
   fs/exec.c:1874:52: sparse:    got void *__envp
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/namei.c:1928:14: sparse: sparse: symbol 'full_name_hash' redeclared with different type (originally declared at include/linux/stringhash.h:66) - different modifiers
   fs/namei.c:1949:5: sparse: sparse: symbol 'hashlen_string' redeclared with different type (originally declared at include/linux/stringhash.h:77) - different modifiers
   fs/namei.c:604:17: sparse: sparse: context imbalance in 'terminate_walk' - unexpected unlock
   include/linux/rcupdate.h:651:9: sparse: sparse: context imbalance in 'unlazy_walk' - unexpected unlock
   include/linux/rcupdate.h:651:9: sparse: sparse: context imbalance in 'unlazy_child' - unexpected unlock
   fs/namei.c:1727:33: sparse: sparse: context imbalance in 'pick_link' - unexpected unlock
   fs/namei.c:2162:19: sparse: sparse: context imbalance in 'path_init' - wrong count at exit
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/fcntl.c:280:22: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected unsigned long long [usertype] *argp @@    got unsigned long longunsigned long long [usertype] *argp @@
   fs/fcntl.c:280:22: sparse:    expected unsigned long long [usertype] *argp
   fs/fcntl.c:280:22: sparse:    got unsigned long long [noderef] [usertype] <asn:1> *
   fs/fcntl.c:287:34: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected void [noderef] <asn:1> *to @@    got unsignevoid [noderef] <asn:1> *to @@
   fs/fcntl.c:287:34: sparse:    expected void [noderef] <asn:1> *to
   fs/fcntl.c:287:34: sparse:    got unsigned long long [usertype] *argp
   fs/fcntl.c:291:40: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected void const [noderef] <asn:1> *from @@    got uvoid const [noderef] <asn:1> *from @@
   fs/fcntl.c:291:40: sparse:    expected void const [noderef] <asn:1> *from
   fs/fcntl.c:291:40: sparse:    got unsigned long long [usertype] *argp
   fs/fcntl.c:303:34: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected void [noderef] <asn:1> *to @@    got unsignevoid [noderef] <asn:1> *to @@
   fs/fcntl.c:303:34: sparse:    expected void [noderef] <asn:1> *to
   fs/fcntl.c:303:34: sparse:    got unsigned long long [usertype] *argp
   fs/fcntl.c:307:40: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected void const [noderef] <asn:1> *from @@    got uvoid const [noderef] <asn:1> *from @@
   fs/fcntl.c:307:40: sparse:    expected void const [noderef] <asn:1> *from
   fs/fcntl.c:307:40: sparse:    got unsigned long long [usertype] *argp
   fs/fcntl.c:936:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
   fs/fcntl.c:936:9: sparse:    struct fasync_struct [noderef] <asn:4> *
   fs/fcntl.c:936:9: sparse:    struct fasync_struct *
   fs/fcntl.c:1010:22: sparse: sparse: incompatible types in comparison expression (different address spaces):
   fs/fcntl.c:1010:22: sparse:    struct fasync_struct [noderef] <asn:4> *
   fs/fcntl.c:1010:22: sparse:    struct fasync_struct *
   fs/fcntl.c:1021:33: sparse: sparse: incompatible types in comparison expression (different address spaces):
   fs/fcntl.c:1021:33: sparse:    struct fasync_struct [noderef] <asn:4> *
   fs/fcntl.c:1021:33: sparse:    struct fasync_struct *
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   include/linux/rculist_bl.h:24:33: sparse: sparse: incompatible types in comparison expression (different address spaces):
   include/linux/rculist_bl.h:24:33: sparse:    struct hlist_bl_node [noderef] <asn:4> *
   include/linux/rculist_bl.h:24:33: sparse:    struct hlist_bl_node *
   include/linux/rculist_bl.h:24:33: sparse: sparse: incompatible types in comparison expression (different address spaces):
   include/linux/rculist_bl.h:24:33: sparse:    struct hlist_bl_node [noderef] <asn:4> *
   include/linux/rculist_bl.h:24:33: sparse:    struct hlist_bl_node *
   include/linux/rculist_bl.h:17:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
   include/linux/rculist_bl.h:17:9: sparse:    struct hlist_bl_node [noderef] <asn:4> *
   include/linux/rculist_bl.h:17:9: sparse:    struct hlist_bl_node *
   include/linux/rculist_bl.h:17:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
   include/linux/rculist_bl.h:17:9: sparse:    struct hlist_bl_node [noderef] <asn:4> *
   include/linux/rculist_bl.h:17:9: sparse:    struct hlist_bl_node *
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in '__dentry_kill' - unexpected unlock
   fs/dcache.c:622:9: sparse: sparse: context imbalance in '__lock_parent' - wrong count at exit
   fs/dcache.c:686:9: sparse: sparse: context imbalance in 'dentry_kill' - wrong count at exit
   fs/dcache.c:859:17: sparse: sparse: context imbalance in 'dput' - unexpected unlock
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in 'dput_to_list' - unexpected unlock
   include/linux/spinlock.h:338:9: sparse: sparse: context imbalance in 'd_prune_aliases' - different lock contexts for basic block
   fs/dcache.c:1055:13: sparse: sparse: context imbalance in 'shrink_lock_dentry' - different lock contexts for basic block
   include/linux/compiler.h:199:9: sparse: sparse: context imbalance in 'shrink_dentry_list' - different lock contexts for basic block
   fs/dcache.c:1124:24: sparse: sparse: context imbalance in 'dentry_lru_isolate' - wrong count at exit
   fs/dcache.c:1205:24: sparse: sparse: context imbalance in 'dentry_lru_isolate_shrink' - wrong count at exit
   fs/dcache.c:1267:13: sparse: sparse: context imbalance in 'd_walk' - different lock contexts for basic block
   fs/dcache.c:1501:24: sparse: sparse: context imbalance in 'select_collect2' - different lock contexts for basic block
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in 'shrink_dcache_parent' - unexpected unlock
   fs/dcache.c:2674:6: sparse: sparse: context imbalance in 'd_add' - different lock contexts for basic block
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in '__d_move' - unexpected unlock
   fs/dcache.c:3044:16: sparse: sparse: context imbalance in 'd_splice_alias' - different lock contexts for basic block
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/inode.c:724:24: sparse: sparse: context imbalance in 'inode_lru_isolate' - wrong count at exit
   fs/inode.c:813:9: sparse: sparse: context imbalance in 'find_inode' - different lock contexts for basic block
   fs/inode.c:844:9: sparse: sparse: context imbalance in 'find_inode_fast' - different lock contexts for basic block
   fs/inode.c:1450:5: sparse: sparse: context imbalance in 'insert_inode_locked' - wrong count at exit
   include/linux/spinlock.h:378:9: sparse: sparse: context imbalance in 'iput_final' - unexpected unlock
   fs/inode.c:1575:6: sparse: sparse: context imbalance in 'iput' - wrong count at exit
   fs/inode.c:1946:13: sparse: sparse: context imbalance in '__wait_on_freeing_inode' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/file.c:335:17: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct file **old_fds @@    got struct file [noderstruct file **old_fds @@
   fs/file.c:335:17: sparse:    expected struct file **old_fds
   fs/file.c:335:17: sparse:    got struct file [noderef] <asn:4> **fd
   fs/file.c:336:17: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct file **new_fds @@    got struct file [noderstruct file **new_fds @@
   fs/file.c:336:17: sparse:    expected struct file **new_fds
   fs/file.c:336:17: sparse:    got struct file [noderef] <asn:4> **fd
   fs/file.c:351:17: sparse: sparse: incompatible types in comparison expression (different address spaces):
   fs/file.c:351:17: sparse:    struct file [noderef] <asn:4> *
   fs/file.c:351:17: sparse:    struct file *
   fs/file.c:386:54: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected struct file *file @@    got struct file [noderef] <asn:4>struct file *file @@
   fs/file.c:386:54: sparse:    expected struct file *file
   fs/file.c:386:54: sparse:    got struct file [noderef] <asn:4> *[assigned] __ret
   fs/file.c:451:28: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected struct fdtable [noderef] <asn:4> *fdt @@    got deref] <asn:4> *fdt @@
   fs/file.c:451:28: sparse:    expected struct fdtable [noderef] <asn:4> *fdt
   fs/file.c:451:28: sparse:    got struct fdtable *
   fs/file.c:630:14: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct file *file @@    got struct file [noderstruct file *file @@
   fs/file.c:630:14: sparse:    expected struct file *file
   fs/file.c:630:14: sparse:    got struct file [noderef] <asn:4> *
   fs/file.c:657:14: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct file *file @@    got struct file [noderstruct file *file @@
   fs/file.c:657:14: sparse:    expected struct file *file
   fs/file.c:657:14: sparse:    got struct file [noderef] <asn:4> *
   fs/file.c:694:30: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct file *file @@    got struct file [noderstruct file *file @@
   fs/file.c:694:30: sparse:    expected struct file *file
   fs/file.c:694:30: sparse:    got struct file [noderef] <asn:4> *
   fs/file.c:865:16: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct file *tofree @@    got struct file [noderstruct file *tofree @@
   fs/file.c:865:16: sparse:    expected struct file *tofree
   fs/file.c:865:16: sparse:    got struct file [noderef] <asn:4> *
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/namespace.c:1731:22: sparse: sparse: symbol 'to_mnt_ns' was not declared. Should it be static?
   fs/namespace.c:2392:35: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected char const [noderef] <asn:1> *name @@    got f] <asn:1> *name @@
   fs/namespace.c:2392:35: sparse:    expected char const [noderef] <asn:1> *name
   fs/namespace.c:2392:35: sparse:    got char const *filename
   fs/namespace.c:3541:38: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected char const [noderef] <asn:1> *name @@    got f] <asn:1> *name @@
   fs/namespace.c:3541:38: sparse:    expected char const [noderef] <asn:1> *name
   fs/namespace.c:3541:38: sparse:    got char const *from_pathname
   fs/namespace.c:3550:36: sparse: sparse: incorrect type in argument 2 (different address spaces) @@    expected char const [noderef] <asn:1> *name @@    got f] <asn:1> *name @@
   fs/namespace.c:3550:36: sparse:    expected char const [noderef] <asn:1> *name
   fs/namespace.c:3550:36: sparse:    got char const *to_pathname
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/seq_file.c:1048:24: sparse: sparse: incompatible types in comparison expression (different address spaces):
   fs/seq_file.c:1048:24: sparse:    struct hlist_node [noderef] <asn:4> *
   fs/seq_file.c:1048:24: sparse:    struct hlist_node *
   fs/seq_file.c:1050:24: sparse: sparse: incompatible types in comparison expression (different address spaces):
   fs/seq_file.c:1050:24: sparse:    struct hlist_node [noderef] <asn:4> *
   fs/seq_file.c:1050:24: sparse:    struct hlist_node *
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/fs-writeback.c:1702:17: sparse: sparse: context imbalance in 'writeback_sb_inodes' - unexpected unlock
   fs/fs-writeback.c:2194:9: sparse: sparse: context imbalance in 'block_dump___mark_inode_dirty' - different lock contexts for basic block
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/d_path.c:155:9: sparse: sparse: context imbalance in 'prepend_path' - wrong count at exit
   include/linux/err.h:24:20: sparse: sparse: context imbalance in '__dentry_path' - different lock contexts for basic block
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/fs_struct.c:163:18: sparse: sparse: symbol 'init_fs' was not declared. Should it be static?
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/fs_pin.c:41:17: sparse: sparse: context imbalance in 'pin_kill' - unexpected unlock
   include/linux/rcupdate.h:59:9: sparse: sparse: context imbalance in 'mnt_pin_kill' - different lock contexts for basic block
   include/linux/rcupdate.h:59:9: sparse: sparse: context imbalance in 'group_pin_kill' - different lock contexts for basic block
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/buffer.c:3358:13: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected int [noderef] <asn:3> *__p @@    got :3> *__p @@
   fs/buffer.c:3358:13: sparse:    expected int [noderef] <asn:3> *__p
   fs/buffer.c:3358:13: sparse:    got int *
   arch/x86/include/asm/bitops.h:77:37: sparse: sparse: cast truncates bits from constant value (ffffff7f becomes 7f)
   fs/buffer.c:3358:13: sparse: sparse: dereference of noderef expression
   fs/buffer.c:3358:13: sparse: sparse: dereference of noderef expression
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/open.c:757:13: sparse: sparse: restricted fmode_t degrades to integer
   fs/open.c:975:18: sparse: sparse: restricted fmode_t degrades to integer
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/nls/nls_base.c:124:20: sparse: sparse: incorrect type in assignment (different base types) @@    expected unsigned short [usertype] @@    got resunsigned short [usertype] @@
   fs/nls/nls_base.c:124:20: sparse:    expected unsigned short [usertype]
   fs/nls/nls_base.c:124:20: sparse:    got restricted __le16 [usertype]
   fs/nls/nls_base.c:127:20: sparse: sparse: incorrect type in assignment (different base types) @@    expected unsigned short [usertype] @@    got resunsigned short [usertype] @@
   fs/nls/nls_base.c:127:20: sparse:    expected unsigned short [usertype]
   fs/nls/nls_base.c:127:20: sparse:    got restricted __be16 [usertype]
   fs/nls/nls_base.c:124:20: sparse: sparse: incorrect type in assignment (different base types) @@    expected unsigned short [usertype] @@    got resunsigned short [usertype] @@
   fs/nls/nls_base.c:124:20: sparse:    expected unsigned short [usertype]
   fs/nls/nls_base.c:124:20: sparse:    got restricted __le16 [usertype]
   fs/nls/nls_base.c:127:20: sparse: sparse: incorrect type in assignment (different base types) @@    expected unsigned short [usertype] @@    got resunsigned short [usertype] @@
   fs/nls/nls_base.c:127:20: sparse:    expected unsigned short [usertype]
   fs/nls/nls_base.c:127:20: sparse:    got restricted __be16 [usertype]
   fs/nls/nls_base.c:124:20: sparse: sparse: incorrect type in assignment (different base types) @@    expected unsigned short [usertype] @@    got resunsigned short [usertype] @@
   fs/nls/nls_base.c:124:20: sparse:    expected unsigned short [usertype]
   fs/nls/nls_base.c:124:20: sparse:    got restricted __le16 [usertype]
   fs/nls/nls_base.c:127:20: sparse: sparse: incorrect type in assignment (different base types) @@    expected unsigned short [usertype] @@    got resunsigned short [usertype] @@
   fs/nls/nls_base.c:127:20: sparse:    expected unsigned short [usertype]
   fs/nls/nls_base.c:127:20: sparse:    got restricted __be16 [usertype]
   fs/nls/nls_base.c:124:20: sparse: sparse: incorrect type in assignment (different base types) @@    expected unsigned short [usertype] @@    got resunsigned short [usertype] @@
   fs/nls/nls_base.c:124:20: sparse:    expected unsigned short [usertype]
   fs/nls/nls_base.c:124:20: sparse:    got restricted __le16 [usertype]
   fs/nls/nls_base.c:127:20: sparse: sparse: incorrect type in assignment (different base types) @@    expected unsigned short [usertype] @@    got resunsigned short [usertype] @@
   fs/nls/nls_base.c:127:20: sparse:    expected unsigned short [usertype]
   fs/nls/nls_base.c:127:20: sparse:    got restricted __be16 [usertype]
   fs/nls/nls_base.c:180:24: sparse: sparse: cast to restricted __le16
   fs/nls/nls_base.c:182:24: sparse: sparse: cast to restricted __be16
   fs/nls/nls_base.c:182:24: sparse: sparse: cast to restricted __be16
   fs/nls/nls_base.c:182:24: sparse: sparse: cast to restricted __be16
   fs/nls/nls_base.c:182:24: sparse: sparse: cast to restricted __be16
   fs/nls/nls_base.c:180:24: sparse: sparse: cast to restricted __le16
   fs/nls/nls_base.c:182:24: sparse: sparse: cast to restricted __be16
   fs/nls/nls_base.c:182:24: sparse: sparse: cast to restricted __be16
   fs/nls/nls_base.c:182:24: sparse: sparse: cast to restricted __be16
   fs/nls/nls_base.c:182:24: sparse: sparse: cast to restricted __be16
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/notify/fsnotify.c:239:16: sparse: sparse: incompatible types in comparison expression (different address spaces):
   fs/notify/fsnotify.c:239:16: sparse:    struct fsnotify_mark_connector [noderef] <asn:4> *
   fs/notify/fsnotify.c:239:16: sparse:    struct fsnotify_mark_connector *
   fs/notify/fsnotify.c:241:24: sparse: sparse: incompatible types in comparison expression (different address spaces):
   fs/notify/fsnotify.c:241:24: sparse:    struct hlist_node [noderef] <asn:4> *
   fs/notify/fsnotify.c:241:24: sparse:    struct hlist_node *
   fs/notify/fsnotify.c:251:24: sparse: sparse: incompatible types in comparison expression (different address spaces):
   fs/notify/fsnotify.c:251:24: sparse:    struct hlist_node [noderef] <asn:4> *
   fs/notify/fsnotify.c:251:24: sparse:    struct hlist_node *
   fs/notify/fsnotify.c:354:38: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct fsnotify_mark_connector **connp @@    got struct fsnotify_mastruct fsnotify_mark_connector **connp @@
   fs/notify/fsnotify.c:354:38: sparse:    expected struct fsnotify_mark_connector **connp
   fs/notify/fsnotify.c:354:38: sparse:    got struct fsnotify_mark_connector [noderef] <asn:4> **
   fs/notify/fsnotify.c:356:38: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct fsnotify_mark_connector **connp @@    got struct fsnotify_mastruct fsnotify_mark_connector **connp @@
   fs/notify/fsnotify.c:356:38: sparse:    expected struct fsnotify_mark_connector **connp
   fs/notify/fsnotify.c:356:38: sparse:    got struct fsnotify_mark_connector [noderef] <asn:4> **
   fs/notify/fsnotify.c:359:46: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct fsnotify_mark_connector **connp @@    got struct fsnotify_mastruct fsnotify_mark_connector **connp @@
   fs/notify/fsnotify.c:359:46: sparse:    expected struct fsnotify_mark_connector **connp
   fs/notify/fsnotify.c:359:46: sparse:    got struct fsnotify_mark_connector [noderef] <asn:4> **
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/notify/mark.c:500:13: sparse: sparse: incorrect type in initializer (different address spaces) @@    expected struct fsnotify_mark_connector [noderef] <asn:4> *__new @@    got fsnotify_mark_connector [noderef] <asn:4> *__new @@
   fs/notify/mark.c:500:13: sparse:    expected struct fsnotify_mark_connector [noderef] <asn:4> *__new
   fs/notify/mark.c:500:13: sparse:    got struct fsnotify_mark_connector *[assigned] conn
   fs/notify/mark.c:255:9: sparse: sparse: context imbalance in 'fsnotify_put_mark' - unexpected unlock
   include/linux/srcu.h:181:9: sparse: sparse: context imbalance in 'fsnotify_prepare_user_wait' - unexpected unlock
   fs/notify/mark.c:357:9: sparse: sparse: context imbalance in 'fsnotify_finish_user_wait' - wrong count at exit
   fs/notify/mark.c:516:39: sparse: sparse: context imbalance in 'fsnotify_grab_connector' - different lock contexts for basic block
   fs/notify/mark.c:626:20: sparse: sparse: context imbalance in 'fsnotify_add_mark_list' - unexpected unlock
   fs/notify/mark.c:709:25: sparse: sparse: context imbalance in 'fsnotify_find_mark' - unexpected unlock
   fs/notify/mark.c:783:17: sparse: sparse: context imbalance in 'fsnotify_destroy_marks' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/notify/fdinfo.c:53:87: sparse: sparse: Using plain integer as NULL pointer
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   arch/x86/include/asm/microcode_amd.h:56:6: sparse: sparse: symbol 'reload_ucode_amd' was not declared. Should it be static?
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   kernel/irq/irqdesc.c:869:17: sparse: sparse: context imbalance in '__irq_get_desc_lock' - wrong count at exit
   kernel/irq/irqdesc.c:893:6: sparse: sparse: context imbalance in '__irq_put_desc_unlock' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/proc/inode.c:50:17: sparse: sparse: incompatible types in comparison expression (different address spaces):
   fs/proc/inode.c:50:17: sparse:    struct ctl_table_header [noderef] <asn:4> *
   fs/proc/inode.c:50:17: sparse:    struct ctl_table_header *
   fs/proc/inode.c:155:17: sparse: sparse: context imbalance in 'close_pdeo' - unexpected unlock
   fs/proc/inode.c:189:27: sparse: sparse: context imbalance in 'proc_entry_rundown' - different lock contexts for basic block
   fs/proc/inode.c:385:12: sparse: sparse: context imbalance in 'proc_reg_release' - wrong count at exit
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/proc/base.c:2087:25: sparse: sparse: cast to restricted fmode_t
   fs/proc/base.c:2144:42: sparse: sparse: cast from restricted fmode_t
   fs/proc/base.c:2241:48: sparse: sparse: cast from restricted fmode_t
   fs/proc/base.c:1083:36: sparse: sparse: context imbalance in '__set_oom_adj' - unexpected unlock
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/proc/array.c:515:44: sparse: sparse: incorrect type in argument 1 (different address spaces) @@    expected struct task_struct *tsk @@    got struct task_structstruct task_struct *tsk @@
   fs/proc/array.c:515:44: sparse:    expected struct task_struct *tsk
   fs/proc/array.c:515:44: sparse:    got struct task_struct [noderef] <asn:4> *real_parent
   fs/proc/array.c:293:9: sparse: sparse: context imbalance in 'proc_pid_status' - different lock contexts for basic block
   fs/proc/array.c:521:9: sparse: sparse: context imbalance in 'do_task_stat' - different lock contexts for basic block
--
>> include/linux/memory_hotplug.h:342:5: sparse: sparse: undefined preprocessor identifier 'CONFIG_MEMORY_HOTPLUG_SPARSE'
   fs/proc/proc_sysctl.c:279:22: sparse: sparse: incorrect type in assignment (different address spaces) @@    expected struct hlist_node *node @@    got struct hlist_node struct hlist_node *node @@
   fs/proc/proc_sysctl.c:279:22: sparse:    expected struct hlist_node *node
   fs/proc/proc_sysctl.c:279:22: sparse:    got struct hlist_node [noderef] <asn:4> *
   fs/proc/proc_sysctl.c:940:16: sparse: sparse: incompatible types in comparison expression (different address spaces):
   fs/proc/proc_sysctl.c:940:16: sparse:    struct ctl_table_header [noderef] <asn:4> *
   fs/proc/proc_sysctl.c:940:16: sparse:    struct ctl_table_header *
   fs/proc/proc_sysctl.c:317:17: sparse: sparse: context imbalance in 'start_unregistering' - unexpected unlock

vim +/CONFIG_MEMORY_HOTPLUG_SPARSE +342 include/linux/memory_hotplug.h

   341	
 > 342	#if CONFIG_MEMORY_HOTPLUG_SPARSE
   343	int check_hotplug_memory_addressable(unsigned long pfn,
   344			unsigned long nr_pages);
   345	#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
   346	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 09/10] powerpc: Enable OpenCAPI Storage Class Memory driver on bare metal
  2019-10-25  4:47 ` [PATCH 09/10] powerpc: Enable OpenCAPI Storage Class Memory driver on bare metal Alastair D'Silva
@ 2019-10-28  2:43   ` Oliver O'Halloran
  2019-11-08  7:10   ` Frederic Barrat
  1 sibling, 0 replies; 45+ messages in thread
From: Oliver O'Halloran @ 2019-10-28  2:43 UTC (permalink / raw)
  To: Alastair D'Silva
  Cc: alastair, Michael Ellerman, Frederic Barrat, Andrew Donnellan,
	linuxppc-dev, Linux Kernel Mailing List, linux-nvdimm, Linux MM

On Fri, Oct 25, 2019 at 3:51 PM Alastair D'Silva <alastair@au1.ibm.com> wrote:
>
> From: Alastair D'Silva <alastair@d-silva.org>
>
> Enable OpenCAPI Storage Class Memory driver on bare metal
>
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> ---
>  arch/powerpc/configs/powernv_defconfig | 4 ++++
>  1 file changed, 4 insertions(+)
>
> diff --git a/arch/powerpc/configs/powernv_defconfig b/arch/powerpc/configs/powernv_defconfig
> index 6658cceb928c..45c0eff94964 100644
> --- a/arch/powerpc/configs/powernv_defconfig
> +++ b/arch/powerpc/configs/powernv_defconfig
> @@ -352,3 +352,7 @@ CONFIG_KVM_BOOK3S_64=m
>  CONFIG_KVM_BOOK3S_64_HV=m
>  CONFIG_VHOST_NET=m
>  CONFIG_PRINTK_TIME=y
> +CONFIG_OCXL_SCM=m

> +CONFIG_DEV_DAX=y
> +CONFIG_DEV_DAX_PMEM=y

These should probably be modules. Having them as builtins will force
their dependencies (i.e. libnvdimm) to be built into the kernel too.

> +CONFIG_FS_DAX=y
> --
> 2.21.0
> _______________________________________________
> Linux-nvdimm mailing list -- linux-nvdimm@lists.01.org
> To unsubscribe send an email to linux-nvdimm-leave@lists.01.org


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory
  2019-10-25  4:46 ` [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory Alastair D'Silva
                     ` (2 preceding siblings ...)
  2019-10-28  2:34   ` kbuild test robot
@ 2019-10-29  1:49   ` Andrew Donnellan
  2019-10-29  2:44     ` Alastair D'Silva
  2019-11-07 17:59   ` Frederic Barrat
  4 siblings, 1 reply; 45+ messages in thread
From: Andrew Donnellan @ 2019-10-29  1:49 UTC (permalink / raw)
  To: Alastair D'Silva, alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Frederic Barrat, Arnd Bergmann, Greg Kroah-Hartman, Dan Williams,
	Vishal Verma, Dave Jiang, Keith Busch, Ira Weiny,
	Geert Uytterhoeven, Krzysztof Kozlowski, Anton Blanchard,
	Allison Randal, Cédric Le Goater, David Gibson,
	Vasant Hegde, Thomas Gleixner, Anju T Sudhakar, Hari Bathini,
	Mahesh Salgaonkar, Greg Kurz, Masahiro Yamada,
	Alexey Kardashevskiy, Andrew Morton, David Hildenbrand,
	Oscar Salvador, Michal Hocko, Pavel Tatashin, Wei Yang, Qian Cai,
	linuxppc-dev, linux-kernel, linux-nvdimm, linux-mm

On 25/10/19 3:46 pm, Alastair D'Silva wrote:
> From: Alastair D'Silva <alastair@d-silva.org>
> 
> This patch adds platform support to map & release LPC memory.
>  > Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> ---
>   arch/powerpc/include/asm/pnv-ocxl.h   |  2 ++
>   arch/powerpc/platforms/powernv/ocxl.c | 41 +++++++++++++++++++++++++++
>   include/linux/memory_hotplug.h        |  5 ++++
>   mm/memory_hotplug.c                   |  3 +-
>   4 files changed, 50 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/powerpc/include/asm/pnv-ocxl.h b/arch/powerpc/include/asm/pnv-ocxl.h
> index 7de82647e761..f8f8ffb48aa8 100644
> --- a/arch/powerpc/include/asm/pnv-ocxl.h
> +++ b/arch/powerpc/include/asm/pnv-ocxl.h
> @@ -32,5 +32,7 @@ extern int pnv_ocxl_spa_remove_pe_from_cache(void *platform_data, int pe_handle)
>   
>   extern int pnv_ocxl_alloc_xive_irq(u32 *irq, u64 *trigger_addr);
>   extern void pnv_ocxl_free_xive_irq(u32 irq);
> +extern u64 pnv_ocxl_platform_lpc_setup(struct pci_dev *pdev, u64 size);
> +extern void pnv_ocxl_platform_lpc_release(struct pci_dev *pdev);
>   
>   #endif /* _ASM_PNV_OCXL_H */
> diff --git a/arch/powerpc/platforms/powernv/ocxl.c b/arch/powerpc/platforms/powernv/ocxl.c
> index 8c65aacda9c8..c6d4234e0aba 100644
> --- a/arch/powerpc/platforms/powernv/ocxl.c
> +++ b/arch/powerpc/platforms/powernv/ocxl.c
> @@ -475,6 +475,47 @@ void pnv_ocxl_spa_release(void *platform_data)
>   }
>   EXPORT_SYMBOL_GPL(pnv_ocxl_spa_release);
>   
> +u64 pnv_ocxl_platform_lpc_setup(struct pci_dev *pdev, u64 size)
> +{
> +	struct pci_controller *hose = pci_bus_to_host(pdev->bus);
> +	struct pnv_phb *phb = hose->private_data;
> +	u32 bdfn = (pdev->bus->number << 8) | pdev->devfn;

I think pci_dev_id() is the canonical way to do this? (same applies in 
release)

> +	u64 base_addr = 0;
> +	int rc;
> +
> +	rc = opal_npu_mem_alloc(phb->opal_id, bdfn, size, &base_addr);
> +	if (rc) {
> +		dev_warn(&pdev->dev,
> +			 "OPAL could not allocate LPC memory, rc=%d\n", rc);
> +		return 0;
> +	}
> +
> +	base_addr = be64_to_cpu(base_addr);

sparse doesn't like this, the way it's usually done is declare a __be64 
variable, pass that to the opal call, then store the conversion in a 
regular u64

> +
> +	rc = check_hotplug_memory_addressable(base_addr >> PAGE_SHIFT,
> +					      size >> PAGE_SHIFT);
> +	if (rc)
> +		return 0;
> +
> +	return base_addr;
> +}
> +EXPORT_SYMBOL_GPL(pnv_ocxl_platform_lpc_setup);
> +
> +void pnv_ocxl_platform_lpc_release(struct pci_dev *pdev)
> +{
> +	struct pci_controller *hose = pci_bus_to_host(pdev->bus);
> +	struct pnv_phb *phb = hose->private_data;
> +	u32 bdfn = (pdev->bus->number << 8) | pdev->devfn;
> +	int rc;
> +
> +	rc = opal_npu_mem_release(phb->opal_id, bdfn);
> +	if (rc)
> +		dev_warn(&pdev->dev,
> +			 "OPAL reported rc=%d when releasing LPC memory\n", rc);
> +}
> +EXPORT_SYMBOL_GPL(pnv_ocxl_platform_lpc_release);
> +
> +
>   int pnv_ocxl_spa_remove_pe_from_cache(void *platform_data, int pe_handle)
>   {
>   	struct spa_data *data = (struct spa_data *) platform_data;
> diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
> index f46ea71b4ffd..3f5f1a642abe 100644
> --- a/include/linux/memory_hotplug.h
> +++ b/include/linux/memory_hotplug.h
> @@ -339,6 +339,11 @@ static inline int remove_memory(int nid, u64 start, u64 size)
>   static inline void __remove_memory(int nid, u64 start, u64 size) {}
>   #endif /* CONFIG_MEMORY_HOTREMOVE */
>   
> +#if CONFIG_MEMORY_HOTPLUG_SPARSE

This needs to be #ifdef.

> +int check_hotplug_memory_addressable(unsigned long pfn,
> +		unsigned long nr_pages);
> +#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
> +
>   extern void __ref free_area_init_core_hotplug(int nid);
>   extern int __add_memory(int nid, u64 start, u64 size);
>   extern int add_memory(int nid, u64 start, u64 size);
> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
> index 2cecf07b396f..b39827dbd071 100644
> --- a/mm/memory_hotplug.c
> +++ b/mm/memory_hotplug.c
> @@ -278,7 +278,7 @@ static int check_pfn_span(unsigned long pfn, unsigned long nr_pages,
>   	return 0;
>   }
>   
> -static int check_hotplug_memory_addressable(unsigned long pfn,
> +int check_hotplug_memory_addressable(unsigned long pfn,
>   					    unsigned long nr_pages)
>   {
>   	const u64 max_addr = PFN_PHYS(pfn + nr_pages) - 1;
> @@ -294,6 +294,7 @@ static int check_hotplug_memory_addressable(unsigned long pfn,
>   
>   	return 0;
>   }
> +EXPORT_SYMBOL_GPL(check_hotplug_memory_addressable);

This export seems unnecessary, you don't seem to be using this function 
in a module anywhere in this series AFAICT.

Also it looks like a whitespace fix from removing the static ended up in 
patch #8 rather than here.

>   
>   /*
>    * Reasonably generic function for adding memory.  It is
> 

-- 
Andrew Donnellan              OzLabs, ADL Canberra
ajd@linux.ibm.com             IBM Australia Limited



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory
  2019-10-29  1:49   ` Andrew Donnellan
@ 2019-10-29  2:44     ` Alastair D'Silva
  2019-10-29  2:47       ` Andrew Donnellan
  0 siblings, 1 reply; 45+ messages in thread
From: Alastair D'Silva @ 2019-10-29  2:44 UTC (permalink / raw)
  To: Andrew Donnellan
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Frederic Barrat, Arnd Bergmann, Greg Kroah-Hartman, Dan Williams,
	Vishal Verma, Dave Jiang, Keith Busch, Ira Weiny,
	Geert Uytterhoeven, Krzysztof Kozlowski, Anton Blanchard,
	Allison Randal, Cédric Le Goater, David Gibson,
	Vasant Hegde, Thomas Gleixner, Anju T Sudhakar, Hari Bathini,
	Mahesh Salgaonkar, Greg Kurz, Masahiro Yamada,
	Alexey Kardashevskiy, Andrew Morton, David Hildenbrand,
	Oscar Salvador, Michal Hocko, Pavel Tatashin, Wei Yang, Qian Cai,
	linuxppc-dev, linux-kernel, linux-nvdimm, linux-mm

On Tue, 2019-10-29 at 12:49 +1100, Andrew Donnellan wrote:
> On 25/10/19 3:46 pm, Alastair D'Silva wrote:
> > From: Alastair D'Silva <alastair@d-silva.org>
> > 
> > This patch adds platform support to map & release LPC memory.
> >  > Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> > ---
> >   arch/powerpc/include/asm/pnv-ocxl.h   |  2 ++
> >   arch/powerpc/platforms/powernv/ocxl.c | 41
> > +++++++++++++++++++++++++++
> >   include/linux/memory_hotplug.h        |  5 ++++
> >   mm/memory_hotplug.c                   |  3 +-
> >   4 files changed, 50 insertions(+), 1 deletion(-)
> > 
> > diff --git a/arch/powerpc/include/asm/pnv-ocxl.h
> > b/arch/powerpc/include/asm/pnv-ocxl.h
> > index 7de82647e761..f8f8ffb48aa8 100644
> > --- a/arch/powerpc/include/asm/pnv-ocxl.h
> > +++ b/arch/powerpc/include/asm/pnv-ocxl.h
> > @@ -32,5 +32,7 @@ extern int pnv_ocxl_spa_remove_pe_from_cache(void
> > *platform_data, int pe_handle)
> >   
> >   extern int pnv_ocxl_alloc_xive_irq(u32 *irq, u64 *trigger_addr);
> >   extern void pnv_ocxl_free_xive_irq(u32 irq);
> > +extern u64 pnv_ocxl_platform_lpc_setup(struct pci_dev *pdev, u64
> > size);
> > +extern void pnv_ocxl_platform_lpc_release(struct pci_dev *pdev);
> >   
> >   #endif /* _ASM_PNV_OCXL_H */
> > diff --git a/arch/powerpc/platforms/powernv/ocxl.c
> > b/arch/powerpc/platforms/powernv/ocxl.c
> > index 8c65aacda9c8..c6d4234e0aba 100644
> > --- a/arch/powerpc/platforms/powernv/ocxl.c
> > +++ b/arch/powerpc/platforms/powernv/ocxl.c
> > @@ -475,6 +475,47 @@ void pnv_ocxl_spa_release(void *platform_data)
> >   }
> >   EXPORT_SYMBOL_GPL(pnv_ocxl_spa_release);
> >   
> > +u64 pnv_ocxl_platform_lpc_setup(struct pci_dev *pdev, u64 size)
> > +{
> > +	struct pci_controller *hose = pci_bus_to_host(pdev->bus);
> > +	struct pnv_phb *phb = hose->private_data;
> > +	u32 bdfn = (pdev->bus->number << 8) | pdev->devfn;
> 
> I think pci_dev_id() is the canonical way to do this? (same applies
> in 
> release)

Thanks.

> 
> > +	u64 base_addr = 0;
> > +	int rc;
> > +
> > +	rc = opal_npu_mem_alloc(phb->opal_id, bdfn, size, &base_addr);
> > +	if (rc) {
> > +		dev_warn(&pdev->dev,
> > +			 "OPAL could not allocate LPC memory, rc=%d\n",
> > rc);
> > +		return 0;
> > +	}
> > +
> > +	base_addr = be64_to_cpu(base_addr);
> 
> sparse doesn't like this, the way it's usually done is declare a
> __be64 
> variable, pass that to the opal call, then store the conversion in a 
> regular u64
> 

Ok.

> > +
> > +	rc = check_hotplug_memory_addressable(base_addr >> PAGE_SHIFT,
> > +					      size >> PAGE_SHIFT);
> > +	if (rc)
> > +		return 0;
> > +
> > +	return base_addr;
> > +}
> > +EXPORT_SYMBOL_GPL(pnv_ocxl_platform_lpc_setup);
> > +
> > +void pnv_ocxl_platform_lpc_release(struct pci_dev *pdev)
> > +{
> > +	struct pci_controller *hose = pci_bus_to_host(pdev->bus);
> > +	struct pnv_phb *phb = hose->private_data;
> > +	u32 bdfn = (pdev->bus->number << 8) | pdev->devfn;
> > +	int rc;
> > +
> > +	rc = opal_npu_mem_release(phb->opal_id, bdfn);
> > +	if (rc)
> > +		dev_warn(&pdev->dev,
> > +			 "OPAL reported rc=%d when releasing LPC
> > memory\n", rc);
> > +}
> > +EXPORT_SYMBOL_GPL(pnv_ocxl_platform_lpc_release);
> > +
> > +
> >   int pnv_ocxl_spa_remove_pe_from_cache(void *platform_data, int
> > pe_handle)
> >   {
> >   	struct spa_data *data = (struct spa_data *) platform_data;
> > diff --git a/include/linux/memory_hotplug.h
> > b/include/linux/memory_hotplug.h
> > index f46ea71b4ffd..3f5f1a642abe 100644
> > --- a/include/linux/memory_hotplug.h
> > +++ b/include/linux/memory_hotplug.h
> > @@ -339,6 +339,11 @@ static inline int remove_memory(int nid, u64
> > start, u64 size)
> >   static inline void __remove_memory(int nid, u64 start, u64 size)
> > {}
> >   #endif /* CONFIG_MEMORY_HOTREMOVE */
> >   
> > +#if CONFIG_MEMORY_HOTPLUG_SPARSE
> 
> This needs to be #ifdef.
> 

Yup, 0 day caught that one too :)

> > +int check_hotplug_memory_addressable(unsigned long pfn,
> > +		unsigned long nr_pages);
> > +#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
> > +
> >   extern void __ref free_area_init_core_hotplug(int nid);
> >   extern int __add_memory(int nid, u64 start, u64 size);
> >   extern int add_memory(int nid, u64 start, u64 size);
> > diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
> > index 2cecf07b396f..b39827dbd071 100644
> > --- a/mm/memory_hotplug.c
> > +++ b/mm/memory_hotplug.c
> > @@ -278,7 +278,7 @@ static int check_pfn_span(unsigned long pfn,
> > unsigned long nr_pages,
> >   	return 0;
> >   }
> >   
> > -static int check_hotplug_memory_addressable(unsigned long pfn,
> > +int check_hotplug_memory_addressable(unsigned long pfn,
> >   					    unsigned long nr_pages)
> >   {
> >   	const u64 max_addr = PFN_PHYS(pfn + nr_pages) - 1;
> > @@ -294,6 +294,7 @@ static int
> > check_hotplug_memory_addressable(unsigned long pfn,
> >   
> >   	return 0;
> >   }
> > +EXPORT_SYMBOL_GPL(check_hotplug_memory_addressable);
> 
> This export seems unnecessary, you don't seem to be using this
> function 
> in a module anywhere in this series AFAICT.
> 

This is used within this patch, in the map call.

> Also it looks like a whitespace fix from removing the static ended up
> in 
> patch #8 rather than here.

Thanks.

> 
> >   
> >   /*
> >    * Reasonably generic function for adding memory.  It is
> > 

-- 
Alastair D'Silva
Open Source Developer
Linux Technology Centre, IBM Australia
mob: 0423 762 819



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory
  2019-10-29  2:44     ` Alastair D'Silva
@ 2019-10-29  2:47       ` Andrew Donnellan
  0 siblings, 0 replies; 45+ messages in thread
From: Andrew Donnellan @ 2019-10-29  2:47 UTC (permalink / raw)
  To: Alastair D'Silva
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Frederic Barrat, Arnd Bergmann, Greg Kroah-Hartman, Dan Williams,
	Vishal Verma, Dave Jiang, Keith Busch, Ira Weiny,
	Geert Uytterhoeven, Krzysztof Kozlowski, Anton Blanchard,
	Allison Randal, Cédric Le Goater, David Gibson,
	Vasant Hegde, Thomas Gleixner, Anju T Sudhakar, Hari Bathini,
	Mahesh Salgaonkar, Greg Kurz, Masahiro Yamada,
	Alexey Kardashevskiy, Andrew Morton, David Hildenbrand,
	Oscar Salvador, Michal Hocko, Pavel Tatashin, Wei Yang, Qian Cai,
	linuxppc-dev, linux-kernel, linux-nvdimm, linux-mm

On 29/10/19 1:44 pm, Alastair D'Silva wrote:
> This is used within this patch, in the map call.

But not in a module, which is what you need to export the symbol for.

-- 
Andrew Donnellan              OzLabs, ADL Canberra
ajd@linux.ibm.com             IBM Australia Limited



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 07/10] ocxl: Save the device serial number in ocxl_fn
  2019-10-25  4:47 ` [PATCH 07/10] ocxl: Save the device serial number in ocxl_fn Alastair D'Silva
@ 2019-11-06  3:10   ` Andrew Donnellan
  2019-11-07 18:18   ` Frederic Barrat
  1 sibling, 0 replies; 45+ messages in thread
From: Andrew Donnellan @ 2019-11-06  3:10 UTC (permalink / raw)
  To: Alastair D'Silva, alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Frederic Barrat, Arnd Bergmann, Greg Kroah-Hartman, Dan Williams,
	Vishal Verma, Dave Jiang, Keith Busch, Ira Weiny,
	Krzysztof Kozlowski, Anton Blanchard, Madhavan Srinivasan,
	Thomas Gleixner, Mahesh Salgaonkar, Vasant Hegde, Hari Bathini,
	Cédric Le Goater, David Gibson, Greg Kurz, Masahiro Yamada,
	Nicholas Piggin, Andrew Morton, David Hildenbrand,
	Oscar Salvador, Michal Hocko, Pavel Tatashin, Wei Yang, Qian Cai,
	linuxppc-dev, linux-kernel, linux-nvdimm, linux-mm

On 25/10/19 3:47 pm, Alastair D'Silva wrote:
> From: Alastair D'Silva <alastair@d-silva.org>
> 
> This patch retrieves the serial number of the card and makes it available
> to consumers of the ocxl driver via the ocxl_fn struct.
> 
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>

Acked-by: Andrew Donnellan <ajd@linux.ibm.com>


-- 
Andrew Donnellan              OzLabs, ADL Canberra
ajd@linux.ibm.com             IBM Australia Limited



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 10/10] ocxl: Conditionally bind SCM devices to the generic OCXL driver
  2019-10-25  4:47 ` [PATCH 10/10] ocxl: Conditionally bind SCM devices to the generic OCXL driver Alastair D'Silva
  2019-10-26  6:43   ` Christoph Hellwig
@ 2019-11-06  3:46   ` Andrew Donnellan
  2019-11-07 18:08   ` Frederic Barrat
  2 siblings, 0 replies; 45+ messages in thread
From: Andrew Donnellan @ 2019-11-06  3:46 UTC (permalink / raw)
  To: Alastair D'Silva, alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Frederic Barrat, Arnd Bergmann, Greg Kroah-Hartman, Dan Williams,
	Vishal Verma, Dave Jiang, Keith Busch, Ira Weiny,
	Geert Uytterhoeven, Anton Blanchard, Krzysztof Kozlowski,
	Vasant Hegde, David Gibson, Hari Bathini, Allison Randal,
	Anju T Sudhakar, Mahesh Salgaonkar, Cédric Le Goater,
	Thomas Gleixner, Greg Kurz, Nicholas Piggin, Masahiro Yamada,
	Alexey Kardashevskiy, Andrew Morton, David Hildenbrand,
	Oscar Salvador, Michal Hocko, Pavel Tatashin, Wei Yang, Qian Cai,
	linuxppc-dev, linux-kernel, linux-nvdimm, linux-mm

On 25/10/19 3:47 pm, Alastair D'Silva wrote:
> From: Alastair D'Silva <alastair@d-silva.org>
> 
> This patch allows the user to bind OpenCAPI SCM devices to the generic OCXL
> driver.
> 
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>

Agree that this needs more explanation - both in the commit and the 
Kconfig help.

> diff --git a/drivers/misc/ocxl/pci.c b/drivers/misc/ocxl/pci.c
> index cb920aa88d3a..7137055c1883 100644
> --- a/drivers/misc/ocxl/pci.c
> +++ b/drivers/misc/ocxl/pci.c
> @@ -10,6 +10,9 @@
>    */
>   static const struct pci_device_id ocxl_pci_tbl[] = {
>   	{ PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x062B), },
> +#ifdef CONFIG_OCXL_SCM_GENERIC
> +	{ PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0625), },
> +#endif
>   	{ }
>   };
>   MODULE_DEVICE_TABLE(pci, ocxl_pci_tbl);
> 

If there's no way to use the ID table from ocxl-scm directly, there 
should at least be a comment both here and in the ocxl-scm device ID 
table mentioning that you need to keep these in sync.

-- 
Andrew Donnellan              OzLabs, ADL Canberra
ajd@linux.ibm.com             IBM Australia Limited



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 02/10] nvdimm: remove prototypes for nonexistent functions
  2019-10-25  4:46 ` [PATCH 02/10] nvdimm: remove prototypes for nonexistent functions Alastair D'Silva
  2019-10-27 23:41   ` Andrew Donnellan
@ 2019-11-07 17:56   ` Frederic Barrat
  2019-11-15  4:30   ` Dan Williams
  2 siblings, 0 replies; 45+ messages in thread
From: Frederic Barrat @ 2019-11-07 17:56 UTC (permalink / raw)
  To: Alastair D'Silva, alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Andrew Donnellan, Arnd Bergmann, Greg Kroah-Hartman,
	Dan Williams, Vishal Verma, Dave Jiang, Keith Busch, Ira Weiny,
	Krzysztof Kozlowski, Geert Uytterhoeven, Anton Blanchard,
	David Gibson, Hari Bathini, Anju T Sudhakar, Thomas Gleixner,
	Cédric Le Goater, Mahesh Salgaonkar, Greg Kurz,
	Masahiro Yamada, Andrew Morton, David Hildenbrand,
	Oscar Salvador, Michal Hocko, Pavel Tatashin, Wei Yang, Qian Cai,
	linuxppc-dev, linux-kernel, linux-nvdimm, linux-mm



Le 25/10/2019 à 06:46, Alastair D'Silva a écrit :
> From: Alastair D'Silva <alastair@d-silva.org>
> 
> These functions don't exist, so remove the prototypes for them.
> 
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> ---


Reviewed-by: Frederic Barrat <fbarrat@linux.ibm.com>


>   drivers/nvdimm/nd-core.h | 4 ----
>   1 file changed, 4 deletions(-)
> 
> diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h
> index 25fa121104d0..9f121a6aeb02 100644
> --- a/drivers/nvdimm/nd-core.h
> +++ b/drivers/nvdimm/nd-core.h
> @@ -124,11 +124,7 @@ void nd_region_create_dax_seed(struct nd_region *nd_region);
>   int nvdimm_bus_create_ndctl(struct nvdimm_bus *nvdimm_bus);
>   void nvdimm_bus_destroy_ndctl(struct nvdimm_bus *nvdimm_bus);
>   void nd_synchronize(void);
> -int nvdimm_bus_register_dimms(struct nvdimm_bus *nvdimm_bus);
> -int nvdimm_bus_register_regions(struct nvdimm_bus *nvdimm_bus);
> -int nvdimm_bus_init_interleave_sets(struct nvdimm_bus *nvdimm_bus);
>   void __nd_device_register(struct device *dev);
> -int nd_match_dimm(struct device *dev, void *data);
>   struct nd_label_id;
>   char *nd_label_gen_id(struct nd_label_id *label_id, u8 *uuid, u32 flags);
>   bool nd_is_uuid_unique(struct device *dev, u8 *uuid);
> 



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 03/10] powerpc: Add OPAL calls for LPC memory alloc/release
  2019-10-25  4:46 ` [PATCH 03/10] powerpc: Add OPAL calls for LPC memory alloc/release Alastair D'Silva
@ 2019-11-07 17:57   ` Frederic Barrat
  0 siblings, 0 replies; 45+ messages in thread
From: Frederic Barrat @ 2019-11-07 17:57 UTC (permalink / raw)
  To: Alastair D'Silva, alastair
  Cc: Andrew Donnellan, Benjamin Herrenschmidt, Paul Mackerras,
	Michael Ellerman, Arnd Bergmann, Greg Kroah-Hartman,
	Dan Williams, Vishal Verma, Dave Jiang, Keith Busch, Ira Weiny,
	Anton Blanchard, Geert Uytterhoeven, Krzysztof Kozlowski,
	Thomas Gleixner, Vasant Hegde, Anju T Sudhakar, Hari Bathini,
	Mahesh Salgaonkar, Cédric Le Goater, Allison Randal,
	Greg Kurz, Nicholas Piggin, David Gibson, Alexey Kardashevskiy,
	Andrew Morton, David Hildenbrand, Oscar Salvador, Michal Hocko,
	Pavel Tatashin, Wei Yang, Qian Cai, linuxppc-dev, linux-kernel,
	linux-nvdimm, linux-mm



Le 25/10/2019 à 06:46, Alastair D'Silva a écrit :
> From: Alastair D'Silva <alastair@d-silva.org>
> 
> Add OPAL calls for LPC memory alloc/release
> 
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> Acked-by: Andrew Donnellan <ajd@linux.ibm.com>
> ---


Acked-by: Frederic Barrat <fbarrat@linux.ibm.com>



>   arch/powerpc/include/asm/opal-api.h        | 2 ++
>   arch/powerpc/include/asm/opal.h            | 3 +++
>   arch/powerpc/platforms/powernv/opal-call.c | 2 ++
>   3 files changed, 7 insertions(+)
> 
> diff --git a/arch/powerpc/include/asm/opal-api.h b/arch/powerpc/include/asm/opal-api.h
> index 378e3997845a..2c88c02e69ed 100644
> --- a/arch/powerpc/include/asm/opal-api.h
> +++ b/arch/powerpc/include/asm/opal-api.h
> @@ -208,6 +208,8 @@
>   #define OPAL_HANDLE_HMI2			166
>   #define	OPAL_NX_COPROC_INIT			167
>   #define OPAL_XIVE_GET_VP_STATE			170
> +#define OPAL_NPU_MEM_ALLOC			171
> +#define OPAL_NPU_MEM_RELEASE			172
>   #define OPAL_MPIPL_UPDATE			173
>   #define OPAL_MPIPL_REGISTER_TAG			174
>   #define OPAL_MPIPL_QUERY_TAG			175
> diff --git a/arch/powerpc/include/asm/opal.h b/arch/powerpc/include/asm/opal.h
> index a0cf8fba4d12..4db135fb54ab 100644
> --- a/arch/powerpc/include/asm/opal.h
> +++ b/arch/powerpc/include/asm/opal.h
> @@ -39,6 +39,9 @@ int64_t opal_npu_spa_clear_cache(uint64_t phb_id, uint32_t bdfn,
>   				uint64_t PE_handle);
>   int64_t opal_npu_tl_set(uint64_t phb_id, uint32_t bdfn, long cap,
>   			uint64_t rate_phys, uint32_t size);
> +int64_t opal_npu_mem_alloc(uint64_t phb_id, uint32_t bdfn,
> +			uint64_t size, uint64_t *bar);
> +int64_t opal_npu_mem_release(uint64_t phb_id, uint32_t bdfn);
>   
>   int64_t opal_console_write(int64_t term_number, __be64 *length,
>   			   const uint8_t *buffer);
> diff --git a/arch/powerpc/platforms/powernv/opal-call.c b/arch/powerpc/platforms/powernv/opal-call.c
> index a2aa5e433ac8..27c4b93c774c 100644
> --- a/arch/powerpc/platforms/powernv/opal-call.c
> +++ b/arch/powerpc/platforms/powernv/opal-call.c
> @@ -287,6 +287,8 @@ OPAL_CALL(opal_pci_set_pbcq_tunnel_bar,		OPAL_PCI_SET_PBCQ_TUNNEL_BAR);
>   OPAL_CALL(opal_sensor_read_u64,			OPAL_SENSOR_READ_U64);
>   OPAL_CALL(opal_sensor_group_enable,		OPAL_SENSOR_GROUP_ENABLE);
>   OPAL_CALL(opal_nx_coproc_init,			OPAL_NX_COPROC_INIT);
> +OPAL_CALL(opal_npu_mem_alloc,			OPAL_NPU_MEM_ALLOC);
> +OPAL_CALL(opal_npu_mem_release,			OPAL_NPU_MEM_RELEASE);
>   OPAL_CALL(opal_mpipl_update,			OPAL_MPIPL_UPDATE);
>   OPAL_CALL(opal_mpipl_register_tag,		OPAL_MPIPL_REGISTER_TAG);
>   OPAL_CALL(opal_mpipl_query_tag,			OPAL_MPIPL_QUERY_TAG);
> 



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory
  2019-10-25  4:46 ` [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory Alastair D'Silva
                     ` (3 preceding siblings ...)
  2019-10-29  1:49   ` Andrew Donnellan
@ 2019-11-07 17:59   ` Frederic Barrat
  4 siblings, 0 replies; 45+ messages in thread
From: Frederic Barrat @ 2019-11-07 17:59 UTC (permalink / raw)
  To: Alastair D'Silva, alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Andrew Donnellan, Arnd Bergmann, Greg Kroah-Hartman,
	Dan Williams, Vishal Verma, Dave Jiang, Keith Busch, Ira Weiny,
	Geert Uytterhoeven, Krzysztof Kozlowski, Anton Blanchard,
	Allison Randal, Cédric Le Goater, David Gibson,
	Vasant Hegde, Thomas Gleixner, Anju T Sudhakar, Hari Bathini,
	Mahesh Salgaonkar, Greg Kurz, Masahiro Yamada,
	Alexey Kardashevskiy, Andrew Morton, David Hildenbrand,
	Oscar Salvador, Michal Hocko, Pavel Tatashin, Wei Yang, Qian Cai,
	linuxppc-dev, linux-kernel, linux-nvdimm, linux-mm



Le 25/10/2019 à 06:46, Alastair D'Silva a écrit :
> From: Alastair D'Silva <alastair@d-silva.org>
> 
> This patch adds platform support to map & release LPC memory.
> 
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> ---
>   arch/powerpc/include/asm/pnv-ocxl.h   |  2 ++
>   arch/powerpc/platforms/powernv/ocxl.c | 41 +++++++++++++++++++++++++++
>   include/linux/memory_hotplug.h        |  5 ++++
>   mm/memory_hotplug.c                   |  3 +-
>   4 files changed, 50 insertions(+), 1 deletion(-)
> 
> diff --git a/arch/powerpc/include/asm/pnv-ocxl.h b/arch/powerpc/include/asm/pnv-ocxl.h
> index 7de82647e761..f8f8ffb48aa8 100644
> --- a/arch/powerpc/include/asm/pnv-ocxl.h
> +++ b/arch/powerpc/include/asm/pnv-ocxl.h
> @@ -32,5 +32,7 @@ extern int pnv_ocxl_spa_remove_pe_from_cache(void *platform_data, int pe_handle)
>   
>   extern int pnv_ocxl_alloc_xive_irq(u32 *irq, u64 *trigger_addr);
>   extern void pnv_ocxl_free_xive_irq(u32 irq);
> +extern u64 pnv_ocxl_platform_lpc_setup(struct pci_dev *pdev, u64 size);
> +extern void pnv_ocxl_platform_lpc_release(struct pci_dev *pdev);
>   
>   #endif /* _ASM_PNV_OCXL_H */
> diff --git a/arch/powerpc/platforms/powernv/ocxl.c b/arch/powerpc/platforms/powernv/ocxl.c
> index 8c65aacda9c8..c6d4234e0aba 100644
> --- a/arch/powerpc/platforms/powernv/ocxl.c
> +++ b/arch/powerpc/platforms/powernv/ocxl.c
> @@ -475,6 +475,47 @@ void pnv_ocxl_spa_release(void *platform_data)
>   }
>   EXPORT_SYMBOL_GPL(pnv_ocxl_spa_release);
>   
> +u64 pnv_ocxl_platform_lpc_setup(struct pci_dev *pdev, u64 size)
> +{
> +	struct pci_controller *hose = pci_bus_to_host(pdev->bus);
> +	struct pnv_phb *phb = hose->private_data;
> +	u32 bdfn = (pdev->bus->number << 8) | pdev->devfn;
> +	u64 base_addr = 0;
> +	int rc;
> +
> +	rc = opal_npu_mem_alloc(phb->opal_id, bdfn, size, &base_addr);
> +	if (rc) {
> +		dev_warn(&pdev->dev,
> +			 "OPAL could not allocate LPC memory, rc=%d\n", rc);
> +		return 0;
> +	}
> +
> +	base_addr = be64_to_cpu(base_addr);
> +
> +	rc = check_hotplug_memory_addressable(base_addr >> PAGE_SHIFT,
> +					      size >> PAGE_SHIFT);
> +	if (rc)
> +		return 0;
> +
> +	return base_addr;
> +}
> +EXPORT_SYMBOL_GPL(pnv_ocxl_platform_lpc_setup);
> +
> +void pnv_ocxl_platform_lpc_release(struct pci_dev *pdev)
> +{
> +	struct pci_controller *hose = pci_bus_to_host(pdev->bus);
> +	struct pnv_phb *phb = hose->private_data;
> +	u32 bdfn = (pdev->bus->number << 8) | pdev->devfn;
> +	int rc;
> +
> +	rc = opal_npu_mem_release(phb->opal_id, bdfn);
> +	if (rc)
> +		dev_warn(&pdev->dev,
> +			 "OPAL reported rc=%d when releasing LPC memory\n", rc);
> +}
> +EXPORT_SYMBOL_GPL(pnv_ocxl_platform_lpc_release);
> +
> +
>   int pnv_ocxl_spa_remove_pe_from_cache(void *platform_data, int pe_handle)
>   {
>   	struct spa_data *data = (struct spa_data *) platform_data;
> diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h
> index f46ea71b4ffd..3f5f1a642abe 100644
> --- a/include/linux/memory_hotplug.h
> +++ b/include/linux/memory_hotplug.h
> @@ -339,6 +339,11 @@ static inline int remove_memory(int nid, u64 start, u64 size)
>   static inline void __remove_memory(int nid, u64 start, u64 size) {}
>   #endif /* CONFIG_MEMORY_HOTREMOVE */
>   
> +#if CONFIG_MEMORY_HOTPLUG_SPARSE
> +int check_hotplug_memory_addressable(unsigned long pfn,
> +		unsigned long nr_pages);
> +#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */
> +
>   extern void __ref free_area_init_core_hotplug(int nid);
>   extern int __add_memory(int nid, u64 start, u64 size);
>   extern int add_memory(int nid, u64 start, u64 size);
> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
> index 2cecf07b396f..b39827dbd071 100644
> --- a/mm/memory_hotplug.c
> +++ b/mm/memory_hotplug.c
> @@ -278,7 +278,7 @@ static int check_pfn_span(unsigned long pfn, unsigned long nr_pages,
>   	return 0;
>   }
>   
> -static int check_hotplug_memory_addressable(unsigned long pfn,
> +int check_hotplug_memory_addressable(unsigned long pfn,
>   					    unsigned long nr_pages)
>   {
>   	const u64 max_addr = PFN_PHYS(pfn + nr_pages) - 1;
> @@ -294,6 +294,7 @@ static int check_hotplug_memory_addressable(unsigned long pfn,
>   
>   	return 0;
>   }
> +EXPORT_SYMBOL_GPL(check_hotplug_memory_addressable);


Making check_hotplug_memory_addressable() visible in the kernel could be 
a separate patch, to make sure it gets the proper attention instead of 
being buried in a powerpc patch.
Also, already mentioned, but it shouldn't be exported.


>   
>   /*
>    * Reasonably generic function for adding memory.  It is
> 



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 05/10] ocxl: Tally up the LPC memory on a link & allow it to be mapped
  2019-10-25  4:47 ` [PATCH 05/10] ocxl: Tally up the LPC memory on a link & allow it to be mapped Alastair D'Silva
@ 2019-11-07 18:02   ` Frederic Barrat
  0 siblings, 0 replies; 45+ messages in thread
From: Frederic Barrat @ 2019-11-07 18:02 UTC (permalink / raw)
  To: Alastair D'Silva, alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Andrew Donnellan, Arnd Bergmann, Greg Kroah-Hartman,
	Dan Williams, Vishal Verma, Dave Jiang, Keith Busch, Ira Weiny,
	Krzysztof Kozlowski, Anton Blanchard, Allison Randal,
	Madhavan Srinivasan, Mahesh Salgaonkar, Hari Bathini,
	Thomas Gleixner, Cédric Le Goater, Greg Kurz,
	Alexey Kardashevskiy, David Gibson, Masahiro Yamada,
	Andrew Morton, David Hildenbrand, Oscar Salvador, Michal Hocko,
	Pavel Tatashin, Wei Yang, Qian Cai, linuxppc-dev, linux-kernel,
	linux-nvdimm, linux-mm



Le 25/10/2019 à 06:47, Alastair D'Silva a écrit :
> From: Alastair D'Silva <alastair@d-silva.org>
> 
> Tally up the LPC memory on an OpenCAPI link & allow it to be mapped
> 
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> ---
>   drivers/misc/ocxl/core.c          | 10 ++++++
>   drivers/misc/ocxl/link.c          | 60 +++++++++++++++++++++++++++++++
>   drivers/misc/ocxl/ocxl_internal.h | 33 +++++++++++++++++
>   3 files changed, 103 insertions(+)
> 
> diff --git a/drivers/misc/ocxl/core.c b/drivers/misc/ocxl/core.c
> index b7a09b21ab36..2531c6cf19a0 100644
> --- a/drivers/misc/ocxl/core.c
> +++ b/drivers/misc/ocxl/core.c
> @@ -230,8 +230,18 @@ static int configure_afu(struct ocxl_afu *afu, u8 afu_idx, struct pci_dev *dev)
>   	if (rc)
>   		goto err_free_pasid;
>   
> +	if (afu->config.lpc_mem_size || afu->config.special_purpose_mem_size) {
> +		rc = ocxl_link_add_lpc_mem(afu->fn->link, afu->config.lpc_mem_offset,
> +					   afu->config.lpc_mem_size +
> +					   afu->config.special_purpose_mem_size);
> +		if (rc)
> +			goto err_free_mmio;
> +	}
> +
>   	return 0;
>   
> +err_free_mmio:
> +	unmap_mmio_areas(afu);
>   err_free_pasid:
>   	reclaim_afu_pasid(afu);
>   err_free_actag:
> diff --git a/drivers/misc/ocxl/link.c b/drivers/misc/ocxl/link.c
> index 58d111afd9f6..1d350d0bb860 100644
> --- a/drivers/misc/ocxl/link.c
> +++ b/drivers/misc/ocxl/link.c
> @@ -84,6 +84,11 @@ struct ocxl_link {
>   	int dev;
>   	atomic_t irq_available;
>   	struct spa *spa;
> +	struct mutex lpc_mem_lock;
> +	u64 lpc_mem_sz; /* Total amount of LPC memory presented on the link */
> +	u64 lpc_mem;
> +	int lpc_consumers;
> +
>   	void *platform_data;
>   };
>   static struct list_head links_list = LIST_HEAD_INIT(links_list);
> @@ -396,6 +401,8 @@ static int alloc_link(struct pci_dev *dev, int PE_mask, struct ocxl_link **out_l
>   	if (rc)
>   		goto err_spa;
>   
> +	mutex_init(&link->lpc_mem_lock);
> +
>   	/* platform specific hook */
>   	rc = pnv_ocxl_spa_setup(dev, link->spa->spa_mem, PE_mask,
>   				&link->platform_data);
> @@ -711,3 +718,56 @@ void ocxl_link_free_irq(void *link_handle, int hw_irq)
>   	atomic_inc(&link->irq_available);
>   }
>   EXPORT_SYMBOL_GPL(ocxl_link_free_irq);
> +
> +int ocxl_link_add_lpc_mem(void *link_handle, u64 offset, u64 size)
> +{
> +	struct ocxl_link *link = (struct ocxl_link *) link_handle;
> +
> +	// Check for overflow
> +	if (offset > (offset + size))
> +		return -EINVAL;
> +
> +	mutex_lock(&link->lpc_mem_lock);
> +	link->lpc_mem_sz = max(link->lpc_mem_sz, offset + size);


Good find to avoid having to maintain a range list!


> +
> +	mutex_unlock(&link->lpc_mem_lock);
> +
> +	return 0;
> +}
> +
> +u64 ocxl_link_lpc_map(void *link_handle, struct pci_dev *pdev)
> +{
> +	struct ocxl_link *link = (struct ocxl_link *) link_handle;
> +	u64 lpc_mem;
> +
> +	mutex_lock(&link->lpc_mem_lock);
> +	if (link->lpc_mem) {
> +		lpc_mem = link->lpc_mem;
> +
> +		link->lpc_consumers++;
> +		mutex_unlock(&link->lpc_mem_lock);
> +		return lpc_mem;
> +	}
> +
> +	link->lpc_mem = pnv_ocxl_platform_lpc_setup(pdev, link->lpc_mem_sz);
> +	if (link->lpc_mem)
> +		link->lpc_consumers++;
> +	lpc_mem = link->lpc_mem;
> +	mutex_unlock(&link->lpc_mem_lock);
> +
> +	return lpc_mem;
> +}
> +
> +void ocxl_link_lpc_release(void *link_handle, struct pci_dev *pdev)
> +{
> +	struct ocxl_link *link = (struct ocxl_link *) link_handle;
> +
> +	mutex_lock(&link->lpc_mem_lock);
> +	link->lpc_consumers--;


Replace with WARN_ON(--link->lpc_consumers < 0) ?


   Fred


> +	if (link->lpc_consumers == 0) {
> +		pnv_ocxl_platform_lpc_release(pdev);
> +		link->lpc_mem = 0;
> +	}
> +
> +	mutex_unlock(&link->lpc_mem_lock);
> +}
> diff --git a/drivers/misc/ocxl/ocxl_internal.h b/drivers/misc/ocxl/ocxl_internal.h
> index 97415afd79f3..20b417e00949 100644
> --- a/drivers/misc/ocxl/ocxl_internal.h
> +++ b/drivers/misc/ocxl/ocxl_internal.h
> @@ -141,4 +141,37 @@ int ocxl_irq_offset_to_id(struct ocxl_context *ctx, u64 offset);
>   u64 ocxl_irq_id_to_offset(struct ocxl_context *ctx, int irq_id);
>   void ocxl_afu_irq_free_all(struct ocxl_context *ctx);
>   
> +/**
> + * ocxl_link_add_lpc_mem() - Increment the amount of memory required by an OpenCAPI link
> + *
> + * @link_handle: The OpenCAPI link handle
> + * @offset: The offset of the memory to add
> + * @size: The amount of memory to increment by
> + *
> + * Return 0 on success, negative on overflow
> + */
> +int ocxl_link_add_lpc_mem(void *link_handle, u64 offset, u64 size);
> +
> +/**
> + * ocxl_link_lpc_map() - Map the LPC memory for an OpenCAPI device
> + *
> + * Since LPC memory belongs to a link, the whole LPC memory available
> + * on the link bust be mapped in order to make it accessible to a device.
> + *
> + * @link_handle: The OpenCAPI link handle
> + * @pdev: A device that is on the link
> + */
> +u64 ocxl_link_lpc_map(void *link_handle, struct pci_dev *pdev);
> +
> +/**
> + * ocxl_link_lpc_release() - Release the LPC memory device for an OpenCAPI device
> + *
> + * Offlines LPC memory on an OpenCAPI link for a device. If this is the
> + * last device on the link to release the memory, unmap it from the link.
> + *
> + * @link_handle: The OpenCAPI link handle
> + * @pdev: A device that is on the link
> + */
> +void ocxl_link_lpc_release(void *link_handle, struct pci_dev *pdev);
> +
>   #endif /* _OCXL_INTERNAL_H_ */
> 



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 06/10] ocxl: Add functions to map/unmap LPC memory
  2019-10-25  4:47 ` [PATCH 06/10] ocxl: Add functions to map/unmap LPC memory Alastair D'Silva
@ 2019-11-07 18:05   ` Frederic Barrat
  0 siblings, 0 replies; 45+ messages in thread
From: Frederic Barrat @ 2019-11-07 18:05 UTC (permalink / raw)
  To: Alastair D'Silva, alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Andrew Donnellan, Arnd Bergmann, Greg Kroah-Hartman,
	Dan Williams, Vishal Verma, Dave Jiang, Keith Busch, Ira Weiny,
	Krzysztof Kozlowski, Anton Blanchard, Geert Uytterhoeven,
	Anju T Sudhakar, Mahesh Salgaonkar, Vasant Hegde,
	Cédric Le Goater, Thomas Gleixner, Hari Bathini, Greg Kurz,
	Masahiro Yamada, Andrew Morton, David Hildenbrand,
	Oscar Salvador, Michal Hocko, Pavel Tatashin, Wei Yang, Qian Cai,
	linuxppc-dev, linux-kernel, linux-nvdimm, linux-mm



Le 25/10/2019 à 06:47, Alastair D'Silva a écrit :
> From: Alastair D'Silva <alastair@d-silva.org>
> 
> Add functions to map/unmap LPC memory
> 
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> ---
>   drivers/misc/ocxl/config.c        |  4 +++
>   drivers/misc/ocxl/core.c          | 50 +++++++++++++++++++++++++++++++
>   drivers/misc/ocxl/ocxl_internal.h |  3 ++
>   include/misc/ocxl.h               | 18 +++++++++++
>   4 files changed, 75 insertions(+)
> 
> diff --git a/drivers/misc/ocxl/config.c b/drivers/misc/ocxl/config.c
> index c8e19bfb5ef9..fb0c3b6f8312 100644
> --- a/drivers/misc/ocxl/config.c
> +++ b/drivers/misc/ocxl/config.c
> @@ -568,6 +568,10 @@ static int read_afu_lpc_memory_info(struct pci_dev *dev,
>   		afu->special_purpose_mem_size =
>   			total_mem_size - lpc_mem_size;
>   	}
> +
> +	dev_info(&dev->dev, "Probed LPC memory of %#llx bytes and special purpose memory of %#llx bytes\n",
> +		afu->lpc_mem_size, afu->special_purpose_mem_size);
> +
>   	return 0;
>   }
>   
> diff --git a/drivers/misc/ocxl/core.c b/drivers/misc/ocxl/core.c
> index 2531c6cf19a0..5554f5ce4b9e 100644
> --- a/drivers/misc/ocxl/core.c
> +++ b/drivers/misc/ocxl/core.c
> @@ -210,6 +210,55 @@ static void unmap_mmio_areas(struct ocxl_afu *afu)
>   	release_fn_bar(afu->fn, afu->config.global_mmio_bar);
>   }
>   
> +int ocxl_afu_map_lpc_mem(struct ocxl_afu *afu)
> +{
> +	struct pci_dev *dev = to_pci_dev(afu->fn->dev.parent);
> +
> +	if ((afu->config.lpc_mem_size + afu->config.special_purpose_mem_size) == 0)
> +		return 0;
> +
> +	afu->lpc_base_addr = ocxl_link_lpc_map(afu->fn->link, dev);
> +	if (afu->lpc_base_addr == 0)
> +		return -EINVAL;
> +
> +	if (afu->config.lpc_mem_size) {
> +		afu->lpc_res.start = afu->lpc_base_addr + afu->config.lpc_mem_offset;
> +		afu->lpc_res.end = afu->lpc_res.start + afu->config.lpc_mem_size - 1;
> +	}
> +
> +	if (afu->config.special_purpose_mem_size) {
> +		afu->special_purpose_res.start = afu->lpc_base_addr +
> +						 afu->config.special_purpose_mem_offset;
> +		afu->special_purpose_res.end = afu->special_purpose_res.start +
> +					       afu->config.special_purpose_mem_size - 1;
> +	}
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(ocxl_afu_map_lpc_mem);


We should use EXPORT_SYMBOL_GPL().

ok, so we're unmapping the lpc memory implicitly when calling 
ocxl_function_close() and therefore don't really need to export 
(ocxl_)unmap_lpc_mem(). I guess that's fine and easy enough to add if 
one day somebody has the need to unmap without closing.


> +
> +struct resource *ocxl_afu_lpc_mem(struct ocxl_afu *afu)
> +{
> +	return &afu->lpc_res;
> +}
> +EXPORT_SYMBOL(ocxl_afu_lpc_mem);
> +
> +static void unmap_lpc_mem(struct ocxl_afu *afu)
> +{
> +	struct pci_dev *dev = to_pci_dev(afu->fn->dev.parent);
> +
> +	if (afu->lpc_res.start || afu->special_purpose_res.start) {
> +		void *link = afu->fn->link;
> +
> +		ocxl_link_lpc_release(link, dev);
> +
> +		afu->lpc_res.start = 0;
> +		afu->lpc_res.end = 0;
> +		afu->special_purpose_res.start = 0;
> +		afu->special_purpose_res.end = 0;
> +	}
> +}
> +
>   static int configure_afu(struct ocxl_afu *afu, u8 afu_idx, struct pci_dev *dev)
>   {
>   	int rc;
> @@ -251,6 +300,7 @@ static int configure_afu(struct ocxl_afu *afu, u8 afu_idx, struct pci_dev *dev)
>   
>   static void deconfigure_afu(struct ocxl_afu *afu)
>   {
> +	unmap_lpc_mem(afu);
>   	unmap_mmio_areas(afu);
>   	reclaim_afu_pasid(afu);
>   	reclaim_afu_actag(afu);
> diff --git a/drivers/misc/ocxl/ocxl_internal.h b/drivers/misc/ocxl/ocxl_internal.h
> index 20b417e00949..9f4b47900e62 100644
> --- a/drivers/misc/ocxl/ocxl_internal.h
> +++ b/drivers/misc/ocxl/ocxl_internal.h
> @@ -52,6 +52,9 @@ struct ocxl_afu {
>   	void __iomem *global_mmio_ptr;
>   	u64 pp_mmio_start;
>   	void *private;
> +	u64 lpc_base_addr; /* Covers both LPC & special purpose memory */
> +	struct resource lpc_res;
> +	struct resource special_purpose_res;
>   };
>   
>   enum ocxl_context_status {
> diff --git a/include/misc/ocxl.h b/include/misc/ocxl.h
> index 06dd5839e438..6f7c02f0d5e3 100644
> --- a/include/misc/ocxl.h
> +++ b/include/misc/ocxl.h
> @@ -212,6 +212,24 @@ int ocxl_irq_set_handler(struct ocxl_context *ctx, int irq_id,
>   
>   // AFU Metadata
>   
> +/**
> + * Map the LPC system & special purpose memory for an AFU
> + *
> + * Do not call this during device discovery, as there may me multiple
> + * devices on a link, and the memory is mapped for the whole link, not
> + * just one device. It should only be called after all devices have
> + * registered their memory on the link.


If we were supporting more than one AFU-carrying functions, we would 
need to rework this, as functions could come and go and the total range 
could be dynamic (even the max address of the range could increase, if a 
function is updated with an AFU with a bigger LPC size). But we don't 
support multiple AFU-carrying functions, so current implementation is 
fine. And simple.

   Fred


> + *
> + * afu: The AFU that has the LPC memory to map
> + */
> +extern int ocxl_afu_map_lpc_mem(struct ocxl_afu *afu);
> +
> +/**
> + * Get the physical address range of LPC memory for an AFU
> + * afu: The AFU associated with the LPC memory
> + */
> +extern struct resource *ocxl_afu_lpc_mem(struct ocxl_afu *afu);
> +
>   /**
>    * Get a pointer to the config for an AFU
>    *
> 



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 10/10] ocxl: Conditionally bind SCM devices to the generic OCXL driver
  2019-10-25  4:47 ` [PATCH 10/10] ocxl: Conditionally bind SCM devices to the generic OCXL driver Alastair D'Silva
  2019-10-26  6:43   ` Christoph Hellwig
  2019-11-06  3:46   ` Andrew Donnellan
@ 2019-11-07 18:08   ` Frederic Barrat
  2019-11-08  0:37     ` Alastair D'Silva
  2 siblings, 1 reply; 45+ messages in thread
From: Frederic Barrat @ 2019-11-07 18:08 UTC (permalink / raw)
  To: Alastair D'Silva, alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Andrew Donnellan, Arnd Bergmann, Greg Kroah-Hartman,
	Dan Williams, Vishal Verma, Dave Jiang, Keith Busch, Ira Weiny,
	Geert Uytterhoeven, Anton Blanchard, Krzysztof Kozlowski,
	Vasant Hegde, David Gibson, Hari Bathini, Allison Randal,
	Anju T Sudhakar, Mahesh Salgaonkar, Cédric Le Goater,
	Thomas Gleixner, Greg Kurz, Nicholas Piggin, Masahiro Yamada,
	Alexey Kardashevskiy, Andrew Morton, David Hildenbrand,
	Oscar Salvador, Michal Hocko, Pavel Tatashin, Wei Yang, Qian Cai,
	linuxppc-dev, linux-kernel, linux-nvdimm, linux-mm



Le 25/10/2019 à 06:47, Alastair D'Silva a écrit :
> From: Alastair D'Silva <alastair@d-silva.org>
> 
> This patch allows the user to bind OpenCAPI SCM devices to the generic OCXL
> driver.
> 
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> ---


I'm wondering if we should upstream this. Is it of any use outside of 
some serious debug session for a developer?
Also we would now have 2 drivers picking up the same device ID, since 
the SCM driver is always registering for that ID, irrespective of 
CONFIG_OCXL_SCM_GENERIC

   Fred


>   drivers/misc/ocxl/Kconfig | 7 +++++++
>   drivers/misc/ocxl/pci.c   | 3 +++
>   2 files changed, 10 insertions(+)
> 
> diff --git a/drivers/misc/ocxl/Kconfig b/drivers/misc/ocxl/Kconfig
> index 1916fa65f2f2..8a683715c97c 100644
> --- a/drivers/misc/ocxl/Kconfig
> +++ b/drivers/misc/ocxl/Kconfig
> @@ -29,3 +29,10 @@ config OCXL
>   	  dedicated OpenCAPI link, and don't follow the same protocol.
>   
>   	  If unsure, say N.
> +
> +config OCXL_SCM_GENERIC
> +	bool "Treat OpenCAPI Storage Class Memory as a generic OpenCAPI device"
> +	default n
> +	help
> +	  Select this option to treat OpenCAPI Storage Class Memory
> +	  devices an generic OpenCAPI devices.
> diff --git a/drivers/misc/ocxl/pci.c b/drivers/misc/ocxl/pci.c
> index cb920aa88d3a..7137055c1883 100644
> --- a/drivers/misc/ocxl/pci.c
> +++ b/drivers/misc/ocxl/pci.c
> @@ -10,6 +10,9 @@
>    */
>   static const struct pci_device_id ocxl_pci_tbl[] = {
>   	{ PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x062B), },
> +#ifdef CONFIG_OCXL_SCM_GENERIC
> +	{ PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0625), },
> +#endif
>   	{ }
>   };
>   MODULE_DEVICE_TABLE(pci, ocxl_pci_tbl);
> 



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 07/10] ocxl: Save the device serial number in ocxl_fn
  2019-10-25  4:47 ` [PATCH 07/10] ocxl: Save the device serial number in ocxl_fn Alastair D'Silva
  2019-11-06  3:10   ` Andrew Donnellan
@ 2019-11-07 18:18   ` Frederic Barrat
  1 sibling, 0 replies; 45+ messages in thread
From: Frederic Barrat @ 2019-11-07 18:18 UTC (permalink / raw)
  To: Alastair D'Silva, alastair
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Andrew Donnellan, Arnd Bergmann, Greg Kroah-Hartman,
	Dan Williams, Vishal Verma, Dave Jiang, Keith Busch, Ira Weiny,
	Krzysztof Kozlowski, Anton Blanchard, Madhavan Srinivasan,
	Thomas Gleixner, Mahesh Salgaonkar, Vasant Hegde, Hari Bathini,
	Cédric Le Goater, David Gibson, Greg Kurz, Masahiro Yamada,
	Nicholas Piggin, Andrew Morton, David Hildenbrand,
	Oscar Salvador, Michal Hocko, Pavel Tatashin, Wei Yang, Qian Cai,
	linuxppc-dev, linux-kernel, linux-nvdimm, linux-mm



Le 25/10/2019 à 06:47, Alastair D'Silva a écrit :
> From: Alastair D'Silva <alastair@d-silva.org>
> 
> This patch retrieves the serial number of the card and makes it available
> to consumers of the ocxl driver via the ocxl_fn struct.
> 
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> ---


Acked-by: Frederic Barrat <fbarrat@linux.ibm.com>



>   drivers/misc/ocxl/config.c | 46 ++++++++++++++++++++++++++++++++++++++
>   include/misc/ocxl.h        |  1 +
>   2 files changed, 47 insertions(+)
> 
> diff --git a/drivers/misc/ocxl/config.c b/drivers/misc/ocxl/config.c
> index fb0c3b6f8312..a9203c309365 100644
> --- a/drivers/misc/ocxl/config.c
> +++ b/drivers/misc/ocxl/config.c
> @@ -71,6 +71,51 @@ static int find_dvsec_afu_ctrl(struct pci_dev *dev, u8 afu_idx)
>   	return 0;
>   }
>   
> +/**
> + * Find a related PCI device (function 0)
> + * @device: PCI device to match
> + *
> + * Returns a pointer to the related device, or null if not found
> + */
> +static struct pci_dev *get_function_0(struct pci_dev *dev)
> +{
> +	unsigned int devfn = PCI_DEVFN(PCI_SLOT(dev->devfn), 0); // Look for function 0
> +
> +	return pci_get_domain_bus_and_slot(pci_domain_nr(dev->bus),
> +					dev->bus->number, devfn);
> +}
> +
> +static void read_serial(struct pci_dev *dev, struct ocxl_fn_config *fn)
> +{
> +	u32 low, high;
> +	int pos;
> +
> +	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_DSN);
> +	if (pos) {
> +		pci_read_config_dword(dev, pos + 0x04, &low);
> +		pci_read_config_dword(dev, pos + 0x08, &high);
> +
> +		fn->serial = low | ((u64)high) << 32;
> +
> +		return;
> +	}
> +
> +	if (PCI_FUNC(dev->devfn) != 0) {
> +		struct pci_dev *related = get_function_0(dev);
> +
> +		if (!related) {
> +			fn->serial = 0;
> +			return;
> +		}
> +
> +		read_serial(related, fn);
> +		pci_dev_put(related);
> +		return;
> +	}
> +
> +	fn->serial = 0;
> +}
> +
>   static void read_pasid(struct pci_dev *dev, struct ocxl_fn_config *fn)
>   {
>   	u16 val;
> @@ -208,6 +253,7 @@ int ocxl_config_read_function(struct pci_dev *dev, struct ocxl_fn_config *fn)
>   	int rc;
>   
>   	read_pasid(dev, fn);
> +	read_serial(dev, fn);
>   
>   	rc = read_dvsec_tl(dev, fn);
>   	if (rc) {
> diff --git a/include/misc/ocxl.h b/include/misc/ocxl.h
> index 6f7c02f0d5e3..9843051c3c5b 100644
> --- a/include/misc/ocxl.h
> +++ b/include/misc/ocxl.h
> @@ -46,6 +46,7 @@ struct ocxl_fn_config {
>   	int dvsec_afu_info_pos; /* offset of the AFU information DVSEC */
>   	s8 max_pasid_log;
>   	s8 max_afu_index;
> +	u64 serial;
>   };
>   
>   enum ocxl_endian {
> 



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 10/10] ocxl: Conditionally bind SCM devices to the generic OCXL driver
  2019-11-07 18:08   ` Frederic Barrat
@ 2019-11-08  0:37     ` Alastair D'Silva
  0 siblings, 0 replies; 45+ messages in thread
From: Alastair D'Silva @ 2019-11-08  0:37 UTC (permalink / raw)
  To: Frederic Barrat
  Cc: Benjamin Herrenschmidt, Paul Mackerras, Michael Ellerman,
	Andrew Donnellan, Arnd Bergmann, Greg Kroah-Hartman,
	Dan Williams, Vishal Verma, Dave Jiang, Keith Busch, Ira Weiny,
	Anton Blanchard, Krzysztof Kozlowski, Vasant Hegde, David Gibson,
	Hari Bathini, Allison Randal, Anju T Sudhakar, Mahesh Salgaonkar,
	Cédric Le Goater, Thomas Gleixner, Greg Kurz,
	Nicholas Piggin, Masahiro Yamada, Alexey Kardashevskiy,
	Andrew Morton, David Hildenbrand, Oscar Salvador, Michal Hocko,
	Pavel Tatashin, Wei Yang, Qian Cai, linuxppc-dev, linux-kernel,
	linux-nvdimm, linux-mm

On Thu, 2019-11-07 at 19:08 +0100, Frederic Barrat wrote:
> 
> Le 25/10/2019 à 06:47, Alastair D'Silva a écrit :
> > From: Alastair D'Silva <alastair@d-silva.org>
> > 
> > This patch allows the user to bind OpenCAPI SCM devices to the
> > generic OCXL
> > driver.
> > 
> > Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> > ---
> 
> I'm wondering if we should upstream this. Is it of any use outside
> of 
> some serious debug session for a developer?
> Also we would now have 2 drivers picking up the same device ID,
> since 
> the SCM driver is always registering for that ID, irrespective of 
> CONFIG_OCXL_SCM_GENERIC
> 
>    Fred
> 

I think I'll drop this patch. It's easy enough to maintain out-of-tree
for our in-house SCM hardware engineers.

> 
> >   drivers/misc/ocxl/Kconfig | 7 +++++++
> >   drivers/misc/ocxl/pci.c   | 3 +++
> >   2 files changed, 10 insertions(+)
> > 
> > diff --git a/drivers/misc/ocxl/Kconfig b/drivers/misc/ocxl/Kconfig
> > index 1916fa65f2f2..8a683715c97c 100644
> > --- a/drivers/misc/ocxl/Kconfig
> > +++ b/drivers/misc/ocxl/Kconfig
> > @@ -29,3 +29,10 @@ config OCXL
> >   	  dedicated OpenCAPI link, and don't follow the same protocol.
> >   
> >   	  If unsure, say N.
> > +
> > +config OCXL_SCM_GENERIC
> > +	bool "Treat OpenCAPI Storage Class Memory as a generic OpenCAPI
> > device"
> > +	default n
> > +	help
> > +	  Select this option to treat OpenCAPI Storage Class Memory
> > +	  devices an generic OpenCAPI devices.
> > diff --git a/drivers/misc/ocxl/pci.c b/drivers/misc/ocxl/pci.c
> > index cb920aa88d3a..7137055c1883 100644
> > --- a/drivers/misc/ocxl/pci.c
> > +++ b/drivers/misc/ocxl/pci.c
> > @@ -10,6 +10,9 @@
> >    */
> >   static const struct pci_device_id ocxl_pci_tbl[] = {
> >   	{ PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x062B), },
> > +#ifdef CONFIG_OCXL_SCM_GENERIC
> > +	{ PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0625), },
> > +#endif
> >   	{ }
> >   };
> >   MODULE_DEVICE_TABLE(pci, ocxl_pci_tbl);
> > 
-- 
Alastair D'Silva
Open Source Developer
Linux Technology Centre, IBM Australia
mob: 0423 762 819



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 09/10] powerpc: Enable OpenCAPI Storage Class Memory driver on bare metal
  2019-10-25  4:47 ` [PATCH 09/10] powerpc: Enable OpenCAPI Storage Class Memory driver on bare metal Alastair D'Silva
  2019-10-28  2:43   ` Oliver O'Halloran
@ 2019-11-08  7:10   ` Frederic Barrat
  2020-01-31  4:56     ` Alastair D'Silva
  1 sibling, 1 reply; 45+ messages in thread
From: Frederic Barrat @ 2019-11-08  7:10 UTC (permalink / raw)
  To: Alastair D'Silva, alastair
  Cc: Oscar Salvador, Madhavan Srinivasan, Geert Uytterhoeven,
	David Hildenbrand, Wei Yang, Keith Busch, linux-mm, Michal Hocko,
	Paul Mackerras, Ira Weiny, Thomas Gleixner, Pavel Tatashin,
	Dave Jiang, linux-nvdimm, Vishal Verma, Krzysztof Kozlowski,
	Anju T Sudhakar, Mahesh Salgaonkar, Andrew Donnellan,
	Arnd Bergmann, Greg Kurz, Qian Cai, Cédric Le Goater,
	Dan Williams, Hari Bathini, Greg Kroah-Hartman, linux-kernel,
	Vasant Hegde, Andrew Morton, linuxppc-dev



Le 25/10/2019 à 06:47, Alastair D'Silva a écrit :
> From: Alastair D'Silva <alastair@d-silva.org>
> 
> Enable OpenCAPI Storage Class Memory driver on bare metal
> 
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> ---
>   arch/powerpc/configs/powernv_defconfig | 4 ++++
>   1 file changed, 4 insertions(+)
> 
> diff --git a/arch/powerpc/configs/powernv_defconfig b/arch/powerpc/configs/powernv_defconfig
> index 6658cceb928c..45c0eff94964 100644
> --- a/arch/powerpc/configs/powernv_defconfig
> +++ b/arch/powerpc/configs/powernv_defconfig
> @@ -352,3 +352,7 @@ CONFIG_KVM_BOOK3S_64=m
>   CONFIG_KVM_BOOK3S_64_HV=m
>   CONFIG_VHOST_NET=m
>   CONFIG_PRINTK_TIME=y
> +CONFIG_OCXL_SCM=m
> +CONFIG_DEV_DAX=y
> +CONFIG_DEV_DAX_PMEM=y
> +CONFIG_FS_DAX=y


If this really the intent or do we want to activate DAX only if 
CONFIG_OCXL_SCM is enabled?

   Fred



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory
  2019-10-25  4:47 ` [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory Alastair D'Silva
                     ` (2 preceding siblings ...)
  2019-10-28  1:12   ` [RFC PATCH] nvdimm: scm_get() can be static kbuild test robot
@ 2019-11-14 13:41   ` Frederic Barrat
  2019-11-14 16:35     ` Dan Williams
  2019-11-18 23:01     ` Alastair D'Silva
  3 siblings, 2 replies; 45+ messages in thread
From: Frederic Barrat @ 2019-11-14 13:41 UTC (permalink / raw)
  To: Alastair D'Silva, alastair
  Cc: Oscar Salvador, Michal Hocko, David Hildenbrand,
	Alexey Kardashevskiy, Wei Yang, Keith Busch, Masahiro Yamada,
	Paul Mackerras, Ira Weiny, Thomas Gleixner, Pavel Tatashin,
	Dave Jiang, linux-nvdimm, Vishal Verma, Krzysztof Kozlowski,
	Mahesh Salgaonkar, Andrew Donnellan, Arnd Bergmann, Greg Kurz,
	Nicholas Piggin, Qian Cai, Cédric Le Goater, Dan Williams,
	Hari Bathini, David Gibson, linux-mm, Greg Kroah-Hartman,
	linux-kernel, Andrew Morton, linuxppc-dev

Hi Alastair,

The patch is huge and could/should probably be split in smaller pieces 
to ease the review. However, having sinned on that same topic in the 
past, I made a first pass anyway. I haven't covered everything but tried 
to focus on the general setup of the driver for now.
Since the patch is very long, I'm writing all the comments in one chunk 
here instead of spreading them over a few thousand lines, where some 
would be easy to miss.


Update MAINTAINERS for the new files

Have you discussed with the directory owner if it's ok to split the 
driver over several files?


Kconfig
=======
Does it make sense to keep OCXL_SCM_DEBUG separate? Why not enabling it 
by default?


ocxl_scm.c
==========

scm_file_init()
---------------
on error paths, should call idr_destroy() (same pb found in base ocxl 
driver)


scm_probe() and _function_0()
-----------------------------

The different init between function 0 and 1 looks a bit off and it seems 
they could be unified. Function 1 does the same thing as function 0 
(call ocxl_function_open()) then some AFU-specific init, which we could 
skip if there's no AFU found on the function. And log a WARN if we find 
an AFU on something else than function 1, since, unlike ocxl, we know 
exactly what the device is like for SCM. But keep an common probe() 
function. It would also simplify the flow on scm_remove() and avoid 
problems (currently ocxl_function_close() is not called for function 1).

scm-data->timeouts: array of size 9 declared, we only init 7 members. 
With the zalloc() the others are at 0, is that correct?


Something looks wrong regarding data release in the error path(s). IIUC, 
we register the device early and rely on the release callback of the 
device to free all resources (in free_scm_dev()). We should probably 
have a comment in probe() to make it obvious. Or maybe better, have a 
subfunction to keep doing the rest of the inits which are dependent on 
the device release to be cleaned up. In the subsequent error paths in 
scm_probe(), we are missing a device_unregister()

Could log 120 times the same "Waiting for SCM to become usable" message, 
which is not really helping much.


free_scm()
---------
Related to above comment in probe(), it would help to be able to easily 
match the what's done in probe vs. undone here. For example, in probe(), 
there's scm_setup_irq(), where we do all things related to interrupts. 
But we don't have a subfunction to clean the interrupts state. It would 
help for readability and track potential misses. I didn't tried to match 
all of them, but the following calls seem missing:

ocxl_context_detach()
ocxl_afu_irq_free()


ocxl_remove()
-------------
see comment above about unifying function 0 and 1 case.
Why is nvdimm_bus_unregister() treated separately? Can't it be part of 
the "normal" freeing of resources done implicitly when calling 
device_unregister() in the free_scm() callback?


scm_setup_device_metadata()
---------------------------
function doesn't do any setup, so the name is misleading.

	for (i = 0; i < 8; i++)
		scm_data->fw_version[i] = (val >> (i * 8)) & 0xff;
=> looks like an endianess conversion? Can't we use the OCXL_BIG_ENDIAN 
when doing the mmio read?


scm_setup_irq()
---------------
if ocxl_afu_irq_get_addr(irq 1) or the ioremap(irq 1) fail, we jump to 
the label 'out_irq0' and will exit the function with rc = 0, instead of 
failing.



scm_setup_command_metadata()
----------------------------
it would make sense to initialize the mutex in the struct 
command_metadata in this function instead of the top of scm_probe(), to 
group all the related data inits.



scm_probe_function_0()
----------------------
comment above function:
  * This is important as it enables higher than 0 across all other 
functions,
  * which in turn enables higher bandwidth accesses

"higher than 0"?
I'm guessing you want to say function 0 configures the link, to ensure 
maximum bandwidth

EFAULT is usually reserved for an invalid memory access. Why not 
PTR_ERR() of the returned value from ocxl_function_open()?



struct scm_fops has a wrong indentation (spaces between .open and '=')



scm_heartbeat()
---------------
the "goto out" at the end of the good path is useless and unusual in the 
kernel, I think.


scm_register_lpc_mem()
----------------------
lpc_mem = ocxl_afu_lpc_mem(scm_data->ocxl_afu);
=> lpc_mem is allocated as part of the afu structure in ocxl, so that 
shouldn't be NULL. Still worth keeping, but I think lpc_mem->start is 
what really needs testing


scm_imn0_handler()
-----------------
I don't think we should return IRQ_NONE. As far as the kernel is 
concerned, an interrupt was raised. So it should be acknowledged, even 
if the fgpa is somehow in an incorrect state. So the IRQ_NONE should be 
IRQ_HANDLED


scm_imn1_handler()
------------------
for the sake of clarity, the potential error when calling scm_chi() 
should be treated the same in the 2 handlers.
What's the effect of nvdimm_bus_unregister() on any application using 
the memory?


#ifdef CONFIG_OCXL_SCM_DEBUG
It's usual and it helps navigate the code, to comment the config macro 
on the else and endif lines:
#endif /* CONFIG_OCXL_SCM_DEBUG */




ocxl-scm_internal.c
====================

scm_admin_command_request
-------------------------
Hardening: would it make sense to test and error out if the ACRA bit 
(used to test command is complete) is at 0 before submitting a new request?


scm_admin_command_complete_timeout
----------------------------------
A delay of 32ms is not that usual. A comment explaining why would be 
interesting.

Why timeout++ ?



ocxl-scm_sysfs.c
================
No reason to export the scm_sysfs_add symbol



ocxl_scm.h
==========
struct scm_ioctl_error_log:
char  fw_revision[8+1]
exposed to uncontrolled padding, which is a problem for a KABI
Remove the extra char?



memory_hotplug.c
================
whitespace diff shouldn't be here


   Fred


Le 25/10/2019 à 06:47, Alastair D'Silva a écrit :
> From: Alastair D'Silva <alastair@d-silva.org>
> 
> This driver exposes LPC memory on OpenCAPI SCM cards
> as an NVDIMM, allowing the existing nvram infrastructure
> to be used.
> 
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> ---
>   drivers/nvdimm/Kconfig             |   17 +
>   drivers/nvdimm/Makefile            |    3 +
>   drivers/nvdimm/ocxl-scm.c          | 2210 ++++++++++++++++++++++++++++
>   drivers/nvdimm/ocxl-scm_internal.c |  232 +++
>   drivers/nvdimm/ocxl-scm_internal.h |  331 +++++
>   drivers/nvdimm/ocxl-scm_sysfs.c    |  219 +++
>   include/uapi/linux/ocxl-scm.h      |  128 ++
>   mm/memory_hotplug.c                |    2 +-
>   8 files changed, 3141 insertions(+), 1 deletion(-)
>   create mode 100644 drivers/nvdimm/ocxl-scm.c
>   create mode 100644 drivers/nvdimm/ocxl-scm_internal.c
>   create mode 100644 drivers/nvdimm/ocxl-scm_internal.h
>   create mode 100644 drivers/nvdimm/ocxl-scm_sysfs.c
>   create mode 100644 include/uapi/linux/ocxl-scm.h
> 
> diff --git a/drivers/nvdimm/Kconfig b/drivers/nvdimm/Kconfig
> index 36af7af6b7cf..e4f7b6b08efd 100644
> --- a/drivers/nvdimm/Kconfig
> +++ b/drivers/nvdimm/Kconfig
> @@ -130,4 +130,21 @@ config NVDIMM_TEST_BUILD
>   	  core devm_memremap_pages() implementation and other
>   	  infrastructure.
>   
> +config OCXL_SCM
> +	tristate "OpenCAPI Storage Class Memory"
> +	depends on LIBNVDIMM
> +	select ZONE_DEVICE
> +	select OCXL
> +	help
> +	  Exposes devices that implement the OpenCAPI Storage Class Memory
> +	  specification as persistent memory regions.
> +
> +	  Select N if unsure.
> +
> +config OCXL_SCM_DEBUG
> +	bool "OpenCAPI Storage Class Memory debugging"
> +	depends on OCXL_SCM
> +	help
> +	  Enables low level IOCTLs for OpenCAPI SCM firmware development
> +
>   endif
> diff --git a/drivers/nvdimm/Makefile b/drivers/nvdimm/Makefile
> index 29203f3d3069..43d826397bfc 100644
> --- a/drivers/nvdimm/Makefile
> +++ b/drivers/nvdimm/Makefile
> @@ -6,6 +6,9 @@ obj-$(CONFIG_ND_BLK) += nd_blk.o
>   obj-$(CONFIG_X86_PMEM_LEGACY) += nd_e820.o
>   obj-$(CONFIG_OF_PMEM) += of_pmem.o
>   obj-$(CONFIG_VIRTIO_PMEM) += virtio_pmem.o nd_virtio.o
> +obj-$(CONFIG_OCXL_SCM) += ocxlscm.o
> +
> +ocxlscm-y := ocxl-scm.o ocxl-scm_internal.o ocxl-scm_sysfs.o
>   
>   nd_pmem-y := pmem.o
>   
> diff --git a/drivers/nvdimm/ocxl-scm.c b/drivers/nvdimm/ocxl-scm.c
> new file mode 100644
> index 000000000000..f4e6cc022de8
> --- /dev/null
> +++ b/drivers/nvdimm/ocxl-scm.c
> @@ -0,0 +1,2210 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +// Copyright 2019 IBM Corp.
> +
> +/*
> + * A driver for Storage Class Memory, connected via OpenCAPI
> + */
> +
> +#include <linux/module.h>
> +#include <misc/ocxl.h>
> +#include <linux/delay.h>
> +#include <linux/ndctl.h>
> +#include <linux/eventfd.h>
> +#include <linux/fs.h>
> +#include <linux/mm_types.h>
> +#include <linux/memory_hotplug.h>
> +#include "ocxl-scm_internal.h"
> +
> +
> +static const struct pci_device_id scm_pci_tbl[] = {
> +	{ PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0625), },
> +	{ }
> +};
> +
> +MODULE_DEVICE_TABLE(pci, scm_pci_tbl);
> +
> +#define SCM_NUM_MINORS 256 // Total to reserve
> +#define SCM_USABLE_TIMEOUT 120 // seconds
> +
> +static dev_t scm_dev;
> +static struct class *scm_class;
> +static struct mutex minors_idr_lock;
> +static struct idr minors_idr;
> +
> +static const struct attribute_group *scm_pmem_attribute_groups[] = {
> +	&nvdimm_bus_attribute_group,
> +	NULL,
> +};
> +
> +static const struct attribute_group *scm_pmem_region_attribute_groups[] = {
> +	&nd_region_attribute_group,
> +	&nd_device_attribute_group,
> +	&nd_mapping_attribute_group,
> +	&nd_numa_attribute_group,
> +	NULL,
> +};
> +
> +/**
> + * scm_ndctl_config_write() - Handle a ND_CMD_SET_CONFIG_DATA command from ndctl
> + * @scm_data: the SCM metadata
> + * @command: the incoming data to write
> + * Return: 0 on success, negative on failure
> + */
> +static int scm_ndctl_config_write(struct scm_data *scm_data,
> +				  struct nd_cmd_set_config_hdr *command)
> +{
> +	if (command->in_offset + command->in_length > SCM_LABEL_AREA_SIZE)
> +		return -EINVAL;
> +
> +	memcpy_flushcache(scm_data->metadata_addr + command->in_offset, command->in_buf,
> +			  command->in_length);
> +
> +	return 0;
> +}
> +
> +/**
> + * scm_ndctl_config_read() - Handle a ND_CMD_GET_CONFIG_DATA command from ndctl
> + * @scm_data: the SCM metadata
> + * @command: the read request
> + * Return: 0 on success, negative on failure
> + */
> +static int scm_ndctl_config_read(struct scm_data *scm_data,
> +				 struct nd_cmd_get_config_data_hdr *command)
> +{
> +	if (command->in_offset + command->in_length > SCM_LABEL_AREA_SIZE)
> +		return -EINVAL;
> +
> +	memcpy(command->out_buf, scm_data->metadata_addr + command->in_offset,
> +	       command->in_length);
> +
> +	return 0;
> +}
> +
> +/**
> + * scm_ndctl_config_size() - Handle a ND_CMD_GET_CONFIG_SIZE command from ndctl
> + * @scm_data: the SCM metadata
> + * @command: the read request
> + * Return: 0 on success, negative on failure
> + */
> +static int scm_ndctl_config_size(struct nd_cmd_get_config_size *command)
> +{
> +	command->status = 0;
> +	command->config_size = SCM_LABEL_AREA_SIZE;
> +	command->max_xfer = PAGE_SIZE;
> +
> +	return 0;
> +}
> +
> +static int read_smart_attrib(struct scm_data *scm_data, u16 offset,
> +			     struct scm_smart_attribs *attribs)
> +{
> +	u64 val;
> +	int rc;
> +	struct scm_smart_attrib *attrib;
> +	u8 attrib_id;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, offset, OCXL_LITTLE_ENDIAN,
> +				     &val);
> +	if (rc)
> +		return rc;
> +
> +	attrib_id = (val >> 56) & 0xff;
> +	switch (attrib_id) {
> +	case SCM_SMART_ATTR_POWER_ON_HOURS:
> +		attrib = &attribs->power_on_hours;
> +		break;
> +
> +	case SCM_SMART_ATTR_TEMPERATURE:
> +		attrib = &attribs->temperature;
> +		break;
> +
> +	case SCM_SMART_ATTR_LIFE_REMAINING:
> +		attrib = &attribs->life_remaining;
> +		break;
> +
> +	default:
> +		dev_err(&scm_data->dev, "Unknown smart attrib '%d'", attrib_id);
> +		return -EFAULT;
> +	}
> +
> +	attrib->id = attrib_id;
> +	attrib->attribute_flags = (val >> 40) & 0xffff;
> +	attrib->current_val = (val >> 32) & 0xff;
> +	attrib->threshold_val = (val >> 24) & 0xff;
> +	attrib->worst_val = (val >> 16) & 0xff;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, offset + 0x08,
> +				     OCXL_LITTLE_ENDIAN, &val);
> +	if (rc)
> +		return rc;
> +
> +	attrib->raw_val = val;
> +
> +	return 0;
> +}
> +
> +static int scm_smart_offset_0x00(struct scm_data *scm_data, u32 *length)
> +{
> +	int rc;
> +	u64 val;
> +
> +	u16 data_identifier;
> +	u32 data_length;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset,
> +				     OCXL_LITTLE_ENDIAN, &val);
> +	if (rc)
> +		return rc;
> +
> +	data_identifier = val >> 48;
> +	data_length = val & 0xFFFFFFFF;
> +
> +	if (data_identifier != 0x534D) {
> +		dev_err(&scm_data->dev,
> +			"Bad data identifier for smart data, expected 'SM', got '%-.*s'\n",
> +			2, (char *)&data_identifier);
> +		return -EFAULT;
> +	}
> +
> +	*length = data_length;
> +	return 0;
> +}
> +
> +static int scm_smart_update(struct scm_data *scm_data)
> +{
> +	u32 length, i;
> +	int rc;
> +
> +	mutex_lock(&scm_data->admin_command.lock);
> +
> +	rc = scm_admin_command_request(scm_data, ADMIN_COMMAND_SMART);
> +	if (rc)
> +		goto out;
> +
> +	rc = scm_admin_command_execute(scm_data);
> +	if (rc)
> +		goto out;
> +
> +	rc = scm_admin_command_complete_timeout(scm_data, ADMIN_COMMAND_SMART);
> +	if (rc < 0) {
> +		dev_err(&scm_data->dev, "SMART timeout\n");
> +		goto out;
> +	}
> +
> +	rc = scm_admin_response(scm_data);
> +	if (rc < 0)
> +		goto out;
> +	if (rc != STATUS_SUCCESS) {
> +		scm_warn_status(scm_data, "Unexpected status from SMART", rc);
> +		goto out;
> +	}
> +
> +	rc = scm_smart_offset_0x00(scm_data, &length);
> +	if (rc)
> +		goto out;
> +
> +	length /= 0x10; // Length now contains the number of attributes
> +
> +	for (i = 0; i < length; i++)
> +		read_smart_attrib(scm_data,
> +				  scm_data->admin_command.data_offset + 0x08 + i * 0x10,
> +				  &scm_data->smart);
> +
> +	rc = scm_admin_response_handled(scm_data);
> +	if (rc)
> +		goto out;
> +
> +	rc = 0;
> +	goto out;
> +
> +out:
> +	mutex_unlock(&scm_data->admin_command.lock);
> +	return rc;
> +}
> +
> +static int scm_ndctl_smart(struct scm_data *scm_data, void *buf,
> +			   unsigned int buf_len)
> +{
> +	int rc;
> +
> +	if (buf_len != sizeof(scm_data->smart))
> +		return -EINVAL;
> +
> +	rc = scm_smart_update(scm_data);
> +	if (rc)
> +		return rc;
> +
> +	memcpy(buf, &scm_data->smart, buf_len);
> +
> +	return 0;
> +}
> +
> +
> +static int scm_ndctl(struct nvdimm_bus_descriptor *nd_desc,
> +		     struct nvdimm *nvdimm,
> +		     unsigned int cmd, void *buf, unsigned int buf_len, int *cmd_rc)
> +{
> +	struct scm_data *scm_data = container_of(nd_desc, struct scm_data, bus_desc);
> +
> +	switch (cmd) {
> +	case ND_CMD_SMART:
> +		*cmd_rc = scm_ndctl_smart(scm_data, buf, buf_len);
> +		return 0;
> +
> +	case ND_CMD_GET_CONFIG_SIZE:
> +		*cmd_rc = scm_ndctl_config_size(buf);
> +		return 0;
> +
> +	case ND_CMD_GET_CONFIG_DATA:
> +		*cmd_rc = scm_ndctl_config_read(scm_data, buf);
> +		return 0;
> +
> +	case ND_CMD_SET_CONFIG_DATA:
> +		*cmd_rc = scm_ndctl_config_write(scm_data, buf);
> +		return 0;
> +
> +	default:
> +		return -ENOTTY;
> +	}
> +}
> +
> +static ssize_t serial_show(struct device *dev,
> +			   struct device_attribute *attr, char *buf)
> +{
> +	struct nvdimm *nvdimm = to_nvdimm(dev);
> +	struct scm_data *scm_data = nvdimm_provider_data(nvdimm);
> +	const struct ocxl_fn_config *config = ocxl_function_config(scm_data->ocxl_fn);
> +
> +	return sprintf(buf, "0x%llx\n", config->serial);
> +}
> +static DEVICE_ATTR_RO(serial);
> +
> +static struct attribute *scm_dimm_attributes[] = {
> +	&dev_attr_serial.attr,
> +	NULL,
> +};
> +
> +static umode_t scm_dimm_attr_visible(struct kobject *kobj,
> +				     struct attribute *a, int n)
> +{
> +	return a->mode;
> +}
> +
> +static const struct attribute_group scm_dimm_attribute_group = {
> +	.name = "scm",
> +	.attrs = scm_dimm_attributes,
> +	.is_visible = scm_dimm_attr_visible,
> +};
> +
> +static const struct attribute_group *scm_dimm_attribute_groups[] = {
> +	&nvdimm_attribute_group,
> +	&nd_device_attribute_group,
> +	&scm_dimm_attribute_group,
> +	NULL,
> +};
> +
> +/**
> + * scm_reserve_metadata() - Reserve space for nvdimm metadata
> + * @scm_data: The SCM device data
> + * @lpc_mem: The resource representing the LPC memory of the SCM device
> + */
> +static int scm_reserve_metadata(struct scm_data *scm_data,
> +				struct resource *lpc_mem)
> +{
> +	scm_data->metadata_addr = devm_memremap(&scm_data->dev, lpc_mem->start,
> +						SCM_LABEL_AREA_SIZE, MEMREMAP_WB);
> +	if (IS_ERR(scm_data->metadata_addr))
> +		return PTR_ERR(scm_data->metadata_addr);
> +
> +	return 0;
> +}
> +
> +/**
> + * scm_overwrite() - Overwrite all data on the card
> + * @scm_data: The SCM device data
> + * Return: 0 on success
> + */
> +int scm_overwrite(struct scm_data *scm_data)
> +{
> +	int rc;
> +
> +	mutex_lock(&scm_data->ns_command.lock);
> +
> +	rc = scm_ns_command_request(scm_data, NS_COMMAND_SECURE_ERASE);
> +	if (rc)
> +		goto out;
> +
> +	rc = scm_ns_command_execute(scm_data);
> +	if (rc)
> +		goto out;
> +
> +	scm_data->overwrite_state = SCM_OVERWRITE_BUSY;
> +
> +	return 0;
> +
> +out:
> +	mutex_unlock(&scm_data->ns_command.lock);
> +	return rc;
> +}
> +
> +/**
> + * scm_secop_overwrite() - Overwrite all data on the card
> + * @nvdimm: The nvdimm representation of the SCM device to start the overwrite on
> + * @key_data: Unused (no security key implementation)
> + * Return: 0 on success
> + */
> +static int scm_secop_overwrite(struct nvdimm *nvdimm,
> +			       const struct nvdimm_key_data *key_data)
> +{
> +	struct scm_data *scm_data = nvdimm_provider_data(nvdimm);
> +
> +	return scm_overwrite(scm_data);
> +}
> +
> +/**
> + * scm_secop_query_overwrite() - Get the current overwrite state
> + * @nvdimm: The nvdimm representation of the SCM device to start the overwrite on
> + * Return: 0 if successful or idle, -EBUSY if busy, -EFAULT if failed
> + */
> +static int scm_secop_query_overwrite(struct nvdimm *nvdimm)
> +{
> +	struct scm_data *scm_data = nvdimm_provider_data(nvdimm);
> +
> +	if (scm_data->overwrite_state == SCM_OVERWRITE_BUSY)
> +		return -EBUSY;
> +
> +	if (scm_data->overwrite_state == SCM_OVERWRITE_FAILED)
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
> +/**
> + * scm_secop_get_flags() - return the security flags for the SCM device
> + */
> +static unsigned long scm_secop_get_flags(struct nvdimm *nvdimm,
> +		enum nvdimm_passphrase_type ptype)
> +{
> +	struct scm_data *scm_data = nvdimm_provider_data(nvdimm);
> +
> +	if (scm_data->overwrite_state == SCM_OVERWRITE_BUSY)
> +		return BIT(NVDIMM_SECURITY_OVERWRITE);
> +
> +	return BIT(NVDIMM_SECURITY_DISABLED);
> +}
> +
> +static const struct nvdimm_security_ops sec_ops  = {
> +	.get_flags = scm_secop_get_flags,
> +	.overwrite = scm_secop_overwrite,
> +	.query_overwrite = scm_secop_query_overwrite,
> +};
> +
> +/**
> + * scm_register_lpc_mem() - Discover persistent memory on a device and register it with the NVDIMM subsystem
> + * @scm_data: The SCM device data
> + * Return: 0 on success
> + */
> +static int scm_register_lpc_mem(struct scm_data *scm_data)
> +{
> +	struct nd_region_desc region_desc;
> +	struct nd_mapping_desc nd_mapping_desc;
> +	struct resource *lpc_mem;
> +	const struct ocxl_afu_config *config;
> +	const struct ocxl_fn_config *fn_config;
> +	int rc;
> +	unsigned long nvdimm_cmd_mask = 0;
> +	unsigned long nvdimm_flags = 0;
> +	int target_node;
> +	char serial[16+1];
> +
> +	// Set up the reserved metadata area
> +	rc = ocxl_afu_map_lpc_mem(scm_data->ocxl_afu);
> +	if (rc < 0)
> +		return rc;
> +
> +	lpc_mem = ocxl_afu_lpc_mem(scm_data->ocxl_afu);
> +	if (lpc_mem == NULL)
> +		return -EINVAL;
> +
> +	config = ocxl_afu_config(scm_data->ocxl_afu);
> +	fn_config = ocxl_function_config(scm_data->ocxl_fn);
> +
> +	rc = scm_reserve_metadata(scm_data, lpc_mem);
> +	if (rc)
> +		return rc;
> +
> +	scm_data->bus_desc.attr_groups = scm_pmem_attribute_groups;
> +	scm_data->bus_desc.provider_name = "scm";
> +	scm_data->bus_desc.ndctl = scm_ndctl;
> +	scm_data->bus_desc.module = THIS_MODULE;
> +
> +	scm_data->nvdimm_bus = nvdimm_bus_register(&scm_data->dev,
> +			       &scm_data->bus_desc);
> +	if (!scm_data->nvdimm_bus)
> +		return -EINVAL;
> +
> +	scm_data->scm_res.start = (u64)lpc_mem->start + SCM_LABEL_AREA_SIZE;
> +	scm_data->scm_res.end = (u64)lpc_mem->start + config->lpc_mem_size - 1;
> +	scm_data->scm_res.name = "SCM persistent memory";
> +
> +	set_bit(ND_CMD_GET_CONFIG_SIZE, &nvdimm_cmd_mask);
> +	set_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm_cmd_mask);
> +	set_bit(ND_CMD_SET_CONFIG_DATA, &nvdimm_cmd_mask);
> +	set_bit(ND_CMD_SMART, &nvdimm_cmd_mask);
> +
> +	set_bit(NDD_ALIASING, &nvdimm_flags);
> +
> +	snprintf(serial, sizeof(serial), "%llx", fn_config->serial);
> +	nd_mapping_desc.nvdimm = __nvdimm_create(scm_data->nvdimm_bus, scm_data,
> +				 scm_dimm_attribute_groups,
> +				 nvdimm_flags, nvdimm_cmd_mask,
> +				 0, NULL, serial, &sec_ops);
> +	if (!nd_mapping_desc.nvdimm)
> +		return -ENOMEM;
> +
> +	if (nvdimm_bus_check_dimm_count(scm_data->nvdimm_bus, 1))
> +		return -EINVAL;
> +
> +	nd_mapping_desc.start = scm_data->scm_res.start;
> +	nd_mapping_desc.size = resource_size(&scm_data->scm_res);
> +	nd_mapping_desc.position = 0;
> +
> +	scm_data->nd_set.cookie1 = fn_config->serial + 1; // allow for empty serial
> +	scm_data->nd_set.cookie2 = fn_config->serial + 1;
> +
> +	target_node = of_node_to_nid(scm_data->pdev->dev.of_node);
> +
> +	memset(&region_desc, 0, sizeof(region_desc));
> +	region_desc.res = &scm_data->scm_res;
> +	region_desc.attr_groups = scm_pmem_region_attribute_groups;
> +	region_desc.numa_node = NUMA_NO_NODE;
> +	region_desc.target_node = target_node;
> +	region_desc.num_mappings = 1;
> +	region_desc.mapping = &nd_mapping_desc;
> +	region_desc.nd_set = &scm_data->nd_set;
> +
> +	set_bit(ND_REGION_PAGEMAP, &region_desc.flags);
> +	/*
> +	 * NB: libnvdimm copies the data from ndr_desc into it's own
> +	 * structures so passing a stack pointer is fine.
> +	 */
> +	scm_data->nd_region = nvdimm_pmem_region_create(scm_data->nvdimm_bus,
> +			      &region_desc);
> +	if (!scm_data->nd_region)
> +		return -EINVAL;
> +
> +	dev_info(&scm_data->dev,
> +		 "Onlining %lluMB of persistent memory\n",
> +		 nd_mapping_desc.size / SZ_1M);
> +
> +	return 0;
> +}
> +
> +/**
> + * scm_is_memory_available() - Does the controller have memory available?
> + * @scm_data: a pointer to the SCM device data
> + * Return: true if the controller has memory available
> + */
> +static bool scm_is_memory_available(const struct scm_data *scm_data)
> +{
> +	u64 val = 0;
> +	int rc = scm_chi(scm_data, &val);
> +
> +	WARN_ON(rc < 0);
> +
> +	return (val & GLOBAL_MMIO_CHI_MA) != 0;
> +}
> +
> +/**
> + * scm_extract_command_metadata() - Extract command data from MMIO & save it for further use
> + * @scm_data: a pointer to the SCM device data
> + * @offset: The base address of the command data structures (address of CREQO)
> + * @command_metadata: A pointer to the command metadata to populate
> + * Return: 0 on success, negative on failure
> + */
> +static int scm_extract_command_metadata(struct scm_data *scm_data, u32 offset,
> +					struct command_metadata *command_metadata)
> +{
> +	int rc;
> +	u64 tmp;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, offset, OCXL_LITTLE_ENDIAN,
> +				     &tmp);
> +	if (rc)
> +		return rc;
> +
> +	command_metadata->request_offset = tmp >> 32;
> +	command_metadata->response_offset = tmp & 0xFFFFFFFF;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, offset + 8, OCXL_LITTLE_ENDIAN,
> +				     &tmp);
> +	if (rc)
> +		return rc;
> +
> +	command_metadata->data_offset = tmp >> 32;
> +	command_metadata->data_size = tmp & 0xFFFFFFFF;
> +
> +	command_metadata->id = 0;
> +
> +	return 0;
> +}
> +
> +/**
> + * scm_setup_command_metadata() - Set up the command metadata
> + * @scm_data: a pointer to the SCM device data
> + */
> +static int scm_setup_command_metadata(struct scm_data *scm_data)
> +{
> +	int rc;
> +
> +	rc = scm_extract_command_metadata(scm_data, GLOBAL_MMIO_ACMA_CREQO,
> +					  &scm_data->admin_command);
> +	if (rc)
> +		return rc;
> +
> +	rc = scm_extract_command_metadata(scm_data, GLOBAL_MMIO_NSCMA_CREQO,
> +					  &scm_data->ns_command);
> +	if (rc)
> +		return rc;
> +
> +	return 0;
> +}
> +
> +/**
> + * scm_heartbeat() - Issue a heartbeat command to the controller
> + * @scm_data: a pointer to the SCM device data
> + * Return: 0 if the controller responded correctly, negative on error
> + */
> +static int scm_heartbeat(struct scm_data *scm_data)
> +{
> +	int rc;
> +
> +	mutex_lock(&scm_data->admin_command.lock);
> +
> +	rc = scm_admin_command_request(scm_data, ADMIN_COMMAND_HEARTBEAT);
> +	if (rc)
> +		goto out;
> +
> +	rc = scm_admin_command_execute(scm_data);
> +	if (rc)
> +		goto out;
> +
> +	rc = scm_admin_command_complete_timeout(scm_data, ADMIN_COMMAND_HEARTBEAT);
> +	if (rc < 0) {
> +		dev_err(&scm_data->dev, "Heartbeat timeout\n");
> +		goto out;
> +	}
> +
> +	rc = scm_admin_response(scm_data);
> +	if (rc < 0)
> +		goto out;
> +	if (rc != STATUS_SUCCESS)
> +		scm_warn_status(scm_data, "Unexpected status from heartbeat", rc);
> +
> +	rc = scm_admin_response_handled(scm_data);
> +
> +	goto out;
> +
> +out:
> +	mutex_unlock(&scm_data->admin_command.lock);
> +	return rc;
> +}
> +
> +/**
> + * scm_is_usable() - Is a controller usable?
> + * @scm_data: a pointer to the SCM device data
> + * Return: true if the controller is usable
> + */
> +static bool scm_is_usable(const struct scm_data *scm_data)
> +{
> +	if (!scm_controller_is_ready(scm_data)) {
> +		dev_err(&scm_data->dev, "SCM controller is not ready.\n");
> +		return false;
> +	}
> +
> +	if (!scm_is_memory_available(scm_data)) {
> +		dev_err(&scm_data->dev,
> +			"SCM controller does not have memory available.\n");
> +		return false;
> +	}
> +
> +	return true;
> +}
> +
> +/**
> + * allocate_scm_minor() - Allocate a minor number to use for an SCM device
> + * @scm_data: The SCM device to associate the minor with
> + * Return: the allocated minor number
> + */
> +static int allocate_scm_minor(struct scm_data *scm_data)
> +{
> +	int minor;
> +
> +	mutex_lock(&minors_idr_lock);
> +	minor = idr_alloc(&minors_idr, scm_data, 0, SCM_NUM_MINORS, GFP_KERNEL);
> +	mutex_unlock(&minors_idr_lock);
> +	return minor;
> +}
> +
> +static void free_scm_minor(struct scm_data *scm_data)
> +{
> +	mutex_lock(&minors_idr_lock);
> +	idr_remove(&minors_idr, MINOR(scm_data->dev.devt));
> +	mutex_unlock(&minors_idr_lock);
> +}
> +
> +/**
> + * free_scm() - Free all members of an SCM struct
> + * @scm_data: the SCM metadata to clear
> + */
> +static void free_scm(struct scm_data *scm_data)
> +{
> +	// Disable doorbells
> +	(void)ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_CHIEC,
> +				     OCXL_LITTLE_ENDIAN,
> +				     GLOBAL_MMIO_CHI_ALL);
> +
> +	free_scm_minor(scm_data);
> +
> +	if (scm_data->irq_addr[1])
> +		iounmap(scm_data->irq_addr[1]);
> +
> +	if (scm_data->irq_addr[0])
> +		iounmap(scm_data->irq_addr[0]);
> +
> +	if (scm_data->cdev.owner)
> +		cdev_del(&scm_data->cdev);
> +
> +	if (scm_data->metadata_addr)
> +		devm_memunmap(&scm_data->dev, scm_data->metadata_addr);
> +
> +	if (scm_data->ocxl_context)
> +		ocxl_context_free(scm_data->ocxl_context);
> +
> +	if (scm_data->ocxl_afu)
> +		ocxl_afu_put(scm_data->ocxl_afu);
> +
> +	if (scm_data->ocxl_fn)
> +		ocxl_function_close(scm_data->ocxl_fn);
> +
> +	kfree(scm_data);
> +}
> +
> +/**
> + * free_scm_dev - Free an SCM device
> + * @dev: The device struct
> + */
> +static void free_scm_dev(struct device *dev)
> +{
> +	struct scm_data *scm_data = container_of(dev, struct scm_data, dev);
> +
> +	free_scm(scm_data);
> +}
> +
> +/**
> + * scm_register - Register an SCM device with the kernel
> + * @scm_data: the SCM metadata
> + * Return: 0 on success, negative on failure
> + */
> +static int scm_register(struct scm_data *scm_data)
> +{
> +	int rc;
> +	int minor = allocate_scm_minor(scm_data);
> +
> +	if (minor < 0)
> +		return minor;
> +
> +	scm_data->dev.release = free_scm_dev;
> +	rc = dev_set_name(&scm_data->dev, "scm%d", minor);
> +	if (rc < 0)
> +		return rc;
> +
> +	scm_data->dev.devt = MKDEV(MAJOR(scm_dev), minor);
> +	scm_data->dev.class = scm_class;
> +	scm_data->dev.parent = &scm_data->pdev->dev;
> +
> +	rc = device_register(&scm_data->dev);
> +	return rc;
> +}
> +
> +static void scm_put(struct scm_data *scm_data)
> +{
> +	put_device(&scm_data->dev);
> +}
> +
> +struct scm_data *scm_get(struct scm_data *scm_data)
> +{
> +	return (get_device(&scm_data->dev) == NULL) ? NULL : scm_data;
> +}
> +
> +static struct scm_data *find_and_get_scm(dev_t devno)
> +{
> +	struct scm_data *scm_data;
> +	int minor = MINOR(devno);
> +	/*
> +	 * We don't declare an RCU critical section here, as our AFU
> +	 * is protected by a reference counter on the device. By the time the
> +	 * minor number of a device is removed from the idr, the ref count of
> +	 * the device is already at 0, so no user API will access that AFU and
> +	 * this function can't return it.
> +	 */
> +	scm_data = idr_find(&minors_idr, minor);
> +	if (scm_data)
> +		scm_get(scm_data);
> +	return scm_data;
> +}
> +
> +static int scm_file_open(struct inode *inode, struct file *file)
> +{
> +	struct scm_data *scm_data;
> +
> +	scm_data = find_and_get_scm(inode->i_rdev);
> +	if (!scm_data)
> +		return -ENODEV;
> +
> +	file->private_data = scm_data;
> +	return 0;
> +}
> +
> +static int scm_file_release(struct inode *inode, struct file *file)
> +{
> +	struct scm_data *scm_data = file->private_data;
> +
> +	if (scm_data->ev_ctx) {
> +		eventfd_ctx_put(scm_data->ev_ctx);
> +		scm_data->ev_ctx = NULL;
> +	}
> +
> +	scm_put(scm_data);
> +	return 0;
> +}
> +
> +static int scm_ioctl_buffer_info(struct scm_data *scm_data,
> +				 struct scm_ioctl_buffer_info __user *uarg)
> +{
> +	struct scm_ioctl_buffer_info args;
> +
> +	args.admin_command_buffer_size = scm_data->admin_command.data_size;
> +	args.near_storage_buffer_size = scm_data->ns_command.data_size;
> +
> +	if (copy_to_user(uarg, &args, sizeof(args)))
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
> +static int scm_error_log_offset_0x00(struct scm_data *scm_data, u16 *length)
> +{
> +	int rc;
> +	u64 val;
> +
> +	u16 data_identifier;
> +	u32 data_length;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset,
> +				     OCXL_LITTLE_ENDIAN, &val);
> +	if (rc)
> +		return rc;
> +
> +	data_identifier = val >> 48;
> +	data_length = val & 0xFFFF;
> +
> +	if (data_identifier != 0x454C) {
> +		dev_err(&scm_data->dev,
> +			"Bad data identifier for error log data, expected 'EL', got '%2s' (%#x), data_length=%u\n",
> +			(char *)&data_identifier,
> +			(unsigned int)data_identifier, data_length);
> +		return -EFAULT;
> +	}
> +
> +	*length = data_length;
> +	return 0;
> +}
> +
> +static int scm_error_log_offset_0x08(struct scm_data *scm_data,
> +				     u32 *log_identifier, u32 *program_ref_code)
> +{
> +	int rc;
> +	u64 val;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset + 0x08,
> +				     OCXL_LITTLE_ENDIAN, &val);
> +	if (rc)
> +		return rc;
> +
> +	*log_identifier = val >> 32;
> +	*program_ref_code = val & 0xFFFFFFFF;
> +
> +	return 0;
> +}
> +
> +static int scm_read_error_log(struct scm_data *scm_data,
> +			      struct scm_ioctl_error_log *log, bool buf_is_user)
> +{
> +	u64 val;
> +	u16 user_buf_length;
> +	u16 buf_length;
> +	u16 i;
> +	int rc;
> +
> +	if (log->buf_size % 8)
> +		return -EINVAL;
> +
> +	rc = scm_chi(scm_data, &val);
> +	if (rc)
> +		goto out;
> +
> +	if (!(val & GLOBAL_MMIO_CHI_ELA))
> +		return -EAGAIN;
> +
> +	user_buf_length = log->buf_size;
> +
> +	mutex_lock(&scm_data->admin_command.lock);
> +
> +	rc = scm_admin_command_request(scm_data, ADMIN_COMMAND_ERRLOG);
> +	if (rc)
> +		goto out;
> +
> +	rc = scm_admin_command_execute(scm_data);
> +	if (rc)
> +		goto out;
> +
> +	rc = scm_admin_command_complete_timeout(scm_data, ADMIN_COMMAND_ERRLOG);
> +	if (rc < 0) {
> +		dev_warn(&scm_data->dev, "Read error log timed out\n");
> +		goto out;
> +	}
> +
> +	rc = scm_admin_response(scm_data);
> +	if (rc < 0)
> +		goto out;
> +	if (rc != STATUS_SUCCESS) {
> +		scm_warn_status(scm_data, "Unexpected status from retrieve error log", rc);
> +		goto out;
> +	}
> +
> +
> +	rc = scm_error_log_offset_0x00(scm_data, &log->buf_size);
> +	if (rc)
> +		goto out;
> +	// log->buf_size now contains the scm buffer size, not the user size
> +
> +	rc = scm_error_log_offset_0x08(scm_data, &log->log_identifier,
> +				       &log->program_reference_code);
> +	if (rc)
> +		goto out;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset + 0x10,
> +				     OCXL_LITTLE_ENDIAN, &val);
> +	if (rc)
> +		goto out;
> +
> +	log->error_log_type = val >> 56;
> +	log->action_flags = (log->error_log_type == SCM_ERROR_LOG_TYPE_GENERAL) ?
> +			    (val >> 32) & 0xFFFFFF : 0;
> +	log->power_on_seconds = val & 0xFFFFFFFF;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset + 0x18,
> +				     OCXL_LITTLE_ENDIAN, &log->timestamp);
> +	if (rc)
> +		goto out;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset + 0x20,
> +				     OCXL_HOST_ENDIAN, &log->wwid[0]);
> +	if (rc)
> +		goto out;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset + 0x28,
> +				     OCXL_HOST_ENDIAN, &log->wwid[1]);
> +	if (rc)
> +		goto out;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset + 0x30,
> +				     OCXL_HOST_ENDIAN, (u64 *)log->fw_revision);
> +	if (rc)
> +		goto out;
> +	log->fw_revision[8] = '\0';
> +
> +	buf_length = (user_buf_length < log->buf_size) ?
> +		     user_buf_length : log->buf_size;
> +	for (i = 0; i < buf_length + 0x48; i += 8) {
> +		u64 val;
> +
> +		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +					     scm_data->admin_command.data_offset + i,
> +					     OCXL_HOST_ENDIAN, &val);
> +		if (rc)
> +			goto out;
> +
> +		if (buf_is_user) {
> +			if (copy_to_user(&log->buf[i], &val, sizeof(u64))) {
> +				rc = -EFAULT;
> +				goto out;
> +			}
> +		} else
> +			log->buf[i] = val;
> +	}
> +
> +	rc = scm_admin_response_handled(scm_data);
> +	if (rc)
> +		goto out;
> +
> +out:
> +	mutex_unlock(&scm_data->admin_command.lock);
> +	return rc;
> +
> +}
> +
> +static int scm_ioctl_error_log(struct scm_data *scm_data,
> +			       struct scm_ioctl_error_log __user *uarg)
> +{
> +	struct scm_ioctl_error_log args;
> +	int rc;
> +
> +	if (copy_from_user(&args, uarg, sizeof(args)))
> +		return -EFAULT;
> +
> +	rc = scm_read_error_log(scm_data, &args, true);
> +	if (rc)
> +		return rc;
> +
> +	if (copy_to_user(uarg, &args, sizeof(args)))
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
> +static int scm_ioctl_controller_dump_data(struct scm_data *scm_data,
> +	struct scm_ioctl_controller_dump_data __user *uarg)
> +{
> +	struct scm_ioctl_controller_dump_data args;
> +	u16 i;
> +	u64 val;
> +	int rc;
> +
> +	if (copy_from_user(&args, uarg, sizeof(args)))
> +		return -EFAULT;
> +
> +	if (args.buf_size % 8)
> +		return -EINVAL;
> +
> +	if (args.buf_size > scm_data->admin_command.data_size)
> +		return -EINVAL;
> +
> +	mutex_lock(&scm_data->admin_command.lock);
> +
> +	rc = scm_admin_command_request(scm_data, ADMIN_COMMAND_CONTROLLER_DUMP);
> +	if (rc)
> +		goto out;
> +
> +	val = ((u64)args.offset) << 32;
> +	val |= args.buf_size;
> +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> +				      scm_data->admin_command.request_offset + 0x08,
> +				      OCXL_LITTLE_ENDIAN, val);
> +	if (rc)
> +		goto out;
> +
> +	rc = scm_admin_command_execute(scm_data);
> +	if (rc)
> +		goto out;
> +
> +	rc = scm_admin_command_complete_timeout(scm_data,
> +						ADMIN_COMMAND_CONTROLLER_DUMP);
> +	if (rc < 0) {
> +		dev_warn(&scm_data->dev, "Controller dump timed out\n");
> +		goto out;
> +	}
> +
> +	rc = scm_admin_response(scm_data);
> +	if (rc < 0)
> +		goto out;
> +	if (rc != STATUS_SUCCESS) {
> +		scm_warn_status(scm_data,
> +				"Unexpected status from retrieve error log",
> +				rc);
> +		goto out;
> +	}
> +
> +	for (i = 0; i < args.buf_size; i += 8) {
> +		u64 val;
> +
> +		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +					     scm_data->admin_command.data_offset + i,
> +					     OCXL_HOST_ENDIAN, &val);
> +		if (rc)
> +			goto out;
> +
> +		if (copy_to_user(&args.buf[i], &val, sizeof(u64))) {
> +			rc = -EFAULT;
> +			goto out;
> +		}
> +	}
> +
> +	if (copy_to_user(uarg, &args, sizeof(args))) {
> +		rc = -EFAULT;
> +		goto out;
> +	}
> +
> +	rc = scm_admin_response_handled(scm_data);
> +	if (rc)
> +		goto out;
> +
> +out:
> +	mutex_unlock(&scm_data->admin_command.lock);
> +	return rc;
> +}
> +
> +int scm_request_controller_dump(struct scm_data *scm_data)
> +{
> +	int rc;
> +	u64 busy = 1;
> +
> +	rc = ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_CHIC,
> +				    OCXL_LITTLE_ENDIAN,
> +				    GLOBAL_MMIO_CHI_CDA);
> +
> +
> +	rc = ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_HCI,
> +				    OCXL_LITTLE_ENDIAN,
> +				    GLOBAL_MMIO_HCI_CONTROLLER_DUMP);
> +	if (rc)
> +		return rc;
> +
> +	while (busy) {
> +		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +					     GLOBAL_MMIO_HCI,
> +					     OCXL_LITTLE_ENDIAN, &busy);
> +		if (rc)
> +			return rc;
> +
> +		busy &= GLOBAL_MMIO_HCI_CONTROLLER_DUMP;
> +		cond_resched();
> +	}
> +
> +	return 0;
> +}
> +
> +static int scm_ioctl_controller_dump_complete(struct scm_data *scm_data)
> +{
> +	int rc;
> +
> +	rc = ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_HCI,
> +				    OCXL_LITTLE_ENDIAN,
> +				    GLOBAL_MMIO_HCI_CONTROLLER_DUMP_COLLECTED);
> +
> +	if (rc)
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
> +static int scm_controller_stats_offset_0x00(struct scm_data *scm_data,
> +	u32 *length)
> +{
> +	int rc;
> +	u64 val;
> +
> +	u16 data_identifier;
> +	u32 data_length;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset,
> +				     OCXL_LITTLE_ENDIAN, &val);
> +	if (rc)
> +		return rc;
> +
> +	data_identifier = val >> 48;
> +	data_length = val & 0xFFFFFFFF;
> +
> +	if (data_identifier != 0x4353) {
> +		dev_err(&scm_data->dev,
> +			"Bad data identifier for controller stats, expected 'CS', got '%-.*s'\n",
> +			2, (char *)&data_identifier);
> +		return -EFAULT;
> +	}
> +
> +	*length = data_length;
> +	return 0;
> +}
> +
> +static int scm_ioctl_controller_stats(struct scm_data *scm_data,
> +				      struct scm_ioctl_controller_stats __user *uarg)
> +{
> +	struct scm_ioctl_controller_stats args;
> +	u32 length;
> +	int rc;
> +	u64 val;
> +
> +	memset(&args, '\0', sizeof(args));
> +
> +	mutex_lock(&scm_data->admin_command.lock);
> +
> +	rc = scm_admin_command_request(scm_data, ADMIN_COMMAND_CONTROLLER_STATS);
> +	if (rc)
> +		goto out;
> +
> +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> +				      scm_data->admin_command.request_offset + 0x08,
> +				      OCXL_LITTLE_ENDIAN, 0);
> +	if (rc)
> +		goto out;
> +
> +	rc = scm_admin_command_execute(scm_data);
> +	if (rc)
> +		goto out;
> +
> +
> +	rc = scm_admin_command_complete_timeout(scm_data,
> +						ADMIN_COMMAND_CONTROLLER_STATS);
> +	if (rc < 0) {
> +		dev_warn(&scm_data->dev, "Controller stats timed out\n");
> +		goto out;
> +	}
> +
> +	rc = scm_admin_response(scm_data);
> +	if (rc < 0)
> +		goto out;
> +	if (rc != STATUS_SUCCESS) {
> +		scm_warn_status(scm_data,
> +				"Unexpected status from controller stats", rc);
> +		goto out;
> +	}
> +
> +	rc = scm_controller_stats_offset_0x00(scm_data, &length);
> +	if (rc)
> +		goto out;
> +
> +	if (length != 0x140)
> +		scm_warn_status(scm_data,
> +				"Unexpected length for controller stats data, expected 0x140, got 0x%x",
> +				length);
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset + 0x08 + 0x08,
> +				     OCXL_LITTLE_ENDIAN, &val);
> +	if (rc)
> +		goto out;
> +
> +	args.reset_count = val >> 32;
> +	args.reset_uptime = val & 0xFFFFFFFF;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset + 0x08 + 0x10,
> +				     OCXL_LITTLE_ENDIAN, &val);
> +	if (rc)
> +		goto out;
> +
> +	args.power_on_uptime = val >> 32;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x08,
> +				     OCXL_LITTLE_ENDIAN, &args.host_load_count);
> +	if (rc)
> +		goto out;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x10,
> +				     OCXL_LITTLE_ENDIAN, &args.host_store_count);
> +	if (rc)
> +		goto out;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x18,
> +				     OCXL_LITTLE_ENDIAN, &args.media_read_count);
> +	if (rc)
> +		goto out;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x20,
> +				     OCXL_LITTLE_ENDIAN, &args.media_write_count);
> +	if (rc)
> +		goto out;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x28,
> +				     OCXL_LITTLE_ENDIAN, &args.cache_hit_count);
> +	if (rc)
> +		goto out;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x30,
> +				     OCXL_LITTLE_ENDIAN, &args.cache_miss_count);
> +	if (rc)
> +		goto out;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x38,
> +				     OCXL_LITTLE_ENDIAN, &args.media_read_latency);
> +	if (rc)
> +		goto out;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x40,
> +				     OCXL_LITTLE_ENDIAN, &args.media_write_latency);
> +	if (rc)
> +		goto out;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x48,
> +				     OCXL_LITTLE_ENDIAN, &args.cache_read_latency);
> +	if (rc)
> +		goto out;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +				     scm_data->admin_command.data_offset + 0x08 + 0x40 + 0x50,
> +				     OCXL_LITTLE_ENDIAN, &args.cache_write_latency);
> +	if (rc)
> +		goto out;
> +
> +	if (copy_to_user(uarg, &args, sizeof(args))) {
> +		rc = -EFAULT;
> +		goto out;
> +	}
> +
> +	rc = scm_admin_response_handled(scm_data);
> +	if (rc)
> +		goto out;
> +
> +	rc = 0;
> +	goto out;
> +
> +out:
> +	mutex_unlock(&scm_data->admin_command.lock);
> +	return rc;
> +}
> +
> +static int scm_ioctl_eventfd(struct scm_data *scm_data,
> +			     struct scm_ioctl_eventfd __user *uarg)
> +{
> +	struct scm_ioctl_eventfd args;
> +
> +	if (copy_from_user(&args, uarg, sizeof(args)))
> +		return -EFAULT;
> +
> +	if (scm_data->ev_ctx)
> +		return -EFAULT;
> +
> +	scm_data->ev_ctx = eventfd_ctx_fdget(args.eventfd);
> +	if (!scm_data->ev_ctx)
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +
> +static int scm_ioctl_event_check(struct scm_data *scm_data, u64 __user *uarg)
> +{
> +	u64 val = 0;
> +	int rc;
> +	u64 chi = 0;
> +
> +	rc = scm_chi(scm_data, &chi);
> +	if (rc < 0)
> +		return -EFAULT;
> +
> +	if (chi & GLOBAL_MMIO_CHI_ELA)
> +		val |= SCM_IOCTL_EVENT_ERROR_LOG_AVAILABLE;
> +
> +	if (chi & GLOBAL_MMIO_CHI_CDA)
> +		val |= SCM_IOCTL_EVENT_CONTROLLER_DUMP_AVAILABLE;
> +
> +	if (chi & GLOBAL_MMIO_CHI_CFFS)
> +		val |= SCM_IOCTL_EVENT_FIRMWARE_FATAL;
> +
> +	if (chi & GLOBAL_MMIO_CHI_CHFS)
> +		val |= SCM_IOCTL_EVENT_HARDWARE_FATAL;
> +
> +	rc = copy_to_user((u64 __user *) uarg, &val, sizeof(val));
> +
> +	return rc;
> +}
> +
> +/**
> + * scm_req_controller_health_perf() - Request controller health & performance data
> + * @scm_data: the SCM metadata
> + * Return: 0 on success, negative on failure
> + */
> +int scm_req_controller_health_perf(struct scm_data *scm_data)
> +{
> +	return ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_HCI,
> +				      OCXL_LITTLE_ENDIAN,
> +				      GLOBAL_MMIO_HCI_REQ_HEALTH_PERF);
> +}
> +
> +#ifdef CONFIG_OCXL_SCM_DEBUG
> +/**
> + * scm_enable_fwdebug() - Enable FW debug on the controller
> + * @scm_data: a pointer to the SCM device data
> + * Return: 0 on success, negative on failure
> + */
> +static int scm_enable_fwdebug(const struct scm_data *scm_data)
> +{
> +	return ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_HCI,
> +				      OCXL_LITTLE_ENDIAN,
> +				      GLOBAL_MMIO_HCI_FW_DEBUG);
> +}
> +
> +/**
> + * scm_disable_fwdebug() - Disable FW debug on the controller
> + * @scm_data: a pointer to the SCM device data
> + * Return: 0 on success, negative on failure
> + */
> +static int scm_disable_fwdebug(const struct scm_data *scm_data)
> +{
> +	return ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_HCIC,
> +				      OCXL_LITTLE_ENDIAN,
> +				      GLOBAL_MMIO_HCI_FW_DEBUG);
> +}
> +
> +static int scm_ioctl_fwdebug(struct scm_data *scm_data,
> +			     struct scm_ioctl_fwdebug __user *uarg)
> +{
> +	struct scm_ioctl_fwdebug args;
> +	u64 val;
> +	int i;
> +	int rc;
> +
> +	if (copy_from_user(&args, uarg, sizeof(args)))
> +		return -EFAULT;
> +
> +	// Buffer size must be a multiple of 8
> +	if ((args.buf_size & 0x07))
> +		return -EINVAL;
> +
> +	if (args.buf_size > scm_data->admin_command.data_size)
> +		return -EINVAL;
> +
> +	mutex_lock(&scm_data->admin_command.lock);
> +
> +	rc = scm_enable_fwdebug(scm_data);
> +	if (rc)
> +		goto out;
> +
> +	rc = scm_admin_command_request(scm_data, ADMIN_COMMAND_FW_DEBUG);
> +	if (rc)
> +		goto out;
> +
> +	// Write DebugAction & FunctionCode
> +	val = ((u64)args.debug_action << 56) | ((u64)args.function_code << 40);
> +
> +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> +				      scm_data->admin_command.request_offset + 0x08,
> +				      OCXL_LITTLE_ENDIAN, val);
> +	if (rc)
> +		goto out;
> +
> +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> +				      scm_data->admin_command.request_offset + 0x10,
> +				      OCXL_LITTLE_ENDIAN, args.debug_parameter_1);
> +	if (rc)
> +		goto out;
> +
> +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> +				      scm_data->admin_command.request_offset + 0x18,
> +				      OCXL_LITTLE_ENDIAN, args.debug_parameter_2);
> +	if (rc)
> +		goto out;
> +
> +	for (i = 0x20; i < 0x38; i += 0x08)
> +		rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> +					      scm_data->admin_command.request_offset + i,
> +					      OCXL_LITTLE_ENDIAN, 0);
> +	if (rc)
> +		goto out;
> +
> +
> +	// Populate admin command buffer
> +	if (args.buf_size) {
> +		for (i = 0; i < args.buf_size; i += sizeof(u64)) {
> +			u64 val;
> +
> +			if (copy_from_user(&val, &args.buf[i], sizeof(u64)))
> +				return -EFAULT;
> +
> +			rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> +						      scm_data->admin_command.data_offset + i,
> +						      OCXL_HOST_ENDIAN, val);
> +			if (rc)
> +				goto out;
> +		}
> +	}
> +
> +	rc = scm_admin_command_execute(scm_data);
> +	if (rc)
> +		goto out;
> +
> +	rc = scm_admin_command_complete_timeout(scm_data,
> +						scm_data->timeouts[ADMIN_COMMAND_FW_DEBUG]);
> +	if (rc < 0)
> +		goto out;
> +
> +	rc = scm_admin_response(scm_data);
> +	if (rc < 0)
> +		goto out;
> +	if (rc != STATUS_SUCCESS) {
> +		scm_warn_status(scm_data, "Unexpected status from FW Debug", rc);
> +		goto out;
> +	}
> +
> +	if (args.buf_size) {
> +		for (i = 0; i < args.buf_size; i += sizeof(u64)) {
> +			u64 val;
> +
> +			rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +						     scm_data->admin_command.data_offset + i,
> +						     OCXL_HOST_ENDIAN, &val);
> +			if (rc)
> +				goto out;
> +
> +			if (copy_to_user(&args.buf[i], &val, sizeof(u64))) {
> +				rc = -EFAULT;
> +				goto out;
> +			}
> +		}
> +	}
> +
> +	rc = scm_admin_response_handled(scm_data);
> +	if (rc)
> +		goto out;
> +
> +	rc = scm_disable_fwdebug(scm_data);
> +	if (rc)
> +		goto out;
> +
> +out:
> +	mutex_unlock(&scm_data->admin_command.lock);
> +	return rc;
> +}
> +
> +static int scm_ioctl_shutdown(struct scm_data *scm_data)
> +{
> +	int rc;
> +
> +	mutex_lock(&scm_data->admin_command.lock);
> +
> +	rc = scm_admin_command_request(scm_data, ADMIN_COMMAND_SHUTDOWN);
> +	if (rc)
> +		goto out;
> +
> +	rc = scm_admin_command_execute(scm_data);
> +	if (rc)
> +		goto out;
> +
> +	rc = scm_admin_command_complete_timeout(scm_data, ADMIN_COMMAND_SHUTDOWN);
> +	if (rc < 0) {
> +		dev_warn(&scm_data->dev, "Shutdown timed out\n");
> +		goto out;
> +	}
> +
> +	rc = 0;
> +	goto out;
> +
> +out:
> +	mutex_unlock(&scm_data->admin_command.lock);
> +	return rc;
> +}
> +
> +static int scm_ioctl_mmio_write(struct scm_data *scm_data,
> +				struct scm_ioctl_mmio __user *uarg)
> +{
> +	struct scm_ioctl_mmio args;
> +
> +	if (copy_from_user(&args, uarg, sizeof(args)))
> +		return -EFAULT;
> +
> +	return ocxl_global_mmio_write64(scm_data->ocxl_afu, args.address,
> +					OCXL_LITTLE_ENDIAN, args.val);
> +}
> +
> +static int scm_ioctl_mmio_read(struct scm_data *scm_data,
> +			       struct scm_ioctl_mmio __user *uarg)
> +{
> +	struct scm_ioctl_mmio args;
> +	int rc;
> +
> +	if (copy_from_user(&args, uarg, sizeof(args)))
> +		return -EFAULT;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, args.address,
> +				     OCXL_LITTLE_ENDIAN, &args.val);
> +	if (rc)
> +		return rc;
> +
> +	if (copy_to_user(uarg, &args, sizeof(args)))
> +		return -EFAULT;
> +
> +	return 0;
> +}
> +#else
> +static int scm_ioctl_fwdebug(struct scm_data *scm_data,
> +			     struct scm_ioctl_fwdebug __user *uarg)
> +{
> +	return -EPERM;
> +}
> +
> +static int scm_ioctl_shutdown(struct scm_data *scm_data)
> +{
> +	return -EPERM;
> +}
> +
> +static int scm_ioctl_mmio_write(struct scm_data *scm_data,
> +				struct scm_ioctl_mmio __user *uarg)
> +{
> +	return -EPERM;
> +}
> +
> +static int scm_ioctl_mmio_read(struct scm_data *scm_data,
> +			       struct scm_ioctl_mmio __user *uarg)
> +{
> +	return -EPERM;
> +}
> +#endif
> +
> +static long scm_file_ioctl(struct file *file, unsigned int cmd,
> +			   unsigned long args)
> +{
> +	struct scm_data *scm_data = file->private_data;
> +	int rc = -EINVAL;
> +
> +	switch (cmd) {
> +	case SCM_IOCTL_BUFFER_INFO:
> +		rc = scm_ioctl_buffer_info(scm_data,
> +					   (struct scm_ioctl_buffer_info __user *)args);
> +		break;
> +
> +	case SCM_IOCTL_ERROR_LOG:
> +		rc = scm_ioctl_error_log(scm_data,
> +					 (struct scm_ioctl_error_log __user *)args);
> +		break;
> +
> +	case SCM_IOCTL_CONTROLLER_DUMP:
> +		rc = scm_request_controller_dump(scm_data);
> +		break;
> +
> +	case SCM_IOCTL_CONTROLLER_DUMP_DATA:
> +		rc = scm_ioctl_controller_dump_data(scm_data,
> +						    (struct scm_ioctl_controller_dump_data __user *)args);
> +		break;
> +
> +	case SCM_IOCTL_CONTROLLER_DUMP_COMPLETE:
> +		rc = scm_ioctl_controller_dump_complete(scm_data);
> +		break;
> +
> +	case SCM_IOCTL_CONTROLLER_STATS:
> +		rc = scm_ioctl_controller_stats(scm_data,
> +						(struct scm_ioctl_controller_stats __user *)args);
> +		break;
> +
> +	case SCM_IOCTL_EVENTFD:
> +		rc = scm_ioctl_eventfd(scm_data,
> +				       (struct scm_ioctl_eventfd __user *)args);
> +		break;
> +
> +	case SCM_IOCTL_EVENT_CHECK:
> +		rc = scm_ioctl_event_check(scm_data, (u64 __user *)args);
> +		break;
> +
> +	case SCM_IOCTL_REQUEST_HEALTH:
> +		rc = scm_req_controller_health_perf(scm_data);
> +		break;
> +
> +	case SCM_IOCTL_FWDEBUG:
> +		rc = scm_ioctl_fwdebug(scm_data,
> +				       (struct scm_ioctl_fwdebug __user *)args);
> +		break;
> +
> +	case SCM_IOCTL_SHUTDOWN:
> +		rc = scm_ioctl_shutdown(scm_data);
> +		break;
> +
> +	case SCM_IOCTL_MMIO_WRITE:
> +		rc = scm_ioctl_mmio_write(scm_data,
> +					  (struct scm_ioctl_mmio __user *)args);
> +		break;
> +
> +	case SCM_IOCTL_MMIO_READ:
> +		rc = scm_ioctl_mmio_read(scm_data,
> +					 (struct scm_ioctl_mmio __user *)args);
> +		break;
> +
> +	}
> +
> +	return rc;
> +}
> +
> +static const struct file_operations scm_fops = {
> +	.owner		= THIS_MODULE,
> +	.open	   = scm_file_open,
> +	.release	= scm_file_release,
> +	.unlocked_ioctl = scm_file_ioctl,
> +	.compat_ioctl   = scm_file_ioctl,
> +};
> +
> +/**
> + * scm_create_cdev() - Create the chardev in /dev for this scm device
> + * @scm_data: the SCM metadata
> + * Return: 0 on success, negative on failure
> + */
> +static int scm_create_cdev(struct scm_data *scm_data)
> +{
> +	int rc;
> +
> +	cdev_init(&scm_data->cdev, &scm_fops);
> +	rc = cdev_add(&scm_data->cdev, scm_data->dev.devt, 1);
> +	if (rc) {
> +		dev_err(&scm_data->dev, "Unable to add afu char device: %d\n", rc);
> +		return rc;
> +	}
> +	return 0;
> +}
> +
> +/**
> + * scm_remove() - Free an OpenCAPI Storage Class Memory device
> + * @pdev: the PCI device information struct
> + */
> +static void scm_remove(struct pci_dev *pdev)
> +{
> +	if (PCI_FUNC(pdev->devfn) == 0) {
> +		struct scm_function_0 *scm_func_0 = pci_get_drvdata(pdev);
> +
> +		if (scm_func_0) {
> +			ocxl_function_close(scm_func_0->ocxl_fn);
> +			scm_func_0->ocxl_fn = NULL;
> +		}
> +	} else {
> +		struct scm_data *scm_data = pci_get_drvdata(pdev);
> +
> +		if (scm_data) {
> +			if (scm_data->nvdimm_bus)
> +				nvdimm_bus_unregister(scm_data->nvdimm_bus);
> +
> +			device_unregister(&scm_data->dev);
> +		}
> +	}
> +}
> +
> +/**
> + * scm_setup_device_metadata() - Retrieve config information from the AFU and save it for future use
> + * @scm_data: the SCM metadata
> + * Return: 0 on success, negative on failure
> + */
> +static int scm_setup_device_metadata(struct scm_data *scm_data)
> +{
> +	u64 val;
> +	int rc;
> +	int i;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, GLOBAL_MMIO_CCAP0,
> +				     OCXL_LITTLE_ENDIAN, &val);
> +	if (rc)
> +		return rc;
> +
> +	scm_data->scm_revision = val & 0xFFFF;
> +	scm_data->read_latency = (val >> 32) & 0xFF;
> +	scm_data->readiness_timeout = val >> 48;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, GLOBAL_MMIO_CCAP1,
> +				     OCXL_LITTLE_ENDIAN, &val);
> +	if (rc)
> +		return rc;
> +
> +	scm_data->max_controller_dump_size = val & 0xFFFFFFFF;
> +
> +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, GLOBAL_MMIO_FWVER,
> +				     OCXL_LITTLE_ENDIAN, &val);
> +	if (rc)
> +		return rc;
> +
> +	for (i = 0; i < 8; i++)
> +		scm_data->fw_version[i] = (val >> (i * 8)) & 0xff;
> +
> +	scm_data->fw_version[8] = '\0';
> +
> +	dev_info(&scm_data->dev,
> +		 "Firmware version '%s' SCM revision %d:%d\n", scm_data->fw_version,
> +		 scm_data->scm_revision >> 4, scm_data->scm_revision & 0x0F);
> +
> +	return 0;
> +}
> +
> +static const char *scm_decode_error_log_type(u8 error_log_type)
> +{
> +	switch (error_log_type) {
> +	case 0x00:
> +		return "general";
> +	case 0x01:
> +		return "predictive failure";
> +	case 0x02:
> +		return "thermal warning";
> +	case 0x03:
> +		return "data loss";
> +	case 0x04:
> +		return "health & performance";
> +	default:
> +		return "unknown";
> +	}
> +}
> +
> +static void scm_dump_error_log(struct scm_data *scm_data)
> +{
> +	struct scm_ioctl_error_log log;
> +	u32 buf_size;
> +	u8 *buf;
> +	int rc;
> +
> +	if (scm_data->admin_command.data_size == 0)
> +		return;
> +
> +	buf_size = scm_data->admin_command.data_size - 0x48;
> +	buf = kzalloc(buf_size, GFP_KERNEL);
> +	if (!buf)
> +		return;
> +
> +	log.buf = buf;
> +	log.buf_size = buf_size;
> +
> +	rc = scm_read_error_log(scm_data, &log, false);
> +	if (rc < 0)
> +		goto out;
> +
> +	dev_warn(&scm_data->dev,
> +		 "SCM Error log: WWID=0x%016llx%016llx LID=0x%x PRC=%x type=0x%x %s, Uptime=%u seconds timestamp=0x%llx\n",
> +		 log.wwid[0], log.wwid[1],
> +		 log.log_identifier, log.program_reference_code,
> +		 log.error_log_type,
> +		 scm_decode_error_log_type(log.error_log_type),
> +		 log.power_on_seconds, log.timestamp);
> +	print_hex_dump(KERN_WARNING, "buf", DUMP_PREFIX_OFFSET, 16, 1, buf,
> +		       log.buf_size, false);
> +
> +out:
> +	kfree(buf);
> +}
> +
> +static void scm_handle_nscra_doorbell(struct scm_data *scm_data)
> +{
> +	int rc;
> +
> +	if (scm_data->ns_command.op_code == NS_COMMAND_SECURE_ERASE) {
> +		u64 success, attempted;
> +
> +
> +		rc = scm_ns_response(scm_data);
> +		if (rc < 0) {
> +			scm_data->overwrite_state = SCM_OVERWRITE_FAILED;
> +			mutex_unlock(&scm_data->ns_command.lock);
> +			return;
> +		}
> +		if (rc != STATUS_SUCCESS)
> +			scm_warn_status(scm_data, "Unexpected status from overwrite", rc);
> +
> +		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +					     scm_data->ns_command.response_offset +
> +					     NS_RESPONSE_SECURE_ERASE_ACCESSIBLE_SUCCESS,
> +					     OCXL_HOST_ENDIAN, &success);
> +		if (rc) {
> +			scm_data->overwrite_state = SCM_OVERWRITE_FAILED;
> +			mutex_unlock(&scm_data->ns_command.lock);
> +			return;
> +		}
> +
> +		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +					     scm_data->ns_command.response_offset +
> +					     NS_RESPONSE_SECURE_ERASE_ACCESSIBLE_ATTEMPTED,
> +					     OCXL_HOST_ENDIAN, &attempted);
> +		if (rc) {
> +			scm_data->overwrite_state = SCM_OVERWRITE_FAILED;
> +			mutex_unlock(&scm_data->ns_command.lock);
> +			return;
> +		}
> +
> +		scm_data->overwrite_state = SCM_OVERWRITE_SUCCESS;
> +		if (success != attempted)
> +			scm_data->overwrite_state = SCM_OVERWRITE_FAILED;
> +
> +		dev_info(&scm_data->dev,
> +			 "Overwritten %llu/%llu accessible pages", success, attempted);
> +
> +		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +					     scm_data->ns_command.response_offset +
> +					     NS_RESPONSE_SECURE_ERASE_DEFECTIVE_SUCCESS,
> +					     OCXL_HOST_ENDIAN, &success);
> +		if (rc) {
> +			scm_data->overwrite_state = SCM_OVERWRITE_FAILED;
> +			mutex_unlock(&scm_data->ns_command.lock);
> +			return;
> +		}
> +
> +		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +					     scm_data->ns_command.response_offset +
> +					     NS_RESPONSE_SECURE_ERASE_DEFECTIVE_ATTEMPTED,
> +					     OCXL_HOST_ENDIAN, &attempted);
> +		if (rc) {
> +			scm_data->overwrite_state = SCM_OVERWRITE_FAILED;
> +			mutex_unlock(&scm_data->ns_command.lock);
> +			return;
> +		}
> +
> +		if (success != attempted)
> +			scm_data->overwrite_state = SCM_OVERWRITE_FAILED;
> +
> +		dev_info(&scm_data->dev,
> +			 "Overwritten %llu/%llu defective pages", success, attempted);
> +
> +		scm_ns_response_handled(scm_data);
> +
> +		mutex_unlock(&scm_data->ns_command.lock);
> +		return;
> +	}
> +}
> +
> +static irqreturn_t scm_imn0_handler(void *private)
> +{
> +	struct scm_data *scm_data = private;
> +	int rc;
> +	u64 chi = 0;
> +
> +	rc = scm_chi(scm_data, &chi);
> +	if (rc < 0)
> +		return IRQ_NONE;
> +
> +	if (chi & GLOBAL_MMIO_CHI_NSCRA)
> +		scm_handle_nscra_doorbell(scm_data);
> +
> +	if (chi & GLOBAL_MMIO_CHI_ELA) {
> +		dev_warn(&scm_data->dev, "Error log is available\n");
> +
> +		if (scm_data->ev_ctx)
> +			eventfd_signal(scm_data->ev_ctx, 1);
> +	}
> +
> +	if (chi & GLOBAL_MMIO_CHI_CDA) {
> +		dev_warn(&scm_data->dev, "Controller dump is available\n");
> +
> +		if (scm_data->ev_ctx)
> +			eventfd_signal(scm_data->ev_ctx, 1);
> +	}
> +
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t scm_imn1_handler(void *private)
> +{
> +	struct scm_data *scm_data = private;
> +	u64 chi = 0;
> +
> +	(void)scm_chi(scm_data, &chi);
> +
> +	if (chi & (GLOBAL_MMIO_CHI_CFFS | GLOBAL_MMIO_CHI_CHFS)) {
> +		dev_err(&scm_data->dev,
> +			"Controller status is fatal, chi=0x%llx, going offline\n", chi);
> +
> +		if (scm_data->nvdimm_bus) {
> +			nvdimm_bus_unregister(scm_data->nvdimm_bus);
> +			scm_data->nvdimm_bus = NULL;
> +		}
> +
> +		if (scm_data->ev_ctx)
> +			eventfd_signal(scm_data->ev_ctx, 1);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +
> +/**
> + * scm_setup_irq() - Set up the IRQs for the SCM device
> + * @scm_data: the SCM metadata
> + * Return: 0 on success, negative on failure
> + */
> +static int scm_setup_irq(struct scm_data *scm_data)
> +{
> +	int rc;
> +	u64 irq_addr;
> +
> +	rc = ocxl_afu_irq_alloc(scm_data->ocxl_context, &scm_data->irq_id[0]);
> +	if (rc)
> +		return rc;
> +
> +	rc = ocxl_irq_set_handler(scm_data->ocxl_context, scm_data->irq_id[0],
> +				  scm_imn0_handler, NULL, scm_data);
> +
> +	irq_addr = ocxl_afu_irq_get_addr(scm_data->ocxl_context, scm_data->irq_id[0]);
> +	if (!irq_addr)
> +		return -EFAULT;
> +
> +	scm_data->irq_addr[0] = ioremap(irq_addr, PAGE_SIZE);
> +	if (!scm_data->irq_addr[0])
> +		return -EINVAL;
> +
> +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu, GLOBAL_MMIO_IMA0_OHP,
> +				      OCXL_LITTLE_ENDIAN,
> +				      (u64)scm_data->irq_addr[0]);
> +	if (rc)
> +		goto out_irq0;
> +
> +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu, GLOBAL_MMIO_IMA0_CFP,
> +				      OCXL_LITTLE_ENDIAN, 0);
> +	if (rc)
> +		goto out_irq0;
> +
> +	rc = ocxl_afu_irq_alloc(scm_data->ocxl_context, &scm_data->irq_id[1]);
> +	if (rc)
> +		goto out_irq0;
> +
> +
> +	rc = ocxl_irq_set_handler(scm_data->ocxl_context, scm_data->irq_id[1],
> +				  scm_imn1_handler, NULL, scm_data);
> +	if (rc)
> +		goto out_irq0;
> +
> +	irq_addr = ocxl_afu_irq_get_addr(scm_data->ocxl_context, scm_data->irq_id[1]);
> +	if (!irq_addr)
> +		goto out_irq0;
> +
> +	scm_data->irq_addr[1] = ioremap(irq_addr, PAGE_SIZE);
> +	if (!scm_data->irq_addr[1])
> +		goto out_irq0;
> +
> +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu, GLOBAL_MMIO_IMA1_OHP,
> +				      OCXL_LITTLE_ENDIAN,
> +				      (u64)scm_data->irq_addr[1]);
> +	if (rc)
> +		goto out_irq1;
> +
> +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu, GLOBAL_MMIO_IMA1_CFP,
> +				      OCXL_LITTLE_ENDIAN, 0);
> +	if (rc)
> +		goto out_irq1;
> +
> +	// Enable doorbells
> +	rc = ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_CHIE,
> +				    OCXL_LITTLE_ENDIAN,
> +				    GLOBAL_MMIO_CHI_ELA | GLOBAL_MMIO_CHI_CDA |
> +				    GLOBAL_MMIO_CHI_CFFS | GLOBAL_MMIO_CHI_CHFS |
> +				    GLOBAL_MMIO_CHI_NSCRA);
> +	if (rc)
> +		goto out_irq1;
> +
> +	return 0;
> +
> +out_irq1:
> +	iounmap(scm_data->irq_addr[1]);
> +	scm_data->irq_addr[1] = NULL;
> +
> +out_irq0:
> +	iounmap(scm_data->irq_addr[0]);
> +	scm_data->irq_addr[0] = NULL;
> +
> +	return rc;
> +}
> +
> +/**
> + * scm_probe_function_0 - Set up function 0 for an OpenCAPI Storage Class Memory device
> + * This is important as it enables higher than 0 across all other functions,
> + * which in turn enables higher bandwidth accesses
> + * @pdev: the PCI device information struct
> + * Return: 0 on success, negative on failure
> + */
> +static int scm_probe_function_0(struct pci_dev *pdev)
> +{
> +	struct scm_function_0 *scm_func_0 = NULL;
> +
> +	scm_func_0 = kzalloc(sizeof(*scm_func_0), GFP_KERNEL);
> +	if (!scm_func_0)
> +		return -ENOMEM;
> +
> +	scm_func_0->pdev = pdev;
> +	scm_func_0->ocxl_fn = ocxl_function_open(pdev);
> +	if (IS_ERR(scm_func_0->ocxl_fn)) {
> +		kfree(scm_func_0);
> +		dev_err(&pdev->dev, "failed to open OCXL function\n");
> +		return -EFAULT;
> +	}
> +
> +	pci_set_drvdata(pdev, scm_func_0);
> +
> +	return 0;
> +}
> +
> +/**
> + * scm_probe - Init an OpenCAPI Storage Class Memory device
> + * @pdev: the PCI device information struct
> + * @ent: The entry from scm_pci_tbl
> + * Return: 0 on success, negative on failure
> + */
> +static int scm_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
> +{
> +	struct scm_data *scm_data = NULL;
> +	int elapsed;
> +	u64 chi;
> +
> +	if (PCI_FUNC(pdev->devfn) == 0)
> +		return scm_probe_function_0(pdev);
> +	else if (PCI_FUNC(pdev->devfn) != 1)
> +		return 0;
> +
> +	scm_data = kzalloc(sizeof(*scm_data), GFP_KERNEL);
> +	if (!scm_data)
> +		goto err;
> +	scm_data->pdev = pdev;
> +	mutex_init(&scm_data->admin_command.lock);
> +	mutex_init(&scm_data->ns_command.lock);
> +
> +
> +	scm_data->timeouts[ADMIN_COMMAND_ERRLOG] = 2000; // ms
> +	scm_data->timeouts[ADMIN_COMMAND_HEARTBEAT] = 100; // ms
> +	scm_data->timeouts[ADMIN_COMMAND_SMART] = 100; // ms
> +	scm_data->timeouts[ADMIN_COMMAND_CONTROLLER_DUMP] = 1000; // ms
> +	scm_data->timeouts[ADMIN_COMMAND_CONTROLLER_STATS] = 100; // ms
> +	scm_data->timeouts[ADMIN_COMMAND_SHUTDOWN] = 1000; // ms
> +	scm_data->timeouts[ADMIN_COMMAND_FW_UPDATE] = 16000; // ms
> +
> +	pci_set_drvdata(pdev, scm_data);
> +
> +	scm_data->ocxl_fn = ocxl_function_open(pdev);
> +	if (IS_ERR(scm_data->ocxl_fn)) {
> +		kfree(scm_data);
> +		scm_data = NULL;
> +		pci_set_drvdata(pdev, NULL);
> +		dev_err(&pdev->dev, "failed to open OCXL function\n");
> +		goto err;
> +	}
> +
> +	scm_data->ocxl_afu = ocxl_function_fetch_afu(scm_data->ocxl_fn, 0);
> +	if (scm_data->ocxl_afu == NULL)
> +		goto err;
> +
> +	ocxl_afu_get(scm_data->ocxl_afu);
> +
> +	if (scm_register(scm_data) < 0)
> +		goto err;
> +
> +	if (ocxl_context_alloc(&scm_data->ocxl_context, scm_data->ocxl_afu, NULL))
> +		goto err;
> +
> +	if (ocxl_context_attach(scm_data->ocxl_context, 0, NULL))
> +		goto err;
> +
> +	if (scm_setup_device_metadata(scm_data))
> +		goto err;
> +
> +	if (scm_setup_irq(scm_data))
> +		goto err;
> +
> +	if (scm_setup_command_metadata(scm_data))
> +		goto err;
> +
> +	if (scm_create_cdev(scm_data))
> +		goto err;
> +
> +	if (scm_sysfs_add(scm_data))
> +		goto err;
> +
> +	if (scm_heartbeat(scm_data))
> +		goto err;
> +
> +	elapsed = 0;
> +	while (!scm_is_usable(scm_data)) {
> +		if (elapsed++ > SCM_USABLE_TIMEOUT) {
> +			dev_warn(&scm_data->dev, "SCM ready timeout.\n");
> +			goto err;
> +		}
> +
> +		dev_warn(&scm_data->dev,
> +			 "Waiting for SCM to become usable (%d/%d seconds)\n",
> +			 elapsed, SCM_USABLE_TIMEOUT);
> +		msleep(1000);
> +	}
> +
> +	if (scm_register_lpc_mem(scm_data))
> +		goto err;
> +
> +	return 0;
> +
> +err:
> +	if (scm_data &&
> +		    (scm_chi(scm_data, &chi) == 0) &&
> +		    (chi & GLOBAL_MMIO_CHI_ELA))
> +		scm_dump_error_log(scm_data);
> +
> +	dev_err(&pdev->dev,
> +		"Error detected, will not register storage class memory\n");
> +	return -ENXIO;
> +}
> +
> +struct pci_driver scm_pci_driver = {
> +	.name = "ocxl-scm",
> +	.id_table = scm_pci_tbl,
> +	.probe = scm_probe,
> +	.remove = scm_remove,
> +	.shutdown = scm_remove,
> +};
> +
> +static int scm_file_init(void)
> +{
> +	int rc;
> +
> +	mutex_init(&minors_idr_lock);
> +	idr_init(&minors_idr);
> +
> +	rc = alloc_chrdev_region(&scm_dev, 0, SCM_NUM_MINORS, "scm");
> +	if (rc) {
> +		pr_err("Unable to allocate scm major number: %d\n", rc);
> +		return rc;
> +	}
> +
> +	scm_class = class_create(THIS_MODULE, "scm");
> +	if (IS_ERR(scm_class)) {
> +		pr_err("Unable to create scm class\n");
> +		unregister_chrdev_region(scm_dev, SCM_NUM_MINORS);
> +		return PTR_ERR(scm_class);
> +	}
> +
> +	return 0;
> +}
> +
> +static void scm_file_exit(void)
> +{
> +	class_destroy(scm_class);
> +	unregister_chrdev_region(scm_dev, SCM_NUM_MINORS);
> +	idr_destroy(&minors_idr);
> +}
> +
> +static int __init scm_init(void)
> +{
> +	int rc = 0;
> +
> +	rc = scm_file_init();
> +	if (rc)
> +		return rc;
> +
> +	rc = pci_register_driver(&scm_pci_driver);
> +	if (rc) {
> +		scm_file_exit();
> +		return rc;
> +	}
> +
> +	return 0;
> +}
> +
> +static void scm_exit(void)
> +{
> +	pci_unregister_driver(&scm_pci_driver);
> +	scm_file_exit();
> +}
> +
> +module_init(scm_init);
> +module_exit(scm_exit);
> +
> +MODULE_DESCRIPTION("Storage Class Memory");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/nvdimm/ocxl-scm_internal.c b/drivers/nvdimm/ocxl-scm_internal.c
> new file mode 100644
> index 000000000000..e7c247835817
> --- /dev/null
> +++ b/drivers/nvdimm/ocxl-scm_internal.c
> @@ -0,0 +1,232 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +// Copyright 2019 IBM Corp.
> +
> +#include <misc/ocxl.h>
> +#include <linux/delay.h>
> +#include "ocxl-scm_internal.h"
> +
> +int scm_chi(const struct scm_data *scm_data, u64 *chi)
> +{
> +	u64 val;
> +	int rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, GLOBAL_MMIO_CHI,
> +					 OCXL_LITTLE_ENDIAN, &val);
> +	if (rc)
> +		return rc;
> +
> +	*chi = val;
> +
> +	return 0;
> +}
> +
> +bool scm_controller_is_ready(const struct scm_data *scm_data)
> +{
> +	u64 val = 0;
> +	int rc = scm_chi(scm_data, &val);
> +
> +	WARN_ON(rc < 0);
> +
> +	return (val & GLOBAL_MMIO_CHI_CRDY) != 0;
> +}
> +
> +static int scm_command_request(const struct scm_data *scm_data,
> +			       struct command_metadata *cmd, u8 op_code)
> +{
> +	u64 val = op_code;
> +	int rc;
> +	u8 i;
> +
> +	if (!scm_controller_is_ready(scm_data))
> +		return -EIO;
> +
> +	cmd->op_code = op_code;
> +	cmd->id++;
> +
> +	val |= ((u64)cmd->id) << 16;
> +
> +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu, cmd->request_offset,
> +				      OCXL_LITTLE_ENDIAN, val);
> +	if (rc)
> +		return rc;
> +
> +	for (i = 0x08; i <= 0x38; i += 0x08) {
> +		rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> +					      cmd->request_offset + i,
> +					      OCXL_LITTLE_ENDIAN, 0);
> +		if (rc)
> +			return rc;
> +	}
> +
> +	return 0;
> +}
> +
> +int scm_admin_command_request(struct scm_data *scm_data, u8 op_code)
> +{
> +	return scm_command_request(scm_data, &scm_data->admin_command, op_code);
> +}
> +
> +int scm_command_response(const struct scm_data *scm_data,
> +			 const struct command_metadata *cmd)
> +{
> +	u64 val;
> +	u16 id;
> +	u8 status;
> +	int rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> +					 cmd->response_offset,
> +					 OCXL_LITTLE_ENDIAN, &val);
> +	if (rc)
> +		return rc;
> +
> +	status = val & 0xff;
> +	id = (val >> 16) & 0xffff;
> +
> +	if (id != cmd->id) {
> +		dev_warn(&scm_data->dev,
> +			 "Expected response for command %d, but received response for command %d instead.\n",
> +			 cmd->id, id);
> +	}
> +
> +	return status;
> +}
> +
> +int scm_admin_response(const struct scm_data *scm_data)
> +{
> +	return scm_command_response(scm_data, &scm_data->admin_command);
> +}
> +
> +
> +int scm_admin_command_execute(const struct scm_data *scm_data)
> +{
> +	return ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_HCI,
> +				      OCXL_LITTLE_ENDIAN, GLOBAL_MMIO_HCI_ACRW);
> +}
> +
> +static bool scm_admin_command_complete(const struct scm_data *scm_data)
> +{
> +	u64 val = 0;
> +
> +	int rc = scm_chi(scm_data, &val);
> +
> +	WARN_ON(rc);
> +
> +	return (val & GLOBAL_MMIO_CHI_ACRA) != 0;
> +}
> +
> +int scm_admin_command_complete_timeout(const struct scm_data *scm_data,
> +				       int command)
> +{
> +	u32 timeout = scm_data->timeouts[command];
> +	timeout++;
> +	timeout /= 32;
> +	if (!timeout)
> +		timeout = SCM_DEFAULT_TIMEOUT / 32;
> +
> +	while (timeout-- > 0) {
> +		if (scm_admin_command_complete(scm_data))
> +			return 0;
> +		msleep(32);
> +	}
> +
> +	return -EBUSY;
> +}
> +
> +int scm_admin_response_handled(const struct scm_data *scm_data)
> +{
> +	return ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_CHIC,
> +				      OCXL_LITTLE_ENDIAN, GLOBAL_MMIO_CHI_ACRA);
> +}
> +
> +int scm_ns_command_request(struct scm_data *scm_data, u8 op_code)
> +{
> +	return scm_command_request(scm_data, &scm_data->ns_command, op_code);
> +}
> +
> +int scm_ns_response(const struct scm_data *scm_data)
> +{
> +	return scm_command_response(scm_data, &scm_data->ns_command);
> +}
> +
> +int scm_ns_command_execute(const struct scm_data *scm_data)
> +{
> +	return ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_HCI,
> +				      OCXL_LITTLE_ENDIAN, GLOBAL_MMIO_HCI_NSCRW);
> +}
> +
> +bool scm_ns_command_complete(const struct scm_data *scm_data)
> +{
> +	u64 val = 0;
> +	int rc = scm_chi(scm_data, &val);
> +
> +	WARN_ON(rc);
> +
> +	return (val & GLOBAL_MMIO_CHI_NSCRA) != 0;
> +}
> +
> +int scm_ns_response_handled(const struct scm_data *scm_data)
> +{
> +	return ocxl_global_mmio_set64(scm_data->ocxl_afu, GLOBAL_MMIO_CHIC,
> +				      OCXL_LITTLE_ENDIAN, GLOBAL_MMIO_CHI_NSCRA);
> +}
> +
> +
> +void scm_warn_status(const struct scm_data *scm_data, const char *message,
> +		     u8 status)
> +{
> +	const char *text = "Unknown";
> +
> +	switch (status) {
> +	case STATUS_SUCCESS:
> +		text = "Success";
> +		break;
> +
> +	case STATUS_MEM_UNAVAILABLE:
> +		text = "Persistent memory unavailable";
> +		break;
> +
> +	case STATUS_BAD_OPCODE:
> +		text = "Bad opcode";
> +		break;
> +
> +	case STATUS_BAD_REQUEST_PARM:
> +		text = "Bad request parameter";
> +		break;
> +
> +	case STATUS_BAD_DATA_PARM:
> +		text = "Bad data parameter";
> +		break;
> +
> +	case STATUS_DEBUG_BLOCKED:
> +		text = "Debug action blocked";
> +		break;
> +
> +	case STATUS_FAIL:
> +		text = "Failed";
> +		break;
> +	}
> +
> +	dev_warn(&scm_data->dev, "%s: %s (%x)\n", message, text, status);
> +}
> +
> +void scm_warn_status_fw_update(const struct scm_data *scm_data,
> +			       const char *message, u8 status)
> +{
> +	const char *text;
> +
> +	switch (status) {
> +	case STATUS_FW_UPDATE_BLOCKED:
> +		text = "Firmware update is blocked, please try again later";
> +		break;
> +
> +	case STATUS_FW_ARG_INVALID:
> +		text = "Internal error in SCM firmware update mechanism";
> +		break;
> +
> +	case STATUS_FW_INVALID:
> +		text = "Firmware content is invalid, please verify firmware update file";
> +		break;
> +
> +	default:
> +		return scm_warn_status(scm_data, message, status);
> +	}
> +
> +	dev_warn(&scm_data->dev, "%s: %s (%x)\n", message, text, status);
> +}
> diff --git a/drivers/nvdimm/ocxl-scm_internal.h b/drivers/nvdimm/ocxl-scm_internal.h
> new file mode 100644
> index 000000000000..c236d8092c6d
> --- /dev/null
> +++ b/drivers/nvdimm/ocxl-scm_internal.h
> @@ -0,0 +1,331 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +// Copyright 2019 IBM Corp.
> +
> +#include <linux/pci.h>
> +#include <linux/cdev.h>
> +#include <misc/ocxl.h>
> +#include <linux/libnvdimm.h>
> +#include <uapi/linux/ocxl-scm.h>
> +#include <linux/mm.h>
> +
> +#define SCM_DEFAULT_TIMEOUT 100
> +
> +#define GLOBAL_MMIO_CHI		0x000
> +#define GLOBAL_MMIO_CHIC	0x008
> +#define GLOBAL_MMIO_CHIE	0x010
> +#define GLOBAL_MMIO_CHIEC	0x018
> +#define GLOBAL_MMIO_HCI		0x020
> +#define GLOBAL_MMIO_HCIC	0x028
> +#define GLOBAL_MMIO_IMA0_OHP	0x040
> +#define GLOBAL_MMIO_IMA0_CFP	0x048
> +#define GLOBAL_MMIO_IMA1_OHP	0x050
> +#define GLOBAL_MMIO_IMA1_CFP	0x058
> +#define GLOBAL_MMIO_ACMA_CREQO	0x100
> +#define GLOBAL_MMIO_ACMA_CRSPO	0x104
> +#define GLOBAL_MMIO_ACMA_CDBO	0x108
> +#define GLOBAL_MMIO_ACMA_CDBS	0x10c
> +#define GLOBAL_MMIO_NSCMA_CREQO	0x120
> +#define GLOBAL_MMIO_NSCMA_CRSPO	0x124
> +#define GLOBAL_MMIO_NSCMA_CDBO	0x128
> +#define GLOBAL_MMIO_NSCMA_CDBS	0x12c
> +#define GLOBAL_MMIO_CSTS	0x140
> +#define GLOBAL_MMIO_FWVER	0x148
> +#define GLOBAL_MMIO_CCAP0	0x160
> +#define GLOBAL_MMIO_CCAP1	0x168
> +
> +#define GLOBAL_MMIO_CHI_ACRA	BIT_ULL(0)
> +#define GLOBAL_MMIO_CHI_NSCRA	BIT_ULL(1)
> +#define GLOBAL_MMIO_CHI_CRDY	BIT_ULL(4)
> +#define GLOBAL_MMIO_CHI_CFFS	BIT_ULL(5)
> +#define GLOBAL_MMIO_CHI_MA	BIT_ULL(6)
> +#define GLOBAL_MMIO_CHI_ELA	BIT_ULL(7)
> +#define GLOBAL_MMIO_CHI_CDA	BIT_ULL(8)
> +#define GLOBAL_MMIO_CHI_CHFS	BIT_ULL(9)
> +
> +#define GLOBAL_MMIO_CHI_ALL	(GLOBAL_MMIO_CHI_ACRA | \
> +				 GLOBAL_MMIO_CHI_NSCRA | \
> +				 GLOBAL_MMIO_CHI_CRDY | \
> +				 GLOBAL_MMIO_CHI_CFFS | \
> +				 GLOBAL_MMIO_CHI_MA | \
> +				 GLOBAL_MMIO_CHI_ELA | \
> +				 GLOBAL_MMIO_CHI_CDA | \
> +				 GLOBAL_MMIO_CHI_CHFS)
> +
> +#define GLOBAL_MMIO_HCI_ACRW				BIT_ULL(0)
> +#define GLOBAL_MMIO_HCI_NSCRW				BIT_ULL(1)
> +#define GLOBAL_MMIO_HCI_AFU_RESET			BIT_ULL(2)
> +#define GLOBAL_MMIO_HCI_FW_DEBUG			BIT_ULL(3)
> +#define GLOBAL_MMIO_HCI_CONTROLLER_DUMP			BIT_ULL(4)
> +#define GLOBAL_MMIO_HCI_CONTROLLER_DUMP_COLLECTED	BIT_ULL(5)
> +#define GLOBAL_MMIO_HCI_REQ_HEALTH_PERF			BIT_ULL(6)
> +
> +#define ADMIN_COMMAND_HEARTBEAT		0x00u
> +#define ADMIN_COMMAND_SHUTDOWN		0x01u
> +#define ADMIN_COMMAND_FW_UPDATE		0x02u
> +#define ADMIN_COMMAND_FW_DEBUG		0x03u
> +#define ADMIN_COMMAND_ERRLOG		0x04u
> +#define ADMIN_COMMAND_SMART		0x05u
> +#define ADMIN_COMMAND_CONTROLLER_STATS	0x06u
> +#define ADMIN_COMMAND_CONTROLLER_DUMP	0x07u
> +#define ADMIN_COMMAND_CMD_CAPS		0x08u
> +#define ADMIN_COMMAND_MAX		0x08u
> +
> +#define NS_COMMAND_SECURE_ERASE	0x20ull
> +
> +#define NS_RESPONSE_SECURE_ERASE_ACCESSIBLE_SUCCESS 0x20
> +#define NS_RESPONSE_SECURE_ERASE_ACCESSIBLE_ATTEMPTED 0x28
> +#define NS_RESPONSE_SECURE_ERASE_DEFECTIVE_SUCCESS 0x30
> +#define NS_RESPONSE_SECURE_ERASE_DEFECTIVE_ATTEMPTED 0x38
> +
> +
> +
> +#define STATUS_SUCCESS		0x00
> +#define STATUS_MEM_UNAVAILABLE	0x20
> +#define STATUS_BAD_OPCODE	0x50
> +#define STATUS_BAD_REQUEST_PARM	0x51
> +#define STATUS_BAD_DATA_PARM	0x52
> +#define STATUS_DEBUG_BLOCKED	0x70
> +#define STATUS_FAIL		0xFF
> +
> +#define STATUS_FW_UPDATE_BLOCKED 0x21
> +#define STATUS_FW_ARG_INVALID	0x51
> +#define STATUS_FW_INVALID	0x52
> +
> +#define SCM_LABEL_AREA_SIZE	(1UL << PA_SECTION_SHIFT)
> +
> +struct command_metadata {
> +	u32 request_offset;
> +	u32 response_offset;
> +	u32 data_offset;
> +	u32 data_size;
> +	struct mutex lock;
> +	u16 id;
> +	u8 op_code;
> +};
> +
> +struct scm_function_0 {
> +	struct pci_dev *pdev;
> +	struct ocxl_fn *ocxl_fn;
> +};
> +
> +enum overwrite_state {
> +	SCM_OVERWRITE_IDLE = 0,
> +	SCM_OVERWRITE_BUSY,
> +	SCM_OVERWRITE_SUCCESS,
> +	SCM_OVERWRITE_FAILED
> +};
> +
> +#define SCM_SMART_ATTR_POWER_ON_HOURS	0x09
> +#define SCM_SMART_ATTR_TEMPERATURE	0xC2
> +#define SCM_SMART_ATTR_LIFE_REMAINING	0xCA
> +
> +struct scm_smart_attrib {
> +	__u8 id; /* out, See defines above */
> +	__u16 attribute_flags;
> +	__u8 current_val;
> +	__u8 threshold_val;
> +	__u8 worst_val;
> +	__u8 reserved;
> +	__u64 raw_val;
> +};
> +
> +struct scm_smart_attribs {
> +	struct scm_smart_attrib power_on_hours;
> +	struct scm_smart_attrib temperature;
> +	struct scm_smart_attrib life_remaining;
> +};
> +
> +struct scm_data {
> +	struct device dev;
> +	struct pci_dev *pdev;
> +	struct cdev cdev;
> +	struct ocxl_fn *ocxl_fn;
> +#define SCM_IRQ_COUNT 2
> +	int irq_id[SCM_IRQ_COUNT];
> +	struct dev_pagemap irq_pgmap[SCM_IRQ_COUNT];
> +	void *irq_addr[SCM_IRQ_COUNT];
> +	struct nd_interleave_set nd_set;
> +	struct nvdimm_bus_descriptor bus_desc;
> +	struct nvdimm_bus *nvdimm_bus;
> +	struct ocxl_afu *ocxl_afu;
> +	struct ocxl_context *ocxl_context;
> +	void *metadata_addr;
> +	struct scm_global_mmio *global_mmio;
> +	struct command_metadata admin_command;
> +	struct command_metadata ns_command;
> +	enum overwrite_state overwrite_state;
> +	struct resource scm_res;
> +	struct nd_region *nd_region;
> +	struct eventfd_ctx *ev_ctx;
> +	struct scm_smart_attribs smart;
> +	char fw_version[8+1];
> +	u32 timeouts[ADMIN_COMMAND_MAX+1];
> +
> +	u16 scm_revision; // major/minor
> +	u16 readiness_timeout; /* The worst case time (in milliseconds) that the host shall
> +				* wait for the controller to become operational following a reset (CHI.CRDY).
> +				*/
> +	u16 read_latency; /* The nominal measure of latency (in nanoseconds)
> +			   * associated with an unassisted read of a memory block.
> +			   * This represents the capability of the raw media technology without assistance
> +			   */
> +	u32 max_controller_dump_size; // bytes
> +};
> +
> +/**
> + * Create sysfs entries for an SCM device
> + * scm_data: The SCM metadata
> + */
> +int scm_sysfs_add(struct scm_data *scm_data);
> +
> +/**
> + * Get the value of the CHI register:
> + * scm_data: The SCM metadata
> + * chi: returns the CHI value
> + *
> + * Returns 0 on success, negative on error
> + */
> +int scm_chi(const struct scm_data *scm_data, u64 *chi);
> +
> +/**
> + * scm_controller_is_ready - Is the controller ready?
> + * @scm_data: a pointer to the SCM device data
> + * Return true if the controller is ready
> + */
> +bool scm_controller_is_ready(const struct scm_data *scm_data);
> +
> +/**
> + * Issue an admin command request
> + *
> + * scm_data: a pointer to the SCM device data
> + * op_code: The op-code for the command
> + *
> + * Returns an identifier for the command, or negative on error
> + */
> +int scm_admin_command_request(struct scm_data *scm_data, u8 op_code);
> +
> +/**
> + * Validate an admin response
> + *
> + * scm_data: a pointer to the SCM device data
> + *
> + * Returns the status code of the command, or negative on error
> + */
> +int scm_admin_response(const struct scm_data *scm_data);
> +
> +/**
> + * Notify the controller to start processing a pending admin command
> + *
> + * scm_data: a pointer to the SCM device data
> + *
> + * Returns 0 on success, negative on error
> + */
> +int scm_admin_command_execute(const struct scm_data *scm_data);
> +
> +/**
> + * Wait for an admin command to finish executing
> + *
> + * scm_data: a pointer to the SCM device data
> + * command: the admin command to wait for completion (determines the timeout)
> + *
> + * Returns 0 on success, -EBUSY on timeout
> + */
> +int scm_admin_command_complete_timeout(const struct scm_data *scm_data,
> +				       int command);
> +
> +/**
> + * Notify the controller that the admin response has been handled
> + *
> + * scm_data: a pointer to the SCM device data
> + *
> + * Returns 0 on success, negative on failure
> + */
> +int scm_admin_response_handled(const struct scm_data *scm_data);
> +
> +/**
> + * Issue a near storage command request
> + *
> + * scm_data: a pointer to the SCM device data
> + * op_code: The op-code for the command
> + *
> + * Returns an identifier for the command, or negative on error
> + */
> +int scm_ns_command_request(struct scm_data *scm_data, u8 op_code);
> +
> +/**
> + * Validate a near storage response
> + *
> + * scm_data: a pointer to the SCM device data
> + *
> + * Returns the status code of the command, or negative on error
> + */
> +int scm_ns_response(const struct scm_data *scm_data);
> +
> +/**
> + * Notify the controller to start processing a pending near storage command
> + *
> + * scm_data: a pointer to the SCM device data
> + *
> + * Returns 0 on success, negative on error
> + */
> +int scm_ns_command_execute(const struct scm_data *scm_data);
> +
> +/**
> + * Is a near storage command executing
> + *
> + * scm_data: a pointer to the SCM device data
> + *
> + * Returns true if the previous admin command has completed
> + */
> +bool scm_ns_command_complete(const struct scm_data *scm_data);
> +
> +/**
> + * Notify the controller that the near storage response has been handled
> + *
> + * scm_data: a pointer to the SCM device data
> + *
> + * Returns 0 on success, negative on failure
> + */
> +int scm_ns_response_handled(const struct scm_data *scm_data);
> +
> +/**
> + * Emit a kernel warning showing a command status.
> + *
> + * scm_data: a pointer to the SCM device data
> + * message: A message to accompany the warning
> + * status: The command status
> + */
> +void scm_warn_status(const struct scm_data *scm_data, const char *message,
> +		     u8 status);
> +
> +/**
> + * Emit a kernel warning showing a command status.
> + *
> + * scm_data: a pointer to the SCM device data
> + * message: A message to accompany the warning
> + * status: The command status
> + */
> +void scm_warn_status_fw_update(const struct scm_data *scm_data,
> +			       const char *message, u8 status);
> +
> +/**
> + * Request a controller dump
> + *
> + * scm_data: a pointer to the SCM device data
> + */
> +int scm_request_controller_dump(struct scm_data *scm_data);
> +
> +/**
> + * Request health & performance data (this will emit error logs with the information)
> + *
> + * scm_data: a pointer to the SCM device data
> + */
> +int scm_req_controller_health_perf(struct scm_data *scm_data);
> +
> +
> +/**
> + * scm_overwrite() - Overwrite all data on the card
> + * @scm_data: The SCM device data
> + * Return: 0 on success
> + */
> +int scm_overwrite(struct scm_data *scm_data);
> diff --git a/drivers/nvdimm/ocxl-scm_sysfs.c b/drivers/nvdimm/ocxl-scm_sysfs.c
> new file mode 100644
> index 000000000000..080bbdeb0e56
> --- /dev/null
> +++ b/drivers/nvdimm/ocxl-scm_sysfs.c
> @@ -0,0 +1,219 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +// Copyright 2018 IBM Corp.
> +
> +#include <linux/sysfs.h>
> +#include <linux/capability.h>
> +#include <linux/limits.h>
> +#include <linux/firmware.h>
> +#include "ocxl-scm_internal.h"
> +
> +static ssize_t admin_command_buffer_size_show(struct device *device,
> +	struct device_attribute *attr,
> +	char *buf)
> +{
> +	struct scm_data *scm_data = container_of(device, struct scm_data, dev);
> +
> +	return scnprintf(buf, PAGE_SIZE, "%d\n", scm_data->admin_command.data_size);
> +}
> +
> +static ssize_t fw_version_show(struct device *device,
> +			       struct device_attribute *attr, char *buf)
> +{
> +	struct scm_data *scm_data = container_of(device, struct scm_data, dev);
> +
> +	return scnprintf(buf, PAGE_SIZE, "%s\n", scm_data->fw_version);
> +}
> +
> +#define SCM_FWUPDATE_BLOCK_SIZE	32768
> +
> +/**
> + * scm_update_firmware() - Write a 32kB block of data to firmware
> + * The block may be less than 32kB if it is the last one
> + *
> + * scm_data the SCM device metadata
> + * offset: the offset of the start of the block
> + * buf: the block data
> + * size: the size of the block
> + */
> +static ssize_t scm_update_firmware(struct scm_data *scm_data, size_t offset,
> +				   const char *buf, size_t size)
> +{
> +	int rc;
> +	size_t i;
> +	u64 val;
> +
> +	if (size > SCM_FWUPDATE_BLOCK_SIZE)
> +		return -EINVAL;
> +
> +	rc = scm_admin_command_request(scm_data, ADMIN_COMMAND_FW_UPDATE);
> +	if (rc)
> +		return rc;
> +
> +	val = (((u64)offset) << 32) | size;
> +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> +				      scm_data->admin_command.request_offset + 8,
> +				      OCXL_LITTLE_ENDIAN, val);
> +	if (rc)
> +		return rc;
> +
> +	for (i = 0; i < size; i += 8) {
> +		val = *(u64 *)(buf + i);
> +		rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> +					      scm_data->admin_command.data_offset + i,
> +					      OCXL_HOST_ENDIAN, val);
> +		if (rc)
> +			return rc;
> +	}
> +
> +	rc = scm_admin_command_execute(scm_data);
> +	if (rc)
> +		return rc;
> +
> +	rc = scm_admin_command_complete_timeout(scm_data,
> +						ADMIN_COMMAND_FW_UPDATE);
> +	if (rc < 0) {
> +		dev_err(&scm_data->dev, "Firmware update timeout\n");
> +		return rc;
> +	}
> +
> +	rc = scm_admin_response(scm_data);
> +	if (rc < 0)
> +		return rc;
> +	if (rc != STATUS_SUCCESS) {
> +		scm_warn_status_fw_update(scm_data, "FW Update", rc);
> +		return rc;
> +	}
> +
> +	return 0;
> +}
> +
> +/*
> + * Parse out a firmware filename from sysfs, retrieve it's contents and write it
> + * to the SCM device firmware storage
> + */
> +static ssize_t fw_update_filename_store(struct device *device,
> +					struct device_attribute *attr,
> +					const char *buf, size_t size)
> +{
> +	char path[NAME_MAX+1];
> +	const char *end;
> +	const struct firmware *firmware = NULL;
> +	size_t offset;
> +	int rc;
> +	struct scm_data *scm_data = container_of(device, struct scm_data, dev);
> +
> +	if (!capable(CAP_SYS_ADMIN))
> +		return -EACCES;
> +
> +	end = strnchr(buf, size, '\n');
> +	if (end == NULL)
> +		end = buf + strnlen(buf, size);
> +
> +	if ((end - buf) > NAME_MAX) {
> +		dev_err(device, "Firmware filename '%-.*s' too long\n",
> +			(int)(end - buf), buf);
> +		return -EIO;
> +	}
> +
> +	memcpy(path, buf, end - buf);
> +	path[end - buf] = '\0';
> +
> +	if (request_firmware(&firmware, path, device)) {
> +		dev_err(device, "Firmware file %s not found\n", path);
> +		return -EIO;
> +	}
> +
> +	if (firmware->size % 8) {
> +		release_firmware(firmware);
> +		dev_err(device, "Firmware '%s' should be a multiple of 8 bytes", path);
> +		return -EINVAL;
> +	}
> +
> +	mutex_lock(&scm_data->admin_command.lock);
> +
> +	for (offset = 0; offset < firmware->size; offset += SCM_FWUPDATE_BLOCK_SIZE) {
> +		size_t remainder = firmware->size - offset;
> +		size_t block_size;
> +
> +		block_size = (remainder > SCM_FWUPDATE_BLOCK_SIZE) ?
> +			      SCM_FWUPDATE_BLOCK_SIZE : remainder;
> +		rc = scm_update_firmware(scm_data, offset,
> +					 firmware->data + offset, block_size);
> +		if (rc) {
> +			mutex_unlock(&scm_data->admin_command.lock);
> +			return -EFAULT;
> +		}
> +	}
> +
> +	mutex_unlock(&scm_data->admin_command.lock);
> +
> +	return size;
> +}
> +
> +/*
> + * Trigger a controller dump
> + */
> +static ssize_t controller_dump_store(struct device *device,
> +				     struct device_attribute *attr,
> +				     const char *buf, size_t size)
> +{
> +	struct scm_data *scm_data = container_of(device, struct scm_data, dev);
> +
> +	scm_request_controller_dump(scm_data);
> +
> +	return size;
> +}
> +
> +/*
> + * Request health & performance data
> + */
> +static ssize_t health_request_store(struct device *device,
> +				    struct device_attribute *attr,
> +				    const char *buf, size_t size)
> +{
> +	struct scm_data *scm_data = container_of(device, struct scm_data, dev);
> +
> +	scm_req_controller_health_perf(scm_data);
> +
> +	return size;
> +}
> +
> +/*
> + * Overwrite all media
> + */
> +static ssize_t overwrite_store(struct device *device,
> +			       struct device_attribute *attr,
> +			       const char *buf, size_t size)
> +{
> +	struct scm_data *scm_data = container_of(device, struct scm_data, dev);
> +
> +	scm_overwrite(scm_data);
> +
> +	return size;
> +}
> +
> +static struct device_attribute scm_attrs[] = {
> +	__ATTR_RO(admin_command_buffer_size),
> +	__ATTR_RO(fw_version),
> +	__ATTR_WO(fw_update_filename),
> +	__ATTR_WO(controller_dump),
> +	__ATTR_WO(health_request),
> +	__ATTR_WO(overwrite),
> +};
> +
> +int scm_sysfs_add(struct scm_data *scm_data)
> +{
> +	int i, rc;
> +
> +	for (i = 0; i < ARRAY_SIZE(scm_attrs); i++) {
> +		rc = device_create_file(&scm_data->dev, &scm_attrs[i]);
> +		if (rc) {
> +			for (; --i >= 0;)
> +				device_remove_file(&scm_data->dev, &scm_attrs[i]);
> +
> +			return rc;
> +		}
> +	}
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(scm_sysfs_add);
> diff --git a/include/uapi/linux/ocxl-scm.h b/include/uapi/linux/ocxl-scm.h
> new file mode 100644
> index 000000000000..6dc7e5196da2
> --- /dev/null
> +++ b/include/uapi/linux/ocxl-scm.h
> @@ -0,0 +1,128 @@
> +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> +/* Copyright 2017 IBM Corp. */
> +#ifndef _UAPI_OCXL_SCM_H
> +#define _UAPI_OCXL_SCM_H
> +
> +#include <linux/types.h>
> +#include <linux/ioctl.h>
> +
> +enum scm_fwdebug_action {
> +	SCM_FWDEBUG_READ_CONTROLLER_MEMORY = 0x01,
> +	SCM_FWDEBUG_WRITE_CONTROLLER_MEMORY = 0x02,
> +	SCM_FWDEBUG_ENABLE_FUNCTION = 0x03,
> +	SCM_FWDEBUG_DISABLE_FUNCTION = 0x04,
> +	SCM_FWDEBUG_GET_PEL = 0x05, // Retrieve Persistent Error Log
> +};
> +
> +struct scm_ioctl_buffer_info {
> +	__u32	admin_command_buffer_size; // out
> +	__u32	near_storage_buffer_size; // out
> +};
> +
> +struct scm_ioctl_fwdebug { // All args are inputs
> +	enum scm_fwdebug_action debug_action;
> +	__u16 function_code;
> +	__u16 buf_size; // Size of optional data buffer
> +	__u64 debug_parameter_1;
> +	__u64 debug_parameter_2;
> +	__u8 *buf; // Pointer to optional in/out data buffer
> +};
> +
> +#define SCM_ERROR_LOG_ACTION_RESET	(1 << (32-32))
> +#define SCM_ERROR_LOG_ACTION_CHKFW	(1 << (53-32))
> +#define SCM_ERROR_LOG_ACTION_REPLACE	(1 << (54-32))
> +#define SCM_ERROR_LOG_ACTION_DUMP	(1 << (55-32))
> +
> +#define SCM_ERROR_LOG_TYPE_GENERAL		(0x00)
> +#define SCM_ERROR_LOG_TYPE_PREDICTIVE_FAILURE	(0x01)
> +#define SCM_ERROR_LOG_TYPE_THERMAL_WARNING	(0x02)
> +#define SCM_ERROR_LOG_TYPE_DATA_LOSS		(0x03)
> +#define SCM_ERROR_LOG_TYPE_HEALTH_PERFORMANCE	(0x04)
> +
> +struct scm_ioctl_error_log {
> +	__u32 log_identifier; // out
> +	__u32 program_reference_code; // out
> +	__u32 action_flags; //out, recommended course of action
> +	__u32 power_on_seconds; // out, Number of seconds the controller has been on when the error occurred
> +	__u64 timestamp; // out, relative time since the current IPL
> +	__u64 wwid[2]; // out, the NAA formatted WWID associated with the controller
> +	char  fw_revision[8+1]; // out, firmware revision as null terminated text
> +	__u16 buf_size; /* in/out, buffer size provided/required.
> +			 * If required is greater than provided, the buffer
> +			 * will be truncated to the amount provided. If its
> +			 * less, then only the required bytes will be populated.
> +			 * If it is 0, then there are no more error log entries.
> +			 */
> +	__u8  error_log_type;
> +	__u8  reserved1;
> +	__u32 reserved2;
> +	__u64 reserved3[2];
> +	__u8 *buf; // pointer to output buffer
> +};
> +
> +struct scm_ioctl_controller_dump_data {
> +	__u8 *buf; // pointer to output buffer
> +	__u16 buf_size; /* in/out, buffer size provided/required.
> +			 * If required is greater than provided, the buffer
> +			 * will be truncated to the amount provided. If its
> +			 * less, then only the required bytes will be populated.
> +			 * If it is 0, then there is no more dump data available.
> +			 */
> +	__u32 offset; // in, Offset within the dump
> +	__u64 reserved[8];
> +};
> +
> +struct scm_ioctl_controller_stats {
> +	__u32 reset_count;
> +	__u32 reset_uptime; // seconds
> +	__u32 power_on_uptime; // seconds
> +	__u64 host_load_count;
> +	__u64 host_store_count;
> +	__u64 media_read_count;
> +	__u64 media_write_count;
> +	__u64 cache_hit_count;
> +	__u64 cache_miss_count;
> +	__u64 media_read_latency; // nanoseconds
> +	__u64 media_write_latency; // nanoseconds
> +	__u64 cache_read_latency; // nanoseconds
> +	__u64 cache_write_latency; // nanoseconds
> +};
> +
> +struct scm_ioctl_mmio {
> +	__u64 address; // Offset in global MMIO space
> +	__u64 val; // value to write/was read
> +};
> +
> +struct scm_ioctl_eventfd {
> +	__s32 eventfd;
> +	__u32 reserved;
> +};
> +
> +#ifndef BIT_ULL
> +#define BIT_ULL(nr)	(1ULL << (nr))
> +#endif
> +
> +#define SCM_IOCTL_EVENT_CONTROLLER_DUMP_AVAILABLE	BIT_ULL(0)
> +#define SCM_IOCTL_EVENT_ERROR_LOG_AVAILABLE		BIT_ULL(1)
> +#define SCM_IOCTL_EVENT_HARDWARE_FATAL			BIT_ULL(2)
> +#define SCM_IOCTL_EVENT_FIRMWARE_FATAL			BIT_ULL(3)
> +
> +/* ioctl numbers */
> +#define SCM_MAGIC 0x5C
> +/* SCM devices */
> +#define SCM_IOCTL_BUFFER_INFO	_IOR(SCM_MAGIC, 0x00, struct scm_ioctl_buffer_info)
> +#define SCM_IOCTL_ERROR_LOG	_IOWR(SCM_MAGIC, 0x01, struct scm_ioctl_error_log)
> +#define SCM_IOCTL_CONTROLLER_DUMP _IO(SCM_MAGIC, 0x02)
> +#define SCM_IOCTL_CONTROLLER_DUMP_DATA _IOWR(SCM_MAGIC, 0x03, struct scm_ioctl_controller_dump_data)
> +#define SCM_IOCTL_CONTROLLER_DUMP_COMPLETE _IO(SCM_MAGIC, 0x04)
> +#define SCM_IOCTL_CONTROLLER_STATS _IO(SCM_MAGIC, 0x05)
> +#define SCM_IOCTL_EVENTFD	_IOW(SCM_MAGIC, 0x06, struct scm_ioctl_eventfd)
> +#define SCM_IOCTL_EVENT_CHECK	_IOR(SCM_MAGIC, 0x07, __u64)
> +#define SCM_IOCTL_REQUEST_HEALTH _IO(SCM_MAGIC, 0x08)
> +
> +#define SCM_IOCTL_FWDEBUG	_IOWR(SCM_MAGIC, 0xf0, struct scm_ioctl_fwdebug)
> +#define SCM_IOCTL_MMIO_WRITE	_IOW(SCM_MAGIC, 0xf1, struct scm_ioctl_mmio)
> +#define SCM_IOCTL_MMIO_READ	_IOWR(SCM_MAGIC, 0xf2, struct scm_ioctl_mmio)
> +#define SCM_IOCTL_SHUTDOWN	_IO(SCM_MAGIC, 0xf3)
> +
> +#endif /* _UAPI_OCXL_SCM_H */
> diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
> index b39827dbd071..376500f4e3a2 100644
> --- a/mm/memory_hotplug.c
> +++ b/mm/memory_hotplug.c
> @@ -279,7 +279,7 @@ static int check_pfn_span(unsigned long pfn, unsigned long nr_pages,
>   }
>   
>   int check_hotplug_memory_addressable(unsigned long pfn,
> -					    unsigned long nr_pages)
> +				     unsigned long nr_pages)
>   {
>   	const u64 max_addr = PFN_PHYS(pfn + nr_pages) - 1;
>   
> 



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory
  2019-11-14 13:41   ` [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory Frederic Barrat
@ 2019-11-14 16:35     ` Dan Williams
  2019-11-18 23:47       ` Andrew Donnellan
  2019-11-18 23:01     ` Alastair D'Silva
  1 sibling, 1 reply; 45+ messages in thread
From: Dan Williams @ 2019-11-14 16:35 UTC (permalink / raw)
  To: Frederic Barrat
  Cc: Alastair D'Silva, alastair, Oscar Salvador, Michal Hocko,
	David Hildenbrand, Alexey Kardashevskiy, Wei Yang, Keith Busch,
	Masahiro Yamada, Paul Mackerras, Ira Weiny, Thomas Gleixner,
	Pavel Tatashin, Dave Jiang, linux-nvdimm, Vishal Verma,
	Krzysztof Kozlowski, Mahesh Salgaonkar, Andrew Donnellan,
	Arnd Bergmann, Greg Kurz, Nicholas Piggin, Qian Cai,
	Cédric Le Goater, Hari Bathini, David Gibson, Linux MM,
	Greg Kroah-Hartman, Linux Kernel Mailing List, Andrew Morton,
	linuxppc-dev

Some quick feedback on your intro concerns...

On Thu, Nov 14, 2019 at 5:41 AM Frederic Barrat <fbarrat@linux.ibm.com> wrote:
>
> Hi Alastair,
>
> The patch is huge and could/should probably be split in smaller pieces

Yeah, it's a must. Split the minimum viable infrastructure by topic
and then follow on with per-feature topic patches.

> to ease the review. However, having sinned on that same topic in the
> past, I made a first pass anyway. I haven't covered everything but tried
> to focus on the general setup of the driver for now.
> Since the patch is very long, I'm writing all the comments in one chunk
> here instead of spreading them over a few thousand lines, where some
> would be easy to miss.
>
>
> Update MAINTAINERS for the new files
>
> Have you discussed with the directory owner if it's ok to split the
> driver over several files?

My thought is to establish drivers/opencapi/ and move this and the
existing drivers/misc/ocxl/ bits there.


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 02/10] nvdimm: remove prototypes for nonexistent functions
  2019-10-25  4:46 ` [PATCH 02/10] nvdimm: remove prototypes for nonexistent functions Alastair D'Silva
  2019-10-27 23:41   ` Andrew Donnellan
  2019-11-07 17:56   ` Frederic Barrat
@ 2019-11-15  4:30   ` Dan Williams
  2 siblings, 0 replies; 45+ messages in thread
From: Dan Williams @ 2019-11-15  4:30 UTC (permalink / raw)
  To: Alastair D'Silva
  Cc: alastair, Benjamin Herrenschmidt, Paul Mackerras,
	Michael Ellerman, Frederic Barrat, Andrew Donnellan,
	Arnd Bergmann, Greg Kroah-Hartman, Vishal Verma, Dave Jiang,
	Keith Busch, Ira Weiny, Krzysztof Kozlowski, Geert Uytterhoeven,
	Anton Blanchard, David Gibson, Hari Bathini, Anju T Sudhakar,
	Thomas Gleixner, Cédric Le Goater, Mahesh Salgaonkar,
	Greg Kurz, Masahiro Yamada, Andrew Morton, David Hildenbrand,
	Oscar Salvador, Michal Hocko, Pavel Tatashin, Wei Yang, Qian Cai,
	linuxppc-dev, Linux Kernel Mailing List, linux-nvdimm, Linux MM

On Thu, Oct 24, 2019 at 9:49 PM Alastair D'Silva <alastair@au1.ibm.com> wrote:
>
> From: Alastair D'Silva <alastair@d-silva.org>
>
> These functions don't exist, so remove the prototypes for them.
>
> Signed-off-by: Alastair D'Silva <alastair@d-silva.org>

Looks good, applied.


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory
  2019-11-14 13:41   ` [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory Frederic Barrat
  2019-11-14 16:35     ` Dan Williams
@ 2019-11-18 23:01     ` Alastair D'Silva
  1 sibling, 0 replies; 45+ messages in thread
From: Alastair D'Silva @ 2019-11-18 23:01 UTC (permalink / raw)
  To: Frederic Barrat
  Cc: Oscar Salvador, Michal Hocko, David Hildenbrand,
	Alexey Kardashevskiy, Wei Yang, Keith Busch, Masahiro Yamada,
	Paul Mackerras, Ira Weiny, Thomas Gleixner, Pavel Tatashin,
	Dave Jiang, linux-nvdimm, Vishal Verma, Krzysztof Kozlowski,
	Mahesh Salgaonkar, Andrew Donnellan, Arnd Bergmann, Greg Kurz,
	Nicholas Piggin, Qian Cai, Cédric Le Goater, Dan Williams,
	Hari Bathini, David Gibson, linux-mm, Greg Kroah-Hartman,
	linux-kernel, Andrew Morton, linuxppc-dev

On Thu, 2019-11-14 at 14:41 +0100, Frederic Barrat wrote:
> Hi Alastair,
> 
> The patch is huge and could/should probably be split in smaller
> pieces 
> to ease the review. However, having sinned on that same topic in the 
> past, I made a first pass anyway. I haven't covered everything but
> tried 
> to focus on the general setup of the driver for now.
> Since the patch is very long, I'm writing all the comments in one
> chunk 
> here instead of spreading them over a few thousand lines, where some 
> would be easy to miss.
> 
> 
> Update MAINTAINERS for the new files
> 
> Have you discussed with the directory owner if it's ok to split the 
> driver over several files?
> 

Not yet, I do have a request as to whether drivers/nvdimm is actually
the right place for this though.

> 
> Kconfig
> =======
> Does it make sense to keep OCXL_SCM_DEBUG separate? Why not enabling
> it 
> by default?

I think so, these features are a bit dangerous for general users, but
very useful for developers.

> 
> 
> ocxl_scm.c
> ==========
> 
> scm_file_init()
> ---------------
> on error paths, should call idr_destroy() (same pb found in base
> ocxl 
> driver)
> 

Ok.

> 
> scm_probe() and _function_0()
> -----------------------------
> 
> The different init between function 0 and 1 looks a bit off and it
> seems 
> they could be unified. Function 1 does the same thing as function 0 
> (call ocxl_function_open()) then some AFU-specific init, which we
> could 
> skip if there's no AFU found on the function. And log a WARN if we
> find 
> an AFU on something else than function 1, since, unlike ocxl, we
> know 
> exactly what the device is like for SCM. But keep an common probe() 
> function. It would also simplify the flow on scm_remove() and avoid 
> problems (currently ocxl_function_close() is not called for function
> 1).

Hmm, the 2 functions do very different things. Function 0 only exists
for link negotiation, and there is a huge amount of other bits & pieces
in the scm_data struct that will never be used for it.

ocxl_function_close() is called in free_scm() via the release handler
for the device.

> 
> scm-data->timeouts: array of size 9 declared, we only init 7
> members. 
> With the zalloc() the others are at 0, is that correct?
> 

Yes, these will be queried dynamically from the card in a later patch,
but that feature is not yet ready for testing.

The timeouts that are currently 0 are never read in the current
implementation of the driver.

> 
> Something looks wrong regarding data release in the error path(s).
> IIUC, 
> we register the device early and rely on the release callback of the 
> device to free all resources (in free_scm_dev()). We should probably 
> have a comment in probe() to make it obvious. Or maybe better, have
> a 

I'll add a comment.

> subfunction to keep doing the rest of the inits which are dependent
> on 
> the device release to be cleaned up. In the subsequent error paths
> in 
> scm_probe(), we are missing a device_unregister()
> 

This is intentional, I want to keep the device online (but not
registered with libnvdimm) in the event of an error as the card can be
interrogated via IOCTLs to find out what wrong.

> Could log 120 times the same "Waiting for SCM to become usable"
> message, 
> which is not really helping much.
> 

I'll quieten that, it was useful during development to identify whether
the machine had locked up or was still waiting on the card.

> 
> free_scm()
> ---------
> Related to above comment in probe(), it would help to be able to
> easily 
> match the what's done in probe vs. undone here. For example, in
> probe(), 
> there's scm_setup_irq(), where we do all things related to
> interrupts. 
> But we don't have a subfunction to clean the interrupts state. It
> would 
> help for readability and track potential misses. I didn't tried to
> match 
> all of them, but the following calls seem missing:
> 
> ocxl_context_detach()
> ocxl_afu_irq_free()

Hmm, we call ocxl_context_detach_all() in ocxl/core.c:remove_afu() (via
ocxl_function_close), but by that stage, I've already called
ocxl_context_free(), so that's clearly a bug.

I'll add in the missing ocxl_context_detach call in the scm_driver, and
in a seperate patch, free the context in ocxl_context_detach_all().

I'll also add in the missing calls to ocxl_afu_irq_free(), but I wonder
whether we should also clean all the IRQ allocations in remove_afu()
too?

> 
> 
> ocxl_remove()
> -------------
> see comment above about unifying function 0 and 1 case.
> Why is nvdimm_bus_unregister() treated separately? Can't it be part
> of 
> the "normal" freeing of resources done implicitly when calling 
> device_unregister() in the free_scm() callback?
> 

Yeah, good observation.

> 
> scm_setup_device_metadata()
> ---------------------------
> function doesn't do any setup, so the name is misleading.
> 

renamed to read_device_metadata().

> 	for (i = 0; i < 8; i++)
> 		scm_data->fw_version[i] = (val >> (i * 8)) & 0xff;
> => looks like an endianess conversion? Can't we use the
> OCXL_BIG_ENDIAN 
> when doing the mmio read?

It's extracting bytes of text out, I've replaced it with a native
endian read & a comment.

> 
> scm_setup_irq()
> ---------------
> if ocxl_afu_irq_get_addr(irq 1) or the ioremap(irq 1) fail, we jump
> to 
> the label 'out_irq0' and will exit the function with rc = 0, instead
> of 
> failing.
> 

Good catch, thanks.

> 
> 
> scm_setup_command_metadata()
> ----------------------------
> it would make sense to initialize the mutex in the struct 
> command_metadata in this function instead of the top of scm_probe(),
> to 
> group all the related data inits.
> 

Agreed.

> 
> 
> scm_probe_function_0()
> ----------------------
> comment above function:
>   * This is important as it enables higher than 0 across all other 
> functions,
>   * which in turn enables higher bandwidth accesses
> 
> "higher than 0"?
> I'm guessing you want to say function 0 configures the link, to
> ensure 
> maximum bandwidth
> 

Ugh, that's terrible, it looks like some words are missing. It should
say "templates higher than 0".

> EFAULT is usually reserved for an invalid memory access. Why not 
> PTR_ERR() of the returned value from ocxl_function_open()?
> 

Agreed.

> 
> 
> struct scm_fops has a wrong indentation (spaces between .open and
> '=')
> 

Thanks.

> 
> 
> scm_heartbeat()
> ---------------
> the "goto out" at the end of the good path is useless and unusual in
> the 
> kernel, I think.
> 

Ok, I tend to add these to show I intended to fall through and didn't
forget to do something else, but I'll remove it for consistency.

> 
> scm_register_lpc_mem()
> ----------------------
> lpc_mem = ocxl_afu_lpc_mem(scm_data->ocxl_afu);
> => lpc_mem is allocated as part of the afu structure in ocxl, so
> that 
> shouldn't be NULL. Still worth keeping, but I think lpc_mem->start
> is 
> what really needs testing
> 

I'll test both, thanks.

> 
> scm_imn0_handler()
> -----------------
> I don't think we should return IRQ_NONE. As far as the kernel is 
> concerned, an interrupt was raised. So it should be acknowledged,
> even 
> if the fgpa is somehow in an incorrect state. So the IRQ_NONE should
> be 
> IRQ_HANDLED
> 

OK.

> 
> scm_imn1_handler()
> ------------------
> for the sake of clarity, the potential error when calling scm_chi() 
> should be treated the same in the 2 handlers.

Agreed.

> What's the effect of nvdimm_bus_unregister() on any application
> using 
> the memory?
> 

Good question, I haven't checked, but I would expect a segfault as the
memory is yanked out.

The situation in which we do this is where the card is in a FATAL
state, so would be unable to satisfy any requests.

> 
> #ifdef CONFIG_OCXL_SCM_DEBUG
> It's usual and it helps navigate the code, to comment the config
> macro 
> on the else and endif lines:
> #endif /* CONFIG_OCXL_SCM_DEBUG */
> 
> 
Agreed.

> 
> 
> ocxl-scm_internal.c
> ====================
> 
> scm_admin_command_request
> -------------------------
> Hardening: would it make sense to test and error out if the ACRA bit 
> (used to test command is complete) is at 0 before submitting a new
> request?
> 

Yes, I'll do that.

> 
> scm_admin_command_complete_timeout
> ----------------------------------
> A delay of 32ms is not that usual. A comment explaining why would be 
> interesting.
> 
Ok, also replaced with a define.

> Why timeout++ ?
Stray code, I'll remove it.

> 
> 
> 
> ocxl-scm_sysfs.c
> ================
> No reason to export the scm_sysfs_add symbol
> 
Ok.

> 
> 
> ocxl_scm.h
> ==========
> struct scm_ioctl_error_log:
> char  fw_revision[8+1]
> exposed to uncontrolled padding, which is a problem for a KABI
> Remove the extra char?
> 

This is always an output field, so the kernel has control of the
padding.

> 
> 
> memory_hotplug.c
> ================
> whitespace diff shouldn't be here
> 

Ok.

> 
>    Fred
> 
> 
> Le 25/10/2019 à 06:47, Alastair D'Silva a écrit :
> > From: Alastair D'Silva <alastair@d-silva.org>
> > 
> > This driver exposes LPC memory on OpenCAPI SCM cards
> > as an NVDIMM, allowing the existing nvram infrastructure
> > to be used.
> > 
> > Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> > ---
> >   drivers/nvdimm/Kconfig             |   17 +
> >   drivers/nvdimm/Makefile            |    3 +
> >   drivers/nvdimm/ocxl-scm.c          | 2210
> > ++++++++++++++++++++++++++++
> >   drivers/nvdimm/ocxl-scm_internal.c |  232 +++
> >   drivers/nvdimm/ocxl-scm_internal.h |  331 +++++
> >   drivers/nvdimm/ocxl-scm_sysfs.c    |  219 +++
> >   include/uapi/linux/ocxl-scm.h      |  128 ++
> >   mm/memory_hotplug.c                |    2 +-
> >   8 files changed, 3141 insertions(+), 1 deletion(-)
> >   create mode 100644 drivers/nvdimm/ocxl-scm.c
> >   create mode 100644 drivers/nvdimm/ocxl-scm_internal.c
> >   create mode 100644 drivers/nvdimm/ocxl-scm_internal.h
> >   create mode 100644 drivers/nvdimm/ocxl-scm_sysfs.c
> >   create mode 100644 include/uapi/linux/ocxl-scm.h
> > 
> > diff --git a/drivers/nvdimm/Kconfig b/drivers/nvdimm/Kconfig
> > index 36af7af6b7cf..e4f7b6b08efd 100644
> > --- a/drivers/nvdimm/Kconfig
> > +++ b/drivers/nvdimm/Kconfig
> > @@ -130,4 +130,21 @@ config NVDIMM_TEST_BUILD
> >   	  core devm_memremap_pages() implementation and other
> >   	  infrastructure.
> >   
> > +config OCXL_SCM
> > +	tristate "OpenCAPI Storage Class Memory"
> > +	depends on LIBNVDIMM
> > +	select ZONE_DEVICE
> > +	select OCXL
> > +	help
> > +	  Exposes devices that implement the OpenCAPI Storage Class
> > Memory
> > +	  specification as persistent memory regions.
> > +
> > +	  Select N if unsure.
> > +
> > +config OCXL_SCM_DEBUG
> > +	bool "OpenCAPI Storage Class Memory debugging"
> > +	depends on OCXL_SCM
> > +	help
> > +	  Enables low level IOCTLs for OpenCAPI SCM firmware
> > development
> > +
> >   endif
> > diff --git a/drivers/nvdimm/Makefile b/drivers/nvdimm/Makefile
> > index 29203f3d3069..43d826397bfc 100644
> > --- a/drivers/nvdimm/Makefile
> > +++ b/drivers/nvdimm/Makefile
> > @@ -6,6 +6,9 @@ obj-$(CONFIG_ND_BLK) += nd_blk.o
> >   obj-$(CONFIG_X86_PMEM_LEGACY) += nd_e820.o
> >   obj-$(CONFIG_OF_PMEM) += of_pmem.o
> >   obj-$(CONFIG_VIRTIO_PMEM) += virtio_pmem.o nd_virtio.o
> > +obj-$(CONFIG_OCXL_SCM) += ocxlscm.o
> > +
> > +ocxlscm-y := ocxl-scm.o ocxl-scm_internal.o ocxl-scm_sysfs.o
> >   
> >   nd_pmem-y := pmem.o
> >   
> > diff --git a/drivers/nvdimm/ocxl-scm.c b/drivers/nvdimm/ocxl-scm.c
> > new file mode 100644
> > index 000000000000..f4e6cc022de8
> > --- /dev/null
> > +++ b/drivers/nvdimm/ocxl-scm.c
> > @@ -0,0 +1,2210 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +// Copyright 2019 IBM Corp.
> > +
> > +/*
> > + * A driver for Storage Class Memory, connected via OpenCAPI
> > + */
> > +
> > +#include <linux/module.h>
> > +#include <misc/ocxl.h>
> > +#include <linux/delay.h>
> > +#include <linux/ndctl.h>
> > +#include <linux/eventfd.h>
> > +#include <linux/fs.h>
> > +#include <linux/mm_types.h>
> > +#include <linux/memory_hotplug.h>
> > +#include "ocxl-scm_internal.h"
> > +
> > +
> > +static const struct pci_device_id scm_pci_tbl[] = {
> > +	{ PCI_DEVICE(PCI_VENDOR_ID_IBM, 0x0625), },
> > +	{ }
> > +};
> > +
> > +MODULE_DEVICE_TABLE(pci, scm_pci_tbl);
> > +
> > +#define SCM_NUM_MINORS 256 // Total to reserve
> > +#define SCM_USABLE_TIMEOUT 120 // seconds
> > +
> > +static dev_t scm_dev;
> > +static struct class *scm_class;
> > +static struct mutex minors_idr_lock;
> > +static struct idr minors_idr;
> > +
> > +static const struct attribute_group *scm_pmem_attribute_groups[] =
> > {
> > +	&nvdimm_bus_attribute_group,
> > +	NULL,
> > +};
> > +
> > +static const struct attribute_group
> > *scm_pmem_region_attribute_groups[] = {
> > +	&nd_region_attribute_group,
> > +	&nd_device_attribute_group,
> > +	&nd_mapping_attribute_group,
> > +	&nd_numa_attribute_group,
> > +	NULL,
> > +};
> > +
> > +/**
> > + * scm_ndctl_config_write() - Handle a ND_CMD_SET_CONFIG_DATA
> > command from ndctl
> > + * @scm_data: the SCM metadata
> > + * @command: the incoming data to write
> > + * Return: 0 on success, negative on failure
> > + */
> > +static int scm_ndctl_config_write(struct scm_data *scm_data,
> > +				  struct nd_cmd_set_config_hdr
> > *command)
> > +{
> > +	if (command->in_offset + command->in_length >
> > SCM_LABEL_AREA_SIZE)
> > +		return -EINVAL;
> > +
> > +	memcpy_flushcache(scm_data->metadata_addr + command->in_offset, 
> > command->in_buf,
> > +			  command->in_length);
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * scm_ndctl_config_read() - Handle a ND_CMD_GET_CONFIG_DATA
> > command from ndctl
> > + * @scm_data: the SCM metadata
> > + * @command: the read request
> > + * Return: 0 on success, negative on failure
> > + */
> > +static int scm_ndctl_config_read(struct scm_data *scm_data,
> > +				 struct nd_cmd_get_config_data_hdr
> > *command)
> > +{
> > +	if (command->in_offset + command->in_length >
> > SCM_LABEL_AREA_SIZE)
> > +		return -EINVAL;
> > +
> > +	memcpy(command->out_buf, scm_data->metadata_addr + command-
> > >in_offset,
> > +	       command->in_length);
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * scm_ndctl_config_size() - Handle a ND_CMD_GET_CONFIG_SIZE
> > command from ndctl
> > + * @scm_data: the SCM metadata
> > + * @command: the read request
> > + * Return: 0 on success, negative on failure
> > + */
> > +static int scm_ndctl_config_size(struct nd_cmd_get_config_size
> > *command)
> > +{
> > +	command->status = 0;
> > +	command->config_size = SCM_LABEL_AREA_SIZE;
> > +	command->max_xfer = PAGE_SIZE;
> > +
> > +	return 0;
> > +}
> > +
> > +static int read_smart_attrib(struct scm_data *scm_data, u16
> > offset,
> > +			     struct scm_smart_attribs *attribs)
> > +{
> > +	u64 val;
> > +	int rc;
> > +	struct scm_smart_attrib *attrib;
> > +	u8 attrib_id;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, offset,
> > OCXL_LITTLE_ENDIAN,
> > +				     &val);
> > +	if (rc)
> > +		return rc;
> > +
> > +	attrib_id = (val >> 56) & 0xff;
> > +	switch (attrib_id) {
> > +	case SCM_SMART_ATTR_POWER_ON_HOURS:
> > +		attrib = &attribs->power_on_hours;
> > +		break;
> > +
> > +	case SCM_SMART_ATTR_TEMPERATURE:
> > +		attrib = &attribs->temperature;
> > +		break;
> > +
> > +	case SCM_SMART_ATTR_LIFE_REMAINING:
> > +		attrib = &attribs->life_remaining;
> > +		break;
> > +
> > +	default:
> > +		dev_err(&scm_data->dev, "Unknown smart attrib '%d'",
> > attrib_id);
> > +		return -EFAULT;
> > +	}
> > +
> > +	attrib->id = attrib_id;
> > +	attrib->attribute_flags = (val >> 40) & 0xffff;
> > +	attrib->current_val = (val >> 32) & 0xff;
> > +	attrib->threshold_val = (val >> 24) & 0xff;
> > +	attrib->worst_val = (val >> 16) & 0xff;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, offset + 0x08,
> > +				     OCXL_LITTLE_ENDIAN, &val);
> > +	if (rc)
> > +		return rc;
> > +
> > +	attrib->raw_val = val;
> > +
> > +	return 0;
> > +}
> > +
> > +static int scm_smart_offset_0x00(struct scm_data *scm_data, u32
> > *length)
> > +{
> > +	int rc;
> > +	u64 val;
> > +
> > +	u16 data_identifier;
> > +	u32 data_length;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset,
> > +				     OCXL_LITTLE_ENDIAN, &val);
> > +	if (rc)
> > +		return rc;
> > +
> > +	data_identifier = val >> 48;
> > +	data_length = val & 0xFFFFFFFF;
> > +
> > +	if (data_identifier != 0x534D) {
> > +		dev_err(&scm_data->dev,
> > +			"Bad data identifier for smart data, expected
> > 'SM', got '%-.*s'\n",
> > +			2, (char *)&data_identifier);
> > +		return -EFAULT;
> > +	}
> > +
> > +	*length = data_length;
> > +	return 0;
> > +}
> > +
> > +static int scm_smart_update(struct scm_data *scm_data)
> > +{
> > +	u32 length, i;
> > +	int rc;
> > +
> > +	mutex_lock(&scm_data->admin_command.lock);
> > +
> > +	rc = scm_admin_command_request(scm_data, ADMIN_COMMAND_SMART);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = scm_admin_command_execute(scm_data);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = scm_admin_command_complete_timeout(scm_data,
> > ADMIN_COMMAND_SMART);
> > +	if (rc < 0) {
> > +		dev_err(&scm_data->dev, "SMART timeout\n");
> > +		goto out;
> > +	}
> > +
> > +	rc = scm_admin_response(scm_data);
> > +	if (rc < 0)
> > +		goto out;
> > +	if (rc != STATUS_SUCCESS) {
> > +		scm_warn_status(scm_data, "Unexpected status from
> > SMART", rc);
> > +		goto out;
> > +	}
> > +
> > +	rc = scm_smart_offset_0x00(scm_data, &length);
> > +	if (rc)
> > +		goto out;
> > +
> > +	length /= 0x10; // Length now contains the number of attributes
> > +
> > +	for (i = 0; i < length; i++)
> > +		read_smart_attrib(scm_data,
> > +				  scm_data->admin_command.data_offset +
> > 0x08 + i * 0x10,
> > +				  &scm_data->smart);
> > +
> > +	rc = scm_admin_response_handled(scm_data);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = 0;
> > +	goto out;
> > +
> > +out:
> > +	mutex_unlock(&scm_data->admin_command.lock);
> > +	return rc;
> > +}
> > +
> > +static int scm_ndctl_smart(struct scm_data *scm_data, void *buf,
> > +			   unsigned int buf_len)
> > +{
> > +	int rc;
> > +
> > +	if (buf_len != sizeof(scm_data->smart))
> > +		return -EINVAL;
> > +
> > +	rc = scm_smart_update(scm_data);
> > +	if (rc)
> > +		return rc;
> > +
> > +	memcpy(buf, &scm_data->smart, buf_len);
> > +
> > +	return 0;
> > +}
> > +
> > +
> > +static int scm_ndctl(struct nvdimm_bus_descriptor *nd_desc,
> > +		     struct nvdimm *nvdimm,
> > +		     unsigned int cmd, void *buf, unsigned int buf_len,
> > int *cmd_rc)
> > +{
> > +	struct scm_data *scm_data = container_of(nd_desc, struct
> > scm_data, bus_desc);
> > +
> > +	switch (cmd) {
> > +	case ND_CMD_SMART:
> > +		*cmd_rc = scm_ndctl_smart(scm_data, buf, buf_len);
> > +		return 0;
> > +
> > +	case ND_CMD_GET_CONFIG_SIZE:
> > +		*cmd_rc = scm_ndctl_config_size(buf);
> > +		return 0;
> > +
> > +	case ND_CMD_GET_CONFIG_DATA:
> > +		*cmd_rc = scm_ndctl_config_read(scm_data, buf);
> > +		return 0;
> > +
> > +	case ND_CMD_SET_CONFIG_DATA:
> > +		*cmd_rc = scm_ndctl_config_write(scm_data, buf);
> > +		return 0;
> > +
> > +	default:
> > +		return -ENOTTY;
> > +	}
> > +}
> > +
> > +static ssize_t serial_show(struct device *dev,
> > +			   struct device_attribute *attr, char *buf)
> > +{
> > +	struct nvdimm *nvdimm = to_nvdimm(dev);
> > +	struct scm_data *scm_data = nvdimm_provider_data(nvdimm);
> > +	const struct ocxl_fn_config *config =
> > ocxl_function_config(scm_data->ocxl_fn);
> > +
> > +	return sprintf(buf, "0x%llx\n", config->serial);
> > +}
> > +static DEVICE_ATTR_RO(serial);
> > +
> > +static struct attribute *scm_dimm_attributes[] = {
> > +	&dev_attr_serial.attr,
> > +	NULL,
> > +};
> > +
> > +static umode_t scm_dimm_attr_visible(struct kobject *kobj,
> > +				     struct attribute *a, int n)
> > +{
> > +	return a->mode;
> > +}
> > +
> > +static const struct attribute_group scm_dimm_attribute_group = {
> > +	.name = "scm",
> > +	.attrs = scm_dimm_attributes,
> > +	.is_visible = scm_dimm_attr_visible,
> > +};
> > +
> > +static const struct attribute_group *scm_dimm_attribute_groups[] =
> > {
> > +	&nvdimm_attribute_group,
> > +	&nd_device_attribute_group,
> > +	&scm_dimm_attribute_group,
> > +	NULL,
> > +};
> > +
> > +/**
> > + * scm_reserve_metadata() - Reserve space for nvdimm metadata
> > + * @scm_data: The SCM device data
> > + * @lpc_mem: The resource representing the LPC memory of the SCM
> > device
> > + */
> > +static int scm_reserve_metadata(struct scm_data *scm_data,
> > +				struct resource *lpc_mem)
> > +{
> > +	scm_data->metadata_addr = devm_memremap(&scm_data->dev,
> > lpc_mem->start,
> > +						SCM_LABEL_AREA_SIZE,
> > MEMREMAP_WB);
> > +	if (IS_ERR(scm_data->metadata_addr))
> > +		return PTR_ERR(scm_data->metadata_addr);
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * scm_overwrite() - Overwrite all data on the card
> > + * @scm_data: The SCM device data
> > + * Return: 0 on success
> > + */
> > +int scm_overwrite(struct scm_data *scm_data)
> > +{
> > +	int rc;
> > +
> > +	mutex_lock(&scm_data->ns_command.lock);
> > +
> > +	rc = scm_ns_command_request(scm_data, NS_COMMAND_SECURE_ERASE);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = scm_ns_command_execute(scm_data);
> > +	if (rc)
> > +		goto out;
> > +
> > +	scm_data->overwrite_state = SCM_OVERWRITE_BUSY;
> > +
> > +	return 0;
> > +
> > +out:
> > +	mutex_unlock(&scm_data->ns_command.lock);
> > +	return rc;
> > +}
> > +
> > +/**
> > + * scm_secop_overwrite() - Overwrite all data on the card
> > + * @nvdimm: The nvdimm representation of the SCM device to start
> > the overwrite on
> > + * @key_data: Unused (no security key implementation)
> > + * Return: 0 on success
> > + */
> > +static int scm_secop_overwrite(struct nvdimm *nvdimm,
> > +			       const struct nvdimm_key_data *key_data)
> > +{
> > +	struct scm_data *scm_data = nvdimm_provider_data(nvdimm);
> > +
> > +	return scm_overwrite(scm_data);
> > +}
> > +
> > +/**
> > + * scm_secop_query_overwrite() - Get the current overwrite state
> > + * @nvdimm: The nvdimm representation of the SCM device to start
> > the overwrite on
> > + * Return: 0 if successful or idle, -EBUSY if busy, -EFAULT if
> > failed
> > + */
> > +static int scm_secop_query_overwrite(struct nvdimm *nvdimm)
> > +{
> > +	struct scm_data *scm_data = nvdimm_provider_data(nvdimm);
> > +
> > +	if (scm_data->overwrite_state == SCM_OVERWRITE_BUSY)
> > +		return -EBUSY;
> > +
> > +	if (scm_data->overwrite_state == SCM_OVERWRITE_FAILED)
> > +		return -EFAULT;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * scm_secop_get_flags() - return the security flags for the SCM
> > device
> > + */
> > +static unsigned long scm_secop_get_flags(struct nvdimm *nvdimm,
> > +		enum nvdimm_passphrase_type ptype)
> > +{
> > +	struct scm_data *scm_data = nvdimm_provider_data(nvdimm);
> > +
> > +	if (scm_data->overwrite_state == SCM_OVERWRITE_BUSY)
> > +		return BIT(NVDIMM_SECURITY_OVERWRITE);
> > +
> > +	return BIT(NVDIMM_SECURITY_DISABLED);
> > +}
> > +
> > +static const struct nvdimm_security_ops sec_ops  = {
> > +	.get_flags = scm_secop_get_flags,
> > +	.overwrite = scm_secop_overwrite,
> > +	.query_overwrite = scm_secop_query_overwrite,
> > +};
> > +
> > +/**
> > + * scm_register_lpc_mem() - Discover persistent memory on a device
> > and register it with the NVDIMM subsystem
> > + * @scm_data: The SCM device data
> > + * Return: 0 on success
> > + */
> > +static int scm_register_lpc_mem(struct scm_data *scm_data)
> > +{
> > +	struct nd_region_desc region_desc;
> > +	struct nd_mapping_desc nd_mapping_desc;
> > +	struct resource *lpc_mem;
> > +	const struct ocxl_afu_config *config;
> > +	const struct ocxl_fn_config *fn_config;
> > +	int rc;
> > +	unsigned long nvdimm_cmd_mask = 0;
> > +	unsigned long nvdimm_flags = 0;
> > +	int target_node;
> > +	char serial[16+1];
> > +
> > +	// Set up the reserved metadata area
> > +	rc = ocxl_afu_map_lpc_mem(scm_data->ocxl_afu);
> > +	if (rc < 0)
> > +		return rc;
> > +
> > +	lpc_mem = ocxl_afu_lpc_mem(scm_data->ocxl_afu);
> > +	if (lpc_mem == NULL)
> > +		return -EINVAL;
> > +
> > +	config = ocxl_afu_config(scm_data->ocxl_afu);
> > +	fn_config = ocxl_function_config(scm_data->ocxl_fn);
> > +
> > +	rc = scm_reserve_metadata(scm_data, lpc_mem);
> > +	if (rc)
> > +		return rc;
> > +
> > +	scm_data->bus_desc.attr_groups = scm_pmem_attribute_groups;
> > +	scm_data->bus_desc.provider_name = "scm";
> > +	scm_data->bus_desc.ndctl = scm_ndctl;
> > +	scm_data->bus_desc.module = THIS_MODULE;
> > +
> > +	scm_data->nvdimm_bus = nvdimm_bus_register(&scm_data->dev,
> > +			       &scm_data->bus_desc);
> > +	if (!scm_data->nvdimm_bus)
> > +		return -EINVAL;
> > +
> > +	scm_data->scm_res.start = (u64)lpc_mem->start +
> > SCM_LABEL_AREA_SIZE;
> > +	scm_data->scm_res.end = (u64)lpc_mem->start + config-
> > >lpc_mem_size - 1;
> > +	scm_data->scm_res.name = "SCM persistent memory";
> > +
> > +	set_bit(ND_CMD_GET_CONFIG_SIZE, &nvdimm_cmd_mask);
> > +	set_bit(ND_CMD_GET_CONFIG_DATA, &nvdimm_cmd_mask);
> > +	set_bit(ND_CMD_SET_CONFIG_DATA, &nvdimm_cmd_mask);
> > +	set_bit(ND_CMD_SMART, &nvdimm_cmd_mask);
> > +
> > +	set_bit(NDD_ALIASING, &nvdimm_flags);
> > +
> > +	snprintf(serial, sizeof(serial), "%llx", fn_config->serial);
> > +	nd_mapping_desc.nvdimm = __nvdimm_create(scm_data->nvdimm_bus,
> > scm_data,
> > +				 scm_dimm_attribute_groups,
> > +				 nvdimm_flags, nvdimm_cmd_mask,
> > +				 0, NULL, serial, &sec_ops);
> > +	if (!nd_mapping_desc.nvdimm)
> > +		return -ENOMEM;
> > +
> > +	if (nvdimm_bus_check_dimm_count(scm_data->nvdimm_bus, 1))
> > +		return -EINVAL;
> > +
> > +	nd_mapping_desc.start = scm_data->scm_res.start;
> > +	nd_mapping_desc.size = resource_size(&scm_data->scm_res);
> > +	nd_mapping_desc.position = 0;
> > +
> > +	scm_data->nd_set.cookie1 = fn_config->serial + 1; // allow for
> > empty serial
> > +	scm_data->nd_set.cookie2 = fn_config->serial + 1;
> > +
> > +	target_node = of_node_to_nid(scm_data->pdev->dev.of_node);
> > +
> > +	memset(&region_desc, 0, sizeof(region_desc));
> > +	region_desc.res = &scm_data->scm_res;
> > +	region_desc.attr_groups = scm_pmem_region_attribute_groups;
> > +	region_desc.numa_node = NUMA_NO_NODE;
> > +	region_desc.target_node = target_node;
> > +	region_desc.num_mappings = 1;
> > +	region_desc.mapping = &nd_mapping_desc;
> > +	region_desc.nd_set = &scm_data->nd_set;
> > +
> > +	set_bit(ND_REGION_PAGEMAP, &region_desc.flags);
> > +	/*
> > +	 * NB: libnvdimm copies the data from ndr_desc into it's own
> > +	 * structures so passing a stack pointer is fine.
> > +	 */
> > +	scm_data->nd_region = nvdimm_pmem_region_create(scm_data-
> > >nvdimm_bus,
> > +			      &region_desc);
> > +	if (!scm_data->nd_region)
> > +		return -EINVAL;
> > +
> > +	dev_info(&scm_data->dev,
> > +		 "Onlining %lluMB of persistent memory\n",
> > +		 nd_mapping_desc.size / SZ_1M);
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * scm_is_memory_available() - Does the controller have memory
> > available?
> > + * @scm_data: a pointer to the SCM device data
> > + * Return: true if the controller has memory available
> > + */
> > +static bool scm_is_memory_available(const struct scm_data
> > *scm_data)
> > +{
> > +	u64 val = 0;
> > +	int rc = scm_chi(scm_data, &val);
> > +
> > +	WARN_ON(rc < 0);
> > +
> > +	return (val & GLOBAL_MMIO_CHI_MA) != 0;
> > +}
> > +
> > +/**
> > + * scm_extract_command_metadata() - Extract command data from MMIO
> > & save it for further use
> > + * @scm_data: a pointer to the SCM device data
> > + * @offset: The base address of the command data structures
> > (address of CREQO)
> > + * @command_metadata: A pointer to the command metadata to
> > populate
> > + * Return: 0 on success, negative on failure
> > + */
> > +static int scm_extract_command_metadata(struct scm_data *scm_data,
> > u32 offset,
> > +					struct command_metadata
> > *command_metadata)
> > +{
> > +	int rc;
> > +	u64 tmp;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, offset,
> > OCXL_LITTLE_ENDIAN,
> > +				     &tmp);
> > +	if (rc)
> > +		return rc;
> > +
> > +	command_metadata->request_offset = tmp >> 32;
> > +	command_metadata->response_offset = tmp & 0xFFFFFFFF;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, offset + 8,
> > OCXL_LITTLE_ENDIAN,
> > +				     &tmp);
> > +	if (rc)
> > +		return rc;
> > +
> > +	command_metadata->data_offset = tmp >> 32;
> > +	command_metadata->data_size = tmp & 0xFFFFFFFF;
> > +
> > +	command_metadata->id = 0;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * scm_setup_command_metadata() - Set up the command metadata
> > + * @scm_data: a pointer to the SCM device data
> > + */
> > +static int scm_setup_command_metadata(struct scm_data *scm_data)
> > +{
> > +	int rc;
> > +
> > +	rc = scm_extract_command_metadata(scm_data,
> > GLOBAL_MMIO_ACMA_CREQO,
> > +					  &scm_data->admin_command);
> > +	if (rc)
> > +		return rc;
> > +
> > +	rc = scm_extract_command_metadata(scm_data,
> > GLOBAL_MMIO_NSCMA_CREQO,
> > +					  &scm_data->ns_command);
> > +	if (rc)
> > +		return rc;
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * scm_heartbeat() - Issue a heartbeat command to the controller
> > + * @scm_data: a pointer to the SCM device data
> > + * Return: 0 if the controller responded correctly, negative on
> > error
> > + */
> > +static int scm_heartbeat(struct scm_data *scm_data)
> > +{
> > +	int rc;
> > +
> > +	mutex_lock(&scm_data->admin_command.lock);
> > +
> > +	rc = scm_admin_command_request(scm_data,
> > ADMIN_COMMAND_HEARTBEAT);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = scm_admin_command_execute(scm_data);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = scm_admin_command_complete_timeout(scm_data,
> > ADMIN_COMMAND_HEARTBEAT);
> > +	if (rc < 0) {
> > +		dev_err(&scm_data->dev, "Heartbeat timeout\n");
> > +		goto out;
> > +	}
> > +
> > +	rc = scm_admin_response(scm_data);
> > +	if (rc < 0)
> > +		goto out;
> > +	if (rc != STATUS_SUCCESS)
> > +		scm_warn_status(scm_data, "Unexpected status from
> > heartbeat", rc);
> > +
> > +	rc = scm_admin_response_handled(scm_data);
> > +
> > +	goto out;
> > +
> > +out:
> > +	mutex_unlock(&scm_data->admin_command.lock);
> > +	return rc;
> > +}
> > +
> > +/**
> > + * scm_is_usable() - Is a controller usable?
> > + * @scm_data: a pointer to the SCM device data
> > + * Return: true if the controller is usable
> > + */
> > +static bool scm_is_usable(const struct scm_data *scm_data)
> > +{
> > +	if (!scm_controller_is_ready(scm_data)) {
> > +		dev_err(&scm_data->dev, "SCM controller is not
> > ready.\n");
> > +		return false;
> > +	}
> > +
> > +	if (!scm_is_memory_available(scm_data)) {
> > +		dev_err(&scm_data->dev,
> > +			"SCM controller does not have memory
> > available.\n");
> > +		return false;
> > +	}
> > +
> > +	return true;
> > +}
> > +
> > +/**
> > + * allocate_scm_minor() - Allocate a minor number to use for an
> > SCM device
> > + * @scm_data: The SCM device to associate the minor with
> > + * Return: the allocated minor number
> > + */
> > +static int allocate_scm_minor(struct scm_data *scm_data)
> > +{
> > +	int minor;
> > +
> > +	mutex_lock(&minors_idr_lock);
> > +	minor = idr_alloc(&minors_idr, scm_data, 0, SCM_NUM_MINORS,
> > GFP_KERNEL);
> > +	mutex_unlock(&minors_idr_lock);
> > +	return minor;
> > +}
> > +
> > +static void free_scm_minor(struct scm_data *scm_data)
> > +{
> > +	mutex_lock(&minors_idr_lock);
> > +	idr_remove(&minors_idr, MINOR(scm_data->dev.devt));
> > +	mutex_unlock(&minors_idr_lock);
> > +}
> > +
> > +/**
> > + * free_scm() - Free all members of an SCM struct
> > + * @scm_data: the SCM metadata to clear
> > + */
> > +static void free_scm(struct scm_data *scm_data)
> > +{
> > +	// Disable doorbells
> > +	(void)ocxl_global_mmio_set64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_CHIEC,
> > +				     OCXL_LITTLE_ENDIAN,
> > +				     GLOBAL_MMIO_CHI_ALL);
> > +
> > +	free_scm_minor(scm_data);
> > +
> > +	if (scm_data->irq_addr[1])
> > +		iounmap(scm_data->irq_addr[1]);
> > +
> > +	if (scm_data->irq_addr[0])
> > +		iounmap(scm_data->irq_addr[0]);
> > +
> > +	if (scm_data->cdev.owner)
> > +		cdev_del(&scm_data->cdev);
> > +
> > +	if (scm_data->metadata_addr)
> > +		devm_memunmap(&scm_data->dev, scm_data->metadata_addr);
> > +
> > +	if (scm_data->ocxl_context)
> > +		ocxl_context_free(scm_data->ocxl_context);
> > +
> > +	if (scm_data->ocxl_afu)
> > +		ocxl_afu_put(scm_data->ocxl_afu);
> > +
> > +	if (scm_data->ocxl_fn)
> > +		ocxl_function_close(scm_data->ocxl_fn);
> > +
> > +	kfree(scm_data);
> > +}
> > +
> > +/**
> > + * free_scm_dev - Free an SCM device
> > + * @dev: The device struct
> > + */
> > +static void free_scm_dev(struct device *dev)
> > +{
> > +	struct scm_data *scm_data = container_of(dev, struct scm_data,
> > dev);
> > +
> > +	free_scm(scm_data);
> > +}
> > +
> > +/**
> > + * scm_register - Register an SCM device with the kernel
> > + * @scm_data: the SCM metadata
> > + * Return: 0 on success, negative on failure
> > + */
> > +static int scm_register(struct scm_data *scm_data)
> > +{
> > +	int rc;
> > +	int minor = allocate_scm_minor(scm_data);
> > +
> > +	if (minor < 0)
> > +		return minor;
> > +
> > +	scm_data->dev.release = free_scm_dev;
> > +	rc = dev_set_name(&scm_data->dev, "scm%d", minor);
> > +	if (rc < 0)
> > +		return rc;
> > +
> > +	scm_data->dev.devt = MKDEV(MAJOR(scm_dev), minor);
> > +	scm_data->dev.class = scm_class;
> > +	scm_data->dev.parent = &scm_data->pdev->dev;
> > +
> > +	rc = device_register(&scm_data->dev);
> > +	return rc;
> > +}
> > +
> > +static void scm_put(struct scm_data *scm_data)
> > +{
> > +	put_device(&scm_data->dev);
> > +}
> > +
> > +struct scm_data *scm_get(struct scm_data *scm_data)
> > +{
> > +	return (get_device(&scm_data->dev) == NULL) ? NULL : scm_data;
> > +}
> > +
> > +static struct scm_data *find_and_get_scm(dev_t devno)
> > +{
> > +	struct scm_data *scm_data;
> > +	int minor = MINOR(devno);
> > +	/*
> > +	 * We don't declare an RCU critical section here, as our AFU
> > +	 * is protected by a reference counter on the device. By the
> > time the
> > +	 * minor number of a device is removed from the idr, the ref
> > count of
> > +	 * the device is already at 0, so no user API will access that
> > AFU and
> > +	 * this function can't return it.
> > +	 */
> > +	scm_data = idr_find(&minors_idr, minor);
> > +	if (scm_data)
> > +		scm_get(scm_data);
> > +	return scm_data;
> > +}
> > +
> > +static int scm_file_open(struct inode *inode, struct file *file)
> > +{
> > +	struct scm_data *scm_data;
> > +
> > +	scm_data = find_and_get_scm(inode->i_rdev);
> > +	if (!scm_data)
> > +		return -ENODEV;
> > +
> > +	file->private_data = scm_data;
> > +	return 0;
> > +}
> > +
> > +static int scm_file_release(struct inode *inode, struct file
> > *file)
> > +{
> > +	struct scm_data *scm_data = file->private_data;
> > +
> > +	if (scm_data->ev_ctx) {
> > +		eventfd_ctx_put(scm_data->ev_ctx);
> > +		scm_data->ev_ctx = NULL;
> > +	}
> > +
> > +	scm_put(scm_data);
> > +	return 0;
> > +}
> > +
> > +static int scm_ioctl_buffer_info(struct scm_data *scm_data,
> > +				 struct scm_ioctl_buffer_info __user
> > *uarg)
> > +{
> > +	struct scm_ioctl_buffer_info args;
> > +
> > +	args.admin_command_buffer_size = scm_data-
> > >admin_command.data_size;
> > +	args.near_storage_buffer_size = scm_data->ns_command.data_size;
> > +
> > +	if (copy_to_user(uarg, &args, sizeof(args)))
> > +		return -EFAULT;
> > +
> > +	return 0;
> > +}
> > +
> > +static int scm_error_log_offset_0x00(struct scm_data *scm_data,
> > u16 *length)
> > +{
> > +	int rc;
> > +	u64 val;
> > +
> > +	u16 data_identifier;
> > +	u32 data_length;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset,
> > +				     OCXL_LITTLE_ENDIAN, &val);
> > +	if (rc)
> > +		return rc;
> > +
> > +	data_identifier = val >> 48;
> > +	data_length = val & 0xFFFF;
> > +
> > +	if (data_identifier != 0x454C) {
> > +		dev_err(&scm_data->dev,
> > +			"Bad data identifier for error log data,
> > expected 'EL', got '%2s' (%#x), data_length=%u\n",
> > +			(char *)&data_identifier,
> > +			(unsigned int)data_identifier, data_length);
> > +		return -EFAULT;
> > +	}
> > +
> > +	*length = data_length;
> > +	return 0;
> > +}
> > +
> > +static int scm_error_log_offset_0x08(struct scm_data *scm_data,
> > +				     u32 *log_identifier, u32
> > *program_ref_code)
> > +{
> > +	int rc;
> > +	u64 val;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset + 0x08,
> > +				     OCXL_LITTLE_ENDIAN, &val);
> > +	if (rc)
> > +		return rc;
> > +
> > +	*log_identifier = val >> 32;
> > +	*program_ref_code = val & 0xFFFFFFFF;
> > +
> > +	return 0;
> > +}
> > +
> > +static int scm_read_error_log(struct scm_data *scm_data,
> > +			      struct scm_ioctl_error_log *log, bool
> > buf_is_user)
> > +{
> > +	u64 val;
> > +	u16 user_buf_length;
> > +	u16 buf_length;
> > +	u16 i;
> > +	int rc;
> > +
> > +	if (log->buf_size % 8)
> > +		return -EINVAL;
> > +
> > +	rc = scm_chi(scm_data, &val);
> > +	if (rc)
> > +		goto out;
> > +
> > +	if (!(val & GLOBAL_MMIO_CHI_ELA))
> > +		return -EAGAIN;
> > +
> > +	user_buf_length = log->buf_size;
> > +
> > +	mutex_lock(&scm_data->admin_command.lock);
> > +
> > +	rc = scm_admin_command_request(scm_data, ADMIN_COMMAND_ERRLOG);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = scm_admin_command_execute(scm_data);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = scm_admin_command_complete_timeout(scm_data,
> > ADMIN_COMMAND_ERRLOG);
> > +	if (rc < 0) {
> > +		dev_warn(&scm_data->dev, "Read error log timed out\n");
> > +		goto out;
> > +	}
> > +
> > +	rc = scm_admin_response(scm_data);
> > +	if (rc < 0)
> > +		goto out;
> > +	if (rc != STATUS_SUCCESS) {
> > +		scm_warn_status(scm_data, "Unexpected status from
> > retrieve error log", rc);
> > +		goto out;
> > +	}
> > +
> > +
> > +	rc = scm_error_log_offset_0x00(scm_data, &log->buf_size);
> > +	if (rc)
> > +		goto out;
> > +	// log->buf_size now contains the scm buffer size, not the user
> > size
> > +
> > +	rc = scm_error_log_offset_0x08(scm_data, &log->log_identifier,
> > +				       &log->program_reference_code);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset + 0x10,
> > +				     OCXL_LITTLE_ENDIAN, &val);
> > +	if (rc)
> > +		goto out;
> > +
> > +	log->error_log_type = val >> 56;
> > +	log->action_flags = (log->error_log_type ==
> > SCM_ERROR_LOG_TYPE_GENERAL) ?
> > +			    (val >> 32) & 0xFFFFFF : 0;
> > +	log->power_on_seconds = val & 0xFFFFFFFF;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset + 0x18,
> > +				     OCXL_LITTLE_ENDIAN, &log-
> > >timestamp);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset + 0x20,
> > +				     OCXL_HOST_ENDIAN, &log->wwid[0]);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset + 0x28,
> > +				     OCXL_HOST_ENDIAN, &log->wwid[1]);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset + 0x30,
> > +				     OCXL_HOST_ENDIAN, (u64 *)log-
> > >fw_revision);
> > +	if (rc)
> > +		goto out;
> > +	log->fw_revision[8] = '\0';
> > +
> > +	buf_length = (user_buf_length < log->buf_size) ?
> > +		     user_buf_length : log->buf_size;
> > +	for (i = 0; i < buf_length + 0x48; i += 8) {
> > +		u64 val;
> > +
> > +		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +					     scm_data-
> > >admin_command.data_offset + i,
> > +					     OCXL_HOST_ENDIAN, &val);
> > +		if (rc)
> > +			goto out;
> > +
> > +		if (buf_is_user) {
> > +			if (copy_to_user(&log->buf[i], &val,
> > sizeof(u64))) {
> > +				rc = -EFAULT;
> > +				goto out;
> > +			}
> > +		} else
> > +			log->buf[i] = val;
> > +	}
> > +
> > +	rc = scm_admin_response_handled(scm_data);
> > +	if (rc)
> > +		goto out;
> > +
> > +out:
> > +	mutex_unlock(&scm_data->admin_command.lock);
> > +	return rc;
> > +
> > +}
> > +
> > +static int scm_ioctl_error_log(struct scm_data *scm_data,
> > +			       struct scm_ioctl_error_log __user *uarg)
> > +{
> > +	struct scm_ioctl_error_log args;
> > +	int rc;
> > +
> > +	if (copy_from_user(&args, uarg, sizeof(args)))
> > +		return -EFAULT;
> > +
> > +	rc = scm_read_error_log(scm_data, &args, true);
> > +	if (rc)
> > +		return rc;
> > +
> > +	if (copy_to_user(uarg, &args, sizeof(args)))
> > +		return -EFAULT;
> > +
> > +	return 0;
> > +}
> > +
> > +static int scm_ioctl_controller_dump_data(struct scm_data
> > *scm_data,
> > +	struct scm_ioctl_controller_dump_data __user *uarg)
> > +{
> > +	struct scm_ioctl_controller_dump_data args;
> > +	u16 i;
> > +	u64 val;
> > +	int rc;
> > +
> > +	if (copy_from_user(&args, uarg, sizeof(args)))
> > +		return -EFAULT;
> > +
> > +	if (args.buf_size % 8)
> > +		return -EINVAL;
> > +
> > +	if (args.buf_size > scm_data->admin_command.data_size)
> > +		return -EINVAL;
> > +
> > +	mutex_lock(&scm_data->admin_command.lock);
> > +
> > +	rc = scm_admin_command_request(scm_data,
> > ADMIN_COMMAND_CONTROLLER_DUMP);
> > +	if (rc)
> > +		goto out;
> > +
> > +	val = ((u64)args.offset) << 32;
> > +	val |= args.buf_size;
> > +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> > +				      scm_data-
> > >admin_command.request_offset + 0x08,
> > +				      OCXL_LITTLE_ENDIAN, val);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = scm_admin_command_execute(scm_data);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = scm_admin_command_complete_timeout(scm_data,
> > +						ADMIN_COMMAND_CONTROLLE
> > R_DUMP);
> > +	if (rc < 0) {
> > +		dev_warn(&scm_data->dev, "Controller dump timed
> > out\n");
> > +		goto out;
> > +	}
> > +
> > +	rc = scm_admin_response(scm_data);
> > +	if (rc < 0)
> > +		goto out;
> > +	if (rc != STATUS_SUCCESS) {
> > +		scm_warn_status(scm_data,
> > +				"Unexpected status from retrieve error
> > log",
> > +				rc);
> > +		goto out;
> > +	}
> > +
> > +	for (i = 0; i < args.buf_size; i += 8) {
> > +		u64 val;
> > +
> > +		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +					     scm_data-
> > >admin_command.data_offset + i,
> > +					     OCXL_HOST_ENDIAN, &val);
> > +		if (rc)
> > +			goto out;
> > +
> > +		if (copy_to_user(&args.buf[i], &val, sizeof(u64))) {
> > +			rc = -EFAULT;
> > +			goto out;
> > +		}
> > +	}
> > +
> > +	if (copy_to_user(uarg, &args, sizeof(args))) {
> > +		rc = -EFAULT;
> > +		goto out;
> > +	}
> > +
> > +	rc = scm_admin_response_handled(scm_data);
> > +	if (rc)
> > +		goto out;
> > +
> > +out:
> > +	mutex_unlock(&scm_data->admin_command.lock);
> > +	return rc;
> > +}
> > +
> > +int scm_request_controller_dump(struct scm_data *scm_data)
> > +{
> > +	int rc;
> > +	u64 busy = 1;
> > +
> > +	rc = ocxl_global_mmio_set64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_CHIC,
> > +				    OCXL_LITTLE_ENDIAN,
> > +				    GLOBAL_MMIO_CHI_CDA);
> > +
> > +
> > +	rc = ocxl_global_mmio_set64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_HCI,
> > +				    OCXL_LITTLE_ENDIAN,
> > +				    GLOBAL_MMIO_HCI_CONTROLLER_DUMP);
> > +	if (rc)
> > +		return rc;
> > +
> > +	while (busy) {
> > +		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +					     GLOBAL_MMIO_HCI,
> > +					     OCXL_LITTLE_ENDIAN,
> > &busy);
> > +		if (rc)
> > +			return rc;
> > +
> > +		busy &= GLOBAL_MMIO_HCI_CONTROLLER_DUMP;
> > +		cond_resched();
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int scm_ioctl_controller_dump_complete(struct scm_data
> > *scm_data)
> > +{
> > +	int rc;
> > +
> > +	rc = ocxl_global_mmio_set64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_HCI,
> > +				    OCXL_LITTLE_ENDIAN,
> > +				    GLOBAL_MMIO_HCI_CONTROLLER_DUMP_COL
> > LECTED);
> > +
> > +	if (rc)
> > +		return -EFAULT;
> > +
> > +	return 0;
> > +}
> > +
> > +static int scm_controller_stats_offset_0x00(struct scm_data
> > *scm_data,
> > +	u32 *length)
> > +{
> > +	int rc;
> > +	u64 val;
> > +
> > +	u16 data_identifier;
> > +	u32 data_length;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset,
> > +				     OCXL_LITTLE_ENDIAN, &val);
> > +	if (rc)
> > +		return rc;
> > +
> > +	data_identifier = val >> 48;
> > +	data_length = val & 0xFFFFFFFF;
> > +
> > +	if (data_identifier != 0x4353) {
> > +		dev_err(&scm_data->dev,
> > +			"Bad data identifier for controller stats,
> > expected 'CS', got '%-.*s'\n",
> > +			2, (char *)&data_identifier);
> > +		return -EFAULT;
> > +	}
> > +
> > +	*length = data_length;
> > +	return 0;
> > +}
> > +
> > +static int scm_ioctl_controller_stats(struct scm_data *scm_data,
> > +				      struct scm_ioctl_controller_stats
> > __user *uarg)
> > +{
> > +	struct scm_ioctl_controller_stats args;
> > +	u32 length;
> > +	int rc;
> > +	u64 val;
> > +
> > +	memset(&args, '\0', sizeof(args));
> > +
> > +	mutex_lock(&scm_data->admin_command.lock);
> > +
> > +	rc = scm_admin_command_request(scm_data,
> > ADMIN_COMMAND_CONTROLLER_STATS);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> > +				      scm_data-
> > >admin_command.request_offset + 0x08,
> > +				      OCXL_LITTLE_ENDIAN, 0);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = scm_admin_command_execute(scm_data);
> > +	if (rc)
> > +		goto out;
> > +
> > +
> > +	rc = scm_admin_command_complete_timeout(scm_data,
> > +						ADMIN_COMMAND_CONTROLLE
> > R_STATS);
> > +	if (rc < 0) {
> > +		dev_warn(&scm_data->dev, "Controller stats timed
> > out\n");
> > +		goto out;
> > +	}
> > +
> > +	rc = scm_admin_response(scm_data);
> > +	if (rc < 0)
> > +		goto out;
> > +	if (rc != STATUS_SUCCESS) {
> > +		scm_warn_status(scm_data,
> > +				"Unexpected status from controller
> > stats", rc);
> > +		goto out;
> > +	}
> > +
> > +	rc = scm_controller_stats_offset_0x00(scm_data, &length);
> > +	if (rc)
> > +		goto out;
> > +
> > +	if (length != 0x140)
> > +		scm_warn_status(scm_data,
> > +				"Unexpected length for controller stats
> > data, expected 0x140, got 0x%x",
> > +				length);
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset + 0x08 + 0x08,
> > +				     OCXL_LITTLE_ENDIAN, &val);
> > +	if (rc)
> > +		goto out;
> > +
> > +	args.reset_count = val >> 32;
> > +	args.reset_uptime = val & 0xFFFFFFFF;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset + 0x08 + 0x10,
> > +				     OCXL_LITTLE_ENDIAN, &val);
> > +	if (rc)
> > +		goto out;
> > +
> > +	args.power_on_uptime = val >> 32;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset + 0x08 + 0x40 + 0x08,
> > +				     OCXL_LITTLE_ENDIAN,
> > &args.host_load_count);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset + 0x08 + 0x40 + 0x10,
> > +				     OCXL_LITTLE_ENDIAN,
> > &args.host_store_count);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset + 0x08 + 0x40 + 0x18,
> > +				     OCXL_LITTLE_ENDIAN,
> > &args.media_read_count);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset + 0x08 + 0x40 + 0x20,
> > +				     OCXL_LITTLE_ENDIAN,
> > &args.media_write_count);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset + 0x08 + 0x40 + 0x28,
> > +				     OCXL_LITTLE_ENDIAN,
> > &args.cache_hit_count);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset + 0x08 + 0x40 + 0x30,
> > +				     OCXL_LITTLE_ENDIAN,
> > &args.cache_miss_count);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset + 0x08 + 0x40 + 0x38,
> > +				     OCXL_LITTLE_ENDIAN,
> > &args.media_read_latency);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset + 0x08 + 0x40 + 0x40,
> > +				     OCXL_LITTLE_ENDIAN,
> > &args.media_write_latency);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset + 0x08 + 0x40 + 0x48,
> > +				     OCXL_LITTLE_ENDIAN,
> > &args.cache_read_latency);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +				     scm_data-
> > >admin_command.data_offset + 0x08 + 0x40 + 0x50,
> > +				     OCXL_LITTLE_ENDIAN,
> > &args.cache_write_latency);
> > +	if (rc)
> > +		goto out;
> > +
> > +	if (copy_to_user(uarg, &args, sizeof(args))) {
> > +		rc = -EFAULT;
> > +		goto out;
> > +	}
> > +
> > +	rc = scm_admin_response_handled(scm_data);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = 0;
> > +	goto out;
> > +
> > +out:
> > +	mutex_unlock(&scm_data->admin_command.lock);
> > +	return rc;
> > +}
> > +
> > +static int scm_ioctl_eventfd(struct scm_data *scm_data,
> > +			     struct scm_ioctl_eventfd __user *uarg)
> > +{
> > +	struct scm_ioctl_eventfd args;
> > +
> > +	if (copy_from_user(&args, uarg, sizeof(args)))
> > +		return -EFAULT;
> > +
> > +	if (scm_data->ev_ctx)
> > +		return -EFAULT;
> > +
> > +	scm_data->ev_ctx = eventfd_ctx_fdget(args.eventfd);
> > +	if (!scm_data->ev_ctx)
> > +		return -EFAULT;
> > +
> > +	return 0;
> > +}
> > +
> > +static int scm_ioctl_event_check(struct scm_data *scm_data, u64
> > __user *uarg)
> > +{
> > +	u64 val = 0;
> > +	int rc;
> > +	u64 chi = 0;
> > +
> > +	rc = scm_chi(scm_data, &chi);
> > +	if (rc < 0)
> > +		return -EFAULT;
> > +
> > +	if (chi & GLOBAL_MMIO_CHI_ELA)
> > +		val |= SCM_IOCTL_EVENT_ERROR_LOG_AVAILABLE;
> > +
> > +	if (chi & GLOBAL_MMIO_CHI_CDA)
> > +		val |= SCM_IOCTL_EVENT_CONTROLLER_DUMP_AVAILABLE;
> > +
> > +	if (chi & GLOBAL_MMIO_CHI_CFFS)
> > +		val |= SCM_IOCTL_EVENT_FIRMWARE_FATAL;
> > +
> > +	if (chi & GLOBAL_MMIO_CHI_CHFS)
> > +		val |= SCM_IOCTL_EVENT_HARDWARE_FATAL;
> > +
> > +	rc = copy_to_user((u64 __user *) uarg, &val, sizeof(val));
> > +
> > +	return rc;
> > +}
> > +
> > +/**
> > + * scm_req_controller_health_perf() - Request controller health &
> > performance data
> > + * @scm_data: the SCM metadata
> > + * Return: 0 on success, negative on failure
> > + */
> > +int scm_req_controller_health_perf(struct scm_data *scm_data)
> > +{
> > +	return ocxl_global_mmio_set64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_HCI,
> > +				      OCXL_LITTLE_ENDIAN,
> > +				      GLOBAL_MMIO_HCI_REQ_HEALTH_PERF);
> > +}
> > +
> > +#ifdef CONFIG_OCXL_SCM_DEBUG
> > +/**
> > + * scm_enable_fwdebug() - Enable FW debug on the controller
> > + * @scm_data: a pointer to the SCM device data
> > + * Return: 0 on success, negative on failure
> > + */
> > +static int scm_enable_fwdebug(const struct scm_data *scm_data)
> > +{
> > +	return ocxl_global_mmio_set64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_HCI,
> > +				      OCXL_LITTLE_ENDIAN,
> > +				      GLOBAL_MMIO_HCI_FW_DEBUG);
> > +}
> > +
> > +/**
> > + * scm_disable_fwdebug() - Disable FW debug on the controller
> > + * @scm_data: a pointer to the SCM device data
> > + * Return: 0 on success, negative on failure
> > + */
> > +static int scm_disable_fwdebug(const struct scm_data *scm_data)
> > +{
> > +	return ocxl_global_mmio_set64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_HCIC,
> > +				      OCXL_LITTLE_ENDIAN,
> > +				      GLOBAL_MMIO_HCI_FW_DEBUG);
> > +}
> > +
> > +static int scm_ioctl_fwdebug(struct scm_data *scm_data,
> > +			     struct scm_ioctl_fwdebug __user *uarg)
> > +{
> > +	struct scm_ioctl_fwdebug args;
> > +	u64 val;
> > +	int i;
> > +	int rc;
> > +
> > +	if (copy_from_user(&args, uarg, sizeof(args)))
> > +		return -EFAULT;
> > +
> > +	// Buffer size must be a multiple of 8
> > +	if ((args.buf_size & 0x07))
> > +		return -EINVAL;
> > +
> > +	if (args.buf_size > scm_data->admin_command.data_size)
> > +		return -EINVAL;
> > +
> > +	mutex_lock(&scm_data->admin_command.lock);
> > +
> > +	rc = scm_enable_fwdebug(scm_data);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = scm_admin_command_request(scm_data,
> > ADMIN_COMMAND_FW_DEBUG);
> > +	if (rc)
> > +		goto out;
> > +
> > +	// Write DebugAction & FunctionCode
> > +	val = ((u64)args.debug_action << 56) | ((u64)args.function_code
> > << 40);
> > +
> > +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> > +				      scm_data-
> > >admin_command.request_offset + 0x08,
> > +				      OCXL_LITTLE_ENDIAN, val);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> > +				      scm_data-
> > >admin_command.request_offset + 0x10,
> > +				      OCXL_LITTLE_ENDIAN,
> > args.debug_parameter_1);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> > +				      scm_data-
> > >admin_command.request_offset + 0x18,
> > +				      OCXL_LITTLE_ENDIAN,
> > args.debug_parameter_2);
> > +	if (rc)
> > +		goto out;
> > +
> > +	for (i = 0x20; i < 0x38; i += 0x08)
> > +		rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> > +					      scm_data-
> > >admin_command.request_offset + i,
> > +					      OCXL_LITTLE_ENDIAN, 0);
> > +	if (rc)
> > +		goto out;
> > +
> > +
> > +	// Populate admin command buffer
> > +	if (args.buf_size) {
> > +		for (i = 0; i < args.buf_size; i += sizeof(u64)) {
> > +			u64 val;
> > +
> > +			if (copy_from_user(&val, &args.buf[i],
> > sizeof(u64)))
> > +				return -EFAULT;
> > +
> > +			rc = ocxl_global_mmio_write64(scm_data-
> > >ocxl_afu,
> > +						      scm_data-
> > >admin_command.data_offset + i,
> > +						      OCXL_HOST_ENDIAN,
> > val);
> > +			if (rc)
> > +				goto out;
> > +		}
> > +	}
> > +
> > +	rc = scm_admin_command_execute(scm_data);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = scm_admin_command_complete_timeout(scm_data,
> > +						scm_data-
> > >timeouts[ADMIN_COMMAND_FW_DEBUG]);
> > +	if (rc < 0)
> > +		goto out;
> > +
> > +	rc = scm_admin_response(scm_data);
> > +	if (rc < 0)
> > +		goto out;
> > +	if (rc != STATUS_SUCCESS) {
> > +		scm_warn_status(scm_data, "Unexpected status from FW
> > Debug", rc);
> > +		goto out;
> > +	}
> > +
> > +	if (args.buf_size) {
> > +		for (i = 0; i < args.buf_size; i += sizeof(u64)) {
> > +			u64 val;
> > +
> > +			rc = ocxl_global_mmio_read64(scm_data-
> > >ocxl_afu,
> > +						     scm_data-
> > >admin_command.data_offset + i,
> > +						     OCXL_HOST_ENDIAN,
> > &val);
> > +			if (rc)
> > +				goto out;
> > +
> > +			if (copy_to_user(&args.buf[i], &val,
> > sizeof(u64))) {
> > +				rc = -EFAULT;
> > +				goto out;
> > +			}
> > +		}
> > +	}
> > +
> > +	rc = scm_admin_response_handled(scm_data);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = scm_disable_fwdebug(scm_data);
> > +	if (rc)
> > +		goto out;
> > +
> > +out:
> > +	mutex_unlock(&scm_data->admin_command.lock);
> > +	return rc;
> > +}
> > +
> > +static int scm_ioctl_shutdown(struct scm_data *scm_data)
> > +{
> > +	int rc;
> > +
> > +	mutex_lock(&scm_data->admin_command.lock);
> > +
> > +	rc = scm_admin_command_request(scm_data,
> > ADMIN_COMMAND_SHUTDOWN);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = scm_admin_command_execute(scm_data);
> > +	if (rc)
> > +		goto out;
> > +
> > +	rc = scm_admin_command_complete_timeout(scm_data,
> > ADMIN_COMMAND_SHUTDOWN);
> > +	if (rc < 0) {
> > +		dev_warn(&scm_data->dev, "Shutdown timed out\n");
> > +		goto out;
> > +	}
> > +
> > +	rc = 0;
> > +	goto out;
> > +
> > +out:
> > +	mutex_unlock(&scm_data->admin_command.lock);
> > +	return rc;
> > +}
> > +
> > +static int scm_ioctl_mmio_write(struct scm_data *scm_data,
> > +				struct scm_ioctl_mmio __user *uarg)
> > +{
> > +	struct scm_ioctl_mmio args;
> > +
> > +	if (copy_from_user(&args, uarg, sizeof(args)))
> > +		return -EFAULT;
> > +
> > +	return ocxl_global_mmio_write64(scm_data->ocxl_afu,
> > args.address,
> > +					OCXL_LITTLE_ENDIAN, args.val);
> > +}
> > +
> > +static int scm_ioctl_mmio_read(struct scm_data *scm_data,
> > +			       struct scm_ioctl_mmio __user *uarg)
> > +{
> > +	struct scm_ioctl_mmio args;
> > +	int rc;
> > +
> > +	if (copy_from_user(&args, uarg, sizeof(args)))
> > +		return -EFAULT;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu, args.address,
> > +				     OCXL_LITTLE_ENDIAN, &args.val);
> > +	if (rc)
> > +		return rc;
> > +
> > +	if (copy_to_user(uarg, &args, sizeof(args)))
> > +		return -EFAULT;
> > +
> > +	return 0;
> > +}
> > +#else
> > +static int scm_ioctl_fwdebug(struct scm_data *scm_data,
> > +			     struct scm_ioctl_fwdebug __user *uarg)
> > +{
> > +	return -EPERM;
> > +}
> > +
> > +static int scm_ioctl_shutdown(struct scm_data *scm_data)
> > +{
> > +	return -EPERM;
> > +}
> > +
> > +static int scm_ioctl_mmio_write(struct scm_data *scm_data,
> > +				struct scm_ioctl_mmio __user *uarg)
> > +{
> > +	return -EPERM;
> > +}
> > +
> > +static int scm_ioctl_mmio_read(struct scm_data *scm_data,
> > +			       struct scm_ioctl_mmio __user *uarg)
> > +{
> > +	return -EPERM;
> > +}
> > +#endif
> > +
> > +static long scm_file_ioctl(struct file *file, unsigned int cmd,
> > +			   unsigned long args)
> > +{
> > +	struct scm_data *scm_data = file->private_data;
> > +	int rc = -EINVAL;
> > +
> > +	switch (cmd) {
> > +	case SCM_IOCTL_BUFFER_INFO:
> > +		rc = scm_ioctl_buffer_info(scm_data,
> > +					   (struct
> > scm_ioctl_buffer_info __user *)args);
> > +		break;
> > +
> > +	case SCM_IOCTL_ERROR_LOG:
> > +		rc = scm_ioctl_error_log(scm_data,
> > +					 (struct scm_ioctl_error_log
> > __user *)args);
> > +		break;
> > +
> > +	case SCM_IOCTL_CONTROLLER_DUMP:
> > +		rc = scm_request_controller_dump(scm_data);
> > +		break;
> > +
> > +	case SCM_IOCTL_CONTROLLER_DUMP_DATA:
> > +		rc = scm_ioctl_controller_dump_data(scm_data,
> > +						    (struct
> > scm_ioctl_controller_dump_data __user *)args);
> > +		break;
> > +
> > +	case SCM_IOCTL_CONTROLLER_DUMP_COMPLETE:
> > +		rc = scm_ioctl_controller_dump_complete(scm_data);
> > +		break;
> > +
> > +	case SCM_IOCTL_CONTROLLER_STATS:
> > +		rc = scm_ioctl_controller_stats(scm_data,
> > +						(struct
> > scm_ioctl_controller_stats __user *)args);
> > +		break;
> > +
> > +	case SCM_IOCTL_EVENTFD:
> > +		rc = scm_ioctl_eventfd(scm_data,
> > +				       (struct scm_ioctl_eventfd __user
> > *)args);
> > +		break;
> > +
> > +	case SCM_IOCTL_EVENT_CHECK:
> > +		rc = scm_ioctl_event_check(scm_data, (u64 __user
> > *)args);
> > +		break;
> > +
> > +	case SCM_IOCTL_REQUEST_HEALTH:
> > +		rc = scm_req_controller_health_perf(scm_data);
> > +		break;
> > +
> > +	case SCM_IOCTL_FWDEBUG:
> > +		rc = scm_ioctl_fwdebug(scm_data,
> > +				       (struct scm_ioctl_fwdebug __user
> > *)args);
> > +		break;
> > +
> > +	case SCM_IOCTL_SHUTDOWN:
> > +		rc = scm_ioctl_shutdown(scm_data);
> > +		break;
> > +
> > +	case SCM_IOCTL_MMIO_WRITE:
> > +		rc = scm_ioctl_mmio_write(scm_data,
> > +					  (struct scm_ioctl_mmio __user
> > *)args);
> > +		break;
> > +
> > +	case SCM_IOCTL_MMIO_READ:
> > +		rc = scm_ioctl_mmio_read(scm_data,
> > +					 (struct scm_ioctl_mmio __user
> > *)args);
> > +		break;
> > +
> > +	}
> > +
> > +	return rc;
> > +}
> > +
> > +static const struct file_operations scm_fops = {
> > +	.owner		= THIS_MODULE,
> > +	.open	   = scm_file_open,
> > +	.release	= scm_file_release,
> > +	.unlocked_ioctl = scm_file_ioctl,
> > +	.compat_ioctl   = scm_file_ioctl,
> > +};
> > +
> > +/**
> > + * scm_create_cdev() - Create the chardev in /dev for this scm
> > device
> > + * @scm_data: the SCM metadata
> > + * Return: 0 on success, negative on failure
> > + */
> > +static int scm_create_cdev(struct scm_data *scm_data)
> > +{
> > +	int rc;
> > +
> > +	cdev_init(&scm_data->cdev, &scm_fops);
> > +	rc = cdev_add(&scm_data->cdev, scm_data->dev.devt, 1);
> > +	if (rc) {
> > +		dev_err(&scm_data->dev, "Unable to add afu char device:
> > %d\n", rc);
> > +		return rc;
> > +	}
> > +	return 0;
> > +}
> > +
> > +/**
> > + * scm_remove() - Free an OpenCAPI Storage Class Memory device
> > + * @pdev: the PCI device information struct
> > + */
> > +static void scm_remove(struct pci_dev *pdev)
> > +{
> > +	if (PCI_FUNC(pdev->devfn) == 0) {
> > +		struct scm_function_0 *scm_func_0 =
> > pci_get_drvdata(pdev);
> > +
> > +		if (scm_func_0) {
> > +			ocxl_function_close(scm_func_0->ocxl_fn);
> > +			scm_func_0->ocxl_fn = NULL;
> > +		}
> > +	} else {
> > +		struct scm_data *scm_data = pci_get_drvdata(pdev);
> > +
> > +		if (scm_data) {
> > +			if (scm_data->nvdimm_bus)
> > +				nvdimm_bus_unregister(scm_data-
> > >nvdimm_bus);
> > +
> > +			device_unregister(&scm_data->dev);
> > +		}
> > +	}
> > +}
> > +
> > +/**
> > + * scm_setup_device_metadata() - Retrieve config information from
> > the AFU and save it for future use
> > + * @scm_data: the SCM metadata
> > + * Return: 0 on success, negative on failure
> > + */
> > +static int scm_setup_device_metadata(struct scm_data *scm_data)
> > +{
> > +	u64 val;
> > +	int rc;
> > +	int i;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_CCAP0,
> > +				     OCXL_LITTLE_ENDIAN, &val);
> > +	if (rc)
> > +		return rc;
> > +
> > +	scm_data->scm_revision = val & 0xFFFF;
> > +	scm_data->read_latency = (val >> 32) & 0xFF;
> > +	scm_data->readiness_timeout = val >> 48;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_CCAP1,
> > +				     OCXL_LITTLE_ENDIAN, &val);
> > +	if (rc)
> > +		return rc;
> > +
> > +	scm_data->max_controller_dump_size = val & 0xFFFFFFFF;
> > +
> > +	rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_FWVER,
> > +				     OCXL_LITTLE_ENDIAN, &val);
> > +	if (rc)
> > +		return rc;
> > +
> > +	for (i = 0; i < 8; i++)
> > +		scm_data->fw_version[i] = (val >> (i * 8)) & 0xff;
> > +
> > +	scm_data->fw_version[8] = '\0';
> > +
> > +	dev_info(&scm_data->dev,
> > +		 "Firmware version '%s' SCM revision %d:%d\n",
> > scm_data->fw_version,
> > +		 scm_data->scm_revision >> 4, scm_data->scm_revision &
> > 0x0F);
> > +
> > +	return 0;
> > +}
> > +
> > +static const char *scm_decode_error_log_type(u8 error_log_type)
> > +{
> > +	switch (error_log_type) {
> > +	case 0x00:
> > +		return "general";
> > +	case 0x01:
> > +		return "predictive failure";
> > +	case 0x02:
> > +		return "thermal warning";
> > +	case 0x03:
> > +		return "data loss";
> > +	case 0x04:
> > +		return "health & performance";
> > +	default:
> > +		return "unknown";
> > +	}
> > +}
> > +
> > +static void scm_dump_error_log(struct scm_data *scm_data)
> > +{
> > +	struct scm_ioctl_error_log log;
> > +	u32 buf_size;
> > +	u8 *buf;
> > +	int rc;
> > +
> > +	if (scm_data->admin_command.data_size == 0)
> > +		return;
> > +
> > +	buf_size = scm_data->admin_command.data_size - 0x48;
> > +	buf = kzalloc(buf_size, GFP_KERNEL);
> > +	if (!buf)
> > +		return;
> > +
> > +	log.buf = buf;
> > +	log.buf_size = buf_size;
> > +
> > +	rc = scm_read_error_log(scm_data, &log, false);
> > +	if (rc < 0)
> > +		goto out;
> > +
> > +	dev_warn(&scm_data->dev,
> > +		 "SCM Error log: WWID=0x%016llx%016llx LID=0x%x PRC=%x
> > type=0x%x %s, Uptime=%u seconds timestamp=0x%llx\n",
> > +		 log.wwid[0], log.wwid[1],
> > +		 log.log_identifier, log.program_reference_code,
> > +		 log.error_log_type,
> > +		 scm_decode_error_log_type(log.error_log_type),
> > +		 log.power_on_seconds, log.timestamp);
> > +	print_hex_dump(KERN_WARNING, "buf", DUMP_PREFIX_OFFSET, 16, 1,
> > buf,
> > +		       log.buf_size, false);
> > +
> > +out:
> > +	kfree(buf);
> > +}
> > +
> > +static void scm_handle_nscra_doorbell(struct scm_data *scm_data)
> > +{
> > +	int rc;
> > +
> > +	if (scm_data->ns_command.op_code == NS_COMMAND_SECURE_ERASE) {
> > +		u64 success, attempted;
> > +
> > +
> > +		rc = scm_ns_response(scm_data);
> > +		if (rc < 0) {
> > +			scm_data->overwrite_state =
> > SCM_OVERWRITE_FAILED;
> > +			mutex_unlock(&scm_data->ns_command.lock);
> > +			return;
> > +		}
> > +		if (rc != STATUS_SUCCESS)
> > +			scm_warn_status(scm_data, "Unexpected status
> > from overwrite", rc);
> > +
> > +		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +					     scm_data-
> > >ns_command.response_offset +
> > +					     NS_RESPONSE_SECURE_ERASE_A
> > CCESSIBLE_SUCCESS,
> > +					     OCXL_HOST_ENDIAN,
> > &success);
> > +		if (rc) {
> > +			scm_data->overwrite_state =
> > SCM_OVERWRITE_FAILED;
> > +			mutex_unlock(&scm_data->ns_command.lock);
> > +			return;
> > +		}
> > +
> > +		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +					     scm_data-
> > >ns_command.response_offset +
> > +					     NS_RESPONSE_SECURE_ERASE_A
> > CCESSIBLE_ATTEMPTED,
> > +					     OCXL_HOST_ENDIAN,
> > &attempted);
> > +		if (rc) {
> > +			scm_data->overwrite_state =
> > SCM_OVERWRITE_FAILED;
> > +			mutex_unlock(&scm_data->ns_command.lock);
> > +			return;
> > +		}
> > +
> > +		scm_data->overwrite_state = SCM_OVERWRITE_SUCCESS;
> > +		if (success != attempted)
> > +			scm_data->overwrite_state =
> > SCM_OVERWRITE_FAILED;
> > +
> > +		dev_info(&scm_data->dev,
> > +			 "Overwritten %llu/%llu accessible pages",
> > success, attempted);
> > +
> > +		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +					     scm_data-
> > >ns_command.response_offset +
> > +					     NS_RESPONSE_SECURE_ERASE_D
> > EFECTIVE_SUCCESS,
> > +					     OCXL_HOST_ENDIAN,
> > &success);
> > +		if (rc) {
> > +			scm_data->overwrite_state =
> > SCM_OVERWRITE_FAILED;
> > +			mutex_unlock(&scm_data->ns_command.lock);
> > +			return;
> > +		}
> > +
> > +		rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +					     scm_data-
> > >ns_command.response_offset +
> > +					     NS_RESPONSE_SECURE_ERASE_D
> > EFECTIVE_ATTEMPTED,
> > +					     OCXL_HOST_ENDIAN,
> > &attempted);
> > +		if (rc) {
> > +			scm_data->overwrite_state =
> > SCM_OVERWRITE_FAILED;
> > +			mutex_unlock(&scm_data->ns_command.lock);
> > +			return;
> > +		}
> > +
> > +		if (success != attempted)
> > +			scm_data->overwrite_state =
> > SCM_OVERWRITE_FAILED;
> > +
> > +		dev_info(&scm_data->dev,
> > +			 "Overwritten %llu/%llu defective pages",
> > success, attempted);
> > +
> > +		scm_ns_response_handled(scm_data);
> > +
> > +		mutex_unlock(&scm_data->ns_command.lock);
> > +		return;
> > +	}
> > +}
> > +
> > +static irqreturn_t scm_imn0_handler(void *private)
> > +{
> > +	struct scm_data *scm_data = private;
> > +	int rc;
> > +	u64 chi = 0;
> > +
> > +	rc = scm_chi(scm_data, &chi);
> > +	if (rc < 0)
> > +		return IRQ_NONE;
> > +
> > +	if (chi & GLOBAL_MMIO_CHI_NSCRA)
> > +		scm_handle_nscra_doorbell(scm_data);
> > +
> > +	if (chi & GLOBAL_MMIO_CHI_ELA) {
> > +		dev_warn(&scm_data->dev, "Error log is available\n");
> > +
> > +		if (scm_data->ev_ctx)
> > +			eventfd_signal(scm_data->ev_ctx, 1);
> > +	}
> > +
> > +	if (chi & GLOBAL_MMIO_CHI_CDA) {
> > +		dev_warn(&scm_data->dev, "Controller dump is
> > available\n");
> > +
> > +		if (scm_data->ev_ctx)
> > +			eventfd_signal(scm_data->ev_ctx, 1);
> > +	}
> > +
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static irqreturn_t scm_imn1_handler(void *private)
> > +{
> > +	struct scm_data *scm_data = private;
> > +	u64 chi = 0;
> > +
> > +	(void)scm_chi(scm_data, &chi);
> > +
> > +	if (chi & (GLOBAL_MMIO_CHI_CFFS | GLOBAL_MMIO_CHI_CHFS)) {
> > +		dev_err(&scm_data->dev,
> > +			"Controller status is fatal, chi=0x%llx, going
> > offline\n", chi);
> > +
> > +		if (scm_data->nvdimm_bus) {
> > +			nvdimm_bus_unregister(scm_data->nvdimm_bus);
> > +			scm_data->nvdimm_bus = NULL;
> > +		}
> > +
> > +		if (scm_data->ev_ctx)
> > +			eventfd_signal(scm_data->ev_ctx, 1);
> > +	}
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +
> > +/**
> > + * scm_setup_irq() - Set up the IRQs for the SCM device
> > + * @scm_data: the SCM metadata
> > + * Return: 0 on success, negative on failure
> > + */
> > +static int scm_setup_irq(struct scm_data *scm_data)
> > +{
> > +	int rc;
> > +	u64 irq_addr;
> > +
> > +	rc = ocxl_afu_irq_alloc(scm_data->ocxl_context, &scm_data-
> > >irq_id[0]);
> > +	if (rc)
> > +		return rc;
> > +
> > +	rc = ocxl_irq_set_handler(scm_data->ocxl_context, scm_data-
> > >irq_id[0],
> > +				  scm_imn0_handler, NULL, scm_data);
> > +
> > +	irq_addr = ocxl_afu_irq_get_addr(scm_data->ocxl_context,
> > scm_data->irq_id[0]);
> > +	if (!irq_addr)
> > +		return -EFAULT;
> > +
> > +	scm_data->irq_addr[0] = ioremap(irq_addr, PAGE_SIZE);
> > +	if (!scm_data->irq_addr[0])
> > +		return -EINVAL;
> > +
> > +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_IMA0_OHP,
> > +				      OCXL_LITTLE_ENDIAN,
> > +				      (u64)scm_data->irq_addr[0]);
> > +	if (rc)
> > +		goto out_irq0;
> > +
> > +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_IMA0_CFP,
> > +				      OCXL_LITTLE_ENDIAN, 0);
> > +	if (rc)
> > +		goto out_irq0;
> > +
> > +	rc = ocxl_afu_irq_alloc(scm_data->ocxl_context, &scm_data-
> > >irq_id[1]);
> > +	if (rc)
> > +		goto out_irq0;
> > +
> > +
> > +	rc = ocxl_irq_set_handler(scm_data->ocxl_context, scm_data-
> > >irq_id[1],
> > +				  scm_imn1_handler, NULL, scm_data);
> > +	if (rc)
> > +		goto out_irq0;
> > +
> > +	irq_addr = ocxl_afu_irq_get_addr(scm_data->ocxl_context,
> > scm_data->irq_id[1]);
> > +	if (!irq_addr)
> > +		goto out_irq0;
> > +
> > +	scm_data->irq_addr[1] = ioremap(irq_addr, PAGE_SIZE);
> > +	if (!scm_data->irq_addr[1])
> > +		goto out_irq0;
> > +
> > +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_IMA1_OHP,
> > +				      OCXL_LITTLE_ENDIAN,
> > +				      (u64)scm_data->irq_addr[1]);
> > +	if (rc)
> > +		goto out_irq1;
> > +
> > +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_IMA1_CFP,
> > +				      OCXL_LITTLE_ENDIAN, 0);
> > +	if (rc)
> > +		goto out_irq1;
> > +
> > +	// Enable doorbells
> > +	rc = ocxl_global_mmio_set64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_CHIE,
> > +				    OCXL_LITTLE_ENDIAN,
> > +				    GLOBAL_MMIO_CHI_ELA |
> > GLOBAL_MMIO_CHI_CDA |
> > +				    GLOBAL_MMIO_CHI_CFFS |
> > GLOBAL_MMIO_CHI_CHFS |
> > +				    GLOBAL_MMIO_CHI_NSCRA);
> > +	if (rc)
> > +		goto out_irq1;
> > +
> > +	return 0;
> > +
> > +out_irq1:
> > +	iounmap(scm_data->irq_addr[1]);
> > +	scm_data->irq_addr[1] = NULL;
> > +
> > +out_irq0:
> > +	iounmap(scm_data->irq_addr[0]);
> > +	scm_data->irq_addr[0] = NULL;
> > +
> > +	return rc;
> > +}
> > +
> > +/**
> > + * scm_probe_function_0 - Set up function 0 for an OpenCAPI
> > Storage Class Memory device
> > + * This is important as it enables higher than 0 across all other
> > functions,
> > + * which in turn enables higher bandwidth accesses
> > + * @pdev: the PCI device information struct
> > + * Return: 0 on success, negative on failure
> > + */
> > +static int scm_probe_function_0(struct pci_dev *pdev)
> > +{
> > +	struct scm_function_0 *scm_func_0 = NULL;
> > +
> > +	scm_func_0 = kzalloc(sizeof(*scm_func_0), GFP_KERNEL);
> > +	if (!scm_func_0)
> > +		return -ENOMEM;
> > +
> > +	scm_func_0->pdev = pdev;
> > +	scm_func_0->ocxl_fn = ocxl_function_open(pdev);
> > +	if (IS_ERR(scm_func_0->ocxl_fn)) {
> > +		kfree(scm_func_0);
> > +		dev_err(&pdev->dev, "failed to open OCXL function\n");
> > +		return -EFAULT;
> > +	}
> > +
> > +	pci_set_drvdata(pdev, scm_func_0);
> > +
> > +	return 0;
> > +}
> > +
> > +/**
> > + * scm_probe - Init an OpenCAPI Storage Class Memory device
> > + * @pdev: the PCI device information struct
> > + * @ent: The entry from scm_pci_tbl
> > + * Return: 0 on success, negative on failure
> > + */
> > +static int scm_probe(struct pci_dev *pdev, const struct
> > pci_device_id *ent)
> > +{
> > +	struct scm_data *scm_data = NULL;
> > +	int elapsed;
> > +	u64 chi;
> > +
> > +	if (PCI_FUNC(pdev->devfn) == 0)
> > +		return scm_probe_function_0(pdev);
> > +	else if (PCI_FUNC(pdev->devfn) != 1)
> > +		return 0;
> > +
> > +	scm_data = kzalloc(sizeof(*scm_data), GFP_KERNEL);
> > +	if (!scm_data)
> > +		goto err;
> > +	scm_data->pdev = pdev;
> > +	mutex_init(&scm_data->admin_command.lock);
> > +	mutex_init(&scm_data->ns_command.lock);
> > +
> > +
> > +	scm_data->timeouts[ADMIN_COMMAND_ERRLOG] = 2000; // ms
> > +	scm_data->timeouts[ADMIN_COMMAND_HEARTBEAT] = 100; // ms
> > +	scm_data->timeouts[ADMIN_COMMAND_SMART] = 100; // ms
> > +	scm_data->timeouts[ADMIN_COMMAND_CONTROLLER_DUMP] = 1000; // ms
> > +	scm_data->timeouts[ADMIN_COMMAND_CONTROLLER_STATS] = 100; // ms
> > +	scm_data->timeouts[ADMIN_COMMAND_SHUTDOWN] = 1000; // ms
> > +	scm_data->timeouts[ADMIN_COMMAND_FW_UPDATE] = 16000; // ms
> > +
> > +	pci_set_drvdata(pdev, scm_data);
> > +
> > +	scm_data->ocxl_fn = ocxl_function_open(pdev);
> > +	if (IS_ERR(scm_data->ocxl_fn)) {
> > +		kfree(scm_data);
> > +		scm_data = NULL;
> > +		pci_set_drvdata(pdev, NULL);
> > +		dev_err(&pdev->dev, "failed to open OCXL function\n");
> > +		goto err;
> > +	}
> > +
> > +	scm_data->ocxl_afu = ocxl_function_fetch_afu(scm_data->ocxl_fn, 
> > 0);
> > +	if (scm_data->ocxl_afu == NULL)
> > +		goto err;
> > +
> > +	ocxl_afu_get(scm_data->ocxl_afu);
> > +
> > +	if (scm_register(scm_data) < 0)
> > +		goto err;
> > +
> > +	if (ocxl_context_alloc(&scm_data->ocxl_context, scm_data-
> > >ocxl_afu, NULL))
> > +		goto err;
> > +
> > +	if (ocxl_context_attach(scm_data->ocxl_context, 0, NULL))
> > +		goto err;
> > +
> > +	if (scm_setup_device_metadata(scm_data))
> > +		goto err;
> > +
> > +	if (scm_setup_irq(scm_data))
> > +		goto err;
> > +
> > +	if (scm_setup_command_metadata(scm_data))
> > +		goto err;
> > +
> > +	if (scm_create_cdev(scm_data))
> > +		goto err;
> > +
> > +	if (scm_sysfs_add(scm_data))
> > +		goto err;
> > +
> > +	if (scm_heartbeat(scm_data))
> > +		goto err;
> > +
> > +	elapsed = 0;
> > +	while (!scm_is_usable(scm_data)) {
> > +		if (elapsed++ > SCM_USABLE_TIMEOUT) {
> > +			dev_warn(&scm_data->dev, "SCM ready
> > timeout.\n");
> > +			goto err;
> > +		}
> > +
> > +		dev_warn(&scm_data->dev,
> > +			 "Waiting for SCM to become usable (%d/%d
> > seconds)\n",
> > +			 elapsed, SCM_USABLE_TIMEOUT);
> > +		msleep(1000);
> > +	}
> > +
> > +	if (scm_register_lpc_mem(scm_data))
> > +		goto err;
> > +
> > +	return 0;
> > +
> > +err:
> > +	if (scm_data &&
> > +		    (scm_chi(scm_data, &chi) == 0) &&
> > +		    (chi & GLOBAL_MMIO_CHI_ELA))
> > +		scm_dump_error_log(scm_data);
> > +
> > +	dev_err(&pdev->dev,
> > +		"Error detected, will not register storage class
> > memory\n");
> > +	return -ENXIO;
> > +}
> > +
> > +struct pci_driver scm_pci_driver = {
> > +	.name = "ocxl-scm",
> > +	.id_table = scm_pci_tbl,
> > +	.probe = scm_probe,
> > +	.remove = scm_remove,
> > +	.shutdown = scm_remove,
> > +};
> > +
> > +static int scm_file_init(void)
> > +{
> > +	int rc;
> > +
> > +	mutex_init(&minors_idr_lock);
> > +	idr_init(&minors_idr);
> > +
> > +	rc = alloc_chrdev_region(&scm_dev, 0, SCM_NUM_MINORS, "scm");
> > +	if (rc) {
> > +		pr_err("Unable to allocate scm major number: %d\n",
> > rc);
> > +		return rc;
> > +	}
> > +
> > +	scm_class = class_create(THIS_MODULE, "scm");
> > +	if (IS_ERR(scm_class)) {
> > +		pr_err("Unable to create scm class\n");
> > +		unregister_chrdev_region(scm_dev, SCM_NUM_MINORS);
> > +		return PTR_ERR(scm_class);
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void scm_file_exit(void)
> > +{
> > +	class_destroy(scm_class);
> > +	unregister_chrdev_region(scm_dev, SCM_NUM_MINORS);
> > +	idr_destroy(&minors_idr);
> > +}
> > +
> > +static int __init scm_init(void)
> > +{
> > +	int rc = 0;
> > +
> > +	rc = scm_file_init();
> > +	if (rc)
> > +		return rc;
> > +
> > +	rc = pci_register_driver(&scm_pci_driver);
> > +	if (rc) {
> > +		scm_file_exit();
> > +		return rc;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static void scm_exit(void)
> > +{
> > +	pci_unregister_driver(&scm_pci_driver);
> > +	scm_file_exit();
> > +}
> > +
> > +module_init(scm_init);
> > +module_exit(scm_exit);
> > +
> > +MODULE_DESCRIPTION("Storage Class Memory");
> > +MODULE_LICENSE("GPL");
> > diff --git a/drivers/nvdimm/ocxl-scm_internal.c
> > b/drivers/nvdimm/ocxl-scm_internal.c
> > new file mode 100644
> > index 000000000000..e7c247835817
> > --- /dev/null
> > +++ b/drivers/nvdimm/ocxl-scm_internal.c
> > @@ -0,0 +1,232 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +// Copyright 2019 IBM Corp.
> > +
> > +#include <misc/ocxl.h>
> > +#include <linux/delay.h>
> > +#include "ocxl-scm_internal.h"
> > +
> > +int scm_chi(const struct scm_data *scm_data, u64 *chi)
> > +{
> > +	u64 val;
> > +	int rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_CHI,
> > +					 OCXL_LITTLE_ENDIAN, &val);
> > +	if (rc)
> > +		return rc;
> > +
> > +	*chi = val;
> > +
> > +	return 0;
> > +}
> > +
> > +bool scm_controller_is_ready(const struct scm_data *scm_data)
> > +{
> > +	u64 val = 0;
> > +	int rc = scm_chi(scm_data, &val);
> > +
> > +	WARN_ON(rc < 0);
> > +
> > +	return (val & GLOBAL_MMIO_CHI_CRDY) != 0;
> > +}
> > +
> > +static int scm_command_request(const struct scm_data *scm_data,
> > +			       struct command_metadata *cmd, u8
> > op_code)
> > +{
> > +	u64 val = op_code;
> > +	int rc;
> > +	u8 i;
> > +
> > +	if (!scm_controller_is_ready(scm_data))
> > +		return -EIO;
> > +
> > +	cmd->op_code = op_code;
> > +	cmd->id++;
> > +
> > +	val |= ((u64)cmd->id) << 16;
> > +
> > +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu, cmd-
> > >request_offset,
> > +				      OCXL_LITTLE_ENDIAN, val);
> > +	if (rc)
> > +		return rc;
> > +
> > +	for (i = 0x08; i <= 0x38; i += 0x08) {
> > +		rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> > +					      cmd->request_offset + i,
> > +					      OCXL_LITTLE_ENDIAN, 0);
> > +		if (rc)
> > +			return rc;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +int scm_admin_command_request(struct scm_data *scm_data, u8
> > op_code)
> > +{
> > +	return scm_command_request(scm_data, &scm_data->admin_command,
> > op_code);
> > +}
> > +
> > +int scm_command_response(const struct scm_data *scm_data,
> > +			 const struct command_metadata *cmd)
> > +{
> > +	u64 val;
> > +	u16 id;
> > +	u8 status;
> > +	int rc = ocxl_global_mmio_read64(scm_data->ocxl_afu,
> > +					 cmd->response_offset,
> > +					 OCXL_LITTLE_ENDIAN, &val);
> > +	if (rc)
> > +		return rc;
> > +
> > +	status = val & 0xff;
> > +	id = (val >> 16) & 0xffff;
> > +
> > +	if (id != cmd->id) {
> > +		dev_warn(&scm_data->dev,
> > +			 "Expected response for command %d, but
> > received response for command %d instead.\n",
> > +			 cmd->id, id);
> > +	}
> > +
> > +	return status;
> > +}
> > +
> > +int scm_admin_response(const struct scm_data *scm_data)
> > +{
> > +	return scm_command_response(scm_data, &scm_data-
> > >admin_command);
> > +}
> > +
> > +
> > +int scm_admin_command_execute(const struct scm_data *scm_data)
> > +{
> > +	return ocxl_global_mmio_set64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_HCI,
> > +				      OCXL_LITTLE_ENDIAN,
> > GLOBAL_MMIO_HCI_ACRW);
> > +}
> > +
> > +static bool scm_admin_command_complete(const struct scm_data
> > *scm_data)
> > +{
> > +	u64 val = 0;
> > +
> > +	int rc = scm_chi(scm_data, &val);
> > +
> > +	WARN_ON(rc);
> > +
> > +	return (val & GLOBAL_MMIO_CHI_ACRA) != 0;
> > +}
> > +
> > +int scm_admin_command_complete_timeout(const struct scm_data
> > *scm_data,
> > +				       int command)
> > +{
> > +	u32 timeout = scm_data->timeouts[command];
> > +	timeout++;
> > +	timeout /= 32;
> > +	if (!timeout)
> > +		timeout = SCM_DEFAULT_TIMEOUT / 32;
> > +
> > +	while (timeout-- > 0) {
> > +		if (scm_admin_command_complete(scm_data))
> > +			return 0;
> > +		msleep(32);
> > +	}
> > +
> > +	return -EBUSY;
> > +}
> > +
> > +int scm_admin_response_handled(const struct scm_data *scm_data)
> > +{
> > +	return ocxl_global_mmio_set64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_CHIC,
> > +				      OCXL_LITTLE_ENDIAN,
> > GLOBAL_MMIO_CHI_ACRA);
> > +}
> > +
> > +int scm_ns_command_request(struct scm_data *scm_data, u8 op_code)
> > +{
> > +	return scm_command_request(scm_data, &scm_data->ns_command,
> > op_code);
> > +}
> > +
> > +int scm_ns_response(const struct scm_data *scm_data)
> > +{
> > +	return scm_command_response(scm_data, &scm_data->ns_command);
> > +}
> > +
> > +int scm_ns_command_execute(const struct scm_data *scm_data)
> > +{
> > +	return ocxl_global_mmio_set64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_HCI,
> > +				      OCXL_LITTLE_ENDIAN,
> > GLOBAL_MMIO_HCI_NSCRW);
> > +}
> > +
> > +bool scm_ns_command_complete(const struct scm_data *scm_data)
> > +{
> > +	u64 val = 0;
> > +	int rc = scm_chi(scm_data, &val);
> > +
> > +	WARN_ON(rc);
> > +
> > +	return (val & GLOBAL_MMIO_CHI_NSCRA) != 0;
> > +}
> > +
> > +int scm_ns_response_handled(const struct scm_data *scm_data)
> > +{
> > +	return ocxl_global_mmio_set64(scm_data->ocxl_afu,
> > GLOBAL_MMIO_CHIC,
> > +				      OCXL_LITTLE_ENDIAN,
> > GLOBAL_MMIO_CHI_NSCRA);
> > +}
> > +
> > +
> > +void scm_warn_status(const struct scm_data *scm_data, const char
> > *message,
> > +		     u8 status)
> > +{
> > +	const char *text = "Unknown";
> > +
> > +	switch (status) {
> > +	case STATUS_SUCCESS:
> > +		text = "Success";
> > +		break;
> > +
> > +	case STATUS_MEM_UNAVAILABLE:
> > +		text = "Persistent memory unavailable";
> > +		break;
> > +
> > +	case STATUS_BAD_OPCODE:
> > +		text = "Bad opcode";
> > +		break;
> > +
> > +	case STATUS_BAD_REQUEST_PARM:
> > +		text = "Bad request parameter";
> > +		break;
> > +
> > +	case STATUS_BAD_DATA_PARM:
> > +		text = "Bad data parameter";
> > +		break;
> > +
> > +	case STATUS_DEBUG_BLOCKED:
> > +		text = "Debug action blocked";
> > +		break;
> > +
> > +	case STATUS_FAIL:
> > +		text = "Failed";
> > +		break;
> > +	}
> > +
> > +	dev_warn(&scm_data->dev, "%s: %s (%x)\n", message, text,
> > status);
> > +}
> > +
> > +void scm_warn_status_fw_update(const struct scm_data *scm_data,
> > +			       const char *message, u8 status)
> > +{
> > +	const char *text;
> > +
> > +	switch (status) {
> > +	case STATUS_FW_UPDATE_BLOCKED:
> > +		text = "Firmware update is blocked, please try again
> > later";
> > +		break;
> > +
> > +	case STATUS_FW_ARG_INVALID:
> > +		text = "Internal error in SCM firmware update
> > mechanism";
> > +		break;
> > +
> > +	case STATUS_FW_INVALID:
> > +		text = "Firmware content is invalid, please verify
> > firmware update file";
> > +		break;
> > +
> > +	default:
> > +		return scm_warn_status(scm_data, message, status);
> > +	}
> > +
> > +	dev_warn(&scm_data->dev, "%s: %s (%x)\n", message, text,
> > status);
> > +}
> > diff --git a/drivers/nvdimm/ocxl-scm_internal.h
> > b/drivers/nvdimm/ocxl-scm_internal.h
> > new file mode 100644
> > index 000000000000..c236d8092c6d
> > --- /dev/null
> > +++ b/drivers/nvdimm/ocxl-scm_internal.h
> > @@ -0,0 +1,331 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +// Copyright 2019 IBM Corp.
> > +
> > +#include <linux/pci.h>
> > +#include <linux/cdev.h>
> > +#include <misc/ocxl.h>
> > +#include <linux/libnvdimm.h>
> > +#include <uapi/linux/ocxl-scm.h>
> > +#include <linux/mm.h>
> > +
> > +#define SCM_DEFAULT_TIMEOUT 100
> > +
> > +#define GLOBAL_MMIO_CHI		0x000
> > +#define GLOBAL_MMIO_CHIC	0x008
> > +#define GLOBAL_MMIO_CHIE	0x010
> > +#define GLOBAL_MMIO_CHIEC	0x018
> > +#define GLOBAL_MMIO_HCI		0x020
> > +#define GLOBAL_MMIO_HCIC	0x028
> > +#define GLOBAL_MMIO_IMA0_OHP	0x040
> > +#define GLOBAL_MMIO_IMA0_CFP	0x048
> > +#define GLOBAL_MMIO_IMA1_OHP	0x050
> > +#define GLOBAL_MMIO_IMA1_CFP	0x058
> > +#define GLOBAL_MMIO_ACMA_CREQO	0x100
> > +#define GLOBAL_MMIO_ACMA_CRSPO	0x104
> > +#define GLOBAL_MMIO_ACMA_CDBO	0x108
> > +#define GLOBAL_MMIO_ACMA_CDBS	0x10c
> > +#define GLOBAL_MMIO_NSCMA_CREQO	0x120
> > +#define GLOBAL_MMIO_NSCMA_CRSPO	0x124
> > +#define GLOBAL_MMIO_NSCMA_CDBO	0x128
> > +#define GLOBAL_MMIO_NSCMA_CDBS	0x12c
> > +#define GLOBAL_MMIO_CSTS	0x140
> > +#define GLOBAL_MMIO_FWVER	0x148
> > +#define GLOBAL_MMIO_CCAP0	0x160
> > +#define GLOBAL_MMIO_CCAP1	0x168
> > +
> > +#define GLOBAL_MMIO_CHI_ACRA	BIT_ULL(0)
> > +#define GLOBAL_MMIO_CHI_NSCRA	BIT_ULL(1)
> > +#define GLOBAL_MMIO_CHI_CRDY	BIT_ULL(4)
> > +#define GLOBAL_MMIO_CHI_CFFS	BIT_ULL(5)
> > +#define GLOBAL_MMIO_CHI_MA	BIT_ULL(6)
> > +#define GLOBAL_MMIO_CHI_ELA	BIT_ULL(7)
> > +#define GLOBAL_MMIO_CHI_CDA	BIT_ULL(8)
> > +#define GLOBAL_MMIO_CHI_CHFS	BIT_ULL(9)
> > +
> > +#define GLOBAL_MMIO_CHI_ALL	(GLOBAL_MMIO_CHI_ACRA | \
> > +				 GLOBAL_MMIO_CHI_NSCRA | \
> > +				 GLOBAL_MMIO_CHI_CRDY | \
> > +				 GLOBAL_MMIO_CHI_CFFS | \
> > +				 GLOBAL_MMIO_CHI_MA | \
> > +				 GLOBAL_MMIO_CHI_ELA | \
> > +				 GLOBAL_MMIO_CHI_CDA | \
> > +				 GLOBAL_MMIO_CHI_CHFS)
> > +
> > +#define GLOBAL_MMIO_HCI_ACRW				BIT_ULL
> > (0)
> > +#define GLOBAL_MMIO_HCI_NSCRW				BIT_ULL
> > (1)
> > +#define GLOBAL_MMIO_HCI_AFU_RESET			BIT_ULL(2)
> > +#define GLOBAL_MMIO_HCI_FW_DEBUG			BIT_ULL(3)
> > +#define GLOBAL_MMIO_HCI_CONTROLLER_DUMP			BIT_ULL
> > (4)
> > +#define GLOBAL_MMIO_HCI_CONTROLLER_DUMP_COLLECTED	BIT_ULL(5)
> > +#define GLOBAL_MMIO_HCI_REQ_HEALTH_PERF			BIT_ULL
> > (6)
> > +
> > +#define ADMIN_COMMAND_HEARTBEAT		0x00u
> > +#define ADMIN_COMMAND_SHUTDOWN		0x01u
> > +#define ADMIN_COMMAND_FW_UPDATE		0x02u
> > +#define ADMIN_COMMAND_FW_DEBUG		0x03u
> > +#define ADMIN_COMMAND_ERRLOG		0x04u
> > +#define ADMIN_COMMAND_SMART		0x05u
> > +#define ADMIN_COMMAND_CONTROLLER_STATS	0x06u
> > +#define ADMIN_COMMAND_CONTROLLER_DUMP	0x07u
> > +#define ADMIN_COMMAND_CMD_CAPS		0x08u
> > +#define ADMIN_COMMAND_MAX		0x08u
> > +
> > +#define NS_COMMAND_SECURE_ERASE	0x20ull
> > +
> > +#define NS_RESPONSE_SECURE_ERASE_ACCESSIBLE_SUCCESS 0x20
> > +#define NS_RESPONSE_SECURE_ERASE_ACCESSIBLE_ATTEMPTED 0x28
> > +#define NS_RESPONSE_SECURE_ERASE_DEFECTIVE_SUCCESS 0x30
> > +#define NS_RESPONSE_SECURE_ERASE_DEFECTIVE_ATTEMPTED 0x38
> > +
> > +
> > +
> > +#define STATUS_SUCCESS		0x00
> > +#define STATUS_MEM_UNAVAILABLE	0x20
> > +#define STATUS_BAD_OPCODE	0x50
> > +#define STATUS_BAD_REQUEST_PARM	0x51
> > +#define STATUS_BAD_DATA_PARM	0x52
> > +#define STATUS_DEBUG_BLOCKED	0x70
> > +#define STATUS_FAIL		0xFF
> > +
> > +#define STATUS_FW_UPDATE_BLOCKED 0x21
> > +#define STATUS_FW_ARG_INVALID	0x51
> > +#define STATUS_FW_INVALID	0x52
> > +
> > +#define SCM_LABEL_AREA_SIZE	(1UL << PA_SECTION_SHIFT)
> > +
> > +struct command_metadata {
> > +	u32 request_offset;
> > +	u32 response_offset;
> > +	u32 data_offset;
> > +	u32 data_size;
> > +	struct mutex lock;
> > +	u16 id;
> > +	u8 op_code;
> > +};
> > +
> > +struct scm_function_0 {
> > +	struct pci_dev *pdev;
> > +	struct ocxl_fn *ocxl_fn;
> > +};
> > +
> > +enum overwrite_state {
> > +	SCM_OVERWRITE_IDLE = 0,
> > +	SCM_OVERWRITE_BUSY,
> > +	SCM_OVERWRITE_SUCCESS,
> > +	SCM_OVERWRITE_FAILED
> > +};
> > +
> > +#define SCM_SMART_ATTR_POWER_ON_HOURS	0x09
> > +#define SCM_SMART_ATTR_TEMPERATURE	0xC2
> > +#define SCM_SMART_ATTR_LIFE_REMAINING	0xCA
> > +
> > +struct scm_smart_attrib {
> > +	__u8 id; /* out, See defines above */
> > +	__u16 attribute_flags;
> > +	__u8 current_val;
> > +	__u8 threshold_val;
> > +	__u8 worst_val;
> > +	__u8 reserved;
> > +	__u64 raw_val;
> > +};
> > +
> > +struct scm_smart_attribs {
> > +	struct scm_smart_attrib power_on_hours;
> > +	struct scm_smart_attrib temperature;
> > +	struct scm_smart_attrib life_remaining;
> > +};
> > +
> > +struct scm_data {
> > +	struct device dev;
> > +	struct pci_dev *pdev;
> > +	struct cdev cdev;
> > +	struct ocxl_fn *ocxl_fn;
> > +#define SCM_IRQ_COUNT 2
> > +	int irq_id[SCM_IRQ_COUNT];
> > +	struct dev_pagemap irq_pgmap[SCM_IRQ_COUNT];
> > +	void *irq_addr[SCM_IRQ_COUNT];
> > +	struct nd_interleave_set nd_set;
> > +	struct nvdimm_bus_descriptor bus_desc;
> > +	struct nvdimm_bus *nvdimm_bus;
> > +	struct ocxl_afu *ocxl_afu;
> > +	struct ocxl_context *ocxl_context;
> > +	void *metadata_addr;
> > +	struct scm_global_mmio *global_mmio;
> > +	struct command_metadata admin_command;
> > +	struct command_metadata ns_command;
> > +	enum overwrite_state overwrite_state;
> > +	struct resource scm_res;
> > +	struct nd_region *nd_region;
> > +	struct eventfd_ctx *ev_ctx;
> > +	struct scm_smart_attribs smart;
> > +	char fw_version[8+1];
> > +	u32 timeouts[ADMIN_COMMAND_MAX+1];
> > +
> > +	u16 scm_revision; // major/minor
> > +	u16 readiness_timeout; /* The worst case time (in milliseconds)
> > that the host shall
> > +				* wait for the controller to become
> > operational following a reset (CHI.CRDY).
> > +				*/
> > +	u16 read_latency; /* The nominal measure of latency (in
> > nanoseconds)
> > +			   * associated with an unassisted read of a
> > memory block.
> > +			   * This represents the capability of the raw
> > media technology without assistance
> > +			   */
> > +	u32 max_controller_dump_size; // bytes
> > +};
> > +
> > +/**
> > + * Create sysfs entries for an SCM device
> > + * scm_data: The SCM metadata
> > + */
> > +int scm_sysfs_add(struct scm_data *scm_data);
> > +
> > +/**
> > + * Get the value of the CHI register:
> > + * scm_data: The SCM metadata
> > + * chi: returns the CHI value
> > + *
> > + * Returns 0 on success, negative on error
> > + */
> > +int scm_chi(const struct scm_data *scm_data, u64 *chi);
> > +
> > +/**
> > + * scm_controller_is_ready - Is the controller ready?
> > + * @scm_data: a pointer to the SCM device data
> > + * Return true if the controller is ready
> > + */
> > +bool scm_controller_is_ready(const struct scm_data *scm_data);
> > +
> > +/**
> > + * Issue an admin command request
> > + *
> > + * scm_data: a pointer to the SCM device data
> > + * op_code: The op-code for the command
> > + *
> > + * Returns an identifier for the command, or negative on error
> > + */
> > +int scm_admin_command_request(struct scm_data *scm_data, u8
> > op_code);
> > +
> > +/**
> > + * Validate an admin response
> > + *
> > + * scm_data: a pointer to the SCM device data
> > + *
> > + * Returns the status code of the command, or negative on error
> > + */
> > +int scm_admin_response(const struct scm_data *scm_data);
> > +
> > +/**
> > + * Notify the controller to start processing a pending admin
> > command
> > + *
> > + * scm_data: a pointer to the SCM device data
> > + *
> > + * Returns 0 on success, negative on error
> > + */
> > +int scm_admin_command_execute(const struct scm_data *scm_data);
> > +
> > +/**
> > + * Wait for an admin command to finish executing
> > + *
> > + * scm_data: a pointer to the SCM device data
> > + * command: the admin command to wait for completion (determines
> > the timeout)
> > + *
> > + * Returns 0 on success, -EBUSY on timeout
> > + */
> > +int scm_admin_command_complete_timeout(const struct scm_data
> > *scm_data,
> > +				       int command);
> > +
> > +/**
> > + * Notify the controller that the admin response has been handled
> > + *
> > + * scm_data: a pointer to the SCM device data
> > + *
> > + * Returns 0 on success, negative on failure
> > + */
> > +int scm_admin_response_handled(const struct scm_data *scm_data);
> > +
> > +/**
> > + * Issue a near storage command request
> > + *
> > + * scm_data: a pointer to the SCM device data
> > + * op_code: The op-code for the command
> > + *
> > + * Returns an identifier for the command, or negative on error
> > + */
> > +int scm_ns_command_request(struct scm_data *scm_data, u8 op_code);
> > +
> > +/**
> > + * Validate a near storage response
> > + *
> > + * scm_data: a pointer to the SCM device data
> > + *
> > + * Returns the status code of the command, or negative on error
> > + */
> > +int scm_ns_response(const struct scm_data *scm_data);
> > +
> > +/**
> > + * Notify the controller to start processing a pending near
> > storage command
> > + *
> > + * scm_data: a pointer to the SCM device data
> > + *
> > + * Returns 0 on success, negative on error
> > + */
> > +int scm_ns_command_execute(const struct scm_data *scm_data);
> > +
> > +/**
> > + * Is a near storage command executing
> > + *
> > + * scm_data: a pointer to the SCM device data
> > + *
> > + * Returns true if the previous admin command has completed
> > + */
> > +bool scm_ns_command_complete(const struct scm_data *scm_data);
> > +
> > +/**
> > + * Notify the controller that the near storage response has been
> > handled
> > + *
> > + * scm_data: a pointer to the SCM device data
> > + *
> > + * Returns 0 on success, negative on failure
> > + */
> > +int scm_ns_response_handled(const struct scm_data *scm_data);
> > +
> > +/**
> > + * Emit a kernel warning showing a command status.
> > + *
> > + * scm_data: a pointer to the SCM device data
> > + * message: A message to accompany the warning
> > + * status: The command status
> > + */
> > +void scm_warn_status(const struct scm_data *scm_data, const char
> > *message,
> > +		     u8 status);
> > +
> > +/**
> > + * Emit a kernel warning showing a command status.
> > + *
> > + * scm_data: a pointer to the SCM device data
> > + * message: A message to accompany the warning
> > + * status: The command status
> > + */
> > +void scm_warn_status_fw_update(const struct scm_data *scm_data,
> > +			       const char *message, u8 status);
> > +
> > +/**
> > + * Request a controller dump
> > + *
> > + * scm_data: a pointer to the SCM device data
> > + */
> > +int scm_request_controller_dump(struct scm_data *scm_data);
> > +
> > +/**
> > + * Request health & performance data (this will emit error logs
> > with the information)
> > + *
> > + * scm_data: a pointer to the SCM device data
> > + */
> > +int scm_req_controller_health_perf(struct scm_data *scm_data);
> > +
> > +
> > +/**
> > + * scm_overwrite() - Overwrite all data on the card
> > + * @scm_data: The SCM device data
> > + * Return: 0 on success
> > + */
> > +int scm_overwrite(struct scm_data *scm_data);
> > diff --git a/drivers/nvdimm/ocxl-scm_sysfs.c b/drivers/nvdimm/ocxl-
> > scm_sysfs.c
> > new file mode 100644
> > index 000000000000..080bbdeb0e56
> > --- /dev/null
> > +++ b/drivers/nvdimm/ocxl-scm_sysfs.c
> > @@ -0,0 +1,219 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +// Copyright 2018 IBM Corp.
> > +
> > +#include <linux/sysfs.h>
> > +#include <linux/capability.h>
> > +#include <linux/limits.h>
> > +#include <linux/firmware.h>
> > +#include "ocxl-scm_internal.h"
> > +
> > +static ssize_t admin_command_buffer_size_show(struct device
> > *device,
> > +	struct device_attribute *attr,
> > +	char *buf)
> > +{
> > +	struct scm_data *scm_data = container_of(device, struct
> > scm_data, dev);
> > +
> > +	return scnprintf(buf, PAGE_SIZE, "%d\n", scm_data-
> > >admin_command.data_size);
> > +}
> > +
> > +static ssize_t fw_version_show(struct device *device,
> > +			       struct device_attribute *attr, char
> > *buf)
> > +{
> > +	struct scm_data *scm_data = container_of(device, struct
> > scm_data, dev);
> > +
> > +	return scnprintf(buf, PAGE_SIZE, "%s\n", scm_data->fw_version);
> > +}
> > +
> > +#define SCM_FWUPDATE_BLOCK_SIZE	32768
> > +
> > +/**
> > + * scm_update_firmware() - Write a 32kB block of data to firmware
> > + * The block may be less than 32kB if it is the last one
> > + *
> > + * scm_data the SCM device metadata
> > + * offset: the offset of the start of the block
> > + * buf: the block data
> > + * size: the size of the block
> > + */
> > +static ssize_t scm_update_firmware(struct scm_data *scm_data,
> > size_t offset,
> > +				   const char *buf, size_t size)
> > +{
> > +	int rc;
> > +	size_t i;
> > +	u64 val;
> > +
> > +	if (size > SCM_FWUPDATE_BLOCK_SIZE)
> > +		return -EINVAL;
> > +
> > +	rc = scm_admin_command_request(scm_data,
> > ADMIN_COMMAND_FW_UPDATE);
> > +	if (rc)
> > +		return rc;
> > +
> > +	val = (((u64)offset) << 32) | size;
> > +	rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> > +				      scm_data-
> > >admin_command.request_offset + 8,
> > +				      OCXL_LITTLE_ENDIAN, val);
> > +	if (rc)
> > +		return rc;
> > +
> > +	for (i = 0; i < size; i += 8) {
> > +		val = *(u64 *)(buf + i);
> > +		rc = ocxl_global_mmio_write64(scm_data->ocxl_afu,
> > +					      scm_data-
> > >admin_command.data_offset + i,
> > +					      OCXL_HOST_ENDIAN, val);
> > +		if (rc)
> > +			return rc;
> > +	}
> > +
> > +	rc = scm_admin_command_execute(scm_data);
> > +	if (rc)
> > +		return rc;
> > +
> > +	rc = scm_admin_command_complete_timeout(scm_data,
> > +						ADMIN_COMMAND_FW_UPDATE
> > );
> > +	if (rc < 0) {
> > +		dev_err(&scm_data->dev, "Firmware update timeout\n");
> > +		return rc;
> > +	}
> > +
> > +	rc = scm_admin_response(scm_data);
> > +	if (rc < 0)
> > +		return rc;
> > +	if (rc != STATUS_SUCCESS) {
> > +		scm_warn_status_fw_update(scm_data, "FW Update", rc);
> > +		return rc;
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +/*
> > + * Parse out a firmware filename from sysfs, retrieve it's
> > contents and write it
> > + * to the SCM device firmware storage
> > + */
> > +static ssize_t fw_update_filename_store(struct device *device,
> > +					struct device_attribute *attr,
> > +					const char *buf, size_t size)
> > +{
> > +	char path[NAME_MAX+1];
> > +	const char *end;
> > +	const struct firmware *firmware = NULL;
> > +	size_t offset;
> > +	int rc;
> > +	struct scm_data *scm_data = container_of(device, struct
> > scm_data, dev);
> > +
> > +	if (!capable(CAP_SYS_ADMIN))
> > +		return -EACCES;
> > +
> > +	end = strnchr(buf, size, '\n');
> > +	if (end == NULL)
> > +		end = buf + strnlen(buf, size);
> > +
> > +	if ((end - buf) > NAME_MAX) {
> > +		dev_err(device, "Firmware filename '%-.*s' too long\n",
> > +			(int)(end - buf), buf);
> > +		return -EIO;
> > +	}
> > +
> > +	memcpy(path, buf, end - buf);
> > +	path[end - buf] = '\0';
> > +
> > +	if (request_firmware(&firmware, path, device)) {
> > +		dev_err(device, "Firmware file %s not found\n", path);
> > +		return -EIO;
> > +	}
> > +
> > +	if (firmware->size % 8) {
> > +		release_firmware(firmware);
> > +		dev_err(device, "Firmware '%s' should be a multiple of
> > 8 bytes", path);
> > +		return -EINVAL;
> > +	}
> > +
> > +	mutex_lock(&scm_data->admin_command.lock);
> > +
> > +	for (offset = 0; offset < firmware->size; offset +=
> > SCM_FWUPDATE_BLOCK_SIZE) {
> > +		size_t remainder = firmware->size - offset;
> > +		size_t block_size;
> > +
> > +		block_size = (remainder > SCM_FWUPDATE_BLOCK_SIZE) ?
> > +			      SCM_FWUPDATE_BLOCK_SIZE : remainder;
> > +		rc = scm_update_firmware(scm_data, offset,
> > +					 firmware->data + offset,
> > block_size);
> > +		if (rc) {
> > +			mutex_unlock(&scm_data->admin_command.lock);
> > +			return -EFAULT;
> > +		}
> > +	}
> > +
> > +	mutex_unlock(&scm_data->admin_command.lock);
> > +
> > +	return size;
> > +}
> > +
> > +/*
> > + * Trigger a controller dump
> > + */
> > +static ssize_t controller_dump_store(struct device *device,
> > +				     struct device_attribute *attr,
> > +				     const char *buf, size_t size)
> > +{
> > +	struct scm_data *scm_data = container_of(device, struct
> > scm_data, dev);
> > +
> > +	scm_request_controller_dump(scm_data);
> > +
> > +	return size;
> > +}
> > +
> > +/*
> > + * Request health & performance data
> > + */
> > +static ssize_t health_request_store(struct device *device,
> > +				    struct device_attribute *attr,
> > +				    const char *buf, size_t size)
> > +{
> > +	struct scm_data *scm_data = container_of(device, struct
> > scm_data, dev);
> > +
> > +	scm_req_controller_health_perf(scm_data);
> > +
> > +	return size;
> > +}
> > +
> > +/*
> > + * Overwrite all media
> > + */
> > +static ssize_t overwrite_store(struct device *device,
> > +			       struct device_attribute *attr,
> > +			       const char *buf, size_t size)
> > +{
> > +	struct scm_data *scm_data = container_of(device, struct
> > scm_data, dev);
> > +
> > +	scm_overwrite(scm_data);
> > +
> > +	return size;
> > +}
> > +
> > +static struct device_attribute scm_attrs[] = {
> > +	__ATTR_RO(admin_command_buffer_size),
> > +	__ATTR_RO(fw_version),
> > +	__ATTR_WO(fw_update_filename),
> > +	__ATTR_WO(controller_dump),
> > +	__ATTR_WO(health_request),
> > +	__ATTR_WO(overwrite),
> > +};
> > +
> > +int scm_sysfs_add(struct scm_data *scm_data)
> > +{
> > +	int i, rc;
> > +
> > +	for (i = 0; i < ARRAY_SIZE(scm_attrs); i++) {
> > +		rc = device_create_file(&scm_data->dev, &scm_attrs[i]);
> > +		if (rc) {
> > +			for (; --i >= 0;)
> > +				device_remove_file(&scm_data->dev,
> > &scm_attrs[i]);
> > +
> > +			return rc;
> > +		}
> > +	}
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(scm_sysfs_add);
> > diff --git a/include/uapi/linux/ocxl-scm.h
> > b/include/uapi/linux/ocxl-scm.h
> > new file mode 100644
> > index 000000000000..6dc7e5196da2
> > --- /dev/null
> > +++ b/include/uapi/linux/ocxl-scm.h
> > @@ -0,0 +1,128 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
> > +/* Copyright 2017 IBM Corp. */
> > +#ifndef _UAPI_OCXL_SCM_H
> > +#define _UAPI_OCXL_SCM_H
> > +
> > +#include <linux/types.h>
> > +#include <linux/ioctl.h>
> > +
> > +enum scm_fwdebug_action {
> > +	SCM_FWDEBUG_READ_CONTROLLER_MEMORY = 0x01,
> > +	SCM_FWDEBUG_WRITE_CONTROLLER_MEMORY = 0x02,
> > +	SCM_FWDEBUG_ENABLE_FUNCTION = 0x03,
> > +	SCM_FWDEBUG_DISABLE_FUNCTION = 0x04,
> > +	SCM_FWDEBUG_GET_PEL = 0x05, // Retrieve Persistent Error Log
> > +};
> > +
> > +struct scm_ioctl_buffer_info {
> > +	__u32	admin_command_buffer_size; // out
> > +	__u32	near_storage_buffer_size; // out
> > +};
> > +
> > +struct scm_ioctl_fwdebug { // All args are inputs
> > +	enum scm_fwdebug_action debug_action;
> > +	__u16 function_code;
> > +	__u16 buf_size; // Size of optional data buffer
> > +	__u64 debug_parameter_1;
> > +	__u64 debug_parameter_2;
> > +	__u8 *buf; // Pointer to optional in/out data buffer
> > +};
> > +
> > +#define SCM_ERROR_LOG_ACTION_RESET	(1 << (32-32))
> > +#define SCM_ERROR_LOG_ACTION_CHKFW	(1 << (53-32))
> > +#define SCM_ERROR_LOG_ACTION_REPLACE	(1 << (54-32))
> > +#define SCM_ERROR_LOG_ACTION_DUMP	(1 << (55-32))
> > +
> > +#define SCM_ERROR_LOG_TYPE_GENERAL		(0x00)
> > +#define SCM_ERROR_LOG_TYPE_PREDICTIVE_FAILURE	(0x01)
> > +#define SCM_ERROR_LOG_TYPE_THERMAL_WARNING	(0x02)
> > +#define SCM_ERROR_LOG_TYPE_DATA_LOSS		(0x03)
> > +#define SCM_ERROR_LOG_TYPE_HEALTH_PERFORMANCE	(0x04)
> > +
> > +struct scm_ioctl_error_log {
> > +	__u32 log_identifier; // out
> > +	__u32 program_reference_code; // out
> > +	__u32 action_flags; //out, recommended course of action
> > +	__u32 power_on_seconds; // out, Number of seconds the
> > controller has been on when the error occurred
> > +	__u64 timestamp; // out, relative time since the current IPL
> > +	__u64 wwid[2]; // out, the NAA formatted WWID associated with
> > the controller
> > +	char  fw_revision[8+1]; // out, firmware revision as null
> > terminated text
> > +	__u16 buf_size; /* in/out, buffer size provided/required.
> > +			 * If required is greater than provided, the
> > buffer
> > +			 * will be truncated to the amount provided. If
> > its
> > +			 * less, then only the required bytes will be
> > populated.
> > +			 * If it is 0, then there are no more error log
> > entries.
> > +			 */
> > +	__u8  error_log_type;
> > +	__u8  reserved1;
> > +	__u32 reserved2;
> > +	__u64 reserved3[2];
> > +	__u8 *buf; // pointer to output buffer
> > +};
> > +
> > +struct scm_ioctl_controller_dump_data {
> > +	__u8 *buf; // pointer to output buffer
> > +	__u16 buf_size; /* in/out, buffer size provided/required.
> > +			 * If required is greater than provided, the
> > buffer
> > +			 * will be truncated to the amount provided. If
> > its
> > +			 * less, then only the required bytes will be
> > populated.
> > +			 * If it is 0, then there is no more dump data
> > available.
> > +			 */
> > +	__u32 offset; // in, Offset within the dump
> > +	__u64 reserved[8];
> > +};
> > +
> > +struct scm_ioctl_controller_stats {
> > +	__u32 reset_count;
> > +	__u32 reset_uptime; // seconds
> > +	__u32 power_on_uptime; // seconds
> > +	__u64 host_load_count;
> > +	__u64 host_store_count;
> > +	__u64 media_read_count;
> > +	__u64 media_write_count;
> > +	__u64 cache_hit_count;
> > +	__u64 cache_miss_count;
> > +	__u64 media_read_latency; // nanoseconds
> > +	__u64 media_write_latency; // nanoseconds
> > +	__u64 cache_read_latency; // nanoseconds
> > +	__u64 cache_write_latency; // nanoseconds
> > +};
> > +
> > +struct scm_ioctl_mmio {
> > +	__u64 address; // Offset in global MMIO space
> > +	__u64 val; // value to write/was read
> > +};
> > +
> > +struct scm_ioctl_eventfd {
> > +	__s32 eventfd;
> > +	__u32 reserved;
> > +};
> > +
> > +#ifndef BIT_ULL
> > +#define BIT_ULL(nr)	(1ULL << (nr))
> > +#endif
> > +
> > +#define SCM_IOCTL_EVENT_CONTROLLER_DUMP_AVAILABLE	BIT_ULL(0)
> > +#define SCM_IOCTL_EVENT_ERROR_LOG_AVAILABLE		BIT_ULL
> > (1)
> > +#define SCM_IOCTL_EVENT_HARDWARE_FATAL			BIT_ULL
> > (2)
> > +#define SCM_IOCTL_EVENT_FIRMWARE_FATAL			BIT_ULL
> > (3)
> > +
> > +/* ioctl numbers */
> > +#define SCM_MAGIC 0x5C
> > +/* SCM devices */
> > +#define SCM_IOCTL_BUFFER_INFO	_IOR(SCM_MAGIC, 0x00, struct
> > scm_ioctl_buffer_info)
> > +#define SCM_IOCTL_ERROR_LOG	_IOWR(SCM_MAGIC, 0x01, struct
> > scm_ioctl_error_log)
> > +#define SCM_IOCTL_CONTROLLER_DUMP _IO(SCM_MAGIC, 0x02)
> > +#define SCM_IOCTL_CONTROLLER_DUMP_DATA _IOWR(SCM_MAGIC, 0x03,
> > struct scm_ioctl_controller_dump_data)
> > +#define SCM_IOCTL_CONTROLLER_DUMP_COMPLETE _IO(SCM_MAGIC, 0x04)
> > +#define SCM_IOCTL_CONTROLLER_STATS _IO(SCM_MAGIC, 0x05)
> > +#define SCM_IOCTL_EVENTFD	_IOW(SCM_MAGIC, 0x06, struct
> > scm_ioctl_eventfd)
> > +#define SCM_IOCTL_EVENT_CHECK	_IOR(SCM_MAGIC, 0x07, __u64)
> > +#define SCM_IOCTL_REQUEST_HEALTH _IO(SCM_MAGIC, 0x08)
> > +
> > +#define SCM_IOCTL_FWDEBUG	_IOWR(SCM_MAGIC, 0xf0, struct
> > scm_ioctl_fwdebug)
> > +#define SCM_IOCTL_MMIO_WRITE	_IOW(SCM_MAGIC, 0xf1, struct
> > scm_ioctl_mmio)
> > +#define SCM_IOCTL_MMIO_READ	_IOWR(SCM_MAGIC, 0xf2, struct
> > scm_ioctl_mmio)
> > +#define SCM_IOCTL_SHUTDOWN	_IO(SCM_MAGIC, 0xf3)
> > +
> > +#endif /* _UAPI_OCXL_SCM_H */
> > diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
> > index b39827dbd071..376500f4e3a2 100644
> > --- a/mm/memory_hotplug.c
> > +++ b/mm/memory_hotplug.c
> > @@ -279,7 +279,7 @@ static int check_pfn_span(unsigned long pfn,
> > unsigned long nr_pages,
> >   }
> >   
> >   int check_hotplug_memory_addressable(unsigned long pfn,
> > -					    unsigned long nr_pages)
> > +				     unsigned long nr_pages)
> >   {
> >   	const u64 max_addr = PFN_PHYS(pfn + nr_pages) - 1;
> >   
> > 
-- 
Alastair D'Silva
Open Source Developer
Linux Technology Centre, IBM Australia
mob: 0423 762 819



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory
  2019-11-14 16:35     ` Dan Williams
@ 2019-11-18 23:47       ` Andrew Donnellan
  2019-11-19  0:04         ` Dan Williams
  2019-11-19  2:48         ` Alastair D'Silva
  0 siblings, 2 replies; 45+ messages in thread
From: Andrew Donnellan @ 2019-11-18 23:47 UTC (permalink / raw)
  To: Dan Williams, Frederic Barrat
  Cc: Alastair D'Silva, alastair, Oscar Salvador, Michal Hocko,
	David Hildenbrand, Alexey Kardashevskiy, Wei Yang, Keith Busch,
	Masahiro Yamada, Paul Mackerras, Ira Weiny, Thomas Gleixner,
	Pavel Tatashin, Dave Jiang, linux-nvdimm, Vishal Verma,
	Krzysztof Kozlowski, Mahesh Salgaonkar, Arnd Bergmann, Greg Kurz,
	Nicholas Piggin, Qian Cai, Cédric Le Goater, Hari Bathini,
	David Gibson, Linux MM, Greg Kroah-Hartman,
	Linux Kernel Mailing List, Andrew Morton, linuxppc-dev

On 15/11/19 3:35 am, Dan Williams wrote:
>> Have you discussed with the directory owner if it's ok to split the
>> driver over several files?
> 
> My thought is to establish drivers/opencapi/ and move this and the
> existing drivers/misc/ocxl/ bits there.

Is there any other justification for this we can think of apart from not 
wanting to put this driver in the nvdimm directory? OpenCAPI drivers 
aren't really a category of driver unto themselves.

-- 
Andrew Donnellan              OzLabs, ADL Canberra
ajd@linux.ibm.com             IBM Australia Limited



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory
  2019-11-18 23:47       ` Andrew Donnellan
@ 2019-11-19  0:04         ` Dan Williams
  2019-11-19  2:48         ` Alastair D'Silva
  1 sibling, 0 replies; 45+ messages in thread
From: Dan Williams @ 2019-11-19  0:04 UTC (permalink / raw)
  To: Andrew Donnellan
  Cc: Frederic Barrat, Alastair D'Silva, alastair, Oscar Salvador,
	Michal Hocko, David Hildenbrand, Alexey Kardashevskiy, Wei Yang,
	Masahiro Yamada, Paul Mackerras, Ira Weiny, Thomas Gleixner,
	Pavel Tatashin, Dave Jiang, linux-nvdimm, Vishal Verma,
	Krzysztof Kozlowski, Mahesh Salgaonkar, Arnd Bergmann, Greg Kurz,
	Nicholas Piggin, Qian Cai, Cédric Le Goater, Hari Bathini,
	David Gibson, Linux MM, Greg Kroah-Hartman,
	Linux Kernel Mailing List, Andrew Morton, linuxppc-dev

On Mon, Nov 18, 2019 at 3:48 PM Andrew Donnellan <ajd@linux.ibm.com> wrote:
>
> On 15/11/19 3:35 am, Dan Williams wrote:
> >> Have you discussed with the directory owner if it's ok to split the
> >> driver over several files?
> >
> > My thought is to establish drivers/opencapi/ and move this and the
> > existing drivers/misc/ocxl/ bits there.
>
> Is there any other justification for this we can think of apart from not
> wanting to put this driver in the nvdimm directory? OpenCAPI drivers
> aren't really a category of driver unto themselves.

The concern is less about adding to drivers/nvdimm/ and more about the
proper location to house opencapi specific transport and enumeration
details. The organization I'm looking for is to group platform
transport and enumeration code together similar to how drivers/pci/
exists independent of all pci drivers that use that common core. For
libnvdimm the enumeration is platform specific and calls into the
nvdimm core. This is why the x86 platform persistent memory bus driver
lives under drivers/acpi/nfit/ instead of drivers/nvdimm/. The nfit
driver is an ACPI extension that translates ACPI details into
libnvdimm core objects.

The usage of "ocxl" in the source leads me to think part of this
driver belongs in a directory that has other opencapi specific
considerations.


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory
  2019-11-18 23:47       ` Andrew Donnellan
  2019-11-19  0:04         ` Dan Williams
@ 2019-11-19  2:48         ` Alastair D'Silva
  2019-11-19  3:26           ` Andrew Donnellan
  1 sibling, 1 reply; 45+ messages in thread
From: Alastair D'Silva @ 2019-11-19  2:48 UTC (permalink / raw)
  To: Andrew Donnellan, Dan Williams, Frederic Barrat
  Cc: Oscar Salvador, Michal Hocko, David Hildenbrand,
	Alexey Kardashevskiy, Wei Yang, Keith Busch, Masahiro Yamada,
	Paul Mackerras, Ira Weiny, Thomas Gleixner, Pavel Tatashin,
	Dave Jiang, linux-nvdimm, Vishal Verma, Krzysztof Kozlowski,
	Mahesh Salgaonkar, Arnd Bergmann, Greg Kurz, Nicholas Piggin,
	Qian Cai, Cédric Le Goater, Hari Bathini, David Gibson,
	Linux MM, Greg Kroah-Hartman, Linux Kernel Mailing List,
	Andrew Morton, linuxppc-dev

On Tue, 2019-11-19 at 10:47 +1100, Andrew Donnellan wrote:
> On 15/11/19 3:35 am, Dan Williams wrote:
> > > Have you discussed with the directory owner if it's ok to split
> > > the
> > > driver over several files?
> > 
> > My thought is to establish drivers/opencapi/ and move this and the
> > existing drivers/misc/ocxl/ bits there.
> 
> Is there any other justification for this we can think of apart from
> not 
> wanting to put this driver in the nvdimm directory? OpenCAPI drivers 
> aren't really a category of driver unto themselves.
> 

There is a precedent for bus-based dirs, eg. drivers/(ide|w1|spi) all
contain drivers for both controllers & connected devices.

Fred, how do you feel about moving the generic OpenCAPI driver out of
drivers/misc?

-- 
Alastair D'Silva
Open Source Developer
Linux Technology Centre, IBM Australia
mob: 0423 762 819



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory
  2019-11-19  2:48         ` Alastair D'Silva
@ 2019-11-19  3:26           ` Andrew Donnellan
  2019-11-19  4:41             ` Dan Williams
  0 siblings, 1 reply; 45+ messages in thread
From: Andrew Donnellan @ 2019-11-19  3:26 UTC (permalink / raw)
  To: Alastair D'Silva, Dan Williams, Frederic Barrat
  Cc: Oscar Salvador, Michal Hocko, David Hildenbrand,
	Alexey Kardashevskiy, Wei Yang, Keith Busch, Masahiro Yamada,
	Paul Mackerras, Ira Weiny, Thomas Gleixner, Pavel Tatashin,
	Dave Jiang, linux-nvdimm, Vishal Verma, Krzysztof Kozlowski,
	Mahesh Salgaonkar, Arnd Bergmann, Greg Kurz, Nicholas Piggin,
	Qian Cai, Cédric Le Goater, Hari Bathini, David Gibson,
	Linux MM, Greg Kroah-Hartman, Linux Kernel Mailing List,
	Andrew Morton, linuxppc-dev

On 19/11/19 1:48 pm, Alastair D'Silva wrote:
> On Tue, 2019-11-19 at 10:47 +1100, Andrew Donnellan wrote:
>> On 15/11/19 3:35 am, Dan Williams wrote:
>>>> Have you discussed with the directory owner if it's ok to split
>>>> the
>>>> driver over several files?
>>>
>>> My thought is to establish drivers/opencapi/ and move this and the
>>> existing drivers/misc/ocxl/ bits there.
>>
>> Is there any other justification for this we can think of apart from
>> not
>> wanting to put this driver in the nvdimm directory? OpenCAPI drivers
>> aren't really a category of driver unto themselves.
>>
> 
> There is a precedent for bus-based dirs, eg. drivers/(ide|w1|spi) all
> contain drivers for both controllers & connected devices.
> 
> Fred, how do you feel about moving the generic OpenCAPI driver out of
> drivers/misc?

Instinctively I don't like the idea of creating a whole opencapi 
directory, as OpenCAPI is a generic bus which is not tightly coupled to 
any particular application area, and drivers for other OpenCAPI devices 
are already spread throughout the tree (e.g. cxlflash in drivers/scsi).


-- 
Andrew Donnellan              OzLabs, ADL Canberra
ajd@linux.ibm.com             IBM Australia Limited



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory
  2019-11-19  3:26           ` Andrew Donnellan
@ 2019-11-19  4:41             ` Dan Williams
  0 siblings, 0 replies; 45+ messages in thread
From: Dan Williams @ 2019-11-19  4:41 UTC (permalink / raw)
  To: Andrew Donnellan
  Cc: Alastair D'Silva, Frederic Barrat, Oscar Salvador,
	Michal Hocko, David Hildenbrand, Alexey Kardashevskiy, Wei Yang,
	Keith Busch, Masahiro Yamada, Paul Mackerras, Ira Weiny,
	Thomas Gleixner, Pavel Tatashin, Dave Jiang, linux-nvdimm,
	Vishal Verma, Krzysztof Kozlowski, Mahesh Salgaonkar,
	Arnd Bergmann, Greg Kurz, Nicholas Piggin, Qian Cai,
	Cédric Le Goater, Hari Bathini, David Gibson, Linux MM,
	Greg Kroah-Hartman, Linux Kernel Mailing List, Andrew Morton,
	linuxppc-dev

On Mon, Nov 18, 2019 at 7:29 PM Andrew Donnellan <ajd@linux.ibm.com> wrote:
>
> On 19/11/19 1:48 pm, Alastair D'Silva wrote:
> > On Tue, 2019-11-19 at 10:47 +1100, Andrew Donnellan wrote:
> >> On 15/11/19 3:35 am, Dan Williams wrote:
> >>>> Have you discussed with the directory owner if it's ok to split
> >>>> the
> >>>> driver over several files?
> >>>
> >>> My thought is to establish drivers/opencapi/ and move this and the
> >>> existing drivers/misc/ocxl/ bits there.
> >>
> >> Is there any other justification for this we can think of apart from
> >> not
> >> wanting to put this driver in the nvdimm directory? OpenCAPI drivers
> >> aren't really a category of driver unto themselves.
> >>
> >
> > There is a precedent for bus-based dirs, eg. drivers/(ide|w1|spi) all
> > contain drivers for both controllers & connected devices.
> >
> > Fred, how do you feel about moving the generic OpenCAPI driver out of
> > drivers/misc?
>
> Instinctively I don't like the idea of creating a whole opencapi
> directory, as OpenCAPI is a generic bus which is not tightly coupled to
> any particular application area, and drivers for other OpenCAPI devices
> are already spread throughout the tree (e.g. cxlflash in drivers/scsi).

I'm not suggesting all opencapi drivers go there, nor the entirety of
this driver, just common infrastructure. That said, it's hard to talk
about specifics given the current state of the patch set. I have not
even taken a deeper look past the changelog as this 3K lines-of-code
submission needs to be broken up into smaller pieces before we settle
on what pieces belong where.

Just looking at the diffstat, at a minimum it's not appropriate for
them to live in drivers/nvdimm/ directly, drivers/nvdimm/oxcl/ would
be an acceptable starting point.


^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 09/10] powerpc: Enable OpenCAPI Storage Class Memory driver on bare metal
  2019-11-08  7:10   ` Frederic Barrat
@ 2020-01-31  4:56     ` Alastair D'Silva
  2020-01-31  5:14       ` Dan Williams
  0 siblings, 1 reply; 45+ messages in thread
From: Alastair D'Silva @ 2020-01-31  4:56 UTC (permalink / raw)
  To: Frederic Barrat
  Cc: Oscar Salvador, Madhavan Srinivasan, Geert Uytterhoeven,
	David Hildenbrand, Wei Yang, Keith Busch, linux-mm, Michal Hocko,
	Paul Mackerras, Ira Weiny, Thomas Gleixner, Pavel Tatashin,
	Dave Jiang, linux-nvdimm, Vishal Verma, Krzysztof Kozlowski,
	Anju T Sudhakar, Mahesh Salgaonkar, Andrew Donnellan,
	Arnd Bergmann, Greg Kurz, Qian Cai, Cédric Le Goater,
	Dan Williams, Hari Bathini, Greg Kroah-Hartman, linux-kernel,
	Vasant Hegde, Andrew Morton, linuxppc-dev

On Fri, 2019-11-08 at 08:10 +0100, Frederic Barrat wrote:
> 
> Le 25/10/2019 à 06:47, Alastair D'Silva a écrit :
> > From: Alastair D'Silva <alastair@d-silva.org>
> > 
> > Enable OpenCAPI Storage Class Memory driver on bare metal
> > 
> > Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> > ---
> >   arch/powerpc/configs/powernv_defconfig | 4 ++++
> >   1 file changed, 4 insertions(+)
> > 
> > diff --git a/arch/powerpc/configs/powernv_defconfig
> > b/arch/powerpc/configs/powernv_defconfig
> > index 6658cceb928c..45c0eff94964 100644
> > --- a/arch/powerpc/configs/powernv_defconfig
> > +++ b/arch/powerpc/configs/powernv_defconfig
> > @@ -352,3 +352,7 @@ CONFIG_KVM_BOOK3S_64=m
> >   CONFIG_KVM_BOOK3S_64_HV=m
> >   CONFIG_VHOST_NET=m
> >   CONFIG_PRINTK_TIME=y
> > +CONFIG_OCXL_SCM=m
> > +CONFIG_DEV_DAX=y
> > +CONFIG_DEV_DAX_PMEM=y
> > +CONFIG_FS_DAX=y
> 
> If this really the intent or do we want to activate DAX only if 
> CONFIG_OCXL_SCM is enabled?
> 
>    Fred

We had a bit of a play around with reworking this the other day.

Putting them in as depends didn't make sense, as they are "soft"
dependancies - the driver works and you can do some things without DAX.

Adding them as selects was rejected as selecting symbols that can also
be manually select is discouraged.

We ended up going full circle and adding them back to the defconfig.

-- 
Alastair D'Silva
Open Source Developer
Linux Technology Centre, IBM Australia
mob: 0423 762 819



^ permalink raw reply	[flat|nested] 45+ messages in thread

* Re: [PATCH 09/10] powerpc: Enable OpenCAPI Storage Class Memory driver on bare metal
  2020-01-31  4:56     ` Alastair D'Silva
@ 2020-01-31  5:14       ` Dan Williams
  0 siblings, 0 replies; 45+ messages in thread
From: Dan Williams @ 2020-01-31  5:14 UTC (permalink / raw)
  To: Alastair D'Silva
  Cc: Frederic Barrat, Oscar Salvador, Madhavan Srinivasan,
	Geert Uytterhoeven, David Hildenbrand, Wei Yang, Keith Busch,
	Linux MM, Michal Hocko, Paul Mackerras, Ira Weiny,
	Thomas Gleixner, Pavel Tatashin, Dave Jiang, linux-nvdimm,
	Vishal Verma, Krzysztof Kozlowski, Anju T Sudhakar,
	Mahesh Salgaonkar, Andrew Donnellan, Arnd Bergmann, Greg Kurz,
	Qian Cai, Cédric Le Goater, Hari Bathini,
	Greg Kroah-Hartman, Linux Kernel Mailing List, Vasant Hegde,
	Andrew Morton, linuxppc-dev

On Thu, Jan 30, 2020 at 8:57 PM Alastair D'Silva <alastair@au1.ibm.com> wrote:
>
> On Fri, 2019-11-08 at 08:10 +0100, Frederic Barrat wrote:
> >
> > Le 25/10/2019 à 06:47, Alastair D'Silva a écrit :
> > > From: Alastair D'Silva <alastair@d-silva.org>
> > >
> > > Enable OpenCAPI Storage Class Memory driver on bare metal
> > >
> > > Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> > > ---
> > >   arch/powerpc/configs/powernv_defconfig | 4 ++++
> > >   1 file changed, 4 insertions(+)
> > >
> > > diff --git a/arch/powerpc/configs/powernv_defconfig
> > > b/arch/powerpc/configs/powernv_defconfig
> > > index 6658cceb928c..45c0eff94964 100644
> > > --- a/arch/powerpc/configs/powernv_defconfig
> > > +++ b/arch/powerpc/configs/powernv_defconfig
> > > @@ -352,3 +352,7 @@ CONFIG_KVM_BOOK3S_64=m
> > >   CONFIG_KVM_BOOK3S_64_HV=m
> > >   CONFIG_VHOST_NET=m
> > >   CONFIG_PRINTK_TIME=y
> > > +CONFIG_OCXL_SCM=m
> > > +CONFIG_DEV_DAX=y
> > > +CONFIG_DEV_DAX_PMEM=y

This specific line is not needed since DEV_DAX_PMEM already defaults to DEV_DAX.

> > > +CONFIG_FS_DAX=y
> >
> > If this really the intent or do we want to activate DAX only if
> > CONFIG_OCXL_SCM is enabled?
> >
> >    Fred
>
> We had a bit of a play around with reworking this the other day.
>
> Putting them in as depends didn't make sense, as they are "soft"
> dependancies - the driver works and you can do some things without DAX.
>
> Adding them as selects was rejected as selecting symbols that can also
> be manually select is discouraged.
>
> We ended up going full circle and adding them back to the defconfig.

This dovetails with a suggestion Dave made a while back [1]. Given all
the pieces that need to be turned on to have a "feature complete"
persistent memory enabled build it would be nice to have general
config symbols that go and select all the necessary dependencies for
DAX, and let the rest happen by default.

[1]: https://lore.kernel.org/lkml/20161129021052.GF28177@dastard/


^ permalink raw reply	[flat|nested] 45+ messages in thread

end of thread, other threads:[~2020-01-31  5:14 UTC | newest]

Thread overview: 45+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-10-25  4:46 [PATCH 00/10] Add support for OpenCAPI SCM devices Alastair D'Silva
2019-10-25  4:46 ` [PATCH 01/10] memory_hotplug: Add a bounds check to __add_pages Alastair D'Silva
2019-10-25  4:46 ` [PATCH 02/10] nvdimm: remove prototypes for nonexistent functions Alastair D'Silva
2019-10-27 23:41   ` Andrew Donnellan
2019-11-07 17:56   ` Frederic Barrat
2019-11-15  4:30   ` Dan Williams
2019-10-25  4:46 ` [PATCH 03/10] powerpc: Add OPAL calls for LPC memory alloc/release Alastair D'Silva
2019-11-07 17:57   ` Frederic Barrat
2019-10-25  4:46 ` [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory Alastair D'Silva
2019-10-27 21:24   ` kbuild test robot
2019-10-27 21:59   ` kbuild test robot
2019-10-28  2:34   ` kbuild test robot
2019-10-29  1:49   ` Andrew Donnellan
2019-10-29  2:44     ` Alastair D'Silva
2019-10-29  2:47       ` Andrew Donnellan
2019-11-07 17:59   ` Frederic Barrat
2019-10-25  4:47 ` [PATCH 05/10] ocxl: Tally up the LPC memory on a link & allow it to be mapped Alastair D'Silva
2019-11-07 18:02   ` Frederic Barrat
2019-10-25  4:47 ` [PATCH 06/10] ocxl: Add functions to map/unmap LPC memory Alastair D'Silva
2019-11-07 18:05   ` Frederic Barrat
2019-10-25  4:47 ` [PATCH 07/10] ocxl: Save the device serial number in ocxl_fn Alastair D'Silva
2019-11-06  3:10   ` Andrew Donnellan
2019-11-07 18:18   ` Frederic Barrat
2019-10-25  4:47 ` [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory Alastair D'Silva
2019-10-27 22:19   ` kbuild test robot
2019-10-27 22:53   ` kbuild test robot
2019-10-28  1:12   ` [RFC PATCH] nvdimm: scm_get() can be static kbuild test robot
2019-11-14 13:41   ` [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory Frederic Barrat
2019-11-14 16:35     ` Dan Williams
2019-11-18 23:47       ` Andrew Donnellan
2019-11-19  0:04         ` Dan Williams
2019-11-19  2:48         ` Alastair D'Silva
2019-11-19  3:26           ` Andrew Donnellan
2019-11-19  4:41             ` Dan Williams
2019-11-18 23:01     ` Alastair D'Silva
2019-10-25  4:47 ` [PATCH 09/10] powerpc: Enable OpenCAPI Storage Class Memory driver on bare metal Alastair D'Silva
2019-10-28  2:43   ` Oliver O'Halloran
2019-11-08  7:10   ` Frederic Barrat
2020-01-31  4:56     ` Alastair D'Silva
2020-01-31  5:14       ` Dan Williams
2019-10-25  4:47 ` [PATCH 10/10] ocxl: Conditionally bind SCM devices to the generic OCXL driver Alastair D'Silva
2019-10-26  6:43   ` Christoph Hellwig
2019-11-06  3:46   ` Andrew Donnellan
2019-11-07 18:08   ` Frederic Barrat
2019-11-08  0:37     ` Alastair D'Silva

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).