1403 lines · 34.2 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 */#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_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 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; 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;		} 		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,}; #ifdef CONFIG_SCHED_CLASS_EXTstatic ssize_tsched_ext_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->ext_server);} static int sched_ext_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->ext_server);} static int sched_ext_server_runtime_open(struct inode *inode, struct file *filp){	return single_open(filp, sched_ext_server_runtime_show, inode->i_private);} static const struct file_operations ext_server_runtime_fops = {	.open		= sched_ext_server_runtime_open,	.write		= sched_ext_server_runtime_write,	.read		= seq_read,	.llseek		= seq_lseek,	.release	= single_release,};#endif /* CONFIG_SCHED_CLASS_EXT */ static ssize_tsched_fair_server_period_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_PERIOD,					&rq->fair_server);} static int sched_fair_server_period_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_PERIOD, &rq->fair_server);} 

Lines 1–500 of 1403.