diff options
author | Ingo Molnar <mingo@kernel.org> | 2016-10-16 07:04:34 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-10-16 07:04:34 -0400 |
commit | 4d69f155d58d0f75c5404ea502178b1943a04755 (patch) | |
tree | fe5b7608f05f6951fce748ea1e93d942b52902bd /tools | |
parent | c474e50711aa79b7bd0ea30b44744baca5650375 (diff) | |
parent | 1001354ca34179f3db924eb66672442a173147dc (diff) |
Merge tag 'v4.9-rc1' into x86/fpu, to resolve conflict
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'tools')
139 files changed, 9873 insertions, 358 deletions
diff --git a/tools/accounting/.gitignore b/tools/accounting/.gitignore new file mode 100644 index 000000000000..86485203c4ae --- /dev/null +++ b/tools/accounting/.gitignore | |||
@@ -0,0 +1 @@ | |||
getdelays | |||
diff --git a/tools/accounting/Makefile b/tools/accounting/Makefile new file mode 100644 index 000000000000..647c94a219bf --- /dev/null +++ b/tools/accounting/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | CC := $(CROSS_COMPILE)gcc | ||
2 | CFLAGS := -I../../usr/include | ||
3 | |||
4 | PROGS := getdelays | ||
5 | |||
6 | all: $(PROGS) | ||
7 | |||
8 | clean: | ||
9 | rm -fr $(PROGS) | ||
diff --git a/tools/accounting/getdelays.c b/tools/accounting/getdelays.c new file mode 100644 index 000000000000..b5ca536e56a8 --- /dev/null +++ b/tools/accounting/getdelays.c | |||
@@ -0,0 +1,550 @@ | |||
1 | /* getdelays.c | ||
2 | * | ||
3 | * Utility to get per-pid and per-tgid delay accounting statistics | ||
4 | * Also illustrates usage of the taskstats interface | ||
5 | * | ||
6 | * Copyright (C) Shailabh Nagar, IBM Corp. 2005 | ||
7 | * Copyright (C) Balbir Singh, IBM Corp. 2006 | ||
8 | * Copyright (c) Jay Lan, SGI. 2006 | ||
9 | * | ||
10 | * Compile with | ||
11 | * gcc -I/usr/src/linux/include getdelays.c -o getdelays | ||
12 | */ | ||
13 | |||
14 | #include <stdio.h> | ||
15 | #include <stdlib.h> | ||
16 | #include <errno.h> | ||
17 | #include <unistd.h> | ||
18 | #include <poll.h> | ||
19 | #include <string.h> | ||
20 | #include <fcntl.h> | ||
21 | #include <sys/types.h> | ||
22 | #include <sys/stat.h> | ||
23 | #include <sys/socket.h> | ||
24 | #include <sys/wait.h> | ||
25 | #include <signal.h> | ||
26 | |||
27 | #include <linux/genetlink.h> | ||
28 | #include <linux/taskstats.h> | ||
29 | #include <linux/cgroupstats.h> | ||
30 | |||
31 | /* | ||
32 | * Generic macros for dealing with netlink sockets. Might be duplicated | ||
33 | * elsewhere. It is recommended that commercial grade applications use | ||
34 | * libnl or libnetlink and use the interfaces provided by the library | ||
35 | */ | ||
36 | #define GENLMSG_DATA(glh) ((void *)(NLMSG_DATA(glh) + GENL_HDRLEN)) | ||
37 | #define GENLMSG_PAYLOAD(glh) (NLMSG_PAYLOAD(glh, 0) - GENL_HDRLEN) | ||
38 | #define NLA_DATA(na) ((void *)((char*)(na) + NLA_HDRLEN)) | ||
39 | #define NLA_PAYLOAD(len) (len - NLA_HDRLEN) | ||
40 | |||
41 | #define err(code, fmt, arg...) \ | ||
42 | do { \ | ||
43 | fprintf(stderr, fmt, ##arg); \ | ||
44 | exit(code); \ | ||
45 | } while (0) | ||
46 | |||
47 | int done; | ||
48 | int rcvbufsz; | ||
49 | char name[100]; | ||
50 | int dbg; | ||
51 | int print_delays; | ||
52 | int print_io_accounting; | ||
53 | int print_task_context_switch_counts; | ||
54 | |||
55 | #define PRINTF(fmt, arg...) { \ | ||
56 | if (dbg) { \ | ||
57 | printf(fmt, ##arg); \ | ||
58 | } \ | ||
59 | } | ||
60 | |||
61 | /* Maximum size of response requested or message sent */ | ||
62 | #define MAX_MSG_SIZE 1024 | ||
63 | /* Maximum number of cpus expected to be specified in a cpumask */ | ||
64 | #define MAX_CPUS 32 | ||
65 | |||
66 | struct msgtemplate { | ||
67 | struct nlmsghdr n; | ||
68 | struct genlmsghdr g; | ||
69 | char buf[MAX_MSG_SIZE]; | ||
70 | }; | ||
71 | |||
72 | char cpumask[100+6*MAX_CPUS]; | ||
73 | |||
74 | static void usage(void) | ||
75 | { | ||
76 | fprintf(stderr, "getdelays [-dilv] [-w logfile] [-r bufsize] " | ||
77 | "[-m cpumask] [-t tgid] [-p pid]\n"); | ||
78 | fprintf(stderr, " -d: print delayacct stats\n"); | ||
79 | fprintf(stderr, " -i: print IO accounting (works only with -p)\n"); | ||
80 | fprintf(stderr, " -l: listen forever\n"); | ||
81 | fprintf(stderr, " -v: debug on\n"); | ||
82 | fprintf(stderr, " -C: container path\n"); | ||
83 | } | ||
84 | |||
85 | /* | ||
86 | * Create a raw netlink socket and bind | ||
87 | */ | ||
88 | static int create_nl_socket(int protocol) | ||
89 | { | ||
90 | int fd; | ||
91 | struct sockaddr_nl local; | ||
92 | |||
93 | fd = socket(AF_NETLINK, SOCK_RAW, protocol); | ||
94 | if (fd < 0) | ||
95 | return -1; | ||
96 | |||
97 | if (rcvbufsz) | ||
98 | if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, | ||
99 | &rcvbufsz, sizeof(rcvbufsz)) < 0) { | ||
100 | fprintf(stderr, "Unable to set socket rcv buf size to %d\n", | ||
101 | rcvbufsz); | ||
102 | goto error; | ||
103 | } | ||
104 | |||
105 | memset(&local, 0, sizeof(local)); | ||
106 | local.nl_family = AF_NETLINK; | ||
107 | |||
108 | if (bind(fd, (struct sockaddr *) &local, sizeof(local)) < 0) | ||
109 | goto error; | ||
110 | |||
111 | return fd; | ||
112 | error: | ||
113 | close(fd); | ||
114 | return -1; | ||
115 | } | ||
116 | |||
117 | |||
118 | static int send_cmd(int sd, __u16 nlmsg_type, __u32 nlmsg_pid, | ||
119 | __u8 genl_cmd, __u16 nla_type, | ||
120 | void *nla_data, int nla_len) | ||
121 | { | ||
122 | struct nlattr *na; | ||
123 | struct sockaddr_nl nladdr; | ||
124 | int r, buflen; | ||
125 | char *buf; | ||
126 | |||
127 | struct msgtemplate msg; | ||
128 | |||
129 | msg.n.nlmsg_len = NLMSG_LENGTH(GENL_HDRLEN); | ||
130 | msg.n.nlmsg_type = nlmsg_type; | ||
131 | msg.n.nlmsg_flags = NLM_F_REQUEST; | ||
132 | msg.n.nlmsg_seq = 0; | ||
133 | msg.n.nlmsg_pid = nlmsg_pid; | ||
134 | msg.g.cmd = genl_cmd; | ||
135 | msg.g.version = 0x1; | ||
136 | na = (struct nlattr *) GENLMSG_DATA(&msg); | ||
137 | na->nla_type = nla_type; | ||
138 | na->nla_len = nla_len + 1 + NLA_HDRLEN; | ||
139 | memcpy(NLA_DATA(na), nla_data, nla_len); | ||
140 | msg.n.nlmsg_len += NLMSG_ALIGN(na->nla_len); | ||
141 | |||
142 | buf = (char *) &msg; | ||
143 | buflen = msg.n.nlmsg_len ; | ||
144 | memset(&nladdr, 0, sizeof(nladdr)); | ||
145 | nladdr.nl_family = AF_NETLINK; | ||
146 | while ((r = sendto(sd, buf, buflen, 0, (struct sockaddr *) &nladdr, | ||
147 | sizeof(nladdr))) < buflen) { | ||
148 | if (r > 0) { | ||
149 | buf += r; | ||
150 | buflen -= r; | ||
151 | } else if (errno != EAGAIN) | ||
152 | return -1; | ||
153 | } | ||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | |||
158 | /* | ||
159 | * Probe the controller in genetlink to find the family id | ||
160 | * for the TASKSTATS family | ||
161 | */ | ||
162 | static int get_family_id(int sd) | ||
163 | { | ||
164 | struct { | ||
165 | struct nlmsghdr n; | ||
166 | struct genlmsghdr g; | ||
167 | char buf[256]; | ||
168 | } ans; | ||
169 | |||
170 | int id = 0, rc; | ||
171 | struct nlattr *na; | ||
172 | int rep_len; | ||
173 | |||
174 | strcpy(name, TASKSTATS_GENL_NAME); | ||
175 | rc = send_cmd(sd, GENL_ID_CTRL, getpid(), CTRL_CMD_GETFAMILY, | ||
176 | CTRL_ATTR_FAMILY_NAME, (void *)name, | ||
177 | strlen(TASKSTATS_GENL_NAME)+1); | ||
178 | if (rc < 0) | ||
179 | return 0; /* sendto() failure? */ | ||
180 | |||
181 | rep_len = recv(sd, &ans, sizeof(ans), 0); | ||
182 | if (ans.n.nlmsg_type == NLMSG_ERROR || | ||
183 | (rep_len < 0) || !NLMSG_OK((&ans.n), rep_len)) | ||
184 | return 0; | ||
185 | |||
186 | na = (struct nlattr *) GENLMSG_DATA(&ans); | ||
187 | na = (struct nlattr *) ((char *) na + NLA_ALIGN(na->nla_len)); | ||
188 | if (na->nla_type == CTRL_ATTR_FAMILY_ID) { | ||
189 | id = *(__u16 *) NLA_DATA(na); | ||
190 | } | ||
191 | return id; | ||
192 | } | ||
193 | |||
194 | #define average_ms(t, c) (t / 1000000ULL / (c ? c : 1)) | ||
195 | |||
196 | static void print_delayacct(struct taskstats *t) | ||
197 | { | ||
198 | printf("\n\nCPU %15s%15s%15s%15s%15s\n" | ||
199 | " %15llu%15llu%15llu%15llu%15.3fms\n" | ||
200 | "IO %15s%15s%15s\n" | ||
201 | " %15llu%15llu%15llums\n" | ||
202 | "SWAP %15s%15s%15s\n" | ||
203 | " %15llu%15llu%15llums\n" | ||
204 | "RECLAIM %12s%15s%15s\n" | ||
205 | " %15llu%15llu%15llums\n", | ||
206 | "count", "real total", "virtual total", | ||
207 | "delay total", "delay average", | ||
208 | (unsigned long long)t->cpu_count, | ||
209 | (unsigned long long)t->cpu_run_real_total, | ||
210 | (unsigned long long)t->cpu_run_virtual_total, | ||
211 | (unsigned long long)t->cpu_delay_total, | ||
212 | average_ms((double)t->cpu_delay_total, t->cpu_count), | ||
213 | "count", "delay total", "delay average", | ||
214 | (unsigned long long)t->blkio_count, | ||
215 | (unsigned long long)t->blkio_delay_total, | ||
216 | average_ms(t->blkio_delay_total, t->blkio_count), | ||
217 | "count", "delay total", "delay average", | ||
218 | (unsigned long long)t->swapin_count, | ||
219 | (unsigned long long)t->swapin_delay_total, | ||
220 | average_ms(t->swapin_delay_total, t->swapin_count), | ||
221 | "count", "delay total", "delay average", | ||
222 | (unsigned long long)t->freepages_count, | ||
223 | (unsigned long long)t->freepages_delay_total, | ||
224 | average_ms(t->freepages_delay_total, t->freepages_count)); | ||
225 | } | ||
226 | |||
227 | static void task_context_switch_counts(struct taskstats *t) | ||
228 | { | ||
229 | printf("\n\nTask %15s%15s\n" | ||
230 | " %15llu%15llu\n", | ||
231 | "voluntary", "nonvoluntary", | ||
232 | (unsigned long long)t->nvcsw, (unsigned long long)t->nivcsw); | ||
233 | } | ||
234 | |||
235 | static void print_cgroupstats(struct cgroupstats *c) | ||
236 | { | ||
237 | printf("sleeping %llu, blocked %llu, running %llu, stopped %llu, " | ||
238 | "uninterruptible %llu\n", (unsigned long long)c->nr_sleeping, | ||
239 | (unsigned long long)c->nr_io_wait, | ||
240 | (unsigned long long)c->nr_running, | ||
241 | (unsigned long long)c->nr_stopped, | ||
242 | (unsigned long long)c->nr_uninterruptible); | ||
243 | } | ||
244 | |||
245 | |||
246 | static void print_ioacct(struct taskstats *t) | ||
247 | { | ||
248 | printf("%s: read=%llu, write=%llu, cancelled_write=%llu\n", | ||
249 | t->ac_comm, | ||
250 | (unsigned long long)t->read_bytes, | ||
251 | (unsigned long long)t->write_bytes, | ||
252 | (unsigned long long)t->cancelled_write_bytes); | ||
253 | } | ||
254 | |||
255 | int main(int argc, char *argv[]) | ||
256 | { | ||
257 | int c, rc, rep_len, aggr_len, len2; | ||
258 | int cmd_type = TASKSTATS_CMD_ATTR_UNSPEC; | ||
259 | __u16 id; | ||
260 | __u32 mypid; | ||
261 | |||
262 | struct nlattr *na; | ||
263 | int nl_sd = -1; | ||
264 | int len = 0; | ||
265 | pid_t tid = 0; | ||
266 | pid_t rtid = 0; | ||
267 | |||
268 | int fd = 0; | ||
269 | int count = 0; | ||
270 | int write_file = 0; | ||
271 | int maskset = 0; | ||
272 | char *logfile = NULL; | ||
273 | int loop = 0; | ||
274 | int containerset = 0; | ||
275 | char *containerpath = NULL; | ||
276 | int cfd = 0; | ||
277 | int forking = 0; | ||
278 | sigset_t sigset; | ||
279 | |||
280 | struct msgtemplate msg; | ||
281 | |||
282 | while (!forking) { | ||
283 | c = getopt(argc, argv, "qdiw:r:m:t:p:vlC:c:"); | ||
284 | if (c < 0) | ||
285 | break; | ||
286 | |||
287 | switch (c) { | ||
288 | case 'd': | ||
289 | printf("print delayacct stats ON\n"); | ||
290 | print_delays = 1; | ||
291 | break; | ||
292 | case 'i': | ||
293 | printf("printing IO accounting\n"); | ||
294 | print_io_accounting = 1; | ||
295 | break; | ||
296 | case 'q': | ||
297 | printf("printing task/process context switch rates\n"); | ||
298 | print_task_context_switch_counts = 1; | ||
299 | break; | ||
300 | case 'C': | ||
301 | containerset = 1; | ||
302 | containerpath = optarg; | ||
303 | break; | ||
304 | case 'w': | ||
305 | logfile = strdup(optarg); | ||
306 | printf("write to file %s\n", logfile); | ||
307 | write_file = 1; | ||
308 | break; | ||
309 | case 'r': | ||
310 | rcvbufsz = atoi(optarg); | ||
311 | printf("receive buf size %d\n", rcvbufsz); | ||
312 | if (rcvbufsz < 0) | ||
313 | err(1, "Invalid rcv buf size\n"); | ||
314 | break; | ||
315 | case 'm': | ||
316 | strncpy(cpumask, optarg, sizeof(cpumask)); | ||
317 | cpumask[sizeof(cpumask) - 1] = '\0'; | ||
318 | maskset = 1; | ||
319 | printf("cpumask %s maskset %d\n", cpumask, maskset); | ||
320 | break; | ||
321 | case 't': | ||
322 | tid = atoi(optarg); | ||
323 | if (!tid) | ||
324 | err(1, "Invalid tgid\n"); | ||
325 | cmd_type = TASKSTATS_CMD_ATTR_TGID; | ||
326 | break; | ||
327 | case 'p': | ||
328 | tid = atoi(optarg); | ||
329 | if (!tid) | ||
330 | err(1, "Invalid pid\n"); | ||
331 | cmd_type = TASKSTATS_CMD_ATTR_PID; | ||
332 | break; | ||
333 | case 'c': | ||
334 | |||
335 | /* Block SIGCHLD for sigwait() later */ | ||
336 | if (sigemptyset(&sigset) == -1) | ||
337 | err(1, "Failed to empty sigset"); | ||
338 | if (sigaddset(&sigset, SIGCHLD)) | ||
339 | err(1, "Failed to set sigchld in sigset"); | ||
340 | sigprocmask(SIG_BLOCK, &sigset, NULL); | ||
341 | |||
342 | /* fork/exec a child */ | ||
343 | tid = fork(); | ||
344 | if (tid < 0) | ||
345 | err(1, "Fork failed\n"); | ||
346 | if (tid == 0) | ||
347 | if (execvp(argv[optind - 1], | ||
348 | &argv[optind - 1]) < 0) | ||
349 | exit(-1); | ||
350 | |||
351 | /* Set the command type and avoid further processing */ | ||
352 | cmd_type = TASKSTATS_CMD_ATTR_PID; | ||
353 | forking = 1; | ||
354 | break; | ||
355 | case 'v': | ||
356 | printf("debug on\n"); | ||
357 | dbg = 1; | ||
358 | break; | ||
359 | case 'l': | ||
360 | printf("listen forever\n"); | ||
361 | loop = 1; | ||
362 | break; | ||
363 | default: | ||
364 | usage(); | ||
365 | exit(-1); | ||
366 | } | ||
367 | } | ||
368 | |||
369 | if (write_file) { | ||
370 | fd = open(logfile, O_WRONLY | O_CREAT | O_TRUNC, | ||
371 | S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); | ||
372 | if (fd == -1) { | ||
373 | perror("Cannot open output file\n"); | ||
374 | exit(1); | ||
375 | } | ||
376 | } | ||
377 | |||
378 | nl_sd = create_nl_socket(NETLINK_GENERIC); | ||
379 | if (nl_sd < 0) | ||
380 | err(1, "error creating Netlink socket\n"); | ||
381 | |||
382 | |||
383 | mypid = getpid(); | ||
384 | id = get_family_id(nl_sd); | ||
385 | if (!id) { | ||
386 | fprintf(stderr, "Error getting family id, errno %d\n", errno); | ||
387 | goto err; | ||
388 | } | ||
389 | PRINTF("family id %d\n", id); | ||
390 | |||
391 | if (maskset) { | ||
392 | rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, | ||
393 | TASKSTATS_CMD_ATTR_REGISTER_CPUMASK, | ||
394 | &cpumask, strlen(cpumask) + 1); | ||
395 | PRINTF("Sent register cpumask, retval %d\n", rc); | ||
396 | if (rc < 0) { | ||
397 | fprintf(stderr, "error sending register cpumask\n"); | ||
398 | goto err; | ||
399 | } | ||
400 | } | ||
401 | |||
402 | if (tid && containerset) { | ||
403 | fprintf(stderr, "Select either -t or -C, not both\n"); | ||
404 | goto err; | ||
405 | } | ||
406 | |||
407 | /* | ||
408 | * If we forked a child, wait for it to exit. Cannot use waitpid() | ||
409 | * as all the delicious data would be reaped as part of the wait | ||
410 | */ | ||
411 | if (tid && forking) { | ||
412 | int sig_received; | ||
413 | sigwait(&sigset, &sig_received); | ||
414 | } | ||
415 | |||
416 | if (tid) { | ||
417 | rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, | ||
418 | cmd_type, &tid, sizeof(__u32)); | ||
419 | PRINTF("Sent pid/tgid, retval %d\n", rc); | ||
420 | if (rc < 0) { | ||
421 | fprintf(stderr, "error sending tid/tgid cmd\n"); | ||
422 | goto done; | ||
423 | } | ||
424 | } | ||
425 | |||
426 | if (containerset) { | ||
427 | cfd = open(containerpath, O_RDONLY); | ||
428 | if (cfd < 0) { | ||
429 | perror("error opening container file"); | ||
430 | goto err; | ||
431 | } | ||
432 | rc = send_cmd(nl_sd, id, mypid, CGROUPSTATS_CMD_GET, | ||
433 | CGROUPSTATS_CMD_ATTR_FD, &cfd, sizeof(__u32)); | ||
434 | if (rc < 0) { | ||
435 | perror("error sending cgroupstats command"); | ||
436 | goto err; | ||
437 | } | ||
438 | } | ||
439 | if (!maskset && !tid && !containerset) { | ||
440 | usage(); | ||
441 | goto err; | ||
442 | } | ||
443 | |||
444 | do { | ||
445 | rep_len = recv(nl_sd, &msg, sizeof(msg), 0); | ||
446 | PRINTF("received %d bytes\n", rep_len); | ||
447 | |||
448 | if (rep_len < 0) { | ||
449 | fprintf(stderr, "nonfatal reply error: errno %d\n", | ||
450 | errno); | ||
451 | continue; | ||
452 | } | ||
453 | if (msg.n.nlmsg_type == NLMSG_ERROR || | ||
454 | !NLMSG_OK((&msg.n), rep_len)) { | ||
455 | struct nlmsgerr *err = NLMSG_DATA(&msg); | ||
456 | fprintf(stderr, "fatal reply error, errno %d\n", | ||
457 | err->error); | ||
458 | goto done; | ||
459 | } | ||
460 | |||
461 | PRINTF("nlmsghdr size=%zu, nlmsg_len=%d, rep_len=%d\n", | ||
462 | sizeof(struct nlmsghdr), msg.n.nlmsg_len, rep_len); | ||
463 | |||
464 | |||
465 | rep_len = GENLMSG_PAYLOAD(&msg.n); | ||
466 | |||
467 | na = (struct nlattr *) GENLMSG_DATA(&msg); | ||
468 | len = 0; | ||
469 | while (len < rep_len) { | ||
470 | len += NLA_ALIGN(na->nla_len); | ||
471 | switch (na->nla_type) { | ||
472 | case TASKSTATS_TYPE_AGGR_TGID: | ||
473 | /* Fall through */ | ||
474 | case TASKSTATS_TYPE_AGGR_PID: | ||
475 | aggr_len = NLA_PAYLOAD(na->nla_len); | ||
476 | len2 = 0; | ||
477 | /* For nested attributes, na follows */ | ||
478 | na = (struct nlattr *) NLA_DATA(na); | ||
479 | done = 0; | ||
480 | while (len2 < aggr_len) { | ||
481 | switch (na->nla_type) { | ||
482 | case TASKSTATS_TYPE_PID: | ||
483 | rtid = *(int *) NLA_DATA(na); | ||
484 | if (print_delays) | ||
485 | printf("PID\t%d\n", rtid); | ||
486 | break; | ||
487 | case TASKSTATS_TYPE_TGID: | ||
488 | rtid = *(int *) NLA_DATA(na); | ||
489 | if (print_delays) | ||
490 | printf("TGID\t%d\n", rtid); | ||
491 | break; | ||
492 | case TASKSTATS_TYPE_STATS: | ||
493 | count++; | ||
494 | if (print_delays) | ||
495 | print_delayacct((struct taskstats *) NLA_DATA(na)); | ||
496 | if (print_io_accounting) | ||
497 | print_ioacct((struct taskstats *) NLA_DATA(na)); | ||
498 | if (print_task_context_switch_counts) | ||
499 | task_context_switch_counts((struct taskstats *) NLA_DATA(na)); | ||
500 | if (fd) { | ||
501 | if (write(fd, NLA_DATA(na), na->nla_len) < 0) { | ||
502 | err(1,"write error\n"); | ||
503 | } | ||
504 | } | ||
505 | if (!loop) | ||
506 | goto done; | ||
507 | break; | ||
508 | case TASKSTATS_TYPE_NULL: | ||
509 | break; | ||
510 | default: | ||
511 | fprintf(stderr, "Unknown nested" | ||
512 | " nla_type %d\n", | ||
513 | na->nla_type); | ||
514 | break; | ||
515 | } | ||
516 | len2 += NLA_ALIGN(na->nla_len); | ||
517 | na = (struct nlattr *)((char *)na + | ||
518 | NLA_ALIGN(na->nla_len)); | ||
519 | } | ||
520 | break; | ||
521 | |||
522 | case CGROUPSTATS_TYPE_CGROUP_STATS: | ||
523 | print_cgroupstats(NLA_DATA(na)); | ||
524 | break; | ||
525 | default: | ||
526 | fprintf(stderr, "Unknown nla_type %d\n", | ||
527 | na->nla_type); | ||
528 | case TASKSTATS_TYPE_NULL: | ||
529 | break; | ||
530 | } | ||
531 | na = (struct nlattr *) (GENLMSG_DATA(&msg) + len); | ||
532 | } | ||
533 | } while (loop); | ||
534 | done: | ||
535 | if (maskset) { | ||
536 | rc = send_cmd(nl_sd, id, mypid, TASKSTATS_CMD_GET, | ||
537 | TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK, | ||
538 | &cpumask, strlen(cpumask) + 1); | ||
539 | printf("Sent deregister mask, retval %d\n", rc); | ||
540 | if (rc < 0) | ||
541 | err(rc, "error sending deregister cpumask\n"); | ||
542 | } | ||
543 | err: | ||
544 | close(nl_sd); | ||
545 | if (fd) | ||
546 | close(fd); | ||
547 | if (cfd) | ||
548 | close(cfd); | ||
549 | return 0; | ||
550 | } | ||
diff --git a/tools/arch/x86/include/asm/cpufeatures.h b/tools/arch/x86/include/asm/cpufeatures.h index 92a8308b96f6..1188bc849ee3 100644 --- a/tools/arch/x86/include/asm/cpufeatures.h +++ b/tools/arch/x86/include/asm/cpufeatures.h | |||
@@ -106,7 +106,6 @@ | |||
106 | #define X86_FEATURE_APERFMPERF ( 3*32+28) /* APERFMPERF */ | 106 | #define X86_FEATURE_APERFMPERF ( 3*32+28) /* APERFMPERF */ |
107 | #define X86_FEATURE_EAGER_FPU ( 3*32+29) /* "eagerfpu" Non lazy FPU restore */ | 107 | #define X86_FEATURE_EAGER_FPU ( 3*32+29) /* "eagerfpu" Non lazy FPU restore */ |
108 | #define X86_FEATURE_NONSTOP_TSC_S3 ( 3*32+30) /* TSC doesn't stop in S3 state */ | 108 | #define X86_FEATURE_NONSTOP_TSC_S3 ( 3*32+30) /* TSC doesn't stop in S3 state */ |
109 | #define X86_FEATURE_MCE_RECOVERY ( 3*32+31) /* cpu has recoverable machine checks */ | ||
110 | 109 | ||
111 | /* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */ | 110 | /* Intel-defined CPU features, CPUID level 0x00000001 (ecx), word 4 */ |
112 | #define X86_FEATURE_XMM3 ( 4*32+ 0) /* "pni" SSE-3 */ | 111 | #define X86_FEATURE_XMM3 ( 4*32+ 0) /* "pni" SSE-3 */ |
diff --git a/tools/arch/x86/lib/memcpy_64.S b/tools/arch/x86/lib/memcpy_64.S index 2ec0b0abbfaa..49e6ebac7e73 100644 --- a/tools/arch/x86/lib/memcpy_64.S +++ b/tools/arch/x86/lib/memcpy_64.S | |||
@@ -181,11 +181,11 @@ ENDPROC(memcpy_orig) | |||
181 | 181 | ||
182 | #ifndef CONFIG_UML | 182 | #ifndef CONFIG_UML |
183 | /* | 183 | /* |
184 | * memcpy_mcsafe - memory copy with machine check exception handling | 184 | * memcpy_mcsafe_unrolled - memory copy with machine check exception handling |
185 | * Note that we only catch machine checks when reading the source addresses. | 185 | * Note that we only catch machine checks when reading the source addresses. |
186 | * Writes to target are posted and don't generate machine checks. | 186 | * Writes to target are posted and don't generate machine checks. |
187 | */ | 187 | */ |
188 | ENTRY(memcpy_mcsafe) | 188 | ENTRY(memcpy_mcsafe_unrolled) |
189 | cmpl $8, %edx | 189 | cmpl $8, %edx |
190 | /* Less than 8 bytes? Go to byte copy loop */ | 190 | /* Less than 8 bytes? Go to byte copy loop */ |
191 | jb .L_no_whole_words | 191 | jb .L_no_whole_words |
@@ -273,7 +273,7 @@ ENTRY(memcpy_mcsafe) | |||
273 | .L_done_memcpy_trap: | 273 | .L_done_memcpy_trap: |
274 | xorq %rax, %rax | 274 | xorq %rax, %rax |
275 | ret | 275 | ret |
276 | ENDPROC(memcpy_mcsafe) | 276 | ENDPROC(memcpy_mcsafe_unrolled) |
277 | 277 | ||
278 | .section .fixup, "ax" | 278 | .section .fixup, "ax" |
279 | /* Return -EFAULT for any failure */ | 279 | /* Return -EFAULT for any failure */ |
diff --git a/tools/build/Build b/tools/build/Build index 63a6c34c0c88..76d1a4960973 100644 --- a/tools/build/Build +++ b/tools/build/Build | |||
@@ -1 +1,3 @@ | |||
1 | hostprogs := fixdep | ||
2 | |||
1 | fixdep-y := fixdep.o | 3 | fixdep-y := fixdep.o |
diff --git a/tools/build/Build.include b/tools/build/Build.include index 4d000bc959b4..1dcb95e76f70 100644 --- a/tools/build/Build.include +++ b/tools/build/Build.include | |||
@@ -90,3 +90,9 @@ if_changed = $(if $(strip $(any-prereq) $(arg-check)), \ | |||
90 | # - per object C flags | 90 | # - per object C flags |
91 | # - BUILD_STR macro to allow '-D"$(variable)"' constructs | 91 | # - BUILD_STR macro to allow '-D"$(variable)"' constructs |
92 | c_flags = -Wp,-MD,$(depfile),-MT,$@ $(CFLAGS) -D"BUILD_STR(s)=\#s" $(CFLAGS_$(basetarget).o) $(CFLAGS_$(obj)) | 92 | c_flags = -Wp,-MD,$(depfile),-MT,$@ $(CFLAGS) -D"BUILD_STR(s)=\#s" $(CFLAGS_$(basetarget).o) $(CFLAGS_$(obj)) |
93 | cxx_flags = -Wp,-MD,$(depfile),-MT,$@ $(CXXFLAGS) -D"BUILD_STR(s)=\#s" $(CXXFLAGS_$(basetarget).o) $(CXXFLAGS_$(obj)) | ||
94 | |||
95 | ### | ||
96 | ## HOSTCC C flags | ||
97 | |||
98 | host_c_flags = -Wp,-MD,$(depfile),-MT,$@ $(CHOSTFLAGS) -D"BUILD_STR(s)=\#s" $(CHOSTFLAGS_$(basetarget).o) $(CHOSTFLAGS_$(obj)) | ||
diff --git a/tools/build/Makefile b/tools/build/Makefile index 0d5a0e3a8fa9..8332959fbca4 100644 --- a/tools/build/Makefile +++ b/tools/build/Makefile | |||
@@ -14,6 +14,12 @@ endef | |||
14 | $(call allow-override,CC,$(CROSS_COMPILE)gcc) | 14 | $(call allow-override,CC,$(CROSS_COMPILE)gcc) |
15 | $(call allow-override,LD,$(CROSS_COMPILE)ld) | 15 | $(call allow-override,LD,$(CROSS_COMPILE)ld) |
16 | 16 | ||
17 | HOSTCC ?= gcc | ||
18 | HOSTLD ?= ld | ||
19 | HOSTAR ?= ar | ||
20 | |||
21 | export HOSTCC HOSTLD HOSTAR | ||
22 | |||
17 | ifeq ($(V),1) | 23 | ifeq ($(V),1) |
18 | Q = | 24 | Q = |
19 | else | 25 | else |
@@ -36,7 +42,7 @@ $(OUTPUT)fixdep-in.o: FORCE | |||
36 | $(Q)$(MAKE) $(build)=fixdep | 42 | $(Q)$(MAKE) $(build)=fixdep |
37 | 43 | ||
38 | $(OUTPUT)fixdep: $(OUTPUT)fixdep-in.o | 44 | $(OUTPUT)fixdep: $(OUTPUT)fixdep-in.o |
39 | $(QUIET_LINK)$(CC) $(LDFLAGS) -o $@ $< | 45 | $(QUIET_LINK)$(HOSTCC) $(LDFLAGS) -o $@ $< |
40 | 46 | ||
41 | FORCE: | 47 | FORCE: |
42 | 48 | ||
diff --git a/tools/build/Makefile.build b/tools/build/Makefile.build index 27f3583193e6..99c0ccd2f176 100644 --- a/tools/build/Makefile.build +++ b/tools/build/Makefile.build | |||
@@ -58,6 +58,12 @@ quiet_cmd_mkdir = MKDIR $(dir $@) | |||
58 | quiet_cmd_cc_o_c = CC $@ | 58 | quiet_cmd_cc_o_c = CC $@ |
59 | cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< | 59 | cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< |
60 | 60 | ||
61 | quiet_cmd_host_cc_o_c = HOSTCC $@ | ||
62 | cmd_host_cc_o_c = $(HOSTCC) $(host_c_flags) -c -o $@ $< | ||
63 | |||
64 | quiet_cmd_cxx_o_c = CXX $@ | ||
65 | cmd_cxx_o_c = $(CXX) $(cxx_flags) -c -o $@ $< | ||
66 | |||
61 | quiet_cmd_cpp_i_c = CPP $@ | 67 | quiet_cmd_cpp_i_c = CPP $@ |
62 | cmd_cpp_i_c = $(CC) $(c_flags) -E -o $@ $< | 68 | cmd_cpp_i_c = $(CC) $(c_flags) -E -o $@ $< |
63 | 69 | ||
@@ -70,16 +76,28 @@ quiet_cmd_gen = GEN $@ | |||
70 | # If there's nothing to link, create empty $@ object. | 76 | # If there's nothing to link, create empty $@ object. |
71 | quiet_cmd_ld_multi = LD $@ | 77 | quiet_cmd_ld_multi = LD $@ |
72 | cmd_ld_multi = $(if $(strip $(obj-y)),\ | 78 | cmd_ld_multi = $(if $(strip $(obj-y)),\ |
73 | $(LD) -r -o $@ $(filter $(obj-y),$^),rm -f $@; $(AR) rcs $@) | 79 | $(LD) -r -o $@ $(filter $(obj-y),$^),rm -f $@; $(AR) rcs $@) |
80 | |||
81 | quiet_cmd_host_ld_multi = HOSTLD $@ | ||
82 | cmd_host_ld_multi = $(if $(strip $(obj-y)),\ | ||
83 | $(HOSTLD) -r -o $@ $(filter $(obj-y),$^),rm -f $@; $(HOSTAR) rcs $@) | ||
84 | |||
85 | ifneq ($(filter $(obj),$(hostprogs)),) | ||
86 | host = host_ | ||
87 | endif | ||
74 | 88 | ||
75 | # Build rules | 89 | # Build rules |
76 | $(OUTPUT)%.o: %.c FORCE | 90 | $(OUTPUT)%.o: %.c FORCE |
77 | $(call rule_mkdir) | 91 | $(call rule_mkdir) |
78 | $(call if_changed_dep,cc_o_c) | 92 | $(call if_changed_dep,$(host)cc_o_c) |
93 | |||
94 | $(OUTPUT)%.o: %.cpp FORCE | ||
95 | $(call rule_mkdir) | ||
96 | $(call if_changed_dep,cxx_o_c) | ||
79 | 97 | ||
80 | $(OUTPUT)%.o: %.S FORCE | 98 | $(OUTPUT)%.o: %.S FORCE |
81 | $(call rule_mkdir) | 99 | $(call rule_mkdir) |
82 | $(call if_changed_dep,cc_o_c) | 100 | $(call if_changed_dep,$(host)cc_o_c) |
83 | 101 | ||
84 | $(OUTPUT)%.i: %.c FORCE | 102 | $(OUTPUT)%.i: %.c FORCE |
85 | $(call rule_mkdir) | 103 | $(call rule_mkdir) |
@@ -119,7 +137,7 @@ $(sort $(subdir-obj-y)): $(subdir-y) ; | |||
119 | 137 | ||
120 | $(in-target): $(obj-y) FORCE | 138 | $(in-target): $(obj-y) FORCE |
121 | $(call rule_mkdir) | 139 | $(call rule_mkdir) |
122 | $(call if_changed,ld_multi) | 140 | $(call if_changed,$(host)ld_multi) |
123 | 141 | ||
124 | __build: $(in-target) | 142 | __build: $(in-target) |
125 | @: | 143 | @: |
diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature index a120c6b755a9..ae52e029dd22 100644 --- a/tools/build/Makefile.feature +++ b/tools/build/Makefile.feature | |||
@@ -7,7 +7,7 @@ endif | |||
7 | 7 | ||
8 | feature_check = $(eval $(feature_check_code)) | 8 | feature_check = $(eval $(feature_check_code)) |
9 | define feature_check_code | 9 | define feature_check_code |
10 | feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS) $(FEATURE_CHECK_CFLAGS-$(1))" LDFLAGS="$(LDFLAGS) $(FEATURE_CHECK_LDFLAGS-$(1))" -C $(feature_dir) $(OUTPUT_FEATURES)test-$1.bin >/dev/null 2>/dev/null && echo 1 || echo 0) | 10 | feature-$(1) := $(shell $(MAKE) OUTPUT=$(OUTPUT_FEATURES) CFLAGS="$(EXTRA_CFLAGS) $(FEATURE_CHECK_CFLAGS-$(1))" CXXFLAGS="$(EXTRA_CXXFLAGS) $(FEATURE_CHECK_CXXFLAGS-$(1))" LDFLAGS="$(LDFLAGS) $(FEATURE_CHECK_LDFLAGS-$(1))" -C $(feature_dir) $(OUTPUT_FEATURES)test-$1.bin >/dev/null 2>/dev/null && echo 1 || echo 0) |
11 | endef | 11 | endef |
12 | 12 | ||
13 | feature_set = $(eval $(feature_set_code)) | 13 | feature_set = $(eval $(feature_set_code)) |
diff --git a/tools/build/Makefile.include b/tools/build/Makefile.include index be630bed66d2..ad22e4e7bc59 100644 --- a/tools/build/Makefile.include +++ b/tools/build/Makefile.include | |||
@@ -1,10 +1,6 @@ | |||
1 | build := -f $(srctree)/tools/build/Makefile.build dir=. obj | 1 | build := -f $(srctree)/tools/build/Makefile.build dir=. obj |
2 | 2 | ||
3 | ifdef CROSS_COMPILE | ||
4 | fixdep: | ||
5 | else | ||
6 | fixdep: | 3 | fixdep: |
7 | $(Q)$(MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= $(OUTPUT)fixdep | 4 | $(Q)$(MAKE) -C $(srctree)/tools/build CFLAGS= LDFLAGS= $(OUTPUT)fixdep |
8 | endif | ||
9 | 5 | ||
10 | .PHONY: fixdep | 6 | .PHONY: fixdep |
diff --git a/tools/build/feature/Makefile b/tools/build/feature/Makefile index a0b29a311816..ac9c477a2a48 100644 --- a/tools/build/feature/Makefile +++ b/tools/build/feature/Makefile | |||
@@ -46,11 +46,13 @@ FILES= \ | |||
46 | test-lzma.bin \ | 46 | test-lzma.bin \ |
47 | test-bpf.bin \ | 47 | test-bpf.bin \ |
48 | test-get_cpuid.bin \ | 48 | test-get_cpuid.bin \ |
49 | test-sdt.bin | 49 | test-sdt.bin \ |
50 | test-cxx.bin | ||
50 | 51 | ||
51 | FILES := $(addprefix $(OUTPUT),$(FILES)) | 52 | FILES := $(addprefix $(OUTPUT),$(FILES)) |
52 | 53 | ||
53 | CC := $(CROSS_COMPILE)gcc -MD | 54 | CC := $(CROSS_COMPILE)gcc -MD |
55 | CXX := $(CROSS_COMPILE)g++ -MD | ||
54 | PKG_CONFIG := $(CROSS_COMPILE)pkg-config | 56 | PKG_CONFIG := $(CROSS_COMPILE)pkg-config |
55 | 57 | ||
56 | all: $(FILES) | 58 | all: $(FILES) |
@@ -58,6 +60,9 @@ all: $(FILES) | |||
58 | __BUILD = $(CC) $(CFLAGS) -Wall -Werror -o $@ $(patsubst %.bin,%.c,$(@F)) $(LDFLAGS) | 60 | __BUILD = $(CC) $(CFLAGS) -Wall -Werror -o $@ $(patsubst %.bin,%.c,$(@F)) $(LDFLAGS) |
59 | BUILD = $(__BUILD) > $(@:.bin=.make.output) 2>&1 | 61 | BUILD = $(__BUILD) > $(@:.bin=.make.output) 2>&1 |
60 | 62 | ||
63 | __BUILDXX = $(CXX) $(CXXFLAGS) -Wall -Werror -o $@ $(patsubst %.bin,%.cpp,$(@F)) $(LDFLAGS) | ||
64 | BUILDXX = $(__BUILDXX) > $(@:.bin=.make.output) 2>&1 | ||
65 | |||
61 | ############################### | 66 | ############################### |
62 | 67 | ||
63 | $(OUTPUT)test-all.bin: | 68 | $(OUTPUT)test-all.bin: |
@@ -217,6 +222,9 @@ $(OUTPUT)test-bpf.bin: | |||
217 | $(OUTPUT)test-sdt.bin: | 222 | $(OUTPUT)test-sdt.bin: |
218 | $(BUILD) | 223 | $(BUILD) |
219 | 224 | ||
225 | $(OUTPUT)test-cxx.bin: | ||
226 | $(BUILDXX) -std=gnu++11 | ||
227 | |||
220 | -include $(OUTPUT)*.d | 228 | -include $(OUTPUT)*.d |
221 | 229 | ||
222 | ############################### | 230 | ############################### |
diff --git a/tools/build/feature/test-cxx.cpp b/tools/build/feature/test-cxx.cpp new file mode 100644 index 000000000000..b1dee9a31d6c --- /dev/null +++ b/tools/build/feature/test-cxx.cpp | |||
@@ -0,0 +1,15 @@ | |||
1 | #include <iostream> | ||
2 | #include <memory> | ||
3 | |||
4 | static void print_str(std::string s) | ||
5 | { | ||
6 | std::cout << s << std::endl; | ||
7 | } | ||
8 | |||
9 | int main() | ||
10 | { | ||
11 | std::string s("Hello World!"); | ||
12 | print_str(std::move(s)); | ||
13 | std::cout << "|" << s << "|" << std::endl; | ||
14 | return 0; | ||
15 | } | ||
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c index 0d9f48ec42bb..bc7adb84e679 100644 --- a/tools/hv/hv_kvp_daemon.c +++ b/tools/hv/hv_kvp_daemon.c | |||
@@ -1433,7 +1433,7 @@ int main(int argc, char *argv[]) | |||
1433 | openlog("KVP", 0, LOG_USER); | 1433 | openlog("KVP", 0, LOG_USER); |
1434 | syslog(LOG_INFO, "KVP starting; pid is:%d", getpid()); | 1434 | syslog(LOG_INFO, "KVP starting; pid is:%d", getpid()); |
1435 | 1435 | ||
1436 | kvp_fd = open("/dev/vmbus/hv_kvp", O_RDWR); | 1436 | kvp_fd = open("/dev/vmbus/hv_kvp", O_RDWR | O_CLOEXEC); |
1437 | 1437 | ||
1438 | if (kvp_fd < 0) { | 1438 | if (kvp_fd < 0) { |
1439 | syslog(LOG_ERR, "open /dev/vmbus/hv_kvp failed; error: %d %s", | 1439 | syslog(LOG_ERR, "open /dev/vmbus/hv_kvp failed; error: %d %s", |
diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c index 5d51d6ff08e6..e0829809c897 100644 --- a/tools/hv/hv_vss_daemon.c +++ b/tools/hv/hv_vss_daemon.c | |||
@@ -250,6 +250,9 @@ int main(int argc, char *argv[]) | |||
250 | syslog(LOG_ERR, "/etc/fstab and /proc/mounts"); | 250 | syslog(LOG_ERR, "/etc/fstab and /proc/mounts"); |
251 | } | 251 | } |
252 | break; | 252 | break; |
253 | case VSS_OP_HOT_BACKUP: | ||
254 | syslog(LOG_INFO, "VSS: op=CHECK HOT BACKUP\n"); | ||
255 | break; | ||
253 | default: | 256 | default: |
254 | syslog(LOG_ERR, "Illegal op:%d\n", op); | 257 | syslog(LOG_ERR, "Illegal op:%d\n", op); |
255 | } | 258 | } |
diff --git a/tools/iio/iio_utils.c b/tools/iio/iio_utils.c index 5eb6793f3972..7a6d61c6c012 100644 --- a/tools/iio/iio_utils.c +++ b/tools/iio/iio_utils.c | |||
@@ -121,10 +121,6 @@ int iioutils_get_type(unsigned *is_signed, unsigned *bytes, unsigned *bits_used, | |||
121 | 121 | ||
122 | ret = -ENOENT; | 122 | ret = -ENOENT; |
123 | while (ent = readdir(dp), ent) | 123 | while (ent = readdir(dp), ent) |
124 | /* | ||
125 | * Do we allow devices to override a generic name with | ||
126 | * a specific one? | ||
127 | */ | ||
128 | if ((strcmp(builtname, ent->d_name) == 0) || | 124 | if ((strcmp(builtname, ent->d_name) == 0) || |
129 | (strcmp(builtname_generic, ent->d_name) == 0)) { | 125 | (strcmp(builtname_generic, ent->d_name) == 0)) { |
130 | ret = asprintf(&filename, | 126 | ret = asprintf(&filename, |
@@ -178,6 +174,13 @@ int iioutils_get_type(unsigned *is_signed, unsigned *bytes, unsigned *bits_used, | |||
178 | sysfsfp = 0; | 174 | sysfsfp = 0; |
179 | free(filename); | 175 | free(filename); |
180 | filename = 0; | 176 | filename = 0; |
177 | |||
178 | /* | ||
179 | * Avoid having a more generic entry overwriting | ||
180 | * the settings. | ||
181 | */ | ||
182 | if (strcmp(builtname, ent->d_name) == 0) | ||
183 | break; | ||
181 | } | 184 | } |
182 | 185 | ||
183 | error_close_sysfsfp: | 186 | error_close_sysfsfp: |
diff --git a/tools/iio/lsiio.c b/tools/iio/lsiio.c index 3d650e668252..ab0f5cf16025 100644 --- a/tools/iio/lsiio.c +++ b/tools/iio/lsiio.c | |||
@@ -51,7 +51,8 @@ static int dump_channels(const char *dev_dir_name) | |||
51 | 51 | ||
52 | while (ent = readdir(dp), ent) | 52 | while (ent = readdir(dp), ent) |
53 | if (check_prefix(ent->d_name, "in_") && | 53 | if (check_prefix(ent->d_name, "in_") && |
54 | check_postfix(ent->d_name, "_raw")) | 54 | (check_postfix(ent->d_name, "_raw") || |
55 | check_postfix(ent->d_name, "_input"))) | ||
55 | printf(" %-10s\n", ent->d_name); | 56 | printf(" %-10s\n", ent->d_name); |
56 | 57 | ||
57 | return (closedir(dp) == -1) ? -errno : 0; | 58 | return (closedir(dp) == -1) ? -errno : 0; |
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index da218fec6056..9e5fc168c8a3 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h | |||
@@ -339,7 +339,7 @@ enum bpf_func_id { | |||
339 | BPF_FUNC_skb_change_type, | 339 | BPF_FUNC_skb_change_type, |
340 | 340 | ||
341 | /** | 341 | /** |
342 | * bpf_skb_in_cgroup(skb, map, index) - Check cgroup2 membership of skb | 342 | * bpf_skb_under_cgroup(skb, map, index) - Check cgroup2 membership of skb |
343 | * @skb: pointer to skb | 343 | * @skb: pointer to skb |
344 | * @map: pointer to bpf_map in BPF_MAP_TYPE_CGROUP_ARRAY type | 344 | * @map: pointer to bpf_map in BPF_MAP_TYPE_CGROUP_ARRAY type |
345 | * @index: index of the cgroup in the bpf_map | 345 | * @index: index of the cgroup in the bpf_map |
@@ -348,7 +348,7 @@ enum bpf_func_id { | |||
348 | * == 1 skb succeeded the cgroup2 descendant test | 348 | * == 1 skb succeeded the cgroup2 descendant test |
349 | * < 0 error | 349 | * < 0 error |
350 | */ | 350 | */ |
351 | BPF_FUNC_skb_in_cgroup, | 351 | BPF_FUNC_skb_under_cgroup, |
352 | 352 | ||
353 | /** | 353 | /** |
354 | * bpf_get_hash_recalc(skb) | 354 | * bpf_get_hash_recalc(skb) |
diff --git a/tools/laptop/dslm/.gitignore b/tools/laptop/dslm/.gitignore new file mode 100644 index 000000000000..9fc984e64386 --- /dev/null +++ b/tools/laptop/dslm/.gitignore | |||
@@ -0,0 +1 @@ | |||
dslm | |||
diff --git a/tools/laptop/dslm/Makefile b/tools/laptop/dslm/Makefile new file mode 100644 index 000000000000..ff613b31730b --- /dev/null +++ b/tools/laptop/dslm/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | CC := $(CROSS_COMPILE)gcc | ||
2 | CFLAGS := -I../../usr/include | ||
3 | |||
4 | PROGS := dslm | ||
5 | |||
6 | all: $(PROGS) | ||
7 | |||
8 | clean: | ||
9 | rm -fr $(PROGS) | ||
diff --git a/tools/laptop/dslm/dslm.c b/tools/laptop/dslm/dslm.c new file mode 100644 index 000000000000..d5dd2d4b04d8 --- /dev/null +++ b/tools/laptop/dslm/dslm.c | |||
@@ -0,0 +1,166 @@ | |||
1 | /* | ||
2 | * dslm.c | ||
3 | * Simple Disk Sleep Monitor | ||
4 | * by Bartek Kania | ||
5 | * Licensed under the GPL | ||
6 | */ | ||
7 | #include <unistd.h> | ||
8 | #include <stdlib.h> | ||
9 | #include <stdio.h> | ||
10 | #include <fcntl.h> | ||
11 | #include <errno.h> | ||
12 | #include <time.h> | ||
13 | #include <string.h> | ||
14 | #include <signal.h> | ||
15 | #include <sys/ioctl.h> | ||
16 | #include <linux/hdreg.h> | ||
17 | |||
18 | #ifdef DEBUG | ||
19 | #define D(x) x | ||
20 | #else | ||
21 | #define D(x) | ||
22 | #endif | ||
23 | |||
24 | int endit = 0; | ||
25 | |||
26 | /* Check if the disk is in powersave-mode | ||
27 | * Most of the code is stolen from hdparm. | ||
28 | * 1 = active, 0 = standby/sleep, -1 = unknown */ | ||
29 | static int check_powermode(int fd) | ||
30 | { | ||
31 | unsigned char args[4] = {WIN_CHECKPOWERMODE1,0,0,0}; | ||
32 | int state; | ||
33 | |||
34 | if (ioctl(fd, HDIO_DRIVE_CMD, &args) | ||
35 | && (args[0] = WIN_CHECKPOWERMODE2) /* try again with 0x98 */ | ||
36 | && ioctl(fd, HDIO_DRIVE_CMD, &args)) { | ||
37 | if (errno != EIO || args[0] != 0 || args[1] != 0) { | ||
38 | state = -1; /* "unknown"; */ | ||
39 | } else | ||
40 | state = 0; /* "sleeping"; */ | ||
41 | } else { | ||
42 | state = (args[2] == 255) ? 1 : 0; | ||
43 | } | ||
44 | D(printf(" drive state is: %d\n", state)); | ||
45 | |||
46 | return state; | ||
47 | } | ||
48 | |||
49 | static char *state_name(int i) | ||
50 | { | ||
51 | if (i == -1) return "unknown"; | ||
52 | if (i == 0) return "sleeping"; | ||
53 | if (i == 1) return "active"; | ||
54 | |||
55 | return "internal error"; | ||
56 | } | ||
57 | |||
58 | static char *myctime(time_t time) | ||
59 | { | ||
60 | char *ts = ctime(&time); | ||
61 | ts[strlen(ts) - 1] = 0; | ||
62 | |||
63 | return ts; | ||
64 | } | ||
65 | |||
66 | static void measure(int fd) | ||
67 | { | ||
68 | time_t start_time; | ||
69 | int last_state; | ||
70 | time_t last_time; | ||
71 | int curr_state; | ||
72 | time_t curr_time = 0; | ||
73 | time_t time_diff; | ||
74 | time_t active_time = 0; | ||
75 | time_t sleep_time = 0; | ||
76 | time_t unknown_time = 0; | ||
77 | time_t total_time = 0; | ||
78 | int changes = 0; | ||
79 | float tmp; | ||
80 | |||
81 | printf("Starting measurements\n"); | ||
82 | |||
83 | last_state = check_powermode(fd); | ||
84 | start_time = last_time = time(0); | ||
85 | printf(" System is in state %s\n\n", state_name(last_state)); | ||
86 | |||
87 | while(!endit) { | ||
88 | sleep(1); | ||
89 | curr_state = check_powermode(fd); | ||
90 | |||
91 | if (curr_state != last_state || endit) { | ||
92 | changes++; | ||
93 | curr_time = time(0); | ||
94 | time_diff = curr_time - last_time; | ||
95 | |||
96 | if (last_state == 1) active_time += time_diff; | ||
97 | else if (last_state == 0) sleep_time += time_diff; | ||
98 | else unknown_time += time_diff; | ||
99 | |||
100 | last_state = curr_state; | ||
101 | last_time = curr_time; | ||
102 | |||
103 | printf("%s: State-change to %s\n", myctime(curr_time), | ||
104 | state_name(curr_state)); | ||
105 | } | ||
106 | } | ||
107 | changes--; /* Compensate for SIGINT */ | ||
108 | |||
109 | total_time = time(0) - start_time; | ||
110 | printf("\nTotal running time: %lus\n", curr_time - start_time); | ||
111 | printf(" State changed %d times\n", changes); | ||
112 | |||
113 | tmp = (float)sleep_time / (float)total_time * 100; | ||
114 | printf(" Time in sleep state: %lus (%.2f%%)\n", sleep_time, tmp); | ||
115 | tmp = (float)active_time / (float)total_time * 100; | ||
116 | printf(" Time in active state: %lus (%.2f%%)\n", active_time, tmp); | ||
117 | tmp = (float)unknown_time / (float)total_time * 100; | ||
118 | printf(" Time in unknown state: %lus (%.2f%%)\n", unknown_time, tmp); | ||
119 | } | ||
120 | |||
121 | static void ender(int s) | ||
122 | { | ||
123 | endit = 1; | ||
124 | } | ||
125 | |||
126 | static void usage(void) | ||
127 | { | ||
128 | puts("usage: dslm [-w <time>] <disk>"); | ||
129 | exit(0); | ||
130 | } | ||
131 | |||
132 | int main(int argc, char **argv) | ||
133 | { | ||
134 | int fd; | ||
135 | char *disk = 0; | ||
136 | int settle_time = 60; | ||
137 | |||
138 | /* Parse the simple command-line */ | ||
139 | if (argc == 2) | ||
140 | disk = argv[1]; | ||
141 | else if (argc == 4) { | ||
142 | settle_time = atoi(argv[2]); | ||
143 | disk = argv[3]; | ||
144 | } else | ||
145 | usage(); | ||
146 | |||
147 | if (!(fd = open(disk, O_RDONLY|O_NONBLOCK))) { | ||
148 | printf("Can't open %s, because: %s\n", disk, strerror(errno)); | ||
149 | exit(-1); | ||
150 | } | ||
151 | |||
152 | if (settle_time) { | ||
153 | printf("Waiting %d seconds for the system to settle down to " | ||
154 | "'normal'\n", settle_time); | ||
155 | sleep(settle_time); | ||
156 | } else | ||
157 | puts("Not waiting for system to settle down"); | ||
158 | |||
159 | signal(SIGINT, ender); | ||
160 | |||
161 | measure(fd); | ||
162 | |||
163 | close(fd); | ||
164 | |||
165 | return 0; | ||
166 | } | ||
diff --git a/tools/lib/subcmd/pager.c b/tools/lib/subcmd/pager.c index d50f3b58606b..6518bea926d6 100644 --- a/tools/lib/subcmd/pager.c +++ b/tools/lib/subcmd/pager.c | |||
@@ -3,6 +3,7 @@ | |||
3 | #include <stdio.h> | 3 | #include <stdio.h> |
4 | #include <string.h> | 4 | #include <string.h> |
5 | #include <signal.h> | 5 | #include <signal.h> |
6 | #include <sys/ioctl.h> | ||
6 | #include "pager.h" | 7 | #include "pager.h" |
7 | #include "run-command.h" | 8 | #include "run-command.h" |
8 | #include "sigchain.h" | 9 | #include "sigchain.h" |
@@ -14,6 +15,7 @@ | |||
14 | */ | 15 | */ |
15 | 16 | ||
16 | static int spawned_pager; | 17 | static int spawned_pager; |
18 | static int pager_columns; | ||
17 | 19 | ||
18 | void pager_init(const char *pager_env) | 20 | void pager_init(const char *pager_env) |
19 | { | 21 | { |
@@ -58,9 +60,12 @@ static void wait_for_pager_signal(int signo) | |||
58 | void setup_pager(void) | 60 | void setup_pager(void) |
59 | { | 61 | { |
60 | const char *pager = getenv(subcmd_config.pager_env); | 62 | const char *pager = getenv(subcmd_config.pager_env); |
63 | struct winsize sz; | ||
61 | 64 | ||
62 | if (!isatty(1)) | 65 | if (!isatty(1)) |
63 | return; | 66 | return; |
67 | if (ioctl(1, TIOCGWINSZ, &sz) == 0) | ||
68 | pager_columns = sz.ws_col; | ||
64 | if (!pager) | 69 | if (!pager) |
65 | pager = getenv("PAGER"); | 70 | pager = getenv("PAGER"); |
66 | if (!(pager || access("/usr/bin/pager", X_OK))) | 71 | if (!(pager || access("/usr/bin/pager", X_OK))) |
@@ -98,3 +103,14 @@ int pager_in_use(void) | |||
98 | { | 103 | { |
99 | return spawned_pager; | 104 | return spawned_pager; |
100 | } | 105 | } |
106 | |||
107 | int pager_get_columns(void) | ||
108 | { | ||
109 | char *s; | ||
110 | |||
111 | s = getenv("COLUMNS"); | ||
112 | if (s) | ||
113 | return atoi(s); | ||
114 | |||
115 | return (pager_columns ? pager_columns : 80) - 2; | ||
116 | } | ||
diff --git a/tools/lib/subcmd/pager.h b/tools/lib/subcmd/pager.h index 8b83714ecf73..623f5542d05d 100644 --- a/tools/lib/subcmd/pager.h +++ b/tools/lib/subcmd/pager.h | |||
@@ -5,5 +5,6 @@ extern void pager_init(const char *pager_env); | |||
5 | 5 | ||
6 | extern void setup_pager(void); | 6 | extern void setup_pager(void); |
7 | extern int pager_in_use(void); | 7 | extern int pager_in_use(void); |
8 | extern int pager_get_columns(void); | ||
8 | 9 | ||
9 | #endif /* __SUBCMD_PAGER_H */ | 10 | #endif /* __SUBCMD_PAGER_H */ |
diff --git a/tools/lib/traceevent/kbuffer-parse.c b/tools/lib/traceevent/kbuffer-parse.c index 3bcada3ae05a..65984f1c2974 100644 --- a/tools/lib/traceevent/kbuffer-parse.c +++ b/tools/lib/traceevent/kbuffer-parse.c | |||
@@ -622,6 +622,7 @@ void *kbuffer_read_at_offset(struct kbuffer *kbuf, int offset, | |||
622 | 622 | ||
623 | /* Reset the buffer */ | 623 | /* Reset the buffer */ |
624 | kbuffer_load_subbuffer(kbuf, kbuf->subbuffer); | 624 | kbuffer_load_subbuffer(kbuf, kbuf->subbuffer); |
625 | data = kbuffer_read_event(kbuf, ts); | ||
625 | 626 | ||
626 | while (kbuf->curr < offset) { | 627 | while (kbuf->curr < offset) { |
627 | data = kbuffer_next_event(kbuf, ts); | 628 | data = kbuffer_next_event(kbuf, ts); |
diff --git a/tools/pcmcia/.gitignore b/tools/pcmcia/.gitignore new file mode 100644 index 000000000000..53d081336757 --- /dev/null +++ b/tools/pcmcia/.gitignore | |||
@@ -0,0 +1 @@ | |||
crc32hash | |||
diff --git a/tools/pcmcia/Makefile b/tools/pcmcia/Makefile new file mode 100644 index 000000000000..81a7498c5cd9 --- /dev/null +++ b/tools/pcmcia/Makefile | |||
@@ -0,0 +1,9 @@ | |||
1 | CC := $(CROSS_COMPILE)gcc | ||
2 | CFLAGS := -I../../usr/include | ||
3 | |||
4 | PROGS := crc32hash | ||
5 | |||
6 | all: $(PROGS) | ||
7 | |||
8 | clean: | ||
9 | rm -fr $(PROGS) | ||
diff --git a/tools/pcmcia/crc32hash.c b/tools/pcmcia/crc32hash.c new file mode 100644 index 000000000000..44f8beea7260 --- /dev/null +++ b/tools/pcmcia/crc32hash.c | |||
@@ -0,0 +1,32 @@ | |||
1 | /* crc32hash.c - derived from linux/lib/crc32.c, GNU GPL v2 */ | ||
2 | /* Usage example: | ||
3 | $ ./crc32hash "Dual Speed" | ||
4 | */ | ||
5 | |||
6 | #include <string.h> | ||
7 | #include <stdio.h> | ||
8 | #include <ctype.h> | ||
9 | #include <stdlib.h> | ||
10 | |||
11 | static unsigned int crc32(unsigned char const *p, unsigned int len) | ||
12 | { | ||
13 | int i; | ||
14 | unsigned int crc = 0; | ||
15 | while (len--) { | ||
16 | crc ^= *p++; | ||
17 | for (i = 0; i < 8; i++) | ||
18 | crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0); | ||
19 | } | ||
20 | return crc; | ||
21 | } | ||
22 | |||
23 | int main(int argc, char **argv) { | ||
24 | unsigned int result; | ||
25 | if (argc != 2) { | ||
26 | printf("no string passed as argument\n"); | ||
27 | return -1; | ||
28 | } | ||
29 | result = crc32((unsigned char const *)argv[1], strlen(argv[1])); | ||
30 | printf("0x%x\n", result); | ||
31 | return 0; | ||
32 | } | ||
diff --git a/tools/perf/Documentation/perf-list.txt b/tools/perf/Documentation/perf-list.txt index a126e97a8114..41857cce5e86 100644 --- a/tools/perf/Documentation/perf-list.txt +++ b/tools/perf/Documentation/perf-list.txt | |||
@@ -8,13 +8,23 @@ perf-list - List all symbolic event types | |||
8 | SYNOPSIS | 8 | SYNOPSIS |
9 | -------- | 9 | -------- |
10 | [verse] | 10 | [verse] |
11 | 'perf list' [hw|sw|cache|tracepoint|pmu|event_glob] | 11 | 'perf list' [--no-desc] [--long-desc] [hw|sw|cache|tracepoint|pmu|event_glob] |
12 | 12 | ||
13 | DESCRIPTION | 13 | DESCRIPTION |
14 | ----------- | 14 | ----------- |
15 | This command displays the symbolic event types which can be selected in the | 15 | This command displays the symbolic event types which can be selected in the |
16 | various perf commands with the -e option. | 16 | various perf commands with the -e option. |
17 | 17 | ||
18 | OPTIONS | ||
19 | ------- | ||
20 | --no-desc:: | ||
21 | Don't print descriptions. | ||
22 | |||
23 | -v:: | ||
24 | --long-desc:: | ||
25 | Print longer event descriptions. | ||
26 | |||
27 | |||
18 | [[EVENT_MODIFIERS]] | 28 | [[EVENT_MODIFIERS]] |
19 | EVENT MODIFIERS | 29 | EVENT MODIFIERS |
20 | --------------- | 30 | --------------- |
diff --git a/tools/perf/Documentation/tips.txt b/tools/perf/Documentation/tips.txt index 5950b5a24efd..8a6479c0eac9 100644 --- a/tools/perf/Documentation/tips.txt +++ b/tools/perf/Documentation/tips.txt | |||
@@ -28,3 +28,7 @@ To change sampling frequency to 100 Hz: perf record -F 100 | |||
28 | See assembly instructions with percentage: perf annotate <symbol> | 28 | See assembly instructions with percentage: perf annotate <symbol> |
29 | If you prefer Intel style assembly, try: perf annotate -M intel | 29 | If you prefer Intel style assembly, try: perf annotate -M intel |
30 | For hierarchical output, try: perf report --hierarchy | 30 | For hierarchical output, try: perf report --hierarchy |
31 | Order by the overhead of source file name and line number: perf report -s srcline | ||
32 | System-wide collection from all CPUs: perf record -a | ||
33 | Show current config key-value pairs: perf config --list | ||
34 | Show user configuration overrides: perf config --user --list | ||
diff --git a/tools/perf/Makefile.perf b/tools/perf/Makefile.perf index d710db16b963..982d6439bb07 100644 --- a/tools/perf/Makefile.perf +++ b/tools/perf/Makefile.perf | |||
@@ -144,6 +144,10 @@ $(call allow-override,LD,$(CROSS_COMPILE)ld) | |||
144 | 144 | ||
145 | LD += $(EXTRA_LDFLAGS) | 145 | LD += $(EXTRA_LDFLAGS) |
146 | 146 | ||
147 | HOSTCC ?= gcc | ||
148 | HOSTLD ?= ld | ||
149 | HOSTAR ?= ar | ||
150 | |||
147 | PKG_CONFIG = $(CROSS_COMPILE)pkg-config | 151 | PKG_CONFIG = $(CROSS_COMPILE)pkg-config |
148 | 152 | ||
149 | RM = rm -f | 153 | RM = rm -f |
@@ -345,8 +349,18 @@ strip: $(PROGRAMS) $(OUTPUT)perf | |||
345 | PERF_IN := $(OUTPUT)perf-in.o | 349 | PERF_IN := $(OUTPUT)perf-in.o |
346 | 350 | ||
347 | export srctree OUTPUT RM CC LD AR CFLAGS V BISON FLEX AWK | 351 | export srctree OUTPUT RM CC LD AR CFLAGS V BISON FLEX AWK |
352 | export HOSTCC HOSTLD HOSTAR | ||
348 | include $(srctree)/tools/build/Makefile.include | 353 | include $(srctree)/tools/build/Makefile.include |
349 | 354 | ||
355 | JEVENTS := $(OUTPUT)pmu-events/jevents | ||
356 | JEVENTS_IN := $(OUTPUT)pmu-events/jevents-in.o | ||
357 | |||
358 | PMU_EVENTS_IN := $(OUTPUT)pmu-events/pmu-events-in.o | ||
359 | |||
360 | export JEVENTS | ||
361 | |||
362 | build := -f $(srctree)/tools/build/Makefile.build dir=. obj | ||
363 | |||
350 | $(PERF_IN): prepare FORCE | 364 | $(PERF_IN): prepare FORCE |
351 | @(test -f ../../include/uapi/linux/perf_event.h && ( \ | 365 | @(test -f ../../include/uapi/linux/perf_event.h && ( \ |
352 | (diff -B ../include/uapi/linux/perf_event.h ../../include/uapi/linux/perf_event.h >/dev/null) \ | 366 | (diff -B ../include/uapi/linux/perf_event.h ../../include/uapi/linux/perf_event.h >/dev/null) \ |
@@ -443,9 +457,18 @@ $(PERF_IN): prepare FORCE | |||
443 | || echo "Warning: tools/include/uapi/linux/mman.h differs from kernel" >&2 )) || true | 457 | || echo "Warning: tools/include/uapi/linux/mman.h differs from kernel" >&2 )) || true |
444 | $(Q)$(MAKE) $(build)=perf | 458 | $(Q)$(MAKE) $(build)=perf |
445 | 459 | ||
446 | $(OUTPUT)perf: $(PERFLIBS) $(PERF_IN) $(LIBTRACEEVENT_DYNAMIC_LIST) | 460 | $(JEVENTS_IN): FORCE |
461 | $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events obj=jevents | ||
462 | |||
463 | $(JEVENTS): $(JEVENTS_IN) | ||
464 | $(QUIET_LINK)$(HOSTCC) $(JEVENTS_IN) -o $@ | ||
465 | |||
466 | $(PMU_EVENTS_IN): $(JEVENTS) FORCE | ||
467 | $(Q)$(MAKE) -f $(srctree)/tools/build/Makefile.build dir=pmu-events obj=pmu-events | ||
468 | |||
469 | $(OUTPUT)perf: $(PERFLIBS) $(PERF_IN) $(PMU_EVENTS_IN) $(LIBTRACEEVENT_DYNAMIC_LIST) | ||
447 | $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS) \ | 470 | $(QUIET_LINK)$(CC) $(CFLAGS) $(LDFLAGS) $(LIBTRACEEVENT_DYNAMIC_LIST_LDFLAGS) \ |
448 | $(PERF_IN) $(LIBS) -o $@ | 471 | $(PERF_IN) $(PMU_EVENTS_IN) $(LIBS) -o $@ |
449 | 472 | ||
450 | $(GTK_IN): fixdep FORCE | 473 | $(GTK_IN): fixdep FORCE |
451 | $(Q)$(MAKE) $(build)=gtk | 474 | $(Q)$(MAKE) $(build)=gtk |
@@ -474,6 +497,8 @@ perf.spec $(SCRIPTS) \ | |||
474 | ifneq ($(OUTPUT),) | 497 | ifneq ($(OUTPUT),) |
475 | %.o: $(OUTPUT)%.o | 498 | %.o: $(OUTPUT)%.o |
476 | @echo " # Redirected target $@ => $(OUTPUT)$@" | 499 | @echo " # Redirected target $@ => $(OUTPUT)$@" |
500 | pmu-events/%.o: $(OUTPUT)pmu-events/%.o | ||
501 | @echo " # Redirected target $@ => $(OUTPUT)$@" | ||
477 | util/%.o: $(OUTPUT)util/%.o | 502 | util/%.o: $(OUTPUT)util/%.o |
478 | @echo " # Redirected target $@ => $(OUTPUT)$@" | 503 | @echo " # Redirected target $@ => $(OUTPUT)$@" |
479 | bench/%.o: $(OUTPUT)bench/%.o | 504 | bench/%.o: $(OUTPUT)bench/%.o |
@@ -729,10 +754,11 @@ clean:: $(LIBTRACEEVENT)-clean $(LIBAPI)-clean $(LIBBPF)-clean $(LIBSUBCMD)-clea | |||
729 | $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS) | 754 | $(call QUIET_CLEAN, core-objs) $(RM) $(LIB_FILE) $(OUTPUT)perf-archive $(OUTPUT)perf-with-kcore $(LANG_BINDINGS) |
730 | $(Q)find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete | 755 | $(Q)find $(if $(OUTPUT),$(OUTPUT),.) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete |
731 | $(Q)$(RM) $(OUTPUT).config-detected | 756 | $(Q)$(RM) $(OUTPUT).config-detected |
732 | $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 | 757 | $(call QUIET_CLEAN, core-progs) $(RM) $(ALL_PROGRAMS) perf perf-read-vdso32 perf-read-vdsox32 $(OUTPUT)pmu-events/jevents |
733 | $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \ | 758 | $(call QUIET_CLEAN, core-gen) $(RM) *.spec *.pyc *.pyo */*.pyc */*.pyo $(OUTPUT)common-cmds.h TAGS tags cscope* $(OUTPUT)PERF-VERSION-FILE $(OUTPUT)FEATURE-DUMP $(OUTPUT)util/*-bison* $(OUTPUT)util/*-flex* \ |
734 | $(OUTPUT)util/intel-pt-decoder/inat-tables.c $(OUTPUT)fixdep \ | 759 | $(OUTPUT)util/intel-pt-decoder/inat-tables.c $(OUTPUT)fixdep \ |
735 | $(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c | 760 | $(OUTPUT)tests/llvm-src-{base,kbuild,prologue,relocation}.c \ |
761 | $(OUTPUT)pmu-events/pmu-events.c | ||
736 | $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean | 762 | $(QUIET_SUBDIR0)Documentation $(QUIET_SUBDIR1) clean |
737 | $(python-clean) | 763 | $(python-clean) |
738 | 764 | ||
diff --git a/tools/perf/arch/powerpc/util/header.c b/tools/perf/arch/powerpc/util/header.c index f8ccee132867..9aaa6f5a9347 100644 --- a/tools/perf/arch/powerpc/util/header.c +++ b/tools/perf/arch/powerpc/util/header.c | |||
@@ -32,3 +32,14 @@ get_cpuid(char *buffer, size_t sz) | |||
32 | } | 32 | } |
33 | return -1; | 33 | return -1; |
34 | } | 34 | } |
35 | |||
36 | char * | ||
37 | get_cpuid_str(void) | ||
38 | { | ||
39 | char *bufp; | ||
40 | |||
41 | if (asprintf(&bufp, "%.8lx", mfspr(SPRN_PVR)) < 0) | ||
42 | bufp = NULL; | ||
43 | |||
44 | return bufp; | ||
45 | } | ||
diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c index ed9d5d15d5b6..1030a6e504bb 100644 --- a/tools/perf/arch/powerpc/util/sym-handling.c +++ b/tools/perf/arch/powerpc/util/sym-handling.c | |||
@@ -82,7 +82,8 @@ void arch__fix_tev_from_maps(struct perf_probe_event *pev, | |||
82 | * | 82 | * |
83 | * In addition, we shouldn't specify an offset for kretprobes. | 83 | * In addition, we shouldn't specify an offset for kretprobes. |
84 | */ | 84 | */ |
85 | if (pev->point.offset || pev->point.retprobe || !map || !sym) | 85 | if (pev->point.offset || (!pev->uprobes && pev->point.retprobe) || |
86 | !map || !sym) | ||
86 | return; | 87 | return; |
87 | 88 | ||
88 | lep_offset = PPC64_LOCAL_ENTRY_OFFSET(sym->arch_sym); | 89 | lep_offset = PPC64_LOCAL_ENTRY_OFFSET(sym->arch_sym); |
diff --git a/tools/perf/arch/x86/util/header.c b/tools/perf/arch/x86/util/header.c index 146d12a1cec0..a74a48db26f5 100644 --- a/tools/perf/arch/x86/util/header.c +++ b/tools/perf/arch/x86/util/header.c | |||
@@ -19,8 +19,8 @@ cpuid(unsigned int op, unsigned int *a, unsigned int *b, unsigned int *c, | |||
19 | : "a" (op)); | 19 | : "a" (op)); |
20 | } | 20 | } |
21 | 21 | ||
22 | int | 22 | static int |
23 | get_cpuid(char *buffer, size_t sz) | 23 | __get_cpuid(char *buffer, size_t sz, const char *fmt) |
24 | { | 24 | { |
25 | unsigned int a, b, c, d, lvl; | 25 | unsigned int a, b, c, d, lvl; |
26 | int family = -1, model = -1, step = -1; | 26 | int family = -1, model = -1, step = -1; |
@@ -48,7 +48,7 @@ get_cpuid(char *buffer, size_t sz) | |||
48 | if (family >= 0x6) | 48 | if (family >= 0x6) |
49 | model += ((a >> 16) & 0xf) << 4; | 49 | model += ((a >> 16) & 0xf) << 4; |
50 | } | 50 | } |
51 | nb = scnprintf(buffer, sz, "%s,%u,%u,%u$", vendor, family, model, step); | 51 | nb = scnprintf(buffer, sz, fmt, vendor, family, model, step); |
52 | 52 | ||
53 | /* look for end marker to ensure the entire data fit */ | 53 | /* look for end marker to ensure the entire data fit */ |
54 | if (strchr(buffer, '$')) { | 54 | if (strchr(buffer, '$')) { |
@@ -57,3 +57,21 @@ get_cpuid(char *buffer, size_t sz) | |||
57 | } | 57 | } |
58 | return -1; | 58 | return -1; |
59 | } | 59 | } |
60 | |||
61 | int | ||
62 | get_cpuid(char *buffer, size_t sz) | ||
63 | { | ||
64 | return __get_cpuid(buffer, sz, "%s,%u,%u,%u$"); | ||
65 | } | ||
66 | |||
67 | char * | ||
68 | get_cpuid_str(void) | ||
69 | { | ||
70 | char *buf = malloc(128); | ||
71 | |||
72 | if (__get_cpuid(buf, 128, "%s-%u-%X$") < 0) { | ||
73 | free(buf); | ||
74 | return NULL; | ||
75 | } | ||
76 | return buf; | ||
77 | } | ||
diff --git a/tools/perf/builtin-list.c b/tools/perf/builtin-list.c index 88ee419e5189..ba9322ff858b 100644 --- a/tools/perf/builtin-list.c +++ b/tools/perf/builtin-list.c | |||
@@ -16,16 +16,23 @@ | |||
16 | #include "util/pmu.h" | 16 | #include "util/pmu.h" |
17 | #include <subcmd/parse-options.h> | 17 | #include <subcmd/parse-options.h> |
18 | 18 | ||
19 | static bool desc_flag = true; | ||
20 | |||
19 | int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) | 21 | int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) |
20 | { | 22 | { |
21 | int i; | 23 | int i; |
22 | bool raw_dump = false; | 24 | bool raw_dump = false; |
25 | bool long_desc_flag = false; | ||
23 | struct option list_options[] = { | 26 | struct option list_options[] = { |
24 | OPT_BOOLEAN(0, "raw-dump", &raw_dump, "Dump raw events"), | 27 | OPT_BOOLEAN(0, "raw-dump", &raw_dump, "Dump raw events"), |
28 | OPT_BOOLEAN('d', "desc", &desc_flag, | ||
29 | "Print extra event descriptions. --no-desc to not print."), | ||
30 | OPT_BOOLEAN('v', "long-desc", &long_desc_flag, | ||
31 | "Print longer event descriptions."), | ||
25 | OPT_END() | 32 | OPT_END() |
26 | }; | 33 | }; |
27 | const char * const list_usage[] = { | 34 | const char * const list_usage[] = { |
28 | "perf list [hw|sw|cache|tracepoint|pmu|sdt|event_glob]", | 35 | "perf list [<options>] [hw|sw|cache|tracepoint|pmu|sdt|event_glob]", |
29 | NULL | 36 | NULL |
30 | }; | 37 | }; |
31 | 38 | ||
@@ -40,7 +47,7 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) | |||
40 | printf("\nList of pre-defined events (to be used in -e):\n\n"); | 47 | printf("\nList of pre-defined events (to be used in -e):\n\n"); |
41 | 48 | ||
42 | if (argc == 0) { | 49 | if (argc == 0) { |
43 | print_events(NULL, raw_dump); | 50 | print_events(NULL, raw_dump, !desc_flag, long_desc_flag); |
44 | return 0; | 51 | return 0; |
45 | } | 52 | } |
46 | 53 | ||
@@ -61,14 +68,16 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) | |||
61 | strcmp(argv[i], "hwcache") == 0) | 68 | strcmp(argv[i], "hwcache") == 0) |
62 | print_hwcache_events(NULL, raw_dump); | 69 | print_hwcache_events(NULL, raw_dump); |
63 | else if (strcmp(argv[i], "pmu") == 0) | 70 | else if (strcmp(argv[i], "pmu") == 0) |
64 | print_pmu_events(NULL, raw_dump); | 71 | print_pmu_events(NULL, raw_dump, !desc_flag, |
72 | long_desc_flag); | ||
65 | else if (strcmp(argv[i], "sdt") == 0) | 73 | else if (strcmp(argv[i], "sdt") == 0) |
66 | print_sdt_events(NULL, NULL, raw_dump); | 74 | print_sdt_events(NULL, NULL, raw_dump); |
67 | else if ((sep = strchr(argv[i], ':')) != NULL) { | 75 | else if ((sep = strchr(argv[i], ':')) != NULL) { |
68 | int sep_idx; | 76 | int sep_idx; |
69 | 77 | ||
70 | if (sep == NULL) { | 78 | if (sep == NULL) { |
71 | print_events(argv[i], raw_dump); | 79 | print_events(argv[i], raw_dump, !desc_flag, |
80 | long_desc_flag); | ||
72 | continue; | 81 | continue; |
73 | } | 82 | } |
74 | sep_idx = sep - argv[i]; | 83 | sep_idx = sep - argv[i]; |
@@ -90,7 +99,8 @@ int cmd_list(int argc, const char **argv, const char *prefix __maybe_unused) | |||
90 | print_symbol_events(s, PERF_TYPE_SOFTWARE, | 99 | print_symbol_events(s, PERF_TYPE_SOFTWARE, |
91 | event_symbols_sw, PERF_COUNT_SW_MAX, raw_dump); | 100 | event_symbols_sw, PERF_COUNT_SW_MAX, raw_dump); |
92 | print_hwcache_events(s, raw_dump); | 101 | print_hwcache_events(s, raw_dump); |
93 | print_pmu_events(s, raw_dump); | 102 | print_pmu_events(s, raw_dump, !desc_flag, |
103 | long_desc_flag); | ||
94 | print_tracepoint_events(NULL, s, raw_dump); | 104 | print_tracepoint_events(NULL, s, raw_dump); |
95 | print_sdt_events(NULL, s, raw_dump); | 105 | print_sdt_events(NULL, s, raw_dump); |
96 | free(s); | 106 | free(s); |
diff --git a/tools/perf/pmu-events/Build b/tools/perf/pmu-events/Build new file mode 100644 index 000000000000..9213a1273697 --- /dev/null +++ b/tools/perf/pmu-events/Build | |||
@@ -0,0 +1,13 @@ | |||
1 | hostprogs := jevents | ||
2 | |||
3 | jevents-y += json.o jsmn.o jevents.o | ||
4 | pmu-events-y += pmu-events.o | ||
5 | JDIR = pmu-events/arch/$(ARCH) | ||
6 | JSON = $(shell [ -d $(JDIR) ] && \ | ||
7 | find $(JDIR) -name '*.json' -o -name 'mapfile.csv') | ||
8 | # | ||
9 | # Locate/process JSON files in pmu-events/arch/ | ||
10 | # directory and create tables in pmu-events.c. | ||
11 | # | ||
12 | $(OUTPUT)pmu-events/pmu-events.c: $(JSON) $(JEVENTS) | ||
13 | $(Q)$(call echo-cmd,gen)$(JEVENTS) $(ARCH) pmu-events/arch $(OUTPUT)pmu-events/pmu-events.c $(V) | ||
diff --git a/tools/perf/pmu-events/README b/tools/perf/pmu-events/README new file mode 100644 index 000000000000..1408ade0d773 --- /dev/null +++ b/tools/perf/pmu-events/README | |||
@@ -0,0 +1,147 @@ | |||
1 | |||
2 | The contents of this directory allow users to specify PMU events in their | ||
3 | CPUs by their symbolic names rather than raw event codes (see example below). | ||
4 | |||
5 | The main program in this directory, is the 'jevents', which is built and | ||
6 | executed _BEFORE_ the perf binary itself is built. | ||
7 | |||
8 | The 'jevents' program tries to locate and process JSON files in the directory | ||
9 | tree tools/perf/pmu-events/arch/foo. | ||
10 | |||
11 | - Regular files with '.json' extension in the name are assumed to be | ||
12 | JSON files, each of which describes a set of PMU events. | ||
13 | |||
14 | - Regular files with basename starting with 'mapfile.csv' are assumed | ||
15 | to be a CSV file that maps a specific CPU to its set of PMU events. | ||
16 | (see below for mapfile format) | ||
17 | |||
18 | - Directories are traversed, but all other files are ignored. | ||
19 | |||
20 | The PMU events supported by a CPU model are expected to grouped into topics | ||
21 | such as Pipelining, Cache, Memory, Floating-point etc. All events for a topic | ||
22 | should be placed in a separate JSON file - where the file name identifies | ||
23 | the topic. Eg: "Floating-point.json". | ||
24 | |||
25 | All the topic JSON files for a CPU model/family should be in a separate | ||
26 | sub directory. Thus for the Silvermont X86 CPU: | ||
27 | |||
28 | $ ls tools/perf/pmu-events/arch/x86/Silvermont_core | ||
29 | Cache.json Memory.json Virtual-Memory.json | ||
30 | Frontend.json Pipeline.json | ||
31 | |||
32 | Using the JSON files and the mapfile, 'jevents' generates the C source file, | ||
33 | 'pmu-events.c', which encodes the two sets of tables: | ||
34 | |||
35 | - Set of 'PMU events tables' for all known CPUs in the architecture, | ||
36 | (one table like the following, per JSON file; table name 'pme_power8' | ||
37 | is derived from JSON file name, 'power8.json'). | ||
38 | |||
39 | struct pmu_event pme_power8[] = { | ||
40 | |||
41 | ... | ||
42 | |||
43 | { | ||
44 | .name = "pm_1plus_ppc_cmpl", | ||
45 | .event = "event=0x100f2", | ||
46 | .desc = "1 or more ppc insts finished,", | ||
47 | }, | ||
48 | |||
49 | ... | ||
50 | } | ||
51 | |||
52 | - A 'mapping table' that maps each CPU of the architecture, to its | ||
53 | 'PMU events table' | ||
54 | |||
55 | struct pmu_events_map pmu_events_map[] = { | ||
56 | { | ||
57 | .cpuid = "004b0000", | ||
58 | .version = "1", | ||
59 | .type = "core", | ||
60 | .table = pme_power8 | ||
61 | }, | ||
62 | ... | ||
63 | |||
64 | }; | ||
65 | |||
66 | After the 'pmu-events.c' is generated, it is compiled and the resulting | ||
67 | 'pmu-events.o' is added to 'libperf.a' which is then used to build perf. | ||
68 | |||
69 | NOTES: | ||
70 | 1. Several CPUs can support same set of events and hence use a common | ||
71 | JSON file. Hence several entries in the pmu_events_map[] could map | ||
72 | to a single 'PMU events table'. | ||
73 | |||
74 | 2. The 'pmu-events.h' has an extern declaration for the mapping table | ||
75 | and the generated 'pmu-events.c' defines this table. | ||
76 | |||
77 | 3. _All_ known CPU tables for architecture are included in the perf | ||
78 | binary. | ||
79 | |||
80 | At run time, perf determines the actual CPU it is running on, finds the | ||
81 | matching events table and builds aliases for those events. This allows | ||
82 | users to specify events by their name: | ||
83 | |||
84 | $ perf stat -e pm_1plus_ppc_cmpl sleep 1 | ||
85 | |||
86 | where 'pm_1plus_ppc_cmpl' is a Power8 PMU event. | ||
87 | |||
88 | In case of errors when processing files in the tools/perf/pmu-events/arch | ||
89 | directory, 'jevents' tries to create an empty mapping file to allow the perf | ||
90 | build to succeed even if the PMU event aliases cannot be used. | ||
91 | |||
92 | However some errors in processing may cause the perf build to fail. | ||
93 | |||
94 | Mapfile format | ||
95 | =============== | ||
96 | |||
97 | The mapfile enables multiple CPU models to share a single set of PMU events. | ||
98 | It is required even if such mapping is 1:1. | ||
99 | |||
100 | The mapfile.csv format is expected to be: | ||
101 | |||
102 | Header line | ||
103 | CPUID,Version,Dir/path/name,Type | ||
104 | |||
105 | where: | ||
106 | |||
107 | Comma: | ||
108 | is the required field delimiter (i.e other fields cannot | ||
109 | have commas within them). | ||
110 | |||
111 | Comments: | ||
112 | Lines in which the first character is either '\n' or '#' | ||
113 | are ignored. | ||
114 | |||
115 | Header line | ||
116 | The header line is the first line in the file, which is | ||
117 | always _IGNORED_. It can empty. | ||
118 | |||
119 | CPUID: | ||
120 | CPUID is an arch-specific char string, that can be used | ||
121 | to identify CPU (and associate it with a set of PMU events | ||
122 | it supports). Multiple CPUIDS can point to the same | ||
123 | File/path/name.json. | ||
124 | |||
125 | Example: | ||
126 | CPUID == 'GenuineIntel-6-2E' (on x86). | ||
127 | CPUID == '004b0100' (PVR value in Powerpc) | ||
128 | Version: | ||
129 | is the Version of the mapfile. | ||
130 | |||
131 | Dir/path/name: | ||
132 | is the pathname to the directory containing the CPU's JSON | ||
133 | files, relative to the directory containing the mapfile.csv | ||
134 | |||
135 | Type: | ||
136 | indicates whether the events or "core" or "uncore" events. | ||
137 | |||
138 | |||
139 | Eg: | ||
140 | |||
141 | $ grep Silvermont tools/perf/pmu-events/arch/x86/mapfile.csv | ||
142 | GenuineIntel-6-37,V13,Silvermont_core,core | ||
143 | GenuineIntel-6-4D,V13,Silvermont_core,core | ||
144 | GenuineIntel-6-4C,V13,Silvermont_core,core | ||
145 | |||
146 | i.e the three CPU models use the JSON files (i.e PMU events) listed | ||
147 | in the directory 'tools/perf/pmu-events/arch/x86/Silvermont_core'. | ||
diff --git a/tools/perf/pmu-events/jevents.c b/tools/perf/pmu-events/jevents.c new file mode 100644 index 000000000000..41611d7f9873 --- /dev/null +++ b/tools/perf/pmu-events/jevents.c | |||
@@ -0,0 +1,814 @@ | |||
1 | #define _XOPEN_SOURCE 500 /* needed for nftw() */ | ||
2 | #define _GNU_SOURCE /* needed for asprintf() */ | ||
3 | |||
4 | /* Parse event JSON files */ | ||
5 | |||
6 | /* | ||
7 | * Copyright (c) 2014, Intel Corporation | ||
8 | * All rights reserved. | ||
9 | * | ||
10 | * Redistribution and use in source and binary forms, with or without | ||
11 | * modification, are permitted provided that the following conditions are met: | ||
12 | * | ||
13 | * 1. Redistributions of source code must retain the above copyright notice, | ||
14 | * this list of conditions and the following disclaimer. | ||
15 | * | ||
16 | * 2. Redistributions in binary form must reproduce the above copyright | ||
17 | * notice, this list of conditions and the following disclaimer in the | ||
18 | * documentation and/or other materials provided with the distribution. | ||
19 | * | ||
20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
22 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | ||
23 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | ||
24 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | ||
25 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
26 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
27 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | ||
29 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
30 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | ||
31 | * OF THE POSSIBILITY OF SUCH DAMAGE. | ||
32 | */ | ||
33 | |||
34 | #include <stdio.h> | ||
35 | #include <stdlib.h> | ||
36 | #include <errno.h> | ||
37 | #include <string.h> | ||
38 | #include <ctype.h> | ||
39 | #include <unistd.h> | ||
40 | #include <stdarg.h> | ||
41 | #include <libgen.h> | ||
42 | #include <dirent.h> | ||
43 | #include <sys/time.h> /* getrlimit */ | ||
44 | #include <sys/resource.h> /* getrlimit */ | ||
45 | #include <ftw.h> | ||
46 | #include <sys/stat.h> | ||
47 | #include "jsmn.h" | ||
48 | #include "json.h" | ||
49 | #include "jevents.h" | ||
50 | |||
51 | #ifndef __maybe_unused | ||
52 | #define __maybe_unused __attribute__((unused)) | ||
53 | #endif | ||
54 | |||
55 | int verbose; | ||
56 | char *prog; | ||
57 | |||
58 | int eprintf(int level, int var, const char *fmt, ...) | ||
59 | { | ||
60 | |||
61 | int ret; | ||
62 | va_list args; | ||
63 | |||
64 | if (var < level) | ||
65 | return 0; | ||
66 | |||
67 | va_start(args, fmt); | ||
68 | |||
69 | ret = vfprintf(stderr, fmt, args); | ||
70 | |||
71 | va_end(args); | ||
72 | |||
73 | return ret; | ||
74 | } | ||
75 | |||
76 | __attribute__((weak)) char *get_cpu_str(void) | ||
77 | { | ||
78 | return NULL; | ||
79 | } | ||
80 | |||
81 | static void addfield(char *map, char **dst, const char *sep, | ||
82 | const char *a, jsmntok_t *bt) | ||
83 | { | ||
84 | unsigned int len = strlen(a) + 1 + strlen(sep); | ||
85 | int olen = *dst ? strlen(*dst) : 0; | ||
86 | int blen = bt ? json_len(bt) : 0; | ||
87 | char *out; | ||
88 | |||
89 | out = realloc(*dst, len + olen + blen); | ||
90 | if (!out) { | ||
91 | /* Don't add field in this case */ | ||
92 | return; | ||
93 | } | ||
94 | *dst = out; | ||
95 | |||
96 | if (!olen) | ||
97 | *(*dst) = 0; | ||
98 | else | ||
99 | strcat(*dst, sep); | ||
100 | strcat(*dst, a); | ||
101 | if (bt) | ||
102 | strncat(*dst, map + bt->start, blen); | ||
103 | } | ||
104 | |||
105 | static void fixname(char *s) | ||
106 | { | ||
107 | for (; *s; s++) | ||
108 | *s = tolower(*s); | ||
109 | } | ||
110 | |||
111 | static void fixdesc(char *s) | ||
112 | { | ||
113 | char *e = s + strlen(s); | ||
114 | |||
115 | /* Remove trailing dots that look ugly in perf list */ | ||
116 | --e; | ||
117 | while (e >= s && isspace(*e)) | ||
118 | --e; | ||
119 | if (*e == '.') | ||
120 | *e = 0; | ||
121 | } | ||
122 | |||
123 | static struct msrmap { | ||
124 | const char *num; | ||
125 | const char *pname; | ||
126 | } msrmap[] = { | ||
127 | { "0x3F6", "ldlat=" }, | ||
128 | { "0x1A6", "offcore_rsp=" }, | ||
129 | { "0x1A7", "offcore_rsp=" }, | ||
130 | { "0x3F7", "frontend=" }, | ||
131 | { NULL, NULL } | ||
132 | }; | ||
133 | |||
134 | static struct field { | ||
135 | const char *field; | ||
136 | const char *kernel; | ||
137 | } fields[] = { | ||
138 | { "EventCode", "event=" }, | ||
139 | { "UMask", "umask=" }, | ||
140 | { "CounterMask", "cmask=" }, | ||
141 | { "Invert", "inv=" }, | ||
142 | { "AnyThread", "any=" }, | ||
143 | { "EdgeDetect", "edge=" }, | ||
144 | { "SampleAfterValue", "period=" }, | ||
145 | { NULL, NULL } | ||
146 | }; | ||
147 | |||
148 | static void cut_comma(char *map, jsmntok_t *newval) | ||
149 | { | ||
150 | int i; | ||
151 | |||
152 | /* Cut off everything after comma */ | ||
153 | for (i = newval->start; i < newval->end; i++) { | ||
154 | if (map[i] == ',') | ||
155 | newval->end = i; | ||
156 | } | ||
157 | } | ||
158 | |||
159 | static int match_field(char *map, jsmntok_t *field, int nz, | ||
160 | char **event, jsmntok_t *val) | ||
161 | { | ||
162 | struct field *f; | ||
163 | jsmntok_t newval = *val; | ||
164 | |||
165 | for (f = fields; f->field; f++) | ||
166 | if (json_streq(map, field, f->field) && nz) { | ||
167 | cut_comma(map, &newval); | ||
168 | addfield(map, event, ",", f->kernel, &newval); | ||
169 | return 1; | ||
170 | } | ||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static struct msrmap *lookup_msr(char *map, jsmntok_t *val) | ||
175 | { | ||
176 | jsmntok_t newval = *val; | ||
177 | static bool warned; | ||
178 | int i; | ||
179 | |||
180 | cut_comma(map, &newval); | ||
181 | for (i = 0; msrmap[i].num; i++) | ||
182 | if (json_streq(map, &newval, msrmap[i].num)) | ||
183 | return &msrmap[i]; | ||
184 | if (!warned) { | ||
185 | warned = true; | ||
186 | pr_err("%s: Unknown MSR in event file %.*s\n", prog, | ||
187 | json_len(val), map + val->start); | ||
188 | } | ||
189 | return NULL; | ||
190 | } | ||
191 | |||
192 | #define EXPECT(e, t, m) do { if (!(e)) { \ | ||
193 | jsmntok_t *loc = (t); \ | ||
194 | if (!(t)->start && (t) > tokens) \ | ||
195 | loc = (t) - 1; \ | ||
196 | pr_err("%s:%d: " m ", got %s\n", fn, \ | ||
197 | json_line(map, loc), \ | ||
198 | json_name(t)); \ | ||
199 | goto out_free; \ | ||
200 | } } while (0) | ||
201 | |||
202 | #define TOPIC_DEPTH 256 | ||
203 | static char *topic_array[TOPIC_DEPTH]; | ||
204 | static int topic_level; | ||
205 | |||
206 | static char *get_topic(void) | ||
207 | { | ||
208 | char *tp_old, *tp = NULL; | ||
209 | int i; | ||
210 | |||
211 | for (i = 0; i < topic_level + 1; i++) { | ||
212 | int n; | ||
213 | |||
214 | tp_old = tp; | ||
215 | n = asprintf(&tp, "%s%s", tp ?: "", topic_array[i]); | ||
216 | if (n < 0) { | ||
217 | pr_info("%s: asprintf() error %s\n", prog); | ||
218 | return NULL; | ||
219 | } | ||
220 | free(tp_old); | ||
221 | } | ||
222 | |||
223 | for (i = 0; i < (int) strlen(tp); i++) { | ||
224 | char c = tp[i]; | ||
225 | |||
226 | if (c == '-') | ||
227 | tp[i] = ' '; | ||
228 | else if (c == '.') { | ||
229 | tp[i] = '\0'; | ||
230 | break; | ||
231 | } | ||
232 | } | ||
233 | |||
234 | return tp; | ||
235 | } | ||
236 | |||
237 | static int add_topic(int level, char *bname) | ||
238 | { | ||
239 | char *topic; | ||
240 | |||
241 | level -= 2; | ||
242 | |||
243 | if (level >= TOPIC_DEPTH) | ||
244 | return -EINVAL; | ||
245 | |||
246 | topic = strdup(bname); | ||
247 | if (!topic) { | ||
248 | pr_info("%s: strdup() error %s for file %s\n", prog, | ||
249 | strerror(errno), bname); | ||
250 | return -ENOMEM; | ||
251 | } | ||
252 | |||
253 | free(topic_array[topic_level]); | ||
254 | topic_array[topic_level] = topic; | ||
255 | topic_level = level; | ||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | struct perf_entry_data { | ||
260 | FILE *outfp; | ||
261 | char *topic; | ||
262 | }; | ||
263 | |||
264 | static int close_table; | ||
265 | |||
266 | static void print_events_table_prefix(FILE *fp, const char *tblname) | ||
267 | { | ||
268 | fprintf(fp, "struct pmu_event %s[] = {\n", tblname); | ||
269 | close_table = 1; | ||
270 | } | ||
271 | |||
272 | static int print_events_table_entry(void *data, char *name, char *event, | ||
273 | char *desc, char *long_desc) | ||
274 | { | ||
275 | struct perf_entry_data *pd = data; | ||
276 | FILE *outfp = pd->outfp; | ||
277 | char *topic = pd->topic; | ||
278 | |||
279 | /* | ||
280 | * TODO: Remove formatting chars after debugging to reduce | ||
281 | * string lengths. | ||
282 | */ | ||
283 | fprintf(outfp, "{\n"); | ||
284 | |||
285 | fprintf(outfp, "\t.name = \"%s\",\n", name); | ||
286 | fprintf(outfp, "\t.event = \"%s\",\n", event); | ||
287 | fprintf(outfp, "\t.desc = \"%s\",\n", desc); | ||
288 | fprintf(outfp, "\t.topic = \"%s\",\n", topic); | ||
289 | if (long_desc && long_desc[0]) | ||
290 | fprintf(outfp, "\t.long_desc = \"%s\",\n", long_desc); | ||
291 | |||
292 | fprintf(outfp, "},\n"); | ||
293 | |||
294 | return 0; | ||
295 | } | ||
296 | |||
297 | static void print_events_table_suffix(FILE *outfp) | ||
298 | { | ||
299 | fprintf(outfp, "{\n"); | ||
300 | |||
301 | fprintf(outfp, "\t.name = 0,\n"); | ||
302 | fprintf(outfp, "\t.event = 0,\n"); | ||
303 | fprintf(outfp, "\t.desc = 0,\n"); | ||
304 | |||
305 | fprintf(outfp, "},\n"); | ||
306 | fprintf(outfp, "};\n"); | ||
307 | close_table = 0; | ||
308 | } | ||
309 | |||
310 | static struct fixed { | ||
311 | const char *name; | ||
312 | const char *event; | ||
313 | } fixed[] = { | ||
314 | { "inst_retired.any", "event=0xc0" }, | ||
315 | { "inst_retired.any_p", "event=0xc0" }, | ||
316 | { "cpu_clk_unhalted.ref", "event=0x0,umask=0x03" }, | ||
317 | { "cpu_clk_unhalted.thread", "event=0x3c" }, | ||
318 | { "cpu_clk_unhalted.thread_any", "event=0x3c,any=1" }, | ||
319 | { NULL, NULL}, | ||
320 | }; | ||
321 | |||
322 | /* | ||
323 | * Handle different fixed counter encodings between JSON and perf. | ||
324 | */ | ||
325 | static char *real_event(const char *name, char *event) | ||
326 | { | ||
327 | int i; | ||
328 | |||
329 | for (i = 0; fixed[i].name; i++) | ||
330 | if (!strcasecmp(name, fixed[i].name)) | ||
331 | return (char *)fixed[i].event; | ||
332 | return event; | ||
333 | } | ||
334 | |||
335 | /* Call func with each event in the json file */ | ||
336 | int json_events(const char *fn, | ||
337 | int (*func)(void *data, char *name, char *event, char *desc, | ||
338 | char *long_desc), | ||
339 | void *data) | ||
340 | { | ||
341 | int err = -EIO; | ||
342 | size_t size; | ||
343 | jsmntok_t *tokens, *tok; | ||
344 | int i, j, len; | ||
345 | char *map; | ||
346 | |||
347 | if (!fn) | ||
348 | return -ENOENT; | ||
349 | |||
350 | tokens = parse_json(fn, &map, &size, &len); | ||
351 | if (!tokens) | ||
352 | return -EIO; | ||
353 | EXPECT(tokens->type == JSMN_ARRAY, tokens, "expected top level array"); | ||
354 | tok = tokens + 1; | ||
355 | for (i = 0; i < tokens->size; i++) { | ||
356 | char *event = NULL, *desc = NULL, *name = NULL; | ||
357 | char *long_desc = NULL; | ||
358 | char *extra_desc = NULL; | ||
359 | struct msrmap *msr = NULL; | ||
360 | jsmntok_t *msrval = NULL; | ||
361 | jsmntok_t *precise = NULL; | ||
362 | jsmntok_t *obj = tok++; | ||
363 | |||
364 | EXPECT(obj->type == JSMN_OBJECT, obj, "expected object"); | ||
365 | for (j = 0; j < obj->size; j += 2) { | ||
366 | jsmntok_t *field, *val; | ||
367 | int nz; | ||
368 | |||
369 | field = tok + j; | ||
370 | EXPECT(field->type == JSMN_STRING, tok + j, | ||
371 | "Expected field name"); | ||
372 | val = tok + j + 1; | ||
373 | EXPECT(val->type == JSMN_STRING, tok + j + 1, | ||
374 | "Expected string value"); | ||
375 | |||
376 | nz = !json_streq(map, val, "0"); | ||
377 | if (match_field(map, field, nz, &event, val)) { | ||
378 | /* ok */ | ||
379 | } else if (json_streq(map, field, "EventName")) { | ||
380 | addfield(map, &name, "", "", val); | ||
381 | } else if (json_streq(map, field, "BriefDescription")) { | ||
382 | addfield(map, &desc, "", "", val); | ||
383 | fixdesc(desc); | ||
384 | } else if (json_streq(map, field, | ||
385 | "PublicDescription")) { | ||
386 | addfield(map, &long_desc, "", "", val); | ||
387 | fixdesc(long_desc); | ||
388 | } else if (json_streq(map, field, "PEBS") && nz) { | ||
389 | precise = val; | ||
390 | } else if (json_streq(map, field, "MSRIndex") && nz) { | ||
391 | msr = lookup_msr(map, val); | ||
392 | } else if (json_streq(map, field, "MSRValue")) { | ||
393 | msrval = val; | ||
394 | } else if (json_streq(map, field, "Errata") && | ||
395 | !json_streq(map, val, "null")) { | ||
396 | addfield(map, &extra_desc, ". ", | ||
397 | " Spec update: ", val); | ||
398 | } else if (json_streq(map, field, "Data_LA") && nz) { | ||
399 | addfield(map, &extra_desc, ". ", | ||
400 | " Supports address when precise", | ||
401 | NULL); | ||
402 | } | ||
403 | /* ignore unknown fields */ | ||
404 | } | ||
405 | if (precise && desc && !strstr(desc, "(Precise Event)")) { | ||
406 | if (json_streq(map, precise, "2")) | ||
407 | addfield(map, &extra_desc, " ", | ||
408 | "(Must be precise)", NULL); | ||
409 | else | ||
410 | addfield(map, &extra_desc, " ", | ||
411 | "(Precise event)", NULL); | ||
412 | } | ||
413 | if (desc && extra_desc) | ||
414 | addfield(map, &desc, " ", extra_desc, NULL); | ||
415 | if (long_desc && extra_desc) | ||
416 | addfield(map, &long_desc, " ", extra_desc, NULL); | ||
417 | if (msr != NULL) | ||
418 | addfield(map, &event, ",", msr->pname, msrval); | ||
419 | fixname(name); | ||
420 | |||
421 | err = func(data, name, real_event(name, event), desc, long_desc); | ||
422 | free(event); | ||
423 | free(desc); | ||
424 | free(name); | ||
425 | free(long_desc); | ||
426 | free(extra_desc); | ||
427 | if (err) | ||
428 | break; | ||
429 | tok += j; | ||
430 | } | ||
431 | EXPECT(tok - tokens == len, tok, "unexpected objects at end"); | ||
432 | err = 0; | ||
433 | out_free: | ||
434 | free_json(map, size, tokens); | ||
435 | return err; | ||
436 | } | ||
437 | |||
438 | static char *file_name_to_table_name(char *fname) | ||
439 | { | ||
440 | unsigned int i; | ||
441 | int n; | ||
442 | int c; | ||
443 | char *tblname; | ||
444 | |||
445 | /* | ||
446 | * Ensure tablename starts with alphabetic character. | ||
447 | * Derive rest of table name from basename of the JSON file, | ||
448 | * replacing hyphens and stripping out .json suffix. | ||
449 | */ | ||
450 | n = asprintf(&tblname, "pme_%s", basename(fname)); | ||
451 | if (n < 0) { | ||
452 | pr_info("%s: asprintf() error %s for file %s\n", prog, | ||
453 | strerror(errno), fname); | ||
454 | return NULL; | ||
455 | } | ||
456 | |||
457 | for (i = 0; i < strlen(tblname); i++) { | ||
458 | c = tblname[i]; | ||
459 | |||
460 | if (c == '-') | ||
461 | tblname[i] = '_'; | ||
462 | else if (c == '.') { | ||
463 | tblname[i] = '\0'; | ||
464 | break; | ||
465 | } else if (!isalnum(c) && c != '_') { | ||
466 | pr_err("%s: Invalid character '%c' in file name %s\n", | ||
467 | prog, c, basename(fname)); | ||
468 | free(tblname); | ||
469 | tblname = NULL; | ||
470 | break; | ||
471 | } | ||
472 | } | ||
473 | |||
474 | return tblname; | ||
475 | } | ||
476 | |||
477 | static void print_mapping_table_prefix(FILE *outfp) | ||
478 | { | ||
479 | fprintf(outfp, "struct pmu_events_map pmu_events_map[] = {\n"); | ||
480 | } | ||
481 | |||
482 | static void print_mapping_table_suffix(FILE *outfp) | ||
483 | { | ||
484 | /* | ||
485 | * Print the terminating, NULL entry. | ||
486 | */ | ||
487 | fprintf(outfp, "{\n"); | ||
488 | fprintf(outfp, "\t.cpuid = 0,\n"); | ||
489 | fprintf(outfp, "\t.version = 0,\n"); | ||
490 | fprintf(outfp, "\t.type = 0,\n"); | ||
491 | fprintf(outfp, "\t.table = 0,\n"); | ||
492 | fprintf(outfp, "},\n"); | ||
493 | |||
494 | /* and finally, the closing curly bracket for the struct */ | ||
495 | fprintf(outfp, "};\n"); | ||
496 | } | ||
497 | |||
498 | static int process_mapfile(FILE *outfp, char *fpath) | ||
499 | { | ||
500 | int n = 16384; | ||
501 | FILE *mapfp; | ||
502 | char *save = NULL; | ||
503 | char *line, *p; | ||
504 | int line_num; | ||
505 | char *tblname; | ||
506 | |||
507 | pr_info("%s: Processing mapfile %s\n", prog, fpath); | ||
508 | |||
509 | line = malloc(n); | ||
510 | if (!line) | ||
511 | return -1; | ||
512 | |||
513 | mapfp = fopen(fpath, "r"); | ||
514 | if (!mapfp) { | ||
515 | pr_info("%s: Error %s opening %s\n", prog, strerror(errno), | ||
516 | fpath); | ||
517 | return -1; | ||
518 | } | ||
519 | |||
520 | print_mapping_table_prefix(outfp); | ||
521 | |||
522 | /* Skip first line (header) */ | ||
523 | p = fgets(line, n, mapfp); | ||
524 | if (!p) | ||
525 | goto out; | ||
526 | |||
527 | line_num = 1; | ||
528 | while (1) { | ||
529 | char *cpuid, *version, *type, *fname; | ||
530 | |||
531 | line_num++; | ||
532 | p = fgets(line, n, mapfp); | ||
533 | if (!p) | ||
534 | break; | ||
535 | |||
536 | if (line[0] == '#' || line[0] == '\n') | ||
537 | continue; | ||
538 | |||
539 | if (line[strlen(line)-1] != '\n') { | ||
540 | /* TODO Deal with lines longer than 16K */ | ||
541 | pr_info("%s: Mapfile %s: line %d too long, aborting\n", | ||
542 | prog, fpath, line_num); | ||
543 | return -1; | ||
544 | } | ||
545 | line[strlen(line)-1] = '\0'; | ||
546 | |||
547 | cpuid = strtok_r(p, ",", &save); | ||
548 | version = strtok_r(NULL, ",", &save); | ||
549 | fname = strtok_r(NULL, ",", &save); | ||
550 | type = strtok_r(NULL, ",", &save); | ||
551 | |||
552 | tblname = file_name_to_table_name(fname); | ||
553 | fprintf(outfp, "{\n"); | ||
554 | fprintf(outfp, "\t.cpuid = \"%s\",\n", cpuid); | ||
555 | fprintf(outfp, "\t.version = \"%s\",\n", version); | ||
556 | fprintf(outfp, "\t.type = \"%s\",\n", type); | ||
557 | |||
558 | /* | ||
559 | * CHECK: We can't use the type (eg "core") field in the | ||
560 | * table name. For us to do that, we need to somehow tweak | ||
561 | * the other caller of file_name_to_table(), process_json() | ||
562 | * to determine the type. process_json() file has no way | ||
563 | * of knowing these are "core" events unless file name has | ||
564 | * core in it. If filename has core in it, we can safely | ||
565 | * ignore the type field here also. | ||
566 | */ | ||
567 | fprintf(outfp, "\t.table = %s\n", tblname); | ||
568 | fprintf(outfp, "},\n"); | ||
569 | } | ||
570 | |||
571 | out: | ||
572 | print_mapping_table_suffix(outfp); | ||
573 | return 0; | ||
574 | } | ||
575 | |||
576 | /* | ||
577 | * If we fail to locate/process JSON and map files, create a NULL mapping | ||
578 | * table. This would at least allow perf to build even if we can't find/use | ||
579 | * the aliases. | ||
580 | */ | ||
581 | static void create_empty_mapping(const char *output_file) | ||
582 | { | ||
583 | FILE *outfp; | ||
584 | |||
585 | pr_info("%s: Creating empty pmu_events_map[] table\n", prog); | ||
586 | |||
587 | /* Truncate file to clear any partial writes to it */ | ||
588 | outfp = fopen(output_file, "w"); | ||
589 | if (!outfp) { | ||
590 | perror("fopen()"); | ||
591 | _Exit(1); | ||
592 | } | ||
593 | |||
594 | fprintf(outfp, "#include \"../../pmu-events/pmu-events.h\"\n"); | ||
595 | print_mapping_table_prefix(outfp); | ||
596 | print_mapping_table_suffix(outfp); | ||
597 | fclose(outfp); | ||
598 | } | ||
599 | |||
600 | static int get_maxfds(void) | ||
601 | { | ||
602 | struct rlimit rlim; | ||
603 | |||
604 | if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) | ||
605 | return min((int)rlim.rlim_max / 2, 512); | ||
606 | |||
607 | return 512; | ||
608 | } | ||
609 | |||
610 | /* | ||
611 | * nftw() doesn't let us pass an argument to the processing function, | ||
612 | * so use a global variables. | ||
613 | */ | ||
614 | static FILE *eventsfp; | ||
615 | static char *mapfile; | ||
616 | |||
617 | static int process_one_file(const char *fpath, const struct stat *sb, | ||
618 | int typeflag, struct FTW *ftwbuf) | ||
619 | { | ||
620 | char *tblname, *bname = (char *) fpath + ftwbuf->base; | ||
621 | int is_dir = typeflag == FTW_D; | ||
622 | int is_file = typeflag == FTW_F; | ||
623 | int level = ftwbuf->level; | ||
624 | int err = 0; | ||
625 | |||
626 | pr_debug("%s %d %7jd %-20s %s\n", | ||
627 | is_file ? "f" : is_dir ? "d" : "x", | ||
628 | level, sb->st_size, bname, fpath); | ||
629 | |||
630 | /* base dir */ | ||
631 | if (level == 0) | ||
632 | return 0; | ||
633 | |||
634 | /* model directory, reset topic */ | ||
635 | if (level == 1 && is_dir) { | ||
636 | if (close_table) | ||
637 | print_events_table_suffix(eventsfp); | ||
638 | |||
639 | /* | ||
640 | * Drop file name suffix. Replace hyphens with underscores. | ||
641 | * Fail if file name contains any alphanum characters besides | ||
642 | * underscores. | ||
643 | */ | ||
644 | tblname = file_name_to_table_name(bname); | ||
645 | if (!tblname) { | ||
646 | pr_info("%s: Error determining table name for %s\n", prog, | ||
647 | bname); | ||
648 | return -1; | ||
649 | } | ||
650 | |||
651 | print_events_table_prefix(eventsfp, tblname); | ||
652 | return 0; | ||
653 | } | ||
654 | |||
655 | /* | ||
656 | * Save the mapfile name for now. We will process mapfile | ||
657 | * after processing all JSON files (so we can write out the | ||
658 | * mapping table after all PMU events tables). | ||
659 | * | ||
660 | * TODO: Allow for multiple mapfiles? Punt for now. | ||
661 | */ | ||
662 | if (level == 1 && is_file) { | ||
663 | if (!strncmp(bname, "mapfile.csv", 11)) { | ||
664 | if (mapfile) { | ||
665 | pr_info("%s: Many mapfiles? Using %s, ignoring %s\n", | ||
666 | prog, mapfile, fpath); | ||
667 | } else { | ||
668 | mapfile = strdup(fpath); | ||
669 | } | ||
670 | return 0; | ||
671 | } | ||
672 | |||
673 | pr_info("%s: Ignoring file %s\n", prog, fpath); | ||
674 | return 0; | ||
675 | } | ||
676 | |||
677 | /* | ||
678 | * If the file name does not have a .json extension, | ||
679 | * ignore it. It could be a readme.txt for instance. | ||
680 | */ | ||
681 | if (is_file) { | ||
682 | char *suffix = bname + strlen(bname) - 5; | ||
683 | |||
684 | if (strncmp(suffix, ".json", 5)) { | ||
685 | pr_info("%s: Ignoring file without .json suffix %s\n", prog, | ||
686 | fpath); | ||
687 | return 0; | ||
688 | } | ||
689 | } | ||
690 | |||
691 | if (level > 1 && add_topic(level, bname)) | ||
692 | return -ENOMEM; | ||
693 | |||
694 | /* | ||
695 | * Assume all other files are JSON files. | ||
696 | * | ||
697 | * If mapfile refers to 'power7_core.json', we create a table | ||
698 | * named 'power7_core'. Any inconsistencies between the mapfile | ||
699 | * and directory tree could result in build failure due to table | ||
700 | * names not being found. | ||
701 | * | ||
702 | * Atleast for now, be strict with processing JSON file names. | ||
703 | * i.e. if JSON file name cannot be mapped to C-style table name, | ||
704 | * fail. | ||
705 | */ | ||
706 | if (is_file) { | ||
707 | struct perf_entry_data data = { | ||
708 | .topic = get_topic(), | ||
709 | .outfp = eventsfp, | ||
710 | }; | ||
711 | |||
712 | err = json_events(fpath, print_events_table_entry, &data); | ||
713 | |||
714 | free(data.topic); | ||
715 | } | ||
716 | |||
717 | return err; | ||
718 | } | ||
719 | |||
720 | #ifndef PATH_MAX | ||
721 | #define PATH_MAX 4096 | ||
722 | #endif | ||
723 | |||
724 | /* | ||
725 | * Starting in directory 'start_dirname', find the "mapfile.csv" and | ||
726 | * the set of JSON files for the architecture 'arch'. | ||
727 | * | ||
728 | * From each JSON file, create a C-style "PMU events table" from the | ||
729 | * JSON file (see struct pmu_event). | ||
730 | * | ||
731 | * From the mapfile, create a mapping between the CPU revisions and | ||
732 | * PMU event tables (see struct pmu_events_map). | ||
733 | * | ||
734 | * Write out the PMU events tables and the mapping table to pmu-event.c. | ||
735 | * | ||
736 | * If unable to process the JSON or arch files, create an empty mapping | ||
737 | * table so we can continue to build/use perf even if we cannot use the | ||
738 | * PMU event aliases. | ||
739 | */ | ||
740 | int main(int argc, char *argv[]) | ||
741 | { | ||
742 | int rc; | ||
743 | int maxfds; | ||
744 | char ldirname[PATH_MAX]; | ||
745 | |||
746 | const char *arch; | ||
747 | const char *output_file; | ||
748 | const char *start_dirname; | ||
749 | |||
750 | prog = basename(argv[0]); | ||
751 | if (argc < 4) { | ||
752 | pr_err("Usage: %s <arch> <starting_dir> <output_file>\n", prog); | ||
753 | return 1; | ||
754 | } | ||
755 | |||
756 | arch = argv[1]; | ||
757 | start_dirname = argv[2]; | ||
758 | output_file = argv[3]; | ||
759 | |||
760 | if (argc > 4) | ||
761 | verbose = atoi(argv[4]); | ||
762 | |||
763 | eventsfp = fopen(output_file, "w"); | ||
764 | if (!eventsfp) { | ||
765 | pr_err("%s Unable to create required file %s (%s)\n", | ||
766 | prog, output_file, strerror(errno)); | ||
767 | return 2; | ||
768 | } | ||
769 | |||
770 | /* Include pmu-events.h first */ | ||
771 | fprintf(eventsfp, "#include \"../../pmu-events/pmu-events.h\"\n"); | ||
772 | |||
773 | sprintf(ldirname, "%s/%s", start_dirname, arch); | ||
774 | |||
775 | /* | ||
776 | * The mapfile allows multiple CPUids to point to the same JSON file, | ||
777 | * so, not sure if there is a need for symlinks within the pmu-events | ||
778 | * directory. | ||
779 | * | ||
780 | * For now, treat symlinks of JSON files as regular files and create | ||
781 | * separate tables for each symlink (presumably, each symlink refers | ||
782 | * to specific version of the CPU). | ||
783 | */ | ||
784 | |||
785 | maxfds = get_maxfds(); | ||
786 | mapfile = NULL; | ||
787 | rc = nftw(ldirname, process_one_file, maxfds, 0); | ||
788 | if (rc && verbose) { | ||
789 | pr_info("%s: Error walking file tree %s\n", prog, ldirname); | ||
790 | goto empty_map; | ||
791 | } else if (rc) { | ||
792 | goto empty_map; | ||
793 | } | ||
794 | |||
795 | if (close_table) | ||
796 | print_events_table_suffix(eventsfp); | ||
797 | |||
798 | if (!mapfile) { | ||
799 | pr_info("%s: No CPU->JSON mapping?\n", prog); | ||
800 | goto empty_map; | ||
801 | } | ||
802 | |||
803 | if (process_mapfile(eventsfp, mapfile)) { | ||
804 | pr_info("%s: Error processing mapfile %s\n", prog, mapfile); | ||
805 | goto empty_map; | ||
806 | } | ||
807 | |||
808 | return 0; | ||
809 | |||
810 | empty_map: | ||
811 | fclose(eventsfp); | ||
812 | create_empty_mapping(output_file); | ||
813 | return 0; | ||
814 | } | ||
diff --git a/tools/perf/pmu-events/jevents.h b/tools/perf/pmu-events/jevents.h new file mode 100644 index 000000000000..b0eb2744b498 --- /dev/null +++ b/tools/perf/pmu-events/jevents.h | |||
@@ -0,0 +1,18 @@ | |||
1 | #ifndef JEVENTS_H | ||
2 | #define JEVENTS_H 1 | ||
3 | |||
4 | int json_events(const char *fn, | ||
5 | int (*func)(void *data, char *name, char *event, char *desc, | ||
6 | char *long_desc), | ||
7 | void *data); | ||
8 | char *get_cpu_str(void); | ||
9 | |||
10 | #ifndef min | ||
11 | #define min(x, y) ({ \ | ||
12 | typeof(x) _min1 = (x); \ | ||
13 | typeof(y) _min2 = (y); \ | ||
14 | (void) (&_min1 == &_min2); \ | ||
15 | _min1 < _min2 ? _min1 : _min2; }) | ||
16 | #endif | ||
17 | |||
18 | #endif | ||
diff --git a/tools/perf/pmu-events/jsmn.c b/tools/perf/pmu-events/jsmn.c new file mode 100644 index 000000000000..11d1fa18bfa5 --- /dev/null +++ b/tools/perf/pmu-events/jsmn.c | |||
@@ -0,0 +1,313 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2010 Serge A. Zaitsev | ||
3 | * | ||
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy | ||
5 | * of this software and associated documentation files (the "Software"), to deal | ||
6 | * in the Software without restriction, including without limitation the rights | ||
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
8 | * copies of the Software, and to permit persons to whom the Software is | ||
9 | * furnished to do so, subject to the following conditions: | ||
10 | * | ||
11 | * The above copyright notice and this permission notice shall be included in | ||
12 | * all copies or substantial portions of the Software. | ||
13 | * | ||
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
20 | * THE SOFTWARE. | ||
21 | * | ||
22 | * Slightly modified by AK to not assume 0 terminated input. | ||
23 | */ | ||
24 | |||
25 | #include <stdlib.h> | ||
26 | #include "jsmn.h" | ||
27 | |||
28 | /* | ||
29 | * Allocates a fresh unused token from the token pool. | ||
30 | */ | ||
31 | static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, | ||
32 | jsmntok_t *tokens, size_t num_tokens) | ||
33 | { | ||
34 | jsmntok_t *tok; | ||
35 | |||
36 | if ((unsigned)parser->toknext >= num_tokens) | ||
37 | return NULL; | ||
38 | tok = &tokens[parser->toknext++]; | ||
39 | tok->start = tok->end = -1; | ||
40 | tok->size = 0; | ||
41 | return tok; | ||
42 | } | ||
43 | |||
44 | /* | ||
45 | * Fills token type and boundaries. | ||
46 | */ | ||
47 | static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, | ||
48 | int start, int end) | ||
49 | { | ||
50 | token->type = type; | ||
51 | token->start = start; | ||
52 | token->end = end; | ||
53 | token->size = 0; | ||
54 | } | ||
55 | |||
56 | /* | ||
57 | * Fills next available token with JSON primitive. | ||
58 | */ | ||
59 | static jsmnerr_t jsmn_parse_primitive(jsmn_parser *parser, const char *js, | ||
60 | size_t len, | ||
61 | jsmntok_t *tokens, size_t num_tokens) | ||
62 | { | ||
63 | jsmntok_t *token; | ||
64 | int start; | ||
65 | |||
66 | start = parser->pos; | ||
67 | |||
68 | for (; parser->pos < len; parser->pos++) { | ||
69 | switch (js[parser->pos]) { | ||
70 | #ifndef JSMN_STRICT | ||
71 | /* | ||
72 | * In strict mode primitive must be followed by "," | ||
73 | * or "}" or "]" | ||
74 | */ | ||
75 | case ':': | ||
76 | #endif | ||
77 | case '\t': | ||
78 | case '\r': | ||
79 | case '\n': | ||
80 | case ' ': | ||
81 | case ',': | ||
82 | case ']': | ||
83 | case '}': | ||
84 | goto found; | ||
85 | default: | ||
86 | break; | ||
87 | } | ||
88 | if (js[parser->pos] < 32 || js[parser->pos] >= 127) { | ||
89 | parser->pos = start; | ||
90 | return JSMN_ERROR_INVAL; | ||
91 | } | ||
92 | } | ||
93 | #ifdef JSMN_STRICT | ||
94 | /* | ||
95 | * In strict mode primitive must be followed by a | ||
96 | * comma/object/array. | ||
97 | */ | ||
98 | parser->pos = start; | ||
99 | return JSMN_ERROR_PART; | ||
100 | #endif | ||
101 | |||
102 | found: | ||
103 | token = jsmn_alloc_token(parser, tokens, num_tokens); | ||
104 | if (token == NULL) { | ||
105 | parser->pos = start; | ||
106 | return JSMN_ERROR_NOMEM; | ||
107 | } | ||
108 | jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); | ||
109 | parser->pos--; /* parent sees closing brackets */ | ||
110 | return JSMN_SUCCESS; | ||
111 | } | ||
112 | |||
113 | /* | ||
114 | * Fills next token with JSON string. | ||
115 | */ | ||
116 | static jsmnerr_t jsmn_parse_string(jsmn_parser *parser, const char *js, | ||
117 | size_t len, | ||
118 | jsmntok_t *tokens, size_t num_tokens) | ||
119 | { | ||
120 | jsmntok_t *token; | ||
121 | int start = parser->pos; | ||
122 | |||
123 | /* Skip starting quote */ | ||
124 | parser->pos++; | ||
125 | |||
126 | for (; parser->pos < len; parser->pos++) { | ||
127 | char c = js[parser->pos]; | ||
128 | |||
129 | /* Quote: end of string */ | ||
130 | if (c == '\"') { | ||
131 | token = jsmn_alloc_token(parser, tokens, num_tokens); | ||
132 | if (token == NULL) { | ||
133 | parser->pos = start; | ||
134 | return JSMN_ERROR_NOMEM; | ||
135 | } | ||
136 | jsmn_fill_token(token, JSMN_STRING, start+1, | ||
137 | parser->pos); | ||
138 | return JSMN_SUCCESS; | ||
139 | } | ||
140 | |||
141 | /* Backslash: Quoted symbol expected */ | ||
142 | if (c == '\\') { | ||
143 | parser->pos++; | ||
144 | switch (js[parser->pos]) { | ||
145 | /* Allowed escaped symbols */ | ||
146 | case '\"': | ||
147 | case '/': | ||
148 | case '\\': | ||
149 | case 'b': | ||
150 | case 'f': | ||
151 | case 'r': | ||
152 | case 'n': | ||
153 | case 't': | ||
154 | break; | ||
155 | /* Allows escaped symbol \uXXXX */ | ||
156 | case 'u': | ||
157 | /* TODO */ | ||
158 | break; | ||
159 | /* Unexpected symbol */ | ||
160 | default: | ||
161 | parser->pos = start; | ||
162 | return JSMN_ERROR_INVAL; | ||
163 | } | ||
164 | } | ||
165 | } | ||
166 | parser->pos = start; | ||
167 | return JSMN_ERROR_PART; | ||
168 | } | ||
169 | |||
170 | /* | ||
171 | * Parse JSON string and fill tokens. | ||
172 | */ | ||
173 | jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, size_t len, | ||
174 | jsmntok_t *tokens, unsigned int num_tokens) | ||
175 | { | ||
176 | jsmnerr_t r; | ||
177 | int i; | ||
178 | jsmntok_t *token; | ||
179 | |||
180 | for (; parser->pos < len; parser->pos++) { | ||
181 | char c; | ||
182 | jsmntype_t type; | ||
183 | |||
184 | c = js[parser->pos]; | ||
185 | switch (c) { | ||
186 | case '{': | ||
187 | case '[': | ||
188 | token = jsmn_alloc_token(parser, tokens, num_tokens); | ||
189 | if (token == NULL) | ||
190 | return JSMN_ERROR_NOMEM; | ||
191 | if (parser->toksuper != -1) | ||
192 | tokens[parser->toksuper].size++; | ||
193 | token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); | ||
194 | token->start = parser->pos; | ||
195 | parser->toksuper = parser->toknext - 1; | ||
196 | break; | ||
197 | case '}': | ||
198 | case ']': | ||
199 | type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); | ||
200 | for (i = parser->toknext - 1; i >= 0; i--) { | ||
201 | token = &tokens[i]; | ||
202 | if (token->start != -1 && token->end == -1) { | ||
203 | if (token->type != type) | ||
204 | return JSMN_ERROR_INVAL; | ||
205 | parser->toksuper = -1; | ||
206 | token->end = parser->pos + 1; | ||
207 | break; | ||
208 | } | ||
209 | } | ||
210 | /* Error if unmatched closing bracket */ | ||
211 | if (i == -1) | ||
212 | return JSMN_ERROR_INVAL; | ||
213 | for (; i >= 0; i--) { | ||
214 | token = &tokens[i]; | ||
215 | if (token->start != -1 && token->end == -1) { | ||
216 | parser->toksuper = i; | ||
217 | break; | ||
218 | } | ||
219 | } | ||
220 | break; | ||
221 | case '\"': | ||
222 | r = jsmn_parse_string(parser, js, len, tokens, | ||
223 | num_tokens); | ||
224 | if (r < 0) | ||
225 | return r; | ||
226 | if (parser->toksuper != -1) | ||
227 | tokens[parser->toksuper].size++; | ||
228 | break; | ||
229 | case '\t': | ||
230 | case '\r': | ||
231 | case '\n': | ||
232 | case ':': | ||
233 | case ',': | ||
234 | case ' ': | ||
235 | break; | ||
236 | #ifdef JSMN_STRICT | ||
237 | /* | ||
238 | * In strict mode primitives are: | ||
239 | * numbers and booleans. | ||
240 | */ | ||
241 | case '-': | ||
242 | case '0': | ||
243 | case '1': | ||
244 | case '2': | ||
245 | case '3': | ||
246 | case '4': | ||
247 | case '5': | ||
248 | case '6': | ||
249 | case '7': | ||
250 | case '8': | ||
251 | case '9': | ||
252 | case 't': | ||
253 | case 'f': | ||
254 | case 'n': | ||
255 | #else | ||
256 | /* | ||
257 | * In non-strict mode every unquoted value | ||
258 | * is a primitive. | ||
259 | */ | ||
260 | /*FALL THROUGH */ | ||
261 | default: | ||
262 | #endif | ||
263 | r = jsmn_parse_primitive(parser, js, len, tokens, | ||
264 | num_tokens); | ||
265 | if (r < 0) | ||
266 | return r; | ||
267 | if (parser->toksuper != -1) | ||
268 | tokens[parser->toksuper].size++; | ||
269 | break; | ||
270 | |||
271 | #ifdef JSMN_STRICT | ||
272 | /* Unexpected char in strict mode */ | ||
273 | default: | ||
274 | return JSMN_ERROR_INVAL; | ||
275 | #endif | ||
276 | } | ||
277 | } | ||
278 | |||
279 | for (i = parser->toknext - 1; i >= 0; i--) { | ||
280 | /* Unmatched opened object or array */ | ||
281 | if (tokens[i].start != -1 && tokens[i].end == -1) | ||
282 | return JSMN_ERROR_PART; | ||
283 | } | ||
284 | |||
285 | return JSMN_SUCCESS; | ||
286 | } | ||
287 | |||
288 | /* | ||
289 | * Creates a new parser based over a given buffer with an array of tokens | ||
290 | * available. | ||
291 | */ | ||
292 | void jsmn_init(jsmn_parser *parser) | ||
293 | { | ||
294 | parser->pos = 0; | ||
295 | parser->toknext = 0; | ||
296 | parser->toksuper = -1; | ||
297 | } | ||
298 | |||
299 | const char *jsmn_strerror(jsmnerr_t err) | ||
300 | { | ||
301 | switch (err) { | ||
302 | case JSMN_ERROR_NOMEM: | ||
303 | return "No enough tokens"; | ||
304 | case JSMN_ERROR_INVAL: | ||
305 | return "Invalid character inside JSON string"; | ||
306 | case JSMN_ERROR_PART: | ||
307 | return "The string is not a full JSON packet, more bytes expected"; | ||
308 | case JSMN_SUCCESS: | ||
309 | return "Success"; | ||
310 | default: | ||
311 | return "Unknown json error"; | ||
312 | } | ||
313 | } | ||
diff --git a/tools/perf/pmu-events/jsmn.h b/tools/perf/pmu-events/jsmn.h new file mode 100644 index 000000000000..d666b10cf25b --- /dev/null +++ b/tools/perf/pmu-events/jsmn.h | |||
@@ -0,0 +1,67 @@ | |||
1 | #ifndef __JSMN_H_ | ||
2 | #define __JSMN_H_ | ||
3 | |||
4 | /* | ||
5 | * JSON type identifier. Basic types are: | ||
6 | * o Object | ||
7 | * o Array | ||
8 | * o String | ||
9 | * o Other primitive: number, boolean (true/false) or null | ||
10 | */ | ||
11 | typedef enum { | ||
12 | JSMN_PRIMITIVE = 0, | ||
13 | JSMN_OBJECT = 1, | ||
14 | JSMN_ARRAY = 2, | ||
15 | JSMN_STRING = 3 | ||
16 | } jsmntype_t; | ||
17 | |||
18 | typedef enum { | ||
19 | /* Not enough tokens were provided */ | ||
20 | JSMN_ERROR_NOMEM = -1, | ||
21 | /* Invalid character inside JSON string */ | ||
22 | JSMN_ERROR_INVAL = -2, | ||
23 | /* The string is not a full JSON packet, more bytes expected */ | ||
24 | JSMN_ERROR_PART = -3, | ||
25 | /* Everything was fine */ | ||
26 | JSMN_SUCCESS = 0 | ||
27 | } jsmnerr_t; | ||
28 | |||
29 | /* | ||
30 | * JSON token description. | ||
31 | * @param type type (object, array, string etc.) | ||
32 | * @param start start position in JSON data string | ||
33 | * @param end end position in JSON data string | ||
34 | */ | ||
35 | typedef struct { | ||
36 | jsmntype_t type; | ||
37 | int start; | ||
38 | int end; | ||
39 | int size; | ||
40 | } jsmntok_t; | ||
41 | |||
42 | /* | ||
43 | * JSON parser. Contains an array of token blocks available. Also stores | ||
44 | * the string being parsed now and current position in that string | ||
45 | */ | ||
46 | typedef struct { | ||
47 | unsigned int pos; /* offset in the JSON string */ | ||
48 | int toknext; /* next token to allocate */ | ||
49 | int toksuper; /* superior token node, e.g parent object or array */ | ||
50 | } jsmn_parser; | ||
51 | |||
52 | /* | ||
53 | * Create JSON parser over an array of tokens | ||
54 | */ | ||
55 | void jsmn_init(jsmn_parser *parser); | ||
56 | |||
57 | /* | ||
58 | * Run JSON parser. It parses a JSON data string into and array of tokens, | ||
59 | * each describing a single JSON object. | ||
60 | */ | ||
61 | jsmnerr_t jsmn_parse(jsmn_parser *parser, const char *js, | ||
62 | size_t len, | ||
63 | jsmntok_t *tokens, unsigned int num_tokens); | ||
64 | |||
65 | const char *jsmn_strerror(jsmnerr_t err); | ||
66 | |||
67 | #endif /* __JSMN_H_ */ | ||
diff --git a/tools/perf/pmu-events/json.c b/tools/perf/pmu-events/json.c new file mode 100644 index 000000000000..f67bbb0aa36e --- /dev/null +++ b/tools/perf/pmu-events/json.c | |||
@@ -0,0 +1,162 @@ | |||
1 | /* Parse JSON files using the JSMN parser. */ | ||
2 | |||
3 | /* | ||
4 | * Copyright (c) 2014, Intel Corporation | ||
5 | * All rights reserved. | ||
6 | * | ||
7 | * Redistribution and use in source and binary forms, with or without | ||
8 | * modification, are permitted provided that the following conditions are met: | ||
9 | * | ||
10 | * 1. Redistributions of source code must retain the above copyright notice, | ||
11 | * this list of conditions and the following disclaimer. | ||
12 | * | ||
13 | * 2. Redistributions in binary form must reproduce the above copyright | ||
14 | * notice, this list of conditions and the following disclaimer in the | ||
15 | * documentation and/or other materials provided with the distribution. | ||
16 | * | ||
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
18 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
19 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | ||
20 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | ||
21 | * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, | ||
22 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
23 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | ||
24 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | ||
26 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | ||
27 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED | ||
28 | * OF THE POSSIBILITY OF SUCH DAMAGE. | ||
29 | */ | ||
30 | |||
31 | #include <stdlib.h> | ||
32 | #include <string.h> | ||
33 | #include <sys/mman.h> | ||
34 | #include <sys/stat.h> | ||
35 | #include <fcntl.h> | ||
36 | #include <stdio.h> | ||
37 | #include <errno.h> | ||
38 | #include <unistd.h> | ||
39 | #include "jsmn.h" | ||
40 | #include "json.h" | ||
41 | #include <linux/kernel.h> | ||
42 | |||
43 | |||
44 | static char *mapfile(const char *fn, size_t *size) | ||
45 | { | ||
46 | unsigned ps = sysconf(_SC_PAGESIZE); | ||
47 | struct stat st; | ||
48 | char *map = NULL; | ||
49 | int err; | ||
50 | int fd = open(fn, O_RDONLY); | ||
51 | |||
52 | if (fd < 0 && verbose && fn) { | ||
53 | pr_err("Error opening events file '%s': %s\n", fn, | ||
54 | strerror(errno)); | ||
55 | } | ||
56 | |||
57 | if (fd < 0) | ||
58 | return NULL; | ||
59 | err = fstat(fd, &st); | ||
60 | if (err < 0) | ||
61 | goto out; | ||
62 | *size = st.st_size; | ||
63 | map = mmap(NULL, | ||
64 | (st.st_size + ps - 1) & ~(ps - 1), | ||
65 | PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); | ||
66 | if (map == MAP_FAILED) | ||
67 | map = NULL; | ||
68 | out: | ||
69 | close(fd); | ||
70 | return map; | ||
71 | } | ||
72 | |||
73 | static void unmapfile(char *map, size_t size) | ||
74 | { | ||
75 | unsigned ps = sysconf(_SC_PAGESIZE); | ||
76 | munmap(map, roundup(size, ps)); | ||
77 | } | ||
78 | |||
79 | /* | ||
80 | * Parse json file using jsmn. Return array of tokens, | ||
81 | * and mapped file. Caller needs to free array. | ||
82 | */ | ||
83 | jsmntok_t *parse_json(const char *fn, char **map, size_t *size, int *len) | ||
84 | { | ||
85 | jsmn_parser parser; | ||
86 | jsmntok_t *tokens; | ||
87 | jsmnerr_t res; | ||
88 | unsigned sz; | ||
89 | |||
90 | *map = mapfile(fn, size); | ||
91 | if (!*map) | ||
92 | return NULL; | ||
93 | /* Heuristic */ | ||
94 | sz = *size * 16; | ||
95 | tokens = malloc(sz); | ||
96 | if (!tokens) | ||
97 | goto error; | ||
98 | jsmn_init(&parser); | ||
99 | res = jsmn_parse(&parser, *map, *size, tokens, | ||
100 | sz / sizeof(jsmntok_t)); | ||
101 | if (res != JSMN_SUCCESS) { | ||
102 | pr_err("%s: json error %s\n", fn, jsmn_strerror(res)); | ||
103 | goto error_free; | ||
104 | } | ||
105 | if (len) | ||
106 | *len = parser.toknext; | ||
107 | return tokens; | ||
108 | error_free: | ||
109 | free(tokens); | ||
110 | error: | ||
111 | unmapfile(*map, *size); | ||
112 | return NULL; | ||
113 | } | ||
114 | |||
115 | void free_json(char *map, size_t size, jsmntok_t *tokens) | ||
116 | { | ||
117 | free(tokens); | ||
118 | unmapfile(map, size); | ||
119 | } | ||
120 | |||
121 | static int countchar(char *map, char c, int end) | ||
122 | { | ||
123 | int i; | ||
124 | int count = 0; | ||
125 | for (i = 0; i < end; i++) | ||
126 | if (map[i] == c) | ||
127 | count++; | ||
128 | return count; | ||
129 | } | ||
130 | |||
131 | /* Return line number of a jsmn token */ | ||
132 | int json_line(char *map, jsmntok_t *t) | ||
133 | { | ||
134 | return countchar(map, '\n', t->start) + 1; | ||
135 | } | ||
136 | |||
137 | static const char * const jsmn_types[] = { | ||
138 | [JSMN_PRIMITIVE] = "primitive", | ||
139 | [JSMN_ARRAY] = "array", | ||
140 | [JSMN_OBJECT] = "object", | ||
141 | [JSMN_STRING] = "string" | ||
142 | }; | ||
143 | |||
144 | #define LOOKUP(a, i) ((i) < (sizeof(a)/sizeof(*(a))) ? ((a)[i]) : "?") | ||
145 | |||
146 | /* Return type name of a jsmn token */ | ||
147 | const char *json_name(jsmntok_t *t) | ||
148 | { | ||
149 | return LOOKUP(jsmn_types, t->type); | ||
150 | } | ||
151 | |||
152 | int json_len(jsmntok_t *t) | ||
153 | { | ||
154 | return t->end - t->start; | ||
155 | } | ||
156 | |||
157 | /* Is string t equal to s? */ | ||
158 | int json_streq(char *map, jsmntok_t *t, const char *s) | ||
159 | { | ||
160 | unsigned len = json_len(t); | ||
161 | return len == strlen(s) && !strncasecmp(map + t->start, s, len); | ||
162 | } | ||
diff --git a/tools/perf/pmu-events/json.h b/tools/perf/pmu-events/json.h new file mode 100644 index 000000000000..278ebd32cfb6 --- /dev/null +++ b/tools/perf/pmu-events/json.h | |||
@@ -0,0 +1,38 @@ | |||
1 | #ifndef JSON_H | ||
2 | #define JSON_H 1 | ||
3 | |||
4 | #include "jsmn.h" | ||
5 | |||
6 | jsmntok_t *parse_json(const char *fn, char **map, size_t *size, int *len); | ||
7 | void free_json(char *map, size_t size, jsmntok_t *tokens); | ||
8 | int json_line(char *map, jsmntok_t *t); | ||
9 | const char *json_name(jsmntok_t *t); | ||
10 | int json_streq(char *map, jsmntok_t *t, const char *s); | ||
11 | int json_len(jsmntok_t *t); | ||
12 | |||
13 | extern int verbose; | ||
14 | |||
15 | #include <stdbool.h> | ||
16 | |||
17 | extern int eprintf(int level, int var, const char *fmt, ...); | ||
18 | #define pr_fmt(fmt) fmt | ||
19 | |||
20 | #define pr_err(fmt, ...) \ | ||
21 | eprintf(0, verbose, pr_fmt(fmt), ##__VA_ARGS__) | ||
22 | |||
23 | #define pr_info(fmt, ...) \ | ||
24 | eprintf(1, verbose, pr_fmt(fmt), ##__VA_ARGS__) | ||
25 | |||
26 | #define pr_debug(fmt, ...) \ | ||
27 | eprintf(2, verbose, pr_fmt(fmt), ##__VA_ARGS__) | ||
28 | |||
29 | #ifndef roundup | ||
30 | #define roundup(x, y) ( \ | ||
31 | { \ | ||
32 | const typeof(y) __y = y; \ | ||
33 | (((x) + (__y - 1)) / __y) * __y; \ | ||
34 | } \ | ||
35 | ) | ||
36 | #endif | ||
37 | |||
38 | #endif | ||
diff --git a/tools/perf/pmu-events/pmu-events.h b/tools/perf/pmu-events/pmu-events.h new file mode 100644 index 000000000000..2eaef595d8a0 --- /dev/null +++ b/tools/perf/pmu-events/pmu-events.h | |||
@@ -0,0 +1,37 @@ | |||
1 | #ifndef PMU_EVENTS_H | ||
2 | #define PMU_EVENTS_H | ||
3 | |||
4 | /* | ||
5 | * Describe each PMU event. Each CPU has a table of PMU events. | ||
6 | */ | ||
7 | struct pmu_event { | ||
8 | const char *name; | ||
9 | const char *event; | ||
10 | const char *desc; | ||
11 | const char *topic; | ||
12 | const char *long_desc; | ||
13 | }; | ||
14 | |||
15 | /* | ||
16 | * | ||
17 | * Map a CPU to its table of PMU events. The CPU is identified by the | ||
18 | * cpuid field, which is an arch-specific identifier for the CPU. | ||
19 | * The identifier specified in tools/perf/pmu-events/arch/xxx/mapfile | ||
20 | * must match the get_cpustr() in tools/perf/arch/xxx/util/header.c) | ||
21 | * | ||
22 | * The cpuid can contain any character other than the comma. | ||
23 | */ | ||
24 | struct pmu_events_map { | ||
25 | const char *cpuid; | ||
26 | const char *version; | ||
27 | const char *type; /* core, uncore etc */ | ||
28 | struct pmu_event *table; | ||
29 | }; | ||
30 | |||
31 | /* | ||
32 | * Global table mapping each known CPU for the architecture to its | ||
33 | * table of PMU events. | ||
34 | */ | ||
35 | extern struct pmu_events_map pmu_events_map[]; | ||
36 | |||
37 | #endif | ||
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c index ea34c5a32c11..d92e02006fb8 100644 --- a/tools/perf/util/evlist.c +++ b/tools/perf/util/evlist.c | |||
@@ -384,15 +384,14 @@ void perf_evlist__toggle_enable(struct perf_evlist *evlist) | |||
384 | static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist, | 384 | static int perf_evlist__enable_event_cpu(struct perf_evlist *evlist, |
385 | struct perf_evsel *evsel, int cpu) | 385 | struct perf_evsel *evsel, int cpu) |
386 | { | 386 | { |
387 | int thread, err; | 387 | int thread; |
388 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); | 388 | int nr_threads = perf_evlist__nr_threads(evlist, evsel); |
389 | 389 | ||
390 | if (!evsel->fd) | 390 | if (!evsel->fd) |
391 | return -EINVAL; | 391 | return -EINVAL; |
392 | 392 | ||
393 | for (thread = 0; thread < nr_threads; thread++) { | 393 | for (thread = 0; thread < nr_threads; thread++) { |
394 | err = ioctl(FD(evsel, cpu, thread), | 394 | int err = ioctl(FD(evsel, cpu, thread), PERF_EVENT_IOC_ENABLE, 0); |
395 | PERF_EVENT_IOC_ENABLE, 0); | ||
396 | if (err) | 395 | if (err) |
397 | return err; | 396 | return err; |
398 | } | 397 | } |
@@ -403,14 +402,14 @@ static int perf_evlist__enable_event_thread(struct perf_evlist *evlist, | |||
403 | struct perf_evsel *evsel, | 402 | struct perf_evsel *evsel, |
404 | int thread) | 403 | int thread) |
405 | { | 404 | { |
406 | int cpu, err; | 405 | int cpu; |
407 | int nr_cpus = cpu_map__nr(evlist->cpus); | 406 | int nr_cpus = cpu_map__nr(evlist->cpus); |
408 | 407 | ||
409 | if (!evsel->fd) | 408 | if (!evsel->fd) |
410 | return -EINVAL; | 409 | return -EINVAL; |
411 | 410 | ||
412 | for (cpu = 0; cpu < nr_cpus; cpu++) { | 411 | for (cpu = 0; cpu < nr_cpus; cpu++) { |
413 | err = ioctl(FD(evsel, cpu, thread), PERF_EVENT_IOC_ENABLE, 0); | 412 | int err = ioctl(FD(evsel, cpu, thread), PERF_EVENT_IOC_ENABLE, 0); |
414 | if (err) | 413 | if (err) |
415 | return err; | 414 | return err; |
416 | } | 415 | } |
@@ -1606,10 +1605,9 @@ void perf_evlist__close(struct perf_evlist *evlist) | |||
1606 | struct perf_evsel *evsel; | 1605 | struct perf_evsel *evsel; |
1607 | int ncpus = cpu_map__nr(evlist->cpus); | 1606 | int ncpus = cpu_map__nr(evlist->cpus); |
1608 | int nthreads = thread_map__nr(evlist->threads); | 1607 | int nthreads = thread_map__nr(evlist->threads); |
1609 | int n; | ||
1610 | 1608 | ||
1611 | evlist__for_each_entry_reverse(evlist, evsel) { | 1609 | evlist__for_each_entry_reverse(evlist, evsel) { |
1612 | n = evsel->cpus ? evsel->cpus->nr : ncpus; | 1610 | int n = evsel->cpus ? evsel->cpus->nr : ncpus; |
1613 | perf_evsel__close(evsel, n, nthreads); | 1611 | perf_evsel__close(evsel, n, nthreads); |
1614 | } | 1612 | } |
1615 | } | 1613 | } |
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c index 380e84c3af3d..8bc271141d9d 100644 --- a/tools/perf/util/evsel.c +++ b/tools/perf/util/evsel.c | |||
@@ -985,14 +985,13 @@ void perf_evsel__config(struct perf_evsel *evsel, struct record_opts *opts, | |||
985 | 985 | ||
986 | static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) | 986 | static int perf_evsel__alloc_fd(struct perf_evsel *evsel, int ncpus, int nthreads) |
987 | { | 987 | { |
988 | int cpu, thread; | ||
989 | |||
990 | if (evsel->system_wide) | 988 | if (evsel->system_wide) |
991 | nthreads = 1; | 989 | nthreads = 1; |
992 | 990 | ||
993 | evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); | 991 | evsel->fd = xyarray__new(ncpus, nthreads, sizeof(int)); |
994 | 992 | ||
995 | if (evsel->fd) { | 993 | if (evsel->fd) { |
994 | int cpu, thread; | ||
996 | for (cpu = 0; cpu < ncpus; cpu++) { | 995 | for (cpu = 0; cpu < ncpus; cpu++) { |
997 | for (thread = 0; thread < nthreads; thread++) { | 996 | for (thread = 0; thread < nthreads; thread++) { |
998 | FD(evsel, cpu, thread) = -1; | 997 | FD(evsel, cpu, thread) = -1; |
diff --git a/tools/perf/util/header.h b/tools/perf/util/header.h index d306ca118449..d30109b421ee 100644 --- a/tools/perf/util/header.h +++ b/tools/perf/util/header.h | |||
@@ -151,4 +151,5 @@ int write_padded(int fd, const void *bf, size_t count, size_t count_aligned); | |||
151 | */ | 151 | */ |
152 | int get_cpuid(char *buffer, size_t sz); | 152 | int get_cpuid(char *buffer, size_t sz); |
153 | 153 | ||
154 | char *get_cpuid_str(void); | ||
154 | #endif /* __PERF_HEADER_H */ | 155 | #endif /* __PERF_HEADER_H */ |
diff --git a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c index 7591a0c37473..16c06d3ae577 100644 --- a/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c +++ b/tools/perf/util/intel-pt-decoder/intel-pt-decoder.c | |||
@@ -90,6 +90,7 @@ struct intel_pt_decoder { | |||
90 | bool pge; | 90 | bool pge; |
91 | bool have_tma; | 91 | bool have_tma; |
92 | bool have_cyc; | 92 | bool have_cyc; |
93 | bool fixup_last_mtc; | ||
93 | uint64_t pos; | 94 | uint64_t pos; |
94 | uint64_t last_ip; | 95 | uint64_t last_ip; |
95 | uint64_t ip; | 96 | uint64_t ip; |
@@ -586,10 +587,31 @@ struct intel_pt_calc_cyc_to_tsc_info { | |||
586 | uint64_t tsc_timestamp; | 587 | uint64_t tsc_timestamp; |
587 | uint64_t timestamp; | 588 | uint64_t timestamp; |
588 | bool have_tma; | 589 | bool have_tma; |
590 | bool fixup_last_mtc; | ||
589 | bool from_mtc; | 591 | bool from_mtc; |
590 | double cbr_cyc_to_tsc; | 592 | double cbr_cyc_to_tsc; |
591 | }; | 593 | }; |
592 | 594 | ||
595 | /* | ||
596 | * MTC provides a 8-bit slice of CTC but the TMA packet only provides the lower | ||
597 | * 16 bits of CTC. If mtc_shift > 8 then some of the MTC bits are not in the CTC | ||
598 | * provided by the TMA packet. Fix-up the last_mtc calculated from the TMA | ||
599 | * packet by copying the missing bits from the current MTC assuming the least | ||
600 | * difference between the two, and that the current MTC comes after last_mtc. | ||
601 | */ | ||
602 | static void intel_pt_fixup_last_mtc(uint32_t mtc, int mtc_shift, | ||
603 | uint32_t *last_mtc) | ||
604 | { | ||
605 | uint32_t first_missing_bit = 1U << (16 - mtc_shift); | ||
606 | uint32_t mask = ~(first_missing_bit - 1); | ||
607 | |||
608 | *last_mtc |= mtc & mask; | ||
609 | if (*last_mtc >= mtc) { | ||
610 | *last_mtc -= first_missing_bit; | ||
611 | *last_mtc &= 0xff; | ||
612 | } | ||
613 | } | ||
614 | |||
593 | static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info) | 615 | static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info) |
594 | { | 616 | { |
595 | struct intel_pt_decoder *decoder = pkt_info->decoder; | 617 | struct intel_pt_decoder *decoder = pkt_info->decoder; |
@@ -619,6 +641,11 @@ static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info) | |||
619 | return 0; | 641 | return 0; |
620 | 642 | ||
621 | mtc = pkt_info->packet.payload; | 643 | mtc = pkt_info->packet.payload; |
644 | if (decoder->mtc_shift > 8 && data->fixup_last_mtc) { | ||
645 | data->fixup_last_mtc = false; | ||
646 | intel_pt_fixup_last_mtc(mtc, decoder->mtc_shift, | ||
647 | &data->last_mtc); | ||
648 | } | ||
622 | if (mtc > data->last_mtc) | 649 | if (mtc > data->last_mtc) |
623 | mtc_delta = mtc - data->last_mtc; | 650 | mtc_delta = mtc - data->last_mtc; |
624 | else | 651 | else |
@@ -687,6 +714,7 @@ static int intel_pt_calc_cyc_cb(struct intel_pt_pkt_info *pkt_info) | |||
687 | 714 | ||
688 | data->ctc_delta = 0; | 715 | data->ctc_delta = 0; |
689 | data->have_tma = true; | 716 | data->have_tma = true; |
717 | data->fixup_last_mtc = true; | ||
690 | 718 | ||
691 | return 0; | 719 | return 0; |
692 | 720 | ||
@@ -753,6 +781,7 @@ static void intel_pt_calc_cyc_to_tsc(struct intel_pt_decoder *decoder, | |||
753 | .tsc_timestamp = decoder->tsc_timestamp, | 781 | .tsc_timestamp = decoder->tsc_timestamp, |
754 | .timestamp = decoder->timestamp, | 782 | .timestamp = decoder->timestamp, |
755 | .have_tma = decoder->have_tma, | 783 | .have_tma = decoder->have_tma, |
784 | .fixup_last_mtc = decoder->fixup_last_mtc, | ||
756 | .from_mtc = from_mtc, | 785 | .from_mtc = from_mtc, |
757 | .cbr_cyc_to_tsc = 0, | 786 | .cbr_cyc_to_tsc = 0, |
758 | }; | 787 | }; |
@@ -1271,6 +1300,7 @@ static void intel_pt_calc_tma(struct intel_pt_decoder *decoder) | |||
1271 | } | 1300 | } |
1272 | decoder->ctc_delta = 0; | 1301 | decoder->ctc_delta = 0; |
1273 | decoder->have_tma = true; | 1302 | decoder->have_tma = true; |
1303 | decoder->fixup_last_mtc = true; | ||
1274 | intel_pt_log("CTC timestamp " x64_fmt " last MTC %#x CTC rem %#x\n", | 1304 | intel_pt_log("CTC timestamp " x64_fmt " last MTC %#x CTC rem %#x\n", |
1275 | decoder->ctc_timestamp, decoder->last_mtc, ctc_rem); | 1305 | decoder->ctc_timestamp, decoder->last_mtc, ctc_rem); |
1276 | } | 1306 | } |
@@ -1285,6 +1315,12 @@ static void intel_pt_calc_mtc_timestamp(struct intel_pt_decoder *decoder) | |||
1285 | 1315 | ||
1286 | mtc = decoder->packet.payload; | 1316 | mtc = decoder->packet.payload; |
1287 | 1317 | ||
1318 | if (decoder->mtc_shift > 8 && decoder->fixup_last_mtc) { | ||
1319 | decoder->fixup_last_mtc = false; | ||
1320 | intel_pt_fixup_last_mtc(mtc, decoder->mtc_shift, | ||
1321 | &decoder->last_mtc); | ||
1322 | } | ||
1323 | |||
1288 | if (mtc > decoder->last_mtc) | 1324 | if (mtc > decoder->last_mtc) |
1289 | mtc_delta = mtc - decoder->last_mtc; | 1325 | mtc_delta = mtc - decoder->last_mtc; |
1290 | else | 1326 | else |
@@ -1353,6 +1389,8 @@ static void intel_pt_calc_cyc_timestamp(struct intel_pt_decoder *decoder) | |||
1353 | timestamp, decoder->timestamp); | 1389 | timestamp, decoder->timestamp); |
1354 | else | 1390 | else |
1355 | decoder->timestamp = timestamp; | 1391 | decoder->timestamp = timestamp; |
1392 | |||
1393 | decoder->timestamp_insn_cnt = 0; | ||
1356 | } | 1394 | } |
1357 | 1395 | ||
1358 | /* Walk PSB+ packets when already in sync. */ | 1396 | /* Walk PSB+ packets when already in sync. */ |
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 18e4519abef2..df85b9efd80f 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c | |||
@@ -1745,9 +1745,8 @@ static int resolve_lbr_callchain_sample(struct thread *thread, | |||
1745 | int max_stack) | 1745 | int max_stack) |
1746 | { | 1746 | { |
1747 | struct ip_callchain *chain = sample->callchain; | 1747 | struct ip_callchain *chain = sample->callchain; |
1748 | int chain_nr = min(max_stack, (int)chain->nr); | 1748 | int chain_nr = min(max_stack, (int)chain->nr), i; |
1749 | u8 cpumode = PERF_RECORD_MISC_USER; | 1749 | u8 cpumode = PERF_RECORD_MISC_USER; |
1750 | int i, j, err; | ||
1751 | u64 ip; | 1750 | u64 ip; |
1752 | 1751 | ||
1753 | for (i = 0; i < chain_nr; i++) { | 1752 | for (i = 0; i < chain_nr; i++) { |
@@ -1758,7 +1757,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread, | |||
1758 | /* LBR only affects the user callchain */ | 1757 | /* LBR only affects the user callchain */ |
1759 | if (i != chain_nr) { | 1758 | if (i != chain_nr) { |
1760 | struct branch_stack *lbr_stack = sample->branch_stack; | 1759 | struct branch_stack *lbr_stack = sample->branch_stack; |
1761 | int lbr_nr = lbr_stack->nr; | 1760 | int lbr_nr = lbr_stack->nr, j; |
1762 | /* | 1761 | /* |
1763 | * LBR callstack can only get user call chain. | 1762 | * LBR callstack can only get user call chain. |
1764 | * The mix_chain_nr is kernel call chain | 1763 | * The mix_chain_nr is kernel call chain |
@@ -1772,6 +1771,7 @@ static int resolve_lbr_callchain_sample(struct thread *thread, | |||
1772 | int mix_chain_nr = i + 1 + lbr_nr + 1; | 1771 | int mix_chain_nr = i + 1 + lbr_nr + 1; |
1773 | 1772 | ||
1774 | for (j = 0; j < mix_chain_nr; j++) { | 1773 | for (j = 0; j < mix_chain_nr; j++) { |
1774 | int err; | ||
1775 | if (callchain_param.order == ORDER_CALLEE) { | 1775 | if (callchain_param.order == ORDER_CALLEE) { |
1776 | if (j < i + 1) | 1776 | if (j < i + 1) |
1777 | ip = chain->ips[j]; | 1777 | ip = chain->ips[j]; |
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c index 33546c3ac1fe..4e778eae1510 100644 --- a/tools/perf/util/parse-events.c +++ b/tools/perf/util/parse-events.c | |||
@@ -924,6 +924,7 @@ config_term_avail(int term_type, struct parse_events_error *err) | |||
924 | case PARSE_EVENTS__TERM_TYPE_CONFIG1: | 924 | case PARSE_EVENTS__TERM_TYPE_CONFIG1: |
925 | case PARSE_EVENTS__TERM_TYPE_CONFIG2: | 925 | case PARSE_EVENTS__TERM_TYPE_CONFIG2: |
926 | case PARSE_EVENTS__TERM_TYPE_NAME: | 926 | case PARSE_EVENTS__TERM_TYPE_NAME: |
927 | case PARSE_EVENTS__TERM_TYPE_SAMPLE_PERIOD: | ||
927 | return true; | 928 | return true; |
928 | default: | 929 | default: |
929 | if (!err) | 930 | if (!err) |
@@ -1458,7 +1459,7 @@ comp_pmu(const void *p1, const void *p2) | |||
1458 | struct perf_pmu_event_symbol *pmu1 = (struct perf_pmu_event_symbol *) p1; | 1459 | struct perf_pmu_event_symbol *pmu1 = (struct perf_pmu_event_symbol *) p1; |
1459 | struct perf_pmu_event_symbol *pmu2 = (struct perf_pmu_event_symbol *) p2; | 1460 | struct perf_pmu_event_symbol *pmu2 = (struct perf_pmu_event_symbol *) p2; |
1460 | 1461 | ||
1461 | return strcmp(pmu1->symbol, pmu2->symbol); | 1462 | return strcasecmp(pmu1->symbol, pmu2->symbol); |
1462 | } | 1463 | } |
1463 | 1464 | ||
1464 | static void perf_pmu__parse_cleanup(void) | 1465 | static void perf_pmu__parse_cleanup(void) |
@@ -2263,7 +2264,8 @@ out_enomem: | |||
2263 | /* | 2264 | /* |
2264 | * Print the help text for the event symbols: | 2265 | * Print the help text for the event symbols: |
2265 | */ | 2266 | */ |
2266 | void print_events(const char *event_glob, bool name_only) | 2267 | void print_events(const char *event_glob, bool name_only, bool quiet_flag, |
2268 | bool long_desc) | ||
2267 | { | 2269 | { |
2268 | print_symbol_events(event_glob, PERF_TYPE_HARDWARE, | 2270 | print_symbol_events(event_glob, PERF_TYPE_HARDWARE, |
2269 | event_symbols_hw, PERF_COUNT_HW_MAX, name_only); | 2271 | event_symbols_hw, PERF_COUNT_HW_MAX, name_only); |
@@ -2273,7 +2275,7 @@ void print_events(const char *event_glob, bool name_only) | |||
2273 | 2275 | ||
2274 | print_hwcache_events(event_glob, name_only); | 2276 | print_hwcache_events(event_glob, name_only); |
2275 | 2277 | ||
2276 | print_pmu_events(event_glob, name_only); | 2278 | print_pmu_events(event_glob, name_only, quiet_flag, long_desc); |
2277 | 2279 | ||
2278 | if (event_glob != NULL) | 2280 | if (event_glob != NULL) |
2279 | return; | 2281 | return; |
diff --git a/tools/perf/util/parse-events.h b/tools/perf/util/parse-events.h index 8d09a976fca8..da246a3ddb69 100644 --- a/tools/perf/util/parse-events.h +++ b/tools/perf/util/parse-events.h | |||
@@ -172,7 +172,8 @@ void parse_events_update_lists(struct list_head *list_event, | |||
172 | void parse_events_evlist_error(struct parse_events_evlist *data, | 172 | void parse_events_evlist_error(struct parse_events_evlist *data, |
173 | int idx, const char *str); | 173 | int idx, const char *str); |
174 | 174 | ||
175 | void print_events(const char *event_glob, bool name_only); | 175 | void print_events(const char *event_glob, bool name_only, bool quiet, |
176 | bool long_desc); | ||
176 | 177 | ||
177 | struct event_symbol { | 178 | struct event_symbol { |
178 | const char *symbol; | 179 | const char *symbol; |
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c index 2babcdf62839..b1474dcadfa2 100644 --- a/tools/perf/util/pmu.c +++ b/tools/perf/util/pmu.c | |||
@@ -12,6 +12,9 @@ | |||
12 | #include "pmu.h" | 12 | #include "pmu.h" |
13 | #include "parse-events.h" | 13 | #include "parse-events.h" |
14 | #include "cpumap.h" | 14 | #include "cpumap.h" |
15 | #include "header.h" | ||
16 | #include "pmu-events/pmu-events.h" | ||
17 | #include "cache.h" | ||
15 | 18 | ||
16 | struct perf_pmu_format { | 19 | struct perf_pmu_format { |
17 | char *name; | 20 | char *name; |
@@ -220,7 +223,8 @@ static int perf_pmu__parse_snapshot(struct perf_pmu_alias *alias, | |||
220 | } | 223 | } |
221 | 224 | ||
222 | static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, | 225 | static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, |
223 | char *desc __maybe_unused, char *val) | 226 | char *desc, char *val, char *long_desc, |
227 | char *topic) | ||
224 | { | 228 | { |
225 | struct perf_pmu_alias *alias; | 229 | struct perf_pmu_alias *alias; |
226 | int ret; | 230 | int ret; |
@@ -253,6 +257,11 @@ static int __perf_pmu__new_alias(struct list_head *list, char *dir, char *name, | |||
253 | perf_pmu__parse_snapshot(alias, dir, name); | 257 | perf_pmu__parse_snapshot(alias, dir, name); |
254 | } | 258 | } |
255 | 259 | ||
260 | alias->desc = desc ? strdup(desc) : NULL; | ||
261 | alias->long_desc = long_desc ? strdup(long_desc) : | ||
262 | desc ? strdup(desc) : NULL; | ||
263 | alias->topic = topic ? strdup(topic) : NULL; | ||
264 | |||
256 | list_add_tail(&alias->list, list); | 265 | list_add_tail(&alias->list, list); |
257 | 266 | ||
258 | return 0; | 267 | return 0; |
@@ -269,7 +278,7 @@ static int perf_pmu__new_alias(struct list_head *list, char *dir, char *name, FI | |||
269 | 278 | ||
270 | buf[ret] = 0; | 279 | buf[ret] = 0; |
271 | 280 | ||
272 | return __perf_pmu__new_alias(list, dir, name, NULL, buf); | 281 | return __perf_pmu__new_alias(list, dir, name, NULL, buf, NULL, NULL); |
273 | } | 282 | } |
274 | 283 | ||
275 | static inline bool pmu_alias_info_file(char *name) | 284 | static inline bool pmu_alias_info_file(char *name) |
@@ -473,6 +482,68 @@ static struct cpu_map *pmu_cpumask(const char *name) | |||
473 | return cpus; | 482 | return cpus; |
474 | } | 483 | } |
475 | 484 | ||
485 | /* | ||
486 | * Return the CPU id as a raw string. | ||
487 | * | ||
488 | * Each architecture should provide a more precise id string that | ||
489 | * can be use to match the architecture's "mapfile". | ||
490 | */ | ||
491 | char * __weak get_cpuid_str(void) | ||
492 | { | ||
493 | return NULL; | ||
494 | } | ||
495 | |||
496 | /* | ||
497 | * From the pmu_events_map, find the table of PMU events that corresponds | ||
498 | * to the current running CPU. Then, add all PMU events from that table | ||
499 | * as aliases. | ||
500 | */ | ||
501 | static void pmu_add_cpu_aliases(struct list_head *head) | ||
502 | { | ||
503 | int i; | ||
504 | struct pmu_events_map *map; | ||
505 | struct pmu_event *pe; | ||
506 | char *cpuid; | ||
507 | |||
508 | cpuid = getenv("PERF_CPUID"); | ||
509 | if (cpuid) | ||
510 | cpuid = strdup(cpuid); | ||
511 | if (!cpuid) | ||
512 | cpuid = get_cpuid_str(); | ||
513 | if (!cpuid) | ||
514 | return; | ||
515 | |||
516 | pr_debug("Using CPUID %s\n", cpuid); | ||
517 | |||
518 | i = 0; | ||
519 | while (1) { | ||
520 | map = &pmu_events_map[i++]; | ||
521 | if (!map->table) | ||
522 | goto out; | ||
523 | |||
524 | if (!strcmp(map->cpuid, cpuid)) | ||
525 | break; | ||
526 | } | ||
527 | |||
528 | /* | ||
529 | * Found a matching PMU events table. Create aliases | ||
530 | */ | ||
531 | i = 0; | ||
532 | while (1) { | ||
533 | pe = &map->table[i++]; | ||
534 | if (!pe->name) | ||
535 | break; | ||
536 | |||
537 | /* need type casts to override 'const' */ | ||
538 | __perf_pmu__new_alias(head, NULL, (char *)pe->name, | ||
539 | (char *)pe->desc, (char *)pe->event, | ||
540 | (char *)pe->long_desc, (char *)pe->topic); | ||
541 | } | ||
542 | |||
543 | out: | ||
544 | free(cpuid); | ||
545 | } | ||
546 | |||
476 | struct perf_event_attr * __weak | 547 | struct perf_event_attr * __weak |
477 | perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) | 548 | perf_pmu__get_default_config(struct perf_pmu *pmu __maybe_unused) |
478 | { | 549 | { |
@@ -497,6 +568,9 @@ static struct perf_pmu *pmu_lookup(const char *name) | |||
497 | if (pmu_aliases(name, &aliases)) | 568 | if (pmu_aliases(name, &aliases)) |
498 | return NULL; | 569 | return NULL; |
499 | 570 | ||
571 | if (!strcmp(name, "cpu")) | ||
572 | pmu_add_cpu_aliases(&aliases); | ||
573 | |||
500 | if (pmu_type(name, &type)) | 574 | if (pmu_type(name, &type)) |
501 | return NULL; | 575 | return NULL; |
502 | 576 | ||
@@ -983,21 +1057,63 @@ static char *format_alias_or(char *buf, int len, struct perf_pmu *pmu, | |||
983 | return buf; | 1057 | return buf; |
984 | } | 1058 | } |
985 | 1059 | ||
986 | static int cmp_string(const void *a, const void *b) | 1060 | struct sevent { |
1061 | char *name; | ||
1062 | char *desc; | ||
1063 | char *topic; | ||
1064 | }; | ||
1065 | |||
1066 | static int cmp_sevent(const void *a, const void *b) | ||
987 | { | 1067 | { |
988 | const char * const *as = a; | 1068 | const struct sevent *as = a; |
989 | const char * const *bs = b; | 1069 | const struct sevent *bs = b; |
990 | return strcmp(*as, *bs); | 1070 | |
1071 | /* Put extra events last */ | ||
1072 | if (!!as->desc != !!bs->desc) | ||
1073 | return !!as->desc - !!bs->desc; | ||
1074 | if (as->topic && bs->topic) { | ||
1075 | int n = strcmp(as->topic, bs->topic); | ||
1076 | |||
1077 | if (n) | ||
1078 | return n; | ||
1079 | } | ||
1080 | return strcmp(as->name, bs->name); | ||
991 | } | 1081 | } |
992 | 1082 | ||
993 | void print_pmu_events(const char *event_glob, bool name_only) | 1083 | static void wordwrap(char *s, int start, int max, int corr) |
1084 | { | ||
1085 | int column = start; | ||
1086 | int n; | ||
1087 | |||
1088 | while (*s) { | ||
1089 | int wlen = strcspn(s, " \t"); | ||
1090 | |||
1091 | if (column + wlen >= max && column > start) { | ||
1092 | printf("\n%*s", start, ""); | ||
1093 | column = start + corr; | ||
1094 | } | ||
1095 | n = printf("%s%.*s", column > start ? " " : "", wlen, s); | ||
1096 | if (n <= 0) | ||
1097 | break; | ||
1098 | s += wlen; | ||
1099 | column += n; | ||
1100 | while (isspace(*s)) | ||
1101 | s++; | ||
1102 | } | ||
1103 | } | ||
1104 | |||
1105 | void print_pmu_events(const char *event_glob, bool name_only, bool quiet_flag, | ||
1106 | bool long_desc) | ||
994 | { | 1107 | { |
995 | struct perf_pmu *pmu; | 1108 | struct perf_pmu *pmu; |
996 | struct perf_pmu_alias *alias; | 1109 | struct perf_pmu_alias *alias; |
997 | char buf[1024]; | 1110 | char buf[1024]; |
998 | int printed = 0; | 1111 | int printed = 0; |
999 | int len, j; | 1112 | int len, j; |
1000 | char **aliases; | 1113 | struct sevent *aliases; |
1114 | int numdesc = 0; | ||
1115 | int columns = pager_get_columns(); | ||
1116 | char *topic = NULL; | ||
1001 | 1117 | ||
1002 | pmu = NULL; | 1118 | pmu = NULL; |
1003 | len = 0; | 1119 | len = 0; |
@@ -1007,14 +1123,15 @@ void print_pmu_events(const char *event_glob, bool name_only) | |||
1007 | if (pmu->selectable) | 1123 | if (pmu->selectable) |
1008 | len++; | 1124 | len++; |
1009 | } | 1125 | } |
1010 | aliases = zalloc(sizeof(char *) * len); | 1126 | aliases = zalloc(sizeof(struct sevent) * len); |
1011 | if (!aliases) | 1127 | if (!aliases) |
1012 | goto out_enomem; | 1128 | goto out_enomem; |
1013 | pmu = NULL; | 1129 | pmu = NULL; |
1014 | j = 0; | 1130 | j = 0; |
1015 | while ((pmu = perf_pmu__scan(pmu)) != NULL) { | 1131 | while ((pmu = perf_pmu__scan(pmu)) != NULL) { |
1016 | list_for_each_entry(alias, &pmu->aliases, list) { | 1132 | list_for_each_entry(alias, &pmu->aliases, list) { |
1017 | char *name = format_alias(buf, sizeof(buf), pmu, alias); | 1133 | char *name = alias->desc ? alias->name : |
1134 | format_alias(buf, sizeof(buf), pmu, alias); | ||
1018 | bool is_cpu = !strcmp(pmu->name, "cpu"); | 1135 | bool is_cpu = !strcmp(pmu->name, "cpu"); |
1019 | 1136 | ||
1020 | if (event_glob != NULL && | 1137 | if (event_glob != NULL && |
@@ -1023,12 +1140,21 @@ void print_pmu_events(const char *event_glob, bool name_only) | |||
1023 | event_glob)))) | 1140 | event_glob)))) |
1024 | continue; | 1141 | continue; |
1025 | 1142 | ||
1026 | if (is_cpu && !name_only) | 1143 | if (is_cpu && !name_only && !alias->desc) |
1027 | name = format_alias_or(buf, sizeof(buf), pmu, alias); | 1144 | name = format_alias_or(buf, sizeof(buf), pmu, alias); |
1028 | 1145 | ||
1029 | aliases[j] = strdup(name); | 1146 | aliases[j].name = name; |
1030 | if (aliases[j] == NULL) | 1147 | if (is_cpu && !name_only && !alias->desc) |
1148 | aliases[j].name = format_alias_or(buf, | ||
1149 | sizeof(buf), | ||
1150 | pmu, alias); | ||
1151 | aliases[j].name = strdup(aliases[j].name); | ||
1152 | if (!aliases[j].name) | ||
1031 | goto out_enomem; | 1153 | goto out_enomem; |
1154 | |||
1155 | aliases[j].desc = long_desc ? alias->long_desc : | ||
1156 | alias->desc; | ||
1157 | aliases[j].topic = alias->topic; | ||
1032 | j++; | 1158 | j++; |
1033 | } | 1159 | } |
1034 | if (pmu->selectable && | 1160 | if (pmu->selectable && |
@@ -1036,25 +1162,39 @@ void print_pmu_events(const char *event_glob, bool name_only) | |||
1036 | char *s; | 1162 | char *s; |
1037 | if (asprintf(&s, "%s//", pmu->name) < 0) | 1163 | if (asprintf(&s, "%s//", pmu->name) < 0) |
1038 | goto out_enomem; | 1164 | goto out_enomem; |
1039 | aliases[j] = s; | 1165 | aliases[j].name = s; |
1040 | j++; | 1166 | j++; |
1041 | } | 1167 | } |
1042 | } | 1168 | } |
1043 | len = j; | 1169 | len = j; |
1044 | qsort(aliases, len, sizeof(char *), cmp_string); | 1170 | qsort(aliases, len, sizeof(struct sevent), cmp_sevent); |
1045 | for (j = 0; j < len; j++) { | 1171 | for (j = 0; j < len; j++) { |
1046 | if (name_only) { | 1172 | if (name_only) { |
1047 | printf("%s ", aliases[j]); | 1173 | printf("%s ", aliases[j].name); |
1048 | continue; | 1174 | continue; |
1049 | } | 1175 | } |
1050 | printf(" %-50s [Kernel PMU event]\n", aliases[j]); | 1176 | if (aliases[j].desc && !quiet_flag) { |
1177 | if (numdesc++ == 0) | ||
1178 | printf("\n"); | ||
1179 | if (aliases[j].topic && (!topic || | ||
1180 | strcmp(topic, aliases[j].topic))) { | ||
1181 | printf("%s%s:\n", topic ? "\n" : "", | ||
1182 | aliases[j].topic); | ||
1183 | topic = aliases[j].topic; | ||
1184 | } | ||
1185 | printf(" %-50s\n", aliases[j].name); | ||
1186 | printf("%*s", 8, "["); | ||
1187 | wordwrap(aliases[j].desc, 8, columns, 0); | ||
1188 | printf("]\n"); | ||
1189 | } else | ||
1190 | printf(" %-50s [Kernel PMU event]\n", aliases[j].name); | ||
1051 | printed++; | 1191 | printed++; |
1052 | } | 1192 | } |
1053 | if (printed && pager_in_use()) | 1193 | if (printed && pager_in_use()) |
1054 | printf("\n"); | 1194 | printf("\n"); |
1055 | out_free: | 1195 | out_free: |
1056 | for (j = 0; j < len; j++) | 1196 | for (j = 0; j < len; j++) |
1057 | zfree(&aliases[j]); | 1197 | zfree(&aliases[j].name); |
1058 | zfree(&aliases); | 1198 | zfree(&aliases); |
1059 | return; | 1199 | return; |
1060 | 1200 | ||
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h index 743422ad900b..25712034c815 100644 --- a/tools/perf/util/pmu.h +++ b/tools/perf/util/pmu.h | |||
@@ -40,6 +40,9 @@ struct perf_pmu_info { | |||
40 | 40 | ||
41 | struct perf_pmu_alias { | 41 | struct perf_pmu_alias { |
42 | char *name; | 42 | char *name; |
43 | char *desc; | ||
44 | char *long_desc; | ||
45 | char *topic; | ||
43 | struct list_head terms; /* HEAD struct parse_events_term -> list */ | 46 | struct list_head terms; /* HEAD struct parse_events_term -> list */ |
44 | struct list_head list; /* ELEM */ | 47 | struct list_head list; /* ELEM */ |
45 | char unit[UNIT_MAX_LEN+1]; | 48 | char unit[UNIT_MAX_LEN+1]; |
@@ -71,7 +74,8 @@ int perf_pmu__format_parse(char *dir, struct list_head *head); | |||
71 | 74 | ||
72 | struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); | 75 | struct perf_pmu *perf_pmu__scan(struct perf_pmu *pmu); |
73 | 76 | ||
74 | void print_pmu_events(const char *event_glob, bool name_only); | 77 | void print_pmu_events(const char *event_glob, bool name_only, bool quiet, |
78 | bool long_desc); | ||
75 | bool pmu_have_event(const char *pname, const char *name); | 79 | bool pmu_have_event(const char *pname, const char *name); |
76 | 80 | ||
77 | int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, | 81 | int perf_pmu__scan_file(struct perf_pmu *pmu, const char *name, const char *fmt, |
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c index fcfbef07b92d..d281ae2b54e8 100644 --- a/tools/perf/util/probe-event.c +++ b/tools/perf/util/probe-event.c | |||
@@ -213,7 +213,7 @@ static int convert_exec_to_group(const char *exec, char **result) | |||
213 | goto out; | 213 | goto out; |
214 | } | 214 | } |
215 | 215 | ||
216 | for (ptr2 = ptr1; ptr2 != '\0'; ptr2++) { | 216 | for (ptr2 = ptr1; *ptr2 != '\0'; ptr2++) { |
217 | if (!isalnum(*ptr2) && *ptr2 != '_') { | 217 | if (!isalnum(*ptr2) && *ptr2 != '_') { |
218 | *ptr2 = '\0'; | 218 | *ptr2 = '\0'; |
219 | break; | 219 | break; |
diff --git a/tools/perf/util/strbuf.h b/tools/perf/util/strbuf.h index b268a6648a5d..318424ea561d 100644 --- a/tools/perf/util/strbuf.h +++ b/tools/perf/util/strbuf.h | |||
@@ -66,9 +66,8 @@ static inline ssize_t strbuf_avail(const struct strbuf *sb) { | |||
66 | int strbuf_grow(struct strbuf *buf, size_t); | 66 | int strbuf_grow(struct strbuf *buf, size_t); |
67 | 67 | ||
68 | static inline int strbuf_setlen(struct strbuf *sb, size_t len) { | 68 | static inline int strbuf_setlen(struct strbuf *sb, size_t len) { |
69 | int ret; | ||
70 | if (!sb->alloc) { | 69 | if (!sb->alloc) { |
71 | ret = strbuf_grow(sb, 0); | 70 | int ret = strbuf_grow(sb, 0); |
72 | if (ret) | 71 | if (ret) |
73 | return ret; | 72 | return ret; |
74 | } | 73 | } |
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 8b10a55410a2..f5af87f66663 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c | |||
@@ -14,13 +14,12 @@ | |||
14 | 14 | ||
15 | int thread__init_map_groups(struct thread *thread, struct machine *machine) | 15 | int thread__init_map_groups(struct thread *thread, struct machine *machine) |
16 | { | 16 | { |
17 | struct thread *leader; | ||
18 | pid_t pid = thread->pid_; | 17 | pid_t pid = thread->pid_; |
19 | 18 | ||
20 | if (pid == thread->tid || pid == -1) { | 19 | if (pid == thread->tid || pid == -1) { |
21 | thread->mg = map_groups__new(machine); | 20 | thread->mg = map_groups__new(machine); |
22 | } else { | 21 | } else { |
23 | leader = __machine__findnew_thread(machine, pid, pid); | 22 | struct thread *leader = __machine__findnew_thread(machine, pid, pid); |
24 | if (leader) { | 23 | if (leader) { |
25 | thread->mg = map_groups__get(leader->mg); | 24 | thread->mg = map_groups__get(leader->mg); |
26 | thread__put(leader); | 25 | thread__put(leader); |
@@ -130,11 +129,10 @@ int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp, | |||
130 | bool exec) | 129 | bool exec) |
131 | { | 130 | { |
132 | struct comm *new, *curr = thread__comm(thread); | 131 | struct comm *new, *curr = thread__comm(thread); |
133 | int err; | ||
134 | 132 | ||
135 | /* Override the default :tid entry */ | 133 | /* Override the default :tid entry */ |
136 | if (!thread->comm_set) { | 134 | if (!thread->comm_set) { |
137 | err = comm__override(curr, str, timestamp, exec); | 135 | int err = comm__override(curr, str, timestamp, exec); |
138 | if (err) | 136 | if (err) |
139 | return err; | 137 | return err; |
140 | } else { | 138 | } else { |
@@ -270,10 +268,9 @@ static int thread__clone_map_groups(struct thread *thread, | |||
270 | 268 | ||
271 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) | 269 | int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp) |
272 | { | 270 | { |
273 | int err; | ||
274 | |||
275 | if (parent->comm_set) { | 271 | if (parent->comm_set) { |
276 | const char *comm = thread__comm_str(parent); | 272 | const char *comm = thread__comm_str(parent); |
273 | int err; | ||
277 | if (!comm) | 274 | if (!comm) |
278 | return -ENOMEM; | 275 | return -ENOMEM; |
279 | err = thread__set_comm(thread, comm, timestamp); | 276 | err = thread__set_comm(thread, comm, timestamp); |
diff --git a/tools/spi/Makefile b/tools/spi/Makefile index cd0db62e4d9d..3815b18ba070 100644 --- a/tools/spi/Makefile +++ b/tools/spi/Makefile | |||
@@ -1,3 +1,5 @@ | |||
1 | CC = $(CROSS_COMPILE)gcc | ||
2 | |||
1 | all: spidev_test spidev_fdx | 3 | all: spidev_test spidev_fdx |
2 | 4 | ||
3 | clean: | 5 | clean: |
diff --git a/tools/spi/spidev_test.c b/tools/spi/spidev_test.c index 8a73d8185316..f046b77cfefe 100644 --- a/tools/spi/spidev_test.c +++ b/tools/spi/spidev_test.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <getopt.h> | 19 | #include <getopt.h> |
20 | #include <fcntl.h> | 20 | #include <fcntl.h> |
21 | #include <sys/ioctl.h> | 21 | #include <sys/ioctl.h> |
22 | #include <linux/ioctl.h> | ||
22 | #include <sys/stat.h> | 23 | #include <sys/stat.h> |
23 | #include <linux/types.h> | 24 | #include <linux/types.h> |
24 | #include <linux/spi/spidev.h> | 25 | #include <linux/spi/spidev.h> |
@@ -284,7 +285,7 @@ static void parse_opts(int argc, char *argv[]) | |||
284 | 285 | ||
285 | static void transfer_escaped_string(int fd, char *str) | 286 | static void transfer_escaped_string(int fd, char *str) |
286 | { | 287 | { |
287 | size_t size = strlen(str + 1); | 288 | size_t size = strlen(str); |
288 | uint8_t *tx; | 289 | uint8_t *tx; |
289 | uint8_t *rx; | 290 | uint8_t *rx; |
290 | 291 | ||
diff --git a/tools/testing/nvdimm/Kbuild b/tools/testing/nvdimm/Kbuild index ad6dd0543019..582db95127ed 100644 --- a/tools/testing/nvdimm/Kbuild +++ b/tools/testing/nvdimm/Kbuild | |||
@@ -13,6 +13,7 @@ ldflags-y += --wrap=__release_region | |||
13 | ldflags-y += --wrap=devm_memremap_pages | 13 | ldflags-y += --wrap=devm_memremap_pages |
14 | ldflags-y += --wrap=insert_resource | 14 | ldflags-y += --wrap=insert_resource |
15 | ldflags-y += --wrap=remove_resource | 15 | ldflags-y += --wrap=remove_resource |
16 | ldflags-y += --wrap=acpi_evaluate_object | ||
16 | 17 | ||
17 | DRIVERS := ../../../drivers | 18 | DRIVERS := ../../../drivers |
18 | NVDIMM_SRC := $(DRIVERS)/nvdimm | 19 | NVDIMM_SRC := $(DRIVERS)/nvdimm |
diff --git a/tools/testing/nvdimm/config_check.c b/tools/testing/nvdimm/config_check.c index 878daf3429e8..7dc5a0af9b54 100644 --- a/tools/testing/nvdimm/config_check.c +++ b/tools/testing/nvdimm/config_check.c | |||
@@ -1,4 +1,3 @@ | |||
1 | #include <linux/kconfig.h> | ||
2 | #include <linux/bug.h> | 1 | #include <linux/bug.h> |
3 | 2 | ||
4 | void check(void) | 3 | void check(void) |
diff --git a/tools/testing/nvdimm/test/iomap.c b/tools/testing/nvdimm/test/iomap.c index c29f8dca9e67..3ccef732fce9 100644 --- a/tools/testing/nvdimm/test/iomap.c +++ b/tools/testing/nvdimm/test/iomap.c | |||
@@ -17,6 +17,7 @@ | |||
17 | #include <linux/module.h> | 17 | #include <linux/module.h> |
18 | #include <linux/types.h> | 18 | #include <linux/types.h> |
19 | #include <linux/pfn_t.h> | 19 | #include <linux/pfn_t.h> |
20 | #include <linux/acpi.h> | ||
20 | #include <linux/io.h> | 21 | #include <linux/io.h> |
21 | #include <linux/mm.h> | 22 | #include <linux/mm.h> |
22 | #include "nfit_test.h" | 23 | #include "nfit_test.h" |
@@ -73,7 +74,7 @@ void __iomem *__nfit_test_ioremap(resource_size_t offset, unsigned long size, | |||
73 | 74 | ||
74 | if (nfit_res) | 75 | if (nfit_res) |
75 | return (void __iomem *) nfit_res->buf + offset | 76 | return (void __iomem *) nfit_res->buf + offset |
76 | - nfit_res->res->start; | 77 | - nfit_res->res.start; |
77 | return fallback_fn(offset, size); | 78 | return fallback_fn(offset, size); |
78 | } | 79 | } |
79 | 80 | ||
@@ -84,7 +85,7 @@ void __iomem *__wrap_devm_ioremap_nocache(struct device *dev, | |||
84 | 85 | ||
85 | if (nfit_res) | 86 | if (nfit_res) |
86 | return (void __iomem *) nfit_res->buf + offset | 87 | return (void __iomem *) nfit_res->buf + offset |
87 | - nfit_res->res->start; | 88 | - nfit_res->res.start; |
88 | return devm_ioremap_nocache(dev, offset, size); | 89 | return devm_ioremap_nocache(dev, offset, size); |
89 | } | 90 | } |
90 | EXPORT_SYMBOL(__wrap_devm_ioremap_nocache); | 91 | EXPORT_SYMBOL(__wrap_devm_ioremap_nocache); |
@@ -95,7 +96,7 @@ void *__wrap_devm_memremap(struct device *dev, resource_size_t offset, | |||
95 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); | 96 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); |
96 | 97 | ||
97 | if (nfit_res) | 98 | if (nfit_res) |
98 | return nfit_res->buf + offset - nfit_res->res->start; | 99 | return nfit_res->buf + offset - nfit_res->res.start; |
99 | return devm_memremap(dev, offset, size, flags); | 100 | return devm_memremap(dev, offset, size, flags); |
100 | } | 101 | } |
101 | EXPORT_SYMBOL(__wrap_devm_memremap); | 102 | EXPORT_SYMBOL(__wrap_devm_memremap); |
@@ -107,7 +108,7 @@ void *__wrap_devm_memremap_pages(struct device *dev, struct resource *res, | |||
107 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); | 108 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); |
108 | 109 | ||
109 | if (nfit_res) | 110 | if (nfit_res) |
110 | return nfit_res->buf + offset - nfit_res->res->start; | 111 | return nfit_res->buf + offset - nfit_res->res.start; |
111 | return devm_memremap_pages(dev, res, ref, altmap); | 112 | return devm_memremap_pages(dev, res, ref, altmap); |
112 | } | 113 | } |
113 | EXPORT_SYMBOL(__wrap_devm_memremap_pages); | 114 | EXPORT_SYMBOL(__wrap_devm_memremap_pages); |
@@ -128,7 +129,7 @@ void *__wrap_memremap(resource_size_t offset, size_t size, | |||
128 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); | 129 | struct nfit_test_resource *nfit_res = get_nfit_res(offset); |
129 | 130 | ||
130 | if (nfit_res) | 131 | if (nfit_res) |
131 | return nfit_res->buf + offset - nfit_res->res->start; | 132 | return nfit_res->buf + offset - nfit_res->res.start; |
132 | return memremap(offset, size, flags); | 133 | return memremap(offset, size, flags); |
133 | } | 134 | } |
134 | EXPORT_SYMBOL(__wrap_memremap); | 135 | EXPORT_SYMBOL(__wrap_memremap); |
@@ -174,6 +175,63 @@ void __wrap_memunmap(void *addr) | |||
174 | } | 175 | } |
175 | EXPORT_SYMBOL(__wrap_memunmap); | 176 | EXPORT_SYMBOL(__wrap_memunmap); |
176 | 177 | ||
178 | static bool nfit_test_release_region(struct device *dev, | ||
179 | struct resource *parent, resource_size_t start, | ||
180 | resource_size_t n); | ||
181 | |||
182 | static void nfit_devres_release(struct device *dev, void *data) | ||
183 | { | ||
184 | struct resource *res = *((struct resource **) data); | ||
185 | |||
186 | WARN_ON(!nfit_test_release_region(NULL, &iomem_resource, res->start, | ||
187 | resource_size(res))); | ||
188 | } | ||
189 | |||
190 | static int match(struct device *dev, void *__res, void *match_data) | ||
191 | { | ||
192 | struct resource *res = *((struct resource **) __res); | ||
193 | resource_size_t start = *((resource_size_t *) match_data); | ||
194 | |||
195 | return res->start == start; | ||
196 | } | ||
197 | |||
198 | static bool nfit_test_release_region(struct device *dev, | ||
199 | struct resource *parent, resource_size_t start, | ||
200 | resource_size_t n) | ||
201 | { | ||
202 | if (parent == &iomem_resource) { | ||
203 | struct nfit_test_resource *nfit_res = get_nfit_res(start); | ||
204 | |||
205 | if (nfit_res) { | ||
206 | struct nfit_test_request *req; | ||
207 | struct resource *res = NULL; | ||
208 | |||
209 | if (dev) { | ||
210 | devres_release(dev, nfit_devres_release, match, | ||
211 | &start); | ||
212 | return true; | ||
213 | } | ||
214 | |||
215 | spin_lock(&nfit_res->lock); | ||
216 | list_for_each_entry(req, &nfit_res->requests, list) | ||
217 | if (req->res.start == start) { | ||
218 | res = &req->res; | ||
219 | list_del(&req->list); | ||
220 | break; | ||
221 | } | ||
222 | spin_unlock(&nfit_res->lock); | ||
223 | |||
224 | WARN(!res || resource_size(res) != n, | ||
225 | "%s: start: %llx n: %llx mismatch: %pr\n", | ||
226 | __func__, start, n, res); | ||
227 | if (res) | ||
228 | kfree(req); | ||
229 | return true; | ||
230 | } | ||
231 | } | ||
232 | return false; | ||
233 | } | ||
234 | |||
177 | static struct resource *nfit_test_request_region(struct device *dev, | 235 | static struct resource *nfit_test_request_region(struct device *dev, |
178 | struct resource *parent, resource_size_t start, | 236 | struct resource *parent, resource_size_t start, |
179 | resource_size_t n, const char *name, int flags) | 237 | resource_size_t n, const char *name, int flags) |
@@ -183,21 +241,57 @@ static struct resource *nfit_test_request_region(struct device *dev, | |||
183 | if (parent == &iomem_resource) { | 241 | if (parent == &iomem_resource) { |
184 | nfit_res = get_nfit_res(start); | 242 | nfit_res = get_nfit_res(start); |
185 | if (nfit_res) { | 243 | if (nfit_res) { |
186 | struct resource *res = nfit_res->res + 1; | 244 | struct nfit_test_request *req; |
245 | struct resource *res = NULL; | ||
187 | 246 | ||
188 | if (start + n > nfit_res->res->start | 247 | if (start + n > nfit_res->res.start |
189 | + resource_size(nfit_res->res)) { | 248 | + resource_size(&nfit_res->res)) { |
190 | pr_debug("%s: start: %llx n: %llx overflow: %pr\n", | 249 | pr_debug("%s: start: %llx n: %llx overflow: %pr\n", |
191 | __func__, start, n, | 250 | __func__, start, n, |
192 | nfit_res->res); | 251 | &nfit_res->res); |
193 | return NULL; | 252 | return NULL; |
194 | } | 253 | } |
195 | 254 | ||
255 | spin_lock(&nfit_res->lock); | ||
256 | list_for_each_entry(req, &nfit_res->requests, list) | ||
257 | if (start == req->res.start) { | ||
258 | res = &req->res; | ||
259 | break; | ||
260 | } | ||
261 | spin_unlock(&nfit_res->lock); | ||
262 | |||
263 | if (res) { | ||
264 | WARN(1, "%pr already busy\n", res); | ||
265 | return NULL; | ||
266 | } | ||
267 | |||
268 | req = kzalloc(sizeof(*req), GFP_KERNEL); | ||
269 | if (!req) | ||
270 | return NULL; | ||
271 | INIT_LIST_HEAD(&req->list); | ||
272 | res = &req->res; | ||
273 | |||
196 | res->start = start; | 274 | res->start = start; |
197 | res->end = start + n - 1; | 275 | res->end = start + n - 1; |
198 | res->name = name; | 276 | res->name = name; |
199 | res->flags = resource_type(parent); | 277 | res->flags = resource_type(parent); |
200 | res->flags |= IORESOURCE_BUSY | flags; | 278 | res->flags |= IORESOURCE_BUSY | flags; |
279 | spin_lock(&nfit_res->lock); | ||
280 | list_add(&req->list, &nfit_res->requests); | ||
281 | spin_unlock(&nfit_res->lock); | ||
282 | |||
283 | if (dev) { | ||
284 | struct resource **d; | ||
285 | |||
286 | d = devres_alloc(nfit_devres_release, | ||
287 | sizeof(struct resource *), | ||
288 | GFP_KERNEL); | ||
289 | if (!d) | ||
290 | return NULL; | ||
291 | *d = res; | ||
292 | devres_add(dev, d); | ||
293 | } | ||
294 | |||
201 | pr_debug("%s: %pr\n", __func__, res); | 295 | pr_debug("%s: %pr\n", __func__, res); |
202 | return res; | 296 | return res; |
203 | } | 297 | } |
@@ -241,29 +335,10 @@ struct resource *__wrap___devm_request_region(struct device *dev, | |||
241 | } | 335 | } |
242 | EXPORT_SYMBOL(__wrap___devm_request_region); | 336 | EXPORT_SYMBOL(__wrap___devm_request_region); |
243 | 337 | ||
244 | static bool nfit_test_release_region(struct resource *parent, | ||
245 | resource_size_t start, resource_size_t n) | ||
246 | { | ||
247 | if (parent == &iomem_resource) { | ||
248 | struct nfit_test_resource *nfit_res = get_nfit_res(start); | ||
249 | if (nfit_res) { | ||
250 | struct resource *res = nfit_res->res + 1; | ||
251 | |||
252 | if (start != res->start || resource_size(res) != n) | ||
253 | pr_info("%s: start: %llx n: %llx mismatch: %pr\n", | ||
254 | __func__, start, n, res); | ||
255 | else | ||
256 | memset(res, 0, sizeof(*res)); | ||
257 | return true; | ||
258 | } | ||
259 | } | ||
260 | return false; | ||
261 | } | ||
262 | |||
263 | void __wrap___release_region(struct resource *parent, resource_size_t start, | 338 | void __wrap___release_region(struct resource *parent, resource_size_t start, |
264 | resource_size_t n) | 339 | resource_size_t n) |
265 | { | 340 | { |
266 | if (!nfit_test_release_region(parent, start, n)) | 341 | if (!nfit_test_release_region(NULL, parent, start, n)) |
267 | __release_region(parent, start, n); | 342 | __release_region(parent, start, n); |
268 | } | 343 | } |
269 | EXPORT_SYMBOL(__wrap___release_region); | 344 | EXPORT_SYMBOL(__wrap___release_region); |
@@ -271,9 +346,25 @@ EXPORT_SYMBOL(__wrap___release_region); | |||
271 | void __wrap___devm_release_region(struct device *dev, struct resource *parent, | 346 | void __wrap___devm_release_region(struct device *dev, struct resource *parent, |
272 | resource_size_t start, resource_size_t n) | 347 | resource_size_t start, resource_size_t n) |
273 | { | 348 | { |
274 | if (!nfit_test_release_region(parent, start, n)) | 349 | if (!nfit_test_release_region(dev, parent, start, n)) |
275 | __devm_release_region(dev, parent, start, n); | 350 | __devm_release_region(dev, parent, start, n); |
276 | } | 351 | } |
277 | EXPORT_SYMBOL(__wrap___devm_release_region); | 352 | EXPORT_SYMBOL(__wrap___devm_release_region); |
278 | 353 | ||
354 | acpi_status __wrap_acpi_evaluate_object(acpi_handle handle, acpi_string path, | ||
355 | struct acpi_object_list *p, struct acpi_buffer *buf) | ||
356 | { | ||
357 | struct nfit_test_resource *nfit_res = get_nfit_res((long) handle); | ||
358 | union acpi_object **obj; | ||
359 | |||
360 | if (!nfit_res || strcmp(path, "_FIT") || !buf) | ||
361 | return acpi_evaluate_object(handle, path, p, buf); | ||
362 | |||
363 | obj = nfit_res->buf; | ||
364 | buf->length = sizeof(union acpi_object); | ||
365 | buf->pointer = *obj; | ||
366 | return AE_OK; | ||
367 | } | ||
368 | EXPORT_SYMBOL(__wrap_acpi_evaluate_object); | ||
369 | |||
279 | MODULE_LICENSE("GPL v2"); | 370 | MODULE_LICENSE("GPL v2"); |
diff --git a/tools/testing/nvdimm/test/nfit.c b/tools/testing/nvdimm/test/nfit.c index f64c57bf1d4b..c9a6458cb63e 100644 --- a/tools/testing/nvdimm/test/nfit.c +++ b/tools/testing/nvdimm/test/nfit.c | |||
@@ -132,6 +132,8 @@ static u32 handle[NUM_DCR] = { | |||
132 | [4] = NFIT_DIMM_HANDLE(0, 1, 0, 0, 0), | 132 | [4] = NFIT_DIMM_HANDLE(0, 1, 0, 0, 0), |
133 | }; | 133 | }; |
134 | 134 | ||
135 | static unsigned long dimm_fail_cmd_flags[NUM_DCR]; | ||
136 | |||
135 | struct nfit_test { | 137 | struct nfit_test { |
136 | struct acpi_nfit_desc acpi_desc; | 138 | struct acpi_nfit_desc acpi_desc; |
137 | struct platform_device pdev; | 139 | struct platform_device pdev; |
@@ -154,11 +156,14 @@ struct nfit_test { | |||
154 | int (*alloc)(struct nfit_test *t); | 156 | int (*alloc)(struct nfit_test *t); |
155 | void (*setup)(struct nfit_test *t); | 157 | void (*setup)(struct nfit_test *t); |
156 | int setup_hotplug; | 158 | int setup_hotplug; |
159 | union acpi_object **_fit; | ||
160 | dma_addr_t _fit_dma; | ||
157 | struct ars_state { | 161 | struct ars_state { |
158 | struct nd_cmd_ars_status *ars_status; | 162 | struct nd_cmd_ars_status *ars_status; |
159 | unsigned long deadline; | 163 | unsigned long deadline; |
160 | spinlock_t lock; | 164 | spinlock_t lock; |
161 | } ars_state; | 165 | } ars_state; |
166 | struct device *dimm_dev[NUM_DCR]; | ||
162 | }; | 167 | }; |
163 | 168 | ||
164 | static struct nfit_test *to_nfit_test(struct device *dev) | 169 | static struct nfit_test *to_nfit_test(struct device *dev) |
@@ -411,6 +416,9 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, | |||
411 | if (i >= ARRAY_SIZE(handle)) | 416 | if (i >= ARRAY_SIZE(handle)) |
412 | return -ENXIO; | 417 | return -ENXIO; |
413 | 418 | ||
419 | if ((1 << func) & dimm_fail_cmd_flags[i]) | ||
420 | return -EIO; | ||
421 | |||
414 | switch (func) { | 422 | switch (func) { |
415 | case ND_CMD_GET_CONFIG_SIZE: | 423 | case ND_CMD_GET_CONFIG_SIZE: |
416 | rc = nfit_test_cmd_get_config_size(buf, buf_len); | 424 | rc = nfit_test_cmd_get_config_size(buf, buf_len); |
@@ -428,6 +436,9 @@ static int nfit_test_ctl(struct nvdimm_bus_descriptor *nd_desc, | |||
428 | break; | 436 | break; |
429 | case ND_CMD_SMART_THRESHOLD: | 437 | case ND_CMD_SMART_THRESHOLD: |
430 | rc = nfit_test_cmd_smart_threshold(buf, buf_len); | 438 | rc = nfit_test_cmd_smart_threshold(buf, buf_len); |
439 | device_lock(&t->pdev.dev); | ||
440 | __acpi_nvdimm_notify(t->dimm_dev[i], 0x81); | ||
441 | device_unlock(&t->pdev.dev); | ||
431 | break; | 442 | break; |
432 | default: | 443 | default: |
433 | return -ENOTTY; | 444 | return -ENOTTY; |
@@ -467,14 +478,12 @@ static struct nfit_test *instances[NUM_NFITS]; | |||
467 | static void release_nfit_res(void *data) | 478 | static void release_nfit_res(void *data) |
468 | { | 479 | { |
469 | struct nfit_test_resource *nfit_res = data; | 480 | struct nfit_test_resource *nfit_res = data; |
470 | struct resource *res = nfit_res->res; | ||
471 | 481 | ||
472 | spin_lock(&nfit_test_lock); | 482 | spin_lock(&nfit_test_lock); |
473 | list_del(&nfit_res->list); | 483 | list_del(&nfit_res->list); |
474 | spin_unlock(&nfit_test_lock); | 484 | spin_unlock(&nfit_test_lock); |
475 | 485 | ||
476 | vfree(nfit_res->buf); | 486 | vfree(nfit_res->buf); |
477 | kfree(res); | ||
478 | kfree(nfit_res); | 487 | kfree(nfit_res); |
479 | } | 488 | } |
480 | 489 | ||
@@ -482,12 +491,11 @@ static void *__test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma, | |||
482 | void *buf) | 491 | void *buf) |
483 | { | 492 | { |
484 | struct device *dev = &t->pdev.dev; | 493 | struct device *dev = &t->pdev.dev; |
485 | struct resource *res = kzalloc(sizeof(*res) * 2, GFP_KERNEL); | ||
486 | struct nfit_test_resource *nfit_res = kzalloc(sizeof(*nfit_res), | 494 | struct nfit_test_resource *nfit_res = kzalloc(sizeof(*nfit_res), |
487 | GFP_KERNEL); | 495 | GFP_KERNEL); |
488 | int rc; | 496 | int rc; |
489 | 497 | ||
490 | if (!res || !buf || !nfit_res) | 498 | if (!buf || !nfit_res) |
491 | goto err; | 499 | goto err; |
492 | rc = devm_add_action(dev, release_nfit_res, nfit_res); | 500 | rc = devm_add_action(dev, release_nfit_res, nfit_res); |
493 | if (rc) | 501 | if (rc) |
@@ -496,10 +504,11 @@ static void *__test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma, | |||
496 | memset(buf, 0, size); | 504 | memset(buf, 0, size); |
497 | nfit_res->dev = dev; | 505 | nfit_res->dev = dev; |
498 | nfit_res->buf = buf; | 506 | nfit_res->buf = buf; |
499 | nfit_res->res = res; | 507 | nfit_res->res.start = *dma; |
500 | res->start = *dma; | 508 | nfit_res->res.end = *dma + size - 1; |
501 | res->end = *dma + size - 1; | 509 | nfit_res->res.name = "NFIT"; |
502 | res->name = "NFIT"; | 510 | spin_lock_init(&nfit_res->lock); |
511 | INIT_LIST_HEAD(&nfit_res->requests); | ||
503 | spin_lock(&nfit_test_lock); | 512 | spin_lock(&nfit_test_lock); |
504 | list_add(&nfit_res->list, &t->resources); | 513 | list_add(&nfit_res->list, &t->resources); |
505 | spin_unlock(&nfit_test_lock); | 514 | spin_unlock(&nfit_test_lock); |
@@ -508,7 +517,6 @@ static void *__test_alloc(struct nfit_test *t, size_t size, dma_addr_t *dma, | |||
508 | err: | 517 | err: |
509 | if (buf) | 518 | if (buf) |
510 | vfree(buf); | 519 | vfree(buf); |
511 | kfree(res); | ||
512 | kfree(nfit_res); | 520 | kfree(nfit_res); |
513 | return NULL; | 521 | return NULL; |
514 | } | 522 | } |
@@ -533,13 +541,13 @@ static struct nfit_test_resource *nfit_test_lookup(resource_size_t addr) | |||
533 | continue; | 541 | continue; |
534 | spin_lock(&nfit_test_lock); | 542 | spin_lock(&nfit_test_lock); |
535 | list_for_each_entry(n, &t->resources, list) { | 543 | list_for_each_entry(n, &t->resources, list) { |
536 | if (addr >= n->res->start && (addr < n->res->start | 544 | if (addr >= n->res.start && (addr < n->res.start |
537 | + resource_size(n->res))) { | 545 | + resource_size(&n->res))) { |
538 | nfit_res = n; | 546 | nfit_res = n; |
539 | break; | 547 | break; |
540 | } else if (addr >= (unsigned long) n->buf | 548 | } else if (addr >= (unsigned long) n->buf |
541 | && (addr < (unsigned long) n->buf | 549 | && (addr < (unsigned long) n->buf |
542 | + resource_size(n->res))) { | 550 | + resource_size(&n->res))) { |
543 | nfit_res = n; | 551 | nfit_res = n; |
544 | break; | 552 | break; |
545 | } | 553 | } |
@@ -564,6 +572,86 @@ static int ars_state_init(struct device *dev, struct ars_state *ars_state) | |||
564 | return 0; | 572 | return 0; |
565 | } | 573 | } |
566 | 574 | ||
575 | static void put_dimms(void *data) | ||
576 | { | ||
577 | struct device **dimm_dev = data; | ||
578 | int i; | ||
579 | |||
580 | for (i = 0; i < NUM_DCR; i++) | ||
581 | if (dimm_dev[i]) | ||
582 | device_unregister(dimm_dev[i]); | ||
583 | } | ||
584 | |||
585 | static struct class *nfit_test_dimm; | ||
586 | |||
587 | static int dimm_name_to_id(struct device *dev) | ||
588 | { | ||
589 | int dimm; | ||
590 | |||
591 | if (sscanf(dev_name(dev), "test_dimm%d", &dimm) != 1 | ||
592 | || dimm >= NUM_DCR || dimm < 0) | ||
593 | return -ENXIO; | ||
594 | return dimm; | ||
595 | } | ||
596 | |||
597 | |||
598 | static ssize_t handle_show(struct device *dev, struct device_attribute *attr, | ||
599 | char *buf) | ||
600 | { | ||
601 | int dimm = dimm_name_to_id(dev); | ||
602 | |||
603 | if (dimm < 0) | ||
604 | return dimm; | ||
605 | |||
606 | return sprintf(buf, "%#x", handle[dimm]); | ||
607 | } | ||
608 | DEVICE_ATTR_RO(handle); | ||
609 | |||
610 | static ssize_t fail_cmd_show(struct device *dev, struct device_attribute *attr, | ||
611 | char *buf) | ||
612 | { | ||
613 | int dimm = dimm_name_to_id(dev); | ||
614 | |||
615 | if (dimm < 0) | ||
616 | return dimm; | ||
617 | |||
618 | return sprintf(buf, "%#lx\n", dimm_fail_cmd_flags[dimm]); | ||
619 | } | ||
620 | |||
621 | static ssize_t fail_cmd_store(struct device *dev, struct device_attribute *attr, | ||
622 | const char *buf, size_t size) | ||
623 | { | ||
624 | int dimm = dimm_name_to_id(dev); | ||
625 | unsigned long val; | ||
626 | ssize_t rc; | ||
627 | |||
628 | if (dimm < 0) | ||
629 | return dimm; | ||
630 | |||
631 | rc = kstrtol(buf, 0, &val); | ||
632 | if (rc) | ||
633 | return rc; | ||
634 | |||
635 | dimm_fail_cmd_flags[dimm] = val; | ||
636 | return size; | ||
637 | } | ||
638 | static DEVICE_ATTR_RW(fail_cmd); | ||
639 | |||
640 | static struct attribute *nfit_test_dimm_attributes[] = { | ||
641 | &dev_attr_fail_cmd.attr, | ||
642 | &dev_attr_handle.attr, | ||
643 | NULL, | ||
644 | }; | ||
645 | |||
646 | static struct attribute_group nfit_test_dimm_attribute_group = { | ||
647 | .attrs = nfit_test_dimm_attributes, | ||
648 | }; | ||
649 | |||
650 | static const struct attribute_group *nfit_test_dimm_attribute_groups[] = { | ||
651 | &nfit_test_dimm_attribute_group, | ||
652 | NULL, | ||
653 | }; | ||
654 | |||
567 | static int nfit_test0_alloc(struct nfit_test *t) | 655 | static int nfit_test0_alloc(struct nfit_test *t) |
568 | { | 656 | { |
569 | size_t nfit_size = sizeof(struct acpi_nfit_system_address) * NUM_SPA | 657 | size_t nfit_size = sizeof(struct acpi_nfit_system_address) * NUM_SPA |
@@ -616,6 +704,21 @@ static int nfit_test0_alloc(struct nfit_test *t) | |||
616 | return -ENOMEM; | 704 | return -ENOMEM; |
617 | } | 705 | } |
618 | 706 | ||
707 | t->_fit = test_alloc(t, sizeof(union acpi_object **), &t->_fit_dma); | ||
708 | if (!t->_fit) | ||
709 | return -ENOMEM; | ||
710 | |||
711 | if (devm_add_action_or_reset(&t->pdev.dev, put_dimms, t->dimm_dev)) | ||
712 | return -ENOMEM; | ||
713 | for (i = 0; i < NUM_DCR; i++) { | ||
714 | t->dimm_dev[i] = device_create_with_groups(nfit_test_dimm, | ||
715 | &t->pdev.dev, 0, NULL, | ||
716 | nfit_test_dimm_attribute_groups, | ||
717 | "test_dimm%d", i); | ||
718 | if (!t->dimm_dev[i]) | ||
719 | return -ENOMEM; | ||
720 | } | ||
721 | |||
619 | return ars_state_init(&t->pdev.dev, &t->ars_state); | 722 | return ars_state_init(&t->pdev.dev, &t->ars_state); |
620 | } | 723 | } |
621 | 724 | ||
@@ -1409,6 +1512,8 @@ static int nfit_test_probe(struct platform_device *pdev) | |||
1409 | struct acpi_nfit_desc *acpi_desc; | 1512 | struct acpi_nfit_desc *acpi_desc; |
1410 | struct device *dev = &pdev->dev; | 1513 | struct device *dev = &pdev->dev; |
1411 | struct nfit_test *nfit_test; | 1514 | struct nfit_test *nfit_test; |
1515 | struct nfit_mem *nfit_mem; | ||
1516 | union acpi_object *obj; | ||
1412 | int rc; | 1517 | int rc; |
1413 | 1518 | ||
1414 | nfit_test = to_nfit_test(&pdev->dev); | 1519 | nfit_test = to_nfit_test(&pdev->dev); |
@@ -1476,14 +1581,30 @@ static int nfit_test_probe(struct platform_device *pdev) | |||
1476 | if (nfit_test->setup != nfit_test0_setup) | 1581 | if (nfit_test->setup != nfit_test0_setup) |
1477 | return 0; | 1582 | return 0; |
1478 | 1583 | ||
1479 | flush_work(&acpi_desc->work); | ||
1480 | nfit_test->setup_hotplug = 1; | 1584 | nfit_test->setup_hotplug = 1; |
1481 | nfit_test->setup(nfit_test); | 1585 | nfit_test->setup(nfit_test); |
1482 | 1586 | ||
1483 | rc = acpi_nfit_init(acpi_desc, nfit_test->nfit_buf, | 1587 | obj = kzalloc(sizeof(*obj), GFP_KERNEL); |
1484 | nfit_test->nfit_size); | 1588 | if (!obj) |
1485 | if (rc) | 1589 | return -ENOMEM; |
1486 | return rc; | 1590 | obj->type = ACPI_TYPE_BUFFER; |
1591 | obj->buffer.length = nfit_test->nfit_size; | ||
1592 | obj->buffer.pointer = nfit_test->nfit_buf; | ||
1593 | *(nfit_test->_fit) = obj; | ||
1594 | __acpi_nfit_notify(&pdev->dev, nfit_test, 0x80); | ||
1595 | |||
1596 | /* associate dimm devices with nfit_mem data for notification testing */ | ||
1597 | mutex_lock(&acpi_desc->init_mutex); | ||
1598 | list_for_each_entry(nfit_mem, &acpi_desc->dimms, list) { | ||
1599 | u32 nfit_handle = __to_nfit_memdev(nfit_mem)->device_handle; | ||
1600 | int i; | ||
1601 | |||
1602 | for (i = 0; i < NUM_DCR; i++) | ||
1603 | if (nfit_handle == handle[i]) | ||
1604 | dev_set_drvdata(nfit_test->dimm_dev[i], | ||
1605 | nfit_mem); | ||
1606 | } | ||
1607 | mutex_unlock(&acpi_desc->init_mutex); | ||
1487 | 1608 | ||
1488 | return 0; | 1609 | return 0; |
1489 | } | 1610 | } |
@@ -1518,6 +1639,10 @@ static __init int nfit_test_init(void) | |||
1518 | { | 1639 | { |
1519 | int rc, i; | 1640 | int rc, i; |
1520 | 1641 | ||
1642 | nfit_test_dimm = class_create(THIS_MODULE, "nfit_test_dimm"); | ||
1643 | if (IS_ERR(nfit_test_dimm)) | ||
1644 | return PTR_ERR(nfit_test_dimm); | ||
1645 | |||
1521 | nfit_test_setup(nfit_test_lookup); | 1646 | nfit_test_setup(nfit_test_lookup); |
1522 | 1647 | ||
1523 | for (i = 0; i < NUM_NFITS; i++) { | 1648 | for (i = 0; i < NUM_NFITS; i++) { |
@@ -1584,6 +1709,7 @@ static __exit void nfit_test_exit(void) | |||
1584 | for (i = 0; i < NUM_NFITS; i++) | 1709 | for (i = 0; i < NUM_NFITS; i++) |
1585 | platform_device_unregister(&instances[i]->pdev); | 1710 | platform_device_unregister(&instances[i]->pdev); |
1586 | nfit_test_teardown(); | 1711 | nfit_test_teardown(); |
1712 | class_destroy(nfit_test_dimm); | ||
1587 | } | 1713 | } |
1588 | 1714 | ||
1589 | module_init(nfit_test_init); | 1715 | module_init(nfit_test_init); |
diff --git a/tools/testing/nvdimm/test/nfit_test.h b/tools/testing/nvdimm/test/nfit_test.h index 9f18e2a4a862..c281dd2e5e2d 100644 --- a/tools/testing/nvdimm/test/nfit_test.h +++ b/tools/testing/nvdimm/test/nfit_test.h | |||
@@ -13,11 +13,21 @@ | |||
13 | #ifndef __NFIT_TEST_H__ | 13 | #ifndef __NFIT_TEST_H__ |
14 | #define __NFIT_TEST_H__ | 14 | #define __NFIT_TEST_H__ |
15 | #include <linux/list.h> | 15 | #include <linux/list.h> |
16 | #include <linux/ioport.h> | ||
17 | #include <linux/spinlock_types.h> | ||
18 | |||
19 | struct nfit_test_request { | ||
20 | struct list_head list; | ||
21 | struct resource res; | ||
22 | }; | ||
16 | 23 | ||
17 | struct nfit_test_resource { | 24 | struct nfit_test_resource { |
25 | struct list_head requests; | ||
18 | struct list_head list; | 26 | struct list_head list; |
19 | struct resource *res; | 27 | struct resource res; |
20 | struct device *dev; | 28 | struct device *dev; |
29 | spinlock_t lock; | ||
30 | int req_count; | ||
21 | void *buf; | 31 | void *buf; |
22 | }; | 32 | }; |
23 | 33 | ||
diff --git a/tools/testing/radix-tree/Makefile b/tools/testing/radix-tree/Makefile index 9d0919ed52a4..f2e07f2fd4b4 100644 --- a/tools/testing/radix-tree/Makefile +++ b/tools/testing/radix-tree/Makefile | |||
@@ -3,7 +3,8 @@ CFLAGS += -I. -g -O2 -Wall -D_LGPL_SOURCE | |||
3 | LDFLAGS += -lpthread -lurcu | 3 | LDFLAGS += -lpthread -lurcu |
4 | TARGETS = main | 4 | TARGETS = main |
5 | OFILES = main.o radix-tree.o linux.o test.o tag_check.o find_next_bit.o \ | 5 | OFILES = main.o radix-tree.o linux.o test.o tag_check.o find_next_bit.o \ |
6 | regression1.o regression2.o regression3.o multiorder.o | 6 | regression1.o regression2.o regression3.o multiorder.o \ |
7 | iteration_check.o | ||
7 | 8 | ||
8 | targets: $(TARGETS) | 9 | targets: $(TARGETS) |
9 | 10 | ||
diff --git a/tools/testing/radix-tree/iteration_check.c b/tools/testing/radix-tree/iteration_check.c new file mode 100644 index 000000000000..9adb8e7415a6 --- /dev/null +++ b/tools/testing/radix-tree/iteration_check.c | |||
@@ -0,0 +1,180 @@ | |||
1 | /* | ||
2 | * iteration_check.c: test races having to do with radix tree iteration | ||
3 | * Copyright (c) 2016 Intel Corporation | ||
4 | * Author: Ross Zwisler <ross.zwisler@linux.intel.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms and conditions of the GNU General Public License, | ||
8 | * version 2, as published by the Free Software Foundation. | ||
9 | * | ||
10 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
13 | * more details. | ||
14 | */ | ||
15 | #include <linux/radix-tree.h> | ||
16 | #include <pthread.h> | ||
17 | #include "test.h" | ||
18 | |||
19 | #define NUM_THREADS 4 | ||
20 | #define TAG 0 | ||
21 | static pthread_mutex_t tree_lock = PTHREAD_MUTEX_INITIALIZER; | ||
22 | static pthread_t threads[NUM_THREADS]; | ||
23 | RADIX_TREE(tree, GFP_KERNEL); | ||
24 | bool test_complete; | ||
25 | |||
26 | /* relentlessly fill the tree with tagged entries */ | ||
27 | static void *add_entries_fn(void *arg) | ||
28 | { | ||
29 | int pgoff; | ||
30 | |||
31 | while (!test_complete) { | ||
32 | for (pgoff = 0; pgoff < 100; pgoff++) { | ||
33 | pthread_mutex_lock(&tree_lock); | ||
34 | if (item_insert(&tree, pgoff) == 0) | ||
35 | item_tag_set(&tree, pgoff, TAG); | ||
36 | pthread_mutex_unlock(&tree_lock); | ||
37 | } | ||
38 | } | ||
39 | |||
40 | return NULL; | ||
41 | } | ||
42 | |||
43 | /* | ||
44 | * Iterate over the tagged entries, doing a radix_tree_iter_retry() as we find | ||
45 | * things that have been removed and randomly resetting our iteration to the | ||
46 | * next chunk with radix_tree_iter_next(). Both radix_tree_iter_retry() and | ||
47 | * radix_tree_iter_next() cause radix_tree_next_slot() to be called with a | ||
48 | * NULL 'slot' variable. | ||
49 | */ | ||
50 | static void *tagged_iteration_fn(void *arg) | ||
51 | { | ||
52 | struct radix_tree_iter iter; | ||
53 | void **slot; | ||
54 | |||
55 | while (!test_complete) { | ||
56 | rcu_read_lock(); | ||
57 | radix_tree_for_each_tagged(slot, &tree, &iter, 0, TAG) { | ||
58 | void *entry; | ||
59 | int i; | ||
60 | |||
61 | /* busy wait to let removals happen */ | ||
62 | for (i = 0; i < 1000000; i++) | ||
63 | ; | ||
64 | |||
65 | entry = radix_tree_deref_slot(slot); | ||
66 | if (unlikely(!entry)) | ||
67 | continue; | ||
68 | |||
69 | if (radix_tree_deref_retry(entry)) { | ||
70 | slot = radix_tree_iter_retry(&iter); | ||
71 | continue; | ||
72 | } | ||
73 | |||
74 | if (rand() % 50 == 0) | ||
75 | slot = radix_tree_iter_next(&iter); | ||
76 | } | ||
77 | rcu_read_unlock(); | ||
78 | } | ||
79 | |||
80 | return NULL; | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * Iterate over the entries, doing a radix_tree_iter_retry() as we find things | ||
85 | * that have been removed and randomly resetting our iteration to the next | ||
86 | * chunk with radix_tree_iter_next(). Both radix_tree_iter_retry() and | ||
87 | * radix_tree_iter_next() cause radix_tree_next_slot() to be called with a | ||
88 | * NULL 'slot' variable. | ||
89 | */ | ||
90 | static void *untagged_iteration_fn(void *arg) | ||
91 | { | ||
92 | struct radix_tree_iter iter; | ||
93 | void **slot; | ||
94 | |||
95 | while (!test_complete) { | ||
96 | rcu_read_lock(); | ||
97 | radix_tree_for_each_slot(slot, &tree, &iter, 0) { | ||
98 | void *entry; | ||
99 | int i; | ||
100 | |||
101 | /* busy wait to let removals happen */ | ||
102 | for (i = 0; i < 1000000; i++) | ||
103 | ; | ||
104 | |||
105 | entry = radix_tree_deref_slot(slot); | ||
106 | if (unlikely(!entry)) | ||
107 | continue; | ||
108 | |||
109 | if (radix_tree_deref_retry(entry)) { | ||
110 | slot = radix_tree_iter_retry(&iter); | ||
111 | continue; | ||
112 | } | ||
113 | |||
114 | if (rand() % 50 == 0) | ||
115 | slot = radix_tree_iter_next(&iter); | ||
116 | } | ||
117 | rcu_read_unlock(); | ||
118 | } | ||
119 | |||
120 | return NULL; | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | * Randomly remove entries to help induce radix_tree_iter_retry() calls in the | ||
125 | * two iteration functions. | ||
126 | */ | ||
127 | static void *remove_entries_fn(void *arg) | ||
128 | { | ||
129 | while (!test_complete) { | ||
130 | int pgoff; | ||
131 | |||
132 | pgoff = rand() % 100; | ||
133 | |||
134 | pthread_mutex_lock(&tree_lock); | ||
135 | item_delete(&tree, pgoff); | ||
136 | pthread_mutex_unlock(&tree_lock); | ||
137 | } | ||
138 | |||
139 | return NULL; | ||
140 | } | ||
141 | |||
142 | /* This is a unit test for a bug found by the syzkaller tester */ | ||
143 | void iteration_test(void) | ||
144 | { | ||
145 | int i; | ||
146 | |||
147 | printf("Running iteration tests for 10 seconds\n"); | ||
148 | |||
149 | srand(time(0)); | ||
150 | test_complete = false; | ||
151 | |||
152 | if (pthread_create(&threads[0], NULL, tagged_iteration_fn, NULL)) { | ||
153 | perror("pthread_create"); | ||
154 | exit(1); | ||
155 | } | ||
156 | if (pthread_create(&threads[1], NULL, untagged_iteration_fn, NULL)) { | ||
157 | perror("pthread_create"); | ||
158 | exit(1); | ||
159 | } | ||
160 | if (pthread_create(&threads[2], NULL, add_entries_fn, NULL)) { | ||
161 | perror("pthread_create"); | ||
162 | exit(1); | ||
163 | } | ||
164 | if (pthread_create(&threads[3], NULL, remove_entries_fn, NULL)) { | ||
165 | perror("pthread_create"); | ||
166 | exit(1); | ||
167 | } | ||
168 | |||
169 | sleep(10); | ||
170 | test_complete = true; | ||
171 | |||
172 | for (i = 0; i < NUM_THREADS; i++) { | ||
173 | if (pthread_join(threads[i], NULL)) { | ||
174 | perror("pthread_join"); | ||
175 | exit(1); | ||
176 | } | ||
177 | } | ||
178 | |||
179 | item_kill_tree(&tree); | ||
180 | } | ||
diff --git a/tools/testing/radix-tree/main.c b/tools/testing/radix-tree/main.c index b7619ff3b552..daa9010693e8 100644 --- a/tools/testing/radix-tree/main.c +++ b/tools/testing/radix-tree/main.c | |||
@@ -332,6 +332,7 @@ int main(int argc, char **argv) | |||
332 | regression1_test(); | 332 | regression1_test(); |
333 | regression2_test(); | 333 | regression2_test(); |
334 | regression3_test(); | 334 | regression3_test(); |
335 | iteration_test(); | ||
335 | single_thread_tests(long_run); | 336 | single_thread_tests(long_run); |
336 | 337 | ||
337 | sleep(1); | 338 | sleep(1); |
diff --git a/tools/testing/radix-tree/regression1.c b/tools/testing/radix-tree/regression1.c index 2d03a63bb79c..0d6813a61b37 100644 --- a/tools/testing/radix-tree/regression1.c +++ b/tools/testing/radix-tree/regression1.c | |||
@@ -43,7 +43,7 @@ | |||
43 | #include "regression.h" | 43 | #include "regression.h" |
44 | 44 | ||
45 | static RADIX_TREE(mt_tree, GFP_KERNEL); | 45 | static RADIX_TREE(mt_tree, GFP_KERNEL); |
46 | static pthread_mutex_t mt_lock; | 46 | static pthread_mutex_t mt_lock = PTHREAD_MUTEX_INITIALIZER; |
47 | 47 | ||
48 | struct page { | 48 | struct page { |
49 | pthread_mutex_t lock; | 49 | pthread_mutex_t lock; |
diff --git a/tools/testing/radix-tree/test.h b/tools/testing/radix-tree/test.h index e85131369723..217fb2403f09 100644 --- a/tools/testing/radix-tree/test.h +++ b/tools/testing/radix-tree/test.h | |||
@@ -27,6 +27,7 @@ void item_kill_tree(struct radix_tree_root *root); | |||
27 | 27 | ||
28 | void tag_check(void); | 28 | void tag_check(void); |
29 | void multiorder_checks(void); | 29 | void multiorder_checks(void); |
30 | void iteration_test(void); | ||
30 | 31 | ||
31 | struct item * | 32 | struct item * |
32 | item_tag_set(struct radix_tree_root *root, unsigned long index, int tag); | 33 | item_tag_set(struct radix_tree_root *root, unsigned long index, int tag); |
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile index ff9e5f20a5a7..f770dba2a6f6 100644 --- a/tools/testing/selftests/Makefile +++ b/tools/testing/selftests/Makefile | |||
@@ -15,6 +15,7 @@ TARGETS += memory-hotplug | |||
15 | TARGETS += mount | 15 | TARGETS += mount |
16 | TARGETS += mqueue | 16 | TARGETS += mqueue |
17 | TARGETS += net | 17 | TARGETS += net |
18 | TARGETS += nsfs | ||
18 | TARGETS += powerpc | 19 | TARGETS += powerpc |
19 | TARGETS += pstore | 20 | TARGETS += pstore |
20 | TARGETS += ptrace | 21 | TARGETS += ptrace |
diff --git a/tools/testing/selftests/filesystems/.gitignore b/tools/testing/selftests/filesystems/.gitignore new file mode 100644 index 000000000000..31d6e426b6d4 --- /dev/null +++ b/tools/testing/selftests/filesystems/.gitignore | |||
@@ -0,0 +1 @@ | |||
dnotify_test | |||
diff --git a/tools/testing/selftests/filesystems/Makefile b/tools/testing/selftests/filesystems/Makefile new file mode 100644 index 000000000000..0ab11307b414 --- /dev/null +++ b/tools/testing/selftests/filesystems/Makefile | |||
@@ -0,0 +1,7 @@ | |||
1 | TEST_PROGS := dnotify_test | ||
2 | all: $(TEST_PROGS) | ||
3 | |||
4 | include ../lib.mk | ||
5 | |||
6 | clean: | ||
7 | rm -fr $(TEST_PROGS) | ||
diff --git a/tools/testing/selftests/filesystems/dnotify_test.c b/tools/testing/selftests/filesystems/dnotify_test.c new file mode 100644 index 000000000000..8b37b4a1e18d --- /dev/null +++ b/tools/testing/selftests/filesystems/dnotify_test.c | |||
@@ -0,0 +1,34 @@ | |||
1 | #define _GNU_SOURCE /* needed to get the defines */ | ||
2 | #include <fcntl.h> /* in glibc 2.2 this has the needed | ||
3 | values defined */ | ||
4 | #include <signal.h> | ||
5 | #include <stdio.h> | ||
6 | #include <unistd.h> | ||
7 | |||
8 | static volatile int event_fd; | ||
9 | |||
10 | static void handler(int sig, siginfo_t *si, void *data) | ||
11 | { | ||
12 | event_fd = si->si_fd; | ||
13 | } | ||
14 | |||
15 | int main(void) | ||
16 | { | ||
17 | struct sigaction act; | ||
18 | int fd; | ||
19 | |||
20 | act.sa_sigaction = handler; | ||
21 | sigemptyset(&act.sa_mask); | ||
22 | act.sa_flags = SA_SIGINFO; | ||
23 | sigaction(SIGRTMIN + 1, &act, NULL); | ||
24 | |||
25 | fd = open(".", O_RDONLY); | ||
26 | fcntl(fd, F_SETSIG, SIGRTMIN + 1); | ||
27 | fcntl(fd, F_NOTIFY, DN_MODIFY|DN_CREATE|DN_MULTISHOT); | ||
28 | /* we will now be notified if any of the files | ||
29 | in "." is modified or new files are created */ | ||
30 | while (1) { | ||
31 | pause(); | ||
32 | printf("Got event on fd=%d\n", event_fd); | ||
33 | } | ||
34 | } | ||
diff --git a/tools/testing/selftests/futex/functional/run.sh b/tools/testing/selftests/futex/functional/run.sh index e87dbe2a0b0d..7ff002eed624 100755 --- a/tools/testing/selftests/futex/functional/run.sh +++ b/tools/testing/selftests/futex/functional/run.sh | |||
@@ -24,7 +24,7 @@ | |||
24 | 24 | ||
25 | # Test for a color capable console | 25 | # Test for a color capable console |
26 | if [ -z "$USE_COLOR" ]; then | 26 | if [ -z "$USE_COLOR" ]; then |
27 | tput setf 7 | 27 | tput setf 7 || tput setaf 7 |
28 | if [ $? -eq 0 ]; then | 28 | if [ $? -eq 0 ]; then |
29 | USE_COLOR=1 | 29 | USE_COLOR=1 |
30 | tput sgr0 | 30 | tput sgr0 |
diff --git a/tools/testing/selftests/futex/run.sh b/tools/testing/selftests/futex/run.sh index 4126312ad64e..88bcb1767362 100755 --- a/tools/testing/selftests/futex/run.sh +++ b/tools/testing/selftests/futex/run.sh | |||
@@ -23,7 +23,7 @@ | |||
23 | 23 | ||
24 | # Test for a color capable shell and pass the result to the subdir scripts | 24 | # Test for a color capable shell and pass the result to the subdir scripts |
25 | USE_COLOR=0 | 25 | USE_COLOR=0 |
26 | tput setf 7 | 26 | tput setf 7 || tput setaf 7 |
27 | if [ $? -eq 0 ]; then | 27 | if [ $? -eq 0 ]; then |
28 | USE_COLOR=1 | 28 | USE_COLOR=1 |
29 | tput sgr0 | 29 | tput sgr0 |
diff --git a/tools/testing/selftests/ia64/.gitignore b/tools/testing/selftests/ia64/.gitignore new file mode 100644 index 000000000000..ab806edc8732 --- /dev/null +++ b/tools/testing/selftests/ia64/.gitignore | |||
@@ -0,0 +1 @@ | |||
aliasing-test | |||
diff --git a/tools/testing/selftests/ia64/Makefile b/tools/testing/selftests/ia64/Makefile new file mode 100644 index 000000000000..2b3de2d3e945 --- /dev/null +++ b/tools/testing/selftests/ia64/Makefile | |||
@@ -0,0 +1,8 @@ | |||
1 | TEST_PROGS := aliasing-test | ||
2 | |||
3 | all: $(TEST_PROGS) | ||
4 | |||
5 | include ../lib.mk | ||
6 | |||
7 | clean: | ||
8 | rm -fr $(TEST_PROGS) | ||
diff --git a/tools/testing/selftests/ia64/aliasing-test.c b/tools/testing/selftests/ia64/aliasing-test.c new file mode 100644 index 000000000000..62a190d45f38 --- /dev/null +++ b/tools/testing/selftests/ia64/aliasing-test.c | |||
@@ -0,0 +1,263 @@ | |||
1 | /* | ||
2 | * Exercise /dev/mem mmap cases that have been troublesome in the past | ||
3 | * | ||
4 | * (c) Copyright 2007 Hewlett-Packard Development Company, L.P. | ||
5 | * Bjorn Helgaas <bjorn.helgaas@hp.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <stdlib.h> | ||
13 | #include <stdio.h> | ||
14 | #include <sys/types.h> | ||
15 | #include <dirent.h> | ||
16 | #include <fcntl.h> | ||
17 | #include <fnmatch.h> | ||
18 | #include <string.h> | ||
19 | #include <sys/ioctl.h> | ||
20 | #include <sys/mman.h> | ||
21 | #include <sys/stat.h> | ||
22 | #include <unistd.h> | ||
23 | #include <linux/pci.h> | ||
24 | |||
25 | int sum; | ||
26 | |||
27 | static int map_mem(char *path, off_t offset, size_t length, int touch) | ||
28 | { | ||
29 | int fd, rc; | ||
30 | void *addr; | ||
31 | int *c; | ||
32 | |||
33 | fd = open(path, O_RDWR); | ||
34 | if (fd == -1) { | ||
35 | perror(path); | ||
36 | return -1; | ||
37 | } | ||
38 | |||
39 | if (fnmatch("/proc/bus/pci/*", path, 0) == 0) { | ||
40 | rc = ioctl(fd, PCIIOC_MMAP_IS_MEM); | ||
41 | if (rc == -1) | ||
42 | perror("PCIIOC_MMAP_IS_MEM ioctl"); | ||
43 | } | ||
44 | |||
45 | addr = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset); | ||
46 | if (addr == MAP_FAILED) | ||
47 | return 1; | ||
48 | |||
49 | if (touch) { | ||
50 | c = (int *) addr; | ||
51 | while (c < (int *) (addr + length)) | ||
52 | sum += *c++; | ||
53 | } | ||
54 | |||
55 | rc = munmap(addr, length); | ||
56 | if (rc == -1) { | ||
57 | perror("munmap"); | ||
58 | return -1; | ||
59 | } | ||
60 | |||
61 | close(fd); | ||
62 | return 0; | ||
63 | } | ||
64 | |||
65 | static int scan_tree(char *path, char *file, off_t offset, size_t length, int touch) | ||
66 | { | ||
67 | struct dirent **namelist; | ||
68 | char *name, *path2; | ||
69 | int i, n, r, rc = 0, result = 0; | ||
70 | struct stat buf; | ||
71 | |||
72 | n = scandir(path, &namelist, 0, alphasort); | ||
73 | if (n < 0) { | ||
74 | perror("scandir"); | ||
75 | return -1; | ||
76 | } | ||
77 | |||
78 | for (i = 0; i < n; i++) { | ||
79 | name = namelist[i]->d_name; | ||
80 | |||
81 | if (fnmatch(".", name, 0) == 0) | ||
82 | goto skip; | ||
83 | if (fnmatch("..", name, 0) == 0) | ||
84 | goto skip; | ||
85 | |||
86 | path2 = malloc(strlen(path) + strlen(name) + 3); | ||
87 | strcpy(path2, path); | ||
88 | strcat(path2, "/"); | ||
89 | strcat(path2, name); | ||
90 | |||
91 | if (fnmatch(file, name, 0) == 0) { | ||
92 | rc = map_mem(path2, offset, length, touch); | ||
93 | if (rc == 0) | ||
94 | fprintf(stderr, "PASS: %s 0x%lx-0x%lx is %s\n", path2, offset, offset + length, touch ? "readable" : "mappable"); | ||
95 | else if (rc > 0) | ||
96 | fprintf(stderr, "PASS: %s 0x%lx-0x%lx not mappable\n", path2, offset, offset + length); | ||
97 | else { | ||
98 | fprintf(stderr, "FAIL: %s 0x%lx-0x%lx not accessible\n", path2, offset, offset + length); | ||
99 | return rc; | ||
100 | } | ||
101 | } else { | ||
102 | r = lstat(path2, &buf); | ||
103 | if (r == 0 && S_ISDIR(buf.st_mode)) { | ||
104 | rc = scan_tree(path2, file, offset, length, touch); | ||
105 | if (rc < 0) | ||
106 | return rc; | ||
107 | } | ||
108 | } | ||
109 | |||
110 | result |= rc; | ||
111 | free(path2); | ||
112 | |||
113 | skip: | ||
114 | free(namelist[i]); | ||
115 | } | ||
116 | free(namelist); | ||
117 | return result; | ||
118 | } | ||
119 | |||
120 | char buf[1024]; | ||
121 | |||
122 | static int read_rom(char *path) | ||
123 | { | ||
124 | int fd, rc; | ||
125 | size_t size = 0; | ||
126 | |||
127 | fd = open(path, O_RDWR); | ||
128 | if (fd == -1) { | ||
129 | perror(path); | ||
130 | return -1; | ||
131 | } | ||
132 | |||
133 | rc = write(fd, "1", 2); | ||
134 | if (rc <= 0) { | ||
135 | close(fd); | ||
136 | perror("write"); | ||
137 | return -1; | ||
138 | } | ||
139 | |||
140 | do { | ||
141 | rc = read(fd, buf, sizeof(buf)); | ||
142 | if (rc > 0) | ||
143 | size += rc; | ||
144 | } while (rc > 0); | ||
145 | |||
146 | close(fd); | ||
147 | return size; | ||
148 | } | ||
149 | |||
150 | static int scan_rom(char *path, char *file) | ||
151 | { | ||
152 | struct dirent **namelist; | ||
153 | char *name, *path2; | ||
154 | int i, n, r, rc = 0, result = 0; | ||
155 | struct stat buf; | ||
156 | |||
157 | n = scandir(path, &namelist, 0, alphasort); | ||
158 | if (n < 0) { | ||
159 | perror("scandir"); | ||
160 | return -1; | ||
161 | } | ||
162 | |||
163 | for (i = 0; i < n; i++) { | ||
164 | name = namelist[i]->d_name; | ||
165 | |||
166 | if (fnmatch(".", name, 0) == 0) | ||
167 | goto skip; | ||
168 | if (fnmatch("..", name, 0) == 0) | ||
169 | goto skip; | ||
170 | |||
171 | path2 = malloc(strlen(path) + strlen(name) + 3); | ||
172 | strcpy(path2, path); | ||
173 | strcat(path2, "/"); | ||
174 | strcat(path2, name); | ||
175 | |||
176 | if (fnmatch(file, name, 0) == 0) { | ||
177 | rc = read_rom(path2); | ||
178 | |||
179 | /* | ||
180 | * It's OK if the ROM is unreadable. Maybe there | ||
181 | * is no ROM, or some other error occurred. The | ||
182 | * important thing is that no MCA happened. | ||
183 | */ | ||
184 | if (rc > 0) | ||
185 | fprintf(stderr, "PASS: %s read %d bytes\n", path2, rc); | ||
186 | else { | ||
187 | fprintf(stderr, "PASS: %s not readable\n", path2); | ||
188 | return rc; | ||
189 | } | ||
190 | } else { | ||
191 | r = lstat(path2, &buf); | ||
192 | if (r == 0 && S_ISDIR(buf.st_mode)) { | ||
193 | rc = scan_rom(path2, file); | ||
194 | if (rc < 0) | ||
195 | return rc; | ||
196 | } | ||
197 | } | ||
198 | |||
199 | result |= rc; | ||
200 | free(path2); | ||
201 | |||
202 | skip: | ||
203 | free(namelist[i]); | ||
204 | } | ||
205 | free(namelist); | ||
206 | return result; | ||
207 | } | ||
208 | |||
209 | int main(void) | ||
210 | { | ||
211 | int rc; | ||
212 | |||
213 | if (map_mem("/dev/mem", 0, 0xA0000, 1) == 0) | ||
214 | fprintf(stderr, "PASS: /dev/mem 0x0-0xa0000 is readable\n"); | ||
215 | else | ||
216 | fprintf(stderr, "FAIL: /dev/mem 0x0-0xa0000 not accessible\n"); | ||
217 | |||
218 | /* | ||
219 | * It's not safe to blindly read the VGA frame buffer. If you know | ||
220 | * how to poke the card the right way, it should respond, but it's | ||
221 | * not safe in general. Many machines, e.g., Intel chipsets, cover | ||
222 | * up a non-responding card by just returning -1, but others will | ||
223 | * report the failure as a machine check. | ||
224 | */ | ||
225 | if (map_mem("/dev/mem", 0xA0000, 0x20000, 0) == 0) | ||
226 | fprintf(stderr, "PASS: /dev/mem 0xa0000-0xc0000 is mappable\n"); | ||
227 | else | ||
228 | fprintf(stderr, "FAIL: /dev/mem 0xa0000-0xc0000 not accessible\n"); | ||
229 | |||
230 | if (map_mem("/dev/mem", 0xC0000, 0x40000, 1) == 0) | ||
231 | fprintf(stderr, "PASS: /dev/mem 0xc0000-0x100000 is readable\n"); | ||
232 | else | ||
233 | fprintf(stderr, "FAIL: /dev/mem 0xc0000-0x100000 not accessible\n"); | ||
234 | |||
235 | /* | ||
236 | * Often you can map all the individual pieces above (0-0xA0000, | ||
237 | * 0xA0000-0xC0000, and 0xC0000-0x100000), but can't map the whole | ||
238 | * thing at once. This is because the individual pieces use different | ||
239 | * attributes, and there's no single attribute supported over the | ||
240 | * whole region. | ||
241 | */ | ||
242 | rc = map_mem("/dev/mem", 0, 1024*1024, 0); | ||
243 | if (rc == 0) | ||
244 | fprintf(stderr, "PASS: /dev/mem 0x0-0x100000 is mappable\n"); | ||
245 | else if (rc > 0) | ||
246 | fprintf(stderr, "PASS: /dev/mem 0x0-0x100000 not mappable\n"); | ||
247 | else | ||
248 | fprintf(stderr, "FAIL: /dev/mem 0x0-0x100000 not accessible\n"); | ||
249 | |||
250 | scan_tree("/sys/class/pci_bus", "legacy_mem", 0, 0xA0000, 1); | ||
251 | scan_tree("/sys/class/pci_bus", "legacy_mem", 0xA0000, 0x20000, 0); | ||
252 | scan_tree("/sys/class/pci_bus", "legacy_mem", 0xC0000, 0x40000, 1); | ||
253 | scan_tree("/sys/class/pci_bus", "legacy_mem", 0, 1024*1024, 0); | ||
254 | |||
255 | scan_rom("/sys/devices", "rom"); | ||
256 | |||
257 | scan_tree("/proc/bus/pci", "??.?", 0, 0xA0000, 1); | ||
258 | scan_tree("/proc/bus/pci", "??.?", 0xA0000, 0x20000, 0); | ||
259 | scan_tree("/proc/bus/pci", "??.?", 0xC0000, 0x40000, 1); | ||
260 | scan_tree("/proc/bus/pci", "??.?", 0, 1024*1024, 0); | ||
261 | |||
262 | return rc; | ||
263 | } | ||
diff --git a/tools/testing/selftests/networking/timestamping/.gitignore b/tools/testing/selftests/networking/timestamping/.gitignore new file mode 100644 index 000000000000..9e69e982fb38 --- /dev/null +++ b/tools/testing/selftests/networking/timestamping/.gitignore | |||
@@ -0,0 +1,3 @@ | |||
1 | timestamping | ||
2 | txtimestamp | ||
3 | hwtstamp_config | ||
diff --git a/tools/testing/selftests/networking/timestamping/Makefile b/tools/testing/selftests/networking/timestamping/Makefile new file mode 100644 index 000000000000..ccbb9edbbbb9 --- /dev/null +++ b/tools/testing/selftests/networking/timestamping/Makefile | |||
@@ -0,0 +1,8 @@ | |||
1 | TEST_PROGS := hwtstamp_config timestamping txtimestamp | ||
2 | |||
3 | all: $(TEST_PROGS) | ||
4 | |||
5 | include ../../lib.mk | ||
6 | |||
7 | clean: | ||
8 | rm -fr $(TEST_PROGS) | ||
diff --git a/tools/testing/selftests/networking/timestamping/hwtstamp_config.c b/tools/testing/selftests/networking/timestamping/hwtstamp_config.c new file mode 100644 index 000000000000..e8b685a7f15f --- /dev/null +++ b/tools/testing/selftests/networking/timestamping/hwtstamp_config.c | |||
@@ -0,0 +1,134 @@ | |||
1 | /* Test program for SIOC{G,S}HWTSTAMP | ||
2 | * Copyright 2013 Solarflare Communications | ||
3 | * Author: Ben Hutchings | ||
4 | */ | ||
5 | |||
6 | #include <errno.h> | ||
7 | #include <stdio.h> | ||
8 | #include <stdlib.h> | ||
9 | #include <string.h> | ||
10 | |||
11 | #include <sys/socket.h> | ||
12 | #include <sys/ioctl.h> | ||
13 | |||
14 | #include <linux/if.h> | ||
15 | #include <linux/net_tstamp.h> | ||
16 | #include <linux/sockios.h> | ||
17 | |||
18 | static int | ||
19 | lookup_value(const char **names, int size, const char *name) | ||
20 | { | ||
21 | int value; | ||
22 | |||
23 | for (value = 0; value < size; value++) | ||
24 | if (names[value] && strcasecmp(names[value], name) == 0) | ||
25 | return value; | ||
26 | |||
27 | return -1; | ||
28 | } | ||
29 | |||
30 | static const char * | ||
31 | lookup_name(const char **names, int size, int value) | ||
32 | { | ||
33 | return (value >= 0 && value < size) ? names[value] : NULL; | ||
34 | } | ||
35 | |||
36 | static void list_names(FILE *f, const char **names, int size) | ||
37 | { | ||
38 | int value; | ||
39 | |||
40 | for (value = 0; value < size; value++) | ||
41 | if (names[value]) | ||
42 | fprintf(f, " %s\n", names[value]); | ||
43 | } | ||
44 | |||
45 | static const char *tx_types[] = { | ||
46 | #define TX_TYPE(name) [HWTSTAMP_TX_ ## name] = #name | ||
47 | TX_TYPE(OFF), | ||
48 | TX_TYPE(ON), | ||
49 | TX_TYPE(ONESTEP_SYNC) | ||
50 | #undef TX_TYPE | ||
51 | }; | ||
52 | #define N_TX_TYPES ((int)(sizeof(tx_types) / sizeof(tx_types[0]))) | ||
53 | |||
54 | static const char *rx_filters[] = { | ||
55 | #define RX_FILTER(name) [HWTSTAMP_FILTER_ ## name] = #name | ||
56 | RX_FILTER(NONE), | ||
57 | RX_FILTER(ALL), | ||
58 | RX_FILTER(SOME), | ||
59 | RX_FILTER(PTP_V1_L4_EVENT), | ||
60 | RX_FILTER(PTP_V1_L4_SYNC), | ||
61 | RX_FILTER(PTP_V1_L4_DELAY_REQ), | ||
62 | RX_FILTER(PTP_V2_L4_EVENT), | ||
63 | RX_FILTER(PTP_V2_L4_SYNC), | ||
64 | RX_FILTER(PTP_V2_L4_DELAY_REQ), | ||
65 | RX_FILTER(PTP_V2_L2_EVENT), | ||
66 | RX_FILTER(PTP_V2_L2_SYNC), | ||
67 | RX_FILTER(PTP_V2_L2_DELAY_REQ), | ||
68 | RX_FILTER(PTP_V2_EVENT), | ||
69 | RX_FILTER(PTP_V2_SYNC), | ||
70 | RX_FILTER(PTP_V2_DELAY_REQ), | ||
71 | #undef RX_FILTER | ||
72 | }; | ||
73 | #define N_RX_FILTERS ((int)(sizeof(rx_filters) / sizeof(rx_filters[0]))) | ||
74 | |||
75 | static void usage(void) | ||
76 | { | ||
77 | fputs("Usage: hwtstamp_config if_name [tx_type rx_filter]\n" | ||
78 | "tx_type is any of (case-insensitive):\n", | ||
79 | stderr); | ||
80 | list_names(stderr, tx_types, N_TX_TYPES); | ||
81 | fputs("rx_filter is any of (case-insensitive):\n", stderr); | ||
82 | list_names(stderr, rx_filters, N_RX_FILTERS); | ||
83 | } | ||
84 | |||
85 | int main(int argc, char **argv) | ||
86 | { | ||
87 | struct ifreq ifr; | ||
88 | struct hwtstamp_config config; | ||
89 | const char *name; | ||
90 | int sock; | ||
91 | |||
92 | if ((argc != 2 && argc != 4) || (strlen(argv[1]) >= IFNAMSIZ)) { | ||
93 | usage(); | ||
94 | return 2; | ||
95 | } | ||
96 | |||
97 | if (argc == 4) { | ||
98 | config.flags = 0; | ||
99 | config.tx_type = lookup_value(tx_types, N_TX_TYPES, argv[2]); | ||
100 | config.rx_filter = lookup_value(rx_filters, N_RX_FILTERS, argv[3]); | ||
101 | if (config.tx_type < 0 || config.rx_filter < 0) { | ||
102 | usage(); | ||
103 | return 2; | ||
104 | } | ||
105 | } | ||
106 | |||
107 | sock = socket(AF_INET, SOCK_DGRAM, 0); | ||
108 | if (sock < 0) { | ||
109 | perror("socket"); | ||
110 | return 1; | ||
111 | } | ||
112 | |||
113 | strcpy(ifr.ifr_name, argv[1]); | ||
114 | ifr.ifr_data = (caddr_t)&config; | ||
115 | |||
116 | if (ioctl(sock, (argc == 2) ? SIOCGHWTSTAMP : SIOCSHWTSTAMP, &ifr)) { | ||
117 | perror("ioctl"); | ||
118 | return 1; | ||
119 | } | ||
120 | |||
121 | printf("flags = %#x\n", config.flags); | ||
122 | name = lookup_name(tx_types, N_TX_TYPES, config.tx_type); | ||
123 | if (name) | ||
124 | printf("tx_type = %s\n", name); | ||
125 | else | ||
126 | printf("tx_type = %d\n", config.tx_type); | ||
127 | name = lookup_name(rx_filters, N_RX_FILTERS, config.rx_filter); | ||
128 | if (name) | ||
129 | printf("rx_filter = %s\n", name); | ||
130 | else | ||
131 | printf("rx_filter = %d\n", config.rx_filter); | ||
132 | |||
133 | return 0; | ||
134 | } | ||
diff --git a/tools/testing/selftests/networking/timestamping/timestamping.c b/tools/testing/selftests/networking/timestamping/timestamping.c new file mode 100644 index 000000000000..5cdfd743447b --- /dev/null +++ b/tools/testing/selftests/networking/timestamping/timestamping.c | |||
@@ -0,0 +1,528 @@ | |||
1 | /* | ||
2 | * This program demonstrates how the various time stamping features in | ||
3 | * the Linux kernel work. It emulates the behavior of a PTP | ||
4 | * implementation in stand-alone master mode by sending PTPv1 Sync | ||
5 | * multicasts once every second. It looks for similar packets, but | ||
6 | * beyond that doesn't actually implement PTP. | ||
7 | * | ||
8 | * Outgoing packets are time stamped with SO_TIMESTAMPING with or | ||
9 | * without hardware support. | ||
10 | * | ||
11 | * Incoming packets are time stamped with SO_TIMESTAMPING with or | ||
12 | * without hardware support, SIOCGSTAMP[NS] (per-socket time stamp) and | ||
13 | * SO_TIMESTAMP[NS]. | ||
14 | * | ||
15 | * Copyright (C) 2009 Intel Corporation. | ||
16 | * Author: Patrick Ohly <patrick.ohly@intel.com> | ||
17 | * | ||
18 | * This program is free software; you can redistribute it and/or modify it | ||
19 | * under the terms and conditions of the GNU General Public License, | ||
20 | * version 2, as published by the Free Software Foundation. | ||
21 | * | ||
22 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
23 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
24 | * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for | ||
25 | * more details. | ||
26 | * | ||
27 | * You should have received a copy of the GNU General Public License along with | ||
28 | * this program; if not, write to the Free Software Foundation, Inc., | ||
29 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
30 | */ | ||
31 | |||
32 | #include <stdio.h> | ||
33 | #include <stdlib.h> | ||
34 | #include <errno.h> | ||
35 | #include <string.h> | ||
36 | |||
37 | #include <sys/time.h> | ||
38 | #include <sys/socket.h> | ||
39 | #include <sys/select.h> | ||
40 | #include <sys/ioctl.h> | ||
41 | #include <arpa/inet.h> | ||
42 | #include <net/if.h> | ||
43 | |||
44 | #include <asm/types.h> | ||
45 | #include <linux/net_tstamp.h> | ||
46 | #include <linux/errqueue.h> | ||
47 | |||
48 | #ifndef SO_TIMESTAMPING | ||
49 | # define SO_TIMESTAMPING 37 | ||
50 | # define SCM_TIMESTAMPING SO_TIMESTAMPING | ||
51 | #endif | ||
52 | |||
53 | #ifndef SO_TIMESTAMPNS | ||
54 | # define SO_TIMESTAMPNS 35 | ||
55 | #endif | ||
56 | |||
57 | #ifndef SIOCGSTAMPNS | ||
58 | # define SIOCGSTAMPNS 0x8907 | ||
59 | #endif | ||
60 | |||
61 | #ifndef SIOCSHWTSTAMP | ||
62 | # define SIOCSHWTSTAMP 0x89b0 | ||
63 | #endif | ||
64 | |||
65 | static void usage(const char *error) | ||
66 | { | ||
67 | if (error) | ||
68 | printf("invalid option: %s\n", error); | ||
69 | printf("timestamping interface option*\n\n" | ||
70 | "Options:\n" | ||
71 | " IP_MULTICAST_LOOP - looping outgoing multicasts\n" | ||
72 | " SO_TIMESTAMP - normal software time stamping, ms resolution\n" | ||
73 | " SO_TIMESTAMPNS - more accurate software time stamping\n" | ||
74 | " SOF_TIMESTAMPING_TX_HARDWARE - hardware time stamping of outgoing packets\n" | ||
75 | " SOF_TIMESTAMPING_TX_SOFTWARE - software fallback for outgoing packets\n" | ||
76 | " SOF_TIMESTAMPING_RX_HARDWARE - hardware time stamping of incoming packets\n" | ||
77 | " SOF_TIMESTAMPING_RX_SOFTWARE - software fallback for incoming packets\n" | ||
78 | " SOF_TIMESTAMPING_SOFTWARE - request reporting of software time stamps\n" | ||
79 | " SOF_TIMESTAMPING_RAW_HARDWARE - request reporting of raw HW time stamps\n" | ||
80 | " SIOCGSTAMP - check last socket time stamp\n" | ||
81 | " SIOCGSTAMPNS - more accurate socket time stamp\n"); | ||
82 | exit(1); | ||
83 | } | ||
84 | |||
85 | static void bail(const char *error) | ||
86 | { | ||
87 | printf("%s: %s\n", error, strerror(errno)); | ||
88 | exit(1); | ||
89 | } | ||
90 | |||
91 | static const unsigned char sync[] = { | ||
92 | 0x00, 0x01, 0x00, 0x01, | ||
93 | 0x5f, 0x44, 0x46, 0x4c, | ||
94 | 0x54, 0x00, 0x00, 0x00, | ||
95 | 0x00, 0x00, 0x00, 0x00, | ||
96 | 0x00, 0x00, 0x00, 0x00, | ||
97 | 0x01, 0x01, | ||
98 | |||
99 | /* fake uuid */ | ||
100 | 0x00, 0x01, | ||
101 | 0x02, 0x03, 0x04, 0x05, | ||
102 | |||
103 | 0x00, 0x01, 0x00, 0x37, | ||
104 | 0x00, 0x00, 0x00, 0x08, | ||
105 | 0x00, 0x00, 0x00, 0x00, | ||
106 | 0x49, 0x05, 0xcd, 0x01, | ||
107 | 0x29, 0xb1, 0x8d, 0xb0, | ||
108 | 0x00, 0x00, 0x00, 0x00, | ||
109 | 0x00, 0x01, | ||
110 | |||
111 | /* fake uuid */ | ||
112 | 0x00, 0x01, | ||
113 | 0x02, 0x03, 0x04, 0x05, | ||
114 | |||
115 | 0x00, 0x00, 0x00, 0x37, | ||
116 | 0x00, 0x00, 0x00, 0x04, | ||
117 | 0x44, 0x46, 0x4c, 0x54, | ||
118 | 0x00, 0x00, 0xf0, 0x60, | ||
119 | 0x00, 0x01, 0x00, 0x00, | ||
120 | 0x00, 0x00, 0x00, 0x01, | ||
121 | 0x00, 0x00, 0xf0, 0x60, | ||
122 | 0x00, 0x00, 0x00, 0x00, | ||
123 | 0x00, 0x00, 0x00, 0x04, | ||
124 | 0x44, 0x46, 0x4c, 0x54, | ||
125 | 0x00, 0x01, | ||
126 | |||
127 | /* fake uuid */ | ||
128 | 0x00, 0x01, | ||
129 | 0x02, 0x03, 0x04, 0x05, | ||
130 | |||
131 | 0x00, 0x00, 0x00, 0x00, | ||
132 | 0x00, 0x00, 0x00, 0x00, | ||
133 | 0x00, 0x00, 0x00, 0x00, | ||
134 | 0x00, 0x00, 0x00, 0x00 | ||
135 | }; | ||
136 | |||
137 | static void sendpacket(int sock, struct sockaddr *addr, socklen_t addr_len) | ||
138 | { | ||
139 | struct timeval now; | ||
140 | int res; | ||
141 | |||
142 | res = sendto(sock, sync, sizeof(sync), 0, | ||
143 | addr, addr_len); | ||
144 | gettimeofday(&now, 0); | ||
145 | if (res < 0) | ||
146 | printf("%s: %s\n", "send", strerror(errno)); | ||
147 | else | ||
148 | printf("%ld.%06ld: sent %d bytes\n", | ||
149 | (long)now.tv_sec, (long)now.tv_usec, | ||
150 | res); | ||
151 | } | ||
152 | |||
153 | static void printpacket(struct msghdr *msg, int res, | ||
154 | char *data, | ||
155 | int sock, int recvmsg_flags, | ||
156 | int siocgstamp, int siocgstampns) | ||
157 | { | ||
158 | struct sockaddr_in *from_addr = (struct sockaddr_in *)msg->msg_name; | ||
159 | struct cmsghdr *cmsg; | ||
160 | struct timeval tv; | ||
161 | struct timespec ts; | ||
162 | struct timeval now; | ||
163 | |||
164 | gettimeofday(&now, 0); | ||
165 | |||
166 | printf("%ld.%06ld: received %s data, %d bytes from %s, %zu bytes control messages\n", | ||
167 | (long)now.tv_sec, (long)now.tv_usec, | ||
168 | (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular", | ||
169 | res, | ||
170 | inet_ntoa(from_addr->sin_addr), | ||
171 | msg->msg_controllen); | ||
172 | for (cmsg = CMSG_FIRSTHDR(msg); | ||
173 | cmsg; | ||
174 | cmsg = CMSG_NXTHDR(msg, cmsg)) { | ||
175 | printf(" cmsg len %zu: ", cmsg->cmsg_len); | ||
176 | switch (cmsg->cmsg_level) { | ||
177 | case SOL_SOCKET: | ||
178 | printf("SOL_SOCKET "); | ||
179 | switch (cmsg->cmsg_type) { | ||
180 | case SO_TIMESTAMP: { | ||
181 | struct timeval *stamp = | ||
182 | (struct timeval *)CMSG_DATA(cmsg); | ||
183 | printf("SO_TIMESTAMP %ld.%06ld", | ||
184 | (long)stamp->tv_sec, | ||
185 | (long)stamp->tv_usec); | ||
186 | break; | ||
187 | } | ||
188 | case SO_TIMESTAMPNS: { | ||
189 | struct timespec *stamp = | ||
190 | (struct timespec *)CMSG_DATA(cmsg); | ||
191 | printf("SO_TIMESTAMPNS %ld.%09ld", | ||
192 | (long)stamp->tv_sec, | ||
193 | (long)stamp->tv_nsec); | ||
194 | break; | ||
195 | } | ||
196 | case SO_TIMESTAMPING: { | ||
197 | struct timespec *stamp = | ||
198 | (struct timespec *)CMSG_DATA(cmsg); | ||
199 | printf("SO_TIMESTAMPING "); | ||
200 | printf("SW %ld.%09ld ", | ||
201 | (long)stamp->tv_sec, | ||
202 | (long)stamp->tv_nsec); | ||
203 | stamp++; | ||
204 | /* skip deprecated HW transformed */ | ||
205 | stamp++; | ||
206 | printf("HW raw %ld.%09ld", | ||
207 | (long)stamp->tv_sec, | ||
208 | (long)stamp->tv_nsec); | ||
209 | break; | ||
210 | } | ||
211 | default: | ||
212 | printf("type %d", cmsg->cmsg_type); | ||
213 | break; | ||
214 | } | ||
215 | break; | ||
216 | case IPPROTO_IP: | ||
217 | printf("IPPROTO_IP "); | ||
218 | switch (cmsg->cmsg_type) { | ||
219 | case IP_RECVERR: { | ||
220 | struct sock_extended_err *err = | ||
221 | (struct sock_extended_err *)CMSG_DATA(cmsg); | ||
222 | printf("IP_RECVERR ee_errno '%s' ee_origin %d => %s", | ||
223 | strerror(err->ee_errno), | ||
224 | err->ee_origin, | ||
225 | #ifdef SO_EE_ORIGIN_TIMESTAMPING | ||
226 | err->ee_origin == SO_EE_ORIGIN_TIMESTAMPING ? | ||
227 | "bounced packet" : "unexpected origin" | ||
228 | #else | ||
229 | "probably SO_EE_ORIGIN_TIMESTAMPING" | ||
230 | #endif | ||
231 | ); | ||
232 | if (res < sizeof(sync)) | ||
233 | printf(" => truncated data?!"); | ||
234 | else if (!memcmp(sync, data + res - sizeof(sync), | ||
235 | sizeof(sync))) | ||
236 | printf(" => GOT OUR DATA BACK (HURRAY!)"); | ||
237 | break; | ||
238 | } | ||
239 | case IP_PKTINFO: { | ||
240 | struct in_pktinfo *pktinfo = | ||
241 | (struct in_pktinfo *)CMSG_DATA(cmsg); | ||
242 | printf("IP_PKTINFO interface index %u", | ||
243 | pktinfo->ipi_ifindex); | ||
244 | break; | ||
245 | } | ||
246 | default: | ||
247 | printf("type %d", cmsg->cmsg_type); | ||
248 | break; | ||
249 | } | ||
250 | break; | ||
251 | default: | ||
252 | printf("level %d type %d", | ||
253 | cmsg->cmsg_level, | ||
254 | cmsg->cmsg_type); | ||
255 | break; | ||
256 | } | ||
257 | printf("\n"); | ||
258 | } | ||
259 | |||
260 | if (siocgstamp) { | ||
261 | if (ioctl(sock, SIOCGSTAMP, &tv)) | ||
262 | printf(" %s: %s\n", "SIOCGSTAMP", strerror(errno)); | ||
263 | else | ||
264 | printf("SIOCGSTAMP %ld.%06ld\n", | ||
265 | (long)tv.tv_sec, | ||
266 | (long)tv.tv_usec); | ||
267 | } | ||
268 | if (siocgstampns) { | ||
269 | if (ioctl(sock, SIOCGSTAMPNS, &ts)) | ||
270 | printf(" %s: %s\n", "SIOCGSTAMPNS", strerror(errno)); | ||
271 | else | ||
272 | printf("SIOCGSTAMPNS %ld.%09ld\n", | ||
273 | (long)ts.tv_sec, | ||
274 | (long)ts.tv_nsec); | ||
275 | } | ||
276 | } | ||
277 | |||
278 | static void recvpacket(int sock, int recvmsg_flags, | ||
279 | int siocgstamp, int siocgstampns) | ||
280 | { | ||
281 | char data[256]; | ||
282 | struct msghdr msg; | ||
283 | struct iovec entry; | ||
284 | struct sockaddr_in from_addr; | ||
285 | struct { | ||
286 | struct cmsghdr cm; | ||
287 | char control[512]; | ||
288 | } control; | ||
289 | int res; | ||
290 | |||
291 | memset(&msg, 0, sizeof(msg)); | ||
292 | msg.msg_iov = &entry; | ||
293 | msg.msg_iovlen = 1; | ||
294 | entry.iov_base = data; | ||
295 | entry.iov_len = sizeof(data); | ||
296 | msg.msg_name = (caddr_t)&from_addr; | ||
297 | msg.msg_namelen = sizeof(from_addr); | ||
298 | msg.msg_control = &control; | ||
299 | msg.msg_controllen = sizeof(control); | ||
300 | |||
301 | res = recvmsg(sock, &msg, recvmsg_flags|MSG_DONTWAIT); | ||
302 | if (res < 0) { | ||
303 | printf("%s %s: %s\n", | ||
304 | "recvmsg", | ||
305 | (recvmsg_flags & MSG_ERRQUEUE) ? "error" : "regular", | ||
306 | strerror(errno)); | ||
307 | } else { | ||
308 | printpacket(&msg, res, data, | ||
309 | sock, recvmsg_flags, | ||
310 | siocgstamp, siocgstampns); | ||
311 | } | ||
312 | } | ||
313 | |||
314 | int main(int argc, char **argv) | ||
315 | { | ||
316 | int so_timestamping_flags = 0; | ||
317 | int so_timestamp = 0; | ||
318 | int so_timestampns = 0; | ||
319 | int siocgstamp = 0; | ||
320 | int siocgstampns = 0; | ||
321 | int ip_multicast_loop = 0; | ||
322 | char *interface; | ||
323 | int i; | ||
324 | int enabled = 1; | ||
325 | int sock; | ||
326 | struct ifreq device; | ||
327 | struct ifreq hwtstamp; | ||
328 | struct hwtstamp_config hwconfig, hwconfig_requested; | ||
329 | struct sockaddr_in addr; | ||
330 | struct ip_mreq imr; | ||
331 | struct in_addr iaddr; | ||
332 | int val; | ||
333 | socklen_t len; | ||
334 | struct timeval next; | ||
335 | |||
336 | if (argc < 2) | ||
337 | usage(0); | ||
338 | interface = argv[1]; | ||
339 | |||
340 | for (i = 2; i < argc; i++) { | ||
341 | if (!strcasecmp(argv[i], "SO_TIMESTAMP")) | ||
342 | so_timestamp = 1; | ||
343 | else if (!strcasecmp(argv[i], "SO_TIMESTAMPNS")) | ||
344 | so_timestampns = 1; | ||
345 | else if (!strcasecmp(argv[i], "SIOCGSTAMP")) | ||
346 | siocgstamp = 1; | ||
347 | else if (!strcasecmp(argv[i], "SIOCGSTAMPNS")) | ||
348 | siocgstampns = 1; | ||
349 | else if (!strcasecmp(argv[i], "IP_MULTICAST_LOOP")) | ||
350 | ip_multicast_loop = 1; | ||
351 | else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_HARDWARE")) | ||
352 | so_timestamping_flags |= SOF_TIMESTAMPING_TX_HARDWARE; | ||
353 | else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_TX_SOFTWARE")) | ||
354 | so_timestamping_flags |= SOF_TIMESTAMPING_TX_SOFTWARE; | ||
355 | else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_HARDWARE")) | ||
356 | so_timestamping_flags |= SOF_TIMESTAMPING_RX_HARDWARE; | ||
357 | else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RX_SOFTWARE")) | ||
358 | so_timestamping_flags |= SOF_TIMESTAMPING_RX_SOFTWARE; | ||
359 | else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_SOFTWARE")) | ||
360 | so_timestamping_flags |= SOF_TIMESTAMPING_SOFTWARE; | ||
361 | else if (!strcasecmp(argv[i], "SOF_TIMESTAMPING_RAW_HARDWARE")) | ||
362 | so_timestamping_flags |= SOF_TIMESTAMPING_RAW_HARDWARE; | ||
363 | else | ||
364 | usage(argv[i]); | ||
365 | } | ||
366 | |||
367 | sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); | ||
368 | if (sock < 0) | ||
369 | bail("socket"); | ||
370 | |||
371 | memset(&device, 0, sizeof(device)); | ||
372 | strncpy(device.ifr_name, interface, sizeof(device.ifr_name)); | ||
373 | if (ioctl(sock, SIOCGIFADDR, &device) < 0) | ||
374 | bail("getting interface IP address"); | ||
375 | |||
376 | memset(&hwtstamp, 0, sizeof(hwtstamp)); | ||
377 | strncpy(hwtstamp.ifr_name, interface, sizeof(hwtstamp.ifr_name)); | ||
378 | hwtstamp.ifr_data = (void *)&hwconfig; | ||
379 | memset(&hwconfig, 0, sizeof(hwconfig)); | ||
380 | hwconfig.tx_type = | ||
381 | (so_timestamping_flags & SOF_TIMESTAMPING_TX_HARDWARE) ? | ||
382 | HWTSTAMP_TX_ON : HWTSTAMP_TX_OFF; | ||
383 | hwconfig.rx_filter = | ||
384 | (so_timestamping_flags & SOF_TIMESTAMPING_RX_HARDWARE) ? | ||
385 | HWTSTAMP_FILTER_PTP_V1_L4_SYNC : HWTSTAMP_FILTER_NONE; | ||
386 | hwconfig_requested = hwconfig; | ||
387 | if (ioctl(sock, SIOCSHWTSTAMP, &hwtstamp) < 0) { | ||
388 | if ((errno == EINVAL || errno == ENOTSUP) && | ||
389 | hwconfig_requested.tx_type == HWTSTAMP_TX_OFF && | ||
390 | hwconfig_requested.rx_filter == HWTSTAMP_FILTER_NONE) | ||
391 | printf("SIOCSHWTSTAMP: disabling hardware time stamping not possible\n"); | ||
392 | else | ||
393 | bail("SIOCSHWTSTAMP"); | ||
394 | } | ||
395 | printf("SIOCSHWTSTAMP: tx_type %d requested, got %d; rx_filter %d requested, got %d\n", | ||
396 | hwconfig_requested.tx_type, hwconfig.tx_type, | ||
397 | hwconfig_requested.rx_filter, hwconfig.rx_filter); | ||
398 | |||
399 | /* bind to PTP port */ | ||
400 | addr.sin_family = AF_INET; | ||
401 | addr.sin_addr.s_addr = htonl(INADDR_ANY); | ||
402 | addr.sin_port = htons(319 /* PTP event port */); | ||
403 | if (bind(sock, | ||
404 | (struct sockaddr *)&addr, | ||
405 | sizeof(struct sockaddr_in)) < 0) | ||
406 | bail("bind"); | ||
407 | |||
408 | /* set multicast group for outgoing packets */ | ||
409 | inet_aton("224.0.1.130", &iaddr); /* alternate PTP domain 1 */ | ||
410 | addr.sin_addr = iaddr; | ||
411 | imr.imr_multiaddr.s_addr = iaddr.s_addr; | ||
412 | imr.imr_interface.s_addr = | ||
413 | ((struct sockaddr_in *)&device.ifr_addr)->sin_addr.s_addr; | ||
414 | if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_IF, | ||
415 | &imr.imr_interface.s_addr, sizeof(struct in_addr)) < 0) | ||
416 | bail("set multicast"); | ||
417 | |||
418 | /* join multicast group, loop our own packet */ | ||
419 | if (setsockopt(sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, | ||
420 | &imr, sizeof(struct ip_mreq)) < 0) | ||
421 | bail("join multicast group"); | ||
422 | |||
423 | if (setsockopt(sock, IPPROTO_IP, IP_MULTICAST_LOOP, | ||
424 | &ip_multicast_loop, sizeof(enabled)) < 0) { | ||
425 | bail("loop multicast"); | ||
426 | } | ||
427 | |||
428 | /* set socket options for time stamping */ | ||
429 | if (so_timestamp && | ||
430 | setsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, | ||
431 | &enabled, sizeof(enabled)) < 0) | ||
432 | bail("setsockopt SO_TIMESTAMP"); | ||
433 | |||
434 | if (so_timestampns && | ||
435 | setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, | ||
436 | &enabled, sizeof(enabled)) < 0) | ||
437 | bail("setsockopt SO_TIMESTAMPNS"); | ||
438 | |||
439 | if (so_timestamping_flags && | ||
440 | setsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, | ||
441 | &so_timestamping_flags, | ||
442 | sizeof(so_timestamping_flags)) < 0) | ||
443 | bail("setsockopt SO_TIMESTAMPING"); | ||
444 | |||
445 | /* request IP_PKTINFO for debugging purposes */ | ||
446 | if (setsockopt(sock, SOL_IP, IP_PKTINFO, | ||
447 | &enabled, sizeof(enabled)) < 0) | ||
448 | printf("%s: %s\n", "setsockopt IP_PKTINFO", strerror(errno)); | ||
449 | |||
450 | /* verify socket options */ | ||
451 | len = sizeof(val); | ||
452 | if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMP, &val, &len) < 0) | ||
453 | printf("%s: %s\n", "getsockopt SO_TIMESTAMP", strerror(errno)); | ||
454 | else | ||
455 | printf("SO_TIMESTAMP %d\n", val); | ||
456 | |||
457 | if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPNS, &val, &len) < 0) | ||
458 | printf("%s: %s\n", "getsockopt SO_TIMESTAMPNS", | ||
459 | strerror(errno)); | ||
460 | else | ||
461 | printf("SO_TIMESTAMPNS %d\n", val); | ||
462 | |||
463 | if (getsockopt(sock, SOL_SOCKET, SO_TIMESTAMPING, &val, &len) < 0) { | ||
464 | printf("%s: %s\n", "getsockopt SO_TIMESTAMPING", | ||
465 | strerror(errno)); | ||
466 | } else { | ||
467 | printf("SO_TIMESTAMPING %d\n", val); | ||
468 | if (val != so_timestamping_flags) | ||
469 | printf(" not the expected value %d\n", | ||
470 | so_timestamping_flags); | ||
471 | } | ||
472 | |||
473 | /* send packets forever every five seconds */ | ||
474 | gettimeofday(&next, 0); | ||
475 | next.tv_sec = (next.tv_sec + 1) / 5 * 5; | ||
476 | next.tv_usec = 0; | ||
477 | while (1) { | ||
478 | struct timeval now; | ||
479 | struct timeval delta; | ||
480 | long delta_us; | ||
481 | int res; | ||
482 | fd_set readfs, errorfs; | ||
483 | |||
484 | gettimeofday(&now, 0); | ||
485 | delta_us = (long)(next.tv_sec - now.tv_sec) * 1000000 + | ||
486 | (long)(next.tv_usec - now.tv_usec); | ||
487 | if (delta_us > 0) { | ||
488 | /* continue waiting for timeout or data */ | ||
489 | delta.tv_sec = delta_us / 1000000; | ||
490 | delta.tv_usec = delta_us % 1000000; | ||
491 | |||
492 | FD_ZERO(&readfs); | ||
493 | FD_ZERO(&errorfs); | ||
494 | FD_SET(sock, &readfs); | ||
495 | FD_SET(sock, &errorfs); | ||
496 | printf("%ld.%06ld: select %ldus\n", | ||
497 | (long)now.tv_sec, (long)now.tv_usec, | ||
498 | delta_us); | ||
499 | res = select(sock + 1, &readfs, 0, &errorfs, &delta); | ||
500 | gettimeofday(&now, 0); | ||
501 | printf("%ld.%06ld: select returned: %d, %s\n", | ||
502 | (long)now.tv_sec, (long)now.tv_usec, | ||
503 | res, | ||
504 | res < 0 ? strerror(errno) : "success"); | ||
505 | if (res > 0) { | ||
506 | if (FD_ISSET(sock, &readfs)) | ||
507 | printf("ready for reading\n"); | ||
508 | if (FD_ISSET(sock, &errorfs)) | ||
509 | printf("has error\n"); | ||
510 | recvpacket(sock, 0, | ||
511 | siocgstamp, | ||
512 | siocgstampns); | ||
513 | recvpacket(sock, MSG_ERRQUEUE, | ||
514 | siocgstamp, | ||
515 | siocgstampns); | ||
516 | } | ||
517 | } else { | ||
518 | /* write one packet */ | ||
519 | sendpacket(sock, | ||
520 | (struct sockaddr *)&addr, | ||
521 | sizeof(addr)); | ||
522 | next.tv_sec += 5; | ||
523 | continue; | ||
524 | } | ||
525 | } | ||
526 | |||
527 | return 0; | ||
528 | } | ||
diff --git a/tools/testing/selftests/networking/timestamping/txtimestamp.c b/tools/testing/selftests/networking/timestamping/txtimestamp.c new file mode 100644 index 000000000000..5df07047ca86 --- /dev/null +++ b/tools/testing/selftests/networking/timestamping/txtimestamp.c | |||
@@ -0,0 +1,549 @@ | |||
1 | /* | ||
2 | * Copyright 2014 Google Inc. | ||
3 | * Author: willemb@google.com (Willem de Bruijn) | ||
4 | * | ||
5 | * Test software tx timestamping, including | ||
6 | * | ||
7 | * - SCHED, SND and ACK timestamps | ||
8 | * - RAW, UDP and TCP | ||
9 | * - IPv4 and IPv6 | ||
10 | * - various packet sizes (to test GSO and TSO) | ||
11 | * | ||
12 | * Consult the command line arguments for help on running | ||
13 | * the various testcases. | ||
14 | * | ||
15 | * This test requires a dummy TCP server. | ||
16 | * A simple `nc6 [-u] -l -p $DESTPORT` will do | ||
17 | * | ||
18 | * | ||
19 | * This program is free software; you can redistribute it and/or modify it | ||
20 | * under the terms and conditions of the GNU General Public License, | ||
21 | * version 2, as published by the Free Software Foundation. | ||
22 | * | ||
23 | * This program is distributed in the hope it will be useful, but WITHOUT | ||
24 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
25 | * FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for | ||
26 | * more details. | ||
27 | * | ||
28 | * You should have received a copy of the GNU General Public License along with | ||
29 | * this program; if not, write to the Free Software Foundation, Inc., | ||
30 | * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. | ||
31 | */ | ||
32 | |||
33 | #define _GNU_SOURCE | ||
34 | |||
35 | #include <arpa/inet.h> | ||
36 | #include <asm/types.h> | ||
37 | #include <error.h> | ||
38 | #include <errno.h> | ||
39 | #include <inttypes.h> | ||
40 | #include <linux/errqueue.h> | ||
41 | #include <linux/if_ether.h> | ||
42 | #include <linux/net_tstamp.h> | ||
43 | #include <netdb.h> | ||
44 | #include <net/if.h> | ||
45 | #include <netinet/in.h> | ||
46 | #include <netinet/ip.h> | ||
47 | #include <netinet/udp.h> | ||
48 | #include <netinet/tcp.h> | ||
49 | #include <netpacket/packet.h> | ||
50 | #include <poll.h> | ||
51 | #include <stdarg.h> | ||
52 | #include <stdbool.h> | ||
53 | #include <stdio.h> | ||
54 | #include <stdlib.h> | ||
55 | #include <string.h> | ||
56 | #include <sys/ioctl.h> | ||
57 | #include <sys/select.h> | ||
58 | #include <sys/socket.h> | ||
59 | #include <sys/time.h> | ||
60 | #include <sys/types.h> | ||
61 | #include <time.h> | ||
62 | #include <unistd.h> | ||
63 | |||
64 | /* command line parameters */ | ||
65 | static int cfg_proto = SOCK_STREAM; | ||
66 | static int cfg_ipproto = IPPROTO_TCP; | ||
67 | static int cfg_num_pkts = 4; | ||
68 | static int do_ipv4 = 1; | ||
69 | static int do_ipv6 = 1; | ||
70 | static int cfg_payload_len = 10; | ||
71 | static bool cfg_show_payload; | ||
72 | static bool cfg_do_pktinfo; | ||
73 | static bool cfg_loop_nodata; | ||
74 | static uint16_t dest_port = 9000; | ||
75 | |||
76 | static struct sockaddr_in daddr; | ||
77 | static struct sockaddr_in6 daddr6; | ||
78 | static struct timespec ts_prev; | ||
79 | |||
80 | static void __print_timestamp(const char *name, struct timespec *cur, | ||
81 | uint32_t key, int payload_len) | ||
82 | { | ||
83 | if (!(cur->tv_sec | cur->tv_nsec)) | ||
84 | return; | ||
85 | |||
86 | fprintf(stderr, " %s: %lu s %lu us (seq=%u, len=%u)", | ||
87 | name, cur->tv_sec, cur->tv_nsec / 1000, | ||
88 | key, payload_len); | ||
89 | |||
90 | if ((ts_prev.tv_sec | ts_prev.tv_nsec)) { | ||
91 | int64_t cur_ms, prev_ms; | ||
92 | |||
93 | cur_ms = (long) cur->tv_sec * 1000 * 1000; | ||
94 | cur_ms += cur->tv_nsec / 1000; | ||
95 | |||
96 | prev_ms = (long) ts_prev.tv_sec * 1000 * 1000; | ||
97 | prev_ms += ts_prev.tv_nsec / 1000; | ||
98 | |||
99 | fprintf(stderr, " (%+" PRId64 " us)", cur_ms - prev_ms); | ||
100 | } | ||
101 | |||
102 | ts_prev = *cur; | ||
103 | fprintf(stderr, "\n"); | ||
104 | } | ||
105 | |||
106 | static void print_timestamp_usr(void) | ||
107 | { | ||
108 | struct timespec ts; | ||
109 | struct timeval tv; /* avoid dependency on -lrt */ | ||
110 | |||
111 | gettimeofday(&tv, NULL); | ||
112 | ts.tv_sec = tv.tv_sec; | ||
113 | ts.tv_nsec = tv.tv_usec * 1000; | ||
114 | |||
115 | __print_timestamp(" USR", &ts, 0, 0); | ||
116 | } | ||
117 | |||
118 | static void print_timestamp(struct scm_timestamping *tss, int tstype, | ||
119 | int tskey, int payload_len) | ||
120 | { | ||
121 | const char *tsname; | ||
122 | |||
123 | switch (tstype) { | ||
124 | case SCM_TSTAMP_SCHED: | ||
125 | tsname = " ENQ"; | ||
126 | break; | ||
127 | case SCM_TSTAMP_SND: | ||
128 | tsname = " SND"; | ||
129 | break; | ||
130 | case SCM_TSTAMP_ACK: | ||
131 | tsname = " ACK"; | ||
132 | break; | ||
133 | default: | ||
134 | error(1, 0, "unknown timestamp type: %u", | ||
135 | tstype); | ||
136 | } | ||
137 | __print_timestamp(tsname, &tss->ts[0], tskey, payload_len); | ||
138 | } | ||
139 | |||
140 | /* TODO: convert to check_and_print payload once API is stable */ | ||
141 | static void print_payload(char *data, int len) | ||
142 | { | ||
143 | int i; | ||
144 | |||
145 | if (!len) | ||
146 | return; | ||
147 | |||
148 | if (len > 70) | ||
149 | len = 70; | ||
150 | |||
151 | fprintf(stderr, "payload: "); | ||
152 | for (i = 0; i < len; i++) | ||
153 | fprintf(stderr, "%02hhx ", data[i]); | ||
154 | fprintf(stderr, "\n"); | ||
155 | } | ||
156 | |||
157 | static void print_pktinfo(int family, int ifindex, void *saddr, void *daddr) | ||
158 | { | ||
159 | char sa[INET6_ADDRSTRLEN], da[INET6_ADDRSTRLEN]; | ||
160 | |||
161 | fprintf(stderr, " pktinfo: ifindex=%u src=%s dst=%s\n", | ||
162 | ifindex, | ||
163 | saddr ? inet_ntop(family, saddr, sa, sizeof(sa)) : "unknown", | ||
164 | daddr ? inet_ntop(family, daddr, da, sizeof(da)) : "unknown"); | ||
165 | } | ||
166 | |||
167 | static void __poll(int fd) | ||
168 | { | ||
169 | struct pollfd pollfd; | ||
170 | int ret; | ||
171 | |||
172 | memset(&pollfd, 0, sizeof(pollfd)); | ||
173 | pollfd.fd = fd; | ||
174 | ret = poll(&pollfd, 1, 100); | ||
175 | if (ret != 1) | ||
176 | error(1, errno, "poll"); | ||
177 | } | ||
178 | |||
179 | static void __recv_errmsg_cmsg(struct msghdr *msg, int payload_len) | ||
180 | { | ||
181 | struct sock_extended_err *serr = NULL; | ||
182 | struct scm_timestamping *tss = NULL; | ||
183 | struct cmsghdr *cm; | ||
184 | int batch = 0; | ||
185 | |||
186 | for (cm = CMSG_FIRSTHDR(msg); | ||
187 | cm && cm->cmsg_len; | ||
188 | cm = CMSG_NXTHDR(msg, cm)) { | ||
189 | if (cm->cmsg_level == SOL_SOCKET && | ||
190 | cm->cmsg_type == SCM_TIMESTAMPING) { | ||
191 | tss = (void *) CMSG_DATA(cm); | ||
192 | } else if ((cm->cmsg_level == SOL_IP && | ||
193 | cm->cmsg_type == IP_RECVERR) || | ||
194 | (cm->cmsg_level == SOL_IPV6 && | ||
195 | cm->cmsg_type == IPV6_RECVERR)) { | ||
196 | serr = (void *) CMSG_DATA(cm); | ||
197 | if (serr->ee_errno != ENOMSG || | ||
198 | serr->ee_origin != SO_EE_ORIGIN_TIMESTAMPING) { | ||
199 | fprintf(stderr, "unknown ip error %d %d\n", | ||
200 | serr->ee_errno, | ||
201 | serr->ee_origin); | ||
202 | serr = NULL; | ||
203 | } | ||
204 | } else if (cm->cmsg_level == SOL_IP && | ||
205 | cm->cmsg_type == IP_PKTINFO) { | ||
206 | struct in_pktinfo *info = (void *) CMSG_DATA(cm); | ||
207 | print_pktinfo(AF_INET, info->ipi_ifindex, | ||
208 | &info->ipi_spec_dst, &info->ipi_addr); | ||
209 | } else if (cm->cmsg_level == SOL_IPV6 && | ||
210 | cm->cmsg_type == IPV6_PKTINFO) { | ||
211 | struct in6_pktinfo *info6 = (void *) CMSG_DATA(cm); | ||
212 | print_pktinfo(AF_INET6, info6->ipi6_ifindex, | ||
213 | NULL, &info6->ipi6_addr); | ||
214 | } else | ||
215 | fprintf(stderr, "unknown cmsg %d,%d\n", | ||
216 | cm->cmsg_level, cm->cmsg_type); | ||
217 | |||
218 | if (serr && tss) { | ||
219 | print_timestamp(tss, serr->ee_info, serr->ee_data, | ||
220 | payload_len); | ||
221 | serr = NULL; | ||
222 | tss = NULL; | ||
223 | batch++; | ||
224 | } | ||
225 | } | ||
226 | |||
227 | if (batch > 1) | ||
228 | fprintf(stderr, "batched %d timestamps\n", batch); | ||
229 | } | ||
230 | |||
231 | static int recv_errmsg(int fd) | ||
232 | { | ||
233 | static char ctrl[1024 /* overprovision*/]; | ||
234 | static struct msghdr msg; | ||
235 | struct iovec entry; | ||
236 | static char *data; | ||
237 | int ret = 0; | ||
238 | |||
239 | data = malloc(cfg_payload_len); | ||
240 | if (!data) | ||
241 | error(1, 0, "malloc"); | ||
242 | |||
243 | memset(&msg, 0, sizeof(msg)); | ||
244 | memset(&entry, 0, sizeof(entry)); | ||
245 | memset(ctrl, 0, sizeof(ctrl)); | ||
246 | |||
247 | entry.iov_base = data; | ||
248 | entry.iov_len = cfg_payload_len; | ||
249 | msg.msg_iov = &entry; | ||
250 | msg.msg_iovlen = 1; | ||
251 | msg.msg_name = NULL; | ||
252 | msg.msg_namelen = 0; | ||
253 | msg.msg_control = ctrl; | ||
254 | msg.msg_controllen = sizeof(ctrl); | ||
255 | |||
256 | ret = recvmsg(fd, &msg, MSG_ERRQUEUE); | ||
257 | if (ret == -1 && errno != EAGAIN) | ||
258 | error(1, errno, "recvmsg"); | ||
259 | |||
260 | if (ret >= 0) { | ||
261 | __recv_errmsg_cmsg(&msg, ret); | ||
262 | if (cfg_show_payload) | ||
263 | print_payload(data, cfg_payload_len); | ||
264 | } | ||
265 | |||
266 | free(data); | ||
267 | return ret == -1; | ||
268 | } | ||
269 | |||
270 | static void do_test(int family, unsigned int opt) | ||
271 | { | ||
272 | char *buf; | ||
273 | int fd, i, val = 1, total_len; | ||
274 | |||
275 | if (family == AF_INET6 && cfg_proto != SOCK_STREAM) { | ||
276 | /* due to lack of checksum generation code */ | ||
277 | fprintf(stderr, "test: skipping datagram over IPv6\n"); | ||
278 | return; | ||
279 | } | ||
280 | |||
281 | total_len = cfg_payload_len; | ||
282 | if (cfg_proto == SOCK_RAW) { | ||
283 | total_len += sizeof(struct udphdr); | ||
284 | if (cfg_ipproto == IPPROTO_RAW) | ||
285 | total_len += sizeof(struct iphdr); | ||
286 | } | ||
287 | |||
288 | buf = malloc(total_len); | ||
289 | if (!buf) | ||
290 | error(1, 0, "malloc"); | ||
291 | |||
292 | fd = socket(family, cfg_proto, cfg_ipproto); | ||
293 | if (fd < 0) | ||
294 | error(1, errno, "socket"); | ||
295 | |||
296 | if (cfg_proto == SOCK_STREAM) { | ||
297 | if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, | ||
298 | (char*) &val, sizeof(val))) | ||
299 | error(1, 0, "setsockopt no nagle"); | ||
300 | |||
301 | if (family == PF_INET) { | ||
302 | if (connect(fd, (void *) &daddr, sizeof(daddr))) | ||
303 | error(1, errno, "connect ipv4"); | ||
304 | } else { | ||
305 | if (connect(fd, (void *) &daddr6, sizeof(daddr6))) | ||
306 | error(1, errno, "connect ipv6"); | ||
307 | } | ||
308 | } | ||
309 | |||
310 | if (cfg_do_pktinfo) { | ||
311 | if (family == AF_INET6) { | ||
312 | if (setsockopt(fd, SOL_IPV6, IPV6_RECVPKTINFO, | ||
313 | &val, sizeof(val))) | ||
314 | error(1, errno, "setsockopt pktinfo ipv6"); | ||
315 | } else { | ||
316 | if (setsockopt(fd, SOL_IP, IP_PKTINFO, | ||
317 | &val, sizeof(val))) | ||
318 | error(1, errno, "setsockopt pktinfo ipv4"); | ||
319 | } | ||
320 | } | ||
321 | |||
322 | opt |= SOF_TIMESTAMPING_SOFTWARE | | ||
323 | SOF_TIMESTAMPING_OPT_CMSG | | ||
324 | SOF_TIMESTAMPING_OPT_ID; | ||
325 | if (cfg_loop_nodata) | ||
326 | opt |= SOF_TIMESTAMPING_OPT_TSONLY; | ||
327 | |||
328 | if (setsockopt(fd, SOL_SOCKET, SO_TIMESTAMPING, | ||
329 | (char *) &opt, sizeof(opt))) | ||
330 | error(1, 0, "setsockopt timestamping"); | ||
331 | |||
332 | for (i = 0; i < cfg_num_pkts; i++) { | ||
333 | memset(&ts_prev, 0, sizeof(ts_prev)); | ||
334 | memset(buf, 'a' + i, total_len); | ||
335 | |||
336 | if (cfg_proto == SOCK_RAW) { | ||
337 | struct udphdr *udph; | ||
338 | int off = 0; | ||
339 | |||
340 | if (cfg_ipproto == IPPROTO_RAW) { | ||
341 | struct iphdr *iph = (void *) buf; | ||
342 | |||
343 | memset(iph, 0, sizeof(*iph)); | ||
344 | iph->ihl = 5; | ||
345 | iph->version = 4; | ||
346 | iph->ttl = 2; | ||
347 | iph->daddr = daddr.sin_addr.s_addr; | ||
348 | iph->protocol = IPPROTO_UDP; | ||
349 | /* kernel writes saddr, csum, len */ | ||
350 | |||
351 | off = sizeof(*iph); | ||
352 | } | ||
353 | |||
354 | udph = (void *) buf + off; | ||
355 | udph->source = ntohs(9000); /* random spoof */ | ||
356 | udph->dest = ntohs(dest_port); | ||
357 | udph->len = ntohs(sizeof(*udph) + cfg_payload_len); | ||
358 | udph->check = 0; /* not allowed for IPv6 */ | ||
359 | } | ||
360 | |||
361 | print_timestamp_usr(); | ||
362 | if (cfg_proto != SOCK_STREAM) { | ||
363 | if (family == PF_INET) | ||
364 | val = sendto(fd, buf, total_len, 0, (void *) &daddr, sizeof(daddr)); | ||
365 | else | ||
366 | val = sendto(fd, buf, total_len, 0, (void *) &daddr6, sizeof(daddr6)); | ||
367 | } else { | ||
368 | val = send(fd, buf, cfg_payload_len, 0); | ||
369 | } | ||
370 | if (val != total_len) | ||
371 | error(1, errno, "send"); | ||
372 | |||
373 | /* wait for all errors to be queued, else ACKs arrive OOO */ | ||
374 | usleep(50 * 1000); | ||
375 | |||
376 | __poll(fd); | ||
377 | |||
378 | while (!recv_errmsg(fd)) {} | ||
379 | } | ||
380 | |||
381 | if (close(fd)) | ||
382 | error(1, errno, "close"); | ||
383 | |||
384 | free(buf); | ||
385 | usleep(400 * 1000); | ||
386 | } | ||
387 | |||
388 | static void __attribute__((noreturn)) usage(const char *filepath) | ||
389 | { | ||
390 | fprintf(stderr, "\nUsage: %s [options] hostname\n" | ||
391 | "\nwhere options are:\n" | ||
392 | " -4: only IPv4\n" | ||
393 | " -6: only IPv6\n" | ||
394 | " -h: show this message\n" | ||
395 | " -I: request PKTINFO\n" | ||
396 | " -l N: send N bytes at a time\n" | ||
397 | " -n: set no-payload option\n" | ||
398 | " -r: use raw\n" | ||
399 | " -R: use raw (IP_HDRINCL)\n" | ||
400 | " -p N: connect to port N\n" | ||
401 | " -u: use udp\n" | ||
402 | " -x: show payload (up to 70 bytes)\n", | ||
403 | filepath); | ||
404 | exit(1); | ||
405 | } | ||
406 | |||
407 | static void parse_opt(int argc, char **argv) | ||
408 | { | ||
409 | int proto_count = 0; | ||
410 | char c; | ||
411 | |||
412 | while ((c = getopt(argc, argv, "46hIl:np:rRux")) != -1) { | ||
413 | switch (c) { | ||
414 | case '4': | ||
415 | do_ipv6 = 0; | ||
416 | break; | ||
417 | case '6': | ||
418 | do_ipv4 = 0; | ||
419 | break; | ||
420 | case 'I': | ||
421 | cfg_do_pktinfo = true; | ||
422 | break; | ||
423 | case 'n': | ||
424 | cfg_loop_nodata = true; | ||
425 | break; | ||
426 | case 'r': | ||
427 | proto_count++; | ||
428 | cfg_proto = SOCK_RAW; | ||
429 | cfg_ipproto = IPPROTO_UDP; | ||
430 | break; | ||
431 | case 'R': | ||
432 | proto_count++; | ||
433 | cfg_proto = SOCK_RAW; | ||
434 | cfg_ipproto = IPPROTO_RAW; | ||
435 | break; | ||
436 | case 'u': | ||
437 | proto_count++; | ||
438 | cfg_proto = SOCK_DGRAM; | ||
439 | cfg_ipproto = IPPROTO_UDP; | ||
440 | break; | ||
441 | case 'l': | ||
442 | cfg_payload_len = strtoul(optarg, NULL, 10); | ||
443 | break; | ||
444 | case 'p': | ||
445 | dest_port = strtoul(optarg, NULL, 10); | ||
446 | break; | ||
447 | case 'x': | ||
448 | cfg_show_payload = true; | ||
449 | break; | ||
450 | case 'h': | ||
451 | default: | ||
452 | usage(argv[0]); | ||
453 | } | ||
454 | } | ||
455 | |||
456 | if (!cfg_payload_len) | ||
457 | error(1, 0, "payload may not be nonzero"); | ||
458 | if (cfg_proto != SOCK_STREAM && cfg_payload_len > 1472) | ||
459 | error(1, 0, "udp packet might exceed expected MTU"); | ||
460 | if (!do_ipv4 && !do_ipv6) | ||
461 | error(1, 0, "pass -4 or -6, not both"); | ||
462 | if (proto_count > 1) | ||
463 | error(1, 0, "pass -r, -R or -u, not multiple"); | ||
464 | |||
465 | if (optind != argc - 1) | ||
466 | error(1, 0, "missing required hostname argument"); | ||
467 | } | ||
468 | |||
469 | static void resolve_hostname(const char *hostname) | ||
470 | { | ||
471 | struct addrinfo *addrs, *cur; | ||
472 | int have_ipv4 = 0, have_ipv6 = 0; | ||
473 | |||
474 | if (getaddrinfo(hostname, NULL, NULL, &addrs)) | ||
475 | error(1, errno, "getaddrinfo"); | ||
476 | |||
477 | cur = addrs; | ||
478 | while (cur && !have_ipv4 && !have_ipv6) { | ||
479 | if (!have_ipv4 && cur->ai_family == AF_INET) { | ||
480 | memcpy(&daddr, cur->ai_addr, sizeof(daddr)); | ||
481 | daddr.sin_port = htons(dest_port); | ||
482 | have_ipv4 = 1; | ||
483 | } | ||
484 | else if (!have_ipv6 && cur->ai_family == AF_INET6) { | ||
485 | memcpy(&daddr6, cur->ai_addr, sizeof(daddr6)); | ||
486 | daddr6.sin6_port = htons(dest_port); | ||
487 | have_ipv6 = 1; | ||
488 | } | ||
489 | cur = cur->ai_next; | ||
490 | } | ||
491 | if (addrs) | ||
492 | freeaddrinfo(addrs); | ||
493 | |||
494 | do_ipv4 &= have_ipv4; | ||
495 | do_ipv6 &= have_ipv6; | ||
496 | } | ||
497 | |||
498 | static void do_main(int family) | ||
499 | { | ||
500 | fprintf(stderr, "family: %s\n", | ||
501 | family == PF_INET ? "INET" : "INET6"); | ||
502 | |||
503 | fprintf(stderr, "test SND\n"); | ||
504 | do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE); | ||
505 | |||
506 | fprintf(stderr, "test ENQ\n"); | ||
507 | do_test(family, SOF_TIMESTAMPING_TX_SCHED); | ||
508 | |||
509 | fprintf(stderr, "test ENQ + SND\n"); | ||
510 | do_test(family, SOF_TIMESTAMPING_TX_SCHED | | ||
511 | SOF_TIMESTAMPING_TX_SOFTWARE); | ||
512 | |||
513 | if (cfg_proto == SOCK_STREAM) { | ||
514 | fprintf(stderr, "\ntest ACK\n"); | ||
515 | do_test(family, SOF_TIMESTAMPING_TX_ACK); | ||
516 | |||
517 | fprintf(stderr, "\ntest SND + ACK\n"); | ||
518 | do_test(family, SOF_TIMESTAMPING_TX_SOFTWARE | | ||
519 | SOF_TIMESTAMPING_TX_ACK); | ||
520 | |||
521 | fprintf(stderr, "\ntest ENQ + SND + ACK\n"); | ||
522 | do_test(family, SOF_TIMESTAMPING_TX_SCHED | | ||
523 | SOF_TIMESTAMPING_TX_SOFTWARE | | ||
524 | SOF_TIMESTAMPING_TX_ACK); | ||
525 | } | ||
526 | } | ||
527 | |||
528 | const char *sock_names[] = { NULL, "TCP", "UDP", "RAW" }; | ||
529 | |||
530 | int main(int argc, char **argv) | ||
531 | { | ||
532 | if (argc == 1) | ||
533 | usage(argv[0]); | ||
534 | |||
535 | parse_opt(argc, argv); | ||
536 | resolve_hostname(argv[argc - 1]); | ||
537 | |||
538 | fprintf(stderr, "protocol: %s\n", sock_names[cfg_proto]); | ||
539 | fprintf(stderr, "payload: %u\n", cfg_payload_len); | ||
540 | fprintf(stderr, "server port: %u\n", dest_port); | ||
541 | fprintf(stderr, "\n"); | ||
542 | |||
543 | if (do_ipv4) | ||
544 | do_main(PF_INET); | ||
545 | if (do_ipv6) | ||
546 | do_main(PF_INET6); | ||
547 | |||
548 | return 0; | ||
549 | } | ||
diff --git a/tools/testing/selftests/nsfs/Makefile b/tools/testing/selftests/nsfs/Makefile new file mode 100644 index 000000000000..2306054a901a --- /dev/null +++ b/tools/testing/selftests/nsfs/Makefile | |||
@@ -0,0 +1,12 @@ | |||
1 | TEST_PROGS := owner pidns | ||
2 | |||
3 | CFLAGS := -Wall -Werror | ||
4 | |||
5 | all: owner pidns | ||
6 | owner: owner.c | ||
7 | pidns: pidns.c | ||
8 | |||
9 | clean: | ||
10 | $(RM) owner pidns | ||
11 | |||
12 | include ../lib.mk | ||
diff --git a/tools/testing/selftests/nsfs/owner.c b/tools/testing/selftests/nsfs/owner.c new file mode 100644 index 000000000000..437205f8b714 --- /dev/null +++ b/tools/testing/selftests/nsfs/owner.c | |||
@@ -0,0 +1,91 @@ | |||
1 | #define _GNU_SOURCE | ||
2 | #include <sched.h> | ||
3 | #include <unistd.h> | ||
4 | #include <stdio.h> | ||
5 | #include <stdlib.h> | ||
6 | #include <signal.h> | ||
7 | #include <errno.h> | ||
8 | #include <sys/types.h> | ||
9 | #include <sys/stat.h> | ||
10 | #include <fcntl.h> | ||
11 | #include <sys/ioctl.h> | ||
12 | #include <sys/prctl.h> | ||
13 | #include <sys/wait.h> | ||
14 | |||
15 | #define NSIO 0xb7 | ||
16 | #define NS_GET_USERNS _IO(NSIO, 0x1) | ||
17 | |||
18 | #define pr_err(fmt, ...) \ | ||
19 | ({ \ | ||
20 | fprintf(stderr, "%s:%d:" fmt ": %m\n", \ | ||
21 | __func__, __LINE__, ##__VA_ARGS__); \ | ||
22 | 1; \ | ||
23 | }) | ||
24 | |||
25 | int main(int argc, char *argvp[]) | ||
26 | { | ||
27 | int pfd[2], ns, uns, init_uns; | ||
28 | struct stat st1, st2; | ||
29 | char path[128]; | ||
30 | pid_t pid; | ||
31 | char c; | ||
32 | |||
33 | if (pipe(pfd)) | ||
34 | return 1; | ||
35 | |||
36 | pid = fork(); | ||
37 | if (pid < 0) | ||
38 | return pr_err("fork"); | ||
39 | if (pid == 0) { | ||
40 | prctl(PR_SET_PDEATHSIG, SIGKILL); | ||
41 | if (unshare(CLONE_NEWUTS | CLONE_NEWUSER)) | ||
42 | return pr_err("unshare"); | ||
43 | close(pfd[0]); | ||
44 | close(pfd[1]); | ||
45 | while (1) | ||
46 | sleep(1); | ||
47 | return 0; | ||
48 | } | ||
49 | close(pfd[1]); | ||
50 | if (read(pfd[0], &c, 1) != 0) | ||
51 | return pr_err("Unable to read from pipe"); | ||
52 | close(pfd[0]); | ||
53 | |||
54 | snprintf(path, sizeof(path), "/proc/%d/ns/uts", pid); | ||
55 | ns = open(path, O_RDONLY); | ||
56 | if (ns < 0) | ||
57 | return pr_err("Unable to open %s", path); | ||
58 | |||
59 | uns = ioctl(ns, NS_GET_USERNS); | ||
60 | if (uns < 0) | ||
61 | return pr_err("Unable to get an owning user namespace"); | ||
62 | |||
63 | if (fstat(uns, &st1)) | ||
64 | return pr_err("fstat"); | ||
65 | |||
66 | snprintf(path, sizeof(path), "/proc/%d/ns/user", pid); | ||
67 | if (stat(path, &st2)) | ||
68 | return pr_err("stat"); | ||
69 | |||
70 | if (st1.st_ino != st2.st_ino) | ||
71 | return pr_err("NS_GET_USERNS returned a wrong namespace"); | ||
72 | |||
73 | init_uns = ioctl(uns, NS_GET_USERNS); | ||
74 | if (uns < 0) | ||
75 | return pr_err("Unable to get an owning user namespace"); | ||
76 | |||
77 | if (ioctl(init_uns, NS_GET_USERNS) >= 0 || errno != EPERM) | ||
78 | return pr_err("Don't get EPERM"); | ||
79 | |||
80 | if (unshare(CLONE_NEWUSER)) | ||
81 | return pr_err("unshare"); | ||
82 | |||
83 | if (ioctl(ns, NS_GET_USERNS) >= 0 || errno != EPERM) | ||
84 | return pr_err("Don't get EPERM"); | ||
85 | if (ioctl(init_uns, NS_GET_USERNS) >= 0 || errno != EPERM) | ||
86 | return pr_err("Don't get EPERM"); | ||
87 | |||
88 | kill(pid, SIGKILL); | ||
89 | wait(NULL); | ||
90 | return 0; | ||
91 | } | ||
diff --git a/tools/testing/selftests/nsfs/pidns.c b/tools/testing/selftests/nsfs/pidns.c new file mode 100644 index 000000000000..ae3a0d68e966 --- /dev/null +++ b/tools/testing/selftests/nsfs/pidns.c | |||
@@ -0,0 +1,78 @@ | |||
1 | #define _GNU_SOURCE | ||
2 | #include <sched.h> | ||
3 | #include <unistd.h> | ||
4 | #include <stdio.h> | ||
5 | #include <stdlib.h> | ||
6 | #include <signal.h> | ||
7 | #include <errno.h> | ||
8 | #include <sys/types.h> | ||
9 | #include <sys/stat.h> | ||
10 | #include <fcntl.h> | ||
11 | #include <sys/ioctl.h> | ||
12 | #include <sys/prctl.h> | ||
13 | #include <sys/wait.h> | ||
14 | |||
15 | #define pr_err(fmt, ...) \ | ||
16 | ({ \ | ||
17 | fprintf(stderr, "%s:%d:" fmt ": %m\n", \ | ||
18 | __func__, __LINE__, ##__VA_ARGS__); \ | ||
19 | 1; \ | ||
20 | }) | ||
21 | |||
22 | #define NSIO 0xb7 | ||
23 | #define NS_GET_USERNS _IO(NSIO, 0x1) | ||
24 | #define NS_GET_PARENT _IO(NSIO, 0x2) | ||
25 | |||
26 | #define __stack_aligned__ __attribute__((aligned(16))) | ||
27 | struct cr_clone_arg { | ||
28 | char stack[128] __stack_aligned__; | ||
29 | char stack_ptr[0]; | ||
30 | }; | ||
31 | |||
32 | static int child(void *args) | ||
33 | { | ||
34 | prctl(PR_SET_PDEATHSIG, SIGKILL); | ||
35 | while (1) | ||
36 | sleep(1); | ||
37 | exit(0); | ||
38 | } | ||
39 | |||
40 | int main(int argc, char *argv[]) | ||
41 | { | ||
42 | char *ns_strs[] = {"pid", "user"}; | ||
43 | char path[] = "/proc/0123456789/ns/pid"; | ||
44 | struct cr_clone_arg ca; | ||
45 | struct stat st1, st2; | ||
46 | int ns, pns, i; | ||
47 | pid_t pid; | ||
48 | |||
49 | pid = clone(child, ca.stack_ptr, CLONE_NEWUSER | CLONE_NEWPID | SIGCHLD, NULL); | ||
50 | if (pid < 0) | ||
51 | return pr_err("clone"); | ||
52 | |||
53 | for (i = 0; i < 2; i++) { | ||
54 | snprintf(path, sizeof(path), "/proc/%d/ns/%s", pid, ns_strs[i]); | ||
55 | ns = open(path, O_RDONLY); | ||
56 | if (ns < 0) | ||
57 | return pr_err("Unable to open %s", path); | ||
58 | |||
59 | pns = ioctl(ns, NS_GET_PARENT); | ||
60 | if (pns < 0) | ||
61 | return pr_err("Unable to get a parent pidns"); | ||
62 | |||
63 | snprintf(path, sizeof(path), "/proc/self/ns/%s", ns_strs[i]); | ||
64 | if (stat(path, &st2)) | ||
65 | return pr_err("Unable to stat %s", path); | ||
66 | if (fstat(pns, &st1)) | ||
67 | return pr_err("Unable to stat the parent pidns"); | ||
68 | if (st1.st_ino != st2.st_ino) | ||
69 | return pr_err("NS_GET_PARENT returned a wrong namespace"); | ||
70 | |||
71 | if (ioctl(pns, NS_GET_PARENT) >= 0 || errno != EPERM) | ||
72 | return pr_err("Don't get EPERM");; | ||
73 | } | ||
74 | |||
75 | kill(pid, SIGKILL); | ||
76 | wait(NULL); | ||
77 | return 0; | ||
78 | } | ||
diff --git a/tools/testing/selftests/powerpc/Makefile b/tools/testing/selftests/powerpc/Makefile index 1cc6d64c39b7..db54a33f850f 100644 --- a/tools/testing/selftests/powerpc/Makefile +++ b/tools/testing/selftests/powerpc/Makefile | |||
@@ -19,6 +19,7 @@ SUB_DIRS = alignment \ | |||
19 | dscr \ | 19 | dscr \ |
20 | mm \ | 20 | mm \ |
21 | pmu \ | 21 | pmu \ |
22 | signal \ | ||
22 | primitives \ | 23 | primitives \ |
23 | stringloops \ | 24 | stringloops \ |
24 | switch_endian \ | 25 | switch_endian \ |
diff --git a/tools/testing/selftests/powerpc/copyloops/asm/export.h b/tools/testing/selftests/powerpc/copyloops/asm/export.h new file mode 100644 index 000000000000..2d14a9b4248c --- /dev/null +++ b/tools/testing/selftests/powerpc/copyloops/asm/export.h | |||
@@ -0,0 +1 @@ | |||
#define EXPORT_SYMBOL(x) | |||
diff --git a/tools/testing/selftests/powerpc/fpu_asm.h b/tools/testing/selftests/powerpc/fpu_asm.h new file mode 100644 index 000000000000..6a387d255e27 --- /dev/null +++ b/tools/testing/selftests/powerpc/fpu_asm.h | |||
@@ -0,0 +1,80 @@ | |||
1 | /* | ||
2 | * Copyright 2016, Cyril Bur, IBM Corp. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | */ | ||
9 | |||
10 | #ifndef _SELFTESTS_POWERPC_FPU_ASM_H | ||
11 | #define _SELFTESTS_POWERPC_FPU_ASM_H | ||
12 | #include "basic_asm.h" | ||
13 | |||
14 | #define PUSH_FPU(stack_size) \ | ||
15 | stfd f31,(stack_size + STACK_FRAME_MIN_SIZE)(%r1); \ | ||
16 | stfd f30,(stack_size + STACK_FRAME_MIN_SIZE - 8)(%r1); \ | ||
17 | stfd f29,(stack_size + STACK_FRAME_MIN_SIZE - 16)(%r1); \ | ||
18 | stfd f28,(stack_size + STACK_FRAME_MIN_SIZE - 24)(%r1); \ | ||
19 | stfd f27,(stack_size + STACK_FRAME_MIN_SIZE - 32)(%r1); \ | ||
20 | stfd f26,(stack_size + STACK_FRAME_MIN_SIZE - 40)(%r1); \ | ||
21 | stfd f25,(stack_size + STACK_FRAME_MIN_SIZE - 48)(%r1); \ | ||
22 | stfd f24,(stack_size + STACK_FRAME_MIN_SIZE - 56)(%r1); \ | ||
23 | stfd f23,(stack_size + STACK_FRAME_MIN_SIZE - 64)(%r1); \ | ||
24 | stfd f22,(stack_size + STACK_FRAME_MIN_SIZE - 72)(%r1); \ | ||
25 | stfd f21,(stack_size + STACK_FRAME_MIN_SIZE - 80)(%r1); \ | ||
26 | stfd f20,(stack_size + STACK_FRAME_MIN_SIZE - 88)(%r1); \ | ||
27 | stfd f19,(stack_size + STACK_FRAME_MIN_SIZE - 96)(%r1); \ | ||
28 | stfd f18,(stack_size + STACK_FRAME_MIN_SIZE - 104)(%r1); \ | ||
29 | stfd f17,(stack_size + STACK_FRAME_MIN_SIZE - 112)(%r1); \ | ||
30 | stfd f16,(stack_size + STACK_FRAME_MIN_SIZE - 120)(%r1); \ | ||
31 | stfd f15,(stack_size + STACK_FRAME_MIN_SIZE - 128)(%r1); \ | ||
32 | stfd f14,(stack_size + STACK_FRAME_MIN_SIZE - 136)(%r1); | ||
33 | |||
34 | #define POP_FPU(stack_size) \ | ||
35 | lfd f31,(stack_size + STACK_FRAME_MIN_SIZE)(%r1); \ | ||
36 | lfd f30,(stack_size + STACK_FRAME_MIN_SIZE - 8)(%r1); \ | ||
37 | lfd f29,(stack_size + STACK_FRAME_MIN_SIZE - 16)(%r1); \ | ||
38 | lfd f28,(stack_size + STACK_FRAME_MIN_SIZE - 24)(%r1); \ | ||
39 | lfd f27,(stack_size + STACK_FRAME_MIN_SIZE - 32)(%r1); \ | ||
40 | lfd f26,(stack_size + STACK_FRAME_MIN_SIZE - 40)(%r1); \ | ||
41 | lfd f25,(stack_size + STACK_FRAME_MIN_SIZE - 48)(%r1); \ | ||
42 | lfd f24,(stack_size + STACK_FRAME_MIN_SIZE - 56)(%r1); \ | ||
43 | lfd f23,(stack_size + STACK_FRAME_MIN_SIZE - 64)(%r1); \ | ||
44 | lfd f22,(stack_size + STACK_FRAME_MIN_SIZE - 72)(%r1); \ | ||
45 | lfd f21,(stack_size + STACK_FRAME_MIN_SIZE - 80)(%r1); \ | ||
46 | lfd f20,(stack_size + STACK_FRAME_MIN_SIZE - 88)(%r1); \ | ||
47 | lfd f19,(stack_size + STACK_FRAME_MIN_SIZE - 96)(%r1); \ | ||
48 | lfd f18,(stack_size + STACK_FRAME_MIN_SIZE - 104)(%r1); \ | ||
49 | lfd f17,(stack_size + STACK_FRAME_MIN_SIZE - 112)(%r1); \ | ||
50 | lfd f16,(stack_size + STACK_FRAME_MIN_SIZE - 120)(%r1); \ | ||
51 | lfd f15,(stack_size + STACK_FRAME_MIN_SIZE - 128)(%r1); \ | ||
52 | lfd f14,(stack_size + STACK_FRAME_MIN_SIZE - 136)(%r1); | ||
53 | |||
54 | /* | ||
55 | * Careful calling this, it will 'clobber' fpu (by design) | ||
56 | * Don't call this from C | ||
57 | */ | ||
58 | FUNC_START(load_fpu) | ||
59 | lfd f14,0(r3) | ||
60 | lfd f15,8(r3) | ||
61 | lfd f16,16(r3) | ||
62 | lfd f17,24(r3) | ||
63 | lfd f18,32(r3) | ||
64 | lfd f19,40(r3) | ||
65 | lfd f20,48(r3) | ||
66 | lfd f21,56(r3) | ||
67 | lfd f22,64(r3) | ||
68 | lfd f23,72(r3) | ||
69 | lfd f24,80(r3) | ||
70 | lfd f25,88(r3) | ||
71 | lfd f26,96(r3) | ||
72 | lfd f27,104(r3) | ||
73 | lfd f28,112(r3) | ||
74 | lfd f29,120(r3) | ||
75 | lfd f30,128(r3) | ||
76 | lfd f31,136(r3) | ||
77 | blr | ||
78 | FUNC_END(load_fpu) | ||
79 | |||
80 | #endif /* _SELFTESTS_POWERPC_FPU_ASM_H */ | ||
diff --git a/tools/testing/selftests/powerpc/gpr_asm.h b/tools/testing/selftests/powerpc/gpr_asm.h new file mode 100644 index 000000000000..f6f38852d3a0 --- /dev/null +++ b/tools/testing/selftests/powerpc/gpr_asm.h | |||
@@ -0,0 +1,96 @@ | |||
1 | /* | ||
2 | * Copyright 2016, Cyril Bur, IBM Corp. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | */ | ||
9 | |||
10 | #ifndef _SELFTESTS_POWERPC_GPR_ASM_H | ||
11 | #define _SELFTESTS_POWERPC_GPR_ASM_H | ||
12 | |||
13 | #include "basic_asm.h" | ||
14 | |||
15 | #define __PUSH_NVREGS(top_pos); \ | ||
16 | std r31,(top_pos)(%r1); \ | ||
17 | std r30,(top_pos - 8)(%r1); \ | ||
18 | std r29,(top_pos - 16)(%r1); \ | ||
19 | std r28,(top_pos - 24)(%r1); \ | ||
20 | std r27,(top_pos - 32)(%r1); \ | ||
21 | std r26,(top_pos - 40)(%r1); \ | ||
22 | std r25,(top_pos - 48)(%r1); \ | ||
23 | std r24,(top_pos - 56)(%r1); \ | ||
24 | std r23,(top_pos - 64)(%r1); \ | ||
25 | std r22,(top_pos - 72)(%r1); \ | ||
26 | std r21,(top_pos - 80)(%r1); \ | ||
27 | std r20,(top_pos - 88)(%r1); \ | ||
28 | std r19,(top_pos - 96)(%r1); \ | ||
29 | std r18,(top_pos - 104)(%r1); \ | ||
30 | std r17,(top_pos - 112)(%r1); \ | ||
31 | std r16,(top_pos - 120)(%r1); \ | ||
32 | std r15,(top_pos - 128)(%r1); \ | ||
33 | std r14,(top_pos - 136)(%r1) | ||
34 | |||
35 | #define __POP_NVREGS(top_pos); \ | ||
36 | ld r31,(top_pos)(%r1); \ | ||
37 | ld r30,(top_pos - 8)(%r1); \ | ||
38 | ld r29,(top_pos - 16)(%r1); \ | ||
39 | ld r28,(top_pos - 24)(%r1); \ | ||
40 | ld r27,(top_pos - 32)(%r1); \ | ||
41 | ld r26,(top_pos - 40)(%r1); \ | ||
42 | ld r25,(top_pos - 48)(%r1); \ | ||
43 | ld r24,(top_pos - 56)(%r1); \ | ||
44 | ld r23,(top_pos - 64)(%r1); \ | ||
45 | ld r22,(top_pos - 72)(%r1); \ | ||
46 | ld r21,(top_pos - 80)(%r1); \ | ||
47 | ld r20,(top_pos - 88)(%r1); \ | ||
48 | ld r19,(top_pos - 96)(%r1); \ | ||
49 | ld r18,(top_pos - 104)(%r1); \ | ||
50 | ld r17,(top_pos - 112)(%r1); \ | ||
51 | ld r16,(top_pos - 120)(%r1); \ | ||
52 | ld r15,(top_pos - 128)(%r1); \ | ||
53 | ld r14,(top_pos - 136)(%r1) | ||
54 | |||
55 | #define PUSH_NVREGS(stack_size) \ | ||
56 | __PUSH_NVREGS(stack_size + STACK_FRAME_MIN_SIZE) | ||
57 | |||
58 | /* 18 NV FPU REGS */ | ||
59 | #define PUSH_NVREGS_BELOW_FPU(stack_size) \ | ||
60 | __PUSH_NVREGS(stack_size + STACK_FRAME_MIN_SIZE - (18 * 8)) | ||
61 | |||
62 | #define POP_NVREGS(stack_size) \ | ||
63 | __POP_NVREGS(stack_size + STACK_FRAME_MIN_SIZE) | ||
64 | |||
65 | /* 18 NV FPU REGS */ | ||
66 | #define POP_NVREGS_BELOW_FPU(stack_size) \ | ||
67 | __POP_NVREGS(stack_size + STACK_FRAME_MIN_SIZE - (18 * 8)) | ||
68 | |||
69 | /* | ||
70 | * Careful calling this, it will 'clobber' NVGPRs (by design) | ||
71 | * Don't call this from C | ||
72 | */ | ||
73 | FUNC_START(load_gpr) | ||
74 | ld r14,0(r3) | ||
75 | ld r15,8(r3) | ||
76 | ld r16,16(r3) | ||
77 | ld r17,24(r3) | ||
78 | ld r18,32(r3) | ||
79 | ld r19,40(r3) | ||
80 | ld r20,48(r3) | ||
81 | ld r21,56(r3) | ||
82 | ld r22,64(r3) | ||
83 | ld r23,72(r3) | ||
84 | ld r24,80(r3) | ||
85 | ld r25,88(r3) | ||
86 | ld r26,96(r3) | ||
87 | ld r27,104(r3) | ||
88 | ld r28,112(r3) | ||
89 | ld r29,120(r3) | ||
90 | ld r30,128(r3) | ||
91 | ld r31,136(r3) | ||
92 | blr | ||
93 | FUNC_END(load_gpr) | ||
94 | |||
95 | |||
96 | #endif /* _SELFTESTS_POWERPC_GPR_ASM_H */ | ||
diff --git a/tools/testing/selftests/powerpc/harness.c b/tools/testing/selftests/powerpc/harness.c index 52f9be7f61f0..248a820048df 100644 --- a/tools/testing/selftests/powerpc/harness.c +++ b/tools/testing/selftests/powerpc/harness.c | |||
@@ -19,9 +19,9 @@ | |||
19 | #include "subunit.h" | 19 | #include "subunit.h" |
20 | #include "utils.h" | 20 | #include "utils.h" |
21 | 21 | ||
22 | #define TIMEOUT 120 | ||
23 | #define KILL_TIMEOUT 5 | 22 | #define KILL_TIMEOUT 5 |
24 | 23 | ||
24 | static uint64_t timeout = 120; | ||
25 | 25 | ||
26 | int run_test(int (test_function)(void), char *name) | 26 | int run_test(int (test_function)(void), char *name) |
27 | { | 27 | { |
@@ -44,7 +44,7 @@ int run_test(int (test_function)(void), char *name) | |||
44 | setpgid(pid, pid); | 44 | setpgid(pid, pid); |
45 | 45 | ||
46 | /* Wake us up in timeout seconds */ | 46 | /* Wake us up in timeout seconds */ |
47 | alarm(TIMEOUT); | 47 | alarm(timeout); |
48 | terminated = false; | 48 | terminated = false; |
49 | 49 | ||
50 | wait: | 50 | wait: |
@@ -94,6 +94,11 @@ static struct sigaction alarm_action = { | |||
94 | .sa_handler = alarm_handler, | 94 | .sa_handler = alarm_handler, |
95 | }; | 95 | }; |
96 | 96 | ||
97 | void test_harness_set_timeout(uint64_t time) | ||
98 | { | ||
99 | timeout = time; | ||
100 | } | ||
101 | |||
97 | int test_harness(int (test_function)(void), char *name) | 102 | int test_harness(int (test_function)(void), char *name) |
98 | { | 103 | { |
99 | int rc; | 104 | int rc; |
diff --git a/tools/testing/selftests/powerpc/math/.gitignore b/tools/testing/selftests/powerpc/math/.gitignore index 4fe13a439fd7..50ded63e25b7 100644 --- a/tools/testing/selftests/powerpc/math/.gitignore +++ b/tools/testing/selftests/powerpc/math/.gitignore | |||
@@ -4,3 +4,4 @@ fpu_preempt | |||
4 | vmx_preempt | 4 | vmx_preempt |
5 | fpu_signal | 5 | fpu_signal |
6 | vmx_signal | 6 | vmx_signal |
7 | vsx_preempt | ||
diff --git a/tools/testing/selftests/powerpc/math/Makefile b/tools/testing/selftests/powerpc/math/Makefile index 5b88875d5955..a505b66d408a 100644 --- a/tools/testing/selftests/powerpc/math/Makefile +++ b/tools/testing/selftests/powerpc/math/Makefile | |||
@@ -1,4 +1,4 @@ | |||
1 | TEST_PROGS := fpu_syscall fpu_preempt fpu_signal vmx_syscall vmx_preempt vmx_signal | 1 | TEST_PROGS := fpu_syscall fpu_preempt fpu_signal vmx_syscall vmx_preempt vmx_signal vsx_preempt |
2 | 2 | ||
3 | all: $(TEST_PROGS) | 3 | all: $(TEST_PROGS) |
4 | 4 | ||
@@ -13,6 +13,9 @@ vmx_syscall: vmx_asm.S | |||
13 | vmx_preempt: vmx_asm.S | 13 | vmx_preempt: vmx_asm.S |
14 | vmx_signal: vmx_asm.S | 14 | vmx_signal: vmx_asm.S |
15 | 15 | ||
16 | vsx_preempt: CFLAGS += -mvsx | ||
17 | vsx_preempt: vsx_asm.S | ||
18 | |||
16 | include ../../lib.mk | 19 | include ../../lib.mk |
17 | 20 | ||
18 | clean: | 21 | clean: |
diff --git a/tools/testing/selftests/powerpc/math/fpu_asm.S b/tools/testing/selftests/powerpc/math/fpu_asm.S index f3711d80e709..241f067a510f 100644 --- a/tools/testing/selftests/powerpc/math/fpu_asm.S +++ b/tools/testing/selftests/powerpc/math/fpu_asm.S | |||
@@ -8,70 +8,7 @@ | |||
8 | */ | 8 | */ |
9 | 9 | ||
10 | #include "../basic_asm.h" | 10 | #include "../basic_asm.h" |
11 | 11 | #include "../fpu_asm.h" | |
12 | #define PUSH_FPU(pos) \ | ||
13 | stfd f14,pos(sp); \ | ||
14 | stfd f15,pos+8(sp); \ | ||
15 | stfd f16,pos+16(sp); \ | ||
16 | stfd f17,pos+24(sp); \ | ||
17 | stfd f18,pos+32(sp); \ | ||
18 | stfd f19,pos+40(sp); \ | ||
19 | stfd f20,pos+48(sp); \ | ||
20 | stfd f21,pos+56(sp); \ | ||
21 | stfd f22,pos+64(sp); \ | ||
22 | stfd f23,pos+72(sp); \ | ||
23 | stfd f24,pos+80(sp); \ | ||
24 | stfd f25,pos+88(sp); \ | ||
25 | stfd f26,pos+96(sp); \ | ||
26 | stfd f27,pos+104(sp); \ | ||
27 | stfd f28,pos+112(sp); \ | ||
28 | stfd f29,pos+120(sp); \ | ||
29 | stfd f30,pos+128(sp); \ | ||
30 | stfd f31,pos+136(sp); | ||
31 | |||
32 | #define POP_FPU(pos) \ | ||
33 | lfd f14,pos(sp); \ | ||
34 | lfd f15,pos+8(sp); \ | ||
35 | lfd f16,pos+16(sp); \ | ||
36 | lfd f17,pos+24(sp); \ | ||
37 | lfd f18,pos+32(sp); \ | ||
38 | lfd f19,pos+40(sp); \ | ||
39 | lfd f20,pos+48(sp); \ | ||
40 | lfd f21,pos+56(sp); \ | ||
41 | lfd f22,pos+64(sp); \ | ||
42 | lfd f23,pos+72(sp); \ | ||
43 | lfd f24,pos+80(sp); \ | ||
44 | lfd f25,pos+88(sp); \ | ||
45 | lfd f26,pos+96(sp); \ | ||
46 | lfd f27,pos+104(sp); \ | ||
47 | lfd f28,pos+112(sp); \ | ||
48 | lfd f29,pos+120(sp); \ | ||
49 | lfd f30,pos+128(sp); \ | ||
50 | lfd f31,pos+136(sp); | ||
51 | |||
52 | # Careful calling this, it will 'clobber' fpu (by design) | ||
53 | # Don't call this from C | ||
54 | FUNC_START(load_fpu) | ||
55 | lfd f14,0(r3) | ||
56 | lfd f15,8(r3) | ||
57 | lfd f16,16(r3) | ||
58 | lfd f17,24(r3) | ||
59 | lfd f18,32(r3) | ||
60 | lfd f19,40(r3) | ||
61 | lfd f20,48(r3) | ||
62 | lfd f21,56(r3) | ||
63 | lfd f22,64(r3) | ||
64 | lfd f23,72(r3) | ||
65 | lfd f24,80(r3) | ||
66 | lfd f25,88(r3) | ||
67 | lfd f26,96(r3) | ||
68 | lfd f27,104(r3) | ||
69 | lfd f28,112(r3) | ||
70 | lfd f29,120(r3) | ||
71 | lfd f30,128(r3) | ||
72 | lfd f31,136(r3) | ||
73 | blr | ||
74 | FUNC_END(load_fpu) | ||
75 | 12 | ||
76 | FUNC_START(check_fpu) | 13 | FUNC_START(check_fpu) |
77 | mr r4,r3 | 14 | mr r4,r3 |
@@ -138,9 +75,9 @@ FUNC_START(test_fpu) | |||
138 | # r4 holds pointer to the pid | 75 | # r4 holds pointer to the pid |
139 | # f14-f31 are non volatiles | 76 | # f14-f31 are non volatiles |
140 | PUSH_BASIC_STACK(256) | 77 | PUSH_BASIC_STACK(256) |
78 | PUSH_FPU(256) | ||
141 | std r3,STACK_FRAME_PARAM(0)(sp) # Address of darray | 79 | std r3,STACK_FRAME_PARAM(0)(sp) # Address of darray |
142 | std r4,STACK_FRAME_PARAM(1)(sp) # Address of pid | 80 | std r4,STACK_FRAME_PARAM(1)(sp) # Address of pid |
143 | PUSH_FPU(STACK_FRAME_LOCAL(2,0)) | ||
144 | 81 | ||
145 | bl load_fpu | 82 | bl load_fpu |
146 | nop | 83 | nop |
@@ -155,7 +92,7 @@ FUNC_START(test_fpu) | |||
155 | bl check_fpu | 92 | bl check_fpu |
156 | nop | 93 | nop |
157 | 94 | ||
158 | POP_FPU(STACK_FRAME_LOCAL(2,0)) | 95 | POP_FPU(256) |
159 | POP_BASIC_STACK(256) | 96 | POP_BASIC_STACK(256) |
160 | blr | 97 | blr |
161 | FUNC_END(test_fpu) | 98 | FUNC_END(test_fpu) |
@@ -166,10 +103,10 @@ FUNC_END(test_fpu) | |||
166 | # registers while running is not zero. | 103 | # registers while running is not zero. |
167 | FUNC_START(preempt_fpu) | 104 | FUNC_START(preempt_fpu) |
168 | PUSH_BASIC_STACK(256) | 105 | PUSH_BASIC_STACK(256) |
106 | PUSH_FPU(256) | ||
169 | std r3,STACK_FRAME_PARAM(0)(sp) # double *darray | 107 | std r3,STACK_FRAME_PARAM(0)(sp) # double *darray |
170 | std r4,STACK_FRAME_PARAM(1)(sp) # int *threads_starting | 108 | std r4,STACK_FRAME_PARAM(1)(sp) # int *threads_starting |
171 | std r5,STACK_FRAME_PARAM(2)(sp) # int *running | 109 | std r5,STACK_FRAME_PARAM(2)(sp) # int *running |
172 | PUSH_FPU(STACK_FRAME_LOCAL(3,0)) | ||
173 | 110 | ||
174 | bl load_fpu | 111 | bl load_fpu |
175 | nop | 112 | nop |
@@ -192,7 +129,7 @@ FUNC_START(preempt_fpu) | |||
192 | cmpwi r5,0 | 129 | cmpwi r5,0 |
193 | bne 2b | 130 | bne 2b |
194 | 131 | ||
195 | 3: POP_FPU(STACK_FRAME_LOCAL(3,0)) | 132 | 3: POP_FPU(256) |
196 | POP_BASIC_STACK(256) | 133 | POP_BASIC_STACK(256) |
197 | blr | 134 | blr |
198 | FUNC_END(preempt_fpu) | 135 | FUNC_END(preempt_fpu) |
diff --git a/tools/testing/selftests/powerpc/math/vmx_asm.S b/tools/testing/selftests/powerpc/math/vmx_asm.S index 1b8c248b3ac1..fd74da488625 100644 --- a/tools/testing/selftests/powerpc/math/vmx_asm.S +++ b/tools/testing/selftests/powerpc/math/vmx_asm.S | |||
@@ -8,90 +8,7 @@ | |||
8 | */ | 8 | */ |
9 | 9 | ||
10 | #include "../basic_asm.h" | 10 | #include "../basic_asm.h" |
11 | 11 | #include "../vmx_asm.h" | |
12 | # POS MUST BE 16 ALIGNED! | ||
13 | #define PUSH_VMX(pos,reg) \ | ||
14 | li reg,pos; \ | ||
15 | stvx v20,reg,sp; \ | ||
16 | addi reg,reg,16; \ | ||
17 | stvx v21,reg,sp; \ | ||
18 | addi reg,reg,16; \ | ||
19 | stvx v22,reg,sp; \ | ||
20 | addi reg,reg,16; \ | ||
21 | stvx v23,reg,sp; \ | ||
22 | addi reg,reg,16; \ | ||
23 | stvx v24,reg,sp; \ | ||
24 | addi reg,reg,16; \ | ||
25 | stvx v25,reg,sp; \ | ||
26 | addi reg,reg,16; \ | ||
27 | stvx v26,reg,sp; \ | ||
28 | addi reg,reg,16; \ | ||
29 | stvx v27,reg,sp; \ | ||
30 | addi reg,reg,16; \ | ||
31 | stvx v28,reg,sp; \ | ||
32 | addi reg,reg,16; \ | ||
33 | stvx v29,reg,sp; \ | ||
34 | addi reg,reg,16; \ | ||
35 | stvx v30,reg,sp; \ | ||
36 | addi reg,reg,16; \ | ||
37 | stvx v31,reg,sp; | ||
38 | |||
39 | # POS MUST BE 16 ALIGNED! | ||
40 | #define POP_VMX(pos,reg) \ | ||
41 | li reg,pos; \ | ||
42 | lvx v20,reg,sp; \ | ||
43 | addi reg,reg,16; \ | ||
44 | lvx v21,reg,sp; \ | ||
45 | addi reg,reg,16; \ | ||
46 | lvx v22,reg,sp; \ | ||
47 | addi reg,reg,16; \ | ||
48 | lvx v23,reg,sp; \ | ||
49 | addi reg,reg,16; \ | ||
50 | lvx v24,reg,sp; \ | ||
51 | addi reg,reg,16; \ | ||
52 | lvx v25,reg,sp; \ | ||
53 | addi reg,reg,16; \ | ||
54 | lvx v26,reg,sp; \ | ||
55 | addi reg,reg,16; \ | ||
56 | lvx v27,reg,sp; \ | ||
57 | addi reg,reg,16; \ | ||
58 | lvx v28,reg,sp; \ | ||
59 | addi reg,reg,16; \ | ||
60 | lvx v29,reg,sp; \ | ||
61 | addi reg,reg,16; \ | ||
62 | lvx v30,reg,sp; \ | ||
63 | addi reg,reg,16; \ | ||
64 | lvx v31,reg,sp; | ||
65 | |||
66 | # Carefull this will 'clobber' vmx (by design) | ||
67 | # Don't call this from C | ||
68 | FUNC_START(load_vmx) | ||
69 | li r5,0 | ||
70 | lvx v20,r5,r3 | ||
71 | addi r5,r5,16 | ||
72 | lvx v21,r5,r3 | ||
73 | addi r5,r5,16 | ||
74 | lvx v22,r5,r3 | ||
75 | addi r5,r5,16 | ||
76 | lvx v23,r5,r3 | ||
77 | addi r5,r5,16 | ||
78 | lvx v24,r5,r3 | ||
79 | addi r5,r5,16 | ||
80 | lvx v25,r5,r3 | ||
81 | addi r5,r5,16 | ||
82 | lvx v26,r5,r3 | ||
83 | addi r5,r5,16 | ||
84 | lvx v27,r5,r3 | ||
85 | addi r5,r5,16 | ||
86 | lvx v28,r5,r3 | ||
87 | addi r5,r5,16 | ||
88 | lvx v29,r5,r3 | ||
89 | addi r5,r5,16 | ||
90 | lvx v30,r5,r3 | ||
91 | addi r5,r5,16 | ||
92 | lvx v31,r5,r3 | ||
93 | blr | ||
94 | FUNC_END(load_vmx) | ||
95 | 12 | ||
96 | # Should be safe from C, only touches r4, r5 and v0,v1,v2 | 13 | # Should be safe from C, only touches r4, r5 and v0,v1,v2 |
97 | FUNC_START(check_vmx) | 14 | FUNC_START(check_vmx) |
diff --git a/tools/testing/selftests/powerpc/math/vsx_asm.S b/tools/testing/selftests/powerpc/math/vsx_asm.S new file mode 100644 index 000000000000..a110dd882d5e --- /dev/null +++ b/tools/testing/selftests/powerpc/math/vsx_asm.S | |||
@@ -0,0 +1,61 @@ | |||
1 | /* | ||
2 | * Copyright 2015, Cyril Bur, IBM Corp. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | */ | ||
9 | |||
10 | #include "../basic_asm.h" | ||
11 | #include "../vsx_asm.h" | ||
12 | |||
13 | #long check_vsx(vector int *r3); | ||
14 | #This function wraps storeing VSX regs to the end of an array and a | ||
15 | #call to a comparison function in C which boils down to a memcmp() | ||
16 | FUNC_START(check_vsx) | ||
17 | PUSH_BASIC_STACK(32) | ||
18 | std r3,STACK_FRAME_PARAM(0)(sp) | ||
19 | addi r3, r3, 16 * 12 #Second half of array | ||
20 | bl store_vsx | ||
21 | ld r3,STACK_FRAME_PARAM(0)(sp) | ||
22 | bl vsx_memcmp | ||
23 | POP_BASIC_STACK(32) | ||
24 | blr | ||
25 | FUNC_END(check_vsx) | ||
26 | |||
27 | # int preempt_vmx(vector int *varray, int *threads_starting, | ||
28 | # int *running); | ||
29 | # On starting will (atomically) decrement threads_starting as a signal | ||
30 | # that the VMX have been loaded with varray. Will proceed to check the | ||
31 | # validity of the VMX registers while running is not zero. | ||
32 | FUNC_START(preempt_vsx) | ||
33 | PUSH_BASIC_STACK(512) | ||
34 | std r3,STACK_FRAME_PARAM(0)(sp) # vector int *varray | ||
35 | std r4,STACK_FRAME_PARAM(1)(sp) # int *threads_starting | ||
36 | std r5,STACK_FRAME_PARAM(2)(sp) # int *running | ||
37 | |||
38 | bl load_vsx | ||
39 | nop | ||
40 | |||
41 | sync | ||
42 | # Atomic DEC | ||
43 | ld r3,STACK_FRAME_PARAM(1)(sp) | ||
44 | 1: lwarx r4,0,r3 | ||
45 | addi r4,r4,-1 | ||
46 | stwcx. r4,0,r3 | ||
47 | bne- 1b | ||
48 | |||
49 | 2: ld r3,STACK_FRAME_PARAM(0)(sp) | ||
50 | bl check_vsx | ||
51 | nop | ||
52 | cmpdi r3,0 | ||
53 | bne 3f | ||
54 | ld r4,STACK_FRAME_PARAM(2)(sp) | ||
55 | ld r5,0(r4) | ||
56 | cmpwi r5,0 | ||
57 | bne 2b | ||
58 | |||
59 | 3: POP_BASIC_STACK(512) | ||
60 | blr | ||
61 | FUNC_END(preempt_vsx) | ||
diff --git a/tools/testing/selftests/powerpc/math/vsx_preempt.c b/tools/testing/selftests/powerpc/math/vsx_preempt.c new file mode 100644 index 000000000000..6387f03a0a6a --- /dev/null +++ b/tools/testing/selftests/powerpc/math/vsx_preempt.c | |||
@@ -0,0 +1,147 @@ | |||
1 | /* | ||
2 | * Copyright 2015, Cyril Bur, IBM Corp. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | * | ||
9 | * This test attempts to see if the VSX registers change across preemption. | ||
10 | * There is no way to be sure preemption happened so this test just | ||
11 | * uses many threads and a long wait. As such, a successful test | ||
12 | * doesn't mean much but a failure is bad. | ||
13 | */ | ||
14 | |||
15 | #include <stdio.h> | ||
16 | #include <string.h> | ||
17 | #include <unistd.h> | ||
18 | #include <sys/syscall.h> | ||
19 | #include <sys/time.h> | ||
20 | #include <sys/types.h> | ||
21 | #include <sys/wait.h> | ||
22 | #include <stdlib.h> | ||
23 | #include <pthread.h> | ||
24 | |||
25 | #include "utils.h" | ||
26 | |||
27 | /* Time to wait for workers to get preempted (seconds) */ | ||
28 | #define PREEMPT_TIME 20 | ||
29 | /* | ||
30 | * Factor by which to multiply number of online CPUs for total number of | ||
31 | * worker threads | ||
32 | */ | ||
33 | #define THREAD_FACTOR 8 | ||
34 | |||
35 | /* | ||
36 | * Ensure there is twice the number of non-volatile VMX regs! | ||
37 | * check_vmx() is going to use the other half as space to put the live | ||
38 | * registers before calling vsx_memcmp() | ||
39 | */ | ||
40 | __thread vector int varray[24] = { | ||
41 | {1, 2, 3, 4 }, {5, 6, 7, 8 }, {9, 10,11,12}, | ||
42 | {13,14,15,16}, {17,18,19,20}, {21,22,23,24}, | ||
43 | {25,26,27,28}, {29,30,31,32}, {33,34,35,36}, | ||
44 | {37,38,39,40}, {41,42,43,44}, {45,46,47,48} | ||
45 | }; | ||
46 | |||
47 | int threads_starting; | ||
48 | int running; | ||
49 | |||
50 | extern long preempt_vsx(vector int *varray, int *threads_starting, int *running); | ||
51 | |||
52 | long vsx_memcmp(vector int *a) { | ||
53 | vector int zero = {0, 0, 0, 0}; | ||
54 | int i; | ||
55 | |||
56 | FAIL_IF(a != varray); | ||
57 | |||
58 | for(i = 0; i < 12; i++) { | ||
59 | if (memcmp(&a[i + 12], &zero, sizeof(vector int)) == 0) { | ||
60 | fprintf(stderr, "Detected zero from the VSX reg %d\n", i + 12); | ||
61 | return 2; | ||
62 | } | ||
63 | } | ||
64 | |||
65 | if (memcmp(a, &a[12], 12 * sizeof(vector int))) { | ||
66 | long *p = (long *)a; | ||
67 | fprintf(stderr, "VSX mismatch\n"); | ||
68 | for (i = 0; i < 24; i=i+2) | ||
69 | fprintf(stderr, "%d: 0x%08lx%08lx | 0x%08lx%08lx\n", | ||
70 | i/2 + i%2 + 20, p[i], p[i + 1], p[i + 24], p[i + 25]); | ||
71 | return 1; | ||
72 | } | ||
73 | return 0; | ||
74 | } | ||
75 | |||
76 | void *preempt_vsx_c(void *p) | ||
77 | { | ||
78 | int i, j; | ||
79 | long rc; | ||
80 | srand(pthread_self()); | ||
81 | for (i = 0; i < 12; i++) | ||
82 | for (j = 0; j < 4; j++) { | ||
83 | varray[i][j] = rand(); | ||
84 | /* Don't want zero because it hides kernel problems */ | ||
85 | if (varray[i][j] == 0) | ||
86 | j--; | ||
87 | } | ||
88 | rc = preempt_vsx(varray, &threads_starting, &running); | ||
89 | if (rc == 2) | ||
90 | fprintf(stderr, "Caught zeros in VSX compares\n"); | ||
91 | return (void *)rc; | ||
92 | } | ||
93 | |||
94 | int test_preempt_vsx(void) | ||
95 | { | ||
96 | int i, rc, threads; | ||
97 | pthread_t *tids; | ||
98 | |||
99 | threads = sysconf(_SC_NPROCESSORS_ONLN) * THREAD_FACTOR; | ||
100 | tids = malloc(threads * sizeof(pthread_t)); | ||
101 | FAIL_IF(!tids); | ||
102 | |||
103 | running = true; | ||
104 | threads_starting = threads; | ||
105 | for (i = 0; i < threads; i++) { | ||
106 | rc = pthread_create(&tids[i], NULL, preempt_vsx_c, NULL); | ||
107 | FAIL_IF(rc); | ||
108 | } | ||
109 | |||
110 | setbuf(stdout, NULL); | ||
111 | /* Not really nessesary but nice to wait for every thread to start */ | ||
112 | printf("\tWaiting for %d workers to start...", threads_starting); | ||
113 | while(threads_starting) | ||
114 | asm volatile("": : :"memory"); | ||
115 | printf("done\n"); | ||
116 | |||
117 | printf("\tWaiting for %d seconds to let some workers get preempted...", PREEMPT_TIME); | ||
118 | sleep(PREEMPT_TIME); | ||
119 | printf("done\n"); | ||
120 | |||
121 | printf("\tStopping workers..."); | ||
122 | /* | ||
123 | * Working are checking this value every loop. In preempt_vsx 'cmpwi r5,0; bne 2b'. | ||
124 | * r5 will have loaded the value of running. | ||
125 | */ | ||
126 | running = 0; | ||
127 | for (i = 0; i < threads; i++) { | ||
128 | void *rc_p; | ||
129 | pthread_join(tids[i], &rc_p); | ||
130 | |||
131 | /* | ||
132 | * Harness will say the fail was here, look at why preempt_vsx | ||
133 | * returned | ||
134 | */ | ||
135 | if ((long) rc_p) | ||
136 | printf("oops\n"); | ||
137 | FAIL_IF((long) rc_p); | ||
138 | } | ||
139 | printf("done\n"); | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | int main(int argc, char *argv[]) | ||
145 | { | ||
146 | return test_harness(test_preempt_vsx, "vsx_preempt"); | ||
147 | } | ||
diff --git a/tools/testing/selftests/powerpc/signal/.gitignore b/tools/testing/selftests/powerpc/signal/.gitignore new file mode 100644 index 000000000000..1b89224a8aab --- /dev/null +++ b/tools/testing/selftests/powerpc/signal/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | signal | ||
2 | signal_tm | ||
diff --git a/tools/testing/selftests/powerpc/signal/Makefile b/tools/testing/selftests/powerpc/signal/Makefile new file mode 100644 index 000000000000..f0eef27458e2 --- /dev/null +++ b/tools/testing/selftests/powerpc/signal/Makefile | |||
@@ -0,0 +1,13 @@ | |||
1 | TEST_PROGS := signal signal_tm | ||
2 | |||
3 | all: $(TEST_PROGS) | ||
4 | |||
5 | $(TEST_PROGS): ../harness.c ../utils.c signal.S | ||
6 | |||
7 | CFLAGS += -maltivec | ||
8 | signal_tm: CFLAGS += -mhtm | ||
9 | |||
10 | include ../../lib.mk | ||
11 | |||
12 | clean: | ||
13 | rm -f $(TEST_PROGS) *.o | ||
diff --git a/tools/testing/selftests/powerpc/signal/signal.S b/tools/testing/selftests/powerpc/signal/signal.S new file mode 100644 index 000000000000..7043d521df0a --- /dev/null +++ b/tools/testing/selftests/powerpc/signal/signal.S | |||
@@ -0,0 +1,50 @@ | |||
1 | /* | ||
2 | * Copyright 2015, Cyril Bur, IBM Corp. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | */ | ||
9 | |||
10 | #include "../basic_asm.h" | ||
11 | |||
12 | /* long signal_self(pid_t pid, int sig); */ | ||
13 | FUNC_START(signal_self) | ||
14 | li r0,37 /* sys_kill */ | ||
15 | /* r3 already has our pid in it */ | ||
16 | /* r4 already has signal type in it */ | ||
17 | sc | ||
18 | bc 4,3,1f | ||
19 | subfze r3,r3 | ||
20 | 1: blr | ||
21 | FUNC_END(signal_self) | ||
22 | |||
23 | /* long tm_signal_self(pid_t pid, int sig, int *ret); */ | ||
24 | FUNC_START(tm_signal_self) | ||
25 | PUSH_BASIC_STACK(8) | ||
26 | std r5,STACK_FRAME_PARAM(0)(sp) /* ret */ | ||
27 | tbegin. | ||
28 | beq 1f | ||
29 | tsuspend. | ||
30 | li r0,37 /* sys_kill */ | ||
31 | /* r3 already has our pid in it */ | ||
32 | /* r4 already has signal type in it */ | ||
33 | sc | ||
34 | ld r5,STACK_FRAME_PARAM(0)(sp) /* ret */ | ||
35 | bc 4,3,2f | ||
36 | subfze r3,r3 | ||
37 | 2: std r3,0(r5) | ||
38 | tabort. 0 | ||
39 | tresume. /* Be nice to some cleanup, jumps back to tbegin then to 1: */ | ||
40 | /* | ||
41 | * Transaction should be proper doomed and we should never get | ||
42 | * here | ||
43 | */ | ||
44 | li r3,1 | ||
45 | POP_BASIC_STACK(8) | ||
46 | blr | ||
47 | 1: li r3,0 | ||
48 | POP_BASIC_STACK(8) | ||
49 | blr | ||
50 | FUNC_END(tm_signal_self) | ||
diff --git a/tools/testing/selftests/powerpc/signal/signal.c b/tools/testing/selftests/powerpc/signal/signal.c new file mode 100644 index 000000000000..e7dedd28b3c2 --- /dev/null +++ b/tools/testing/selftests/powerpc/signal/signal.c | |||
@@ -0,0 +1,111 @@ | |||
1 | /* | ||
2 | * Copyright 2016, Cyril Bur, IBM Corp. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | * | ||
9 | * Sending one self a signal should always get delivered. | ||
10 | */ | ||
11 | |||
12 | #include <signal.h> | ||
13 | #include <stdio.h> | ||
14 | #include <stdlib.h> | ||
15 | #include <string.h> | ||
16 | #include <sys/types.h> | ||
17 | #include <sys/wait.h> | ||
18 | #include <unistd.h> | ||
19 | |||
20 | #include <altivec.h> | ||
21 | |||
22 | #include "utils.h" | ||
23 | |||
24 | #define MAX_ATTEMPT 500000 | ||
25 | #define TIMEOUT 5 | ||
26 | |||
27 | extern long signal_self(pid_t pid, int sig); | ||
28 | |||
29 | static sig_atomic_t signaled; | ||
30 | static sig_atomic_t fail; | ||
31 | |||
32 | static void signal_handler(int sig) | ||
33 | { | ||
34 | if (sig == SIGUSR1) | ||
35 | signaled = 1; | ||
36 | else | ||
37 | fail = 1; | ||
38 | } | ||
39 | |||
40 | static int test_signal() | ||
41 | { | ||
42 | int i; | ||
43 | struct sigaction act; | ||
44 | pid_t ppid = getpid(); | ||
45 | pid_t pid; | ||
46 | |||
47 | act.sa_handler = signal_handler; | ||
48 | act.sa_flags = 0; | ||
49 | sigemptyset(&act.sa_mask); | ||
50 | if (sigaction(SIGUSR1, &act, NULL) < 0) { | ||
51 | perror("sigaction SIGUSR1"); | ||
52 | exit(1); | ||
53 | } | ||
54 | if (sigaction(SIGALRM, &act, NULL) < 0) { | ||
55 | perror("sigaction SIGALRM"); | ||
56 | exit(1); | ||
57 | } | ||
58 | |||
59 | /* Don't do this for MAX_ATTEMPT, its simply too long */ | ||
60 | for(i = 0; i < 1000; i++) { | ||
61 | pid = fork(); | ||
62 | if (pid == -1) { | ||
63 | perror("fork"); | ||
64 | exit(1); | ||
65 | } | ||
66 | if (pid == 0) { | ||
67 | signal_self(ppid, SIGUSR1); | ||
68 | exit(1); | ||
69 | } else { | ||
70 | alarm(0); /* Disable any pending */ | ||
71 | alarm(2); | ||
72 | while (!signaled && !fail) | ||
73 | asm volatile("": : :"memory"); | ||
74 | if (!signaled) { | ||
75 | fprintf(stderr, "Didn't get signal from child\n"); | ||
76 | FAIL_IF(1); /* For the line number */ | ||
77 | } | ||
78 | /* Otherwise we'll loop too fast and fork() will eventually fail */ | ||
79 | waitpid(pid, NULL, 0); | ||
80 | } | ||
81 | } | ||
82 | |||
83 | for (i = 0; i < MAX_ATTEMPT; i++) { | ||
84 | long rc; | ||
85 | |||
86 | alarm(0); /* Disable any pending */ | ||
87 | signaled = 0; | ||
88 | alarm(TIMEOUT); | ||
89 | rc = signal_self(ppid, SIGUSR1); | ||
90 | if (rc) { | ||
91 | fprintf(stderr, "(%d) Fail reason: %d rc=0x%lx", | ||
92 | i, fail, rc); | ||
93 | FAIL_IF(1); /* For the line number */ | ||
94 | } | ||
95 | while (!signaled && !fail) | ||
96 | asm volatile("": : :"memory"); | ||
97 | if (!signaled) { | ||
98 | fprintf(stderr, "(%d) Fail reason: %d rc=0x%lx", | ||
99 | i, fail, rc); | ||
100 | FAIL_IF(1); /* For the line number */ | ||
101 | } | ||
102 | } | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | int main(void) | ||
108 | { | ||
109 | test_harness_set_timeout(300); | ||
110 | return test_harness(test_signal, "signal"); | ||
111 | } | ||
diff --git a/tools/testing/selftests/powerpc/signal/signal_tm.c b/tools/testing/selftests/powerpc/signal/signal_tm.c new file mode 100644 index 000000000000..2e7451a37cc6 --- /dev/null +++ b/tools/testing/selftests/powerpc/signal/signal_tm.c | |||
@@ -0,0 +1,110 @@ | |||
1 | /* | ||
2 | * Copyright 2016, Cyril Bur, IBM Corp. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | * | ||
9 | * Sending one self a signal should always get delivered. | ||
10 | */ | ||
11 | |||
12 | #include <errno.h> | ||
13 | #include <stdlib.h> | ||
14 | #include <stdio.h> | ||
15 | #include <string.h> | ||
16 | #include <signal.h> | ||
17 | #include <unistd.h> | ||
18 | |||
19 | #include <altivec.h> | ||
20 | |||
21 | #include "utils.h" | ||
22 | #include "../tm/tm.h" | ||
23 | |||
24 | #define MAX_ATTEMPT 500000 | ||
25 | #define TIMEOUT 10 | ||
26 | |||
27 | extern long tm_signal_self(pid_t pid, int sig, long *ret); | ||
28 | |||
29 | static sig_atomic_t signaled; | ||
30 | static sig_atomic_t fail; | ||
31 | |||
32 | static void signal_handler(int sig) | ||
33 | { | ||
34 | if (tcheck_active()) { | ||
35 | fail = 2; | ||
36 | return; | ||
37 | } | ||
38 | |||
39 | if (sig == SIGUSR1) | ||
40 | signaled = 1; | ||
41 | else | ||
42 | fail = 1; | ||
43 | } | ||
44 | |||
45 | static int test_signal_tm() | ||
46 | { | ||
47 | int i; | ||
48 | struct sigaction act; | ||
49 | |||
50 | act.sa_handler = signal_handler; | ||
51 | act.sa_flags = 0; | ||
52 | sigemptyset(&act.sa_mask); | ||
53 | if (sigaction(SIGUSR1, &act, NULL) < 0) { | ||
54 | perror("sigaction SIGUSR1"); | ||
55 | exit(1); | ||
56 | } | ||
57 | if (sigaction(SIGALRM, &act, NULL) < 0) { | ||
58 | perror("sigaction SIGALRM"); | ||
59 | exit(1); | ||
60 | } | ||
61 | |||
62 | SKIP_IF(!have_htm()); | ||
63 | |||
64 | for (i = 0; i < MAX_ATTEMPT; i++) { | ||
65 | /* | ||
66 | * If anything bad happens in ASM and we fail to set ret | ||
67 | * because *handwave* TM this will cause failure | ||
68 | */ | ||
69 | long ret = 0xdead; | ||
70 | long rc = 0xbeef; | ||
71 | |||
72 | alarm(0); /* Disable any pending */ | ||
73 | signaled = 0; | ||
74 | alarm(TIMEOUT); | ||
75 | FAIL_IF(tcheck_transactional()); | ||
76 | rc = tm_signal_self(getpid(), SIGUSR1, &ret); | ||
77 | if (ret == 0xdead) | ||
78 | /* | ||
79 | * This basically means the transaction aborted before we | ||
80 | * even got to the suspend... this is crazy but it | ||
81 | * happens. | ||
82 | * Yes this also means we might never make forward | ||
83 | * progress... the alarm() will trip eventually... | ||
84 | */ | ||
85 | continue; | ||
86 | |||
87 | if (rc || ret) { | ||
88 | /* Ret is actually an errno */ | ||
89 | printf("TEXASR 0x%016lx, TFIAR 0x%016lx\n", | ||
90 | __builtin_get_texasr(), __builtin_get_tfiar()); | ||
91 | fprintf(stderr, "(%d) Fail reason: %d rc=0x%lx ret=0x%lx\n", | ||
92 | i, fail, rc, ret); | ||
93 | FAIL_IF(ret); | ||
94 | } | ||
95 | while(!signaled && !fail) | ||
96 | asm volatile("": : :"memory"); | ||
97 | if (!signaled) { | ||
98 | fprintf(stderr, "(%d) Fail reason: %d rc=0x%lx ret=0x%lx\n", | ||
99 | i, fail, rc, ret); | ||
100 | FAIL_IF(fail); /* For the line number */ | ||
101 | } | ||
102 | } | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | int main(void) | ||
108 | { | ||
109 | return test_harness(test_signal_tm, "signal_tm"); | ||
110 | } | ||
diff --git a/tools/testing/selftests/powerpc/stringloops/asm/export.h b/tools/testing/selftests/powerpc/stringloops/asm/export.h new file mode 100644 index 000000000000..2d14a9b4248c --- /dev/null +++ b/tools/testing/selftests/powerpc/stringloops/asm/export.h | |||
@@ -0,0 +1 @@ | |||
#define EXPORT_SYMBOL(x) | |||
diff --git a/tools/testing/selftests/powerpc/tm/.gitignore b/tools/testing/selftests/powerpc/tm/.gitignore index 82c0a9ce6e74..427621792229 100644 --- a/tools/testing/selftests/powerpc/tm/.gitignore +++ b/tools/testing/selftests/powerpc/tm/.gitignore | |||
@@ -7,3 +7,7 @@ tm-fork | |||
7 | tm-tar | 7 | tm-tar |
8 | tm-tmspr | 8 | tm-tmspr |
9 | tm-exec | 9 | tm-exec |
10 | tm-signal-context-chk-fpu | ||
11 | tm-signal-context-chk-gpr | ||
12 | tm-signal-context-chk-vmx | ||
13 | tm-signal-context-chk-vsx | ||
diff --git a/tools/testing/selftests/powerpc/tm/Makefile b/tools/testing/selftests/powerpc/tm/Makefile index 9d301d785d9e..c6c53c82fdd6 100644 --- a/tools/testing/selftests/powerpc/tm/Makefile +++ b/tools/testing/selftests/powerpc/tm/Makefile | |||
@@ -1,5 +1,8 @@ | |||
1 | SIGNAL_CONTEXT_CHK_TESTS := tm-signal-context-chk-gpr tm-signal-context-chk-fpu \ | ||
2 | tm-signal-context-chk-vmx tm-signal-context-chk-vsx | ||
3 | |||
1 | TEST_PROGS := tm-resched-dscr tm-syscall tm-signal-msr-resv tm-signal-stack \ | 4 | TEST_PROGS := tm-resched-dscr tm-syscall tm-signal-msr-resv tm-signal-stack \ |
2 | tm-vmxcopy tm-fork tm-tar tm-tmspr tm-exec tm-execed | 5 | tm-vmxcopy tm-fork tm-tar tm-tmspr $(SIGNAL_CONTEXT_CHK_TESTS) |
3 | 6 | ||
4 | all: $(TEST_PROGS) | 7 | all: $(TEST_PROGS) |
5 | 8 | ||
@@ -11,6 +14,9 @@ tm-syscall: tm-syscall-asm.S | |||
11 | tm-syscall: CFLAGS += -I../../../../../usr/include | 14 | tm-syscall: CFLAGS += -I../../../../../usr/include |
12 | tm-tmspr: CFLAGS += -pthread | 15 | tm-tmspr: CFLAGS += -pthread |
13 | 16 | ||
17 | $(SIGNAL_CONTEXT_CHK_TESTS): tm-signal.S | ||
18 | $(SIGNAL_CONTEXT_CHK_TESTS): CFLAGS += -mhtm -m64 -mvsx | ||
19 | |||
14 | include ../../lib.mk | 20 | include ../../lib.mk |
15 | 21 | ||
16 | clean: | 22 | clean: |
diff --git a/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-fpu.c b/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-fpu.c new file mode 100644 index 000000000000..c760debbd5ad --- /dev/null +++ b/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-fpu.c | |||
@@ -0,0 +1,92 @@ | |||
1 | /* | ||
2 | * Copyright 2016, Cyril Bur, IBM Corp. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | * | ||
9 | * | ||
10 | * Test the kernel's signal frame code. | ||
11 | * | ||
12 | * The kernel sets up two sets of ucontexts if the signal was to be | ||
13 | * delivered while the thread was in a transaction. | ||
14 | * Expected behaviour is that the checkpointed state is in the user | ||
15 | * context passed to the signal handler. The speculated state can be | ||
16 | * accessed with the uc_link pointer. | ||
17 | * | ||
18 | * The rationale for this is that if TM unaware code (which linked | ||
19 | * against TM libs) installs a signal handler it will not know of the | ||
20 | * speculative nature of the 'live' registers and may infer the wrong | ||
21 | * thing. | ||
22 | */ | ||
23 | |||
24 | #include <stdlib.h> | ||
25 | #include <stdio.h> | ||
26 | #include <signal.h> | ||
27 | #include <unistd.h> | ||
28 | |||
29 | #include <altivec.h> | ||
30 | |||
31 | #include "utils.h" | ||
32 | #include "tm.h" | ||
33 | |||
34 | #define MAX_ATTEMPT 500000 | ||
35 | |||
36 | #define NV_FPU_REGS 18 | ||
37 | |||
38 | long tm_signal_self_context_load(pid_t pid, long *gprs, double *fps, vector int *vms, vector int *vss); | ||
39 | |||
40 | /* Be sure there are 2x as many as there are NV FPU regs (2x18) */ | ||
41 | static double fps[] = { | ||
42 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, | ||
43 | -1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18 | ||
44 | }; | ||
45 | |||
46 | static sig_atomic_t fail; | ||
47 | |||
48 | static void signal_usr1(int signum, siginfo_t *info, void *uc) | ||
49 | { | ||
50 | int i; | ||
51 | ucontext_t *ucp = uc; | ||
52 | ucontext_t *tm_ucp = ucp->uc_link; | ||
53 | |||
54 | for (i = 0; i < NV_FPU_REGS && !fail; i++) { | ||
55 | fail = (ucp->uc_mcontext.fp_regs[i + 14] != fps[i]); | ||
56 | fail |= (tm_ucp->uc_mcontext.fp_regs[i + 14] != fps[i + NV_FPU_REGS]); | ||
57 | if (fail) | ||
58 | printf("Failed on %d FP %g or %g\n", i, ucp->uc_mcontext.fp_regs[i + 14], tm_ucp->uc_mcontext.fp_regs[i + 14]); | ||
59 | } | ||
60 | } | ||
61 | |||
62 | static int tm_signal_context_chk_fpu() | ||
63 | { | ||
64 | struct sigaction act; | ||
65 | int i; | ||
66 | long rc; | ||
67 | pid_t pid = getpid(); | ||
68 | |||
69 | SKIP_IF(!have_htm()); | ||
70 | |||
71 | act.sa_sigaction = signal_usr1; | ||
72 | sigemptyset(&act.sa_mask); | ||
73 | act.sa_flags = SA_SIGINFO; | ||
74 | if (sigaction(SIGUSR1, &act, NULL) < 0) { | ||
75 | perror("sigaction sigusr1"); | ||
76 | exit(1); | ||
77 | } | ||
78 | |||
79 | i = 0; | ||
80 | while (i < MAX_ATTEMPT && !fail) { | ||
81 | rc = tm_signal_self_context_load(pid, NULL, fps, NULL, NULL); | ||
82 | FAIL_IF(rc != pid); | ||
83 | i++; | ||
84 | } | ||
85 | |||
86 | return fail; | ||
87 | } | ||
88 | |||
89 | int main(void) | ||
90 | { | ||
91 | return test_harness(tm_signal_context_chk_fpu, "tm_signal_context_chk_fpu"); | ||
92 | } | ||
diff --git a/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-gpr.c b/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-gpr.c new file mode 100644 index 000000000000..df91330a08ef --- /dev/null +++ b/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-gpr.c | |||
@@ -0,0 +1,90 @@ | |||
1 | /* | ||
2 | * Copyright 2016, Cyril Bur, IBM Corp. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | * | ||
9 | * | ||
10 | * Test the kernel's signal frame code. | ||
11 | * | ||
12 | * The kernel sets up two sets of ucontexts if the signal was to be | ||
13 | * delivered while the thread was in a transaction. | ||
14 | * Expected behaviour is that the checkpointed state is in the user | ||
15 | * context passed to the signal handler. The speculated state can be | ||
16 | * accessed with the uc_link pointer. | ||
17 | * | ||
18 | * The rationale for this is that if TM unaware code (which linked | ||
19 | * against TM libs) installs a signal handler it will not know of the | ||
20 | * speculative nature of the 'live' registers and may infer the wrong | ||
21 | * thing. | ||
22 | */ | ||
23 | |||
24 | #include <stdlib.h> | ||
25 | #include <stdio.h> | ||
26 | #include <signal.h> | ||
27 | #include <unistd.h> | ||
28 | |||
29 | #include <altivec.h> | ||
30 | |||
31 | #include "utils.h" | ||
32 | #include "tm.h" | ||
33 | |||
34 | #define MAX_ATTEMPT 500000 | ||
35 | |||
36 | #define NV_GPR_REGS 18 | ||
37 | |||
38 | long tm_signal_self_context_load(pid_t pid, long *gprs, double *fps, vector int *vms, vector int *vss); | ||
39 | |||
40 | static sig_atomic_t fail; | ||
41 | |||
42 | static long gps[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, | ||
43 | -1,-2,-3,-4,-5,-6,-7,-8,-9,-10,-11,-12,-13,-14,-15,-16,-17,-18}; | ||
44 | |||
45 | static void signal_usr1(int signum, siginfo_t *info, void *uc) | ||
46 | { | ||
47 | int i; | ||
48 | ucontext_t *ucp = uc; | ||
49 | ucontext_t *tm_ucp = ucp->uc_link; | ||
50 | |||
51 | for (i = 0; i < NV_GPR_REGS && !fail; i++) { | ||
52 | fail = (ucp->uc_mcontext.gp_regs[i + 14] != gps[i]); | ||
53 | fail |= (tm_ucp->uc_mcontext.gp_regs[i + 14] != gps[i + NV_GPR_REGS]); | ||
54 | if (fail) | ||
55 | printf("Failed on %d GPR %lu or %lu\n", i, | ||
56 | ucp->uc_mcontext.gp_regs[i + 14], tm_ucp->uc_mcontext.gp_regs[i + 14]); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | static int tm_signal_context_chk_gpr() | ||
61 | { | ||
62 | struct sigaction act; | ||
63 | int i; | ||
64 | long rc; | ||
65 | pid_t pid = getpid(); | ||
66 | |||
67 | SKIP_IF(!have_htm()); | ||
68 | |||
69 | act.sa_sigaction = signal_usr1; | ||
70 | sigemptyset(&act.sa_mask); | ||
71 | act.sa_flags = SA_SIGINFO; | ||
72 | if (sigaction(SIGUSR1, &act, NULL) < 0) { | ||
73 | perror("sigaction sigusr1"); | ||
74 | exit(1); | ||
75 | } | ||
76 | |||
77 | i = 0; | ||
78 | while (i < MAX_ATTEMPT && !fail) { | ||
79 | rc = tm_signal_self_context_load(pid, gps, NULL, NULL, NULL); | ||
80 | FAIL_IF(rc != pid); | ||
81 | i++; | ||
82 | } | ||
83 | |||
84 | return fail; | ||
85 | } | ||
86 | |||
87 | int main(void) | ||
88 | { | ||
89 | return test_harness(tm_signal_context_chk_gpr, "tm_signal_context_chk_gpr"); | ||
90 | } | ||
diff --git a/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-vmx.c b/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-vmx.c new file mode 100644 index 000000000000..f0ee55fd5185 --- /dev/null +++ b/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-vmx.c | |||
@@ -0,0 +1,110 @@ | |||
1 | /* | ||
2 | * Copyright 2016, Cyril Bur, IBM Corp. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | * | ||
9 | * | ||
10 | * Test the kernel's signal frame code. | ||
11 | * | ||
12 | * The kernel sets up two sets of ucontexts if the signal was to be | ||
13 | * delivered while the thread was in a transaction. | ||
14 | * Expected behaviour is that the checkpointed state is in the user | ||
15 | * context passed to the signal handler. The speculated state can be | ||
16 | * accessed with the uc_link pointer. | ||
17 | * | ||
18 | * The rationale for this is that if TM unaware code (which linked | ||
19 | * against TM libs) installs a signal handler it will not know of the | ||
20 | * speculative nature of the 'live' registers and may infer the wrong | ||
21 | * thing. | ||
22 | */ | ||
23 | |||
24 | #include <stdlib.h> | ||
25 | #include <stdio.h> | ||
26 | #include <string.h> | ||
27 | #include <signal.h> | ||
28 | #include <unistd.h> | ||
29 | |||
30 | #include <altivec.h> | ||
31 | |||
32 | #include "utils.h" | ||
33 | #include "tm.h" | ||
34 | |||
35 | #define MAX_ATTEMPT 500000 | ||
36 | |||
37 | #define NV_VMX_REGS 12 | ||
38 | |||
39 | long tm_signal_self_context_load(pid_t pid, long *gprs, double *fps, vector int *vms, vector int *vss); | ||
40 | |||
41 | static sig_atomic_t fail; | ||
42 | |||
43 | vector int vms[] = { | ||
44 | {1, 2, 3, 4 },{5, 6, 7, 8 },{9, 10,11,12}, | ||
45 | {13,14,15,16},{17,18,19,20},{21,22,23,24}, | ||
46 | {25,26,27,28},{29,30,31,32},{33,34,35,36}, | ||
47 | {37,38,39,40},{41,42,43,44},{45,46,47,48}, | ||
48 | {-1, -2, -3, -4}, {-5, -6, -7, -8}, {-9, -10,-11,-12}, | ||
49 | {-13,-14,-15,-16},{-17,-18,-19,-20},{-21,-22,-23,-24}, | ||
50 | {-25,-26,-27,-28},{-29,-30,-31,-32},{-33,-34,-35,-36}, | ||
51 | {-37,-38,-39,-40},{-41,-42,-43,-44},{-45,-46,-47,-48} | ||
52 | }; | ||
53 | |||
54 | static void signal_usr1(int signum, siginfo_t *info, void *uc) | ||
55 | { | ||
56 | int i; | ||
57 | ucontext_t *ucp = uc; | ||
58 | ucontext_t *tm_ucp = ucp->uc_link; | ||
59 | |||
60 | for (i = 0; i < NV_VMX_REGS && !fail; i++) { | ||
61 | fail = memcmp(ucp->uc_mcontext.v_regs->vrregs[i + 20], | ||
62 | &vms[i], sizeof(vector int)); | ||
63 | fail |= memcmp(tm_ucp->uc_mcontext.v_regs->vrregs[i + 20], | ||
64 | &vms[i + NV_VMX_REGS], sizeof (vector int)); | ||
65 | |||
66 | if (fail) { | ||
67 | int j; | ||
68 | |||
69 | fprintf(stderr, "Failed on %d vmx 0x", i); | ||
70 | for (j = 0; j < 4; j++) | ||
71 | fprintf(stderr, "%04x", ucp->uc_mcontext.v_regs->vrregs[i + 20][j]); | ||
72 | fprintf(stderr, " vs 0x"); | ||
73 | for (j = 0 ; j < 4; j++) | ||
74 | fprintf(stderr, "%04x", tm_ucp->uc_mcontext.v_regs->vrregs[i + 20][j]); | ||
75 | fprintf(stderr, "\n"); | ||
76 | } | ||
77 | } | ||
78 | } | ||
79 | |||
80 | static int tm_signal_context_chk() | ||
81 | { | ||
82 | struct sigaction act; | ||
83 | int i; | ||
84 | long rc; | ||
85 | pid_t pid = getpid(); | ||
86 | |||
87 | SKIP_IF(!have_htm()); | ||
88 | |||
89 | act.sa_sigaction = signal_usr1; | ||
90 | sigemptyset(&act.sa_mask); | ||
91 | act.sa_flags = SA_SIGINFO; | ||
92 | if (sigaction(SIGUSR1, &act, NULL) < 0) { | ||
93 | perror("sigaction sigusr1"); | ||
94 | exit(1); | ||
95 | } | ||
96 | |||
97 | i = 0; | ||
98 | while (i < MAX_ATTEMPT && !fail) { | ||
99 | rc = tm_signal_self_context_load(pid, NULL, NULL, vms, NULL); | ||
100 | FAIL_IF(rc != pid); | ||
101 | i++; | ||
102 | } | ||
103 | |||
104 | return fail; | ||
105 | } | ||
106 | |||
107 | int main(void) | ||
108 | { | ||
109 | return test_harness(tm_signal_context_chk, "tm_signal_context_chk_vmx"); | ||
110 | } | ||
diff --git a/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-vsx.c b/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-vsx.c new file mode 100644 index 000000000000..b99c3d835957 --- /dev/null +++ b/tools/testing/selftests/powerpc/tm/tm-signal-context-chk-vsx.c | |||
@@ -0,0 +1,125 @@ | |||
1 | /* | ||
2 | * Copyright 2016, Cyril Bur, IBM Corp. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | * | ||
9 | * | ||
10 | * Test the kernel's signal frame code. | ||
11 | * | ||
12 | * The kernel sets up two sets of ucontexts if the signal was to be | ||
13 | * delivered while the thread was in a transaction. | ||
14 | * Expected behaviour is that the checkpointed state is in the user | ||
15 | * context passed to the signal handler. The speculated state can be | ||
16 | * accessed with the uc_link pointer. | ||
17 | * | ||
18 | * The rationale for this is that if TM unaware code (which linked | ||
19 | * against TM libs) installs a signal handler it will not know of the | ||
20 | * speculative nature of the 'live' registers and may infer the wrong | ||
21 | * thing. | ||
22 | */ | ||
23 | |||
24 | #include <stdlib.h> | ||
25 | #include <stdio.h> | ||
26 | #include <string.h> | ||
27 | #include <signal.h> | ||
28 | #include <unistd.h> | ||
29 | |||
30 | #include <altivec.h> | ||
31 | |||
32 | #include "utils.h" | ||
33 | #include "tm.h" | ||
34 | |||
35 | #define MAX_ATTEMPT 500000 | ||
36 | |||
37 | #define NV_VSX_REGS 12 | ||
38 | |||
39 | long tm_signal_self_context_load(pid_t pid, long *gprs, double *fps, vector int *vms, vector int *vss); | ||
40 | |||
41 | static sig_atomic_t fail; | ||
42 | |||
43 | vector int vss[] = { | ||
44 | {1, 2, 3, 4 },{5, 6, 7, 8 },{9, 10,11,12}, | ||
45 | {13,14,15,16},{17,18,19,20},{21,22,23,24}, | ||
46 | {25,26,27,28},{29,30,31,32},{33,34,35,36}, | ||
47 | {37,38,39,40},{41,42,43,44},{45,46,47,48}, | ||
48 | {-1, -2, -3, -4 },{-5, -6, -7, -8 },{-9, -10,-11,-12}, | ||
49 | {-13,-14,-15,-16},{-17,-18,-19,-20},{-21,-22,-23,-24}, | ||
50 | {-25,-26,-27,-28},{-29,-30,-31,-32},{-33,-34,-35,-36}, | ||
51 | {-37,-38,-39,-40},{-41,-42,-43,-44},{-45,-46,-47,-48} | ||
52 | }; | ||
53 | |||
54 | static void signal_usr1(int signum, siginfo_t *info, void *uc) | ||
55 | { | ||
56 | int i; | ||
57 | uint8_t vsc[sizeof(vector int)]; | ||
58 | uint8_t vst[sizeof(vector int)]; | ||
59 | ucontext_t *ucp = uc; | ||
60 | ucontext_t *tm_ucp = ucp->uc_link; | ||
61 | |||
62 | /* | ||
63 | * The other half of the VSX regs will be after v_regs. | ||
64 | * | ||
65 | * In short, vmx_reserve array holds everything. v_regs is a 16 | ||
66 | * byte aligned pointer at the start of vmx_reserve (vmx_reserve | ||
67 | * may or may not be 16 aligned) where the v_regs structure exists. | ||
68 | * (half of) The VSX regsters are directly after v_regs so the | ||
69 | * easiest way to find them below. | ||
70 | */ | ||
71 | long *vsx_ptr = (long *)(ucp->uc_mcontext.v_regs + 1); | ||
72 | long *tm_vsx_ptr = (long *)(tm_ucp->uc_mcontext.v_regs + 1); | ||
73 | for (i = 0; i < NV_VSX_REGS && !fail; i++) { | ||
74 | memcpy(vsc, &ucp->uc_mcontext.fp_regs[i + 20], 8); | ||
75 | memcpy(vsc + 8, &vsx_ptr[20 + i], 8); | ||
76 | fail = memcmp(vsc, &vss[i], sizeof(vector int)); | ||
77 | memcpy(vst, &tm_ucp->uc_mcontext.fp_regs[i + 20], 8); | ||
78 | memcpy(vst + 8, &tm_vsx_ptr[20 + i], 8); | ||
79 | fail |= memcmp(vst, &vss[i + NV_VSX_REGS], sizeof(vector int)); | ||
80 | |||
81 | if (fail) { | ||
82 | int j; | ||
83 | |||
84 | fprintf(stderr, "Failed on %d vsx 0x", i); | ||
85 | for (j = 0; j < 16; j++) | ||
86 | fprintf(stderr, "%02x", vsc[j]); | ||
87 | fprintf(stderr, " vs 0x"); | ||
88 | for (j = 0; j < 16; j++) | ||
89 | fprintf(stderr, "%02x", vst[j]); | ||
90 | fprintf(stderr, "\n"); | ||
91 | } | ||
92 | } | ||
93 | } | ||
94 | |||
95 | static int tm_signal_context_chk() | ||
96 | { | ||
97 | struct sigaction act; | ||
98 | int i; | ||
99 | long rc; | ||
100 | pid_t pid = getpid(); | ||
101 | |||
102 | SKIP_IF(!have_htm()); | ||
103 | |||
104 | act.sa_sigaction = signal_usr1; | ||
105 | sigemptyset(&act.sa_mask); | ||
106 | act.sa_flags = SA_SIGINFO; | ||
107 | if (sigaction(SIGUSR1, &act, NULL) < 0) { | ||
108 | perror("sigaction sigusr1"); | ||
109 | exit(1); | ||
110 | } | ||
111 | |||
112 | i = 0; | ||
113 | while (i < MAX_ATTEMPT && !fail) { | ||
114 | rc = tm_signal_self_context_load(pid, NULL, NULL, NULL, vss); | ||
115 | FAIL_IF(rc != pid); | ||
116 | i++; | ||
117 | } | ||
118 | |||
119 | return fail; | ||
120 | } | ||
121 | |||
122 | int main(void) | ||
123 | { | ||
124 | return test_harness(tm_signal_context_chk, "tm_signal_context_chk_vsx"); | ||
125 | } | ||
diff --git a/tools/testing/selftests/powerpc/tm/tm-signal.S b/tools/testing/selftests/powerpc/tm/tm-signal.S new file mode 100644 index 000000000000..4e13e8b3a96f --- /dev/null +++ b/tools/testing/selftests/powerpc/tm/tm-signal.S | |||
@@ -0,0 +1,114 @@ | |||
1 | /* | ||
2 | * Copyright 2015, Cyril Bur, IBM Corp. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | */ | ||
9 | |||
10 | #include "../basic_asm.h" | ||
11 | #include "../gpr_asm.h" | ||
12 | #include "../fpu_asm.h" | ||
13 | #include "../vmx_asm.h" | ||
14 | #include "../vsx_asm.h" | ||
15 | |||
16 | /* | ||
17 | * Large caveat here being that the caller cannot expect the | ||
18 | * signal to always be sent! The hardware can (AND WILL!) abort | ||
19 | * the transaction between the tbegin and the tsuspend (however | ||
20 | * unlikely it seems or infrequently it actually happens). | ||
21 | * You have been warned. | ||
22 | */ | ||
23 | /* long tm_signal_self(pid_t pid, long *gprs, double *fps, vector *vms, vector *vss); */ | ||
24 | FUNC_START(tm_signal_self_context_load) | ||
25 | PUSH_BASIC_STACK(512) | ||
26 | /* | ||
27 | * Don't strictly need to save and restore as it depends on if | ||
28 | * we're going to use them, however this reduces messy logic | ||
29 | */ | ||
30 | PUSH_VMX(STACK_FRAME_LOCAL(5,0),r8) | ||
31 | PUSH_FPU(512) | ||
32 | PUSH_NVREGS_BELOW_FPU(512) | ||
33 | std r3, STACK_FRAME_PARAM(0)(sp) /* pid */ | ||
34 | std r4, STACK_FRAME_PARAM(1)(sp) /* gps */ | ||
35 | std r5, STACK_FRAME_PARAM(2)(sp) /* fps */ | ||
36 | std r6, STACK_FRAME_PARAM(3)(sp) /* vms */ | ||
37 | std r7, STACK_FRAME_PARAM(4)(sp) /* vss */ | ||
38 | |||
39 | ld r3, STACK_FRAME_PARAM(1)(sp) | ||
40 | cmpdi r3, 0 | ||
41 | beq skip_gpr_lc | ||
42 | bl load_gpr | ||
43 | skip_gpr_lc: | ||
44 | ld r3, STACK_FRAME_PARAM(2)(sp) | ||
45 | cmpdi r3, 0 | ||
46 | beq skip_fpu_lc | ||
47 | bl load_fpu | ||
48 | skip_fpu_lc: | ||
49 | ld r3, STACK_FRAME_PARAM(3)(sp) | ||
50 | cmpdi r3, 0 | ||
51 | beq skip_vmx_lc | ||
52 | bl load_vmx | ||
53 | skip_vmx_lc: | ||
54 | ld r3, STACK_FRAME_PARAM(4)(sp) | ||
55 | cmpdi r3, 0 | ||
56 | beq skip_vsx_lc | ||
57 | bl load_vsx | ||
58 | skip_vsx_lc: | ||
59 | /* | ||
60 | * Set r3 (return value) before tbegin. Use the pid as a known | ||
61 | * 'all good' return value, zero is used to indicate a non-doomed | ||
62 | * transaction. | ||
63 | */ | ||
64 | ld r3, STACK_FRAME_PARAM(0)(sp) | ||
65 | tbegin. | ||
66 | beq 1f | ||
67 | tsuspend. /* Can't enter a syscall transactionally */ | ||
68 | ld r3, STACK_FRAME_PARAM(1)(sp) | ||
69 | cmpdi r3, 0 | ||
70 | beq skip_gpr_lt | ||
71 | /* Get the second half of the array */ | ||
72 | addi r3, r3, 8 * 18 | ||
73 | bl load_gpr | ||
74 | skip_gpr_lt: | ||
75 | ld r3, STACK_FRAME_PARAM(2)(sp) | ||
76 | cmpdi r3, 0 | ||
77 | beq skip_fpu_lt | ||
78 | /* Get the second half of the array */ | ||
79 | addi r3, r3, 8 * 18 | ||
80 | bl load_fpu | ||
81 | skip_fpu_lt: | ||
82 | ld r3, STACK_FRAME_PARAM(3)(sp) | ||
83 | cmpdi r3, 0 | ||
84 | beq skip_vmx_lt | ||
85 | /* Get the second half of the array */ | ||
86 | addi r3, r3, 16 * 12 | ||
87 | bl load_vmx | ||
88 | skip_vmx_lt: | ||
89 | ld r3, STACK_FRAME_PARAM(4)(sp) | ||
90 | cmpdi r3, 0 | ||
91 | beq skip_vsx_lt | ||
92 | /* Get the second half of the array */ | ||
93 | addi r3, r3, 16 * 12 | ||
94 | bl load_vsx | ||
95 | skip_vsx_lt: | ||
96 | li r0, 37 /* sys_kill */ | ||
97 | ld r3, STACK_FRAME_PARAM(0)(sp) /* pid */ | ||
98 | li r4, 10 /* SIGUSR1 */ | ||
99 | sc /* Taking the signal will doom the transaction */ | ||
100 | tabort. 0 | ||
101 | tresume. /* Be super sure we abort */ | ||
102 | /* | ||
103 | * This will cause us to resume doomed transaction and cause | ||
104 | * hardware to cleanup, we'll end up at 1: anything between | ||
105 | * tresume. and 1: shouldn't ever run. | ||
106 | */ | ||
107 | li r3, 0 | ||
108 | 1: | ||
109 | POP_VMX(STACK_FRAME_LOCAL(5,0),r4) | ||
110 | POP_FPU(512) | ||
111 | POP_NVREGS_BELOW_FPU(512) | ||
112 | POP_BASIC_STACK(512) | ||
113 | blr | ||
114 | FUNC_END(tm_signal_self_context_load) | ||
diff --git a/tools/testing/selftests/powerpc/tm/tm.h b/tools/testing/selftests/powerpc/tm/tm.h index 60318bad7d7a..2c8da74304e7 100644 --- a/tools/testing/selftests/powerpc/tm/tm.h +++ b/tools/testing/selftests/powerpc/tm/tm.h | |||
@@ -52,4 +52,31 @@ static inline bool failure_is_nesting(void) | |||
52 | return (__builtin_get_texasru() & 0x400000); | 52 | return (__builtin_get_texasru() & 0x400000); |
53 | } | 53 | } |
54 | 54 | ||
55 | static inline int tcheck(void) | ||
56 | { | ||
57 | long cr; | ||
58 | asm volatile ("tcheck 0" : "=r"(cr) : : "cr0"); | ||
59 | return (cr >> 28) & 4; | ||
60 | } | ||
61 | |||
62 | static inline bool tcheck_doomed(void) | ||
63 | { | ||
64 | return tcheck() & 8; | ||
65 | } | ||
66 | |||
67 | static inline bool tcheck_active(void) | ||
68 | { | ||
69 | return tcheck() & 4; | ||
70 | } | ||
71 | |||
72 | static inline bool tcheck_suspended(void) | ||
73 | { | ||
74 | return tcheck() & 2; | ||
75 | } | ||
76 | |||
77 | static inline bool tcheck_transactional(void) | ||
78 | { | ||
79 | return tcheck() & 6; | ||
80 | } | ||
81 | |||
55 | #endif /* _SELFTESTS_POWERPC_TM_TM_H */ | 82 | #endif /* _SELFTESTS_POWERPC_TM_TM_H */ |
diff --git a/tools/testing/selftests/powerpc/utils.h b/tools/testing/selftests/powerpc/utils.h index fbd33e52ef8f..53405e8a52ab 100644 --- a/tools/testing/selftests/powerpc/utils.h +++ b/tools/testing/selftests/powerpc/utils.h | |||
@@ -22,7 +22,7 @@ typedef uint32_t u32; | |||
22 | typedef uint16_t u16; | 22 | typedef uint16_t u16; |
23 | typedef uint8_t u8; | 23 | typedef uint8_t u8; |
24 | 24 | ||
25 | 25 | void test_harness_set_timeout(uint64_t time); | |
26 | int test_harness(int (test_function)(void), char *name); | 26 | int test_harness(int (test_function)(void), char *name); |
27 | extern void *get_auxv_entry(int type); | 27 | extern void *get_auxv_entry(int type); |
28 | int pick_online_cpu(void); | 28 | int pick_online_cpu(void); |
@@ -32,10 +32,17 @@ static inline bool have_hwcap(unsigned long ftr) | |||
32 | return ((unsigned long)get_auxv_entry(AT_HWCAP) & ftr) == ftr; | 32 | return ((unsigned long)get_auxv_entry(AT_HWCAP) & ftr) == ftr; |
33 | } | 33 | } |
34 | 34 | ||
35 | #ifdef AT_HWCAP2 | ||
35 | static inline bool have_hwcap2(unsigned long ftr2) | 36 | static inline bool have_hwcap2(unsigned long ftr2) |
36 | { | 37 | { |
37 | return ((unsigned long)get_auxv_entry(AT_HWCAP2) & ftr2) == ftr2; | 38 | return ((unsigned long)get_auxv_entry(AT_HWCAP2) & ftr2) == ftr2; |
38 | } | 39 | } |
40 | #else | ||
41 | static inline bool have_hwcap2(unsigned long ftr2) | ||
42 | { | ||
43 | return false; | ||
44 | } | ||
45 | #endif | ||
39 | 46 | ||
40 | /* Yes, this is evil */ | 47 | /* Yes, this is evil */ |
41 | #define FAIL_IF(x) \ | 48 | #define FAIL_IF(x) \ |
diff --git a/tools/testing/selftests/powerpc/vmx_asm.h b/tools/testing/selftests/powerpc/vmx_asm.h new file mode 100644 index 000000000000..2eaaeca9cf1d --- /dev/null +++ b/tools/testing/selftests/powerpc/vmx_asm.h | |||
@@ -0,0 +1,96 @@ | |||
1 | /* | ||
2 | * Copyright 2015, Cyril Bur, IBM Corp. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | */ | ||
9 | |||
10 | #include "basic_asm.h" | ||
11 | |||
12 | /* POS MUST BE 16 ALIGNED! */ | ||
13 | #define PUSH_VMX(pos,reg) \ | ||
14 | li reg,pos; \ | ||
15 | stvx v20,reg,%r1; \ | ||
16 | addi reg,reg,16; \ | ||
17 | stvx v21,reg,%r1; \ | ||
18 | addi reg,reg,16; \ | ||
19 | stvx v22,reg,%r1; \ | ||
20 | addi reg,reg,16; \ | ||
21 | stvx v23,reg,%r1; \ | ||
22 | addi reg,reg,16; \ | ||
23 | stvx v24,reg,%r1; \ | ||
24 | addi reg,reg,16; \ | ||
25 | stvx v25,reg,%r1; \ | ||
26 | addi reg,reg,16; \ | ||
27 | stvx v26,reg,%r1; \ | ||
28 | addi reg,reg,16; \ | ||
29 | stvx v27,reg,%r1; \ | ||
30 | addi reg,reg,16; \ | ||
31 | stvx v28,reg,%r1; \ | ||
32 | addi reg,reg,16; \ | ||
33 | stvx v29,reg,%r1; \ | ||
34 | addi reg,reg,16; \ | ||
35 | stvx v30,reg,%r1; \ | ||
36 | addi reg,reg,16; \ | ||
37 | stvx v31,reg,%r1; | ||
38 | |||
39 | /* POS MUST BE 16 ALIGNED! */ | ||
40 | #define POP_VMX(pos,reg) \ | ||
41 | li reg,pos; \ | ||
42 | lvx v20,reg,%r1; \ | ||
43 | addi reg,reg,16; \ | ||
44 | lvx v21,reg,%r1; \ | ||
45 | addi reg,reg,16; \ | ||
46 | lvx v22,reg,%r1; \ | ||
47 | addi reg,reg,16; \ | ||
48 | lvx v23,reg,%r1; \ | ||
49 | addi reg,reg,16; \ | ||
50 | lvx v24,reg,%r1; \ | ||
51 | addi reg,reg,16; \ | ||
52 | lvx v25,reg,%r1; \ | ||
53 | addi reg,reg,16; \ | ||
54 | lvx v26,reg,%r1; \ | ||
55 | addi reg,reg,16; \ | ||
56 | lvx v27,reg,%r1; \ | ||
57 | addi reg,reg,16; \ | ||
58 | lvx v28,reg,%r1; \ | ||
59 | addi reg,reg,16; \ | ||
60 | lvx v29,reg,%r1; \ | ||
61 | addi reg,reg,16; \ | ||
62 | lvx v30,reg,%r1; \ | ||
63 | addi reg,reg,16; \ | ||
64 | lvx v31,reg,%r1; | ||
65 | |||
66 | /* | ||
67 | * Careful this will 'clobber' vmx (by design) | ||
68 | * Don't call this from C | ||
69 | */ | ||
70 | FUNC_START(load_vmx) | ||
71 | li r5,0 | ||
72 | lvx v20,r5,r3 | ||
73 | addi r5,r5,16 | ||
74 | lvx v21,r5,r3 | ||
75 | addi r5,r5,16 | ||
76 | lvx v22,r5,r3 | ||
77 | addi r5,r5,16 | ||
78 | lvx v23,r5,r3 | ||
79 | addi r5,r5,16 | ||
80 | lvx v24,r5,r3 | ||
81 | addi r5,r5,16 | ||
82 | lvx v25,r5,r3 | ||
83 | addi r5,r5,16 | ||
84 | lvx v26,r5,r3 | ||
85 | addi r5,r5,16 | ||
86 | lvx v27,r5,r3 | ||
87 | addi r5,r5,16 | ||
88 | lvx v28,r5,r3 | ||
89 | addi r5,r5,16 | ||
90 | lvx v29,r5,r3 | ||
91 | addi r5,r5,16 | ||
92 | lvx v30,r5,r3 | ||
93 | addi r5,r5,16 | ||
94 | lvx v31,r5,r3 | ||
95 | blr | ||
96 | FUNC_END(load_vmx) | ||
diff --git a/tools/testing/selftests/powerpc/vsx_asm.h b/tools/testing/selftests/powerpc/vsx_asm.h new file mode 100644 index 000000000000..d828bfb6ef2d --- /dev/null +++ b/tools/testing/selftests/powerpc/vsx_asm.h | |||
@@ -0,0 +1,71 @@ | |||
1 | /* | ||
2 | * Copyright 2015, Cyril Bur, IBM Corp. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | */ | ||
9 | |||
10 | #include "basic_asm.h" | ||
11 | |||
12 | /* | ||
13 | * Careful this will 'clobber' vsx (by design), VSX are always | ||
14 | * volatile though so unlike vmx this isn't so much of an issue | ||
15 | * Still should avoid calling from C | ||
16 | */ | ||
17 | FUNC_START(load_vsx) | ||
18 | li r5,0 | ||
19 | lxvx vs20,r5,r3 | ||
20 | addi r5,r5,16 | ||
21 | lxvx vs21,r5,r3 | ||
22 | addi r5,r5,16 | ||
23 | lxvx vs22,r5,r3 | ||
24 | addi r5,r5,16 | ||
25 | lxvx vs23,r5,r3 | ||
26 | addi r5,r5,16 | ||
27 | lxvx vs24,r5,r3 | ||
28 | addi r5,r5,16 | ||
29 | lxvx vs25,r5,r3 | ||
30 | addi r5,r5,16 | ||
31 | lxvx vs26,r5,r3 | ||
32 | addi r5,r5,16 | ||
33 | lxvx vs27,r5,r3 | ||
34 | addi r5,r5,16 | ||
35 | lxvx vs28,r5,r3 | ||
36 | addi r5,r5,16 | ||
37 | lxvx vs29,r5,r3 | ||
38 | addi r5,r5,16 | ||
39 | lxvx vs30,r5,r3 | ||
40 | addi r5,r5,16 | ||
41 | lxvx vs31,r5,r3 | ||
42 | blr | ||
43 | FUNC_END(load_vsx) | ||
44 | |||
45 | FUNC_START(store_vsx) | ||
46 | li r5,0 | ||
47 | stxvx vs20,r5,r3 | ||
48 | addi r5,r5,16 | ||
49 | stxvx vs21,r5,r3 | ||
50 | addi r5,r5,16 | ||
51 | stxvx vs22,r5,r3 | ||
52 | addi r5,r5,16 | ||
53 | stxvx vs23,r5,r3 | ||
54 | addi r5,r5,16 | ||
55 | stxvx vs24,r5,r3 | ||
56 | addi r5,r5,16 | ||
57 | stxvx vs25,r5,r3 | ||
58 | addi r5,r5,16 | ||
59 | stxvx vs26,r5,r3 | ||
60 | addi r5,r5,16 | ||
61 | stxvx vs27,r5,r3 | ||
62 | addi r5,r5,16 | ||
63 | stxvx vs28,r5,r3 | ||
64 | addi r5,r5,16 | ||
65 | stxvx vs29,r5,r3 | ||
66 | addi r5,r5,16 | ||
67 | stxvx vs30,r5,r3 | ||
68 | addi r5,r5,16 | ||
69 | stxvx vs31,r5,r3 | ||
70 | blr | ||
71 | FUNC_END(store_vsx) | ||
diff --git a/tools/testing/selftests/prctl/.gitignore b/tools/testing/selftests/prctl/.gitignore new file mode 100644 index 000000000000..0b5c27447bf6 --- /dev/null +++ b/tools/testing/selftests/prctl/.gitignore | |||
@@ -0,0 +1,3 @@ | |||
1 | disable-tsc-ctxt-sw-stress-test | ||
2 | disable-tsc-on-off-stress-test | ||
3 | disable-tsc-test | ||
diff --git a/tools/testing/selftests/prctl/Makefile b/tools/testing/selftests/prctl/Makefile new file mode 100644 index 000000000000..35aa1c8f2df2 --- /dev/null +++ b/tools/testing/selftests/prctl/Makefile | |||
@@ -0,0 +1,15 @@ | |||
1 | ifndef CROSS_COMPILE | ||
2 | uname_M := $(shell uname -m 2>/dev/null || echo not) | ||
3 | ARCH ?= $(shell echo $(uname_M) | sed -e s/i.86/x86/ -e s/x86_64/x86/) | ||
4 | |||
5 | ifeq ($(ARCH),x86) | ||
6 | TEST_PROGS := disable-tsc-ctxt-sw-stress-test disable-tsc-on-off-stress-test \ | ||
7 | disable-tsc-test | ||
8 | all: $(TEST_PROGS) | ||
9 | |||
10 | include ../lib.mk | ||
11 | |||
12 | clean: | ||
13 | rm -fr $(TEST_PROGS) | ||
14 | endif | ||
15 | endif | ||
diff --git a/tools/testing/selftests/prctl/disable-tsc-ctxt-sw-stress-test.c b/tools/testing/selftests/prctl/disable-tsc-ctxt-sw-stress-test.c new file mode 100644 index 000000000000..f7499d1c0415 --- /dev/null +++ b/tools/testing/selftests/prctl/disable-tsc-ctxt-sw-stress-test.c | |||
@@ -0,0 +1,97 @@ | |||
1 | /* | ||
2 | * Tests for prctl(PR_GET_TSC, ...) / prctl(PR_SET_TSC, ...) | ||
3 | * | ||
4 | * Tests if the control register is updated correctly | ||
5 | * at context switches | ||
6 | * | ||
7 | * Warning: this test will cause a very high load for a few seconds | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <stdio.h> | ||
12 | #include <stdlib.h> | ||
13 | #include <unistd.h> | ||
14 | #include <signal.h> | ||
15 | #include <inttypes.h> | ||
16 | #include <wait.h> | ||
17 | |||
18 | |||
19 | #include <sys/prctl.h> | ||
20 | #include <linux/prctl.h> | ||
21 | |||
22 | /* Get/set the process' ability to use the timestamp counter instruction */ | ||
23 | #ifndef PR_GET_TSC | ||
24 | #define PR_GET_TSC 25 | ||
25 | #define PR_SET_TSC 26 | ||
26 | # define PR_TSC_ENABLE 1 /* allow the use of the timestamp counter */ | ||
27 | # define PR_TSC_SIGSEGV 2 /* throw a SIGSEGV instead of reading the TSC */ | ||
28 | #endif | ||
29 | |||
30 | static uint64_t rdtsc(void) | ||
31 | { | ||
32 | uint32_t lo, hi; | ||
33 | /* We cannot use "=A", since this would use %rax on x86_64 */ | ||
34 | __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); | ||
35 | return (uint64_t)hi << 32 | lo; | ||
36 | } | ||
37 | |||
38 | static void sigsegv_expect(int sig) | ||
39 | { | ||
40 | /* */ | ||
41 | } | ||
42 | |||
43 | static void segvtask(void) | ||
44 | { | ||
45 | if (prctl(PR_SET_TSC, PR_TSC_SIGSEGV) < 0) | ||
46 | { | ||
47 | perror("prctl"); | ||
48 | exit(0); | ||
49 | } | ||
50 | signal(SIGSEGV, sigsegv_expect); | ||
51 | alarm(10); | ||
52 | rdtsc(); | ||
53 | fprintf(stderr, "FATAL ERROR, rdtsc() succeeded while disabled\n"); | ||
54 | exit(0); | ||
55 | } | ||
56 | |||
57 | |||
58 | static void sigsegv_fail(int sig) | ||
59 | { | ||
60 | fprintf(stderr, "FATAL ERROR, rdtsc() failed while enabled\n"); | ||
61 | exit(0); | ||
62 | } | ||
63 | |||
64 | static void rdtsctask(void) | ||
65 | { | ||
66 | if (prctl(PR_SET_TSC, PR_TSC_ENABLE) < 0) | ||
67 | { | ||
68 | perror("prctl"); | ||
69 | exit(0); | ||
70 | } | ||
71 | signal(SIGSEGV, sigsegv_fail); | ||
72 | alarm(10); | ||
73 | for(;;) rdtsc(); | ||
74 | } | ||
75 | |||
76 | |||
77 | int main(void) | ||
78 | { | ||
79 | int n_tasks = 100, i; | ||
80 | |||
81 | fprintf(stderr, "[No further output means we're allright]\n"); | ||
82 | |||
83 | for (i=0; i<n_tasks; i++) | ||
84 | if (fork() == 0) | ||
85 | { | ||
86 | if (i & 1) | ||
87 | segvtask(); | ||
88 | else | ||
89 | rdtsctask(); | ||
90 | } | ||
91 | |||
92 | for (i=0; i<n_tasks; i++) | ||
93 | wait(NULL); | ||
94 | |||
95 | exit(0); | ||
96 | } | ||
97 | |||
diff --git a/tools/testing/selftests/prctl/disable-tsc-on-off-stress-test.c b/tools/testing/selftests/prctl/disable-tsc-on-off-stress-test.c new file mode 100644 index 000000000000..a06f027e9d16 --- /dev/null +++ b/tools/testing/selftests/prctl/disable-tsc-on-off-stress-test.c | |||
@@ -0,0 +1,96 @@ | |||
1 | /* | ||
2 | * Tests for prctl(PR_GET_TSC, ...) / prctl(PR_SET_TSC, ...) | ||
3 | * | ||
4 | * Tests if the control register is updated correctly | ||
5 | * when set with prctl() | ||
6 | * | ||
7 | * Warning: this test will cause a very high load for a few seconds | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <stdio.h> | ||
12 | #include <stdlib.h> | ||
13 | #include <unistd.h> | ||
14 | #include <signal.h> | ||
15 | #include <inttypes.h> | ||
16 | #include <wait.h> | ||
17 | |||
18 | |||
19 | #include <sys/prctl.h> | ||
20 | #include <linux/prctl.h> | ||
21 | |||
22 | /* Get/set the process' ability to use the timestamp counter instruction */ | ||
23 | #ifndef PR_GET_TSC | ||
24 | #define PR_GET_TSC 25 | ||
25 | #define PR_SET_TSC 26 | ||
26 | # define PR_TSC_ENABLE 1 /* allow the use of the timestamp counter */ | ||
27 | # define PR_TSC_SIGSEGV 2 /* throw a SIGSEGV instead of reading the TSC */ | ||
28 | #endif | ||
29 | |||
30 | /* snippet from wikipedia :-) */ | ||
31 | |||
32 | static uint64_t rdtsc(void) | ||
33 | { | ||
34 | uint32_t lo, hi; | ||
35 | /* We cannot use "=A", since this would use %rax on x86_64 */ | ||
36 | __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); | ||
37 | return (uint64_t)hi << 32 | lo; | ||
38 | } | ||
39 | |||
40 | int should_segv = 0; | ||
41 | |||
42 | static void sigsegv_cb(int sig) | ||
43 | { | ||
44 | if (!should_segv) | ||
45 | { | ||
46 | fprintf(stderr, "FATAL ERROR, rdtsc() failed while enabled\n"); | ||
47 | exit(0); | ||
48 | } | ||
49 | if (prctl(PR_SET_TSC, PR_TSC_ENABLE) < 0) | ||
50 | { | ||
51 | perror("prctl"); | ||
52 | exit(0); | ||
53 | } | ||
54 | should_segv = 0; | ||
55 | |||
56 | rdtsc(); | ||
57 | } | ||
58 | |||
59 | static void task(void) | ||
60 | { | ||
61 | signal(SIGSEGV, sigsegv_cb); | ||
62 | alarm(10); | ||
63 | for(;;) | ||
64 | { | ||
65 | rdtsc(); | ||
66 | if (should_segv) | ||
67 | { | ||
68 | fprintf(stderr, "FATAL ERROR, rdtsc() succeeded while disabled\n"); | ||
69 | exit(0); | ||
70 | } | ||
71 | if (prctl(PR_SET_TSC, PR_TSC_SIGSEGV) < 0) | ||
72 | { | ||
73 | perror("prctl"); | ||
74 | exit(0); | ||
75 | } | ||
76 | should_segv = 1; | ||
77 | } | ||
78 | } | ||
79 | |||
80 | |||
81 | int main(void) | ||
82 | { | ||
83 | int n_tasks = 100, i; | ||
84 | |||
85 | fprintf(stderr, "[No further output means we're allright]\n"); | ||
86 | |||
87 | for (i=0; i<n_tasks; i++) | ||
88 | if (fork() == 0) | ||
89 | task(); | ||
90 | |||
91 | for (i=0; i<n_tasks; i++) | ||
92 | wait(NULL); | ||
93 | |||
94 | exit(0); | ||
95 | } | ||
96 | |||
diff --git a/tools/testing/selftests/prctl/disable-tsc-test.c b/tools/testing/selftests/prctl/disable-tsc-test.c new file mode 100644 index 000000000000..8d494f7bebdb --- /dev/null +++ b/tools/testing/selftests/prctl/disable-tsc-test.c | |||
@@ -0,0 +1,95 @@ | |||
1 | /* | ||
2 | * Tests for prctl(PR_GET_TSC, ...) / prctl(PR_SET_TSC, ...) | ||
3 | * | ||
4 | * Basic test to test behaviour of PR_GET_TSC and PR_SET_TSC | ||
5 | */ | ||
6 | |||
7 | #include <stdio.h> | ||
8 | #include <stdlib.h> | ||
9 | #include <unistd.h> | ||
10 | #include <signal.h> | ||
11 | #include <inttypes.h> | ||
12 | |||
13 | |||
14 | #include <sys/prctl.h> | ||
15 | #include <linux/prctl.h> | ||
16 | |||
17 | /* Get/set the process' ability to use the timestamp counter instruction */ | ||
18 | #ifndef PR_GET_TSC | ||
19 | #define PR_GET_TSC 25 | ||
20 | #define PR_SET_TSC 26 | ||
21 | # define PR_TSC_ENABLE 1 /* allow the use of the timestamp counter */ | ||
22 | # define PR_TSC_SIGSEGV 2 /* throw a SIGSEGV instead of reading the TSC */ | ||
23 | #endif | ||
24 | |||
25 | const char *tsc_names[] = | ||
26 | { | ||
27 | [0] = "[not set]", | ||
28 | [PR_TSC_ENABLE] = "PR_TSC_ENABLE", | ||
29 | [PR_TSC_SIGSEGV] = "PR_TSC_SIGSEGV", | ||
30 | }; | ||
31 | |||
32 | static uint64_t rdtsc(void) | ||
33 | { | ||
34 | uint32_t lo, hi; | ||
35 | /* We cannot use "=A", since this would use %rax on x86_64 */ | ||
36 | __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); | ||
37 | return (uint64_t)hi << 32 | lo; | ||
38 | } | ||
39 | |||
40 | static void sigsegv_cb(int sig) | ||
41 | { | ||
42 | int tsc_val = 0; | ||
43 | |||
44 | printf("[ SIG_SEGV ]\n"); | ||
45 | printf("prctl(PR_GET_TSC, &tsc_val); "); | ||
46 | fflush(stdout); | ||
47 | |||
48 | if ( prctl(PR_GET_TSC, &tsc_val) == -1) | ||
49 | perror("prctl"); | ||
50 | |||
51 | printf("tsc_val == %s\n", tsc_names[tsc_val]); | ||
52 | printf("prctl(PR_SET_TSC, PR_TSC_ENABLE)\n"); | ||
53 | fflush(stdout); | ||
54 | if ( prctl(PR_SET_TSC, PR_TSC_ENABLE) == -1) | ||
55 | perror("prctl"); | ||
56 | |||
57 | printf("rdtsc() == "); | ||
58 | } | ||
59 | |||
60 | int main(void) | ||
61 | { | ||
62 | int tsc_val = 0; | ||
63 | |||
64 | signal(SIGSEGV, sigsegv_cb); | ||
65 | |||
66 | printf("rdtsc() == %llu\n", (unsigned long long)rdtsc()); | ||
67 | printf("prctl(PR_GET_TSC, &tsc_val); "); | ||
68 | fflush(stdout); | ||
69 | |||
70 | if ( prctl(PR_GET_TSC, &tsc_val) == -1) | ||
71 | perror("prctl"); | ||
72 | |||
73 | printf("tsc_val == %s\n", tsc_names[tsc_val]); | ||
74 | printf("rdtsc() == %llu\n", (unsigned long long)rdtsc()); | ||
75 | printf("prctl(PR_SET_TSC, PR_TSC_ENABLE)\n"); | ||
76 | fflush(stdout); | ||
77 | |||
78 | if ( prctl(PR_SET_TSC, PR_TSC_ENABLE) == -1) | ||
79 | perror("prctl"); | ||
80 | |||
81 | printf("rdtsc() == %llu\n", (unsigned long long)rdtsc()); | ||
82 | printf("prctl(PR_SET_TSC, PR_TSC_SIGSEGV)\n"); | ||
83 | fflush(stdout); | ||
84 | |||
85 | if ( prctl(PR_SET_TSC, PR_TSC_SIGSEGV) == -1) | ||
86 | perror("prctl"); | ||
87 | |||
88 | printf("rdtsc() == "); | ||
89 | fflush(stdout); | ||
90 | printf("%llu\n", (unsigned long long)rdtsc()); | ||
91 | fflush(stdout); | ||
92 | |||
93 | exit(EXIT_SUCCESS); | ||
94 | } | ||
95 | |||
diff --git a/tools/testing/selftests/ptp/.gitignore b/tools/testing/selftests/ptp/.gitignore new file mode 100644 index 000000000000..f562e49d6917 --- /dev/null +++ b/tools/testing/selftests/ptp/.gitignore | |||
@@ -0,0 +1 @@ | |||
testptp | |||
diff --git a/tools/testing/selftests/ptp/Makefile b/tools/testing/selftests/ptp/Makefile new file mode 100644 index 000000000000..83dd42b2129e --- /dev/null +++ b/tools/testing/selftests/ptp/Makefile | |||
@@ -0,0 +1,8 @@ | |||
1 | TEST_PROGS := testptp | ||
2 | LDLIBS += -lrt | ||
3 | all: $(TEST_PROGS) | ||
4 | |||
5 | include ../lib.mk | ||
6 | |||
7 | clean: | ||
8 | rm -fr $(TEST_PROGS) | ||
diff --git a/tools/testing/selftests/ptp/testptp.c b/tools/testing/selftests/ptp/testptp.c new file mode 100644 index 000000000000..5d2eae16f7ee --- /dev/null +++ b/tools/testing/selftests/ptp/testptp.c | |||
@@ -0,0 +1,523 @@ | |||
1 | /* | ||
2 | * PTP 1588 clock support - User space test program | ||
3 | * | ||
4 | * Copyright (C) 2010 OMICRON electronics GmbH | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program; if not, write to the Free Software | ||
18 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
19 | */ | ||
20 | #define _GNU_SOURCE | ||
21 | #define __SANE_USERSPACE_TYPES__ /* For PPC64, to get LL64 types */ | ||
22 | #include <errno.h> | ||
23 | #include <fcntl.h> | ||
24 | #include <inttypes.h> | ||
25 | #include <math.h> | ||
26 | #include <signal.h> | ||
27 | #include <stdio.h> | ||
28 | #include <stdlib.h> | ||
29 | #include <string.h> | ||
30 | #include <sys/ioctl.h> | ||
31 | #include <sys/mman.h> | ||
32 | #include <sys/stat.h> | ||
33 | #include <sys/time.h> | ||
34 | #include <sys/timex.h> | ||
35 | #include <sys/types.h> | ||
36 | #include <time.h> | ||
37 | #include <unistd.h> | ||
38 | |||
39 | #include <linux/ptp_clock.h> | ||
40 | |||
41 | #define DEVICE "/dev/ptp0" | ||
42 | |||
43 | #ifndef ADJ_SETOFFSET | ||
44 | #define ADJ_SETOFFSET 0x0100 | ||
45 | #endif | ||
46 | |||
47 | #ifndef CLOCK_INVALID | ||
48 | #define CLOCK_INVALID -1 | ||
49 | #endif | ||
50 | |||
51 | /* clock_adjtime is not available in GLIBC < 2.14 */ | ||
52 | #if !__GLIBC_PREREQ(2, 14) | ||
53 | #include <sys/syscall.h> | ||
54 | static int clock_adjtime(clockid_t id, struct timex *tx) | ||
55 | { | ||
56 | return syscall(__NR_clock_adjtime, id, tx); | ||
57 | } | ||
58 | #endif | ||
59 | |||
60 | static clockid_t get_clockid(int fd) | ||
61 | { | ||
62 | #define CLOCKFD 3 | ||
63 | #define FD_TO_CLOCKID(fd) ((~(clockid_t) (fd) << 3) | CLOCKFD) | ||
64 | |||
65 | return FD_TO_CLOCKID(fd); | ||
66 | } | ||
67 | |||
68 | static void handle_alarm(int s) | ||
69 | { | ||
70 | printf("received signal %d\n", s); | ||
71 | } | ||
72 | |||
73 | static int install_handler(int signum, void (*handler)(int)) | ||
74 | { | ||
75 | struct sigaction action; | ||
76 | sigset_t mask; | ||
77 | |||
78 | /* Unblock the signal. */ | ||
79 | sigemptyset(&mask); | ||
80 | sigaddset(&mask, signum); | ||
81 | sigprocmask(SIG_UNBLOCK, &mask, NULL); | ||
82 | |||
83 | /* Install the signal handler. */ | ||
84 | action.sa_handler = handler; | ||
85 | action.sa_flags = 0; | ||
86 | sigemptyset(&action.sa_mask); | ||
87 | sigaction(signum, &action, NULL); | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static long ppb_to_scaled_ppm(int ppb) | ||
93 | { | ||
94 | /* | ||
95 | * The 'freq' field in the 'struct timex' is in parts per | ||
96 | * million, but with a 16 bit binary fractional field. | ||
97 | * Instead of calculating either one of | ||
98 | * | ||
99 | * scaled_ppm = (ppb / 1000) << 16 [1] | ||
100 | * scaled_ppm = (ppb << 16) / 1000 [2] | ||
101 | * | ||
102 | * we simply use double precision math, in order to avoid the | ||
103 | * truncation in [1] and the possible overflow in [2]. | ||
104 | */ | ||
105 | return (long) (ppb * 65.536); | ||
106 | } | ||
107 | |||
108 | static int64_t pctns(struct ptp_clock_time *t) | ||
109 | { | ||
110 | return t->sec * 1000000000LL + t->nsec; | ||
111 | } | ||
112 | |||
113 | static void usage(char *progname) | ||
114 | { | ||
115 | fprintf(stderr, | ||
116 | "usage: %s [options]\n" | ||
117 | " -a val request a one-shot alarm after 'val' seconds\n" | ||
118 | " -A val request a periodic alarm every 'val' seconds\n" | ||
119 | " -c query the ptp clock's capabilities\n" | ||
120 | " -d name device to open\n" | ||
121 | " -e val read 'val' external time stamp events\n" | ||
122 | " -f val adjust the ptp clock frequency by 'val' ppb\n" | ||
123 | " -g get the ptp clock time\n" | ||
124 | " -h prints this message\n" | ||
125 | " -i val index for event/trigger\n" | ||
126 | " -k val measure the time offset between system and phc clock\n" | ||
127 | " for 'val' times (Maximum 25)\n" | ||
128 | " -l list the current pin configuration\n" | ||
129 | " -L pin,val configure pin index 'pin' with function 'val'\n" | ||
130 | " the channel index is taken from the '-i' option\n" | ||
131 | " 'val' specifies the auxiliary function:\n" | ||
132 | " 0 - none\n" | ||
133 | " 1 - external time stamp\n" | ||
134 | " 2 - periodic output\n" | ||
135 | " -p val enable output with a period of 'val' nanoseconds\n" | ||
136 | " -P val enable or disable (val=1|0) the system clock PPS\n" | ||
137 | " -s set the ptp clock time from the system time\n" | ||
138 | " -S set the system time from the ptp clock time\n" | ||
139 | " -t val shift the ptp clock time by 'val' seconds\n" | ||
140 | " -T val set the ptp clock time to 'val' seconds\n", | ||
141 | progname); | ||
142 | } | ||
143 | |||
144 | int main(int argc, char *argv[]) | ||
145 | { | ||
146 | struct ptp_clock_caps caps; | ||
147 | struct ptp_extts_event event; | ||
148 | struct ptp_extts_request extts_request; | ||
149 | struct ptp_perout_request perout_request; | ||
150 | struct ptp_pin_desc desc; | ||
151 | struct timespec ts; | ||
152 | struct timex tx; | ||
153 | |||
154 | static timer_t timerid; | ||
155 | struct itimerspec timeout; | ||
156 | struct sigevent sigevent; | ||
157 | |||
158 | struct ptp_clock_time *pct; | ||
159 | struct ptp_sys_offset *sysoff; | ||
160 | |||
161 | |||
162 | char *progname; | ||
163 | unsigned int i; | ||
164 | int c, cnt, fd; | ||
165 | |||
166 | char *device = DEVICE; | ||
167 | clockid_t clkid; | ||
168 | int adjfreq = 0x7fffffff; | ||
169 | int adjtime = 0; | ||
170 | int capabilities = 0; | ||
171 | int extts = 0; | ||
172 | int gettime = 0; | ||
173 | int index = 0; | ||
174 | int list_pins = 0; | ||
175 | int oneshot = 0; | ||
176 | int pct_offset = 0; | ||
177 | int n_samples = 0; | ||
178 | int periodic = 0; | ||
179 | int perout = -1; | ||
180 | int pin_index = -1, pin_func; | ||
181 | int pps = -1; | ||
182 | int seconds = 0; | ||
183 | int settime = 0; | ||
184 | |||
185 | int64_t t1, t2, tp; | ||
186 | int64_t interval, offset; | ||
187 | |||
188 | progname = strrchr(argv[0], '/'); | ||
189 | progname = progname ? 1+progname : argv[0]; | ||
190 | while (EOF != (c = getopt(argc, argv, "a:A:cd:e:f:ghi:k:lL:p:P:sSt:T:v"))) { | ||
191 | switch (c) { | ||
192 | case 'a': | ||
193 | oneshot = atoi(optarg); | ||
194 | break; | ||
195 | case 'A': | ||
196 | periodic = atoi(optarg); | ||
197 | break; | ||
198 | case 'c': | ||
199 | capabilities = 1; | ||
200 | break; | ||
201 | case 'd': | ||
202 | device = optarg; | ||
203 | break; | ||
204 | case 'e': | ||
205 | extts = atoi(optarg); | ||
206 | break; | ||
207 | case 'f': | ||
208 | adjfreq = atoi(optarg); | ||
209 | break; | ||
210 | case 'g': | ||
211 | gettime = 1; | ||
212 | break; | ||
213 | case 'i': | ||
214 | index = atoi(optarg); | ||
215 | break; | ||
216 | case 'k': | ||
217 | pct_offset = 1; | ||
218 | n_samples = atoi(optarg); | ||
219 | break; | ||
220 | case 'l': | ||
221 | list_pins = 1; | ||
222 | break; | ||
223 | case 'L': | ||
224 | cnt = sscanf(optarg, "%d,%d", &pin_index, &pin_func); | ||
225 | if (cnt != 2) { | ||
226 | usage(progname); | ||
227 | return -1; | ||
228 | } | ||
229 | break; | ||
230 | case 'p': | ||
231 | perout = atoi(optarg); | ||
232 | break; | ||
233 | case 'P': | ||
234 | pps = atoi(optarg); | ||
235 | break; | ||
236 | case 's': | ||
237 | settime = 1; | ||
238 | break; | ||
239 | case 'S': | ||
240 | settime = 2; | ||
241 | break; | ||
242 | case 't': | ||
243 | adjtime = atoi(optarg); | ||
244 | break; | ||
245 | case 'T': | ||
246 | settime = 3; | ||
247 | seconds = atoi(optarg); | ||
248 | break; | ||
249 | case 'h': | ||
250 | usage(progname); | ||
251 | return 0; | ||
252 | case '?': | ||
253 | default: | ||
254 | usage(progname); | ||
255 | return -1; | ||
256 | } | ||
257 | } | ||
258 | |||
259 | fd = open(device, O_RDWR); | ||
260 | if (fd < 0) { | ||
261 | fprintf(stderr, "opening %s: %s\n", device, strerror(errno)); | ||
262 | return -1; | ||
263 | } | ||
264 | |||
265 | clkid = get_clockid(fd); | ||
266 | if (CLOCK_INVALID == clkid) { | ||
267 | fprintf(stderr, "failed to read clock id\n"); | ||
268 | return -1; | ||
269 | } | ||
270 | |||
271 | if (capabilities) { | ||
272 | if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) { | ||
273 | perror("PTP_CLOCK_GETCAPS"); | ||
274 | } else { | ||
275 | printf("capabilities:\n" | ||
276 | " %d maximum frequency adjustment (ppb)\n" | ||
277 | " %d programmable alarms\n" | ||
278 | " %d external time stamp channels\n" | ||
279 | " %d programmable periodic signals\n" | ||
280 | " %d pulse per second\n" | ||
281 | " %d programmable pins\n" | ||
282 | " %d cross timestamping\n", | ||
283 | caps.max_adj, | ||
284 | caps.n_alarm, | ||
285 | caps.n_ext_ts, | ||
286 | caps.n_per_out, | ||
287 | caps.pps, | ||
288 | caps.n_pins, | ||
289 | caps.cross_timestamping); | ||
290 | } | ||
291 | } | ||
292 | |||
293 | if (0x7fffffff != adjfreq) { | ||
294 | memset(&tx, 0, sizeof(tx)); | ||
295 | tx.modes = ADJ_FREQUENCY; | ||
296 | tx.freq = ppb_to_scaled_ppm(adjfreq); | ||
297 | if (clock_adjtime(clkid, &tx)) { | ||
298 | perror("clock_adjtime"); | ||
299 | } else { | ||
300 | puts("frequency adjustment okay"); | ||
301 | } | ||
302 | } | ||
303 | |||
304 | if (adjtime) { | ||
305 | memset(&tx, 0, sizeof(tx)); | ||
306 | tx.modes = ADJ_SETOFFSET; | ||
307 | tx.time.tv_sec = adjtime; | ||
308 | tx.time.tv_usec = 0; | ||
309 | if (clock_adjtime(clkid, &tx) < 0) { | ||
310 | perror("clock_adjtime"); | ||
311 | } else { | ||
312 | puts("time shift okay"); | ||
313 | } | ||
314 | } | ||
315 | |||
316 | if (gettime) { | ||
317 | if (clock_gettime(clkid, &ts)) { | ||
318 | perror("clock_gettime"); | ||
319 | } else { | ||
320 | printf("clock time: %ld.%09ld or %s", | ||
321 | ts.tv_sec, ts.tv_nsec, ctime(&ts.tv_sec)); | ||
322 | } | ||
323 | } | ||
324 | |||
325 | if (settime == 1) { | ||
326 | clock_gettime(CLOCK_REALTIME, &ts); | ||
327 | if (clock_settime(clkid, &ts)) { | ||
328 | perror("clock_settime"); | ||
329 | } else { | ||
330 | puts("set time okay"); | ||
331 | } | ||
332 | } | ||
333 | |||
334 | if (settime == 2) { | ||
335 | clock_gettime(clkid, &ts); | ||
336 | if (clock_settime(CLOCK_REALTIME, &ts)) { | ||
337 | perror("clock_settime"); | ||
338 | } else { | ||
339 | puts("set time okay"); | ||
340 | } | ||
341 | } | ||
342 | |||
343 | if (settime == 3) { | ||
344 | ts.tv_sec = seconds; | ||
345 | ts.tv_nsec = 0; | ||
346 | if (clock_settime(clkid, &ts)) { | ||
347 | perror("clock_settime"); | ||
348 | } else { | ||
349 | puts("set time okay"); | ||
350 | } | ||
351 | } | ||
352 | |||
353 | if (extts) { | ||
354 | memset(&extts_request, 0, sizeof(extts_request)); | ||
355 | extts_request.index = index; | ||
356 | extts_request.flags = PTP_ENABLE_FEATURE; | ||
357 | if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) { | ||
358 | perror("PTP_EXTTS_REQUEST"); | ||
359 | extts = 0; | ||
360 | } else { | ||
361 | puts("external time stamp request okay"); | ||
362 | } | ||
363 | for (; extts; extts--) { | ||
364 | cnt = read(fd, &event, sizeof(event)); | ||
365 | if (cnt != sizeof(event)) { | ||
366 | perror("read"); | ||
367 | break; | ||
368 | } | ||
369 | printf("event index %u at %lld.%09u\n", event.index, | ||
370 | event.t.sec, event.t.nsec); | ||
371 | fflush(stdout); | ||
372 | } | ||
373 | /* Disable the feature again. */ | ||
374 | extts_request.flags = 0; | ||
375 | if (ioctl(fd, PTP_EXTTS_REQUEST, &extts_request)) { | ||
376 | perror("PTP_EXTTS_REQUEST"); | ||
377 | } | ||
378 | } | ||
379 | |||
380 | if (list_pins) { | ||
381 | int n_pins = 0; | ||
382 | if (ioctl(fd, PTP_CLOCK_GETCAPS, &caps)) { | ||
383 | perror("PTP_CLOCK_GETCAPS"); | ||
384 | } else { | ||
385 | n_pins = caps.n_pins; | ||
386 | } | ||
387 | for (i = 0; i < n_pins; i++) { | ||
388 | desc.index = i; | ||
389 | if (ioctl(fd, PTP_PIN_GETFUNC, &desc)) { | ||
390 | perror("PTP_PIN_GETFUNC"); | ||
391 | break; | ||
392 | } | ||
393 | printf("name %s index %u func %u chan %u\n", | ||
394 | desc.name, desc.index, desc.func, desc.chan); | ||
395 | } | ||
396 | } | ||
397 | |||
398 | if (oneshot) { | ||
399 | install_handler(SIGALRM, handle_alarm); | ||
400 | /* Create a timer. */ | ||
401 | sigevent.sigev_notify = SIGEV_SIGNAL; | ||
402 | sigevent.sigev_signo = SIGALRM; | ||
403 | if (timer_create(clkid, &sigevent, &timerid)) { | ||
404 | perror("timer_create"); | ||
405 | return -1; | ||
406 | } | ||
407 | /* Start the timer. */ | ||
408 | memset(&timeout, 0, sizeof(timeout)); | ||
409 | timeout.it_value.tv_sec = oneshot; | ||
410 | if (timer_settime(timerid, 0, &timeout, NULL)) { | ||
411 | perror("timer_settime"); | ||
412 | return -1; | ||
413 | } | ||
414 | pause(); | ||
415 | timer_delete(timerid); | ||
416 | } | ||
417 | |||
418 | if (periodic) { | ||
419 | install_handler(SIGALRM, handle_alarm); | ||
420 | /* Create a timer. */ | ||
421 | sigevent.sigev_notify = SIGEV_SIGNAL; | ||
422 | sigevent.sigev_signo = SIGALRM; | ||
423 | if (timer_create(clkid, &sigevent, &timerid)) { | ||
424 | perror("timer_create"); | ||
425 | return -1; | ||
426 | } | ||
427 | /* Start the timer. */ | ||
428 | memset(&timeout, 0, sizeof(timeout)); | ||
429 | timeout.it_interval.tv_sec = periodic; | ||
430 | timeout.it_value.tv_sec = periodic; | ||
431 | if (timer_settime(timerid, 0, &timeout, NULL)) { | ||
432 | perror("timer_settime"); | ||
433 | return -1; | ||
434 | } | ||
435 | while (1) { | ||
436 | pause(); | ||
437 | } | ||
438 | timer_delete(timerid); | ||
439 | } | ||
440 | |||
441 | if (perout >= 0) { | ||
442 | if (clock_gettime(clkid, &ts)) { | ||
443 | perror("clock_gettime"); | ||
444 | return -1; | ||
445 | } | ||
446 | memset(&perout_request, 0, sizeof(perout_request)); | ||
447 | perout_request.index = index; | ||
448 | perout_request.start.sec = ts.tv_sec + 2; | ||
449 | perout_request.start.nsec = 0; | ||
450 | perout_request.period.sec = 0; | ||
451 | perout_request.period.nsec = perout; | ||
452 | if (ioctl(fd, PTP_PEROUT_REQUEST, &perout_request)) { | ||
453 | perror("PTP_PEROUT_REQUEST"); | ||
454 | } else { | ||
455 | puts("periodic output request okay"); | ||
456 | } | ||
457 | } | ||
458 | |||
459 | if (pin_index >= 0) { | ||
460 | memset(&desc, 0, sizeof(desc)); | ||
461 | desc.index = pin_index; | ||
462 | desc.func = pin_func; | ||
463 | desc.chan = index; | ||
464 | if (ioctl(fd, PTP_PIN_SETFUNC, &desc)) { | ||
465 | perror("PTP_PIN_SETFUNC"); | ||
466 | } else { | ||
467 | puts("set pin function okay"); | ||
468 | } | ||
469 | } | ||
470 | |||
471 | if (pps != -1) { | ||
472 | int enable = pps ? 1 : 0; | ||
473 | if (ioctl(fd, PTP_ENABLE_PPS, enable)) { | ||
474 | perror("PTP_ENABLE_PPS"); | ||
475 | } else { | ||
476 | puts("pps for system time request okay"); | ||
477 | } | ||
478 | } | ||
479 | |||
480 | if (pct_offset) { | ||
481 | if (n_samples <= 0 || n_samples > 25) { | ||
482 | puts("n_samples should be between 1 and 25"); | ||
483 | usage(progname); | ||
484 | return -1; | ||
485 | } | ||
486 | |||
487 | sysoff = calloc(1, sizeof(*sysoff)); | ||
488 | if (!sysoff) { | ||
489 | perror("calloc"); | ||
490 | return -1; | ||
491 | } | ||
492 | sysoff->n_samples = n_samples; | ||
493 | |||
494 | if (ioctl(fd, PTP_SYS_OFFSET, sysoff)) | ||
495 | perror("PTP_SYS_OFFSET"); | ||
496 | else | ||
497 | puts("system and phc clock time offset request okay"); | ||
498 | |||
499 | pct = &sysoff->ts[0]; | ||
500 | for (i = 0; i < sysoff->n_samples; i++) { | ||
501 | t1 = pctns(pct+2*i); | ||
502 | tp = pctns(pct+2*i+1); | ||
503 | t2 = pctns(pct+2*i+2); | ||
504 | interval = t2 - t1; | ||
505 | offset = (t2 + t1) / 2 - tp; | ||
506 | |||
507 | printf("system time: %lld.%u\n", | ||
508 | (pct+2*i)->sec, (pct+2*i)->nsec); | ||
509 | printf("phc time: %lld.%u\n", | ||
510 | (pct+2*i+1)->sec, (pct+2*i+1)->nsec); | ||
511 | printf("system time: %lld.%u\n", | ||
512 | (pct+2*i+2)->sec, (pct+2*i+2)->nsec); | ||
513 | printf("system/phc clock time offset is %" PRId64 " ns\n" | ||
514 | "system clock time delay is %" PRId64 " ns\n", | ||
515 | offset, interval); | ||
516 | } | ||
517 | |||
518 | free(sysoff); | ||
519 | } | ||
520 | |||
521 | close(fd); | ||
522 | return 0; | ||
523 | } | ||
diff --git a/tools/testing/selftests/ptp/testptp.mk b/tools/testing/selftests/ptp/testptp.mk new file mode 100644 index 000000000000..4ef2d9755421 --- /dev/null +++ b/tools/testing/selftests/ptp/testptp.mk | |||
@@ -0,0 +1,33 @@ | |||
1 | # PTP 1588 clock support - User space test program | ||
2 | # | ||
3 | # Copyright (C) 2010 OMICRON electronics GmbH | ||
4 | # | ||
5 | # This program is free software; you can redistribute it and/or modify | ||
6 | # it under the terms of the GNU General Public License as published by | ||
7 | # the Free Software Foundation; either version 2 of the License, or | ||
8 | # (at your option) any later version. | ||
9 | # | ||
10 | # This program is distributed in the hope that it will be useful, | ||
11 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | # GNU General Public License for more details. | ||
14 | # | ||
15 | # You should have received a copy of the GNU General Public License | ||
16 | # along with this program; if not, write to the Free Software | ||
17 | # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | ||
18 | |||
19 | CC = $(CROSS_COMPILE)gcc | ||
20 | INC = -I$(KBUILD_OUTPUT)/usr/include | ||
21 | CFLAGS = -Wall $(INC) | ||
22 | LDLIBS = -lrt | ||
23 | PROGS = testptp | ||
24 | |||
25 | all: $(PROGS) | ||
26 | |||
27 | testptp: testptp.o | ||
28 | |||
29 | clean: | ||
30 | rm -f testptp.o | ||
31 | |||
32 | distclean: clean | ||
33 | rm -f $(PROGS) | ||
diff --git a/tools/testing/selftests/timers/posix_timers.c b/tools/testing/selftests/timers/posix_timers.c index 5a246a02dff3..15cf56d32155 100644 --- a/tools/testing/selftests/timers/posix_timers.c +++ b/tools/testing/selftests/timers/posix_timers.c | |||
@@ -122,7 +122,7 @@ static int check_itimer(int which) | |||
122 | else if (which == ITIMER_REAL) | 122 | else if (which == ITIMER_REAL) |
123 | idle_loop(); | 123 | idle_loop(); |
124 | 124 | ||
125 | gettimeofday(&end, NULL); | 125 | err = gettimeofday(&end, NULL); |
126 | if (err < 0) { | 126 | if (err < 0) { |
127 | perror("Can't call gettimeofday()\n"); | 127 | perror("Can't call gettimeofday()\n"); |
128 | return -1; | 128 | return -1; |
@@ -175,7 +175,7 @@ static int check_timer_create(int which) | |||
175 | 175 | ||
176 | user_loop(); | 176 | user_loop(); |
177 | 177 | ||
178 | gettimeofday(&end, NULL); | 178 | err = gettimeofday(&end, NULL); |
179 | if (err < 0) { | 179 | if (err < 0) { |
180 | perror("Can't call gettimeofday()\n"); | 180 | perror("Can't call gettimeofday()\n"); |
181 | return -1; | 181 | return -1; |
diff --git a/tools/testing/selftests/vDSO/.gitignore b/tools/testing/selftests/vDSO/.gitignore new file mode 100644 index 000000000000..133bf9ee986c --- /dev/null +++ b/tools/testing/selftests/vDSO/.gitignore | |||
@@ -0,0 +1,2 @@ | |||
1 | vdso_test | ||
2 | vdso_standalone_test_x86 | ||
diff --git a/tools/testing/selftests/vDSO/Makefile b/tools/testing/selftests/vDSO/Makefile new file mode 100644 index 000000000000..706b68b1c372 --- /dev/null +++ b/tools/testing/selftests/vDSO/Makefile | |||
@@ -0,0 +1,20 @@ | |||
1 | ifndef CROSS_COMPILE | ||
2 | CFLAGS := -std=gnu99 | ||
3 | CFLAGS_vdso_standalone_test_x86 := -nostdlib -fno-asynchronous-unwind-tables -fno-stack-protector | ||
4 | ifeq ($(CONFIG_X86_32),y) | ||
5 | LDLIBS += -lgcc_s | ||
6 | endif | ||
7 | |||
8 | TEST_PROGS := vdso_test vdso_standalone_test_x86 | ||
9 | |||
10 | all: $(TEST_PROGS) | ||
11 | vdso_test: parse_vdso.c vdso_test.c | ||
12 | vdso_standalone_test_x86: vdso_standalone_test_x86.c parse_vdso.c | ||
13 | $(CC) $(CFLAGS) $(CFLAGS_vdso_standalone_test_x86) \ | ||
14 | vdso_standalone_test_x86.c parse_vdso.c \ | ||
15 | -o vdso_standalone_test_x86 | ||
16 | |||
17 | include ../lib.mk | ||
18 | clean: | ||
19 | rm -fr $(TEST_PROGS) | ||
20 | endif | ||
diff --git a/tools/testing/selftests/vDSO/parse_vdso.c b/tools/testing/selftests/vDSO/parse_vdso.c new file mode 100644 index 000000000000..1dbb4b87268f --- /dev/null +++ b/tools/testing/selftests/vDSO/parse_vdso.c | |||
@@ -0,0 +1,269 @@ | |||
1 | /* | ||
2 | * parse_vdso.c: Linux reference vDSO parser | ||
3 | * Written by Andrew Lutomirski, 2011-2014. | ||
4 | * | ||
5 | * This code is meant to be linked in to various programs that run on Linux. | ||
6 | * As such, it is available with as few restrictions as possible. This file | ||
7 | * is licensed under the Creative Commons Zero License, version 1.0, | ||
8 | * available at http://creativecommons.org/publicdomain/zero/1.0/legalcode | ||
9 | * | ||
10 | * The vDSO is a regular ELF DSO that the kernel maps into user space when | ||
11 | * it starts a program. It works equally well in statically and dynamically | ||
12 | * linked binaries. | ||
13 | * | ||
14 | * This code is tested on x86. In principle it should work on any | ||
15 | * architecture that has a vDSO. | ||
16 | */ | ||
17 | |||
18 | #include <stdbool.h> | ||
19 | #include <stdint.h> | ||
20 | #include <string.h> | ||
21 | #include <limits.h> | ||
22 | #include <elf.h> | ||
23 | |||
24 | /* | ||
25 | * To use this vDSO parser, first call one of the vdso_init_* functions. | ||
26 | * If you've already parsed auxv, then pass the value of AT_SYSINFO_EHDR | ||
27 | * to vdso_init_from_sysinfo_ehdr. Otherwise pass auxv to vdso_init_from_auxv. | ||
28 | * Then call vdso_sym for each symbol you want. For example, to look up | ||
29 | * gettimeofday on x86_64, use: | ||
30 | * | ||
31 | * <some pointer> = vdso_sym("LINUX_2.6", "gettimeofday"); | ||
32 | * or | ||
33 | * <some pointer> = vdso_sym("LINUX_2.6", "__vdso_gettimeofday"); | ||
34 | * | ||
35 | * vdso_sym will return 0 if the symbol doesn't exist or if the init function | ||
36 | * failed or was not called. vdso_sym is a little slow, so its return value | ||
37 | * should be cached. | ||
38 | * | ||
39 | * vdso_sym is threadsafe; the init functions are not. | ||
40 | * | ||
41 | * These are the prototypes: | ||
42 | */ | ||
43 | extern void vdso_init_from_auxv(void *auxv); | ||
44 | extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); | ||
45 | extern void *vdso_sym(const char *version, const char *name); | ||
46 | |||
47 | |||
48 | /* And here's the code. */ | ||
49 | #ifndef ELF_BITS | ||
50 | # if ULONG_MAX > 0xffffffffUL | ||
51 | # define ELF_BITS 64 | ||
52 | # else | ||
53 | # define ELF_BITS 32 | ||
54 | # endif | ||
55 | #endif | ||
56 | |||
57 | #define ELF_BITS_XFORM2(bits, x) Elf##bits##_##x | ||
58 | #define ELF_BITS_XFORM(bits, x) ELF_BITS_XFORM2(bits, x) | ||
59 | #define ELF(x) ELF_BITS_XFORM(ELF_BITS, x) | ||
60 | |||
61 | static struct vdso_info | ||
62 | { | ||
63 | bool valid; | ||
64 | |||
65 | /* Load information */ | ||
66 | uintptr_t load_addr; | ||
67 | uintptr_t load_offset; /* load_addr - recorded vaddr */ | ||
68 | |||
69 | /* Symbol table */ | ||
70 | ELF(Sym) *symtab; | ||
71 | const char *symstrings; | ||
72 | ELF(Word) *bucket, *chain; | ||
73 | ELF(Word) nbucket, nchain; | ||
74 | |||
75 | /* Version table */ | ||
76 | ELF(Versym) *versym; | ||
77 | ELF(Verdef) *verdef; | ||
78 | } vdso_info; | ||
79 | |||
80 | /* Straight from the ELF specification. */ | ||
81 | static unsigned long elf_hash(const unsigned char *name) | ||
82 | { | ||
83 | unsigned long h = 0, g; | ||
84 | while (*name) | ||
85 | { | ||
86 | h = (h << 4) + *name++; | ||
87 | if (g = h & 0xf0000000) | ||
88 | h ^= g >> 24; | ||
89 | h &= ~g; | ||
90 | } | ||
91 | return h; | ||
92 | } | ||
93 | |||
94 | void vdso_init_from_sysinfo_ehdr(uintptr_t base) | ||
95 | { | ||
96 | size_t i; | ||
97 | bool found_vaddr = false; | ||
98 | |||
99 | vdso_info.valid = false; | ||
100 | |||
101 | vdso_info.load_addr = base; | ||
102 | |||
103 | ELF(Ehdr) *hdr = (ELF(Ehdr)*)base; | ||
104 | if (hdr->e_ident[EI_CLASS] != | ||
105 | (ELF_BITS == 32 ? ELFCLASS32 : ELFCLASS64)) { | ||
106 | return; /* Wrong ELF class -- check ELF_BITS */ | ||
107 | } | ||
108 | |||
109 | ELF(Phdr) *pt = (ELF(Phdr)*)(vdso_info.load_addr + hdr->e_phoff); | ||
110 | ELF(Dyn) *dyn = 0; | ||
111 | |||
112 | /* | ||
113 | * We need two things from the segment table: the load offset | ||
114 | * and the dynamic table. | ||
115 | */ | ||
116 | for (i = 0; i < hdr->e_phnum; i++) | ||
117 | { | ||
118 | if (pt[i].p_type == PT_LOAD && !found_vaddr) { | ||
119 | found_vaddr = true; | ||
120 | vdso_info.load_offset = base | ||
121 | + (uintptr_t)pt[i].p_offset | ||
122 | - (uintptr_t)pt[i].p_vaddr; | ||
123 | } else if (pt[i].p_type == PT_DYNAMIC) { | ||
124 | dyn = (ELF(Dyn)*)(base + pt[i].p_offset); | ||
125 | } | ||
126 | } | ||
127 | |||
128 | if (!found_vaddr || !dyn) | ||
129 | return; /* Failed */ | ||
130 | |||
131 | /* | ||
132 | * Fish out the useful bits of the dynamic table. | ||
133 | */ | ||
134 | ELF(Word) *hash = 0; | ||
135 | vdso_info.symstrings = 0; | ||
136 | vdso_info.symtab = 0; | ||
137 | vdso_info.versym = 0; | ||
138 | vdso_info.verdef = 0; | ||
139 | for (i = 0; dyn[i].d_tag != DT_NULL; i++) { | ||
140 | switch (dyn[i].d_tag) { | ||
141 | case DT_STRTAB: | ||
142 | vdso_info.symstrings = (const char *) | ||
143 | ((uintptr_t)dyn[i].d_un.d_ptr | ||
144 | + vdso_info.load_offset); | ||
145 | break; | ||
146 | case DT_SYMTAB: | ||
147 | vdso_info.symtab = (ELF(Sym) *) | ||
148 | ((uintptr_t)dyn[i].d_un.d_ptr | ||
149 | + vdso_info.load_offset); | ||
150 | break; | ||
151 | case DT_HASH: | ||
152 | hash = (ELF(Word) *) | ||
153 | ((uintptr_t)dyn[i].d_un.d_ptr | ||
154 | + vdso_info.load_offset); | ||
155 | break; | ||
156 | case DT_VERSYM: | ||
157 | vdso_info.versym = (ELF(Versym) *) | ||
158 | ((uintptr_t)dyn[i].d_un.d_ptr | ||
159 | + vdso_info.load_offset); | ||
160 | break; | ||
161 | case DT_VERDEF: | ||
162 | vdso_info.verdef = (ELF(Verdef) *) | ||
163 | ((uintptr_t)dyn[i].d_un.d_ptr | ||
164 | + vdso_info.load_offset); | ||
165 | break; | ||
166 | } | ||
167 | } | ||
168 | if (!vdso_info.symstrings || !vdso_info.symtab || !hash) | ||
169 | return; /* Failed */ | ||
170 | |||
171 | if (!vdso_info.verdef) | ||
172 | vdso_info.versym = 0; | ||
173 | |||
174 | /* Parse the hash table header. */ | ||
175 | vdso_info.nbucket = hash[0]; | ||
176 | vdso_info.nchain = hash[1]; | ||
177 | vdso_info.bucket = &hash[2]; | ||
178 | vdso_info.chain = &hash[vdso_info.nbucket + 2]; | ||
179 | |||
180 | /* That's all we need. */ | ||
181 | vdso_info.valid = true; | ||
182 | } | ||
183 | |||
184 | static bool vdso_match_version(ELF(Versym) ver, | ||
185 | const char *name, ELF(Word) hash) | ||
186 | { | ||
187 | /* | ||
188 | * This is a helper function to check if the version indexed by | ||
189 | * ver matches name (which hashes to hash). | ||
190 | * | ||
191 | * The version definition table is a mess, and I don't know how | ||
192 | * to do this in better than linear time without allocating memory | ||
193 | * to build an index. I also don't know why the table has | ||
194 | * variable size entries in the first place. | ||
195 | * | ||
196 | * For added fun, I can't find a comprehensible specification of how | ||
197 | * to parse all the weird flags in the table. | ||
198 | * | ||
199 | * So I just parse the whole table every time. | ||
200 | */ | ||
201 | |||
202 | /* First step: find the version definition */ | ||
203 | ver &= 0x7fff; /* Apparently bit 15 means "hidden" */ | ||
204 | ELF(Verdef) *def = vdso_info.verdef; | ||
205 | while(true) { | ||
206 | if ((def->vd_flags & VER_FLG_BASE) == 0 | ||
207 | && (def->vd_ndx & 0x7fff) == ver) | ||
208 | break; | ||
209 | |||
210 | if (def->vd_next == 0) | ||
211 | return false; /* No definition. */ | ||
212 | |||
213 | def = (ELF(Verdef) *)((char *)def + def->vd_next); | ||
214 | } | ||
215 | |||
216 | /* Now figure out whether it matches. */ | ||
217 | ELF(Verdaux) *aux = (ELF(Verdaux)*)((char *)def + def->vd_aux); | ||
218 | return def->vd_hash == hash | ||
219 | && !strcmp(name, vdso_info.symstrings + aux->vda_name); | ||
220 | } | ||
221 | |||
222 | void *vdso_sym(const char *version, const char *name) | ||
223 | { | ||
224 | unsigned long ver_hash; | ||
225 | if (!vdso_info.valid) | ||
226 | return 0; | ||
227 | |||
228 | ver_hash = elf_hash(version); | ||
229 | ELF(Word) chain = vdso_info.bucket[elf_hash(name) % vdso_info.nbucket]; | ||
230 | |||
231 | for (; chain != STN_UNDEF; chain = vdso_info.chain[chain]) { | ||
232 | ELF(Sym) *sym = &vdso_info.symtab[chain]; | ||
233 | |||
234 | /* Check for a defined global or weak function w/ right name. */ | ||
235 | if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC) | ||
236 | continue; | ||
237 | if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL && | ||
238 | ELF64_ST_BIND(sym->st_info) != STB_WEAK) | ||
239 | continue; | ||
240 | if (sym->st_shndx == SHN_UNDEF) | ||
241 | continue; | ||
242 | if (strcmp(name, vdso_info.symstrings + sym->st_name)) | ||
243 | continue; | ||
244 | |||
245 | /* Check symbol version. */ | ||
246 | if (vdso_info.versym | ||
247 | && !vdso_match_version(vdso_info.versym[chain], | ||
248 | version, ver_hash)) | ||
249 | continue; | ||
250 | |||
251 | return (void *)(vdso_info.load_offset + sym->st_value); | ||
252 | } | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | void vdso_init_from_auxv(void *auxv) | ||
258 | { | ||
259 | ELF(auxv_t) *elf_auxv = auxv; | ||
260 | for (int i = 0; elf_auxv[i].a_type != AT_NULL; i++) | ||
261 | { | ||
262 | if (elf_auxv[i].a_type == AT_SYSINFO_EHDR) { | ||
263 | vdso_init_from_sysinfo_ehdr(elf_auxv[i].a_un.a_val); | ||
264 | return; | ||
265 | } | ||
266 | } | ||
267 | |||
268 | vdso_info.valid = false; | ||
269 | } | ||
diff --git a/tools/testing/selftests/vDSO/vdso_standalone_test_x86.c b/tools/testing/selftests/vDSO/vdso_standalone_test_x86.c new file mode 100644 index 000000000000..93b0ebf8cc38 --- /dev/null +++ b/tools/testing/selftests/vDSO/vdso_standalone_test_x86.c | |||
@@ -0,0 +1,128 @@ | |||
1 | /* | ||
2 | * vdso_test.c: Sample code to test parse_vdso.c on x86 | ||
3 | * Copyright (c) 2011-2014 Andy Lutomirski | ||
4 | * Subject to the GNU General Public License, version 2 | ||
5 | * | ||
6 | * You can amuse yourself by compiling with: | ||
7 | * gcc -std=gnu99 -nostdlib | ||
8 | * -Os -fno-asynchronous-unwind-tables -flto -lgcc_s | ||
9 | * vdso_standalone_test_x86.c parse_vdso.c | ||
10 | * to generate a small binary. On x86_64, you can omit -lgcc_s | ||
11 | * if you want the binary to be completely standalone. | ||
12 | */ | ||
13 | |||
14 | #include <sys/syscall.h> | ||
15 | #include <sys/time.h> | ||
16 | #include <unistd.h> | ||
17 | #include <stdint.h> | ||
18 | |||
19 | extern void *vdso_sym(const char *version, const char *name); | ||
20 | extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); | ||
21 | extern void vdso_init_from_auxv(void *auxv); | ||
22 | |||
23 | /* We need a libc functions... */ | ||
24 | int strcmp(const char *a, const char *b) | ||
25 | { | ||
26 | /* This implementation is buggy: it never returns -1. */ | ||
27 | while (*a || *b) { | ||
28 | if (*a != *b) | ||
29 | return 1; | ||
30 | if (*a == 0 || *b == 0) | ||
31 | return 1; | ||
32 | a++; | ||
33 | b++; | ||
34 | } | ||
35 | |||
36 | return 0; | ||
37 | } | ||
38 | |||
39 | /* ...and two syscalls. This is x86-specific. */ | ||
40 | static inline long x86_syscall3(long nr, long a0, long a1, long a2) | ||
41 | { | ||
42 | long ret; | ||
43 | #ifdef __x86_64__ | ||
44 | asm volatile ("syscall" : "=a" (ret) : "a" (nr), | ||
45 | "D" (a0), "S" (a1), "d" (a2) : | ||
46 | "cc", "memory", "rcx", | ||
47 | "r8", "r9", "r10", "r11" ); | ||
48 | #else | ||
49 | asm volatile ("int $0x80" : "=a" (ret) : "a" (nr), | ||
50 | "b" (a0), "c" (a1), "d" (a2) : | ||
51 | "cc", "memory" ); | ||
52 | #endif | ||
53 | return ret; | ||
54 | } | ||
55 | |||
56 | static inline long linux_write(int fd, const void *data, size_t len) | ||
57 | { | ||
58 | return x86_syscall3(__NR_write, fd, (long)data, (long)len); | ||
59 | } | ||
60 | |||
61 | static inline void linux_exit(int code) | ||
62 | { | ||
63 | x86_syscall3(__NR_exit, code, 0, 0); | ||
64 | } | ||
65 | |||
66 | void to_base10(char *lastdig, time_t n) | ||
67 | { | ||
68 | while (n) { | ||
69 | *lastdig = (n % 10) + '0'; | ||
70 | n /= 10; | ||
71 | lastdig--; | ||
72 | } | ||
73 | } | ||
74 | |||
75 | __attribute__((externally_visible)) void c_main(void **stack) | ||
76 | { | ||
77 | /* Parse the stack */ | ||
78 | long argc = (long)*stack; | ||
79 | stack += argc + 2; | ||
80 | |||
81 | /* Now we're pointing at the environment. Skip it. */ | ||
82 | while(*stack) | ||
83 | stack++; | ||
84 | stack++; | ||
85 | |||
86 | /* Now we're pointing at auxv. Initialize the vDSO parser. */ | ||
87 | vdso_init_from_auxv((void *)stack); | ||
88 | |||
89 | /* Find gettimeofday. */ | ||
90 | typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); | ||
91 | gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday"); | ||
92 | |||
93 | if (!gtod) | ||
94 | linux_exit(1); | ||
95 | |||
96 | struct timeval tv; | ||
97 | long ret = gtod(&tv, 0); | ||
98 | |||
99 | if (ret == 0) { | ||
100 | char buf[] = "The time is .000000\n"; | ||
101 | to_base10(buf + 31, tv.tv_sec); | ||
102 | to_base10(buf + 38, tv.tv_usec); | ||
103 | linux_write(1, buf, sizeof(buf) - 1); | ||
104 | } else { | ||
105 | linux_exit(ret); | ||
106 | } | ||
107 | |||
108 | linux_exit(0); | ||
109 | } | ||
110 | |||
111 | /* | ||
112 | * This is the real entry point. It passes the initial stack into | ||
113 | * the C entry point. | ||
114 | */ | ||
115 | asm ( | ||
116 | ".text\n" | ||
117 | ".global _start\n" | ||
118 | ".type _start,@function\n" | ||
119 | "_start:\n\t" | ||
120 | #ifdef __x86_64__ | ||
121 | "mov %rsp,%rdi\n\t" | ||
122 | "jmp c_main" | ||
123 | #else | ||
124 | "push %esp\n\t" | ||
125 | "call c_main\n\t" | ||
126 | "int $3" | ||
127 | #endif | ||
128 | ); | ||
diff --git a/tools/testing/selftests/vDSO/vdso_test.c b/tools/testing/selftests/vDSO/vdso_test.c new file mode 100644 index 000000000000..8daeb7d7032c --- /dev/null +++ b/tools/testing/selftests/vDSO/vdso_test.c | |||
@@ -0,0 +1,52 @@ | |||
1 | /* | ||
2 | * vdso_test.c: Sample code to test parse_vdso.c | ||
3 | * Copyright (c) 2014 Andy Lutomirski | ||
4 | * Subject to the GNU General Public License, version 2 | ||
5 | * | ||
6 | * Compile with: | ||
7 | * gcc -std=gnu99 vdso_test.c parse_vdso.c | ||
8 | * | ||
9 | * Tested on x86, 32-bit and 64-bit. It may work on other architectures, too. | ||
10 | */ | ||
11 | |||
12 | #include <stdint.h> | ||
13 | #include <elf.h> | ||
14 | #include <stdio.h> | ||
15 | #include <sys/auxv.h> | ||
16 | #include <sys/time.h> | ||
17 | |||
18 | extern void *vdso_sym(const char *version, const char *name); | ||
19 | extern void vdso_init_from_sysinfo_ehdr(uintptr_t base); | ||
20 | extern void vdso_init_from_auxv(void *auxv); | ||
21 | |||
22 | int main(int argc, char **argv) | ||
23 | { | ||
24 | unsigned long sysinfo_ehdr = getauxval(AT_SYSINFO_EHDR); | ||
25 | if (!sysinfo_ehdr) { | ||
26 | printf("AT_SYSINFO_EHDR is not present!\n"); | ||
27 | return 0; | ||
28 | } | ||
29 | |||
30 | vdso_init_from_sysinfo_ehdr(getauxval(AT_SYSINFO_EHDR)); | ||
31 | |||
32 | /* Find gettimeofday. */ | ||
33 | typedef long (*gtod_t)(struct timeval *tv, struct timezone *tz); | ||
34 | gtod_t gtod = (gtod_t)vdso_sym("LINUX_2.6", "__vdso_gettimeofday"); | ||
35 | |||
36 | if (!gtod) { | ||
37 | printf("Could not find __vdso_gettimeofday\n"); | ||
38 | return 1; | ||
39 | } | ||
40 | |||
41 | struct timeval tv; | ||
42 | long ret = gtod(&tv, 0); | ||
43 | |||
44 | if (ret == 0) { | ||
45 | printf("The time is %lld.%06lld\n", | ||
46 | (long long)tv.tv_sec, (long long)tv.tv_usec); | ||
47 | } else { | ||
48 | printf("__vdso_gettimeofday failed\n"); | ||
49 | } | ||
50 | |||
51 | return 0; | ||
52 | } | ||
diff --git a/tools/testing/selftests/vm/.gitignore b/tools/testing/selftests/vm/.gitignore index a937a9d26b60..142c565bb351 100644 --- a/tools/testing/selftests/vm/.gitignore +++ b/tools/testing/selftests/vm/.gitignore | |||
@@ -7,3 +7,4 @@ mlock2-tests | |||
7 | on-fault-limit | 7 | on-fault-limit |
8 | transhuge-stress | 8 | transhuge-stress |
9 | userfaultfd | 9 | userfaultfd |
10 | mlock-intersect-test | ||
diff --git a/tools/testing/selftests/vm/Makefile b/tools/testing/selftests/vm/Makefile index e4bb1de1d526..bbab7f4664ac 100644 --- a/tools/testing/selftests/vm/Makefile +++ b/tools/testing/selftests/vm/Makefile | |||
@@ -10,6 +10,7 @@ BINARIES += on-fault-limit | |||
10 | BINARIES += thuge-gen | 10 | BINARIES += thuge-gen |
11 | BINARIES += transhuge-stress | 11 | BINARIES += transhuge-stress |
12 | BINARIES += userfaultfd | 12 | BINARIES += userfaultfd |
13 | BINARIES += mlock-random-test | ||
13 | 14 | ||
14 | all: $(BINARIES) | 15 | all: $(BINARIES) |
15 | %: %.c | 16 | %: %.c |
@@ -17,6 +18,9 @@ all: $(BINARIES) | |||
17 | userfaultfd: userfaultfd.c ../../../../usr/include/linux/kernel.h | 18 | userfaultfd: userfaultfd.c ../../../../usr/include/linux/kernel.h |
18 | $(CC) $(CFLAGS) -O2 -o $@ $< -lpthread | 19 | $(CC) $(CFLAGS) -O2 -o $@ $< -lpthread |
19 | 20 | ||
21 | mlock-random-test: mlock-random-test.c | ||
22 | $(CC) $(CFLAGS) -o $@ $< -lcap | ||
23 | |||
20 | ../../../../usr/include/linux/kernel.h: | 24 | ../../../../usr/include/linux/kernel.h: |
21 | make -C ../../../.. headers_install | 25 | make -C ../../../.. headers_install |
22 | 26 | ||
diff --git a/tools/testing/selftests/vm/mlock-random-test.c b/tools/testing/selftests/vm/mlock-random-test.c new file mode 100644 index 000000000000..83de4f58d262 --- /dev/null +++ b/tools/testing/selftests/vm/mlock-random-test.c | |||
@@ -0,0 +1,293 @@ | |||
1 | /* | ||
2 | * It tests the mlock/mlock2() when they are invoked | ||
3 | * on randomly memory region. | ||
4 | */ | ||
5 | #include <unistd.h> | ||
6 | #include <sys/resource.h> | ||
7 | #include <sys/capability.h> | ||
8 | #include <sys/mman.h> | ||
9 | #include <fcntl.h> | ||
10 | #include <string.h> | ||
11 | #include <sys/ipc.h> | ||
12 | #include <sys/shm.h> | ||
13 | #include <time.h> | ||
14 | #include "mlock2.h" | ||
15 | |||
16 | #define CHUNK_UNIT (128 * 1024) | ||
17 | #define MLOCK_RLIMIT_SIZE (CHUNK_UNIT * 2) | ||
18 | #define MLOCK_WITHIN_LIMIT_SIZE CHUNK_UNIT | ||
19 | #define MLOCK_OUTOF_LIMIT_SIZE (CHUNK_UNIT * 3) | ||
20 | |||
21 | #define TEST_LOOP 100 | ||
22 | #define PAGE_ALIGN(size, ps) (((size) + ((ps) - 1)) & ~((ps) - 1)) | ||
23 | |||
24 | int set_cap_limits(rlim_t max) | ||
25 | { | ||
26 | struct rlimit new; | ||
27 | cap_t cap = cap_init(); | ||
28 | |||
29 | new.rlim_cur = max; | ||
30 | new.rlim_max = max; | ||
31 | if (setrlimit(RLIMIT_MEMLOCK, &new)) { | ||
32 | perror("setrlimit() returns error\n"); | ||
33 | return -1; | ||
34 | } | ||
35 | |||
36 | /* drop capabilities including CAP_IPC_LOCK */ | ||
37 | if (cap_set_proc(cap)) { | ||
38 | perror("cap_set_proc() returns error\n"); | ||
39 | return -2; | ||
40 | } | ||
41 | |||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | int get_proc_locked_vm_size(void) | ||
46 | { | ||
47 | FILE *f; | ||
48 | int ret = -1; | ||
49 | char line[1024] = {0}; | ||
50 | unsigned long lock_size = 0; | ||
51 | |||
52 | f = fopen("/proc/self/status", "r"); | ||
53 | if (!f) { | ||
54 | perror("fopen"); | ||
55 | return -1; | ||
56 | } | ||
57 | |||
58 | while (fgets(line, 1024, f)) { | ||
59 | if (strstr(line, "VmLck")) { | ||
60 | ret = sscanf(line, "VmLck:\t%8lu kB", &lock_size); | ||
61 | if (ret <= 0) { | ||
62 | printf("sscanf() on VmLck error: %s: %d\n", | ||
63 | line, ret); | ||
64 | fclose(f); | ||
65 | return -1; | ||
66 | } | ||
67 | fclose(f); | ||
68 | return (int)(lock_size << 10); | ||
69 | } | ||
70 | } | ||
71 | |||
72 | perror("cann't parse VmLck in /proc/self/status\n"); | ||
73 | fclose(f); | ||
74 | return -1; | ||
75 | } | ||
76 | |||
77 | /* | ||
78 | * Get the MMUPageSize of the memory region including input | ||
79 | * address from proc file. | ||
80 | * | ||
81 | * return value: on error case, 0 will be returned. | ||
82 | * Otherwise the page size(in bytes) is returned. | ||
83 | */ | ||
84 | int get_proc_page_size(unsigned long addr) | ||
85 | { | ||
86 | FILE *smaps; | ||
87 | char *line; | ||
88 | unsigned long mmupage_size = 0; | ||
89 | size_t size; | ||
90 | |||
91 | smaps = seek_to_smaps_entry(addr); | ||
92 | if (!smaps) { | ||
93 | printf("Unable to parse /proc/self/smaps\n"); | ||
94 | return 0; | ||
95 | } | ||
96 | |||
97 | while (getline(&line, &size, smaps) > 0) { | ||
98 | if (!strstr(line, "MMUPageSize")) { | ||
99 | free(line); | ||
100 | line = NULL; | ||
101 | size = 0; | ||
102 | continue; | ||
103 | } | ||
104 | |||
105 | /* found the MMUPageSize of this section */ | ||
106 | if (sscanf(line, "MMUPageSize: %8lu kB", | ||
107 | &mmupage_size) < 1) { | ||
108 | printf("Unable to parse smaps entry for Size:%s\n", | ||
109 | line); | ||
110 | break; | ||
111 | } | ||
112 | |||
113 | } | ||
114 | free(line); | ||
115 | if (smaps) | ||
116 | fclose(smaps); | ||
117 | return mmupage_size << 10; | ||
118 | } | ||
119 | |||
120 | /* | ||
121 | * Test mlock/mlock2() on provided memory chunk. | ||
122 | * It expects the mlock/mlock2() to be successful (within rlimit) | ||
123 | * | ||
124 | * With allocated memory chunk [p, p + alloc_size), this | ||
125 | * test will choose start/len randomly to perform mlock/mlock2 | ||
126 | * [start, start + len] memory range. The range is within range | ||
127 | * of the allocated chunk. | ||
128 | * | ||
129 | * The memory region size alloc_size is within the rlimit. | ||
130 | * So we always expect a success of mlock/mlock2. | ||
131 | * | ||
132 | * VmLck is assumed to be 0 before this test. | ||
133 | * | ||
134 | * return value: 0 - success | ||
135 | * else: failure | ||
136 | */ | ||
137 | int test_mlock_within_limit(char *p, int alloc_size) | ||
138 | { | ||
139 | int i; | ||
140 | int ret = 0; | ||
141 | int locked_vm_size = 0; | ||
142 | struct rlimit cur; | ||
143 | int page_size = 0; | ||
144 | |||
145 | getrlimit(RLIMIT_MEMLOCK, &cur); | ||
146 | if (cur.rlim_cur < alloc_size) { | ||
147 | printf("alloc_size[%d] < %u rlimit,lead to mlock failure\n", | ||
148 | alloc_size, (unsigned int)cur.rlim_cur); | ||
149 | return -1; | ||
150 | } | ||
151 | |||
152 | srand(time(NULL)); | ||
153 | for (i = 0; i < TEST_LOOP; i++) { | ||
154 | /* | ||
155 | * - choose mlock/mlock2 randomly | ||
156 | * - choose lock_size randomly but lock_size < alloc_size | ||
157 | * - choose start_offset randomly but p+start_offset+lock_size | ||
158 | * < p+alloc_size | ||
159 | */ | ||
160 | int is_mlock = !!(rand() % 2); | ||
161 | int lock_size = rand() % alloc_size; | ||
162 | int start_offset = rand() % (alloc_size - lock_size); | ||
163 | |||
164 | if (is_mlock) | ||
165 | ret = mlock(p + start_offset, lock_size); | ||
166 | else | ||
167 | ret = mlock2_(p + start_offset, lock_size, | ||
168 | MLOCK_ONFAULT); | ||
169 | |||
170 | if (ret) { | ||
171 | printf("%s() failure at |%p(%d)| mlock:|%p(%d)|\n", | ||
172 | is_mlock ? "mlock" : "mlock2", | ||
173 | p, alloc_size, | ||
174 | p + start_offset, lock_size); | ||
175 | return ret; | ||
176 | } | ||
177 | } | ||
178 | |||
179 | /* | ||
180 | * Check VmLck left by the tests. | ||
181 | */ | ||
182 | locked_vm_size = get_proc_locked_vm_size(); | ||
183 | page_size = get_proc_page_size((unsigned long)p); | ||
184 | if (page_size == 0) { | ||
185 | printf("cannot get proc MMUPageSize\n"); | ||
186 | return -1; | ||
187 | } | ||
188 | |||
189 | if (locked_vm_size > PAGE_ALIGN(alloc_size, page_size) + page_size) { | ||
190 | printf("test_mlock_within_limit() left VmLck:%d on %d chunk\n", | ||
191 | locked_vm_size, alloc_size); | ||
192 | return -1; | ||
193 | } | ||
194 | |||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | |||
199 | /* | ||
200 | * We expect the mlock/mlock2() to be fail (outof limitation) | ||
201 | * | ||
202 | * With allocated memory chunk [p, p + alloc_size), this | ||
203 | * test will randomly choose start/len and perform mlock/mlock2 | ||
204 | * on [start, start+len] range. | ||
205 | * | ||
206 | * The memory region size alloc_size is above the rlimit. | ||
207 | * And the len to be locked is higher than rlimit. | ||
208 | * So we always expect a failure of mlock/mlock2. | ||
209 | * No locked page number should be increased as a side effect. | ||
210 | * | ||
211 | * return value: 0 - success | ||
212 | * else: failure | ||
213 | */ | ||
214 | int test_mlock_outof_limit(char *p, int alloc_size) | ||
215 | { | ||
216 | int i; | ||
217 | int ret = 0; | ||
218 | int locked_vm_size = 0, old_locked_vm_size = 0; | ||
219 | struct rlimit cur; | ||
220 | |||
221 | getrlimit(RLIMIT_MEMLOCK, &cur); | ||
222 | if (cur.rlim_cur >= alloc_size) { | ||
223 | printf("alloc_size[%d] >%u rlimit, violates test condition\n", | ||
224 | alloc_size, (unsigned int)cur.rlim_cur); | ||
225 | return -1; | ||
226 | } | ||
227 | |||
228 | old_locked_vm_size = get_proc_locked_vm_size(); | ||
229 | srand(time(NULL)); | ||
230 | for (i = 0; i < TEST_LOOP; i++) { | ||
231 | int is_mlock = !!(rand() % 2); | ||
232 | int lock_size = (rand() % (alloc_size - cur.rlim_cur)) | ||
233 | + cur.rlim_cur; | ||
234 | int start_offset = rand() % (alloc_size - lock_size); | ||
235 | |||
236 | if (is_mlock) | ||
237 | ret = mlock(p + start_offset, lock_size); | ||
238 | else | ||
239 | ret = mlock2_(p + start_offset, lock_size, | ||
240 | MLOCK_ONFAULT); | ||
241 | if (ret == 0) { | ||
242 | printf("%s() succeeds? on %p(%d) mlock%p(%d)\n", | ||
243 | is_mlock ? "mlock" : "mlock2", | ||
244 | p, alloc_size, | ||
245 | p + start_offset, lock_size); | ||
246 | return -1; | ||
247 | } | ||
248 | } | ||
249 | |||
250 | locked_vm_size = get_proc_locked_vm_size(); | ||
251 | if (locked_vm_size != old_locked_vm_size) { | ||
252 | printf("tests leads to new mlocked page: old[%d], new[%d]\n", | ||
253 | old_locked_vm_size, | ||
254 | locked_vm_size); | ||
255 | return -1; | ||
256 | } | ||
257 | |||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | int main(int argc, char **argv) | ||
262 | { | ||
263 | char *p = NULL; | ||
264 | int ret = 0; | ||
265 | |||
266 | if (set_cap_limits(MLOCK_RLIMIT_SIZE)) | ||
267 | return -1; | ||
268 | |||
269 | p = malloc(MLOCK_WITHIN_LIMIT_SIZE); | ||
270 | if (p == NULL) { | ||
271 | perror("malloc() failure\n"); | ||
272 | return -1; | ||
273 | } | ||
274 | ret = test_mlock_within_limit(p, MLOCK_WITHIN_LIMIT_SIZE); | ||
275 | if (ret) | ||
276 | return ret; | ||
277 | munlock(p, MLOCK_WITHIN_LIMIT_SIZE); | ||
278 | free(p); | ||
279 | |||
280 | |||
281 | p = malloc(MLOCK_OUTOF_LIMIT_SIZE); | ||
282 | if (p == NULL) { | ||
283 | perror("malloc() failure\n"); | ||
284 | return -1; | ||
285 | } | ||
286 | ret = test_mlock_outof_limit(p, MLOCK_OUTOF_LIMIT_SIZE); | ||
287 | if (ret) | ||
288 | return ret; | ||
289 | munlock(p, MLOCK_OUTOF_LIMIT_SIZE); | ||
290 | free(p); | ||
291 | |||
292 | return 0; | ||
293 | } | ||
diff --git a/tools/testing/selftests/vm/mlock2-tests.c b/tools/testing/selftests/vm/mlock2-tests.c index 02ca5e0177c5..ff0cda2b19c9 100644 --- a/tools/testing/selftests/vm/mlock2-tests.c +++ b/tools/testing/selftests/vm/mlock2-tests.c | |||
@@ -1,33 +1,12 @@ | |||
1 | #define _GNU_SOURCE | 1 | #define _GNU_SOURCE |
2 | #include <sys/mman.h> | 2 | #include <sys/mman.h> |
3 | #include <stdint.h> | 3 | #include <stdint.h> |
4 | #include <stdio.h> | ||
5 | #include <stdlib.h> | ||
6 | #include <unistd.h> | 4 | #include <unistd.h> |
7 | #include <string.h> | 5 | #include <string.h> |
8 | #include <sys/time.h> | 6 | #include <sys/time.h> |
9 | #include <sys/resource.h> | 7 | #include <sys/resource.h> |
10 | #include <syscall.h> | ||
11 | #include <errno.h> | ||
12 | #include <stdbool.h> | 8 | #include <stdbool.h> |
13 | 9 | #include "mlock2.h" | |
14 | #ifndef MLOCK_ONFAULT | ||
15 | #define MLOCK_ONFAULT 1 | ||
16 | #endif | ||
17 | |||
18 | #ifndef MCL_ONFAULT | ||
19 | #define MCL_ONFAULT (MCL_FUTURE << 1) | ||
20 | #endif | ||
21 | |||
22 | static int mlock2_(void *start, size_t len, int flags) | ||
23 | { | ||
24 | #ifdef __NR_mlock2 | ||
25 | return syscall(__NR_mlock2, start, len, flags); | ||
26 | #else | ||
27 | errno = ENOSYS; | ||
28 | return -1; | ||
29 | #endif | ||
30 | } | ||
31 | 10 | ||
32 | struct vm_boundaries { | 11 | struct vm_boundaries { |
33 | unsigned long start; | 12 | unsigned long start; |
@@ -138,46 +117,6 @@ static uint64_t get_kpageflags(unsigned long pfn) | |||
138 | return flags; | 117 | return flags; |
139 | } | 118 | } |
140 | 119 | ||
141 | static FILE *seek_to_smaps_entry(unsigned long addr) | ||
142 | { | ||
143 | FILE *file; | ||
144 | char *line = NULL; | ||
145 | size_t size = 0; | ||
146 | unsigned long start, end; | ||
147 | char perms[5]; | ||
148 | unsigned long offset; | ||
149 | char dev[32]; | ||
150 | unsigned long inode; | ||
151 | char path[BUFSIZ]; | ||
152 | |||
153 | file = fopen("/proc/self/smaps", "r"); | ||
154 | if (!file) { | ||
155 | perror("fopen smaps"); | ||
156 | _exit(1); | ||
157 | } | ||
158 | |||
159 | while (getline(&line, &size, file) > 0) { | ||
160 | if (sscanf(line, "%lx-%lx %s %lx %s %lu %s\n", | ||
161 | &start, &end, perms, &offset, dev, &inode, path) < 6) | ||
162 | goto next; | ||
163 | |||
164 | if (start <= addr && addr < end) | ||
165 | goto out; | ||
166 | |||
167 | next: | ||
168 | free(line); | ||
169 | line = NULL; | ||
170 | size = 0; | ||
171 | } | ||
172 | |||
173 | fclose(file); | ||
174 | file = NULL; | ||
175 | |||
176 | out: | ||
177 | free(line); | ||
178 | return file; | ||
179 | } | ||
180 | |||
181 | #define VMFLAGS "VmFlags:" | 120 | #define VMFLAGS "VmFlags:" |
182 | 121 | ||
183 | static bool is_vmflag_set(unsigned long addr, const char *vmflag) | 122 | static bool is_vmflag_set(unsigned long addr, const char *vmflag) |
diff --git a/tools/testing/selftests/vm/mlock2.h b/tools/testing/selftests/vm/mlock2.h new file mode 100644 index 000000000000..7ee062929d3e --- /dev/null +++ b/tools/testing/selftests/vm/mlock2.h | |||
@@ -0,0 +1,62 @@ | |||
1 | #include <syscall.h> | ||
2 | #include <errno.h> | ||
3 | #include <stdio.h> | ||
4 | #include <stdlib.h> | ||
5 | |||
6 | #ifndef MLOCK_ONFAULT | ||
7 | #define MLOCK_ONFAULT 1 | ||
8 | #endif | ||
9 | |||
10 | #ifndef MCL_ONFAULT | ||
11 | #define MCL_ONFAULT (MCL_FUTURE << 1) | ||
12 | #endif | ||
13 | |||
14 | static int mlock2_(void *start, size_t len, int flags) | ||
15 | { | ||
16 | #ifdef __NR_mlock2 | ||
17 | return syscall(__NR_mlock2, start, len, flags); | ||
18 | #else | ||
19 | errno = ENOSYS; | ||
20 | return -1; | ||
21 | #endif | ||
22 | } | ||
23 | |||
24 | static FILE *seek_to_smaps_entry(unsigned long addr) | ||
25 | { | ||
26 | FILE *file; | ||
27 | char *line = NULL; | ||
28 | size_t size = 0; | ||
29 | unsigned long start, end; | ||
30 | char perms[5]; | ||
31 | unsigned long offset; | ||
32 | char dev[32]; | ||
33 | unsigned long inode; | ||
34 | char path[BUFSIZ]; | ||
35 | |||
36 | file = fopen("/proc/self/smaps", "r"); | ||
37 | if (!file) { | ||
38 | perror("fopen smaps"); | ||
39 | _exit(1); | ||
40 | } | ||
41 | |||
42 | while (getline(&line, &size, file) > 0) { | ||
43 | if (sscanf(line, "%lx-%lx %s %lx %s %lu %s\n", | ||
44 | &start, &end, perms, &offset, dev, &inode, path) < 6) | ||
45 | goto next; | ||
46 | |||
47 | if (start <= addr && addr < end) | ||
48 | goto out; | ||
49 | |||
50 | next: | ||
51 | free(line); | ||
52 | line = NULL; | ||
53 | size = 0; | ||
54 | } | ||
55 | |||
56 | fclose(file); | ||
57 | file = NULL; | ||
58 | |||
59 | out: | ||
60 | free(line); | ||
61 | return file; | ||
62 | } | ||
diff --git a/tools/testing/selftests/watchdog/.gitignore b/tools/testing/selftests/watchdog/.gitignore new file mode 100644 index 000000000000..5aac51575c7e --- /dev/null +++ b/tools/testing/selftests/watchdog/.gitignore | |||
@@ -0,0 +1 @@ | |||
watchdog-test | |||
diff --git a/tools/testing/selftests/watchdog/Makefile b/tools/testing/selftests/watchdog/Makefile new file mode 100644 index 000000000000..f863c664e3d1 --- /dev/null +++ b/tools/testing/selftests/watchdog/Makefile | |||
@@ -0,0 +1,8 @@ | |||
1 | TEST_PROGS := watchdog-test | ||
2 | |||
3 | all: $(TEST_PROGS) | ||
4 | |||
5 | include ../lib.mk | ||
6 | |||
7 | clean: | ||
8 | rm -fr $(TEST_PROGS) | ||
diff --git a/tools/testing/selftests/watchdog/watchdog-test.c b/tools/testing/selftests/watchdog/watchdog-test.c new file mode 100644 index 000000000000..6983d05097e2 --- /dev/null +++ b/tools/testing/selftests/watchdog/watchdog-test.c | |||
@@ -0,0 +1,105 @@ | |||
1 | /* | ||
2 | * Watchdog Driver Test Program | ||
3 | */ | ||
4 | |||
5 | #include <errno.h> | ||
6 | #include <stdio.h> | ||
7 | #include <stdlib.h> | ||
8 | #include <string.h> | ||
9 | #include <unistd.h> | ||
10 | #include <fcntl.h> | ||
11 | #include <signal.h> | ||
12 | #include <sys/ioctl.h> | ||
13 | #include <linux/types.h> | ||
14 | #include <linux/watchdog.h> | ||
15 | |||
16 | int fd; | ||
17 | const char v = 'V'; | ||
18 | |||
19 | /* | ||
20 | * This function simply sends an IOCTL to the driver, which in turn ticks | ||
21 | * the PC Watchdog card to reset its internal timer so it doesn't trigger | ||
22 | * a computer reset. | ||
23 | */ | ||
24 | static void keep_alive(void) | ||
25 | { | ||
26 | int dummy; | ||
27 | |||
28 | printf("."); | ||
29 | ioctl(fd, WDIOC_KEEPALIVE, &dummy); | ||
30 | } | ||
31 | |||
32 | /* | ||
33 | * The main program. Run the program with "-d" to disable the card, | ||
34 | * or "-e" to enable the card. | ||
35 | */ | ||
36 | |||
37 | static void term(int sig) | ||
38 | { | ||
39 | int ret = write(fd, &v, 1); | ||
40 | |||
41 | close(fd); | ||
42 | if (ret < 0) | ||
43 | printf("\nStopping watchdog ticks failed (%d)...\n", errno); | ||
44 | else | ||
45 | printf("\nStopping watchdog ticks...\n"); | ||
46 | exit(0); | ||
47 | } | ||
48 | |||
49 | int main(int argc, char *argv[]) | ||
50 | { | ||
51 | int flags; | ||
52 | unsigned int ping_rate = 1; | ||
53 | int ret; | ||
54 | |||
55 | setbuf(stdout, NULL); | ||
56 | |||
57 | fd = open("/dev/watchdog", O_WRONLY); | ||
58 | |||
59 | if (fd == -1) { | ||
60 | printf("Watchdog device not enabled.\n"); | ||
61 | exit(-1); | ||
62 | } | ||
63 | |||
64 | if (argc > 1) { | ||
65 | if (!strncasecmp(argv[1], "-d", 2)) { | ||
66 | flags = WDIOS_DISABLECARD; | ||
67 | ioctl(fd, WDIOC_SETOPTIONS, &flags); | ||
68 | printf("Watchdog card disabled.\n"); | ||
69 | goto end; | ||
70 | } else if (!strncasecmp(argv[1], "-e", 2)) { | ||
71 | flags = WDIOS_ENABLECARD; | ||
72 | ioctl(fd, WDIOC_SETOPTIONS, &flags); | ||
73 | printf("Watchdog card enabled.\n"); | ||
74 | goto end; | ||
75 | } else if (!strncasecmp(argv[1], "-t", 2) && argv[2]) { | ||
76 | flags = atoi(argv[2]); | ||
77 | ioctl(fd, WDIOC_SETTIMEOUT, &flags); | ||
78 | printf("Watchdog timeout set to %u seconds.\n", flags); | ||
79 | goto end; | ||
80 | } else if (!strncasecmp(argv[1], "-p", 2) && argv[2]) { | ||
81 | ping_rate = strtoul(argv[2], NULL, 0); | ||
82 | printf("Watchdog ping rate set to %u seconds.\n", ping_rate); | ||
83 | } else { | ||
84 | printf("-d to disable, -e to enable, -t <n> to set " \ | ||
85 | "the timeout,\n-p <n> to set the ping rate, and \n"); | ||
86 | printf("run by itself to tick the card.\n"); | ||
87 | goto end; | ||
88 | } | ||
89 | } | ||
90 | |||
91 | printf("Watchdog Ticking Away!\n"); | ||
92 | |||
93 | signal(SIGINT, term); | ||
94 | |||
95 | while(1) { | ||
96 | keep_alive(); | ||
97 | sleep(ping_rate); | ||
98 | } | ||
99 | end: | ||
100 | ret = write(fd, &v, 1); | ||
101 | if (ret < 0) | ||
102 | printf("Stopping watchdog ticks failed (%d)...\n", errno); | ||
103 | close(fd); | ||
104 | return 0; | ||
105 | } | ||
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index 4f747ee07f10..a89f80a5b711 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile | |||
@@ -5,7 +5,8 @@ include ../lib.mk | |||
5 | .PHONY: all all_32 all_64 warn_32bit_failure clean | 5 | .PHONY: all all_32 all_64 warn_32bit_failure clean |
6 | 6 | ||
7 | TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_syscall test_mremap_vdso \ | 7 | TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_syscall test_mremap_vdso \ |
8 | check_initial_reg_state sigreturn ldt_gdt iopl mpx-mini-test | 8 | check_initial_reg_state sigreturn ldt_gdt iopl \ |
9 | protection_keys | ||
9 | TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \ | 10 | TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \ |
10 | test_FCMOV test_FCOMI test_FISTTP \ | 11 | test_FCMOV test_FCOMI test_FISTTP \ |
11 | vdso_restorer | 12 | vdso_restorer |
diff --git a/tools/testing/selftests/x86/pkey-helpers.h b/tools/testing/selftests/x86/pkey-helpers.h new file mode 100644 index 000000000000..b20293956eec --- /dev/null +++ b/tools/testing/selftests/x86/pkey-helpers.h | |||
@@ -0,0 +1,219 @@ | |||
1 | #ifndef _PKEYS_HELPER_H | ||
2 | #define _PKEYS_HELPER_H | ||
3 | #define _GNU_SOURCE | ||
4 | #include <string.h> | ||
5 | #include <stdarg.h> | ||
6 | #include <stdio.h> | ||
7 | #include <stdint.h> | ||
8 | #include <stdbool.h> | ||
9 | #include <signal.h> | ||
10 | #include <assert.h> | ||
11 | #include <stdlib.h> | ||
12 | #include <ucontext.h> | ||
13 | #include <sys/mman.h> | ||
14 | |||
15 | #define NR_PKEYS 16 | ||
16 | #define PKRU_BITS_PER_PKEY 2 | ||
17 | |||
18 | #ifndef DEBUG_LEVEL | ||
19 | #define DEBUG_LEVEL 0 | ||
20 | #endif | ||
21 | #define DPRINT_IN_SIGNAL_BUF_SIZE 4096 | ||
22 | extern int dprint_in_signal; | ||
23 | extern char dprint_in_signal_buffer[DPRINT_IN_SIGNAL_BUF_SIZE]; | ||
24 | static inline void sigsafe_printf(const char *format, ...) | ||
25 | { | ||
26 | va_list ap; | ||
27 | |||
28 | va_start(ap, format); | ||
29 | if (!dprint_in_signal) { | ||
30 | vprintf(format, ap); | ||
31 | } else { | ||
32 | int len = vsnprintf(dprint_in_signal_buffer, | ||
33 | DPRINT_IN_SIGNAL_BUF_SIZE, | ||
34 | format, ap); | ||
35 | /* | ||
36 | * len is amount that would have been printed, | ||
37 | * but actual write is truncated at BUF_SIZE. | ||
38 | */ | ||
39 | if (len > DPRINT_IN_SIGNAL_BUF_SIZE) | ||
40 | len = DPRINT_IN_SIGNAL_BUF_SIZE; | ||
41 | write(1, dprint_in_signal_buffer, len); | ||
42 | } | ||
43 | va_end(ap); | ||
44 | } | ||
45 | #define dprintf_level(level, args...) do { \ | ||
46 | if (level <= DEBUG_LEVEL) \ | ||
47 | sigsafe_printf(args); \ | ||
48 | fflush(NULL); \ | ||
49 | } while (0) | ||
50 | #define dprintf0(args...) dprintf_level(0, args) | ||
51 | #define dprintf1(args...) dprintf_level(1, args) | ||
52 | #define dprintf2(args...) dprintf_level(2, args) | ||
53 | #define dprintf3(args...) dprintf_level(3, args) | ||
54 | #define dprintf4(args...) dprintf_level(4, args) | ||
55 | |||
56 | extern unsigned int shadow_pkru; | ||
57 | static inline unsigned int __rdpkru(void) | ||
58 | { | ||
59 | unsigned int eax, edx; | ||
60 | unsigned int ecx = 0; | ||
61 | unsigned int pkru; | ||
62 | |||
63 | asm volatile(".byte 0x0f,0x01,0xee\n\t" | ||
64 | : "=a" (eax), "=d" (edx) | ||
65 | : "c" (ecx)); | ||
66 | pkru = eax; | ||
67 | return pkru; | ||
68 | } | ||
69 | |||
70 | static inline unsigned int _rdpkru(int line) | ||
71 | { | ||
72 | unsigned int pkru = __rdpkru(); | ||
73 | |||
74 | dprintf4("rdpkru(line=%d) pkru: %x shadow: %x\n", | ||
75 | line, pkru, shadow_pkru); | ||
76 | assert(pkru == shadow_pkru); | ||
77 | |||
78 | return pkru; | ||
79 | } | ||
80 | |||
81 | #define rdpkru() _rdpkru(__LINE__) | ||
82 | |||
83 | static inline void __wrpkru(unsigned int pkru) | ||
84 | { | ||
85 | unsigned int eax = pkru; | ||
86 | unsigned int ecx = 0; | ||
87 | unsigned int edx = 0; | ||
88 | |||
89 | dprintf4("%s() changing %08x to %08x\n", __func__, __rdpkru(), pkru); | ||
90 | asm volatile(".byte 0x0f,0x01,0xef\n\t" | ||
91 | : : "a" (eax), "c" (ecx), "d" (edx)); | ||
92 | assert(pkru == __rdpkru()); | ||
93 | } | ||
94 | |||
95 | static inline void wrpkru(unsigned int pkru) | ||
96 | { | ||
97 | dprintf4("%s() changing %08x to %08x\n", __func__, __rdpkru(), pkru); | ||
98 | /* will do the shadow check for us: */ | ||
99 | rdpkru(); | ||
100 | __wrpkru(pkru); | ||
101 | shadow_pkru = pkru; | ||
102 | dprintf4("%s(%08x) pkru: %08x\n", __func__, pkru, __rdpkru()); | ||
103 | } | ||
104 | |||
105 | /* | ||
106 | * These are technically racy. since something could | ||
107 | * change PKRU between the read and the write. | ||
108 | */ | ||
109 | static inline void __pkey_access_allow(int pkey, int do_allow) | ||
110 | { | ||
111 | unsigned int pkru = rdpkru(); | ||
112 | int bit = pkey * 2; | ||
113 | |||
114 | if (do_allow) | ||
115 | pkru &= (1<<bit); | ||
116 | else | ||
117 | pkru |= (1<<bit); | ||
118 | |||
119 | dprintf4("pkru now: %08x\n", rdpkru()); | ||
120 | wrpkru(pkru); | ||
121 | } | ||
122 | |||
123 | static inline void __pkey_write_allow(int pkey, int do_allow_write) | ||
124 | { | ||
125 | long pkru = rdpkru(); | ||
126 | int bit = pkey * 2 + 1; | ||
127 | |||
128 | if (do_allow_write) | ||
129 | pkru &= (1<<bit); | ||
130 | else | ||
131 | pkru |= (1<<bit); | ||
132 | |||
133 | wrpkru(pkru); | ||
134 | dprintf4("pkru now: %08x\n", rdpkru()); | ||
135 | } | ||
136 | |||
137 | #define PROT_PKEY0 0x10 /* protection key value (bit 0) */ | ||
138 | #define PROT_PKEY1 0x20 /* protection key value (bit 1) */ | ||
139 | #define PROT_PKEY2 0x40 /* protection key value (bit 2) */ | ||
140 | #define PROT_PKEY3 0x80 /* protection key value (bit 3) */ | ||
141 | |||
142 | #define PAGE_SIZE 4096 | ||
143 | #define MB (1<<20) | ||
144 | |||
145 | static inline void __cpuid(unsigned int *eax, unsigned int *ebx, | ||
146 | unsigned int *ecx, unsigned int *edx) | ||
147 | { | ||
148 | /* ecx is often an input as well as an output. */ | ||
149 | asm volatile( | ||
150 | "cpuid;" | ||
151 | : "=a" (*eax), | ||
152 | "=b" (*ebx), | ||
153 | "=c" (*ecx), | ||
154 | "=d" (*edx) | ||
155 | : "0" (*eax), "2" (*ecx)); | ||
156 | } | ||
157 | |||
158 | /* Intel-defined CPU features, CPUID level 0x00000007:0 (ecx) */ | ||
159 | #define X86_FEATURE_PKU (1<<3) /* Protection Keys for Userspace */ | ||
160 | #define X86_FEATURE_OSPKE (1<<4) /* OS Protection Keys Enable */ | ||
161 | |||
162 | static inline int cpu_has_pku(void) | ||
163 | { | ||
164 | unsigned int eax; | ||
165 | unsigned int ebx; | ||
166 | unsigned int ecx; | ||
167 | unsigned int edx; | ||
168 | |||
169 | eax = 0x7; | ||
170 | ecx = 0x0; | ||
171 | __cpuid(&eax, &ebx, &ecx, &edx); | ||
172 | |||
173 | if (!(ecx & X86_FEATURE_PKU)) { | ||
174 | dprintf2("cpu does not have PKU\n"); | ||
175 | return 0; | ||
176 | } | ||
177 | if (!(ecx & X86_FEATURE_OSPKE)) { | ||
178 | dprintf2("cpu does not have OSPKE\n"); | ||
179 | return 0; | ||
180 | } | ||
181 | return 1; | ||
182 | } | ||
183 | |||
184 | #define XSTATE_PKRU_BIT (9) | ||
185 | #define XSTATE_PKRU 0x200 | ||
186 | |||
187 | int pkru_xstate_offset(void) | ||
188 | { | ||
189 | unsigned int eax; | ||
190 | unsigned int ebx; | ||
191 | unsigned int ecx; | ||
192 | unsigned int edx; | ||
193 | int xstate_offset; | ||
194 | int xstate_size; | ||
195 | unsigned long XSTATE_CPUID = 0xd; | ||
196 | int leaf; | ||
197 | |||
198 | /* assume that XSTATE_PKRU is set in XCR0 */ | ||
199 | leaf = XSTATE_PKRU_BIT; | ||
200 | { | ||
201 | eax = XSTATE_CPUID; | ||
202 | ecx = leaf; | ||
203 | __cpuid(&eax, &ebx, &ecx, &edx); | ||
204 | |||
205 | if (leaf == XSTATE_PKRU_BIT) { | ||
206 | xstate_offset = ebx; | ||
207 | xstate_size = eax; | ||
208 | } | ||
209 | } | ||
210 | |||
211 | if (xstate_size == 0) { | ||
212 | printf("could not find size/offset of PKRU in xsave state\n"); | ||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | return xstate_offset; | ||
217 | } | ||
218 | |||
219 | #endif /* _PKEYS_HELPER_H */ | ||
diff --git a/tools/testing/selftests/x86/protection_keys.c b/tools/testing/selftests/x86/protection_keys.c new file mode 100644 index 000000000000..bdd58c78902e --- /dev/null +++ b/tools/testing/selftests/x86/protection_keys.c | |||
@@ -0,0 +1,1410 @@ | |||
1 | /* | ||
2 | * Tests x86 Memory Protection Keys (see Documentation/x86/protection-keys.txt) | ||
3 | * | ||
4 | * There are examples in here of: | ||
5 | * * how to set protection keys on memory | ||
6 | * * how to set/clear bits in PKRU (the rights register) | ||
7 | * * how to handle SEGV_PKRU signals and extract pkey-relevant | ||
8 | * information from the siginfo | ||
9 | * | ||
10 | * Things to add: | ||
11 | * make sure KSM and KSM COW breaking works | ||
12 | * prefault pages in at malloc, or not | ||
13 | * protect MPX bounds tables with protection keys? | ||
14 | * make sure VMA splitting/merging is working correctly | ||
15 | * OOMs can destroy mm->mmap (see exit_mmap()), so make sure it is immune to pkeys | ||
16 | * look for pkey "leaks" where it is still set on a VMA but "freed" back to the kernel | ||
17 | * do a plain mprotect() to a mprotect_pkey() area and make sure the pkey sticks | ||
18 | * | ||
19 | * Compile like this: | ||
20 | * gcc -o protection_keys -O2 -g -std=gnu99 -pthread -Wall protection_keys.c -lrt -ldl -lm | ||
21 | * gcc -m32 -o protection_keys_32 -O2 -g -std=gnu99 -pthread -Wall protection_keys.c -lrt -ldl -lm | ||
22 | */ | ||
23 | #define _GNU_SOURCE | ||
24 | #include <errno.h> | ||
25 | #include <linux/futex.h> | ||
26 | #include <sys/time.h> | ||
27 | #include <sys/syscall.h> | ||
28 | #include <string.h> | ||
29 | #include <stdio.h> | ||
30 | #include <stdint.h> | ||
31 | #include <stdbool.h> | ||
32 | #include <signal.h> | ||
33 | #include <assert.h> | ||
34 | #include <stdlib.h> | ||
35 | #include <ucontext.h> | ||
36 | #include <sys/mman.h> | ||
37 | #include <sys/types.h> | ||
38 | #include <sys/wait.h> | ||
39 | #include <sys/stat.h> | ||
40 | #include <fcntl.h> | ||
41 | #include <unistd.h> | ||
42 | #include <sys/ptrace.h> | ||
43 | #include <setjmp.h> | ||
44 | |||
45 | #include "pkey-helpers.h" | ||
46 | |||
47 | int iteration_nr = 1; | ||
48 | int test_nr; | ||
49 | |||
50 | unsigned int shadow_pkru; | ||
51 | |||
52 | #define HPAGE_SIZE (1UL<<21) | ||
53 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) | ||
54 | #define ALIGN_UP(x, align_to) (((x) + ((align_to)-1)) & ~((align_to)-1)) | ||
55 | #define ALIGN_DOWN(x, align_to) ((x) & ~((align_to)-1)) | ||
56 | #define ALIGN_PTR_UP(p, ptr_align_to) ((typeof(p))ALIGN_UP((unsigned long)(p), ptr_align_to)) | ||
57 | #define ALIGN_PTR_DOWN(p, ptr_align_to) ((typeof(p))ALIGN_DOWN((unsigned long)(p), ptr_align_to)) | ||
58 | #define __stringify_1(x...) #x | ||
59 | #define __stringify(x...) __stringify_1(x) | ||
60 | |||
61 | #define PTR_ERR_ENOTSUP ((void *)-ENOTSUP) | ||
62 | |||
63 | int dprint_in_signal; | ||
64 | char dprint_in_signal_buffer[DPRINT_IN_SIGNAL_BUF_SIZE]; | ||
65 | |||
66 | extern void abort_hooks(void); | ||
67 | #define pkey_assert(condition) do { \ | ||
68 | if (!(condition)) { \ | ||
69 | dprintf0("assert() at %s::%d test_nr: %d iteration: %d\n", \ | ||
70 | __FILE__, __LINE__, \ | ||
71 | test_nr, iteration_nr); \ | ||
72 | dprintf0("errno at assert: %d", errno); \ | ||
73 | abort_hooks(); \ | ||
74 | assert(condition); \ | ||
75 | } \ | ||
76 | } while (0) | ||
77 | #define raw_assert(cond) assert(cond) | ||
78 | |||
79 | void cat_into_file(char *str, char *file) | ||
80 | { | ||
81 | int fd = open(file, O_RDWR); | ||
82 | int ret; | ||
83 | |||
84 | dprintf2("%s(): writing '%s' to '%s'\n", __func__, str, file); | ||
85 | /* | ||
86 | * these need to be raw because they are called under | ||
87 | * pkey_assert() | ||
88 | */ | ||
89 | raw_assert(fd >= 0); | ||
90 | ret = write(fd, str, strlen(str)); | ||
91 | if (ret != strlen(str)) { | ||
92 | perror("write to file failed"); | ||
93 | fprintf(stderr, "filename: '%s' str: '%s'\n", file, str); | ||
94 | raw_assert(0); | ||
95 | } | ||
96 | close(fd); | ||
97 | } | ||
98 | |||
99 | #if CONTROL_TRACING > 0 | ||
100 | static int warned_tracing; | ||
101 | int tracing_root_ok(void) | ||
102 | { | ||
103 | if (geteuid() != 0) { | ||
104 | if (!warned_tracing) | ||
105 | fprintf(stderr, "WARNING: not run as root, " | ||
106 | "can not do tracing control\n"); | ||
107 | warned_tracing = 1; | ||
108 | return 0; | ||
109 | } | ||
110 | return 1; | ||
111 | } | ||
112 | #endif | ||
113 | |||
114 | void tracing_on(void) | ||
115 | { | ||
116 | #if CONTROL_TRACING > 0 | ||
117 | #define TRACEDIR "/sys/kernel/debug/tracing" | ||
118 | char pidstr[32]; | ||
119 | |||
120 | if (!tracing_root_ok()) | ||
121 | return; | ||
122 | |||
123 | sprintf(pidstr, "%d", getpid()); | ||
124 | cat_into_file("0", TRACEDIR "/tracing_on"); | ||
125 | cat_into_file("\n", TRACEDIR "/trace"); | ||
126 | if (1) { | ||
127 | cat_into_file("function_graph", TRACEDIR "/current_tracer"); | ||
128 | cat_into_file("1", TRACEDIR "/options/funcgraph-proc"); | ||
129 | } else { | ||
130 | cat_into_file("nop", TRACEDIR "/current_tracer"); | ||
131 | } | ||
132 | cat_into_file(pidstr, TRACEDIR "/set_ftrace_pid"); | ||
133 | cat_into_file("1", TRACEDIR "/tracing_on"); | ||
134 | dprintf1("enabled tracing\n"); | ||
135 | #endif | ||
136 | } | ||
137 | |||
138 | void tracing_off(void) | ||
139 | { | ||
140 | #if CONTROL_TRACING > 0 | ||
141 | if (!tracing_root_ok()) | ||
142 | return; | ||
143 | cat_into_file("0", "/sys/kernel/debug/tracing/tracing_on"); | ||
144 | #endif | ||
145 | } | ||
146 | |||
147 | void abort_hooks(void) | ||
148 | { | ||
149 | fprintf(stderr, "running %s()...\n", __func__); | ||
150 | tracing_off(); | ||
151 | #ifdef SLEEP_ON_ABORT | ||
152 | sleep(SLEEP_ON_ABORT); | ||
153 | #endif | ||
154 | } | ||
155 | |||
156 | static inline void __page_o_noops(void) | ||
157 | { | ||
158 | /* 8-bytes of instruction * 512 bytes = 1 page */ | ||
159 | asm(".rept 512 ; nopl 0x7eeeeeee(%eax) ; .endr"); | ||
160 | } | ||
161 | |||
162 | /* | ||
163 | * This attempts to have roughly a page of instructions followed by a few | ||
164 | * instructions that do a write, and another page of instructions. That | ||
165 | * way, we are pretty sure that the write is in the second page of | ||
166 | * instructions and has at least a page of padding behind it. | ||
167 | * | ||
168 | * *That* lets us be sure to madvise() away the write instruction, which | ||
169 | * will then fault, which makes sure that the fault code handles | ||
170 | * execute-only memory properly. | ||
171 | */ | ||
172 | __attribute__((__aligned__(PAGE_SIZE))) | ||
173 | void lots_o_noops_around_write(int *write_to_me) | ||
174 | { | ||
175 | dprintf3("running %s()\n", __func__); | ||
176 | __page_o_noops(); | ||
177 | /* Assume this happens in the second page of instructions: */ | ||
178 | *write_to_me = __LINE__; | ||
179 | /* pad out by another page: */ | ||
180 | __page_o_noops(); | ||
181 | dprintf3("%s() done\n", __func__); | ||
182 | } | ||
183 | |||
184 | /* Define some kernel-like types */ | ||
185 | #define u8 uint8_t | ||
186 | #define u16 uint16_t | ||
187 | #define u32 uint32_t | ||
188 | #define u64 uint64_t | ||
189 | |||
190 | #ifdef __i386__ | ||
191 | #define SYS_mprotect_key 380 | ||
192 | #define SYS_pkey_alloc 381 | ||
193 | #define SYS_pkey_free 382 | ||
194 | #define REG_IP_IDX REG_EIP | ||
195 | #define si_pkey_offset 0x18 | ||
196 | #else | ||
197 | #define SYS_mprotect_key 329 | ||
198 | #define SYS_pkey_alloc 330 | ||
199 | #define SYS_pkey_free 331 | ||
200 | #define REG_IP_IDX REG_RIP | ||
201 | #define si_pkey_offset 0x20 | ||
202 | #endif | ||
203 | |||
204 | void dump_mem(void *dumpme, int len_bytes) | ||
205 | { | ||
206 | char *c = (void *)dumpme; | ||
207 | int i; | ||
208 | |||
209 | for (i = 0; i < len_bytes; i += sizeof(u64)) { | ||
210 | u64 *ptr = (u64 *)(c + i); | ||
211 | dprintf1("dump[%03d][@%p]: %016jx\n", i, ptr, *ptr); | ||
212 | } | ||
213 | } | ||
214 | |||
215 | #define __SI_FAULT (3 << 16) | ||
216 | #define SEGV_BNDERR (__SI_FAULT|3) /* failed address bound checks */ | ||
217 | #define SEGV_PKUERR (__SI_FAULT|4) | ||
218 | |||
219 | static char *si_code_str(int si_code) | ||
220 | { | ||
221 | if (si_code & SEGV_MAPERR) | ||
222 | return "SEGV_MAPERR"; | ||
223 | if (si_code & SEGV_ACCERR) | ||
224 | return "SEGV_ACCERR"; | ||
225 | if (si_code & SEGV_BNDERR) | ||
226 | return "SEGV_BNDERR"; | ||
227 | if (si_code & SEGV_PKUERR) | ||
228 | return "SEGV_PKUERR"; | ||
229 | return "UNKNOWN"; | ||
230 | } | ||
231 | |||
232 | int pkru_faults; | ||
233 | int last_si_pkey = -1; | ||
234 | void signal_handler(int signum, siginfo_t *si, void *vucontext) | ||
235 | { | ||
236 | ucontext_t *uctxt = vucontext; | ||
237 | int trapno; | ||
238 | unsigned long ip; | ||
239 | char *fpregs; | ||
240 | u32 *pkru_ptr; | ||
241 | u64 si_pkey; | ||
242 | u32 *si_pkey_ptr; | ||
243 | int pkru_offset; | ||
244 | fpregset_t fpregset; | ||
245 | |||
246 | dprint_in_signal = 1; | ||
247 | dprintf1(">>>>===============SIGSEGV============================\n"); | ||
248 | dprintf1("%s()::%d, pkru: 0x%x shadow: %x\n", __func__, __LINE__, | ||
249 | __rdpkru(), shadow_pkru); | ||
250 | |||
251 | trapno = uctxt->uc_mcontext.gregs[REG_TRAPNO]; | ||
252 | ip = uctxt->uc_mcontext.gregs[REG_IP_IDX]; | ||
253 | fpregset = uctxt->uc_mcontext.fpregs; | ||
254 | fpregs = (void *)fpregset; | ||
255 | |||
256 | dprintf2("%s() trapno: %d ip: 0x%lx info->si_code: %s/%d\n", __func__, | ||
257 | trapno, ip, si_code_str(si->si_code), si->si_code); | ||
258 | #ifdef __i386__ | ||
259 | /* | ||
260 | * 32-bit has some extra padding so that userspace can tell whether | ||
261 | * the XSTATE header is present in addition to the "legacy" FPU | ||
262 | * state. We just assume that it is here. | ||
263 | */ | ||
264 | fpregs += 0x70; | ||
265 | #endif | ||
266 | pkru_offset = pkru_xstate_offset(); | ||
267 | pkru_ptr = (void *)(&fpregs[pkru_offset]); | ||
268 | |||
269 | dprintf1("siginfo: %p\n", si); | ||
270 | dprintf1(" fpregs: %p\n", fpregs); | ||
271 | /* | ||
272 | * If we got a PKRU fault, we *HAVE* to have at least one bit set in | ||
273 | * here. | ||
274 | */ | ||
275 | dprintf1("pkru_xstate_offset: %d\n", pkru_xstate_offset()); | ||
276 | if (DEBUG_LEVEL > 4) | ||
277 | dump_mem(pkru_ptr - 128, 256); | ||
278 | pkey_assert(*pkru_ptr); | ||
279 | |||
280 | si_pkey_ptr = (u32 *)(((u8 *)si) + si_pkey_offset); | ||
281 | dprintf1("si_pkey_ptr: %p\n", si_pkey_ptr); | ||
282 | dump_mem(si_pkey_ptr - 8, 24); | ||
283 | si_pkey = *si_pkey_ptr; | ||
284 | pkey_assert(si_pkey < NR_PKEYS); | ||
285 | last_si_pkey = si_pkey; | ||
286 | |||
287 | if ((si->si_code == SEGV_MAPERR) || | ||
288 | (si->si_code == SEGV_ACCERR) || | ||
289 | (si->si_code == SEGV_BNDERR)) { | ||
290 | printf("non-PK si_code, exiting...\n"); | ||
291 | exit(4); | ||
292 | } | ||
293 | |||
294 | dprintf1("signal pkru from xsave: %08x\n", *pkru_ptr); | ||
295 | /* need __rdpkru() version so we do not do shadow_pkru checking */ | ||
296 | dprintf1("signal pkru from pkru: %08x\n", __rdpkru()); | ||
297 | dprintf1("si_pkey from siginfo: %jx\n", si_pkey); | ||
298 | *(u64 *)pkru_ptr = 0x00000000; | ||
299 | dprintf1("WARNING: set PRKU=0 to allow faulting instruction to continue\n"); | ||
300 | pkru_faults++; | ||
301 | dprintf1("<<<<==================================================\n"); | ||
302 | return; | ||
303 | if (trapno == 14) { | ||
304 | fprintf(stderr, | ||
305 | "ERROR: In signal handler, page fault, trapno = %d, ip = %016lx\n", | ||
306 | trapno, ip); | ||
307 | fprintf(stderr, "si_addr %p\n", si->si_addr); | ||
308 | fprintf(stderr, "REG_ERR: %lx\n", | ||
309 | (unsigned long)uctxt->uc_mcontext.gregs[REG_ERR]); | ||
310 | exit(1); | ||
311 | } else { | ||
312 | fprintf(stderr, "unexpected trap %d! at 0x%lx\n", trapno, ip); | ||
313 | fprintf(stderr, "si_addr %p\n", si->si_addr); | ||
314 | fprintf(stderr, "REG_ERR: %lx\n", | ||
315 | (unsigned long)uctxt->uc_mcontext.gregs[REG_ERR]); | ||
316 | exit(2); | ||
317 | } | ||
318 | dprint_in_signal = 0; | ||
319 | } | ||
320 | |||
321 | int wait_all_children(void) | ||
322 | { | ||
323 | int status; | ||
324 | return waitpid(-1, &status, 0); | ||
325 | } | ||
326 | |||
327 | void sig_chld(int x) | ||
328 | { | ||
329 | dprint_in_signal = 1; | ||
330 | dprintf2("[%d] SIGCHLD: %d\n", getpid(), x); | ||
331 | dprint_in_signal = 0; | ||
332 | } | ||
333 | |||
334 | void setup_sigsegv_handler(void) | ||
335 | { | ||
336 | int r, rs; | ||
337 | struct sigaction newact; | ||
338 | struct sigaction oldact; | ||
339 | |||
340 | /* #PF is mapped to sigsegv */ | ||
341 | int signum = SIGSEGV; | ||
342 | |||
343 | newact.sa_handler = 0; | ||
344 | newact.sa_sigaction = signal_handler; | ||
345 | |||
346 | /*sigset_t - signals to block while in the handler */ | ||
347 | /* get the old signal mask. */ | ||
348 | rs = sigprocmask(SIG_SETMASK, 0, &newact.sa_mask); | ||
349 | pkey_assert(rs == 0); | ||
350 | |||
351 | /* call sa_sigaction, not sa_handler*/ | ||
352 | newact.sa_flags = SA_SIGINFO; | ||
353 | |||
354 | newact.sa_restorer = 0; /* void(*)(), obsolete */ | ||
355 | r = sigaction(signum, &newact, &oldact); | ||
356 | r = sigaction(SIGALRM, &newact, &oldact); | ||
357 | pkey_assert(r == 0); | ||
358 | } | ||
359 | |||
360 | void setup_handlers(void) | ||
361 | { | ||
362 | signal(SIGCHLD, &sig_chld); | ||
363 | setup_sigsegv_handler(); | ||
364 | } | ||
365 | |||
366 | pid_t fork_lazy_child(void) | ||
367 | { | ||
368 | pid_t forkret; | ||
369 | |||
370 | forkret = fork(); | ||
371 | pkey_assert(forkret >= 0); | ||
372 | dprintf3("[%d] fork() ret: %d\n", getpid(), forkret); | ||
373 | |||
374 | if (!forkret) { | ||
375 | /* in the child */ | ||
376 | while (1) { | ||
377 | dprintf1("child sleeping...\n"); | ||
378 | sleep(30); | ||
379 | } | ||
380 | } | ||
381 | return forkret; | ||
382 | } | ||
383 | |||
384 | void davecmp(void *_a, void *_b, int len) | ||
385 | { | ||
386 | int i; | ||
387 | unsigned long *a = _a; | ||
388 | unsigned long *b = _b; | ||
389 | |||
390 | for (i = 0; i < len / sizeof(*a); i++) { | ||
391 | if (a[i] == b[i]) | ||
392 | continue; | ||
393 | |||
394 | dprintf3("[%3d]: a: %016lx b: %016lx\n", i, a[i], b[i]); | ||
395 | } | ||
396 | } | ||
397 | |||
398 | void dumpit(char *f) | ||
399 | { | ||
400 | int fd = open(f, O_RDONLY); | ||
401 | char buf[100]; | ||
402 | int nr_read; | ||
403 | |||
404 | dprintf2("maps fd: %d\n", fd); | ||
405 | do { | ||
406 | nr_read = read(fd, &buf[0], sizeof(buf)); | ||
407 | write(1, buf, nr_read); | ||
408 | } while (nr_read > 0); | ||
409 | close(fd); | ||
410 | } | ||
411 | |||
412 | #define PKEY_DISABLE_ACCESS 0x1 | ||
413 | #define PKEY_DISABLE_WRITE 0x2 | ||
414 | |||
415 | u32 pkey_get(int pkey, unsigned long flags) | ||
416 | { | ||
417 | u32 mask = (PKEY_DISABLE_ACCESS|PKEY_DISABLE_WRITE); | ||
418 | u32 pkru = __rdpkru(); | ||
419 | u32 shifted_pkru; | ||
420 | u32 masked_pkru; | ||
421 | |||
422 | dprintf1("%s(pkey=%d, flags=%lx) = %x / %d\n", | ||
423 | __func__, pkey, flags, 0, 0); | ||
424 | dprintf2("%s() raw pkru: %x\n", __func__, pkru); | ||
425 | |||
426 | shifted_pkru = (pkru >> (pkey * PKRU_BITS_PER_PKEY)); | ||
427 | dprintf2("%s() shifted_pkru: %x\n", __func__, shifted_pkru); | ||
428 | masked_pkru = shifted_pkru & mask; | ||
429 | dprintf2("%s() masked pkru: %x\n", __func__, masked_pkru); | ||
430 | /* | ||
431 | * shift down the relevant bits to the lowest two, then | ||
432 | * mask off all the other high bits. | ||
433 | */ | ||
434 | return masked_pkru; | ||
435 | } | ||
436 | |||
437 | int pkey_set(int pkey, unsigned long rights, unsigned long flags) | ||
438 | { | ||
439 | u32 mask = (PKEY_DISABLE_ACCESS|PKEY_DISABLE_WRITE); | ||
440 | u32 old_pkru = __rdpkru(); | ||
441 | u32 new_pkru; | ||
442 | |||
443 | /* make sure that 'rights' only contains the bits we expect: */ | ||
444 | assert(!(rights & ~mask)); | ||
445 | |||
446 | /* copy old pkru */ | ||
447 | new_pkru = old_pkru; | ||
448 | /* mask out bits from pkey in old value: */ | ||
449 | new_pkru &= ~(mask << (pkey * PKRU_BITS_PER_PKEY)); | ||
450 | /* OR in new bits for pkey: */ | ||
451 | new_pkru |= (rights << (pkey * PKRU_BITS_PER_PKEY)); | ||
452 | |||
453 | __wrpkru(new_pkru); | ||
454 | |||
455 | dprintf3("%s(pkey=%d, rights=%lx, flags=%lx) = %x pkru now: %x old_pkru: %x\n", | ||
456 | __func__, pkey, rights, flags, 0, __rdpkru(), old_pkru); | ||
457 | return 0; | ||
458 | } | ||
459 | |||
460 | void pkey_disable_set(int pkey, int flags) | ||
461 | { | ||
462 | unsigned long syscall_flags = 0; | ||
463 | int ret; | ||
464 | int pkey_rights; | ||
465 | u32 orig_pkru; | ||
466 | |||
467 | dprintf1("START->%s(%d, 0x%x)\n", __func__, | ||
468 | pkey, flags); | ||
469 | pkey_assert(flags & (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE)); | ||
470 | |||
471 | pkey_rights = pkey_get(pkey, syscall_flags); | ||
472 | |||
473 | dprintf1("%s(%d) pkey_get(%d): %x\n", __func__, | ||
474 | pkey, pkey, pkey_rights); | ||
475 | pkey_assert(pkey_rights >= 0); | ||
476 | |||
477 | pkey_rights |= flags; | ||
478 | |||
479 | ret = pkey_set(pkey, pkey_rights, syscall_flags); | ||
480 | assert(!ret); | ||
481 | /*pkru and flags have the same format */ | ||
482 | shadow_pkru |= flags << (pkey * 2); | ||
483 | dprintf1("%s(%d) shadow: 0x%x\n", __func__, pkey, shadow_pkru); | ||
484 | |||
485 | pkey_assert(ret >= 0); | ||
486 | |||
487 | pkey_rights = pkey_get(pkey, syscall_flags); | ||
488 | dprintf1("%s(%d) pkey_get(%d): %x\n", __func__, | ||
489 | pkey, pkey, pkey_rights); | ||
490 | |||
491 | dprintf1("%s(%d) pkru: 0x%x\n", __func__, pkey, rdpkru()); | ||
492 | if (flags) | ||
493 | pkey_assert(rdpkru() > orig_pkru); | ||
494 | dprintf1("END<---%s(%d, 0x%x)\n", __func__, | ||
495 | pkey, flags); | ||
496 | } | ||
497 | |||
498 | void pkey_disable_clear(int pkey, int flags) | ||
499 | { | ||
500 | unsigned long syscall_flags = 0; | ||
501 | int ret; | ||
502 | int pkey_rights = pkey_get(pkey, syscall_flags); | ||
503 | u32 orig_pkru = rdpkru(); | ||
504 | |||
505 | pkey_assert(flags & (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE)); | ||
506 | |||
507 | dprintf1("%s(%d) pkey_get(%d): %x\n", __func__, | ||
508 | pkey, pkey, pkey_rights); | ||
509 | pkey_assert(pkey_rights >= 0); | ||
510 | |||
511 | pkey_rights |= flags; | ||
512 | |||
513 | ret = pkey_set(pkey, pkey_rights, 0); | ||
514 | /* pkru and flags have the same format */ | ||
515 | shadow_pkru &= ~(flags << (pkey * 2)); | ||
516 | pkey_assert(ret >= 0); | ||
517 | |||
518 | pkey_rights = pkey_get(pkey, syscall_flags); | ||
519 | dprintf1("%s(%d) pkey_get(%d): %x\n", __func__, | ||
520 | pkey, pkey, pkey_rights); | ||
521 | |||
522 | dprintf1("%s(%d) pkru: 0x%x\n", __func__, pkey, rdpkru()); | ||
523 | if (flags) | ||
524 | assert(rdpkru() > orig_pkru); | ||
525 | } | ||
526 | |||
527 | void pkey_write_allow(int pkey) | ||
528 | { | ||
529 | pkey_disable_clear(pkey, PKEY_DISABLE_WRITE); | ||
530 | } | ||
531 | void pkey_write_deny(int pkey) | ||
532 | { | ||
533 | pkey_disable_set(pkey, PKEY_DISABLE_WRITE); | ||
534 | } | ||
535 | void pkey_access_allow(int pkey) | ||
536 | { | ||
537 | pkey_disable_clear(pkey, PKEY_DISABLE_ACCESS); | ||
538 | } | ||
539 | void pkey_access_deny(int pkey) | ||
540 | { | ||
541 | pkey_disable_set(pkey, PKEY_DISABLE_ACCESS); | ||
542 | } | ||
543 | |||
544 | int sys_mprotect_pkey(void *ptr, size_t size, unsigned long orig_prot, | ||
545 | unsigned long pkey) | ||
546 | { | ||
547 | int sret; | ||
548 | |||
549 | dprintf2("%s(0x%p, %zx, prot=%lx, pkey=%lx)\n", __func__, | ||
550 | ptr, size, orig_prot, pkey); | ||
551 | |||
552 | errno = 0; | ||
553 | sret = syscall(SYS_mprotect_key, ptr, size, orig_prot, pkey); | ||
554 | if (errno) { | ||
555 | dprintf2("SYS_mprotect_key sret: %d\n", sret); | ||
556 | dprintf2("SYS_mprotect_key prot: 0x%lx\n", orig_prot); | ||
557 | dprintf2("SYS_mprotect_key failed, errno: %d\n", errno); | ||
558 | if (DEBUG_LEVEL >= 2) | ||
559 | perror("SYS_mprotect_pkey"); | ||
560 | } | ||
561 | return sret; | ||
562 | } | ||
563 | |||
564 | int sys_pkey_alloc(unsigned long flags, unsigned long init_val) | ||
565 | { | ||
566 | int ret = syscall(SYS_pkey_alloc, flags, init_val); | ||
567 | dprintf1("%s(flags=%lx, init_val=%lx) syscall ret: %d errno: %d\n", | ||
568 | __func__, flags, init_val, ret, errno); | ||
569 | return ret; | ||
570 | } | ||
571 | |||
572 | int alloc_pkey(void) | ||
573 | { | ||
574 | int ret; | ||
575 | unsigned long init_val = 0x0; | ||
576 | |||
577 | dprintf1("alloc_pkey()::%d, pkru: 0x%x shadow: %x\n", | ||
578 | __LINE__, __rdpkru(), shadow_pkru); | ||
579 | ret = sys_pkey_alloc(0, init_val); | ||
580 | /* | ||
581 | * pkey_alloc() sets PKRU, so we need to reflect it in | ||
582 | * shadow_pkru: | ||
583 | */ | ||
584 | dprintf4("alloc_pkey()::%d, ret: %d pkru: 0x%x shadow: 0x%x\n", | ||
585 | __LINE__, ret, __rdpkru(), shadow_pkru); | ||
586 | if (ret) { | ||
587 | /* clear both the bits: */ | ||
588 | shadow_pkru &= ~(0x3 << (ret * 2)); | ||
589 | dprintf4("alloc_pkey()::%d, ret: %d pkru: 0x%x shadow: 0x%x\n", | ||
590 | __LINE__, ret, __rdpkru(), shadow_pkru); | ||
591 | /* | ||
592 | * move the new state in from init_val | ||
593 | * (remember, we cheated and init_val == pkru format) | ||
594 | */ | ||
595 | shadow_pkru |= (init_val << (ret * 2)); | ||
596 | } | ||
597 | dprintf4("alloc_pkey()::%d, ret: %d pkru: 0x%x shadow: 0x%x\n", | ||
598 | __LINE__, ret, __rdpkru(), shadow_pkru); | ||
599 | dprintf1("alloc_pkey()::%d errno: %d\n", __LINE__, errno); | ||
600 | /* for shadow checking: */ | ||
601 | rdpkru(); | ||
602 | dprintf4("alloc_pkey()::%d, ret: %d pkru: 0x%x shadow: 0x%x\n", | ||
603 | __LINE__, ret, __rdpkru(), shadow_pkru); | ||
604 | return ret; | ||
605 | } | ||
606 | |||
607 | int sys_pkey_free(unsigned long pkey) | ||
608 | { | ||
609 | int ret = syscall(SYS_pkey_free, pkey); | ||
610 | dprintf1("%s(pkey=%ld) syscall ret: %d\n", __func__, pkey, ret); | ||
611 | return ret; | ||
612 | } | ||
613 | |||
614 | /* | ||
615 | * I had a bug where pkey bits could be set by mprotect() but | ||
616 | * not cleared. This ensures we get lots of random bit sets | ||
617 | * and clears on the vma and pte pkey bits. | ||
618 | */ | ||
619 | int alloc_random_pkey(void) | ||
620 | { | ||
621 | int max_nr_pkey_allocs; | ||
622 | int ret; | ||
623 | int i; | ||
624 | int alloced_pkeys[NR_PKEYS]; | ||
625 | int nr_alloced = 0; | ||
626 | int random_index; | ||
627 | memset(alloced_pkeys, 0, sizeof(alloced_pkeys)); | ||
628 | |||
629 | /* allocate every possible key and make a note of which ones we got */ | ||
630 | max_nr_pkey_allocs = NR_PKEYS; | ||
631 | max_nr_pkey_allocs = 1; | ||
632 | for (i = 0; i < max_nr_pkey_allocs; i++) { | ||
633 | int new_pkey = alloc_pkey(); | ||
634 | if (new_pkey < 0) | ||
635 | break; | ||
636 | alloced_pkeys[nr_alloced++] = new_pkey; | ||
637 | } | ||
638 | |||
639 | pkey_assert(nr_alloced > 0); | ||
640 | /* select a random one out of the allocated ones */ | ||
641 | random_index = rand() % nr_alloced; | ||
642 | ret = alloced_pkeys[random_index]; | ||
643 | /* now zero it out so we don't free it next */ | ||
644 | alloced_pkeys[random_index] = 0; | ||
645 | |||
646 | /* go through the allocated ones that we did not want and free them */ | ||
647 | for (i = 0; i < nr_alloced; i++) { | ||
648 | int free_ret; | ||
649 | if (!alloced_pkeys[i]) | ||
650 | continue; | ||
651 | free_ret = sys_pkey_free(alloced_pkeys[i]); | ||
652 | pkey_assert(!free_ret); | ||
653 | } | ||
654 | dprintf1("%s()::%d, ret: %d pkru: 0x%x shadow: 0x%x\n", __func__, | ||
655 | __LINE__, ret, __rdpkru(), shadow_pkru); | ||
656 | return ret; | ||
657 | } | ||
658 | |||
659 | int mprotect_pkey(void *ptr, size_t size, unsigned long orig_prot, | ||
660 | unsigned long pkey) | ||
661 | { | ||
662 | int nr_iterations = random() % 100; | ||
663 | int ret; | ||
664 | |||
665 | while (0) { | ||
666 | int rpkey = alloc_random_pkey(); | ||
667 | ret = sys_mprotect_pkey(ptr, size, orig_prot, pkey); | ||
668 | dprintf1("sys_mprotect_pkey(%p, %zx, prot=0x%lx, pkey=%ld) ret: %d\n", | ||
669 | ptr, size, orig_prot, pkey, ret); | ||
670 | if (nr_iterations-- < 0) | ||
671 | break; | ||
672 | |||
673 | dprintf1("%s()::%d, ret: %d pkru: 0x%x shadow: 0x%x\n", __func__, | ||
674 | __LINE__, ret, __rdpkru(), shadow_pkru); | ||
675 | sys_pkey_free(rpkey); | ||
676 | dprintf1("%s()::%d, ret: %d pkru: 0x%x shadow: 0x%x\n", __func__, | ||
677 | __LINE__, ret, __rdpkru(), shadow_pkru); | ||
678 | } | ||
679 | pkey_assert(pkey < NR_PKEYS); | ||
680 | |||
681 | ret = sys_mprotect_pkey(ptr, size, orig_prot, pkey); | ||
682 | dprintf1("mprotect_pkey(%p, %zx, prot=0x%lx, pkey=%ld) ret: %d\n", | ||
683 | ptr, size, orig_prot, pkey, ret); | ||
684 | pkey_assert(!ret); | ||
685 | dprintf1("%s()::%d, ret: %d pkru: 0x%x shadow: 0x%x\n", __func__, | ||
686 | __LINE__, ret, __rdpkru(), shadow_pkru); | ||
687 | return ret; | ||
688 | } | ||
689 | |||
690 | struct pkey_malloc_record { | ||
691 | void *ptr; | ||
692 | long size; | ||
693 | }; | ||
694 | struct pkey_malloc_record *pkey_malloc_records; | ||
695 | long nr_pkey_malloc_records; | ||
696 | void record_pkey_malloc(void *ptr, long size) | ||
697 | { | ||
698 | long i; | ||
699 | struct pkey_malloc_record *rec = NULL; | ||
700 | |||
701 | for (i = 0; i < nr_pkey_malloc_records; i++) { | ||
702 | rec = &pkey_malloc_records[i]; | ||
703 | /* find a free record */ | ||
704 | if (rec) | ||
705 | break; | ||
706 | } | ||
707 | if (!rec) { | ||
708 | /* every record is full */ | ||
709 | size_t old_nr_records = nr_pkey_malloc_records; | ||
710 | size_t new_nr_records = (nr_pkey_malloc_records * 2 + 1); | ||
711 | size_t new_size = new_nr_records * sizeof(struct pkey_malloc_record); | ||
712 | dprintf2("new_nr_records: %zd\n", new_nr_records); | ||
713 | dprintf2("new_size: %zd\n", new_size); | ||
714 | pkey_malloc_records = realloc(pkey_malloc_records, new_size); | ||
715 | pkey_assert(pkey_malloc_records != NULL); | ||
716 | rec = &pkey_malloc_records[nr_pkey_malloc_records]; | ||
717 | /* | ||
718 | * realloc() does not initialize memory, so zero it from | ||
719 | * the first new record all the way to the end. | ||
720 | */ | ||
721 | for (i = 0; i < new_nr_records - old_nr_records; i++) | ||
722 | memset(rec + i, 0, sizeof(*rec)); | ||
723 | } | ||
724 | dprintf3("filling malloc record[%d/%p]: {%p, %ld}\n", | ||
725 | (int)(rec - pkey_malloc_records), rec, ptr, size); | ||
726 | rec->ptr = ptr; | ||
727 | rec->size = size; | ||
728 | nr_pkey_malloc_records++; | ||
729 | } | ||
730 | |||
731 | void free_pkey_malloc(void *ptr) | ||
732 | { | ||
733 | long i; | ||
734 | int ret; | ||
735 | dprintf3("%s(%p)\n", __func__, ptr); | ||
736 | for (i = 0; i < nr_pkey_malloc_records; i++) { | ||
737 | struct pkey_malloc_record *rec = &pkey_malloc_records[i]; | ||
738 | dprintf4("looking for ptr %p at record[%ld/%p]: {%p, %ld}\n", | ||
739 | ptr, i, rec, rec->ptr, rec->size); | ||
740 | if ((ptr < rec->ptr) || | ||
741 | (ptr >= rec->ptr + rec->size)) | ||
742 | continue; | ||
743 | |||
744 | dprintf3("found ptr %p at record[%ld/%p]: {%p, %ld}\n", | ||
745 | ptr, i, rec, rec->ptr, rec->size); | ||
746 | nr_pkey_malloc_records--; | ||
747 | ret = munmap(rec->ptr, rec->size); | ||
748 | dprintf3("munmap ret: %d\n", ret); | ||
749 | pkey_assert(!ret); | ||
750 | dprintf3("clearing rec->ptr, rec: %p\n", rec); | ||
751 | rec->ptr = NULL; | ||
752 | dprintf3("done clearing rec->ptr, rec: %p\n", rec); | ||
753 | return; | ||
754 | } | ||
755 | pkey_assert(false); | ||
756 | } | ||
757 | |||
758 | |||
759 | void *malloc_pkey_with_mprotect(long size, int prot, u16 pkey) | ||
760 | { | ||
761 | void *ptr; | ||
762 | int ret; | ||
763 | |||
764 | rdpkru(); | ||
765 | dprintf1("doing %s(size=%ld, prot=0x%x, pkey=%d)\n", __func__, | ||
766 | size, prot, pkey); | ||
767 | pkey_assert(pkey < NR_PKEYS); | ||
768 | ptr = mmap(NULL, size, prot, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); | ||
769 | pkey_assert(ptr != (void *)-1); | ||
770 | ret = mprotect_pkey((void *)ptr, PAGE_SIZE, prot, pkey); | ||
771 | pkey_assert(!ret); | ||
772 | record_pkey_malloc(ptr, size); | ||
773 | rdpkru(); | ||
774 | |||
775 | dprintf1("%s() for pkey %d @ %p\n", __func__, pkey, ptr); | ||
776 | return ptr; | ||
777 | } | ||
778 | |||
779 | void *malloc_pkey_anon_huge(long size, int prot, u16 pkey) | ||
780 | { | ||
781 | int ret; | ||
782 | void *ptr; | ||
783 | |||
784 | dprintf1("doing %s(size=%ld, prot=0x%x, pkey=%d)\n", __func__, | ||
785 | size, prot, pkey); | ||
786 | /* | ||
787 | * Guarantee we can fit at least one huge page in the resulting | ||
788 | * allocation by allocating space for 2: | ||
789 | */ | ||
790 | size = ALIGN_UP(size, HPAGE_SIZE * 2); | ||
791 | ptr = mmap(NULL, size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); | ||
792 | pkey_assert(ptr != (void *)-1); | ||
793 | record_pkey_malloc(ptr, size); | ||
794 | mprotect_pkey(ptr, size, prot, pkey); | ||
795 | |||
796 | dprintf1("unaligned ptr: %p\n", ptr); | ||
797 | ptr = ALIGN_PTR_UP(ptr, HPAGE_SIZE); | ||
798 | dprintf1(" aligned ptr: %p\n", ptr); | ||
799 | ret = madvise(ptr, HPAGE_SIZE, MADV_HUGEPAGE); | ||
800 | dprintf1("MADV_HUGEPAGE ret: %d\n", ret); | ||
801 | ret = madvise(ptr, HPAGE_SIZE, MADV_WILLNEED); | ||
802 | dprintf1("MADV_WILLNEED ret: %d\n", ret); | ||
803 | memset(ptr, 0, HPAGE_SIZE); | ||
804 | |||
805 | dprintf1("mmap()'d thp for pkey %d @ %p\n", pkey, ptr); | ||
806 | return ptr; | ||
807 | } | ||
808 | |||
809 | int hugetlb_setup_ok; | ||
810 | #define GET_NR_HUGE_PAGES 10 | ||
811 | void setup_hugetlbfs(void) | ||
812 | { | ||
813 | int err; | ||
814 | int fd; | ||
815 | int validated_nr_pages; | ||
816 | int i; | ||
817 | char buf[] = "123"; | ||
818 | |||
819 | if (geteuid() != 0) { | ||
820 | fprintf(stderr, "WARNING: not run as root, can not do hugetlb test\n"); | ||
821 | return; | ||
822 | } | ||
823 | |||
824 | cat_into_file(__stringify(GET_NR_HUGE_PAGES), "/proc/sys/vm/nr_hugepages"); | ||
825 | |||
826 | /* | ||
827 | * Now go make sure that we got the pages and that they | ||
828 | * are 2M pages. Someone might have made 1G the default. | ||
829 | */ | ||
830 | fd = open("/sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages", O_RDONLY); | ||
831 | if (fd < 0) { | ||
832 | perror("opening sysfs 2M hugetlb config"); | ||
833 | return; | ||
834 | } | ||
835 | |||
836 | /* -1 to guarantee leaving the trailing \0 */ | ||
837 | err = read(fd, buf, sizeof(buf)-1); | ||
838 | close(fd); | ||
839 | if (err <= 0) { | ||
840 | perror("reading sysfs 2M hugetlb config"); | ||
841 | return; | ||
842 | } | ||
843 | |||
844 | if (atoi(buf) != GET_NR_HUGE_PAGES) { | ||
845 | fprintf(stderr, "could not confirm 2M pages, got: '%s' expected %d\n", | ||
846 | buf, GET_NR_HUGE_PAGES); | ||
847 | return; | ||
848 | } | ||
849 | |||
850 | hugetlb_setup_ok = 1; | ||
851 | } | ||
852 | |||
853 | void *malloc_pkey_hugetlb(long size, int prot, u16 pkey) | ||
854 | { | ||
855 | void *ptr; | ||
856 | int flags = MAP_ANONYMOUS|MAP_PRIVATE|MAP_HUGETLB; | ||
857 | |||
858 | if (!hugetlb_setup_ok) | ||
859 | return PTR_ERR_ENOTSUP; | ||
860 | |||
861 | dprintf1("doing %s(%ld, %x, %x)\n", __func__, size, prot, pkey); | ||
862 | size = ALIGN_UP(size, HPAGE_SIZE * 2); | ||
863 | pkey_assert(pkey < NR_PKEYS); | ||
864 | ptr = mmap(NULL, size, PROT_NONE, flags, -1, 0); | ||
865 | pkey_assert(ptr != (void *)-1); | ||
866 | mprotect_pkey(ptr, size, prot, pkey); | ||
867 | |||
868 | record_pkey_malloc(ptr, size); | ||
869 | |||
870 | dprintf1("mmap()'d hugetlbfs for pkey %d @ %p\n", pkey, ptr); | ||
871 | return ptr; | ||
872 | } | ||
873 | |||
874 | void *malloc_pkey_mmap_dax(long size, int prot, u16 pkey) | ||
875 | { | ||
876 | void *ptr; | ||
877 | int fd; | ||
878 | |||
879 | dprintf1("doing %s(size=%ld, prot=0x%x, pkey=%d)\n", __func__, | ||
880 | size, prot, pkey); | ||
881 | pkey_assert(pkey < NR_PKEYS); | ||
882 | fd = open("/dax/foo", O_RDWR); | ||
883 | pkey_assert(fd >= 0); | ||
884 | |||
885 | ptr = mmap(0, size, prot, MAP_SHARED, fd, 0); | ||
886 | pkey_assert(ptr != (void *)-1); | ||
887 | |||
888 | mprotect_pkey(ptr, size, prot, pkey); | ||
889 | |||
890 | record_pkey_malloc(ptr, size); | ||
891 | |||
892 | dprintf1("mmap()'d for pkey %d @ %p\n", pkey, ptr); | ||
893 | close(fd); | ||
894 | return ptr; | ||
895 | } | ||
896 | |||
897 | void *(*pkey_malloc[])(long size, int prot, u16 pkey) = { | ||
898 | |||
899 | malloc_pkey_with_mprotect, | ||
900 | malloc_pkey_anon_huge, | ||
901 | malloc_pkey_hugetlb | ||
902 | /* can not do direct with the pkey_mprotect() API: | ||
903 | malloc_pkey_mmap_direct, | ||
904 | malloc_pkey_mmap_dax, | ||
905 | */ | ||
906 | }; | ||
907 | |||
908 | void *malloc_pkey(long size, int prot, u16 pkey) | ||
909 | { | ||
910 | void *ret; | ||
911 | static int malloc_type; | ||
912 | int nr_malloc_types = ARRAY_SIZE(pkey_malloc); | ||
913 | |||
914 | pkey_assert(pkey < NR_PKEYS); | ||
915 | |||
916 | while (1) { | ||
917 | pkey_assert(malloc_type < nr_malloc_types); | ||
918 | |||
919 | ret = pkey_malloc[malloc_type](size, prot, pkey); | ||
920 | pkey_assert(ret != (void *)-1); | ||
921 | |||
922 | malloc_type++; | ||
923 | if (malloc_type >= nr_malloc_types) | ||
924 | malloc_type = (random()%nr_malloc_types); | ||
925 | |||
926 | /* try again if the malloc_type we tried is unsupported */ | ||
927 | if (ret == PTR_ERR_ENOTSUP) | ||
928 | continue; | ||
929 | |||
930 | break; | ||
931 | } | ||
932 | |||
933 | dprintf3("%s(%ld, prot=%x, pkey=%x) returning: %p\n", __func__, | ||
934 | size, prot, pkey, ret); | ||
935 | return ret; | ||
936 | } | ||
937 | |||
938 | int last_pkru_faults; | ||
939 | void expected_pk_fault(int pkey) | ||
940 | { | ||
941 | dprintf2("%s(): last_pkru_faults: %d pkru_faults: %d\n", | ||
942 | __func__, last_pkru_faults, pkru_faults); | ||
943 | dprintf2("%s(%d): last_si_pkey: %d\n", __func__, pkey, last_si_pkey); | ||
944 | pkey_assert(last_pkru_faults + 1 == pkru_faults); | ||
945 | pkey_assert(last_si_pkey == pkey); | ||
946 | /* | ||
947 | * The signal handler shold have cleared out PKRU to let the | ||
948 | * test program continue. We now have to restore it. | ||
949 | */ | ||
950 | if (__rdpkru() != 0) | ||
951 | pkey_assert(0); | ||
952 | |||
953 | __wrpkru(shadow_pkru); | ||
954 | dprintf1("%s() set PKRU=%x to restore state after signal nuked it\n", | ||
955 | __func__, shadow_pkru); | ||
956 | last_pkru_faults = pkru_faults; | ||
957 | last_si_pkey = -1; | ||
958 | } | ||
959 | |||
960 | void do_not_expect_pk_fault(void) | ||
961 | { | ||
962 | pkey_assert(last_pkru_faults == pkru_faults); | ||
963 | } | ||
964 | |||
965 | int test_fds[10] = { -1 }; | ||
966 | int nr_test_fds; | ||
967 | void __save_test_fd(int fd) | ||
968 | { | ||
969 | pkey_assert(fd >= 0); | ||
970 | pkey_assert(nr_test_fds < ARRAY_SIZE(test_fds)); | ||
971 | test_fds[nr_test_fds] = fd; | ||
972 | nr_test_fds++; | ||
973 | } | ||
974 | |||
975 | int get_test_read_fd(void) | ||
976 | { | ||
977 | int test_fd = open("/etc/passwd", O_RDONLY); | ||
978 | __save_test_fd(test_fd); | ||
979 | return test_fd; | ||
980 | } | ||
981 | |||
982 | void close_test_fds(void) | ||
983 | { | ||
984 | int i; | ||
985 | |||
986 | for (i = 0; i < nr_test_fds; i++) { | ||
987 | if (test_fds[i] < 0) | ||
988 | continue; | ||
989 | close(test_fds[i]); | ||
990 | test_fds[i] = -1; | ||
991 | } | ||
992 | nr_test_fds = 0; | ||
993 | } | ||
994 | |||
995 | #define barrier() __asm__ __volatile__("": : :"memory") | ||
996 | __attribute__((noinline)) int read_ptr(int *ptr) | ||
997 | { | ||
998 | /* | ||
999 | * Keep GCC from optimizing this away somehow | ||
1000 | */ | ||
1001 | barrier(); | ||
1002 | return *ptr; | ||
1003 | } | ||
1004 | |||
1005 | void test_read_of_write_disabled_region(int *ptr, u16 pkey) | ||
1006 | { | ||
1007 | int ptr_contents; | ||
1008 | |||
1009 | dprintf1("disabling write access to PKEY[1], doing read\n"); | ||
1010 | pkey_write_deny(pkey); | ||
1011 | ptr_contents = read_ptr(ptr); | ||
1012 | dprintf1("*ptr: %d\n", ptr_contents); | ||
1013 | dprintf1("\n"); | ||
1014 | } | ||
1015 | void test_read_of_access_disabled_region(int *ptr, u16 pkey) | ||
1016 | { | ||
1017 | int ptr_contents; | ||
1018 | |||
1019 | dprintf1("disabling access to PKEY[%02d], doing read @ %p\n", pkey, ptr); | ||
1020 | rdpkru(); | ||
1021 | pkey_access_deny(pkey); | ||
1022 | ptr_contents = read_ptr(ptr); | ||
1023 | dprintf1("*ptr: %d\n", ptr_contents); | ||
1024 | expected_pk_fault(pkey); | ||
1025 | } | ||
1026 | void test_write_of_write_disabled_region(int *ptr, u16 pkey) | ||
1027 | { | ||
1028 | dprintf1("disabling write access to PKEY[%02d], doing write\n", pkey); | ||
1029 | pkey_write_deny(pkey); | ||
1030 | *ptr = __LINE__; | ||
1031 | expected_pk_fault(pkey); | ||
1032 | } | ||
1033 | void test_write_of_access_disabled_region(int *ptr, u16 pkey) | ||
1034 | { | ||
1035 | dprintf1("disabling access to PKEY[%02d], doing write\n", pkey); | ||
1036 | pkey_access_deny(pkey); | ||
1037 | *ptr = __LINE__; | ||
1038 | expected_pk_fault(pkey); | ||
1039 | } | ||
1040 | void test_kernel_write_of_access_disabled_region(int *ptr, u16 pkey) | ||
1041 | { | ||
1042 | int ret; | ||
1043 | int test_fd = get_test_read_fd(); | ||
1044 | |||
1045 | dprintf1("disabling access to PKEY[%02d], " | ||
1046 | "having kernel read() to buffer\n", pkey); | ||
1047 | pkey_access_deny(pkey); | ||
1048 | ret = read(test_fd, ptr, 1); | ||
1049 | dprintf1("read ret: %d\n", ret); | ||
1050 | pkey_assert(ret); | ||
1051 | } | ||
1052 | void test_kernel_write_of_write_disabled_region(int *ptr, u16 pkey) | ||
1053 | { | ||
1054 | int ret; | ||
1055 | int test_fd = get_test_read_fd(); | ||
1056 | |||
1057 | pkey_write_deny(pkey); | ||
1058 | ret = read(test_fd, ptr, 100); | ||
1059 | dprintf1("read ret: %d\n", ret); | ||
1060 | if (ret < 0 && (DEBUG_LEVEL > 0)) | ||
1061 | perror("verbose read result (OK for this to be bad)"); | ||
1062 | pkey_assert(ret); | ||
1063 | } | ||
1064 | |||
1065 | void test_kernel_gup_of_access_disabled_region(int *ptr, u16 pkey) | ||
1066 | { | ||
1067 | int pipe_ret, vmsplice_ret; | ||
1068 | struct iovec iov; | ||
1069 | int pipe_fds[2]; | ||
1070 | |||
1071 | pipe_ret = pipe(pipe_fds); | ||
1072 | |||
1073 | pkey_assert(pipe_ret == 0); | ||
1074 | dprintf1("disabling access to PKEY[%02d], " | ||
1075 | "having kernel vmsplice from buffer\n", pkey); | ||
1076 | pkey_access_deny(pkey); | ||
1077 | iov.iov_base = ptr; | ||
1078 | iov.iov_len = PAGE_SIZE; | ||
1079 | vmsplice_ret = vmsplice(pipe_fds[1], &iov, 1, SPLICE_F_GIFT); | ||
1080 | dprintf1("vmsplice() ret: %d\n", vmsplice_ret); | ||
1081 | pkey_assert(vmsplice_ret == -1); | ||
1082 | |||
1083 | close(pipe_fds[0]); | ||
1084 | close(pipe_fds[1]); | ||
1085 | } | ||
1086 | |||
1087 | void test_kernel_gup_write_to_write_disabled_region(int *ptr, u16 pkey) | ||
1088 | { | ||
1089 | int ignored = 0xdada; | ||
1090 | int futex_ret; | ||
1091 | int some_int = __LINE__; | ||
1092 | |||
1093 | dprintf1("disabling write to PKEY[%02d], " | ||
1094 | "doing futex gunk in buffer\n", pkey); | ||
1095 | *ptr = some_int; | ||
1096 | pkey_write_deny(pkey); | ||
1097 | futex_ret = syscall(SYS_futex, ptr, FUTEX_WAIT, some_int-1, NULL, | ||
1098 | &ignored, ignored); | ||
1099 | if (DEBUG_LEVEL > 0) | ||
1100 | perror("futex"); | ||
1101 | dprintf1("futex() ret: %d\n", futex_ret); | ||
1102 | } | ||
1103 | |||
1104 | /* Assumes that all pkeys other than 'pkey' are unallocated */ | ||
1105 | void test_pkey_syscalls_on_non_allocated_pkey(int *ptr, u16 pkey) | ||
1106 | { | ||
1107 | int err; | ||
1108 | int i; | ||
1109 | |||
1110 | /* Note: 0 is the default pkey, so don't mess with it */ | ||
1111 | for (i = 1; i < NR_PKEYS; i++) { | ||
1112 | if (pkey == i) | ||
1113 | continue; | ||
1114 | |||
1115 | dprintf1("trying get/set/free to non-allocated pkey: %2d\n", i); | ||
1116 | err = sys_pkey_free(i); | ||
1117 | pkey_assert(err); | ||
1118 | |||
1119 | /* not enforced when pkey_get() is not a syscall | ||
1120 | err = pkey_get(i, 0); | ||
1121 | pkey_assert(err < 0); | ||
1122 | */ | ||
1123 | |||
1124 | err = sys_pkey_free(i); | ||
1125 | pkey_assert(err); | ||
1126 | |||
1127 | err = sys_mprotect_pkey(ptr, PAGE_SIZE, PROT_READ, i); | ||
1128 | pkey_assert(err); | ||
1129 | } | ||
1130 | } | ||
1131 | |||
1132 | /* Assumes that all pkeys other than 'pkey' are unallocated */ | ||
1133 | void test_pkey_syscalls_bad_args(int *ptr, u16 pkey) | ||
1134 | { | ||
1135 | int err; | ||
1136 | int bad_flag = (PKEY_DISABLE_ACCESS | PKEY_DISABLE_WRITE) + 1; | ||
1137 | int bad_pkey = NR_PKEYS+99; | ||
1138 | |||
1139 | /* not enforced when pkey_get() is not a syscall | ||
1140 | err = pkey_get(bad_pkey, bad_flag); | ||
1141 | pkey_assert(err < 0); | ||
1142 | */ | ||
1143 | |||
1144 | /* pass a known-invalid pkey in: */ | ||
1145 | err = sys_mprotect_pkey(ptr, PAGE_SIZE, PROT_READ, bad_pkey); | ||
1146 | pkey_assert(err); | ||
1147 | } | ||
1148 | |||
1149 | /* Assumes that all pkeys other than 'pkey' are unallocated */ | ||
1150 | void test_pkey_alloc_exhaust(int *ptr, u16 pkey) | ||
1151 | { | ||
1152 | unsigned long flags; | ||
1153 | unsigned long init_val; | ||
1154 | int err; | ||
1155 | int allocated_pkeys[NR_PKEYS] = {0}; | ||
1156 | int nr_allocated_pkeys = 0; | ||
1157 | int i; | ||
1158 | |||
1159 | for (i = 0; i < NR_PKEYS*2; i++) { | ||
1160 | int new_pkey; | ||
1161 | dprintf1("%s() alloc loop: %d\n", __func__, i); | ||
1162 | new_pkey = alloc_pkey(); | ||
1163 | dprintf4("%s()::%d, err: %d pkru: 0x%x shadow: 0x%x\n", __func__, | ||
1164 | __LINE__, err, __rdpkru(), shadow_pkru); | ||
1165 | rdpkru(); /* for shadow checking */ | ||
1166 | dprintf2("%s() errno: %d ENOSPC: %d\n", __func__, errno, ENOSPC); | ||
1167 | if ((new_pkey == -1) && (errno == ENOSPC)) { | ||
1168 | dprintf2("%s() failed to allocate pkey after %d tries\n", | ||
1169 | __func__, nr_allocated_pkeys); | ||
1170 | break; | ||
1171 | } | ||
1172 | pkey_assert(nr_allocated_pkeys < NR_PKEYS); | ||
1173 | allocated_pkeys[nr_allocated_pkeys++] = new_pkey; | ||
1174 | } | ||
1175 | |||
1176 | dprintf3("%s()::%d\n", __func__, __LINE__); | ||
1177 | |||
1178 | /* | ||
1179 | * ensure it did not reach the end of the loop without | ||
1180 | * failure: | ||
1181 | */ | ||
1182 | pkey_assert(i < NR_PKEYS*2); | ||
1183 | |||
1184 | /* | ||
1185 | * There are 16 pkeys supported in hardware. One is taken | ||
1186 | * up for the default (0) and another can be taken up by | ||
1187 | * an execute-only mapping. Ensure that we can allocate | ||
1188 | * at least 14 (16-2). | ||
1189 | */ | ||
1190 | pkey_assert(i >= NR_PKEYS-2); | ||
1191 | |||
1192 | for (i = 0; i < nr_allocated_pkeys; i++) { | ||
1193 | err = sys_pkey_free(allocated_pkeys[i]); | ||
1194 | pkey_assert(!err); | ||
1195 | rdpkru(); /* for shadow checking */ | ||
1196 | } | ||
1197 | } | ||
1198 | |||
1199 | void test_ptrace_of_child(int *ptr, u16 pkey) | ||
1200 | { | ||
1201 | __attribute__((__unused__)) int peek_result; | ||
1202 | pid_t child_pid; | ||
1203 | void *ignored = 0; | ||
1204 | long ret; | ||
1205 | int status; | ||
1206 | /* | ||
1207 | * This is the "control" for our little expermient. Make sure | ||
1208 | * we can always access it when ptracing. | ||
1209 | */ | ||
1210 | int *plain_ptr_unaligned = malloc(HPAGE_SIZE); | ||
1211 | int *plain_ptr = ALIGN_PTR_UP(plain_ptr_unaligned, PAGE_SIZE); | ||
1212 | |||
1213 | /* | ||
1214 | * Fork a child which is an exact copy of this process, of course. | ||
1215 | * That means we can do all of our tests via ptrace() and then plain | ||
1216 | * memory access and ensure they work differently. | ||
1217 | */ | ||
1218 | child_pid = fork_lazy_child(); | ||
1219 | dprintf1("[%d] child pid: %d\n", getpid(), child_pid); | ||
1220 | |||
1221 | ret = ptrace(PTRACE_ATTACH, child_pid, ignored, ignored); | ||
1222 | if (ret) | ||
1223 | perror("attach"); | ||
1224 | dprintf1("[%d] attach ret: %ld %d\n", getpid(), ret, __LINE__); | ||
1225 | pkey_assert(ret != -1); | ||
1226 | ret = waitpid(child_pid, &status, WUNTRACED); | ||
1227 | if ((ret != child_pid) || !(WIFSTOPPED(status))) { | ||
1228 | fprintf(stderr, "weird waitpid result %ld stat %x\n", | ||
1229 | ret, status); | ||
1230 | pkey_assert(0); | ||
1231 | } | ||
1232 | dprintf2("waitpid ret: %ld\n", ret); | ||
1233 | dprintf2("waitpid status: %d\n", status); | ||
1234 | |||
1235 | pkey_access_deny(pkey); | ||
1236 | pkey_write_deny(pkey); | ||
1237 | |||
1238 | /* Write access, untested for now: | ||
1239 | ret = ptrace(PTRACE_POKEDATA, child_pid, peek_at, data); | ||
1240 | pkey_assert(ret != -1); | ||
1241 | dprintf1("poke at %p: %ld\n", peek_at, ret); | ||
1242 | */ | ||
1243 | |||
1244 | /* | ||
1245 | * Try to access the pkey-protected "ptr" via ptrace: | ||
1246 | */ | ||
1247 | ret = ptrace(PTRACE_PEEKDATA, child_pid, ptr, ignored); | ||
1248 | /* expect it to work, without an error: */ | ||
1249 | pkey_assert(ret != -1); | ||
1250 | /* Now access from the current task, and expect an exception: */ | ||
1251 | peek_result = read_ptr(ptr); | ||
1252 | expected_pk_fault(pkey); | ||
1253 | |||
1254 | /* | ||
1255 | * Try to access the NON-pkey-protected "plain_ptr" via ptrace: | ||
1256 | */ | ||
1257 | ret = ptrace(PTRACE_PEEKDATA, child_pid, plain_ptr, ignored); | ||
1258 | /* expect it to work, without an error: */ | ||
1259 | pkey_assert(ret != -1); | ||
1260 | /* Now access from the current task, and expect NO exception: */ | ||
1261 | peek_result = read_ptr(plain_ptr); | ||
1262 | do_not_expect_pk_fault(); | ||
1263 | |||
1264 | ret = ptrace(PTRACE_DETACH, child_pid, ignored, 0); | ||
1265 | pkey_assert(ret != -1); | ||
1266 | |||
1267 | ret = kill(child_pid, SIGKILL); | ||
1268 | pkey_assert(ret != -1); | ||
1269 | |||
1270 | wait(&status); | ||
1271 | |||
1272 | free(plain_ptr_unaligned); | ||
1273 | } | ||
1274 | |||
1275 | void test_executing_on_unreadable_memory(int *ptr, u16 pkey) | ||
1276 | { | ||
1277 | void *p1; | ||
1278 | int scratch; | ||
1279 | int ptr_contents; | ||
1280 | int ret; | ||
1281 | |||
1282 | p1 = ALIGN_PTR_UP(&lots_o_noops_around_write, PAGE_SIZE); | ||
1283 | dprintf3("&lots_o_noops: %p\n", &lots_o_noops_around_write); | ||
1284 | /* lots_o_noops_around_write should be page-aligned already */ | ||
1285 | assert(p1 == &lots_o_noops_around_write); | ||
1286 | |||
1287 | /* Point 'p1' at the *second* page of the function: */ | ||
1288 | p1 += PAGE_SIZE; | ||
1289 | |||
1290 | madvise(p1, PAGE_SIZE, MADV_DONTNEED); | ||
1291 | lots_o_noops_around_write(&scratch); | ||
1292 | ptr_contents = read_ptr(p1); | ||
1293 | dprintf2("ptr (%p) contents@%d: %x\n", p1, __LINE__, ptr_contents); | ||
1294 | |||
1295 | ret = mprotect_pkey(p1, PAGE_SIZE, PROT_EXEC, (u64)pkey); | ||
1296 | pkey_assert(!ret); | ||
1297 | pkey_access_deny(pkey); | ||
1298 | |||
1299 | dprintf2("pkru: %x\n", rdpkru()); | ||
1300 | |||
1301 | /* | ||
1302 | * Make sure this is an *instruction* fault | ||
1303 | */ | ||
1304 | madvise(p1, PAGE_SIZE, MADV_DONTNEED); | ||
1305 | lots_o_noops_around_write(&scratch); | ||
1306 | do_not_expect_pk_fault(); | ||
1307 | ptr_contents = read_ptr(p1); | ||
1308 | dprintf2("ptr (%p) contents@%d: %x\n", p1, __LINE__, ptr_contents); | ||
1309 | expected_pk_fault(pkey); | ||
1310 | } | ||
1311 | |||
1312 | void test_mprotect_pkey_on_unsupported_cpu(int *ptr, u16 pkey) | ||
1313 | { | ||
1314 | int size = PAGE_SIZE; | ||
1315 | int sret; | ||
1316 | |||
1317 | if (cpu_has_pku()) { | ||
1318 | dprintf1("SKIP: %s: no CPU support\n", __func__); | ||
1319 | return; | ||
1320 | } | ||
1321 | |||
1322 | sret = syscall(SYS_mprotect_key, ptr, size, PROT_READ, pkey); | ||
1323 | pkey_assert(sret < 0); | ||
1324 | } | ||
1325 | |||
1326 | void (*pkey_tests[])(int *ptr, u16 pkey) = { | ||
1327 | test_read_of_write_disabled_region, | ||
1328 | test_read_of_access_disabled_region, | ||
1329 | test_write_of_write_disabled_region, | ||
1330 | test_write_of_access_disabled_region, | ||
1331 | test_kernel_write_of_access_disabled_region, | ||
1332 | test_kernel_write_of_write_disabled_region, | ||
1333 | test_kernel_gup_of_access_disabled_region, | ||
1334 | test_kernel_gup_write_to_write_disabled_region, | ||
1335 | test_executing_on_unreadable_memory, | ||
1336 | test_ptrace_of_child, | ||
1337 | test_pkey_syscalls_on_non_allocated_pkey, | ||
1338 | test_pkey_syscalls_bad_args, | ||
1339 | test_pkey_alloc_exhaust, | ||
1340 | }; | ||
1341 | |||
1342 | void run_tests_once(void) | ||
1343 | { | ||
1344 | int *ptr; | ||
1345 | int prot = PROT_READ|PROT_WRITE; | ||
1346 | |||
1347 | for (test_nr = 0; test_nr < ARRAY_SIZE(pkey_tests); test_nr++) { | ||
1348 | int pkey; | ||
1349 | int orig_pkru_faults = pkru_faults; | ||
1350 | |||
1351 | dprintf1("======================\n"); | ||
1352 | dprintf1("test %d preparing...\n", test_nr); | ||
1353 | |||
1354 | tracing_on(); | ||
1355 | pkey = alloc_random_pkey(); | ||
1356 | dprintf1("test %d starting with pkey: %d\n", test_nr, pkey); | ||
1357 | ptr = malloc_pkey(PAGE_SIZE, prot, pkey); | ||
1358 | dprintf1("test %d starting...\n", test_nr); | ||
1359 | pkey_tests[test_nr](ptr, pkey); | ||
1360 | dprintf1("freeing test memory: %p\n", ptr); | ||
1361 | free_pkey_malloc(ptr); | ||
1362 | sys_pkey_free(pkey); | ||
1363 | |||
1364 | dprintf1("pkru_faults: %d\n", pkru_faults); | ||
1365 | dprintf1("orig_pkru_faults: %d\n", orig_pkru_faults); | ||
1366 | |||
1367 | tracing_off(); | ||
1368 | close_test_fds(); | ||
1369 | |||
1370 | printf("test %2d PASSED (itertation %d)\n", test_nr, iteration_nr); | ||
1371 | dprintf1("======================\n\n"); | ||
1372 | } | ||
1373 | iteration_nr++; | ||
1374 | } | ||
1375 | |||
1376 | void pkey_setup_shadow(void) | ||
1377 | { | ||
1378 | shadow_pkru = __rdpkru(); | ||
1379 | } | ||
1380 | |||
1381 | int main(void) | ||
1382 | { | ||
1383 | int nr_iterations = 22; | ||
1384 | |||
1385 | setup_handlers(); | ||
1386 | |||
1387 | printf("has pku: %d\n", cpu_has_pku()); | ||
1388 | |||
1389 | if (!cpu_has_pku()) { | ||
1390 | int size = PAGE_SIZE; | ||
1391 | int *ptr; | ||
1392 | |||
1393 | printf("running PKEY tests for unsupported CPU/OS\n"); | ||
1394 | |||
1395 | ptr = mmap(NULL, size, PROT_NONE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); | ||
1396 | assert(ptr != (void *)-1); | ||
1397 | test_mprotect_pkey_on_unsupported_cpu(ptr, 1); | ||
1398 | exit(0); | ||
1399 | } | ||
1400 | |||
1401 | pkey_setup_shadow(); | ||
1402 | printf("startup pkru: %x\n", rdpkru()); | ||
1403 | setup_hugetlbfs(); | ||
1404 | |||
1405 | while (nr_iterations-- > 0) | ||
1406 | run_tests_once(); | ||
1407 | |||
1408 | printf("done (all tests OK)\n"); | ||
1409 | return 0; | ||
1410 | } | ||
diff --git a/tools/testing/selftests/zram/README b/tools/testing/selftests/zram/README index eb17917c8a3a..7972cc512408 100644 --- a/tools/testing/selftests/zram/README +++ b/tools/testing/selftests/zram/README | |||
@@ -13,7 +13,7 @@ Statistics for individual zram devices are exported through sysfs nodes at | |||
13 | 13 | ||
14 | Kconfig required: | 14 | Kconfig required: |
15 | CONFIG_ZRAM=y | 15 | CONFIG_ZRAM=y |
16 | CONFIG_ZRAM_LZ4_COMPRESS=y | 16 | CONFIG_CRYPTO_LZ4=y |
17 | CONFIG_ZPOOL=y | 17 | CONFIG_ZPOOL=y |
18 | CONFIG_ZSMALLOC=y | 18 | CONFIG_ZSMALLOC=y |
19 | 19 | ||