diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-27 08:25:51 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-07-27 08:25:51 -0700 | 
| commit | 523634db145a22cd5562714d4c59ea74686afe38 (patch) | |
| tree | 14f3fa773866d9afe83af9257ff0dffac84e25ac /drivers/nvdimm | |
| parent | 5168afe6ef596eaf2ff7a533b780c79ce14445e4 (diff) | |
| parent | 87a30e1f05d73a34e6d1895065541369131aaf1c (diff) | |
| download | linux-523634db145a22cd5562714d4c59ea74686afe38.tar.bz2 | |
Merge tag 'libnvdimm-fixes-5.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm
Pull libnvdimm fixes from Dan Williams:
 "A collection of locking and async operations fixes for v5.3-rc2. These
  had been soaking in a branch targeting the merge window, but missed
  due to a regression hunt. This fixed up version has otherwise been in
  -next this past week with no reported issues.
  In order to gain confidence in the locking changes the pull also
  includes a debug / instrumentation patch to enable lockdep coverage
  for libnvdimm subsystem operations that depend on the device_lock for
  exclusion. As mentioned in the changelog it is a hack, but it works
  and documents the locking expectations of the sub-system in a way that
  others can use lockdep to verify. The driver core touches got an ack
  from Greg.
  Summary:
   - Fix duplicate device_unregister() calls (multiple threads competing
     to do unregister work when scheduling device removal from a sysfs
     attribute of the self-same device).
   - Fix badblocks registration order bug. Ensure region badblocks are
     initialized in advance of namespace registration.
   - Fix a deadlock between the bus lock and probe operations.
   - Export device-core infrastructure to coordinate async operations
     via the device ->dead state.
   - Add device-core infrastructure to validate device_lock() usage with
     lockdep"
* tag 'libnvdimm-fixes-5.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/nvdimm/nvdimm:
  driver-core, libnvdimm: Let device subsystems add local lockdep coverage
  libnvdimm/bus: Fix wait_nvdimm_bus_probe_idle() ABBA deadlock
  libnvdimm/bus: Stop holding nvdimm_bus_list_mutex over __nd_ioctl()
  libnvdimm/bus: Prepare the nd_ioctl() path to be re-entrant
  libnvdimm/region: Register badblocks before namespaces
  libnvdimm/bus: Prevent duplicate device_unregister() calls
  drivers/base: Introduce kill_device()
Diffstat (limited to 'drivers/nvdimm')
| -rw-r--r-- | drivers/nvdimm/btt_devs.c | 16 | ||||
| -rw-r--r-- | drivers/nvdimm/bus.c | 210 | ||||
| -rw-r--r-- | drivers/nvdimm/core.c | 10 | ||||
| -rw-r--r-- | drivers/nvdimm/dimm_devs.c | 4 | ||||
| -rw-r--r-- | drivers/nvdimm/namespace_devs.c | 36 | ||||
| -rw-r--r-- | drivers/nvdimm/nd-core.h | 71 | ||||
| -rw-r--r-- | drivers/nvdimm/pfn_devs.c | 24 | ||||
| -rw-r--r-- | drivers/nvdimm/pmem.c | 4 | ||||
| -rw-r--r-- | drivers/nvdimm/region.c | 24 | ||||
| -rw-r--r-- | drivers/nvdimm/region_devs.c | 12 | 
10 files changed, 277 insertions, 134 deletions
diff --git a/drivers/nvdimm/btt_devs.c b/drivers/nvdimm/btt_devs.c index 62d00fffa4af..3508a79110c7 100644 --- a/drivers/nvdimm/btt_devs.c +++ b/drivers/nvdimm/btt_devs.c @@ -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;  } diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index 2dca3034fee0..798c5c4aea9c 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -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) @@ -73,7 +73,7 @@ static void nvdimm_bus_probe_end(struct nvdimm_bus *nvdimm_bus)  {  	nvdimm_bus_lock(&nvdimm_bus->dev);  	if (--nvdimm_bus->probe_active == 0) -		wake_up(&nvdimm_bus->probe_wait); +		wake_up(&nvdimm_bus->wait);  	nvdimm_bus_unlock(&nvdimm_bus->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;  } @@ -341,7 +347,7 @@ struct nvdimm_bus *nvdimm_bus_register(struct device *parent,  		return NULL;  	INIT_LIST_HEAD(&nvdimm_bus->list);  	INIT_LIST_HEAD(&nvdimm_bus->mapping_list); -	init_waitqueue_head(&nvdimm_bus->probe_wait); +	init_waitqueue_head(&nvdimm_bus->wait);  	nvdimm_bus->id = ida_simple_get(&nd_ida, 0, 0, GFP_KERNEL);  	if (nvdimm_bus->id < 0) {  		kfree(nvdimm_bus); @@ -426,6 +432,9 @@ static int nd_bus_remove(struct device *dev)  	list_del_init(&nvdimm_bus->list);  	mutex_unlock(&nvdimm_bus_list_mutex); +	wait_event(nvdimm_bus->wait, +			atomic_read(&nvdimm_bus->ioctl_active) == 0); +  	nd_synchronize();  	device_for_each_child(&nvdimm_bus->dev, NULL, child_unregister); @@ -547,13 +556,38 @@ EXPORT_SYMBOL(nd_device_register);  void nd_device_unregister(struct device *dev, enum nd_async_mode mode)  { +	bool killed; +  	switch (mode) {  	case ND_ASYNC: +		/* +		 * In the async case this is being triggered with the +		 * device lock held and the unregistration work needs to +		 * be moved out of line iff this is thread has won the +		 * race to schedule the deletion. +		 */ +		if (!kill_device(dev)) +			return; +  		get_device(dev);  		async_schedule_domain(nd_async_device_unregister, dev,  				&nd_async_domain);  		break;  	case ND_SYNC: +		/* +		 * In the sync case the device is being unregistered due +		 * to a state change of the parent. Claim the kill state +		 * to synchronize against other unregistration requests, +		 * or otherwise let the async path handle it if the +		 * unregistration was already queued. +		 */ +		nd_device_lock(dev); +		killed = kill_device(dev); +		nd_device_unlock(dev); + +		if (!killed) +			return; +  		nd_synchronize();  		device_unregister(dev);  		break; @@ -859,10 +893,12 @@ void wait_nvdimm_bus_probe_idle(struct device *dev)  	do {  		if (nvdimm_bus->probe_active == 0)  			break; -		nvdimm_bus_unlock(&nvdimm_bus->dev); -		wait_event(nvdimm_bus->probe_wait, +		nvdimm_bus_unlock(dev); +		nd_device_unlock(dev); +		wait_event(nvdimm_bus->wait,  				nvdimm_bus->probe_active == 0); -		nvdimm_bus_lock(&nvdimm_bus->dev); +		nd_device_lock(dev); +		nvdimm_bus_lock(dev);  	} while (true);  } @@ -945,20 +981,19 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,  		int read_only, unsigned int ioctl_cmd, unsigned long arg)  {  	struct nvdimm_bus_descriptor *nd_desc = nvdimm_bus->nd_desc; -	static char out_env[ND_CMD_MAX_ENVELOPE]; -	static char in_env[ND_CMD_MAX_ENVELOPE];  	const struct nd_cmd_desc *desc = NULL;  	unsigned int cmd = _IOC_NR(ioctl_cmd);  	struct device *dev = &nvdimm_bus->dev;  	void __user *p = (void __user *) arg; +	char *out_env = NULL, *in_env = NULL;  	const char *cmd_name, *dimm_name;  	u32 in_len = 0, out_len = 0;  	unsigned int func = cmd;  	unsigned long cmd_mask;  	struct nd_cmd_pkg pkg;  	int rc, i, cmd_rc; +	void *buf = NULL;  	u64 buf_len = 0; -	void *buf;  	if (nvdimm) {  		desc = nd_cmd_dimm_desc(cmd); @@ -989,7 +1024,7 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,  		case ND_CMD_ARS_START:  		case ND_CMD_CLEAR_ERROR:  		case ND_CMD_CALL: -			dev_dbg(&nvdimm_bus->dev, "'%s' command while read-only.\n", +			dev_dbg(dev, "'%s' command while read-only.\n",  					nvdimm ? nvdimm_cmd_name(cmd)  					: nvdimm_bus_cmd_name(cmd));  			return -EPERM; @@ -998,6 +1033,9 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,  		}  	/* process an input envelope */ +	in_env = kzalloc(ND_CMD_MAX_ENVELOPE, GFP_KERNEL); +	if (!in_env) +		return -ENOMEM;  	for (i = 0; i < desc->in_num; i++) {  		u32 in_size, copy; @@ -1005,14 +1043,17 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,  		if (in_size == UINT_MAX) {  			dev_err(dev, "%s:%s unknown input size cmd: %s field: %d\n",  					__func__, dimm_name, cmd_name, i); -			return -ENXIO; +			rc = -ENXIO; +			goto out;  		} -		if (in_len < sizeof(in_env)) -			copy = min_t(u32, sizeof(in_env) - in_len, in_size); +		if (in_len < ND_CMD_MAX_ENVELOPE) +			copy = min_t(u32, ND_CMD_MAX_ENVELOPE - in_len, in_size);  		else  			copy = 0; -		if (copy && copy_from_user(&in_env[in_len], p + in_len, copy)) -			return -EFAULT; +		if (copy && copy_from_user(&in_env[in_len], p + in_len, copy)) { +			rc = -EFAULT; +			goto out; +		}  		in_len += in_size;  	} @@ -1024,6 +1065,12 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,  	}  	/* process an output envelope */ +	out_env = kzalloc(ND_CMD_MAX_ENVELOPE, GFP_KERNEL); +	if (!out_env) { +		rc = -ENOMEM; +		goto out; +	} +  	for (i = 0; i < desc->out_num; i++) {  		u32 out_size = nd_cmd_out_size(nvdimm, cmd, desc, i,  				(u32 *) in_env, (u32 *) out_env, 0); @@ -1032,15 +1079,18 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,  		if (out_size == UINT_MAX) {  			dev_dbg(dev, "%s unknown output size cmd: %s field: %d\n",  					dimm_name, cmd_name, i); -			return -EFAULT; +			rc = -EFAULT; +			goto out;  		} -		if (out_len < sizeof(out_env)) -			copy = min_t(u32, sizeof(out_env) - out_len, out_size); +		if (out_len < ND_CMD_MAX_ENVELOPE) +			copy = min_t(u32, ND_CMD_MAX_ENVELOPE - out_len, out_size);  		else  			copy = 0;  		if (copy && copy_from_user(&out_env[out_len], -					p + in_len + out_len, copy)) -			return -EFAULT; +					p + in_len + out_len, copy)) { +			rc = -EFAULT; +			goto out; +		}  		out_len += out_size;  	} @@ -1048,19 +1098,23 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,  	if (buf_len > ND_IOCTL_MAX_BUFLEN) {  		dev_dbg(dev, "%s cmd: %s buf_len: %llu > %d\n", dimm_name,  				cmd_name, buf_len, ND_IOCTL_MAX_BUFLEN); -		return -EINVAL; +		rc = -EINVAL; +		goto out;  	}  	buf = vmalloc(buf_len); -	if (!buf) -		return -ENOMEM; +	if (!buf) { +		rc = -ENOMEM; +		goto out; +	}  	if (copy_from_user(buf, p, buf_len)) {  		rc = -EFAULT;  		goto out;  	} -	nvdimm_bus_lock(&nvdimm_bus->dev); +	nd_device_lock(dev); +	nvdimm_bus_lock(dev);  	rc = nd_cmd_clear_to_send(nvdimm_bus, nvdimm, func, buf);  	if (rc)  		goto out_unlock; @@ -1075,39 +1129,24 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm,  		nvdimm_account_cleared_poison(nvdimm_bus, clear_err->address,  				clear_err->cleared);  	} -	nvdimm_bus_unlock(&nvdimm_bus->dev);  	if (copy_to_user(p, buf, buf_len))  		rc = -EFAULT; -	vfree(buf); -	return rc; - - out_unlock: -	nvdimm_bus_unlock(&nvdimm_bus->dev); - out: +out_unlock: +	nvdimm_bus_unlock(dev); +	nd_device_unlock(dev); +out: +	kfree(in_env); +	kfree(out_env);  	vfree(buf);  	return rc;  } -static long nd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) -{ -	long id = (long) file->private_data; -	int rc = -ENXIO, ro; -	struct nvdimm_bus *nvdimm_bus; - -	ro = ((file->f_flags & O_ACCMODE) == O_RDONLY); -	mutex_lock(&nvdimm_bus_list_mutex); -	list_for_each_entry(nvdimm_bus, &nvdimm_bus_list, list) { -		if (nvdimm_bus->id == id) { -			rc = __nd_ioctl(nvdimm_bus, NULL, ro, cmd, arg); -			break; -		} -	} -	mutex_unlock(&nvdimm_bus_list_mutex); - -	return rc; -} +enum nd_ioctl_mode { +	BUS_IOCTL, +	DIMM_IOCTL, +};  static int match_dimm(struct device *dev, void *data)  { @@ -1122,31 +1161,62 @@ static int match_dimm(struct device *dev, void *data)  	return 0;  } -static long nvdimm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +static long nd_ioctl(struct file *file, unsigned int cmd, unsigned long arg, +		enum nd_ioctl_mode mode) +  { -	int rc = -ENXIO, ro; -	struct nvdimm_bus *nvdimm_bus; +	struct nvdimm_bus *nvdimm_bus, *found = NULL; +	long id = (long) file->private_data; +	struct nvdimm *nvdimm = NULL; +	int rc, ro;  	ro = ((file->f_flags & O_ACCMODE) == O_RDONLY);  	mutex_lock(&nvdimm_bus_list_mutex);  	list_for_each_entry(nvdimm_bus, &nvdimm_bus_list, list) { -		struct device *dev = device_find_child(&nvdimm_bus->dev, -				file->private_data, match_dimm); -		struct nvdimm *nvdimm; - -		if (!dev) -			continue; +		if (mode == DIMM_IOCTL) { +			struct device *dev; + +			dev = device_find_child(&nvdimm_bus->dev, +					file->private_data, match_dimm); +			if (!dev) +				continue; +			nvdimm = to_nvdimm(dev); +			found = nvdimm_bus; +		} else if (nvdimm_bus->id == id) { +			found = nvdimm_bus; +		} -		nvdimm = to_nvdimm(dev); -		rc = __nd_ioctl(nvdimm_bus, nvdimm, ro, cmd, arg); -		put_device(dev); -		break; +		if (found) { +			atomic_inc(&nvdimm_bus->ioctl_active); +			break; +		}  	}  	mutex_unlock(&nvdimm_bus_list_mutex); +	if (!found) +		return -ENXIO; + +	nvdimm_bus = found; +	rc = __nd_ioctl(nvdimm_bus, nvdimm, ro, cmd, arg); + +	if (nvdimm) +		put_device(&nvdimm->dev); +	if (atomic_dec_and_test(&nvdimm_bus->ioctl_active)) +		wake_up(&nvdimm_bus->wait); +  	return rc;  } +static long bus_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ +	return nd_ioctl(file, cmd, arg, BUS_IOCTL); +} + +static long dimm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ +	return nd_ioctl(file, cmd, arg, DIMM_IOCTL); +} +  static int nd_open(struct inode *inode, struct file *file)  {  	long minor = iminor(inode); @@ -1158,16 +1228,16 @@ static int nd_open(struct inode *inode, struct file *file)  static const struct file_operations nvdimm_bus_fops = {  	.owner = THIS_MODULE,  	.open = nd_open, -	.unlocked_ioctl = nd_ioctl, -	.compat_ioctl = nd_ioctl, +	.unlocked_ioctl = bus_ioctl, +	.compat_ioctl = bus_ioctl,  	.llseek = noop_llseek,  };  static const struct file_operations nvdimm_fops = {  	.owner = THIS_MODULE,  	.open = nd_open, -	.unlocked_ioctl = nvdimm_ioctl, -	.compat_ioctl = nvdimm_ioctl, +	.unlocked_ioctl = dimm_ioctl, +	.compat_ioctl = dimm_ioctl,  	.llseek = noop_llseek,  }; diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c index 5e1f060547bf..9204f1e9fd14 100644 --- a/drivers/nvdimm/core.c +++ b/drivers/nvdimm/core.c @@ -246,7 +246,7 @@ static int nd_uuid_parse(struct device *dev, u8 *uuid_out, const char *buf,   *   * Enforce that uuids can only be changed while the device is disabled   * (driver detached) - * LOCKING: expects device_lock() is held on entry + * LOCKING: expects nd_device_lock() is held on entry   */  int nd_uuid_store(struct device *dev, u8 **uuid_out, const char *buf,  		size_t len) @@ -347,15 +347,15 @@ static DEVICE_ATTR_RO(provider);  static int flush_namespaces(struct device *dev, void *data)  { -	device_lock(dev); -	device_unlock(dev); +	nd_device_lock(dev); +	nd_device_unlock(dev);  	return 0;  }  static int flush_regions_dimms(struct device *dev, void *data)  { -	device_lock(dev); -	device_unlock(dev); +	nd_device_lock(dev); +	nd_device_unlock(dev);  	device_for_each_child(dev, NULL, flush_namespaces);  	return 0;  } diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index dfecd6e17043..29a065e769ea 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c @@ -484,12 +484,12 @@ static ssize_t security_store(struct device *dev,  	 * done while probing is idle and the DIMM is not in active use  	 * in any region.  	 */ -	device_lock(dev); +	nd_device_lock(dev);  	nvdimm_bus_lock(dev);  	wait_nvdimm_bus_probe_idle(dev);  	rc = __security_store(dev, buf, len);  	nvdimm_bus_unlock(dev); -	device_unlock(dev); +	nd_device_unlock(dev);  	return rc;  } diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index 2d8d7e554877..a16e52251a30 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c @@ -410,7 +410,7 @@ static ssize_t alt_name_store(struct device *dev,  	struct nd_region *nd_region = to_nd_region(dev->parent);  	ssize_t rc; -	device_lock(dev); +	nd_device_lock(dev);  	nvdimm_bus_lock(dev);  	wait_nvdimm_bus_probe_idle(dev);  	rc = __alt_name_store(dev, buf, len); @@ -418,7 +418,7 @@ static ssize_t alt_name_store(struct device *dev,  		rc = nd_namespace_label_update(nd_region, dev);  	dev_dbg(dev, "%s(%zd)\n", rc < 0 ? "fail " : "", rc);  	nvdimm_bus_unlock(dev); -	device_unlock(dev); +	nd_device_unlock(dev);  	return rc < 0 ? rc : len;  } @@ -1077,7 +1077,7 @@ static ssize_t size_store(struct device *dev,  	if (rc)  		return rc; -	device_lock(dev); +	nd_device_lock(dev);  	nvdimm_bus_lock(dev);  	wait_nvdimm_bus_probe_idle(dev);  	rc = __size_store(dev, val); @@ -1103,7 +1103,7 @@ static ssize_t size_store(struct device *dev,  	dev_dbg(dev, "%llx %s (%d)\n", val, rc < 0 ? "fail" : "success", rc);  	nvdimm_bus_unlock(dev); -	device_unlock(dev); +	nd_device_unlock(dev);  	return rc < 0 ? rc : len;  } @@ -1286,7 +1286,7 @@ static ssize_t uuid_store(struct device *dev,  	} else  		return -ENXIO; -	device_lock(dev); +	nd_device_lock(dev);  	nvdimm_bus_lock(dev);  	wait_nvdimm_bus_probe_idle(dev);  	if (to_ndns(dev)->claim) @@ -1302,7 +1302,7 @@ static ssize_t uuid_store(struct device *dev,  	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 < 0 ? rc : len;  } @@ -1376,7 +1376,7 @@ static ssize_t sector_size_store(struct device *dev,  	} else  		return -ENXIO; -	device_lock(dev); +	nd_device_lock(dev);  	nvdimm_bus_lock(dev);  	if (to_ndns(dev)->claim)  		rc = -EBUSY; @@ -1387,7 +1387,7 @@ static ssize_t sector_size_store(struct device *dev,  	dev_dbg(dev, "result: %zd %s: %s%s", rc, rc < 0 ? "tried" : "wrote",  			buf, buf[len - 1] == '\n' ? "" : "\n");  	nvdimm_bus_unlock(dev); -	device_unlock(dev); +	nd_device_unlock(dev);  	return rc ? rc : len;  } @@ -1502,9 +1502,9 @@ static ssize_t holder_show(struct device *dev,  	struct nd_namespace_common *ndns = to_ndns(dev);  	ssize_t rc; -	device_lock(dev); +	nd_device_lock(dev);  	rc = sprintf(buf, "%s\n", ndns->claim ? dev_name(ndns->claim) : ""); -	device_unlock(dev); +	nd_device_unlock(dev);  	return rc;  } @@ -1541,7 +1541,7 @@ static ssize_t holder_class_store(struct device *dev,  	struct nd_region *nd_region = to_nd_region(dev->parent);  	ssize_t rc; -	device_lock(dev); +	nd_device_lock(dev);  	nvdimm_bus_lock(dev);  	wait_nvdimm_bus_probe_idle(dev);  	rc = __holder_class_store(dev, buf); @@ -1549,7 +1549,7 @@ static ssize_t holder_class_store(struct device *dev,  		rc = nd_namespace_label_update(nd_region, dev);  	dev_dbg(dev, "%s(%zd)\n", rc < 0 ? "fail " : "", rc);  	nvdimm_bus_unlock(dev); -	device_unlock(dev); +	nd_device_unlock(dev);  	return rc < 0 ? rc : len;  } @@ -1560,7 +1560,7 @@ static ssize_t holder_class_show(struct device *dev,  	struct nd_namespace_common *ndns = to_ndns(dev);  	ssize_t rc; -	device_lock(dev); +	nd_device_lock(dev);  	if (ndns->claim_class == NVDIMM_CCLASS_NONE)  		rc = sprintf(buf, "\n");  	else if ((ndns->claim_class == NVDIMM_CCLASS_BTT) || @@ -1572,7 +1572,7 @@ static ssize_t holder_class_show(struct device *dev,  		rc = sprintf(buf, "dax\n");  	else  		rc = sprintf(buf, "<unknown>\n"); -	device_unlock(dev); +	nd_device_unlock(dev);  	return rc;  } @@ -1586,7 +1586,7 @@ static ssize_t mode_show(struct device *dev,  	char *mode;  	ssize_t rc; -	device_lock(dev); +	nd_device_lock(dev);  	claim = ndns->claim;  	if (claim && is_nd_btt(claim))  		mode = "safe"; @@ -1599,7 +1599,7 @@ static ssize_t mode_show(struct device *dev,  	else  		mode = "raw";  	rc = sprintf(buf, "%s\n", mode); -	device_unlock(dev); +	nd_device_unlock(dev);  	return rc;  } @@ -1703,8 +1703,8 @@ struct nd_namespace_common *nvdimm_namespace_common_probe(struct device *dev)  		 * Flush any in-progess probes / removals in the driver  		 * for the raw personality of this namespace.  		 */ -		device_lock(&ndns->dev); -		device_unlock(&ndns->dev); +		nd_device_lock(&ndns->dev); +		nd_device_unlock(&ndns->dev);  		if (ndns->dev.driver) {  			dev_dbg(&ndns->dev, "is active, can't bind %s\n",  					dev_name(dev)); diff --git a/drivers/nvdimm/nd-core.h b/drivers/nvdimm/nd-core.h index 391e88de3a29..0ac52b6eb00e 100644 --- a/drivers/nvdimm/nd-core.h +++ b/drivers/nvdimm/nd-core.h @@ -9,6 +9,7 @@  #include <linux/sizes.h>  #include <linux/mutex.h>  #include <linux/nd.h> +#include "nd.h"  extern struct list_head nvdimm_bus_list;  extern struct mutex nvdimm_bus_list_mutex; @@ -17,10 +18,11 @@ extern struct workqueue_struct *nvdimm_wq;  struct nvdimm_bus {  	struct nvdimm_bus_descriptor *nd_desc; -	wait_queue_head_t probe_wait; +	wait_queue_head_t wait;  	struct list_head list;  	struct device dev;  	int id, probe_active; +	atomic_t ioctl_active;  	struct list_head mapping_list;  	struct mutex reconfig_mutex;  	struct badrange badrange; @@ -181,4 +183,71 @@ ssize_t nd_namespace_store(struct device *dev,  		struct nd_namespace_common **_ndns, const char *buf,  		size_t len);  struct nd_pfn *to_nd_pfn_safe(struct device *dev); +bool is_nvdimm_bus(struct device *dev); + +#ifdef CONFIG_PROVE_LOCKING +extern struct class *nd_class; + +enum { +	LOCK_BUS, +	LOCK_NDCTL, +	LOCK_REGION, +	LOCK_DIMM = LOCK_REGION, +	LOCK_NAMESPACE, +	LOCK_CLAIM, +}; + +static inline void debug_nvdimm_lock(struct device *dev) +{ +	if (is_nd_region(dev)) +		mutex_lock_nested(&dev->lockdep_mutex, LOCK_REGION); +	else if (is_nvdimm(dev)) +		mutex_lock_nested(&dev->lockdep_mutex, LOCK_DIMM); +	else if (is_nd_btt(dev) || is_nd_pfn(dev) || is_nd_dax(dev)) +		mutex_lock_nested(&dev->lockdep_mutex, LOCK_CLAIM); +	else if (dev->parent && (is_nd_region(dev->parent))) +		mutex_lock_nested(&dev->lockdep_mutex, LOCK_NAMESPACE); +	else if (is_nvdimm_bus(dev)) +		mutex_lock_nested(&dev->lockdep_mutex, LOCK_BUS); +	else if (dev->class && dev->class == nd_class) +		mutex_lock_nested(&dev->lockdep_mutex, LOCK_NDCTL); +	else +		dev_WARN(dev, "unknown lock level\n"); +} + +static inline void debug_nvdimm_unlock(struct device *dev) +{ +	mutex_unlock(&dev->lockdep_mutex); +} + +static inline void nd_device_lock(struct device *dev) +{ +	device_lock(dev); +	debug_nvdimm_lock(dev); +} + +static inline void nd_device_unlock(struct device *dev) +{ +	debug_nvdimm_unlock(dev); +	device_unlock(dev); +} +#else +static inline void nd_device_lock(struct device *dev) +{ +	device_lock(dev); +} + +static inline void nd_device_unlock(struct device *dev) +{ +	device_unlock(dev); +} + +static inline void debug_nvdimm_lock(struct device *dev) +{ +} + +static inline void debug_nvdimm_unlock(struct device *dev) +{ +} +#endif  #endif /* __ND_CORE_H__ */ diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c index df2bdbd22450..3e7b11cf1aae 100644 --- a/drivers/nvdimm/pfn_devs.c +++ b/drivers/nvdimm/pfn_devs.c @@ -67,7 +67,7 @@ static ssize_t mode_store(struct device *dev,  	struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev);  	ssize_t rc = 0; -	device_lock(dev); +	nd_device_lock(dev);  	nvdimm_bus_lock(dev);  	if (dev->driver)  		rc = -EBUSY; @@ -89,7 +89,7 @@ static ssize_t mode_store(struct device *dev,  	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;  } @@ -132,14 +132,14 @@ static ssize_t align_store(struct device *dev,  	struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev);  	ssize_t rc; -	device_lock(dev); +	nd_device_lock(dev);  	nvdimm_bus_lock(dev);  	rc = nd_size_select_store(dev, buf, &nd_pfn->align,  			nd_pfn_supported_alignments());  	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;  } @@ -161,11 +161,11 @@ static ssize_t uuid_store(struct device *dev,  	struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev);  	ssize_t rc; -	device_lock(dev); +	nd_device_lock(dev);  	rc = nd_uuid_store(dev, &nd_pfn->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;  } @@ -190,13 +190,13 @@ static ssize_t namespace_store(struct device *dev,  	struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev);  	ssize_t rc; -	device_lock(dev); +	nd_device_lock(dev);  	nvdimm_bus_lock(dev);  	rc = nd_namespace_store(dev, &nd_pfn->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;  } @@ -208,7 +208,7 @@ static ssize_t resource_show(struct device *dev,  	struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev);  	ssize_t rc; -	device_lock(dev); +	nd_device_lock(dev);  	if (dev->driver) {  		struct nd_pfn_sb *pfn_sb = nd_pfn->pfn_sb;  		u64 offset = __le64_to_cpu(pfn_sb->dataoff); @@ -222,7 +222,7 @@ static ssize_t resource_show(struct device *dev,  		/* no address to convey if the pfn instance is disabled */  		rc = -ENXIO;  	} -	device_unlock(dev); +	nd_device_unlock(dev);  	return rc;  } @@ -234,7 +234,7 @@ static ssize_t size_show(struct device *dev,  	struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev);  	ssize_t rc; -	device_lock(dev); +	nd_device_lock(dev);  	if (dev->driver) {  		struct nd_pfn_sb *pfn_sb = nd_pfn->pfn_sb;  		u64 offset = __le64_to_cpu(pfn_sb->dataoff); @@ -250,7 +250,7 @@ static ssize_t size_show(struct device *dev,  		/* no size to convey if the pfn instance is disabled */  		rc = -ENXIO;  	} -	device_unlock(dev); +	nd_device_unlock(dev);  	return rc;  } diff --git a/drivers/nvdimm/pmem.c b/drivers/nvdimm/pmem.c index 2bf3acd69613..4c121dd03dd9 100644 --- a/drivers/nvdimm/pmem.c +++ b/drivers/nvdimm/pmem.c @@ -522,8 +522,8 @@ static int nd_pmem_remove(struct device *dev)  		nvdimm_namespace_detach_btt(to_nd_btt(dev));  	else {  		/* -		 * Note, this assumes device_lock() context to not race -		 * nd_pmem_notify() +		 * Note, this assumes nd_device_lock() context to not +		 * race nd_pmem_notify()  		 */  		sysfs_put(pmem->bb_state);  		pmem->bb_state = NULL; diff --git a/drivers/nvdimm/region.c b/drivers/nvdimm/region.c index ef46cc3a71ae..37bf8719a2a4 100644 --- a/drivers/nvdimm/region.c +++ b/drivers/nvdimm/region.c @@ -34,17 +34,6 @@ static int nd_region_probe(struct device *dev)  	if (rc)  		return rc; -	rc = nd_region_register_namespaces(nd_region, &err); -	if (rc < 0) -		return rc; - -	ndrd = dev_get_drvdata(dev); -	ndrd->ns_active = rc; -	ndrd->ns_count = rc + err; - -	if (rc && err && rc == err) -		return -ENODEV; -  	if (is_nd_pmem(&nd_region->dev)) {  		struct resource ndr_res; @@ -60,6 +49,17 @@ static int nd_region_probe(struct device *dev)  		nvdimm_badblocks_populate(nd_region, &nd_region->bb, &ndr_res);  	} +	rc = nd_region_register_namespaces(nd_region, &err); +	if (rc < 0) +		return rc; + +	ndrd = dev_get_drvdata(dev); +	ndrd->ns_active = rc; +	ndrd->ns_count = rc + err; + +	if (rc && err && rc == err) +		return -ENODEV; +  	nd_region->btt_seed = nd_btt_create(nd_region);  	nd_region->pfn_seed = nd_pfn_create(nd_region);  	nd_region->dax_seed = nd_dax_create(nd_region); @@ -102,7 +102,7 @@ static int nd_region_remove(struct device *dev)  	nvdimm_bus_unlock(dev);  	/* -	 * Note, this assumes device_lock() context to not race +	 * Note, this assumes nd_device_lock() context to not race  	 * nd_region_notify()  	 */  	sysfs_put(nd_region->bb_state); diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index 56f2227f192a..af30cbe7a8ea 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -331,7 +331,7 @@ static ssize_t set_cookie_show(struct device *dev,  	 * the v1.1 namespace label cookie definition. To read all this  	 * data we need to wait for probing to settle.  	 */ -	device_lock(dev); +	nd_device_lock(dev);  	nvdimm_bus_lock(dev);  	wait_nvdimm_bus_probe_idle(dev);  	if (nd_region->ndr_mappings) { @@ -348,7 +348,7 @@ static ssize_t set_cookie_show(struct device *dev,  		}  	}  	nvdimm_bus_unlock(dev); -	device_unlock(dev); +	nd_device_unlock(dev);  	if (rc)  		return rc; @@ -424,10 +424,12 @@ static ssize_t available_size_show(struct device *dev,  	 * memory nvdimm_bus_lock() is dropped, but that's userspace's  	 * problem to not race itself.  	 */ +	nd_device_lock(dev);  	nvdimm_bus_lock(dev);  	wait_nvdimm_bus_probe_idle(dev);  	available = nd_region_available_dpa(nd_region);  	nvdimm_bus_unlock(dev); +	nd_device_unlock(dev);  	return sprintf(buf, "%llu\n", available);  } @@ -439,10 +441,12 @@ static ssize_t max_available_extent_show(struct device *dev,  	struct nd_region *nd_region = to_nd_region(dev);  	unsigned long long available = 0; +	nd_device_lock(dev);  	nvdimm_bus_lock(dev);  	wait_nvdimm_bus_probe_idle(dev);  	available = nd_region_allocatable_dpa(nd_region);  	nvdimm_bus_unlock(dev); +	nd_device_unlock(dev);  	return sprintf(buf, "%llu\n", available);  } @@ -561,12 +565,12 @@ static ssize_t region_badblocks_show(struct device *dev,  	struct nd_region *nd_region = to_nd_region(dev);  	ssize_t rc; -	device_lock(dev); +	nd_device_lock(dev);  	if (dev->driver)  		rc = badblocks_show(&nd_region->bb, buf, 0);  	else  		rc = -ENXIO; -	device_unlock(dev); +	nd_device_unlock(dev);  	return rc;  }  |