xref: /aosp_15_r20/external/cpuinfo/src/linux/cpulist.c (revision 2b54f0db79fd8303838913b20ff3780cddaa909f)
1*2b54f0dbSXin Li #include <stdbool.h>
2*2b54f0dbSXin Li #include <stdint.h>
3*2b54f0dbSXin Li #include <stdlib.h>
4*2b54f0dbSXin Li #include <string.h>
5*2b54f0dbSXin Li #include <errno.h>
6*2b54f0dbSXin Li 
7*2b54f0dbSXin Li #include <sys/types.h>
8*2b54f0dbSXin Li #include <sys/stat.h>
9*2b54f0dbSXin Li #include <unistd.h>
10*2b54f0dbSXin Li #include <fcntl.h>
11*2b54f0dbSXin Li #include <sched.h>
12*2b54f0dbSXin Li 
13*2b54f0dbSXin Li #if CPUINFO_MOCK
14*2b54f0dbSXin Li 	#include <cpuinfo-mock.h>
15*2b54f0dbSXin Li #endif
16*2b54f0dbSXin Li #include <linux/api.h>
17*2b54f0dbSXin Li #include <cpuinfo/log.h>
18*2b54f0dbSXin Li 
19*2b54f0dbSXin Li 
20*2b54f0dbSXin Li /*
21*2b54f0dbSXin Li  * Size, in chars, of the on-stack buffer used for parsing cpu lists.
22*2b54f0dbSXin Li  * This is also the limit on the length of a single entry
23*2b54f0dbSXin Li  * (<cpu-number> or <cpu-number-start>-<cpu-number-end>)
24*2b54f0dbSXin Li  * in the cpu list.
25*2b54f0dbSXin Li  */
26*2b54f0dbSXin Li #define BUFFER_SIZE 256
27*2b54f0dbSXin Li 
28*2b54f0dbSXin Li 
29*2b54f0dbSXin Li /* Locale-independent */
is_whitespace(char c)30*2b54f0dbSXin Li inline static bool is_whitespace(char c) {
31*2b54f0dbSXin Li 	switch (c) {
32*2b54f0dbSXin Li 		case ' ':
33*2b54f0dbSXin Li 		case '\t':
34*2b54f0dbSXin Li 		case '\n':
35*2b54f0dbSXin Li 		case '\r':
36*2b54f0dbSXin Li 			return true;
37*2b54f0dbSXin Li 		default:
38*2b54f0dbSXin Li 			return false;
39*2b54f0dbSXin Li 	}
40*2b54f0dbSXin Li }
41*2b54f0dbSXin Li 
parse_number(const char * string,const char * end,uint32_t number_ptr[restrict static1])42*2b54f0dbSXin Li inline static const char* parse_number(const char* string, const char* end, uint32_t number_ptr[restrict static 1]) {
43*2b54f0dbSXin Li 	uint32_t number = 0;
44*2b54f0dbSXin Li 	while (string != end) {
45*2b54f0dbSXin Li 		const uint32_t digit = (uint32_t) (*string) - (uint32_t) '0';
46*2b54f0dbSXin Li 		if (digit >= 10) {
47*2b54f0dbSXin Li 			break;
48*2b54f0dbSXin Li 		}
49*2b54f0dbSXin Li 		number = number * UINT32_C(10) + digit;
50*2b54f0dbSXin Li 		string += 1;
51*2b54f0dbSXin Li 	}
52*2b54f0dbSXin Li 	*number_ptr = number;
53*2b54f0dbSXin Li 	return string;
54*2b54f0dbSXin Li }
55*2b54f0dbSXin Li 
parse_entry(const char * entry_start,const char * entry_end,cpuinfo_cpulist_callback callback,void * context)56*2b54f0dbSXin Li inline static bool parse_entry(const char* entry_start, const char* entry_end, cpuinfo_cpulist_callback callback, void* context) {
57*2b54f0dbSXin Li 	/* Skip whitespace at the beginning of an entry */
58*2b54f0dbSXin Li 	for (; entry_start != entry_end; entry_start++) {
59*2b54f0dbSXin Li 		if (!is_whitespace(*entry_start)) {
60*2b54f0dbSXin Li 			break;
61*2b54f0dbSXin Li 		}
62*2b54f0dbSXin Li 	}
63*2b54f0dbSXin Li 	/* Skip whitespace at the end of an entry */
64*2b54f0dbSXin Li 	for (; entry_end != entry_start; entry_end--) {
65*2b54f0dbSXin Li 		if (!is_whitespace(entry_end[-1])) {
66*2b54f0dbSXin Li 			break;
67*2b54f0dbSXin Li 		}
68*2b54f0dbSXin Li 	}
69*2b54f0dbSXin Li 
70*2b54f0dbSXin Li 	const size_t entry_length = (size_t) (entry_end - entry_start);
71*2b54f0dbSXin Li 	if (entry_length == 0) {
72*2b54f0dbSXin Li 		cpuinfo_log_warning("unexpected zero-length cpu list entry ignored");
73*2b54f0dbSXin Li 		return false;
74*2b54f0dbSXin Li 	}
75*2b54f0dbSXin Li 
76*2b54f0dbSXin Li 	#if CPUINFO_LOG_DEBUG_PARSERS
77*2b54f0dbSXin Li 		cpuinfo_log_debug("parse cpu list entry \"%.*s\" (%zu chars)", (int) entry_length, entry_start, entry_length);
78*2b54f0dbSXin Li 	#endif
79*2b54f0dbSXin Li 	uint32_t first_cpu, last_cpu;
80*2b54f0dbSXin Li 
81*2b54f0dbSXin Li 	const char* number_end = parse_number(entry_start, entry_end, &first_cpu);
82*2b54f0dbSXin Li 	if (number_end == entry_start) {
83*2b54f0dbSXin Li 		/* Failed to parse the number; ignore the entry */
84*2b54f0dbSXin Li 		cpuinfo_log_warning("invalid character '%c' in the cpu list entry \"%.*s\": entry is ignored",
85*2b54f0dbSXin Li 			entry_start[0], (int) entry_length, entry_start);
86*2b54f0dbSXin Li 		return false;
87*2b54f0dbSXin Li 	} else if (number_end == entry_end) {
88*2b54f0dbSXin Li 		/* Completely parsed the entry */
89*2b54f0dbSXin Li 		#if CPUINFO_LOG_DEBUG_PARSERS
90*2b54f0dbSXin Li 			cpuinfo_log_debug("cpulist: call callback with list_start = %"PRIu32", list_end = %"PRIu32,
91*2b54f0dbSXin Li 				first_cpu, first_cpu + 1);
92*2b54f0dbSXin Li 		#endif
93*2b54f0dbSXin Li 		return callback(first_cpu, first_cpu + 1, context);
94*2b54f0dbSXin Li 	}
95*2b54f0dbSXin Li 
96*2b54f0dbSXin Li 	/* Parse the second part of the entry */
97*2b54f0dbSXin Li 	if (*number_end != '-') {
98*2b54f0dbSXin Li 		cpuinfo_log_warning("invalid character '%c' in the cpu list entry \"%.*s\": entry is ignored",
99*2b54f0dbSXin Li 			*number_end, (int) entry_length, entry_start);
100*2b54f0dbSXin Li 		return false;
101*2b54f0dbSXin Li 	}
102*2b54f0dbSXin Li 
103*2b54f0dbSXin Li 	const char* number_start = number_end + 1;
104*2b54f0dbSXin Li 	number_end = parse_number(number_start, entry_end, &last_cpu);
105*2b54f0dbSXin Li 	if (number_end == number_start) {
106*2b54f0dbSXin Li 		/* Failed to parse the second number; ignore the entry */
107*2b54f0dbSXin Li 		cpuinfo_log_warning("invalid character '%c' in the cpu list entry \"%.*s\": entry is ignored",
108*2b54f0dbSXin Li 			*number_start, (int) entry_length, entry_start);
109*2b54f0dbSXin Li 		return false;
110*2b54f0dbSXin Li 	}
111*2b54f0dbSXin Li 
112*2b54f0dbSXin Li 	if (number_end != entry_end) {
113*2b54f0dbSXin Li 		/* Partially parsed the entry; ignore unparsed characters and continue with the parsed part */
114*2b54f0dbSXin Li 		cpuinfo_log_warning("ignored invalid characters \"%.*s\" at the end of cpu list entry \"%.*s\"",
115*2b54f0dbSXin Li 			(int) (entry_end - number_end), number_start, (int) entry_length, entry_start);
116*2b54f0dbSXin Li 	}
117*2b54f0dbSXin Li 
118*2b54f0dbSXin Li 	if (last_cpu < first_cpu) {
119*2b54f0dbSXin Li 		cpuinfo_log_warning("ignored cpu list entry \"%.*s\": invalid range %"PRIu32"-%"PRIu32,
120*2b54f0dbSXin Li 			(int) entry_length, entry_start, first_cpu, last_cpu);
121*2b54f0dbSXin Li 		return false;
122*2b54f0dbSXin Li 	}
123*2b54f0dbSXin Li 
124*2b54f0dbSXin Li 	/* Parsed both parts of the entry; update CPU set */
125*2b54f0dbSXin Li 	#if CPUINFO_LOG_DEBUG_PARSERS
126*2b54f0dbSXin Li 		cpuinfo_log_debug("cpulist: call callback with list_start = %"PRIu32", list_end = %"PRIu32,
127*2b54f0dbSXin Li 			first_cpu, last_cpu + 1);
128*2b54f0dbSXin Li 	#endif
129*2b54f0dbSXin Li 	return callback(first_cpu, last_cpu + 1, context);
130*2b54f0dbSXin Li }
131*2b54f0dbSXin Li 
cpuinfo_linux_parse_cpulist(const char * filename,cpuinfo_cpulist_callback callback,void * context)132*2b54f0dbSXin Li bool cpuinfo_linux_parse_cpulist(const char* filename, cpuinfo_cpulist_callback callback, void* context) {
133*2b54f0dbSXin Li 	bool status = true;
134*2b54f0dbSXin Li 	int file = -1;
135*2b54f0dbSXin Li 	char buffer[BUFFER_SIZE];
136*2b54f0dbSXin Li 	#if CPUINFO_LOG_DEBUG_PARSERS
137*2b54f0dbSXin Li 		cpuinfo_log_debug("parsing cpu list from file %s", filename);
138*2b54f0dbSXin Li 	#endif
139*2b54f0dbSXin Li 
140*2b54f0dbSXin Li #if CPUINFO_MOCK
141*2b54f0dbSXin Li 	file = cpuinfo_mock_open(filename, O_RDONLY);
142*2b54f0dbSXin Li #else
143*2b54f0dbSXin Li 	file = open(filename, O_RDONLY);
144*2b54f0dbSXin Li #endif
145*2b54f0dbSXin Li 	if (file == -1) {
146*2b54f0dbSXin Li 		cpuinfo_log_info("failed to open %s: %s", filename, strerror(errno));
147*2b54f0dbSXin Li 		status = false;
148*2b54f0dbSXin Li 		goto cleanup;
149*2b54f0dbSXin Li 	}
150*2b54f0dbSXin Li 
151*2b54f0dbSXin Li 	size_t position = 0;
152*2b54f0dbSXin Li 	const char* buffer_end = &buffer[BUFFER_SIZE];
153*2b54f0dbSXin Li 	char* data_start = buffer;
154*2b54f0dbSXin Li 	ssize_t bytes_read;
155*2b54f0dbSXin Li 	do {
156*2b54f0dbSXin Li #if CPUINFO_MOCK
157*2b54f0dbSXin Li 		bytes_read = cpuinfo_mock_read(file, data_start, (size_t) (buffer_end - data_start));
158*2b54f0dbSXin Li #else
159*2b54f0dbSXin Li 		bytes_read = read(file, data_start, (size_t) (buffer_end - data_start));
160*2b54f0dbSXin Li #endif
161*2b54f0dbSXin Li 		if (bytes_read < 0) {
162*2b54f0dbSXin Li 			cpuinfo_log_info("failed to read file %s at position %zu: %s", filename, position, strerror(errno));
163*2b54f0dbSXin Li 			status = false;
164*2b54f0dbSXin Li 			goto cleanup;
165*2b54f0dbSXin Li 		}
166*2b54f0dbSXin Li 
167*2b54f0dbSXin Li 		position += (size_t) bytes_read;
168*2b54f0dbSXin Li 		const char* data_end = data_start + (size_t) bytes_read;
169*2b54f0dbSXin Li 		const char* entry_start = buffer;
170*2b54f0dbSXin Li 
171*2b54f0dbSXin Li 		if (bytes_read == 0) {
172*2b54f0dbSXin Li 			/* No more data in the file: process the remaining text in the buffer as a single entry */
173*2b54f0dbSXin Li 			const char* entry_end = data_end;
174*2b54f0dbSXin Li 			const bool entry_status = parse_entry(entry_start, entry_end, callback, context);
175*2b54f0dbSXin Li 			status &= entry_status;
176*2b54f0dbSXin Li 		} else {
177*2b54f0dbSXin Li 			const char* entry_end;
178*2b54f0dbSXin Li 			do {
179*2b54f0dbSXin Li 				/* Find the end of the entry, as indicated by a comma (',') */
180*2b54f0dbSXin Li 				for (entry_end = entry_start; entry_end != data_end; entry_end++) {
181*2b54f0dbSXin Li 					if (*entry_end == ',') {
182*2b54f0dbSXin Li 						break;
183*2b54f0dbSXin Li 					}
184*2b54f0dbSXin Li 				}
185*2b54f0dbSXin Li 
186*2b54f0dbSXin Li 				/*
187*2b54f0dbSXin Li 				 * If we located separator at the end of the entry, parse it.
188*2b54f0dbSXin Li 				 * Otherwise, there may be more data at the end; read the file once again.
189*2b54f0dbSXin Li 				 */
190*2b54f0dbSXin Li 				if (entry_end != data_end) {
191*2b54f0dbSXin Li 					const bool entry_status = parse_entry(entry_start, entry_end, callback, context);
192*2b54f0dbSXin Li 					status &= entry_status;
193*2b54f0dbSXin Li 					entry_start = entry_end + 1;
194*2b54f0dbSXin Li 				}
195*2b54f0dbSXin Li 			} while (entry_end != data_end);
196*2b54f0dbSXin Li 
197*2b54f0dbSXin Li 			/* Move remaining partial entry data at the end to the beginning of the buffer */
198*2b54f0dbSXin Li 			const size_t entry_length = (size_t) (entry_end - entry_start);
199*2b54f0dbSXin Li 			memmove(buffer, entry_start, entry_length);
200*2b54f0dbSXin Li 			data_start = &buffer[entry_length];
201*2b54f0dbSXin Li 		}
202*2b54f0dbSXin Li 	} while (bytes_read != 0);
203*2b54f0dbSXin Li 
204*2b54f0dbSXin Li cleanup:
205*2b54f0dbSXin Li 	if (file != -1) {
206*2b54f0dbSXin Li #if CPUINFO_MOCK
207*2b54f0dbSXin Li 		cpuinfo_mock_close(file);
208*2b54f0dbSXin Li #else
209*2b54f0dbSXin Li 		close(file);
210*2b54f0dbSXin Li #endif
211*2b54f0dbSXin Li 		file = -1;
212*2b54f0dbSXin Li 	}
213*2b54f0dbSXin Li 	return status;
214*2b54f0dbSXin Li }
215