cxl/pmem: Fix offline_nvdimm_bus() to offline by bridge
authorDan Williams <dan.j.williams@intel.com>
Sat, 11 Jun 2022 00:39:59 +0000 (17:39 -0700)
committerDan Williams <dan.j.williams@intel.com>
Tue, 26 Jul 2022 19:23:01 +0000 (12:23 -0700)
Be careful to only disable cxl_pmem objects related to a given
cxl_nvdimm_bridge. Otherwise, offline_nvdimm_bus() reaches across CXL
domains and disables more than is expected.

Fixes: 21083f51521f ("cxl/pmem: Register 'pmem' / cxl_nvdimm devices")
Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Link: https://lore.kernel.org/r/165784339569.1758207.1557084545278004577.stgit@dwillia2-xfh.jf.intel.com
Signed-off-by: Dan Williams <dan.j.williams@intel.com>
drivers/cxl/cxl.h
drivers/cxl/pmem.c

index e96bf8c..46d3f17 100644 (file)
@@ -418,6 +418,7 @@ struct cxl_nvdimm_bridge {
 struct cxl_nvdimm {
        struct device dev;
        struct cxl_memdev *cxlmd;
+       struct cxl_nvdimm_bridge *bridge;
 };
 
 /**
index 0aaa70b..b271f6e 100644 (file)
@@ -26,7 +26,10 @@ static void clear_exclusive(void *cxlds)
 
 static void unregister_nvdimm(void *nvdimm)
 {
+       struct cxl_nvdimm *cxl_nvd = nvdimm_provider_data(nvdimm);
+
        nvdimm_delete(nvdimm);
+       cxl_nvd->bridge = NULL;
 }
 
 static int cxl_nvdimm_probe(struct device *dev)
@@ -66,6 +69,7 @@ static int cxl_nvdimm_probe(struct device *dev)
        }
 
        dev_set_drvdata(dev, nvdimm);
+       cxl_nvd->bridge = cxl_nvb;
        rc = devm_add_action_or_reset(dev, unregister_nvdimm, nvdimm);
 out:
        device_unlock(&cxl_nvb->dev);
@@ -204,15 +208,23 @@ static bool online_nvdimm_bus(struct cxl_nvdimm_bridge *cxl_nvb)
        return cxl_nvb->nvdimm_bus != NULL;
 }
 
-static int cxl_nvdimm_release_driver(struct device *dev, void *data)
+static int cxl_nvdimm_release_driver(struct device *dev, void *cxl_nvb)
 {
+       struct cxl_nvdimm *cxl_nvd;
+
        if (!is_cxl_nvdimm(dev))
                return 0;
+
+       cxl_nvd = to_cxl_nvdimm(dev);
+       if (cxl_nvd->bridge != cxl_nvb)
+               return 0;
+
        device_release_driver(dev);
        return 0;
 }
 
-static void offline_nvdimm_bus(struct nvdimm_bus *nvdimm_bus)
+static void offline_nvdimm_bus(struct cxl_nvdimm_bridge *cxl_nvb,
+                              struct nvdimm_bus *nvdimm_bus)
 {
        if (!nvdimm_bus)
                return;
@@ -222,7 +234,8 @@ static void offline_nvdimm_bus(struct nvdimm_bus *nvdimm_bus)
         * nvdimm_bus_unregister() rips the nvdimm objects out from
         * underneath them.
         */
-       bus_for_each_dev(&cxl_bus_type, NULL, NULL, cxl_nvdimm_release_driver);
+       bus_for_each_dev(&cxl_bus_type, NULL, cxl_nvb,
+                        cxl_nvdimm_release_driver);
        nvdimm_bus_unregister(nvdimm_bus);
 }
 
@@ -260,7 +273,7 @@ static void cxl_nvb_update_state(struct work_struct *work)
 
                dev_dbg(&cxl_nvb->dev, "rescan: %d\n", rc);
        }
-       offline_nvdimm_bus(victim_bus);
+       offline_nvdimm_bus(cxl_nvb, victim_bus);
 
        put_device(&cxl_nvb->dev);
 }