]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/2.6.20.2/fix-various-bugs-with-aligned-reads-in-raid5.patch
4.9-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 2.6.20.2 / fix-various-bugs-with-aligned-reads-in-raid5.patch
1 From stable-bounces@linux.kernel.org Tue Feb 6 15:45:04 2007
2 From: Neil Brown <neilb@suse.de>
3 Date: Wed, 7 Feb 2007 10:26:56 +1100
4 Subject: Fix various bugs with aligned reads in RAID5.
5 To: "Kai" <epimetreus@fastmail.fm>, Andrew Morton <akpm@linux-foundation.org>, stable@kernel.org
6 Cc: linux-kernel@vger.kernel.org, Jens Axboe <jens.axboe@oracle.com>
7 Message-ID: <17865.3776.511594.763544@notabene.brown>
8
9 From: Neil Brown <neilb@suse.de>
10
11 Fix various bugs with aligned reads in RAID5.
12
13 It is possible for raid5 to be sent a bio that is too big
14 for an underlying device. So if it is a READ that we
15 pass stright down to a device, it will fail and confuse
16 RAID5.
17
18 So in 'chunk_aligned_read' we check that the bio fits within the
19 parameters for the target device and if it doesn't fit, fall back
20 on reading through the stripe cache and making lots of one-page
21 requests.
22
23 Note that this is the earliest time we can check against the device
24 because earlier we don't have a lock on the device, so it could change
25 underneath us.
26
27 Also, the code for handling a retry through the cache when a read
28 fails has not been tested and was badly broken. This patch fixes that
29 code.
30
31 Signed-off-by: Neil Brown <neilb@suse.de>
32 Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
33
34 ---
35 drivers/md/raid5.c | 42 +++++++++++++++++++++++++++++++++++++++---
36 1 file changed, 39 insertions(+), 3 deletions(-)
37
38 --- linux-2.6.20.1.orig/drivers/md/raid5.c
39 +++ linux-2.6.20.1/drivers/md/raid5.c
40 @@ -2620,7 +2620,7 @@ static struct bio *remove_bio_from_retry
41 }
42 bi = conf->retry_read_aligned_list;
43 if(bi) {
44 - conf->retry_read_aligned = bi->bi_next;
45 + conf->retry_read_aligned_list = bi->bi_next;
46 bi->bi_next = NULL;
47 bi->bi_phys_segments = 1; /* biased count of active stripes */
48 bi->bi_hw_segments = 0; /* count of processed stripes */
49 @@ -2669,6 +2669,27 @@ static int raid5_align_endio(struct bio
50 return 0;
51 }
52
53 +static int bio_fits_rdev(struct bio *bi)
54 +{
55 + request_queue_t *q = bdev_get_queue(bi->bi_bdev);
56 +
57 + if ((bi->bi_size>>9) > q->max_sectors)
58 + return 0;
59 + blk_recount_segments(q, bi);
60 + if (bi->bi_phys_segments > q->max_phys_segments ||
61 + bi->bi_hw_segments > q->max_hw_segments)
62 + return 0;
63 +
64 + if (q->merge_bvec_fn)
65 + /* it's too hard to apply the merge_bvec_fn at this stage,
66 + * just just give up
67 + */
68 + return 0;
69 +
70 + return 1;
71 +}
72 +
73 +
74 static int chunk_aligned_read(request_queue_t *q, struct bio * raid_bio)
75 {
76 mddev_t *mddev = q->queuedata;
77 @@ -2715,6 +2736,13 @@ static int chunk_aligned_read(request_qu
78 align_bi->bi_flags &= ~(1 << BIO_SEG_VALID);
79 align_bi->bi_sector += rdev->data_offset;
80
81 + if (!bio_fits_rdev(align_bi)) {
82 + /* too big in some way */
83 + bio_put(align_bi);
84 + rdev_dec_pending(rdev, mddev);
85 + return 0;
86 + }
87 +
88 spin_lock_irq(&conf->device_lock);
89 wait_event_lock_irq(conf->wait_for_stripe,
90 conf->quiesce == 0,
91 @@ -3107,7 +3135,9 @@ static int retry_aligned_read(raid5_con
92 last_sector = raid_bio->bi_sector + (raid_bio->bi_size>>9);
93
94 for (; logical_sector < last_sector;
95 - logical_sector += STRIPE_SECTORS, scnt++) {
96 + logical_sector += STRIPE_SECTORS,
97 + sector += STRIPE_SECTORS,
98 + scnt++) {
99
100 if (scnt < raid_bio->bi_hw_segments)
101 /* already done this stripe */
102 @@ -3123,7 +3153,13 @@ static int retry_aligned_read(raid5_con
103 }
104
105 set_bit(R5_ReadError, &sh->dev[dd_idx].flags);
106 - add_stripe_bio(sh, raid_bio, dd_idx, 0);
107 + if (!add_stripe_bio(sh, raid_bio, dd_idx, 0)) {
108 + release_stripe(sh);
109 + raid_bio->bi_hw_segments = scnt;
110 + conf->retry_read_aligned = raid_bio;
111 + return handled;
112 + }
113 +
114 handle_stripe(sh, NULL);
115 release_stripe(sh);
116 handled++;