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

Commit 7ec75f25 authored by Jeff Mahoney's avatar Jeff Mahoney Committed by Linus Torvalds
Browse files

[PATCH] dm: fix mapped device ref counting



To avoid races, _minor_lock must be held while changing mapped device
reference counts.

There are a few paths where a mapped_device pointer is returned before a
reference is taken.  This patch fixes them.

[akpm: too late for 2.6.17 - suitable for 2.6.17.x after it has settled]

Signed-off-by: default avatarJeff Mahoney <jeffm@suse.com>
Signed-off-by: default avatarAlasdair G Kergon <agk@redhat.com>
Cc: <stable@kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent fba9f90e
Loading
Loading
Loading
Loading
+24 −10
Original line number Diff line number Diff line
@@ -102,8 +102,10 @@ static struct hash_cell *__get_name_cell(const char *str)
	unsigned int h = hash_str(str);

	list_for_each_entry (hc, _name_buckets + h, name_list)
		if (!strcmp(hc->name, str))
		if (!strcmp(hc->name, str)) {
			dm_get(hc->md);
			return hc;
		}

	return NULL;
}
@@ -114,8 +116,10 @@ static struct hash_cell *__get_uuid_cell(const char *str)
	unsigned int h = hash_str(str);

	list_for_each_entry (hc, _uuid_buckets + h, uuid_list)
		if (!strcmp(hc->uuid, str))
		if (!strcmp(hc->uuid, str)) {
			dm_get(hc->md);
			return hc;
		}

	return NULL;
}
@@ -191,7 +195,7 @@ static int unregister_with_devfs(struct hash_cell *hc)
 */
static int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md)
{
	struct hash_cell *cell;
	struct hash_cell *cell, *hc;

	/*
	 * Allocate the new cells.
@@ -204,14 +208,19 @@ static int dm_hash_insert(const char *name, const char *uuid, struct mapped_devi
	 * Insert the cell into both hash tables.
	 */
	down_write(&_hash_lock);
	if (__get_name_cell(name))
	hc = __get_name_cell(name);
	if (hc) {
		dm_put(hc->md);
		goto bad;
	}

	list_add(&cell->name_list, _name_buckets + hash_str(name));

	if (uuid) {
		if (__get_uuid_cell(uuid)) {
		hc = __get_uuid_cell(uuid);
		if (hc) {
			list_del(&cell->name_list);
			dm_put(hc->md);
			goto bad;
		}
		list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid));
@@ -289,6 +298,7 @@ static int dm_hash_rename(const char *old, const char *new)
	if (hc) {
		DMWARN("asked to rename to an already existing name %s -> %s",
		       old, new);
		dm_put(hc->md);
		up_write(&_hash_lock);
		kfree(new_name);
		return -EBUSY;
@@ -328,6 +338,7 @@ static int dm_hash_rename(const char *old, const char *new)
		dm_table_put(table);
	}

	dm_put(hc->md);
	up_write(&_hash_lock);
	kfree(old_name);
	return 0;
@@ -611,10 +622,8 @@ static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param)
		return __get_name_cell(param->name);

	md = dm_get_md(huge_decode_dev(param->dev));
	if (md) {
	if (md)
		mdptr = dm_get_mdptr(md);
		dm_put(md);
	}

	return mdptr;
}
@@ -628,7 +637,6 @@ static struct mapped_device *find_device(struct dm_ioctl *param)
	hc = __find_device_hash_cell(param);
	if (hc) {
		md = hc->md;
		dm_get(md);

		/*
		 * Sneakily write in both the name and the uuid
@@ -653,6 +661,7 @@ static struct mapped_device *find_device(struct dm_ioctl *param)
static int dev_remove(struct dm_ioctl *param, size_t param_size)
{
	struct hash_cell *hc;
	struct mapped_device *md;

	down_write(&_hash_lock);
	hc = __find_device_hash_cell(param);
@@ -663,8 +672,11 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size)
		return -ENXIO;
	}

	md = hc->md;

	__hash_remove(hc);
	up_write(&_hash_lock);
	dm_put(md);
	param->data_size = 0;
	return 0;
}
@@ -790,7 +802,6 @@ static int do_resume(struct dm_ioctl *param)
	}

	md = hc->md;
	dm_get(md);

	new_map = hc->new_map;
	hc->new_map = NULL;
@@ -1078,6 +1089,7 @@ static int table_clear(struct dm_ioctl *param, size_t param_size)
{
	int r;
	struct hash_cell *hc;
	struct mapped_device *md;

	down_write(&_hash_lock);

@@ -1096,7 +1108,9 @@ static int table_clear(struct dm_ioctl *param, size_t param_size)
	param->flags &= ~DM_INACTIVE_PRESENT_FLAG;

	r = __dev_status(hc->md, param);
	md = hc->md;
	up_write(&_hash_lock);
	dm_put(md);
	return r;
}