1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Intel Speed Select -- Allow speed select to daemonize
4  * Copyright (c) 2022 Intel Corporation.
5  */
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <stdarg.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <fcntl.h>
13 #include <sys/file.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <errno.h>
17 #include <getopt.h>
18 #include <signal.h>
19 #include <time.h>
20 
21 #include "isst.h"
22 
23 static int per_package_levels_info[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE];
24 static time_t per_package_levels_tm[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE];
25 
26 static void init_levels(void)
27 {
28 	int i, j;
29 
30 	for (i = 0; i < MAX_PACKAGE_COUNT; ++i)
31 		for (j = 0; j < MAX_DIE_PER_PACKAGE; ++j)
32 			per_package_levels_info[i][j] = -1;
33 }
34 
35 void process_level_change(int cpu)
36 {
37 	struct isst_pkg_ctdp_level_info ctdp_level;
38 	int pkg_id = get_physical_package_id(cpu);
39 	int die_id = get_physical_die_id(cpu);
40 	struct isst_pkg_ctdp pkg_dev;
41 	time_t tm;
42 	int ret;
43 
44 	if (pkg_id >= MAX_PACKAGE_COUNT || die_id > MAX_DIE_PER_PACKAGE) {
45 		debug_printf("Invalid package/die info for cpu:%d\n", cpu);
46 		return;
47 	}
48 
49 	tm = time(NULL);
50 	if (tm - per_package_levels_tm[pkg_id][die_id] < 2 )
51 		return;
52 
53 	per_package_levels_tm[pkg_id][die_id] = tm;
54 
55 	ret = isst_get_ctdp_levels(cpu, &pkg_dev);
56 	if (ret) {
57 		debug_printf("Can't get tdp levels for cpu:%d\n", cpu);
58 		return;
59 	}
60 
61 	debug_printf("Get Config level %d pkg:%d die:%d current_level:%d \n", cpu,
62 		      pkg_id, die_id, pkg_dev.current_level);
63 
64 	if (pkg_dev.locked) {
65 		debug_printf("config TDP s locked \n");
66 		return;
67 	}
68 
69 	if (per_package_levels_info[pkg_id][die_id] == pkg_dev.current_level)
70 		return;
71 
72 	debug_printf("**Config level change for cpu:%d pkg:%d die:%d from %d to %d\n",
73 		      cpu, pkg_id, die_id, per_package_levels_info[pkg_id][die_id],
74 		      pkg_dev.current_level);
75 
76 	per_package_levels_info[pkg_id][die_id] = pkg_dev.current_level;
77 
78 	ctdp_level.core_cpumask_size =
79 		alloc_cpu_set(&ctdp_level.core_cpumask);
80 	ret = isst_get_coremask_info(cpu, pkg_dev.current_level, &ctdp_level);
81 	if (ret) {
82 		free_cpu_set(ctdp_level.core_cpumask);
83 		debug_printf("Can't get core_mask:%d\n", cpu);
84 		return;
85 	}
86 
87 	if (ctdp_level.cpu_count) {
88 		int i, max_cpus = get_topo_max_cpus();
89 		for (i = 0; i < max_cpus; ++i) {
90 			if (pkg_id != get_physical_package_id(i) || die_id != get_physical_die_id(i))
91 				continue;
92 			if (CPU_ISSET_S(i, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask)) {
93 				fprintf(stderr, "online cpu %d\n", i);
94 				set_cpu_online_offline(i, 1);
95 			} else {
96 				fprintf(stderr, "offline cpu %d\n", i);
97 				set_cpu_online_offline(i, 0);
98 			}
99 		}
100 	}
101 
102 	free_cpu_set(ctdp_level.core_cpumask);
103 }
104 
105 static void _poll_for_config_change(int cpu, void *arg1, void *arg2,
106 				    void *arg3, void *arg4)
107 {
108 	process_level_change(cpu);
109 }
110 
111 static void poll_for_config_change(void)
112 {
113 	for_each_online_package_in_set(_poll_for_config_change, NULL, NULL,
114 				       NULL, NULL);
115 }
116 
117 static int done = 0;
118 static int pid_file_handle;
119 
120 static void signal_handler(int sig)
121 {
122 	switch (sig) {
123 	case SIGINT:
124 	case SIGTERM:
125 		done = 1;
126 		hfi_exit();
127 		exit(0);
128 		break;
129 	default:
130 		break;
131 	}
132 }
133 
134 static void daemonize(char *rundir, char *pidfile)
135 {
136 	int pid, sid, i;
137 	char str[10];
138 	struct sigaction sig_actions;
139 	sigset_t sig_set;
140 	int ret;
141 
142 	if (getppid() == 1)
143 		return;
144 
145 	sigemptyset(&sig_set);
146 	sigaddset(&sig_set, SIGCHLD);
147 	sigaddset(&sig_set, SIGTSTP);
148 	sigaddset(&sig_set, SIGTTOU);
149 	sigaddset(&sig_set, SIGTTIN);
150 	sigprocmask(SIG_BLOCK, &sig_set, NULL);
151 
152 	sig_actions.sa_handler = signal_handler;
153 	sigemptyset(&sig_actions.sa_mask);
154 	sig_actions.sa_flags = 0;
155 
156 	sigaction(SIGHUP, &sig_actions, NULL);
157 	sigaction(SIGTERM, &sig_actions, NULL);
158 	sigaction(SIGINT, &sig_actions, NULL);
159 
160 	pid = fork();
161 	if (pid < 0) {
162 		/* Could not fork */
163 		exit(EXIT_FAILURE);
164 	}
165 	if (pid > 0)
166 		exit(EXIT_SUCCESS);
167 
168 	umask(027);
169 
170 	sid = setsid();
171 	if (sid < 0)
172 		exit(EXIT_FAILURE);
173 
174 	/* close all descriptors */
175 	for (i = getdtablesize(); i >= 0; --i)
176 		close(i);
177 
178 	i = open("/dev/null", O_RDWR);
179 	ret = dup(i);
180 	if (ret == -1)
181 		exit(EXIT_FAILURE);
182 
183 	ret = dup(i);
184 	if (ret == -1)
185 		exit(EXIT_FAILURE);
186 
187 	ret = chdir(rundir);
188 	if (ret == -1)
189 		exit(EXIT_FAILURE);
190 
191 	pid_file_handle = open(pidfile, O_RDWR | O_CREAT, 0600);
192 	if (pid_file_handle == -1) {
193 		/* Couldn't open lock file */
194 		exit(1);
195 	}
196 	/* Try to lock file */
197 #ifdef LOCKF_SUPPORT
198 	if (lockf(pid_file_handle, F_TLOCK, 0) == -1) {
199 #else
200 	if (flock(pid_file_handle, LOCK_EX|LOCK_NB) < 0) {
201 #endif
202 		/* Couldn't get lock on lock file */
203 		fprintf(stderr, "Couldn't get lock file %d\n", getpid());
204 		exit(1);
205 	}
206 	snprintf(str, sizeof(str), "%d\n", getpid());
207 	ret = write(pid_file_handle, str, strlen(str));
208 	if (ret == -1)
209 		exit(EXIT_FAILURE);
210 
211 	close(i);
212 }
213 
214 int isst_daemon(int debug_mode, int poll_interval, int no_daemon)
215 {
216 	int ret;
217 
218 	if (!no_daemon && poll_interval < 0 && !debug_mode) {
219 		fprintf(stderr, "OOB mode is enabled and will run as daemon\n");
220 		daemonize((char *) "/tmp/",
221 				(char *)"/tmp/hfi-events.pid");
222 	} else {
223 		signal(SIGINT, signal_handler);
224 	}
225 
226 	init_levels();
227 
228 	if (poll_interval < 0) {
229 		ret = hfi_main();
230 		if (ret) {
231 			fprintf(stderr, "HFI initialization failed\n");
232 		}
233 		fprintf(stderr, "Must specify poll-interval\n");
234 		return ret;
235 	}
236 
237 	debug_printf("Starting loop\n");
238 	while (!done) {
239 		sleep(poll_interval);
240 		poll_for_config_change();
241 	}
242 
243 	return 0;
244 }
245