--- linux/drivers/scsi/sg.c.or	Sun Aug  2 11:30:19 1998
+++ linux/drivers/scsi/sg.c	Sat Oct 31 19:17:25 1998
@@ -5,7 +5,15 @@
  *  Development Sponsored by Killy Corp. NY NY
  *   
  *  Borrows code from st driver.
- */
+ *
+ *  Heiko Ei0xdffeldt, 08.01.1998
+ *  Changed buffer allocation to a lazy kmalloc scheme in order to allow
+ *  multiple requests in parallel on different devices. For example reading
+ *  from a cdrom and writing on a cd burner.
+ *
+ *  NOTE: SG_BIG_BUFF is kept for compatibility, but has no effect anymore.
+ *  The corresponding sysctl entry is gone.
+*/
 #include <linux/module.h>
 
 #include <linux/fs.h>
@@ -27,8 +35,6 @@
 #include <scsi/scsi_ioctl.h>
 #include <scsi/sg.h>
 
-int sg_big_buff = SG_BIG_BUFF;		/* for now, sg_big_buff is read-only through sysctl */
-
 static int sg_init(void);
 static int sg_attach(Scsi_Device *);
 static int sg_detect(Scsi_Device *);
@@ -63,7 +69,6 @@
 };
 
 static struct scsi_generic *scsi_generics=NULL;
-static void sg_free(char *buff,int size);
 
 static int sg_ioctl(struct inode * inode,struct file * file,
 		    unsigned int cmd_in, unsigned long arg)
@@ -94,6 +99,47 @@
     }
 }
 
+static char *sg_lazy_malloc(int bsize, int *buff_len,
+       const struct scsi_generic *s_dev)
+{
+    void * retval;
+    int chunksize;
+
+    /* if we have enough space in last buffer, recycle buffer */
+    if (s_dev->buff != NULL && *buff_len >= bsize)
+       return s_dev->buff;
+
+    /* we don't have enough space. Forget the old buffer,
+       then get a bigger one */
+    if (s_dev->buff != NULL)
+       kfree(s_dev->buff);
+
+    /* quantify block size: 32K or 64K or 128K or more, if possible */
+    if (bsize <= 32 * 1024) {
+       chunksize = 32 * 1024;
+    } else if (bsize <= 64 * 1024) {
+       chunksize = 64 * 1024;
+    } else if (bsize <= 128 * 1024) {
+       chunksize = 128 * 1024;
+    } else {
+       /* for future extensions */
+       chunksize = bsize;
+    }
+
+    /* alloc a new block */
+    retval = kmalloc(chunksize, GFP_KERNEL | GFP_DMA);
+
+    /* if the kmalloc failed, let the size be zero */
+    if (retval == NULL) {
+       chunksize = 0;
+    }
+
+    /* register size of allocated block */
+    *buff_len = chunksize;
+
+    return (char *) retval;
+}
+
 static int sg_open(struct inode * inode, struct file * filp)
 {
     int dev=MINOR(inode->i_rdev);
@@ -106,6 +152,9 @@
   /*
    * 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.
+   *
+   * FIXME (HE): The device can be used through another driver!
+   * This goes currently unnoticed. Add a check for this.
    */
     if (flags & O_EXCL)
     {
@@ -143,7 +192,7 @@
         && scsi_generics[dev].complete)
     {
 	if (scsi_generics[dev].buff != NULL)
-	    sg_free(scsi_generics[dev].buff,scsi_generics[dev].buff_len);
+	    kfree(scsi_generics[dev].buff);
 	scsi_generics[dev].buff=NULL;
 	scsi_generics[dev].pending=0;
     }
@@ -162,44 +211,16 @@
     scsi_generics[dev].users--;
     if (scsi_generics[dev].device->host->hostt->usage_count)
 	(*scsi_generics[dev].device->host->hostt->usage_count)--;
+    if (scsi_generics[dev].device->host->hostt->usage_count
+	&& scsi_generics[dev].buff != NULL) {
+        kfree(scsi_generics[dev].buff);
+        scsi_generics[dev].buff = NULL;
+    }
     if(sg_template.usage_count) (*sg_template.usage_count)--;
     scsi_generics[dev].exclude=0;
     wake_up(&scsi_generics[dev].generic_wait);
 }
 
-static char *sg_malloc(int size)
-{
-    if (size<=4096)
-	return (char *) scsi_malloc(size);
-#ifdef SG_BIG_BUFF
-    if (size<=SG_BIG_BUFF)
-    {
-	while(big_inuse)
-	{
-	    interruptible_sleep_on(&big_wait);
-	    if (current->signal & ~current->blocked)
-		return NULL;
-	}
-	big_inuse=1;
-	return big_buff;
-    }
-#endif   
-    return NULL;
-}
-
-static void sg_free(char *buff,int size) 
-{
-#ifdef SG_BIG_BUFF
-    if (buff==big_buff)
-    {
-	big_inuse=0;
-	wake_up(&big_wait);
-	return;
-    }
-#endif
-    scsi_free(buff,size);
-}
-
 /*
  * Read back the results of a previous command.  We use the pending and
  * complete semaphores to tell us whether the buffer is available for us
@@ -257,8 +278,6 @@
      * Clean up, and release the device so that we can send another
      * command.
      */
-    sg_free(device->buff,device->buff_len);
-    device->buff = NULL;
     device->pending=0;
     wake_up(&device->write_wait);
     return count;
@@ -416,12 +435,12 @@
     amt=bsize;
     if (!bsize)
 	bsize++;
-    bsize=(bsize+511) & ~511;
 
     /*
      * If we cannot allocate the buffer, report an error.
      */
-    if ((bsize<0) || !(device->buff=sg_malloc(device->buff_len=bsize)))
+    if ((bsize<0)
+        || !(device->buff=sg_lazy_malloc(bsize, &device->buff_len, device)))
     {
 	device->pending=0;
 	wake_up(&device->write_wait);
@@ -440,8 +459,6 @@
     {
 	device->pending=0;
 	wake_up(&device->write_wait);
-	sg_free(device->buff,device->buff_len);
-	device->buff = NULL;
 	return -EAGAIN;
     } 
 #ifdef DEBUG
@@ -573,10 +590,6 @@
     printk("sg: Init generic device.\n");
 #endif
     
-#ifdef SG_BIG_BUFF
-    big_buff= (char *) scsi_init_malloc(SG_BIG_BUFF, GFP_ATOMIC | GFP_DMA);
-#endif
-    
     scsi_generics = (struct scsi_generic *) 
 	scsi_init_malloc((sg_template.dev_noticed + SG_EXTRA_DEVS) 
 			 * sizeof(struct scsi_generic), GFP_ATOMIC);
@@ -656,10 +669,6 @@
 		       * sizeof(struct scsi_generic));
     }
     sg_template.dev_max = 0;
-#ifdef SG_BIG_BUFF
-    if(big_buff != NULL)
-	scsi_init_free(big_buff, SG_BIG_BUFF);
-#endif
 }
 #endif /* MODULE */
 
