Commit 03ff079a authored by Dan Williams's avatar Dan Williams
Browse files

cxl/pmem: Remove the cxl_pmem_wq and related infrastructure



Now that cxl_nvdimm and cxl_pmem_region objects are torn down
sychronously with the removal of either the bridge, or an endpoint, the
cxl_pmem_wq infrastructure can be jettisoned.

Tested-by: default avatarRobert Richter <rrichter@amd.com>
Link: https://lore.kernel.org/r/166993042335.1882361.17022872468068436287.stgit@dwillia2-xfh.jf.intel.com


Reviewed-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
parent f17b558d
Loading
Loading
Loading
Loading
+0 −22
Original line number Diff line number Diff line
@@ -99,7 +99,6 @@ static struct cxl_nvdimm_bridge *cxl_nvdimm_bridge_alloc(struct cxl_port *port)

	dev = &cxl_nvb->dev;
	cxl_nvb->port = port;
	cxl_nvb->state = CXL_NVB_NEW;
	device_initialize(dev);
	lockdep_set_class(&dev->mutex, &cxl_nvdimm_bridge_key);
	device_set_pm_not_required(dev);
@@ -117,28 +116,7 @@ static struct cxl_nvdimm_bridge *cxl_nvdimm_bridge_alloc(struct cxl_port *port)
static void unregister_nvb(void *_cxl_nvb)
{
	struct cxl_nvdimm_bridge *cxl_nvb = _cxl_nvb;
	bool flush;

	/*
	 * If the bridge was ever activated then there might be in-flight state
	 * work to flush. Once the state has been changed to 'dead' then no new
	 * work can be queued by user-triggered bind.
	 */
	device_lock(&cxl_nvb->dev);
	flush = cxl_nvb->state != CXL_NVB_NEW;
	cxl_nvb->state = CXL_NVB_DEAD;
	device_unlock(&cxl_nvb->dev);

	/*
	 * Even though the device core will trigger device_release_driver()
	 * before the unregister, it does not know about the fact that
	 * cxl_nvdimm_bridge_driver defers ->remove() work. So, do the driver
	 * release not and flush it before tearing down the nvdimm device
	 * hierarchy.
	 */
	device_release_driver(&cxl_nvb->dev);
	if (flush)
		flush_work(&cxl_nvb->state_work);
	device_unregister(&cxl_nvb->dev);
}

+0 −17
Original line number Diff line number Diff line
@@ -400,34 +400,17 @@ struct cxl_region {
	struct cxl_region_params params;
};

/**
 * enum cxl_nvdimm_brige_state - state machine for managing bus rescans
 * @CXL_NVB_NEW: Set at bridge create and after cxl_pmem_wq is destroyed
 * @CXL_NVB_DEAD: Set at brige unregistration to preclude async probing
 * @CXL_NVB_ONLINE: Target state after successful ->probe()
 * @CXL_NVB_OFFLINE: Target state after ->remove() or failed ->probe()
 */
enum cxl_nvdimm_brige_state {
	CXL_NVB_NEW,
	CXL_NVB_DEAD,
	CXL_NVB_ONLINE,
	CXL_NVB_OFFLINE,
};

struct cxl_nvdimm_bridge {
	int id;
	struct device dev;
	struct cxl_port *port;
	struct nvdimm_bus *nvdimm_bus;
	struct nvdimm_bus_descriptor nd_desc;
	struct work_struct state_work;
	enum cxl_nvdimm_brige_state state;
};

struct cxl_nvdimm {
	struct device dev;
	struct cxl_memdev *cxlmd;
	struct cxl_nvdimm_bridge *bridge;
};

struct cxl_pmem_region_mapping {
+1 −142
Original line number Diff line number Diff line
@@ -11,13 +11,6 @@
#include "cxlmem.h"
#include "cxl.h"

/*
 * Ordered workqueue for cxl nvdimm device arrival and departure
 * to coordinate bus rescans when a bridge arrives and trigger remove
 * operations when the bridge is removed.
 */
static struct workqueue_struct *cxl_pmem_wq;

static __read_mostly DECLARE_BITMAP(exclusive_cmds, CXL_MEM_COMMAND_ID_MAX);

static void clear_exclusive(void *cxlds)
@@ -191,105 +184,6 @@ static void unregister_nvdimm_bus(void *_cxl_nvb)
	nvdimm_bus_unregister(nvdimm_bus);
}

static bool online_nvdimm_bus(struct cxl_nvdimm_bridge *cxl_nvb)
{
	if (cxl_nvb->nvdimm_bus)
		return true;
	cxl_nvb->nvdimm_bus =
		nvdimm_bus_register(&cxl_nvb->dev, &cxl_nvb->nd_desc);
	return cxl_nvb->nvdimm_bus != NULL;
}

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 cxl_nvdimm_bridge *cxl_nvb,
			       struct nvdimm_bus *nvdimm_bus)
{
	if (!nvdimm_bus)
		return;

	/*
	 * Set the state of cxl_nvdimm devices to unbound / idle before
	 * nvdimm_bus_unregister() rips the nvdimm objects out from
	 * underneath them.
	 */
	bus_for_each_dev(&cxl_bus_type, NULL, cxl_nvb,
			 cxl_nvdimm_release_driver);
	nvdimm_bus_unregister(nvdimm_bus);
}

static void cxl_nvb_update_state(struct work_struct *work)
{
	struct cxl_nvdimm_bridge *cxl_nvb =
		container_of(work, typeof(*cxl_nvb), state_work);
	struct nvdimm_bus *victim_bus = NULL;
	bool release = false, rescan = false;

	device_lock(&cxl_nvb->dev);
	switch (cxl_nvb->state) {
	case CXL_NVB_ONLINE:
		if (!online_nvdimm_bus(cxl_nvb)) {
			dev_err(&cxl_nvb->dev,
				"failed to establish nvdimm bus\n");
			release = true;
		} else
			rescan = true;
		break;
	case CXL_NVB_OFFLINE:
	case CXL_NVB_DEAD:
		victim_bus = cxl_nvb->nvdimm_bus;
		cxl_nvb->nvdimm_bus = NULL;
		break;
	default:
		break;
	}
	device_unlock(&cxl_nvb->dev);

	if (release)
		device_release_driver(&cxl_nvb->dev);
	if (rescan) {
		int rc = bus_rescan_devices(&cxl_bus_type);

		dev_dbg(&cxl_nvb->dev, "rescan: %d\n", rc);
	}
	offline_nvdimm_bus(cxl_nvb, victim_bus);

	put_device(&cxl_nvb->dev);
}

static void cxl_nvdimm_bridge_state_work(struct cxl_nvdimm_bridge *cxl_nvb)
{
	/*
	 * Take a reference that the workqueue will drop if new work
	 * gets queued.
	 */
	get_device(&cxl_nvb->dev);
	if (!queue_work(cxl_pmem_wq, &cxl_nvb->state_work))
		put_device(&cxl_nvb->dev);
}

static void cxl_nvdimm_bridge_remove(struct device *dev)
{
	struct cxl_nvdimm_bridge *cxl_nvb = to_cxl_nvdimm_bridge(dev);

	if (cxl_nvb->state == CXL_NVB_ONLINE)
		cxl_nvb->state = CXL_NVB_OFFLINE;
	cxl_nvdimm_bridge_state_work(cxl_nvb);
}

static int cxl_nvdimm_bridge_probe(struct device *dev)
{
	struct cxl_nvdimm_bridge *cxl_nvb = to_cxl_nvdimm_bridge(dev);
@@ -306,15 +200,12 @@ static int cxl_nvdimm_bridge_probe(struct device *dev)
	if (!cxl_nvb->nvdimm_bus)
		return -ENOMEM;

	INIT_WORK(&cxl_nvb->state_work, cxl_nvb_update_state);

	return devm_add_action_or_reset(dev, unregister_nvdimm_bus, cxl_nvb);
}

static struct cxl_driver cxl_nvdimm_bridge_driver = {
	.name = "cxl_nvdimm_bridge",
	.probe = cxl_nvdimm_bridge_probe,
	.remove = cxl_nvdimm_bridge_remove,
	.id = CXL_DEVICE_NVDIMM_BRIDGE,
	.drv = {
		.suppress_bind_attrs = true,
@@ -453,31 +344,6 @@ static struct cxl_driver cxl_pmem_region_driver = {
	},
};

/*
 * Return all bridges to the CXL_NVB_NEW state to invalidate any
 * ->state_work referring to the now destroyed cxl_pmem_wq.
 */
static int cxl_nvdimm_bridge_reset(struct device *dev, void *data)
{
	struct cxl_nvdimm_bridge *cxl_nvb;

	if (!is_cxl_nvdimm_bridge(dev))
		return 0;

	cxl_nvb = to_cxl_nvdimm_bridge(dev);
	device_lock(dev);
	cxl_nvb->state = CXL_NVB_NEW;
	device_unlock(dev);

	return 0;
}

static void destroy_cxl_pmem_wq(void)
{
	destroy_workqueue(cxl_pmem_wq);
	bus_for_each_dev(&cxl_bus_type, NULL, NULL, cxl_nvdimm_bridge_reset);
}

static __init int cxl_pmem_init(void)
{
	int rc;
@@ -485,13 +351,9 @@ static __init int cxl_pmem_init(void)
	set_bit(CXL_MEM_COMMAND_ID_SET_SHUTDOWN_STATE, exclusive_cmds);
	set_bit(CXL_MEM_COMMAND_ID_SET_LSA, exclusive_cmds);

	cxl_pmem_wq = alloc_ordered_workqueue("cxl_pmem", 0);
	if (!cxl_pmem_wq)
		return -ENXIO;

	rc = cxl_driver_register(&cxl_nvdimm_bridge_driver);
	if (rc)
		goto err_bridge;
		return rc;

	rc = cxl_driver_register(&cxl_nvdimm_driver);
	if (rc)
@@ -507,8 +369,6 @@ static __init int cxl_pmem_init(void)
	cxl_driver_unregister(&cxl_nvdimm_driver);
err_nvdimm:
	cxl_driver_unregister(&cxl_nvdimm_bridge_driver);
err_bridge:
	destroy_cxl_pmem_wq();
	return rc;
}

@@ -517,7 +377,6 @@ static __exit void cxl_pmem_exit(void)
	cxl_driver_unregister(&cxl_pmem_region_driver);
	cxl_driver_unregister(&cxl_nvdimm_driver);
	cxl_driver_unregister(&cxl_nvdimm_bridge_driver);
	destroy_cxl_pmem_wq();
}

MODULE_LICENSE("GPL v2");