diff options
Diffstat (limited to 'kernel/bpf/arraymap.c')
| -rw-r--r-- | kernel/bpf/arraymap.c | 102 |
1 files changed, 74 insertions, 28 deletions
diff --git a/kernel/bpf/arraymap.c b/kernel/bpf/arraymap.c index bfedcbdb4d84..5af30732697b 100644 --- a/kernel/bpf/arraymap.c +++ b/kernel/bpf/arraymap.c | |||
| @@ -427,59 +427,105 @@ static int __init register_prog_array_map(void) | |||
| 427 | } | 427 | } |
| 428 | late_initcall(register_prog_array_map); | 428 | late_initcall(register_prog_array_map); |
| 429 | 429 | ||
| 430 | static void perf_event_array_map_free(struct bpf_map *map) | 430 | static struct bpf_event_entry *bpf_event_entry_gen(struct file *perf_file, |
| 431 | struct file *map_file) | ||
| 431 | { | 432 | { |
| 432 | bpf_fd_array_map_clear(map); | 433 | struct bpf_event_entry *ee; |
| 433 | fd_array_map_free(map); | 434 | |
| 435 | ee = kzalloc(sizeof(*ee), GFP_KERNEL); | ||
| 436 | if (ee) { | ||
| 437 | ee->event = perf_file->private_data; | ||
| 438 | ee->perf_file = perf_file; | ||
| 439 | ee->map_file = map_file; | ||
| 440 | } | ||
| 441 | |||
| 442 | return ee; | ||
| 443 | } | ||
| 444 | |||
| 445 | static void __bpf_event_entry_free(struct rcu_head *rcu) | ||
| 446 | { | ||
| 447 | struct bpf_event_entry *ee; | ||
| 448 | |||
| 449 | ee = container_of(rcu, struct bpf_event_entry, rcu); | ||
| 450 | fput(ee->perf_file); | ||
| 451 | kfree(ee); | ||
| 452 | } | ||
| 453 | |||
| 454 | static void bpf_event_entry_free_rcu(struct bpf_event_entry *ee) | ||
| 455 | { | ||
| 456 | call_rcu(&ee->rcu, __bpf_event_entry_free); | ||
| 434 | } | 457 | } |
| 435 | 458 | ||
| 436 | static void *perf_event_fd_array_get_ptr(struct bpf_map *map, | 459 | static void *perf_event_fd_array_get_ptr(struct bpf_map *map, |
| 437 | struct file *map_file, int fd) | 460 | struct file *map_file, int fd) |
| 438 | { | 461 | { |
| 439 | struct perf_event *event; | ||
| 440 | const struct perf_event_attr *attr; | 462 | const struct perf_event_attr *attr; |
| 441 | struct file *file; | 463 | struct bpf_event_entry *ee; |
| 464 | struct perf_event *event; | ||
| 465 | struct file *perf_file; | ||
| 442 | 466 | ||
| 443 | file = perf_event_get(fd); | 467 | perf_file = perf_event_get(fd); |
| 444 | if (IS_ERR(file)) | 468 | if (IS_ERR(perf_file)) |
| 445 | return file; | 469 | return perf_file; |
| 446 | 470 | ||
| 447 | event = file->private_data; | 471 | event = perf_file->private_data; |
| 472 | ee = ERR_PTR(-EINVAL); | ||
| 448 | 473 | ||
| 449 | attr = perf_event_attrs(event); | 474 | attr = perf_event_attrs(event); |
| 450 | if (IS_ERR(attr)) | 475 | if (IS_ERR(attr) || attr->inherit) |
| 451 | goto err; | 476 | goto err_out; |
| 452 | 477 | ||
| 453 | if (attr->inherit) | 478 | switch (attr->type) { |
| 454 | goto err; | 479 | case PERF_TYPE_SOFTWARE: |
| 455 | 480 | if (attr->config != PERF_COUNT_SW_BPF_OUTPUT) | |
| 456 | if (attr->type == PERF_TYPE_RAW) | 481 | goto err_out; |
| 457 | return file; | 482 | /* fall-through */ |
| 458 | 483 | case PERF_TYPE_RAW: | |
| 459 | if (attr->type == PERF_TYPE_HARDWARE) | 484 | case PERF_TYPE_HARDWARE: |
| 460 | return file; | 485 | ee = bpf_event_entry_gen(perf_file, map_file); |
| 486 | if (ee) | ||
| 487 | return ee; | ||
| 488 | ee = ERR_PTR(-ENOMEM); | ||
| 489 | /* fall-through */ | ||
| 490 | default: | ||
| 491 | break; | ||
| 492 | } | ||
| 461 | 493 | ||
| 462 | if (attr->type == PERF_TYPE_SOFTWARE && | 494 | err_out: |
| 463 | attr->config == PERF_COUNT_SW_BPF_OUTPUT) | 495 | fput(perf_file); |
| 464 | return file; | 496 | return ee; |
| 465 | err: | ||
| 466 | fput(file); | ||
| 467 | return ERR_PTR(-EINVAL); | ||
| 468 | } | 497 | } |
| 469 | 498 | ||
| 470 | static void perf_event_fd_array_put_ptr(void *ptr) | 499 | static void perf_event_fd_array_put_ptr(void *ptr) |
| 471 | { | 500 | { |
| 472 | fput((struct file *)ptr); | 501 | bpf_event_entry_free_rcu(ptr); |
| 502 | } | ||
| 503 | |||
| 504 | static void perf_event_fd_array_release(struct bpf_map *map, | ||
| 505 | struct file *map_file) | ||
| 506 | { | ||
| 507 | struct bpf_array *array = container_of(map, struct bpf_array, map); | ||
| 508 | struct bpf_event_entry *ee; | ||
| 509 | int i; | ||
| 510 | |||
| 511 | rcu_read_lock(); | ||
| 512 | for (i = 0; i < array->map.max_entries; i++) { | ||
| 513 | ee = READ_ONCE(array->ptrs[i]); | ||
| 514 | if (ee && ee->map_file == map_file) | ||
| 515 | fd_array_map_delete_elem(map, &i); | ||
| 516 | } | ||
| 517 | rcu_read_unlock(); | ||
| 473 | } | 518 | } |
| 474 | 519 | ||
| 475 | static const struct bpf_map_ops perf_event_array_ops = { | 520 | static const struct bpf_map_ops perf_event_array_ops = { |
| 476 | .map_alloc = fd_array_map_alloc, | 521 | .map_alloc = fd_array_map_alloc, |
| 477 | .map_free = perf_event_array_map_free, | 522 | .map_free = fd_array_map_free, |
| 478 | .map_get_next_key = array_map_get_next_key, | 523 | .map_get_next_key = array_map_get_next_key, |
| 479 | .map_lookup_elem = fd_array_map_lookup_elem, | 524 | .map_lookup_elem = fd_array_map_lookup_elem, |
| 480 | .map_delete_elem = fd_array_map_delete_elem, | 525 | .map_delete_elem = fd_array_map_delete_elem, |
| 481 | .map_fd_get_ptr = perf_event_fd_array_get_ptr, | 526 | .map_fd_get_ptr = perf_event_fd_array_get_ptr, |
| 482 | .map_fd_put_ptr = perf_event_fd_array_put_ptr, | 527 | .map_fd_put_ptr = perf_event_fd_array_put_ptr, |
| 528 | .map_release = perf_event_fd_array_release, | ||
| 483 | }; | 529 | }; |
| 484 | 530 | ||
| 485 | static struct bpf_map_type_list perf_event_array_type __read_mostly = { | 531 | static struct bpf_map_type_list perf_event_array_type __read_mostly = { |
