patch-2.3.6 linux/drivers/scsi/sg.c

Next file: linux/drivers/sound/dmasound.c
Previous file: linux/drivers/scsi/sd.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.5/linux/drivers/scsi/sg.c linux/drivers/scsi/sg.c
@@ -16,12 +16,10 @@
  *
  *  Borrows code from st driver. Thanks to Alessandro Rubini's "dd" book.
  */
- static char * sg_version_str = "Version: 2.1.32 (990501)";
+ static char * sg_version_str = "Version: 2.1.34 (990603)";
+ static int sg_version_num = 20134; /* 2 digits for each component */
 /*
- *  D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au)
- *      - scatter list logic replaces previous large atomic SG_BIG_BUFF
- *        sized allocation. See notes in <scsi/sg.h> include file. 
- * 
+ *  D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes:
  *      - scsi logging is available via SCSI_LOG_TIMEOUT macros. First
  *        the kernel/module needs to be built with CONFIG_SCSI_LOGGING
  *        (otherwise the macros compile to empty statements), then do
@@ -34,13 +32,27 @@
  *        Should use hlcomplete but it is too "noisy" (sd uses it).
  *
  *      - This driver obtains memory (heap) for the low-level driver to
- *        transfer/dma to and from. It is obtained from up to 4 sources:
- *              - 1 SG_SCATTER_SZ sized buffer on open() (per fd)
- *                [could be less if SG_SCATTER_SZ bytes not available]
- *              - obtain heap as required on write()s (get_free_pages)
+ *        transfer/dma to and from. It is obtained from up to 3 sources:
+ *              - obtain heap via get_free_pages()
  *              - obtain heap from the shared scsi dma pool
  *              - obtain heap from kernel directly (kmalloc) [last choice]
- *        the 'alt_address' field in the scatter_list structure and the
+ *        Each open() attempts to obtain a "reserve" buffer of
+ *        SG_DEF_RESERVED_SIZE bytes (or 0 bytes if opened O_RDONLY). The
+ *        amount actually obtained [which could be 0 bytes] can be found from
+ *        the SG_GET_RESERVED_SIZE ioctl(). This reserved buffer size can
+ *        be changed by calling the SG_SET_RESERVED_SIZE ioctl(). Since this
+ *        is an ambit claim, it should be followed by a SG_GET_RESERVED_SIZE
+ *        ioctl() to find out how much was actually obtained.
+ *        A subsequent write() to this file descriptor will use the
+ *        reserved buffer unless:
+ *              - it is already in use (eg during command queuing)
+ *              - or the write() needs a buffer size larger than the
+ *                reserved size
+ *        In these cases the write() will attempt to get the required memory
+ *        for the duration of this request but, if memory is low, it may
+ *        fail with ENOMEM.
+ *
+ *      - The 'alt_address' field in the scatter_list structure and the
  *        related 'mem_src' indicate the source of the heap allocation.
  *
  */
@@ -67,11 +79,11 @@
 #include <scsi/sg.h>
 
 
-int sg_big_buff = SG_SCATTER_SZ;  /* sg_big_buff is ro through sysctl */
+int sg_big_buff = SG_DEF_RESERVED_SIZE; /* sg_big_buff is ro through sysctl */
 /* N.B. This global is here to keep existing software happy. It now holds
-   the size of the "first buffer" of the most recent sucessful sg_open(). 
+   the size of the reserve buffer of the most recent sucessful sg_open(). 
    Only available when 'sg' compiled into kernel (rather than a module). 
-   This should probably be deprecated (use SG_GET_RESERVED_SIZE instead). */
+   This is deprecated (use SG_GET_RESERVED_SIZE ioctl() instead). */
 
 #define SG_SECTOR_SZ 512
 #define SG_SECTOR_MSK (SG_SECTOR_SZ - 1)
@@ -89,7 +101,6 @@
 static int sg_num_page = 0;
 #endif
 
-#define SG_HEAP_FB 0    /* heap obtained at open() (one buffer per fd) */
 #define SG_HEAP_PAGE 1  /* heap from kernel via get_free_pages() */
 #define SG_HEAP_KMAL 2  /* heap from kernel via kmalloc() */
 #define SG_HEAP_POOL 3  /* heap from scsi dma pool (mid-level) */
@@ -112,9 +123,9 @@
 {
     unsigned short use_sg;      /* Number of pieces of scatter-gather */
     unsigned short sglist_len;  /* size of malloc'd scatter-gather list */
-    unsigned bufflen;           /* Size of data buffer */
+    unsigned bufflen;           /* Size of (aggregate) data buffer */
     unsigned b_malloc_len;      /* actual len malloc'ed in buffer */
-    void * buffer;              /* Data buffer or scatter list (12 bytes) */
+    void * buffer;              /* Data buffer or scatter list,12 bytes each*/
     char mem_src;               /* heap whereabouts of 'buffer' */
 } Sg_scatter_hold;    /* 20 bytes long on i386 */
 
@@ -126,10 +137,10 @@
     Scsi_Cmnd * my_cmdp;        /* NULL -> ready to read, else id */
     struct sg_request * nextrp; /* NULL -> tail request (slist) */
     struct sg_fd * parentfp;    /* NULL -> not in use */
-    Sg_scatter_hold data;       /* hold buffers, perhaps scatter list */
-    struct sg_header header;    /* scsi command+info <include/sg.h> */
-    char fb_used;               /* 1 -> using fst_buf, normally 0 (used) */
-} Sg_request; /* around 72 bytes long on i386 */
+    Sg_scatter_hold data;       /* hold buffer, perhaps scatter list */
+    struct sg_header header;    /* scsi command+info, see <scsi/sg.h> */
+    char res_used;              /* 1 -> using reserve buffer, 0 -> not ... */
+} Sg_request; /* 72 bytes long on i386 */
 
 typedef struct sg_fd /* holds the state of a file descriptor */
 {
@@ -137,41 +148,52 @@
     struct sg_device * parentdp;     /* owning device */
     wait_queue_head_t read_wait;   /* queue read until command done */
     int timeout;                     /* defaults to SG_DEFAULT_TIMEOUT */
-    char * fst_buf;     /* try to grab SG_SCATTER_SZ sized buffer on open */
-    int fb_size;        /* actual size of allocated fst_buf */
-    Sg_request * headrp; /* head of request slist, NULL->empty */
+    Sg_scatter_hold reserve;  /* buffer held for this file descriptor */
+    unsigned save_scat_len;   /* original length of trunc. scat. element */
+    Sg_request * headrp;      /* head of request slist, NULL->empty */
     struct fasync_struct * async_qp; /* used by asynchronous notification */
     Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */
-    char low_dma;       /* as in parent but possible overridden to 1 */
+    char low_dma;       /* as in parent but possibly overridden to 1 */
     char force_packid;  /* 1 -> pack_id input to read(), 0 -> ignored */
     char closed;        /* 1 -> fd closed but request(s) outstanding */
-    char my_mem_src;    /* heap whereabouts of this sg_fb object */
+    char my_mem_src;    /* heap whereabouts of this Sg_fd object */
     char cmd_q;         /* 1 -> allow command queuing, 0 -> don't */
     char underrun_flag; /* 1 -> flag underruns, 0 -> don't, 2 -> test */
-} Sg_fd; /* around 1192 bytes long on i386 */
+    char next_cmd_len;  /* 0 -> automatic (def), >0 -> use on next write() */
+} Sg_fd; /* 1208 bytes long on i386 */
 
 typedef struct sg_device /* holds the state of each scsi generic device */
 {
     Scsi_Device * device;
-    wait_queue_head_t generic_wait;/* queue open if O_EXCL on prev. open */
+    wait_queue_head_t o_excl_wait; /* queue open() when O_EXCL in use */
     int sg_tablesize;   /* adapter's max scatter-gather table size */
     Sg_fd * headfp;     /* first open fd belonging to this device */
     kdev_t i_rdev;      /* holds device major+minor number */
     char exclude;       /* opened for exclusive access */
     char sgdebug;       /* 0->off, 1->sense, 9->dump dev, 10-> all devs */
-    unsigned char merge_fd; /* 0->sequencing per fd (def) else fd count */
-} Sg_device; /* around 24 bytes long on i386 */
+    unsigned char merge_fd; /* 0->sequencing per fd, else fd count */
+} Sg_device; /* 24 bytes long on i386 */
 
 
 static int sg_fasync(int fd, struct file * filp, int mode);
 static void sg_command_done(Scsi_Cmnd * SCpnt);
-static int sg_sc_build(Sg_request * srp, int max_buff_size,
-                       const char * inp, int num_write_xfer);
-static int sg_sc_undo_rem(Sg_request * srp, char * outp,
-                          int num_read_xfer);
-static char * sg_malloc(Sg_request * srp, int size, int * retSzp, 
+static int sg_start_req(Sg_request * srp, int max_buff_size,
+                        const char * inp, int num_write_xfer);
+static void sg_finish_rem_req(Sg_request * srp, char * outp,
+                              int num_read_xfer);
+static int sg_build_scat(Sg_scatter_hold * schp, int buff_size, 
+                         const Sg_fd * sfp);
+static void sg_write_xfer(Sg_scatter_hold * schp, const char * inp, 
+                          int num_write_xfer);
+static void sg_remove_scat(Sg_scatter_hold * schp);
+static void sg_read_xfer(Sg_scatter_hold * schp, char * outp,
+                         int num_read_xfer);
+static void sg_build_reserve(Sg_fd * sfp, int req_size);
+static void sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size);
+static void sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp);
+static char * sg_malloc(const Sg_fd * sfp, int size, int * retSzp, 
                         int * mem_srcp);
-static void sg_free(Sg_request * srp, char * buff, int size, int mem_src);
+static void sg_free(char * buff, int size, int mem_src);
 static char * sg_low_malloc(int rqSz, int lowDma, int mem_src, 
                             int * retSzp);
 static void sg_low_free(char * buff, int size, int mem_src);
@@ -180,7 +202,7 @@
 static Sg_request * sg_get_request(const Sg_fd * sfp, int pack_id);
 static Sg_request * sg_add_request(Sg_fd * sfp);
 static int sg_remove_request(Sg_fd * sfp, const Sg_request * srp);
-static int sg_fb_in_use(const Sg_fd * sfp);
+static int sg_res_in_use(const Sg_fd * sfp);
 static void sg_clr_scpnt(Scsi_Cmnd * SCpnt);
 static void sg_shorten_timeout(Scsi_Cmnd * scpnt);
 static void sg_debug(const Sg_device * sdp, const Sg_fd * sfp, int part_of);
@@ -197,6 +219,7 @@
     int flags = filp->f_flags;
     Sg_device * sdp;
     Sg_fd * sfp;
+    int res;
 
     if ((NULL == sg_dev_arr) || (dev < 0) || (dev >= sg_template.dev_max))
         return -ENXIO;
@@ -207,38 +230,35 @@
         printk("sg_open: inode maj=%d, min=%d   sdp maj=%d, min=%d\n",
                MAJOR(inode->i_rdev), MINOR(inode->i_rdev),
                MAJOR(sdp->i_rdev), MINOR(sdp->i_rdev));
+    /* If we are in the middle of error recovery, don't let anyone
+     * else try and use this device.  Also, if error recovery fails, it
+     * may try and take the device offline, in which case all further
+     * access to the device is prohibited.  */
     if(! scsi_block_when_processing_errors(sdp->device))
         return -ENXIO;
-/*  if (O_RDWR != (flags & O_ACCMODE)) */
-/*      return -EACCES;   May just want to get to a ioctl, so remove */
 
     SCSI_LOG_TIMEOUT(3, printk("sg_open: dev=%d, flags=0x%x\n", dev, flags));
-   /* If we want exclusive access, then wait until the device is not
-    * busy, and then set the flag to prevent anyone else from using it. */
+
     if (flags & O_EXCL) {
         if (O_RDONLY == (flags & O_ACCMODE))
             return -EACCES;   /* Can't lock it with read only access */
-        while (sdp->headfp) {
-            if (flags & O_NONBLOCK)
-                return -EBUSY;
-            interruptible_sleep_on(&sdp->generic_wait);
-            if (signal_pending(current))
-                return -ERESTARTSYS;
-        }
-        sdp->exclude = 1;
-    }
-    else { /* Wait until nobody has an exclusive open on this device. */
-        while (sdp->exclude) {
-            if (flags & O_NONBLOCK)
-                return -EBUSY;
-            interruptible_sleep_on(&sdp->generic_wait);
-            if (signal_pending(current))
-                return -ERESTARTSYS;
-        }
+        if (sdp->headfp && (filp->f_flags & O_NONBLOCK))
+            return -EBUSY;
+        res = 0;  /* following is a macro that beats race condition */
+        __wait_event_interruptible(sdp->o_excl_wait, 
+               ((sdp->headfp || sdp->exclude) ? 0 : (sdp->exclude = 1)), 
+                                   res);
+        if (res)
+            return res; /* -ERESTARTSYS because signal hit process */
+    }
+    else if (sdp->exclude) { /* some other fd has an exclusive lock on dev */
+        if (filp->f_flags & O_NONBLOCK)
+            return -EBUSY;
+        res = 0;  /* following is a macro that beats race condition */
+        __wait_event_interruptible(sdp->o_excl_wait, (! sdp->exclude), res);
+        if (res)
+            return res; /* -ERESTARTSYS because signal hit process */
     }
-    /* OK, we should have grabbed the device.  Mark the thing so
-     * that other processes know that we have it, and initialize the
-     * state variables to known values. */
     if (! sdp->headfp) { /* no existing opens on this device */
         sdp->sgdebug = 0;
         sdp->sg_tablesize = sdp->device->host->sg_tablesize;
@@ -284,16 +304,14 @@
     if(sg_template.module)
         __MOD_DEC_USE_COUNT(sg_template.module);
     sdp->exclude = 0;
-    wake_up_interruptible(&sdp->generic_wait);
+    wake_up_interruptible(&sdp->o_excl_wait);
     return 0;
 }
 
-/* Read back the results of a SCSI command which was sent in a prior
-   write(). */
 static ssize_t sg_read(struct file * filp, char * buf,
                        size_t count, loff_t *ppos)
 {
-    int k;
+    int k, res;
     Sg_device * sdp;
     Sg_fd * sfp;
     Sg_request * srp;
@@ -305,13 +323,8 @@
     SCSI_LOG_TIMEOUT(3, printk("sg_read: dev=%d, count=%d\n", 
                                MINOR(sdp->i_rdev), (int)count));
     
-    /* If we are in the middle of error recovery, don't let anyone
-     * else try and use this device.  Also, if error recovery fails, it
-     * may try and take the device offline, in which case all further
-     * access to the device is prohibited.  */
     if(! scsi_block_when_processing_errors(sdp->device))
         return -ENXIO;
-
     if (ppos != &filp->f_pos)
         ; /* FIXME: Hmm.  Seek to the right place, or fail?  */
     if ((k = verify_area(VERIFY_WRITE, buf, count)))
@@ -319,13 +332,15 @@
     if (sfp->force_packid && (count >= size_sg_header))
         req_pack_id = shp->pack_id;
     srp = sg_get_request(sfp, req_pack_id);
-    while(! srp) {
+    if (! srp) { /* now wait on packet to arrive */
         if (filp->f_flags & O_NONBLOCK)
             return -EAGAIN;
-        interruptible_sleep_on(&sfp->read_wait);
-        if (signal_pending(current))
-            return -ERESTARTSYS;
-        srp = sg_get_request(sfp, req_pack_id);
+        res = 0;  /* following is a macro that beats race condition */
+        __wait_event_interruptible(sfp->read_wait, 
+                                   (srp = sg_get_request(sfp, req_pack_id)),
+                                   res);
+        if (res)
+            return res; /* -ERESTARTSYS because signal hit process */
     }
     if (2 != sfp->underrun_flag)
         srp->header.pack_len = srp->header.reply_len;   /* Why ????? */
@@ -337,13 +352,13 @@
         if (count > srp->header.reply_len)
             count = srp->header.reply_len;
         if (count > size_sg_header) /* release does copy_to_user */
-            sg_sc_undo_rem(srp, buf, count - size_sg_header);
+            sg_finish_rem_req(srp, buf, count - size_sg_header);
         else
-            sg_sc_undo_rem(srp, NULL, 0);
+            sg_finish_rem_req(srp, NULL, 0);
     }
     else {
         count = (srp->header.result == 0) ? 0 : -EIO;
-        sg_sc_undo_rem(srp, NULL, 0);
+        sg_finish_rem_req(srp, NULL, 0);
     }
     return count;
 }
@@ -366,22 +381,15 @@
     SCSI_LOG_TIMEOUT(3, printk("sg_write: dev=%d, count=%d\n", 
                                MINOR(sdp->i_rdev), (int)count));
 
-/* If we are in the middle of error recovery, don't let anyone
- * else try and use this device.  Also, if error recovery fails, it
- * may try and take the device offline, in which case all further
- * access to the device is prohibited.  */
     if(! scsi_block_when_processing_errors(sdp->device) )
         return -ENXIO;
-
     if (ppos != &filp->f_pos)
         ; /* FIXME: Hmm.  Seek to the right place, or fail?  */
 
     if ((k = verify_area(VERIFY_READ, buf, count)))
         return k;  /* protects following copy_from_user()s + get_user()s */
-/* The minimum scsi command length is 6 bytes.  If we get anything
- * less than this, it is clearly bogus.  */
     if (count < (size_sg_header + 6))
-        return -EIO;
+        return -EIO;   /* The minimum scsi command length is 6 bytes. */ 
 
     srp = sg_add_request(sfp);
     if (! srp) {
@@ -392,55 +400,54 @@
     buf += size_sg_header;
     srp->header.pack_len = count;
     __get_user(opcode, buf);
-    cmd_size = COMMAND_SIZE(opcode);
-    if ((opcode >= 0xc0) && srp->header.twelve_byte) 
-        cmd_size = 12;
+    if (sfp->next_cmd_len > 0) {
+        if (sfp->next_cmd_len > MAX_COMMAND_SIZE) {
+            SCSI_LOG_TIMEOUT(1, printk("sg_write: command length too long\n"));
+            sfp->next_cmd_len = 0;
+            return -EDOM;
+        }
+        cmd_size = sfp->next_cmd_len;
+        sfp->next_cmd_len = 0; /* reset so only this write() effected */
+    }
+    else {
+        cmd_size = COMMAND_SIZE(opcode); /* based on SCSI command group */
+        if ((opcode >= 0xc0) && srp->header.twelve_byte) 
+            cmd_size = 12;
+    }
     SCSI_LOG_TIMEOUT(4, printk("sg_write:   scsi opcode=0x%02x, cmd_size=%d\n", 
                                (int)opcode, cmd_size));
 /* Determine buffer size.  */
     input_size = count - cmd_size;
     mxsize = (input_size > srp->header.reply_len) ? input_size :
                                                     srp->header.reply_len;
-/* Don't include the command header itself in the size.  */
     mxsize -= size_sg_header;
     input_size -= size_sg_header;
-/* Verify user has actually passed enough bytes for this command. */
     if (input_size < 0) {
-        sg_sc_undo_rem(srp, NULL, 0);
-        return -EIO; 
+        sg_remove_request(sfp, srp);
+        return -EIO; /* User did not pass enough bytes for this command. */
     }
-
-/* If we cannot allocate the buffer, report an error. */
-    if ((k = sg_sc_build(srp, mxsize, buf + cmd_size, input_size))) {
+    if ((k = sg_start_req(srp, mxsize, buf + cmd_size, input_size))) {
         SCSI_LOG_TIMEOUT(1, printk("sg_write: build err=%d\n", k));
-        sg_sc_undo_rem(srp, NULL, 0);
-        return k;
+        sg_finish_rem_req(srp, NULL, 0);
+        return k;    /* probably out of space --> ENOMEM */
     }
-
 /*  SCSI_LOG_TIMEOUT(7, printk("sg_write: allocating device\n")); */
-/* Grab a command pointer for the device we want to talk to. If we
- * don't want to block, just return with the appropriate message.  */
     if (! (SCpnt = scsi_allocate_device(NULL, sdp->device, 
                                         !(filp->f_flags & O_NONBLOCK)))) {
-        sg_sc_undo_rem(srp, NULL, 0);
-        return -EAGAIN;
+        sg_finish_rem_req(srp, NULL, 0);
+        return -EAGAIN;   /* No available command blocks at the moment */
     }
 /*  SCSI_LOG_TIMEOUT(7, printk("sg_write: device allocated\n")); */
-
     srp->my_cmdp = SCpnt;
     SCpnt->request.rq_dev = sdp->i_rdev;
     SCpnt->request.rq_status = RQ_ACTIVE;
     SCpnt->sense_buffer[0] = 0;
     SCpnt->cmd_len = cmd_size;
-    /* Now copy the SCSI command from the user's address space.  */
     __copy_from_user(cmnd, buf, cmd_size);
-
-/* Set the LUN field in the command structure.  */
+/* Set the LUN field in the command structure, overriding user input  */
     cmnd[1]= (cmnd[1] & 0x1f) | (sdp->device->lun << 5);
+
 /*  SCSI_LOG_TIMEOUT(7, printk("sg_write: do cmd\n")); */
-/* Now pass the actual command down to the low-level driver.  We
- * do not do any more here - when the interrupt arrives, we will
- * then do the post-processing.  */
     spin_lock_irqsave(&io_request_lock, flags);
     SCpnt->use_sg = srp->data.use_sg;
     SCpnt->sglist_len = srp->data.sglist_len;
@@ -454,6 +461,8 @@
     srp->data.sglist_len = 0;
     srp->data.bufflen = 0;
     srp->data.buffer = NULL;
+/* Now send everything of to mid-level. The next time we hear about this
+   packet is when sg_command_done() is called (ie a callback). */
     scsi_do_cmd(SCpnt, (void *)cmnd,
                 (void *)SCpnt->buffer, mxsize,
                 sg_command_done, sfp->timeout, SG_DEFAULT_RETRIES);
@@ -475,9 +484,6 @@
         return -ENXIO;
     SCSI_LOG_TIMEOUT(3, printk("sg_ioctl: dev=%d, cmd=0x%x\n", 
                                MINOR(sdp->i_rdev), (int)cmd_in));
-     /* If we are in the middle of error recovery, then don't allow any
-     *  access to this device.  Also, error recovery *may* have taken the
-     *  device offline, in which case all further access is prohibited.  */
     if(! scsi_block_when_processing_errors(sdp->device) )
         return -ENXIO;
 
@@ -485,20 +491,18 @@
     {
     case SG_SET_TIMEOUT:
         return get_user(sfp->timeout, (int *)arg);
-    case SG_GET_TIMEOUT:
+    case SG_GET_TIMEOUT:  /* N.B. User receives timeout as return value */
         return sfp->timeout; /* strange ..., for backward compatibility */
     case SG_SET_FORCE_LOW_DMA:
         result = get_user(val, (int *)arg);
         if (result) return result;
         if (val) {
-            if ((0 == sfp->low_dma) && (0 == sg_fb_in_use(sfp))) {
-                sg_low_free(sfp->fst_buf, sfp->fb_size, SG_HEAP_PAGE);
-                sfp->fst_buf = sg_low_malloc(SG_SCATTER_SZ, 1, 
-                                             SG_HEAP_PAGE, &sfp->fb_size);
-            }
             sfp->low_dma = 1;
-            if (! sfp->fst_buf)
-                return -ENOMEM;
+            if ((0 == sfp->low_dma) && (0 == sg_res_in_use(sfp))) {
+                val = (int)sfp->reserve.bufflen;
+                sg_remove_scat(&sfp->reserve);
+                sg_build_reserve(sfp, val);
+            }
         }
         else
             sfp->low_dma = sdp->device->host->unchecked_isa_dma;
@@ -550,15 +554,20 @@
     case SG_GET_SG_TABLESIZE:
         return put_user(sdp->sg_tablesize, (int *)arg);
     case SG_SET_RESERVED_SIZE:
-        /* currently ignored, future extension */
         if (O_RDWR != (filp->f_flags & O_ACCMODE))
             return -EACCES;
         result = get_user(val, (int *)arg);
         if (result) return result;
-        /* logic should go here */
+        if (val != sfp->reserve.bufflen) {
+            if (sg_res_in_use(sfp))
+                return -EBUSY;
+            sg_remove_scat(&sfp->reserve);
+            sg_build_reserve(sfp, val);
+        }
         return 0;
     case SG_GET_RESERVED_SIZE:
-        return put_user(sfp->fb_size, (int *)arg);
+        val = (int)sfp->reserve.bufflen;
+        return put_user(val, (int *)arg);
     case SG_GET_MERGE_FD:
         return put_user((int)sdp->merge_fd, (int *)arg);
     case SG_SET_MERGE_FD:
@@ -587,6 +596,13 @@
         return 0;
     case SG_GET_UNDERRUN_FLAG:
         return put_user((int)sfp->underrun_flag, (int *)arg);
+    case SG_NEXT_CMD_LEN:
+        result = get_user(val, (int *)arg);
+        if (result) return result;
+        sfp->next_cmd_len = (val > 0) ? val : 0;
+        return 0;
+    case SG_GET_VERSION_NUM:
+        return put_user(sg_version_num, (int *)arg);
     case SG_EMULATED_HOST:
         return put_user(sdp->device->host->hostt->emulated, (int *)arg);
     case SCSI_IOCTL_SEND_COMMAND:
@@ -594,8 +610,7 @@
            user already has read/write access to the generic device and so
            can execute arbitrary SCSI commands.  */
         if (O_RDWR != (filp->f_flags & O_ACCMODE))
-            return -EACCES; /* require write access since these could be
-                               dangerous */
+            return -EACCES; /* very dangerous things can be done here */
         return scsi_ioctl_send_command(sdp->device, (void *)arg);
     case SG_SET_DEBUG:
         result = get_user(val, (int *)arg);
@@ -613,8 +628,7 @@
         return scsi_ioctl(sdp->device, cmd_in, (void *)arg);
     default:
         if (O_RDWR != (filp->f_flags & O_ACCMODE))
-            return -EACCES; /* require write access since these could be
-                               dangerous */
+            return -EACCES; /* don't know so take safe approach */
         return scsi_ioctl(sdp->device, cmd_in, (void *)arg);
     }
 }
@@ -664,8 +678,7 @@
 }
 
 /* This function is called by the interrupt handler when we
- * actually have a command that is complete.  Change the
- * flags to indicate that we have a result.  */
+ * actually have a command that is complete. */
 static void sg_command_done(Scsi_Cmnd * SCpnt)
 {
     int dev = MINOR(SCpnt->request.rq_dev);
@@ -712,17 +725,16 @@
     sg_clr_scpnt(SCpnt);
     srp->my_cmdp = NULL;
 
-    SCSI_LOG_TIMEOUT(4, 
-              printk("sg__done: dev=%d, scsi_stat=%d, res=0x%x\n", 
+    SCSI_LOG_TIMEOUT(4, printk("sg__done: dev=%d, scsi_stat=%d, res=0x%x\n", 
                 dev, (int)status_byte(SCpnt->result), (int)SCpnt->result));
-/* See if the command completed normally, or whether something went wrong. */
     memcpy(srp->header.sense_buffer, SCpnt->sense_buffer,
            sizeof(SCpnt->sense_buffer));
     switch (host_byte(SCpnt->result)) 
-    {
+    { /* This setup of 'result' is for backward compatibility and is best
+         ignored by the user who should use target, host + driver status */
     case DID_OK:
-    case DID_PASSTHROUGH: /* just guessing */
-    case DID_SOFT_ERROR:  /* just guessing */
+    case DID_PASSTHROUGH: 
+    case DID_SOFT_ERROR:
       srp->header.result = 0;
       break;
     case DID_NO_CONNECT:
@@ -738,12 +750,6 @@
       srp->header.result = EIO;
       break;
     case DID_ERROR:
-      /* There really should be DID_UNDERRUN and DID_OVERRUN error values,
-       * and a means for callers of scsi_do_cmd to indicate whether an
-       * underrun or overrun should signal an error.  Until that can be
-       * implemented, this kludge allows for returning useful error values
-       * except in cases that return DID_ERROR that might be due to an
-       * underrun. */
       if (SCpnt->sense_buffer[0] == 0 &&
           status_byte(SCpnt->result) == GOOD)
           srp->header.result = 0;
@@ -767,8 +773,6 @@
 /* filesystems using this device. */
         sdp->device->changed = 1;
     }
-
-/* Pick up error and status information */
     srp->header.target_status = status_byte(SCpnt->result);
     if ((sdp->sgdebug > 0) && 
         ((CHECK_CONDITION == srp->header.target_status) ||
@@ -784,15 +788,14 @@
         SCSI_LOG_TIMEOUT(1,
                printk("sg__done: already closed, freeing ...\n"));
 /* should check if module is unloaded <<<<<<< */
-        sg_sc_undo_rem(srp, NULL, 0);
+        sg_finish_rem_req(srp, NULL, 0);
         if (NULL == sfp->headrp) { 
             SCSI_LOG_TIMEOUT(1,
                 printk("sg__done: already closed, final cleanup\n"));
             sg_remove_sfp(sdp, sfp);
         }
     }
-/* Now wake up the process that is waiting for the result. */
-    /* A. Rubini says this is preferable+faster than wake_up() */
+/* Now wake up any sg_read() that is waiting for this packet. */
     wake_up_interruptible(&sfp->read_wait);
     if ((sfp->async_qp) && (! closed))
         kill_fasync(sfp->async_qp, SIGPOLL);
@@ -873,17 +876,20 @@
             printk("  *** Following data belongs to invoking FD ***\n");
         else if (! fp->parentdp)
             printk(">> Following FD has NULL parent pointer ???\n");
-        printk("   FD(%d): timeout=%d, fb_size=%d, cmd_q=%d\n",
-               k, fp->timeout, fp->fb_size, (int)fp->cmd_q);
-        printk("   low_dma=%d, force_packid=%d, urun_flag=%d, closed=%d\n",
-               (int)fp->low_dma, (int)fp->force_packid, 
-               (int)fp->underrun_flag, (int)fp->closed);
+        printk("   FD(%d): timeout=%d, bufflen=%d, use_sg=%d\n",
+               k, fp->timeout, fp->reserve.bufflen, (int)fp->reserve.use_sg);
+        printk("   low_dma=%d, cmd_q=%d, s_sc_len=%d, f_packid=%d\n",
+               (int)fp->low_dma, (int)fp->cmd_q, (int)fp->save_scat_len,
+               (int)fp->force_packid);
+        printk("   urun_flag=%d, next_cmd_len=%d, closed=%d\n",
+               (int)fp->underrun_flag, (int)fp->next_cmd_len, 
+               (int)fp->closed);
         srp = fp->headrp;
         if (NULL == srp)
             printk("     No requests active\n");
         while (srp) {
-            if (srp->fb_used)
-                printk("using 1st buff >> ");
+            if (srp->res_used)
+                printk("reserved buff >> ");
             else
                 printk("     ");
             if (srp->my_cmdp)
@@ -988,7 +994,7 @@
 
     SCSI_LOG_TIMEOUT(3, printk("sg_attach: dev=%d \n", k));
     sdp->device = scsidp;
-    init_waitqueue_head(&sdp->generic_wait);
+    init_waitqueue_head(&sdp->o_excl_wait);
     sdp->headfp= NULL;
     sdp->exclude = 0;
     sdp->merge_fd = 0;  /* Cope with SG_DEF_MERGE_FD on open */
@@ -1041,10 +1047,8 @@
         }
         scsidp->attached--;
         sg_template.nr_dev--;
-        /*
-         * avoid associated device /dev/sg? bying incremented
-         * each time module is inserted/removed , <dan@lectra.fr>
-         */
+/* avoid associated device /dev/sg? being incremented
+ * each time module is inserted/removed , <dan@lectra.fr> */
         sg_template.dev_noticed--;
         return;
     }
@@ -1099,42 +1103,80 @@
 #endif
 }
 
-static int sg_sc_build(Sg_request * srp, int max_buff_size,
-                       const char * inp, int num_write_xfer)
+static int sg_start_req(Sg_request * srp, int max_buff_size,
+                        const char * inp, int num_write_xfer)
+{
+    int res;
+    Sg_fd * sfp = srp->parentfp;
+    Sg_scatter_hold * req_schp = &srp->data;
+    Sg_scatter_hold * rsv_schp = &sfp->reserve;
+
+    SCSI_LOG_TIMEOUT(4, printk("sg_start_req: max_buff_size=%d\n", 
+                               max_buff_size)); 
+    if ((! sg_res_in_use(sfp)) && (max_buff_size <= rsv_schp->bufflen)) {
+        sg_link_reserve(sfp, srp, max_buff_size);
+        sg_write_xfer(req_schp, inp, num_write_xfer);
+    }
+    else {
+        res = sg_build_scat(req_schp, max_buff_size, sfp);
+        if (res) {
+            sg_remove_scat(req_schp);
+            return res;
+        }
+        sg_write_xfer(req_schp, inp, num_write_xfer);
+    }
+    return 0;
+}
+
+static void sg_finish_rem_req(Sg_request * srp, char * outp, 
+                              int num_read_xfer)
+{
+    Sg_fd * sfp = srp->parentfp;
+    Sg_scatter_hold * req_schp = &srp->data;
+
+    SCSI_LOG_TIMEOUT(4, printk("sg_finish_rem_req: res_used=%d\n",
+                               (int)srp->res_used)); 
+    if (num_read_xfer > 0)
+        sg_read_xfer(req_schp, outp, num_read_xfer);
+    if (srp->res_used)
+        sg_unlink_reserve(sfp, srp);
+    else 
+        sg_remove_scat(req_schp);
+    sg_remove_request(sfp, srp);
+}
+
+static int sg_build_scat(Sg_scatter_hold * schp, int buff_size, 
+                         const Sg_fd * sfp)
 {
     int ret_sz, mem_src;
-    int blk_size = max_buff_size;
+    int blk_size = buff_size;
     char * p = NULL;
 
-    if ((blk_size < 0) || (! srp))
+    if ((blk_size < 0) || (! sfp))
         return -EFAULT;
-    
-    SCSI_LOG_TIMEOUT(4, printk("sg_sc_build: m_b_s=%d, num_write_xfer=%d\n", 
-                                  max_buff_size, num_write_xfer)); 
     if (0 == blk_size)
         ++blk_size;             /* don't know why */
 /* round request up to next highest SG_SECTOR_SZ byte boundary */
     blk_size = (blk_size + SG_SECTOR_MSK) & (~SG_SECTOR_MSK);
-    SCSI_LOG_TIMEOUT(5, printk("sg_sc_build: blk_size=%d\n", blk_size));
-
+    SCSI_LOG_TIMEOUT(4, printk("sg_build_scat: buff_size=%d, blk_size=%d\n",
+                               buff_size, blk_size));
     if (blk_size <= SG_SCATTER_SZ) {
-        mem_src = SG_HEAP_FB;
-        p = sg_malloc(srp, blk_size, &ret_sz, &mem_src);
+        mem_src = SG_HEAP_PAGE;
+        p = sg_malloc(sfp, blk_size, &ret_sz, &mem_src);
         if (! p)
             return -ENOMEM;
         if (blk_size == ret_sz) { /* got it on the first attempt */
-            srp->data.buffer = p;
-            srp->data.bufflen = blk_size;
-            srp->data.mem_src = mem_src;
-            srp->data.b_malloc_len = blk_size;
-            if (inp && (num_write_xfer > 0))
-                __copy_from_user(srp->data.buffer, inp, num_write_xfer);
+            schp->use_sg = 0;
+            schp->buffer = p;
+            schp->bufflen = blk_size;
+            schp->mem_src = mem_src;
+            schp->b_malloc_len = blk_size;
             return 0;
         }
     }
     else {
         mem_src = SG_HEAP_PAGE;
-        p = sg_malloc(srp, SG_SCATTER_SZ, &ret_sz, &mem_src);
+        p = sg_malloc(sfp, SG_SCATTER_SZ, &ret_sz, &mem_src);
         if (! p)
             return -ENOMEM;
     }
@@ -1144,23 +1186,23 @@
         int k, rem_sz, num, nxt;
         int sc_bufflen = PAGE_SIZE;
         int mx_sc_elems = (sc_bufflen / sizeof(struct scatterlist)) - 1;
-        int sg_tablesize = srp->parentfp->parentdp->sg_tablesize;
+        int sg_tablesize = sfp->parentdp->sg_tablesize;
         int first = 1;
 
         k = SG_HEAP_KMAL;  /* want to protect mem_src, use k as scratch */
-        srp->data.buffer = (struct scatterlist *)sg_malloc(srp, 
+        schp->buffer = (struct scatterlist *)sg_malloc(sfp, 
                                 sc_bufflen, &num, &k);
-        srp->data.mem_src = (char)k;
+        schp->mem_src = (char)k;
         /* N.B. ret_sz and mem_src carried into this block ... */
-        if (! srp->data.buffer)
+        if (! schp->buffer)
             return -ENOMEM;
         else if (num != sc_bufflen) {
             sc_bufflen = num;
             mx_sc_elems = (sc_bufflen / sizeof(struct scatterlist)) - 1;
         }
-        srp->data.sglist_len = sc_bufflen;
-        memset(srp->data.buffer, 0, sc_bufflen);
-        for (k = 0, sclp = srp->data.buffer, rem_sz = blk_size, nxt =0; 
+        schp->sglist_len = sc_bufflen;
+        memset(schp->buffer, 0, sc_bufflen);
+        for (k = 0, sclp = schp->buffer, rem_sz = blk_size, nxt =0; 
              (k < sg_tablesize) && (rem_sz > 0) && (k < mx_sc_elems); 
              ++k, rem_sz -= ret_sz, ++sclp) {
             if (first) 
@@ -1168,7 +1210,7 @@
             else {
                 num = (rem_sz > SG_SCATTER_SZ) ? SG_SCATTER_SZ : rem_sz;
                 mem_src = SG_HEAP_PAGE;
-                p = sg_malloc(srp, num, &ret_sz, &mem_src);
+                p = sg_malloc(sfp, num, &ret_sz, &mem_src);
                 if (! p)
                     break;
             }
@@ -1176,73 +1218,188 @@
             sclp->length = ret_sz;
             sclp->alt_address = (char *)(long)mem_src;
             
-            if(inp && (num_write_xfer > 0)) {
-                num = (ret_sz > num_write_xfer) ? num_write_xfer : ret_sz;
-                __copy_from_user(sclp->address, inp, num);
-                num_write_xfer -= num;
-                inp += num;
-            }
             SCSI_LOG_TIMEOUT(5, 
-                printk("sg_sc_build: k=%d, a=0x%p, len=%d, ms=%d\n", 
+                printk("sg_build_build: k=%d, a=0x%p, len=%d, ms=%d\n", 
                 k, sclp->address, ret_sz, mem_src));
         } /* end of for loop */
-        srp->data.use_sg = k;
+        schp->use_sg = k;
         SCSI_LOG_TIMEOUT(5, 
-            printk("sg_sc_build: use_sg=%d, rem_sz=%d\n", k, rem_sz));
-        srp->data.bufflen = blk_size;
+            printk("sg_build_scat: use_sg=%d, rem_sz=%d\n", k, rem_sz));
+        schp->bufflen = blk_size;
         if (rem_sz > 0)   /* must have failed */
             return -ENOMEM;
     }
     return 0;
 }
 
-static int sg_sc_undo_rem(Sg_request * srp, char * outp,
-                          int num_read_xfer)
+static void sg_write_xfer(Sg_scatter_hold * schp, const char * inp, 
+                          int num_write_xfer)
 {
-    if (! srp)
-        return -EFAULT;
-    SCSI_LOG_TIMEOUT(4, printk("sg_sc_undo_rem: num_read_xfer=%d\n", 
-                               num_read_xfer)); 
-    if (! outp)
-        num_read_xfer = 0;
-    if(srp->data.use_sg) {
-        int k, num, mem_src;
-        struct scatterlist * sclp = (struct scatterlist *)srp->data.buffer;
-
-        for (k = 0; (k < srp->data.use_sg) && sclp->address; ++k, ++sclp) {
-            if (num_read_xfer > 0) {
-                num = (int)sclp->length;
-                if (num > num_read_xfer) {
-                    __copy_to_user(outp, sclp->address, num_read_xfer);
-                    outp += num_read_xfer;
-                    num_read_xfer = 0;
-                }
-                else {
-                    __copy_to_user(outp, sclp->address, num);
-                    outp += num;
-                    num_read_xfer -= num;
-                }
+    SCSI_LOG_TIMEOUT(4, printk("sg_write_xfer: num_write_xfer=%d, use_sg=%d\n", 
+                               num_write_xfer, schp->use_sg)); 
+    if ((! inp) || (num_write_xfer <= 0))
+        return;
+    if (schp->use_sg > 0) {
+        int k, num;
+        struct scatterlist * sclp = (struct scatterlist *)schp->buffer;
+
+        for (k = 0; (k < schp->use_sg) && sclp->address; ++k, ++sclp) { 
+            num = (int)sclp->length;
+            if (num > num_write_xfer) {
+                __copy_from_user(sclp->address, inp, num_write_xfer);
+                break;
             }
+            else {
+                __copy_from_user(sclp->address, inp, num);
+                num_write_xfer -= num;
+                if (num_write_xfer <= 0)
+                    break;
+                inp += num;
+            }
+        }
+    }
+    else
+        __copy_from_user(schp->buffer, inp, num_write_xfer);
+}
+
+static void sg_remove_scat(Sg_scatter_hold * schp)
+{
+    SCSI_LOG_TIMEOUT(4, printk("sg_remove_scat: use_sg=%d\n", schp->use_sg)); 
+    if(schp->use_sg > 0) {
+        int k, mem_src;
+        struct scatterlist * sclp = (struct scatterlist *)schp->buffer;
+
+        for (k = 0; (k < schp->use_sg) && sclp->address; ++k, ++sclp) {
             mem_src = (int)(long)sclp->alt_address;
             SCSI_LOG_TIMEOUT(5, 
-                printk("sg_sc_undo_rem: k=%d, a=0x%p, len=%d, ms=%d\n", 
+                printk("sg_remove_scat: k=%d, a=0x%p, len=%d, ms=%d\n", 
                        k, sclp->address, sclp->length, mem_src));
-            sg_free(srp, sclp->address, sclp->length, mem_src);
+            sg_free(sclp->address, sclp->length, mem_src);
+            sclp->address = NULL;
+            sclp->length = 0;
+        }
+        sg_free(schp->buffer, schp->sglist_len, schp->mem_src);
+    }
+    else if (schp->buffer)
+        sg_free(schp->buffer, schp->b_malloc_len, schp->mem_src);
+    schp->buffer = NULL;
+    schp->bufflen = 0;
+    schp->use_sg = 0;
+    schp->sglist_len = 0;
+}
+
+static void sg_read_xfer(Sg_scatter_hold * schp, char * outp,
+                         int num_read_xfer)
+{
+    SCSI_LOG_TIMEOUT(4, printk("sg_read_xfer: num_read_xfer=%d\n", 
+                               num_read_xfer)); 
+    if ((! outp) || (num_read_xfer <= 0))
+        return;
+    if(schp->use_sg > 0) {
+        int k, num;
+        struct scatterlist * sclp = (struct scatterlist *)schp->buffer;
+
+        for (k = 0; (k < schp->use_sg) && sclp->address; ++k, ++sclp) {
+            num = (int)sclp->length;
+            if (num > num_read_xfer) {
+                __copy_to_user(outp, sclp->address, num_read_xfer);
+                break;
+            }
+            else {
+                __copy_to_user(outp, sclp->address, num);
+                num_read_xfer -= num;
+                if (num_read_xfer <= 0)
+                    break;
+                outp += num;
+            }
         }
-        sg_free(srp, srp->data.buffer, srp->data.sglist_len, 
-                srp->data.mem_src);
+    }
+    else
+        __copy_to_user(outp, schp->buffer, num_read_xfer);
+}
+
+static void sg_build_reserve(Sg_fd * sfp, int req_size)
+{
+    Sg_scatter_hold * schp = &sfp->reserve;
+
+    SCSI_LOG_TIMEOUT(4, printk("sg_build_reserve: req_size=%d\n", req_size)); 
+    do {
+        if (req_size < PAGE_SIZE)
+            req_size = PAGE_SIZE;
+        if (0 == sg_build_scat(schp, req_size, sfp))
+            return;
+        else
+            sg_remove_scat(schp);
+        req_size >>= 1; /* divide by 2 */
+    } while (req_size >  (PAGE_SIZE / 2));
+}
+
+static void sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size)
+{
+    Sg_scatter_hold * req_schp = &srp->data;
+    Sg_scatter_hold * rsv_schp = &sfp->reserve;
+
+    SCSI_LOG_TIMEOUT(4, printk("sg_link_reserve: size=%d\n", size)); 
+    if (rsv_schp->use_sg > 0) {
+        int k, num;
+        int rem = size;
+        struct scatterlist * sclp = (struct scatterlist *)rsv_schp->buffer;
+
+        for (k = 0; k < rsv_schp->use_sg; ++k, ++sclp) {
+            num = (int)sclp->length;
+            if (rem <= num) {
+                sfp->save_scat_len = num;
+                sclp->length = (unsigned)rem;
+                break;
+            }
+            else
+                rem -= num;
+        }
+        if (k < rsv_schp->use_sg) {
+            req_schp->use_sg = k + 1;   /* adjust scatter list length */
+            req_schp->bufflen = size;
+            req_schp->sglist_len = rsv_schp->sglist_len;
+            req_schp->buffer = rsv_schp->buffer;
+            req_schp->mem_src = rsv_schp->mem_src;
+            req_schp->b_malloc_len = rsv_schp->b_malloc_len;
+        }
+        else
+            SCSI_LOG_TIMEOUT(1, printk("sg_link_reserve: BAD size\n")); 
     }
     else {
-        if (num_read_xfer > 0) 
-            __copy_to_user(outp, srp->data.buffer, num_read_xfer);
-        sg_free(srp, srp->data.buffer, srp->data.b_malloc_len, 
-                srp->data.mem_src);
-    }
-    if (0 == sg_remove_request(srp->parentfp, srp)) {
-        SCSI_LOG_TIMEOUT(1, printk("sg_sc_undo_rem: srp=0x%p not found\n", 
-                                   srp));
+        req_schp->use_sg = 0;
+        req_schp->bufflen = size;
+        req_schp->buffer = rsv_schp->buffer;
+        req_schp->mem_src = rsv_schp->mem_src;
+        req_schp->use_sg = rsv_schp->use_sg;
+        req_schp->b_malloc_len = rsv_schp->b_malloc_len;
     }
-    return 0;
+    srp->res_used = 1;
+}
+
+static void sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp)
+{
+    Sg_scatter_hold * req_schp = &srp->data;
+    Sg_scatter_hold * rsv_schp = &sfp->reserve;
+
+    SCSI_LOG_TIMEOUT(4, printk("sg_unlink_reserve: req->use_sg=%d\n",
+                               (int)req_schp->use_sg)); 
+    if (rsv_schp->use_sg > 0) {
+        struct scatterlist * sclp = (struct scatterlist *)rsv_schp->buffer;
+
+        if (sfp->save_scat_len > 0) 
+            (sclp + (req_schp->use_sg - 1))->length = 
+                                        (unsigned)sfp->save_scat_len;
+        else
+            SCSI_LOG_TIMEOUT(1, printk(
+                        "sg_unlink_reserve: BAD save_scat_len\n")); 
+    }
+    req_schp->use_sg = 0;
+    req_schp->bufflen = 0;
+    req_schp->buffer = NULL;
+    req_schp->sglist_len = 0;
+    sfp->save_scat_len = 0;
+    srp->res_used = 0;
 }
 
 static Sg_request * sg_get_request(const Sg_fd * sfp, int pack_id)
@@ -1292,7 +1449,7 @@
     if (resp) {
         resp->parentfp = sfp;
         resp->nextrp = NULL;
-        resp->fb_used = 0;
+        resp->res_used = 0;
         memset(&resp->data, 0, sizeof(Sg_scatter_hold));
         memset(&resp->header, 0, sizeof(struct sg_header));
         resp->my_cmdp = NULL;
@@ -1347,13 +1504,6 @@
                    sdp->device->host->unchecked_isa_dma : 1;
     sfp->cmd_q = SG_DEF_COMMAND_Q;
     sfp->underrun_flag = SG_DEF_UNDERRUN_FLAG;
-    if (get_reserved)
-        sfp->fst_buf = sg_low_malloc(SG_SCATTER_SZ, sfp->low_dma, 
-                                     SG_HEAP_PAGE, &sfp->fb_size);
-    else
-        sfp->fst_buf = NULL;
-    if (! sfp->fst_buf)
-        sfp->fb_size = 0;
     sfp->parentdp = sdp;
     if (! sdp->headfp)
         sdp->headfp = sfp;
@@ -1363,11 +1513,14 @@
            pfp = pfp->nextfp;
         pfp->nextfp = sfp;
     }
-    sg_big_buff = sfp->fb_size; /* show sysctl most recent "fb" size */
     SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: sfp=0x%p, m_s=%d\n",
                                sfp, (int)sfp->my_mem_src));
-    SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp:   fb_sz=%d, fst_buf=0x%p\n",
-                               sfp->fb_size, sfp->fst_buf));
+    if (get_reserved) {
+        sg_build_reserve(sfp, SG_DEF_RESERVED_SIZE);
+        sg_big_buff = sfp->reserve.bufflen; /* sysctl shows most recent size */
+        SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp:   bufflen=%d, use_sg=%d\n",
+                               sfp->reserve.bufflen, sfp->reserve.use_sg));
+    }
     return sfp;
 }
 
@@ -1388,7 +1541,7 @@
         while (srp) {
             tsrp = srp->nextrp;
             if (! srp->my_cmdp)
-                sg_sc_undo_rem(srp, NULL, 0);
+                sg_finish_rem_req(srp, NULL, 0);
             else
                 ++dirty;
             srp = tsrp;
@@ -1409,12 +1562,12 @@
                 prev_fp = fp;
             }
         }
-SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp:    fb_sz=%d, fst_buf=0x%p\n",
-                 sfp->fb_size, sfp->fst_buf));
-        sg_low_free(sfp->fst_buf, sfp->fb_size, SG_HEAP_PAGE);
+        if (sfp->reserve.bufflen > 0) {
+SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp:    bufflen=%d, use_sg=%d\n",
+                 (int)sfp->reserve.bufflen, (int)sfp->reserve.use_sg));
+            sg_remove_scat(&sfp->reserve);
+        }
         sfp->parentdp = NULL;
-        sfp->fst_buf = NULL;
-        sfp->fb_size = 0;
     SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp:    sfp=0x%p\n", sfp));
         sg_low_free((char *)sfp, sizeof(Sg_fd), sfp->my_mem_src);
         res = 1;
@@ -1427,12 +1580,12 @@
     return res;
 }
 
-static int sg_fb_in_use(const Sg_fd * sfp)
+static int sg_res_in_use(const Sg_fd * sfp)
 {
     const Sg_request * srp = sfp->headrp;
 
     while (srp) {
-        if (srp->fb_used)
+        if (srp->res_used)
             return 1;
         srp = srp->nextrp;
     }
@@ -1511,7 +1664,7 @@
     return resp;
 }
 
-static char * sg_malloc(Sg_request * srp, int size, int * retSzp, 
+static char * sg_malloc(const Sg_fd * sfp, int size, int * retSzp, 
                         int * mem_srcp)
 {
     char * resp = NULL;
@@ -1520,26 +1673,16 @@
     if (size <= 0)
         ;
     else {
-        Sg_fd * sfp = srp->parentfp;
         int low_dma = sfp->low_dma;
         int l_ms = -1;  /* invalid value */
 
         switch (*mem_srcp) 
         {
         case SG_HEAP_PAGE:
-        case SG_HEAP_FB:
             l_ms = (size < PAGE_SIZE) ? SG_HEAP_POOL : SG_HEAP_PAGE;
             resp = sg_low_malloc(size, low_dma, l_ms, 0);
             if (resp)
                 break;
-            if ((size <= sfp->fb_size) && (0 == sg_fb_in_use(sfp))) {
-                SCSI_LOG_TIMEOUT(6,
-                    printk("sg_malloc: scsi_malloc failed, get fst_buf\n"));
-                resp = sfp->fst_buf;
-                srp->fb_used = 1;
-                l_ms = SG_HEAP_FB;
-                break;
-            }
             resp = sg_low_malloc(size, low_dma, l_ms, &size);
             if (! resp) {
                 l_ms = (SG_HEAP_POOL == l_ms) ? SG_HEAP_PAGE : SG_HEAP_POOL;
@@ -1595,18 +1738,12 @@
                mem_src, buff, size);
 }
 
-static void sg_free(Sg_request * srp, char * buff, int size, int mem_src)
+static void sg_free(char * buff, int size, int mem_src)
 {
-    Sg_fd * sfp = srp->parentfp;
-
     SCSI_LOG_TIMEOUT(6, 
         printk("sg_free: buff=0x%p, size=%d\n", buff, size));
-    if ((! sfp) || (! buff) || (size <= 0))
+    if ((! buff) || (size <= 0))
         ;
-    else if (sfp->fst_buf == buff) {
-        srp->fb_used = 0;
-        SCSI_LOG_TIMEOUT(6, printk("sg_free:   left cause fst_buf\n"));
-    }
     else
         sg_low_free(buff, size, mem_src);
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)