[SCSI] add support for per-host cmd pools
authorChristoph Hellwig <hch@infradead.org>
Thu, 20 Feb 2014 22:21:01 +0000 (14:21 -0800)
committerJames Bottomley <JBottomley@Parallels.com>
Thu, 27 Mar 2014 15:26:33 +0000 (08:26 -0700)
This allows drivers to specify the size of their per-command private
data in the host template and then get extra memory allocated for
each command instead of needing another allocation in ->queuecommand.

With the current SCSI code that already does multiple allocations for
each command this probably doesn't make a big performance impact, but
it allows to clean up the drivers, and prepare them for using the
blk-mq infrastructure where the common allocation will make a difference.

Signed-off-by: Christoph Hellwig <hch@lst.de>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
drivers/scsi/scsi.c
include/scsi/scsi_host.h

index 586c241c941e2e4de313fb0f5224065dbde0bd1b..c4d632c27a3ecdf2c7b89020a26d16a4d029c8f8 100644 (file)
@@ -331,46 +331,103 @@ void scsi_put_command(struct scsi_cmnd *cmd)
 }
 EXPORT_SYMBOL(scsi_put_command);
 
-static struct scsi_host_cmd_pool *scsi_get_host_cmd_pool(gfp_t gfp_mask)
+static struct scsi_host_cmd_pool *
+scsi_find_host_cmd_pool(struct Scsi_Host *shost)
 {
+       if (shost->hostt->cmd_size)
+               return shost->hostt->cmd_pool;
+       if (shost->unchecked_isa_dma)
+               return &scsi_cmd_dma_pool;
+       return &scsi_cmd_pool;
+}
+
+static void
+scsi_free_host_cmd_pool(struct scsi_host_cmd_pool *pool)
+{
+       kfree(pool->sense_name);
+       kfree(pool->cmd_name);
+       kfree(pool);
+}
+
+static struct scsi_host_cmd_pool *
+scsi_alloc_host_cmd_pool(struct Scsi_Host *shost)
+{
+       struct scsi_host_template *hostt = shost->hostt;
+       struct scsi_host_cmd_pool *pool;
+
+       pool = kzalloc(sizeof(*pool), GFP_KERNEL);
+       if (!pool)
+               return NULL;
+
+       pool->cmd_name = kasprintf(GFP_KERNEL, "%s_cmd", hostt->name);
+       pool->sense_name = kasprintf(GFP_KERNEL, "%s_sense", hostt->name);
+       if (!pool->cmd_name || !pool->sense_name) {
+               scsi_free_host_cmd_pool(pool);
+               return NULL;
+       }
+
+       pool->slab_flags = SLAB_HWCACHE_ALIGN;
+       if (shost->unchecked_isa_dma) {
+               pool->slab_flags |= SLAB_CACHE_DMA;
+               pool->gfp_mask = __GFP_DMA;
+       }
+       return pool;
+}
+
+static struct scsi_host_cmd_pool *
+scsi_get_host_cmd_pool(struct Scsi_Host *shost)
+{
+       struct scsi_host_template *hostt = shost->hostt;
        struct scsi_host_cmd_pool *retval = NULL, *pool;
+       size_t cmd_size = sizeof(struct scsi_cmnd) + hostt->cmd_size;
+
        /*
         * Select a command slab for this host and create it if not
         * yet existent.
         */
        mutex_lock(&host_cmd_pool_mutex);
-       pool = (gfp_mask & __GFP_DMA) ? &scsi_cmd_dma_pool :
-               &scsi_cmd_pool;
+       pool = scsi_find_host_cmd_pool(shost);
+       if (!pool) {
+               pool = scsi_alloc_host_cmd_pool(shost);
+               if (!pool)
+                       goto out;
+       }
+
        if (!pool->users) {
-               pool->cmd_slab = kmem_cache_create(pool->cmd_name,
-                                                  sizeof(struct scsi_cmnd), 0,
+               pool->cmd_slab = kmem_cache_create(pool->cmd_name, cmd_size, 0,
                                                   pool->slab_flags, NULL);
                if (!pool->cmd_slab)
-                       goto fail;
+                       goto out_free_pool;
 
                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;
-               }
+               if (!pool->sense_slab)
+                       goto out_free_slab;
        }
 
        pool->users++;
        retval = pool;
- fail:
+out:
        mutex_unlock(&host_cmd_pool_mutex);
        return retval;
+
+out_free_slab:
+       kmem_cache_destroy(pool->cmd_slab);
+out_free_pool:
+       if (hostt->cmd_size)
+               scsi_free_host_cmd_pool(pool);
+       goto out;
 }
 
-static void scsi_put_host_cmd_pool(gfp_t gfp_mask)
+static void scsi_put_host_cmd_pool(struct Scsi_Host *shost)
 {
+       struct scsi_host_template *hostt = shost->hostt;
        struct scsi_host_cmd_pool *pool;
 
        mutex_lock(&host_cmd_pool_mutex);
-       pool = (gfp_mask & __GFP_DMA) ? &scsi_cmd_dma_pool :
-               &scsi_cmd_pool;
+       pool = scsi_find_host_cmd_pool(shost);
+
        /*
         * This may happen if a driver has a mismatched get and put
         * of the command pool; the driver should be implicated in
@@ -381,6 +438,8 @@ static void scsi_put_host_cmd_pool(gfp_t gfp_mask)
        if (!--pool->users) {
                kmem_cache_destroy(pool->cmd_slab);
                kmem_cache_destroy(pool->sense_slab);
+               if (hostt->cmd_size)
+                       scsi_free_host_cmd_pool(pool);
        }
        mutex_unlock(&host_cmd_pool_mutex);
 }
@@ -397,14 +456,13 @@ static void scsi_put_host_cmd_pool(gfp_t gfp_mask)
  */
 int scsi_setup_command_freelist(struct Scsi_Host *shost)
 {
-       struct scsi_cmnd *cmd;
        const gfp_t gfp_mask = shost->unchecked_isa_dma ? GFP_DMA : GFP_KERNEL;
+       struct scsi_cmnd *cmd;
 
        spin_lock_init(&shost->free_list_lock);
        INIT_LIST_HEAD(&shost->free_list);
 
-       shost->cmd_pool = scsi_get_host_cmd_pool(gfp_mask);
-
+       shost->cmd_pool = scsi_get_host_cmd_pool(shost);
        if (!shost->cmd_pool)
                return -ENOMEM;
 
@@ -413,7 +471,7 @@ int scsi_setup_command_freelist(struct Scsi_Host *shost)
         */
        cmd = scsi_host_alloc_command(shost, gfp_mask);
        if (!cmd) {
-               scsi_put_host_cmd_pool(gfp_mask);
+               scsi_put_host_cmd_pool(shost);
                shost->cmd_pool = NULL;
                return -ENOMEM;
        }
@@ -442,7 +500,7 @@ void scsi_destroy_command_freelist(struct Scsi_Host *shost)
                scsi_host_free_command(shost, cmd);
        }
        shost->cmd_pool = NULL;
-       scsi_put_host_cmd_pool(shost->unchecked_isa_dma ? GFP_DMA : GFP_KERNEL);
+       scsi_put_host_cmd_pool(shost);
 }
 
 #ifdef CONFIG_SCSI_LOGGING
index 53075e5039e6e3ff2a246064e78394f790abbe69..94844fc77b9759f20c6f51c6bcb16e008f2167b3 100644 (file)
@@ -15,6 +15,7 @@ struct completion;
 struct module;
 struct scsi_cmnd;
 struct scsi_device;
+struct scsi_host_cmd_pool;
 struct scsi_target;
 struct Scsi_Host;
 struct scsi_host_cmd_pool;
@@ -524,6 +525,12 @@ struct scsi_host_template {
         *   scsi_netlink.h
         */
        u64 vendor_id;
+
+       /*
+        * Additional per-command data allocated for the driver.
+        */
+       unsigned int cmd_size;
+       struct scsi_host_cmd_pool *cmd_pool;
 };
 
 /*