1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  *  (C) 2016 SUSE Software Solutions GmbH
4  *           Thomas Renninger <trenn@suse.de>
5  */
6 
7 #if defined(__i386__) || defined(__x86_64__)
8 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdint.h>
12 #include <time.h>
13 #include <string.h>
14 
15 #include <pci/pci.h>
16 
17 #include "idle_monitor/cpupower-monitor.h"
18 #include "helpers/helpers.h"
19 #include "powercap.h"
20 
21 #define MAX_RAPL_ZONES 10
22 
23 int rapl_zone_count;
24 cstate_t rapl_zones[MAX_RAPL_ZONES];
25 struct powercap_zone *rapl_zones_pt[MAX_RAPL_ZONES] = { 0 };
26 
27 unsigned long long rapl_zone_previous_count[MAX_RAPL_ZONES];
28 unsigned long long rapl_zone_current_count[MAX_RAPL_ZONES];
29 unsigned long long rapl_max_count;
30 
rapl_get_count_uj(unsigned int id,unsigned long long * count,unsigned int cpu)31 static int rapl_get_count_uj(unsigned int id, unsigned long long *count,
32 			     unsigned int cpu)
33 {
34 	if (rapl_zones_pt[id] == NULL)
35 		/* error */
36 		return -1;
37 
38 	*count = rapl_zone_current_count[id] - rapl_zone_previous_count[id];
39 
40 	return 0;
41 }
42 
powercap_count_zones(struct powercap_zone * zone)43 static int powercap_count_zones(struct powercap_zone *zone)
44 {
45 	uint64_t val;
46 	int uj;
47 
48 	if (rapl_zone_count >= MAX_RAPL_ZONES)
49 		return -1;
50 
51 	if (!zone->has_energy_uj)
52 		return 0;
53 
54 	printf("%s\n", zone->sys_name);
55 	uj = powercap_get_energy_uj(zone, &val);
56 	printf("%d\n", uj);
57 
58 	strncpy(rapl_zones[rapl_zone_count].name, zone->name, CSTATE_NAME_LEN - 1);
59 	strcpy(rapl_zones[rapl_zone_count].desc, "");
60 	rapl_zones[rapl_zone_count].id = rapl_zone_count;
61 	rapl_zones[rapl_zone_count].range = RANGE_MACHINE;
62 	rapl_zones[rapl_zone_count].get_count = rapl_get_count_uj;
63 	rapl_zones_pt[rapl_zone_count] = zone;
64 	rapl_zone_count++;
65 
66 	return 0;
67 }
68 
rapl_start(void)69 static int rapl_start(void)
70 {
71 	int i, ret;
72 	uint64_t uj_val;
73 
74 	for (i = 0; i < rapl_zone_count; i++) {
75 		ret = powercap_get_energy_uj(rapl_zones_pt[i], &uj_val);
76 		if (ret)
77 			return ret;
78 		rapl_zone_previous_count[i] = uj_val;
79 	}
80 
81 	return 0;
82 }
83 
rapl_stop(void)84 static int rapl_stop(void)
85 {
86 	int i;
87 	uint64_t uj_val;
88 
89 	for (i = 0; i < rapl_zone_count; i++) {
90 		int ret;
91 
92 		ret = powercap_get_energy_uj(rapl_zones_pt[i], &uj_val);
93 		if (ret)
94 			return ret;
95 		rapl_zone_current_count[i] = uj_val;
96 		if (rapl_max_count < uj_val)
97 			rapl_max_count = uj_val - rapl_zone_previous_count[i];
98 	}
99 	return 0;
100 }
101 
rapl_register(void)102 struct cpuidle_monitor *rapl_register(void)
103 {
104 	struct powercap_zone *root_zone;
105 	char line[MAX_LINE_LEN] = "";
106 	int ret, val;
107 
108 	ret = powercap_get_driver(line, MAX_LINE_LEN);
109 	if (ret < 0) {
110 		dprint("No powercapping driver loaded\n");
111 		return NULL;
112 	}
113 
114 	dprint("Driver: %s\n", line);
115 	ret = powercap_get_enabled(&val);
116 	if (ret < 0)
117 		return NULL;
118 	if (!val) {
119 		dprint("Powercapping is disabled\n");
120 		return NULL;
121 	}
122 
123 	dprint("Powercap domain hierarchy:\n\n");
124 	root_zone = powercap_init_zones();
125 
126 	if (root_zone == NULL) {
127 		dprint("No powercap info found\n");
128 		return NULL;
129 	}
130 
131 	powercap_walk_zones(root_zone, powercap_count_zones);
132 	rapl_monitor.hw_states_num = rapl_zone_count;
133 
134 	return &rapl_monitor;
135 }
136 
137 struct cpuidle_monitor rapl_monitor = {
138 	.name			= "RAPL",
139 	.hw_states		= rapl_zones,
140 	.hw_states_num		= 0,
141 	.start			= rapl_start,
142 	.stop			= rapl_stop,
143 	.do_register		= rapl_register,
144 	.flags.needs_root	= 0,
145 	.overflow_s		= 60 * 60 * 24 * 100, /* To be implemented */
146 };
147 
148 #endif
149