From: FUJITA Tomonori <fujita.tomonori@xxxxxxxxxxxxx> Subject: [PATCH 0/2] bidi support Date: Sat, 05 May 2007 18:02:29 +0900 > This patchset add bidi support for block pc requests. Oh, this patchset is against Linus' tree. The first patch (the block layer changes) can be applied against Jens' tree. The second patch (the scsi mid-layer changes) has one minor reject against the scsi-misc tree. The following patch is against the scsi-misc. --- >From 6a8c5375f1f6dbd574107920dd0a613527bd556b Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori <fujita.tomonori@xxxxxxxxxxxxx> Date: Sat, 5 May 2007 18:11:42 +0900 Subject: [PATCH] add bidi support for block pc requests This adds bidi support for block pc requests. A bidi request uses req->next_rq pointer for an in request. This patch introduces a new structure, scsi_data_buffer to hold the data buffer information. To avoid make scsi_cmnd structure fatter, the scsi mid-layer uses cmnd->request->next_rq->special pointer for a scsi_data_buffer structure. LLDs don't touch the second request (req->next_rq) so it's safe to use req->special. scsi_blk_pc_done() always completes the whole command so scsi_end_request() simply completes the bidi chunk too. A helper function, scsi_bidi_data_buffer() is for LLDs to access to the scsi_data_buffer structure easily. Signed-off-by: FUJITA Tomonori <fujita.tomonori@xxxxxxxxxxxxx> --- drivers/scsi/scsi_lib.c | 120 +++++++++++++++++++++++++++++++++++++++------ include/scsi/scsi_cmnd.h | 14 +++++ 2 files changed, 118 insertions(+), 16 deletions(-) diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index be8e655..8f7873a 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -66,6 +66,12 @@ #undef SP static void scsi_run_queue(struct request_queue *q); +struct scsi_data_buffer *scsi_bidi_data_buffer(struct scsi_cmnd *cmd) +{ + return blk_bidi_rq(cmd->request) ? cmd->request->next_rq->special : NULL; +} +EXPORT_SYMBOL(scsi_bidi_data_buffer); + /* * Function: scsi_unprep_request() * @@ -85,6 +91,7 @@ static void scsi_unprep_request(struct r req->cmd_flags &= ~REQ_DONTPREP; req->special = NULL; + kfree(scsi_bidi_data_buffer(cmd)); scsi_put_command(cmd); } @@ -657,6 +664,7 @@ static struct scsi_cmnd *scsi_end_reques request_queue_t *q = cmd->device->request_queue; struct request *req = cmd->request; unsigned long flags; + struct scsi_data_buffer *sdb = scsi_bidi_data_buffer(cmd); /* * If there are blocks left over at the end, set up the command @@ -685,6 +693,14 @@ static struct scsi_cmnd *scsi_end_reques } } + /* + * a REQ_BLOCK_PC command is always completed fully so just do + * end the bidi chunk. + */ + if (sdb) + end_that_request_chunk(req->next_rq, uptodate, + sdb->request_bufflen); + add_disk_randomness(req->rq_disk); spin_lock_irqsave(q->queue_lock, flags); @@ -701,34 +717,35 @@ static struct scsi_cmnd *scsi_end_reques return NULL; } -struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *cmd, gfp_t gfp_mask) +static struct scatterlist *do_scsi_alloc_sgtable(unsigned short use_sg, + unsigned short *sglist_len, + gfp_t gfp_mask) { struct scsi_host_sg_pool *sgp; - struct scatterlist *sgl; - BUG_ON(!cmd->use_sg); + BUG_ON(!use_sg); - switch (cmd->use_sg) { + switch (use_sg) { case 1 ... 8: - cmd->sglist_len = 0; + *sglist_len = 0; break; case 9 ... 16: - cmd->sglist_len = 1; + *sglist_len = 1; break; case 17 ... 32: - cmd->sglist_len = 2; + *sglist_len = 2; break; #if (SCSI_MAX_PHYS_SEGMENTS > 32) case 33 ... 64: - cmd->sglist_len = 3; + *sglist_len = 3; break; #if (SCSI_MAX_PHYS_SEGMENTS > 64) case 65 ... 128: - cmd->sglist_len = 4; + *sglist_len = 4; break; #if (SCSI_MAX_PHYS_SEGMENTS > 128) case 129 ... 256: - cmd->sglist_len = 5; + *sglist_len = 5; break; #endif #endif @@ -737,11 +754,14 @@ #endif return NULL; } - sgp = scsi_sg_pools + cmd->sglist_len; - sgl = mempool_alloc(sgp->pool, gfp_mask); - return sgl; + sgp = scsi_sg_pools + *sglist_len; + return mempool_alloc(sgp->pool, gfp_mask); } +struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *cmd, gfp_t gfp_mask) +{ + return do_scsi_alloc_sgtable(cmd->use_sg, &cmd->sglist_len, gfp_mask); +} EXPORT_SYMBOL(scsi_alloc_sgtable); void scsi_free_sgtable(struct scatterlist *sgl, int index) @@ -775,6 +795,8 @@ EXPORT_SYMBOL(scsi_free_sgtable); */ static void scsi_release_buffers(struct scsi_cmnd *cmd) { + struct scsi_data_buffer *sdb = scsi_bidi_data_buffer(cmd); + if (cmd->use_sg) scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len); @@ -784,6 +806,13 @@ static void scsi_release_buffers(struct */ cmd->request_buffer = NULL; cmd->request_bufflen = 0; + + if (sdb) { + if (sdb->use_sg) + scsi_free_sgtable(sdb->request_buffer, sdb->sglist_len); + sdb->request_buffer = NULL; + sdb->request_bufflen = 0; + } } /* @@ -834,6 +863,7 @@ void scsi_io_completion(struct scsi_cmnd } if (blk_pc_request(req)) { /* SG_IO ioctl from block level */ + struct scsi_data_buffer *sdb = scsi_bidi_data_buffer(cmd); req->errors = result; if (result) { clear_errors = 0; @@ -850,6 +880,8 @@ void scsi_io_completion(struct scsi_cmnd } } req->data_len = cmd->resid; + if (sdb) + req->next_rq->data_len = sdb->resid; } /* @@ -1075,6 +1107,38 @@ static struct scsi_cmnd *scsi_get_cmd_fr return cmd; } +static int scsi_data_buffer_init(struct scsi_cmnd *cmd) +{ + struct scatterlist *sgpnt; + struct scsi_data_buffer *sdb = scsi_bidi_data_buffer(cmd); + struct request *req = cmd->request; + int count; + + sdb->use_sg = req->next_rq->nr_phys_segments; + sgpnt = do_scsi_alloc_sgtable(sdb->use_sg, &sdb->sglist_len, + GFP_ATOMIC); + if (unlikely(!sgpnt)) { + scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len); + scsi_unprep_request(req); + return BLKPREP_DEFER; + } + + req->buffer = NULL; + sdb->request_buffer = (char *) sgpnt; + sdb->request_bufflen = req->next_rq->data_len; + + count = blk_rq_map_sg(req->q, req->next_rq, sgpnt); + if (likely(count <= sdb->use_sg)) { + sdb->use_sg = count; + return BLKPREP_OK; + } + + scsi_release_buffers(cmd); + scsi_put_command(cmd); + + return BLKPREP_KILL; +} + static void scsi_blk_pc_done(struct scsi_cmnd *cmd) { BUG_ON(!blk_pc_request(cmd->request)); @@ -1090,10 +1154,21 @@ static void scsi_blk_pc_done(struct scsi static int scsi_setup_blk_pc_cmnd(struct scsi_device *sdev, struct request *req) { struct scsi_cmnd *cmd; + struct scsi_data_buffer *sdb = NULL; + + if (blk_bidi_rq(req)) { + sdb = kzalloc(sizeof(*sdb), GFP_ATOMIC); + if (unlikely(!sdb)) + return BLKPREP_DEFER; + req->next_rq->special = sdb; + } cmd = scsi_get_cmd_from_req(sdev, req); - if (unlikely(!cmd)) + if (unlikely(!cmd)) { + req->next_rq->special = NULL; + kfree(sdb); return BLKPREP_DEFER; + } /* * BLOCK_PC requests may transfer data, in which case they must @@ -1109,6 +1184,12 @@ static int scsi_setup_blk_pc_cmnd(struct ret = scsi_init_io(cmd); if (unlikely(ret)) return ret; + + if (sdb) { + ret = scsi_data_buffer_init(cmd); + if (ret != BLKPREP_OK) + return ret; + } } else { BUG_ON(req->data_len); BUG_ON(req->data); @@ -1122,13 +1203,15 @@ static int scsi_setup_blk_pc_cmnd(struct BUILD_BUG_ON(sizeof(req->cmd) > sizeof(cmd->cmnd)); memcpy(cmd->cmnd, req->cmd, sizeof(cmd->cmnd)); cmd->cmd_len = req->cmd_len; - if (!req->data_len) + if (sdb) + cmd->sc_data_direction = DMA_BIDIRECTIONAL; + else if (!req->data_len) cmd->sc_data_direction = DMA_NONE; else if (rq_data_dir(req) == WRITE) cmd->sc_data_direction = DMA_TO_DEVICE; else cmd->sc_data_direction = DMA_FROM_DEVICE; - + cmd->transfersize = req->data_len; cmd->allowed = req->retries; cmd->timeout_per_command = req->timeout; @@ -1178,6 +1261,11 @@ static int scsi_prep_fn(struct request_q struct scsi_device *sdev = q->queuedata; int ret = BLKPREP_OK; + if (WARN_ON(!blk_pc_request(req) && blk_bidi_rq(req))) { + ret = BLKPREP_KILL; + goto out; + } + /* * If the device is not in running state we will reject some * or all commands. diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index a2e0c10..597c48c 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -28,6 +28,18 @@ struct scsi_pointer { volatile int phase; }; +struct scsi_data_buffer { + unsigned short use_sg; /* Number of pieces of scatter-gather */ + unsigned short sglist_len; /* size of malloc'd scatter-gather list */ + void *request_buffer; /* Actual requested buffer */ + unsigned request_bufflen; /* Actual request size */ + /* + * Number of bytes requested to be transferred less actual + * number transferred (0 if not supported) + */ + int resid; +}; + struct scsi_cmnd { struct scsi_device *device; struct list_head list; /* scsi_cmnd participates in queue lists */ @@ -135,4 +147,6 @@ extern void scsi_kunmap_atomic_sg(void * extern struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *, gfp_t); extern void scsi_free_sgtable(struct scatterlist *, int); +extern struct scsi_data_buffer *scsi_bidi_data_buffer(struct scsi_cmnd *); + #endif /* _SCSI_SCSI_CMND_H */ -- 1.4.3.2 - To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html