/* SPDX-License-Identifier: GPL-2.0 */ #ifndef _BLK_CGROUP_H #define _BLK_CGROUP_H /* * Common Block IO controller cgroup interface * * Based on ideas and code from CFQ, CFS and BFQ: * Copyright (C) 2003 Jens Axboe * * Copyright (C) 2008 Fabio Checconi * Paolo Valente * * Copyright (C) 2009 Vivek Goyal * Nauman Rafique */ #include #include #include #include #include #include #include #include #include #include #define FC_APPID_LEN 129 #ifdef CONFIG_BLK_CGROUP enum blkg_iostat_type { BLKG_IOSTAT_READ, BLKG_IOSTAT_WRITE, BLKG_IOSTAT_DISCARD, BLKG_IOSTAT_NR, }; struct blkcg_gq; struct blkg_policy_data; struct blkcg { struct cgroup_subsys_state css; spinlock_t lock; refcount_t online_pin; struct radix_tree_root blkg_tree; struct blkcg_gq __rcu *blkg_hint; struct hlist_head blkg_list; struct blkcg_policy_data *cpd[BLKCG_MAX_POLS]; struct list_head all_blkcgs_node; #ifdef CONFIG_BLK_CGROUP_FC_APPID char fc_app_id[FC_APPID_LEN]; #endif #ifdef CONFIG_CGROUP_WRITEBACK struct list_head cgwb_list; #endif }; struct blkg_iostat { u64 bytes[BLKG_IOSTAT_NR]; u64 ios[BLKG_IOSTAT_NR]; }; struct blkg_iostat_set { struct u64_stats_sync sync; struct blkg_iostat cur; struct blkg_iostat last; }; /* association between a blk cgroup and a request queue */ struct blkcg_gq { /* Pointer to the associated request_queue */ struct request_queue *q; struct list_head q_node; struct hlist_node blkcg_node; struct blkcg *blkcg; /* all non-root blkcg_gq's are guaranteed to have access to parent */ struct blkcg_gq *parent; /* reference count */ struct percpu_ref refcnt; /* is this blkg online? protected by both blkcg and q locks */ bool online; struct blkg_iostat_set __percpu *iostat_cpu; struct blkg_iostat_set iostat; struct blkg_policy_data *pd[BLKCG_MAX_POLS]; spinlock_t async_bio_lock; struct bio_list async_bios; union { struct work_struct async_bio_work; struct work_struct free_work; }; atomic_t use_delay; atomic64_t delay_nsec; atomic64_t delay_start; u64 last_delay; int last_use; struct rcu_head rcu_head; }; extern struct cgroup_subsys_state * const blkcg_root_css; void blkcg_destroy_blkgs(struct blkcg *blkcg); void blkcg_schedule_throttle(struct request_queue *q, bool use_memdelay); void blkcg_maybe_throttle_current(void); static inline struct blkcg *css_to_blkcg(struct cgroup_subsys_state *css) { return css ? container_of(css, struct blkcg, css) : NULL; } /** * bio_blkcg - grab the blkcg associated with a bio * @bio: target bio * * This returns the blkcg associated with a bio, %NULL if not associated. * Callers are expected to either handle %NULL or know association has been * done prior to calling this. */ static inline struct blkcg *bio_blkcg(struct bio *bio) { if (bio && bio->bi_blkg) return bio->bi_blkg->blkcg; return NULL; } static inline bool blk_cgroup_congested(void) { struct cgroup_subsys_state *css; bool ret = false; rcu_read_lock(); css = kthread_blkcg(); if (!css) css = task_css(current, io_cgrp_id); while (css) { if (atomic_read(&css->cgroup->congestion_count)) { ret = true; break; } css = css->parent; } rcu_read_unlock(); return ret; } /** * blkcg_parent - get the parent of a blkcg * @blkcg: blkcg of interest * * Return the parent blkcg of @blkcg. Can be called anytime. */ static inline struct blkcg *blkcg_parent(struct blkcg *blkcg) { return css_to_blkcg(blkcg->css.parent); } /** * blkcg_pin_online - pin online state * @blkcg: blkcg of interest * * While pinned, a blkcg is kept online. This is primarily used to * impedance-match blkg and cgwb lifetimes so that blkg doesn't go offline * while an associated cgwb is still active. */ static inline void blkcg_pin_online(struct blkcg *blkcg) { refcount_inc(&blkcg->online_pin); } /** * blkcg_unpin_online - unpin online state * @blkcg: blkcg of interest * * This is primarily used to impedance-match blkg and cgwb lifetimes so * that blkg doesn't go offline while an associated cgwb is still active. * When this count goes to zero, all active cgwbs have finished so the * blkcg can continue destruction by calling blkcg_destroy_blkgs(). */ static inline void blkcg_unpin_online(struct blkcg *blkcg) { do { if (!refcount_dec_and_test(&blkcg->online_pin)) break; blkcg_destroy_blkgs(blkcg); blkcg = blkcg_parent(blkcg); } while (blkcg); } #else /* CONFIG_BLK_CGROUP */ struct blkcg { }; struct blkcg_gq { }; #define blkcg_root_css ((struct cgroup_subsys_state *)ERR_PTR(-EINVAL)) static inline void blkcg_maybe_throttle_current(void) { } static inline bool blk_cgroup_congested(void) { return false; } #ifdef CONFIG_BLOCK static inline void blkcg_schedule_throttle(struct request_queue *q, bool use_memdelay) { } static inline struct blkcg *bio_blkcg(struct bio *bio) { return NULL; } #endif /* CONFIG_BLOCK */ #endif /* CONFIG_BLK_CGROUP */ #ifdef CONFIG_BLK_CGROUP_FC_APPID /* * Sets the fc_app_id field associted to blkcg * @app_id: application identifier * @cgrp_id: cgroup id * @app_id_len: size of application identifier */ static inline int blkcg_set_fc_appid(char *app_id, u64 cgrp_id, size_t app_id_len) { struct cgroup *cgrp; struct cgroup_subsys_state *css; struct blkcg *blkcg; int ret = 0; if (app_id_len > FC_APPID_LEN) return -EINVAL; cgrp = cgroup_get_from_id(cgrp_id); if (!cgrp) return -ENOENT; css = cgroup_get_e_css(cgrp, &io_cgrp_subsys); if (!css) { ret = -ENOENT; goto out_cgrp_put; } blkcg = css_to_blkcg(css); /* * There is a slight race condition on setting the appid. * Worst case an I/O may not find the right id. * This is no different from the I/O we let pass while obtaining * the vmid from the fabric. * Adding the overhead of a lock is not necessary. */ strlcpy(blkcg->fc_app_id, app_id, app_id_len); css_put(css); out_cgrp_put: cgroup_put(cgrp); return ret; } /** * blkcg_get_fc_appid - get the fc app identifier associated with a bio * @bio: target bio * * On success return the fc_app_id, on failure return NULL */ static inline char *blkcg_get_fc_appid(struct bio *bio) { if (bio && bio->bi_blkg && (bio->bi_blkg->blkcg->fc_app_id[0] != '\0')) return bio->bi_blkg->blkcg->fc_app_id; return NULL; } #else static inline int blkcg_set_fc_appid(char *buf, u64 id, size_t len) { return -EINVAL; } static inline char *blkcg_get_fc_appid(struct bio *bio) { return NULL; } #endif /*CONFIG_BLK_CGROUP_FC_APPID*/ #endif /* _BLK_CGROUP_H */