// SPDX-License-Identifier: GPL-2.0-only/* * kernel/sched/debug.c * * Print the CFS rbtree and other debugging details * * Copyright(C) 2007, Red Hat, Inc., Ingo Molnar */#include <linux/debugfs.h>#include <linux/nmi.h>#include <linux/log2.h>#include "sched.h" /* * This allows printing both to /sys/kernel/debug/sched/debug and * to the console */#define SEQ_printf(m, x...) \ do { \ if (m) \ seq_printf(m, x); \ else \ pr_cont(x); \ } while (0) /* * Ease the printing of nsec fields: */static long long nsec_high(unsigned long long nsec){ if ((long long)nsec < 0) { nsec = -nsec; do_div(nsec, 1000000); return -nsec; } do_div(nsec, 1000000); return nsec;} static unsigned long nsec_low(unsigned long long nsec){ if ((long long)nsec < 0) nsec = -nsec; return do_div(nsec, 1000000);} #define SPLIT_NS(x) nsec_high(x), nsec_low(x) #define SCHED_FEAT(name, enabled) \ #name , static const char * const sched_feat_names[] = {#include "features.h"}; #undef SCHED_FEAT static int sched_feat_show(struct seq_file *m, void *v){ int i; for (i = 0; i < __SCHED_FEAT_NR; i++) { if (!(sysctl_sched_features & (1UL << i))) seq_puts(m, "NO_"); seq_printf(m, "%s ", sched_feat_names[i]); } seq_puts(m, "\n"); return 0;} #ifdef CONFIG_JUMP_LABEL #define jump_label_key__true STATIC_KEY_INIT_TRUE#define jump_label_key__false STATIC_KEY_INIT_FALSE #define SCHED_FEAT(name, enabled) \ jump_label_key__##enabled , struct static_key sched_feat_keys[__SCHED_FEAT_NR] = {#include "features.h"}; #undef SCHED_FEAT static void sched_feat_disable(int i){ static_key_disable_cpuslocked(&sched_feat_keys[i]);} static void sched_feat_enable(int i){ static_key_enable_cpuslocked(&sched_feat_keys[i]);}#else /* !CONFIG_JUMP_LABEL: */static void sched_feat_disable(int i) { };static void sched_feat_enable(int i) { };#endif /* !CONFIG_JUMP_LABEL */ static int sched_feat_set(char *cmp){ int i; int neg = 0; if (strncmp(cmp, "NO_", 3) == 0) { neg = 1; cmp += 3; } i = match_string(sched_feat_names, __SCHED_FEAT_NR, cmp); if (i < 0) return i; if (neg) { sysctl_sched_features &= ~(1UL << i); sched_feat_disable(i); } else { sysctl_sched_features |= (1UL << i); sched_feat_enable(i); } return 0;} static ssize_tsched_feat_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos){ char buf[64]; char *cmp; int ret; struct inode *inode; if (cnt > 63) cnt = 63; if (copy_from_user(buf, ubuf, cnt)) return -EFAULT; buf[cnt] = 0; cmp = strstrip(buf); /* Ensure the static_key remains in a consistent state */ inode = file_inode(filp); cpus_read_lock(); inode_lock(inode); ret = sched_feat_set(cmp); inode_unlock(inode); cpus_read_unlock(); if (ret < 0) return ret; *ppos += cnt; return cnt;} static int sched_feat_open(struct inode *inode, struct file *filp){ return single_open(filp, sched_feat_show, NULL);} static const struct file_operations sched_feat_fops = { .open = sched_feat_open, .write = sched_feat_write, .read = seq_read, .llseek = seq_lseek, .release = single_release,}; static ssize_t sched_scaling_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos){ unsigned int scaling; int ret; ret = kstrtouint_from_user(ubuf, cnt, 10, &scaling); if (ret) return ret; if (scaling >= SCHED_TUNABLESCALING_END) return -EINVAL; sysctl_sched_tunable_scaling = scaling; if (sched_update_scaling()) return -EINVAL; *ppos += cnt; return cnt;} static int sched_scaling_show(struct seq_file *m, void *v){ seq_printf(m, "%d\n", sysctl_sched_tunable_scaling); return 0;} static int sched_scaling_open(struct inode *inode, struct file *filp){ return single_open(filp, sched_scaling_show, NULL);} static const struct file_operations sched_scaling_fops = { .open = sched_scaling_open, .write = sched_scaling_write, .read = seq_read, .llseek = seq_lseek, .release = single_release,}; #ifdef CONFIG_SCHED_CACHEstatic ssize_tsched_cache_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos){ bool val; int ret; ret = kstrtobool_from_user(ubuf, cnt, &val); if (ret) return ret; sysctl_sched_cache_user = val; sched_cache_active_set(); *ppos += cnt; return cnt;} static int sched_cache_enable_show(struct seq_file *m, void *v){ seq_printf(m, "%d\n", sysctl_sched_cache_user); return 0;} static int sched_cache_enable_open(struct inode *inode, struct file *filp){ return single_open(filp, sched_cache_enable_show, NULL);} static const struct file_operations sched_cache_enable_fops = { .open = sched_cache_enable_open, .write = sched_cache_enable_write, .read = seq_read, .llseek = seq_lseek, .release = single_release,};#endif #ifdef CONFIG_PREEMPT_DYNAMIC static ssize_t sched_dynamic_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos){ char buf[16]; int mode; if (cnt > 15) cnt = 15; if (copy_from_user(buf, ubuf, cnt)) return -EFAULT; buf[cnt] = 0; mode = sched_dynamic_mode(strstrip(buf)); if (mode < 0) return mode; sched_dynamic_update(mode); *ppos += cnt; return cnt;} static int sched_dynamic_show(struct seq_file *m, void *v){ int i = (IS_ENABLED(CONFIG_PREEMPT_RT) || IS_ENABLED(CONFIG_ARCH_HAS_PREEMPT_LAZY)) * 2; int mode = READ_ONCE(preempt_dynamic_mode); int j; /* Count entries in NULL terminated preempt_modes */ for (j = 0; preempt_modes[j]; j++) ; j -= !IS_ENABLED(CONFIG_ARCH_HAS_PREEMPT_LAZY); for (; i < j; i++) { if (mode == i) seq_puts(m, "("); seq_puts(m, preempt_modes[i]); if (mode == i) seq_puts(m, ")"); seq_puts(m, " "); } seq_puts(m, "\n"); return 0;} static int sched_dynamic_open(struct inode *inode, struct file *filp){ return single_open(filp, sched_dynamic_show, NULL);} static const struct file_operations sched_dynamic_fops = { .open = sched_dynamic_open, .write = sched_dynamic_write, .read = seq_read, .llseek = seq_lseek, .release = single_release,}; #endif /* CONFIG_PREEMPT_DYNAMIC */ __read_mostly bool sched_debug_verbose; static struct dentry *sd_dentry; static ssize_t sched_verbose_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos){ ssize_t result; bool orig; cpus_read_lock(); sched_domains_mutex_lock(); orig = sched_debug_verbose; result = debugfs_write_file_bool(filp, ubuf, cnt, ppos); if (sched_debug_verbose && !orig) update_sched_domain_debugfs(); else if (!sched_debug_verbose && orig) { debugfs_remove(sd_dentry); sd_dentry = NULL; } sched_domains_mutex_unlock(); cpus_read_unlock(); return result;} static const struct file_operations sched_verbose_fops = { .read = debugfs_read_file_bool, .write = sched_verbose_write, .open = simple_open, .llseek = default_llseek,}; static const struct seq_operations sched_debug_sops; static int sched_debug_open(struct inode *inode, struct file *filp){ return seq_open(filp, &sched_debug_sops);} static const struct file_operations sched_debug_fops = { .open = sched_debug_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release,}; enum dl_param { DL_RUNTIME = 0, DL_PERIOD,}; static unsigned long dl_server_period_max = (1UL << 22) * NSEC_PER_USEC; /* ~4 seconds */static unsigned long dl_server_period_min = (100) * NSEC_PER_USEC; /* 100 us */ static ssize_t sched_server_write_common(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos, enum dl_param param, void *server){ long cpu = (long) ((struct seq_file *) filp->private_data)->private; struct sched_dl_entity *dl_se = (struct sched_dl_entity *)server; u64 old_runtime, runtime, period; struct rq *rq = cpu_rq(cpu); int retval = 0; size_t err; u64 value; err = kstrtoull_from_user(ubuf, cnt, 10, &value); if (err) return err; scoped_guard (rq_lock_irqsave, rq) { old_runtime = runtime = dl_se->dl_runtime; period = dl_se->dl_period; switch (param) { case DL_RUNTIME: if (runtime == value) break; runtime = value; break; case DL_PERIOD: if (value == period) break; period = value; break; } if (runtime > period || period > dl_server_period_max || period < dl_server_period_min) { return -EINVAL; } if (!cpu_online(cpu_of(rq))) return -EBUSY; update_rq_clock(rq); dl_server_stop(dl_se); retval = dl_server_apply_params(dl_se, runtime, period, 0); dl_server_start(dl_se); if (retval < 0) return retval; } if (!!old_runtime ^ !!runtime) { pr_info("%s server %sabled on CPU %d%s.\n", server == &rq->fair_server ? "Fair" : "Ext", runtime ? "en" : "dis", cpu_of(rq), runtime ? "" : ", system may malfunction due to starvation"); } *ppos += cnt; return cnt;} static size_t sched_server_show_common(struct seq_file *m, void *v, enum dl_param param, void *server){ struct sched_dl_entity *dl_se = (struct sched_dl_entity *)server; u64 value; switch (param) { case DL_RUNTIME: value = dl_se->dl_runtime; break; case DL_PERIOD: value = dl_se->dl_period; break; } seq_printf(m, "%llu\n", value); return 0;} static ssize_tsched_fair_server_runtime_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos){ long cpu = (long) ((struct seq_file *) filp->private_data)->private; struct rq *rq = cpu_rq(cpu); return sched_server_write_common(filp, ubuf, cnt, ppos, DL_RUNTIME, &rq->fair_server);} static int sched_fair_server_runtime_show(struct seq_file *m, void *v){ unsigned long cpu = (unsigned long) m->private; struct rq *rq = cpu_rq(cpu); return sched_server_show_common(m, v, DL_RUNTIME, &rq->fair_server);} static int sched_fair_server_runtime_open(struct inode *inode, struct file *filp){ return single_open(filp, sched_fair_server_runtime_show, inode->i_private);} static const struct file_operations fair_server_runtime_fops = { .open = sched_fair_server_runtime_open, .write = sched_fair_server_runtime_write, .read = seq_read, .llseek = seq_lseek, .release = single_release,}; static struct dentry *debugfs_sched; #ifdef CONFIG_SCHED_CLASS_EXTstatic ssize_tsched_ext_server_runtime_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos){Lines 1–500 of 1541. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * kernel/sched/debug.c * * Print the CFS rbtree and other debugging details * * Copyright(C) 2007, Red Hat, Inc., Ingo Molnar */ #include <linux/debugfs.h> #include <linux/nmi.h> #include <linux/log2.h> #include "sched.h" /* * This allows printing both to /sys/kernel/debug/sched/debug and * to the console */ #define SEQ_printf(m, x...) \ do { \ if (m) \ seq_printf(m, x); \ else \ pr_cont(x); \ } while (0) /* * Ease the printing of nsec fields: */ static long long nsec_high(unsigned long long nsec) { if ((long long)nsec < 0) { nsec = -nsec; do_div(nsec, 1000000); return -nsec; } do_div(nsec, 1000000); return nsec; } static unsigned long nsec_low(unsigned long long nsec) { if ((long long)nsec < 0) nsec = -nsec; return do_div(nsec, 1000000); } #define SPLIT_NS(x) nsec_high(x), nsec_low(x) #define SCHED_FEAT(name, enabled) \ #name , static const char * const sched_feat_names[] = { #include "features.h" }; #undef SCHED_FEAT static int sched_feat_show(struct seq_file *m, void *v) { int i; for (i = 0; i < __SCHED_FEAT_NR; i++) { if (!(sysctl_sched_features & (1UL << i))) seq_puts(m, "NO_"); seq_printf(m, "%s ", sched_feat_names[i]); } seq_puts(m, "\n"); return 0; } #ifdef CONFIG_JUMP_LABEL #define jump_label_key__true STATIC_KEY_INIT_TRUE #define jump_label_key__false STATIC_KEY_INIT_FALSE #define SCHED_FEAT(name, enabled) \ jump_label_key__##enabled , struct static_key sched_feat_keys[__SCHED_FEAT_NR] = { #include "features.h" }; #undef SCHED_FEAT static void sched_feat_disable(int i) { static_key_disable_cpuslocked(&sched_feat_keys[i]); } static void sched_feat_enable(int i) { static_key_enable_cpuslocked(&sched_feat_keys[i]); } #else /* !CONFIG_JUMP_LABEL: */ static void sched_feat_disable(int i) { }; static void sched_feat_enable(int i) { }; #endif /* !CONFIG_JUMP_LABEL */ static int sched_feat_set(char *cmp) { int i; int neg = 0; if (strncmp(cmp, "NO_", 3) == 0) { neg = 1; cmp += 3; } i = match_string(sched_feat_names, __SCHED_FEAT_NR, cmp); if (i < 0) return i; if (neg) { sysctl_sched_features &= ~(1UL << i); sched_feat_disable(i); } else { sysctl_sched_features |= (1UL << i); sched_feat_enable(i); } return 0; } static ssize_t sched_feat_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { char buf[64]; char *cmp; int ret; struct inode *inode; if (cnt > 63) cnt = 63; if (copy_from_user(buf, ubuf, cnt)) return -EFAULT; buf[cnt] = 0; cmp = strstrip(buf); /* Ensure the static_key remains in a consistent state */ inode = file_inode(filp); cpus_read_lock(); inode_lock(inode); ret = sched_feat_set(cmp); inode_unlock(inode); cpus_read_unlock(); if (ret < 0) return ret; *ppos += cnt; return cnt; } static int sched_feat_open(struct inode *inode, struct file *filp) { return single_open(filp, sched_feat_show, NULL); } static const struct file_operations sched_feat_fops = { .open = sched_feat_open, .write = sched_feat_write, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static ssize_t sched_scaling_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { unsigned int scaling; int ret; ret = kstrtouint_from_user(ubuf, cnt, 10, &scaling); if (ret) return ret; if (scaling >= SCHED_TUNABLESCALING_END) return -EINVAL; sysctl_sched_tunable_scaling = scaling; if (sched_update_scaling()) return -EINVAL; *ppos += cnt; return cnt; } static int sched_scaling_show(struct seq_file *m, void *v) { seq_printf(m, "%d\n", sysctl_sched_tunable_scaling); return 0; } static int sched_scaling_open(struct inode *inode, struct file *filp) { return single_open(filp, sched_scaling_show, NULL); } static const struct file_operations sched_scaling_fops = { .open = sched_scaling_open, .write = sched_scaling_write, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; #ifdef CONFIG_SCHED_CACHE static ssize_t sched_cache_enable_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { bool val; int ret; ret = kstrtobool_from_user(ubuf, cnt, &val); if (ret) return ret; sysctl_sched_cache_user = val; sched_cache_active_set(); *ppos += cnt; return cnt; } static int sched_cache_enable_show(struct seq_file *m, void *v) { seq_printf(m, "%d\n", sysctl_sched_cache_user); return 0; } static int sched_cache_enable_open(struct inode *inode, struct file *filp) { return single_open(filp, sched_cache_enable_show, NULL); } static const struct file_operations sched_cache_enable_fops = { .open = sched_cache_enable_open, .write = sched_cache_enable_write, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; #endif #ifdef CONFIG_PREEMPT_DYNAMIC static ssize_t sched_dynamic_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { char buf[16]; int mode; if (cnt > 15) cnt = 15; if (copy_from_user(buf, ubuf, cnt)) return -EFAULT; buf[cnt] = 0; mode = sched_dynamic_mode(strstrip(buf)); if (mode < 0) return mode; sched_dynamic_update(mode); *ppos += cnt; return cnt; } static int sched_dynamic_show(struct seq_file *m, void *v) { int i = (IS_ENABLED(CONFIG_PREEMPT_RT) || IS_ENABLED(CONFIG_ARCH_HAS_PREEMPT_LAZY)) * 2; int mode = READ_ONCE(preempt_dynamic_mode); int j; /* Count entries in NULL terminated preempt_modes */ for (j = 0; preempt_modes[j]; j++) ; j -= !IS_ENABLED(CONFIG_ARCH_HAS_PREEMPT_LAZY); for (; i < j; i++) { if (mode == i) seq_puts(m, "("); seq_puts(m, preempt_modes[i]); if (mode == i) seq_puts(m, ")"); seq_puts(m, " "); } seq_puts(m, "\n"); return 0; } static int sched_dynamic_open(struct inode *inode, struct file *filp) { return single_open(filp, sched_dynamic_show, NULL); } static const struct file_operations sched_dynamic_fops = { .open = sched_dynamic_open, .write = sched_dynamic_write, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; #endif /* CONFIG_PREEMPT_DYNAMIC */ __read_mostly bool sched_debug_verbose; static struct dentry *sd_dentry; static ssize_t sched_verbose_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { ssize_t result; bool orig; cpus_read_lock(); sched_domains_mutex_lock(); orig = sched_debug_verbose; result = debugfs_write_file_bool(filp, ubuf, cnt, ppos); if (sched_debug_verbose && !orig) update_sched_domain_debugfs(); else if (!sched_debug_verbose && orig) { debugfs_remove(sd_dentry); sd_dentry = NULL; } sched_domains_mutex_unlock(); cpus_read_unlock(); return result; } static const struct file_operations sched_verbose_fops = { .read = debugfs_read_file_bool, .write = sched_verbose_write, .open = simple_open, .llseek = default_llseek, }; static const struct seq_operations sched_debug_sops; static int sched_debug_open(struct inode *inode, struct file *filp) { return seq_open(filp, &sched_debug_sops); } static const struct file_operations sched_debug_fops = { .open = sched_debug_open, .read = seq_read, .llseek = seq_lseek, .release = seq_release, }; enum dl_param { DL_RUNTIME = 0, DL_PERIOD, }; static unsigned long dl_server_period_max = (1UL << 22) * NSEC_PER_USEC; /* ~4 seconds */ static unsigned long dl_server_period_min = (100) * NSEC_PER_USEC; /* 100 us */ static ssize_t sched_server_write_common(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos, enum dl_param param, void *server) { long cpu = (long) ((struct seq_file *) filp->private_data)->private; struct sched_dl_entity *dl_se = (struct sched_dl_entity *)server; u64 old_runtime, runtime, period; struct rq *rq = cpu_rq(cpu); int retval = 0; size_t err; u64 value; err = kstrtoull_from_user(ubuf, cnt, 10, &value); if (err) return err; scoped_guard (rq_lock_irqsave, rq) { old_runtime = runtime = dl_se->dl_runtime; period = dl_se->dl_period; switch (param) { case DL_RUNTIME: if (runtime == value) break; runtime = value; break; case DL_PERIOD: if (value == period) break; period = value; break; } if (runtime > period || period > dl_server_period_max || period < dl_server_period_min) { return -EINVAL; } if (!cpu_online(cpu_of(rq))) return -EBUSY; update_rq_clock(rq); dl_server_stop(dl_se); retval = dl_server_apply_params(dl_se, runtime, period, 0); dl_server_start(dl_se); if (retval < 0) return retval; } if (!!old_runtime ^ !!runtime) { pr_info("%s server %sabled on CPU %d%s.\n", server == &rq->fair_server ? "Fair" : "Ext", runtime ? "en" : "dis", cpu_of(rq), runtime ? "" : ", system may malfunction due to starvation"); } *ppos += cnt; return cnt; } static size_t sched_server_show_common(struct seq_file *m, void *v, enum dl_param param, void *server) { struct sched_dl_entity *dl_se = (struct sched_dl_entity *)server; u64 value; switch (param) { case DL_RUNTIME: value = dl_se->dl_runtime; break; case DL_PERIOD: value = dl_se->dl_period; break; } seq_printf(m, "%llu\n", value); return 0; } static ssize_t sched_fair_server_runtime_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { long cpu = (long) ((struct seq_file *) filp->private_data)->private; struct rq *rq = cpu_rq(cpu); return sched_server_write_common(filp, ubuf, cnt, ppos, DL_RUNTIME, &rq->fair_server); } static int sched_fair_server_runtime_show(struct seq_file *m, void *v) { unsigned long cpu = (unsigned long) m->private; struct rq *rq = cpu_rq(cpu); return sched_server_show_common(m, v, DL_RUNTIME, &rq->fair_server); } static int sched_fair_server_runtime_open(struct inode *inode, struct file *filp) { return single_open(filp, sched_fair_server_runtime_show, inode->i_private); } static const struct file_operations fair_server_runtime_fops = { .open = sched_fair_server_runtime_open, .write = sched_fair_server_runtime_write, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static struct dentry *debugfs_sched; #ifdef CONFIG_SCHED_CLASS_EXT static ssize_t sched_ext_server_runtime_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) {