]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/scsi/scsi.c
[SCSI] consolidate command allocation in a single place
[linux-2.6-omap-h63xx.git] / drivers / scsi / scsi.c
index ebc019380835d66063f990c1204df3c71b3aba39..2cf9a625f2279ef40b325b2ebb53a561ae2b1a0f 100644 (file)
@@ -141,26 +141,75 @@ 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,
 };
 
 static DEFINE_MUTEX(host_cmd_pool_mutex);
 
+/**
+ * scsi_pool_alloc_command - internal function to get a fully allocated command
+ * @pool:      slab pool to allocate the command from
+ * @gfp_mask:  mask for the allocation
+ *
+ * Returns a fully allocated command (with the allied sense buffer) or
+ * NULL on failure
+ */
+static struct scsi_cmnd *
+scsi_pool_alloc_command(struct scsi_host_cmd_pool *pool, gfp_t gfp_mask)
+{
+       struct scsi_cmnd *cmd;
+
+       cmd = kmem_cache_alloc(pool->cmd_slab, gfp_mask | pool->gfp_mask);
+       if (!cmd)
+               return NULL;
+
+       memset(cmd, 0, sizeof(*cmd));
+
+       cmd->sense_buffer = kmem_cache_alloc(pool->sense_slab,
+                                            gfp_mask | pool->gfp_mask);
+       if (!cmd->sense_buffer) {
+               kmem_cache_free(pool->cmd_slab, cmd);
+               return NULL;
+       }
+
+       return cmd;
+}
+
+/**
+ * scsi_pool_free_command - internal function to release a command
+ * @pool:      slab pool to allocate the command from
+ * @cmd:       command to release
+ *
+ * the command must previously have been allocated by
+ * scsi_pool_alloc_command.
+ */
+static void
+scsi_pool_free_command(struct scsi_host_cmd_pool *pool,
+                        struct scsi_cmnd *cmd)
+{
+       kmem_cache_free(pool->sense_slab, cmd->sense_buffer);
+       kmem_cache_free(pool->cmd_slab, cmd);
+}
+
 /**
  * __scsi_get_command - Allocate a struct scsi_cmnd
  * @shost: host to transmit command
@@ -172,9 +221,9 @@ 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 = scsi_pool_alloc_command(shost->cmd_pool, gfp_mask);
 
        if (unlikely(!cmd)) {
                unsigned long flags;
@@ -186,6 +235,12 @@ 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;
+               }
        }
 
        return cmd;
@@ -212,7 +267,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);
@@ -247,7 +301,7 @@ 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);
+               scsi_pool_free_command(shost->cmd_pool, cmd);
 
        put_device(dev);
 }
@@ -301,11 +355,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 +377,22 @@ 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 = scsi_pool_alloc_command(shost->cmd_pool, GFP_KERNEL);
        if (!cmd)
                goto fail2;
-       list_add(&cmd->list, &shost->free_list);                
+
+       list_add(&cmd->list, &shost->free_list);
        return 0;
 
  fail2:
-       if (!--pool->users)
-               kmem_cache_destroy(pool->slab);
-       return -ENOMEM;
+       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 +406,14 @@ 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);
+               scsi_pool_free_command(shost->cmd_pool, 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 +777,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 +989,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