Commit 32cb5031 authored by Mariusz Tkaczyk's avatar Mariusz Tkaczyk Committed by Greg Kroah-Hartman

md: fix for divide error in status_resync

[ Upstream commit 9642fa73 ]

Stopping external metadata arrays during resync/recovery causes
retries, loop of interrupting and starting reconstruction, until it
hit at good moment to stop completely. While these retries
curr_mark_cnt can be small- especially on HDD drives, so subtraction
result can be smaller than 0. However it is casted to uint without
checking. As a result of it the status bar in /proc/mdstat while stopping
is strange (it jumps between 0% and 99%).

The real problem occurs here after commit 72deb455 ("block: remove
CONFIG_LBDAF"). Sector_div() macro has been changed, now the
divisor is casted to uint32. For db = -8 the divisior(db/32-1) becomes 0.

Check if db value can be really counted and replace these macro by
div64_u64() inline.
Signed-off-by: default avatarMariusz Tkaczyk <mariusz.tkaczyk@intel.com>
Signed-off-by: default avatarSong Liu <songliubraving@fb.com>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent e62ebc06
...@@ -7617,9 +7617,9 @@ static void status_unused(struct seq_file *seq) ...@@ -7617,9 +7617,9 @@ static void status_unused(struct seq_file *seq)
static int status_resync(struct seq_file *seq, struct mddev *mddev) static int status_resync(struct seq_file *seq, struct mddev *mddev)
{ {
sector_t max_sectors, resync, res; sector_t max_sectors, resync, res;
unsigned long dt, db; unsigned long dt, db = 0;
sector_t rt; sector_t rt, curr_mark_cnt, resync_mark_cnt;
int scale; int scale, recovery_active;
unsigned int per_milli; unsigned int per_milli;
if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) || if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ||
...@@ -7708,7 +7708,10 @@ static int status_resync(struct seq_file *seq, struct mddev *mddev) ...@@ -7708,7 +7708,10 @@ static int status_resync(struct seq_file *seq, struct mddev *mddev)
* db: blocks written from mark until now * db: blocks written from mark until now
* rt: remaining time * rt: remaining time
* *
* rt is a sector_t, so could be 32bit or 64bit. * rt is a sector_t, which is always 64bit now. We are keeping
* the original algorithm, but it is not really necessary.
*
* Original algorithm:
* So we divide before multiply in case it is 32bit and close * So we divide before multiply in case it is 32bit and close
* to the limit. * to the limit.
* We scale the divisor (db) by 32 to avoid losing precision * We scale the divisor (db) by 32 to avoid losing precision
...@@ -7719,11 +7722,16 @@ static int status_resync(struct seq_file *seq, struct mddev *mddev) ...@@ -7719,11 +7722,16 @@ static int status_resync(struct seq_file *seq, struct mddev *mddev)
*/ */
dt = ((jiffies - mddev->resync_mark) / HZ); dt = ((jiffies - mddev->resync_mark) / HZ);
if (!dt) dt++; if (!dt) dt++;
db = (mddev->curr_mark_cnt - atomic_read(&mddev->recovery_active))
- mddev->resync_mark_cnt; curr_mark_cnt = mddev->curr_mark_cnt;
recovery_active = atomic_read(&mddev->recovery_active);
resync_mark_cnt = mddev->resync_mark_cnt;
if (curr_mark_cnt >= (recovery_active + resync_mark_cnt))
db = curr_mark_cnt - (recovery_active + resync_mark_cnt);
rt = max_sectors - resync; /* number of remaining sectors */ rt = max_sectors - resync; /* number of remaining sectors */
sector_div(rt, db/32+1); rt = div64_u64(rt, db/32+1);
rt *= dt; rt *= dt;
rt >>= 5; rt >>= 5;
......
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