From ad5af755e625f1dd37fc04edf4e54e7a1b81279a Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Thu, 29 Nov 2007 16:16:24 -0600 Subject: [PATCH 13/13] convert st to blk/bio helpers This patch is going to be buggy as heck. I do not have tape drive, so I tried to hack up scsi_debug. I did some attachment tests and that is all. The patch should be considered more of a reference/guide. I wanted to make sure the blk/bio helpers could support all of st's features so this patch is my attempt at converting st to the helpers to make sure it was going to work. I am sure I messed up some tape stuff. Kai, you probably do not have to waste time on this patch. I will send something that is better tested later. At this point I just want to make sure the blk/bio helper API is going to work for everyone. Signed-off-by: Mike Christie --- drivers/scsi/st.c | 1242 +++++++++++++++++++++++------------------------------ drivers/scsi/st.h | 22 +- 2 files changed, 540 insertions(+), 724 deletions(-) diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index 98dfd6e..e11efcb 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -181,18 +181,7 @@ static struct scsi_tape **scsi_tapes = NULL; static int modes_defined; -static struct st_buffer *new_tape_buffer(int, int, int); -static int enlarge_buffer(struct st_buffer *, int, int); -static void normalize_buffer(struct st_buffer *); -static int append_to_buffer(const char __user *, struct st_buffer *, int); -static int from_buffer(struct st_buffer *, char __user *, int); -static void move_buffer_data(struct st_buffer *, int); -static void buf_to_sg(struct st_buffer *, unsigned int); - -static int sgl_map_user_pages(struct scatterlist *, const unsigned int, - unsigned long, size_t, int); -static int sgl_unmap_user_pages(struct scatterlist *, const unsigned int, int); - +static struct st_buffer *new_tape_buffer(struct scsi_tape *); static int st_probe(struct device *); static int st_remove(struct device *); @@ -435,62 +424,142 @@ static int st_chk_result(struct scsi_tape *STp, struct st_request * SRpnt) /* Wakeup from interrupt */ -static void st_sleep_done(void *data, char *sense, int result, int resid) +static void st_sleep_done(struct request *rq, int error) { - struct st_request *SRpnt = data; + struct st_request *SRpnt = rq->end_io_data; struct scsi_tape *STp = SRpnt->stp; - memcpy(SRpnt->sense, sense, SCSI_SENSE_BUFFERSIZE); - (STp->buffer)->cmdstat.midlevel_result = SRpnt->result = result; + (STp->buffer)->cmdstat.midlevel_result = SRpnt->result = rq->errors; DEB( STp->write_pending = 0; ) if (SRpnt->waiting) complete(SRpnt->waiting); } -static struct st_request *st_allocate_request(void) +static struct st_request *st_allocate_request(struct scsi_tape *STp, int rw) { - return kzalloc(sizeof(struct st_request), GFP_KERNEL); + struct st_request *SRpnt; + + SRpnt = kzalloc(sizeof(struct st_request), GFP_KERNEL); + if (!SRpnt) + return NULL; + + SRpnt->rq = blk_get_request(STp->device->request_queue, rw, + GFP_KERNEL); + if (!SRpnt->rq) { + kfree(SRpnt); + return NULL; + } + return SRpnt; } static void st_release_request(struct st_request *streq) { + blk_put_request(streq->rq); kfree(streq); } +/* + * setup_kernel_buffering - map a kernel buffer to a request + * @STp: scsi tape + * @len: len of request + * @rw: READ or WRITE + * + * Allocate a request and setup buffering if needed. There is not + * a buffer destructor like with setup_user_buffering, because + * we do not need to transfer and data to userspace. As a result + * when the command has completed only st_release_request needs to + * be called. + */ +static struct st_request *setup_kernel_buffering(struct scsi_tape *STp, + int len, int rw) +{ + struct st_buffer *STbp = STp->buffer; + struct st_request *SRpnt; + + SRpnt = st_allocate_request(STp, rw); + if (!SRpnt) + return ERR_PTR(signal_pending(current) ? -EINTR : -EBUSY); + + if (!len) + goto done; + + memset(STbp->b_data, 0, PAGE_SIZE); + if (blk_rq_map_kern(STbp->bs, SRpnt->rq->q, SRpnt->rq, + STbp->b_data, len, GFP_KERNEL)) { + /* + * This most likely failed due to a allocation failure. + * All users pass len < PAGE_SIZE so that must fit withing + * all driver/queue limits. + */ + st_release_request(SRpnt); + return ERR_PTR(signal_pending(current) ? -EINTR : -EBUSY); + } +done: + return SRpnt; +} + +/** + * destroy_user_buffering - destroy buffering and copy data + * @STbp: tape buffer + * @SRpnt: tape request + * @buf: buffer to transfer to if read + * @len: number of bytes to transfer + * + * If this is a write or if the caller wants to just drop the data then + * NULL and len=0 can be passed in. + * + * If this is a partial transfer the number of bytes copied is returned, + * else zero. + */ +static int destroy_user_buffering(struct st_buffer *STbp, + struct st_request *SRpnt, + char *buf, int len) +{ + struct sg_iovec iov; + int retval = 0; + + if (STbp->do_dio) + blk_rq_destroy_buffer(STbp->bio); + else { + iov.iov_base = buf; + iov.iov_len = len; + + retval = blk_rq_uncopy_user_iov(STbp->bio, &iov, 1); + if (retval > 0) + /* reads do not have to copy all the data at once */ + return retval; + } + + STbp->buffer_bytes = 0; + STbp->read_pointer = 0; + STbp->do_dio = 0; + STbp->bio = NULL; + STbp->current_SRpnt = NULL; + st_release_request(SRpnt); + return 0; +} + /* Do the scsi command. Waits until command performed if do_wait is true. Otherwise write_behind_check() is used to check that the command has finished. */ -static struct st_request * -st_do_scsi(struct st_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd, - int bytes, int direction, int timeout, int retries, int do_wait) +static int +st_do_scsi(struct st_request * SRpnt, struct scsi_tape * STp, + unsigned char *cmd, int timeout, int retries, int do_wait) { struct completion *waiting; + struct request *rq; /* if async, make sure there's no command outstanding */ if (!do_wait && ((STp->buffer)->last_SRpnt)) { printk(KERN_ERR "%s: Async command already active.\n", tape_name(STp)); if (signal_pending(current)) - (STp->buffer)->syscall_result = (-EINTR); + return (-EINTR); else - (STp->buffer)->syscall_result = (-EBUSY); - return NULL; - } - - if (SRpnt == NULL) { - SRpnt = st_allocate_request(); - if (SRpnt == NULL) { - DEBC( printk(KERN_ERR "%s: Can't get SCSI request.\n", - tape_name(STp)); ); - if (signal_pending(current)) - (STp->buffer)->syscall_result = (-EINTR); - else - (STp->buffer)->syscall_result = (-EBUSY); - return NULL; - } - SRpnt->stp = STp; + return (-EBUSY); } + SRpnt->stp = STp; /* If async IO, set last_SRpnt. This ptr tells write_behind_check which IO is outstanding. It's nulled out when the IO completes. */ @@ -501,27 +570,29 @@ st_do_scsi(struct st_request * SRpnt, struct scsi_tape * STp, unsigned char *cmd init_completion(waiting); SRpnt->waiting = waiting; - if (!STp->buffer->do_dio) - buf_to_sg(STp->buffer, bytes); - memcpy(SRpnt->cmd, cmd, sizeof(SRpnt->cmd)); STp->buffer->cmdstat.have_sense = 0; STp->buffer->syscall_result = 0; - if (scsi_execute_async(STp->device, cmd, COMMAND_SIZE(cmd[0]), direction, - &((STp->buffer)->sg[0]), bytes, (STp->buffer)->sg_segs, - timeout, retries, SRpnt, st_sleep_done, GFP_KERNEL)) { - /* could not allocate the buffer or request was too large */ - (STp->buffer)->syscall_result = (-EBUSY); - (STp->buffer)->last_SRpnt = NULL; - } - else if (do_wait) { + rq = SRpnt->rq; + memset(SRpnt->sense, 0, SCSI_SENSE_BUFFERSIZE); + rq->sense = SRpnt->sense; + rq->sense_len = 0; + rq->cmd_len = COMMAND_SIZE(cmd[0]); + memcpy(rq->cmd, cmd, rq->cmd_len); + rq->timeout = timeout; + rq->retries = retries; + rq->cmd_type = REQ_TYPE_BLOCK_PC; + rq->cmd_flags |= REQ_QUIET; + rq->end_io_data = SRpnt; + + blk_execute_rq_nowait(rq->q, NULL, rq, 1, st_sleep_done); + if (do_wait) { wait_for_completion(waiting); SRpnt->waiting = NULL; (STp->buffer)->syscall_result = st_chk_result(STp, SRpnt); } - - return SRpnt; + return 0; } @@ -554,9 +625,8 @@ static int write_behind_check(struct scsi_tape * STp) SRpnt->waiting = NULL; (STp->buffer)->syscall_result = st_chk_result(STp, SRpnt); - st_release_request(SRpnt); + destroy_user_buffering(STbuffer, SRpnt, NULL, 0); - STbuffer->buffer_bytes -= STbuffer->writing; STps = &(STp->ps[STp->partition]); if (STps->drv_block >= 0) { if (STp->block_size == 0) @@ -596,6 +666,7 @@ static int cross_eof(struct scsi_tape * STp, int forward) { struct st_request *SRpnt; unsigned char cmd[MAX_COMMAND_SIZE]; + int retval; cmd[0] = SPACE; cmd[1] = 0x01; /* Space FileMarks */ @@ -609,13 +680,15 @@ static int cross_eof(struct scsi_tape * STp, int forward) DEBC(printk(ST_DEB_MSG "%s: Stepping over filemark %s.\n", tape_name(STp), forward ? "forward" : "backward")); - SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE, - STp->device->timeout, MAX_RETRIES, 1); - if (!SRpnt) - return (STp->buffer)->syscall_result; + SRpnt = setup_kernel_buffering(STp, 0, READ); + if (IS_ERR(SRpnt)) + return PTR_ERR(SRpnt); + retval = st_do_scsi(SRpnt, STp, cmd, STp->device->timeout, + MAX_RETRIES, 1); st_release_request(SRpnt); - SRpnt = NULL; + if (retval) + return retval; if ((STp->buffer)->cmdstat.midlevel_result != 0) printk(KERN_ERR "%s: Stepping over filemark %s failed.\n", @@ -631,7 +704,6 @@ static int flush_write_buffer(struct scsi_tape * STp) int offset, transfer, blks; int result; unsigned char cmd[MAX_COMMAND_SIZE]; - struct st_request *SRpnt; struct st_partstat *STps; result = write_behind_check(STp); @@ -640,15 +712,19 @@ static int flush_write_buffer(struct scsi_tape * STp) result = 0; if (STp->dirty == 1) { + BUG_ON(!STp->buffer->current_SRpnt); - offset = (STp->buffer)->buffer_bytes; + offset = STp->buffer->buffer_bytes; transfer = ((offset + STp->block_size - 1) / STp->block_size) * STp->block_size; DEBC(printk(ST_DEB_MSG "%s: Flushing %d bytes.\n", tape_name(STp), transfer)); - - memset((STp->buffer)->b_data + offset, 0, transfer - offset); - + /* + * mnc: ask Kai what this was for. b_data points to the + * data in one segment. If the transfer spanned two segments + * then we are overwriting who knows what. + memset((STp->buffer)->b_data + offset, 0, transfer - offset) + */ memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = WRITE_6; cmd[1] = 1; @@ -657,10 +733,13 @@ static int flush_write_buffer(struct scsi_tape * STp) cmd[3] = blks >> 8; cmd[4] = blks; - SRpnt = st_do_scsi(NULL, STp, cmd, transfer, DMA_TO_DEVICE, - STp->device->timeout, MAX_WRITE_RETRIES, 1); - if (!SRpnt) - return (STp->buffer)->syscall_result; + result = st_do_scsi(STp->buffer->current_SRpnt, STp, cmd, + STp->device->timeout, MAX_WRITE_RETRIES, 1); + destroy_user_buffering(STp->buffer, STp->buffer->current_SRpnt, + NULL, 0); + (STp->buffer)->current_SRpnt = NULL; + if (result) + return result; STps = &(STp->ps[STp->partition]); if ((STp->buffer)->syscall_result != 0) { @@ -673,7 +752,6 @@ static int flush_write_buffer(struct scsi_tape * STp) (!cmdstatp->remainder_valid || cmdstatp->uremainder64 == 0)) { /* All written at EOM early warning */ STp->dirty = 0; - (STp->buffer)->buffer_bytes = 0; if (STps->drv_block >= 0) STps->drv_block += blks; result = (-ENOSPC); @@ -687,10 +765,7 @@ static int flush_write_buffer(struct scsi_tape * STp) if (STps->drv_block >= 0) STps->drv_block += blks; STp->dirty = 0; - (STp->buffer)->buffer_bytes = 0; } - st_release_request(SRpnt); - SRpnt = NULL; } return result; } @@ -726,6 +801,9 @@ static int flush_buffer(struct scsi_tape *STp, int seek_next) (STp->buffer)->read_pointer) / STp->block_size - ((STp->buffer)->read_pointer + STp->block_size - 1) / STp->block_size; + if ((STp->buffer)->bio) + destroy_user_buffering(STp->buffer, + (STp->buffer)->current_SRpnt, NULL, 0); (STp->buffer)->buffer_bytes = 0; (STp->buffer)->read_pointer = 0; result = 0; @@ -851,15 +929,19 @@ static int test_ready(struct scsi_tape *STp, int do_wait) max_wait = do_wait ? ST_BLOCK_SECONDS : 0; for (attentions=waits=0; ; ) { + SRpnt = setup_kernel_buffering(STp, 0, READ); + if (IS_ERR(SRpnt)) { + retval = PTR_ERR(SRpnt); + break; + } + memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE); cmd[0] = TEST_UNIT_READY; - SRpnt = st_do_scsi(SRpnt, STp, cmd, 0, DMA_NONE, - STp->long_timeout, MAX_READY_RETRIES, 1); - - if (!SRpnt) { - retval = (STp->buffer)->syscall_result; + retval = st_do_scsi(SRpnt, STp, cmd, STp->long_timeout, + MAX_READY_RETRIES, 1); + st_release_request(SRpnt); + if (retval) break; - } if (cmdstatp->have_sense) { @@ -902,9 +984,6 @@ static int test_ready(struct scsi_tape *STp, int do_wait) retval = new_session ? CHKRES_NEW_SESSION : CHKRES_READY; break; } - - if (SRpnt != NULL) - st_release_request(SRpnt); return retval; } @@ -984,17 +1063,23 @@ static int check_tape(struct scsi_tape *STp, struct file *filp) if (STp->omit_blklims) STp->min_block = STp->max_block = (-1); else { + SRpnt= setup_kernel_buffering(STp, 6, READ); + if (IS_ERR(SRpnt)) { + retval = PTR_ERR(SRpnt); + goto err_out; + } + memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE); cmd[0] = READ_BLOCK_LIMITS; - SRpnt = st_do_scsi(SRpnt, STp, cmd, 6, DMA_FROM_DEVICE, - STp->device->timeout, MAX_READY_RETRIES, 1); - if (!SRpnt) { - retval = (STp->buffer)->syscall_result; + retval = st_do_scsi(SRpnt, STp, cmd, STp->device->timeout, + MAX_READY_RETRIES, 1); + st_release_request(SRpnt); + if (retval) goto err_out; - } - if (!SRpnt->result && !STp->buffer->cmdstat.have_sense) { + if (!STp->buffer->cmdstat.midlevel_result && + !STp->buffer->cmdstat.have_sense) { STp->max_block = ((STp->buffer)->b_data[1] << 16) | ((STp->buffer)->b_data[2] << 8) | (STp->buffer)->b_data[3]; STp->min_block = ((STp->buffer)->b_data[4] << 8) | @@ -1010,16 +1095,20 @@ static int check_tape(struct scsi_tape *STp, struct file *filp) } } - memset((void *) &cmd[0], 0, MAX_COMMAND_SIZE); + SRpnt = setup_kernel_buffering(STp, 12, READ); + if (IS_ERR(SRpnt)) { + retval = PTR_ERR(SRpnt); + goto err_out; + } + cmd[0] = MODE_SENSE; cmd[4] = 12; - SRpnt = st_do_scsi(SRpnt, STp, cmd, 12, DMA_FROM_DEVICE, - STp->device->timeout, MAX_READY_RETRIES, 1); - if (!SRpnt) { - retval = (STp->buffer)->syscall_result; + retval = st_do_scsi(SRpnt, STp, cmd, STp->device->timeout, + MAX_READY_RETRIES, 1); + st_release_request(SRpnt); + if (retval) goto err_out; - } if ((STp->buffer)->syscall_result != 0) { DEBC(printk(ST_DEB_MSG "%s: No Mode Sense.\n", name)); @@ -1046,21 +1135,10 @@ static int check_tape(struct scsi_tape *STp, struct file *filp) } STp->drv_write_prot = ((STp->buffer)->b_data[2] & 0x80) != 0; } - st_release_request(SRpnt); - SRpnt = NULL; STp->inited = 1; - if (STp->block_size > 0) - (STp->buffer)->buffer_blocks = - (STp->buffer)->buffer_size / STp->block_size; - else - (STp->buffer)->buffer_blocks = 1; - (STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0; - DEBC(printk(ST_DEB_MSG - "%s: Block size: %d, buffer size: %d (%d blocks).\n", name, - STp->block_size, (STp->buffer)->buffer_size, - (STp->buffer)->buffer_blocks)); + "%s: Block size: %d.\n", name, STp->block_size)); if (STp->drv_write_prot) { STp->write_prot = 1; @@ -1110,8 +1188,7 @@ static int check_tape(struct scsi_tape *STp, struct file *filp) return retval; } - - /* Open the device. Needs to be called with BKL only because of incrementing the SCSI host +/* Open the device. Needs to be called with BKL only because of incrementing the SCSI host module count. */ static int st_open(struct inode *inode, struct file *filp) { @@ -1151,14 +1228,6 @@ static int st_open(struct inode *inode, struct file *filp) goto err_out; } - /* See that we have at least a one page buffer available */ - if (!enlarge_buffer(STp->buffer, PAGE_SIZE, STp->restr_dma)) { - printk(KERN_WARNING "%s: Can't allocate one page tape buffer.\n", - name); - retval = (-EOVERFLOW); - goto err_out; - } - (STp->buffer)->writing = 0; (STp->buffer)->syscall_result = 0; @@ -1188,13 +1257,11 @@ static int st_open(struct inode *inode, struct file *filp) return 0; err_out: - normalize_buffer(STp->buffer); STp->in_use = 0; scsi_tape_put(STp); return retval; } - /* Flush the tape buffer before close */ static int st_flush(struct file *filp, fl_owner_t id) @@ -1240,13 +1307,17 @@ static int st_flush(struct file *filp, fl_owner_t id) cmd[0] = WRITE_FILEMARKS; cmd[4] = 1 + STp->two_fm; - SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE, - STp->device->timeout, MAX_WRITE_RETRIES, 1); - if (!SRpnt) { - result = (STp->buffer)->syscall_result; + SRpnt = setup_kernel_buffering(STp, 0, READ); + if (IS_ERR(SRpnt)) { + result = PTR_ERR(SRpnt); goto out; } + result = st_do_scsi(SRpnt, STp, cmd, STp->device->timeout, + MAX_WRITE_RETRIES, 1); + if (result) + goto out; + if (STp->buffer->syscall_result == 0 || (cmdstatp->have_sense && !cmdstatp->deferred && (cmdstatp->flags & SENSE_EOM) && @@ -1318,7 +1389,6 @@ static int st_release(struct inode *inode, struct file *filp) if (STp->door_locked == ST_LOCKED_AUTO) do_door_lock(STp, 0); - normalize_buffer(STp->buffer); write_lock(&st_dev_arr_lock); STp->in_use = 0; write_unlock(&st_dev_arr_lock); @@ -1394,90 +1464,156 @@ static ssize_t rw_checks(struct scsi_tape *STp, struct file *filp, size_t count) return retval; } +static int check_buffer_readability(char __user *buf, size_t count, int rw) +{ + int retval = 0, i; -static int setup_buffering(struct scsi_tape *STp, const char __user *buf, - size_t count, int is_read) + if (rw == READ) { + if (copy_from_user(&i, buf, 1) != 0 || + copy_to_user(buf, &i, 1) != 0 || + copy_from_user(&i, buf + count - 1, 1) != 0 || + copy_to_user(buf + count - 1, &i, 1) != 0) + retval = -EFAULT; + } else { + if ((copy_from_user(&i, buf, 1) != 0 || + copy_from_user(&i, buf + count - 1, 1) != 0)) + retval = (-EFAULT); + } + return retval; +} + +/* + * setup_user_buffering - map or copy buf to request buffer + * @STp: scsi tape + * @buf: user buffer to copy from/to + * @count: number of bytes to copy + * @rw: READ or WRITE + * @bytes_setup: number of bytes added to buffer + * + * setup_user_buffering will map or copy data as requested. If there was + * leftover bytes to write from a previous st_write call, then we add + * on to the previous buffer. + * + * When the command is completed destroy_user_buffering should be called. + */ +static struct st_request * +setup_user_buffering(struct scsi_tape *STp, char __user *buf, + size_t count, int rw, size_t *bytes_setup) { - int i, bufsize, retval = 0; + int do_dio, retval = 0; struct st_buffer *STbp = STp->buffer; + struct st_request *SRpnt; + struct sg_iovec iov; + size_t bufsize; + + *bytes_setup = 0; + /* + * if the last write was a fixed block write and it was not + * full enough (number of bytes to write was not equal to + * a whole block or st_fixed_buffer_size) then there will + * be a request that we just want to copy more data into + */ + if (STbp->current_SRpnt) { + SRpnt = STbp->current_SRpnt; + goto copy_buf; + } - if (is_read) - i = STp->try_dio_now && try_rdio; + SRpnt = st_allocate_request(STp, rw); + if (!SRpnt) + return ERR_PTR(signal_pending(current) ? -EINTR : -EBUSY); + + if (rw == READ) + do_dio = STp->try_dio_now && try_rdio; else - i = STp->try_dio_now && try_wdio; - - if (i && ((unsigned long)buf & queue_dma_alignment( - STp->device->request_queue)) == 0) { - i = sgl_map_user_pages(&(STbp->sg[0]), STbp->use_sg, - (unsigned long)buf, count, (is_read ? READ : WRITE)); - if (i > 0) { - STbp->do_dio = i; - STbp->buffer_bytes = 0; /* can be used as transfer counter */ + do_dio = STp->try_dio_now && try_wdio; + if (do_dio) { + retval = blk_rq_setup_buffer(STbp->bs, SRpnt->rq, buf, + count, GFP_KERNEL); + if (!retval) { + if (rw == READ) { + retval = check_buffer_readability(buf, count, + rw); + if (retval) { + blk_rq_complete_transfer(SRpnt->rq->bio, + NULL, 0); + goto free_srpnt; + } + } + /* mnc - TODO: add request and bio loop to count + * nbr_combinable and nbr_pages, or maybe we can + * just add that to the block layer stats since + * it would then be exposed for all drivers. + */ + DEB(STp->nbr_dio++); + STbp->do_dio = 1; + STbp->bio = SRpnt->rq->bio; + *bytes_setup = count; + return SRpnt; } - else - STbp->do_dio = 0; /* fall back to buffering with any error */ - STbp->sg_segs = STbp->do_dio; - STbp->frp_sg_current = 0; - DEB( - if (STbp->do_dio) { - STp->nbr_dio++; - STp->nbr_pages += STbp->do_dio; - for (i=1; i < STbp->do_dio; i++) - if (page_to_pfn(STbp->sg[i].page) == page_to_pfn(STbp->sg[i-1].page) + 1) - STp->nbr_combinable++; - } - ) - } else - STbp->do_dio = 0; - DEB( STp->nbr_requests++; ) + /* Could not do dio, so fall back to copy */ + } + STbp->do_dio = 0; - if (!STbp->do_dio) { - if (STp->block_size) - bufsize = STp->block_size > st_fixed_buffer_size ? +copy_buf: + DEB( STp->nbr_requests++; ) + memset(&iov, 0, sizeof(iov)); + if (STp->block_size) { + bufsize = STp->block_size > st_fixed_buffer_size ? STp->block_size : st_fixed_buffer_size; - else - bufsize = count; - if (bufsize > STbp->buffer_size && - !enlarge_buffer(STbp, bufsize, STp->restr_dma)) { - printk(KERN_WARNING "%s: Can't allocate %d byte tape buffer.\n", - tape_name(STp), bufsize); - retval = (-EOVERFLOW); - goto out; + /* if we are appending to an exising buffer + * then we already have some data in the buffer */ + bufsize -= SRpnt->rq->data_len; + /* + * for writes the count is going to be a multiple of + * block_size, but for reads we can do arbitrary + * counts and read from the internal buffer. + */ + if (rw == WRITE) { + bufsize = min(bufsize, count); + retval = check_buffer_readability(buf, bufsize, rw); + if (retval) + goto free_srpnt; } - if (STp->block_size) - STbp->buffer_blocks = bufsize / STp->block_size; - } - - out: - return retval; -} + } else + bufsize = count; + iov.iov_base = buf; + iov.iov_len = bufsize; + /* + * if this is called on a previously setup request it just adds + * on to it. + */ + retval = blk_rq_copy_user_iov(STbp->bs, SRpnt->rq, &iov, 1, + bufsize, GFP_KERNEL); + if (retval) { + printk(KERN_WARNING "%s: Cannot setup tape buffer of size " + "%lu. Error %d.\n", tape_name(STp), bufsize, retval); + /* probably could not allocate buffer or request was too large */ + retval = -EBUSY; + goto free_srpnt; + } -/* Can be called more than once after each setup_buffer() */ -static void release_buffering(struct scsi_tape *STp, int is_read) -{ - struct st_buffer *STbp; + STbp->bio = SRpnt->rq->bio; + *bytes_setup = bufsize; + return SRpnt; - STbp = STp->buffer; - if (STbp->do_dio) { - sgl_unmap_user_pages(&(STbp->sg[0]), STbp->do_dio, is_read); - STbp->do_dio = 0; - STbp->sg_segs = 0; - } +free_srpnt: + if (!STbp->current_SRpnt) + st_release_request(SRpnt); + return ERR_PTR(retval); } - /* Write command */ static ssize_t st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) { ssize_t total; ssize_t i, do_count, blks, transfer; - ssize_t retval; + ssize_t retval, bytes_setup; int undone, retry_eot = 0, scode; int async_write; unsigned char cmd[MAX_COMMAND_SIZE]; - const char __user *b_point; + char __user *b_point; struct st_request *SRpnt = NULL; struct scsi_tape *STp = filp->private_data; struct st_modedef *STm; @@ -1550,20 +1686,6 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) goto out; } - /* Check the buffer readability in cases where copy_user might catch - the problems after some tape movement. */ - if (STp->block_size != 0 && - !STbp->do_dio && - (copy_from_user(&i, buf, 1) != 0 || - copy_from_user(&i, buf + count - 1, 1) != 0)) { - retval = (-EFAULT); - goto out; - } - - retval = setup_buffering(STp, buf, count, 0); - if (retval) - goto out; - total = count; memset(cmd, 0, MAX_COMMAND_SIZE); @@ -1572,28 +1694,13 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) STps->rw = ST_WRITING; - b_point = buf; + b_point = (char __user *)buf; while (count > 0 && !retry_eot) { - - if (STbp->do_dio) { - do_count = count; - } - else { - if (STp->block_size == 0) - do_count = count; - else { - do_count = STbp->buffer_blocks * STp->block_size - - STbp->buffer_bytes; - if (do_count > count) - do_count = count; - } - - i = append_to_buffer(b_point, STbp, do_count); - if (i) { - retval = i; - goto out; - } - } + SRpnt = setup_user_buffering(STp, b_point, count, WRITE, + &bytes_setup); + if (IS_ERR(SRpnt)) + break; + do_count = bytes_setup; count -= do_count; b_point += do_count; @@ -1602,42 +1709,42 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) if (STp->block_size != 0 && STm->do_buffer_writes && !(STp->try_dio_now && try_wdio) && STps->eof < ST_EOM_OK && - STbp->buffer_bytes < STbp->buffer_size) { + (SRpnt->rq->data_len < + max(STp->block_size, st_fixed_buffer_size))) { STp->dirty = 1; /* Don't write a buffer that is not full enough. */ - if (!async_write && count == 0) + if (!async_write && count == 0) { + if (!STbp->do_dio) + STbp->buffer_bytes = + SRpnt->rq->data_len; + STbp->current_SRpnt = SRpnt; break; + } } - retry_write: - if (STp->block_size == 0) - blks = transfer = do_count; - else { - if (!STbp->do_dio) - blks = STbp->buffer_bytes; - else - blks = do_count; - blks /= STp->block_size; - transfer = blks * STp->block_size; - } +retry_write: + blks = transfer = SRpnt->rq->data_len; + if (STp->block_size != 0) + blks = transfer / STp->block_size; cmd[2] = blks >> 16; cmd[3] = blks >> 8; cmd[4] = blks; - SRpnt = st_do_scsi(SRpnt, STp, cmd, transfer, DMA_TO_DEVICE, - STp->device->timeout, MAX_WRITE_RETRIES, !async_write); - if (!SRpnt) { - retval = STbp->syscall_result; + retval = st_do_scsi(SRpnt, STp, cmd, STp->device->timeout, + MAX_WRITE_RETRIES, !async_write); + if (retval) { + destroy_user_buffering(STbp, SRpnt, NULL, 0); goto out; } + if (async_write && !STbp->syscall_result) { STbp->writing = transfer; - STp->dirty = !(STbp->writing == - STbp->buffer_bytes); + STp->dirty = 0; SRpnt = NULL; /* Prevent releasing this request! */ DEB( STp->write_pending = 1; ) break; } + destroy_user_buffering(STbp, SRpnt, NULL, 0); if (STbp->syscall_result != 0) { struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat; @@ -1656,6 +1763,13 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) undone *= STp->block_size; if (undone <= do_count) { /* Only data from this write is not written */ + /* mnc - ask kai if this should + * move the b_point back to account + * for $count getting bytes added + * back. If we don't when we loop back + * around b_point was already + * incremented by do_count bytes and + * we could copy past the buffer */ count += undone; do_count -= undone; if (STp->block_size) @@ -1676,7 +1790,19 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) in fixed block mode without direct i/o) */ if (!retry_eot && !cmdstatp->deferred && (scode == NO_SENSE || scode == RECOVERED_ERROR)) { - move_buffer_data(STp->buffer, transfer - undone); + /* b_point got incremented + * when we made the bufer and + * copied data, so move it back + * to where this command left + * off */ + b_point -= undone; + SRpnt = setup_user_buffering(STp, + b_point, + undone, + WRITE, + &bytes_setup); + if (IS_ERR(SRpnt)) + break; retry_eot = 1; if (STps->drv_block >= 0) { STps->drv_block += (transfer - undone) / @@ -1685,7 +1811,7 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) STps->eof = ST_EOM_OK; DEBC(printk(ST_DEB_MSG "%s: Retry write of %d bytes at EOM.\n", - name, STp->buffer->buffer_bytes)); + name, SRpnt->rq->data_len)); goto retry_write; } else { @@ -1706,7 +1832,6 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) STps->drv_block = (-1); /* Too cautious? */ retval = STbp->syscall_result; } - } if (STps->drv_block >= 0) { @@ -1716,7 +1841,6 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) STps->drv_block += blks; } - STbp->buffer_bytes = 0; STp->dirty = 0; if (retval || retry_eot) { @@ -1733,9 +1857,6 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) retval = total - count; out: - if (SRpnt != NULL) - st_release_request(SRpnt); - release_buffering(STp, 0); mutex_unlock(&STp->lock); return retval; @@ -1746,41 +1867,31 @@ st_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) fatal error. Otherwise updates the buffer and the eof state. Does release user buffer mapping if it is set. + + Returns less than zero on error, and the number of bytes read on + success or zero in special cases; */ -static long read_tape(struct scsi_tape *STp, long count, - struct st_request ** aSRpnt) +static long read_tape(struct scsi_tape *STp, struct st_request *SRpnt) { - int transfer, blks, bytes; + int transfer, blks, bytes, bytes_read = 0; unsigned char cmd[MAX_COMMAND_SIZE]; - struct st_request *SRpnt; struct st_modedef *STm; struct st_partstat *STps; struct st_buffer *STbp; int retval = 0; char *name = tape_name(STp); - if (count == 0) - return 0; - STm = &(STp->modes[STp->current_mode]); STps = &(STp->ps[STp->partition]); if (STps->eof == ST_FM_HIT) - return 1; + return 0; STbp = STp->buffer; if (STp->block_size == 0) - blks = bytes = count; + blks = bytes = SRpnt->rq->data_len; else { - if (!(STp->try_dio_now && try_rdio) && STm->do_read_ahead) { - blks = (STp->buffer)->buffer_blocks; - bytes = blks * STp->block_size; - } else { - bytes = count; - if (!STbp->do_dio && bytes > (STp->buffer)->buffer_size) - bytes = (STp->buffer)->buffer_size; - blks = bytes / STp->block_size; - bytes = blks * STp->block_size; - } + blks = SRpnt->rq->data_len / STp->block_size; + bytes = SRpnt->rq->data_len; } memset(cmd, 0, MAX_COMMAND_SIZE); @@ -1790,17 +1901,13 @@ static long read_tape(struct scsi_tape *STp, long count, cmd[3] = blks >> 8; cmd[4] = blks; - SRpnt = *aSRpnt; - SRpnt = st_do_scsi(SRpnt, STp, cmd, bytes, DMA_FROM_DEVICE, - STp->device->timeout, MAX_RETRIES, 1); - release_buffering(STp, 1); - *aSRpnt = SRpnt; - if (!SRpnt) - return STbp->syscall_result; - - STbp->read_pointer = 0; + retval = st_do_scsi(SRpnt, STp, cmd, STp->device->timeout, + MAX_RETRIES, 1); + if (retval) + goto done; STps->at_sm = 0; + bytes_read = bytes; /* Something to check */ if (STbp->syscall_result) { struct st_cmdstatus *cmdstatp = &STp->buffer->cmdstat; @@ -1836,30 +1943,34 @@ static long read_tape(struct scsi_tape *STp, long count, name, bytes - transfer, bytes); if (STps->drv_block >= 0) STps->drv_block += 1; - STbp->buffer_bytes = 0; - return (-ENOMEM); + retval = (-ENOMEM); + goto done; } - STbp->buffer_bytes = bytes - transfer; + bytes_read = bytes - transfer; } else { - st_release_request(SRpnt); - SRpnt = *aSRpnt = NULL; if (transfer == blks) { /* We did not get anything, error */ printk(KERN_NOTICE "%s: Incorrect block size.\n", name); if (STps->drv_block >= 0) STps->drv_block += blks - transfer + 1; st_int_ioctl(STp, MTBSR, 1); - return (-EIO); + retval =(-EIO); + goto done; } /* We have some data, deliver it */ - STbp->buffer_bytes = (blks - transfer) * + bytes_read = (blks - transfer) * STp->block_size; DEBC(printk(ST_DEB_MSG "%s: ILI but enough data received %ld %d.\n", - name, count, STbp->buffer_bytes)); + name, count, bytes_read)); if (STps->drv_block >= 0) STps->drv_block += 1; if (st_int_ioctl(STp, MTBSR, 1)) - return (-EIO); + /* mnc - ask Kai if this is right. If we return + * EIO here, then then data that got read in will + * only get transferred on the next st_read call + */ + retval = (-EIO); + goto done; } } else if (cmdstatp->flags & SENSE_FMK) { /* FM overrides EOM */ if (STps->eof != ST_FM_HIT) @@ -1867,26 +1978,26 @@ static long read_tape(struct scsi_tape *STp, long count, else STps->eof = ST_EOD_2; if (STp->block_size == 0) - STbp->buffer_bytes = 0; + bytes_read = 0; else - STbp->buffer_bytes = + bytes_read = bytes - transfer * STp->block_size; DEBC(printk(ST_DEB_MSG "%s: EOF detected (%d bytes read).\n", - name, STbp->buffer_bytes)); + name, bytes_read)); } else if (cmdstatp->flags & SENSE_EOM) { if (STps->eof == ST_FM) STps->eof = ST_EOD_1; else STps->eof = ST_EOM_OK; if (STp->block_size == 0) - STbp->buffer_bytes = bytes - transfer; + bytes_read = bytes - transfer; else - STbp->buffer_bytes = + bytes_read = bytes - transfer * STp->block_size; DEBC(printk(ST_DEB_MSG "%s: EOM detected (%d bytes read).\n", - name, STbp->buffer_bytes)); + name, bytes_read)); } } /* end of EOF, EOM, ILI test */ @@ -1904,8 +2015,8 @@ static long read_tape(struct scsi_tape *STp, long count, retval = (-EIO); } - if (STbp->buffer_bytes < 0) /* Caused by bogus sense data */ - STbp->buffer_bytes = 0; + if (bytes_read < 0) /* Caused by bogus sense data */ + bytes_read = 0; } /* End of extended sense test */ else { /* Non-extended sense */ @@ -1914,18 +2025,20 @@ static long read_tape(struct scsi_tape *STp, long count, } /* End of error handling */ - else /* Read successful */ - STbp->buffer_bytes = bytes; if (STps->drv_block >= 0) { if (STp->block_size == 0) STps->drv_block++; else - STps->drv_block += STbp->buffer_bytes / STp->block_size; + STps->drv_block += bytes_read / STp->block_size; } - return retval; + +done: + if (retval < 0) + return retval; + else + return bytes_read; } - /* Read command */ static ssize_t @@ -1933,13 +2046,13 @@ st_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) { ssize_t total; ssize_t retval = 0; - ssize_t i, transfer; - int special, do_dio = 0; - struct st_request *SRpnt = NULL; + ssize_t transfer, bytes_setup; + int special; struct scsi_tape *STp = filp->private_data; struct st_modedef *STm; struct st_partstat *STps; struct st_buffer *STbp = STp->buffer; + struct st_request *SRpnt; DEB( char *name = tape_name(STp); ) if (mutex_lock_interruptible(&STp->lock)) @@ -1971,11 +2084,6 @@ st_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) STps->eof, STbp->buffer_bytes); ) /* end DEB */ - retval = setup_buffering(STp, buf, count, 1); - if (retval) - goto out; - do_dio = STbp->do_dio; - if (STbp->buffer_bytes == 0 && STps->eof >= ST_EOD_1) { if (STps->eof < ST_EOD) { @@ -1986,52 +2094,42 @@ st_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) retval = (-EIO); /* EOM or Blank Check */ goto out; } - - if (do_dio) { - /* Check the buffer writability before any tape movement. Don't alter - buffer data. */ - if (copy_from_user(&i, buf, 1) != 0 || - copy_to_user(buf, &i, 1) != 0 || - copy_from_user(&i, buf + count - 1, 1) != 0 || - copy_to_user(buf + count - 1, &i, 1) != 0) { - retval = (-EFAULT); - goto out; - } - } - STps->rw = ST_READING; - /* Loop until enough data in buffer or a special condition found */ for (total = 0, special = 0; total < count && !special;) { - - /* Get new data if the buffer is empty */ - if (STbp->buffer_bytes == 0) { - special = read_tape(STp, count - total, &SRpnt); - if (special < 0) { /* No need to continue read */ + SRpnt = STbp->current_SRpnt; + if (!SRpnt) { + /* Get new data if the buffer is empty */ + SRpnt = setup_user_buffering(STp, buf, total, READ, + &bytes_setup); + if (IS_ERR(SRpnt)) { + retval = PTR_ERR(SRpnt); + goto out; + } + STbp->current_SRpnt = SRpnt; + special = read_tape(STp, SRpnt); + if (special < 0) { /* No need to continue read */ + destroy_user_buffering(STbp, SRpnt, NULL, 0); retval = special; goto out; } + STbp->buffer_bytes = special; } /* Move the data from driver buffer to user buffer */ if (STbp->buffer_bytes > 0) { - DEB( - if (debugging && STps->eof != ST_NOEOF) - printk(ST_DEB_MSG - "%s: EOF up (%d). Left %d, needed %d.\n", name, - STps->eof, STbp->buffer_bytes, - (int)(count - total)); - ) /* end DEB */ transfer = STbp->buffer_bytes < count - total ? - STbp->buffer_bytes : count - total; - if (!do_dio) { - i = from_buffer(STbp, buf, transfer); - if (i) { - retval = i; - goto out; - } + STbp->buffer_bytes : count - total; + retval = destroy_user_buffering(STbp, SRpnt, buf, + transfer); + if (retval < 0) + goto out; + if (retval > 0) { + STbp->buffer_bytes -= retval; + STbp->read_pointer += retval; } + buf += transfer; total += transfer; } @@ -2061,14 +2159,6 @@ st_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) retval = total; out: - if (SRpnt != NULL) { - st_release_request(SRpnt); - SRpnt = NULL; - } - if (do_dio) { - release_buffering(STp, 1); - STbp->buffer_bytes = 0; - } mutex_unlock(&STp->lock); return retval; @@ -2293,6 +2383,7 @@ static int read_mode_page(struct scsi_tape *STp, int page, int omit_block_descs) { unsigned char cmd[MAX_COMMAND_SIZE]; struct st_request *SRpnt = NULL; + int retval; memset(cmd, 0, MAX_COMMAND_SIZE); cmd[0] = MODE_SENSE; @@ -2301,13 +2392,14 @@ static int read_mode_page(struct scsi_tape *STp, int page, int omit_block_descs) cmd[2] = page; cmd[4] = 255; - SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_FROM_DEVICE, - STp->device->timeout, 0, 1); - if (SRpnt == NULL) - return (STp->buffer)->syscall_result; + SRpnt = setup_kernel_buffering(STp, cmd[4], READ); + if (IS_ERR(SRpnt)) + return PTR_ERR(SRpnt); + retval = st_do_scsi(SRpnt, STp, cmd, STp->device->timeout, 0, 1); st_release_request(SRpnt); - + if (retval) + return retval; return (STp->buffer)->syscall_result; } @@ -2316,7 +2408,7 @@ static int read_mode_page(struct scsi_tape *STp, int page, int omit_block_descs) in the buffer is correctly formatted. The long timeout is used if slow is non-zero. */ static int write_mode_page(struct scsi_tape *STp, int page, int slow) { - int pgo; + int pgo, retval; unsigned char cmd[MAX_COMMAND_SIZE]; struct st_request *SRpnt = NULL; @@ -2326,19 +2418,21 @@ static int write_mode_page(struct scsi_tape *STp, int page, int slow) pgo = MODE_HEADER_LENGTH + (STp->buffer)->b_data[MH_OFF_BDESCS_LENGTH]; cmd[4] = pgo + (STp->buffer)->b_data[pgo + MP_OFF_PAGE_LENGTH] + 2; + SRpnt = setup_kernel_buffering(STp, cmd[4], WRITE); + if (IS_ERR(SRpnt)) + return PTR_ERR(SRpnt); + /* Clear reserved fields */ (STp->buffer)->b_data[MH_OFF_DATA_LENGTH] = 0; (STp->buffer)->b_data[MH_OFF_MEDIUM_TYPE] = 0; (STp->buffer)->b_data[MH_OFF_DEV_SPECIFIC] &= ~MH_BIT_WP; (STp->buffer)->b_data[pgo + MP_OFF_PAGE_NBR] &= MP_MSK_PAGE_NBR; - SRpnt = st_do_scsi(SRpnt, STp, cmd, cmd[4], DMA_TO_DEVICE, - (slow ? STp->long_timeout : STp->device->timeout), 0, 1); - if (SRpnt == NULL) - return (STp->buffer)->syscall_result; - + retval = st_do_scsi(SRpnt, STp, cmd, + (slow ? STp->long_timeout : STp->device->timeout), 0, 1); st_release_request(SRpnt); - + if (retval) + return retval; return (STp->buffer)->syscall_result; } @@ -2457,14 +2551,16 @@ static int do_load_unload(struct scsi_tape *STp, struct file *filp, int load_cod printk(ST_DEB_MSG "%s: Loading tape.\n", name); ); - SRpnt = st_do_scsi(NULL, STp, cmd, 0, DMA_NONE, - timeout, MAX_RETRIES, 1); - if (!SRpnt) - return (STp->buffer)->syscall_result; + SRpnt = setup_kernel_buffering(STp, 0, READ); + if (IS_ERR(SRpnt)) + return PTR_ERR(SRpnt); - retval = (STp->buffer)->syscall_result; + retval = st_do_scsi(SRpnt, STp, cmd, timeout, MAX_RETRIES, 1); st_release_request(SRpnt); + if (retval) + return retval; + retval = (STp->buffer)->syscall_result; if (!retval) { /* SCSI command successful */ if (!load_code) { @@ -2514,7 +2610,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon struct st_request *SRpnt; struct st_partstat *STps; int fileno, blkno, at_sm, undone; - int datalen = 0, direction = DMA_NONE; + int datalen = 0, direction = DMA_NONE, retval; char *name = tape_name(STp); WARN_ON(STp->buffer->do_dio != 0); @@ -2696,7 +2792,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon case MTSETDRVBUFFER: /* Set drive buffering */ case SET_DENS_AND_BLK: /* Set density and block size */ chg_eof = 0; - if (STp->dirty || (STp->buffer)->buffer_bytes != 0) + if (STp->dirty || (STp->buffer->bio && !STp->buffer->do_dio)) return (-EIO); /* Not allowed if data in buffer */ if ((cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) && (arg & MT_ST_BLKSIZE_MASK) != 0 && @@ -2757,13 +2853,18 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon return (-ENOSYS); } - SRpnt = st_do_scsi(NULL, STp, cmd, datalen, direction, - timeout, MAX_RETRIES, 1); - if (!SRpnt) - return (STp->buffer)->syscall_result; + SRpnt = setup_kernel_buffering(STp, datalen, + direction == DMA_TO_DEVICE); + if (IS_ERR(SRpnt)) + return PTR_ERR(SRpnt); - ioctl_result = (STp->buffer)->syscall_result; + retval = st_do_scsi(SRpnt, STp, cmd, timeout, MAX_RETRIES, 1); + if (retval) { + st_release_request(SRpnt); + return retval; + } + ioctl_result = (STp->buffer)->syscall_result; if (!ioctl_result) { /* SCSI command successful */ st_release_request(SRpnt); SRpnt = NULL; @@ -2777,14 +2878,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon ioctl_result = st_int_ioctl(STp, MTBSF, 1); if (cmd_in == MTSETBLK || cmd_in == SET_DENS_AND_BLK) { - int old_block_size = STp->block_size; STp->block_size = arg & MT_ST_BLKSIZE_MASK; - if (STp->block_size != 0) { - if (old_block_size == 0) - normalize_buffer(STp->buffer); - (STp->buffer)->buffer_blocks = - (STp->buffer)->buffer_size / STp->block_size; - } (STp->buffer)->buffer_bytes = (STp->buffer)->read_pointer = 0; if (cmd_in == SET_DENS_AND_BLK) STp->density = arg >> MT_ST_DENSITY_SHIFT; @@ -2924,10 +3018,15 @@ static int get_location(struct scsi_tape *STp, unsigned int *block, int *partiti if (!logical && !STp->scsi2_logical) scmd[1] = 1; } - SRpnt = st_do_scsi(NULL, STp, scmd, 20, DMA_FROM_DEVICE, - STp->device->timeout, MAX_READY_RETRIES, 1); - if (!SRpnt) - return (STp->buffer)->syscall_result; + + SRpnt = setup_kernel_buffering(STp, 20, READ); + if (IS_ERR(SRpnt)) + return PTR_ERR(SRpnt); + + result = st_do_scsi(SRpnt, STp, scmd, STp->device->timeout, + MAX_READY_RETRIES, 1); + if (result) + return result; if ((STp->buffer)->syscall_result != 0 || (STp->device->scsi_level >= SCSI_2 && @@ -3029,10 +3128,14 @@ static int set_location(struct scsi_tape *STp, unsigned int block, int partition timeout = STp->device->timeout; } - SRpnt = st_do_scsi(NULL, STp, scmd, 0, DMA_NONE, - timeout, MAX_READY_RETRIES, 1); - if (!SRpnt) - return (STp->buffer)->syscall_result; + SRpnt = setup_kernel_buffering(STp, 0, READ); + if (IS_ERR(SRpnt)) + return PTR_ERR(SRpnt); + + result = st_do_scsi(SRpnt, STp, scmd, timeout, MAX_READY_RETRIES, 1); + st_release_request(SRpnt); + if (result) + return result; STps->drv_block = STps->drv_file = (-1); STps->eof = ST_NOEOF; @@ -3057,10 +3160,6 @@ static int set_location(struct scsi_tape *STp, unsigned int block, int partition STps->drv_block = STps->drv_file = 0; result = 0; } - - st_release_request(SRpnt); - SRpnt = NULL; - return result; } @@ -3582,232 +3681,58 @@ static long st_compat_ioctl(struct file *file, unsigned int cmd, unsigned long a } #endif - - /* Try to allocate a new tape buffer. Calling function must not hold dev_arr_lock. */ static struct st_buffer * - new_tape_buffer(int from_initialization, int need_dma, int max_sg) +new_tape_buffer(struct scsi_tape *STp) { - int i, got = 0; - gfp_t priority; struct st_buffer *tb; - if (from_initialization) - priority = GFP_ATOMIC; - else - priority = GFP_KERNEL; - - i = sizeof(struct st_buffer) + (max_sg - 1) * sizeof(struct scatterlist) + - max_sg * sizeof(struct st_buf_fragment); - tb = kzalloc(i, priority); + tb = kzalloc(sizeof(*tb), GFP_KERNEL); if (!tb) { printk(KERN_NOTICE "st: Can't allocate new tape buffer.\n"); return NULL; } - tb->frp_segs = tb->orig_frp_segs = 0; - tb->use_sg = max_sg; - tb->frp = (struct st_buf_fragment *)(&(tb->sg[0]) + max_sg); - - tb->dma = need_dma; - tb->buffer_size = got; - - return tb; -} - - -/* Try to allocate enough space in the tape buffer */ -static int enlarge_buffer(struct st_buffer * STbuffer, int new_size, int need_dma) -{ - int segs, nbr, max_segs, b_size, order, got; - gfp_t priority; - - if (new_size <= STbuffer->buffer_size) - return 1; - - if (STbuffer->buffer_size <= PAGE_SIZE) - normalize_buffer(STbuffer); /* Avoid extra segment */ - - max_segs = STbuffer->use_sg; - nbr = max_segs - STbuffer->frp_segs; - if (nbr <= 0) - return 0; - - priority = GFP_KERNEL | __GFP_NOWARN; - if (need_dma) - priority |= GFP_DMA; - for (b_size = PAGE_SIZE, order=0; order <= 6 && - b_size < new_size - STbuffer->buffer_size; - order++, b_size *= 2) - ; /* empty */ - - for (segs = STbuffer->frp_segs, got = STbuffer->buffer_size; - segs < max_segs && got < new_size;) { - STbuffer->frp[segs].page = alloc_pages(priority, order); - if (STbuffer->frp[segs].page == NULL) { - if (new_size - got <= (max_segs - segs) * b_size / 2) { - b_size /= 2; /* Large enough for the rest of the buffers */ - order--; - continue; - } - DEB(STbuffer->buffer_size = got); - normalize_buffer(STbuffer); - return 0; - } - STbuffer->frp[segs].length = b_size; - STbuffer->frp_segs += 1; - got += b_size; - STbuffer->buffer_size = got; - segs++; - } - STbuffer->b_data = page_address(STbuffer->frp[0].page); - - return 1; -} - -/* Release the extra buffer */ -static void normalize_buffer(struct st_buffer * STbuffer) -{ - int i, order; - - for (i = STbuffer->orig_frp_segs; i < STbuffer->frp_segs; i++) { - order = get_order(STbuffer->frp[i].length); - __free_pages(STbuffer->frp[i].page, order); - STbuffer->buffer_size -= STbuffer->frp[i].length; - } - STbuffer->frp_segs = STbuffer->orig_frp_segs; - STbuffer->frp_sg_current = 0; - STbuffer->sg_segs = 0; -} - - -/* Move data from the user buffer to the tape buffer. Returns zero (success) or - negative error code. */ -static int append_to_buffer(const char __user *ubp, struct st_buffer * st_bp, int do_count) -{ - int i, cnt, res, offset; - - for (i = 0, offset = st_bp->buffer_bytes; - i < st_bp->frp_segs && offset >= st_bp->frp[i].length; i++) - offset -= st_bp->frp[i].length; - if (i == st_bp->frp_segs) { /* Should never happen */ - printk(KERN_WARNING "st: append_to_buffer offset overflow.\n"); - return (-EIO); + /* + * This page is used for cmds with no user buffer that originate + * from the kernel. + */ + tb->page = alloc_page(GFP_KERNEL | + STp->device->request_queue->bounce_gfp); + if (!tb->page) { + printk(KERN_WARNING "st: Can't allocate one page tape buffer.\n"); + goto free_buffer; } - for (; i < st_bp->frp_segs && do_count > 0; i++) { - cnt = st_bp->frp[i].length - offset < do_count ? - st_bp->frp[i].length - offset : do_count; - res = copy_from_user(page_address(st_bp->frp[i].page) + offset, ubp, cnt); - if (res) - return (-EFAULT); - do_count -= cnt; - st_bp->buffer_bytes += cnt; - ubp += cnt; - offset = 0; + tb->b_data = page_address(tb->page); + + /* See that we have at least a one segment available */ + tb->bs = bioset_pagepool_create(1, 1, 1, + get_order(STp->device->request_queue->max_segment_size)); + if (!tb->bs) { + printk(KERN_WARNING "st: Can't allocate page reserves.\n"); + goto free_page; } - if (do_count) /* Should never happen */ - return (-EIO); - - return 0; -} - - -/* Move data from the tape buffer to the user buffer. Returns zero (success) or - negative error code. */ -static int from_buffer(struct st_buffer * st_bp, char __user *ubp, int do_count) -{ - int i, cnt, res, offset; - for (i = 0, offset = st_bp->read_pointer; - i < st_bp->frp_segs && offset >= st_bp->frp[i].length; i++) - offset -= st_bp->frp[i].length; - if (i == st_bp->frp_segs) { /* Should never happen */ - printk(KERN_WARNING "st: from_buffer offset overflow.\n"); - return (-EIO); - } - for (; i < st_bp->frp_segs && do_count > 0; i++) { - cnt = st_bp->frp[i].length - offset < do_count ? - st_bp->frp[i].length - offset : do_count; - res = copy_to_user(ubp, page_address(st_bp->frp[i].page) + offset, cnt); - if (res) - return (-EFAULT); - do_count -= cnt; - st_bp->buffer_bytes -= cnt; - st_bp->read_pointer += cnt; - ubp += cnt; - offset = 0; - } - if (do_count) /* Should never happen */ - return (-EIO); + return tb; - return 0; +free_page: + __free_page(tb->page); +free_buffer: + kfree(tb); + return NULL; } - -/* Move data towards start of buffer */ -static void move_buffer_data(struct st_buffer * st_bp, int offset) +static void free_tape_buffer(struct scsi_tape *STp) { - int src_seg, dst_seg, src_offset = 0, dst_offset; - int count, total; - - if (offset == 0) - return; - - total=st_bp->buffer_bytes - offset; - for (src_seg=0; src_seg < st_bp->frp_segs; src_seg++) { - src_offset = offset; - if (src_offset < st_bp->frp[src_seg].length) - break; - offset -= st_bp->frp[src_seg].length; - } - - st_bp->buffer_bytes = st_bp->read_pointer = total; - for (dst_seg=dst_offset=0; total > 0; ) { - count = min(st_bp->frp[dst_seg].length - dst_offset, - st_bp->frp[src_seg].length - src_offset); - memmove(page_address(st_bp->frp[dst_seg].page) + dst_offset, - page_address(st_bp->frp[src_seg].page) + src_offset, count); - src_offset += count; - if (src_offset >= st_bp->frp[src_seg].length) { - src_seg++; - src_offset = 0; - } - dst_offset += count; - if (dst_offset >= st_bp->frp[dst_seg].length) { - dst_seg++; - dst_offset = 0; - } - total -= count; + if (STp->buffer) { + bioset_pagepool_free(STp->buffer->bs); + __free_page(STp->buffer->page); + kfree(STp->buffer); + STp->buffer = NULL; } } - -/* Fill the s/g list up to the length required for this transfer */ -static void buf_to_sg(struct st_buffer *STbp, unsigned int length) -{ - int i; - unsigned int count; - struct scatterlist *sg; - struct st_buf_fragment *frp; - - if (length == STbp->frp_sg_current) - return; /* work already done */ - - sg = &(STbp->sg[0]); - frp = STbp->frp; - for (i=count=0; count < length; i++) { - if (length - count > frp[i].length) - sg_set_page(&sg[i], frp[i].page, frp[i].length, 0); - else - sg_set_page(&sg[i], frp[i].page, length - count, 0); - count += sg[i].length; - } - STbp->sg_segs = i; - STbp->frp_sg_current = length; -} - - /* Validate the options from command line or module parameters */ static void validate_options(void) { @@ -3886,7 +3811,6 @@ static int st_probe(struct device *dev) struct scsi_tape *tpnt = NULL; struct st_modedef *STm; struct st_partstat *STps; - struct st_buffer *buffer; int i, j, mode, dev_num, error; char *stp; @@ -3898,21 +3822,10 @@ static int st_probe(struct device *dev) return -ENODEV; } - i = min(SDp->request_queue->max_hw_segments, - SDp->request_queue->max_phys_segments); - if (st_max_sg_segs < i) - i = st_max_sg_segs; - buffer = new_tape_buffer(1, (SDp->host)->unchecked_isa_dma, i); - if (buffer == NULL) { - printk(KERN_ERR - "st: Can't allocate new tape buffer. Device not attached.\n"); - goto out; - } - disk = alloc_disk(1); if (!disk) { printk(KERN_ERR "st: out of memory. Device not attached.\n"); - goto out_buffer_free; + return -ENODEV; } write_lock(&st_dev_arr_lock); @@ -3974,7 +3887,12 @@ static int st_probe(struct device *dev) else tpnt->tape_type = MT_ISSCSI2; - tpnt->buffer = buffer; + tpnt->buffer = new_tape_buffer(tpnt); + if (tpnt->buffer == NULL) { + printk(KERN_ERR + "st: Can't allocate new tape buffer. Device not attached.\n"); + goto out_free_tape; + } tpnt->buffer->last_SRpnt = NULL; tpnt->inited = 0; @@ -4052,14 +3970,14 @@ static int st_probe(struct device *dev) printk(KERN_ERR "st%d: Can't add %s-rewind mode %d\n", dev_num, j ? "non" : "auto", mode); printk(KERN_ERR "st%d: Device not attached.\n", dev_num); - goto out_free_tape; + goto out_free_buffer; } STm->cdevs[j] = cdev; } error = do_create_class_files(tpnt, dev_num, mode); if (error) - goto out_free_tape; + goto out_free_buffer; } sdev_printk(KERN_NOTICE, SDp, @@ -4070,6 +3988,8 @@ static int st_probe(struct device *dev) return 0; +out_free_buffer: + free_tape_buffer(tpnt); out_free_tape: for (mode=0; mode < ST_NBR_MODES; mode++) { STm = &(tpnt->modes[mode]); @@ -4095,9 +4015,6 @@ out_free_tape: out_put_disk: put_disk(disk); kfree(tpnt); -out_buffer_free: - kfree(buffer); -out: return -ENODEV; }; @@ -4153,13 +4070,7 @@ static void scsi_tape_release(struct kref *kref) struct gendisk *disk = tpnt->disk; tpnt->device = NULL; - - if (tpnt->buffer) { - tpnt->buffer->orig_frp_segs = 0; - normalize_buffer(tpnt->buffer); - kfree(tpnt->buffer); - } - + free_tape_buffer(tpnt); disk->private_data = NULL; put_disk(disk); kfree(tpnt); @@ -4392,102 +4303,3 @@ static int do_create_class_files(struct scsi_tape *STp, int dev_num, int mode) out: return error; } - -/* The following functions may be useful for a larger audience. */ -static int sgl_map_user_pages(struct scatterlist *sgl, const unsigned int max_pages, - unsigned long uaddr, size_t count, int rw) -{ - unsigned long end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT; - unsigned long start = uaddr >> PAGE_SHIFT; - const int nr_pages = end - start; - int res, i, j; - struct page **pages; - - /* User attempted Overflow! */ - if ((uaddr + count) < uaddr) - return -EINVAL; - - /* Too big */ - if (nr_pages > max_pages) - return -ENOMEM; - - /* Hmm? */ - if (count == 0) - return 0; - - if ((pages = kmalloc(max_pages * sizeof(*pages), GFP_KERNEL)) == NULL) - return -ENOMEM; - - /* Try to fault in all of the necessary pages */ - down_read(¤t->mm->mmap_sem); - /* rw==READ means read from drive, write into memory area */ - res = get_user_pages( - current, - current->mm, - uaddr, - nr_pages, - rw == READ, - 0, /* don't force */ - pages, - NULL); - up_read(¤t->mm->mmap_sem); - - /* Errors and no page mapped should return here */ - if (res < nr_pages) - goto out_unmap; - - for (i=0; i < nr_pages; i++) { - /* FIXME: flush superflous for rw==READ, - * probably wrong function for rw==WRITE - */ - flush_dcache_page(pages[i]); - } - - /* Populate the scatter/gather list */ - sg_set_page(&sgl[0], pages[0], 0, uaddr & ~PAGE_MASK); - if (nr_pages > 1) { - sgl[0].length = PAGE_SIZE - sgl[0].offset; - count -= sgl[0].length; - for (i=1; i < nr_pages ; i++) { - sg_set_page(&sgl[i], pages[i], - count < PAGE_SIZE ? count : PAGE_SIZE, 0);; - count -= PAGE_SIZE; - } - } - else { - sgl[0].length = count; - } - - kfree(pages); - return nr_pages; - - out_unmap: - if (res > 0) { - for (j=0; j < res; j++) - page_cache_release(pages[j]); - res = 0; - } - kfree(pages); - return res; -} - - -/* And unmap them... */ -static int sgl_unmap_user_pages(struct scatterlist *sgl, const unsigned int nr_pages, - int dirtied) -{ - int i; - - for (i=0; i < nr_pages; i++) { - struct page *page = sg_page(&sgl[i]); - - if (dirtied) - SetPageDirty(page); - /* FIXME: cache flush missing for rw==READ - * FIXME: call the correct reference counting function - */ - page_cache_release(page); - } - - return 0; -} diff --git a/drivers/scsi/st.h b/drivers/scsi/st.h index 6c80757..e945dba 100644 --- a/drivers/scsi/st.h +++ b/drivers/scsi/st.h @@ -28,28 +28,32 @@ struct st_request { int result; struct scsi_tape *stp; struct completion *waiting; + struct request *rq; }; /* The tape buffer descriptor. */ struct st_buffer { unsigned char dma; /* DMA-able buffer */ unsigned char do_dio; /* direct i/o set up? */ - int buffer_size; - int buffer_blocks; + struct bio_set *bs; int buffer_bytes; int read_pointer; int writing; int syscall_result; struct st_request *last_SRpnt; + /* + * If a write buffer is not full then this points to the SRpnt + * st_write began to setup. On later st_write calls we append + * to the buffers setup for this request. + * + * If a read is smaller than the block_size/st_fixed_buffer_size + * then this points to the SRpnt that has the extra data. + */ + struct st_request *current_SRpnt; struct st_cmdstatus cmdstat; + struct page *page; /* used for st.c command with no user buf backing */ unsigned char *b_data; - unsigned short use_sg; /* zero or max number of s/g segments for this adapter */ - unsigned short sg_segs; /* number of segments in s/g list */ - unsigned short orig_frp_segs; /* number of segments allocated at first try */ - unsigned short frp_segs; /* number of buffer segments */ - unsigned int frp_sg_current; /* driver buffer length currently in s/g list */ - struct st_buf_fragment *frp; /* the allocated buffer fragment list */ - struct scatterlist sg[1]; /* MUST BE last item */ + struct bio *bio; }; /* The tape buffer fragment descriptor */ -- 1.5.4.1