summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLi Nan <linan122@huawei.com>2025-02-27 15:55:00 +0800
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2025-04-10 14:44:06 +0200
commit8e5785cba5420dad67d3c0ad04431acdd3902439 (patch)
tree6cca3d7cbd7502b59ae3c5a11efb4a2d14b74161
parenta87d76deebf87b36ef541c91fc73f43815870d0b (diff)
downloadlinux-8e5785cba5420dad67d3c0ad04431acdd3902439.tar.gz
linux-8e5785cba5420dad67d3c0ad04431acdd3902439.tar.bz2
linux-8e5785cba5420dad67d3c0ad04431acdd3902439.zip
badblocks: return error if any badblock set fails
[ Upstream commit 7f500f0a59b1d7345a05ec4ae703babf34b7e470 ] _badblocks_set() returns success if at least one badblock is set successfully, even if others fail. This can lead to data inconsistencies in raid, where a failed badblock set should trigger the disk to be kicked out to prevent future reads from failed write areas. _badblocks_set() should return error if any badblock set fails. Instead of relying on 'rv', directly returning 'sectors' for clearer logic. If all badblocks are successfully set, 'sectors' will be 0, otherwise it indicates the number of badblocks that have not been set yet, thus signaling failure. By the way, it can also fix an issue: when a newly set unack badblock is included in an existing ack badblock, the setting will return an error. ยทยทยท echo "0 100" /sys/block/md0/md/dev-loop1/bad_blocks echo "0 100" /sys/block/md0/md/dev-loop1/unacknowledged_bad_blocks -bash: echo: write error: No space left on device ``` After fix, it will return success. Fixes: aa511ff8218b ("badblocks: switch to the improved badblock handling code") Signed-off-by: Li Nan <linan122@huawei.com> Reviewed-by: Yu Kuai <yukuai3@huawei.com> Acked-by: Coly Li <colyli@kernel.org> Link: https://lore.kernel.org/r/20250227075507.151331-6-zhengqixing@huaweicloud.com Signed-off-by: Jens Axboe <axboe@kernel.dk> Signed-off-by: Sasha Levin <sashal@kernel.org>
-rw-r--r--block/badblocks.c17
1 files changed, 5 insertions, 12 deletions
diff --git a/block/badblocks.c b/block/badblocks.c
index 1c8b8f65f6df..88f27d4f3856 100644
--- a/block/badblocks.c
+++ b/block/badblocks.c
@@ -843,7 +843,6 @@ static int _badblocks_set(struct badblocks *bb, sector_t s, int sectors,
struct badblocks_context bad;
int prev = -1, hint = -1;
unsigned long flags;
- int rv = 0;
u64 *p;
if (bb->shift < 0)
@@ -873,10 +872,8 @@ re_insert:
bad.len = sectors;
len = 0;
- if (badblocks_full(bb)) {
- rv = 1;
+ if (badblocks_full(bb))
goto out;
- }
if (badblocks_empty(bb)) {
len = insert_at(bb, 0, &bad);
@@ -916,10 +913,8 @@ re_insert:
int extra = 0;
if (!can_front_overwrite(bb, prev, &bad, &extra)) {
- if (extra > 0) {
- rv = 1;
+ if (extra > 0)
goto out;
- }
len = min_t(sector_t,
BB_END(p[prev]) - s, sectors);
@@ -986,10 +981,7 @@ out:
write_sequnlock_irqrestore(&bb->lock, flags);
- if (!added)
- rv = 1;
-
- return rv;
+ return sectors;
}
/*
@@ -1353,7 +1345,8 @@ EXPORT_SYMBOL_GPL(badblocks_check);
*
* Return:
* 0: success
- * 1: failed to set badblocks (out of space)
+ * other: failed to set badblocks (out of space). Parital setting will be
+ * treated as failure.
*/
int badblocks_set(struct badblocks *bb, sector_t s, int sectors,
int acknowledged)