aboutsummaryrefslogtreecommitdiffstats
path: root/block
diff options
context:
space:
mode:
Diffstat (limited to 'block')
-rw-r--r--block/Makefile4
-rw-r--r--block/blk-core.c7
-rw-r--r--block/blk-settings.c12
-rw-r--r--block/blk-softirq.c30
-rw-r--r--block/blk-timeout.c155
-rw-r--r--block/blk.h24
-rw-r--r--block/elevator.c8
7 files changed, 226 insertions, 14 deletions
diff --git a/block/Makefile b/block/Makefile
index 0da976ce67dd..bfe73049f939 100644
--- a/block/Makefile
+++ b/block/Makefile
@@ -4,8 +4,8 @@
4 4
5obj-$(CONFIG_BLOCK) := elevator.o blk-core.o blk-tag.o blk-sysfs.o \ 5obj-$(CONFIG_BLOCK) := elevator.o blk-core.o blk-tag.o blk-sysfs.o \
6 blk-barrier.o blk-settings.o blk-ioc.o blk-map.o \ 6 blk-barrier.o blk-settings.o blk-ioc.o blk-map.o \
7 blk-exec.o blk-merge.o blk-softirq.o ioctl.o genhd.o \ 7 blk-exec.o blk-merge.o blk-softirq.o blk-timeout.o \
8 scsi_ioctl.o cmd-filter.o 8 ioctl.o genhd.o scsi_ioctl.o cmd-filter.o
9 9
10obj-$(CONFIG_BLK_DEV_BSG) += bsg.o 10obj-$(CONFIG_BLK_DEV_BSG) += bsg.o
11obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o 11obj-$(CONFIG_IOSCHED_NOOP) += noop-iosched.o
diff --git a/block/blk-core.c b/block/blk-core.c
index f25eb9786d94..d768a8ddc173 100644
--- a/block/blk-core.c
+++ b/block/blk-core.c
@@ -110,6 +110,7 @@ void blk_rq_init(struct request_queue *q, struct request *rq)
110 memset(rq, 0, sizeof(*rq)); 110 memset(rq, 0, sizeof(*rq));
111 111
112 INIT_LIST_HEAD(&rq->queuelist); 112 INIT_LIST_HEAD(&rq->queuelist);
113 INIT_LIST_HEAD(&rq->timeout_list);
113 rq->cpu = -1; 114 rq->cpu = -1;
114 rq->q = q; 115 rq->q = q;
115 rq->sector = rq->hard_sector = (sector_t) -1; 116 rq->sector = rq->hard_sector = (sector_t) -1;
@@ -490,6 +491,8 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
490 } 491 }
491 492
492 init_timer(&q->unplug_timer); 493 init_timer(&q->unplug_timer);
494 setup_timer(&q->timeout, blk_rq_timed_out_timer, (unsigned long) q);
495 INIT_LIST_HEAD(&q->timeout_list);
493 496
494 kobject_init(&q->kobj, &blk_queue_ktype); 497 kobject_init(&q->kobj, &blk_queue_ktype);
495 498
@@ -897,6 +900,8 @@ EXPORT_SYMBOL(blk_start_queueing);
897 */ 900 */
898void blk_requeue_request(struct request_queue *q, struct request *rq) 901void blk_requeue_request(struct request_queue *q, struct request *rq)
899{ 902{
903 blk_delete_timer(rq);
904 blk_clear_rq_complete(rq);
900 blk_add_trace_rq(q, rq, BLK_TA_REQUEUE); 905 blk_add_trace_rq(q, rq, BLK_TA_REQUEUE);
901 906
902 if (blk_rq_tagged(rq)) 907 if (blk_rq_tagged(rq))
@@ -1650,6 +1655,8 @@ static void end_that_request_last(struct request *req, int error)
1650{ 1655{
1651 struct gendisk *disk = req->rq_disk; 1656 struct gendisk *disk = req->rq_disk;
1652 1657
1658 blk_delete_timer(req);
1659
1653 if (blk_rq_tagged(req)) 1660 if (blk_rq_tagged(req))
1654 blk_queue_end_tag(req->q, req); 1661 blk_queue_end_tag(req->q, req);
1655 1662
diff --git a/block/blk-settings.c b/block/blk-settings.c
index d70692badcdb..1d0330d0b40a 100644
--- a/block/blk-settings.c
+++ b/block/blk-settings.c
@@ -77,6 +77,18 @@ void blk_queue_softirq_done(struct request_queue *q, softirq_done_fn *fn)
77} 77}
78EXPORT_SYMBOL(blk_queue_softirq_done); 78EXPORT_SYMBOL(blk_queue_softirq_done);
79 79
80void blk_queue_rq_timeout(struct request_queue *q, unsigned int timeout)
81{
82 q->rq_timeout = timeout;
83}
84EXPORT_SYMBOL_GPL(blk_queue_rq_timeout);
85
86void blk_queue_rq_timed_out(struct request_queue *q, rq_timed_out_fn *fn)
87{
88 q->rq_timed_out_fn = fn;
89}
90EXPORT_SYMBOL_GPL(blk_queue_rq_timed_out);
91
80/** 92/**
81 * blk_queue_make_request - define an alternate make_request function for a device 93 * blk_queue_make_request - define an alternate make_request function for a device
82 * @q: the request queue for the device to be affected 94 * @q: the request queue for the device to be affected
diff --git a/block/blk-softirq.c b/block/blk-softirq.c
index 3a1af551191e..7ab344afb16f 100644
--- a/block/blk-softirq.c
+++ b/block/blk-softirq.c
@@ -101,18 +101,7 @@ static struct notifier_block __cpuinitdata blk_cpu_notifier = {
101 .notifier_call = blk_cpu_notify, 101 .notifier_call = blk_cpu_notify,
102}; 102};
103 103
104/** 104void __blk_complete_request(struct request *req)
105 * blk_complete_request - end I/O on a request
106 * @req: the request being processed
107 *
108 * Description:
109 * Ends all I/O on a request. It does not handle partial completions,
110 * unless the driver actually implements this in its completion callback
111 * through requeueing. The actual completion happens out-of-order,
112 * through a softirq handler. The user must have registered a completion
113 * callback through blk_queue_softirq_done().
114 **/
115void blk_complete_request(struct request *req)
116{ 105{
117 struct request_queue *q = req->q; 106 struct request_queue *q = req->q;
118 unsigned long flags; 107 unsigned long flags;
@@ -151,6 +140,23 @@ do_local:
151 140
152 local_irq_restore(flags); 141 local_irq_restore(flags);
153} 142}
143
144/**
145 * blk_complete_request - end I/O on a request
146 * @req: the request being processed
147 *
148 * Description:
149 * Ends all I/O on a request. It does not handle partial completions,
150 * unless the driver actually implements this in its completion callback
151 * through requeueing. The actual completion happens out-of-order,
152 * through a softirq handler. The user must have registered a completion
153 * callback through blk_queue_softirq_done().
154 **/
155void blk_complete_request(struct request *req)
156{
157 if (!blk_mark_rq_complete(req))
158 __blk_complete_request(req);
159}
154EXPORT_SYMBOL(blk_complete_request); 160EXPORT_SYMBOL(blk_complete_request);
155 161
156__init int blk_softirq_init(void) 162__init int blk_softirq_init(void)
diff --git a/block/blk-timeout.c b/block/blk-timeout.c
new file mode 100644
index 000000000000..b36d07bf0afb
--- /dev/null
+++ b/block/blk-timeout.c
@@ -0,0 +1,155 @@
1/*
2 * Functions related to generic timeout handling of requests.
3 */
4#include <linux/kernel.h>
5#include <linux/module.h>
6#include <linux/blkdev.h>
7
8#include "blk.h"
9
10/*
11 * blk_delete_timer - Delete/cancel timer for a given function.
12 * @req: request that we are canceling timer for
13 *
14 */
15void blk_delete_timer(struct request *req)
16{
17 struct request_queue *q = req->q;
18
19 /*
20 * Nothing to detach
21 */
22 if (!q->rq_timed_out_fn || !req->deadline)
23 return;
24
25 list_del_init(&req->timeout_list);
26
27 if (list_empty(&q->timeout_list))
28 del_timer(&q->timeout);
29}
30
31static void blk_rq_timed_out(struct request *req)
32{
33 struct request_queue *q = req->q;
34 enum blk_eh_timer_return ret;
35
36 ret = q->rq_timed_out_fn(req);
37 switch (ret) {
38 case BLK_EH_HANDLED:
39 __blk_complete_request(req);
40 break;
41 case BLK_EH_RESET_TIMER:
42 blk_clear_rq_complete(req);
43 blk_add_timer(req);
44 break;
45 case BLK_EH_NOT_HANDLED:
46 /*
47 * LLD handles this for now but in the future
48 * we can send a request msg to abort the command
49 * and we can move more of the generic scsi eh code to
50 * the blk layer.
51 */
52 break;
53 default:
54 printk(KERN_ERR "block: bad eh return: %d\n", ret);
55 break;
56 }
57}
58
59void blk_rq_timed_out_timer(unsigned long data)
60{
61 struct request_queue *q = (struct request_queue *) data;
62 unsigned long flags, uninitialized_var(next), next_set = 0;
63 struct request *rq, *tmp;
64
65 spin_lock_irqsave(q->queue_lock, flags);
66
67 list_for_each_entry_safe(rq, tmp, &q->timeout_list, timeout_list) {
68 if (time_after_eq(jiffies, rq->deadline)) {
69 list_del_init(&rq->timeout_list);
70
71 /*
72 * Check if we raced with end io completion
73 */
74 if (blk_mark_rq_complete(rq))
75 continue;
76 blk_rq_timed_out(rq);
77 }
78 if (!next_set) {
79 next = rq->deadline;
80 next_set = 1;
81 } else if (time_after(next, rq->deadline))
82 next = rq->deadline;
83 }
84
85 if (next_set && !list_empty(&q->timeout_list))
86 mod_timer(&q->timeout, round_jiffies(next));
87
88 spin_unlock_irqrestore(q->queue_lock, flags);
89}
90
91/**
92 * blk_abort_request -- Request request recovery for the specified command
93 * @req: pointer to the request of interest
94 *
95 * This function requests that the block layer start recovery for the
96 * request by deleting the timer and calling the q's timeout function.
97 * LLDDs who implement their own error recovery MAY ignore the timeout
98 * event if they generated blk_abort_req. Must hold queue lock.
99 */
100void blk_abort_request(struct request *req)
101{
102 blk_delete_timer(req);
103 blk_rq_timed_out(req);
104}
105EXPORT_SYMBOL_GPL(blk_abort_request);
106
107/**
108 * blk_add_timer - Start timeout timer for a single request
109 * @req: request that is about to start running.
110 *
111 * Notes:
112 * Each request has its own timer, and as it is added to the queue, we
113 * set up the timer. When the request completes, we cancel the timer.
114 */
115void blk_add_timer(struct request *req)
116{
117 struct request_queue *q = req->q;
118 unsigned long expiry;
119
120 if (!q->rq_timed_out_fn)
121 return;
122
123 BUG_ON(!list_empty(&req->timeout_list));
124 BUG_ON(test_bit(REQ_ATOM_COMPLETE, &req->atomic_flags));
125
126 if (req->timeout)
127 req->deadline = jiffies + req->timeout;
128 else {
129 req->deadline = jiffies + q->rq_timeout;
130 /*
131 * Some LLDs, like scsi, peek at the timeout to prevent
132 * a command from being retried forever.
133 */
134 req->timeout = q->rq_timeout;
135 }
136 list_add_tail(&req->timeout_list, &q->timeout_list);
137
138 /*
139 * If the timer isn't already pending or this timeout is earlier
140 * than an existing one, modify the timer. Round to next nearest
141 * second.
142 */
143 expiry = round_jiffies(req->deadline);
144
145 /*
146 * We use ->deadline == 0 to detect whether a timer was added or
147 * not, so just increase to next jiffy for that specific case
148 */
149 if (unlikely(!req->deadline))
150 req->deadline = 1;
151
152 if (!timer_pending(&q->timeout) ||
153 time_before(expiry, q->timeout.expires))
154 mod_timer(&q->timeout, expiry);
155}
diff --git a/block/blk.h b/block/blk.h
index de74254cb916..a4f4a50aefaa 100644
--- a/block/blk.h
+++ b/block/blk.h
@@ -17,6 +17,30 @@ void __blk_queue_free_tags(struct request_queue *q);
17 17
18void blk_unplug_work(struct work_struct *work); 18void blk_unplug_work(struct work_struct *work);
19void blk_unplug_timeout(unsigned long data); 19void blk_unplug_timeout(unsigned long data);
20void blk_rq_timed_out_timer(unsigned long data);
21void blk_delete_timer(struct request *);
22void blk_add_timer(struct request *);
23
24/*
25 * Internal atomic flags for request handling
26 */
27enum rq_atomic_flags {
28 REQ_ATOM_COMPLETE = 0,
29};
30
31/*
32 * EH timer and IO completion will both attempt to 'grab' the request, make
33 * sure that only one of them suceeds
34 */
35static inline int blk_mark_rq_complete(struct request *rq)
36{
37 return test_and_set_bit(REQ_ATOM_COMPLETE, &rq->atomic_flags);
38}
39
40static inline void blk_clear_rq_complete(struct request *rq)
41{
42 clear_bit(REQ_ATOM_COMPLETE, &rq->atomic_flags);
43}
20 44
21struct io_context *current_io_context(gfp_t gfp_flags, int node); 45struct io_context *current_io_context(gfp_t gfp_flags, int node);
22 46
diff --git a/block/elevator.c b/block/elevator.c
index 8e3fc3afc77b..a91fc59edd01 100644
--- a/block/elevator.c
+++ b/block/elevator.c
@@ -36,6 +36,8 @@
36#include <linux/hash.h> 36#include <linux/hash.h>
37#include <linux/uaccess.h> 37#include <linux/uaccess.h>
38 38
39#include "blk.h"
40
39static DEFINE_SPINLOCK(elv_list_lock); 41static DEFINE_SPINLOCK(elv_list_lock);
40static LIST_HEAD(elv_list); 42static LIST_HEAD(elv_list);
41 43
@@ -771,6 +773,12 @@ struct request *elv_next_request(struct request_queue *q)
771 */ 773 */
772 rq->cmd_flags |= REQ_STARTED; 774 rq->cmd_flags |= REQ_STARTED;
773 blk_add_trace_rq(q, rq, BLK_TA_ISSUE); 775 blk_add_trace_rq(q, rq, BLK_TA_ISSUE);
776
777 /*
778 * We are now handing the request to the hardware,
779 * add the timeout handler
780 */
781 blk_add_timer(rq);
774 } 782 }
775 783
776 if (!q->boundary_rq || q->boundary_rq == rq) { 784 if (!q->boundary_rq || q->boundary_rq == rq) {