md: factor out the rdev overlaps check from rdev_size_store
This splits the code into nicely readable chunks and also avoids the refcount inc/dec manipulations. Signed-off-by: Christoph Hellwig <hch@lst.de> Reviewed-by: Logan Gunthorpe <logang@deltatee.com> Reviewed-by: Hannes Reinecke <hare@suse.de> Signed-off-by: Song Liu <song@kernel.org> Signed-off-by: Jens Axboe <axboe@kernel.dk>
This commit is contained in:
parent
33b614e334
commit
2652a1bd2e
@ -3343,14 +3343,33 @@ rdev_size_show(struct md_rdev *rdev, char *page)
|
||||
return sprintf(page, "%llu\n", (unsigned long long)rdev->sectors / 2);
|
||||
}
|
||||
|
||||
static int overlaps(sector_t s1, sector_t l1, sector_t s2, sector_t l2)
|
||||
static int md_rdevs_overlap(struct md_rdev *a, struct md_rdev *b)
|
||||
{
|
||||
/* check if two start/length pairs overlap */
|
||||
if (s1+l1 <= s2)
|
||||
return 0;
|
||||
if (s2+l2 <= s1)
|
||||
return 0;
|
||||
return 1;
|
||||
if (a->data_offset + a->sectors <= b->data_offset)
|
||||
return false;
|
||||
if (b->data_offset + b->sectors <= a->data_offset)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool md_rdev_overlaps(struct md_rdev *rdev)
|
||||
{
|
||||
struct mddev *mddev;
|
||||
struct md_rdev *rdev2;
|
||||
|
||||
spin_lock(&all_mddevs_lock);
|
||||
list_for_each_entry(mddev, &all_mddevs, all_mddevs) {
|
||||
rdev_for_each(rdev2, mddev) {
|
||||
if (rdev != rdev2 && rdev->bdev == rdev2->bdev &&
|
||||
md_rdevs_overlap(rdev, rdev2)) {
|
||||
spin_unlock(&all_mddevs_lock);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock(&all_mddevs_lock);
|
||||
return false;
|
||||
}
|
||||
|
||||
static int strict_blocks_to_sectors(const char *buf, sector_t *sectors)
|
||||
@ -3402,46 +3421,21 @@ rdev_size_store(struct md_rdev *rdev, const char *buf, size_t len)
|
||||
return -EINVAL; /* component must fit device */
|
||||
|
||||
rdev->sectors = sectors;
|
||||
if (sectors > oldsectors && my_mddev->external) {
|
||||
/* Need to check that all other rdevs with the same
|
||||
* ->bdev do not overlap. 'rcu' is sufficient to walk
|
||||
* the rdev lists safely.
|
||||
* This check does not provide a hard guarantee, it
|
||||
* just helps avoid dangerous mistakes.
|
||||
|
||||
/*
|
||||
* Check that all other rdevs with the same bdev do not overlap. This
|
||||
* check does not provide a hard guarantee, it just helps avoid
|
||||
* dangerous mistakes.
|
||||
*/
|
||||
if (sectors > oldsectors && my_mddev->external &&
|
||||
md_rdev_overlaps(rdev)) {
|
||||
/*
|
||||
* Someone else could have slipped in a size change here, but
|
||||
* doing so is just silly. We put oldsectors back because we
|
||||
* know it is safe, and trust userspace not to race with itself.
|
||||
*/
|
||||
struct mddev *mddev;
|
||||
int overlap = 0;
|
||||
struct list_head *tmp;
|
||||
|
||||
rcu_read_lock();
|
||||
for_each_mddev(mddev, tmp) {
|
||||
struct md_rdev *rdev2;
|
||||
|
||||
rdev_for_each(rdev2, mddev)
|
||||
if (rdev->bdev == rdev2->bdev &&
|
||||
rdev != rdev2 &&
|
||||
overlaps(rdev->data_offset, rdev->sectors,
|
||||
rdev2->data_offset,
|
||||
rdev2->sectors)) {
|
||||
overlap = 1;
|
||||
break;
|
||||
}
|
||||
if (overlap) {
|
||||
mddev_put(mddev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
rcu_read_unlock();
|
||||
if (overlap) {
|
||||
/* Someone else could have slipped in a size
|
||||
* change here, but doing so is just silly.
|
||||
* We put oldsectors back because we *know* it is
|
||||
* safe, and trust userspace not to race with
|
||||
* itself
|
||||
*/
|
||||
rdev->sectors = oldsectors;
|
||||
return -EBUSY;
|
||||
}
|
||||
rdev->sectors = oldsectors;
|
||||
return -EBUSY;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user