| 1 | |
| 2 | #include <assert.h>
|
| 3 | #include <ctype.h>
|
| 4 | #include <errno.h>
|
| 5 | #include <limits.h>
|
| 6 | #include <stdbool.h>
|
| 7 | #include <stdio.h>
|
| 8 | #include <stdlib.h>
|
| 9 | #include <string.h>
|
| 10 | #include <sys/vfs.h>
|
| 11 | #include <sys/types.h>
|
| 12 | #include <sys/stat.h>
|
| 13 | #include <fcntl.h>
|
| 14 | #include <pthread.h>
|
| 15 | #include <unistd.h>
|
| 16 | #include <sys/mount.h>
|
| 17 |
|
| 18 | #include "fs.h"
|
| 19 | #include "../io.h"
|
| 20 | #include "debug-internal.h"
|
| 21 |
|
| 22 | #define _STR(x) #x
|
| 23 | #define STR(x) _STR(x)
|
| 24 |
|
| 25 | #ifndef SYSFS_MAGIC
|
| 26 | #define SYSFS_MAGIC 0x62656572
|
| 27 | #endif
|
| 28 |
|
| 29 | #ifndef PROC_SUPER_MAGIC
|
| 30 | #define PROC_SUPER_MAGIC 0x9fa0
|
| 31 | #endif
|
| 32 |
|
| 33 | #ifndef DEBUGFS_MAGIC
|
| 34 | #define DEBUGFS_MAGIC 0x64626720
|
| 35 | #endif
|
| 36 |
|
| 37 | #ifndef TRACEFS_MAGIC
|
| 38 | #define TRACEFS_MAGIC 0x74726163
|
| 39 | #endif
|
| 40 |
|
| 41 | #ifndef HUGETLBFS_MAGIC
|
| 42 | #define HUGETLBFS_MAGIC 0x958458f6
|
| 43 | #endif
|
| 44 |
|
| 45 | #ifndef BPF_FS_MAGIC
|
| 46 | #define BPF_FS_MAGIC 0xcafe4a11
|
| 47 | #endif
|
| 48 |
|
| 49 | static const char * const sysfs__known_mountpoints[] = {
|
| 50 | "/sys",
|
| 51 | 0,
|
| 52 | };
|
| 53 |
|
| 54 | static const char * const procfs__known_mountpoints[] = {
|
| 55 | "/proc",
|
| 56 | 0,
|
| 57 | };
|
| 58 |
|
| 59 | #ifndef DEBUGFS_DEFAULT_PATH
|
| 60 | #define DEBUGFS_DEFAULT_PATH "/sys/kernel/debug"
|
| 61 | #endif
|
| 62 |
|
| 63 | static const char * const debugfs__known_mountpoints[] = {
|
| 64 | DEBUGFS_DEFAULT_PATH,
|
| 65 | "/debug",
|
| 66 | 0,
|
| 67 | };
|
| 68 |
|
| 69 |
|
| 70 | #ifndef TRACEFS_DEFAULT_PATH
|
| 71 | #define TRACEFS_DEFAULT_PATH "/sys/kernel/tracing"
|
| 72 | #endif
|
| 73 |
|
| 74 | static const char * const tracefs__known_mountpoints[] = {
|
| 75 | TRACEFS_DEFAULT_PATH,
|
| 76 | "/sys/kernel/debug/tracing",
|
| 77 | "/tracing",
|
| 78 | "/trace",
|
| 79 | 0,
|
| 80 | };
|
| 81 |
|
| 82 | static const char * const hugetlbfs__known_mountpoints[] = {
|
| 83 | 0,
|
| 84 | };
|
| 85 |
|
| 86 | static const char * const bpf_fs__known_mountpoints[] = {
|
| 87 | "/sys/fs/bpf",
|
| 88 | 0,
|
| 89 | };
|
| 90 |
|
| 91 | struct fs {
|
| 92 | const char * const name;
|
| 93 | const char * const * const mounts;
|
| 94 | char *path;
|
| 95 | pthread_mutex_t mount_mutex;
|
| 96 | const long magic;
|
| 97 | };
|
| 98 |
|
| 99 | #ifndef TRACEFS_MAGIC
|
| 100 | #define TRACEFS_MAGIC 0x74726163
|
| 101 | #endif
|
| 102 |
|
| 103 | static void fs__init_once(struct fs *fs);
|
| 104 | static const char *fs__mountpoint(const struct fs *fs);
|
| 105 | static const char *fs__mount(struct fs *fs);
|
| 106 |
|
| 107 | #define FS(lower_name, fs_name, upper_name) \
|
| 108 | static struct fs fs__##lower_name = { \
|
| 109 | .name = #fs_name, \
|
| 110 | .mounts = lower_name##__known_mountpoints, \
|
| 111 | .magic = upper_name##_MAGIC, \
|
| 112 | .mount_mutex = PTHREAD_MUTEX_INITIALIZER, \
|
| 113 | }; \
|
| 114 | \
|
| 115 | static void lower_name##_init_once(void) \
|
| 116 | { \
|
| 117 | struct fs *fs = &fs__##lower_name; \
|
| 118 | \
|
| 119 | fs__init_once(fs); \
|
| 120 | } \
|
| 121 | \
|
| 122 | const char *lower_name##__mountpoint(void) \
|
| 123 | { \
|
| 124 | static pthread_once_t init_once = PTHREAD_ONCE_INIT; \
|
| 125 | struct fs *fs = &fs__##lower_name; \
|
| 126 | \
|
| 127 | pthread_once(&init_once, lower_name##_init_once); \
|
| 128 | return fs__mountpoint(fs); \
|
| 129 | } \
|
| 130 | \
|
| 131 | const char *lower_name##__mount(void) \
|
| 132 | { \
|
| 133 | const char *mountpoint = lower_name##__mountpoint(); \
|
| 134 | struct fs *fs = &fs__##lower_name; \
|
| 135 | \
|
| 136 | if (mountpoint) \
|
| 137 | return mountpoint; \
|
| 138 | \
|
| 139 | return fs__mount(fs); \
|
| 140 | } \
|
| 141 | \
|
| 142 | bool lower_name##__configured(void) \
|
| 143 | { \
|
| 144 | return lower_name##__mountpoint() != NULL; \
|
| 145 | }
|
| 146 |
|
| 147 | FS(sysfs, sysfs, SYSFS);
|
| 148 | FS(procfs, procfs, PROC_SUPER);
|
| 149 | FS(debugfs, debugfs, DEBUGFS);
|
| 150 | FS(tracefs, tracefs, TRACEFS);
|
| 151 | FS(hugetlbfs, hugetlbfs, HUGETLBFS);
|
| 152 | FS(bpf_fs, bpf, BPF_FS);
|
| 153 |
|
| 154 | static bool fs__read_mounts(struct fs *fs)
|
| 155 | {
|
| 156 | char type[100];
|
| 157 | FILE *fp;
|
| 158 | char path[PATH_MAX + 1];
|
| 159 |
|
| 160 | fp = fopen("/proc/mounts", "r");
|
| 161 | if (fp == NULL)
|
| 162 | return false;
|
| 163 |
|
| 164 | while (fscanf(fp, "%*s %" STR(PATH_MAX) "s %99s %*s %*d %*d\n",
|
| 165 | path, type) == 2) {
|
| 166 |
|
| 167 | if (strcmp(type, fs->name) == 0) {
|
| 168 | fs->path = strdup(path);
|
| 169 | fclose(fp);
|
| 170 | return fs->path != NULL;
|
| 171 | }
|
| 172 | }
|
| 173 | fclose(fp);
|
| 174 | return false;
|
| 175 | }
|
| 176 |
|
| 177 | static int fs__valid_mount(const char *fs, long magic)
|
| 178 | {
|
| 179 | struct statfs st_fs;
|
| 180 |
|
| 181 | if (statfs(fs, &st_fs) < 0)
|
| 182 | return -ENOENT;
|
| 183 | else if ((long)st_fs.f_type != magic)
|
| 184 | return -ENOENT;
|
| 185 |
|
| 186 | return 0;
|
| 187 | }
|
| 188 |
|
| 189 | static bool fs__check_mounts(struct fs *fs)
|
| 190 | {
|
| 191 | const char * const *ptr;
|
| 192 |
|
| 193 | ptr = fs->mounts;
|
| 194 | while (*ptr) {
|
| 195 | if (fs__valid_mount(*ptr, fs->magic) == 0) {
|
| 196 | fs->path = strdup(*ptr);
|
| 197 | if (!fs->path)
|
| 198 | return false;
|
| 199 | return true;
|
| 200 | }
|
| 201 | ptr++;
|
| 202 | }
|
| 203 |
|
| 204 | return false;
|
| 205 | }
|
| 206 |
|
| 207 | static void mem_toupper(char *f, size_t len)
|
| 208 | {
|
| 209 | while (len) {
|
| 210 | *f = toupper(*f);
|
| 211 | f++;
|
| 212 | len--;
|
| 213 | }
|
| 214 | }
|
| 215 |
|
| 216 | |
| 217 | * Check for "NAME_PATH" environment variable to override fs location (for
|
| 218 | * testing). This matches the recommendation in Documentation/admin-guide/sysfs-rules.rst
|
| 219 | * for SYSFS_PATH.
|
| 220 |
|
| 221 | static bool fs__env_override(struct fs *fs)
|
| 222 | {
|
| 223 | char *override_path;
|
| 224 | size_t name_len = strlen(fs->name);
|
| 225 |
|
| 226 | char upper_name[name_len + 5 + 1];
|
| 227 |
|
| 228 | memcpy(upper_name, fs->name, name_len);
|
| 229 | mem_toupper(upper_name, name_len);
|
| 230 | strcpy(&upper_name[name_len], "_PATH");
|
| 231 |
|
| 232 | override_path = getenv(upper_name);
|
| 233 | if (!override_path)
|
| 234 | return false;
|
| 235 |
|
| 236 | fs->path = strdup(override_path);
|
| 237 | if (!fs->path)
|
| 238 | return false;
|
| 239 | return true;
|
| 240 | }
|
| 241 |
|
| 242 | static void fs__init_once(struct fs *fs)
|
| 243 | {
|
| 244 | if (!fs__env_override(fs) &&
|
| 245 | !fs__check_mounts(fs) &&
|
| 246 | !fs__read_mounts(fs)) {
|
| 247 | assert(!fs->path);
|
| 248 | } else {
|
| 249 | assert(fs->path);
|
| 250 | }
|
| 251 | }
|
| 252 |
|
| 253 | static const char *fs__mountpoint(const struct fs *fs)
|
| 254 | {
|
| 255 | return fs->path;
|
| 256 | }
|
| 257 |
|
| 258 | static const char *mount_overload(struct fs *fs)
|
| 259 | {
|
| 260 | size_t name_len = strlen(fs->name);
|
| 261 |
|
| 262 | char upper_name[5 + name_len + 12 + 1];
|
| 263 |
|
| 264 | snprintf(upper_name, sizeof(upper_name), "PERF_%s_ENVIRONMENT", fs->name);
|
| 265 | mem_toupper(upper_name, strlen(upper_name));
|
| 266 |
|
| 267 | return getenv(upper_name) ?: *fs->mounts;
|
| 268 | }
|
| 269 |
|
| 270 | static const char *fs__mount(struct fs *fs)
|
| 271 | {
|
| 272 | const char *mountpoint;
|
| 273 |
|
| 274 | pthread_mutex_lock(&fs->mount_mutex);
|
| 275 |
|
| 276 |
|
| 277 | mountpoint = fs__mountpoint(fs);
|
| 278 | if (mountpoint)
|
| 279 | goto out;
|
| 280 |
|
| 281 | mountpoint = mount_overload(fs);
|
| 282 |
|
| 283 | if (mount(NULL, mountpoint, fs->name, 0, NULL) == 0 &&
|
| 284 | fs__valid_mount(mountpoint, fs->magic) == 0) {
|
| 285 | fs->path = strdup(mountpoint);
|
| 286 | mountpoint = fs->path;
|
| 287 | }
|
| 288 | out:
|
| 289 | pthread_mutex_unlock(&fs->mount_mutex);
|
| 290 | return mountpoint;
|
| 291 | }
|
| 292 |
|
| 293 | int filename__read_int(const char *filename, int *value)
|
| 294 | {
|
| 295 | char line[64];
|
| 296 | int fd = open(filename, O_RDONLY), err = -1;
|
| 297 | ssize_t n;
|
| 298 |
|
| 299 | if (fd < 0)
|
| 300 | return -errno;
|
| 301 |
|
| 302 | n = read(fd, line, sizeof(line) - 1);
|
| 303 | if (n > 0) {
|
| 304 | line[n] = '\0';
|
| 305 | *value = atoi(line);
|
| 306 | err = 0;
|
| 307 | }
|
| 308 |
|
| 309 | close(fd);
|
| 310 | return err;
|
| 311 | }
|
| 312 |
|
| 313 | static int filename__read_ull_base(const char *filename,
|
| 314 | unsigned long long *value, int base)
|
| 315 | {
|
| 316 | char line[64];
|
| 317 | int fd = open(filename, O_RDONLY), err = -1;
|
| 318 | ssize_t n;
|
| 319 |
|
| 320 | if (fd < 0)
|
| 321 | return -errno;
|
| 322 |
|
| 323 | n = read(fd, line, sizeof(line) - 1);
|
| 324 | if (n > 0) {
|
| 325 | line[n] = '\0';
|
| 326 | *value = strtoull(line, NULL, base);
|
| 327 | if (*value != ULLONG_MAX)
|
| 328 | err = 0;
|
| 329 | }
|
| 330 |
|
| 331 | close(fd);
|
| 332 | return err;
|
| 333 | }
|
| 334 |
|
| 335 | |
| 336 | * Parses @value out of @filename with strtoull.
|
| 337 | * By using 16 for base to treat the number as hex.
|
| 338 |
|
| 339 | int filename__read_xll(const char *filename, unsigned long long *value)
|
| 340 | {
|
| 341 | return filename__read_ull_base(filename, value, 16);
|
| 342 | }
|
| 343 |
|
| 344 | |
| 345 | * Parses @value out of @filename with strtoull.
|
| 346 | * By using 0 for base, the strtoull detects the
|
| 347 | * base automatically (see man strtoull).
|
| 348 |
|
| 349 | int filename__read_ull(const char *filename, unsigned long long *value)
|
| 350 | {
|
| 351 | return filename__read_ull_base(filename, value, 0);
|
| 352 | }
|
| 353 |
|
| 354 | int filename__read_str(const char *filename, char **buf, size_t *sizep)
|
| 355 | {
|
| 356 | struct io io;
|
| 357 | char bf[128];
|
| 358 | int err;
|
| 359 |
|
| 360 | io.fd = open(filename, O_RDONLY);
|
| 361 | if (io.fd < 0)
|
| 362 | return -errno;
|
| 363 | io__init(&io, io.fd, bf, sizeof(bf));
|
| 364 | *buf = NULL;
|
| 365 | err = io__getdelim(&io, buf, sizep, -1);
|
| 366 | if (err < 0) {
|
| 367 | free(*buf);
|
| 368 | *buf = NULL;
|
| 369 | } else
|
| 370 | err = 0;
|
| 371 | close(io.fd);
|
| 372 | return err;
|
| 373 | }
|
| 374 |
|
| 375 | int filename__write_int(const char *filename, int value)
|
| 376 | {
|
| 377 | int fd = open(filename, O_WRONLY), err = -1;
|
| 378 | char buf[64];
|
| 379 | int len;
|
| 380 |
|
| 381 | if (fd < 0)
|
| 382 | return -errno;
|
| 383 |
|
| 384 | len = sprintf(buf, "%d", value);
|
| 385 | if (write(fd, buf, len) == len)
|
| 386 | err = 0;
|
| 387 |
|
| 388 | close(fd);
|
| 389 | return err;
|
| 390 | }
|
| 391 |
|
| 392 | int procfs__read_str(const char *entry, char **buf, size_t *sizep)
|
| 393 | {
|
| 394 | char path[PATH_MAX];
|
| 395 | const char *procfs = procfs__mountpoint();
|
| 396 |
|
| 397 | if (!procfs)
|
| 398 | return -1;
|
| 399 |
|
| 400 | snprintf(path, sizeof(path), "%s/%s", procfs, entry);
|
| 401 |
|
| 402 | return filename__read_str(path, buf, sizep);
|
| 403 | }
|
| 404 |
|
| 405 | static int sysfs__read_ull_base(const char *entry,
|
| 406 | unsigned long long *value, int base)
|
| 407 | {
|
| 408 | char path[PATH_MAX];
|
| 409 | const char *sysfs = sysfs__mountpoint();
|
| 410 |
|
| 411 | if (!sysfs)
|
| 412 | return -1;
|
| 413 |
|
| 414 | snprintf(path, sizeof(path), "%s/%s", sysfs, entry);
|
| 415 |
|
| 416 | return filename__read_ull_base(path, value, base);
|
| 417 | }
|
| 418 |
|
| 419 | int sysfs__read_xll(const char *entry, unsigned long long *value)
|
| 420 | {
|
| 421 | return sysfs__read_ull_base(entry, value, 16);
|
| 422 | }
|
| 423 |
|
| 424 | int sysfs__read_ull(const char *entry, unsigned long long *value)
|
| 425 | {
|
| 426 | return sysfs__read_ull_base(entry, value, 0);
|
| 427 | }
|
| 428 |
|
| 429 | int sysfs__read_int(const char *entry, int *value)
|
| 430 | {
|
| 431 | char path[PATH_MAX];
|
| 432 | const char *sysfs = sysfs__mountpoint();
|
| 433 |
|
| 434 | if (!sysfs)
|
| 435 | return -1;
|
| 436 |
|
| 437 | snprintf(path, sizeof(path), "%s/%s", sysfs, entry);
|
| 438 |
|
| 439 | return filename__read_int(path, value);
|
| 440 | }
|
| 441 |
|
| 442 | int sysfs__read_str(const char *entry, char **buf, size_t *sizep)
|
| 443 | {
|
| 444 | char path[PATH_MAX];
|
| 445 | const char *sysfs = sysfs__mountpoint();
|
| 446 |
|
| 447 | if (!sysfs)
|
| 448 | return -1;
|
| 449 |
|
| 450 | snprintf(path, sizeof(path), "%s/%s", sysfs, entry);
|
| 451 |
|
| 452 | return filename__read_str(path, buf, sizep);
|
| 453 | }
|
| 454 |
|
| 455 | int sysfs__read_bool(const char *entry, bool *value)
|
| 456 | {
|
| 457 | struct io io;
|
| 458 | char bf[16];
|
| 459 | int ret = 0;
|
| 460 | char path[PATH_MAX];
|
| 461 | const char *sysfs = sysfs__mountpoint();
|
| 462 |
|
| 463 | if (!sysfs)
|
| 464 | return -1;
|
| 465 |
|
| 466 | snprintf(path, sizeof(path), "%s/%s", sysfs, entry);
|
| 467 | io.fd = open(path, O_RDONLY);
|
| 468 | if (io.fd < 0)
|
| 469 | return -errno;
|
| 470 |
|
| 471 | io__init(&io, io.fd, bf, sizeof(bf));
|
| 472 | switch (io__get_char(&io)) {
|
| 473 | case '1':
|
| 474 | case 'y':
|
| 475 | case 'Y':
|
| 476 | *value = true;
|
| 477 | break;
|
| 478 | case '0':
|
| 479 | case 'n':
|
| 480 | case 'N':
|
| 481 | *value = false;
|
| 482 | break;
|
| 483 | default:
|
| 484 | ret = -1;
|
| 485 | }
|
| 486 | close(io.fd);
|
| 487 |
|
| 488 | return ret;
|
| 489 | }
|
| 490 | int sysctl__read_int(const char *sysctl, int *value)
|
| 491 | {
|
| 492 | char path[PATH_MAX];
|
| 493 | const char *procfs = procfs__mountpoint();
|
| 494 |
|
| 495 | if (!procfs)
|
| 496 | return -1;
|
| 497 |
|
| 498 | snprintf(path, sizeof(path), "%s/sys/%s", procfs, sysctl);
|
| 499 |
|
| 500 | return filename__read_int(path, value);
|
| 501 | }
|
| 502 |
|
| 503 | int sysfs__write_int(const char *entry, int value)
|
| 504 | {
|
| 505 | char path[PATH_MAX];
|
| 506 | const char *sysfs = sysfs__mountpoint();
|
| 507 |
|
| 508 | if (!sysfs)
|
| 509 | return -1;
|
| 510 |
|
| 511 | if (snprintf(path, sizeof(path), "%s/%s", sysfs, entry) >= PATH_MAX)
|
| 512 | return -1;
|
| 513 |
|
| 514 | return filename__write_int(path, value);
|
| 515 | }
|