Skip to content

Commit 3bc0f20

Browse files
committed
tools/power/x86/intel-speed-select: Use cgroup isolate for CPU 0
From kernel version 6.5, CPU 0 hotplug capability is deprecated. If some SST profile doesn't have CPU 0, then it is no longer possible to offline CPU 0. This means that user space threads will still run on CPU 0. To workaround this issue, use cgroup v2 isolation feature. Whenever there /sys/devices/system/cpu/cpu0/online file is absent or open fails, isolate CPU 0 via CPU cgroup v2 isolation. Also add a command line option to force even if the /sys/devices/system/cpu/cpu0/online is present. The previous commit "01bcb56f059e ("tools/power/x86/intel-speed-select: Prevent CPU 0 offline") was just warning about this issue based on the kernel version 6.5 and above. With this new approach, instead of warning take action to mitigate the issue. Signed-off-by: Srinivas Pandruvada <[email protected]>
1 parent bc5370c commit 3bc0f20

File tree

3 files changed

+97
-39
lines changed

3 files changed

+97
-39
lines changed

tools/power/x86/intel-speed-select/isst-config.c

Lines changed: 93 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66

77
#include <ctype.h>
88
#include <linux/isst_if.h>
9-
#include <sys/utsname.h>
109

1110
#include "isst.h"
1211

@@ -56,6 +55,8 @@ static int clos_min = -1;
5655
static int clos_max = -1;
5756
static int clos_desired = -1;
5857
static int clos_priority_type;
58+
static int cpu_0_cgroupv2;
59+
static int cpu_0_workaround(int isolate);
5960

6061
struct _cpu_map {
6162
unsigned short core_id;
@@ -475,52 +476,26 @@ static unsigned int is_cpu_online(int cpu)
475476
return online;
476477
}
477478

478-
static int get_kernel_version(int *major, int *minor)
479-
{
480-
struct utsname buf;
481-
int ret;
482-
483-
ret = uname(&buf);
484-
if (ret)
485-
return ret;
486-
487-
ret = sscanf(buf.release, "%d.%d", major, minor);
488-
if (ret != 2)
489-
return ret;
490-
491-
return 0;
492-
}
493-
494-
#define CPU0_HOTPLUG_DEPRECATE_MAJOR_VER 6
495-
#define CPU0_HOTPLUG_DEPRECATE_MINOR_VER 5
496-
497479
void set_cpu_online_offline(int cpu, int state)
498480
{
499481
char buffer[128];
500482
int fd, ret;
501483

502-
if (!cpu) {
503-
int major, minor;
504-
505-
ret = get_kernel_version(&major, &minor);
506-
if (!ret) {
507-
if (major > CPU0_HOTPLUG_DEPRECATE_MAJOR_VER || (major == CPU0_HOTPLUG_DEPRECATE_MAJOR_VER &&
508-
minor >= CPU0_HOTPLUG_DEPRECATE_MINOR_VER)) {
509-
debug_printf("Ignore CPU 0 offline/online for kernel version >= %d.%d\n", major, minor);
510-
debug_printf("Use cgroups to isolate CPU 0\n");
511-
return;
512-
}
513-
}
484+
if (cpu_0_cgroupv2 && !cpu) {
485+
fprintf(stderr, "Will use cgroup v2 for CPU 0\n");
486+
cpu_0_workaround(!state);
487+
return;
514488
}
515489

516490
snprintf(buffer, sizeof(buffer),
517491
"/sys/devices/system/cpu/cpu%d/online", cpu);
518492

519493
fd = open(buffer, O_WRONLY);
520494
if (fd < 0) {
521-
if (!cpu && state) {
495+
if (!cpu) {
522496
fprintf(stderr, "This system is not configured for CPU 0 online/offline\n");
523-
fprintf(stderr, "Ignoring online request for CPU 0 as this is already online\n");
497+
fprintf(stderr, "Will use cgroup v2\n");
498+
cpu_0_workaround(!state);
524499
return;
525500
}
526501
err(-1, "%s open failed", buffer);
@@ -907,7 +882,7 @@ int enable_cpuset_controller(void)
907882
return 0;
908883
}
909884

910-
int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int level)
885+
int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int level, int cpu_0_only)
911886
{
912887
int i, first, curr_index, index, ret, fd;
913888
static char str[512], dir_name[64];
@@ -950,6 +925,12 @@ int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int lev
950925
curr_index = 0;
951926
first = 1;
952927
str[0] = '\0';
928+
929+
if (cpu_0_only) {
930+
snprintf(str, str_len, "0");
931+
goto create_partition;
932+
}
933+
953934
for (i = 0; i < get_topo_max_cpus(); ++i) {
954935
if (!is_cpu_in_power_domain(i, id))
955936
continue;
@@ -972,6 +953,7 @@ int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int lev
972953
first = 0;
973954
}
974955

956+
create_partition:
975957
debug_printf("isolated CPUs list: package:%d curr_index:%d [%s]\n", id->pkg, curr_index ,str);
976958

977959
snprintf(cpuset_cpus, sizeof(cpuset_cpus), "%s/cpuset.cpus", dir_name);
@@ -1012,6 +994,74 @@ int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int lev
1012994
return 0;
1013995
}
1014996

997+
static int cpu_0_workaround(int isolate)
998+
{
999+
int fd, fd1, len, ret;
1000+
cpu_set_t cpu_mask;
1001+
struct isst_id id;
1002+
char str[2];
1003+
1004+
debug_printf("isolate CPU 0 state: %d\n", isolate);
1005+
1006+
if (isolate)
1007+
goto isolate;
1008+
1009+
/* First check if CPU 0 was isolated to remove isolation. */
1010+
1011+
/* If the cpuset.cpus doesn't exist, that means that none of the CPUs are isolated*/
1012+
fd = open("/sys/fs/cgroup/0-0-0/cpuset.cpus", O_RDONLY, 0);
1013+
if (fd < 0)
1014+
return 0;
1015+
1016+
len = read(fd, str, sizeof(str));
1017+
/* Error check, but unlikely to fail. If fails that means that not isolated */
1018+
if (len == -1)
1019+
return 0;
1020+
1021+
1022+
/* Is CPU 0 is in isolate list, the display is sorted so first element will be CPU 0*/
1023+
if (str[0] != '0') {
1024+
close(fd);
1025+
return 0;
1026+
}
1027+
1028+
fd1 = open("/sys/fs/cgroup/0-0-0/cpuset.cpus.partition", O_RDONLY, 0);
1029+
/* Unlikely that, this attribute is not present, but handle error */
1030+
if (fd1 < 0) {
1031+
close(fd);
1032+
return 0;
1033+
}
1034+
1035+
/* Is CPU 0 already changed partition to "member" */
1036+
len = read(fd1, str, sizeof(str));
1037+
if (len != -1 && str[0] == 'm') {
1038+
close(fd1);
1039+
close(fd);
1040+
return 0;
1041+
}
1042+
1043+
close(fd1);
1044+
close(fd);
1045+
1046+
debug_printf("CPU 0 was isolated before, so remove isolation\n");
1047+
1048+
isolate:
1049+
ret = enable_cpuset_controller();
1050+
if (ret)
1051+
goto isolate_fail;
1052+
1053+
CPU_ZERO(&cpu_mask);
1054+
memset(&id, 0, sizeof(struct isst_id));
1055+
CPU_SET(0, &cpu_mask);
1056+
1057+
ret = isolate_cpus(&id, sizeof(cpu_mask), &cpu_mask, isolate, 1);
1058+
isolate_fail:
1059+
if (ret)
1060+
fprintf(stderr, "Can't isolate CPU 0\n");
1061+
1062+
return ret;
1063+
}
1064+
10151065
static int isst_fill_platform_info(void)
10161066
{
10171067
const char *pathname = "/dev/isst_interface";
@@ -1458,7 +1508,8 @@ static void set_tdp_level_for_cpu(struct isst_id *id, void *arg1, void *arg2, vo
14581508
if (ret)
14591509
goto use_offline;
14601510

1461-
ret = isolate_cpus(id, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask, tdp_level);
1511+
ret = isolate_cpus(id, ctdp_level.core_cpumask_size,
1512+
ctdp_level.core_cpumask, tdp_level, 0);
14621513
if (ret)
14631514
goto use_offline;
14641515

@@ -3054,6 +3105,7 @@ static void usage(void)
30543105
printf("\t[-n|--no-daemon : Don't run as daemon. By default --oob will turn on daemon mode\n");
30553106
printf("\t[-w|--delay : Delay for reading config level state change in OOB poll mode.\n");
30563107
printf("\t[-g|--cgroupv2 : Try to use cgroup v2 CPU isolation instead of CPU online/offline.\n");
3108+
printf("\t[-u|--cpu0-workaround : Don't try to online/offline CPU0 instead use cgroup v2.\n");
30573109
printf("\nResult format\n");
30583110
printf("\tResult display uses a common format for each command:\n");
30593111
printf("\tResults are formatted in text/JSON with\n");
@@ -3107,6 +3159,7 @@ static void cmdline(int argc, char **argv)
31073159
{ "no-daemon", no_argument, 0, 'n' },
31083160
{ "poll-interval", required_argument, 0, 'w' },
31093161
{ "cgroupv2", required_argument, 0, 'g' },
3162+
{ "cpu0-workaround", required_argument, 0, 'u' },
31103163
{ 0, 0, 0, 0 }
31113164
};
31123165

@@ -3137,7 +3190,7 @@ static void cmdline(int argc, char **argv)
31373190
goto out;
31383191

31393192
progname = argv[0];
3140-
while ((opt = getopt_long_only(argc, argv, "+c:df:hio:vabw:ng", long_options,
3193+
while ((opt = getopt_long_only(argc, argv, "+c:df:hio:vabw:ngu", long_options,
31413194
&option_index)) != -1) {
31423195
switch (opt) {
31433196
case 'a':
@@ -3199,6 +3252,9 @@ static void cmdline(int argc, char **argv)
31993252
case 'g':
32003253
cgroupv2 = 1;
32013254
break;
3255+
case 'u':
3256+
cpu_0_cgroupv2 = 1;
3257+
break;
32023258
default:
32033259
usage();
32043260
}

tools/power/x86/intel-speed-select/isst-daemon.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ void process_level_change(struct isst_id *id)
9090
if (ret)
9191
goto use_offline;
9292

93-
isolate_cpus(id, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask, pkg_dev.current_level);
93+
isolate_cpus(id, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask,
94+
pkg_dev.current_level, 0);
9495

9596
goto free_mask;
9697
}

tools/power/x86/intel-speed-select/isst.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,8 @@ extern struct isst_platform_ops *tpmi_get_platform_ops(void);
318318

319319
/* Cgroup related interface */
320320
extern int enable_cpuset_controller(void);
321-
extern int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask, int level);
321+
extern int isolate_cpus(struct isst_id *id, int mask_size, cpu_set_t *cpu_mask,
322+
int level, int cpu_0_only);
322323
extern int use_cgroupv2(void);
323324

324325
#endif

0 commit comments

Comments
 (0)