aboutsummaryrefslogtreecommitdiff
path: root/block/blk-exec.c
diff options
context:
space:
mode:
Diffstat (limited to 'block/blk-exec.c')
-rw-r--r--block/blk-exec.c72
1 files changed, 54 insertions, 18 deletions
diff --git a/block/blk-exec.c b/block/blk-exec.c
index 9bceff7674f..f4d27b12c90 100644
--- a/block/blk-exec.c
+++ b/block/blk-exec.c
@@ -5,6 +5,8 @@
#include <linux/module.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
+#include <linux/blk-mq.h>
+#include <linux/sched/sysctl.h>
#include "blk.h"
@@ -16,14 +18,13 @@
/**
* blk_end_sync_rq - executes a completion event on a request
* @rq: request to complete
- * @error: end io status of the request
+ * @error: end I/O status of the request
*/
static void blk_end_sync_rq(struct request *rq, int error)
{
struct completion *waiting = rq->end_io_data;
rq->end_io_data = NULL;
- __blk_put_request(rq->q, rq);
/*
* complete last, if this is a stack request the process (and thus
@@ -41,25 +42,54 @@ static void blk_end_sync_rq(struct request *rq, int error)
* @done: I/O completion handler
*
* Description:
- * Insert a fully prepared request at the back of the io scheduler queue
+ * Insert a fully prepared request at the back of the I/O scheduler queue
* for execution. Don't wait for completion.
+ *
+ * Note:
+ * This function will invoke @done directly if the queue is dead.
*/
void blk_execute_rq_nowait(struct request_queue *q, struct gendisk *bd_disk,
struct request *rq, int at_head,
rq_end_io_fn *done)
{
int where = at_head ? ELEVATOR_INSERT_FRONT : ELEVATOR_INSERT_BACK;
+ bool is_pm_resume;
+
+ WARN_ON(irqs_disabled());
rq->rq_disk = bd_disk;
- rq->cmd_flags |= REQ_NOMERGE;
rq->end_io = done;
- WARN_ON(irqs_disabled());
+
+ /*
+ * don't check dying flag for MQ because the request won't
+ * be resued after dying flag is set
+ */
+ if (q->mq_ops) {
+ blk_mq_insert_request(rq, at_head, true, false);
+ return;
+ }
+
+ /*
+ * need to check this before __blk_run_queue(), because rq can
+ * be freed before that returns.
+ */
+ is_pm_resume = rq->cmd_type == REQ_TYPE_PM_RESUME;
+
spin_lock_irq(q->queue_lock);
- __elv_add_request(q, rq, where, 1);
- __generic_unplug_device(q);
- /* the queue is stopped so it won't be plugged+unplugged */
- if (blk_pm_resume_request(rq))
- q->request_fn(q);
+
+ if (unlikely(blk_queue_dying(q))) {
+ rq->cmd_flags |= REQ_QUIET;
+ rq->errors = -ENXIO;
+ __blk_end_request_all(rq, rq->errors);
+ spin_unlock_irq(q->queue_lock);
+ return;
+ }
+
+ __elv_add_request(q, rq, where);
+ __blk_run_queue(q);
+ /* the queue is stopped so it won't be run */
+ if (is_pm_resume)
+ __blk_run_queue_uncond(q);
spin_unlock_irq(q->queue_lock);
}
EXPORT_SYMBOL_GPL(blk_execute_rq_nowait);
@@ -72,7 +102,7 @@ EXPORT_SYMBOL_GPL(blk_execute_rq_nowait);
* @at_head: insert request at head or tail of queue
*
* Description:
- * Insert a fully prepared request at the back of the io scheduler queue
+ * Insert a fully prepared request at the back of the I/O scheduler queue
* for execution and wait for completion.
*/
int blk_execute_rq(struct request_queue *q, struct gendisk *bd_disk,
@@ -81,12 +111,7 @@ int blk_execute_rq(struct request_queue *q, struct gendisk *bd_disk,
DECLARE_COMPLETION_ONSTACK(wait);
char sense[SCSI_SENSE_BUFFERSIZE];
int err = 0;
-
- /*
- * we need an extra reference to the request, so we can look at
- * it after io completion
- */
- rq->ref_count++;
+ unsigned long hang_check;
if (!rq->sense) {
memset(sense, 0, sizeof(sense));
@@ -96,11 +121,22 @@ int blk_execute_rq(struct request_queue *q, struct gendisk *bd_disk,
rq->end_io_data = &wait;
blk_execute_rq_nowait(q, bd_disk, rq, at_head, blk_end_sync_rq);
- wait_for_completion(&wait);
+
+ /* Prevent hang_check timer from firing at us during very long I/O */
+ hang_check = sysctl_hung_task_timeout_secs;
+ if (hang_check)
+ while (!wait_for_completion_io_timeout(&wait, hang_check * (HZ/2)));
+ else
+ wait_for_completion_io(&wait);
if (rq->errors)
err = -EIO;
+ if (rq->sense == sense) {
+ rq->sense = NULL;
+ rq->sense_len = 0;
+ }
+
return err;
}
EXPORT_SYMBOL(blk_execute_rq);