mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 08:38:45 +02:00 
			
		
		
		
	 9d9af2372f
			
		
	
	
		9d9af2372f
		
	
	
	
	
		
			
			Simplify the scheduler by making CONFIG_SMP=y schedstats debugging output unconditional. Signed-off-by: Ingo Molnar <mingo@kernel.org> Acked-by: Peter Zijlstra <peterz@infradead.org> Cc: Dietmar Eggemann <dietmar.eggemann@arm.com> Cc: Juri Lelli <juri.lelli@redhat.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Mel Gorman <mgorman@suse.de> Cc: Sebastian Andrzej Siewior <bigeasy@linutronix.de> Cc: Shrikanth Hegde <sshegde@linux.ibm.com> Cc: Steven Rostedt <rostedt@goodmis.org> Cc: Valentin Schneider <vschneid@redhat.com> Cc: Vincent Guittot <vincent.guittot@linaro.org> Link: https://lore.kernel.org/r/20250528080924.2273858-32-mingo@kernel.org
		
			
				
	
	
		
			220 lines
		
	
	
	
		
			5.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			220 lines
		
	
	
	
		
			5.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| /*
 | |
|  * /proc/schedstat implementation
 | |
|  */
 | |
| #include "sched.h"
 | |
| 
 | |
| void __update_stats_wait_start(struct rq *rq, struct task_struct *p,
 | |
| 			       struct sched_statistics *stats)
 | |
| {
 | |
| 	u64 wait_start, prev_wait_start;
 | |
| 
 | |
| 	wait_start = rq_clock(rq);
 | |
| 	prev_wait_start = schedstat_val(stats->wait_start);
 | |
| 
 | |
| 	if (p && likely(wait_start > prev_wait_start))
 | |
| 		wait_start -= prev_wait_start;
 | |
| 
 | |
| 	__schedstat_set(stats->wait_start, wait_start);
 | |
| }
 | |
| 
 | |
| void __update_stats_wait_end(struct rq *rq, struct task_struct *p,
 | |
| 			     struct sched_statistics *stats)
 | |
| {
 | |
| 	u64 delta = rq_clock(rq) - schedstat_val(stats->wait_start);
 | |
| 
 | |
| 	if (p) {
 | |
| 		if (task_on_rq_migrating(p)) {
 | |
| 			/*
 | |
| 			 * Preserve migrating task's wait time so wait_start
 | |
| 			 * time stamp can be adjusted to accumulate wait time
 | |
| 			 * prior to migration.
 | |
| 			 */
 | |
| 			__schedstat_set(stats->wait_start, delta);
 | |
| 
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		trace_sched_stat_wait(p, delta);
 | |
| 	}
 | |
| 
 | |
| 	__schedstat_set(stats->wait_max,
 | |
| 			max(schedstat_val(stats->wait_max), delta));
 | |
| 	__schedstat_inc(stats->wait_count);
 | |
| 	__schedstat_add(stats->wait_sum, delta);
 | |
| 	__schedstat_set(stats->wait_start, 0);
 | |
| }
 | |
| 
 | |
| void __update_stats_enqueue_sleeper(struct rq *rq, struct task_struct *p,
 | |
| 				    struct sched_statistics *stats)
 | |
| {
 | |
| 	u64 sleep_start, block_start;
 | |
| 
 | |
| 	sleep_start = schedstat_val(stats->sleep_start);
 | |
| 	block_start = schedstat_val(stats->block_start);
 | |
| 
 | |
| 	if (sleep_start) {
 | |
| 		u64 delta = rq_clock(rq) - sleep_start;
 | |
| 
 | |
| 		if ((s64)delta < 0)
 | |
| 			delta = 0;
 | |
| 
 | |
| 		if (unlikely(delta > schedstat_val(stats->sleep_max)))
 | |
| 			__schedstat_set(stats->sleep_max, delta);
 | |
| 
 | |
| 		__schedstat_set(stats->sleep_start, 0);
 | |
| 		__schedstat_add(stats->sum_sleep_runtime, delta);
 | |
| 
 | |
| 		if (p) {
 | |
| 			account_scheduler_latency(p, delta >> 10, 1);
 | |
| 			trace_sched_stat_sleep(p, delta);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (block_start) {
 | |
| 		u64 delta = rq_clock(rq) - block_start;
 | |
| 
 | |
| 		if ((s64)delta < 0)
 | |
| 			delta = 0;
 | |
| 
 | |
| 		if (unlikely(delta > schedstat_val(stats->block_max)))
 | |
| 			__schedstat_set(stats->block_max, delta);
 | |
| 
 | |
| 		__schedstat_set(stats->block_start, 0);
 | |
| 		__schedstat_add(stats->sum_sleep_runtime, delta);
 | |
| 		__schedstat_add(stats->sum_block_runtime, delta);
 | |
| 
 | |
| 		if (p) {
 | |
| 			if (p->in_iowait) {
 | |
| 				__schedstat_add(stats->iowait_sum, delta);
 | |
| 				__schedstat_inc(stats->iowait_count);
 | |
| 				trace_sched_stat_iowait(p, delta);
 | |
| 			}
 | |
| 
 | |
| 			trace_sched_stat_blocked(p, delta);
 | |
| 
 | |
| 			account_scheduler_latency(p, delta >> 10, 0);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Current schedstat API version.
 | |
|  *
 | |
|  * Bump this up when changing the output format or the meaning of an existing
 | |
|  * format, so that tools can adapt (or abort)
 | |
|  */
 | |
| #define SCHEDSTAT_VERSION 17
 | |
| 
 | |
| static int show_schedstat(struct seq_file *seq, void *v)
 | |
| {
 | |
| 	int cpu;
 | |
| 
 | |
| 	if (v == (void *)1) {
 | |
| 		seq_printf(seq, "version %d\n", SCHEDSTAT_VERSION);
 | |
| 		seq_printf(seq, "timestamp %lu\n", jiffies);
 | |
| 	} else {
 | |
| 		struct rq *rq;
 | |
| 		struct sched_domain *sd;
 | |
| 		int dcount = 0;
 | |
| 		cpu = (unsigned long)(v - 2);
 | |
| 		rq = cpu_rq(cpu);
 | |
| 
 | |
| 		/* runqueue-specific stats */
 | |
| 		seq_printf(seq,
 | |
| 		    "cpu%d %u 0 %u %u %u %u %llu %llu %lu",
 | |
| 		    cpu, rq->yld_count,
 | |
| 		    rq->sched_count, rq->sched_goidle,
 | |
| 		    rq->ttwu_count, rq->ttwu_local,
 | |
| 		    rq->rq_cpu_time,
 | |
| 		    rq->rq_sched_info.run_delay, rq->rq_sched_info.pcount);
 | |
| 
 | |
| 		seq_printf(seq, "\n");
 | |
| 
 | |
| 		/* domain-specific stats */
 | |
| 		rcu_read_lock();
 | |
| 		for_each_domain(cpu, sd) {
 | |
| 			enum cpu_idle_type itype;
 | |
| 
 | |
| 			seq_printf(seq, "domain%d %s %*pb", dcount++, sd->name,
 | |
| 				   cpumask_pr_args(sched_domain_span(sd)));
 | |
| 			for (itype = 0; itype < CPU_MAX_IDLE_TYPES; itype++) {
 | |
| 				seq_printf(seq, " %u %u %u %u %u %u %u %u %u %u %u",
 | |
| 				    sd->lb_count[itype],
 | |
| 				    sd->lb_balanced[itype],
 | |
| 				    sd->lb_failed[itype],
 | |
| 				    sd->lb_imbalance_load[itype],
 | |
| 				    sd->lb_imbalance_util[itype],
 | |
| 				    sd->lb_imbalance_task[itype],
 | |
| 				    sd->lb_imbalance_misfit[itype],
 | |
| 				    sd->lb_gained[itype],
 | |
| 				    sd->lb_hot_gained[itype],
 | |
| 				    sd->lb_nobusyq[itype],
 | |
| 				    sd->lb_nobusyg[itype]);
 | |
| 			}
 | |
| 			seq_printf(seq,
 | |
| 				   " %u %u %u %u %u %u %u %u %u %u %u %u\n",
 | |
| 			    sd->alb_count, sd->alb_failed, sd->alb_pushed,
 | |
| 			    sd->sbe_count, sd->sbe_balanced, sd->sbe_pushed,
 | |
| 			    sd->sbf_count, sd->sbf_balanced, sd->sbf_pushed,
 | |
| 			    sd->ttwu_wake_remote, sd->ttwu_move_affine,
 | |
| 			    sd->ttwu_move_balance);
 | |
| 		}
 | |
| 		rcu_read_unlock();
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * This iterator needs some explanation.
 | |
|  * It returns 1 for the header position.
 | |
|  * This means 2 is cpu 0.
 | |
|  * In a hotplugged system some CPUs, including cpu 0, may be missing so we have
 | |
|  * to use cpumask_* to iterate over the CPUs.
 | |
|  */
 | |
| static void *schedstat_start(struct seq_file *file, loff_t *offset)
 | |
| {
 | |
| 	unsigned long n = *offset;
 | |
| 
 | |
| 	if (n == 0)
 | |
| 		return (void *) 1;
 | |
| 
 | |
| 	n--;
 | |
| 
 | |
| 	if (n > 0)
 | |
| 		n = cpumask_next(n - 1, cpu_online_mask);
 | |
| 	else
 | |
| 		n = cpumask_first(cpu_online_mask);
 | |
| 
 | |
| 	*offset = n + 1;
 | |
| 
 | |
| 	if (n < nr_cpu_ids)
 | |
| 		return (void *)(unsigned long)(n + 2);
 | |
| 
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| static void *schedstat_next(struct seq_file *file, void *data, loff_t *offset)
 | |
| {
 | |
| 	(*offset)++;
 | |
| 
 | |
| 	return schedstat_start(file, offset);
 | |
| }
 | |
| 
 | |
| static void schedstat_stop(struct seq_file *file, void *data)
 | |
| {
 | |
| }
 | |
| 
 | |
| static const struct seq_operations schedstat_sops = {
 | |
| 	.start = schedstat_start,
 | |
| 	.next  = schedstat_next,
 | |
| 	.stop  = schedstat_stop,
 | |
| 	.show  = show_schedstat,
 | |
| };
 | |
| 
 | |
| static int __init proc_schedstat_init(void)
 | |
| {
 | |
| 	proc_create_seq("schedstat", 0, NULL, &schedstat_sops);
 | |
| 	return 0;
 | |
| }
 | |
| subsys_initcall(proc_schedstat_init);
 |