drivers/cpuidle/governors/teo.c

Source file repositories/reference/linux-study-clean/drivers/cpuidle/governors/teo.c

File Facts

System
Linux kernel
Corpus path
drivers/cpuidle/governors/teo.c
Extension
.c
Size
19611 bytes
Lines
578
Domain
Driver Families
Bucket
drivers/cpuidle
Inferred role
Driver Families: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct teo_bin {
	unsigned int intercepts;
	unsigned int hits;
};

/**
 * struct teo_cpu - CPU data used by the TEO cpuidle governor.
 * @sleep_length_ns: Time till the closest timer event (at the selection time).
 * @state_bins: Idle state data bins for this CPU.
 * @total: Grand total of the "intercepts" and "hits" metrics for all bins.
 * @total_tick: Wakeups by the scheduler tick.
 * @tick_intercepts: "Intercepts" before TICK_NSEC.
 * @short_idles: Wakeups after short idle periods.
 * @tick_wakeup: Set if the last wakeup was by the scheduler tick.
 */
struct teo_cpu {
	s64 sleep_length_ns;
	struct teo_bin state_bins[CPUIDLE_STATE_MAX];
	unsigned int total;
	unsigned int total_tick;
	unsigned int tick_intercepts;
	unsigned int short_idles;
	bool tick_wakeup;
};

static DEFINE_PER_CPU(struct teo_cpu, teo_cpus);

static void teo_decay(unsigned int *metric)
{
	unsigned int delta = *metric >> DECAY_SHIFT;

	if (delta)
		*metric -= delta;
	else
		*metric = 0;
}

/**
 * teo_update - Update CPU metrics after wakeup.
 * @drv: cpuidle driver containing state data.
 * @dev: Target CPU.
 */
static void teo_update(struct cpuidle_driver *drv, struct cpuidle_device *dev)
{
	s64 lat_ns = drv->states[dev->last_state_idx].exit_latency_ns;
	struct teo_cpu *cpu_data = this_cpu_ptr(&teo_cpus);
	int i, idx_timer = 0, idx_duration = 0;
	s64 target_residency_ns, measured_ns;
	unsigned int total = 0;

	teo_decay(&cpu_data->short_idles);

	if (dev->poll_time_limit) {
		dev->poll_time_limit = false;
		/*
		 * Polling state timeout has triggered, so assume that this
		 * might have been a long sleep.
		 */
		measured_ns = S64_MAX;
	} else {
		measured_ns = dev->last_residency_ns;
		/*
		 * The delay between the wakeup and the first instruction
		 * executed by the CPU is not likely to be worst-case every
		 * time, so take 1/2 of the exit latency as a very rough
		 * approximation of the average of it.
		 */
		if (measured_ns >= lat_ns) {
			measured_ns -= lat_ns / 2;
			if (measured_ns < RESIDENCY_THRESHOLD_NS)
				cpu_data->short_idles += PULSE;
		} else {
			measured_ns /= 2;
			cpu_data->short_idles += PULSE;
		}
	}

	/*
	 * Decay the "hits" and "intercepts" metrics for all of the bins and
	 * find the bins that the sleep length and the measured idle duration
	 * fall into.
	 */
	for (i = 0; i < drv->state_count; i++) {
		struct teo_bin *bin = &cpu_data->state_bins[i];

		teo_decay(&bin->hits);
		total += bin->hits;
		teo_decay(&bin->intercepts);
		total += bin->intercepts;

Annotation

Implementation Notes