Commit 99183d26 authored by Dan Williams's avatar Dan Williams
Browse files

cxl/pmem: Fix offline_nvdimm_bus() to offline by bridge



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: 21083f51 ("cxl/pmem: Register 'pmem' / cxl_nvdimm devices")
Reviewed-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
Link: https://lore.kernel.org/r/165784339569.1758207.1557084545278004577.stgit@dwillia2-xfh.jf.intel.com


Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
parent 8d48817d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -418,6 +418,7 @@ struct cxl_nvdimm_bridge {
struct cxl_nvdimm {
	struct device dev;
	struct cxl_memdev *cxlmd;
	struct cxl_nvdimm_bridge *bridge;
};

/**
+17 −4
Original line number Diff line number Diff line
@@ -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);
}