diff options
author | NeilBrown <neilb@suse.com> | 2016-06-02 02:19:52 -0400 |
---|---|---|
committer | Shaohua Li <shli@fb.com> | 2016-06-13 14:54:18 -0400 |
commit | 707a6a420ccf31634f2b15d8f06f09536e2de079 (patch) | |
tree | bd4661927839a5e2e65a8e8de6ef3572841d6ea1 | |
parent | 854abd75841413f7966bc4fed83b36e78a1c285c (diff) |
md/raid1: add rcu protection to rdev in fix_read_error
Since remove_and_add_spares() was added to hot_remove_disk() it has
been possible for an rdev to be hot-removed while fix_read_error()
was running, so we need to be more careful, and take a reference to
the rdev while performing IO.
Signed-off-by: NeilBrown <neilb@suse.com>
Signed-off-by: Shaohua Li <shli@fb.com>
-rw-r--r-- | drivers/md/raid1.c | 52 |
1 files changed, 32 insertions, 20 deletions
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 60c293df03f1..34f20c03d1f6 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c | |||
@@ -2056,29 +2056,30 @@ static void fix_read_error(struct r1conf *conf, int read_disk, | |||
2056 | s = PAGE_SIZE >> 9; | 2056 | s = PAGE_SIZE >> 9; |
2057 | 2057 | ||
2058 | do { | 2058 | do { |
2059 | /* Note: no rcu protection needed here | ||
2060 | * as this is synchronous in the raid1d thread | ||
2061 | * which is the thread that might remove | ||
2062 | * a device. If raid1d ever becomes multi-threaded.... | ||
2063 | */ | ||
2064 | sector_t first_bad; | 2059 | sector_t first_bad; |
2065 | int bad_sectors; | 2060 | int bad_sectors; |
2066 | 2061 | ||
2067 | rdev = conf->mirrors[d].rdev; | 2062 | rcu_read_lock(); |
2063 | rdev = rcu_dereference(conf->mirrors[d].rdev); | ||
2068 | if (rdev && | 2064 | if (rdev && |
2069 | (test_bit(In_sync, &rdev->flags) || | 2065 | (test_bit(In_sync, &rdev->flags) || |
2070 | (!test_bit(Faulty, &rdev->flags) && | 2066 | (!test_bit(Faulty, &rdev->flags) && |
2071 | rdev->recovery_offset >= sect + s)) && | 2067 | rdev->recovery_offset >= sect + s)) && |
2072 | is_badblock(rdev, sect, s, | 2068 | is_badblock(rdev, sect, s, |
2073 | &first_bad, &bad_sectors) == 0 && | 2069 | &first_bad, &bad_sectors) == 0) { |
2074 | sync_page_io(rdev, sect, s<<9, | 2070 | atomic_inc(&rdev->nr_pending); |
2075 | conf->tmppage, READ, false)) | 2071 | rcu_read_unlock(); |
2076 | success = 1; | 2072 | if (sync_page_io(rdev, sect, s<<9, |
2077 | else { | 2073 | conf->tmppage, READ, false)) |
2078 | d++; | 2074 | success = 1; |
2079 | if (d == conf->raid_disks * 2) | 2075 | rdev_dec_pending(rdev, mddev); |
2080 | d = 0; | 2076 | if (success) |
2081 | } | 2077 | break; |
2078 | } else | ||
2079 | rcu_read_unlock(); | ||
2080 | d++; | ||
2081 | if (d == conf->raid_disks * 2) | ||
2082 | d = 0; | ||
2082 | } while (!success && d != read_disk); | 2083 | } while (!success && d != read_disk); |
2083 | 2084 | ||
2084 | if (!success) { | 2085 | if (!success) { |
@@ -2094,11 +2095,17 @@ static void fix_read_error(struct r1conf *conf, int read_disk, | |||
2094 | if (d==0) | 2095 | if (d==0) |
2095 | d = conf->raid_disks * 2; | 2096 | d = conf->raid_disks * 2; |
2096 | d--; | 2097 | d--; |
2097 | rdev = conf->mirrors[d].rdev; | 2098 | rcu_read_lock(); |
2099 | rdev = rcu_dereference(conf->mirrors[d].rdev); | ||
2098 | if (rdev && | 2100 | if (rdev && |
2099 | !test_bit(Faulty, &rdev->flags)) | 2101 | !test_bit(Faulty, &rdev->flags)) { |
2102 | atomic_inc(&rdev->nr_pending); | ||
2103 | rcu_read_unlock(); | ||
2100 | r1_sync_page_io(rdev, sect, s, | 2104 | r1_sync_page_io(rdev, sect, s, |
2101 | conf->tmppage, WRITE); | 2105 | conf->tmppage, WRITE); |
2106 | rdev_dec_pending(rdev, mddev); | ||
2107 | } else | ||
2108 | rcu_read_unlock(); | ||
2102 | } | 2109 | } |
2103 | d = start; | 2110 | d = start; |
2104 | while (d != read_disk) { | 2111 | while (d != read_disk) { |
@@ -2106,9 +2113,12 @@ static void fix_read_error(struct r1conf *conf, int read_disk, | |||
2106 | if (d==0) | 2113 | if (d==0) |
2107 | d = conf->raid_disks * 2; | 2114 | d = conf->raid_disks * 2; |
2108 | d--; | 2115 | d--; |
2109 | rdev = conf->mirrors[d].rdev; | 2116 | rcu_read_lock(); |
2117 | rdev = rcu_dereference(conf->mirrors[d].rdev); | ||
2110 | if (rdev && | 2118 | if (rdev && |
2111 | !test_bit(Faulty, &rdev->flags)) { | 2119 | !test_bit(Faulty, &rdev->flags)) { |
2120 | atomic_inc(&rdev->nr_pending); | ||
2121 | rcu_read_unlock(); | ||
2112 | if (r1_sync_page_io(rdev, sect, s, | 2122 | if (r1_sync_page_io(rdev, sect, s, |
2113 | conf->tmppage, READ)) { | 2123 | conf->tmppage, READ)) { |
2114 | atomic_add(s, &rdev->corrected_errors); | 2124 | atomic_add(s, &rdev->corrected_errors); |
@@ -2117,10 +2127,12 @@ static void fix_read_error(struct r1conf *conf, int read_disk, | |||
2117 | "(%d sectors at %llu on %s)\n", | 2127 | "(%d sectors at %llu on %s)\n", |
2118 | mdname(mddev), s, | 2128 | mdname(mddev), s, |
2119 | (unsigned long long)(sect + | 2129 | (unsigned long long)(sect + |
2120 | rdev->data_offset), | 2130 | rdev->data_offset), |
2121 | bdevname(rdev->bdev, b)); | 2131 | bdevname(rdev->bdev, b)); |
2122 | } | 2132 | } |
2123 | } | 2133 | rdev_dec_pending(rdev, mddev); |
2134 | } else | ||
2135 | rcu_read_unlock(); | ||
2124 | } | 2136 | } |
2125 | sectors -= s; | 2137 | sectors -= s; |
2126 | sect += s; | 2138 | sect += s; |