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 <[EMAIL PROTECTED]>
---
drivers/scsi/scsi_lib.c | 123 +++++++++++++++++++++++++++++++++++++++------
include/scsi/scsi_cmnd.h | 14 +++++
2 files changed, 120 insertions(+), 17 deletions(-)
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 05d79af..3bb62da 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;
+ }
}
/*
@@ -848,8 +877,12 @@ void scsi_io_completion(struct scsi_cmnd
memcpy(req->sense, cmd->sense_buffer, len);
req->sense_len = len;
}
- } else
+ } else {
+ struct scsi_data_buffer *sdb =
scsi_bidi_data_buffer(cmd);
req->data_len = cmd->resid;
+ if (sdb)
+ req->next_rq->data_len = sdb->resid;
+ }
}
/*
@@ -1077,6 +1110,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));
@@ -1092,10 +1157,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
@@ -1111,6 +1187,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);
@@ -1124,13 +1206,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;
@@ -1180,6 +1264,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 d6948d0..4d38e1b 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 */
@@ -138,4 +150,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 [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html