diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-04 14:16:35 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-02-04 14:16:35 -0500 |
commit | 64b28683deba132f301d1cecfc25c32e295f53a1 (patch) | |
tree | be38a4e77c530fb129339f983a9d307c60312df8 /block | |
parent | d3658c2266012f270da52e3e0365536e394bd3bd (diff) | |
parent | 1d51877578799bfe0fcfe189d8233c9fccf05931 (diff) |
Merge tag 'for-linus-20180204' of git://git.kernel.dk/linux-block
Pull more block updates from Jens Axboe:
"Most of this is fixes and not new code/features:
- skd fix from Arnd, fixing a build error dependent on sla allocator
type.
- blk-mq scheduler discard merging fixes, one from me and one from
Keith. This fixes a segment miscalculation for blk-mq-sched, where
we mistakenly think two segments are physically contigious even
though the request isn't carrying real data. Also fixes a bio-to-rq
merge case.
- Don't re-set a bit on the buffer_head flags, if it's already set.
This can cause scalability concerns on bigger machines and
workloads. From Kemi Wang.
- Add BLK_STS_DEV_RESOURCE return value to blk-mq, allowing us to
distuingish between a local (device related) resource starvation
and a global one. The latter might happen without IO being in
flight, so it has to be handled a bit differently. From Ming"
* tag 'for-linus-20180204' of git://git.kernel.dk/linux-block:
block: skd: fix incorrect linux/slab_def.h inclusion
buffer: Avoid setting buffer bits that are already set
blk-mq-sched: Enable merging discard bio into request
blk-mq: fix discard merge with scheduler attached
blk-mq: introduce BLK_STS_DEV_RESOURCE
Diffstat (limited to 'block')
-rw-r--r-- | block/blk-core.c | 3 | ||||
-rw-r--r-- | block/blk-merge.c | 29 | ||||
-rw-r--r-- | block/blk-mq-sched.c | 2 | ||||
-rw-r--r-- | block/blk-mq.c | 20 |
4 files changed, 47 insertions, 7 deletions
diff --git a/block/blk-core.c b/block/blk-core.c index a2005a485335..d0d104268f1a 100644 --- a/block/blk-core.c +++ b/block/blk-core.c | |||
@@ -145,6 +145,7 @@ static const struct { | |||
145 | [BLK_STS_MEDIUM] = { -ENODATA, "critical medium" }, | 145 | [BLK_STS_MEDIUM] = { -ENODATA, "critical medium" }, |
146 | [BLK_STS_PROTECTION] = { -EILSEQ, "protection" }, | 146 | [BLK_STS_PROTECTION] = { -EILSEQ, "protection" }, |
147 | [BLK_STS_RESOURCE] = { -ENOMEM, "kernel resource" }, | 147 | [BLK_STS_RESOURCE] = { -ENOMEM, "kernel resource" }, |
148 | [BLK_STS_DEV_RESOURCE] = { -EBUSY, "device resource" }, | ||
148 | [BLK_STS_AGAIN] = { -EAGAIN, "nonblocking retry" }, | 149 | [BLK_STS_AGAIN] = { -EAGAIN, "nonblocking retry" }, |
149 | 150 | ||
150 | /* device mapper special case, should not leak out: */ | 151 | /* device mapper special case, should not leak out: */ |
@@ -3282,6 +3283,8 @@ void blk_rq_bio_prep(struct request_queue *q, struct request *rq, | |||
3282 | { | 3283 | { |
3283 | if (bio_has_data(bio)) | 3284 | if (bio_has_data(bio)) |
3284 | rq->nr_phys_segments = bio_phys_segments(q, bio); | 3285 | rq->nr_phys_segments = bio_phys_segments(q, bio); |
3286 | else if (bio_op(bio) == REQ_OP_DISCARD) | ||
3287 | rq->nr_phys_segments = 1; | ||
3285 | 3288 | ||
3286 | rq->__data_len = bio->bi_iter.bi_size; | 3289 | rq->__data_len = bio->bi_iter.bi_size; |
3287 | rq->bio = rq->biotail = bio; | 3290 | rq->bio = rq->biotail = bio; |
diff --git a/block/blk-merge.c b/block/blk-merge.c index 8452fc7164cc..782940c65d8a 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c | |||
@@ -550,6 +550,24 @@ static bool req_no_special_merge(struct request *req) | |||
550 | return !q->mq_ops && req->special; | 550 | return !q->mq_ops && req->special; |
551 | } | 551 | } |
552 | 552 | ||
553 | static bool req_attempt_discard_merge(struct request_queue *q, struct request *req, | ||
554 | struct request *next) | ||
555 | { | ||
556 | unsigned short segments = blk_rq_nr_discard_segments(req); | ||
557 | |||
558 | if (segments >= queue_max_discard_segments(q)) | ||
559 | goto no_merge; | ||
560 | if (blk_rq_sectors(req) + bio_sectors(next->bio) > | ||
561 | blk_rq_get_max_sectors(req, blk_rq_pos(req))) | ||
562 | goto no_merge; | ||
563 | |||
564 | req->nr_phys_segments = segments + blk_rq_nr_discard_segments(next); | ||
565 | return true; | ||
566 | no_merge: | ||
567 | req_set_nomerge(q, req); | ||
568 | return false; | ||
569 | } | ||
570 | |||
553 | static int ll_merge_requests_fn(struct request_queue *q, struct request *req, | 571 | static int ll_merge_requests_fn(struct request_queue *q, struct request *req, |
554 | struct request *next) | 572 | struct request *next) |
555 | { | 573 | { |
@@ -683,9 +701,13 @@ static struct request *attempt_merge(struct request_queue *q, | |||
683 | * If we are allowed to merge, then append bio list | 701 | * If we are allowed to merge, then append bio list |
684 | * from next to rq and release next. merge_requests_fn | 702 | * from next to rq and release next. merge_requests_fn |
685 | * will have updated segment counts, update sector | 703 | * will have updated segment counts, update sector |
686 | * counts here. | 704 | * counts here. Handle DISCARDs separately, as they |
705 | * have separate settings. | ||
687 | */ | 706 | */ |
688 | if (!ll_merge_requests_fn(q, req, next)) | 707 | if (req_op(req) == REQ_OP_DISCARD) { |
708 | if (!req_attempt_discard_merge(q, req, next)) | ||
709 | return NULL; | ||
710 | } else if (!ll_merge_requests_fn(q, req, next)) | ||
689 | return NULL; | 711 | return NULL; |
690 | 712 | ||
691 | /* | 713 | /* |
@@ -715,7 +737,8 @@ static struct request *attempt_merge(struct request_queue *q, | |||
715 | 737 | ||
716 | req->__data_len += blk_rq_bytes(next); | 738 | req->__data_len += blk_rq_bytes(next); |
717 | 739 | ||
718 | elv_merge_requests(q, req, next); | 740 | if (req_op(req) != REQ_OP_DISCARD) |
741 | elv_merge_requests(q, req, next); | ||
719 | 742 | ||
720 | /* | 743 | /* |
721 | * 'next' is going away, so update stats accordingly | 744 | * 'next' is going away, so update stats accordingly |
diff --git a/block/blk-mq-sched.c b/block/blk-mq-sched.c index 55c0a745b427..25c14c58385c 100644 --- a/block/blk-mq-sched.c +++ b/block/blk-mq-sched.c | |||
@@ -259,6 +259,8 @@ bool blk_mq_sched_try_merge(struct request_queue *q, struct bio *bio, | |||
259 | if (!*merged_request) | 259 | if (!*merged_request) |
260 | elv_merged_request(q, rq, ELEVATOR_FRONT_MERGE); | 260 | elv_merged_request(q, rq, ELEVATOR_FRONT_MERGE); |
261 | return true; | 261 | return true; |
262 | case ELEVATOR_DISCARD_MERGE: | ||
263 | return bio_attempt_discard_merge(q, rq, bio); | ||
262 | default: | 264 | default: |
263 | return false; | 265 | return false; |
264 | } | 266 | } |
diff --git a/block/blk-mq.c b/block/blk-mq.c index 01f271d40825..df93102e2149 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c | |||
@@ -1162,6 +1162,8 @@ static bool blk_mq_mark_tag_wait(struct blk_mq_hw_ctx **hctx, | |||
1162 | return true; | 1162 | return true; |
1163 | } | 1163 | } |
1164 | 1164 | ||
1165 | #define BLK_MQ_RESOURCE_DELAY 3 /* ms units */ | ||
1166 | |||
1165 | bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list, | 1167 | bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list, |
1166 | bool got_budget) | 1168 | bool got_budget) |
1167 | { | 1169 | { |
@@ -1169,6 +1171,7 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list, | |||
1169 | struct request *rq, *nxt; | 1171 | struct request *rq, *nxt; |
1170 | bool no_tag = false; | 1172 | bool no_tag = false; |
1171 | int errors, queued; | 1173 | int errors, queued; |
1174 | blk_status_t ret = BLK_STS_OK; | ||
1172 | 1175 | ||
1173 | if (list_empty(list)) | 1176 | if (list_empty(list)) |
1174 | return false; | 1177 | return false; |
@@ -1181,7 +1184,6 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list, | |||
1181 | errors = queued = 0; | 1184 | errors = queued = 0; |
1182 | do { | 1185 | do { |
1183 | struct blk_mq_queue_data bd; | 1186 | struct blk_mq_queue_data bd; |
1184 | blk_status_t ret; | ||
1185 | 1187 | ||
1186 | rq = list_first_entry(list, struct request, queuelist); | 1188 | rq = list_first_entry(list, struct request, queuelist); |
1187 | if (!blk_mq_get_driver_tag(rq, &hctx, false)) { | 1189 | if (!blk_mq_get_driver_tag(rq, &hctx, false)) { |
@@ -1226,7 +1228,7 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list, | |||
1226 | } | 1228 | } |
1227 | 1229 | ||
1228 | ret = q->mq_ops->queue_rq(hctx, &bd); | 1230 | ret = q->mq_ops->queue_rq(hctx, &bd); |
1229 | if (ret == BLK_STS_RESOURCE) { | 1231 | if (ret == BLK_STS_RESOURCE || ret == BLK_STS_DEV_RESOURCE) { |
1230 | /* | 1232 | /* |
1231 | * If an I/O scheduler has been configured and we got a | 1233 | * If an I/O scheduler has been configured and we got a |
1232 | * driver tag for the next request already, free it | 1234 | * driver tag for the next request already, free it |
@@ -1257,6 +1259,8 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list, | |||
1257 | * that is where we will continue on next queue run. | 1259 | * that is where we will continue on next queue run. |
1258 | */ | 1260 | */ |
1259 | if (!list_empty(list)) { | 1261 | if (!list_empty(list)) { |
1262 | bool needs_restart; | ||
1263 | |||
1260 | spin_lock(&hctx->lock); | 1264 | spin_lock(&hctx->lock); |
1261 | list_splice_init(list, &hctx->dispatch); | 1265 | list_splice_init(list, &hctx->dispatch); |
1262 | spin_unlock(&hctx->lock); | 1266 | spin_unlock(&hctx->lock); |
@@ -1280,10 +1284,17 @@ bool blk_mq_dispatch_rq_list(struct request_queue *q, struct list_head *list, | |||
1280 | * - Some but not all block drivers stop a queue before | 1284 | * - Some but not all block drivers stop a queue before |
1281 | * returning BLK_STS_RESOURCE. Two exceptions are scsi-mq | 1285 | * returning BLK_STS_RESOURCE. Two exceptions are scsi-mq |
1282 | * and dm-rq. | 1286 | * and dm-rq. |
1287 | * | ||
1288 | * If driver returns BLK_STS_RESOURCE and SCHED_RESTART | ||
1289 | * bit is set, run queue after a delay to avoid IO stalls | ||
1290 | * that could otherwise occur if the queue is idle. | ||
1283 | */ | 1291 | */ |
1284 | if (!blk_mq_sched_needs_restart(hctx) || | 1292 | needs_restart = blk_mq_sched_needs_restart(hctx); |
1293 | if (!needs_restart || | ||
1285 | (no_tag && list_empty_careful(&hctx->dispatch_wait.entry))) | 1294 | (no_tag && list_empty_careful(&hctx->dispatch_wait.entry))) |
1286 | blk_mq_run_hw_queue(hctx, true); | 1295 | blk_mq_run_hw_queue(hctx, true); |
1296 | else if (needs_restart && (ret == BLK_STS_RESOURCE)) | ||
1297 | blk_mq_delay_run_hw_queue(hctx, BLK_MQ_RESOURCE_DELAY); | ||
1287 | } | 1298 | } |
1288 | 1299 | ||
1289 | return (queued + errors) != 0; | 1300 | return (queued + errors) != 0; |
@@ -1764,6 +1775,7 @@ static blk_status_t __blk_mq_issue_directly(struct blk_mq_hw_ctx *hctx, | |||
1764 | *cookie = new_cookie; | 1775 | *cookie = new_cookie; |
1765 | break; | 1776 | break; |
1766 | case BLK_STS_RESOURCE: | 1777 | case BLK_STS_RESOURCE: |
1778 | case BLK_STS_DEV_RESOURCE: | ||
1767 | __blk_mq_requeue_request(rq); | 1779 | __blk_mq_requeue_request(rq); |
1768 | break; | 1780 | break; |
1769 | default: | 1781 | default: |
@@ -1826,7 +1838,7 @@ static void blk_mq_try_issue_directly(struct blk_mq_hw_ctx *hctx, | |||
1826 | hctx_lock(hctx, &srcu_idx); | 1838 | hctx_lock(hctx, &srcu_idx); |
1827 | 1839 | ||
1828 | ret = __blk_mq_try_issue_directly(hctx, rq, cookie, false); | 1840 | ret = __blk_mq_try_issue_directly(hctx, rq, cookie, false); |
1829 | if (ret == BLK_STS_RESOURCE) | 1841 | if (ret == BLK_STS_RESOURCE || ret == BLK_STS_DEV_RESOURCE) |
1830 | blk_mq_sched_insert_request(rq, false, true, false); | 1842 | blk_mq_sched_insert_request(rq, false, true, false); |
1831 | else if (ret != BLK_STS_OK) | 1843 | else if (ret != BLK_STS_OK) |
1832 | blk_mq_end_request(rq, ret); | 1844 | blk_mq_end_request(rq, ret); |