Merge patch series "scsi: fixes for targets with many LUNs, and scsi_target_block...
authorMartin K. Petersen <martin.petersen@oracle.com>
Fri, 16 Jun 2023 16:21:04 +0000 (12:21 -0400)
committerMartin K. Petersen <martin.petersen@oracle.com>
Fri, 16 Jun 2023 16:21:04 +0000 (12:21 -0400)
Martin Wilck <mwilck@suse.com> says:

This patch series addresses some issues we saw in a test setup with a
large number of SCSI LUNs. The first two patches simply increase the
number of available sg and bsg devices. 3-5 fix a large delay we
encountered between blocking a Fibre Channel remote port and the
dev_loss_tmo. 6 renames scsi_target_block() to scsi_block_targets(),
and makes additional changes to this API, as suggested in the review
of the v2 series. 7 improves a warning message.

Link: https://lore.kernel.org/r/20230614103616.31857-1-mwilck@suse.com
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
1  2 
drivers/scsi/scsi_lib.c
include/scsi/scsi_device.h

diff --combined drivers/scsi/scsi_lib.c
@@@ -122,9 -122,11 +122,9 @@@ static void scsi_mq_requeue_cmd(struct 
                WARN_ON_ONCE(true);
        }
  
 -      if (msecs) {
 -              blk_mq_requeue_request(rq, false);
 +      blk_mq_requeue_request(rq, false);
 +      if (!scsi_host_in_recovery(cmd->device->host))
                blk_mq_delay_kick_requeue_list(rq->q, msecs);
 -      } else
 -              blk_mq_requeue_request(rq, true);
  }
  
  /**
@@@ -163,8 -165,7 +163,8 @@@ static void __scsi_queue_insert(struct 
         */
        cmd->result = 0;
  
 -      blk_mq_requeue_request(scsi_cmd_to_rq(cmd), true);
 +      blk_mq_requeue_request(scsi_cmd_to_rq(cmd),
 +                             !scsi_host_in_recovery(cmd->device->host));
  }
  
  /**
@@@ -452,7 -453,6 +452,7 @@@ static void scsi_run_queue(struct reque
        if (!list_empty(&sdev->host->starved_list))
                scsi_starved_list_run(sdev->host);
  
 +      blk_mq_kick_requeue_list(q);
        blk_mq_run_hw_queues(q, false);
  }
  
@@@ -503,9 -503,6 +503,9 @@@ static void scsi_mq_uninit_cmd(struct s
  
  static void scsi_run_queue_async(struct scsi_device *sdev)
  {
 +      if (scsi_host_in_recovery(sdev->host))
 +              return;
 +
        if (scsi_target(sdev)->single_lun ||
            !list_empty(&sdev->host->starved_list)) {
                kblockd_schedule_work(&sdev->requeue_work);
@@@ -581,6 -578,11 +581,6 @@@ static bool scsi_end_request(struct req
        return false;
  }
  
 -static inline u8 get_scsi_ml_byte(int result)
 -{
 -      return (result >> 8) & 0xff;
 -}
 -
  /**
   * scsi_result_to_blk_status - translate a SCSI result code into blk_status_t
   * @result:   scsi error code
@@@ -593,19 -595,17 +593,19 @@@ static blk_status_t scsi_result_to_blk_
         * Check the scsi-ml byte first in case we converted a host or status
         * byte.
         */
 -      switch (get_scsi_ml_byte(result)) {
 +      switch (scsi_ml_byte(result)) {
        case SCSIML_STAT_OK:
                break;
        case SCSIML_STAT_RESV_CONFLICT:
 -              return BLK_STS_NEXUS;
 +              return BLK_STS_RESV_CONFLICT;
        case SCSIML_STAT_NOSPC:
                return BLK_STS_NOSPC;
        case SCSIML_STAT_MED_ERROR:
                return BLK_STS_MEDIUM;
        case SCSIML_STAT_TGT_FAILURE:
                return BLK_STS_TARGET;
 +      case SCSIML_STAT_DL_TIMEOUT:
 +              return BLK_STS_DURATION_LIMIT;
        }
  
        switch (host_byte(result)) {
@@@ -803,8 -803,6 +803,8 @@@ static void scsi_io_completion_action(s
                                blk_stat = BLK_STS_ZONE_OPEN_RESOURCE;
                        }
                        break;
 +              case COMPLETED:
 +                      fallthrough;
                default:
                        action = ACTION_FAIL;
                        break;
@@@ -1984,8 -1982,6 +1984,8 @@@ int scsi_mq_setup_tags(struct Scsi_Hos
        tag_set->flags = BLK_MQ_F_SHOULD_MERGE;
        tag_set->flags |=
                BLK_ALLOC_POLICY_TO_MQ_FLAG(shost->hostt->tag_alloc_policy);
 +      if (shost->queuecommand_may_block)
 +              tag_set->flags |= BLK_MQ_F_BLOCKING;
        tag_set->driver_data = shost;
        if (shost->host_tagset)
                tag_set->flags |= BLK_MQ_F_TAG_HCTX_SHARED;
@@@ -2153,7 -2149,6 +2153,7 @@@ EXPORT_SYMBOL_GPL(scsi_mode_select)
   *    @sdev:  SCSI device to be queried
   *    @dbd:   set to prevent mode sense from returning block descriptors
   *    @modepage: mode page being requested
 + *    @subpage: sub-page of the mode page being requested
   *    @buffer: request buffer (may not be smaller than eight bytes)
   *    @len:   length of request buffer.
   *    @timeout: command timeout
   *    Returns zero if successful, or a negative error number on failure
   */
  int
 -scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
 +scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage, int subpage,
                  unsigned char *buffer, int len, int timeout, int retries,
                  struct scsi_mode_data *data, struct scsi_sense_hdr *sshdr)
  {
        dbd = sdev->set_dbd_for_ms ? 8 : dbd;
        cmd[1] = dbd & 0x18;    /* allows DBD and LLBA bits */
        cmd[2] = modepage;
 +      cmd[3] = subpage;
  
        sshdr = exec_args.sshdr;
  
@@@ -2731,24 -2725,16 +2731,16 @@@ void scsi_start_queue(struct scsi_devic
                blk_mq_unquiesce_queue(sdev->request_queue);
  }
  
- static void scsi_stop_queue(struct scsi_device *sdev, bool nowait)
+ static void scsi_stop_queue(struct scsi_device *sdev)
  {
        /*
         * The atomic variable of ->queue_stopped covers that
         * blk_mq_quiesce_queue* is balanced with blk_mq_unquiesce_queue.
         *
-        * However, we still need to wait until quiesce is done
-        * in case that queue has been stopped.
+        * The caller needs to wait until quiesce is done.
         */
-       if (!cmpxchg(&sdev->queue_stopped, 0, 1)) {
-               if (nowait)
-                       blk_mq_quiesce_queue_nowait(sdev->request_queue);
-               else
-                       blk_mq_quiesce_queue(sdev->request_queue);
-       } else {
-               if (!nowait)
-                       blk_mq_wait_quiesce_done(sdev->request_queue->tag_set);
-       }
+       if (!cmpxchg(&sdev->queue_stopped, 0, 1))
+               blk_mq_quiesce_queue_nowait(sdev->request_queue);
  }
  
  /**
@@@ -2775,19 -2761,19 +2767,19 @@@ int scsi_internal_device_block_nowait(s
         * request queue.
         */
        if (!ret)
-               scsi_stop_queue(sdev, true);
+               scsi_stop_queue(sdev);
        return ret;
  }
  EXPORT_SYMBOL_GPL(scsi_internal_device_block_nowait);
  
  /**
-  * scsi_internal_device_block - try to transition to the SDEV_BLOCK state
+  * scsi_device_block - try to transition to the SDEV_BLOCK state
   * @sdev: device to block
+  * @data: dummy argument, ignored
   *
-  * 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 a negative error code upon failure.
+  * Pause SCSI command processing on the specified device. Callers must wait
+  * until all ongoing scsi_queue_rq() calls have finished after this function
+  * returns.
   *
   * Note:
   * This routine transitions the device to the SDEV_BLOCK state (which must be
   * is paused until the device leaves the SDEV_BLOCK state. See also
   * scsi_internal_device_unblock().
   */
- static int scsi_internal_device_block(struct scsi_device *sdev)
+ static void scsi_device_block(struct scsi_device *sdev, void *data)
  {
        int err;
+       enum scsi_device_state state;
  
        mutex_lock(&sdev->state_mutex);
        err = __scsi_internal_device_block_nowait(sdev);
+       state = sdev->sdev_state;
        if (err == 0)
-               scsi_stop_queue(sdev, false);
+               /*
+                * scsi_stop_queue() must be called with the state_mutex
+                * held. Otherwise a simultaneous scsi_start_queue() call
+                * might unquiesce the queue before we quiesce it.
+                */
+               scsi_stop_queue(sdev);
        mutex_unlock(&sdev->state_mutex);
  
-       return err;
+       WARN_ONCE(err, "%s: failed to block %s in state %d\n",
+                 __func__, dev_name(&sdev->sdev_gendev), state);
  }
  
  /**
@@@ -2888,36 -2883,35 +2889,35 @@@ static int scsi_internal_device_unblock
        return ret;
  }
  
- static void
- device_block(struct scsi_device *sdev, void *data)
- {
-       int ret;
-       ret = scsi_internal_device_block(sdev);
-       WARN_ONCE(ret, "scsi_internal_device_block(%s) failed: ret = %d\n",
-                 dev_name(&sdev->sdev_gendev), ret);
- }
  static int
  target_block(struct device *dev, void *data)
  {
        if (scsi_is_target_device(dev))
                starget_for_each_device(to_scsi_target(dev), NULL,
-                                       device_block);
+                                       scsi_device_block);
        return 0;
  }
  
+ /**
+  * scsi_block_targets - transition all SCSI child devices to SDEV_BLOCK state
+  * @dev: a parent device of one or more scsi_target devices
+  * @shost: the Scsi_Host to which this device belongs
+  *
+  * Iterate over all children of @dev, which should be scsi_target devices,
+  * and switch all subordinate scsi devices to SDEV_BLOCK state. Wait for
+  * ongoing scsi_queue_rq() calls to finish. May sleep.
+  *
+  * Note:
+  * @dev must not itself be a scsi_target device.
+  */
  void
- scsi_target_block(struct device *dev)
+ scsi_block_targets(struct Scsi_Host *shost, struct device *dev)
  {
-       if (scsi_is_target_device(dev))
-               starget_for_each_device(to_scsi_target(dev), NULL,
-                                       device_block);
-       else
-               device_for_each_child(dev, NULL, target_block);
+       WARN_ON_ONCE(scsi_is_target_device(dev));
+       device_for_each_child(dev, NULL, target_block);
+       blk_mq_wait_quiesce_done(&shost->tag_set);
  }
- EXPORT_SYMBOL_GPL(scsi_target_block);
+ EXPORT_SYMBOL_GPL(scsi_block_targets);
  
  static void
  device_unblock(struct scsi_device *sdev, void *data)
@@@ -2945,20 -2939,11 +2945,20 @@@ scsi_target_unblock(struct device *dev
  }
  EXPORT_SYMBOL_GPL(scsi_target_unblock);
  
 +/**
 + * scsi_host_block - Try to transition all logical units to the SDEV_BLOCK state
 + * @shost: device to block
 + *
 + * Pause SCSI command processing for all logical units associated with the SCSI
 + * host and wait until pending scsi_queue_rq() calls have finished.
 + *
 + * Returns zero if successful or a negative error code upon failure.
 + */
  int
  scsi_host_block(struct Scsi_Host *shost)
  {
        struct scsi_device *sdev;
 -      int ret = 0;
 +      int ret;
  
        /*
         * Call scsi_internal_device_block_nowait so we can avoid
                mutex_unlock(&sdev->state_mutex);
                if (ret) {
                        scsi_device_put(sdev);
 -                      break;
 +                      return ret;
                }
        }
  
 -      /*
 -       * SCSI never enables blk-mq's BLK_MQ_F_BLOCKING flag so
 -       * calling synchronize_rcu() once is enough.
 -       */
 -      WARN_ON_ONCE(shost->tag_set.flags & BLK_MQ_F_BLOCKING);
 -
 -      if (!ret)
 -              synchronize_rcu();
 +      /* Wait for ongoing scsi_queue_rq() calls to finish. */
 +      blk_mq_wait_quiesce_done(&shost->tag_set);
  
 -      return ret;
 +      return 0;
  }
  EXPORT_SYMBOL_GPL(scsi_host_block);
  
@@@ -218,9 -218,6 +218,9 @@@ struct scsi_device 
        unsigned silence_suspend:1;     /* Do not print runtime PM related messages */
        unsigned no_vpd_size:1;         /* No VPD size reported in header */
  
 +      unsigned cdl_supported:1;       /* Command duration limits supported */
 +      unsigned cdl_enable:1;          /* Enable/disable Command duration limits */
 +
        unsigned int queue_stopped;     /* request queue is quiesced */
        bool offline_already;           /* Device offline message logged */
  
@@@ -367,8 -364,6 +367,8 @@@ extern int scsi_register_device_handler
  extern void scsi_remove_device(struct scsi_device *);
  extern int scsi_unregister_device_handler(struct scsi_device_handler *scsi_dh);
  void scsi_attach_vpd(struct scsi_device *sdev);
 +void scsi_cdl_check(struct scsi_device *sdev);
 +int scsi_cdl_enable(struct scsi_device *sdev, bool enable);
  
  extern struct scsi_device *scsi_device_from_queue(struct request_queue *q);
  extern int __must_check scsi_device_get(struct scsi_device *);
@@@ -426,10 -421,10 +426,10 @@@ extern int scsi_track_queue_full(struc
  
  extern int scsi_set_medium_removal(struct scsi_device *, char);
  
 -extern int scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
 -                         unsigned char *buffer, int len, int timeout,
 -                         int retries, struct scsi_mode_data *data,
 -                         struct scsi_sense_hdr *);
 +int scsi_mode_sense(struct scsi_device *sdev, int dbd, int modepage,
 +                  int subpage, unsigned char *buffer, int len, int timeout,
 +                  int retries, struct scsi_mode_data *data,
 +                  struct scsi_sense_hdr *);
  extern int scsi_mode_select(struct scsi_device *sdev, int pf, int sp,
                            unsigned char *buffer, int len, int timeout,
                            int retries, struct scsi_mode_data *data,
@@@ -438,9 -433,8 +438,9 @@@ extern int scsi_test_unit_ready(struct 
                                int retries, struct scsi_sense_hdr *sshdr);
  extern int scsi_get_vpd_page(struct scsi_device *, u8 page, unsigned char *buf,
                             int buf_len);
 -extern int scsi_report_opcode(struct scsi_device *sdev, unsigned char *buffer,
 -                            unsigned int len, unsigned char opcode);
 +int scsi_report_opcode(struct scsi_device *sdev, unsigned char *buffer,
 +                     unsigned int len, unsigned char opcode,
 +                     unsigned short sa);
  extern int scsi_device_set_state(struct scsi_device *sdev,
                                 enum scsi_device_state state);
  extern struct scsi_event *sdev_evt_alloc(enum scsi_device_event evt_type,
@@@ -456,7 -450,7 +456,7 @@@ extern void scsi_scan_target(struct dev
                             unsigned int id, u64 lun,
                             enum scsi_scan_mode rescan);
  extern void scsi_target_reap(struct scsi_target *);
extern void scsi_target_block(struct device *);
void scsi_block_targets(struct Scsi_Host *shost, struct device *dev);
  extern void scsi_target_unblock(struct device *, enum scsi_device_state);
  extern void scsi_remove_target(struct device *);
  extern const char *scsi_device_state_name(enum scsi_device_state);