sysfs.c 8.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
/*
 *  (C) 2004-2009  Dominik Brodowski <linux@dominikbrodowski.de>
 *  (C) 2011       Thomas Renninger <trenn@novell.com> Novell Inc.
 *
 *  Licensed under the terms of the GNU GPL License version 2.
 */

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include "helpers/sysfs.h"

unsigned int sysfs_read_file(const char *path, char *buf, size_t buflen)
{
	int fd;
22
	ssize_t numread;
23

24 25
	fd = open(path, O_RDONLY);
	if (fd == -1)
26 27 28
		return 0;

	numread = read(fd, buf, buflen - 1);
29
	if (numread < 1) {
30 31 32 33 34 35 36
		close(fd);
		return 0;
	}

	buf[numread] = '\0';
	close(fd);

37
	return (unsigned int) numread;
38 39 40 41 42 43
}

static unsigned int sysfs_write_file(const char *path,
				     const char *value, size_t len)
{
	int fd;
44
	ssize_t numwrite;
45

46 47
	fd = open(path, O_WRONLY);
	if (fd == -1)
48 49 50
		return 0;

	numwrite = write(fd, value, len);
51
	if (numwrite < 1) {
52 53 54 55
		close(fd);
		return 0;
	}
	close(fd);
56
	return (unsigned int) numwrite;
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
/*
 * Detect whether a CPU is online
 *
 * Returns:
 *     1 -> if CPU is online
 *     0 -> if CPU is offline
 *     negative errno values in error case
 */
int sysfs_is_cpu_online(unsigned int cpu)
{
	char path[SYSFS_PATH_MAX];
	int fd;
	ssize_t numread;
	unsigned long long value;
	char linebuf[MAX_LINE_LEN];
	char *endp;
	struct stat statbuf;

	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u", cpu);

	if (stat(path, &statbuf) != 0)
		return 0;

	/*
	 * kernel without CONFIG_HOTPLUG_CPU
	 * -> cpuX directory exists, but not cpuX/online file
	 */
	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/online", cpu);
	if (stat(path, &statbuf) != 0)
		return 1;

	fd = open(path, O_RDONLY);
	if (fd == -1)
		return -errno;

	numread = read(fd, linebuf, MAX_LINE_LEN - 1);
	if (numread < 1) {
		close(fd);
		return -EIO;
	}
	linebuf[numread] = '\0';
	close(fd);

	value = strtoull(linebuf, &endp, 0);
	if (value > 1 || value < 0)
		return -EINVAL;

	return value;
}

109 110 111 112 113 114 115 116 117 118 119 120 121
/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */

/*
 * helper function to read file from /sys into given buffer
 * fname is a relative path under "cpuX/cpuidle/stateX/" dir
 * cstates starting with 0, C0 is not counted as cstate.
 * This means if you want C1 info, pass 0 as idlestate param
 */
unsigned int sysfs_idlestate_read_file(unsigned int cpu, unsigned int idlestate,
			     const char *fname, char *buf, size_t buflen)
{
	char path[SYSFS_PATH_MAX];
	int fd;
122
	ssize_t numread;
123 124 125 126

	snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/cpuidle/state%u/%s",
		 cpu, idlestate, fname);

127 128
	fd = open(path, O_RDONLY);
	if (fd == -1)
129 130 131
		return 0;

	numread = read(fd, buf, buflen - 1);
132
	if (numread < 1) {
133 134 135 136 137 138 139
		close(fd);
		return 0;
	}

	buf[numread] = '\0';
	close(fd);

140
	return (unsigned int) numread;
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
}

/* read access to files which contain one numeric value */

enum idlestate_value {
	IDLESTATE_USAGE,
	IDLESTATE_POWER,
	IDLESTATE_LATENCY,
	IDLESTATE_TIME,
	MAX_IDLESTATE_VALUE_FILES
};

static const char *idlestate_value_files[MAX_IDLESTATE_VALUE_FILES] = {
	[IDLESTATE_USAGE] = "usage",
	[IDLESTATE_POWER] = "power",
	[IDLESTATE_LATENCY] = "latency",
	[IDLESTATE_TIME]  = "time",
};

static unsigned long long sysfs_idlestate_get_one_value(unsigned int cpu,
						     unsigned int idlestate,
						     enum idlestate_value which)
{
	unsigned long long value;
	unsigned int len;
	char linebuf[MAX_LINE_LEN];
	char *endp;

169
	if (which >= MAX_IDLESTATE_VALUE_FILES)
170 171
		return 0;

172 173 174 175
	len = sysfs_idlestate_read_file(cpu, idlestate,
					idlestate_value_files[which],
					linebuf, sizeof(linebuf));
	if (len == 0)
176 177 178 179
		return 0;

	value = strtoull(linebuf, &endp, 0);

180
	if (endp == linebuf || errno == ERANGE)
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199
		return 0;

	return value;
}

/* read access to files which contain one string */

enum idlestate_string {
	IDLESTATE_DESC,
	IDLESTATE_NAME,
	MAX_IDLESTATE_STRING_FILES
};

static const char *idlestate_string_files[MAX_IDLESTATE_STRING_FILES] = {
	[IDLESTATE_DESC] = "desc",
	[IDLESTATE_NAME] = "name",
};


200 201 202
static char *sysfs_idlestate_get_one_string(unsigned int cpu,
					unsigned int idlestate,
					enum idlestate_string which)
203 204 205 206 207 208 209 210
{
	char linebuf[MAX_LINE_LEN];
	char *result;
	unsigned int len;

	if (which >= MAX_IDLESTATE_STRING_FILES)
		return NULL;

211 212 213 214
	len = sysfs_idlestate_read_file(cpu, idlestate,
					idlestate_string_files[which],
					linebuf, sizeof(linebuf));
	if (len == 0)
215 216
		return NULL;

217 218
	result = strdup(linebuf);
	if (result == NULL)
219 220 221 222 223 224 225 226
		return NULL;

	if (result[strlen(result) - 1] == '\n')
		result[strlen(result) - 1] = '\0';

	return result;
}

227 228
unsigned long sysfs_get_idlestate_latency(unsigned int cpu,
					unsigned int idlestate)
229 230 231 232
{
	return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_LATENCY);
}

233 234
unsigned long sysfs_get_idlestate_usage(unsigned int cpu,
					unsigned int idlestate)
235 236 237 238
{
	return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_USAGE);
}

239 240
unsigned long long sysfs_get_idlestate_time(unsigned int cpu,
					unsigned int idlestate)
241 242 243 244
{
	return sysfs_idlestate_get_one_value(cpu, idlestate, IDLESTATE_TIME);
}

245
char *sysfs_get_idlestate_name(unsigned int cpu, unsigned int idlestate)
246 247 248 249
{
	return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_NAME);
}

250
char *sysfs_get_idlestate_desc(unsigned int cpu, unsigned int idlestate)
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
{
	return sysfs_idlestate_get_one_string(cpu, idlestate, IDLESTATE_DESC);
}

/*
 * Returns number of supported C-states of CPU core cpu
 * Negativ in error case
 * Zero if cpuidle does not export any C-states
 */
int sysfs_get_idlestate_count(unsigned int cpu)
{
	char file[SYSFS_PATH_MAX];
	struct stat statbuf;
	int idlestates = 1;


	snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpuidle");
268
	if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
269 270 271
		return -ENODEV;

	snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU "cpu%u/cpuidle/state0", cpu);
272
	if (stat(file, &statbuf) != 0 || !S_ISDIR(statbuf.st_mode))
273 274
		return 0;

275
	while (stat(file, &statbuf) == 0 && S_ISDIR(statbuf.st_mode)) {
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
		snprintf(file, SYSFS_PATH_MAX, PATH_TO_CPU
			 "cpu%u/cpuidle/state%d", cpu, idlestates);
		idlestates++;
	}
	idlestates--;
	return idlestates;
}

/* CPUidle general /sys/devices/system/cpu/cpuidle/ sysfs access ********/

/*
 * helper function to read file from /sys into given buffer
 * fname is a relative path under "cpu/cpuidle/" dir
 */
static unsigned int sysfs_cpuidle_read_file(const char *fname, char *buf,
					    size_t buflen)
{
	char path[SYSFS_PATH_MAX];

	snprintf(path, sizeof(path), PATH_TO_CPU "cpuidle/%s", fname);

	return sysfs_read_file(path, buf, buflen);
}



/* read access to files which contain one string */

enum cpuidle_string {
	CPUIDLE_GOVERNOR,
	CPUIDLE_GOVERNOR_RO,
	CPUIDLE_DRIVER,
	MAX_CPUIDLE_STRING_FILES
};

static const char *cpuidle_string_files[MAX_CPUIDLE_STRING_FILES] = {
	[CPUIDLE_GOVERNOR]	= "current_governor",
	[CPUIDLE_GOVERNOR_RO]	= "current_governor_ro",
	[CPUIDLE_DRIVER]	= "current_driver",
};


318
static char *sysfs_cpuidle_get_one_string(enum cpuidle_string which)
319 320 321 322 323 324 325 326
{
	char linebuf[MAX_LINE_LEN];
	char *result;
	unsigned int len;

	if (which >= MAX_CPUIDLE_STRING_FILES)
		return NULL;

327 328 329
	len = sysfs_cpuidle_read_file(cpuidle_string_files[which],
				linebuf, sizeof(linebuf));
	if (len == 0)
330 331
		return NULL;

332 333
	result = strdup(linebuf);
	if (result == NULL)
334 335 336 337 338 339 340 341
		return NULL;

	if (result[strlen(result) - 1] == '\n')
		result[strlen(result) - 1] = '\0';

	return result;
}

342
char *sysfs_get_cpuidle_governor(void)
343 344 345 346 347 348 349 350
{
	char *tmp = sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR_RO);
	if (!tmp)
		return sysfs_cpuidle_get_one_string(CPUIDLE_GOVERNOR);
	else
		return tmp;
}

351
char *sysfs_get_cpuidle_driver(void)
352 353 354 355 356 357 358 359 360 361 362
{
	return sysfs_cpuidle_get_one_string(CPUIDLE_DRIVER);
}
/* CPUidle idlestate specific /sys/devices/system/cpu/cpuX/cpuidle/ access */

/*
 * Get sched_mc or sched_smt settings
 * Pass "mc" or "smt" as argument
 *
 * Returns negative value on failure
 */
363
int sysfs_get_sched(const char *smt_mc)
364
{
365
	return -ENODEV;
366 367 368 369 370 371 372 373
}

/*
 * Get sched_mc or sched_smt settings
 * Pass "mc" or "smt" as argument
 *
 * Returns negative value on failure
 */
374
int sysfs_set_sched(const char *smt_mc, int val)
375
{
376
	return -ENODEV;
377
}