xen-blkfront: handle bvecs with partial data
authorRoger Pau Monne <roger.pau@citrix.com>
Fri, 7 Dec 2012 18:00:31 +0000 (19:00 +0100)
committerKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Tue, 18 Dec 2012 02:56:03 +0000 (21:56 -0500)
Currently blkfront fails to handle cases in blkif_completion like the
following:

1st loop in rq_for_each_segment
 * bv_offset: 3584
 * bv_len: 512
 * offset += bv_len
 * i: 0

2nd loop:
 * bv_offset: 0
 * bv_len: 512
 * i: 0

In the second loop i should be 1, since we assume we only wanted to
read a part of the previous page. This patches fixes this cases where
only a part of the shared page is read, and blkif_completion assumes
that if the bv_offset of a bvec is less than the previous bv_offset
plus the bv_size we have to switch to the next shared page.

Reported-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Signed-off-by: Roger Pau Monné <roger.pau@citrix.com>
Cc: linux-kernel@vger.kernel.org
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
drivers/block/xen-blkfront.c

index cfdb033c7107ae2ff5ecdaa64fe80d1dc5820251..11043c18ac5ab01fcfd2e492fff70a87260143fd 100644 (file)
@@ -836,7 +836,7 @@ static void blkif_free(struct blkfront_info *info, int suspend)
 static void blkif_completion(struct blk_shadow *s, struct blkfront_info *info,
                             struct blkif_response *bret)
 {
-       int i;
+       int i = 0;
        struct bio_vec *bvec;
        struct req_iterator iter;
        unsigned long flags;
@@ -853,7 +853,8 @@ static void blkif_completion(struct blk_shadow *s, struct blkfront_info *info,
                 */
                rq_for_each_segment(bvec, s->request, iter) {
                        BUG_ON((bvec->bv_offset + bvec->bv_len) > PAGE_SIZE);
-                       i = offset >> PAGE_SHIFT;
+                       if (bvec->bv_offset < offset)
+                               i++;
                        BUG_ON(i >= s->req.u.rw.nr_segments);
                        shared_data = kmap_atomic(
                                pfn_to_page(s->grants_used[i]->pfn));
@@ -862,7 +863,7 @@ static void blkif_completion(struct blk_shadow *s, struct blkfront_info *info,
                                bvec->bv_len);
                        bvec_kunmap_irq(bvec_data, &flags);
                        kunmap_atomic(shared_data);
-                       offset += bvec->bv_len;
+                       offset = bvec->bv_offset + bvec->bv_len;
                }
        }
        /* Add the persistent grant into the list of free grants */