summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-07-27 13:25:11 +0100
committerMark Brown <broonie@kernel.org>2026-07-27 13:25:11 +0100
commitfed738703be77b2ff979335d0a8dab4efb59bc64 (patch)
treec107a9507433e06ffc231a847cdf3e6ce1b09fec /tools
parent1432683838d9122c62bf4a0fa139fcf26968911e (diff)
parent5bff6e212ff5715e86f51c6038ec5b1548f0d8a3 (diff)
downloadlinux-next-fed738703be77b2ff979335d0a8dab4efb59bc64.tar.gz
linux-next-fed738703be77b2ff979335d0a8dab4efb59bc64.zip
Merge branch 'mm-nonmm-unstable' of https://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
Diffstat (limited to 'tools')
-rw-r--r--tools/accounting/delaytop.c288
-rw-r--r--tools/accounting/getdelays.c37
-rw-r--r--tools/include/linux/compiler.h2
-rw-r--r--tools/testing/selftests/acct/.gitignore1
-rw-r--r--tools/testing/selftests/acct/Makefile11
-rw-r--r--tools/testing/selftests/acct/cgroupstats.c231
-rw-r--r--tools/testing/selftests/acct/netlink_helper.c116
-rw-r--r--tools/testing/selftests/acct/netlink_helper.h44
-rw-r--r--tools/testing/selftests/acct/taskstats_fill_stats_tgid.c134
-rw-r--r--tools/testing/selftests/ipc/msgque.c2
10 files changed, 683 insertions, 183 deletions
diff --git a/tools/accounting/delaytop.c b/tools/accounting/delaytop.c
index 72cc500b44b1..af8a1e42a055 100644
--- a/tools/accounting/delaytop.c
+++ b/tools/accounting/delaytop.c
@@ -75,13 +75,23 @@
{#name, #cmd, \
offsetof(struct task_info, name##_delay_total), \
offsetof(struct task_info, name##_count), \
+ offsetof(struct task_info, name##_delay_max), \
+ offsetof(struct task_info, name##_delay_max_ts), \
modes}
-#define END_FIELD {NULL, 0, 0}
+#define SORT_FIELD_NO_MAX(name, cmd, modes) \
+ {#name, #cmd, \
+ offsetof(struct task_info, name##_delay_total), \
+ offsetof(struct task_info, name##_count), \
+ 0, \
+ 0, \
+ modes}
+#define END_FIELD {NULL, 0, 0, 0, 0, 0, 0}
/* Display mode types */
#define MODE_TYPE_ALL (0xFFFFFFFF)
#define MODE_DEFAULT (1 << 0)
#define MODE_MEMVERBOSE (1 << 1)
+#define MODE_TYPE (1 << 2) /* Display specific type with avg/max */
/* PSI statistics structure */
struct psi_stats {
@@ -108,20 +118,36 @@ struct task_info {
char command[TASK_COMM_LEN];
unsigned long long cpu_count;
unsigned long long cpu_delay_total;
+ unsigned long long cpu_delay_max;
+ struct __kernel_timespec cpu_delay_max_ts;
unsigned long long blkio_count;
unsigned long long blkio_delay_total;
+ unsigned long long blkio_delay_max;
+ struct __kernel_timespec blkio_delay_max_ts;
unsigned long long swapin_count;
unsigned long long swapin_delay_total;
+ unsigned long long swapin_delay_max;
+ struct __kernel_timespec swapin_delay_max_ts;
unsigned long long freepages_count;
unsigned long long freepages_delay_total;
+ unsigned long long freepages_delay_max;
+ struct __kernel_timespec freepages_delay_max_ts;
unsigned long long thrashing_count;
unsigned long long thrashing_delay_total;
+ unsigned long long thrashing_delay_max;
+ struct __kernel_timespec thrashing_delay_max_ts;
unsigned long long compact_count;
unsigned long long compact_delay_total;
+ unsigned long long compact_delay_max;
+ struct __kernel_timespec compact_delay_max_ts;
unsigned long long wpcopy_count;
unsigned long long wpcopy_delay_total;
+ unsigned long long wpcopy_delay_max;
+ struct __kernel_timespec wpcopy_delay_max_ts;
unsigned long long irq_count;
unsigned long long irq_delay_total;
+ unsigned long long irq_delay_max;
+ struct __kernel_timespec irq_delay_max_ts;
unsigned long long mem_count;
unsigned long long mem_delay_total;
};
@@ -141,6 +167,8 @@ struct field_desc {
const char *cmd_char; /* Interactive command */
unsigned long total_offset; /* Offset of total delay in task_info */
unsigned long count_offset; /* Offset of count in task_info */
+ unsigned long max_offset; /* Offset of max delay in task_info */
+ unsigned long max_ts_offset; /* Offset of max delay timestamp in task_info */
size_t supported_modes; /* Supported display modes */
};
@@ -153,6 +181,7 @@ struct config {
int monitor_pid; /* Monitor specific PID */
char *container_path; /* Path to container cgroup */
const struct field_desc *sort_field; /* Current sort field */
+ const struct field_desc *type_field; /* Type field for -t option */
size_t display_mode; /* Current display mode */
};
@@ -164,15 +193,15 @@ static int task_count;
static int running = 1;
static struct container_stats container_stats;
static const struct field_desc sort_fields[] = {
- SORT_FIELD(cpu, c, MODE_DEFAULT),
- SORT_FIELD(blkio, i, MODE_DEFAULT),
- SORT_FIELD(irq, q, MODE_DEFAULT),
- SORT_FIELD(mem, m, MODE_DEFAULT | MODE_MEMVERBOSE),
- SORT_FIELD(swapin, s, MODE_MEMVERBOSE),
- SORT_FIELD(freepages, r, MODE_MEMVERBOSE),
- SORT_FIELD(thrashing, t, MODE_MEMVERBOSE),
- SORT_FIELD(compact, p, MODE_MEMVERBOSE),
- SORT_FIELD(wpcopy, w, MODE_MEMVERBOSE),
+ SORT_FIELD(cpu, c, MODE_DEFAULT | MODE_TYPE),
+ SORT_FIELD(blkio, i, MODE_DEFAULT | MODE_TYPE),
+ SORT_FIELD(irq, q, MODE_DEFAULT | MODE_TYPE),
+ SORT_FIELD_NO_MAX(mem, m, MODE_DEFAULT | MODE_MEMVERBOSE),
+ SORT_FIELD(swapin, s, MODE_MEMVERBOSE | MODE_TYPE),
+ SORT_FIELD(freepages, r, MODE_MEMVERBOSE | MODE_TYPE),
+ SORT_FIELD(thrashing, t, MODE_MEMVERBOSE | MODE_TYPE),
+ SORT_FIELD(compact, p, MODE_MEMVERBOSE | MODE_TYPE),
+ SORT_FIELD(wpcopy, w, MODE_MEMVERBOSE | MODE_TYPE),
END_FIELD
};
static int sort_selected;
@@ -265,6 +294,8 @@ static void usage(void)
" -p, --pid=PID Monitor only the specified PID\n"
" -C, --container=PATH Monitor the container at specified cgroup path\n"
" -s, --sort=FIELD Sort by delay field (default: cpu)\n"
+ " -t, --type=FIELD Display only specified delay type with avg/max/timestamp\n"
+ " (rows sorted by MAX for that type, largest first)\n"
" -M, --memverbose Display memory detailed information\n");
exit(0);
}
@@ -283,6 +314,7 @@ static void parse_args(int argc, char **argv)
{"processes", required_argument, 0, 'P'},
{"sort", required_argument, 0, 's'},
{"container", required_argument, 0, 'C'},
+ {"type", required_argument, 0, 't'},
{"memverbose", no_argument, 0, 'M'},
{0, 0, 0, 0}
};
@@ -292,6 +324,7 @@ static void parse_args(int argc, char **argv)
cfg.iterations = 0;
cfg.max_processes = 20;
cfg.sort_field = &sort_fields[0]; /* Default sorted by CPU delay */
+ cfg.type_field = NULL; /* No type field by default */
cfg.output_one_time = 0;
cfg.monitor_pid = 0; /* 0 means monitor all PIDs */
cfg.container_path = NULL;
@@ -300,7 +333,7 @@ static void parse_args(int argc, char **argv)
while (1) {
int option_index = 0;
- c = getopt_long(argc, argv, "hd:n:p:oP:C:s:M", long_options, &option_index);
+ c = getopt_long(argc, argv, "hd:n:p:oP:C:s:t:M", long_options, &option_index);
if (c == -1)
break;
@@ -363,9 +396,32 @@ static void parse_args(int argc, char **argv)
cfg.sort_field = field;
break;
+ case 't':
+ if (strlen(optarg) == 0) {
+ fprintf(stderr, "Error: empty type field\n");
+ exit(1);
+ }
+
+ field = get_field_by_name(optarg);
+ /* Show available fields if invalid option provided */
+ if (!field || !(field->supported_modes & MODE_TYPE)) {
+ fprintf(stderr, "Error: invalid type field '%s'\n", optarg);
+ display_available_fields(MODE_TYPE);
+ exit(1);
+ }
+
+ cfg.type_field = field;
+ cfg.display_mode = MODE_TYPE;
+ break;
case 'M':
cfg.display_mode = MODE_MEMVERBOSE;
- cfg.sort_field = get_field_by_name("mem");
+ /* Find first field supporting MODE_MEMVERBOSE for sorting */
+ for (field = sort_fields; field->name != NULL; field++) {
+ if (field->supported_modes & MODE_MEMVERBOSE) {
+ cfg.sort_field = field;
+ break;
+ }
+ }
break;
default:
fprintf(stderr, "Try 'delaytop --help' for more information.\n");
@@ -690,7 +746,12 @@ static void fetch_and_fill_task_info(int pid, const char *comm)
nested_len = NLA_PAYLOAD(na->nla_len);
while (nested_len > 0) {
if (nested->nla_type == TASKSTATS_TYPE_STATS) {
- memcpy(&stats, NLA_DATA(nested), sizeof(stats));
+ size_t payload_len = NLA_PAYLOAD(nested->nla_len);
+
+ memset(&stats, 0, sizeof(stats));
+ if (payload_len > sizeof(stats))
+ payload_len = sizeof(stats);
+ memcpy(&stats, NLA_DATA(nested), payload_len);
if (task_count < MAX_TASKS) {
tasks[task_count].pid = pid;
tasks[task_count].tgid = pid;
@@ -699,20 +760,36 @@ static void fetch_and_fill_task_info(int pid, const char *comm)
tasks[task_count].command[TASK_COMM_LEN - 1] = '\0';
SET_TASK_STAT(task_count, cpu_count);
SET_TASK_STAT(task_count, cpu_delay_total);
+ SET_TASK_STAT(task_count, cpu_delay_max);
+ SET_TASK_STAT(task_count, cpu_delay_max_ts);
SET_TASK_STAT(task_count, blkio_count);
SET_TASK_STAT(task_count, blkio_delay_total);
+ SET_TASK_STAT(task_count, blkio_delay_max);
+ SET_TASK_STAT(task_count, blkio_delay_max_ts);
SET_TASK_STAT(task_count, swapin_count);
SET_TASK_STAT(task_count, swapin_delay_total);
+ SET_TASK_STAT(task_count, swapin_delay_max);
+ SET_TASK_STAT(task_count, swapin_delay_max_ts);
SET_TASK_STAT(task_count, freepages_count);
SET_TASK_STAT(task_count, freepages_delay_total);
+ SET_TASK_STAT(task_count, freepages_delay_max);
+ SET_TASK_STAT(task_count, freepages_delay_max_ts);
SET_TASK_STAT(task_count, thrashing_count);
SET_TASK_STAT(task_count, thrashing_delay_total);
+ SET_TASK_STAT(task_count, thrashing_delay_max);
+ SET_TASK_STAT(task_count, thrashing_delay_max_ts);
SET_TASK_STAT(task_count, compact_count);
SET_TASK_STAT(task_count, compact_delay_total);
+ SET_TASK_STAT(task_count, compact_delay_max);
+ SET_TASK_STAT(task_count, compact_delay_max_ts);
SET_TASK_STAT(task_count, wpcopy_count);
SET_TASK_STAT(task_count, wpcopy_delay_total);
+ SET_TASK_STAT(task_count, wpcopy_delay_max);
+ SET_TASK_STAT(task_count, wpcopy_delay_max_ts);
SET_TASK_STAT(task_count, irq_count);
SET_TASK_STAT(task_count, irq_delay_total);
+ SET_TASK_STAT(task_count, irq_delay_max);
+ SET_TASK_STAT(task_count, irq_delay_max_ts);
set_mem_count(&tasks[task_count]);
set_mem_delay_total(&tasks[task_count]);
task_count++;
@@ -762,6 +839,15 @@ static void get_task_delays(void)
closedir(dir);
}
+static void field_delay_max_and_ts(const struct task_info *task,
+ const struct field_desc *field,
+ unsigned long long *max_ns,
+ struct __kernel_timespec *max_ts);
+static void get_field_delay_values(const struct task_info *task,
+ const struct field_desc *field,
+ double *avg_ms, double *max_ms,
+ struct __kernel_timespec *max_ts);
+
/* Calculate average delay in milliseconds */
static double average_ms(unsigned long long total, unsigned long long count)
{
@@ -770,6 +856,41 @@ static double average_ms(unsigned long long total, unsigned long long count)
return (double)total / 1000000.0 / count;
}
+/*
+ * Format __kernel_timespec to human readable string (YYYY-MM-DDTHH:MM:SS)
+ * Returns formatted string or "N/A" if timestamp is zero
+ */
+static const char *format_kernel_timespec(struct __kernel_timespec *ts)
+{
+ static char buffer[32];
+ time_t time_sec;
+ struct tm tm_info;
+
+ /* Check if timestamp is zero (not set) */
+ if (ts->tv_sec == 0 && ts->tv_nsec == 0)
+ return "N/A";
+
+ /* Avoid Y2038 truncation: check if timestamp fits in time_t on 32-bit platforms */
+ if (sizeof(time_t) < sizeof(ts->tv_sec) &&
+ ts->tv_sec > (__u64)((1ULL << (sizeof(time_t) * 8 - 1)) - 1))
+ return "N/A";
+
+ time_sec = (time_t)ts->tv_sec;
+
+ if (localtime_r(&time_sec, &tm_info) == NULL)
+ return "N/A";
+
+ snprintf(buffer, sizeof(buffer), "%04d-%02d-%02dT%02d:%02d:%02d",
+ tm_info.tm_year + 1900,
+ tm_info.tm_mon + 1,
+ tm_info.tm_mday,
+ tm_info.tm_hour,
+ tm_info.tm_min,
+ tm_info.tm_sec);
+
+ return buffer;
+}
+
/* Comparison function for sorting tasks */
static int compare_tasks(const void *a, const void *b)
{
@@ -777,14 +898,24 @@ static int compare_tasks(const void *a, const void *b)
const struct task_info *t2 = (const struct task_info *)b;
unsigned long long total1;
unsigned long long total2;
- unsigned long count1;
- unsigned long count2;
+ unsigned long long count1;
+ unsigned long long count2;
double avg1, avg2;
+ unsigned long long max1, max2;
+
+ /* -t/--type: default sort by MAX column for the selected type (descending) */
+ if (cfg.display_mode == MODE_TYPE && cfg.type_field) {
+ field_delay_max_and_ts(t1, cfg.type_field, &max1, NULL);
+ field_delay_max_and_ts(t2, cfg.type_field, &max2, NULL);
+ if (max1 != max2)
+ return max2 > max1 ? 1 : -1;
+ return 0;
+ }
total1 = *(unsigned long long *)((char *)t1 + cfg.sort_field->total_offset);
total2 = *(unsigned long long *)((char *)t2 + cfg.sort_field->total_offset);
- count1 = *(unsigned long *)((char *)t1 + cfg.sort_field->count_offset);
- count2 = *(unsigned long *)((char *)t2 + cfg.sort_field->count_offset);
+ count1 = *(unsigned long long *)((char *)t1 + cfg.sort_field->count_offset);
+ count2 = *(unsigned long long *)((char *)t2 + cfg.sort_field->count_offset);
avg1 = average_ms(total1, count1);
avg2 = average_ms(total2, count2);
@@ -794,6 +925,50 @@ static int compare_tasks(const void *a, const void *b)
return 0;
}
+/* Max delay (ns) and timestamp for field (shared by display and sort) */
+static void field_delay_max_and_ts(const struct task_info *task, const struct field_desc *field,
+ unsigned long long *max_ns, struct __kernel_timespec *max_ts)
+{
+ if (!field || !field->max_offset) {
+ *max_ns = 0;
+ if (max_ts)
+ memset(max_ts, 0, sizeof(*max_ts));
+ return;
+ }
+
+ *max_ns = *(unsigned long long *)((char *)task + field->max_offset);
+
+ if (max_ts) {
+ if (field->max_ts_offset)
+ *max_ts = *(struct __kernel_timespec *)((char *)task +
+ field->max_ts_offset);
+ else
+ memset(max_ts, 0, sizeof(*max_ts));
+ }
+}
+
+/* Get delay values for a specific field */
+static void get_field_delay_values(const struct task_info *task, const struct field_desc *field,
+ double *avg_ms, double *max_ms,
+ struct __kernel_timespec *max_ts)
+{
+ unsigned long long total, count, max;
+
+ if (!field || !field->max_offset) {
+ *avg_ms = 0;
+ *max_ms = 0;
+ memset(max_ts, 0, sizeof(*max_ts));
+ return;
+ }
+
+ total = *(unsigned long long *)((char *)task + field->total_offset);
+ count = *(unsigned long long *)((char *)task + field->count_offset);
+ *avg_ms = average_ms(total, count);
+
+ field_delay_max_and_ts(task, field, &max, max_ts);
+ *max_ms = (double)max / 1000000.0; /* Convert nanoseconds to milliseconds */
+}
+
/* Sort tasks by selected field */
static void sort_tasks(void)
{
@@ -847,7 +1022,12 @@ static void get_container_stats(void)
while (nl_len > 0) {
if (na->nla_type == CGROUPSTATS_TYPE_CGROUP_STATS) {
/* Get the cgroupstats structure */
- memcpy(&stats, NLA_DATA(na), sizeof(stats));
+ size_t payload_len = NLA_PAYLOAD(na->nla_len);
+
+ memset(&stats, 0, sizeof(stats));
+ if (payload_len > sizeof(stats))
+ payload_len = sizeof(stats);
+ memcpy(&stats, NLA_DATA(na), payload_len);
/* Fill container stats */
container_stats.nr_sleeping = stats.nr_sleeping;
@@ -878,7 +1058,7 @@ static void display_results(int psi_ret)
suc &= BOOL_FPRINT(out, "\033[H\033[J");
/* PSI output (one-line, no cat style) */
- suc &= BOOL_FPRINT(out, "System Pressure Information: (avg10/avg60vg300/total)\n");
+ suc &= BOOL_FPRINT(out, "System Pressure Information: (avg10/avg60/avg300/total)\n");
if (psi_ret) {
suc &= BOOL_FPRINT(out, " PSI not found: check if psi=1 enabled in cmdline\n");
} else {
@@ -936,7 +1116,10 @@ static void display_results(int psi_ret)
}
/* Interacive command */
- suc &= BOOL_FPRINT(out, "[o]sort [M]memverbose [q]quit\n");
+ if (cfg.display_mode == MODE_TYPE && cfg.type_field)
+ suc &= BOOL_FPRINT(out, "[q]quit\n");
+ else
+ suc &= BOOL_FPRINT(out, "[o]sort [M]memverbose [q]quit\n");
if (sort_selected) {
if (cfg.display_mode == MODE_MEMVERBOSE)
suc &= BOOL_FPRINT(out,
@@ -947,32 +1130,55 @@ static void display_results(int psi_ret)
}
/* Task delay output */
- suc &= BOOL_FPRINT(out, "Top %d processes (sorted by %s delay):\n",
- cfg.max_processes, get_name_by_field(cfg.sort_field));
-
- suc &= BOOL_FPRINT(out, "%8s %8s %-17s", "PID", "TGID", "COMMAND");
- if (cfg.display_mode == MODE_MEMVERBOSE) {
- suc &= BOOL_FPRINT(out, "%8s %8s %8s %8s %8s %8s\n",
- "MEM(ms)", "SWAP(ms)", "RCL(ms)",
- "THR(ms)", "CMP(ms)", "WP(ms)");
- suc &= BOOL_FPRINT(out, "-----------------------");
- suc &= BOOL_FPRINT(out, "-----------------------");
- suc &= BOOL_FPRINT(out, "-----------------------");
- suc &= BOOL_FPRINT(out, "---------------------\n");
+ if (cfg.display_mode == MODE_TYPE && cfg.type_field)
+ suc &= BOOL_FPRINT(out,
+ "Top %d processes (sorted by %s MAX delay, largest first):\n",
+ cfg.max_processes, get_name_by_field(cfg.type_field));
+ else
+ suc &= BOOL_FPRINT(out, "Top %d processes (sorted by %s delay):\n",
+ cfg.max_processes, get_name_by_field(cfg.sort_field));
+
+ if (cfg.display_mode == MODE_TYPE && cfg.type_field) {
+ /* Display mode for -t option: show only specified type with avg/max/timestamp */
+ suc &= BOOL_FPRINT(out, "%8s %8s %-17s %12s %12s %20s\n",
+ "PID", "TGID", "COMMAND",
+ "AVG(ms)", "MAX(ms)", "MAX_TIMESTAMP");
+ suc &= BOOL_FPRINT(out, "--------------------------------------------------------");
+ suc &= BOOL_FPRINT(out, "----------------------------------------\n");
} else {
- suc &= BOOL_FPRINT(out, "%8s %8s %8s %8s\n",
- "CPU(ms)", "IO(ms)", "IRQ(ms)", "MEM(ms)");
- suc &= BOOL_FPRINT(out, "-----------------------");
- suc &= BOOL_FPRINT(out, "-----------------------");
- suc &= BOOL_FPRINT(out, "--------------------------\n");
+ suc &= BOOL_FPRINT(out, "%8s %8s %-17s", "PID", "TGID", "COMMAND");
+ if (cfg.display_mode == MODE_MEMVERBOSE) {
+ suc &= BOOL_FPRINT(out, "%8s %8s %8s %8s %8s %8s\n",
+ "MEM(ms)", "SWAP(ms)", "RCL(ms)",
+ "THR(ms)", "CMP(ms)", "WP(ms)");
+ suc &= BOOL_FPRINT(out, "-----------------------");
+ suc &= BOOL_FPRINT(out, "-----------------------");
+ suc &= BOOL_FPRINT(out, "-----------------------");
+ suc &= BOOL_FPRINT(out, "---------------------\n");
+ } else {
+ suc &= BOOL_FPRINT(out, "%8s %8s %8s %8s\n",
+ "CPU(ms)", "IO(ms)", "IRQ(ms)", "MEM(ms)");
+ suc &= BOOL_FPRINT(out, "-----------------------");
+ suc &= BOOL_FPRINT(out, "-----------------------");
+ suc &= BOOL_FPRINT(out, "--------------------------\n");
+ }
}
count = task_count < cfg.max_processes ? task_count : cfg.max_processes;
for (i = 0; i < count; i++) {
- suc &= BOOL_FPRINT(out, "%8d %8d %-15s",
+ suc &= BOOL_FPRINT(out, "%8d %8d %-17s",
tasks[i].pid, tasks[i].tgid, tasks[i].command);
- if (cfg.display_mode == MODE_MEMVERBOSE) {
+ if (cfg.display_mode == MODE_TYPE && cfg.type_field) {
+ double avg_ms, max_ms;
+ struct __kernel_timespec max_ts;
+
+ get_field_delay_values(&tasks[i], cfg.type_field, &avg_ms,
+ &max_ms, &max_ts);
+
+ suc &= BOOL_FPRINT(out, "%12.2f %12.2f %20s\n",
+ avg_ms, max_ms, format_kernel_timespec(&max_ts));
+ } else if (cfg.display_mode == MODE_MEMVERBOSE) {
suc &= BOOL_FPRINT(out, DELAY_FMT_MEMVERBOSE,
TASK_AVG(tasks[i], mem),
TASK_AVG(tasks[i], swapin),
@@ -1040,9 +1246,13 @@ static void handle_keypress(char ch, int *running)
} else {
switch (ch) {
case 'o':
+ if (cfg.display_mode == MODE_TYPE)
+ break;
sort_selected = 1;
break;
case 'M':
+ if (cfg.display_mode == MODE_TYPE)
+ break;
toggle_display_mode();
for (field = sort_fields; field->name != NULL; field++) {
if (field->supported_modes & cfg.display_mode) {
diff --git a/tools/accounting/getdelays.c b/tools/accounting/getdelays.c
index caa5fe9dd573..263ded4567fd 100644
--- a/tools/accounting/getdelays.c
+++ b/tools/accounting/getdelays.c
@@ -235,6 +235,11 @@ static const char *format_timespec(struct __kernel_timespec *ts)
if (ts->tv_sec == 0 && ts->tv_nsec == 0)
return "N/A";
+ /* Avoid Y2038 truncation on 32-bit platforms */
+ if (sizeof(time_sec) < sizeof(ts->tv_sec) &&
+ ts->tv_sec > (__u64)((1ULL << (sizeof(time_sec) * 8 - 1)) - 1))
+ return "N/A";
+
time_sec = ts->tv_sec;
/* Use thread-safe localtime_r */
@@ -302,7 +307,7 @@ static const char *format_timespec(struct __kernel_timespec *ts)
average_ms((double)(t)->cpu_delay_total, (t)->cpu_count)); \
} \
} while (0)
-#define PRINT_FILED_DELAY(name, version, t, count, total, max, min) \
+#define PRINT_FIELD_DELAY(name, version, t, count, total, max, min) \
do { \
if (version >= 16) { \
printf("%-10s%15s%15s%15s%15s%15s\n", \
@@ -324,7 +329,7 @@ static const char *format_timespec(struct __kernel_timespec *ts)
} \
} while (0)
-#define PRINT_FILED_DELAY_WITH_TS(name, version, t, count, total, max, min, max_ts) \
+#define PRINT_FIELD_DELAY_WITH_TS(name, version, t, count, total, max, min, max_ts) \
do { \
if (version >= 17) { \
printf("%-10s%15s%15s%15s%15s%15s%25s\n", \
@@ -365,71 +370,71 @@ static void print_delayacct(struct taskstats *t)
/* Use new macro with timestamp support for version >= 17 */
if (t->version >= 17) {
- PRINT_FILED_DELAY_WITH_TS("IO", t->version, t,
+ PRINT_FIELD_DELAY_WITH_TS("IO", t->version, t,
blkio_count, blkio_delay_total,
blkio_delay_max, blkio_delay_min, blkio_delay_max_ts);
- PRINT_FILED_DELAY_WITH_TS("SWAP", t->version, t,
+ PRINT_FIELD_DELAY_WITH_TS("SWAP", t->version, t,
swapin_count, swapin_delay_total,
swapin_delay_max, swapin_delay_min, swapin_delay_max_ts);
- PRINT_FILED_DELAY_WITH_TS("RECLAIM", t->version, t,
+ PRINT_FIELD_DELAY_WITH_TS("RECLAIM", t->version, t,
freepages_count, freepages_delay_total,
freepages_delay_max, freepages_delay_min, freepages_delay_max_ts);
- PRINT_FILED_DELAY_WITH_TS("THRASHING", t->version, t,
+ PRINT_FIELD_DELAY_WITH_TS("THRASHING", t->version, t,
thrashing_count, thrashing_delay_total,
thrashing_delay_max, thrashing_delay_min, thrashing_delay_max_ts);
if (t->version >= 11) {
- PRINT_FILED_DELAY_WITH_TS("COMPACT", t->version, t,
+ PRINT_FIELD_DELAY_WITH_TS("COMPACT", t->version, t,
compact_count, compact_delay_total,
compact_delay_max, compact_delay_min, compact_delay_max_ts);
}
if (t->version >= 13) {
- PRINT_FILED_DELAY_WITH_TS("WPCOPY", t->version, t,
+ PRINT_FIELD_DELAY_WITH_TS("WPCOPY", t->version, t,
wpcopy_count, wpcopy_delay_total,
wpcopy_delay_max, wpcopy_delay_min, wpcopy_delay_max_ts);
}
if (t->version >= 14) {
- PRINT_FILED_DELAY_WITH_TS("IRQ", t->version, t,
+ PRINT_FIELD_DELAY_WITH_TS("IRQ", t->version, t,
irq_count, irq_delay_total,
irq_delay_max, irq_delay_min, irq_delay_max_ts);
}
} else {
/* Use original macro for older versions */
- PRINT_FILED_DELAY("IO", t->version, t,
+ PRINT_FIELD_DELAY("IO", t->version, t,
blkio_count, blkio_delay_total,
blkio_delay_max, blkio_delay_min);
- PRINT_FILED_DELAY("SWAP", t->version, t,
+ PRINT_FIELD_DELAY("SWAP", t->version, t,
swapin_count, swapin_delay_total,
swapin_delay_max, swapin_delay_min);
- PRINT_FILED_DELAY("RECLAIM", t->version, t,
+ PRINT_FIELD_DELAY("RECLAIM", t->version, t,
freepages_count, freepages_delay_total,
freepages_delay_max, freepages_delay_min);
- PRINT_FILED_DELAY("THRASHING", t->version, t,
+ PRINT_FIELD_DELAY("THRASHING", t->version, t,
thrashing_count, thrashing_delay_total,
thrashing_delay_max, thrashing_delay_min);
if (t->version >= 11) {
- PRINT_FILED_DELAY("COMPACT", t->version, t,
+ PRINT_FIELD_DELAY("COMPACT", t->version, t,
compact_count, compact_delay_total,
compact_delay_max, compact_delay_min);
}
if (t->version >= 13) {
- PRINT_FILED_DELAY("WPCOPY", t->version, t,
+ PRINT_FIELD_DELAY("WPCOPY", t->version, t,
wpcopy_count, wpcopy_delay_total,
wpcopy_delay_max, wpcopy_delay_min);
}
if (t->version >= 14) {
- PRINT_FILED_DELAY("IRQ", t->version, t,
+ PRINT_FIELD_DELAY("IRQ", t->version, t,
irq_count, irq_delay_total,
irq_delay_max, irq_delay_min);
}
diff --git a/tools/include/linux/compiler.h b/tools/include/linux/compiler.h
index f40bd2b04c29..f2f54b038168 100644
--- a/tools/include/linux/compiler.h
+++ b/tools/include/linux/compiler.h
@@ -119,7 +119,7 @@
#define __read_mostly
#ifndef __attribute_const__
-# define __attribute_const__
+# define __attribute_const__ __attribute__ ((__const__))
#endif
#ifndef __maybe_unused
diff --git a/tools/testing/selftests/acct/.gitignore b/tools/testing/selftests/acct/.gitignore
index 9e9c61c5bfd6..fe0896f54e15 100644
--- a/tools/testing/selftests/acct/.gitignore
+++ b/tools/testing/selftests/acct/.gitignore
@@ -1,4 +1,5 @@
acct_syscall
taskstats_fill_stats_tgid
+cgroupstats
config
process_log
diff --git a/tools/testing/selftests/acct/Makefile b/tools/testing/selftests/acct/Makefile
index 083cab5ddb72..93a11a28a636 100644
--- a/tools/testing/selftests/acct/Makefile
+++ b/tools/testing/selftests/acct/Makefile
@@ -1,8 +1,19 @@
# SPDX-License-Identifier: GPL-2.0
TEST_GEN_PROGS := acct_syscall
TEST_GEN_PROGS += taskstats_fill_stats_tgid
+TEST_GEN_PROGS += cgroupstats
+
+NETLINK_HELPER_PROGS := cgroupstats taskstats_fill_stats_tgid
CFLAGS += -Wall
LDLIBS += -lpthread
include ../lib.mk
+
+$(NETLINK_HELPER_PROGS): %: %.c netlink_helper.c netlink_helper.h
+ $(call msg,CC,,$@)
+ $(Q)$(LINK.c) $< netlink_helper.c $(LDLIBS) -o $@
+
+$(addprefix $(OUTPUT)/,$(NETLINK_HELPER_PROGS)): $(OUTPUT)/%: %.c netlink_helper.c netlink_helper.h
+ $(call msg,CC,,$@)
+ $(Q)$(LINK.c) $< netlink_helper.c $(LDLIBS) -o $@
diff --git a/tools/testing/selftests/acct/cgroupstats.c b/tools/testing/selftests/acct/cgroupstats.c
new file mode 100644
index 000000000000..0b421a4ca72b
--- /dev/null
+++ b/tools/testing/selftests/acct/cgroupstats.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/cgroupstats.h>
+#include <linux/genetlink.h>
+#include <linux/netlink.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "netlink_helper.h"
+#include "kselftest.h"
+
+static int send_cgroupstats_cmd(int fd, int family_id, uint32_t cgroup_fd,
+ int flags)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct genlmsghdr genl;
+ char buf[256];
+ } req = { 0 };
+ struct nlattr *na;
+
+ req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+ req.nlh.nlmsg_type = family_id;
+ req.nlh.nlmsg_flags = NLM_F_REQUEST | flags;
+ req.nlh.nlmsg_seq = 2;
+ req.nlh.nlmsg_pid = getpid();
+
+ req.genl.cmd = CGROUPSTATS_CMD_GET;
+ req.genl.version = 1;
+
+ na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len));
+ na->nla_type = CGROUPSTATS_CMD_ATTR_FD;
+ na->nla_len = NLA_HDRLEN + sizeof(cgroup_fd);
+ memcpy(nla_data(na), &cgroup_fd, sizeof(cgroup_fd));
+ req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len);
+
+ return send_request(fd, &req, req.nlh.nlmsg_len);
+}
+
+/*
+ * Receive and decode a cgroupstats response.
+ *
+ * Returns:
+ * 0 — success, stats filled from CGROUPSTATS_CMD_NEW reply
+ * <0 — NLMSG_ERROR errno (e.g. -EBADF, -EINVAL)
+ */
+static int recv_cgroupstats_response(int fd, struct cgroupstats *stats)
+{
+ char resp[8192];
+ struct nlmsghdr *nlh;
+ struct genlmsghdr *genl;
+ struct nlattr *na;
+ int len;
+ int rem;
+
+ memset(stats, 0, sizeof(*stats));
+
+ len = recv(fd, resp, sizeof(resp), 0);
+ if (len < 0)
+ return -errno;
+
+ for (nlh = (struct nlmsghdr *)resp; NLMSG_OK(nlh, len);
+ nlh = NLMSG_NEXT(nlh, len)) {
+ if (nlh->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = NLMSG_DATA(nlh);
+
+ return err->error;
+ }
+
+ genl = (struct genlmsghdr *)NLMSG_DATA(nlh);
+ if (genl->cmd != CGROUPSTATS_CMD_NEW)
+ continue;
+
+ rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN;
+ na = (struct nlattr *)((char *)genl + GENL_HDRLEN);
+ while (nla_ok(na, rem)) {
+ if (na->nla_type == CGROUPSTATS_TYPE_CGROUP_STATS) {
+ memcpy(stats, nla_data(na), sizeof(*stats));
+ return 0;
+ }
+ na = nla_next(na, &rem);
+ }
+ }
+
+ return -EIO;
+}
+
+/* mkdtemp() modifies the template in place, so this cannot be const. */
+static char cg_mountpoint[32];
+static bool cg_mounted;
+
+static int setup_cgroup_v1(void)
+{
+ strcpy(cg_mountpoint, "/tmp/cgstats_test_XXXXXX");
+
+ if (!mkdtemp(cg_mountpoint))
+ return -errno;
+
+ if (mount("cgstats_test", cg_mountpoint, "cgroup", 0,
+ "none,name=cgstats_test") < 0) {
+ int ret = -errno;
+
+ rmdir(cg_mountpoint);
+ return ret;
+ }
+
+ cg_mounted = true;
+ return 0;
+}
+
+static void cleanup_cgroup_v1(void)
+{
+ if (!cg_mounted)
+ return;
+ umount2(cg_mountpoint, MNT_DETACH);
+ rmdir(cg_mountpoint);
+ cg_mounted = false;
+}
+
+int main(void)
+{
+ struct cgroupstats stats;
+ uint64_t total_tasks;
+ int family_id;
+ int nl_fd;
+ int cg_fd;
+ int ret;
+
+ ksft_print_header();
+
+ nl_fd = netlink_open();
+ if (nl_fd < 0)
+ ksft_exit_skip("failed to open generic netlink socket: %s\n",
+ strerror(-nl_fd));
+
+ family_id = get_family_id(nl_fd, TASKSTATS_GENL_NAME);
+ if (family_id < 0)
+ ksft_exit_skip("taskstats generic netlink family unavailable: %s\n",
+ strerror(-family_id));
+
+ ksft_set_plan(3);
+
+ /*
+ * Test 1: mount a private cgroup v1 hierarchy, query it, and
+ * verify the response contains sane task counts. If the test
+ * environment cannot create a private cgroup v1 mount, skip this
+ * case and continue with the unprivileged regression checks below.
+ */
+ ret = setup_cgroup_v1();
+ if (ret) {
+ ksft_test_result_skip("cgroupstats query: cannot mount cgroup v1: %s\n",
+ strerror(-ret));
+ } else {
+ cg_fd = open(cg_mountpoint, O_RDONLY | O_DIRECTORY);
+ if (cg_fd < 0) {
+ ksft_test_result_fail("cgroupstats query: open mountpoint: %s\n",
+ strerror(errno));
+ } else {
+ ret = send_cgroupstats_cmd(nl_fd, family_id,
+ (uint32_t)cg_fd, 0);
+ if (ret) {
+ ksft_test_result_fail("cgroupstats query: send: %s\n",
+ strerror(-ret));
+ } else {
+ ret = recv_cgroupstats_response(nl_fd, &stats);
+ if (ret < 0) {
+ ksft_test_result_fail("cgroupstats query: %s\n",
+ strerror(-ret));
+ } else {
+ total_tasks = (uint64_t)stats.nr_sleeping +
+ (uint64_t)stats.nr_running +
+ (uint64_t)stats.nr_stopped +
+ (uint64_t)stats.nr_uninterruptible +
+ (uint64_t)stats.nr_io_wait;
+
+ ksft_print_msg("cgroupstats query: total_tasks=%llu\n",
+ (unsigned long long)total_tasks);
+
+ ksft_test_result(total_tasks > 0,
+ "cgroupstats query returns valid stats\n");
+ }
+ }
+ close(cg_fd);
+ }
+ }
+ cleanup_cgroup_v1();
+
+ /*
+ * Test 2: invalid fd without NLM_F_ACK. The kernel should
+ * return -EBADF via NLMSG_ERROR regardless of whether the
+ * client requested an explicit ACK.
+ */
+ ret = send_cgroupstats_cmd(nl_fd, family_id, 0xFFFFFFFF, 0);
+ if (ret)
+ ksft_exit_fail_msg("send test 2 failed: %s\n", strerror(-ret));
+
+ ret = recv_cgroupstats_response(nl_fd, &stats);
+ ksft_print_msg("bad fd (no ACK): response=%d (%s)\n",
+ ret, ret < 0 ? strerror(-ret) : "unexpected success");
+ ksft_test_result(ret == -EBADF,
+ "cgroupstats rejects bad fd without NLM_F_ACK\n");
+
+ /*
+ * Test 3: invalid fd with NLM_F_ACK. Same expectation as
+ * test 2, but exercised through a different netlink flag
+ * path in the kernel's ack/error handling.
+ */
+ ret = send_cgroupstats_cmd(nl_fd, family_id, 0xFFFFFFFF, NLM_F_ACK);
+ if (ret)
+ ksft_exit_fail_msg("send test 3 failed: %s\n", strerror(-ret));
+
+ ret = recv_cgroupstats_response(nl_fd, &stats);
+ ksft_print_msg("bad fd (with ACK): response=%d (%s)\n",
+ ret, ret < 0 ? strerror(-ret) : "unexpected success");
+ ksft_test_result(ret == -EBADF,
+ "cgroupstats rejects bad fd with NLM_F_ACK\n");
+
+ close(nl_fd);
+ ksft_finished();
+ return ksft_get_fail_cnt() ? KSFT_FAIL : KSFT_PASS;
+}
diff --git a/tools/testing/selftests/acct/netlink_helper.c b/tools/testing/selftests/acct/netlink_helper.c
new file mode 100644
index 000000000000..3ed834f0e770
--- /dev/null
+++ b/tools/testing/selftests/acct/netlink_helper.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <errno.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <linux/genetlink.h>
+
+#include "netlink_helper.h"
+
+int netlink_open(void)
+{
+ struct timeval tv = { .tv_sec = ACCT_RCV_TIMEOUT_SEC };
+ struct sockaddr_nl addr = {
+ .nl_family = AF_NETLINK,
+ .nl_pid = getpid(),
+ };
+ int fd;
+
+ fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
+ if (fd < 0)
+ return -errno;
+
+ if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
+ int err = -errno;
+
+ close(fd);
+ return err;
+ }
+
+ if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ int err = -errno;
+
+ close(fd);
+ return err;
+ }
+
+ return fd;
+}
+
+int send_request(int fd, void *buf, size_t len)
+{
+ struct sockaddr_nl addr = {
+ .nl_family = AF_NETLINK,
+ };
+
+ if (sendto(fd, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0)
+ return -errno;
+
+ return 0;
+}
+
+/*
+ * Resolve the generic netlink family ID for @name.
+ * Returns the family ID (>= 0) on success, negative errno on failure.
+ */
+int get_family_id(int fd, const char *name)
+{
+ struct {
+ struct nlmsghdr nlh;
+ struct genlmsghdr genl;
+ char buf[256];
+ } req = { 0 };
+ char resp[8192];
+ struct nlmsghdr *nlh;
+ struct genlmsghdr *genl;
+ struct nlattr *na;
+ int len;
+ int rem;
+ int ret;
+
+ req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
+ req.nlh.nlmsg_type = GENL_ID_CTRL;
+ req.nlh.nlmsg_flags = NLM_F_REQUEST;
+ req.nlh.nlmsg_seq = 1;
+ req.nlh.nlmsg_pid = getpid();
+
+ req.genl.cmd = CTRL_CMD_GETFAMILY;
+ req.genl.version = 1;
+
+ na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len));
+ na->nla_type = CTRL_ATTR_FAMILY_NAME;
+ na->nla_len = NLA_HDRLEN + strlen(name) + 1;
+ memcpy(nla_data(na), name, strlen(name) + 1);
+ req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len);
+
+ ret = send_request(fd, &req, req.nlh.nlmsg_len);
+ if (ret)
+ return ret;
+
+ len = recv(fd, resp, sizeof(resp), 0);
+ if (len < 0)
+ return -errno;
+
+ for (nlh = (struct nlmsghdr *)resp; NLMSG_OK(nlh, len);
+ nlh = NLMSG_NEXT(nlh, len)) {
+ if (nlh->nlmsg_type == NLMSG_ERROR) {
+ struct nlmsgerr *err = NLMSG_DATA(nlh);
+
+ return err->error ? err->error : -ENOENT;
+ }
+
+ genl = (struct genlmsghdr *)NLMSG_DATA(nlh);
+ rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN;
+ na = (struct nlattr *)((char *)genl + GENL_HDRLEN);
+ while (nla_ok(na, rem)) {
+ if (na->nla_type == CTRL_ATTR_FAMILY_ID)
+ return *(uint16_t *)nla_data(na);
+ na = nla_next(na, &rem);
+ }
+ }
+
+ return -ENOENT;
+}
diff --git a/tools/testing/selftests/acct/netlink_helper.h b/tools/testing/selftests/acct/netlink_helper.h
new file mode 100644
index 000000000000..0320729c4c06
--- /dev/null
+++ b/tools/testing/selftests/acct/netlink_helper.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Shared generic netlink helpers for the acct selftests.
+ */
+#ifndef ACSELFTESTS_ACCT_NETLINK_HELPER_H
+#define ACSELFTESTS_ACCT_NETLINK_HELPER_H
+
+#include <stdbool.h>
+#include <linux/netlink.h>
+
+#ifndef NLA_ALIGNTO
+#define NLA_ALIGNTO 4
+#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
+#define NLA_HDRLEN ((int)NLA_ALIGN(sizeof(struct nlattr)))
+#endif
+
+/* Fail an individual test case instead of hanging the whole binary. */
+#define ACCT_RCV_TIMEOUT_SEC 2
+
+static inline void *nla_data(const struct nlattr *na)
+{
+ return (void *)((char *)na + NLA_HDRLEN);
+}
+
+static inline bool nla_ok(const struct nlattr *na, int remaining)
+{
+ return remaining >= (int)sizeof(*na) &&
+ na->nla_len >= sizeof(*na) &&
+ na->nla_len <= remaining;
+}
+
+static inline struct nlattr *nla_next(const struct nlattr *na, int *remaining)
+{
+ int aligned_len = NLA_ALIGN(na->nla_len);
+
+ *remaining -= aligned_len;
+ return (struct nlattr *)((char *)na + aligned_len);
+}
+
+int netlink_open(void);
+int send_request(int fd, void *buf, size_t len);
+int get_family_id(int fd, const char *name);
+
+#endif /* ACSELFTESTS_ACCT_NETLINK_HELPER_H */
diff --git a/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c b/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c
index d6cab4ae26f2..9a4c1554dee3 100644
--- a/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c
+++ b/tools/testing/selftests/acct/taskstats_fill_stats_tgid.c
@@ -16,14 +16,9 @@
#include <time.h>
#include <unistd.h>
+#include "netlink_helper.h"
#include "kselftest.h"
-#ifndef NLA_ALIGN
-#define NLA_ALIGNTO 4
-#define NLA_ALIGN(len) (((len) + NLA_ALIGNTO - 1) & ~(NLA_ALIGNTO - 1))
-#define NLA_HDRLEN ((int)NLA_ALIGN(sizeof(struct nlattr)))
-#endif
-
#define BUSY_NS (200ULL * 1000 * 1000)
struct worker_ctx {
@@ -35,26 +30,6 @@ struct worker_ctx {
static unsigned long busy_sink;
-static void *taskstats_nla_data(const struct nlattr *na)
-{
- return (void *)((char *)na + NLA_HDRLEN);
-}
-
-static bool taskstats_nla_ok(const struct nlattr *na, int remaining)
-{
- return remaining >= (int)sizeof(*na) &&
- na->nla_len >= sizeof(*na) &&
- na->nla_len <= remaining;
-}
-
-static struct nlattr *taskstats_nla_next(const struct nlattr *na, int *remaining)
-{
- int aligned_len = NLA_ALIGN(na->nla_len);
-
- *remaining -= aligned_len;
- return (struct nlattr *)((char *)na + aligned_len);
-}
-
static uint64_t timespec_diff_ns(const struct timespec *start,
const struct timespec *end)
{
@@ -84,99 +59,6 @@ static void burn_cpu_for_ns(uint64_t runtime_ns)
busy_sink = acc;
}
-static int netlink_open(void)
-{
- struct sockaddr_nl addr = {
- .nl_family = AF_NETLINK,
- .nl_pid = getpid(),
- };
- int fd;
-
- fd = socket(AF_NETLINK, SOCK_RAW, NETLINK_GENERIC);
- if (fd < 0)
- return -errno;
-
- if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
- int err = -errno;
-
- close(fd);
- return err;
- }
-
- return fd;
-}
-
-static int send_request(int fd, void *buf, size_t len)
-{
- struct sockaddr_nl addr = {
- .nl_family = AF_NETLINK,
- };
-
- if (sendto(fd, buf, len, 0, (struct sockaddr *)&addr, sizeof(addr)) < 0)
- return -errno;
-
- return 0;
-}
-
-static int get_family_id(int fd, const char *name)
-{
- struct {
- struct nlmsghdr nlh;
- struct genlmsghdr genl;
- char buf[256];
- } req = { 0 };
- char resp[8192];
- struct nlmsghdr *nlh;
- struct genlmsghdr *genl;
- struct nlattr *na;
- int len;
- int rem;
- int ret;
-
- req.nlh.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN);
- req.nlh.nlmsg_type = GENL_ID_CTRL;
- req.nlh.nlmsg_flags = NLM_F_REQUEST;
- req.nlh.nlmsg_seq = 1;
- req.nlh.nlmsg_pid = getpid();
-
- req.genl.cmd = CTRL_CMD_GETFAMILY;
- req.genl.version = 1;
-
- na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len));
- na->nla_type = CTRL_ATTR_FAMILY_NAME;
- na->nla_len = NLA_HDRLEN + strlen(name) + 1;
- memcpy(taskstats_nla_data(na), name, strlen(name) + 1);
- req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len);
-
- ret = send_request(fd, &req, req.nlh.nlmsg_len);
- if (ret)
- return ret;
-
- len = recv(fd, resp, sizeof(resp), 0);
- if (len < 0)
- return -errno;
-
- for (nlh = (struct nlmsghdr *)resp; NLMSG_OK(nlh, len);
- nlh = NLMSG_NEXT(nlh, len)) {
- if (nlh->nlmsg_type == NLMSG_ERROR) {
- struct nlmsgerr *err = NLMSG_DATA(nlh);
-
- return err->error ? err->error : -ENOENT;
- }
-
- genl = (struct genlmsghdr *)NLMSG_DATA(nlh);
- rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN;
- na = (struct nlattr *)((char *)genl + GENL_HDRLEN);
- while (taskstats_nla_ok(na, rem)) {
- if (na->nla_type == CTRL_ATTR_FAMILY_ID)
- return *(uint16_t *)taskstats_nla_data(na);
- na = taskstats_nla_next(na, &rem);
- }
- }
-
- return -ENOENT;
-}
-
static int get_taskstats(int fd, int family_id, uint16_t attr_type, uint32_t id,
struct taskstats *stats)
{
@@ -209,7 +91,7 @@ static int get_taskstats(int fd, int family_id, uint16_t attr_type, uint32_t id,
na = (struct nlattr *)((char *)&req + NLMSG_ALIGN(req.nlh.nlmsg_len));
na->nla_type = attr_type;
na->nla_len = NLA_HDRLEN + sizeof(id);
- memcpy(taskstats_nla_data(na), &id, sizeof(id));
+ memcpy(nla_data(na), &id, sizeof(id));
req.nlh.nlmsg_len = NLMSG_ALIGN(req.nlh.nlmsg_len) + NLA_ALIGN(na->nla_len);
ret = send_request(fd, &req, req.nlh.nlmsg_len);
@@ -231,21 +113,21 @@ static int get_taskstats(int fd, int family_id, uint16_t attr_type, uint32_t id,
genl = (struct genlmsghdr *)NLMSG_DATA(nlh);
rem = nlh->nlmsg_len - NLMSG_HDRLEN - GENL_HDRLEN;
na = (struct nlattr *)((char *)genl + GENL_HDRLEN);
- while (taskstats_nla_ok(na, rem)) {
+ while (nla_ok(na, rem)) {
if (na->nla_type == TASKSTATS_TYPE_AGGR_PID ||
na->nla_type == TASKSTATS_TYPE_AGGR_TGID) {
- nested = (struct nlattr *)taskstats_nla_data(na);
+ nested = (struct nlattr *)nla_data(na);
nrem = na->nla_len - NLA_HDRLEN;
- while (taskstats_nla_ok(nested, nrem)) {
+ while (nla_ok(nested, nrem)) {
if (nested->nla_type == TASKSTATS_TYPE_STATS) {
- memcpy(stats, taskstats_nla_data(nested),
+ memcpy(stats, nla_data(nested),
sizeof(*stats));
return 0;
}
- nested = taskstats_nla_next(nested, &nrem);
+ nested = nla_next(nested, &nrem);
}
}
- na = taskstats_nla_next(na, &rem);
+ na = nla_next(na, &rem);
}
}
diff --git a/tools/testing/selftests/ipc/msgque.c b/tools/testing/selftests/ipc/msgque.c
index 82f73cdae120..4b4c1abfac0f 100644
--- a/tools/testing/selftests/ipc/msgque.c
+++ b/tools/testing/selftests/ipc/msgque.c
@@ -161,7 +161,7 @@ int dump_queue(struct msgque_data *msgque)
ret = msgrcv(msgque->msq_id, &msgque->messages[i].mtype,
MAX_MSG_SIZE, i, IPC_NOWAIT | MSG_COPY);
if (ret < 0) {
- if (errno == ENOSYS)
+ if (errno == EOPNOTSUPP)
ksft_exit_skip("MSG_COPY not supported\n");
ksft_test_result_fail("Failed to copy IPC message: %m (%d)\n", errno);