2019-10-25 19:22:30

by Alastair D'Silva

[permalink] [raw]
Subject: [PATCH 00/10] Add support for OpenCAPI SCM devices

From: Alastair D'Silva <[email protected]>

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


2019-10-25 19:22:37

by Alastair D'Silva

[permalink] [raw]
Subject: [PATCH 02/10] nvdimm: remove prototypes for nonexistent functions

From: Alastair D'Silva <[email protected]>

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

Signed-off-by: Alastair D'Silva <[email protected]>
---
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

2019-10-25 19:22:44

by Alastair D'Silva

[permalink] [raw]
Subject: [PATCH 05/10] ocxl: Tally up the LPC memory on a link & allow it to be mapped

From: Alastair D'Silva <[email protected]>

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

Signed-off-by: Alastair D'Silva <[email protected]>
---
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

2019-10-25 19:22:52

by Alastair D'Silva

[permalink] [raw]
Subject: [PATCH 07/10] ocxl: Save the device serial number in ocxl_fn

From: Alastair D'Silva <[email protected]>

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 <[email protected]>
---
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

2019-10-25 19:22:58

by Alastair D'Silva

[permalink] [raw]
Subject: [PATCH 10/10] ocxl: Conditionally bind SCM devices to the generic OCXL driver

From: Alastair D'Silva <[email protected]>

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

Signed-off-by: Alastair D'Silva <[email protected]>
---
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

2019-10-25 19:22:57

by Alastair D'Silva

[permalink] [raw]
Subject: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory

From: Alastair D'Silva <[email protected]>

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 <[email protected]>
---
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

2019-10-25 19:24:29

by Alastair D'Silva

[permalink] [raw]
Subject: [PATCH 01/10] memory_hotplug: Add a bounds check to __add_pages

From: Alastair D'Silva <[email protected]>

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/[email protected]

Signed-off-by: Alastair D'Silva <[email protected]>
Reviewed-by: David Hildenbrand <[email protected]>
Acked-by: Michal Hocko <[email protected]>
---
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

2019-10-25 19:24:37

by Alastair D'Silva

[permalink] [raw]
Subject: [PATCH 03/10] powerpc: Add OPAL calls for LPC memory alloc/release

From: Alastair D'Silva <[email protected]>

Add OPAL calls for LPC memory alloc/release

Signed-off-by: Alastair D'Silva <[email protected]>
Acked-by: Andrew Donnellan <[email protected]>
---
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

2019-10-25 19:24:43

by Alastair D'Silva

[permalink] [raw]
Subject: [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory

From: Alastair D'Silva <[email protected]>

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

Signed-off-by: Alastair D'Silva <[email protected]>
---
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

2019-10-25 19:24:45

by Alastair D'Silva

[permalink] [raw]
Subject: [PATCH 06/10] ocxl: Add functions to map/unmap LPC memory

From: Alastair D'Silva <[email protected]>

Add functions to map/unmap LPC memory

Signed-off-by: Alastair D'Silva <[email protected]>
---
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

2019-10-25 19:24:59

by Alastair D'Silva

[permalink] [raw]
Subject: [PATCH 09/10] powerpc: Enable OpenCAPI Storage Class Memory driver on bare metal

From: Alastair D'Silva <[email protected]>

Enable OpenCAPI Storage Class Memory driver on bare metal

Signed-off-by: Alastair D'Silva <[email protected]>
---
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

2019-10-25 19:30:21

by Geert Uytterhoeven

[permalink] [raw]
Subject: Re: [PATCH 00/10] Add support for OpenCAPI SCM devices

Hi Alastair,

On Fri, Oct 25, 2019 at 6:48 AM Alastair D'Silva <[email protected]> wrote:
> From: Alastair D'Silva <[email protected]>
>
> This series adds support for OpenCAPI SCM devices, exposing
> them as nvdimms so that we can make use of the existing
> infrastructure.

Thanks for your series!

The long CC list is a sign of get_maintainter.pl-considered-harmful.
Please trim it (by removing me, a.o. ;-) for next submission.

Thanks!

Gr{oetje,eeting}s,

Geert

--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- [email protected]

In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds

2019-10-26 06:48:05

by Christoph Hellwig

[permalink] [raw]
Subject: Re: [PATCH 10/10] ocxl: Conditionally bind SCM devices to the generic OCXL driver

On Fri, Oct 25, 2019 at 03:47:05PM +1100, Alastair D'Silva wrote:
> From: Alastair D'Silva <[email protected]>
>
> 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.

2019-10-28 10:41:21

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory

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 <[email protected]>

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


Attachments:
(No filename) (2.49 kB)
.config.gz (6.98 kB)
Download all attachments

2019-10-28 11:44:19

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory

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 <[email protected]>

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


Attachments:
(No filename) (2.36 kB)
.config.gz (60.59 kB)
Download all attachments

2019-10-28 11:46:05

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory

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 <[email protected]>

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


Attachments:
(No filename) (8.39 kB)
.config.gz (68.50 kB)
Download all attachments

2019-10-28 12:11:22

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory

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 <[email protected]>

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 <[email protected]>
:::::: CC: Michael Ellerman <[email protected]>

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


Attachments:
(No filename) (3.49 kB)
.config.gz (55.04 kB)
Download all attachments

2019-10-28 12:44:06

by Andrew Donnellan

[permalink] [raw]
Subject: Re: [PATCH 02/10] nvdimm: remove prototypes for nonexistent functions

On 25/10/19 3:46 pm, Alastair D'Silva wrote:
> From: Alastair D'Silva <[email protected]>
>
> These functions don't exist, so remove the prototypes for them.
>
> Signed-off-by: Alastair D'Silva <[email protected]>

Indeed, they do not.

Reviewed-by: Andrew Donnellan <[email protected]>

> ---
> 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
[email protected] IBM Australia Limited

2019-10-28 12:44:20

by kernel test robot

[permalink] [raw]
Subject: [RFC PATCH] nvdimm: scm_get() can be static


Fixes: 0d40f55b9035 ("nvdimm: Add driver for OpenCAPI Storage Class Memory")
Signed-off-by: kbuild test robot <[email protected]>
---
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;

2019-10-28 12:49:32

by kernel test robot

[permalink] [raw]
Subject: Re: [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory

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 <[email protected]>


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

2019-10-28 14:03:46

by Oliver O'Halloran

[permalink] [raw]
Subject: Re: [PATCH 09/10] powerpc: Enable OpenCAPI Storage Class Memory driver on bare metal

On Fri, Oct 25, 2019 at 3:51 PM Alastair D'Silva <[email protected]> wrote:
>
> From: Alastair D'Silva <[email protected]>
>
> Enable OpenCAPI Storage Class Memory driver on bare metal
>
> Signed-off-by: Alastair D'Silva <[email protected]>
> ---
> 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 -- [email protected]
> To unsubscribe send an email to [email protected]

2019-10-29 07:22:42

by Andrew Donnellan

[permalink] [raw]
Subject: Re: [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory

On 25/10/19 3:46 pm, Alastair D'Silva wrote:
> From: Alastair D'Silva <[email protected]>
>
> This patch adds platform support to map & release LPC memory.
> > Signed-off-by: Alastair D'Silva <[email protected]>
> ---
> 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
[email protected] IBM Australia Limited

2019-10-29 07:25:57

by Alastair D'Silva

[permalink] [raw]
Subject: Re: [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory

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 <[email protected]>
> >
> > This patch adds platform support to map & release LPC memory.
> > > Signed-off-by: Alastair D'Silva <[email protected]>
> > ---
> > 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

2019-10-29 07:25:59

by Andrew Donnellan

[permalink] [raw]
Subject: Re: [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory

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
[email protected] IBM Australia Limited

2019-11-06 03:15:06

by Andrew Donnellan

[permalink] [raw]
Subject: Re: [PATCH 07/10] ocxl: Save the device serial number in ocxl_fn

On 25/10/19 3:47 pm, Alastair D'Silva wrote:
> From: Alastair D'Silva <[email protected]>
>
> 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 <[email protected]>

Acked-by: Andrew Donnellan <[email protected]>


--
Andrew Donnellan OzLabs, ADL Canberra
[email protected] IBM Australia Limited

2019-11-06 03:48:26

by Andrew Donnellan

[permalink] [raw]
Subject: Re: [PATCH 10/10] ocxl: Conditionally bind SCM devices to the generic OCXL driver

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

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
[email protected] IBM Australia Limited

2019-11-07 17:58:14

by Frederic Barrat

[permalink] [raw]
Subject: Re: [PATCH 02/10] nvdimm: remove prototypes for nonexistent functions



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


Reviewed-by: Frederic Barrat <[email protected]>


> 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);
>

2019-11-07 18:00:44

by Frederic Barrat

[permalink] [raw]
Subject: Re: [PATCH 03/10] powerpc: Add OPAL calls for LPC memory alloc/release



Le 25/10/2019 à 06:46, Alastair D'Silva a écrit :
> From: Alastair D'Silva <[email protected]>
>
> Add OPAL calls for LPC memory alloc/release
>
> Signed-off-by: Alastair D'Silva <[email protected]>
> Acked-by: Andrew Donnellan <[email protected]>
> ---


Acked-by: Frederic Barrat <[email protected]>



> 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);
>

2019-11-07 18:01:31

by Frederic Barrat

[permalink] [raw]
Subject: Re: [PATCH 04/10] powerpc: Map & release OpenCAPI LPC memory



Le 25/10/2019 à 06:46, Alastair D'Silva a écrit :
> From: Alastair D'Silva <[email protected]>
>
> This patch adds platform support to map & release LPC memory.
>
> Signed-off-by: Alastair D'Silva <[email protected]>
> ---
> 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
>

2019-11-07 18:04:40

by Frederic Barrat

[permalink] [raw]
Subject: Re: [PATCH 05/10] ocxl: Tally up the LPC memory on a link & allow it to be mapped



Le 25/10/2019 à 06:47, Alastair D'Silva a écrit :
> From: Alastair D'Silva <[email protected]>
>
> Tally up the LPC memory on an OpenCAPI link & allow it to be mapped
>
> Signed-off-by: Alastair D'Silva <[email protected]>
> ---
> 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_ */
>

2019-11-07 18:06:52

by Frederic Barrat

[permalink] [raw]
Subject: Re: [PATCH 06/10] ocxl: Add functions to map/unmap LPC memory



Le 25/10/2019 à 06:47, Alastair D'Silva a écrit :
> From: Alastair D'Silva <[email protected]>
>
> Add functions to map/unmap LPC memory
>
> Signed-off-by: Alastair D'Silva <[email protected]>
> ---
> 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
> *
>

2019-11-07 18:10:12

by Frederic Barrat

[permalink] [raw]
Subject: Re: [PATCH 10/10] ocxl: Conditionally bind SCM devices to the generic OCXL driver



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


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);
>

2019-11-07 18:20:09

by Frederic Barrat

[permalink] [raw]
Subject: Re: [PATCH 07/10] ocxl: Save the device serial number in ocxl_fn



Le 25/10/2019 à 06:47, Alastair D'Silva a écrit :
> From: Alastair D'Silva <[email protected]>
>
> 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 <[email protected]>
> ---


Acked-by: Frederic Barrat <[email protected]>



> 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 {
>

2019-11-08 00:38:36

by Alastair D'Silva

[permalink] [raw]
Subject: Re: [PATCH 10/10] ocxl: Conditionally bind SCM devices to the generic OCXL driver

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 <[email protected]>
> >
> > This patch allows the user to bind OpenCAPI SCM devices to the
> > generic OCXL
> > driver.
> >
> > Signed-off-by: Alastair D'Silva <[email protected]>
> > ---
>
> 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

2019-11-08 07:12:25

by Frederic Barrat

[permalink] [raw]
Subject: Re: [PATCH 09/10] powerpc: Enable OpenCAPI Storage Class Memory driver on bare metal



Le 25/10/2019 à 06:47, Alastair D'Silva a écrit :
> From: Alastair D'Silva <[email protected]>
>
> Enable OpenCAPI Storage Class Memory driver on bare metal
>
> Signed-off-by: Alastair D'Silva <[email protected]>
> ---
> 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

2019-11-14 13:44:17

by Frederic Barrat

[permalink] [raw]
Subject: Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory

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 <[email protected]>
>
> 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 <[email protected]>
> ---
> 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;
>
>

2019-11-14 16:37:44

by Dan Williams

[permalink] [raw]
Subject: Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory

Some quick feedback on your intro concerns...

On Thu, Nov 14, 2019 at 5:41 AM Frederic Barrat <[email protected]> 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.

2019-11-15 04:34:47

by Dan Williams

[permalink] [raw]
Subject: Re: [PATCH 02/10] nvdimm: remove prototypes for nonexistent functions

On Thu, Oct 24, 2019 at 9:49 PM Alastair D'Silva <[email protected]> wrote:
>
> From: Alastair D'Silva <[email protected]>
>
> These functions don't exist, so remove the prototypes for them.
>
> Signed-off-by: Alastair D'Silva <[email protected]>

Looks good, applied.

2019-11-18 23:06:15

by Alastair D'Silva

[permalink] [raw]
Subject: Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory

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 <[email protected]>
> >
> > 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 <[email protected]>
> > ---
> > 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

2019-11-18 23:49:57

by Andrew Donnellan

[permalink] [raw]
Subject: Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory

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
[email protected] IBM Australia Limited

2019-11-19 00:06:41

by Dan Williams

[permalink] [raw]
Subject: Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory

On Mon, Nov 18, 2019 at 3:48 PM Andrew Donnellan <[email protected]> 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.

2019-11-19 02:53:26

by Alastair D'Silva

[permalink] [raw]
Subject: Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory

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

2019-11-19 03:28:40

by Andrew Donnellan

[permalink] [raw]
Subject: Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory

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
[email protected] IBM Australia Limited

2019-11-19 04:43:23

by Dan Williams

[permalink] [raw]
Subject: Re: [PATCH 08/10] nvdimm: Add driver for OpenCAPI Storage Class Memory

On Mon, Nov 18, 2019 at 7:29 PM Andrew Donnellan <[email protected]> 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.

2020-01-31 04:59:57

by Alastair D'Silva

[permalink] [raw]
Subject: Re: [PATCH 09/10] powerpc: Enable OpenCAPI Storage Class Memory driver on bare metal

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 <[email protected]>
> >
> > Enable OpenCAPI Storage Class Memory driver on bare metal
> >
> > Signed-off-by: Alastair D'Silva <[email protected]>
> > ---
> > 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

2020-01-31 05:16:24

by Dan Williams

[permalink] [raw]
Subject: Re: [PATCH 09/10] powerpc: Enable OpenCAPI Storage Class Memory driver on bare metal

On Thu, Jan 30, 2020 at 8:57 PM Alastair D'Silva <[email protected]> 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 <[email protected]>
> > >
> > > Enable OpenCAPI Storage Class Memory driver on bare metal
> > >
> > > Signed-off-by: Alastair D'Silva <[email protected]>
> > > ---
> > > 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/