| .. | .. |
|---|
| 7 | 7 | #include <linux/module.h> /* for MODULE_NAME_LEN via KSYM_SYMBOL_LEN */ |
|---|
| 8 | 8 | #include <linux/ftrace.h> |
|---|
| 9 | 9 | #include <linux/perf_event.h> |
|---|
| 10 | +#include <linux/xarray.h> |
|---|
| 10 | 11 | #include <asm/syscall.h> |
|---|
| 11 | 12 | |
|---|
| 12 | 13 | #include "trace_output.h" |
|---|
| .. | .. |
|---|
| 30 | 31 | extern struct syscall_metadata *__start_syscalls_metadata[]; |
|---|
| 31 | 32 | extern struct syscall_metadata *__stop_syscalls_metadata[]; |
|---|
| 32 | 33 | |
|---|
| 34 | +static DEFINE_XARRAY(syscalls_metadata_sparse); |
|---|
| 33 | 35 | static struct syscall_metadata **syscalls_metadata; |
|---|
| 34 | 36 | |
|---|
| 35 | 37 | #ifndef ARCH_HAS_SYSCALL_MATCH_SYM_NAME |
|---|
| .. | .. |
|---|
| 101 | 103 | |
|---|
| 102 | 104 | static struct syscall_metadata *syscall_nr_to_meta(int nr) |
|---|
| 103 | 105 | { |
|---|
| 106 | + if (IS_ENABLED(CONFIG_HAVE_SPARSE_SYSCALL_NR)) |
|---|
| 107 | + return xa_load(&syscalls_metadata_sparse, (unsigned long)nr); |
|---|
| 108 | + |
|---|
| 104 | 109 | if (!syscalls_metadata || nr >= NR_syscalls || nr < 0) |
|---|
| 105 | 110 | return NULL; |
|---|
| 106 | 111 | |
|---|
| .. | .. |
|---|
| 198 | 203 | |
|---|
| 199 | 204 | extern char *__bad_type_size(void); |
|---|
| 200 | 205 | |
|---|
| 201 | | -#define SYSCALL_FIELD(type, field, name) \ |
|---|
| 202 | | - sizeof(type) != sizeof(trace.field) ? \ |
|---|
| 203 | | - __bad_type_size() : \ |
|---|
| 204 | | - #type, #name, offsetof(typeof(trace), field), \ |
|---|
| 205 | | - sizeof(trace.field), is_signed_type(type) |
|---|
| 206 | +#define SYSCALL_FIELD(_type, _name) { \ |
|---|
| 207 | + .type = #_type, .name = #_name, \ |
|---|
| 208 | + .size = sizeof(_type), .align = __alignof__(_type), \ |
|---|
| 209 | + .is_signed = is_signed_type(_type), .filter_type = FILTER_OTHER } |
|---|
| 206 | 210 | |
|---|
| 207 | 211 | static int __init |
|---|
| 208 | 212 | __set_enter_print_fmt(struct syscall_metadata *entry, char *buf, int len) |
|---|
| .. | .. |
|---|
| 269 | 273 | { |
|---|
| 270 | 274 | struct syscall_trace_enter trace; |
|---|
| 271 | 275 | struct syscall_metadata *meta = call->data; |
|---|
| 272 | | - int ret; |
|---|
| 273 | | - int i; |
|---|
| 274 | 276 | int offset = offsetof(typeof(trace), args); |
|---|
| 275 | | - |
|---|
| 276 | | - ret = trace_define_field(call, SYSCALL_FIELD(int, nr, __syscall_nr), |
|---|
| 277 | | - FILTER_OTHER); |
|---|
| 278 | | - if (ret) |
|---|
| 279 | | - return ret; |
|---|
| 277 | + int ret = 0; |
|---|
| 278 | + int i; |
|---|
| 280 | 279 | |
|---|
| 281 | 280 | for (i = 0; i < meta->nb_args; i++) { |
|---|
| 282 | 281 | ret = trace_define_field(call, meta->types[i], |
|---|
| 283 | 282 | meta->args[i], offset, |
|---|
| 284 | 283 | sizeof(unsigned long), 0, |
|---|
| 285 | 284 | FILTER_OTHER); |
|---|
| 285 | + if (ret) |
|---|
| 286 | + break; |
|---|
| 286 | 287 | offset += sizeof(unsigned long); |
|---|
| 287 | 288 | } |
|---|
| 288 | | - |
|---|
| 289 | | - return ret; |
|---|
| 290 | | -} |
|---|
| 291 | | - |
|---|
| 292 | | -static int __init syscall_exit_define_fields(struct trace_event_call *call) |
|---|
| 293 | | -{ |
|---|
| 294 | | - struct syscall_trace_exit trace; |
|---|
| 295 | | - int ret; |
|---|
| 296 | | - |
|---|
| 297 | | - ret = trace_define_field(call, SYSCALL_FIELD(int, nr, __syscall_nr), |
|---|
| 298 | | - FILTER_OTHER); |
|---|
| 299 | | - if (ret) |
|---|
| 300 | | - return ret; |
|---|
| 301 | | - |
|---|
| 302 | | - ret = trace_define_field(call, SYSCALL_FIELD(long, ret, ret), |
|---|
| 303 | | - FILTER_OTHER); |
|---|
| 304 | 289 | |
|---|
| 305 | 290 | return ret; |
|---|
| 306 | 291 | } |
|---|
| .. | .. |
|---|
| 312 | 297 | struct syscall_trace_enter *entry; |
|---|
| 313 | 298 | struct syscall_metadata *sys_data; |
|---|
| 314 | 299 | struct ring_buffer_event *event; |
|---|
| 315 | | - struct ring_buffer *buffer; |
|---|
| 316 | | - unsigned long irq_flags; |
|---|
| 317 | | - int pc; |
|---|
| 300 | + struct trace_buffer *buffer; |
|---|
| 301 | + unsigned int trace_ctx; |
|---|
| 302 | + unsigned long args[6]; |
|---|
| 318 | 303 | int syscall_nr; |
|---|
| 319 | 304 | int size; |
|---|
| 320 | 305 | |
|---|
| .. | .. |
|---|
| 336 | 321 | |
|---|
| 337 | 322 | size = sizeof(*entry) + sizeof(unsigned long) * sys_data->nb_args; |
|---|
| 338 | 323 | |
|---|
| 339 | | - local_save_flags(irq_flags); |
|---|
| 340 | | - pc = preempt_count(); |
|---|
| 324 | + trace_ctx = tracing_gen_ctx(); |
|---|
| 341 | 325 | |
|---|
| 342 | | - buffer = tr->trace_buffer.buffer; |
|---|
| 326 | + buffer = tr->array_buffer.buffer; |
|---|
| 343 | 327 | event = trace_buffer_lock_reserve(buffer, |
|---|
| 344 | | - sys_data->enter_event->event.type, size, irq_flags, pc); |
|---|
| 328 | + sys_data->enter_event->event.type, size, trace_ctx); |
|---|
| 345 | 329 | if (!event) |
|---|
| 346 | 330 | return; |
|---|
| 347 | 331 | |
|---|
| 348 | 332 | entry = ring_buffer_event_data(event); |
|---|
| 349 | 333 | entry->nr = syscall_nr; |
|---|
| 350 | | - syscall_get_arguments(current, regs, 0, sys_data->nb_args, entry->args); |
|---|
| 334 | + syscall_get_arguments(current, regs, args); |
|---|
| 335 | + memcpy(entry->args, args, sizeof(unsigned long) * sys_data->nb_args); |
|---|
| 351 | 336 | |
|---|
| 352 | 337 | event_trigger_unlock_commit(trace_file, buffer, event, entry, |
|---|
| 353 | | - irq_flags, pc); |
|---|
| 338 | + trace_ctx); |
|---|
| 354 | 339 | } |
|---|
| 355 | 340 | |
|---|
| 356 | 341 | static void ftrace_syscall_exit(void *data, struct pt_regs *regs, long ret) |
|---|
| .. | .. |
|---|
| 360 | 345 | struct syscall_trace_exit *entry; |
|---|
| 361 | 346 | struct syscall_metadata *sys_data; |
|---|
| 362 | 347 | struct ring_buffer_event *event; |
|---|
| 363 | | - struct ring_buffer *buffer; |
|---|
| 364 | | - unsigned long irq_flags; |
|---|
| 365 | | - int pc; |
|---|
| 348 | + struct trace_buffer *buffer; |
|---|
| 349 | + unsigned int trace_ctx; |
|---|
| 366 | 350 | int syscall_nr; |
|---|
| 367 | 351 | |
|---|
| 368 | 352 | syscall_nr = trace_get_syscall_nr(current, regs); |
|---|
| .. | .. |
|---|
| 381 | 365 | if (!sys_data) |
|---|
| 382 | 366 | return; |
|---|
| 383 | 367 | |
|---|
| 384 | | - local_save_flags(irq_flags); |
|---|
| 385 | | - pc = preempt_count(); |
|---|
| 368 | + trace_ctx = tracing_gen_ctx(); |
|---|
| 386 | 369 | |
|---|
| 387 | | - buffer = tr->trace_buffer.buffer; |
|---|
| 370 | + buffer = tr->array_buffer.buffer; |
|---|
| 388 | 371 | event = trace_buffer_lock_reserve(buffer, |
|---|
| 389 | 372 | sys_data->exit_event->event.type, sizeof(*entry), |
|---|
| 390 | | - irq_flags, pc); |
|---|
| 373 | + trace_ctx); |
|---|
| 391 | 374 | if (!event) |
|---|
| 392 | 375 | return; |
|---|
| 393 | 376 | |
|---|
| .. | .. |
|---|
| 396 | 379 | entry->ret = syscall_get_return_value(current, regs); |
|---|
| 397 | 380 | |
|---|
| 398 | 381 | event_trigger_unlock_commit(trace_file, buffer, event, entry, |
|---|
| 399 | | - irq_flags, pc); |
|---|
| 382 | + trace_ctx); |
|---|
| 400 | 383 | } |
|---|
| 401 | 384 | |
|---|
| 402 | 385 | static int reg_event_syscall_enter(struct trace_event_file *file, |
|---|
| .. | .. |
|---|
| 500 | 483 | return id; |
|---|
| 501 | 484 | } |
|---|
| 502 | 485 | |
|---|
| 486 | +static struct trace_event_fields __refdata syscall_enter_fields_array[] = { |
|---|
| 487 | + SYSCALL_FIELD(int, __syscall_nr), |
|---|
| 488 | + { .type = TRACE_FUNCTION_TYPE, |
|---|
| 489 | + .define_fields = syscall_enter_define_fields }, |
|---|
| 490 | + {} |
|---|
| 491 | +}; |
|---|
| 492 | + |
|---|
| 503 | 493 | struct trace_event_functions enter_syscall_print_funcs = { |
|---|
| 504 | 494 | .trace = print_syscall_enter, |
|---|
| 505 | 495 | }; |
|---|
| .. | .. |
|---|
| 511 | 501 | struct trace_event_class __refdata event_class_syscall_enter = { |
|---|
| 512 | 502 | .system = "syscalls", |
|---|
| 513 | 503 | .reg = syscall_enter_register, |
|---|
| 514 | | - .define_fields = syscall_enter_define_fields, |
|---|
| 504 | + .fields_array = syscall_enter_fields_array, |
|---|
| 515 | 505 | .get_fields = syscall_get_enter_fields, |
|---|
| 516 | 506 | .raw_init = init_syscall_trace, |
|---|
| 517 | 507 | }; |
|---|
| .. | .. |
|---|
| 519 | 509 | struct trace_event_class __refdata event_class_syscall_exit = { |
|---|
| 520 | 510 | .system = "syscalls", |
|---|
| 521 | 511 | .reg = syscall_exit_register, |
|---|
| 522 | | - .define_fields = syscall_exit_define_fields, |
|---|
| 512 | + .fields_array = (struct trace_event_fields[]){ |
|---|
| 513 | + SYSCALL_FIELD(int, __syscall_nr), |
|---|
| 514 | + SYSCALL_FIELD(long, ret), |
|---|
| 515 | + {} |
|---|
| 516 | + }, |
|---|
| 523 | 517 | .fields = LIST_HEAD_INIT(event_class_syscall_exit.fields), |
|---|
| 524 | 518 | .raw_init = init_syscall_trace, |
|---|
| 525 | 519 | }; |
|---|
| .. | .. |
|---|
| 534 | 528 | struct syscall_metadata *meta; |
|---|
| 535 | 529 | unsigned long addr; |
|---|
| 536 | 530 | int i; |
|---|
| 531 | + void *ret; |
|---|
| 537 | 532 | |
|---|
| 538 | | - syscalls_metadata = kcalloc(NR_syscalls, sizeof(*syscalls_metadata), |
|---|
| 539 | | - GFP_KERNEL); |
|---|
| 540 | | - if (!syscalls_metadata) { |
|---|
| 541 | | - WARN_ON(1); |
|---|
| 542 | | - return; |
|---|
| 533 | + if (!IS_ENABLED(CONFIG_HAVE_SPARSE_SYSCALL_NR)) { |
|---|
| 534 | + syscalls_metadata = kcalloc(NR_syscalls, |
|---|
| 535 | + sizeof(*syscalls_metadata), |
|---|
| 536 | + GFP_KERNEL); |
|---|
| 537 | + if (!syscalls_metadata) { |
|---|
| 538 | + WARN_ON(1); |
|---|
| 539 | + return; |
|---|
| 540 | + } |
|---|
| 543 | 541 | } |
|---|
| 544 | 542 | |
|---|
| 545 | 543 | for (i = 0; i < NR_syscalls; i++) { |
|---|
| .. | .. |
|---|
| 549 | 547 | continue; |
|---|
| 550 | 548 | |
|---|
| 551 | 549 | meta->syscall_nr = i; |
|---|
| 552 | | - syscalls_metadata[i] = meta; |
|---|
| 550 | + |
|---|
| 551 | + if (!IS_ENABLED(CONFIG_HAVE_SPARSE_SYSCALL_NR)) { |
|---|
| 552 | + syscalls_metadata[i] = meta; |
|---|
| 553 | + } else { |
|---|
| 554 | + ret = xa_store(&syscalls_metadata_sparse, i, meta, |
|---|
| 555 | + GFP_KERNEL); |
|---|
| 556 | + WARN(xa_is_err(ret), |
|---|
| 557 | + "Syscall memory allocation failed\n"); |
|---|
| 558 | + } |
|---|
| 559 | + |
|---|
| 553 | 560 | } |
|---|
| 554 | 561 | } |
|---|
| 555 | 562 | |
|---|
| .. | .. |
|---|
| 583 | 590 | struct syscall_metadata *sys_data; |
|---|
| 584 | 591 | struct syscall_trace_enter *rec; |
|---|
| 585 | 592 | struct hlist_head *head; |
|---|
| 593 | + unsigned long args[6]; |
|---|
| 586 | 594 | bool valid_prog_array; |
|---|
| 587 | 595 | int syscall_nr; |
|---|
| 588 | 596 | int rctx; |
|---|
| .. | .. |
|---|
| 613 | 621 | return; |
|---|
| 614 | 622 | |
|---|
| 615 | 623 | rec->nr = syscall_nr; |
|---|
| 616 | | - syscall_get_arguments(current, regs, 0, sys_data->nb_args, |
|---|
| 617 | | - (unsigned long *)&rec->args); |
|---|
| 624 | + syscall_get_arguments(current, regs, args); |
|---|
| 625 | + memcpy(&rec->args, args, sizeof(unsigned long) * sys_data->nb_args); |
|---|
| 618 | 626 | |
|---|
| 619 | 627 | if ((valid_prog_array && |
|---|
| 620 | 628 | !perf_call_bpf_enter(sys_data->enter_event, regs, sys_data, rec)) || |
|---|