[PATCH 2/5] block: add function to issue compare and write

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

 



From: Mike Christie <michaelc@xxxxxxxxxxx>

This patch adds block layer helpers to send a compare and write request.
It differs from requests like discard and write same in that there is a
setup function and a issue one. I did this to allow callers add multiple
pages with variying offsets and lengths.

For the miscompare failure case I am currently having drivers return
-ECANCELED, but was not sure if there is a better return code.

Signed-off-by: Mike Christie <michaelc@xxxxxxxxxxx>
---
 block/blk-lib.c           |   79 +++++++++++++++++++++++++++++++++++++++++++++
 include/linux/blk_types.h |    6 ++-
 include/linux/blkdev.h    |    6 +++
 3 files changed, 89 insertions(+), 2 deletions(-)

diff --git a/block/blk-lib.c b/block/blk-lib.c
index 8411be3..fbb1a91 100644
--- a/block/blk-lib.c
+++ b/block/blk-lib.c
@@ -141,6 +141,85 @@ int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
 }
 EXPORT_SYMBOL(blkdev_issue_discard);
 
+struct bio_cmp_and_write_data {
+	int			err;
+	struct completion	*wait;
+};
+
+static void bio_cmp_and_write_end_io(struct bio *bio, int err)
+{
+	struct bio_cmp_and_write_data *data = bio->bi_private;
+
+	data->err = err;
+	complete(data->wait);
+	bio_put(bio);
+}
+
+/**
+ * blkdev_setup_cmp_and_write - setup a bio for a compare and write operation
+ * @bdev:	blockdev to issue discard for
+ * @sector:	start sector
+ * @gfp_mask:	memory allocation flags (for bio_alloc)
+ * @nr_pages:	number of pages that contain the data to be written and compared
+ *
+ * This function should be called to allocate the bio used for
+ * blkdev_issue_cmp_and_write. The caller should add the pages to be compared
+ * followed by the write pages using bio_add_pc_page.
+ */
+struct bio *blkdev_setup_cmp_and_write(struct block_device *bdev,
+				       sector_t sector, gfp_t gfp_mask,
+				       int nr_pages)
+{
+	struct bio *bio;
+
+	bio = bio_alloc(gfp_mask, nr_pages);
+	if (!bio)
+		return NULL;
+
+	bio->bi_iter.bi_sector = sector;
+	bio->bi_end_io = bio_cmp_and_write_end_io;
+	bio->bi_bdev = bdev;
+	return bio;
+}
+EXPORT_SYMBOL(blkdev_setup_cmp_and_write);
+
+/**
+ * blkdev_issue_cmp_and_write - queue a compare and write operation
+ * @bio:	bio prepd with blkdev_setup_cmp_and_write.
+ * Description:
+ *    Issue a compare and write bio for the sectors in question. For the
+ *    execution of this request the handler should atomically read @nr_sects
+ *    from starting sector @sector, compare them, and if matched write
+ *    @nr_sects to @sector.
+ *
+ *    If the compare fails and data is not written the handler should return
+ *    -ECANCELED.
+ */
+int blkdev_issue_cmp_and_write(struct bio *bio)
+{
+	struct request_queue *q = bdev_get_queue(bio->bi_bdev);
+	DECLARE_COMPLETION_ONSTACK(wait);
+	struct bio_cmp_and_write_data data;
+	unsigned int max_cmp_and_write_sectors;
+
+	max_cmp_and_write_sectors = q->limits.max_cmp_and_write_sectors;
+	if (max_cmp_and_write_sectors == 0)
+		return -EOPNOTSUPP;
+
+	if (max_cmp_and_write_sectors < bio->bi_iter.bi_size >> 9)
+		return -EINVAL;
+
+	data.err = 0;
+	data.wait = &wait;
+	bio->bi_private = &data;
+
+	submit_bio(REQ_WRITE | REQ_CMP_AND_WRITE, bio);
+	/* Wait for bio in-flight */
+	wait_for_completion_io(&wait);
+	return data.err;
+}
+EXPORT_SYMBOL(blkdev_issue_cmp_and_write);
+
 /**
  * blkdev_issue_write_same - queue a write same operation
  * @bdev:	target blockdev
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index 66c2167..37d1eff 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -160,7 +160,7 @@ enum rq_flag_bits {
 	__REQ_DISCARD,		/* request to discard sectors */
 	__REQ_SECURE,		/* secure discard (used with __REQ_DISCARD) */
 	__REQ_WRITE_SAME,	/* write same block many times */
-
+	__REQ_CMP_AND_WRITE,	/* compare data and write if matched */
 	__REQ_NOIDLE,		/* don't anticipate more IO after this one */
 	__REQ_FUA,		/* forced unit access */
 	__REQ_FLUSH,		/* request for cache flush */
@@ -203,14 +203,16 @@ enum rq_flag_bits {
 #define REQ_PRIO		(1ULL << __REQ_PRIO)
 #define REQ_DISCARD		(1ULL << __REQ_DISCARD)
 #define REQ_WRITE_SAME		(1ULL << __REQ_WRITE_SAME)
+#define REQ_CMP_AND_WRITE	(1ULL << __REQ_CMP_AND_WRITE)
 #define REQ_NOIDLE		(1ULL << __REQ_NOIDLE)
 
+
 #define REQ_FAILFAST_MASK \
 	(REQ_FAILFAST_DEV | REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER)
 #define REQ_COMMON_MASK \
 	(REQ_WRITE | REQ_FAILFAST_MASK | REQ_SYNC | REQ_META | REQ_PRIO | \
 	 REQ_DISCARD | REQ_WRITE_SAME | REQ_NOIDLE | REQ_FLUSH | REQ_FUA | \
-	 REQ_SECURE)
+	 REQ_SECURE | REQ_CMP_AND_WRITE)
 #define REQ_CLONE_MASK		REQ_COMMON_MASK
 
 #define BIO_NO_ADVANCE_ITER_MASK	(REQ_DISCARD|REQ_WRITE_SAME)
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 6d03206..746d5b4 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -696,6 +696,9 @@ static inline bool blk_check_merge_flags(unsigned int flags1,
 	if ((flags1 & REQ_WRITE_SAME) != (flags2 & REQ_WRITE_SAME))
 		return false;
 
+	if (flags1 & REQ_CMP_AND_WRITE || flags2 & REQ_CMP_AND_WRITE)
+		return false;
+
 	return true;
 }
 
@@ -1167,6 +1170,9 @@ static inline struct request *blk_map_queue_find_tag(struct blk_queue_tag *bqt,
 
 #define BLKDEV_DISCARD_SECURE  0x01    /* secure discard */
 
+extern struct bio *blkdev_setup_cmp_and_write(struct block_device *, sector_t,
+		gfp_t, int);
+extern int blkdev_issue_cmp_and_write(struct bio *bio);
 extern int blkdev_issue_flush(struct block_device *, gfp_t, sector_t *);
 extern int blkdev_issue_discard(struct block_device *bdev, sector_t sector,
 		sector_t nr_sects, gfp_t gfp_mask, unsigned long flags);
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe ceph-devel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [CEPH Users]     [Ceph Large]     [Information on CEPH]     [Linux BTRFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]
  Powered by Linux