162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Block rq-qos policy for assigning an I/O priority class to requests. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Using an rq-qos policy for assigning I/O priority class has two advantages 662306a36Sopenharmony_ci * over using the ioprio_set() system call: 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * - This policy is cgroup based so it has all the advantages of cgroups. 962306a36Sopenharmony_ci * - While ioprio_set() does not affect page cache writeback I/O, this rq-qos 1062306a36Sopenharmony_ci * controller affects page cache writeback I/O for filesystems that support 1162306a36Sopenharmony_ci * assiociating a cgroup with writeback I/O. See also 1262306a36Sopenharmony_ci * Documentation/admin-guide/cgroup-v2.rst. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include <linux/blk-mq.h> 1662306a36Sopenharmony_ci#include <linux/blk_types.h> 1762306a36Sopenharmony_ci#include <linux/kernel.h> 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include "blk-cgroup.h" 2062306a36Sopenharmony_ci#include "blk-ioprio.h" 2162306a36Sopenharmony_ci#include "blk-rq-qos.h" 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci/** 2462306a36Sopenharmony_ci * enum prio_policy - I/O priority class policy. 2562306a36Sopenharmony_ci * @POLICY_NO_CHANGE: (default) do not modify the I/O priority class. 2662306a36Sopenharmony_ci * @POLICY_PROMOTE_TO_RT: modify no-IOPRIO_CLASS_RT to IOPRIO_CLASS_RT. 2762306a36Sopenharmony_ci * @POLICY_RESTRICT_TO_BE: modify IOPRIO_CLASS_NONE and IOPRIO_CLASS_RT into 2862306a36Sopenharmony_ci * IOPRIO_CLASS_BE. 2962306a36Sopenharmony_ci * @POLICY_ALL_TO_IDLE: change the I/O priority class into IOPRIO_CLASS_IDLE. 3062306a36Sopenharmony_ci * @POLICY_NONE_TO_RT: an alias for POLICY_PROMOTE_TO_RT. 3162306a36Sopenharmony_ci * 3262306a36Sopenharmony_ci * See also <linux/ioprio.h>. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_cienum prio_policy { 3562306a36Sopenharmony_ci POLICY_NO_CHANGE = 0, 3662306a36Sopenharmony_ci POLICY_PROMOTE_TO_RT = 1, 3762306a36Sopenharmony_ci POLICY_RESTRICT_TO_BE = 2, 3862306a36Sopenharmony_ci POLICY_ALL_TO_IDLE = 3, 3962306a36Sopenharmony_ci POLICY_NONE_TO_RT = 4, 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic const char *policy_name[] = { 4362306a36Sopenharmony_ci [POLICY_NO_CHANGE] = "no-change", 4462306a36Sopenharmony_ci [POLICY_PROMOTE_TO_RT] = "promote-to-rt", 4562306a36Sopenharmony_ci [POLICY_RESTRICT_TO_BE] = "restrict-to-be", 4662306a36Sopenharmony_ci [POLICY_ALL_TO_IDLE] = "idle", 4762306a36Sopenharmony_ci [POLICY_NONE_TO_RT] = "none-to-rt", 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic struct blkcg_policy ioprio_policy; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/** 5362306a36Sopenharmony_ci * struct ioprio_blkg - Per (cgroup, request queue) data. 5462306a36Sopenharmony_ci * @pd: blkg_policy_data structure. 5562306a36Sopenharmony_ci */ 5662306a36Sopenharmony_cistruct ioprio_blkg { 5762306a36Sopenharmony_ci struct blkg_policy_data pd; 5862306a36Sopenharmony_ci}; 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci/** 6162306a36Sopenharmony_ci * struct ioprio_blkcg - Per cgroup data. 6262306a36Sopenharmony_ci * @cpd: blkcg_policy_data structure. 6362306a36Sopenharmony_ci * @prio_policy: One of the IOPRIO_CLASS_* values. See also <linux/ioprio.h>. 6462306a36Sopenharmony_ci */ 6562306a36Sopenharmony_cistruct ioprio_blkcg { 6662306a36Sopenharmony_ci struct blkcg_policy_data cpd; 6762306a36Sopenharmony_ci enum prio_policy prio_policy; 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic inline struct ioprio_blkg *pd_to_ioprio(struct blkg_policy_data *pd) 7162306a36Sopenharmony_ci{ 7262306a36Sopenharmony_ci return pd ? container_of(pd, struct ioprio_blkg, pd) : NULL; 7362306a36Sopenharmony_ci} 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_cistatic struct ioprio_blkcg *blkcg_to_ioprio_blkcg(struct blkcg *blkcg) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci return container_of(blkcg_to_cpd(blkcg, &ioprio_policy), 7862306a36Sopenharmony_ci struct ioprio_blkcg, cpd); 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic struct ioprio_blkcg * 8262306a36Sopenharmony_ciioprio_blkcg_from_css(struct cgroup_subsys_state *css) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci return blkcg_to_ioprio_blkcg(css_to_blkcg(css)); 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_cistatic struct ioprio_blkcg *ioprio_blkcg_from_bio(struct bio *bio) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct blkg_policy_data *pd = blkg_to_pd(bio->bi_blkg, &ioprio_policy); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (!pd) 9262306a36Sopenharmony_ci return NULL; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return blkcg_to_ioprio_blkcg(pd->blkg->blkcg); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic int ioprio_show_prio_policy(struct seq_file *sf, void *v) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci struct ioprio_blkcg *blkcg = ioprio_blkcg_from_css(seq_css(sf)); 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci seq_printf(sf, "%s\n", policy_name[blkcg->prio_policy]); 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic ssize_t ioprio_set_prio_policy(struct kernfs_open_file *of, char *buf, 10662306a36Sopenharmony_ci size_t nbytes, loff_t off) 10762306a36Sopenharmony_ci{ 10862306a36Sopenharmony_ci struct ioprio_blkcg *blkcg = ioprio_blkcg_from_css(of_css(of)); 10962306a36Sopenharmony_ci int ret; 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci if (off != 0) 11262306a36Sopenharmony_ci return -EIO; 11362306a36Sopenharmony_ci /* kernfs_fop_write_iter() terminates 'buf' with '\0'. */ 11462306a36Sopenharmony_ci ret = sysfs_match_string(policy_name, buf); 11562306a36Sopenharmony_ci if (ret < 0) 11662306a36Sopenharmony_ci return ret; 11762306a36Sopenharmony_ci blkcg->prio_policy = ret; 11862306a36Sopenharmony_ci return nbytes; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_cistatic struct blkg_policy_data * 12262306a36Sopenharmony_ciioprio_alloc_pd(struct gendisk *disk, struct blkcg *blkcg, gfp_t gfp) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct ioprio_blkg *ioprio_blkg; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci ioprio_blkg = kzalloc(sizeof(*ioprio_blkg), gfp); 12762306a36Sopenharmony_ci if (!ioprio_blkg) 12862306a36Sopenharmony_ci return NULL; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci return &ioprio_blkg->pd; 13162306a36Sopenharmony_ci} 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_cistatic void ioprio_free_pd(struct blkg_policy_data *pd) 13462306a36Sopenharmony_ci{ 13562306a36Sopenharmony_ci struct ioprio_blkg *ioprio_blkg = pd_to_ioprio(pd); 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci kfree(ioprio_blkg); 13862306a36Sopenharmony_ci} 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_cistatic struct blkcg_policy_data *ioprio_alloc_cpd(gfp_t gfp) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct ioprio_blkcg *blkcg; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci blkcg = kzalloc(sizeof(*blkcg), gfp); 14562306a36Sopenharmony_ci if (!blkcg) 14662306a36Sopenharmony_ci return NULL; 14762306a36Sopenharmony_ci blkcg->prio_policy = POLICY_NO_CHANGE; 14862306a36Sopenharmony_ci return &blkcg->cpd; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic void ioprio_free_cpd(struct blkcg_policy_data *cpd) 15262306a36Sopenharmony_ci{ 15362306a36Sopenharmony_ci struct ioprio_blkcg *blkcg = container_of(cpd, typeof(*blkcg), cpd); 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci kfree(blkcg); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci#define IOPRIO_ATTRS \ 15962306a36Sopenharmony_ci { \ 16062306a36Sopenharmony_ci .name = "prio.class", \ 16162306a36Sopenharmony_ci .seq_show = ioprio_show_prio_policy, \ 16262306a36Sopenharmony_ci .write = ioprio_set_prio_policy, \ 16362306a36Sopenharmony_ci }, \ 16462306a36Sopenharmony_ci { } /* sentinel */ 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* cgroup v2 attributes */ 16762306a36Sopenharmony_cistatic struct cftype ioprio_files[] = { 16862306a36Sopenharmony_ci IOPRIO_ATTRS 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/* cgroup v1 attributes */ 17262306a36Sopenharmony_cistatic struct cftype ioprio_legacy_files[] = { 17362306a36Sopenharmony_ci IOPRIO_ATTRS 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic struct blkcg_policy ioprio_policy = { 17762306a36Sopenharmony_ci .dfl_cftypes = ioprio_files, 17862306a36Sopenharmony_ci .legacy_cftypes = ioprio_legacy_files, 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci .cpd_alloc_fn = ioprio_alloc_cpd, 18162306a36Sopenharmony_ci .cpd_free_fn = ioprio_free_cpd, 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci .pd_alloc_fn = ioprio_alloc_pd, 18462306a36Sopenharmony_ci .pd_free_fn = ioprio_free_pd, 18562306a36Sopenharmony_ci}; 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_civoid blkcg_set_ioprio(struct bio *bio) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci struct ioprio_blkcg *blkcg = ioprio_blkcg_from_bio(bio); 19062306a36Sopenharmony_ci u16 prio; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (!blkcg || blkcg->prio_policy == POLICY_NO_CHANGE) 19362306a36Sopenharmony_ci return; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci if (blkcg->prio_policy == POLICY_PROMOTE_TO_RT || 19662306a36Sopenharmony_ci blkcg->prio_policy == POLICY_NONE_TO_RT) { 19762306a36Sopenharmony_ci /* 19862306a36Sopenharmony_ci * For RT threads, the default priority level is 4 because 19962306a36Sopenharmony_ci * task_nice is 0. By promoting non-RT io-priority to RT-class 20062306a36Sopenharmony_ci * and default level 4, those requests that are already 20162306a36Sopenharmony_ci * RT-class but need a higher io-priority can use ioprio_set() 20262306a36Sopenharmony_ci * to achieve this. 20362306a36Sopenharmony_ci */ 20462306a36Sopenharmony_ci if (IOPRIO_PRIO_CLASS(bio->bi_ioprio) != IOPRIO_CLASS_RT) 20562306a36Sopenharmony_ci bio->bi_ioprio = IOPRIO_PRIO_VALUE(IOPRIO_CLASS_RT, 4); 20662306a36Sopenharmony_ci return; 20762306a36Sopenharmony_ci } 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci /* 21062306a36Sopenharmony_ci * Except for IOPRIO_CLASS_NONE, higher I/O priority numbers 21162306a36Sopenharmony_ci * correspond to a lower priority. Hence, the max_t() below selects 21262306a36Sopenharmony_ci * the lower priority of bi_ioprio and the cgroup I/O priority class. 21362306a36Sopenharmony_ci * If the bio I/O priority equals IOPRIO_CLASS_NONE, the cgroup I/O 21462306a36Sopenharmony_ci * priority is assigned to the bio. 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_ci prio = max_t(u16, bio->bi_ioprio, 21762306a36Sopenharmony_ci IOPRIO_PRIO_VALUE(blkcg->prio_policy, 0)); 21862306a36Sopenharmony_ci if (prio > bio->bi_ioprio) 21962306a36Sopenharmony_ci bio->bi_ioprio = prio; 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_civoid blk_ioprio_exit(struct gendisk *disk) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci blkcg_deactivate_policy(disk, &ioprio_policy); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ciint blk_ioprio_init(struct gendisk *disk) 22862306a36Sopenharmony_ci{ 22962306a36Sopenharmony_ci return blkcg_activate_policy(disk, &ioprio_policy); 23062306a36Sopenharmony_ci} 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_cistatic int __init ioprio_init(void) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci return blkcg_policy_register(&ioprio_policy); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistatic void __exit ioprio_exit(void) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci blkcg_policy_unregister(&ioprio_policy); 24062306a36Sopenharmony_ci} 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_cimodule_init(ioprio_init); 24362306a36Sopenharmony_cimodule_exit(ioprio_exit); 244