| // SPDX-License-Identifier: GPL-2.0 | |
| /* | |
| * Block rq-qos policy for assigning an I/O priority class to requests. | |
| * | |
| * Using an rq-qos policy for assigning I/O priority class has two advantages | |
| * over using the ioprio_set() system call: | |
| * | |
| * - This policy is cgroup based so it has all the advantages of cgroups. | |
| * - While ioprio_set() does not affect page cache writeback I/O, this rq-qos | |
| * controller affects page cache writeback I/O for filesystems that support | |
| * assiociating a cgroup with writeback I/O. See also | |
| * Documentation/admin-guide/cgroup-v2.rst. | |
| */ | |
| #include <linux/blk-mq.h> | |
| #include <linux/blk_types.h> | |
| #include <linux/kernel.h> | |
| #include <linux/module.h> | |
| #include "blk-cgroup.h" | |
| #include "blk-ioprio.h" | |
| #include "blk-rq-qos.h" | |
| /** | |
| * enum prio_policy - I/O priority class policy. | |
| * @POLICY_NO_CHANGE: (default) do not modify the I/O priority class. | |
| * @POLICY_PROMOTE_TO_RT: modify no-IOPRIO_CLASS_RT to IOPRIO_CLASS_RT. | |
| * @POLICY_RESTRICT_TO_BE: modify IOPRIO_CLASS_NONE and IOPRIO_CLASS_RT into | |
| * IOPRIO_CLASS_BE. | |
| * @POLICY_ALL_TO_IDLE: change the I/O priority class into IOPRIO_CLASS_IDLE. | |
| * @POLICY_NONE_TO_RT: an alias for POLICY_PROMOTE_TO_RT. | |
| * | |
| * See also <linux/ioprio.h>. | |
| */ | |
| enum prio_policy { | |
| POLICY_NO_CHANGE = 0, | |
| POLICY_PROMOTE_TO_RT = 1, | |
| POLICY_RESTRICT_TO_BE = 2, | |
| POLICY_ALL_TO_IDLE = 3, | |
| POLICY_NONE_TO_RT = 4, | |
| }; | |
| static const char *policy_name[] = { | |
| [POLICY_NO_CHANGE] = "no-change", | |
| [POLICY_PROMOTE_TO_RT] = "promote-to-rt", | |
| [POLICY_RESTRICT_TO_BE] = "restrict-to-be", | |
| [POLICY_ALL_TO_IDLE] = "idle", | |
| [POLICY_NONE_TO_RT] = "none-to-rt", | |
| }; | |
| static struct blkcg_policy ioprio_policy; | |
| /** | |
| * struct ioprio_blkcg - Per cgroup data. | |
| * @cpd: blkcg_policy_data structure. | |
| * @prio_policy: One of the IOPRIO_CLASS_* values. See also <linux/ioprio.h>. | |
| */ | |
| struct ioprio_blkcg { | |
| struct blkcg_policy_data cpd; | |
| enum prio_policy prio_policy; | |
| }; | |
| static struct ioprio_blkcg *blkcg_to_ioprio_blkcg(struct blkcg *blkcg) | |
| { | |
| return container_of(blkcg_to_cpd(blkcg, &ioprio_policy), | |
| struct ioprio_blkcg, cpd); | |
| } | |
| static struct ioprio_blkcg * | |
| ioprio_blkcg_from_css(struct cgroup_subsys_state *css) | |
| { | |
| return blkcg_to_ioprio_blkcg(css_to_blkcg(css)); | |
| } | |
| static int ioprio_show_prio_policy(struct seq_file *sf, void *v) | |
| { | |
| struct ioprio_blkcg *blkcg = ioprio_blkcg_from_css(seq_css(sf)); | |
| seq_printf(sf, "%s\n", policy_name[blkcg->prio_policy]); | |
| return 0; | |
| } | |
| static ssize_t ioprio_set_prio_policy(struct kernfs_open_file *of, char *buf, | |
| size_t nbytes, loff_t off) | |
| { | |
| struct ioprio_blkcg *blkcg = ioprio_blkcg_from_css(of_css(of)); | |
| int ret; | |
| if (off != 0) | |
| return -EIO; | |
| /* kernfs_fop_write_iter() terminates 'buf' with '\0'. */ | |
| ret = sysfs_match_string(policy_name, buf); | |
| if (ret < 0) | |
| return ret; | |
| blkcg->prio_policy = ret; | |
| return nbytes; | |
| } | |
| static struct blkcg_policy_data *ioprio_alloc_cpd(gfp_t gfp) | |
| { | |
| struct ioprio_blkcg *blkcg; | |
| blkcg = kzalloc_obj(*blkcg, gfp); | |
| if (!blkcg) | |
| return NULL; | |
| blkcg->prio_policy = POLICY_NO_CHANGE; | |
| return &blkcg->cpd; | |
| } | |
| static void ioprio_free_cpd(struct blkcg_policy_data *cpd) | |
| { | |
| struct ioprio_blkcg *blkcg = container_of(cpd, typeof(*blkcg), cpd); | |
| kfree(blkcg); | |
| } | |
| static struct cftype ioprio_files[] = { | |
| { | |
| .name = "prio.class", | |
| .seq_show = ioprio_show_prio_policy, | |
| .write = ioprio_set_prio_policy, | |
| }, | |
| { } /* sentinel */ | |
| }; | |
| static struct blkcg_policy ioprio_policy = { | |
| .dfl_cftypes = ioprio_files, | |
| .legacy_cftypes = ioprio_files, | |
| .cpd_alloc_fn = ioprio_alloc_cpd, | |
| .cpd_free_fn = ioprio_free_cpd, | |
| }; | |
| void blkcg_set_ioprio(struct bio *bio) | |
| { | |
| struct ioprio_blkcg *blkcg = blkcg_to_ioprio_blkcg(bio->bi_blkg->blkcg); | |
| u16 prio; | |
| if (!blkcg || blkcg->prio_policy == POLICY_NO_CHANGE) | |
| return; | |
| if (blkcg->prio_policy == POLICY_PROMOTE_TO_RT || | |
| blkcg->prio_policy == POLICY_NONE_TO_RT) { | |
| /* | |
| * For RT threads, the default priority level is 4 because | |
| * task_nice is 0. By promoting non-RT io-priority to RT-class | |
| * and default level 4, those requests that are already | |
| * RT-class but need a higher io-priority can use ioprio_set() | |
| * to achieve this. | |
| */ | |
| if (IOPRIO_PRIO_CLASS(bio->bi_ioprio) != IOPRIO_CLASS_RT) | |
| bio->bi_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_RT, 4); | |
| return; | |
| } | |
| /* | |
| * Except for IOPRIO_CLASS_NONE, higher I/O priority numbers | |
| * correspond to a lower priority. Hence, the max_t() below selects | |
| * the lower priority of bi_ioprio and the cgroup I/O priority class. | |
| * If the bio I/O priority equals IOPRIO_CLASS_NONE, the cgroup I/O | |
| * priority is assigned to the bio. | |
| */ | |
| prio = max_t(u16, bio->bi_ioprio, | |
| IOPRIO_PRIO_VALUE(blkcg->prio_policy, 0)); | |
| if (prio > bio->bi_ioprio) | |
| bio->bi_ioprio = prio; | |
| } | |
| static int __init ioprio_init(void) | |
| { | |
| return blkcg_policy_register(&ioprio_policy); | |
| } | |
| static void __exit ioprio_exit(void) | |
| { | |
| blkcg_policy_unregister(&ioprio_policy); | |
| } | |
| module_init(ioprio_init); | |
| module_exit(ioprio_exit); |