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