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

Commit 98f33285 authored by Kiyoshi Ueda's avatar Kiyoshi Ueda Committed by Alasdair G Kergon
Browse files

dm ioctl: release _hash_lock between devices in remove_all



This patch changes dm_hash_remove_all() to release _hash_lock when
removing a device.  After removing the device, dm_hash_remove_all()
takes _hash_lock and searches the hash from scratch again.

This patch is a preparation for the next patch, which changes device
deletion code to wait for md reference to be 0.  Without this patch,
the wait in the next patch may cause AB-BA deadlock:
  CPU0                                CPU1
  -----------------------------------------------------------------------
  dm_hash_remove_all()
    down_write(_hash_lock)
                                      table_status()
                                        md = find_device()
                                               dm_get(md)
                                                 <increment md->holders>
                                        dm_get_live_or_inactive_table()
                                          dm_get_inactive_table()
                                            down_write(_hash_lock)
    <in the md deletion code>
      <wait for md->holders to be 0>

Signed-off-by: default avatarKiyoshi Ueda <k-ueda@ct.jp.nec.com>
Signed-off-by: default avatarJun'ichi Nomura <j-nomura@ce.jp.nec.com>
Cc: stable@kernel.org
Signed-off-by: default avatarAlasdair G Kergon <agk@redhat.com>
parent abdc568b
Loading
Loading
Loading
Loading
+25 −19
Original line number Diff line number Diff line
@@ -249,40 +249,46 @@ static void __hash_remove(struct hash_cell *hc)

static void dm_hash_remove_all(int keep_open_devices)
{
	int i, dev_skipped, dev_removed;
	int i, dev_skipped;
	struct hash_cell *hc;
	struct list_head *tmp, *n;
	struct mapped_device *md;

retry:
	dev_skipped = 0;

	down_write(&_hash_lock);

retry:
	dev_skipped = dev_removed = 0;
	for (i = 0; i < NUM_BUCKETS; i++) {
		list_for_each_safe (tmp, n, _name_buckets + i) {
			hc = list_entry(tmp, struct hash_cell, name_list);
		list_for_each_entry(hc, _name_buckets + i, name_list) {
			md = hc->md;
			dm_get(md);

			if (keep_open_devices &&
			    dm_lock_for_deletion(hc->md)) {
			if (keep_open_devices && dm_lock_for_deletion(md)) {
				dm_put(md);
				dev_skipped++;
				continue;
			}

			__hash_remove(hc);
			dev_removed = 1;
		}
	}

			up_write(&_hash_lock);

			dm_put(md);

			/*
	 * Some mapped devices may be using other mapped devices, so if any
	 * still exist, repeat until we make no further progress.
			 * Some mapped devices may be using other mapped
			 * devices, so repeat until we make no further
			 * progress.  If a new mapped device is created
			 * here it will also get removed.
			 */
	if (dev_skipped) {
		if (dev_removed)
			goto retry;

		DMWARN("remove_all left %d open device(s)", dev_skipped);
		}
	}

	up_write(&_hash_lock);

	if (dev_skipped)
		DMWARN("remove_all left %d open device(s)", dev_skipped);
}

static struct mapped_device *dm_hash_rename(struct dm_ioctl *param,