|
@@ -98,163 +98,6 @@ EXPORT_SYMBOL(scsi_sd_probe_domain);
|
|
|
ASYNC_DOMAIN_EXCLUSIVE(scsi_sd_pm_domain);
|
|
|
EXPORT_SYMBOL(scsi_sd_pm_domain);
|
|
|
|
|
|
-struct scsi_host_cmd_pool {
|
|
|
- struct kmem_cache *cmd_slab;
|
|
|
- unsigned int users;
|
|
|
- char *cmd_name;
|
|
|
-};
|
|
|
-
|
|
|
-static struct scsi_host_cmd_pool scsi_cmd_pool = {
|
|
|
- .cmd_name = "scsi_cmd_cache",
|
|
|
-};
|
|
|
-
|
|
|
-static DEFINE_MUTEX(host_cmd_pool_mutex);
|
|
|
-
|
|
|
-/**
|
|
|
- * scsi_host_free_command - internal function to release a command
|
|
|
- * @shost: host to free the command for
|
|
|
- * @cmd: command to release
|
|
|
- *
|
|
|
- * the command must previously have been allocated by
|
|
|
- * scsi_host_alloc_command.
|
|
|
- */
|
|
|
-static void
|
|
|
-scsi_host_free_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
|
|
|
-{
|
|
|
- struct scsi_host_cmd_pool *pool = shost->cmd_pool;
|
|
|
-
|
|
|
- if (cmd->prot_sdb)
|
|
|
- kmem_cache_free(scsi_sdb_cache, cmd->prot_sdb);
|
|
|
- scsi_free_sense_buffer(shost, cmd->sense_buffer);
|
|
|
- kmem_cache_free(pool->cmd_slab, cmd);
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * scsi_host_alloc_command - internal function to allocate command
|
|
|
- * @shost: SCSI host whose pool to allocate from
|
|
|
- * @gfp_mask: mask for the allocation
|
|
|
- *
|
|
|
- * Returns a fully allocated command with sense buffer and protection
|
|
|
- * data buffer (where applicable) or NULL on failure
|
|
|
- */
|
|
|
-static struct scsi_cmnd *
|
|
|
-scsi_host_alloc_command(struct Scsi_Host *shost, gfp_t gfp_mask)
|
|
|
-{
|
|
|
- struct scsi_host_cmd_pool *pool = shost->cmd_pool;
|
|
|
- struct scsi_cmnd *cmd;
|
|
|
-
|
|
|
- cmd = kmem_cache_zalloc(pool->cmd_slab, gfp_mask);
|
|
|
- if (!cmd)
|
|
|
- goto fail;
|
|
|
-
|
|
|
- cmd->sense_buffer = scsi_alloc_sense_buffer(shost, gfp_mask,
|
|
|
- NUMA_NO_NODE);
|
|
|
- if (!cmd->sense_buffer)
|
|
|
- goto fail_free_cmd;
|
|
|
-
|
|
|
- if (scsi_host_get_prot(shost) >= SHOST_DIX_TYPE0_PROTECTION) {
|
|
|
- cmd->prot_sdb = kmem_cache_zalloc(scsi_sdb_cache, gfp_mask);
|
|
|
- if (!cmd->prot_sdb)
|
|
|
- goto fail_free_sense;
|
|
|
- }
|
|
|
-
|
|
|
- return cmd;
|
|
|
-
|
|
|
-fail_free_sense:
|
|
|
- scsi_free_sense_buffer(shost, cmd->sense_buffer);
|
|
|
-fail_free_cmd:
|
|
|
- kmem_cache_free(pool->cmd_slab, cmd);
|
|
|
-fail:
|
|
|
- return NULL;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * __scsi_get_command - Allocate a struct scsi_cmnd
|
|
|
- * @shost: host to transmit command
|
|
|
- * @gfp_mask: allocation mask
|
|
|
- *
|
|
|
- * Description: allocate a struct scsi_cmd from host's slab, recycling from the
|
|
|
- * host's free_list if necessary.
|
|
|
- */
|
|
|
-static struct scsi_cmnd *
|
|
|
-__scsi_get_command(struct Scsi_Host *shost, gfp_t gfp_mask)
|
|
|
-{
|
|
|
- struct scsi_cmnd *cmd = scsi_host_alloc_command(shost, gfp_mask);
|
|
|
-
|
|
|
- if (unlikely(!cmd)) {
|
|
|
- unsigned long flags;
|
|
|
-
|
|
|
- spin_lock_irqsave(&shost->free_list_lock, flags);
|
|
|
- if (likely(!list_empty(&shost->free_list))) {
|
|
|
- cmd = list_entry(shost->free_list.next,
|
|
|
- struct scsi_cmnd, list);
|
|
|
- list_del_init(&cmd->list);
|
|
|
- }
|
|
|
- spin_unlock_irqrestore(&shost->free_list_lock, flags);
|
|
|
-
|
|
|
- if (cmd) {
|
|
|
- void *buf, *prot;
|
|
|
-
|
|
|
- buf = cmd->sense_buffer;
|
|
|
- prot = cmd->prot_sdb;
|
|
|
-
|
|
|
- memset(cmd, 0, sizeof(*cmd));
|
|
|
-
|
|
|
- cmd->sense_buffer = buf;
|
|
|
- cmd->prot_sdb = prot;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return cmd;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * scsi_get_command - Allocate and setup a scsi command block
|
|
|
- * @dev: parent scsi device
|
|
|
- * @gfp_mask: allocator flags
|
|
|
- *
|
|
|
- * Returns: The allocated scsi command structure.
|
|
|
- */
|
|
|
-struct scsi_cmnd *scsi_get_command(struct scsi_device *dev, gfp_t gfp_mask)
|
|
|
-{
|
|
|
- struct scsi_cmnd *cmd = __scsi_get_command(dev->host, gfp_mask);
|
|
|
- unsigned long flags;
|
|
|
-
|
|
|
- if (unlikely(cmd == NULL))
|
|
|
- return NULL;
|
|
|
-
|
|
|
- cmd->device = dev;
|
|
|
- INIT_LIST_HEAD(&cmd->list);
|
|
|
- INIT_DELAYED_WORK(&cmd->abort_work, scmd_eh_abort_handler);
|
|
|
- spin_lock_irqsave(&dev->list_lock, flags);
|
|
|
- list_add_tail(&cmd->list, &dev->cmd_list);
|
|
|
- spin_unlock_irqrestore(&dev->list_lock, flags);
|
|
|
- cmd->jiffies_at_alloc = jiffies;
|
|
|
- return cmd;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * __scsi_put_command - Free a struct scsi_cmnd
|
|
|
- * @shost: dev->host
|
|
|
- * @cmd: Command to free
|
|
|
- */
|
|
|
-static void __scsi_put_command(struct Scsi_Host *shost, struct scsi_cmnd *cmd)
|
|
|
-{
|
|
|
- unsigned long flags;
|
|
|
-
|
|
|
- if (unlikely(list_empty(&shost->free_list))) {
|
|
|
- spin_lock_irqsave(&shost->free_list_lock, flags);
|
|
|
- if (list_empty(&shost->free_list)) {
|
|
|
- list_add(&cmd->list, &shost->free_list);
|
|
|
- cmd = NULL;
|
|
|
- }
|
|
|
- spin_unlock_irqrestore(&shost->free_list_lock, flags);
|
|
|
- }
|
|
|
-
|
|
|
- if (likely(cmd != NULL))
|
|
|
- scsi_host_free_command(shost, cmd);
|
|
|
-}
|
|
|
-
|
|
|
/**
|
|
|
* scsi_put_command - Free a scsi command block
|
|
|
* @cmd: command block to free
|
|
@@ -274,168 +117,6 @@ void scsi_put_command(struct scsi_cmnd *cmd)
|
|
|
spin_unlock_irqrestore(&cmd->device->list_lock, flags);
|
|
|
|
|
|
BUG_ON(delayed_work_pending(&cmd->abort_work));
|
|
|
-
|
|
|
- __scsi_put_command(cmd->device->host, cmd);
|
|
|
-}
|
|
|
-
|
|
|
-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;
|
|
|
- return &scsi_cmd_pool;
|
|
|
-}
|
|
|
-
|
|
|
-static void
|
|
|
-scsi_free_host_cmd_pool(struct scsi_host_cmd_pool *pool)
|
|
|
-{
|
|
|
- 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->proc_name);
|
|
|
- if (!pool->cmd_name) {
|
|
|
- scsi_free_host_cmd_pool(pool);
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-
|
|
|
- if (hostt->cmd_size)
|
|
|
- hostt->cmd_pool = pool;
|
|
|
-
|
|
|
- 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 = 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, cmd_size, 0,
|
|
|
- SLAB_HWCACHE_ALIGN, NULL);
|
|
|
- if (!pool->cmd_slab)
|
|
|
- goto out_free_pool;
|
|
|
- }
|
|
|
-
|
|
|
- pool->users++;
|
|
|
- retval = pool;
|
|
|
-out:
|
|
|
- mutex_unlock(&host_cmd_pool_mutex);
|
|
|
- return retval;
|
|
|
-
|
|
|
-out_free_pool:
|
|
|
- if (hostt->cmd_size) {
|
|
|
- scsi_free_host_cmd_pool(pool);
|
|
|
- hostt->cmd_pool = NULL;
|
|
|
- }
|
|
|
- goto out;
|
|
|
-}
|
|
|
-
|
|
|
-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 = 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
|
|
|
- * the stack trace
|
|
|
- */
|
|
|
- BUG_ON(pool->users == 0);
|
|
|
-
|
|
|
- if (!--pool->users) {
|
|
|
- kmem_cache_destroy(pool->cmd_slab);
|
|
|
- if (hostt->cmd_size) {
|
|
|
- scsi_free_host_cmd_pool(pool);
|
|
|
- hostt->cmd_pool = NULL;
|
|
|
- }
|
|
|
- }
|
|
|
- mutex_unlock(&host_cmd_pool_mutex);
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * scsi_setup_command_freelist - Setup the command freelist for a scsi host.
|
|
|
- * @shost: host to allocate the freelist for.
|
|
|
- *
|
|
|
- * Description: The command freelist protects against system-wide out of memory
|
|
|
- * deadlock by preallocating one SCSI command structure for each host, so the
|
|
|
- * system can always write to a swap file on a device associated with that host.
|
|
|
- *
|
|
|
- * Returns: Nothing.
|
|
|
- */
|
|
|
-int scsi_setup_command_freelist(struct Scsi_Host *shost)
|
|
|
-{
|
|
|
- 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(shost);
|
|
|
- if (!shost->cmd_pool)
|
|
|
- return -ENOMEM;
|
|
|
-
|
|
|
- /*
|
|
|
- * Get one backup command for this host.
|
|
|
- */
|
|
|
- cmd = scsi_host_alloc_command(shost, GFP_KERNEL);
|
|
|
- if (!cmd) {
|
|
|
- scsi_put_host_cmd_pool(shost);
|
|
|
- shost->cmd_pool = NULL;
|
|
|
- return -ENOMEM;
|
|
|
- }
|
|
|
- list_add(&cmd->list, &shost->free_list);
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * scsi_destroy_command_freelist - Release the command freelist for a scsi host.
|
|
|
- * @shost: host whose freelist is going to be destroyed
|
|
|
- */
|
|
|
-void scsi_destroy_command_freelist(struct Scsi_Host *shost)
|
|
|
-{
|
|
|
- /*
|
|
|
- * If cmd_pool is NULL the free list was not initialized, so
|
|
|
- * do not attempt to release resources.
|
|
|
- */
|
|
|
- if (!shost->cmd_pool)
|
|
|
- return;
|
|
|
-
|
|
|
- while (!list_empty(&shost->free_list)) {
|
|
|
- struct scsi_cmnd *cmd;
|
|
|
-
|
|
|
- cmd = list_entry(shost->free_list.next, struct scsi_cmnd, list);
|
|
|
- list_del_init(&cmd->list);
|
|
|
- scsi_host_free_command(shost, cmd);
|
|
|
- }
|
|
|
- shost->cmd_pool = NULL;
|
|
|
- scsi_put_host_cmd_pool(shost);
|
|
|
}
|
|
|
|
|
|
#ifdef CONFIG_SCSI_LOGGING
|