Merge branch 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6
[safe/jmp/linux-2.6] / drivers / scsi / st.c
index 203f22e..24211d0 100644 (file)
@@ -17,7 +17,7 @@
    Last modified: 18-JAN-1998 Richard Gooch <rgooch@atnf.csiro.au> Devfs support
  */
 
-static const char *verstr = "20080504";
+static const char *verstr = "20081215";
 
 #include <linux/module.h>
 
@@ -27,6 +27,7 @@ static const char *verstr = "20080504";
 #include <linux/mm.h>
 #include <linux/init.h>
 #include <linux/string.h>
+#include <linux/slab.h>
 #include <linux/errno.h>
 #include <linux/mtio.h>
 #include <linux/cdrom.h>
@@ -182,18 +183,16 @@ 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 clear_buffer(struct st_buffer *);
 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, 
+static int sgl_map_user_pages(struct st_buffer *, const unsigned int,
                              unsigned long, size_t, int);
-static int sgl_unmap_user_pages(struct scatterlist *, const unsigned int, int);
+static int sgl_unmap_user_pages(struct st_buffer *, const unsigned int, int);
 
 static int st_probe(struct device *);
 static int st_remove(struct device *);
@@ -376,9 +375,9 @@ static int st_chk_result(struct scsi_tape *STp, struct st_request * SRpnt)
        if (!debugging) { /* Abnormal conditions for tape */
                if (!cmdstatp->have_sense)
                        printk(KERN_WARNING
-                              "%s: Error %x (sugg. bt 0x%x, driver bt 0x%x, host bt 0x%x).\n",
-                              name, result, suggestion(result),
-                              driver_byte(result) & DRIVER_MASK, host_byte(result));
+                              "%s: Error %x (driver bt 0x%x, host bt 0x%x).\n",
+                              name, result, driver_byte(result),
+                              host_byte(result));
                else if (cmdstatp->have_sense &&
                         scode != NO_SENSE &&
                         scode != RECOVERED_ERROR &&
@@ -435,22 +434,6 @@ static int st_chk_result(struct scsi_tape *STp, struct st_request * SRpnt)
        return (-EIO);
 }
 
-
-/* Wakeup from interrupt */
-static void st_sleep_done(void *data, char *sense, int result, int resid)
-{
-       struct st_request *SRpnt = 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.residual = resid;
-       DEB( STp->write_pending = 0; )
-
-       if (SRpnt->waiting)
-               complete(SRpnt->waiting);
-}
-
 static struct st_request *st_allocate_request(struct scsi_tape *stp)
 {
        struct st_request *streq;
@@ -475,6 +458,63 @@ static void st_release_request(struct st_request *streq)
        kfree(streq);
 }
 
+static void st_scsi_execute_end(struct request *req, int uptodate)
+{
+       struct st_request *SRpnt = req->end_io_data;
+       struct scsi_tape *STp = SRpnt->stp;
+
+       STp->buffer->cmdstat.midlevel_result = SRpnt->result = req->errors;
+       STp->buffer->cmdstat.residual = req->resid_len;
+
+       if (SRpnt->waiting)
+               complete(SRpnt->waiting);
+
+       blk_rq_unmap_user(SRpnt->bio);
+       __blk_put_request(req->q, req);
+}
+
+static int st_scsi_execute(struct st_request *SRpnt, const unsigned char *cmd,
+                          int data_direction, void *buffer, unsigned bufflen,
+                          int timeout, int retries)
+{
+       struct request *req;
+       struct rq_map_data *mdata = &SRpnt->stp->buffer->map_data;
+       int err = 0;
+       int write = (data_direction == DMA_TO_DEVICE);
+
+       req = blk_get_request(SRpnt->stp->device->request_queue, write,
+                             GFP_KERNEL);
+       if (!req)
+               return DRIVER_ERROR << 24;
+
+       req->cmd_type = REQ_TYPE_BLOCK_PC;
+       req->cmd_flags |= REQ_QUIET;
+
+       mdata->null_mapped = 1;
+
+       if (bufflen) {
+               err = blk_rq_map_user(req->q, req, mdata, NULL, bufflen,
+                                     GFP_KERNEL);
+               if (err) {
+                       blk_put_request(req);
+                       return DRIVER_ERROR << 24;
+               }
+       }
+
+       SRpnt->bio = req->bio;
+       req->cmd_len = COMMAND_SIZE(cmd[0]);
+       memset(req->cmd, 0, BLK_MAX_CDB);
+       memcpy(req->cmd, cmd, req->cmd_len);
+       req->sense = SRpnt->sense;
+       req->sense_len = 0;
+       req->timeout = timeout;
+       req->retries = retries;
+       req->end_io_data = SRpnt;
+
+       blk_execute_rq_nowait(req->q, NULL, req, 1, st_scsi_execute_end);
+       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. */
@@ -483,6 +523,8 @@ 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)
 {
        struct completion *waiting;
+       struct rq_map_data *mdata = &STp->buffer->map_data;
+       int ret;
 
        /* if async, make sure there's no command outstanding */
        if (!do_wait && ((STp->buffer)->last_SRpnt)) {
@@ -510,21 +552,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);
+       if (STp->buffer->do_dio) {
+               mdata->page_order = 0;
+               mdata->nr_entries = STp->buffer->sg_segs;
+               mdata->pages = STp->buffer->mapped_pages;
+       } else {
+               mdata->page_order = STp->buffer->reserved_page_order;
+               mdata->nr_entries =
+                       DIV_ROUND_UP(bytes, PAGE_SIZE << mdata->page_order);
+               mdata->pages = STp->buffer->reserved_pages;
+               mdata->offset = 0;
+       }
 
        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)) {
+       ret = st_scsi_execute(SRpnt, cmd, direction, NULL, bytes, timeout,
+                             retries);
+       if (ret) {
                /* could not allocate the buffer or request was too large */
                (STp->buffer)->syscall_result = (-EBUSY);
                (STp->buffer)->last_SRpnt = NULL;
-       }
-       else if (do_wait) {
+       } else if (do_wait) {
                wait_for_completion(waiting);
                SRpnt->waiting = NULL;
                (STp->buffer)->syscall_result = st_chk_result(STp, SRpnt);
@@ -1425,8 +1475,8 @@ static int setup_buffering(struct scsi_tape *STp, const char __user *buf,
 
        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));
+               i = sgl_map_user_pages(STbp, 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 */
@@ -1434,7 +1484,6 @@ static int setup_buffering(struct scsi_tape *STp, const char __user *buf,
                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++;
@@ -1480,7 +1529,7 @@ static void release_buffering(struct scsi_tape *STp, int is_read)
 
        STbp = STp->buffer;
        if (STbp->do_dio) {
-               sgl_unmap_user_pages(&(STbp->sg[0]), STbp->do_dio, is_read);
+               sgl_unmap_user_pages(STbp, STbp->do_dio, is_read);
                STbp->do_dio = 0;
                STbp->sg_segs = 0;
        }
@@ -2234,7 +2283,8 @@ static int st_set_options(struct scsi_tape *STp, long options)
        } else if (code == MT_ST_SET_CLN) {
                value = (options & ~MT_ST_OPTIONS) & 0xff;
                if (value != 0 &&
-                   value < EXTENDED_SENSE_START && value >= SCSI_SENSE_BUFFERSIZE)
+                       (value < EXTENDED_SENSE_START ||
+                               value >= SCSI_SENSE_BUFFERSIZE))
                        return (-EINVAL);
                STp->cln_mode = value;
                STp->cln_sense_mask = (options >> 8) & 0xff;
@@ -2325,7 +2375,7 @@ static int st_set_options(struct scsi_tape *STp, long options)
 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;
+       struct st_request *SRpnt;
 
        memset(cmd, 0, MAX_COMMAND_SIZE);
        cmd[0] = MODE_SENSE;
@@ -2334,14 +2384,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,
+       SRpnt = st_do_scsi(NULL, STp, cmd, cmd[4], DMA_FROM_DEVICE,
                           STp->device->request_queue->rq_timeout, 0, 1);
        if (SRpnt == NULL)
                return (STp->buffer)->syscall_result;
 
        st_release_request(SRpnt);
 
-       return (STp->buffer)->syscall_result;
+       return STp->buffer->syscall_result;
 }
 
 
@@ -2351,7 +2401,8 @@ static int write_mode_page(struct scsi_tape *STp, int page, int slow)
 {
        int pgo;
        unsigned char cmd[MAX_COMMAND_SIZE];
-       struct st_request *SRpnt = NULL;
+       struct st_request *SRpnt;
+       int timeout;
 
        memset(cmd, 0, MAX_COMMAND_SIZE);
        cmd[0] = MODE_SELECT;
@@ -2365,14 +2416,16 @@ static int write_mode_page(struct scsi_tape *STp, int page, int slow)
        (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->request_queue->rq_timeout), 0, 1);
+       timeout = slow ?
+               STp->long_timeout : STp->device->request_queue->rq_timeout;
+       SRpnt = st_do_scsi(NULL, STp, cmd, cmd[4], DMA_TO_DEVICE,
+                          timeout, 0, 1);
        if (SRpnt == NULL)
                return (STp->buffer)->syscall_result;
 
        st_release_request(SRpnt);
 
-       return (STp->buffer)->syscall_result;
+       return STp->buffer->syscall_result;
 }
 
 
@@ -2810,11 +2863,8 @@ 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;
                        }
@@ -2915,7 +2965,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
                            !(STp->use_pf & PF_TESTED)) {
                                /* Try the other possible state of Page Format if not
                                   already tried */
-                               STp->use_pf = !STp->use_pf | PF_TESTED;
+                               STp->use_pf = (STp->use_pf ^ USE_PF) | PF_TESTED;
                                st_release_request(SRpnt);
                                SRpnt = NULL;
                                return st_int_ioctl(STp, cmd_in, arg);
@@ -3620,38 +3670,34 @@ static long st_compat_ioctl(struct file *file, unsigned int cmd, unsigned long a
 
 /* 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)
+static struct st_buffer *new_tape_buffer(int need_dma, int max_sg)
 {
-       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(struct st_buffer), GFP_ATOMIC);
        if (!tb) {
                printk(KERN_NOTICE "st: Can't allocate new tape buffer.\n");
                return NULL;
        }
-       tb->frp_segs = tb->orig_frp_segs = 0;
+       tb->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;
-       sg_init_table(tb->sg, max_sg);
+       tb->buffer_size = 0;
+
+       tb->reserved_pages = kzalloc(max_sg * sizeof(struct page *),
+                                    GFP_ATOMIC);
+       if (!tb->reserved_pages) {
+               kfree(tb);
+               return NULL;
+       }
 
        return tb;
 }
 
 
 /* Try to allocate enough space in the tape buffer */
+#define ST_MAX_ORDER 6
+
 static int enlarge_buffer(struct st_buffer * STbuffer, int new_size, int need_dma)
 {
        int segs, nbr, max_segs, b_size, order, got;
@@ -3671,33 +3717,45 @@ static int enlarge_buffer(struct st_buffer * STbuffer, int new_size, int need_dm
        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 */
+
+       if (STbuffer->cleared)
+               priority |= __GFP_ZERO;
+
+       if (STbuffer->frp_segs) {
+               order = STbuffer->reserved_page_order;
+               b_size = PAGE_SIZE << order;
+       } else {
+               for (b_size = PAGE_SIZE, order = 0;
+                    order < ST_MAX_ORDER && b_size < new_size;
+                    order++, b_size *= 2)
+                       ;  /* empty */
+       }
+       if (max_segs * (PAGE_SIZE << order) < new_size) {
+               if (order == ST_MAX_ORDER)
+                       return 0;
+               normalize_buffer(STbuffer);
+               return enlarge_buffer(STbuffer, new_size, need_dma);
+       }
 
        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;
-                       }
+               struct page *page;
+
+               page = alloc_pages(priority, order);
+               if (!page) {
                        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;
-               if (STbuffer->cleared)
-                       memset(page_address(STbuffer->frp[segs].page), 0, b_size);
+               STbuffer->reserved_pages[segs] = page;
                segs++;
        }
-       STbuffer->b_data = page_address(STbuffer->frp[0].page);
+       STbuffer->b_data = page_address(STbuffer->reserved_pages[0]);
+       STbuffer->reserved_page_order = order;
 
        return 1;
 }
@@ -3709,7 +3767,8 @@ static void clear_buffer(struct st_buffer * st_bp)
        int i;
 
        for (i=0; i < st_bp->frp_segs; i++)
-               memset(page_address(st_bp->frp[i].page), 0, st_bp->frp[i].length);
+               memset(page_address(st_bp->reserved_pages[i]), 0,
+                      PAGE_SIZE << st_bp->reserved_page_order);
        st_bp->cleared = 1;
 }
 
@@ -3717,16 +3776,16 @@ static void clear_buffer(struct st_buffer * st_bp)
 /* Release the extra buffer */
 static void normalize_buffer(struct st_buffer * STbuffer)
 {
-       int i, order;
+       int i, order = STbuffer->reserved_page_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;
+       for (i = 0; i < STbuffer->frp_segs; i++) {
+               __free_pages(STbuffer->reserved_pages[i], order);
+               STbuffer->buffer_size -= (PAGE_SIZE << order);
        }
-       STbuffer->frp_segs = STbuffer->orig_frp_segs;
-       STbuffer->frp_sg_current = 0;
+       STbuffer->frp_segs = 0;
        STbuffer->sg_segs = 0;
+       STbuffer->reserved_page_order = 0;
+       STbuffer->map_data.offset = 0;
 }
 
 
@@ -3735,18 +3794,19 @@ static void normalize_buffer(struct st_buffer * STbuffer)
 static int append_to_buffer(const char __user *ubp, struct st_buffer * st_bp, int do_count)
 {
        int i, cnt, res, offset;
+       int length = PAGE_SIZE << st_bp->reserved_page_order;
 
        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;
+            i < st_bp->frp_segs && offset >= length; i++)
+               offset -= length;
        if (i == st_bp->frp_segs) {     /* Should never happen */
                printk(KERN_WARNING "st: append_to_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_from_user(page_address(st_bp->frp[i].page) + offset, ubp, cnt);
+               struct page *page = st_bp->reserved_pages[i];
+               cnt = length - offset < do_count ? length - offset : do_count;
+               res = copy_from_user(page_address(page) + offset, ubp, cnt);
                if (res)
                        return (-EFAULT);
                do_count -= cnt;
@@ -3766,18 +3826,19 @@ static int append_to_buffer(const char __user *ubp, struct st_buffer * st_bp, in
 static int from_buffer(struct st_buffer * st_bp, char __user *ubp, int do_count)
 {
        int i, cnt, res, offset;
+       int length = PAGE_SIZE << st_bp->reserved_page_order;
 
        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;
+            i < st_bp->frp_segs && offset >= length; i++)
+               offset -= 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);
+               struct page *page = st_bp->reserved_pages[i];
+               cnt = length - offset < do_count ? length - offset : do_count;
+               res = copy_to_user(ubp, page_address(page) + offset, cnt);
                if (res)
                        return (-EFAULT);
                do_count -= cnt;
@@ -3798,6 +3859,7 @@ static void move_buffer_data(struct st_buffer * st_bp, int offset)
 {
        int src_seg, dst_seg, src_offset = 0, dst_offset;
        int count, total;
+       int length = PAGE_SIZE << st_bp->reserved_page_order;
 
        if (offset == 0)
                return;
@@ -3805,24 +3867,26 @@ static void move_buffer_data(struct st_buffer * st_bp, int offset)
        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)
+               if (src_offset < length)
                        break;
-               offset -= st_bp->frp[src_seg].length;
+               offset -= 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);
+               struct page *dpage = st_bp->reserved_pages[dst_seg];
+               struct page *spage = st_bp->reserved_pages[src_seg];
+
+               count = min(length - dst_offset, length - src_offset);
+               memmove(page_address(dpage) + dst_offset,
+                       page_address(spage) + src_offset, count);
                src_offset += count;
-               if (src_offset >= st_bp->frp[src_seg].length) {
+               if (src_offset >= length) {
                        src_seg++;
                        src_offset = 0;
                }
                dst_offset += count;
-               if (dst_offset >= st_bp->frp[dst_seg].length) {
+               if (dst_offset >= length) {
                        dst_seg++;
                        dst_offset = 0;
                }
@@ -3830,32 +3894,6 @@ static void move_buffer_data(struct st_buffer * st_bp, int offset)
        }
 }
 
-
-/* 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)
 {
@@ -3924,6 +3962,7 @@ static const struct file_operations st_fops =
        .open =         st_open,
        .flush =        st_flush,
        .release =      st_release,
+       .llseek =       noop_llseek,
 };
 
 static int st_probe(struct device *dev)
@@ -3946,11 +3985,10 @@ static int st_probe(struct device *dev)
                return -ENODEV;
        }
 
-       i = min(SDp->request_queue->max_hw_segments,
-               SDp->request_queue->max_phys_segments);
+       i = queue_max_segments(SDp->request_queue);
        if (st_max_sg_segs < i)
                i = st_max_sg_segs;
-       buffer = new_tape_buffer(1, (SDp->host)->unchecked_isa_dma, i);
+       buffer = new_tape_buffer((SDp->host)->unchecked_isa_dma, i);
        if (buffer == NULL) {
                printk(KERN_ERR
                       "st: Can't allocate new tape buffer. Device not attached.\n");
@@ -4204,8 +4242,8 @@ static void scsi_tape_release(struct kref *kref)
        tpnt->device = NULL;
 
        if (tpnt->buffer) {
-               tpnt->buffer->orig_frp_segs = 0;
                normalize_buffer(tpnt->buffer);
+               kfree(tpnt->buffer->reserved_pages);
                kfree(tpnt->buffer);
        }
 
@@ -4491,14 +4529,16 @@ out:
 }
 
 /* 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)
+static int sgl_map_user_pages(struct st_buffer *STbp,
+                             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;
+       struct rq_map_data *mdata = &STbp->map_data;
 
        /* User attempted Overflow! */
        if ((uaddr + count) < uaddr)
@@ -4540,24 +4580,10 @@ static int sgl_map_user_pages(struct scatterlist *sgl, const unsigned int max_pa
                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;
-       }
+       mdata->offset = uaddr & ~PAGE_MASK;
+       STbp->mapped_pages = pages;
 
-       kfree(pages);
        return nr_pages;
-
  out_unmap:
        if (res > 0) {
                for (j=0; j < res; j++)
@@ -4570,13 +4596,13 @@ static int sgl_map_user_pages(struct scatterlist *sgl, const unsigned int max_pa
 
 
 /* And unmap them... */
-static int sgl_unmap_user_pages(struct scatterlist *sgl, const unsigned int nr_pages,
-                               int dirtied)
+static int sgl_unmap_user_pages(struct st_buffer *STbp,
+                               const unsigned int nr_pages, int dirtied)
 {
        int i;
 
        for (i=0; i < nr_pages; i++) {
-               struct page *page = sg_page(&sgl[i]);
+               struct page *page = STbp->mapped_pages[i];
 
                if (dirtied)
                        SetPageDirty(page);
@@ -4585,6 +4611,8 @@ static int sgl_unmap_user_pages(struct scatterlist *sgl, const unsigned int nr_p
                 */
                page_cache_release(page);
        }
+       kfree(STbp->mapped_pages);
+       STbp->mapped_pages = NULL;
 
        return 0;
 }