- Add bidi members to struct scsi_cmnd. - Add API at the SCSI level for bidirectional commands. - Implement support for BIDI requests in scsi_setup_blk_pc_cmnd() and scsi_io_completion(). Signed-off-by: Benny Halevy <bhalevy@xxxxxxxxxxx> Signed-off-by: Boaz Harrosh <bharrosh@xxxxxxxxxxx> --- drivers/scsi/scsi_lib.c | 324 +++++++++++++++++++++++++++++++++++--------- include/scsi/scsi_cmnd.h | 55 +++++++- include/scsi/scsi_device.h | 16 ++ 3 files changed, 330 insertions(+), 65 deletions(-) diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 9c21025..92d3d44 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -246,27 +246,28 @@ static void scsi_end_async(struct reques { struct scsi_io_context *sioc = req->end_io_data; - if (sioc->done) - sioc->done(sioc->data, sioc->sense, req->errors, rq_uni(req)->data_len); + if (sioc->done) /* FIXME: API of done needs to change for bidi requests*/ + sioc->done(sioc->data, sioc->sense, req->errors, req->uni.data_len); kmem_cache_free(scsi_io_context_cache, sioc); __blk_put_request(req->q, req); } -static int scsi_merge_bio(struct request *rq, struct bio *bio) +static int scsi_merge_bio(struct request *rq, struct bio *bio, + enum dma_data_direction dir) { struct request_queue *q = rq->q; - struct request_io_part* req_io = rq_uni(rq); + struct request_io_part* req_io = rq_io(rq, dir); bio->bi_flags &= ~(1 << BIO_SEG_VALID); - if (rq_uni_write_dir(rq)) + if (dir == DMA_TO_DEVICE) bio->bi_rw |= (1 << BIO_RW); else bio->bi_rw &= ~(1 << BIO_RW); blk_queue_bounce(q, &bio); - if (!rq_uni(rq)->bio) - blk_rq_bio_prep(q, rq, bio); + if (!req_io->bio) + blk_rq_bio_prep_bidi(q, rq, bio, dir); else if (!ll_back_merge_fn(q, rq, bio)) return -EINVAL; else { @@ -299,7 +300,7 @@ static int scsi_bi_endio(struct bio *bio * sent to use, as some ULDs use that struct to only organize the pages. */ static int scsi_req_map_sg(struct request *rq, struct scatterlist *sgl, - int nsegs, unsigned bufflen, gfp_t gfp) + int nsegs, unsigned bufflen, gfp_t gfp, enum dma_data_direction dir) { struct request_queue *q = rq->q; int nr_pages = (bufflen + sgl[0].offset + PAGE_SIZE - 1) >> PAGE_SHIFT; @@ -337,7 +338,7 @@ static int scsi_req_map_sg(struct reques } if (bio->bi_vcnt >= nr_vecs) { - err = scsi_merge_bio(rq, bio); + err = scsi_merge_bio(rq, bio ,dir); if (err) { bio_endio(bio, bio->bi_size, 0); goto free_bios; @@ -352,12 +353,12 @@ static int scsi_req_map_sg(struct reques } rq->buffer = rq->data = NULL; - rq_uni(rq)->data_len = data_len; + rq_io(rq,dir)->data_len = data_len; return 0; free_bios: - while ((bio = rq_uni(rq)->bio) != NULL) { - rq_uni(rq)->bio = bio->bi_next; + while ((bio = rq_io(rq,dir)->bio) != NULL) { + rq_io(rq,dir)->bio = bio->bi_next; /* * call endio instead of bio_put incase it was bounced */ @@ -385,31 +386,89 @@ int scsi_execute_async(struct scsi_devic int use_sg, int timeout, int retries, void *privdata, void (*done)(void *, char *, int, int), gfp_t gfp) { + struct scsi_cmnd_buff buff; + + buff.use_sg = use_sg; + buff.buffer = buffer; + buff.bufflen = bufflen; + + return scsi_execute_bidi_async( + sdev, (unsigned char *)cmd, cmd_len, data_direction, + &buff, NULL, + timeout, retries, privdata, done, gfp ); +} +EXPORT_SYMBOL_GPL(scsi_execute_async); + +/** + * scsi_execute_bidi_async - insert bidi request, don't wait + * @sdev: scsi device + * @cmd: scsi command + * @cmd_len: length of scsi cdb + * @data_direction: data direction + * @bidi_write_buff.buffer: data buffer (this can be a kernel buffer or scatterlist) + * @bidi_write_buff.bufflen: len of buffer + * @bidi_write_buff.use_sg: if buffer is a scatterlist this is the number of elements + * @bidi_read_buff: same as above bidi_write_buff but for the bidi read part + * @timeout: request timeout in jiffies + * @retries: number of times to retry request + * @privdata: user data passed to done function + * @done: pointer to done function called at io completion. + * signature: void done(void *user_data, char *sence, int errors, int data_bytes_advanced) + * @gfp: gfp allocation flags + **/ +int scsi_execute_bidi_async(struct scsi_device *sdev, + unsigned char *cmd, int cmd_len, int data_direction, + struct scsi_cmnd_buff *buff, struct scsi_cmnd_buff *bidi_read_buff, + int timeout, int retries, void *privdata, + void (*done)(void *, char *, int, int), gfp_t gfp) +{ struct request *req; struct scsi_io_context *sioc; int err = 0; + int is_bidi; sioc = kmem_cache_alloc(scsi_io_context_cache, gfp); if (!sioc) return DRIVER_ERROR << 24; memset(sioc, 0, sizeof(*sioc)); + WARN_ON( (data_direction==DMA_NONE) && buff->bufflen); + WARN_ON( (data_direction!=DMA_NONE) && !buff->bufflen); + WARN_ON( (data_direction==DMA_BIDIRECTIONAL) && !bidi_read_buff); req = blk_get_request(sdev->request_queue, data_direction, gfp); if (!req) goto free_sense; req->cmd_type = REQ_TYPE_BLOCK_PC; req->cmd_flags |= REQ_QUIET; - - if (use_sg) - err = scsi_req_map_sg(req, buffer, use_sg, bufflen, gfp); - else if (bufflen) - err = blk_rq_map_kern(req->q, req, buffer, bufflen, gfp); - + is_bidi = (data_direction == DMA_BIDIRECTIONAL); + if (buff->use_sg) + err = scsi_req_map_sg( + req, buff->buffer, buff->use_sg, buff->bufflen, gfp, + is_bidi ? DMA_TO_DEVICE : data_direction); + else if (buff->bufflen) + err = blk_rq_map_kern_bidi( + req->q, req, buff->buffer, buff->bufflen, gfp, + is_bidi ? DMA_TO_DEVICE : data_direction); if (err) goto free_req; req->cmd_len = cmd_len; memset(req->cmd, 0, BLK_MAX_CDB); /* ATAPI hates garbage after CDB */ + if (is_bidi) { + if (bidi_read_buff->use_sg) + err = scsi_req_map_sg( + req, bidi_read_buff->buffer, bidi_read_buff->use_sg, + bidi_read_buff->bufflen, gfp, DMA_FROM_DEVICE); + else if (buff->bufflen) + err = blk_rq_map_kern_bidi( + req->q, req, bidi_read_buff->buffer, + bidi_read_buff->bufflen, gfp, DMA_FROM_DEVICE); + if (err) { + end_that_request_block(req ,0);/* free up the write part */ + goto free_req; + } + } + memcpy(req->cmd, cmd, req->cmd_len); req->sense = sioc->sense; req->sense_len = 0; @@ -429,7 +488,60 @@ free_sense: kmem_cache_free(scsi_io_context_cache, sioc); return DRIVER_ERROR << 24; } -EXPORT_SYMBOL_GPL(scsi_execute_async); +EXPORT_SYMBOL_GPL(scsi_execute_bidi_async); + +struct scsi_execute_bidi_done_t { + struct completion *waiting; + char* sense; + int errors; +} ; + +static void scsi_execute_bidi_done(void *user_data, char *sense, int errors, int data_len) +{ + struct scsi_execute_bidi_done_t *sebd = user_data; + sebd->errors = errors; + if (sebd->sense) { + memcpy(sebd->sense, sense, SCSI_SENSE_BUFFERSIZE); + } + complete(sebd->waiting); +} + +/** + * scsi_execute_bidi - insert a bidi request + * @sdev: scsi device + * @cmd: scsi command + * @cmd_len: length of scsi cdb + * @data_direction: data direction + * @bidi_write_buff.buffer: data buffer (this can be a kernel buffer or scatterlist) + * @bidi_write_buff.bufflen: len of buffer + * @bidi_write_buff.use_sg: if buffer is a scatterlist this is the number of elements + * @bidi_read_buff: same as above bidi_write_buff but for the bidi read part. can be NULL + * @sense: optional sense buffer + * @timeout: request timeout in jiffies + * @retries: number of times to retry request + **/ +int scsi_execute_bidi(struct scsi_device *sdev, + unsigned char *cmd, int cmd_len, int data_direction, + struct scsi_cmnd_buff *buff, struct scsi_cmnd_buff *bidi_read_buff, + char* sense, int timeout, int retries) +{ + int ret; + struct scsi_execute_bidi_done_t sebd; + DECLARE_COMPLETION_ONSTACK(wait); + sebd.sense = sense; + sebd.waiting = &wait; + + ret = scsi_execute_bidi_async(sdev,cmd, cmd_len, data_direction, + buff, bidi_read_buff,timeout, retries, + &sebd, scsi_execute_bidi_done, __GFP_WAIT); + if (ret) + return ret; + + wait_for_completion(&wait); + + return sebd.errors; +} +EXPORT_SYMBOL_GPL(scsi_execute_bidi); /* * Function: scsi_init_cmd_errh() @@ -660,6 +772,16 @@ static struct scsi_cmnd *scsi_end_reques struct request *req = cmd->request; unsigned long flags; + if (is_bidi_cmnd(cmd)) { + if ( end_io_error(uptodate) && requeue ) { + WARN_ON(bytes); + scsi_requeue_command(q, cmd); + return NULL; + } + end_that_request_block(req, uptodate); + goto common_code; + } + /* * If there are blocks left over at the end, set up the command * to queue the remainder of them. @@ -687,6 +809,7 @@ static struct scsi_cmnd *scsi_end_reques } } +common_code: add_disk_randomness(req->rq_disk); spin_lock_irqsave(q->queue_lock, flags); @@ -703,34 +826,34 @@ 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 *__scsi_alloc_sgtable(struct scsi_data_buffer *sdb, gfp_t gfp_mask) { struct scsi_host_sg_pool *sgp; struct scatterlist *sgl; - BUG_ON(!cmd->use_sg); + BUG_ON(!sdb->use_sg); - switch (cmd->use_sg) { + switch (sdb->use_sg) { case 1 ... 8: - cmd->sglist_len = 0; + sdb->sglist_len = 0; break; case 9 ... 16: - cmd->sglist_len = 1; + sdb->sglist_len = 1; break; case 17 ... 32: - cmd->sglist_len = 2; + sdb->sglist_len = 2; break; #if (SCSI_MAX_PHYS_SEGMENTS > 32) case 33 ... 64: - cmd->sglist_len = 3; + sdb->sglist_len = 3; break; #if (SCSI_MAX_PHYS_SEGMENTS > 64) case 65 ... 128: - cmd->sglist_len = 4; + sdb->sglist_len = 4; break; #if (SCSI_MAX_PHYS_SEGMENTS > 128) case 129 ... 256: - cmd->sglist_len = 5; + sdb->sglist_len = 5; break; #endif #endif @@ -739,11 +862,25 @@ #endif return NULL; } - sgp = scsi_sg_pools + cmd->sglist_len; + sgp = scsi_sg_pools + sdb->sglist_len; sgl = mempool_alloc(sgp->pool, gfp_mask); return sgl; } +struct scatterlist *scsi_alloc_sgtable(struct scsi_cmnd *cmd, gfp_t gfp_mask) +{ + struct scsi_data_buffer uni_sdb; + struct scatterlist *ret = __scsi_alloc_sgtable(&uni_sdb, gfp_mask); + + if (likely(ret)) { + cmd->request_bufflen = uni_sdb.request_bufflen; + cmd->request_buffer = uni_sdb.request_buffer; + cmd->use_sg = uni_sdb.use_sg; + cmd->sglist_len = uni_sdb.sglist_len; + } + return ret; +} + EXPORT_SYMBOL(scsi_alloc_sgtable); void scsi_free_sgtable(struct scatterlist *sgl, int index) @@ -777,7 +914,7 @@ EXPORT_SYMBOL(scsi_free_sgtable); */ static void scsi_release_buffers(struct scsi_cmnd *cmd) { - if (cmd->use_sg) + if (cmd->use_sg && cmd->request_buffer) scsi_free_sgtable(cmd->request_buffer, cmd->sglist_len); /* @@ -786,6 +923,15 @@ static void scsi_release_buffers(struct */ cmd->request_buffer = NULL; cmd->request_bufflen = 0; + + if (is_bidi_cmnd(cmd) && cmd->bidi_read_sdb.request_buffer) { + if (cmd->bidi_read_sdb.use_sg) + scsi_free_sgtable( + cmd->bidi_read_sdb.request_buffer, + cmd->bidi_read_sdb.sglist_len); + cmd->bidi_read_sdb.request_buffer = NULL; + cmd->bidi_read_sdb.request_bufflen = 0; + } } /* @@ -850,17 +996,19 @@ void scsi_io_completion(struct scsi_cmnd memcpy(req->sense, cmd->sense_buffer, len); req->sense_len = len; } - } else - rq_uni(req)->data_len = cmd->resid; + } else { + /* FIXME: implement bidi resid */ + req->uni.data_len = cmd->resid; + } } /* * Next deal with any sectors which we were able to correctly * handle. */ - SCSI_LOG_HLCOMPLETE(1, printk("%ld sectors total, " - "%d bytes done.\n", - rq_uni(req)->nr_sectors, good_bytes)); + SCSI_LOG_HLCOMPLETE(1, printk( + "%ld(in) sectors %ld(out) sectors total, %d bytes done.\n", + rq_in(req)->nr_sectors, rq_out(req)->nr_sectors, good_bytes)); SCSI_LOG_HLCOMPLETE(1, printk("use_sg is %d\n", cmd->use_sg)); if (clear_errors) @@ -873,6 +1021,8 @@ void scsi_io_completion(struct scsi_cmnd if (scsi_end_request(cmd, 1, good_bytes, result == 0) == NULL) return; + BUG_ON(is_bidi_cmnd(cmd)); + /* good_bytes = 0, or (inclusive) there were leftovers and * result = 0, so scsi_end_request couldn't retry. */ @@ -982,19 +1132,24 @@ void scsi_io_completion(struct scsi_cmnd EXPORT_SYMBOL(scsi_io_completion); /* - * Function: scsi_init_io() + * Function: scsi_init_data_buf() * - * Purpose: SCSI I/O initialize function. + * Purpose: SCSI I/O initialize helper. + * maps the request buffers into the given sdb. * - * Arguments: cmd - Command descriptor we wish to initialize + * Arguments: sdb - Command data buff we wish to initialize + * req - request to map from + * dir - which part of the request to map * * Returns: 0 on success * 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_data_buff( + struct scsi_data_buffer *sdb, struct request* req, + enum dma_data_direction dir) { - struct request *req = cmd->request; + struct request_io_part *req_io = rq_io(req, dir); struct scatterlist *sgpnt; int count; @@ -1003,45 +1158,87 @@ static int scsi_init_io(struct scsi_cmnd * but now we do (it makes highmem I/O easier to support without * kmapping pages) */ - cmd->use_sg = rq_uni(req)->nr_phys_segments; + sdb->use_sg = req_io->nr_phys_segments; /* * If sg table allocation fails, requeue request later. */ - sgpnt = scsi_alloc_sgtable(cmd, GFP_ATOMIC); + sgpnt = __scsi_alloc_sgtable(sdb, GFP_ATOMIC); if (unlikely(!sgpnt)) { scsi_unprep_request(req); return BLKPREP_DEFER; } - req->buffer = NULL; - cmd->request_buffer = (char *) sgpnt; + sdb->request_buffer = (char *) sgpnt; if (blk_pc_request(req)) - cmd->request_bufflen = rq_uni(req)->data_len; + sdb->request_bufflen = req_io->data_len; else - cmd->request_bufflen = rq_uni(req)->nr_sectors << 9; + sdb->request_bufflen = req_io->nr_sectors << 9; /* * Next, walk the list, and fill in the addresses and sizes of * each segment. */ - count = blk_rq_map_sg(req->q, req, cmd->request_buffer); - if (likely(count <= cmd->use_sg)) { - cmd->use_sg = count; + count = blk_rq_map_sg_bidi(req->q, req, sdb->request_buffer, dir); + if (likely(count <= sdb->use_sg)) { + sdb->use_sg = count; return BLKPREP_OK; } printk(KERN_ERR "Incorrect number of segments after building list\n"); - printk(KERN_ERR "counted %d, received %d\n", count, cmd->use_sg); - printk(KERN_ERR "req nr_sec %lu, cur_nr_sec %u\n", rq_uni(req)->nr_sectors, - rq_uni(req)->current_nr_sectors); + printk(KERN_ERR "counted %d, received %d\n", count, sdb->use_sg); + printk(KERN_ERR "req nr_sec %lu, cur_nr_sec %u\n", req_io->nr_sectors, + req_io->current_nr_sectors); - /* release the command and kill it */ - scsi_release_buffers(cmd); - scsi_put_command(cmd); return BLKPREP_KILL; } +/* + * Function: scsi_init_io() + * + * Purpose: SCSI I/O initialize function. + * + * Arguments: cmd - Command descriptor we wish to initialize + * + * Returns: 0 on success + * BLKPREP_DEFER if the failure is retryable + * BLKPREP_KILL if the failure is fatal + */ +static int scsi_init_io(struct scsi_cmnd *cmd) +{ + struct scsi_data_buffer uni_sdb; + struct request *req = cmd->request; + int error; + int is_bidi = is_bidi_cmnd(cmd); + + error = scsi_init_data_buff(&uni_sdb, req, + is_bidi ? DMA_TO_DEVICE : rq_dma_dir(req) ); + if (error) + goto err_exit; + + cmd->request_bufflen = uni_sdb.request_bufflen; + cmd->request_buffer = uni_sdb.request_buffer; + cmd->use_sg = uni_sdb.use_sg; + cmd->sglist_len = uni_sdb.sglist_len; + + if (is_bidi) { + error = scsi_init_data_buff(&cmd->bidi_read_sdb, req, DMA_FROM_DEVICE); + if (error) + goto err_exit; + } + + req->buffer = NULL; + return 0 ; + +err_exit: + scsi_release_buffers(cmd); + if (error == BLKPREP_KILL) { + /* release the command and kill it */ + scsi_put_command(cmd); + } + return error; +} + static int scsi_issue_flush_fn(request_queue_t *q, struct gendisk *disk, sector_t *error_sector) { @@ -1094,6 +1291,7 @@ 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; + int has_data; cmd = scsi_get_cmd_from_req(sdev, req); if (unlikely(!cmd)) @@ -1105,22 +1303,22 @@ static int scsi_setup_blk_pc_cmnd(struct * that does not transfer data, in which case they may optionally * submit a request without an attached bio. */ - if (rq_uni(req)->bio) { - int ret; - - BUG_ON(!rq_uni(req)->nr_phys_segments); + has_data = rq_out(req)->bio || rq_in(req)->bio; + if (has_data) { + int ret; + BUG_ON(rq_bidi_dir(req) && (!rq_out(req)->bio || !rq_in(req)->bio)); ret = scsi_init_io(cmd); if (unlikely(ret)) return ret; } else { - BUG_ON(rq_uni(req)->data_len); - BUG_ON(req->data); + WARN_ON(rq_uni_dir(req) && rq_uni(req)->data_len); + WARN_ON(rq_bidi_dir(req)); + /* FIXME: WARN_ON(rq_dma_dir!=DMA_NONE) */ cmd->request_bufflen = 0; cmd->request_buffer = NULL; cmd->use_sg = 0; - req->buffer = NULL; req->data_dir = DMA_NONE; } @@ -1129,7 +1327,7 @@ static int scsi_setup_blk_pc_cmnd(struct cmd->cmd_len = req->cmd_len; cmd->sc_data_direction = rq_dma_dir(req); - cmd->transfersize = rq_uni(req)->data_len; + cmd->transfersize = is_bidi_cmnd(cmd) ? 0 : rq_uni(req)->data_len; cmd->allowed = req->retries; cmd->timeout_per_command = req->timeout; cmd->done = scsi_blk_pc_done; diff --git a/include/scsi/scsi_cmnd.h b/include/scsi/scsi_cmnd.h index d6948d0..78ae417 100644 --- a/include/scsi/scsi_cmnd.h +++ b/include/scsi/scsi_cmnd.h @@ -1,16 +1,34 @@ #ifndef _SCSI_SCSI_CMND_H #define _SCSI_SCSI_CMND_H -#include <linux/dma-mapping.h> +#include <linux/blkdev.h> #include <linux/list.h> #include <linux/types.h> #include <linux/timer.h> +#include <scsi/scsi_device.h> -struct request; struct scatterlist; struct Scsi_Host; struct scsi_device; +/* + * This structure maps data buffers into a scatter-gather list for DMA purposes. + * Embedded in struct scsi_cmnd. + * + * FIXME: We currently embed this structure in scsi_cmnd only for + * bidi read buffers. Buffers for uni-directional commands and write + * buffers of bidi commands are mapped in a backward compatible way by an + * equivalent set of fields, scattered in the scsi_cmnd. + * These should be incorporated into an instance of scsi_cmn_sgl. + * This will require a major rework of most scsi LLDDs. + */ + +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 */ +}; /* embedded in scsi_cmnd */ struct scsi_pointer { @@ -120,8 +138,41 @@ #define SCSI_SENSE_BUFFERSIZE 96 unsigned char tag; /* SCSI-II queued command tag */ unsigned long pid; /* Process ID, starts at 0. Unique per host. */ + + /* + * map read buffers of bi-directional commands + * iff sc_data_direction==DMA_BIDIRECTIONAL + */ + struct scsi_data_buffer bidi_read_sdb; }; +#define is_bidi_cmnd(cmd) rq_bidi_dir((cmd)->request) + +/* + * these inline helpers take into account the scsi cmnd's data direction + * to correctly find sg maps for uni-directional and bi-directional commands + */ +static inline void +scsi_get_out_buff(struct scsi_cmnd *sc, struct scsi_cmnd_buff *scb) +{ + scb->use_sg = sc->use_sg; + scb->buffer = sc->request_buffer; + scb->bufflen = sc->request_bufflen; +} + +static inline void +scsi_get_in_buff(struct scsi_cmnd *sc, struct scsi_cmnd_buff *scb) +{ + if (!is_bidi_cmnd(sc)) { + scsi_get_out_buff(sc, scb); + return; + } + + scb->use_sg = sc->bidi_read_sdb.use_sg; + scb->buffer = sc->bidi_read_sdb.request_buffer; + scb->bufflen = sc->bidi_read_sdb.request_bufflen; +} + extern struct scsi_cmnd *scsi_get_command(struct scsi_device *, gfp_t); extern struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *, gfp_t); extern void scsi_put_command(struct scsi_cmnd *); diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index ebf31b1..a10989c 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -198,6 +198,13 @@ #define transport_class_to_starget(class #define starget_printk(prefix, starget, fmt, a...) \ dev_printk(prefix, &(starget)->dev, fmt, ##a) +/* used by APIs as a shorthand for <use_sg, sg, bufflen> */ +struct scsi_cmnd_buff { + unsigned short use_sg; /* Number of pieces of scatter-gather */ + void *buffer; /* if use_sg==0 requested buffer else an sg list */ + unsigned bufflen; /* Actual request size */ +}; + extern struct scsi_device *__scsi_add_device(struct Scsi_Host *, uint, uint, uint, void *hostdata); extern int scsi_add_device(struct Scsi_Host *host, uint channel, @@ -297,6 +304,15 @@ extern int scsi_execute_async(struct scs int timeout, int retries, void *privdata, void (*done)(void *, char *, int, int), gfp_t gfp); +extern int scsi_execute_bidi_async(struct scsi_device *sdev, + unsigned char *cmd, int cmd_len, int data_direction, + struct scsi_cmnd_buff *buff, struct scsi_cmnd_buff *bidi_read_buff, + int timeout, int retries, void *privdata, + void (*done)(void *, char *, int, int), gfp_t gfp); +extern int scsi_execute_bidi(struct scsi_device *sdev, + unsigned char *cmd, int cmd_len, int data_direction, + struct scsi_cmnd_buff *buff, struct scsi_cmnd_buff *bidi_read_buff, + char* sense, int timeout, int retries); static inline int __must_check scsi_device_reprobe(struct scsi_device *sdev) { -- - 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