blkdev: add blkdev_issue_zeroout helper function
authorDmitry Monakhov <dmonakhov@openvz.org>
Wed, 28 Apr 2010 13:55:09 +0000 (17:55 +0400)
committerJens Axboe <jens.axboe@oracle.com>
Wed, 28 Apr 2010 17:47:36 +0000 (19:47 +0200)
- Add bio_batch helper primitive. This is rather generic primitive
  for submitting/waiting a complex request which consists of several
  bios.
- blkdev_issue_zeroout() generate number of zero filed write bios.

Signed-off-by: Dmitry Monakhov <dmonakhov@openvz.org>
Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
block/blk-lib.c
include/linux/blkdev.h

index 0dc438812d81b35ccd33624a17d78b653523bc47..886c3f9e1be4ec50ea80964bb36b0d8d0516f6ed 100644 (file)
@@ -112,3 +112,121 @@ out:
        return -ENOMEM;
 }
 EXPORT_SYMBOL(blkdev_issue_discard);
+
+struct bio_batch
+{
+       atomic_t                done;
+       unsigned long           flags;
+       struct completion       *wait;
+       bio_end_io_t            *end_io;
+};
+
+static void bio_batch_end_io(struct bio *bio, int err)
+{
+       struct bio_batch *bb = bio->bi_private;
+       if (err) {
+               if (err == -EOPNOTSUPP)
+                       set_bit(BIO_EOPNOTSUPP, &bb->flags);
+               else
+                       clear_bit(BIO_UPTODATE, &bb->flags);
+       }
+       if (bb) {
+               if (bb->end_io)
+                       bb->end_io(bio, err);
+               atomic_inc(&bb->done);
+               complete(bb->wait);
+       }
+       bio_put(bio);
+}
+
+/**
+ * blkdev_issue_zeroout generate number of zero filed write bios
+ * @bdev:      blockdev to issue
+ * @sector:    start sector
+ * @nr_sects:  number of sectors to write
+ * @gfp_mask:  memory allocation flags (for bio_alloc)
+ * @flags:     BLKDEV_IFL_* flags to control behaviour
+ *
+ * Description:
+ *  Generate and issue number of bios with zerofiled pages.
+ *  Send barrier at the beginning and at the end if requested. This guarantie
+ *  correct request ordering. Empty barrier allow us to avoid post queue flush.
+ */
+
+int blkdev_issue_zeroout(struct block_device *bdev, sector_t sector,
+                       sector_t nr_sects, gfp_t gfp_mask, unsigned long flags)
+{
+       int ret = 0;
+       struct bio *bio;
+       struct bio_batch bb;
+       unsigned int sz, issued = 0;
+       DECLARE_COMPLETION_ONSTACK(wait);
+
+       atomic_set(&bb.done, 0);
+       bb.flags = 1 << BIO_UPTODATE;
+       bb.wait = &wait;
+       bb.end_io = NULL;
+
+       if (flags & BLKDEV_IFL_BARRIER) {
+               /* issue async barrier before the data */
+               ret = blkdev_issue_flush(bdev, gfp_mask, NULL, 0);
+               if (ret)
+                       return ret;
+       }
+submit:
+       while (nr_sects != 0) {
+               bio = bio_alloc(gfp_mask,
+                               min(nr_sects, (sector_t)BIO_MAX_PAGES));
+               if (!bio)
+                       break;
+
+               bio->bi_sector = sector;
+               bio->bi_bdev   = bdev;
+               bio->bi_end_io = bio_batch_end_io;
+               if (flags & BLKDEV_IFL_WAIT)
+                       bio->bi_private = &bb;
+
+               while(nr_sects != 0) {
+                       sz = min(PAGE_SIZE >> 9 , nr_sects);
+                       if (sz == 0)
+                               /* bio has maximum size possible */
+                               break;
+                       ret = bio_add_page(bio, ZERO_PAGE(0), sz << 9, 0);
+                       nr_sects -= ret >> 9;
+                       sector += ret >> 9;
+                       if (ret < (sz << 9))
+                               break;
+               }
+               issued++;
+               submit_bio(WRITE, bio);
+       }
+       /*
+        * When all data bios are in flight. Send final barrier if requeted.
+        */
+       if (nr_sects == 0 && flags & BLKDEV_IFL_BARRIER)
+               ret = blkdev_issue_flush(bdev, gfp_mask, NULL,
+                                       flags & BLKDEV_IFL_WAIT);
+
+
+       if (flags & BLKDEV_IFL_WAIT)
+               /* Wait for bios in-flight */
+               while ( issued != atomic_read(&bb.done))
+                       wait_for_completion(&wait);
+
+       if (!test_bit(BIO_UPTODATE, &bb.flags))
+               /* One of bios in the batch was completed with error.*/
+               ret = -EIO;
+
+       if (ret)
+               goto out;
+
+       if (test_bit(BIO_EOPNOTSUPP, &bb.flags)) {
+               ret = -EOPNOTSUPP;
+               goto out;
+       }
+       if (nr_sects != 0)
+               goto submit;
+out:
+       return ret;
+}
+EXPORT_SYMBOL(blkdev_issue_zeroout);
index 59b9aed0ee7d8a414973c3b137eb5903d844020b..3ac2bd2fc48583fbfa1f3420e62d51f87a835353 100644 (file)
@@ -1008,7 +1008,8 @@ extern int blkdev_issue_flush(struct block_device *, gfp_t, sector_t *,
                        unsigned long);
 extern int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
                sector_t nr_sects, gfp_t gfp_mask, unsigned long flags);
-
+extern int blkdev_issue_zeroout(struct block_device *bdev, sector_t sector,
+                       sector_t nr_sects, gfp_t gfp_mask, unsigned long flags);
 static inline int sb_issue_discard(struct super_block *sb,
                                   sector_t block, sector_t nr_blocks)
 {