Commit b0920ede authored by Logan Gunthorpe's avatar Logan Gunthorpe Committed by Song Liu

md/raid5: Add __rcu annotation to struct disk_info

rdev and replacement are protected in some circumstances with
rcu_dereference and synchronize_rcu (in raid5_remove_disk()). However,
they were not annotated with __rcu so a sparse warning is emitted for
every rcu_dereference() call.

Add the __rcu annotation and fix up the initialization with
RCU_INIT_POINTER, all pointer modifications with rcu_assign_pointer(),
a few cases where the pointer value is tested with rcu_access_pointer()
and one case where READ_ONCE() is used instead of rcu_dereference(),
a case in print_raid5_conf() that should have rcu_dereference() and
rcu_read_[un]lock() calls.

Additional sparse issues will be fixed up in further commits.
Signed-off-by: default avatarLogan Gunthorpe <logang@deltatee.com>
Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarSong Liu <song@kernel.org>
parent 3d9a644c
...@@ -6285,7 +6285,7 @@ static inline sector_t raid5_sync_request(struct mddev *mddev, sector_t sector_n ...@@ -6285,7 +6285,7 @@ static inline sector_t raid5_sync_request(struct mddev *mddev, sector_t sector_n
*/ */
rcu_read_lock(); rcu_read_lock();
for (i = 0; i < conf->raid_disks; i++) { for (i = 0; i < conf->raid_disks; i++) {
struct md_rdev *rdev = READ_ONCE(conf->disks[i].rdev); struct md_rdev *rdev = rcu_dereference(conf->disks[i].rdev);
if (rdev == NULL || test_bit(Faulty, &rdev->flags)) if (rdev == NULL || test_bit(Faulty, &rdev->flags))
still_degraded = 1; still_degraded = 1;
...@@ -7317,11 +7317,11 @@ static struct r5conf *setup_conf(struct mddev *mddev) ...@@ -7317,11 +7317,11 @@ static struct r5conf *setup_conf(struct mddev *mddev)
if (test_bit(Replacement, &rdev->flags)) { if (test_bit(Replacement, &rdev->flags)) {
if (disk->replacement) if (disk->replacement)
goto abort; goto abort;
disk->replacement = rdev; RCU_INIT_POINTER(disk->replacement, rdev);
} else { } else {
if (disk->rdev) if (disk->rdev)
goto abort; goto abort;
disk->rdev = rdev; RCU_INIT_POINTER(disk->rdev, rdev);
} }
if (test_bit(In_sync, &rdev->flags)) { if (test_bit(In_sync, &rdev->flags)) {
...@@ -7628,11 +7628,11 @@ static int raid5_run(struct mddev *mddev) ...@@ -7628,11 +7628,11 @@ static int raid5_run(struct mddev *mddev)
rdev = conf->disks[i].replacement; rdev = conf->disks[i].replacement;
conf->disks[i].replacement = NULL; conf->disks[i].replacement = NULL;
clear_bit(Replacement, &rdev->flags); clear_bit(Replacement, &rdev->flags);
conf->disks[i].rdev = rdev; rcu_assign_pointer(conf->disks[i].rdev, rdev);
} }
if (!rdev) if (!rdev)
continue; continue;
if (conf->disks[i].replacement && if (rcu_access_pointer(conf->disks[i].replacement) &&
conf->reshape_progress != MaxSector) { conf->reshape_progress != MaxSector) {
/* replacements and reshape simply do not mix. */ /* replacements and reshape simply do not mix. */
pr_warn("md: cannot handle concurrent replacement and reshape.\n"); pr_warn("md: cannot handle concurrent replacement and reshape.\n");
...@@ -7829,8 +7829,8 @@ static void raid5_status(struct seq_file *seq, struct mddev *mddev) ...@@ -7829,8 +7829,8 @@ static void raid5_status(struct seq_file *seq, struct mddev *mddev)
static void print_raid5_conf (struct r5conf *conf) static void print_raid5_conf (struct r5conf *conf)
{ {
struct md_rdev *rdev;
int i; int i;
struct disk_info *tmp;
pr_debug("RAID conf printout:\n"); pr_debug("RAID conf printout:\n");
if (!conf) { if (!conf) {
...@@ -7841,14 +7841,16 @@ static void print_raid5_conf (struct r5conf *conf) ...@@ -7841,14 +7841,16 @@ static void print_raid5_conf (struct r5conf *conf)
conf->raid_disks, conf->raid_disks,
conf->raid_disks - conf->mddev->degraded); conf->raid_disks - conf->mddev->degraded);
rcu_read_lock();
for (i = 0; i < conf->raid_disks; i++) { for (i = 0; i < conf->raid_disks; i++) {
char b[BDEVNAME_SIZE]; char b[BDEVNAME_SIZE];
tmp = conf->disks + i; rdev = rcu_dereference(conf->disks[i].rdev);
if (tmp->rdev) if (rdev)
pr_debug(" disk %d, o:%d, dev:%s\n", pr_debug(" disk %d, o:%d, dev:%s\n",
i, !test_bit(Faulty, &tmp->rdev->flags), i, !test_bit(Faulty, &rdev->flags),
bdevname(tmp->rdev->bdev, b)); bdevname(rdev->bdev, b));
} }
rcu_read_unlock();
} }
static int raid5_spare_active(struct mddev *mddev) static int raid5_spare_active(struct mddev *mddev)
...@@ -7899,8 +7901,9 @@ static int raid5_remove_disk(struct mddev *mddev, struct md_rdev *rdev) ...@@ -7899,8 +7901,9 @@ static int raid5_remove_disk(struct mddev *mddev, struct md_rdev *rdev)
struct r5conf *conf = mddev->private; struct r5conf *conf = mddev->private;
int err = 0; int err = 0;
int number = rdev->raid_disk; int number = rdev->raid_disk;
struct md_rdev **rdevp; struct md_rdev __rcu **rdevp;
struct disk_info *p = conf->disks + number; struct disk_info *p = conf->disks + number;
struct md_rdev *tmp;
print_raid5_conf(conf); print_raid5_conf(conf);
if (test_bit(Journal, &rdev->flags) && conf->log) { if (test_bit(Journal, &rdev->flags) && conf->log) {
...@@ -7918,9 +7921,9 @@ static int raid5_remove_disk(struct mddev *mddev, struct md_rdev *rdev) ...@@ -7918,9 +7921,9 @@ static int raid5_remove_disk(struct mddev *mddev, struct md_rdev *rdev)
log_exit(conf); log_exit(conf);
return 0; return 0;
} }
if (rdev == p->rdev) if (rdev == rcu_access_pointer(p->rdev))
rdevp = &p->rdev; rdevp = &p->rdev;
else if (rdev == p->replacement) else if (rdev == rcu_access_pointer(p->replacement))
rdevp = &p->replacement; rdevp = &p->replacement;
else else
return 0; return 0;
...@@ -7940,7 +7943,8 @@ static int raid5_remove_disk(struct mddev *mddev, struct md_rdev *rdev) ...@@ -7940,7 +7943,8 @@ static int raid5_remove_disk(struct mddev *mddev, struct md_rdev *rdev)
if (!test_bit(Faulty, &rdev->flags) && if (!test_bit(Faulty, &rdev->flags) &&
mddev->recovery_disabled != conf->recovery_disabled && mddev->recovery_disabled != conf->recovery_disabled &&
!has_failed(conf) && !has_failed(conf) &&
(!p->replacement || p->replacement == rdev) && (!rcu_access_pointer(p->replacement) ||
rcu_access_pointer(p->replacement) == rdev) &&
number < conf->raid_disks) { number < conf->raid_disks) {
err = -EBUSY; err = -EBUSY;
goto abort; goto abort;
...@@ -7951,7 +7955,7 @@ static int raid5_remove_disk(struct mddev *mddev, struct md_rdev *rdev) ...@@ -7951,7 +7955,7 @@ static int raid5_remove_disk(struct mddev *mddev, struct md_rdev *rdev)
if (atomic_read(&rdev->nr_pending)) { if (atomic_read(&rdev->nr_pending)) {
/* lost the race, try later */ /* lost the race, try later */
err = -EBUSY; err = -EBUSY;
*rdevp = rdev; rcu_assign_pointer(*rdevp, rdev);
} }
} }
if (!err) { if (!err) {
...@@ -7959,17 +7963,19 @@ static int raid5_remove_disk(struct mddev *mddev, struct md_rdev *rdev) ...@@ -7959,17 +7963,19 @@ static int raid5_remove_disk(struct mddev *mddev, struct md_rdev *rdev)
if (err) if (err)
goto abort; goto abort;
} }
if (p->replacement) {
tmp = rcu_access_pointer(p->replacement);
if (tmp) {
/* We must have just cleared 'rdev' */ /* We must have just cleared 'rdev' */
p->rdev = p->replacement; rcu_assign_pointer(p->rdev, tmp);
clear_bit(Replacement, &p->replacement->flags); clear_bit(Replacement, &tmp->flags);
smp_mb(); /* Make sure other CPUs may see both as identical smp_mb(); /* Make sure other CPUs may see both as identical
* but will never see neither - if they are careful * but will never see neither - if they are careful
*/ */
p->replacement = NULL; rcu_assign_pointer(p->replacement, NULL);
if (!err) if (!err)
err = log_modify(conf, p->rdev, true); err = log_modify(conf, tmp, true);
} }
clear_bit(WantReplacement, &rdev->flags); clear_bit(WantReplacement, &rdev->flags);
......
...@@ -473,7 +473,8 @@ enum { ...@@ -473,7 +473,8 @@ enum {
*/ */
struct disk_info { struct disk_info {
struct md_rdev *rdev, *replacement; struct md_rdev __rcu *rdev;
struct md_rdev __rcu *replacement;
struct page *extra_page; /* extra page to use in prexor */ struct page *extra_page; /* extra page to use in prexor */
}; };
......
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