File: config.c

package info (click to toggle)
s390-tools 2.40.0-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 13,288 kB
  • sloc: ansic: 187,079; sh: 12,157; cpp: 5,049; makefile: 2,812; perl: 2,541; asm: 1,097; python: 697; xml: 29
file content (337 lines) | stat: -rw-r--r-- 9,792 bytes parent folder | download | duplicates (2)
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
/*
 * cpuplugd - Linux for System z Hotplug Daemon
 *
 * Config file parsing
 *
 * Copyright IBM Corp. 2007, 2018
 *
 * s390-tools is free software; you can redistribute it and/or modify
 * it under the terms of the MIT license. See LICENSE for details.
 */

#include "cpuplugd.h"

/*
 * Return the value of a variable which parse_config() found within the
 * configuration file. Use only for values valid >= 0, because -1 is returned
 * in error case.
 */
static long parse_positive_value(char *ptr)
{
	long value = 0;
	unsigned int i;

	if (ptr == NULL)
		return -1;
	for (i = 0; i < strlen(ptr); i++) {
		if (isdigit(ptr[i]) == 0)
			return -1;
	}
	sscanf(ptr, "%ld", &value);
	return value;
}

char *get_var_rvalue(char *var_name)
{
	char tmp_name[MAX_VARNAME + 3]; /* +3 for '\0', '=' and '\n' */
	unsigned int tmp_length;
	char *rvalue;

	tmp_name[0] = '\n';
	strncpy(&tmp_name[1], var_name, MAX_VARNAME + 1); /* +1 for '\0' */
	tmp_length = strlen(tmp_name);
	tmp_name[tmp_length] = '=';
	tmp_name[tmp_length + 1] = '\0';
	rvalue = strstr(varinfo, tmp_name);
	if (rvalue == NULL)
		return NULL;
	rvalue += strlen(tmp_name);
	return rvalue;
}

static void add_var(char *name, char *rvalue)
{
	size_t offset, size;
	unsigned int i;

	if (get_var_rvalue(name))
		cpuplugd_exit("Variable defined twice: %s\n", name);
	for (i = 0; i < sym_names_count; i++) {
		if (strncmp(name, sym_names[i].name,
			    MAX(strlen(sym_names[i].name), strlen(name))) != 0)
			continue;
		cpuplugd_exit("Cannot use (pre-defined) variable name: %s\n",
			      name);
	}

	offset = strlen(varinfo);
	/* +3 because of extra '=', '\n' and '\0' */
	size = offset + strlen(name) + strlen(rvalue) + 3;
	if (size > varinfo_size)
		//TODO realloc?
		cpuplugd_exit("buffer for variables too small: need %ld, "
			      "have %ld (bytes)\n", size, varinfo_size);
	size -= offset;
	snprintf(&varinfo[offset], size, "%s=%s\n", name, rvalue);
	return;
}

static int check_term(char *symbol, char *name, char *rvalue, struct term **term)
{
	if (!strncasecmp(name, symbol, strlen(symbol))) {
		cpuplugd_debug("found the following rule: %s = %s\n",
			       name, rvalue);
		*term = parse_term(&rvalue, OP_PRIO_NONE);
		if (rvalue[0] == '\0')
			return 1;
		cpuplugd_exit("parsing error at %s, position: %s\n", symbol,
			      rvalue);
	}
	return 0;
}

static int check_value(char *symbol, char *name, char *rvalue, long *value)
{
	if (!strncasecmp(name, symbol, strlen(symbol))) {
		*value = parse_positive_value(rvalue);
		cpuplugd_debug("found %s value: %ld\n", symbol, *value);
		if (*value >= 0)
			return 1;
		cpuplugd_exit("parsing error at update\n");
	}
	return 0;
}

/*
 * Parse a single line of the configuration file
 */
static void parse_configline(char *line)
{
	char *match, *name, *rvalue, *start, *stop;
	int i, j;
	size_t len;
	char temp[strlen(line) + 1];

	if (line[0] == '#')
		return;
	for (i = j = 0; line[i] != 0; i++) /* Remove whitespace. */
		if (!isblank(line[i]) && !isspace(line[i]))
			temp[j++] = line[i];
	temp[j] = '\0';
	match = strchr(temp, '=');
	if (match == NULL)
		return;
	*match = '\0';		/* Separate name and right hand value */
	name = temp;		/* left side of = */
	rvalue = match + 1;	/* right side of = */
	/*
	 * remove the double quotes
	 * example:  CPU_MIN="2"
	 */
	start = strchr(rvalue, '"');	/* points to first " */
	stop = strrchr(rvalue, '"');	/* points to last " */
	len = stop - start;
	if (start != NULL && stop != NULL && len > 0) {
		rvalue[len] = '\0';
		rvalue = rvalue + 1;
	} else
		cpuplugd_exit("the configuration file has syntax "
			      "errors at %s, position: %s\n", name, rvalue);

	if (check_term("hotplug", name, rvalue, &cfg.hotplug))
		return;
	if (check_term("hotunplug", name, rvalue, &cfg.hotunplug))
		return;
	if (check_term("memplug", name, rvalue, &cfg.memplug))
		return;
	if (check_term("memunplug", name, rvalue, &cfg.memunplug))
		return;
	if (check_term("cmm_inc", name, rvalue, &cfg.cmm_inc))
		return;
	if (check_term("cmm_dec", name, rvalue, &cfg.cmm_dec))
		return;

	if (check_value("update", name, rvalue, &cfg.update)) {
		if (cfg.update > 0)
			return;
		cpuplugd_exit("update must be > 0\n");
	}
	if (check_value("cpu_min", name, rvalue, &cfg.cpu_min)) {
		if (cfg.cpu_min > 0)
			return;
		cpuplugd_exit("cpu_min must be > 0\n");
	}
	if (check_value("cpu_max", name, rvalue, &cfg.cpu_max)) {
		if (cfg.cpu_max == 0)
			/* if cpu_max is 0, we use the overall number of cpus */
			cfg.cpu_max = get_numcpus();
		return;
	}
	if (check_value("cmm_min", name, rvalue, &cfg.cmm_min))
		return;
	if (check_value("cmm_max", name, rvalue, &cfg.cmm_max))
		return;

	cpuplugd_debug("found the following variable: %s = %s\n",
		       name, rvalue);
	if (strlen(name) > MAX_VARNAME)
		cpuplugd_exit("Variable name too long (max. length is "
			      "%i chars): %s\n", MAX_VARNAME, name);
	add_var(name, rvalue);
}

/*
 * Function used to parse the min and max values at the beginning of the
 * configuration file as well as the hotplug and hotunplug rules.
 */
void parse_configfile(char *file)
{
	char linebuffer[MAX_LINESIZE + 2]; /* current line incl. \n and \0 */
	char *linep_offset;
	FILE *filp;

	filp = fopen(file, "r");
	if (!filp)
		cpuplugd_exit("Opening configuration file failed: %s\n",
			      strerror(errno));
	while (fgets(linebuffer, sizeof(linebuffer), filp) != NULL) {
		if (!(linep_offset = strchr(linebuffer, '\n')))
			cpuplugd_exit("Line is too long (max. length is %i "
				      "characters): %s\n", MAX_LINESIZE,
				      linebuffer);
		parse_configline(linebuffer);
	}
	fclose(filp);
}

void apply_cpu_config(void)
{
	/*
	 * Check that the initial number of cpus is not below the
	 * minimum
	 */
	if (num_cpu_start < cfg.cpu_min &&
	    get_numcpus() >= cfg.cpu_min) {
		cpuplugd_debug("The number of online cpus is below ");
		cpuplugd_debug("the minimum and will be increased.\n");
		while (get_num_online_cpus() < cfg.cpu_min) {
			if (hotplug_one_cpu())
				break;
		}
	}
	if (get_num_online_cpus() > cfg.cpu_max) {
		cpuplugd_debug("The number of online cpus is above the maximum");
		cpuplugd_debug(" and will be decreased.\n");
		while (get_num_online_cpus() > cfg.cpu_max) {
			if (hotunplug_one_cpu())
				break;
		}
	}
	if (cfg.cpu_min > get_numcpus()) {
		/*
		 * This check only works if nobody used the
		 * additional_cpus in the boot parameter section
		 */
		cpuplugd_debug("The minimum amount of cpus is above the ");
		cpuplugd_debug("number of available cpus.\n");
		cpuplugd_exit("Detected %d available cpus\n", get_numcpus());
	}
	if (get_num_online_cpus() < cfg.cpu_min) {
		cpuplugd_debug("Failed to set the number of online cpus to ");
		cpuplugd_debug("the minimum. ");
		cpuplugd_exit("Aborting.\n");
	}
}

/*
 * Check if the required settings are found in the configuration file.
 * "Autodetect" if cpu and/or memory hotplug configuration entries
 * where specified
 */
void check_config()
{
	int lpar_status;

	lpar_status = check_lpar();
	if (cfg.update < 0)
		cpuplugd_exit("No valid update interval specified.\n");
	if (cfg.cpu_max < cfg.cpu_min && cfg.cpu_max != 0)
		cpuplugd_exit("cpu_max below cpu_min, aborting.\n");
	if (cfg.cpu_max < 0 || cfg.cpu_min < 0 || cfg.hotplug == NULL ||
	    cfg.hotunplug == NULL) {
		cpuplugd_error("No valid CPU hotplug configuration "
			       "detected.\n");
		cpu = 0;
	} else {
		cpu = 1;
		cpuplugd_debug("Valid CPU hotplug configuration detected.\n");
	}
	if (cfg.cmm_max < 0 || cfg.cmm_min < 0 || cfg.memplug == NULL ||
	    cfg.memunplug == NULL || cfg.cmm_inc == NULL ||
	    cfg.cmm_max < cfg.cmm_min) {
		cpuplugd_error("No valid memory hotplug configuration "
			       "detected.\n");
		memory = 0;
	} else {
		memory = 1;
		/*
		 * check if all the necessary files exit below /proc
		 */
		if (check_cmmfiles() != 0 && lpar_status == 0) {
			cpuplugd_info("Can not open /proc/sys/vm/cmm_pages. "
				      "The memory hotplug function will be "
				      "disabled.\n");
			memory = 0;
		}
		if (memory == 1  && lpar_status == 0)
			cpuplugd_debug("Valid memory hotplug configuration "
				       "detected.\n");
		if (memory == 1  && lpar_status == 1) {
			cpuplugd_debug("Valid memory hotplug configuration "
				       "detected inside LPAR. "
				       "The memory hotplug function will be "
				       "disabled. \n");
			memory = 0;
		}
	}
	if (memory == 0 && cpu == 0)
		cpuplugd_exit("Exiting, because neither a valid cpu nor a val"
			      "id memory hotplug configuration was found.\n");
	/*
	* Save the number of online cpus and the cmm_pagesize at startup,
	* so that we can enable exactly the same amount when the daemon ends
	*
	* Don't adjust cpus if system is on vertical polarization
	*/
	saved_polarization = get_polarization();
	num_cpu_start = get_num_online_cpus();
	cpuplugd_debug("Daemon started with %d active cpus.\n",
		       num_cpu_start);
	if (saved_polarization < 0) {
		cpuplugd_debug("Daemon couldn't determine system polarization\n");
		cpuplugd_debug("Starting without evaluating cpu rules\n");
	} else if (saved_polarization == PLR_VERTICAL) {
		cpuplugd_debug("Daemon started with vertical polarization.\n");
		cpuplugd_debug("Cpu adjustments won't be made until system ");
		cpuplugd_debug("is in horizontal polarization\n");
	} else if (cpu == 1) {
		apply_cpu_config();
	}
	if (memory == 1) {
		/*
		 * Check that the initial value of cmm_pages is not below
		 * cmm_min or above cmm_max
		 */
		cmm_pagesize_start = get_cmmpages_size();
		if (cmm_pagesize_start < cfg.cmm_min) {
			cpuplugd_debug("cmm_pages is below minimum and will "
				       "be increased.\n");
			set_cmm_pages(cfg.cmm_min);
		}
		if (cmm_pagesize_start > cfg.cmm_max) {
			cpuplugd_debug("cmm_pages is above the maximum and will"
				       " be decreased.\n");
			set_cmm_pages(cfg.cmm_max);
		}
	}
}