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