block, dm: don't copy bios for request clones
Subsystems (2)
PATCH 1/1 block, dm: don't copy bios for request clones
[recent, the dm-devel list server doesn't seem to like mail sent using git-send-email..]
Currently dm-multipath has to clone the bios for every request sent to the lower devices, which wastes cpu cycles and ties down memory.
This patch instead adds a new REQ_CLONE flag that instructs req_bio_endio to not complete bios attached to a request, which we set on clone requests similar to bios in a flush sequence. With this change I/O errors on a path failure only get propagated to dm-multipath, which can then either resubmit the I/O or complete the bios on the original request.
I've done some basic testing of this on a Linux target with ALUA support, and it survives path failures during I/O nicely.
--- block/blk-core.c | 92 +++----------------------- drivers/md/dm-table.c | 27 +++++--- drivers/md/dm.c | 162 ++++++++++++---------------------------------- drivers/md/dm.h | 4 +- include/linux/blk_types.h | 2 + include/linux/blkdev.h | 6 +- 6 files changed, 73 insertions(+), 220 deletions(-)
block/blk-core.c +10 -82
@@ -128,7 +128,8 @@ static void req_bio_endio(struct request *rq, struct bio *bio,bio_advance (bio, nbytes);/* don't actually finish bio if it's part of flush sequence */ -if (bio->bi_iter.bi_size ==0 && !(rq->cmd_flags &REQ_FLUSH_SEQ )) +if (bio->bi_iter.bi_size ==0 && + !(rq->cmd_flags & (REQ_FLUSH_SEQ |REQ_CLONE )))bio_endio (bio, error); }
@@ -2901,95 +2902,22 @@ int blk_lld_busy(struct request_queue *q) }EXPORT_SYMBOL_GPL (blk_lld_busy); -/** -* blk_rq_unprep_clone - Helper function to free all bios in a cloned request -* @rq: the clone request to be cleaned up -* -* Description: -* Free all bios in @rq for a cloned request. -*/ -void blk_rq_unprep_clone (struct request *rq) -{ -struct bio *bio; - -while ((bio = rq->bio) !=NULL ) { - rq->bio = bio->bi_next; - -bio_put (bio); - } -} -EXPORT_SYMBOL_GPL (blk_rq_unprep_clone); - -/* -* Copy attributes of the original request to the clone request. -* The actual data parts (e.g. ->cmd, ->sense) are not copied. -*/ -static void __blk_rq_prep_clone (struct request *dst,struct request *src) +void blk_rq_prep_clone (struct request *dst,struct request *src) { dst->cpu = src->cpu; - dst->cmd_flags |= (src->cmd_flags &REQ_CLONE_MASK ) |REQ_NOMERGE ; + dst->cmd_flags |= (src->cmd_flags &REQ_CLONE_MASK ); + dst->cmd_flags |=REQ_NOMERGE |REQ_CLONE ; dst->cmd_type = src->cmd_type; dst->__sector =blk_rq_pos (src); dst->__data_len =blk_rq_bytes (src); dst->nr_phys_segments = src->nr_phys_segments; dst->ioprio = src->ioprio; dst->extra_len = src->extra_len; -} - -/** -* blk_rq_prep_clone - Helper function to setup clone request -* @rq: the request to be setup -* @rq_src: original request to be cloned -* @bs: bio_set that bios for clone are allocated from -* @gfp_mask: memory allocation mask for bio -* @bio_ctr: setup function to be called for each clone bio. -* Returns %0 for success, non %0 for failure. -* @data: private data to be passed to @bio_ctr -* -* Description: -* Clones bios in @rq_src to @rq, and copies attributes of @rq_src to @rq. -* The actual data parts of @rq_src (e.g. ->cmd, ->sense) -* are not copied, and copying such parts is the caller's responsibility. -* Also, pages which the original bios are pointing to are not copied -* and the cloned bios just point same pages. -* So cloned bios must be completed before original bios, which means -* the caller must complete @rq before @rq_src. -*/ -int blk_rq_prep_clone (struct request *rq,struct request *rq_src, -struct bio_set *bs,gfp_t gfp_mask, -int (*bio_ctr)(struct bio *,struct bio *,void *), -void *data) -{ -struct bio *bio, *bio_src; - -if (!bs) - bs = fs_bio_set; - -__rq_for_each_bio (bio_src, rq_src) { - bio =bio_clone_fast (bio_src, gfp_mask, bs); -if (!bio) - goto free_and_out; - -if (bio_ctr &&bio_ctr (bio, bio_src, data)) - goto free_and_out; - -if (rq->bio) { - rq->biotail->bi_next = bio; - rq->biotail = bio; - }else - rq->bio = rq->biotail = bio; - } - -__blk_rq_prep_clone (rq, rq_src); - -return 0 ; - -free_and_out: -if (bio) -bio_put (bio); -blk_rq_unprep_clone (rq); - -return -ENOMEM ; + dst->bio = src->bio; + dst->biotail = src->biotail; + dst->cmd = src->cmd; + dst->cmd_len = src->cmd_len; + dst->sense = src->sense; }EXPORT_SYMBOL_GPL (blk_rq_prep_clone);
drivers/md/dm-table.c +17 -10
@@ -941,23 +941,30 @@ bool dm_table_mq_request_based(struct dm_table *t)static int dm_table_alloc_md_mempools (struct dm_table *t) { -unsigned type =dm_table_get_type (t); +int type =dm_table_get_type (t);unsigned per_bio_data_size =0 ; -struct dm_target *tgt;unsigned i; -if (unlikely (type ==DM_TYPE_NONE )) { +switch (type) { +for (i =0 ; i < t->num_targets; i++) { +struct dm_target *tgt = t->targets + i; + + per_bio_data_size =max (per_bio_data_size, + tgt->per_bio_data_size); + } + + t->mempools =dm_alloc_bio_mempools (t->integrity_supported, + per_bio_data_size); +break ; +case DM_TYPE_REQUEST_BASED : +case DM_TYPE_MQ_REQUEST_BASED : + t->mempools =dm_alloc_rq_mempools (type); +break ; +default :DMWARN ("no table type is set, can't allocate mempools" );return -EINVAL ; } -if (type ==DM_TYPE_BIO_BASED ) -for (i =0 ; i < t->num_targets; i++) { - tgt = t->targets + i; - per_bio_data_size =max (per_bio_data_size, tgt->per_bio_data_size); - } - - t->mempools =dm_alloc_md_mempools (type, t->integrity_supported, per_bio_data_size);if (!t->mempools)return -ENOMEM ;
drivers/md/dm.c +40 -122
222 lines not on this page · raw message
drivers/md/dm.h +3 -1
@@ -221,7 +221,9 @@ void dm_kcopyd_exit(void);/* * Mempool operations */ -struct dm_md_mempools *dm_alloc_md_mempools (unsigned type,unsigned integrity,unsigned per_bio_data_size); +struct dm_md_mempools *dm_alloc_bio_mempools (unsigned integrity, +unsigned per_bio_data_size); +struct dm_md_mempools *dm_alloc_rq_mempools (unsigned type);void dm_free_md_mempools (struct dm_md_mempools *pools);/*
include/linux/blk_types.h +2 -0
@@ -195,6 +195,7 @@ enum rq_flag_bits { __REQ_HASHED,/* on IO scheduler merge hash */ __REQ_MQ_INFLIGHT,/* track inflight for MQ */ __REQ_NO_TIMEOUT,/* requests may never expire */ + __REQ_CLONE,/* cloned bios */ __REQ_NR_BITS, /* stops here */ };
@@ -249,5 +250,6 @@ enum rq_flag_bits {#define REQ_HASHED (1ULL << __REQ_HASHED)#define REQ_MQ_INFLIGHT (1ULL << __REQ_MQ_INFLIGHT)#define REQ_NO_TIMEOUT (1ULL << __REQ_NO_TIMEOUT) +#define REQ_CLONE (1ULL << __REQ_CLONE)#endif /* __LINUX_BLK_TYPES_H */
include/linux/blkdev.h +1 -5
@@ -804,11 +804,7 @@ extern void blk_add_request_payload(struct request *rq, struct page *page, unsigned int len);extern int blk_rq_check_limits (struct request_queue *q,struct request *rq);extern int blk_lld_busy (struct request_queue *q); -extern int blk_rq_prep_clone (struct request *rq,struct request *rq_src, -struct bio_set *bs,gfp_t gfp_mask, -int (*bio_ctr)(struct bio *,struct bio *,void *), -void *data); -extern void blk_rq_unprep_clone (struct request *rq); +extern void blk_rq_prep_clone (struct request *rq,struct request *rq_src);extern int blk_insert_cloned_request (struct request_queue *q,struct request *rq);extern void blk_delay_queue (struct request_queue *,unsigned long );
Signature · 1 line
1.9.1