This status is returned from driver to block layer if device related
resource is run out of, but driver can guarantee that IO dispatch is
triggered in future when the resource is available.

This patch converts some drivers to use this return value. Meantime
if driver returns BLK_STS_RESOURCE and S_SCHED_RESTART is marked, run
queue after 10ms for avoiding IO hang.

Suggested-by: Jens Axboe <ax...@kernel.dk>
Cc: Mike Snitzer <snit...@redhat.com>
Cc: Laurence Oberman <lober...@redhat.com>
Cc: Bart Van Assche <bart.vanass...@sandisk.com>
Cc: James E.J. Bottomley <j...@linux.vnet.ibm.com>
Cc: Martin K. Petersen <martin.peter...@oracle.com>
Cc: linux-s...@vger.kernel.org
Signed-off-by: Ming Lei <ming....@redhat.com>
---
 block/blk-core.c             |  1 +
 block/blk-mq.c               | 19 +++++++++++++++----
 drivers/block/null_blk.c     |  2 +-
 drivers/block/virtio_blk.c   |  2 +-
 drivers/block/xen-blkfront.c |  2 +-
 drivers/md/dm-rq.c           |  2 +-
 drivers/scsi/scsi_lib.c      |  6 +++---
 include/linux/blk_types.h    |  7 +++++++
 8 files changed, 30 insertions(+), 11 deletions(-)

diff --git a/block/blk-core.c b/block/blk-core.c
index a2005a485335..ac4789c5e329 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -145,6 +145,7 @@ static const struct {
        [BLK_STS_MEDIUM]        = { -ENODATA,   "critical medium" },
        [BLK_STS_PROTECTION]    = { -EILSEQ,    "protection" },
        [BLK_STS_RESOURCE]      = { -ENOMEM,    "kernel resource" },
+       [BLK_STS_DEV_RESOURCE]  = { -ENOMEM,    "device resource" },
        [BLK_STS_AGAIN]         = { -EAGAIN,    "nonblocking retry" },
 
        /* device mapper special case, should not leak out: */
diff --git a/block/blk-mq.c b/block/blk-mq.c
index 01f271d40825..e91d688792a9 100644
--- a/block/blk-mq.c
+++ b/block/blk-mq.c
@@ -1169,6 +1169,7 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, 
struct list_head *list,
        struct request *rq, *nxt;
        bool no_tag = false;
        int errors, queued;
+       blk_status_t ret = BLK_STS_OK;
 
        if (list_empty(list))
                return false;
@@ -1181,7 +1182,6 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, 
struct list_head *list,
        errors = queued = 0;
        do {
                struct blk_mq_queue_data bd;
-               blk_status_t ret;
 
                rq = list_first_entry(list, struct request, queuelist);
                if (!blk_mq_get_driver_tag(rq, &hctx, false)) {
@@ -1226,7 +1226,7 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, 
struct list_head *list,
                }
 
                ret = q->mq_ops->queue_rq(hctx, &bd);
-               if (ret == BLK_STS_RESOURCE) {
+               if ((ret == BLK_STS_RESOURCE) || (ret == BLK_STS_DEV_RESOURCE)) 
{
                        /*
                         * If an I/O scheduler has been configured and we got a
                         * driver tag for the next request already, free it
@@ -1257,6 +1257,8 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, 
struct list_head *list,
         * that is where we will continue on next queue run.
         */
        if (!list_empty(list)) {
+               bool needs_restart;
+
                spin_lock(&hctx->lock);
                list_splice_init(list, &hctx->dispatch);
                spin_unlock(&hctx->lock);
@@ -1280,10 +1282,18 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, 
struct list_head *list,
                 * - Some but not all block drivers stop a queue before
                 *   returning BLK_STS_RESOURCE. Two exceptions are scsi-mq
                 *   and dm-rq.
+                *
+                * If drivers return BLK_STS_RESOURCE and S_SCHED_RESTART
+                * bit is set, run queue after 10ms for avoiding IO hang
+                * because the queue may be idle and the RESTART mechanism
+                * can't work any more.
                 */
-               if (!blk_mq_sched_needs_restart(hctx) ||
+               needs_restart = blk_mq_sched_needs_restart(hctx);
+               if (!needs_restart ||
                    (no_tag && list_empty_careful(&hctx->dispatch_wait.entry)))
                        blk_mq_run_hw_queue(hctx, true);
+               else if (needs_restart && (ret == BLK_STS_RESOURCE))
+                       blk_mq_delay_run_hw_queue(hctx, 10);
        }
 
        return (queued + errors) != 0;
@@ -1764,6 +1774,7 @@ static blk_status_t __blk_mq_issue_directly(struct 
blk_mq_hw_ctx *hctx,
                *cookie = new_cookie;
                break;
        case BLK_STS_RESOURCE:
+       case BLK_STS_DEV_RESOURCE:
                __blk_mq_requeue_request(rq);
                break;
        default:
@@ -1826,7 +1837,7 @@ static void blk_mq_try_issue_directly(struct 
blk_mq_hw_ctx *hctx,
        hctx_lock(hctx, &srcu_idx);
 
        ret = __blk_mq_try_issue_directly(hctx, rq, cookie, false);
-       if (ret == BLK_STS_RESOURCE)
+       if ((ret == BLK_STS_RESOURCE) || (ret == BLK_STS_DEV_RESOURCE))
                blk_mq_sched_insert_request(rq, false, true, false);
        else if (ret != BLK_STS_OK)
                blk_mq_end_request(rq, ret);
diff --git a/drivers/block/null_blk.c b/drivers/block/null_blk.c
index 6655893a3a7a..287a09611c0f 100644
--- a/drivers/block/null_blk.c
+++ b/drivers/block/null_blk.c
@@ -1230,7 +1230,7 @@ static blk_status_t null_handle_cmd(struct nullb_cmd *cmd)
                                return BLK_STS_OK;
                        } else
                                /* requeue request */
-                               return BLK_STS_RESOURCE;
+                               return BLK_STS_DEV_RESOURCE;
                }
        }
 
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 68846897d213..79908e6ddbf2 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -276,7 +276,7 @@ static blk_status_t virtio_queue_rq(struct blk_mq_hw_ctx 
*hctx,
                /* Out of mem doesn't actually happen, since we fall back
                 * to direct descriptors */
                if (err == -ENOMEM || err == -ENOSPC)
-                       return BLK_STS_RESOURCE;
+                       return BLK_STS_DEV_RESOURCE;
                return BLK_STS_IOERR;
        }
 
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c
index 891265acb10e..e126e4cac2ca 100644
--- a/drivers/block/xen-blkfront.c
+++ b/drivers/block/xen-blkfront.c
@@ -911,7 +911,7 @@ static blk_status_t blkif_queue_rq(struct blk_mq_hw_ctx 
*hctx,
 out_busy:
        blk_mq_stop_hw_queue(hctx);
        spin_unlock_irqrestore(&rinfo->ring_lock, flags);
-       return BLK_STS_RESOURCE;
+       return BLK_STS_DEV_RESOURCE;
 }
 
 static void blkif_complete_rq(struct request *rq)
diff --git a/drivers/md/dm-rq.c b/drivers/md/dm-rq.c
index d8519ddd7e1a..e0bb56723b9b 100644
--- a/drivers/md/dm-rq.c
+++ b/drivers/md/dm-rq.c
@@ -500,7 +500,7 @@ static int map_request(struct dm_rq_target_io *tio)
                trace_block_rq_remap(clone->q, clone, disk_devt(dm_disk(md)),
                                     blk_rq_pos(rq));
                ret = dm_dispatch_clone_request(clone, rq);
-               if (ret == BLK_STS_RESOURCE) {
+               if (ret == BLK_STS_RESOURCE || ret == BLK_STS_DEV_RESOURCE) {
                        blk_rq_unprep_clone(clone);
                        tio->ti->type->release_clone_rq(clone);
                        tio->clone = NULL;
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index d9ca1dfab154..55be2550c555 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -2030,9 +2030,9 @@ static blk_status_t scsi_queue_rq(struct blk_mq_hw_ctx 
*hctx,
        case BLK_STS_OK:
                break;
        case BLK_STS_RESOURCE:
-               if (atomic_read(&sdev->device_busy) == 0 &&
-                   !scsi_device_blocked(sdev))
-                       blk_mq_delay_run_hw_queue(hctx, SCSI_QUEUE_DELAY);
+               if (atomic_read(&sdev->device_busy) ||
+                   scsi_device_blocked(sdev))
+                       ret = BLK_STS_DEV_RESOURCE;
                break;
        default:
                /*
diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
index c5d3db0d83f8..d76f1744a7ca 100644
--- a/include/linux/blk_types.h
+++ b/include/linux/blk_types.h
@@ -39,6 +39,13 @@ typedef u8 __bitwise blk_status_t;
 
 #define BLK_STS_AGAIN          ((__force blk_status_t)12)
 
+/*
+ * This status is returned from driver to block layer if device related
+ * resource is run out of, but driver can guarantee that IO dispatch is
+ * triggered in future when the resource is available.
+ */
+#define BLK_STS_DEV_RESOURCE   ((__force blk_status_t)13)
+
 /**
  * blk_path_error - returns true if error may be path related
  * @error: status the request was completed with
-- 
2.9.5

Reply via email to