2019-05-29 00:57:18 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-only
|
2015-11-24 10:05:38 +08:00
|
|
|
/*
|
|
|
|
* Copyright 2013-2015, Michael Ellerman, IBM Corp.
|
|
|
|
*/
|
|
|
|
|
2015-12-16 15:59:31 +08:00
|
|
|
#define _GNU_SOURCE /* For CPU_ZERO etc. */
|
|
|
|
|
2015-11-24 10:05:38 +08:00
|
|
|
#include <elf.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <link.h>
|
2015-12-16 15:59:31 +08:00
|
|
|
#include <sched.h>
|
2015-11-24 10:05:38 +08:00
|
|
|
#include <stdio.h>
|
2018-05-21 23:13:57 +08:00
|
|
|
#include <stdlib.h>
|
2018-07-26 20:24:57 +08:00
|
|
|
#include <string.h>
|
2018-05-21 23:13:57 +08:00
|
|
|
#include <sys/ioctl.h>
|
2015-11-24 10:05:38 +08:00
|
|
|
#include <sys/stat.h>
|
2020-07-30 13:08:46 +08:00
|
|
|
#include <sys/sysinfo.h>
|
2015-11-24 10:05:38 +08:00
|
|
|
#include <sys/types.h>
|
2018-07-26 20:24:57 +08:00
|
|
|
#include <sys/utsname.h>
|
2015-11-24 10:05:38 +08:00
|
|
|
#include <unistd.h>
|
2018-05-21 23:13:57 +08:00
|
|
|
#include <asm/unistd.h>
|
|
|
|
#include <linux/limits.h>
|
2015-11-24 10:05:38 +08:00
|
|
|
|
|
|
|
#include "utils.h"
|
|
|
|
|
|
|
|
static char auxv[4096];
|
|
|
|
|
2017-02-06 18:13:27 +08:00
|
|
|
int read_auxv(char *buf, ssize_t buf_size)
|
2015-11-24 10:05:38 +08:00
|
|
|
{
|
|
|
|
ssize_t num;
|
2017-02-06 18:13:27 +08:00
|
|
|
int rc, fd;
|
2015-11-24 10:05:38 +08:00
|
|
|
|
|
|
|
fd = open("/proc/self/auxv", O_RDONLY);
|
|
|
|
if (fd == -1) {
|
|
|
|
perror("open");
|
2017-02-06 18:13:27 +08:00
|
|
|
return -errno;
|
2015-11-24 10:05:38 +08:00
|
|
|
}
|
|
|
|
|
2017-02-06 18:13:27 +08:00
|
|
|
num = read(fd, buf, buf_size);
|
2015-11-24 10:05:38 +08:00
|
|
|
if (num < 0) {
|
|
|
|
perror("read");
|
2017-02-06 18:13:27 +08:00
|
|
|
rc = -EIO;
|
2015-11-24 10:05:38 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2017-02-06 18:13:27 +08:00
|
|
|
if (num > buf_size) {
|
|
|
|
printf("overflowed auxv buffer\n");
|
|
|
|
rc = -EOVERFLOW;
|
2015-11-24 10:05:38 +08:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2017-02-06 18:13:27 +08:00
|
|
|
rc = 0;
|
|
|
|
out:
|
|
|
|
close(fd);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *find_auxv_entry(int type, char *auxv)
|
|
|
|
{
|
|
|
|
ElfW(auxv_t) *p;
|
|
|
|
|
2015-11-24 10:05:38 +08:00
|
|
|
p = (ElfW(auxv_t) *)auxv;
|
|
|
|
|
|
|
|
while (p->a_type != AT_NULL) {
|
2017-02-06 18:13:27 +08:00
|
|
|
if (p->a_type == type)
|
|
|
|
return p;
|
2015-11-24 10:05:38 +08:00
|
|
|
|
|
|
|
p++;
|
|
|
|
}
|
2017-02-06 18:13:27 +08:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *get_auxv_entry(int type)
|
|
|
|
{
|
|
|
|
ElfW(auxv_t) *p;
|
|
|
|
|
|
|
|
if (read_auxv(auxv, sizeof(auxv)))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
p = find_auxv_entry(type, auxv);
|
|
|
|
if (p)
|
|
|
|
return (void *)p->a_un.a_val;
|
|
|
|
|
|
|
|
return NULL;
|
2015-11-24 10:05:38 +08:00
|
|
|
}
|
2015-12-16 15:59:31 +08:00
|
|
|
|
|
|
|
int pick_online_cpu(void)
|
|
|
|
{
|
2020-07-30 13:08:46 +08:00
|
|
|
int ncpus, cpu = -1;
|
|
|
|
cpu_set_t *mask;
|
|
|
|
size_t size;
|
|
|
|
|
|
|
|
ncpus = get_nprocs_conf();
|
|
|
|
size = CPU_ALLOC_SIZE(ncpus);
|
|
|
|
mask = CPU_ALLOC(ncpus);
|
|
|
|
if (!mask) {
|
|
|
|
perror("malloc");
|
|
|
|
return -1;
|
|
|
|
}
|
2015-12-16 15:59:31 +08:00
|
|
|
|
2020-07-30 13:08:46 +08:00
|
|
|
CPU_ZERO_S(size, mask);
|
2015-12-16 15:59:31 +08:00
|
|
|
|
2020-07-30 13:08:46 +08:00
|
|
|
if (sched_getaffinity(0, size, mask)) {
|
2015-12-16 15:59:31 +08:00
|
|
|
perror("sched_getaffinity");
|
2020-07-30 13:08:46 +08:00
|
|
|
goto done;
|
2015-12-16 15:59:31 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* We prefer a primary thread, but skip 0 */
|
2020-07-30 13:08:46 +08:00
|
|
|
for (cpu = 8; cpu < ncpus; cpu += 8)
|
|
|
|
if (CPU_ISSET_S(cpu, size, mask))
|
|
|
|
goto done;
|
2015-12-16 15:59:31 +08:00
|
|
|
|
|
|
|
/* Search for anything, but in reverse */
|
2020-07-30 13:08:46 +08:00
|
|
|
for (cpu = ncpus - 1; cpu >= 0; cpu--)
|
|
|
|
if (CPU_ISSET_S(cpu, size, mask))
|
|
|
|
goto done;
|
2015-12-16 15:59:31 +08:00
|
|
|
|
|
|
|
printf("No cpus in affinity mask?!\n");
|
2020-07-30 13:08:46 +08:00
|
|
|
|
|
|
|
done:
|
|
|
|
CPU_FREE(mask);
|
|
|
|
return cpu;
|
2015-12-16 15:59:31 +08:00
|
|
|
}
|
2018-07-26 20:24:57 +08:00
|
|
|
|
|
|
|
bool is_ppc64le(void)
|
|
|
|
{
|
|
|
|
struct utsname uts;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
rc = uname(&uts);
|
|
|
|
if (rc) {
|
|
|
|
perror("uname");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return strcmp(uts.machine, "ppc64le") == 0;
|
|
|
|
}
|
2018-05-21 23:13:57 +08:00
|
|
|
|
2019-05-20 18:55:20 +08:00
|
|
|
int read_sysfs_file(char *fpath, char *result, size_t result_size)
|
|
|
|
{
|
|
|
|
char path[PATH_MAX] = "/sys/";
|
|
|
|
int rc = -1, fd;
|
|
|
|
|
|
|
|
strncat(path, fpath, PATH_MAX - strlen(path) - 1);
|
|
|
|
|
|
|
|
if ((fd = open(path, O_RDONLY)) < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
rc = read(fd, result, result_size);
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
if (rc < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-05-21 23:13:57 +08:00
|
|
|
int read_debugfs_file(char *debugfs_file, int *result)
|
|
|
|
{
|
|
|
|
int rc = -1, fd;
|
|
|
|
char path[PATH_MAX];
|
|
|
|
char value[16];
|
|
|
|
|
|
|
|
strcpy(path, "/sys/kernel/debug/");
|
|
|
|
strncat(path, debugfs_file, PATH_MAX - strlen(path) - 1);
|
|
|
|
|
|
|
|
if ((fd = open(path, O_RDONLY)) < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
if ((rc = read(fd, value, sizeof(value))) < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
value[15] = 0;
|
|
|
|
*result = atoi(value);
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int write_debugfs_file(char *debugfs_file, int result)
|
|
|
|
{
|
|
|
|
int rc = -1, fd;
|
|
|
|
char path[PATH_MAX];
|
|
|
|
char value[16];
|
|
|
|
|
|
|
|
strcpy(path, "/sys/kernel/debug/");
|
|
|
|
strncat(path, debugfs_file, PATH_MAX - strlen(path) - 1);
|
|
|
|
|
|
|
|
if ((fd = open(path, O_WRONLY)) < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
snprintf(value, 16, "%d", result);
|
|
|
|
|
|
|
|
if ((rc = write(fd, value, strlen(value))) < 0)
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
close(fd);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
|
|
|
|
int cpu, int group_fd, unsigned long flags)
|
|
|
|
{
|
|
|
|
return syscall(__NR_perf_event_open, hw_event, pid, cpu,
|
|
|
|
group_fd, flags);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void perf_event_attr_init(struct perf_event_attr *event_attr,
|
|
|
|
unsigned int type,
|
|
|
|
unsigned long config)
|
|
|
|
{
|
|
|
|
memset(event_attr, 0, sizeof(*event_attr));
|
|
|
|
|
|
|
|
event_attr->type = type;
|
|
|
|
event_attr->size = sizeof(struct perf_event_attr);
|
|
|
|
event_attr->config = config;
|
|
|
|
event_attr->read_format = PERF_FORMAT_GROUP;
|
|
|
|
event_attr->disabled = 1;
|
|
|
|
event_attr->exclude_kernel = 1;
|
|
|
|
event_attr->exclude_hv = 1;
|
|
|
|
event_attr->exclude_guest = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int perf_event_open_counter(unsigned int type,
|
|
|
|
unsigned long config, int group_fd)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
struct perf_event_attr event_attr;
|
|
|
|
|
|
|
|
perf_event_attr_init(&event_attr, type, config);
|
|
|
|
|
|
|
|
fd = perf_event_open(&event_attr, 0, -1, group_fd, 0);
|
|
|
|
|
|
|
|
if (fd < 0)
|
|
|
|
perror("perf_event_open() failed");
|
|
|
|
|
|
|
|
return fd;
|
|
|
|
}
|
|
|
|
|
|
|
|
int perf_event_enable(int fd)
|
|
|
|
{
|
|
|
|
if (ioctl(fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) {
|
|
|
|
perror("error while enabling perf events");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int perf_event_disable(int fd)
|
|
|
|
{
|
|
|
|
if (ioctl(fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) {
|
|
|
|
perror("error disabling perf events");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int perf_event_reset(int fd)
|
|
|
|
{
|
|
|
|
if (ioctl(fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1) {
|
|
|
|
perror("error resetting perf events");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-06-04 20:56:09 +08:00
|
|
|
int using_hash_mmu(bool *using_hash)
|
|
|
|
{
|
|
|
|
char line[128];
|
|
|
|
FILE *f;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
f = fopen("/proc/cpuinfo", "r");
|
|
|
|
FAIL_IF(!f);
|
|
|
|
|
|
|
|
rc = 0;
|
|
|
|
while (fgets(line, sizeof(line), f) != NULL) {
|
2020-08-19 09:57:19 +08:00
|
|
|
if (!strcmp(line, "MMU : Hash\n") ||
|
|
|
|
!strcmp(line, "platform : Cell\n") ||
|
|
|
|
!strcmp(line, "platform : PowerMac\n")) {
|
2020-06-04 20:56:09 +08:00
|
|
|
*using_hash = true;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(line, "MMU : Radix\n") == 0) {
|
|
|
|
*using_hash = false;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rc = -1;
|
|
|
|
out:
|
|
|
|
fclose(f);
|
|
|
|
return rc;
|
|
|
|
}
|