]> err.no Git - linux-2.6/blobdiff - drivers/scsi/scsi.c
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/jk/spufs into...
[linux-2.6] / drivers / scsi / scsi.c
index 412a351c6cf77ba3b7f36bc45fd5fd75c96a8309..e5c6f6af876558c0b92dea602c6d2f6eed1a23a5 100644 (file)
@@ -141,20 +141,24 @@ const char * scsi_device_type(unsigned type)
 EXPORT_SYMBOL(scsi_device_type);
 
 struct scsi_host_cmd_pool {
-       struct kmem_cache       *slab;
-       unsigned int    users;
-       char            *name;
-       unsigned int    slab_flags;
-       gfp_t           gfp_mask;
+       struct kmem_cache       *cmd_slab;
+       struct kmem_cache       *sense_slab;
+       unsigned int            users;
+       char                    *cmd_name;
+       char                    *sense_name;
+       unsigned int            slab_flags;
+       gfp_t                   gfp_mask;
 };
 
 static struct scsi_host_cmd_pool scsi_cmd_pool = {
-       .name           = "scsi_cmd_cache",
+       .cmd_name       = "scsi_cmd_cache",
+       .sense_name     = "scsi_sense_cache",
        .slab_flags     = SLAB_HWCACHE_ALIGN,
 };
 
 static struct scsi_host_cmd_pool scsi_cmd_dma_pool = {
-       .name           = "scsi_cmd_cache(DMA)",
+       .cmd_name       = "scsi_cmd_cache(DMA)",
+       .sense_name     = "scsi_sense_cache(DMA)",
        .slab_flags     = SLAB_HWCACHE_ALIGN|SLAB_CACHE_DMA,
        .gfp_mask       = __GFP_DMA,
 };
@@ -172,9 +176,10 @@ static DEFINE_MUTEX(host_cmd_pool_mutex);
 struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, gfp_t gfp_mask)
 {
        struct scsi_cmnd *cmd;
+       unsigned char *buf;
 
-       cmd = kmem_cache_alloc(shost->cmd_pool->slab,
-                       gfp_mask | shost->cmd_pool->gfp_mask);
+       cmd = kmem_cache_alloc(shost->cmd_pool->cmd_slab,
+                              gfp_mask | shost->cmd_pool->gfp_mask);
 
        if (unlikely(!cmd)) {
                unsigned long flags;
@@ -186,6 +191,22 @@ struct scsi_cmnd *__scsi_get_command(struct Scsi_Host *shost, gfp_t gfp_mask)
                        list_del_init(&cmd->list);
                }
                spin_unlock_irqrestore(&shost->free_list_lock, flags);
+
+               if (cmd) {
+                       buf = cmd->sense_buffer;
+                       memset(cmd, 0, sizeof(*cmd));
+                       cmd->sense_buffer = buf;
+               }
+       } else {
+               buf = kmem_cache_alloc(shost->cmd_pool->sense_slab,
+                                      gfp_mask | shost->cmd_pool->gfp_mask);
+               if (likely(buf)) {
+                       memset(cmd, 0, sizeof(*cmd));
+                       cmd->sense_buffer = buf;
+               } else {
+                       kmem_cache_free(shost->cmd_pool->cmd_slab, cmd);
+                       cmd = NULL;
+               }
        }
 
        return cmd;
@@ -212,7 +233,6 @@ struct scsi_cmnd *scsi_get_command(struct scsi_device *dev, gfp_t gfp_mask)
        if (likely(cmd != NULL)) {
                unsigned long flags;
 
-               memset(cmd, 0, sizeof(*cmd));
                cmd->device = dev;
                init_timer(&cmd->eh_timeout);
                INIT_LIST_HEAD(&cmd->list);
@@ -246,8 +266,11 @@ void __scsi_put_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd,
        }
        spin_unlock_irqrestore(&shost->free_list_lock, flags);
 
-       if (likely(cmd != NULL))
-               kmem_cache_free(shost->cmd_pool->slab, cmd);
+       if (likely(cmd != NULL)) {
+               kmem_cache_free(shost->cmd_pool->sense_slab,
+                               cmd->sense_buffer);
+               kmem_cache_free(shost->cmd_pool->cmd_slab, cmd);
+       }
 
        put_device(dev);
 }
@@ -301,11 +324,19 @@ int scsi_setup_command_freelist(struct Scsi_Host *shost)
        mutex_lock(&host_cmd_pool_mutex);
        pool = (shost->unchecked_isa_dma ? &scsi_cmd_dma_pool : &scsi_cmd_pool);
        if (!pool->users) {
-               pool->slab = kmem_cache_create(pool->name,
-                               sizeof(struct scsi_cmnd), 0,
-                               pool->slab_flags, NULL);
-               if (!pool->slab)
+               pool->cmd_slab = kmem_cache_create(pool->cmd_name,
+                                                  sizeof(struct scsi_cmnd), 0,
+                                                  pool->slab_flags, NULL);
+               if (!pool->cmd_slab)
+                       goto fail;
+
+               pool->sense_slab = kmem_cache_create(pool->sense_name,
+                                                    SCSI_SENSE_BUFFERSIZE, 0,
+                                                    pool->slab_flags, NULL);
+               if (!pool->sense_slab) {
+                       kmem_cache_destroy(pool->cmd_slab);
                        goto fail;
+               }
        }
 
        pool->users++;
@@ -315,21 +346,31 @@ int scsi_setup_command_freelist(struct Scsi_Host *shost)
        /*
         * Get one backup command for this host.
         */
-       cmd = kmem_cache_alloc(shost->cmd_pool->slab,
-                       GFP_KERNEL | shost->cmd_pool->gfp_mask);
+       cmd = kmem_cache_alloc(shost->cmd_pool->cmd_slab,
+                              GFP_KERNEL | shost->cmd_pool->gfp_mask);
        if (!cmd)
                goto fail2;
-       list_add(&cmd->list, &shost->free_list);                
+
+       cmd->sense_buffer = kmem_cache_alloc(shost->cmd_pool->sense_slab,
+                                            GFP_KERNEL |
+                                            shost->cmd_pool->gfp_mask);
+       if (!cmd->sense_buffer)
+               goto fail2;
+
+       list_add(&cmd->list, &shost->free_list);
        return 0;
 
  fail2:
-       if (!--pool->users)
-               kmem_cache_destroy(pool->slab);
-       return -ENOMEM;
+       if (cmd)
+               kmem_cache_free(shost->cmd_pool->cmd_slab, cmd);
+       mutex_lock(&host_cmd_pool_mutex);
+       if (!--pool->users) {
+               kmem_cache_destroy(pool->cmd_slab);
+               kmem_cache_destroy(pool->sense_slab);
+       }
  fail:
        mutex_unlock(&host_cmd_pool_mutex);
        return -ENOMEM;
-
 }
 
 /**
@@ -343,12 +384,16 @@ void scsi_destroy_command_freelist(struct Scsi_Host *shost)
 
                cmd = list_entry(shost->free_list.next, struct scsi_cmnd, list);
                list_del_init(&cmd->list);
-               kmem_cache_free(shost->cmd_pool->slab, cmd);
+               kmem_cache_free(shost->cmd_pool->sense_slab,
+                               cmd->sense_buffer);
+               kmem_cache_free(shost->cmd_pool->cmd_slab, cmd);
        }
 
        mutex_lock(&host_cmd_pool_mutex);
-       if (!--shost->cmd_pool->users)
-               kmem_cache_destroy(shost->cmd_pool->slab);
+       if (!--shost->cmd_pool->users) {
+               kmem_cache_destroy(shost->cmd_pool->cmd_slab);
+               kmem_cache_destroy(shost->cmd_pool->sense_slab);
+       }
        mutex_unlock(&host_cmd_pool_mutex);
 }
 
@@ -712,7 +757,7 @@ void scsi_finish_command(struct scsi_cmnd *cmd)
                                "Notifying upper driver of completion "
                                "(result %x)\n", cmd->result));
 
-       good_bytes = cmd->request_bufflen;
+       good_bytes = scsi_bufflen(cmd) + cmd->request->extra_len;
         if (cmd->request->cmd_type != REQ_TYPE_BLOCK_PC) {
                drv = scsi_cmd_to_driver(cmd);
                if (drv->done)
@@ -924,9 +969,10 @@ void starget_for_each_device(struct scsi_target *starget, void *data,
 EXPORT_SYMBOL(starget_for_each_device);
 
 /**
- * __starget_for_each_device  -  helper to walk all devices of a target
- *                              (UNLOCKED)
+ * __starget_for_each_device - helper to walk all devices of a target (UNLOCKED)
  * @starget:   target whose devices we want to iterate over.
+ * @data:      parameter for callback @fn()
+ * @fn:                callback function that is invoked for each device
  *
  * This traverses over each device of @starget.  It does _not_
  * take a reference on the scsi_device, so the whole loop must be
@@ -960,7 +1006,7 @@ EXPORT_SYMBOL(__starget_for_each_device);
  * reference.  You must hold the host's host_lock over this call and
  * any access to the returned scsi_device.
  *
- * Note:  The only reason why drivers would want to use this is because
+ * Note:  The only reason why drivers should use this is because
  * they need to access the device list in irq context.  Otherwise you
  * really want to use scsi_device_lookup_by_target instead.
  **/