Commit 2652a1bd authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Jens Axboe

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: default avatarChristoph Hellwig <hch@lst.de>
Reviewed-by: default avatarLogan Gunthorpe <logang@deltatee.com>
Reviewed-by: default avatarHannes Reinecke <hare@suse.de>
Signed-off-by: default avatarSong Liu <song@kernel.org>
Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 33b614e3
...@@ -3343,14 +3343,33 @@ rdev_size_show(struct md_rdev *rdev, char *page) ...@@ -3343,14 +3343,33 @@ rdev_size_show(struct md_rdev *rdev, char *page)
return sprintf(page, "%llu\n", (unsigned long long)rdev->sectors / 2); 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 */ /* check if two start/length pairs overlap */
if (s1+l1 <= s2) if (a->data_offset + a->sectors <= b->data_offset)
return 0; return false;
if (s2+l2 <= s1) if (b->data_offset + b->sectors <= a->data_offset)
return 0; return false;
return 1; 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) 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) ...@@ -3402,46 +3421,21 @@ rdev_size_store(struct md_rdev *rdev, const char *buf, size_t len)
return -EINVAL; /* component must fit device */ return -EINVAL; /* component must fit device */
rdev->sectors = sectors; 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.
*/
struct mddev *mddev;
int overlap = 0;
struct list_head *tmp;
rcu_read_lock(); /*
for_each_mddev(mddev, tmp) { * Check that all other rdevs with the same bdev do not overlap. This
struct md_rdev *rdev2; * check does not provide a hard guarantee, it just helps avoid
* dangerous mistakes.
rdev_for_each(rdev2, mddev) */
if (rdev->bdev == rdev2->bdev && if (sectors > oldsectors && my_mddev->external &&
rdev != rdev2 && md_rdev_overlaps(rdev)) {
overlaps(rdev->data_offset, rdev->sectors, /*
rdev2->data_offset, * Someone else could have slipped in a size change here, but
rdev2->sectors)) { * doing so is just silly. We put oldsectors back because we
overlap = 1; * know it is safe, and trust userspace not to race with itself.
break; */
} rdev->sectors = oldsectors;
if (overlap) { return -EBUSY;
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;
}
} }
return len; return len;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment