[PATCH 1/2] block: add bio based rw helper for data buffer

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



One of the common application for the file systems and drivers to map
the buffer to the bio and issue I/Os on the block device.

This is a prep patch which adds two helper functions for block layer
which allows file-systems and the drivers to map data buffer on to the
bios and issue bios synchronously using blkdev_issue_rw() or
asynchronously using __blkdev_issue_rw().

Signed-off-by: Chaitanya Kulkarni <chaitanya.kulkarni@xxxxxxx>
---
 block/blk-lib.c        | 105 +++++++++++++++++++++++++++++++++++++++++
 include/linux/blkdev.h |   7 +++
 2 files changed, 112 insertions(+)

diff --git a/block/blk-lib.c b/block/blk-lib.c
index 5f2c429d4378..451c367fc0d6 100644
--- a/block/blk-lib.c
+++ b/block/blk-lib.c
@@ -405,3 +405,108 @@ int blkdev_issue_zeroout(struct block_device *bdev, sector_t sector,
 	return ret;
 }
 EXPORT_SYMBOL(blkdev_issue_zeroout);
+
+/**
+ * __blkdev_ssue_rw - issue read/write bios from buffer asynchronously
+ * @bdev:	blockdev to read/write
+ * @buf:	data buffer
+ * @sector:	start sector
+ * @nr_sects:	number of sectors
+ * @op:	REQ_OP_READ or REQ_OP_WRITE
+ * @opf:	flags
+ * @gfp_mask:	memory allocation flags (for bio_alloc)
+ * @biop:	pointer to anchor bio
+ *
+ * Description:
+ *  Generic helper function to map data buffer into bios for read and write ops.
+ *  Returns pointer to the anchored last bio for caller to submit asynchronously
+ *  or synchronously.
+ */
+int __blkdev_issue_rw(struct block_device *bdev, char *buf, sector_t sector,
+		      sector_t nr_sects, unsigned op, unsigned opf,
+		      gfp_t gfp_mask, struct bio **biop)
+{
+	bool vm = is_vmalloc_addr(buf) ? true : false;
+	struct bio *bio = *biop;
+	unsigned int nr_pages;
+	struct page *page;
+	unsigned int off;
+	unsigned int len;
+	int bi_size;
+
+	if (!bdev_get_queue(bdev))
+		return -ENXIO;
+
+	if (bdev_read_only(bdev))
+		return -EPERM;
+
+	if (!(op == REQ_OP_READ || op == REQ_OP_WRITE))
+		return -EINVAL;
+
+	while (nr_sects != 0) {
+		nr_pages = __blkdev_sectors_to_bio_pages(nr_sects);
+
+		bio = blk_next_bio(bio, nr_pages, gfp_mask);
+		bio->bi_iter.bi_sector = sector;
+		bio_set_dev(bio, bdev);
+		bio_set_op_attrs(bio, op, 0);
+
+		while (nr_sects != 0) {
+			off = offset_in_page(buf);
+			page = vm ? vmalloc_to_page(buf) : virt_to_page(buf);
+			len = min((sector_t) PAGE_SIZE, nr_sects << 9);
+
+			bi_size = bio_add_page(bio, page, len, off);
+
+			nr_sects -= bi_size >> 9;
+			sector += bi_size >> 9;
+			buf += bi_size;
+
+			if (bi_size < len)
+				break;
+		}
+		cond_resched();
+	}
+	*biop = bio;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(__blkdev_issue_rw);
+
+/**
+ * blkdev_execute_rw_sync - issue read/write bios from buffer synchronously
+ * @bdev:	blockdev to read/write
+ * @buf:	data buffer
+ * @sector:	start sector
+ * @count:	number of bytes
+ * @op:	REQ_OP_READ or REQ_OP_WRITE
+ * @opf:	flags
+ * @gfp_mask:	memory allocation flags (for bio_alloc)
+ *
+ * Description:
+ *  Generic helper function to map data buffer buffer into bios for read and
+ *  write requests.
+ */
+int blkdev_issue_rw(struct block_device *b, char *buf, sector_t sector,
+		     unsigned count, unsigned op, unsigned opf, gfp_t mask)
+{
+	unsigned int is_vmalloc = is_vmalloc_addr(buf);
+	sector_t nr_sects = count >> 9;
+	struct bio *bio = NULL;
+	int error;
+
+	if (is_vmalloc && op == REQ_OP_WRITE)
+		flush_kernel_vmap_range(buf, count);
+
+	opf |= REQ_SYNC;
+	error = __blkdev_issue_rw(b, buf, sector, nr_sects, op, opf, mask, &bio);
+	if (!error && bio) {
+		error = submit_bio_wait(bio);
+		bio_put(bio);
+	}
+
+	if (is_vmalloc && op == REQ_OP_READ)
+		invalidate_kernel_vmap_range(buf, count);
+
+	return error;
+}
+EXPORT_SYMBOL_GPL(blkdev_issue_rw);
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 32868fbedc9e..cb315b301ad9 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -1248,6 +1248,13 @@ static inline int sb_issue_zeroout(struct super_block *sb, sector_t block,
 				    gfp_mask, 0);
 }
 
+extern int __blkdev_issue_rw(struct block_device *bdev, char *buf,
+			     sector_t sector, sector_t nr_sects, unsigned op,
+			     unsigned opf, gfp_t gfp_mask, struct bio **biop);
+extern int blkdev_issue_rw(struct block_device *b, char *buf, sector_t sector,
+			   unsigned count, unsigned op, unsigned opf,
+			   gfp_t mask);
+
 extern int blk_verify_command(unsigned char *cmd, fmode_t mode);
 
 enum blk_default_limits {
-- 
2.22.1




[Index of Archives]     [Linux RAID]     [Linux SCSI]     [Linux ATA RAID]     [IDE]     [Linux Wireless]     [Linux Kernel]     [ATH6KL]     [Linux Bluetooth]     [Linux Netdev]     [Kernel Newbies]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Device Mapper]

  Powered by Linux