From 8d53e07b4101f8738a48d2a85dfb76e68e71a0ca Mon Sep 17 00:00:00 2001 From: Boaz Harrosh Date: Wed, 8 Aug 2007 17:57:01 +0300 Subject: [PATCH 05/12] SCSI: bidi support At the block level bidi request uses req->next_rq pointer for a second bidi_read request. At Scsi-midlayer a second scsi_data_buff structure is used for the bidi_read part. This bidi scsi_data_buff is put on request->next_rq->special. Struct scsi_cmnd is not changed. - Define scsi_end_bidi_request() to do what scsi_end_request() does but for a bidi request. This is necessary because bidi commands are a bit tricky here. (See comments in body) - scsi_release_buffers() will also release the bidi_read scsi_data_buff - scsi_io_completion() will now call scsi_end_bidi_request on bidi commands and return. - The previous work done in scsi_init_io() is now done in a new scsi_init_sgtable() (which is 98% identical to old scsi_init_io()) The new scsi_init_io() will call the above twice if needed also for the bidi_read command. Only at this point is a command bidi. - Define scsi_bidi_cmnd() to return true if it is a bidi request and a second sgtable was allocated. - Define scsi_in()/scsi_out() to return the in or out scsi_data_buff from this command This API is to isolate users from the mechanics of bidi. - In scsi_error.c at scsi_send_eh_cmnd() make sure bidi-lld is not confused by a get-sense command that looks like bidi. This is done by puting NULL at request->next_rq, and restoring before exit. --- drivers/scsi/scsi_error.c | 5 ++ drivers/scsi/scsi_lib.c | 126 +++++++++++++++++++++++++++++++++++++-------- include/scsi/scsi_cmnd.h | 23 ++++++++- 3 files changed, 131 insertions(+), 23 deletions(-) diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 0526333..a7dd830 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -618,6 +618,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, enum dma_data_direction old_data_direction; unsigned char old_cmd_len; struct scsi_data_buff old_sdb; + struct request* old_next_rq; int rtn; /* @@ -632,6 +633,9 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, old_data_direction = scmd->sc_data_direction; old_cmd_len = scmd->cmd_len; + old_next_rq = scmd->request->next_rq; + scmd->request->next_rq = NULL; + memset(scmd->cmnd, 0, sizeof(scmd->cmnd)); memcpy(scmd->cmnd, cmnd, cmnd_size); @@ -709,6 +713,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd, /* * Restore original data */ + scmd->request->next_rq = old_next_rq; scmd->sdb = old_sdb; memcpy(scmd->cmnd, old_cmnd, sizeof(scmd->cmnd)); scmd->sc_data_direction = old_data_direction; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 972b637..6d2d6c9 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -52,8 +52,7 @@ struct scsi_host_sg_pool { mempool_t *pool; }; static struct scsi_host_sg_pool scsi_sg_pools[SG_MEMPOOL_NR]; - - +static struct kmem_cache *scsi_bidi_sdb_cache; static void scsi_run_queue(struct request_queue *q); @@ -620,6 +619,28 @@ void scsi_run_host_queues(struct Scsi_Host *shost) scsi_run_queue(sdev->request_queue); } +static void scsi_finalize_request(struct scsi_cmnd *cmd, int uptodate) +{ + struct request_queue *q = cmd->device->request_queue; + struct request *req = cmd->request; + unsigned long flags; + + add_disk_randomness(req->rq_disk); + + spin_lock_irqsave(q->queue_lock, flags); + if (blk_rq_tagged(req)) + blk_queue_end_tag(q, req); + + end_that_request_last(req, uptodate); + spin_unlock_irqrestore(q->queue_lock, flags); + + /* + * This will goose the queue request function at the end, so we don't + * need to worry about launching another command. + */ + scsi_next_command(cmd); +} + /* * Function: scsi_end_request() * @@ -647,7 +668,6 @@ static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd *cmd, int uptodate, { struct request_queue *q = cmd->device->request_queue; struct request *req = cmd->request; - unsigned long flags; /* * If there are blocks left over at the end, set up the command @@ -676,19 +696,7 @@ static struct scsi_cmnd *scsi_end_request(struct scsi_cmnd *cmd, int uptodate, } } - add_disk_randomness(req->rq_disk); - - spin_lock_irqsave(q->queue_lock, flags); - if (blk_rq_tagged(req)) - blk_queue_end_tag(q, req); - end_that_request_last(req, uptodate); - spin_unlock_irqrestore(q->queue_lock, flags); - - /* - * This will goose the queue request function at the end, so we don't - * need to worry about launching another command. - */ - scsi_next_command(cmd); + scsi_finalize_request(cmd ,uptodate); return NULL; } @@ -753,6 +761,42 @@ static void scsi_release_buffers(struct scsi_cmnd *cmd) scsi_free_sgtable(&cmd->sdb); memset(&cmd->sdb, 0, sizeof(cmd->sdb)); + + if (scsi_bidi_cmnd(cmd)) { + struct scsi_data_buff *bidi_sdb = cmd->request->next_rq->special; + scsi_free_sgtable(bidi_sdb); + kmem_cache_free(scsi_bidi_sdb_cache, bidi_sdb); + cmd->request->next_rq->special = NULL; + } +} + +/* + * Bidi commands Must be complete as a whole, both sides at once. + * If part of the bytes were written and lld returned + * scsi_in()->resid and/or scsi_out()->resid this information will be left + * in req->data_len and req->next_rq->data_len. The upper-layer driver can + * decide what to do with this information. + */ +void scsi_end_bidi_request(struct scsi_cmnd *cmd) +{ + struct request *req = cmd->request; + + end_that_request_chunk(req, 1, req->data_len); + req->data_len = scsi_out(cmd)->resid; + + end_that_request_chunk(req->next_rq, 1, req->next_rq->data_len); + req->next_rq->data_len = scsi_in(cmd)->resid; + + scsi_release_buffers(cmd); + + /* + *FIXME: If ll_rw_blk.c is changed to also put_request(req->next_rq) + * in end_that_request_last() then this WARN_ON must be removed. + * for now, upper-driver must have registered an end_io. + */ + WARN_ON(!req->end_io); + + scsi_finalize_request(cmd, 1); } /* @@ -816,9 +860,15 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes) req->sense_len = len; } } + if (scsi_bidi_cmnd(cmd)) { + /* will also release_buffers */ + scsi_end_bidi_request(cmd); + return; + } req->data_len = scsi_get_resid(cmd); } + BUG_ON(blk_bidi_rq(req)); /* bidi not support for !blk_pc_request yet*/ scsi_release_buffers(cmd); /* @@ -956,11 +1006,9 @@ EXPORT_SYMBOL(scsi_io_completion); * BLKPREP_DEFER if the failure is retryable * BLKPREP_KILL if the failure is fatal */ -static int scsi_init_io(struct scsi_cmnd *cmd) +static int scsi_init_sgtable(struct request *req, struct scsi_data_buff *sdb) { - struct request *req = cmd->request; int count; - struct scsi_data_buff* sdb = &cmd->sdb; /* * We used to not use scatter-gather for single segment request, @@ -998,11 +1046,37 @@ static int scsi_init_io(struct scsi_cmnd *cmd) printk(KERN_ERR "counted %d, received %d\n", count, sdb->sg_count); printk(KERN_ERR "req nr_sec %lu, cur_nr_sec %u\n", req->nr_sectors, req->current_nr_sectors); + return BLKPREP_KILL; +} + +static int scsi_init_io(struct scsi_cmnd *cmd) +{ + int error = scsi_init_sgtable(cmd->request ,&cmd->sdb); + if (error) + goto err_exit; + + if (blk_bidi_rq(cmd->request)) { + struct scsi_data_buff *bidi_sdb = kmem_cache_zalloc( + scsi_bidi_sdb_cache, GFP_ATOMIC); + if (!bidi_sdb) + goto err_exit; + + cmd->request->next_rq->special = bidi_sdb; + error = scsi_init_sgtable(cmd->request->next_rq ,bidi_sdb); + if (error) + goto err_exit; + } + + return BLKPREP_OK ; - /* release the command and kill it */ +err_exit: scsi_release_buffers(cmd); - scsi_put_command(cmd); - return BLKPREP_KILL; + if (error == BLKPREP_KILL) + scsi_put_command(cmd); + else /* BLKPREP_DEFER */ + scsi_unprep_request(cmd->request); + + return error; } static struct scsi_cmnd *scsi_get_cmd_from_req(struct scsi_device *sdev, @@ -1621,6 +1695,14 @@ int __init scsi_init_queue(void) return -ENOMEM; } + scsi_bidi_sdb_cache = kmem_cache_create("scsi_bidi_sdb", + sizeof(struct scsi_data_buff), + 0, 0, NULL); + if (!scsi_bidi_sdb_cache) { + printk(KERN_ERR "SCSI: can't init scsi bidi sdb cache\n"); + return -ENOMEM; + } + for (i = 0, size = 8; i < SG_MEMPOOL_NR; i++, size <<= 1) { struct scsi_host_sg_pool *sgp = scsi_sg_pools + i; sgp->size = (i != SG_MEMPOOL_NR-1) ? size : SCSI_MAX_SG_SEGMENTS; diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index 1d02484..9ee3047 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -2,11 +2,11 @@ #define _SCSI_SCSI_CMND_H #include +#include #include #include #include -struct request; struct scatterlist; struct Scsi_Host; struct scsi_device; @@ -180,4 +180,25 @@ static inline int scsi_get_resid(struct scsi_cmnd *cmd) #define scsi_for_each_sg(cmd, sg, nseg, __i) \ for (__i = 0, sg = scsi_sglist(cmd); __i < (nseg); __i++, (sg)++) +static inline int scsi_bidi_cmnd(struct scsi_cmnd *cmd) +{ + return blk_bidi_rq(cmd->request) && + (cmd->request->next_rq->special != NULL); +} + +static inline struct scsi_data_buff *scsi_in(struct scsi_cmnd *cmd) +{ + WARN_ON((cmd->sc_data_direction != DMA_FROM_DEVICE) && + !scsi_bidi_cmnd(cmd)); + return scsi_bidi_cmnd(cmd) ? + cmd->request->next_rq->special : &cmd->sdb; +} + +static inline struct scsi_data_buff *scsi_out(struct scsi_cmnd *cmd) +{ + WARN_ON((cmd->sc_data_direction != DMA_TO_DEVICE) && + !scsi_bidi_cmnd(cmd)); + return &cmd->sdb; +} + #endif /* _SCSI_SCSI_CMND_H */ -- 1.5.2.2.249.g45fd