Commit 02acc3a4 authored by Mike Snitzer's avatar Mike Snitzer Committed by Alasdair G Kergon
Browse files

dm table: ensure targets are aligned to logical_block_size



Ensure I/O is aligned to the logical block size of target devices.

Rename check_device_area() to device_area_is_valid() for clarity and
establish the device limits including the logical block size prior to
calling it.

Signed-off-by: default avatarMike Snitzer <snitzer@redhat.com>
Signed-off-by: default avatarAlasdair G Kergon <agk@redhat.com>
parent 60935eb2
Loading
Loading
Loading
Loading
+44 −14
Original line number Diff line number Diff line
@@ -383,16 +383,45 @@ static void close_dev(struct dm_dev_internal *d, struct mapped_device *md)
/*
 * If possible, this checks an area of a destination device is valid.
 */
static int check_device_area(struct dm_dev_internal *dd, sector_t start,
			     sector_t len)
static int device_area_is_valid(struct dm_target *ti, struct block_device *bdev,
			     sector_t start, sector_t len)
{
	sector_t dev_size = i_size_read(dd->dm_dev.bdev->bd_inode) >>
			    SECTOR_SHIFT;
	sector_t dev_size = i_size_read(bdev->bd_inode) >> SECTOR_SHIFT;
	unsigned short logical_block_size_sectors =
		ti->limits.logical_block_size >> SECTOR_SHIFT;
	char b[BDEVNAME_SIZE];

	if (!dev_size)
		return 1;

	return ((start < dev_size) && (len <= (dev_size - start)));
	if ((start >= dev_size) || (start + len > dev_size)) {
		DMWARN("%s: %s too small for target",
		       dm_device_name(ti->table->md), bdevname(bdev, b));
		return 0;
	}

	if (logical_block_size_sectors <= 1)
		return 1;

	if (start & (logical_block_size_sectors - 1)) {
		DMWARN("%s: start=%llu not aligned to h/w "
		       "logical block size %hu of %s",
		       dm_device_name(ti->table->md),
		       (unsigned long long)start,
		       ti->limits.logical_block_size, bdevname(bdev, b));
		return 0;
	}

	if (len & (logical_block_size_sectors - 1)) {
		DMWARN("%s: len=%llu not aligned to h/w "
		       "logical block size %hu of %s",
		       dm_device_name(ti->table->md),
		       (unsigned long long)len,
		       ti->limits.logical_block_size, bdevname(bdev, b));
		return 0;
	}

	return 1;
}

/*
@@ -478,14 +507,7 @@ static int __table_get_device(struct dm_table *t, struct dm_target *ti,
	}
	atomic_inc(&dd->count);

	if (!check_device_area(dd, start, len)) {
		DMWARN("device %s too small for target", path);
		dm_put_device(ti, &dd->dm_dev);
		return -EINVAL;
	}

	*result = &dd->dm_dev;

	return 0;
}

@@ -554,9 +576,17 @@ int dm_get_device(struct dm_target *ti, const char *path, sector_t start,
	int r = __table_get_device(ti->table, ti, path,
				   start, len, mode, result);

	if (!r)
	if (r)
		return r;

	dm_set_device_limits(ti, (*result)->bdev);

	if (!device_area_is_valid(ti, (*result)->bdev, start, len)) {
		dm_put_device(ti, *result);
		*result = NULL;
		return -EINVAL;
	}

	return r;
}