]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/scsi/scsi_lib.c
scsi: Only add commands to the device command list if required by the LLD
[karo-tx-linux.git] / drivers / scsi / scsi_lib.c
index e31f1cc90b815b28a332d1a6915c82e19519a62f..1470c93a87e7f4024618fed3a8ee0570ade04485 100644 (file)
@@ -45,23 +45,23 @@ static struct kmem_cache *scsi_sense_isadma_cache;
 static DEFINE_MUTEX(scsi_sense_cache_mutex);
 
 static inline struct kmem_cache *
-scsi_select_sense_cache(struct Scsi_Host *shost)
+scsi_select_sense_cache(bool unchecked_isa_dma)
 {
-       return shost->unchecked_isa_dma ?
-               scsi_sense_isadma_cache : scsi_sense_cache;
+       return unchecked_isa_dma ? scsi_sense_isadma_cache : scsi_sense_cache;
 }
 
-static void scsi_free_sense_buffer(struct Scsi_Host *shost,
-               unsigned char *sense_buffer)
+static void scsi_free_sense_buffer(bool unchecked_isa_dma,
+                                  unsigned char *sense_buffer)
 {
-       kmem_cache_free(scsi_select_sense_cache(shost), sense_buffer);
+       kmem_cache_free(scsi_select_sense_cache(unchecked_isa_dma),
+                       sense_buffer);
 }
 
-static unsigned char *scsi_alloc_sense_buffer(struct Scsi_Host *shost,
+static unsigned char *scsi_alloc_sense_buffer(bool unchecked_isa_dma,
        gfp_t gfp_mask, int numa_node)
 {
-       return kmem_cache_alloc_node(scsi_select_sense_cache(shost), gfp_mask,
-                       numa_node);
+       return kmem_cache_alloc_node(scsi_select_sense_cache(unchecked_isa_dma),
+                                    gfp_mask, numa_node);
 }
 
 int scsi_init_sense_cache(struct Scsi_Host *shost)
@@ -69,7 +69,7 @@ int scsi_init_sense_cache(struct Scsi_Host *shost)
        struct kmem_cache *cache;
        int ret = 0;
 
-       cache = scsi_select_sense_cache(shost);
+       cache = scsi_select_sense_cache(shost->unchecked_isa_dma);
        if (cache)
                return 0;
 
@@ -584,19 +584,9 @@ static void scsi_mq_free_sgtables(struct scsi_cmnd *cmd)
 
 static void scsi_mq_uninit_cmd(struct scsi_cmnd *cmd)
 {
-       struct scsi_device *sdev = cmd->device;
-       struct Scsi_Host *shost = sdev->host;
-       unsigned long flags;
-
        scsi_mq_free_sgtables(cmd);
        scsi_uninit_cmd(cmd);
-
-       if (shost->use_cmd_list) {
-               BUG_ON(list_empty(&cmd->list));
-               spin_lock_irqsave(&sdev->list_lock, flags);
-               list_del_init(&cmd->list);
-               spin_unlock_irqrestore(&sdev->list_lock, flags);
-       }
+       scsi_del_cmd_from_list(cmd);
 }
 
 /*
@@ -1134,11 +1124,40 @@ err_exit:
 }
 EXPORT_SYMBOL(scsi_init_io);
 
+/* Add a command to the list used by the aacraid and dpt_i2o drivers */
+void scsi_add_cmd_to_list(struct scsi_cmnd *cmd)
+{
+       struct scsi_device *sdev = cmd->device;
+       struct Scsi_Host *shost = sdev->host;
+       unsigned long flags;
+
+       if (shost->use_cmd_list) {
+               spin_lock_irqsave(&sdev->list_lock, flags);
+               list_add_tail(&cmd->list, &sdev->cmd_list);
+               spin_unlock_irqrestore(&sdev->list_lock, flags);
+       }
+}
+
+/* Remove a command from the list used by the aacraid and dpt_i2o drivers */
+void scsi_del_cmd_from_list(struct scsi_cmnd *cmd)
+{
+       struct scsi_device *sdev = cmd->device;
+       struct Scsi_Host *shost = sdev->host;
+       unsigned long flags;
+
+       if (shost->use_cmd_list) {
+               spin_lock_irqsave(&sdev->list_lock, flags);
+               BUG_ON(list_empty(&cmd->list));
+               list_del_init(&cmd->list);
+               spin_unlock_irqrestore(&sdev->list_lock, flags);
+       }
+}
+
 void scsi_init_command(struct scsi_device *dev, struct scsi_cmnd *cmd)
 {
        void *buf = cmd->sense_buffer;
        void *prot = cmd->prot_sdb;
-       unsigned long flags;
+       unsigned int unchecked_isa_dma = cmd->flags & SCMD_UNCHECKED_ISA_DMA;
 
        /* zero out the cmd, except for the embedded scsi_request */
        memset((char *)cmd + sizeof(cmd->req), 0,
@@ -1147,12 +1166,11 @@ void scsi_init_command(struct scsi_device *dev, struct scsi_cmnd *cmd)
        cmd->device = dev;
        cmd->sense_buffer = buf;
        cmd->prot_sdb = prot;
+       cmd->flags = unchecked_isa_dma;
        INIT_DELAYED_WORK(&cmd->abort_work, scmd_eh_abort_handler);
        cmd->jiffies_at_alloc = jiffies;
 
-       spin_lock_irqsave(&dev->list_lock, flags);
-       list_add_tail(&cmd->list, &dev->cmd_list);
-       spin_unlock_irqrestore(&dev->list_lock, flags);
+       scsi_add_cmd_to_list(cmd);
 }
 
 static int scsi_setup_scsi_cmnd(struct scsi_device *sdev, struct request *req)
@@ -1847,17 +1865,19 @@ static int scsi_mq_prep_fn(struct request *req)
        struct scsi_device *sdev = req->q->queuedata;
        struct Scsi_Host *shost = sdev->host;
        unsigned char *sense_buf = cmd->sense_buffer;
+       unsigned int unchecked_isa_dma = cmd->flags & SCMD_UNCHECKED_ISA_DMA;
        struct scatterlist *sg;
 
        /* zero out the cmd, except for the embedded scsi_request */
        memset((char *)cmd + sizeof(cmd->req), 0,
-               sizeof(*cmd) - sizeof(cmd->req));
+               sizeof(*cmd) - sizeof(cmd->req) + shost->hostt->cmd_size);
 
        req->special = cmd;
 
        cmd->request = req;
        cmd->device = sdev;
        cmd->sense_buffer = sense_buf;
+       cmd->flags = unchecked_isa_dma;
 
        cmd->tag = req->tag;
 
@@ -1867,11 +1887,7 @@ static int scsi_mq_prep_fn(struct request *req)
        INIT_DELAYED_WORK(&cmd->abort_work, scmd_eh_abort_handler);
        cmd->jiffies_at_alloc = jiffies;
 
-       if (shost->use_cmd_list) {
-               spin_lock_irq(&sdev->list_lock);
-               list_add_tail(&cmd->list, &sdev->cmd_list);
-               spin_unlock_irq(&sdev->list_lock);
-       }
+       scsi_add_cmd_to_list(cmd);
 
        sg = (void *)cmd + sizeof(struct scsi_cmnd) + shost->hostt->cmd_size;
        cmd->sdb.table.sgl = sg;
@@ -2004,10 +2020,13 @@ static int scsi_init_request(struct blk_mq_tag_set *set, struct request *rq,
                unsigned int hctx_idx, unsigned int numa_node)
 {
        struct Scsi_Host *shost = set->driver_data;
+       const bool unchecked_isa_dma = shost->unchecked_isa_dma;
        struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq);
 
-       cmd->sense_buffer =
-               scsi_alloc_sense_buffer(shost, GFP_KERNEL, numa_node);
+       if (unchecked_isa_dma)
+               cmd->flags |= SCMD_UNCHECKED_ISA_DMA;
+       cmd->sense_buffer = scsi_alloc_sense_buffer(unchecked_isa_dma,
+                                                   GFP_KERNEL, numa_node);
        if (!cmd->sense_buffer)
                return -ENOMEM;
        cmd->req.sense = cmd->sense_buffer;
@@ -2017,10 +2036,10 @@ static int scsi_init_request(struct blk_mq_tag_set *set, struct request *rq,
 static void scsi_exit_request(struct blk_mq_tag_set *set, struct request *rq,
                unsigned int hctx_idx)
 {
-       struct Scsi_Host *shost = set->driver_data;
        struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq);
 
-       scsi_free_sense_buffer(shost, cmd->sense_buffer);
+       scsi_free_sense_buffer(cmd->flags & SCMD_UNCHECKED_ISA_DMA,
+                              cmd->sense_buffer);
 }
 
 static int scsi_map_queues(struct blk_mq_tag_set *set)
@@ -2093,11 +2112,15 @@ EXPORT_SYMBOL_GPL(__scsi_init_queue);
 static int scsi_init_rq(struct request_queue *q, struct request *rq, gfp_t gfp)
 {
        struct Scsi_Host *shost = q->rq_alloc_data;
+       const bool unchecked_isa_dma = shost->unchecked_isa_dma;
        struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq);
 
        memset(cmd, 0, sizeof(*cmd));
 
-       cmd->sense_buffer = scsi_alloc_sense_buffer(shost, gfp, NUMA_NO_NODE);
+       if (unchecked_isa_dma)
+               cmd->flags |= SCMD_UNCHECKED_ISA_DMA;
+       cmd->sense_buffer = scsi_alloc_sense_buffer(unchecked_isa_dma, gfp,
+                                                   NUMA_NO_NODE);
        if (!cmd->sense_buffer)
                goto fail;
        cmd->req.sense = cmd->sense_buffer;
@@ -2111,19 +2134,19 @@ static int scsi_init_rq(struct request_queue *q, struct request *rq, gfp_t gfp)
        return 0;
 
 fail_free_sense:
-       scsi_free_sense_buffer(shost, cmd->sense_buffer);
+       scsi_free_sense_buffer(unchecked_isa_dma, cmd->sense_buffer);
 fail:
        return -ENOMEM;
 }
 
 static void scsi_exit_rq(struct request_queue *q, struct request *rq)
 {
-       struct Scsi_Host *shost = q->rq_alloc_data;
        struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(rq);
 
        if (cmd->prot_sdb)
                kmem_cache_free(scsi_sdb_cache, cmd->prot_sdb);
-       scsi_free_sense_buffer(shost, cmd->sense_buffer);
+       scsi_free_sense_buffer(cmd->flags & SCMD_UNCHECKED_ISA_DMA,
+                              cmd->sense_buffer);
 }
 
 struct request_queue *scsi_alloc_queue(struct scsi_device *sdev)
@@ -2614,7 +2637,6 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state)
                case SDEV_QUIESCE:
                case SDEV_OFFLINE:
                case SDEV_TRANSPORT_OFFLINE:
-               case SDEV_BLOCK:
                        break;
                default:
                        goto illegal;
@@ -2628,6 +2650,7 @@ scsi_device_set_state(struct scsi_device *sdev, enum scsi_device_state state)
                case SDEV_OFFLINE:
                case SDEV_TRANSPORT_OFFLINE:
                case SDEV_CANCEL:
+               case SDEV_BLOCK:
                case SDEV_CREATED_BLOCK:
                        break;
                default:
@@ -2871,7 +2894,12 @@ static void scsi_wait_for_queuecommand(struct scsi_device *sdev)
 int
 scsi_device_quiesce(struct scsi_device *sdev)
 {
-       int err = scsi_device_set_state(sdev, SDEV_QUIESCE);
+       int err;
+
+       mutex_lock(&sdev->state_mutex);
+       err = scsi_device_set_state(sdev, SDEV_QUIESCE);
+       mutex_unlock(&sdev->state_mutex);
+
        if (err)
                return err;
 
@@ -2899,10 +2927,11 @@ void scsi_device_resume(struct scsi_device *sdev)
         * so assume the state is being managed elsewhere (for example
         * device deleted during suspend)
         */
-       if (sdev->sdev_state != SDEV_QUIESCE ||
-           scsi_device_set_state(sdev, SDEV_RUNNING))
-               return;
-       scsi_run_queue(sdev->request_queue);
+       mutex_lock(&sdev->state_mutex);
+       if (sdev->sdev_state == SDEV_QUIESCE &&
+           scsi_device_set_state(sdev, SDEV_RUNNING) == 0)
+               scsi_run_queue(sdev->request_queue);
+       mutex_unlock(&sdev->state_mutex);
 }
 EXPORT_SYMBOL(scsi_device_resume);
 
@@ -2933,28 +2962,20 @@ scsi_target_resume(struct scsi_target *starget)
 EXPORT_SYMBOL(scsi_target_resume);
 
 /**
- * scsi_internal_device_block - internal function to put a device temporarily into the SDEV_BLOCK state
- * @sdev:      device to block
- * @wait:      Whether or not to wait until ongoing .queuecommand() /
- *             .queue_rq() calls have finished.
- *
- * Block request made by scsi lld's to temporarily stop all
- * scsi commands on the specified device. May sleep.
+ * scsi_internal_device_block_nowait - try to transition to the SDEV_BLOCK state
+ * @sdev: device to block
  *
- * Returns zero if successful or error if not
+ * Pause SCSI command processing on the specified device. Does not sleep.
  *
- * Notes:       
- *     This routine transitions the device to the SDEV_BLOCK state
- *     (which must be a legal transition).  When the device is in this
- *     state, all commands are deferred until the scsi lld reenables
- *     the device with scsi_device_unblock or device_block_tmo fires.
+ * Returns zero if successful or a negative error code upon failure.
  *
- * To do: avoid that scsi_send_eh_cmnd() calls queuecommand() after
- * scsi_internal_device_block() has blocked a SCSI device and also
- * remove the rport mutex lock and unlock calls from srp_queuecommand().
+ * Notes:
+ * This routine transitions the device to the SDEV_BLOCK state (which must be
+ * a legal transition). When the device is in this state, command processing
+ * is paused until the device leaves the SDEV_BLOCK state. See also
+ * scsi_internal_device_unblock_nowait().
  */
-int
-scsi_internal_device_block(struct scsi_device *sdev, bool wait)
+int scsi_internal_device_block_nowait(struct scsi_device *sdev)
 {
        struct request_queue *q = sdev->request_queue;
        unsigned long flags;
@@ -2974,45 +2995,86 @@ scsi_internal_device_block(struct scsi_device *sdev, bool wait)
         * request queue. 
         */
        if (q->mq_ops) {
-               if (wait)
-                       blk_mq_quiesce_queue(q);
-               else
-                       blk_mq_stop_hw_queues(q);
+               blk_mq_stop_hw_queues(q);
        } else {
                spin_lock_irqsave(q->queue_lock, flags);
                blk_stop_queue(q);
                spin_unlock_irqrestore(q->queue_lock, flags);
-               if (wait)
-                       scsi_wait_for_queuecommand(sdev);
        }
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(scsi_internal_device_block);
+EXPORT_SYMBOL_GPL(scsi_internal_device_block_nowait);
+
 /**
- * scsi_internal_device_unblock - resume a device after a block request
- * @sdev:      device to resume
- * @new_state: state to set devices to after unblocking
+ * scsi_internal_device_block - try to transition to the SDEV_BLOCK state
+ * @sdev: device to block
  *
- * Called by scsi lld's or the midlayer to restart the device queue
- * for the previously suspended scsi device.  Called from interrupt or
- * normal process context.
+ * Pause SCSI command processing on the specified device and wait until all
+ * ongoing scsi_request_fn() / scsi_queue_rq() calls have finished. May sleep.
  *
- * Returns zero if successful or error if not.
+ * Returns zero if successful or a negative error code upon failure.
  *
- * Notes:       
- *     This routine transitions the device to the SDEV_RUNNING state
- *     or to one of the offline states (which must be a legal transition)
- *     allowing the midlayer to goose the queue for this device.
+ * Note:
+ * This routine transitions the device to the SDEV_BLOCK state (which must be
+ * a legal transition). When the device is in this state, command processing
+ * is paused until the device leaves the SDEV_BLOCK state. See also
+ * scsi_internal_device_unblock().
+ *
+ * To do: avoid that scsi_send_eh_cmnd() calls queuecommand() after
+ * scsi_internal_device_block() has blocked a SCSI device and also
+ * remove the rport mutex lock and unlock calls from srp_queuecommand().
  */
-int
-scsi_internal_device_unblock(struct scsi_device *sdev,
-                            enum scsi_device_state new_state)
+static int scsi_internal_device_block(struct scsi_device *sdev)
 {
-       struct request_queue *q = sdev->request_queue; 
+       struct request_queue *q = sdev->request_queue;
+       int err;
+
+       mutex_lock(&sdev->state_mutex);
+       err = scsi_internal_device_block_nowait(sdev);
+       if (err == 0) {
+               if (q->mq_ops)
+                       blk_mq_quiesce_queue(q);
+               else
+                       scsi_wait_for_queuecommand(sdev);
+       }
+       mutex_unlock(&sdev->state_mutex);
+
+       return err;
+}
+void scsi_start_queue(struct scsi_device *sdev)
+{
+       struct request_queue *q = sdev->request_queue;
        unsigned long flags;
 
+       if (q->mq_ops) {
+               blk_mq_start_stopped_hw_queues(q, false);
+       } else {
+               spin_lock_irqsave(q->queue_lock, flags);
+               blk_start_queue(q);
+               spin_unlock_irqrestore(q->queue_lock, flags);
+       }
+}
+
+/**
+ * scsi_internal_device_unblock_nowait - resume a device after a block request
+ * @sdev:      device to resume
+ * @new_state: state to set the device to after unblocking
+ *
+ * Restart the device queue for a previously suspended SCSI device. Does not
+ * sleep.
+ *
+ * Returns zero if successful or a negative error code upon failure.
+ *
+ * Notes:
+ * This routine transitions the device to the SDEV_RUNNING state or to one of
+ * the offline states (which must be a legal transition) allowing the midlayer
+ * to goose the queue for this device.
+ */
+int scsi_internal_device_unblock_nowait(struct scsi_device *sdev,
+                                       enum scsi_device_state new_state)
+{
        /*
         * Try to transition the scsi device to SDEV_RUNNING or one of the
         * offlined states and goose the device queue if successful.
@@ -3030,22 +3092,42 @@ scsi_internal_device_unblock(struct scsi_device *sdev,
                 sdev->sdev_state != SDEV_OFFLINE)
                return -EINVAL;
 
-       if (q->mq_ops) {
-               blk_mq_start_stopped_hw_queues(q, false);
-       } else {
-               spin_lock_irqsave(q->queue_lock, flags);
-               blk_start_queue(q);
-               spin_unlock_irqrestore(q->queue_lock, flags);
-       }
+       scsi_start_queue(sdev);
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(scsi_internal_device_unblock);
+EXPORT_SYMBOL_GPL(scsi_internal_device_unblock_nowait);
+
+/**
+ * scsi_internal_device_unblock - resume a device after a block request
+ * @sdev:      device to resume
+ * @new_state: state to set the device to after unblocking
+ *
+ * Restart the device queue for a previously suspended SCSI device. May sleep.
+ *
+ * Returns zero if successful or a negative error code upon failure.
+ *
+ * Notes:
+ * This routine transitions the device to the SDEV_RUNNING state or to one of
+ * the offline states (which must be a legal transition) allowing the midlayer
+ * to goose the queue for this device.
+ */
+static int scsi_internal_device_unblock(struct scsi_device *sdev,
+                                       enum scsi_device_state new_state)
+{
+       int ret;
+
+       mutex_lock(&sdev->state_mutex);
+       ret = scsi_internal_device_unblock_nowait(sdev, new_state);
+       mutex_unlock(&sdev->state_mutex);
+
+       return ret;
+}
 
 static void
 device_block(struct scsi_device *sdev, void *data)
 {
-       scsi_internal_device_block(sdev, true);
+       scsi_internal_device_block(sdev);
 }
 
 static int