1305 lines · 30.6 KB · C
RawDownload
// 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 */ /* * 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]);}#elsestatic 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,}; #ifdef CONFIG_SMP static ssize_t sched_scaling_write(struct file *filp, const char __user *ubuf,				   size_t cnt, loff_t *ppos){	char buf[16];	unsigned int scaling; 	if (cnt > 15)		cnt = 15; 	if (copy_from_user(&buf, ubuf, cnt))		return -EFAULT;	buf[cnt] = '\0'; 	if (kstrtouint(buf, 10, &scaling))		return -EINVAL; 	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,}; #endif /* SMP */ #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) * 2;	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 (preempt_dynamic_mode == i)			seq_puts(m, "(");		seq_puts(m, preempt_modes[i]);		if (preempt_dynamic_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; #ifdef CONFIG_SMPstatic 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;}#else#define sched_verbose_write debugfs_write_file_bool#endif 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 fair_server_period_max = (1UL << 22) * NSEC_PER_USEC; /* ~4 seconds */static unsigned long fair_server_period_min = (100) * NSEC_PER_USEC;     /* 100 us */ static ssize_t sched_fair_server_write(struct file *filp, const char __user *ubuf,				       size_t cnt, loff_t *ppos, enum dl_param param){	long cpu = (long) ((struct seq_file *) filp->private_data)->private;	struct rq *rq = cpu_rq(cpu);	u64 runtime, period;	size_t err;	int retval;	u64 value; 	err = kstrtoull_from_user(ubuf, cnt, 10, &value);	if (err)		return err; 	scoped_guard (rq_lock_irqsave, rq) {		runtime  = rq->fair_server.dl_runtime;		period = rq->fair_server.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 > fair_server_period_max ||		    period < fair_server_period_min) {			return  -EINVAL;		} 		if (rq->cfs.h_nr_queued) {			update_rq_clock(rq);			dl_server_stop(&rq->fair_server);		} 		retval = dl_server_apply_params(&rq->fair_server, runtime, period, 0);		if (retval)			cnt = retval; 		if (!runtime)			printk_deferred("Fair server disabled in CPU %d, system may crash due to starvation.\n",					cpu_of(rq)); 		if (rq->cfs.h_nr_queued)			dl_server_start(&rq->fair_server);	} 	*ppos += cnt;	return cnt;} static size_t sched_fair_server_show(struct seq_file *m, void *v, enum dl_param param){	unsigned long cpu = (unsigned long) m->private;	struct rq *rq = cpu_rq(cpu);	u64 value; 	switch (param) {	case DL_RUNTIME:		value = rq->fair_server.dl_runtime;		break;	case DL_PERIOD:		value = rq->fair_server.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){	return sched_fair_server_write(filp, ubuf, cnt, ppos, DL_RUNTIME);} static int sched_fair_server_runtime_show(struct seq_file *m, void *v){	return sched_fair_server_show(m, v, DL_RUNTIME);} 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 ssize_tsched_fair_server_period_write(struct file *filp, const char __user *ubuf,			       size_t cnt, loff_t *ppos){	return sched_fair_server_write(filp, ubuf, cnt, ppos, DL_PERIOD);} static int sched_fair_server_period_show(struct seq_file *m, void *v){	return sched_fair_server_show(m, v, DL_PERIOD);} static int sched_fair_server_period_open(struct inode *inode, struct file *filp){	return single_open(filp, sched_fair_server_period_show, inode->i_private);} static const struct file_operations fair_server_period_fops = {	.open		= sched_fair_server_period_open,	.write		= sched_fair_server_period_write,	.read		= seq_read,	.llseek		= seq_lseek,	.release	= single_release,}; static struct dentry *debugfs_sched; static void debugfs_fair_server_init(void){	struct dentry *d_fair;	unsigned long cpu; 	d_fair = debugfs_create_dir("fair_server", debugfs_sched);	if (!d_fair)		return; 	for_each_possible_cpu(cpu) {		struct dentry *d_cpu;		char buf[32]; 		snprintf(buf, sizeof(buf), "cpu%lu", cpu);		d_cpu = debugfs_create_dir(buf, d_fair); 		debugfs_create_file("runtime", 0644, d_cpu, (void *) cpu, &fair_server_runtime_fops);		debugfs_create_file("period", 0644, d_cpu, (void *) cpu, &fair_server_period_fops);	}} static __init int sched_init_debug(void){	struct dentry __maybe_unused *numa;

Lines 1–500 of 1305.