This repository has been archived by the owner on Jan 1, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
krapl.c
430 lines (373 loc) · 13.1 KB
/
krapl.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
#include <linux/sysfs.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/cpu.h>
#define failout(condition, str,error) if (condition) { printk(KERN_INFO str); return error; }
#define RAPL_POWER_UNIT 0x606
#define RAPL_PKG 0x610
#define RAPL_DRAM 0x618
#define RAPL_PP0 0x638
#define RAPL_PP1 0x640
#define RAPL_POWER_LIMIT 0x0
#define RAPL_ENERGY_STATUS 0x1
#define RAPL_POLICY 0x2
#define RAPL_PERF_STATUS 0x3
#define RAPL_POWER_INFO 0x4
#define SYS_PKG 0
#define SYS_DRAM 1
#define SYS_PP0 2
#define SYS_PP1 3
#define SYS_ROOT 4
#define SANDY 0x2A
#define SANDY_EP 0x2D
#define IVY 0x3A
#define IVY_E 0x3E // or 3F?
#define HASWELL 0x3C
/*#define HASWELL_E 0x3F
#define HASWELL_45 0x45
#define HASWELL_46 0x45 ??*/
static struct bus_type rapl_subsys = {
.name = "rapl",
.dev_name = "rapl"
};
static struct kobject *krapl_kobject[MAX_NUMNODES][5];
typedef struct {
unsigned int msr;
unsigned long long *raw;
} msr_query;
static void __wrmsrl(void *arg) {
msr_query *q = (msr_query*)arg;
wrmsrl(q->msr,*(q->raw));
return;
}
static void __rdmsrl(void *arg) {
msr_query *q = (msr_query*)arg;
rdmsrl(q->msr,*(q->raw));
return;
}
//find a representative cpu for the requested node
static int nodecpu_of_kobj(struct kobject *kobj) {
int node,i;
struct cpumask m = CPU_MASK_NONE;
for_each_node(node) {
for (i = 0; i < 5; i++) {
if (krapl_kobject[node][i] == kobj) {
m = *cpumask_of_node(node);
break;
}
}
if (!cpumask_empty(&m)) break;
}
if (cpumask_empty(&m))
failout(cpumask_empty(&m),"No cpus on node!\n",-ENODEV);
return cpumask_first(&m);
}
#define CREATE_SHOW(name,obj,domain,reg) \
static ssize_t show_##name##_##domain(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { \
obj tmp; int cpu; \
msr_query q = { .msr = reg, .raw = &(tmp.raw) }; \
if ((cpu = nodecpu_of_kobj(kobj)) < 0) return cpu;\
smp_call_function_single(cpu,__rdmsrl, (void*)&q,true); \
return sprintf(buf,"%u\n",tmp.name); \
} \
#define CREATE_WRITE(name,obj,domain,reg) \
static ssize_t write_##name##_##domain(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) { \
obj tmp; int cpu; unsigned long val; int err;\
msr_query q = { .msr = reg, .raw = &(tmp.raw) }; \
if ((cpu = nodecpu_of_kobj(kobj)) < 0) return cpu;\
smp_call_function_single(cpu,__rdmsrl, (void*)&q,true); \
err = kstrtoul(buf,10,&val); \
failout(err, "Invalid input", -EINVAL); \
tmp.name = val; \
smp_call_function_single(cpu,__wrmsrl, (void*)&q,true); \
return count; \
} \
#define CREATE_REG_ATTR_RO(name,obj,domain,reg) \
CREATE_SHOW(name,obj,domain,reg); \
static struct kobj_attribute name##_##domain##_attribute = __ATTR(name,0444,show_##name##_##domain, NULL);
#define CREATE_REG_ATTR_RW(name,obj,domain,reg) \
CREATE_SHOW(name,obj,domain,reg); \
CREATE_WRITE(name,obj,domain,reg); \
static struct kobj_attribute name##_##domain##_attribute = __ATTR(name,0664,show_##name##_##domain, write_##name##_##domain);
#define GET_ATTR(name,obj) &name##_##obj##_attribute.attr
typedef union {
struct {
unsigned power_unit:4,:4;
unsigned energy_unit:5,:3;
unsigned time_unit:4;
};
unsigned long long raw;
} power_unit_struct;
typedef union {
struct {
unsigned limit_1:15;
unsigned enable_limit_1:1;
unsigned clamping_limit_1:1;
unsigned time_window_1:7,:0;
unsigned limit_2:15;
unsigned enable_limit_2:1;
unsigned clamping_limit_2:1;
unsigned time_window_2:7,:7;
unsigned lock:1;
};
unsigned long long raw;
} pkg_power_limit_struct;
typedef union {
struct {
unsigned energy:32;
};
unsigned long long raw;
} pkg_energy_status_struct;
typedef union {
struct {
unsigned thermal_spec_power:15,:1;
unsigned min_power:15,:1;
unsigned max_power:15,:1;
unsigned max_time_window:6;
};
unsigned long long raw;
} pkg_power_info_struct;
typedef union {
struct {
unsigned throttle_time:32;
};
unsigned long long raw;
} pkg_perf_status_struct;
typedef union {
struct {
unsigned limit:15;
unsigned enable_limit:1;
unsigned clamping_limit:1;
unsigned time_window:7,:7;
unsigned lock:1;
};
unsigned long long raw;
} pp_power_limit_struct;
typedef union {
struct {
unsigned priority:5;
};
unsigned long long raw;
} pp_policy_struct;
typedef pkg_energy_status_struct pp_energy_status_struct;
typedef pkg_perf_status_struct pp_perf_status_struct;
typedef pp_power_limit_struct dram_power_limit_struct;
typedef pkg_energy_status_struct dram_energy_status_struct;
typedef pkg_power_info_struct dram_power_info_struct;
typedef pkg_perf_status_struct dram_perf_status_struct;
#define MK_POWER_UNIT(name) CREATE_REG_ATTR_RO(name,power_unit_struct,units,RAPL_POWER_UNIT)
MK_POWER_UNIT(energy_unit)
MK_POWER_UNIT(time_unit)
MK_POWER_UNIT(power_unit)
#undef MK_POWER_UNIT
//PKG Power Limit
#define MK_POWER_LIMIT(name) CREATE_REG_ATTR_RW(name,pkg_power_limit_struct,pkg,RAPL_PKG+RAPL_POWER_LIMIT)
MK_POWER_LIMIT(limit_1);
MK_POWER_LIMIT(enable_limit_1);
MK_POWER_LIMIT(clamping_limit_1);
MK_POWER_LIMIT(time_window_1);
MK_POWER_LIMIT(limit_2);
MK_POWER_LIMIT(enable_limit_2);
MK_POWER_LIMIT(clamping_limit_2);
MK_POWER_LIMIT(time_window_2);
MK_POWER_LIMIT(lock);
#undef MK_POWER_LIMIT
//PKG Energy Status
CREATE_REG_ATTR_RO(energy,pkg_energy_status_struct,pkg,RAPL_PKG+RAPL_ENERGY_STATUS);
//PKG Power Info
#define MK_POWER_INFO(name) CREATE_REG_ATTR_RO(name,pkg_power_info_struct,pkg,RAPL_PKG+RAPL_POWER_INFO)
MK_POWER_INFO(thermal_spec_power);
MK_POWER_INFO(min_power);
MK_POWER_INFO(max_power);
MK_POWER_INFO(max_time_window);
#undef MK_POWER_INFO
//PKG Perf Status (only on Servers!)
CREATE_REG_ATTR_RO(throttle_time,pkg_perf_status_struct,pkg,RAPL_PKG+RAPL_PERF_STATUS);
//PP0 Power Limit
#define MK_POWER_LIMIT(name) CREATE_REG_ATTR_RW(name,pp_power_limit_struct,pp0,RAPL_PP0+RAPL_POWER_LIMIT)
MK_POWER_LIMIT(limit);
MK_POWER_LIMIT(enable_limit);
MK_POWER_LIMIT(clamping_limit);
MK_POWER_LIMIT(time_window);
MK_POWER_LIMIT(lock);
#undef MK_POWER_LIMIT
//PP0 Energy Status
CREATE_REG_ATTR_RO(energy,pp_energy_status_struct,pp0,RAPL_PP0+RAPL_ENERGY_STATUS);
//PP0 Policy (only on non-servers)
CREATE_REG_ATTR_RW(priority,pp_policy_struct,pp0,RAPL_PP0+RAPL_POLICY);
//PP0 Perf Status (only on non-servers)
CREATE_REG_ATTR_RO(throttle_time,pp_perf_status_struct,pp0,RAPL_PP0+RAPL_PERF_STATUS);
//PP1 Power Limit (only on non-servers)
#define MK_POWER_LIMIT(name) CREATE_REG_ATTR_RW(name,pp_power_limit_struct,pp1,RAPL_PP1+RAPL_POWER_LIMIT)
MK_POWER_LIMIT(limit);
MK_POWER_LIMIT(enable_limit);
MK_POWER_LIMIT(clamping_limit);
MK_POWER_LIMIT(time_window);
MK_POWER_LIMIT(lock);
#undef MK_POWER_LIMIT
//PP1 Energy Status (only on non-servers)
CREATE_REG_ATTR_RO(energy,pp_energy_status_struct,pp1,RAPL_PP1+RAPL_ENERGY_STATUS);
//PP1 Policy (only on non-servers)
CREATE_REG_ATTR_RW(priority,pp_policy_struct,pp1,RAPL_PP1+RAPL_POLICY);
//DRAM Power Limit (only on servers)
#define MK_POWER_LIMIT(name) CREATE_REG_ATTR_RW(name,dram_power_limit_struct,dram,RAPL_DRAM+RAPL_POWER_LIMIT)
MK_POWER_LIMIT(limit);
MK_POWER_LIMIT(enable_limit);
MK_POWER_LIMIT(clamping_limit);
MK_POWER_LIMIT(time_window);
MK_POWER_LIMIT(lock);
#undef MK_POWER_LIMIT
//DRAM Energy Status (only on servers)
CREATE_REG_ATTR_RO(energy,dram_energy_status_struct,dram,RAPL_DRAM+RAPL_ENERGY_STATUS);
//DRAM Perf Status (only on servers)
CREATE_REG_ATTR_RO(throttle_time,dram_perf_status_struct,dram,RAPL_DRAM+RAPL_PERF_STATUS);
//DRAM Power Info (only on servers)
#define MK_POWER_INFO(name) CREATE_REG_ATTR_RO(name,dram_power_info_struct,dram,RAPL_DRAM+RAPL_POWER_INFO)
MK_POWER_INFO(thermal_spec_power);
MK_POWER_INFO(min_power);
MK_POWER_INFO(max_power);
MK_POWER_INFO(max_time_window);
#undef MK_POWER_INFO
static struct attribute *pkg_attrs[] = {
GET_ATTR(limit_1,pkg), //PKG Power Limit
GET_ATTR(enable_limit_1,pkg),
GET_ATTR(clamping_limit_1,pkg),
GET_ATTR(time_window_1,pkg),
GET_ATTR(limit_2,pkg),
GET_ATTR(enable_limit_2,pkg),
GET_ATTR(clamping_limit_2,pkg),
GET_ATTR(time_window_2,pkg),
GET_ATTR(lock,pkg),
GET_ATTR(energy,pkg), //PKG Energy Status
GET_ATTR(thermal_spec_power,pkg), //PKG Power Info
GET_ATTR(min_power,pkg),
GET_ATTR(max_power,pkg),
GET_ATTR(max_time_window,pkg),
NULL, //Perf Status only on servers! Created dynamically, Entry 14
NULL
};
static struct attribute *pp0_attrs[] = {
GET_ATTR(limit,pp0), //PP0 Power Limit
GET_ATTR(enable_limit,pp0),
GET_ATTR(clamping_limit,pp0),
GET_ATTR(time_window,pp0),
GET_ATTR(lock,pp0),
GET_ATTR(energy,pp0), //PP0 Energy Status
NULL, //Priority only on non-servers, Entry 6
NULL, //Perf Status only on non-servers, Entry 7
NULL
};
static struct attribute *pp1_attrs[] = { //only on non-servers
GET_ATTR(limit,pp1), //PP1 Power Limit
GET_ATTR(enable_limit,pp1),
GET_ATTR(clamping_limit,pp1),
GET_ATTR(time_window,pp1),
GET_ATTR(lock,pp1),
GET_ATTR(energy,pp1), //PP1 Energy Status
GET_ATTR(priority,pp1), //PP1 Policy
NULL
};
static struct attribute *dram_attrs[] = { //only on servers
GET_ATTR(limit,dram), //DRAM Power Limit
GET_ATTR(enable_limit,dram),
GET_ATTR(clamping_limit,dram),
GET_ATTR(time_window,dram),
GET_ATTR(lock,dram),
GET_ATTR(energy,dram), //DRAM Energy Status
GET_ATTR(throttle_time,dram), //DRAM Perf Status
GET_ATTR(thermal_spec_power,dram), //DRAM Power Info
GET_ATTR(min_power,dram),
GET_ATTR(max_power,dram),
GET_ATTR(max_time_window,dram),
};
static struct attribute *attrs[] = {
GET_ATTR(energy_unit,units),
GET_ATTR(time_unit,units),
GET_ATTR(power_unit,units),
NULL
};
static struct attribute_group pkg_group = { .attrs = pkg_attrs };
static struct attribute_group krapl_group = { .attrs = attrs };
static struct attribute_group dram_group = { .attrs = dram_attrs };
static struct attribute_group pp1_group = { .attrs = pp1_attrs };
static struct attribute_group pp0_group = { .attrs = pp0_attrs };
static void krapl_cleanup(void) {
int i, node;
for_each_node(node) {
for (i=0; i < 5; i++)
kobject_put(krapl_kobject[node][i]);
}
}
static int __init krapl_init(void)
{
unsigned int eax, ebx, ecx, edx, family, model;
int node;
bool isServer = false;
char buf[32];
cpuid(0,&eax,&ebx,&ecx,&edx);
failout(ebx != 0x756e6547, "Only Intel CPUs are supported!\n",-ENODEV);
cpuid(1,&eax,&ebx,&ecx,&edx);
family = ((eax>>8)&0xF) + ((eax>>20)&0xFF);
model = ((eax>>12)&0xF0) | ((eax>>4)&0xF);
failout(family != 0x06 || model < SANDY,"Only Sandy Bridge and newer CPUs supported!\n",-ENODEV);
//Detecting RAPL capabilities
switch (model) {
case SANDY: //0x2A
case IVY: //0x3A
case HASWELL: //0x3C
isServer = false;
//Exclusive
break;
case SANDY_EP: //0x2D
case IVY_E: //0x3E
isServer = true;
break;
}
//Done.
failout((node=subsys_system_register(&rapl_subsys,NULL)),"Unable to register bus!\n",node);
#define MAKE_NODE(type,name,parent) \
failout(!(krapl_kobject[node][type] = kobject_create_and_add(name,parent)),\
"Could not create entry in node\n", -ENOMEM)
#define MAKE_GROUP(type, group) \
do { if (sysfs_create_group(krapl_kobject[node][type], group)) { \
krapl_cleanup(); \
return -ENOMEM; }} while (0)
for_each_node(node) {
if (!sprintf(buf,"rapl%u",node)) return -ENOMEM;
MAKE_NODE(SYS_ROOT,buf,&rapl_subsys.dev_root->kobj);
MAKE_NODE(SYS_PKG,"PKG",krapl_kobject[node][SYS_ROOT]);
MAKE_NODE(SYS_PP0,"PP0",krapl_kobject[node][SYS_ROOT]);
if (isServer) {
pkg_attrs[14] = GET_ATTR(throttle_time,pkg);
MAKE_NODE(SYS_DRAM, "DRAM", krapl_kobject[node][SYS_ROOT]);
if (sysfs_create_group(krapl_kobject[node][SYS_DRAM], &dram_group)) {
krapl_cleanup();
return -ENOMEM;
}
} else {
pp0_attrs[6] = GET_ATTR(priority,pp0);
pp0_attrs[7] = GET_ATTR(throttle_time,pp0);
MAKE_NODE(SYS_PP1, "PP1", krapl_kobject[node][SYS_ROOT]);
MAKE_GROUP(SYS_PP1,&pp1_group);
}
MAKE_GROUP(SYS_ROOT, &krapl_group);
MAKE_GROUP(SYS_PKG, &pkg_group);
MAKE_GROUP(SYS_PP0, &pp0_group);
}
#undef MAKE_NODE
#undef MAKE_GROUP
printk(KERN_INFO "Krapl loaded!\n");
return 0;
}
static void __exit krapl_exit(void)
{
put_device(rapl_subsys.dev_root);
bus_unregister(&rapl_subsys);
krapl_cleanup();
printk(KERN_INFO "Krapl unloaded!\n");
}
MODULE_AUTHOR("Marcus Haehnel <[email protected]>");
MODULE_DESCRIPTION("An INTEL RAPL Driver");
MODULE_LICENSE("GPL");
module_init(krapl_init);
module_exit(krapl_exit)