Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /* Copyright(c) 2020 Intel Corporation. */
0003 #include <linux/device.h>
0004 #include <linux/slab.h>
0005 #include <linux/idr.h>
0006 #include <cxlmem.h>
0007 #include <cxl.h>
0008 #include "core.h"
0009 
0010 /**
0011  * DOC: cxl pmem
0012  *
0013  * The core CXL PMEM infrastructure supports persistent memory
0014  * provisioning and serves as a bridge to the LIBNVDIMM subsystem. A CXL
0015  * 'bridge' device is added at the root of a CXL device topology if
0016  * platform firmware advertises at least one persistent memory capable
0017  * CXL window. That root-level bridge corresponds to a LIBNVDIMM 'bus'
0018  * device. Then for each cxl_memdev in the CXL device topology a bridge
0019  * device is added to host a LIBNVDIMM dimm object. When these bridges
0020  * are registered native LIBNVDIMM uapis are translated to CXL
0021  * operations, for example, namespace label access commands.
0022  */
0023 
0024 static DEFINE_IDA(cxl_nvdimm_bridge_ida);
0025 
0026 static void cxl_nvdimm_bridge_release(struct device *dev)
0027 {
0028     struct cxl_nvdimm_bridge *cxl_nvb = to_cxl_nvdimm_bridge(dev);
0029 
0030     ida_free(&cxl_nvdimm_bridge_ida, cxl_nvb->id);
0031     kfree(cxl_nvb);
0032 }
0033 
0034 static const struct attribute_group *cxl_nvdimm_bridge_attribute_groups[] = {
0035     &cxl_base_attribute_group,
0036     NULL,
0037 };
0038 
0039 const struct device_type cxl_nvdimm_bridge_type = {
0040     .name = "cxl_nvdimm_bridge",
0041     .release = cxl_nvdimm_bridge_release,
0042     .groups = cxl_nvdimm_bridge_attribute_groups,
0043 };
0044 
0045 struct cxl_nvdimm_bridge *to_cxl_nvdimm_bridge(struct device *dev)
0046 {
0047     if (dev_WARN_ONCE(dev, dev->type != &cxl_nvdimm_bridge_type,
0048               "not a cxl_nvdimm_bridge device\n"))
0049         return NULL;
0050     return container_of(dev, struct cxl_nvdimm_bridge, dev);
0051 }
0052 EXPORT_SYMBOL_NS_GPL(to_cxl_nvdimm_bridge, CXL);
0053 
0054 bool is_cxl_nvdimm_bridge(struct device *dev)
0055 {
0056     return dev->type == &cxl_nvdimm_bridge_type;
0057 }
0058 EXPORT_SYMBOL_NS_GPL(is_cxl_nvdimm_bridge, CXL);
0059 
0060 static int match_nvdimm_bridge(struct device *dev, void *data)
0061 {
0062     return is_cxl_nvdimm_bridge(dev);
0063 }
0064 
0065 struct cxl_nvdimm_bridge *cxl_find_nvdimm_bridge(struct device *start)
0066 {
0067     struct cxl_port *port = find_cxl_root(start);
0068     struct device *dev;
0069 
0070     if (!port)
0071         return NULL;
0072 
0073     dev = device_find_child(&port->dev, NULL, match_nvdimm_bridge);
0074     put_device(&port->dev);
0075 
0076     if (!dev)
0077         return NULL;
0078 
0079     return to_cxl_nvdimm_bridge(dev);
0080 }
0081 EXPORT_SYMBOL_NS_GPL(cxl_find_nvdimm_bridge, CXL);
0082 
0083 static struct lock_class_key cxl_nvdimm_bridge_key;
0084 
0085 static struct cxl_nvdimm_bridge *cxl_nvdimm_bridge_alloc(struct cxl_port *port)
0086 {
0087     struct cxl_nvdimm_bridge *cxl_nvb;
0088     struct device *dev;
0089     int rc;
0090 
0091     cxl_nvb = kzalloc(sizeof(*cxl_nvb), GFP_KERNEL);
0092     if (!cxl_nvb)
0093         return ERR_PTR(-ENOMEM);
0094 
0095     rc = ida_alloc(&cxl_nvdimm_bridge_ida, GFP_KERNEL);
0096     if (rc < 0)
0097         goto err;
0098     cxl_nvb->id = rc;
0099 
0100     dev = &cxl_nvb->dev;
0101     cxl_nvb->port = port;
0102     cxl_nvb->state = CXL_NVB_NEW;
0103     device_initialize(dev);
0104     lockdep_set_class(&dev->mutex, &cxl_nvdimm_bridge_key);
0105     device_set_pm_not_required(dev);
0106     dev->parent = &port->dev;
0107     dev->bus = &cxl_bus_type;
0108     dev->type = &cxl_nvdimm_bridge_type;
0109 
0110     return cxl_nvb;
0111 
0112 err:
0113     kfree(cxl_nvb);
0114     return ERR_PTR(rc);
0115 }
0116 
0117 static void unregister_nvb(void *_cxl_nvb)
0118 {
0119     struct cxl_nvdimm_bridge *cxl_nvb = _cxl_nvb;
0120     bool flush;
0121 
0122     /*
0123      * If the bridge was ever activated then there might be in-flight state
0124      * work to flush. Once the state has been changed to 'dead' then no new
0125      * work can be queued by user-triggered bind.
0126      */
0127     device_lock(&cxl_nvb->dev);
0128     flush = cxl_nvb->state != CXL_NVB_NEW;
0129     cxl_nvb->state = CXL_NVB_DEAD;
0130     device_unlock(&cxl_nvb->dev);
0131 
0132     /*
0133      * Even though the device core will trigger device_release_driver()
0134      * before the unregister, it does not know about the fact that
0135      * cxl_nvdimm_bridge_driver defers ->remove() work. So, do the driver
0136      * release not and flush it before tearing down the nvdimm device
0137      * hierarchy.
0138      */
0139     device_release_driver(&cxl_nvb->dev);
0140     if (flush)
0141         flush_work(&cxl_nvb->state_work);
0142     device_unregister(&cxl_nvb->dev);
0143 }
0144 
0145 /**
0146  * devm_cxl_add_nvdimm_bridge() - add the root of a LIBNVDIMM topology
0147  * @host: platform firmware root device
0148  * @port: CXL port at the root of a CXL topology
0149  *
0150  * Return: bridge device that can host cxl_nvdimm objects
0151  */
0152 struct cxl_nvdimm_bridge *devm_cxl_add_nvdimm_bridge(struct device *host,
0153                              struct cxl_port *port)
0154 {
0155     struct cxl_nvdimm_bridge *cxl_nvb;
0156     struct device *dev;
0157     int rc;
0158 
0159     if (!IS_ENABLED(CONFIG_CXL_PMEM))
0160         return ERR_PTR(-ENXIO);
0161 
0162     cxl_nvb = cxl_nvdimm_bridge_alloc(port);
0163     if (IS_ERR(cxl_nvb))
0164         return cxl_nvb;
0165 
0166     dev = &cxl_nvb->dev;
0167     rc = dev_set_name(dev, "nvdimm-bridge%d", cxl_nvb->id);
0168     if (rc)
0169         goto err;
0170 
0171     rc = device_add(dev);
0172     if (rc)
0173         goto err;
0174 
0175     rc = devm_add_action_or_reset(host, unregister_nvb, cxl_nvb);
0176     if (rc)
0177         return ERR_PTR(rc);
0178 
0179     return cxl_nvb;
0180 
0181 err:
0182     put_device(dev);
0183     return ERR_PTR(rc);
0184 }
0185 EXPORT_SYMBOL_NS_GPL(devm_cxl_add_nvdimm_bridge, CXL);
0186 
0187 static void cxl_nvdimm_release(struct device *dev)
0188 {
0189     struct cxl_nvdimm *cxl_nvd = to_cxl_nvdimm(dev);
0190 
0191     kfree(cxl_nvd);
0192 }
0193 
0194 static const struct attribute_group *cxl_nvdimm_attribute_groups[] = {
0195     &cxl_base_attribute_group,
0196     NULL,
0197 };
0198 
0199 const struct device_type cxl_nvdimm_type = {
0200     .name = "cxl_nvdimm",
0201     .release = cxl_nvdimm_release,
0202     .groups = cxl_nvdimm_attribute_groups,
0203 };
0204 
0205 bool is_cxl_nvdimm(struct device *dev)
0206 {
0207     return dev->type == &cxl_nvdimm_type;
0208 }
0209 EXPORT_SYMBOL_NS_GPL(is_cxl_nvdimm, CXL);
0210 
0211 struct cxl_nvdimm *to_cxl_nvdimm(struct device *dev)
0212 {
0213     if (dev_WARN_ONCE(dev, !is_cxl_nvdimm(dev),
0214               "not a cxl_nvdimm device\n"))
0215         return NULL;
0216     return container_of(dev, struct cxl_nvdimm, dev);
0217 }
0218 EXPORT_SYMBOL_NS_GPL(to_cxl_nvdimm, CXL);
0219 
0220 static struct lock_class_key cxl_nvdimm_key;
0221 
0222 static struct cxl_nvdimm *cxl_nvdimm_alloc(struct cxl_memdev *cxlmd)
0223 {
0224     struct cxl_nvdimm *cxl_nvd;
0225     struct device *dev;
0226 
0227     cxl_nvd = kzalloc(sizeof(*cxl_nvd), GFP_KERNEL);
0228     if (!cxl_nvd)
0229         return ERR_PTR(-ENOMEM);
0230 
0231     dev = &cxl_nvd->dev;
0232     cxl_nvd->cxlmd = cxlmd;
0233     device_initialize(dev);
0234     lockdep_set_class(&dev->mutex, &cxl_nvdimm_key);
0235     device_set_pm_not_required(dev);
0236     dev->parent = &cxlmd->dev;
0237     dev->bus = &cxl_bus_type;
0238     dev->type = &cxl_nvdimm_type;
0239 
0240     return cxl_nvd;
0241 }
0242 
0243 static void cxl_nvd_unregister(void *dev)
0244 {
0245     device_unregister(dev);
0246 }
0247 
0248 /**
0249  * devm_cxl_add_nvdimm() - add a bridge between a cxl_memdev and an nvdimm
0250  * @host: same host as @cxlmd
0251  * @cxlmd: cxl_memdev instance that will perform LIBNVDIMM operations
0252  *
0253  * Return: 0 on success negative error code on failure.
0254  */
0255 int devm_cxl_add_nvdimm(struct device *host, struct cxl_memdev *cxlmd)
0256 {
0257     struct cxl_nvdimm *cxl_nvd;
0258     struct device *dev;
0259     int rc;
0260 
0261     cxl_nvd = cxl_nvdimm_alloc(cxlmd);
0262     if (IS_ERR(cxl_nvd))
0263         return PTR_ERR(cxl_nvd);
0264 
0265     dev = &cxl_nvd->dev;
0266     rc = dev_set_name(dev, "pmem%d", cxlmd->id);
0267     if (rc)
0268         goto err;
0269 
0270     rc = device_add(dev);
0271     if (rc)
0272         goto err;
0273 
0274     dev_dbg(host, "%s: register %s\n", dev_name(dev->parent),
0275         dev_name(dev));
0276 
0277     return devm_add_action_or_reset(host, cxl_nvd_unregister, dev);
0278 
0279 err:
0280     put_device(dev);
0281     return rc;
0282 }
0283 EXPORT_SYMBOL_NS_GPL(devm_cxl_add_nvdimm, CXL);