| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0 |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * buffered writeback throttling. loosely based on CoDel. We can't drop |
|---|
| 3 | 4 | * packets for IO scheduling, so the logic is something like this: |
|---|
| .. | .. |
|---|
| 313 | 314 | calc_wb_limits(rwb); |
|---|
| 314 | 315 | rwb->unknown_cnt = 0; |
|---|
| 315 | 316 | rwb_wake_all(rwb); |
|---|
| 316 | | - rwb_trace_step(rwb, "scale up"); |
|---|
| 317 | + rwb_trace_step(rwb, tracepoint_string("scale up")); |
|---|
| 317 | 318 | } |
|---|
| 318 | 319 | |
|---|
| 319 | 320 | static void scale_down(struct rq_wb *rwb, bool hard_throttle) |
|---|
| .. | .. |
|---|
| 322 | 323 | return; |
|---|
| 323 | 324 | calc_wb_limits(rwb); |
|---|
| 324 | 325 | rwb->unknown_cnt = 0; |
|---|
| 325 | | - rwb_trace_step(rwb, "scale down"); |
|---|
| 326 | + rwb_trace_step(rwb, tracepoint_string("scale down")); |
|---|
| 326 | 327 | } |
|---|
| 327 | 328 | |
|---|
| 328 | 329 | static void rwb_arm_timer(struct rq_wb *rwb) |
|---|
| .. | .. |
|---|
| 405 | 406 | rwb_arm_timer(rwb); |
|---|
| 406 | 407 | } |
|---|
| 407 | 408 | |
|---|
| 408 | | -static void __wbt_update_limits(struct rq_wb *rwb) |
|---|
| 409 | +static void wbt_update_limits(struct rq_wb *rwb) |
|---|
| 409 | 410 | { |
|---|
| 410 | 411 | struct rq_depth *rqd = &rwb->rq_depth; |
|---|
| 411 | 412 | |
|---|
| .. | .. |
|---|
| 416 | 417 | calc_wb_limits(rwb); |
|---|
| 417 | 418 | |
|---|
| 418 | 419 | rwb_wake_all(rwb); |
|---|
| 419 | | -} |
|---|
| 420 | | - |
|---|
| 421 | | -void wbt_update_limits(struct request_queue *q) |
|---|
| 422 | | -{ |
|---|
| 423 | | - struct rq_qos *rqos = wbt_rq_qos(q); |
|---|
| 424 | | - if (!rqos) |
|---|
| 425 | | - return; |
|---|
| 426 | | - __wbt_update_limits(RQWB(rqos)); |
|---|
| 427 | 420 | } |
|---|
| 428 | 421 | |
|---|
| 429 | 422 | u64 wbt_get_min_lat(struct request_queue *q) |
|---|
| .. | .. |
|---|
| 441 | 434 | return; |
|---|
| 442 | 435 | RQWB(rqos)->min_lat_nsec = val; |
|---|
| 443 | 436 | RQWB(rqos)->enable_state = WBT_STATE_ON_MANUAL; |
|---|
| 444 | | - __wbt_update_limits(RQWB(rqos)); |
|---|
| 437 | + wbt_update_limits(RQWB(rqos)); |
|---|
| 445 | 438 | } |
|---|
| 446 | 439 | |
|---|
| 447 | 440 | |
|---|
| .. | .. |
|---|
| 492 | 485 | } |
|---|
| 493 | 486 | |
|---|
| 494 | 487 | struct wbt_wait_data { |
|---|
| 495 | | - struct wait_queue_entry wq; |
|---|
| 496 | | - struct task_struct *task; |
|---|
| 497 | 488 | struct rq_wb *rwb; |
|---|
| 498 | | - struct rq_wait *rqw; |
|---|
| 489 | + enum wbt_flags wb_acct; |
|---|
| 499 | 490 | unsigned long rw; |
|---|
| 500 | | - bool got_token; |
|---|
| 501 | 491 | }; |
|---|
| 502 | 492 | |
|---|
| 503 | | -static int wbt_wake_function(struct wait_queue_entry *curr, unsigned int mode, |
|---|
| 504 | | - int wake_flags, void *key) |
|---|
| 493 | +static bool wbt_inflight_cb(struct rq_wait *rqw, void *private_data) |
|---|
| 505 | 494 | { |
|---|
| 506 | | - struct wbt_wait_data *data = container_of(curr, struct wbt_wait_data, |
|---|
| 507 | | - wq); |
|---|
| 495 | + struct wbt_wait_data *data = private_data; |
|---|
| 496 | + return rq_wait_inc_below(rqw, get_limit(data->rwb, data->rw)); |
|---|
| 497 | +} |
|---|
| 508 | 498 | |
|---|
| 509 | | - /* |
|---|
| 510 | | - * If we fail to get a budget, return -1 to interrupt the wake up |
|---|
| 511 | | - * loop in __wake_up_common. |
|---|
| 512 | | - */ |
|---|
| 513 | | - if (!rq_wait_inc_below(data->rqw, get_limit(data->rwb, data->rw))) |
|---|
| 514 | | - return -1; |
|---|
| 515 | | - |
|---|
| 516 | | - data->got_token = true; |
|---|
| 517 | | - list_del_init(&curr->entry); |
|---|
| 518 | | - wake_up_process(data->task); |
|---|
| 519 | | - return 1; |
|---|
| 499 | +static void wbt_cleanup_cb(struct rq_wait *rqw, void *private_data) |
|---|
| 500 | +{ |
|---|
| 501 | + struct wbt_wait_data *data = private_data; |
|---|
| 502 | + wbt_rqw_done(data->rwb, rqw, data->wb_acct); |
|---|
| 520 | 503 | } |
|---|
| 521 | 504 | |
|---|
| 522 | 505 | /* |
|---|
| .. | .. |
|---|
| 524 | 507 | * the timer to kick off queuing again. |
|---|
| 525 | 508 | */ |
|---|
| 526 | 509 | static void __wbt_wait(struct rq_wb *rwb, enum wbt_flags wb_acct, |
|---|
| 527 | | - unsigned long rw, spinlock_t *lock) |
|---|
| 528 | | - __releases(lock) |
|---|
| 529 | | - __acquires(lock) |
|---|
| 510 | + unsigned long rw) |
|---|
| 530 | 511 | { |
|---|
| 531 | 512 | struct rq_wait *rqw = get_rq_wait(rwb, wb_acct); |
|---|
| 532 | 513 | struct wbt_wait_data data = { |
|---|
| 533 | | - .wq = { |
|---|
| 534 | | - .func = wbt_wake_function, |
|---|
| 535 | | - .entry = LIST_HEAD_INIT(data.wq.entry), |
|---|
| 536 | | - }, |
|---|
| 537 | | - .task = current, |
|---|
| 538 | 514 | .rwb = rwb, |
|---|
| 539 | | - .rqw = rqw, |
|---|
| 515 | + .wb_acct = wb_acct, |
|---|
| 540 | 516 | .rw = rw, |
|---|
| 541 | 517 | }; |
|---|
| 542 | | - bool has_sleeper; |
|---|
| 543 | 518 | |
|---|
| 544 | | - has_sleeper = wq_has_sleeper(&rqw->wait); |
|---|
| 545 | | - if (!has_sleeper && rq_wait_inc_below(rqw, get_limit(rwb, rw))) |
|---|
| 546 | | - return; |
|---|
| 547 | | - |
|---|
| 548 | | - prepare_to_wait_exclusive(&rqw->wait, &data.wq, TASK_UNINTERRUPTIBLE); |
|---|
| 549 | | - do { |
|---|
| 550 | | - if (data.got_token) |
|---|
| 551 | | - break; |
|---|
| 552 | | - |
|---|
| 553 | | - if (!has_sleeper && |
|---|
| 554 | | - rq_wait_inc_below(rqw, get_limit(rwb, rw))) { |
|---|
| 555 | | - finish_wait(&rqw->wait, &data.wq); |
|---|
| 556 | | - |
|---|
| 557 | | - /* |
|---|
| 558 | | - * We raced with wbt_wake_function() getting a token, |
|---|
| 559 | | - * which means we now have two. Put our local token |
|---|
| 560 | | - * and wake anyone else potentially waiting for one. |
|---|
| 561 | | - */ |
|---|
| 562 | | - if (data.got_token) |
|---|
| 563 | | - wbt_rqw_done(rwb, rqw, wb_acct); |
|---|
| 564 | | - break; |
|---|
| 565 | | - } |
|---|
| 566 | | - |
|---|
| 567 | | - if (lock) { |
|---|
| 568 | | - spin_unlock_irq(lock); |
|---|
| 569 | | - io_schedule(); |
|---|
| 570 | | - spin_lock_irq(lock); |
|---|
| 571 | | - } else |
|---|
| 572 | | - io_schedule(); |
|---|
| 573 | | - |
|---|
| 574 | | - has_sleeper = false; |
|---|
| 575 | | - } while (1); |
|---|
| 576 | | - |
|---|
| 577 | | - finish_wait(&rqw->wait, &data.wq); |
|---|
| 519 | + rq_qos_wait(rqw, &data, wbt_inflight_cb, wbt_cleanup_cb); |
|---|
| 578 | 520 | } |
|---|
| 579 | 521 | |
|---|
| 580 | 522 | static inline bool wbt_should_throttle(struct rq_wb *rwb, struct bio *bio) |
|---|
| .. | .. |
|---|
| 587 | 529 | if ((bio->bi_opf & (REQ_SYNC | REQ_IDLE)) == |
|---|
| 588 | 530 | (REQ_SYNC | REQ_IDLE)) |
|---|
| 589 | 531 | return false; |
|---|
| 590 | | - /* fallthrough */ |
|---|
| 532 | + fallthrough; |
|---|
| 591 | 533 | case REQ_OP_DISCARD: |
|---|
| 592 | 534 | return true; |
|---|
| 593 | 535 | default: |
|---|
| .. | .. |
|---|
| 627 | 569 | * in an irq held spinlock, if it holds one when calling this function. |
|---|
| 628 | 570 | * If we do sleep, we'll release and re-grab it. |
|---|
| 629 | 571 | */ |
|---|
| 630 | | -static void wbt_wait(struct rq_qos *rqos, struct bio *bio, spinlock_t *lock) |
|---|
| 572 | +static void wbt_wait(struct rq_qos *rqos, struct bio *bio) |
|---|
| 631 | 573 | { |
|---|
| 632 | 574 | struct rq_wb *rwb = RQWB(rqos); |
|---|
| 633 | 575 | enum wbt_flags flags; |
|---|
| .. | .. |
|---|
| 639 | 581 | return; |
|---|
| 640 | 582 | } |
|---|
| 641 | 583 | |
|---|
| 642 | | - __wbt_wait(rwb, flags, bio->bi_opf, lock); |
|---|
| 584 | + __wbt_wait(rwb, flags, bio->bi_opf); |
|---|
| 643 | 585 | |
|---|
| 644 | 586 | if (!blk_stat_is_active(rwb->cb)) |
|---|
| 645 | 587 | rwb_arm_timer(rwb); |
|---|
| .. | .. |
|---|
| 651 | 593 | rq->wbt_flags |= bio_to_wbt_flags(rwb, bio); |
|---|
| 652 | 594 | } |
|---|
| 653 | 595 | |
|---|
| 654 | | -void wbt_issue(struct rq_qos *rqos, struct request *rq) |
|---|
| 596 | +static void wbt_issue(struct rq_qos *rqos, struct request *rq) |
|---|
| 655 | 597 | { |
|---|
| 656 | 598 | struct rq_wb *rwb = RQWB(rqos); |
|---|
| 657 | 599 | |
|---|
| .. | .. |
|---|
| 671 | 613 | } |
|---|
| 672 | 614 | } |
|---|
| 673 | 615 | |
|---|
| 674 | | -void wbt_requeue(struct rq_qos *rqos, struct request *rq) |
|---|
| 616 | +static void wbt_requeue(struct rq_qos *rqos, struct request *rq) |
|---|
| 675 | 617 | { |
|---|
| 676 | 618 | struct rq_wb *rwb = RQWB(rqos); |
|---|
| 677 | 619 | if (!rwb_enabled(rwb)) |
|---|
| .. | .. |
|---|
| 679 | 621 | if (rq == rwb->sync_cookie) { |
|---|
| 680 | 622 | rwb->sync_issue = 0; |
|---|
| 681 | 623 | rwb->sync_cookie = NULL; |
|---|
| 682 | | - } |
|---|
| 683 | | -} |
|---|
| 684 | | - |
|---|
| 685 | | -void wbt_set_queue_depth(struct request_queue *q, unsigned int depth) |
|---|
| 686 | | -{ |
|---|
| 687 | | - struct rq_qos *rqos = wbt_rq_qos(q); |
|---|
| 688 | | - if (rqos) { |
|---|
| 689 | | - RQWB(rqos)->rq_depth.queue_depth = depth; |
|---|
| 690 | | - __wbt_update_limits(RQWB(rqos)); |
|---|
| 691 | 624 | } |
|---|
| 692 | 625 | } |
|---|
| 693 | 626 | |
|---|
| .. | .. |
|---|
| 716 | 649 | if (!blk_queue_registered(q)) |
|---|
| 717 | 650 | return; |
|---|
| 718 | 651 | |
|---|
| 719 | | - if ((q->mq_ops && IS_ENABLED(CONFIG_BLK_WBT_MQ)) || |
|---|
| 720 | | - (q->request_fn && IS_ENABLED(CONFIG_BLK_WBT_SQ))) |
|---|
| 652 | + if (queue_is_mq(q) && IS_ENABLED(CONFIG_BLK_WBT_MQ)) |
|---|
| 721 | 653 | wbt_init(q); |
|---|
| 722 | 654 | } |
|---|
| 723 | 655 | EXPORT_SYMBOL_GPL(wbt_enable_default); |
|---|
| .. | .. |
|---|
| 747 | 679 | return -1; |
|---|
| 748 | 680 | } |
|---|
| 749 | 681 | |
|---|
| 682 | +static void wbt_queue_depth_changed(struct rq_qos *rqos) |
|---|
| 683 | +{ |
|---|
| 684 | + RQWB(rqos)->rq_depth.queue_depth = blk_queue_depth(rqos->q); |
|---|
| 685 | + wbt_update_limits(RQWB(rqos)); |
|---|
| 686 | +} |
|---|
| 687 | + |
|---|
| 750 | 688 | static void wbt_exit(struct rq_qos *rqos) |
|---|
| 751 | 689 | { |
|---|
| 752 | 690 | struct rq_wb *rwb = RQWB(rqos); |
|---|
| .. | .. |
|---|
| 774 | 712 | } |
|---|
| 775 | 713 | EXPORT_SYMBOL_GPL(wbt_disable_default); |
|---|
| 776 | 714 | |
|---|
| 715 | +#ifdef CONFIG_BLK_DEBUG_FS |
|---|
| 716 | +static int wbt_curr_win_nsec_show(void *data, struct seq_file *m) |
|---|
| 717 | +{ |
|---|
| 718 | + struct rq_qos *rqos = data; |
|---|
| 719 | + struct rq_wb *rwb = RQWB(rqos); |
|---|
| 720 | + |
|---|
| 721 | + seq_printf(m, "%llu\n", rwb->cur_win_nsec); |
|---|
| 722 | + return 0; |
|---|
| 723 | +} |
|---|
| 724 | + |
|---|
| 725 | +static int wbt_enabled_show(void *data, struct seq_file *m) |
|---|
| 726 | +{ |
|---|
| 727 | + struct rq_qos *rqos = data; |
|---|
| 728 | + struct rq_wb *rwb = RQWB(rqos); |
|---|
| 729 | + |
|---|
| 730 | + seq_printf(m, "%d\n", rwb->enable_state); |
|---|
| 731 | + return 0; |
|---|
| 732 | +} |
|---|
| 733 | + |
|---|
| 734 | +static int wbt_id_show(void *data, struct seq_file *m) |
|---|
| 735 | +{ |
|---|
| 736 | + struct rq_qos *rqos = data; |
|---|
| 737 | + |
|---|
| 738 | + seq_printf(m, "%u\n", rqos->id); |
|---|
| 739 | + return 0; |
|---|
| 740 | +} |
|---|
| 741 | + |
|---|
| 742 | +static int wbt_inflight_show(void *data, struct seq_file *m) |
|---|
| 743 | +{ |
|---|
| 744 | + struct rq_qos *rqos = data; |
|---|
| 745 | + struct rq_wb *rwb = RQWB(rqos); |
|---|
| 746 | + int i; |
|---|
| 747 | + |
|---|
| 748 | + for (i = 0; i < WBT_NUM_RWQ; i++) |
|---|
| 749 | + seq_printf(m, "%d: inflight %d\n", i, |
|---|
| 750 | + atomic_read(&rwb->rq_wait[i].inflight)); |
|---|
| 751 | + return 0; |
|---|
| 752 | +} |
|---|
| 753 | + |
|---|
| 754 | +static int wbt_min_lat_nsec_show(void *data, struct seq_file *m) |
|---|
| 755 | +{ |
|---|
| 756 | + struct rq_qos *rqos = data; |
|---|
| 757 | + struct rq_wb *rwb = RQWB(rqos); |
|---|
| 758 | + |
|---|
| 759 | + seq_printf(m, "%lu\n", rwb->min_lat_nsec); |
|---|
| 760 | + return 0; |
|---|
| 761 | +} |
|---|
| 762 | + |
|---|
| 763 | +static int wbt_unknown_cnt_show(void *data, struct seq_file *m) |
|---|
| 764 | +{ |
|---|
| 765 | + struct rq_qos *rqos = data; |
|---|
| 766 | + struct rq_wb *rwb = RQWB(rqos); |
|---|
| 767 | + |
|---|
| 768 | + seq_printf(m, "%u\n", rwb->unknown_cnt); |
|---|
| 769 | + return 0; |
|---|
| 770 | +} |
|---|
| 771 | + |
|---|
| 772 | +static int wbt_normal_show(void *data, struct seq_file *m) |
|---|
| 773 | +{ |
|---|
| 774 | + struct rq_qos *rqos = data; |
|---|
| 775 | + struct rq_wb *rwb = RQWB(rqos); |
|---|
| 776 | + |
|---|
| 777 | + seq_printf(m, "%u\n", rwb->wb_normal); |
|---|
| 778 | + return 0; |
|---|
| 779 | +} |
|---|
| 780 | + |
|---|
| 781 | +static int wbt_background_show(void *data, struct seq_file *m) |
|---|
| 782 | +{ |
|---|
| 783 | + struct rq_qos *rqos = data; |
|---|
| 784 | + struct rq_wb *rwb = RQWB(rqos); |
|---|
| 785 | + |
|---|
| 786 | + seq_printf(m, "%u\n", rwb->wb_background); |
|---|
| 787 | + return 0; |
|---|
| 788 | +} |
|---|
| 789 | + |
|---|
| 790 | +static const struct blk_mq_debugfs_attr wbt_debugfs_attrs[] = { |
|---|
| 791 | + {"curr_win_nsec", 0400, wbt_curr_win_nsec_show}, |
|---|
| 792 | + {"enabled", 0400, wbt_enabled_show}, |
|---|
| 793 | + {"id", 0400, wbt_id_show}, |
|---|
| 794 | + {"inflight", 0400, wbt_inflight_show}, |
|---|
| 795 | + {"min_lat_nsec", 0400, wbt_min_lat_nsec_show}, |
|---|
| 796 | + {"unknown_cnt", 0400, wbt_unknown_cnt_show}, |
|---|
| 797 | + {"wb_normal", 0400, wbt_normal_show}, |
|---|
| 798 | + {"wb_background", 0400, wbt_background_show}, |
|---|
| 799 | + {}, |
|---|
| 800 | +}; |
|---|
| 801 | +#endif |
|---|
| 777 | 802 | |
|---|
| 778 | 803 | static struct rq_qos_ops wbt_rqos_ops = { |
|---|
| 779 | 804 | .throttle = wbt_wait, |
|---|
| .. | .. |
|---|
| 782 | 807 | .requeue = wbt_requeue, |
|---|
| 783 | 808 | .done = wbt_done, |
|---|
| 784 | 809 | .cleanup = wbt_cleanup, |
|---|
| 810 | + .queue_depth_changed = wbt_queue_depth_changed, |
|---|
| 785 | 811 | .exit = wbt_exit, |
|---|
| 812 | +#ifdef CONFIG_BLK_DEBUG_FS |
|---|
| 813 | + .debugfs_attrs = wbt_debugfs_attrs, |
|---|
| 814 | +#endif |
|---|
| 786 | 815 | }; |
|---|
| 787 | 816 | |
|---|
| 788 | 817 | int wbt_init(struct request_queue *q) |
|---|
| .. | .. |
|---|
| 809 | 838 | rwb->last_comp = rwb->last_issue = jiffies; |
|---|
| 810 | 839 | rwb->win_nsec = RWB_WINDOW_NSEC; |
|---|
| 811 | 840 | rwb->enable_state = WBT_STATE_ON_DEFAULT; |
|---|
| 812 | | - rwb->wc = 1; |
|---|
| 841 | + rwb->wc = test_bit(QUEUE_FLAG_WC, &q->queue_flags); |
|---|
| 813 | 842 | rwb->rq_depth.default_depth = RWB_DEF_DEPTH; |
|---|
| 814 | | - __wbt_update_limits(rwb); |
|---|
| 843 | + rwb->min_lat_nsec = wbt_default_latency_nsec(q); |
|---|
| 844 | + |
|---|
| 845 | + wbt_queue_depth_changed(&rwb->rqos); |
|---|
| 815 | 846 | |
|---|
| 816 | 847 | /* |
|---|
| 817 | 848 | * Assign rwb and add the stats callback. |
|---|
| 818 | 849 | */ |
|---|
| 819 | 850 | rq_qos_add(q, &rwb->rqos); |
|---|
| 820 | 851 | blk_stat_add_callback(q, rwb->cb); |
|---|
| 821 | | - |
|---|
| 822 | | - rwb->min_lat_nsec = wbt_default_latency_nsec(q); |
|---|
| 823 | | - |
|---|
| 824 | | - wbt_set_queue_depth(q, blk_queue_depth(q)); |
|---|
| 825 | | - wbt_set_write_cache(q, test_bit(QUEUE_FLAG_WC, &q->queue_flags)); |
|---|
| 826 | 852 | |
|---|
| 827 | 853 | return 0; |
|---|
| 828 | 854 | } |
|---|