[RFC 3/6] bidi support: bidirectional request

From: Boaz Harrosh
Date: Sun Jan 21 2007 - 18:26:29 EST


- Instantiate another request_io_part in request for bidi_read.
- Define & Implement new API for accessing bidi parts.
- API to Build bidi requests and map to sglists.
- Define new end_that_request_block() function to end a complete request.

Signed-off-by: Benny Halevy <bhalevy@xxxxxxxxxxx>
Signed-off-by: Boaz Harrosh <bharrosh@xxxxxxxxxxx>

---
block/elevator.c | 7 +---
block/ll_rw_blk.c | 113 ++++++++++++++++++++++++++++++++++++++++++------
include/linux/blkdev.h | 49 ++++++++++++++++++++-
3 files changed, 149 insertions(+), 20 deletions(-)

diff --git a/block/elevator.c b/block/elevator.c
index 0e9ea69..53b552e 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -741,14 +741,9 @@ struct request *elv_next_request(request
rq = NULL;
break;
} else if (ret == BLKPREP_KILL) {
- int nr_bytes = rq_uni(rq)->hard_nr_sectors << 9;
-
- if (!nr_bytes)
- nr_bytes = rq_uni(rq)->data_len;
-
blkdev_dequeue_request(rq);
rq->cmd_flags |= REQ_QUIET;
- end_that_request_chunk(rq, 0, nr_bytes);
+ end_that_request_block(rq, 0);
end_that_request_last(rq, 0);
} else {
printk(KERN_ERR "%s: bad return=%d\n", __FUNCTION__,
diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c
index 7d46f6a..1358b35 100644
--- a/block/ll_rw_blk.c
+++ b/block/ll_rw_blk.c
@@ -261,6 +261,7 @@ static void rq_init(request_queue_t *q,
rq->end_io_data = NULL;
rq->completion_data = NULL;
rq_init_io_part(&rq->uni);
+ rq_init_io_part(&rq->bidi_read);
}

/**
@@ -1312,15 +1313,16 @@ static int blk_hw_contig_segment(request
}

/*
- * map a request to scatterlist, return number of sg entries setup. Caller
- * must make sure sg can hold rq->nr_phys_segments entries
+ * map a request_io_part to scatterlist, return number of sg entries setup.
+ * Caller must make sure sg can hold rq_io(rq, dir)->nr_phys_segments entries
*/
-int blk_rq_map_sg(request_queue_t *q, struct request *rq, struct scatterlist *sg)
+int blk_rq_map_sg_bidi(request_queue_t *q, struct request *rq,
+ struct scatterlist *sg, enum dma_data_direction dir)
{
struct bio_vec *bvec, *bvprv;
struct bio *bio;
int nsegs, i, cluster;
- struct request_io_part* req_io = rq_uni(rq);
+ struct request_io_part* req_io = rq_io(rq, dir);

nsegs = 0;
cluster = q->queue_flags & (1 << QUEUE_FLAG_CLUSTER);
@@ -1362,6 +1364,17 @@ new_segment:
return nsegs;
}

+EXPORT_SYMBOL(blk_rq_map_sg_bidi);
+
+/*
+ * map a request to scatterlist, return number of sg entries setup. Caller
+ * must make sure sg can hold rq->nr_phys_segments entries
+ */
+int blk_rq_map_sg(request_queue_t *q, struct request *rq, struct scatterlist *sg)
+{
+ return blk_rq_map_sg_bidi(q, rq, sg, rq->data_dir);
+}
+
EXPORT_SYMBOL(blk_rq_map_sg);

/*
@@ -2561,15 +2574,18 @@ int blk_rq_unmap_user(struct bio *bio)
EXPORT_SYMBOL(blk_rq_unmap_user);

/**
- * blk_rq_map_kern - map kernel data to a request, for REQ_BLOCK_PC usage
+ * blk_rq_map_kern_bidi - maps kernel data to a request_io_part, for BIDI usage
* @q: request queue where request should be inserted
* @rq: request to fill
* @kbuf: the kernel buffer
* @len: length of user data
* @gfp_mask: memory allocation flags
+ * @dir: if it is a BIDIRECTIONAL request than DMA_TO_DEVICE to prepare
+ * the bidi_write side or DMA_FROM_DEVICE to prepare the bidi_read
+ * side, else it should be same as req->data_dir
*/
-int blk_rq_map_kern(request_queue_t *q, struct request *rq, void *kbuf,
- unsigned int len, gfp_t gfp_mask)
+int blk_rq_map_kern_bidi(request_queue_t *q, struct request *rq, void *kbuf,
+ unsigned int len, gfp_t gfp_mask, enum dma_data_direction dir)
{
struct bio *bio;

@@ -2582,14 +2598,30 @@ int blk_rq_map_kern(request_queue_t *q,
if (IS_ERR(bio))
return PTR_ERR(bio);

- if (rq_uni_write_dir(rq) == WRITE)
+ if (dma_write_dir(dir))
bio->bi_rw |= (1 << BIO_RW);

- blk_rq_bio_prep(q, rq, bio);
+ blk_rq_bio_prep_bidi(q, rq, bio ,dir);
rq->buffer = rq->data = NULL;
return 0;
}

+EXPORT_SYMBOL(blk_rq_map_kern_bidi);
+
+/**
+ * blk_rq_map_kern - map kernel data to a request, for REQ_BLOCK_PC usage
+ * @q: request queue where request should be inserted
+ * @rq: request to fill
+ * @kbuf: the kernel buffer
+ * @len: length of user data
+ * @gfp_mask: memory allocation flags
+ */
+int blk_rq_map_kern(request_queue_t *q, struct request *rq, void *kbuf,
+ unsigned int len, gfp_t gfp_mask)
+{
+ return blk_rq_map_kern_bidi( q, rq, kbuf, len, gfp_mask, rq->data_dir);
+}
+
EXPORT_SYMBOL(blk_rq_map_kern);

/**
@@ -3353,11 +3385,11 @@ static void blk_recalc_rq_sectors(struct
}

static int __end_that_request_first(struct request *req, int uptodate,
- int nr_bytes)
+ int nr_bytes, enum dma_data_direction dir)
{
int total_bytes, bio_nbytes, error, next_idx = 0;
struct bio *bio;
- struct request_io_part* req_io = rq_uni(req);
+ struct request_io_part* req_io = rq_io(req, dir);

blk_add_trace_rq(req->q, req, BLK_TA_COMPLETE);

@@ -3447,6 +3479,8 @@ static int __end_that_request_first(stru
if (!req_io->bio)
return 0;

+ WARN_ON(rq_bidi_dir(req));
+
/*
* if the request wasn't completed, update state
*/
@@ -3479,7 +3513,7 @@ static int __end_that_request_first(stru
**/
int end_that_request_first(struct request *req, int uptodate, int nr_sectors)
{
- return __end_that_request_first(req, uptodate, nr_sectors << 9);
+ return end_that_request_chunk(req, uptodate, nr_sectors << 9);
}

EXPORT_SYMBOL(end_that_request_first);
@@ -3501,11 +3535,55 @@ EXPORT_SYMBOL(end_that_request_first);
**/
int end_that_request_chunk(struct request *req, int uptodate, int nr_bytes)
{
- return __end_that_request_first(req, uptodate, nr_bytes);
+ WARN_ON_BIDI_FLAG(req);
+ WARN_ON(!rq_uni_dir(req));
+ return __end_that_request_first(req, uptodate, nr_bytes,
+ rq_uni_dir(req) ? rq_dma_dir(req) : DMA_TO_DEVICE);
}

EXPORT_SYMBOL(end_that_request_chunk);

+static void __end_req_io_block(struct request_io_part *req_io, int error)
+{
+ struct bio *next, *bio = req_io->bio;
+ req_io->bio = NULL;
+
+ for (; bio; bio = next) {
+ next = bio->bi_next;
+ bio_endio(bio, bio->bi_size, error);
+ }
+}
+
+/**
+ * end_that_request_block - end ALL I/O on a request in one "shloop",
+ * including the bidi part.
+ * @req: the request being processed
+ * @uptodate: 1 for success, 0 for I/O error, < 0 for specific error
+ *
+ * Description:
+ * Ends ALL I/O on @req, both read/write or bidi. frees all bio resources.
+ **/
+void end_that_request_block(struct request *req, int uptodate)
+{
+ if (blk_pc_request(req)) {
+ int error = 0;
+ if (end_io_error(uptodate))
+ error = !uptodate ? -EIO : uptodate;
+ blk_add_trace_rq(req->q, req, BLK_TA_COMPLETE);
+
+ __end_req_io_block(&req->uni, error);
+ if (rq_bidi_dir(req))
+ __end_req_io_block(&req->bidi_read, 0);
+ } else { /* needs elevator bookeeping */
+ int nr_bytes = req->uni.hard_nr_sectors << 9;
+ if (!nr_bytes)
+ nr_bytes = req->uni.data_len;
+ end_that_request_chunk(req, uptodate, nr_bytes);
+ }
+}
+
+EXPORT_SYMBOL(end_that_request_block);
+
/*
* splice the completion data to a local structure and hand off to
* process_completion_queue() to complete the requests
@@ -3633,6 +3711,15 @@ void end_request(struct request *req, in

EXPORT_SYMBOL(end_request);

+void blk_rq_bio_prep_bidi(request_queue_t *q, struct request *rq,
+ struct bio *bio, enum dma_data_direction dir)
+{
+ init_req_io_part_from_bio(q, rq_io(rq, dir), bio);
+ rq->buffer = NULL;
+}
+
+EXPORT_SYMBOL(blk_rq_bio_prep_bidi);
+
void blk_rq_bio_prep(request_queue_t *q, struct request *rq, struct bio *bio)
{
rq->data_dir = bio_data_dir(bio) ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index 3cabbaf..eff3960 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -323,6 +323,7 @@ struct request {
void *end_io_data;

struct request_io_part uni;
+ struct request_io_part bidi_read;
};

/*
@@ -599,6 +600,27 @@ static inline struct request_io_part* rq
return &req->uni;
}

+static inline struct request_io_part* rq_in(struct request* req)
+{
+ WARN_ON_BIDI_FLAG(req);
+ if (likely(rq_dma_dir(req) != DMA_BIDIRECTIONAL))
+ return &req->uni;
+
+ if (likely(req->cmd_flags & REQ_BIDI))
+ return &req->bidi_read;
+
+ return &req->uni;
+}
+
+static inline struct request_io_part* rq_io(struct request* req, enum dma_data_direction dir)
+{
+ if (dir == DMA_FROM_DEVICE)
+ return rq_in(req);
+
+ WARN_ON( (dir != DMA_TO_DEVICE) && (dir != DMA_NONE) );
+ return &req->uni;
+}
+
/*
* We regard a request as sync, if it's a READ or a SYNC write.
*/
@@ -636,7 +658,8 @@ static inline void blk_clear_queue_full(
#define RQ_NOMERGE_FLAGS \
(REQ_NOMERGE | REQ_STARTED | REQ_HARDBARRIER | REQ_SOFTBARRIER)
#define rq_mergeable(rq) \
- (!((rq)->cmd_flags & RQ_NOMERGE_FLAGS) && blk_fs_request((rq)))
+ (!((rq)->cmd_flags & RQ_NOMERGE_FLAGS) && blk_fs_request((rq)) && \
+ ((rq)->data_dir != DMA_BIDIRECTIONAL))

/*
* q->prep_rq_fn return values
@@ -767,6 +790,15 @@ extern void end_request(struct request *
extern void blk_complete_request(struct request *);

/*
+ * end_request_block will complete and free all bio resources held
+ * by the request in one call. User will still need to call
+ * end_that_request_last(..).
+ * It is the only one that can deal with BIDI.
+ * can be called for parial bidi allocation and cleanup.
+ */
+extern void end_that_request_block(struct request *req, int uptodate);
+
+/*
* end_that_request_first/chunk() takes an uptodate argument. we account
* any value <= as an io error. 0 means -EIO for compatability reasons,
* any other < 0 value is the direct error type. An uptodate value of
@@ -845,6 +877,21 @@ static inline struct request *blk_map_qu
extern void blk_rq_bio_prep(request_queue_t *, struct request *, struct bio *);
extern int blkdev_issue_flush(struct block_device *, sector_t *);

+/* BIDI API
+ * build a request. for bidi requests must be called twice to map/prepare
+ * the data-in and data-out buffers, one at a time according to
+ * the given dma_data_direction.
+ */
+extern void blk_rq_bio_prep_bidi(request_queue_t *, struct request *,
+ struct bio *, enum dma_data_direction);
+extern int blk_rq_map_kern_bidi(request_queue_t *, struct request *,
+ void *, unsigned int, gfp_t, enum dma_data_direction);
+/* retrieve the mapped pages for bidi according to
+ * the given dma_data_direction
+ */
+extern int blk_rq_map_sg_bidi(request_queue_t *, struct request *,
+ struct scatterlist *, enum dma_data_direction);
+
#define MAX_PHYS_SEGMENTS 128
#define MAX_HW_SEGMENTS 128
#define SAFE_MAX_SECTORS 255
--
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/