2021-08-02 10:29:49 -07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
/* Copyright(c) 2020 Intel Corporation. */
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/slab.h>
|
2021-09-14 12:08:40 -07:00
|
|
|
#include <linux/idr.h>
|
2021-08-02 10:29:49 -07:00
|
|
|
#include <cxlmem.h>
|
|
|
|
#include <cxl.h>
|
|
|
|
#include "core.h"
|
|
|
|
|
2021-09-03 19:21:01 -07:00
|
|
|
/**
|
|
|
|
* DOC: cxl pmem
|
|
|
|
*
|
|
|
|
* The core CXL PMEM infrastructure supports persistent memory
|
|
|
|
* provisioning and serves as a bridge to the LIBNVDIMM subsystem. A CXL
|
|
|
|
* 'bridge' device is added at the root of a CXL device topology if
|
|
|
|
* platform firmware advertises at least one persistent memory capable
|
|
|
|
* CXL window. That root-level bridge corresponds to a LIBNVDIMM 'bus'
|
|
|
|
* device. Then for each cxl_memdev in the CXL device topology a bridge
|
|
|
|
* device is added to host a LIBNVDIMM dimm object. When these bridges
|
|
|
|
* are registered native LIBNVDIMM uapis are translated to CXL
|
|
|
|
* operations, for example, namespace label access commands.
|
|
|
|
*/
|
|
|
|
|
2021-09-14 12:08:40 -07:00
|
|
|
static DEFINE_IDA(cxl_nvdimm_bridge_ida);
|
|
|
|
|
2021-08-02 10:29:49 -07:00
|
|
|
static void cxl_nvdimm_bridge_release(struct device *dev)
|
|
|
|
{
|
|
|
|
struct cxl_nvdimm_bridge *cxl_nvb = to_cxl_nvdimm_bridge(dev);
|
|
|
|
|
2021-09-14 12:08:40 -07:00
|
|
|
ida_free(&cxl_nvdimm_bridge_ida, cxl_nvb->id);
|
2021-08-02 10:29:49 -07:00
|
|
|
kfree(cxl_nvb);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct attribute_group *cxl_nvdimm_bridge_attribute_groups[] = {
|
|
|
|
&cxl_base_attribute_group,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct device_type cxl_nvdimm_bridge_type = {
|
|
|
|
.name = "cxl_nvdimm_bridge",
|
|
|
|
.release = cxl_nvdimm_bridge_release,
|
|
|
|
.groups = cxl_nvdimm_bridge_attribute_groups,
|
|
|
|
};
|
|
|
|
|
|
|
|
struct cxl_nvdimm_bridge *to_cxl_nvdimm_bridge(struct device *dev)
|
|
|
|
{
|
|
|
|
if (dev_WARN_ONCE(dev, dev->type != &cxl_nvdimm_bridge_type,
|
|
|
|
"not a cxl_nvdimm_bridge device\n"))
|
|
|
|
return NULL;
|
|
|
|
return container_of(dev, struct cxl_nvdimm_bridge, dev);
|
|
|
|
}
|
2021-11-12 17:32:58 -07:00
|
|
|
EXPORT_SYMBOL_NS_GPL(to_cxl_nvdimm_bridge, CXL);
|
2021-08-02 10:29:49 -07:00
|
|
|
|
2021-11-11 11:19:05 -07:00
|
|
|
bool is_cxl_nvdimm_bridge(struct device *dev)
|
2021-09-14 12:08:40 -07:00
|
|
|
{
|
|
|
|
return dev->type == &cxl_nvdimm_bridge_type;
|
|
|
|
}
|
2021-11-11 11:19:05 -07:00
|
|
|
EXPORT_SYMBOL_NS_GPL(is_cxl_nvdimm_bridge, CXL);
|
|
|
|
|
2022-01-31 17:34:40 -07:00
|
|
|
static int match_nvdimm_bridge(struct device *dev, void *data)
|
2021-11-11 11:19:05 -07:00
|
|
|
{
|
|
|
|
return is_cxl_nvdimm_bridge(dev);
|
|
|
|
}
|
2021-09-14 12:08:40 -07:00
|
|
|
|
2024-06-11 23:44:23 -07:00
|
|
|
/**
|
|
|
|
* cxl_find_nvdimm_bridge() - find a bridge device relative to a port
|
|
|
|
* @port: any descendant port of an nvdimm-bridge associated
|
|
|
|
* root-cxl-port
|
|
|
|
*/
|
|
|
|
struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct cxl_port *port)
|
2021-09-14 12:08:40 -07:00
|
|
|
{
|
2024-06-11 23:44:23 -07:00
|
|
|
struct cxl_root *cxl_root __free(put_cxl_root) = find_cxl_root(port);
|
2021-09-14 12:08:40 -07:00
|
|
|
struct device *dev;
|
|
|
|
|
2024-01-05 15:07:40 -07:00
|
|
|
if (!cxl_root)
|
2022-01-31 17:34:40 -07:00
|
|
|
return NULL;
|
|
|
|
|
2024-01-05 15:07:53 -07:00
|
|
|
dev = device_find_child(&cxl_root->port.dev, NULL, match_nvdimm_bridge);
|
2022-01-31 17:34:40 -07:00
|
|
|
|
2021-09-14 12:08:40 -07:00
|
|
|
if (!dev)
|
|
|
|
return NULL;
|
2022-01-31 17:34:40 -07:00
|
|
|
|
2021-09-14 12:08:40 -07:00
|
|
|
return to_cxl_nvdimm_bridge(dev);
|
|
|
|
}
|
2021-11-12 17:32:58 -07:00
|
|
|
EXPORT_SYMBOL_NS_GPL(cxl_find_nvdimm_bridge, CXL);
|
2021-09-14 12:08:40 -07:00
|
|
|
|
2022-04-21 08:33:13 -07:00
|
|
|
static struct lock_class_key cxl_nvdimm_bridge_key;
|
|
|
|
|
2022-01-31 17:34:40 -07:00
|
|
|
static struct cxl_nvdimm_bridge *cxl_nvdimm_bridge_alloc(struct cxl_port *port)
|
2021-08-02 10:29:49 -07:00
|
|
|
{
|
|
|
|
struct cxl_nvdimm_bridge *cxl_nvb;
|
|
|
|
struct device *dev;
|
2021-09-14 12:08:40 -07:00
|
|
|
int rc;
|
2021-08-02 10:29:49 -07:00
|
|
|
|
|
|
|
cxl_nvb = kzalloc(sizeof(*cxl_nvb), GFP_KERNEL);
|
|
|
|
if (!cxl_nvb)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
2021-09-14 12:08:40 -07:00
|
|
|
rc = ida_alloc(&cxl_nvdimm_bridge_ida, GFP_KERNEL);
|
|
|
|
if (rc < 0)
|
|
|
|
goto err;
|
|
|
|
cxl_nvb->id = rc;
|
|
|
|
|
2021-08-02 10:29:49 -07:00
|
|
|
dev = &cxl_nvb->dev;
|
|
|
|
cxl_nvb->port = port;
|
|
|
|
device_initialize(dev);
|
2022-04-21 08:33:13 -07:00
|
|
|
lockdep_set_class(&dev->mutex, &cxl_nvdimm_bridge_key);
|
2021-08-02 10:29:49 -07:00
|
|
|
device_set_pm_not_required(dev);
|
|
|
|
dev->parent = &port->dev;
|
|
|
|
dev->bus = &cxl_bus_type;
|
|
|
|
dev->type = &cxl_nvdimm_bridge_type;
|
|
|
|
|
|
|
|
return cxl_nvb;
|
2021-09-14 12:08:40 -07:00
|
|
|
|
|
|
|
err:
|
|
|
|
kfree(cxl_nvb);
|
|
|
|
return ERR_PTR(rc);
|
2021-08-02 10:29:49 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
static void unregister_nvb(void *_cxl_nvb)
|
|
|
|
{
|
|
|
|
struct cxl_nvdimm_bridge *cxl_nvb = _cxl_nvb;
|
2022-12-01 14:33:43 -07:00
|
|
|
|
2021-08-02 10:29:49 -07:00
|
|
|
device_unregister(&cxl_nvb->dev);
|
|
|
|
}
|
|
|
|
|
2021-09-03 19:21:01 -07:00
|
|
|
/**
|
|
|
|
* devm_cxl_add_nvdimm_bridge() - add the root of a LIBNVDIMM topology
|
|
|
|
* @host: platform firmware root device
|
|
|
|
* @port: CXL port at the root of a CXL topology
|
|
|
|
*
|
|
|
|
* Return: bridge device that can host cxl_nvdimm objects
|
|
|
|
*/
|
2021-08-02 10:29:49 -07:00
|
|
|
struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host,
|
|
|
|
struct cxl_port *port)
|
|
|
|
{
|
|
|
|
struct cxl_nvdimm_bridge *cxl_nvb;
|
|
|
|
struct device *dev;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (!IS_ENABLED(CONFIG_CXL_PMEM))
|
|
|
|
return ERR_PTR(-ENXIO);
|
|
|
|
|
|
|
|
cxl_nvb = cxl_nvdimm_bridge_alloc(port);
|
|
|
|
if (IS_ERR(cxl_nvb))
|
|
|
|
return cxl_nvb;
|
|
|
|
|
|
|
|
dev = &cxl_nvb->dev;
|
2021-09-14 12:08:40 -07:00
|
|
|
rc = dev_set_name(dev, "nvdimm-bridge%d", cxl_nvb->id);
|
2021-08-02 10:29:49 -07:00
|
|
|
if (rc)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
rc = device_add(dev);
|
|
|
|
if (rc)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
rc = devm_add_action_or_reset(host, unregister_nvb, cxl_nvb);
|
|
|
|
if (rc)
|
|
|
|
return ERR_PTR(rc);
|
|
|
|
|
|
|
|
return cxl_nvb;
|
|
|
|
|
|
|
|
err:
|
|
|
|
put_device(dev);
|
|
|
|
return ERR_PTR(rc);
|
|
|
|
}
|
2021-11-12 17:32:58 -07:00
|
|
|
EXPORT_SYMBOL_NS_GPL(devm_cxl_add_nvdimm_bridge, CXL);
|
2021-08-02 10:29:49 -07:00
|
|
|
|
|
|
|
static void cxl_nvdimm_release(struct device *dev)
|
|
|
|
{
|
|
|
|
struct cxl_nvdimm *cxl_nvd = to_cxl_nvdimm(dev);
|
|
|
|
|
|
|
|
kfree(cxl_nvd);
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct attribute_group *cxl_nvdimm_attribute_groups[] = {
|
|
|
|
&cxl_base_attribute_group,
|
|
|
|
NULL,
|
|
|
|
};
|
|
|
|
|
|
|
|
const struct device_type cxl_nvdimm_type = {
|
|
|
|
.name = "cxl_nvdimm",
|
|
|
|
.release = cxl_nvdimm_release,
|
|
|
|
.groups = cxl_nvdimm_attribute_groups,
|
|
|
|
};
|
|
|
|
|
|
|
|
bool is_cxl_nvdimm(struct device *dev)
|
|
|
|
{
|
|
|
|
return dev->type == &cxl_nvdimm_type;
|
|
|
|
}
|
2021-11-12 17:32:58 -07:00
|
|
|
EXPORT_SYMBOL_NS_GPL(is_cxl_nvdimm, CXL);
|
2021-08-02 10:29:49 -07:00
|
|
|
|
|
|
|
struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev)
|
|
|
|
{
|
|
|
|
if (dev_WARN_ONCE(dev, !is_cxl_nvdimm(dev),
|
|
|
|
"not a cxl_nvdimm device\n"))
|
|
|
|
return NULL;
|
|
|
|
return container_of(dev, struct cxl_nvdimm, dev);
|
|
|
|
}
|
2021-11-12 17:32:58 -07:00
|
|
|
EXPORT_SYMBOL_NS_GPL(to_cxl_nvdimm, CXL);
|
2021-08-02 10:29:49 -07:00
|
|
|
|
2022-04-21 08:33:13 -07:00
|
|
|
static struct lock_class_key cxl_nvdimm_key;
|
|
|
|
|
cxl/pmem: Refactor nvdimm device registration, delete the workqueue
The three objects 'struct cxl_nvdimm_bridge', 'struct cxl_nvdimm', and
'struct cxl_pmem_region' manage CXL persistent memory resources. The
bridge represents base platform resources, the nvdimm represents one or
more endpoints, and the region is a collection of nvdimms that
contribute to an assembled address range.
Their relationship is such that a region is torn down if any component
endpoints are removed. All regions and endpoints are torn down if the
foundational bridge device goes down.
A workqueue was deployed to manage these interdependencies, but it is
difficult to reason about, and fragile. A recent attempt to take the CXL
root device lock in the cxl_mem driver was reported by lockdep as
colliding with the flush_work() in the cxl_pmem flows.
Instead of the workqueue, arrange for all pmem/nvdimm devices to be torn
down immediately and hierarchically. A similar change is made to both
the 'cxl_nvdimm' and 'cxl_pmem_region' objects. For bisect-ability both
changes are made in the same patch which unfortunately makes the patch
bigger than desired.
Arrange for cxl_memdev and cxl_region to register a cxl_nvdimm and
cxl_pmem_region as a devres release action of the bridge device.
Additionally, include a devres release action of the cxl_memdev or
cxl_region device that triggers the bridge's release action if an endpoint
exits before the bridge. I.e. this allows either unplugging the bridge,
or unplugging and endpoint to result in the same cleanup actions.
To keep the patch smaller the cleanup of the now defunct workqueue
infrastructure is saved for a follow-on patch.
Tested-by: Robert Richter <rrichter@amd.com>
Link: https://lore.kernel.org/r/166993041773.1882361.16444301376147207609.stgit@dwillia2-xfh.jf.intel.com
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2022-12-01 14:33:37 -07:00
|
|
|
static struct cxl_nvdimm *cxl_nvdimm_alloc(struct cxl_nvdimm_bridge *cxl_nvb,
|
|
|
|
struct cxl_memdev *cxlmd)
|
2021-08-02 10:29:49 -07:00
|
|
|
{
|
|
|
|
struct cxl_nvdimm *cxl_nvd;
|
|
|
|
struct device *dev;
|
|
|
|
|
|
|
|
cxl_nvd = kzalloc(sizeof(*cxl_nvd), GFP_KERNEL);
|
|
|
|
if (!cxl_nvd)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
dev = &cxl_nvd->dev;
|
|
|
|
cxl_nvd->cxlmd = cxlmd;
|
cxl/pmem: Refactor nvdimm device registration, delete the workqueue
The three objects 'struct cxl_nvdimm_bridge', 'struct cxl_nvdimm', and
'struct cxl_pmem_region' manage CXL persistent memory resources. The
bridge represents base platform resources, the nvdimm represents one or
more endpoints, and the region is a collection of nvdimms that
contribute to an assembled address range.
Their relationship is such that a region is torn down if any component
endpoints are removed. All regions and endpoints are torn down if the
foundational bridge device goes down.
A workqueue was deployed to manage these interdependencies, but it is
difficult to reason about, and fragile. A recent attempt to take the CXL
root device lock in the cxl_mem driver was reported by lockdep as
colliding with the flush_work() in the cxl_pmem flows.
Instead of the workqueue, arrange for all pmem/nvdimm devices to be torn
down immediately and hierarchically. A similar change is made to both
the 'cxl_nvdimm' and 'cxl_pmem_region' objects. For bisect-ability both
changes are made in the same patch which unfortunately makes the patch
bigger than desired.
Arrange for cxl_memdev and cxl_region to register a cxl_nvdimm and
cxl_pmem_region as a devres release action of the bridge device.
Additionally, include a devres release action of the cxl_memdev or
cxl_region device that triggers the bridge's release action if an endpoint
exits before the bridge. I.e. this allows either unplugging the bridge,
or unplugging and endpoint to result in the same cleanup actions.
To keep the patch smaller the cleanup of the now defunct workqueue
infrastructure is saved for a follow-on patch.
Tested-by: Robert Richter <rrichter@amd.com>
Link: https://lore.kernel.org/r/166993041773.1882361.16444301376147207609.stgit@dwillia2-xfh.jf.intel.com
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2022-12-01 14:33:37 -07:00
|
|
|
cxlmd->cxl_nvd = cxl_nvd;
|
2021-08-02 10:29:49 -07:00
|
|
|
device_initialize(dev);
|
2022-04-21 08:33:13 -07:00
|
|
|
lockdep_set_class(&dev->mutex, &cxl_nvdimm_key);
|
2021-08-02 10:29:49 -07:00
|
|
|
device_set_pm_not_required(dev);
|
|
|
|
dev->parent = &cxlmd->dev;
|
|
|
|
dev->bus = &cxl_bus_type;
|
|
|
|
dev->type = &cxl_nvdimm_type;
|
2022-12-01 15:03:19 -07:00
|
|
|
/*
|
|
|
|
* A "%llx" string is 17-bytes vs dimm_id that is max
|
|
|
|
* NVDIMM_KEY_DESC_LEN
|
|
|
|
*/
|
|
|
|
BUILD_BUG_ON(sizeof(cxl_nvd->dev_id) < 17 ||
|
|
|
|
sizeof(cxl_nvd->dev_id) > NVDIMM_KEY_DESC_LEN);
|
|
|
|
sprintf(cxl_nvd->dev_id, "%llx", cxlmd->cxlds->serial);
|
2021-08-02 10:29:49 -07:00
|
|
|
|
|
|
|
return cxl_nvd;
|
|
|
|
}
|
|
|
|
|
2023-01-20 17:26:12 -07:00
|
|
|
static void cxlmd_release_nvdimm(void *_cxlmd)
|
2021-09-21 12:22:16 -07:00
|
|
|
{
|
2023-01-20 17:26:12 -07:00
|
|
|
struct cxl_memdev *cxlmd = _cxlmd;
|
|
|
|
struct cxl_nvdimm *cxl_nvd = cxlmd->cxl_nvd;
|
cxl/pmem: Refactor nvdimm device registration, delete the workqueue
The three objects 'struct cxl_nvdimm_bridge', 'struct cxl_nvdimm', and
'struct cxl_pmem_region' manage CXL persistent memory resources. The
bridge represents base platform resources, the nvdimm represents one or
more endpoints, and the region is a collection of nvdimms that
contribute to an assembled address range.
Their relationship is such that a region is torn down if any component
endpoints are removed. All regions and endpoints are torn down if the
foundational bridge device goes down.
A workqueue was deployed to manage these interdependencies, but it is
difficult to reason about, and fragile. A recent attempt to take the CXL
root device lock in the cxl_mem driver was reported by lockdep as
colliding with the flush_work() in the cxl_pmem flows.
Instead of the workqueue, arrange for all pmem/nvdimm devices to be torn
down immediately and hierarchically. A similar change is made to both
the 'cxl_nvdimm' and 'cxl_pmem_region' objects. For bisect-ability both
changes are made in the same patch which unfortunately makes the patch
bigger than desired.
Arrange for cxl_memdev and cxl_region to register a cxl_nvdimm and
cxl_pmem_region as a devres release action of the bridge device.
Additionally, include a devres release action of the cxl_memdev or
cxl_region device that triggers the bridge's release action if an endpoint
exits before the bridge. I.e. this allows either unplugging the bridge,
or unplugging and endpoint to result in the same cleanup actions.
To keep the patch smaller the cleanup of the now defunct workqueue
infrastructure is saved for a follow-on patch.
Tested-by: Robert Richter <rrichter@amd.com>
Link: https://lore.kernel.org/r/166993041773.1882361.16444301376147207609.stgit@dwillia2-xfh.jf.intel.com
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2022-12-01 14:33:37 -07:00
|
|
|
struct cxl_nvdimm_bridge *cxl_nvb = cxlmd->cxl_nvb;
|
|
|
|
|
|
|
|
cxl_nvd->cxlmd = NULL;
|
|
|
|
cxlmd->cxl_nvd = NULL;
|
2023-01-20 17:26:12 -07:00
|
|
|
cxlmd->cxl_nvb = NULL;
|
cxl/pmem: Refactor nvdimm device registration, delete the workqueue
The three objects 'struct cxl_nvdimm_bridge', 'struct cxl_nvdimm', and
'struct cxl_pmem_region' manage CXL persistent memory resources. The
bridge represents base platform resources, the nvdimm represents one or
more endpoints, and the region is a collection of nvdimms that
contribute to an assembled address range.
Their relationship is such that a region is torn down if any component
endpoints are removed. All regions and endpoints are torn down if the
foundational bridge device goes down.
A workqueue was deployed to manage these interdependencies, but it is
difficult to reason about, and fragile. A recent attempt to take the CXL
root device lock in the cxl_mem driver was reported by lockdep as
colliding with the flush_work() in the cxl_pmem flows.
Instead of the workqueue, arrange for all pmem/nvdimm devices to be torn
down immediately and hierarchically. A similar change is made to both
the 'cxl_nvdimm' and 'cxl_pmem_region' objects. For bisect-ability both
changes are made in the same patch which unfortunately makes the patch
bigger than desired.
Arrange for cxl_memdev and cxl_region to register a cxl_nvdimm and
cxl_pmem_region as a devres release action of the bridge device.
Additionally, include a devres release action of the cxl_memdev or
cxl_region device that triggers the bridge's release action if an endpoint
exits before the bridge. I.e. this allows either unplugging the bridge,
or unplugging and endpoint to result in the same cleanup actions.
To keep the patch smaller the cleanup of the now defunct workqueue
infrastructure is saved for a follow-on patch.
Tested-by: Robert Richter <rrichter@amd.com>
Link: https://lore.kernel.org/r/166993041773.1882361.16444301376147207609.stgit@dwillia2-xfh.jf.intel.com
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2022-12-01 14:33:37 -07:00
|
|
|
device_unregister(&cxl_nvd->dev);
|
|
|
|
put_device(&cxl_nvb->dev);
|
2021-09-21 12:22:16 -07:00
|
|
|
}
|
|
|
|
|
2021-09-03 19:21:01 -07:00
|
|
|
/**
|
|
|
|
* devm_cxl_add_nvdimm() - add a bridge between a cxl_memdev and an nvdimm
|
2024-06-11 23:44:23 -07:00
|
|
|
* @parent_port: parent port for the (to be added) @cxlmd endpoint port
|
2021-09-03 19:21:01 -07:00
|
|
|
* @cxlmd: cxl_memdev instance that will perform LIBNVDIMM operations
|
|
|
|
*
|
|
|
|
* Return: 0 on success negative error code on failure.
|
|
|
|
*/
|
2024-06-11 23:44:23 -07:00
|
|
|
int devm_cxl_add_nvdimm(struct cxl_port *parent_port,
|
|
|
|
struct cxl_memdev *cxlmd)
|
2021-08-02 10:29:49 -07:00
|
|
|
{
|
cxl/pmem: Refactor nvdimm device registration, delete the workqueue
The three objects 'struct cxl_nvdimm_bridge', 'struct cxl_nvdimm', and
'struct cxl_pmem_region' manage CXL persistent memory resources. The
bridge represents base platform resources, the nvdimm represents one or
more endpoints, and the region is a collection of nvdimms that
contribute to an assembled address range.
Their relationship is such that a region is torn down if any component
endpoints are removed. All regions and endpoints are torn down if the
foundational bridge device goes down.
A workqueue was deployed to manage these interdependencies, but it is
difficult to reason about, and fragile. A recent attempt to take the CXL
root device lock in the cxl_mem driver was reported by lockdep as
colliding with the flush_work() in the cxl_pmem flows.
Instead of the workqueue, arrange for all pmem/nvdimm devices to be torn
down immediately and hierarchically. A similar change is made to both
the 'cxl_nvdimm' and 'cxl_pmem_region' objects. For bisect-ability both
changes are made in the same patch which unfortunately makes the patch
bigger than desired.
Arrange for cxl_memdev and cxl_region to register a cxl_nvdimm and
cxl_pmem_region as a devres release action of the bridge device.
Additionally, include a devres release action of the cxl_memdev or
cxl_region device that triggers the bridge's release action if an endpoint
exits before the bridge. I.e. this allows either unplugging the bridge,
or unplugging and endpoint to result in the same cleanup actions.
To keep the patch smaller the cleanup of the now defunct workqueue
infrastructure is saved for a follow-on patch.
Tested-by: Robert Richter <rrichter@amd.com>
Link: https://lore.kernel.org/r/166993041773.1882361.16444301376147207609.stgit@dwillia2-xfh.jf.intel.com
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2022-12-01 14:33:37 -07:00
|
|
|
struct cxl_nvdimm_bridge *cxl_nvb;
|
2021-08-02 10:29:49 -07:00
|
|
|
struct cxl_nvdimm *cxl_nvd;
|
|
|
|
struct device *dev;
|
|
|
|
int rc;
|
|
|
|
|
2024-06-11 23:44:23 -07:00
|
|
|
cxl_nvb = cxl_find_nvdimm_bridge(parent_port);
|
cxl/pmem: Refactor nvdimm device registration, delete the workqueue
The three objects 'struct cxl_nvdimm_bridge', 'struct cxl_nvdimm', and
'struct cxl_pmem_region' manage CXL persistent memory resources. The
bridge represents base platform resources, the nvdimm represents one or
more endpoints, and the region is a collection of nvdimms that
contribute to an assembled address range.
Their relationship is such that a region is torn down if any component
endpoints are removed. All regions and endpoints are torn down if the
foundational bridge device goes down.
A workqueue was deployed to manage these interdependencies, but it is
difficult to reason about, and fragile. A recent attempt to take the CXL
root device lock in the cxl_mem driver was reported by lockdep as
colliding with the flush_work() in the cxl_pmem flows.
Instead of the workqueue, arrange for all pmem/nvdimm devices to be torn
down immediately and hierarchically. A similar change is made to both
the 'cxl_nvdimm' and 'cxl_pmem_region' objects. For bisect-ability both
changes are made in the same patch which unfortunately makes the patch
bigger than desired.
Arrange for cxl_memdev and cxl_region to register a cxl_nvdimm and
cxl_pmem_region as a devres release action of the bridge device.
Additionally, include a devres release action of the cxl_memdev or
cxl_region device that triggers the bridge's release action if an endpoint
exits before the bridge. I.e. this allows either unplugging the bridge,
or unplugging and endpoint to result in the same cleanup actions.
To keep the patch smaller the cleanup of the now defunct workqueue
infrastructure is saved for a follow-on patch.
Tested-by: Robert Richter <rrichter@amd.com>
Link: https://lore.kernel.org/r/166993041773.1882361.16444301376147207609.stgit@dwillia2-xfh.jf.intel.com
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2022-12-01 14:33:37 -07:00
|
|
|
if (!cxl_nvb)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
cxl_nvd = cxl_nvdimm_alloc(cxl_nvb, cxlmd);
|
|
|
|
if (IS_ERR(cxl_nvd)) {
|
|
|
|
rc = PTR_ERR(cxl_nvd);
|
|
|
|
goto err_alloc;
|
|
|
|
}
|
|
|
|
cxlmd->cxl_nvb = cxl_nvb;
|
2021-08-02 10:29:49 -07:00
|
|
|
|
|
|
|
dev = &cxl_nvd->dev;
|
|
|
|
rc = dev_set_name(dev, "pmem%d", cxlmd->id);
|
|
|
|
if (rc)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
rc = device_add(dev);
|
|
|
|
if (rc)
|
|
|
|
goto err;
|
|
|
|
|
cxl/pmem: Refactor nvdimm device registration, delete the workqueue
The three objects 'struct cxl_nvdimm_bridge', 'struct cxl_nvdimm', and
'struct cxl_pmem_region' manage CXL persistent memory resources. The
bridge represents base platform resources, the nvdimm represents one or
more endpoints, and the region is a collection of nvdimms that
contribute to an assembled address range.
Their relationship is such that a region is torn down if any component
endpoints are removed. All regions and endpoints are torn down if the
foundational bridge device goes down.
A workqueue was deployed to manage these interdependencies, but it is
difficult to reason about, and fragile. A recent attempt to take the CXL
root device lock in the cxl_mem driver was reported by lockdep as
colliding with the flush_work() in the cxl_pmem flows.
Instead of the workqueue, arrange for all pmem/nvdimm devices to be torn
down immediately and hierarchically. A similar change is made to both
the 'cxl_nvdimm' and 'cxl_pmem_region' objects. For bisect-ability both
changes are made in the same patch which unfortunately makes the patch
bigger than desired.
Arrange for cxl_memdev and cxl_region to register a cxl_nvdimm and
cxl_pmem_region as a devres release action of the bridge device.
Additionally, include a devres release action of the cxl_memdev or
cxl_region device that triggers the bridge's release action if an endpoint
exits before the bridge. I.e. this allows either unplugging the bridge,
or unplugging and endpoint to result in the same cleanup actions.
To keep the patch smaller the cleanup of the now defunct workqueue
infrastructure is saved for a follow-on patch.
Tested-by: Robert Richter <rrichter@amd.com>
Link: https://lore.kernel.org/r/166993041773.1882361.16444301376147207609.stgit@dwillia2-xfh.jf.intel.com
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2022-12-01 14:33:37 -07:00
|
|
|
dev_dbg(&cxlmd->dev, "register %s\n", dev_name(dev));
|
|
|
|
|
|
|
|
/* @cxlmd carries a reference on @cxl_nvb until cxlmd_release_nvdimm */
|
|
|
|
return devm_add_action_or_reset(&cxlmd->dev, cxlmd_release_nvdimm, cxlmd);
|
2021-08-02 10:29:49 -07:00
|
|
|
|
|
|
|
err:
|
|
|
|
put_device(dev);
|
cxl/pmem: Refactor nvdimm device registration, delete the workqueue
The three objects 'struct cxl_nvdimm_bridge', 'struct cxl_nvdimm', and
'struct cxl_pmem_region' manage CXL persistent memory resources. The
bridge represents base platform resources, the nvdimm represents one or
more endpoints, and the region is a collection of nvdimms that
contribute to an assembled address range.
Their relationship is such that a region is torn down if any component
endpoints are removed. All regions and endpoints are torn down if the
foundational bridge device goes down.
A workqueue was deployed to manage these interdependencies, but it is
difficult to reason about, and fragile. A recent attempt to take the CXL
root device lock in the cxl_mem driver was reported by lockdep as
colliding with the flush_work() in the cxl_pmem flows.
Instead of the workqueue, arrange for all pmem/nvdimm devices to be torn
down immediately and hierarchically. A similar change is made to both
the 'cxl_nvdimm' and 'cxl_pmem_region' objects. For bisect-ability both
changes are made in the same patch which unfortunately makes the patch
bigger than desired.
Arrange for cxl_memdev and cxl_region to register a cxl_nvdimm and
cxl_pmem_region as a devres release action of the bridge device.
Additionally, include a devres release action of the cxl_memdev or
cxl_region device that triggers the bridge's release action if an endpoint
exits before the bridge. I.e. this allows either unplugging the bridge,
or unplugging and endpoint to result in the same cleanup actions.
To keep the patch smaller the cleanup of the now defunct workqueue
infrastructure is saved for a follow-on patch.
Tested-by: Robert Richter <rrichter@amd.com>
Link: https://lore.kernel.org/r/166993041773.1882361.16444301376147207609.stgit@dwillia2-xfh.jf.intel.com
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
2022-12-01 14:33:37 -07:00
|
|
|
err_alloc:
|
|
|
|
cxlmd->cxl_nvb = NULL;
|
|
|
|
cxlmd->cxl_nvd = NULL;
|
|
|
|
put_device(&cxl_nvb->dev);
|
|
|
|
|
2021-08-02 10:29:49 -07:00
|
|
|
return rc;
|
|
|
|
}
|
2021-11-12 17:32:58 -07:00
|
|
|
EXPORT_SYMBOL_NS_GPL(devm_cxl_add_nvdimm, CXL);
|