Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 87a30e1f authored by Dan Williams's avatar Dan Williams
Browse files

driver-core, libnvdimm: Let device subsystems add local lockdep coverage



For good reason, the standard device_lock() is marked
lockdep_set_novalidate_class() because there is simply no sane way to
describe the myriad ways the device_lock() ordered with other locks.
However, that leaves subsystems that know their own local device_lock()
ordering rules to find lock ordering mistakes manually. Instead,
introduce an optional / additional lockdep-enabled lock that a subsystem
can acquire in all the same paths that the device_lock() is acquired.

A conversion of the NFIT driver and NVDIMM subsystem to a
lockdep-validate device_lock() scheme is included. The
debug_nvdimm_lock() implementation implements the correct lock-class and
stacking order for the libnvdimm device topology hierarchy.

Yes, this is a hack, but hopefully it is a useful hack for other
subsystems device_lock() debug sessions. Quoting Greg:

    "Yeah, it feels a bit hacky but it's really up to a subsystem to mess up
     using it as much as anything else, so user beware :)

     I don't object to it if it makes things easier for you to debug."

Cc: Ingo Molnar <mingo@redhat.com>
Cc: Ira Weiny <ira.weiny@intel.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Dave Jiang <dave.jiang@intel.com>
Cc: Keith Busch <keith.busch@intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Vishal Verma <vishal.l.verma@intel.com>
Cc: "Rafael J. Wysocki" <rjw@rjwysocki.net>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
Acked-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Reviewed-by: default avatarIra Weiny <ira.weiny@intel.com>
Link: https://lore.kernel.org/r/156341210661.292348.7014034644265455704.stgit@dwillia2-desk3.amr.corp.intel.com
parent ca6bf264
Loading
Loading
Loading
Loading
+14 −14
Original line number Diff line number Diff line
@@ -1282,7 +1282,7 @@ static ssize_t hw_error_scrub_store(struct device *dev,
	if (rc)
		return rc;

	device_lock(dev);
	nfit_device_lock(dev);
	nd_desc = dev_get_drvdata(dev);
	if (nd_desc) {
		struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);
@@ -1299,7 +1299,7 @@ static ssize_t hw_error_scrub_store(struct device *dev,
			break;
		}
	}
	device_unlock(dev);
	nfit_device_unlock(dev);
	if (rc)
		return rc;
	return size;
@@ -1319,7 +1319,7 @@ static ssize_t scrub_show(struct device *dev,
	ssize_t rc = -ENXIO;
	bool busy;

	device_lock(dev);
	nfit_device_lock(dev);
	nd_desc = dev_get_drvdata(dev);
	if (!nd_desc) {
		device_unlock(dev);
@@ -1339,7 +1339,7 @@ static ssize_t scrub_show(struct device *dev,
	}

	mutex_unlock(&acpi_desc->init_mutex);
	device_unlock(dev);
	nfit_device_unlock(dev);
	return rc;
}

@@ -1356,14 +1356,14 @@ static ssize_t scrub_store(struct device *dev,
	if (val != 1)
		return -EINVAL;

	device_lock(dev);
	nfit_device_lock(dev);
	nd_desc = dev_get_drvdata(dev);
	if (nd_desc) {
		struct acpi_nfit_desc *acpi_desc = to_acpi_desc(nd_desc);

		rc = acpi_nfit_ars_rescan(acpi_desc, ARS_REQ_LONG);
	}
	device_unlock(dev);
	nfit_device_unlock(dev);
	if (rc)
		return rc;
	return size;
@@ -1749,9 +1749,9 @@ static void acpi_nvdimm_notify(acpi_handle handle, u32 event, void *data)
	struct acpi_device *adev = data;
	struct device *dev = &adev->dev;

	device_lock(dev->parent);
	nfit_device_lock(dev->parent);
	__acpi_nvdimm_notify(dev, event);
	device_unlock(dev->parent);
	nfit_device_unlock(dev->parent);
}

static bool acpi_nvdimm_has_method(struct acpi_device *adev, char *method)
@@ -3457,8 +3457,8 @@ static int acpi_nfit_flush_probe(struct nvdimm_bus_descriptor *nd_desc)
	struct device *dev = acpi_desc->dev;

	/* Bounce the device lock to flush acpi_nfit_add / acpi_nfit_notify */
	device_lock(dev);
	device_unlock(dev);
	nfit_device_lock(dev);
	nfit_device_unlock(dev);

	/* Bounce the init_mutex to complete initial registration */
	mutex_lock(&acpi_desc->init_mutex);
@@ -3602,8 +3602,8 @@ void acpi_nfit_shutdown(void *data)
	 * acpi_nfit_ars_rescan() submissions have had a chance to
	 * either submit or see ->cancel set.
	 */
	device_lock(bus_dev);
	device_unlock(bus_dev);
	nfit_device_lock(bus_dev);
	nfit_device_unlock(bus_dev);

	flush_workqueue(nfit_wq);
}
@@ -3746,9 +3746,9 @@ EXPORT_SYMBOL_GPL(__acpi_nfit_notify);

static void acpi_nfit_notify(struct acpi_device *adev, u32 event)
{
	device_lock(&adev->dev);
	nfit_device_lock(&adev->dev);
	__acpi_nfit_notify(&adev->dev, adev->handle, event);
	device_unlock(&adev->dev);
	nfit_device_unlock(&adev->dev);
}

static const struct acpi_device_id acpi_nfit_ids[] = {
+24 −0
Original line number Diff line number Diff line
@@ -312,6 +312,30 @@ static inline struct acpi_nfit_desc *to_acpi_desc(
	return container_of(nd_desc, struct acpi_nfit_desc, nd_desc);
}

#ifdef CONFIG_PROVE_LOCKING
static inline void nfit_device_lock(struct device *dev)
{
	device_lock(dev);
	mutex_lock(&dev->lockdep_mutex);
}

static inline void nfit_device_unlock(struct device *dev)
{
	mutex_unlock(&dev->lockdep_mutex);
	device_unlock(dev);
}
#else
static inline void nfit_device_lock(struct device *dev)
{
	device_lock(dev);
}

static inline void nfit_device_unlock(struct device *dev)
{
	device_unlock(dev);
}
#endif

const guid_t *to_nfit_uuid(enum nfit_uuids id);
int acpi_nfit_init(struct acpi_nfit_desc *acpi_desc, void *nfit, acpi_size sz);
void acpi_nfit_shutdown(void *data);
+3 −0
Original line number Diff line number Diff line
@@ -1663,6 +1663,9 @@ void device_initialize(struct device *dev)
	kobject_init(&dev->kobj, &device_ktype);
	INIT_LIST_HEAD(&dev->dma_pools);
	mutex_init(&dev->mutex);
#ifdef CONFIG_PROVE_LOCKING
	mutex_init(&dev->lockdep_mutex);
#endif
	lockdep_set_novalidate_class(&dev->mutex);
	spin_lock_init(&dev->devres_lock);
	INIT_LIST_HEAD(&dev->devres_head);
+8 −8
Original line number Diff line number Diff line
@@ -62,14 +62,14 @@ static ssize_t sector_size_store(struct device *dev,
	struct nd_btt *nd_btt = to_nd_btt(dev);
	ssize_t rc;

	device_lock(dev);
	nd_device_lock(dev);
	nvdimm_bus_lock(dev);
	rc = nd_size_select_store(dev, buf, &nd_btt->lbasize,
			btt_lbasize_supported);
	dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf,
			buf[len - 1] == '\n' ? "" : "\n");
	nvdimm_bus_unlock(dev);
	device_unlock(dev);
	nd_device_unlock(dev);

	return rc ? rc : len;
}
@@ -91,11 +91,11 @@ static ssize_t uuid_store(struct device *dev,
	struct nd_btt *nd_btt = to_nd_btt(dev);
	ssize_t rc;

	device_lock(dev);
	nd_device_lock(dev);
	rc = nd_uuid_store(dev, &nd_btt->uuid, buf, len);
	dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf,
			buf[len - 1] == '\n' ? "" : "\n");
	device_unlock(dev);
	nd_device_unlock(dev);

	return rc ? rc : len;
}
@@ -120,13 +120,13 @@ static ssize_t namespace_store(struct device *dev,
	struct nd_btt *nd_btt = to_nd_btt(dev);
	ssize_t rc;

	device_lock(dev);
	nd_device_lock(dev);
	nvdimm_bus_lock(dev);
	rc = nd_namespace_store(dev, &nd_btt->ndns, buf, len);
	dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf,
			buf[len - 1] == '\n' ? "" : "\n");
	nvdimm_bus_unlock(dev);
	device_unlock(dev);
	nd_device_unlock(dev);

	return rc;
}
@@ -138,14 +138,14 @@ static ssize_t size_show(struct device *dev,
	struct nd_btt *nd_btt = to_nd_btt(dev);
	ssize_t rc;

	device_lock(dev);
	nd_device_lock(dev);
	if (dev->driver)
		rc = sprintf(buf, "%llu\n", nd_btt->size);
	else {
		/* no size to convey if the btt instance is disabled */
		rc = -ENXIO;
	}
	device_unlock(dev);
	nd_device_unlock(dev);

	return rc;
}
+17 −11
Original line number Diff line number Diff line
@@ -26,7 +26,7 @@

int nvdimm_major;
static int nvdimm_bus_major;
static struct class *nd_class;
struct class *nd_class;
static DEFINE_IDA(nd_ida);

static int to_nd_device_type(struct device *dev)
@@ -91,7 +91,10 @@ static int nvdimm_bus_probe(struct device *dev)
			dev->driver->name, dev_name(dev));

	nvdimm_bus_probe_start(nvdimm_bus);
	debug_nvdimm_lock(dev);
	rc = nd_drv->probe(dev);
	debug_nvdimm_unlock(dev);

	if (rc == 0)
		nd_region_probe_success(nvdimm_bus, dev);
	else
@@ -113,8 +116,11 @@ static int nvdimm_bus_remove(struct device *dev)
	struct nvdimm_bus *nvdimm_bus = walk_to_nvdimm_bus(dev);
	int rc = 0;

	if (nd_drv->remove)
	if (nd_drv->remove) {
		debug_nvdimm_lock(dev);
		rc = nd_drv->remove(dev);
		debug_nvdimm_unlock(dev);
	}
	nd_region_disable(nvdimm_bus, dev);

	dev_dbg(&nvdimm_bus->dev, "%s.remove(%s) = %d\n", dev->driver->name,
@@ -140,7 +146,7 @@ static void nvdimm_bus_shutdown(struct device *dev)

void nd_device_notify(struct device *dev, enum nvdimm_event event)
{
	device_lock(dev);
	nd_device_lock(dev);
	if (dev->driver) {
		struct nd_device_driver *nd_drv;

@@ -148,7 +154,7 @@ void nd_device_notify(struct device *dev, enum nvdimm_event event)
		if (nd_drv->notify)
			nd_drv->notify(dev, event);
	}
	device_unlock(dev);
	nd_device_unlock(dev);
}
EXPORT_SYMBOL(nd_device_notify);

@@ -296,7 +302,7 @@ static void nvdimm_bus_release(struct device *dev)
	kfree(nvdimm_bus);
}

static bool is_nvdimm_bus(struct device *dev)
bool is_nvdimm_bus(struct device *dev)
{
	return dev->release == nvdimm_bus_release;
}
@@ -575,9 +581,9 @@ void nd_device_unregister(struct device *dev, enum nd_async_mode mode)
		 * or otherwise let the async path handle it if the
		 * unregistration was already queued.
		 */
		device_lock(dev);
		nd_device_lock(dev);
		killed = kill_device(dev);
		device_unlock(dev);
		nd_device_unlock(dev);

		if (!killed)
			return;
@@ -888,10 +894,10 @@ void wait_nvdimm_bus_probe_idle(struct device *dev)
		if (nvdimm_bus->probe_active == 0)
			break;
		nvdimm_bus_unlock(dev);
		device_unlock(dev);
		nd_device_unlock(dev);
		wait_event(nvdimm_bus->wait,
				nvdimm_bus->probe_active == 0);
		device_lock(dev);
		nd_device_lock(dev);
		nvdimm_bus_lock(dev);
	} while (true);
}
@@ -1107,7 +1113,7 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,
		goto out;
	}

	device_lock(dev);
	nd_device_lock(dev);
	nvdimm_bus_lock(dev);
	rc = nd_cmd_clear_to_send(nvdimm_bus, nvdimm, func, buf);
	if (rc)
@@ -1129,7 +1135,7 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,

out_unlock:
	nvdimm_bus_unlock(dev);
	device_unlock(dev);
	nd_device_unlock(dev);
out:
	kfree(in_env);
	kfree(out_env);
Loading