block: add bio_truncate to fix guard_bio_eod
[linux-2.6-microblaze.git] / block / bio.c
index 9d54aa3..006bcc5 100644 (file)
@@ -538,6 +538,45 @@ void zero_fill_bio_iter(struct bio *bio, struct bvec_iter start)
 }
 EXPORT_SYMBOL(zero_fill_bio_iter);
 
+void bio_truncate(struct bio *bio, unsigned new_size)
+{
+       struct bio_vec bv;
+       struct bvec_iter iter;
+       unsigned int done = 0;
+       bool truncated = false;
+
+       if (new_size >= bio->bi_iter.bi_size)
+               return;
+
+       if (bio_data_dir(bio) != READ)
+               goto exit;
+
+       bio_for_each_segment(bv, bio, iter) {
+               if (done + bv.bv_len > new_size) {
+                       unsigned offset;
+
+                       if (!truncated)
+                               offset = new_size - done;
+                       else
+                               offset = 0;
+                       zero_user(bv.bv_page, offset, bv.bv_len - offset);
+                       truncated = true;
+               }
+               done += bv.bv_len;
+       }
+
+ exit:
+       /*
+        * Don't touch bvec table here and make it really immutable, since
+        * fs bio user has to retrieve all pages via bio_for_each_segment_all
+        * in its .end_bio() callback.
+        *
+        * It is enough to truncate bio by updating .bi_size since we can make
+        * correct bvec with the updated .bi_size for drivers.
+        */
+       bio->bi_iter.bi_size = new_size;
+}
+
 /**
  * bio_put - release a reference to a bio
  * @bio:   bio to release reference to
@@ -754,10 +793,12 @@ bool __bio_try_merge_page(struct bio *bio, struct page *page,
        if (WARN_ON_ONCE(bio_flagged(bio, BIO_CLONED)))
                return false;
 
-       if (bio->bi_vcnt > 0 && !bio_full(bio, len)) {
+       if (bio->bi_vcnt > 0) {
                struct bio_vec *bv = &bio->bi_io_vec[bio->bi_vcnt - 1];
 
                if (page_is_mergeable(bv, page, len, off, same_page)) {
+                       if (bio->bi_iter.bi_size > UINT_MAX - len)
+                               return false;
                        bv->bv_len += len;
                        bio->bi_iter.bi_size += len;
                        return true;