File Index
Files and directories
Sign in
FILE

hsw_ext_idle.c

File preview with syntax highlighting.

linux-6.19.14/tools/power/cpupower/utils/idle_monitor/hsw_ext_idle.c C
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 *  (C) 2010,2011       Thomas Renninger <trenn@suse.de>, Novell Inc.
  4 *
  5 *  Based on SandyBridge monitor. Implements the new package C-states
  6 *  (PC8, PC9, PC10) coming with a specific Haswell (family 0x45) CPU.
  7 */
  8
  9#if defined(__i386__) || defined(__x86_64__)
 10
 11#include <stdio.h>
 12#include <stdint.h>
 13#include <stdlib.h>
 14#include <string.h>
 15
 16#include "helpers/helpers.h"
 17#include "idle_monitor/cpupower-monitor.h"
 18
 19#define MSR_PKG_C8_RESIDENCY           0x00000630
 20#define MSR_PKG_C9_RESIDENCY           0x00000631
 21#define MSR_PKG_C10_RESIDENCY          0x00000632
 22
 23#define MSR_TSC	0x10
 24
 25enum intel_hsw_ext_id { PC8 = 0, PC9, PC10, HSW_EXT_CSTATE_COUNT,
 26			TSC = 0xFFFF };
 27
 28static int hsw_ext_get_count_percent(unsigned int self_id, double *percent,
 29				 unsigned int cpu);
 30
 31static cstate_t hsw_ext_cstates[HSW_EXT_CSTATE_COUNT] = {
 32	{
 33		.name			= "PC8",
 34		.desc			= N_("Processor Package C8"),
 35		.id			= PC8,
 36		.range			= RANGE_PACKAGE,
 37		.get_count_percent	= hsw_ext_get_count_percent,
 38	},
 39	{
 40		.name			= "PC9",
 41		.desc			= N_("Processor Package C9"),
 42		.id			= PC9,
 43		.range			= RANGE_PACKAGE,
 44		.get_count_percent	= hsw_ext_get_count_percent,
 45	},
 46	{
 47		.name			= "PC10",
 48		.desc			= N_("Processor Package C10"),
 49		.id			= PC10,
 50		.range			= RANGE_PACKAGE,
 51		.get_count_percent	= hsw_ext_get_count_percent,
 52	},
 53};
 54
 55static unsigned long long tsc_at_measure_start;
 56static unsigned long long tsc_at_measure_end;
 57static unsigned long long *previous_count[HSW_EXT_CSTATE_COUNT];
 58static unsigned long long *current_count[HSW_EXT_CSTATE_COUNT];
 59/* valid flag for all CPUs. If a MSR read failed it will be zero */
 60static int *is_valid;
 61
 62static int hsw_ext_get_count(enum intel_hsw_ext_id id, unsigned long long *val,
 63			unsigned int cpu)
 64{
 65	int msr;
 66
 67	switch (id) {
 68	case PC8:
 69		msr = MSR_PKG_C8_RESIDENCY;
 70		break;
 71	case PC9:
 72		msr = MSR_PKG_C9_RESIDENCY;
 73		break;
 74	case PC10:
 75		msr = MSR_PKG_C10_RESIDENCY;
 76		break;
 77	case TSC:
 78		msr = MSR_TSC;
 79		break;
 80	default:
 81		return -1;
 82	}
 83	if (read_msr(cpu, msr, val))
 84		return -1;
 85	return 0;
 86}
 87
 88static int hsw_ext_get_count_percent(unsigned int id, double *percent,
 89				 unsigned int cpu)
 90{
 91	*percent = 0.0;
 92
 93	if (!is_valid[cpu])
 94		return -1;
 95
 96	*percent = (100.0 *
 97		(current_count[id][cpu] - previous_count[id][cpu])) /
 98		(tsc_at_measure_end - tsc_at_measure_start);
 99
100	dprint("%s: previous: %llu - current: %llu - (%u)\n",
101		hsw_ext_cstates[id].name, previous_count[id][cpu],
102		current_count[id][cpu], cpu);
103
104	dprint("%s: tsc_diff: %llu - count_diff: %llu - percent: %2.f (%u)\n",
105	       hsw_ext_cstates[id].name,
106	       (unsigned long long) tsc_at_measure_end - tsc_at_measure_start,
107	       current_count[id][cpu] - previous_count[id][cpu],
108	       *percent, cpu);
109
110	return 0;
111}
112
113static int hsw_ext_start(void)
114{
115	int num, cpu;
116	unsigned long long val;
117
118	for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
119		for (cpu = 0; cpu < cpu_count; cpu++) {
120			is_valid[cpu] = !hsw_ext_get_count(num, &val, cpu);
121			previous_count[num][cpu] = val;
122		}
123	}
124	hsw_ext_get_count(TSC, &tsc_at_measure_start, base_cpu);
125	return 0;
126}
127
128static int hsw_ext_stop(void)
129{
130	unsigned long long val;
131	int num, cpu;
132
133	hsw_ext_get_count(TSC, &tsc_at_measure_end, base_cpu);
134
135	for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
136		for (cpu = 0; cpu < cpu_count; cpu++) {
137			is_valid[cpu] |= !hsw_ext_get_count(num, &val, cpu);
138			current_count[num][cpu] = val;
139		}
140	}
141	return 0;
142}
143
144struct cpuidle_monitor intel_hsw_ext_monitor;
145
146static struct cpuidle_monitor *hsw_ext_register(void)
147{
148	int num;
149
150	if (cpupower_cpu_info.vendor != X86_VENDOR_INTEL
151	    || cpupower_cpu_info.family != 6)
152		return NULL;
153
154	switch (cpupower_cpu_info.model) {
155	case 0x45: /* HSW */
156		break;
157	default:
158		return NULL;
159	}
160
161	is_valid = calloc(cpu_count, sizeof(int));
162	for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
163		previous_count[num] = calloc(cpu_count,
164					sizeof(unsigned long long));
165		current_count[num]  = calloc(cpu_count,
166					sizeof(unsigned long long));
167	}
168	intel_hsw_ext_monitor.name_len = strlen(intel_hsw_ext_monitor.name);
169	return &intel_hsw_ext_monitor;
170}
171
172void hsw_ext_unregister(void)
173{
174	int num;
175	free(is_valid);
176	for (num = 0; num < HSW_EXT_CSTATE_COUNT; num++) {
177		free(previous_count[num]);
178		free(current_count[num]);
179	}
180}
181
182struct cpuidle_monitor intel_hsw_ext_monitor = {
183	.name			= "HaswellExtended",
184	.hw_states		= hsw_ext_cstates,
185	.hw_states_num		= HSW_EXT_CSTATE_COUNT,
186	.start			= hsw_ext_start,
187	.stop			= hsw_ext_stop,
188	.do_register		= hsw_ext_register,
189	.unregister		= hsw_ext_unregister,
190	.flags.needs_root	= 1,
191	.overflow_s		= 922000000 /* 922337203 seconds TSC overflow
192					       at 20GHz */
193};
194#endif /* defined(__i386__) || defined(__x86_64__) */
~/ Read-only file preview.