128fbcfa0SAlexei Starovoitov /* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com 228fbcfa0SAlexei Starovoitov * 328fbcfa0SAlexei Starovoitov * This program is free software; you can redistribute it and/or 428fbcfa0SAlexei Starovoitov * modify it under the terms of version 2 of the GNU General Public 528fbcfa0SAlexei Starovoitov * License as published by the Free Software Foundation. 628fbcfa0SAlexei Starovoitov * 728fbcfa0SAlexei Starovoitov * This program is distributed in the hope that it will be useful, but 828fbcfa0SAlexei Starovoitov * WITHOUT ANY WARRANTY; without even the implied warranty of 928fbcfa0SAlexei Starovoitov * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1028fbcfa0SAlexei Starovoitov * General Public License for more details. 1128fbcfa0SAlexei Starovoitov */ 1228fbcfa0SAlexei Starovoitov #include <linux/bpf.h> 1328fbcfa0SAlexei Starovoitov #include <linux/err.h> 1428fbcfa0SAlexei Starovoitov #include <linux/vmalloc.h> 1528fbcfa0SAlexei Starovoitov #include <linux/slab.h> 1628fbcfa0SAlexei Starovoitov #include <linux/mm.h> 1704fd61abSAlexei Starovoitov #include <linux/filter.h> 1828fbcfa0SAlexei Starovoitov 1928fbcfa0SAlexei Starovoitov /* Called from syscall */ 2028fbcfa0SAlexei Starovoitov static struct bpf_map *array_map_alloc(union bpf_attr *attr) 2128fbcfa0SAlexei Starovoitov { 2228fbcfa0SAlexei Starovoitov struct bpf_array *array; 23daaf427cSAlexei Starovoitov u32 elem_size, array_size; 2428fbcfa0SAlexei Starovoitov 2528fbcfa0SAlexei Starovoitov /* check sanity of attributes */ 2628fbcfa0SAlexei Starovoitov if (attr->max_entries == 0 || attr->key_size != 4 || 2728fbcfa0SAlexei Starovoitov attr->value_size == 0) 2828fbcfa0SAlexei Starovoitov return ERR_PTR(-EINVAL); 2928fbcfa0SAlexei Starovoitov 3028fbcfa0SAlexei Starovoitov elem_size = round_up(attr->value_size, 8); 3128fbcfa0SAlexei Starovoitov 32daaf427cSAlexei Starovoitov /* check round_up into zero and u32 overflow */ 33daaf427cSAlexei Starovoitov if (elem_size == 0 || 34daaf427cSAlexei Starovoitov attr->max_entries > (U32_MAX - sizeof(*array)) / elem_size) 35daaf427cSAlexei Starovoitov return ERR_PTR(-ENOMEM); 36daaf427cSAlexei Starovoitov 37daaf427cSAlexei Starovoitov array_size = sizeof(*array) + attr->max_entries * elem_size; 38daaf427cSAlexei Starovoitov 3928fbcfa0SAlexei Starovoitov /* allocate all map elements and zero-initialize them */ 40daaf427cSAlexei Starovoitov array = kzalloc(array_size, GFP_USER | __GFP_NOWARN); 4128fbcfa0SAlexei Starovoitov if (!array) { 42daaf427cSAlexei Starovoitov array = vzalloc(array_size); 4328fbcfa0SAlexei Starovoitov if (!array) 4428fbcfa0SAlexei Starovoitov return ERR_PTR(-ENOMEM); 4528fbcfa0SAlexei Starovoitov } 4628fbcfa0SAlexei Starovoitov 4728fbcfa0SAlexei Starovoitov /* copy mandatory map attributes */ 4828fbcfa0SAlexei Starovoitov array->map.key_size = attr->key_size; 4928fbcfa0SAlexei Starovoitov array->map.value_size = attr->value_size; 5028fbcfa0SAlexei Starovoitov array->map.max_entries = attr->max_entries; 5128fbcfa0SAlexei Starovoitov 5228fbcfa0SAlexei Starovoitov array->elem_size = elem_size; 5328fbcfa0SAlexei Starovoitov 5428fbcfa0SAlexei Starovoitov return &array->map; 5528fbcfa0SAlexei Starovoitov } 5628fbcfa0SAlexei Starovoitov 5728fbcfa0SAlexei Starovoitov /* Called from syscall or from eBPF program */ 5828fbcfa0SAlexei Starovoitov static void *array_map_lookup_elem(struct bpf_map *map, void *key) 5928fbcfa0SAlexei Starovoitov { 6028fbcfa0SAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 6128fbcfa0SAlexei Starovoitov u32 index = *(u32 *)key; 6228fbcfa0SAlexei Starovoitov 6328fbcfa0SAlexei Starovoitov if (index >= array->map.max_entries) 6428fbcfa0SAlexei Starovoitov return NULL; 6528fbcfa0SAlexei Starovoitov 6628fbcfa0SAlexei Starovoitov return array->value + array->elem_size * index; 6728fbcfa0SAlexei Starovoitov } 6828fbcfa0SAlexei Starovoitov 6928fbcfa0SAlexei Starovoitov /* Called from syscall */ 7028fbcfa0SAlexei Starovoitov static int array_map_get_next_key(struct bpf_map *map, void *key, void *next_key) 7128fbcfa0SAlexei Starovoitov { 7228fbcfa0SAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 7328fbcfa0SAlexei Starovoitov u32 index = *(u32 *)key; 7428fbcfa0SAlexei Starovoitov u32 *next = (u32 *)next_key; 7528fbcfa0SAlexei Starovoitov 7628fbcfa0SAlexei Starovoitov if (index >= array->map.max_entries) { 7728fbcfa0SAlexei Starovoitov *next = 0; 7828fbcfa0SAlexei Starovoitov return 0; 7928fbcfa0SAlexei Starovoitov } 8028fbcfa0SAlexei Starovoitov 8128fbcfa0SAlexei Starovoitov if (index == array->map.max_entries - 1) 8228fbcfa0SAlexei Starovoitov return -ENOENT; 8328fbcfa0SAlexei Starovoitov 8428fbcfa0SAlexei Starovoitov *next = index + 1; 8528fbcfa0SAlexei Starovoitov return 0; 8628fbcfa0SAlexei Starovoitov } 8728fbcfa0SAlexei Starovoitov 8828fbcfa0SAlexei Starovoitov /* Called from syscall or from eBPF program */ 8928fbcfa0SAlexei Starovoitov static int array_map_update_elem(struct bpf_map *map, void *key, void *value, 9028fbcfa0SAlexei Starovoitov u64 map_flags) 9128fbcfa0SAlexei Starovoitov { 9228fbcfa0SAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 9328fbcfa0SAlexei Starovoitov u32 index = *(u32 *)key; 9428fbcfa0SAlexei Starovoitov 9528fbcfa0SAlexei Starovoitov if (map_flags > BPF_EXIST) 9628fbcfa0SAlexei Starovoitov /* unknown flags */ 9728fbcfa0SAlexei Starovoitov return -EINVAL; 9828fbcfa0SAlexei Starovoitov 9928fbcfa0SAlexei Starovoitov if (index >= array->map.max_entries) 10028fbcfa0SAlexei Starovoitov /* all elements were pre-allocated, cannot insert a new one */ 10128fbcfa0SAlexei Starovoitov return -E2BIG; 10228fbcfa0SAlexei Starovoitov 10328fbcfa0SAlexei Starovoitov if (map_flags == BPF_NOEXIST) 104daaf427cSAlexei Starovoitov /* all elements already exist */ 10528fbcfa0SAlexei Starovoitov return -EEXIST; 10628fbcfa0SAlexei Starovoitov 10728fbcfa0SAlexei Starovoitov memcpy(array->value + array->elem_size * index, value, array->elem_size); 10828fbcfa0SAlexei Starovoitov return 0; 10928fbcfa0SAlexei Starovoitov } 11028fbcfa0SAlexei Starovoitov 11128fbcfa0SAlexei Starovoitov /* Called from syscall or from eBPF program */ 11228fbcfa0SAlexei Starovoitov static int array_map_delete_elem(struct bpf_map *map, void *key) 11328fbcfa0SAlexei Starovoitov { 11428fbcfa0SAlexei Starovoitov return -EINVAL; 11528fbcfa0SAlexei Starovoitov } 11628fbcfa0SAlexei Starovoitov 11728fbcfa0SAlexei Starovoitov /* Called when map->refcnt goes to zero, either from workqueue or from syscall */ 11828fbcfa0SAlexei Starovoitov static void array_map_free(struct bpf_map *map) 11928fbcfa0SAlexei Starovoitov { 12028fbcfa0SAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 12128fbcfa0SAlexei Starovoitov 12228fbcfa0SAlexei Starovoitov /* at this point bpf_prog->aux->refcnt == 0 and this map->refcnt == 0, 12328fbcfa0SAlexei Starovoitov * so the programs (can be more than one that used this map) were 12428fbcfa0SAlexei Starovoitov * disconnected from events. Wait for outstanding programs to complete 12528fbcfa0SAlexei Starovoitov * and free the array 12628fbcfa0SAlexei Starovoitov */ 12728fbcfa0SAlexei Starovoitov synchronize_rcu(); 12828fbcfa0SAlexei Starovoitov 12928fbcfa0SAlexei Starovoitov kvfree(array); 13028fbcfa0SAlexei Starovoitov } 13128fbcfa0SAlexei Starovoitov 132a2c83fffSDaniel Borkmann static const struct bpf_map_ops array_ops = { 13328fbcfa0SAlexei Starovoitov .map_alloc = array_map_alloc, 13428fbcfa0SAlexei Starovoitov .map_free = array_map_free, 13528fbcfa0SAlexei Starovoitov .map_get_next_key = array_map_get_next_key, 13628fbcfa0SAlexei Starovoitov .map_lookup_elem = array_map_lookup_elem, 13728fbcfa0SAlexei Starovoitov .map_update_elem = array_map_update_elem, 13828fbcfa0SAlexei Starovoitov .map_delete_elem = array_map_delete_elem, 13928fbcfa0SAlexei Starovoitov }; 14028fbcfa0SAlexei Starovoitov 141a2c83fffSDaniel Borkmann static struct bpf_map_type_list array_type __read_mostly = { 14228fbcfa0SAlexei Starovoitov .ops = &array_ops, 14328fbcfa0SAlexei Starovoitov .type = BPF_MAP_TYPE_ARRAY, 14428fbcfa0SAlexei Starovoitov }; 14528fbcfa0SAlexei Starovoitov 14628fbcfa0SAlexei Starovoitov static int __init register_array_map(void) 14728fbcfa0SAlexei Starovoitov { 148a2c83fffSDaniel Borkmann bpf_register_map_type(&array_type); 14928fbcfa0SAlexei Starovoitov return 0; 15028fbcfa0SAlexei Starovoitov } 15128fbcfa0SAlexei Starovoitov late_initcall(register_array_map); 15204fd61abSAlexei Starovoitov 1532a36f0b9SWang Nan static struct bpf_map *fd_array_map_alloc(union bpf_attr *attr) 15404fd61abSAlexei Starovoitov { 1552a36f0b9SWang Nan /* only file descriptors can be stored in this type of map */ 15604fd61abSAlexei Starovoitov if (attr->value_size != sizeof(u32)) 15704fd61abSAlexei Starovoitov return ERR_PTR(-EINVAL); 15804fd61abSAlexei Starovoitov return array_map_alloc(attr); 15904fd61abSAlexei Starovoitov } 16004fd61abSAlexei Starovoitov 1612a36f0b9SWang Nan static void fd_array_map_free(struct bpf_map *map) 16204fd61abSAlexei Starovoitov { 16304fd61abSAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 16404fd61abSAlexei Starovoitov int i; 16504fd61abSAlexei Starovoitov 16604fd61abSAlexei Starovoitov synchronize_rcu(); 16704fd61abSAlexei Starovoitov 16804fd61abSAlexei Starovoitov /* make sure it's empty */ 16904fd61abSAlexei Starovoitov for (i = 0; i < array->map.max_entries; i++) 1702a36f0b9SWang Nan BUG_ON(array->ptrs[i] != NULL); 17104fd61abSAlexei Starovoitov kvfree(array); 17204fd61abSAlexei Starovoitov } 17304fd61abSAlexei Starovoitov 1742a36f0b9SWang Nan static void *fd_array_map_lookup_elem(struct bpf_map *map, void *key) 17504fd61abSAlexei Starovoitov { 17604fd61abSAlexei Starovoitov return NULL; 17704fd61abSAlexei Starovoitov } 17804fd61abSAlexei Starovoitov 17904fd61abSAlexei Starovoitov /* only called from syscall */ 1802a36f0b9SWang Nan static int fd_array_map_update_elem(struct bpf_map *map, void *key, 18104fd61abSAlexei Starovoitov void *value, u64 map_flags) 18204fd61abSAlexei Starovoitov { 18304fd61abSAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 1842a36f0b9SWang Nan void *new_ptr, *old_ptr; 18504fd61abSAlexei Starovoitov u32 index = *(u32 *)key, ufd; 18604fd61abSAlexei Starovoitov 18704fd61abSAlexei Starovoitov if (map_flags != BPF_ANY) 18804fd61abSAlexei Starovoitov return -EINVAL; 18904fd61abSAlexei Starovoitov 19004fd61abSAlexei Starovoitov if (index >= array->map.max_entries) 19104fd61abSAlexei Starovoitov return -E2BIG; 19204fd61abSAlexei Starovoitov 19304fd61abSAlexei Starovoitov ufd = *(u32 *)value; 1942a36f0b9SWang Nan new_ptr = map->ops->map_fd_get_ptr(map, ufd); 1952a36f0b9SWang Nan if (IS_ERR(new_ptr)) 1962a36f0b9SWang Nan return PTR_ERR(new_ptr); 19704fd61abSAlexei Starovoitov 1982a36f0b9SWang Nan old_ptr = xchg(array->ptrs + index, new_ptr); 1992a36f0b9SWang Nan if (old_ptr) 2002a36f0b9SWang Nan map->ops->map_fd_put_ptr(old_ptr); 20104fd61abSAlexei Starovoitov 20204fd61abSAlexei Starovoitov return 0; 20304fd61abSAlexei Starovoitov } 20404fd61abSAlexei Starovoitov 2052a36f0b9SWang Nan static int fd_array_map_delete_elem(struct bpf_map *map, void *key) 20604fd61abSAlexei Starovoitov { 20704fd61abSAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 2082a36f0b9SWang Nan void *old_ptr; 20904fd61abSAlexei Starovoitov u32 index = *(u32 *)key; 21004fd61abSAlexei Starovoitov 21104fd61abSAlexei Starovoitov if (index >= array->map.max_entries) 21204fd61abSAlexei Starovoitov return -E2BIG; 21304fd61abSAlexei Starovoitov 2142a36f0b9SWang Nan old_ptr = xchg(array->ptrs + index, NULL); 2152a36f0b9SWang Nan if (old_ptr) { 2162a36f0b9SWang Nan map->ops->map_fd_put_ptr(old_ptr); 21704fd61abSAlexei Starovoitov return 0; 21804fd61abSAlexei Starovoitov } else { 21904fd61abSAlexei Starovoitov return -ENOENT; 22004fd61abSAlexei Starovoitov } 22104fd61abSAlexei Starovoitov } 22204fd61abSAlexei Starovoitov 2232a36f0b9SWang Nan static void *prog_fd_array_get_ptr(struct bpf_map *map, int fd) 2242a36f0b9SWang Nan { 2252a36f0b9SWang Nan struct bpf_array *array = container_of(map, struct bpf_array, map); 2262a36f0b9SWang Nan struct bpf_prog *prog = bpf_prog_get(fd); 2272a36f0b9SWang Nan if (IS_ERR(prog)) 2282a36f0b9SWang Nan return prog; 2292a36f0b9SWang Nan 2302a36f0b9SWang Nan if (!bpf_prog_array_compatible(array, prog)) { 2312a36f0b9SWang Nan bpf_prog_put(prog); 2322a36f0b9SWang Nan return ERR_PTR(-EINVAL); 2332a36f0b9SWang Nan } 2342a36f0b9SWang Nan return prog; 2352a36f0b9SWang Nan } 2362a36f0b9SWang Nan 2372a36f0b9SWang Nan static void prog_fd_array_put_ptr(void *ptr) 2382a36f0b9SWang Nan { 2392a36f0b9SWang Nan struct bpf_prog *prog = ptr; 2402a36f0b9SWang Nan 2412a36f0b9SWang Nan bpf_prog_put_rcu(prog); 2422a36f0b9SWang Nan } 2432a36f0b9SWang Nan 24404fd61abSAlexei Starovoitov /* decrement refcnt of all bpf_progs that are stored in this map */ 2452a36f0b9SWang Nan void bpf_fd_array_map_clear(struct bpf_map *map) 24604fd61abSAlexei Starovoitov { 24704fd61abSAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 24804fd61abSAlexei Starovoitov int i; 24904fd61abSAlexei Starovoitov 25004fd61abSAlexei Starovoitov for (i = 0; i < array->map.max_entries; i++) 2512a36f0b9SWang Nan fd_array_map_delete_elem(map, &i); 25204fd61abSAlexei Starovoitov } 25304fd61abSAlexei Starovoitov 25404fd61abSAlexei Starovoitov static const struct bpf_map_ops prog_array_ops = { 2552a36f0b9SWang Nan .map_alloc = fd_array_map_alloc, 2562a36f0b9SWang Nan .map_free = fd_array_map_free, 25704fd61abSAlexei Starovoitov .map_get_next_key = array_map_get_next_key, 2582a36f0b9SWang Nan .map_lookup_elem = fd_array_map_lookup_elem, 2592a36f0b9SWang Nan .map_update_elem = fd_array_map_update_elem, 2602a36f0b9SWang Nan .map_delete_elem = fd_array_map_delete_elem, 2612a36f0b9SWang Nan .map_fd_get_ptr = prog_fd_array_get_ptr, 2622a36f0b9SWang Nan .map_fd_put_ptr = prog_fd_array_put_ptr, 26304fd61abSAlexei Starovoitov }; 26404fd61abSAlexei Starovoitov 26504fd61abSAlexei Starovoitov static struct bpf_map_type_list prog_array_type __read_mostly = { 26604fd61abSAlexei Starovoitov .ops = &prog_array_ops, 26704fd61abSAlexei Starovoitov .type = BPF_MAP_TYPE_PROG_ARRAY, 26804fd61abSAlexei Starovoitov }; 26904fd61abSAlexei Starovoitov 27004fd61abSAlexei Starovoitov static int __init register_prog_array_map(void) 27104fd61abSAlexei Starovoitov { 27204fd61abSAlexei Starovoitov bpf_register_map_type(&prog_array_type); 27304fd61abSAlexei Starovoitov return 0; 27404fd61abSAlexei Starovoitov } 27504fd61abSAlexei Starovoitov late_initcall(register_prog_array_map); 276*ea317b26SKaixu Xia 277*ea317b26SKaixu Xia static void perf_event_array_map_free(struct bpf_map *map) 278*ea317b26SKaixu Xia { 279*ea317b26SKaixu Xia bpf_fd_array_map_clear(map); 280*ea317b26SKaixu Xia fd_array_map_free(map); 281*ea317b26SKaixu Xia } 282*ea317b26SKaixu Xia 283*ea317b26SKaixu Xia static void *perf_event_fd_array_get_ptr(struct bpf_map *map, int fd) 284*ea317b26SKaixu Xia { 285*ea317b26SKaixu Xia struct perf_event *event; 286*ea317b26SKaixu Xia const struct perf_event_attr *attr; 287*ea317b26SKaixu Xia 288*ea317b26SKaixu Xia event = perf_event_get(fd); 289*ea317b26SKaixu Xia if (IS_ERR(event)) 290*ea317b26SKaixu Xia return event; 291*ea317b26SKaixu Xia 292*ea317b26SKaixu Xia attr = perf_event_attrs(event); 293*ea317b26SKaixu Xia if (IS_ERR(attr)) 294*ea317b26SKaixu Xia return (void *)attr; 295*ea317b26SKaixu Xia 296*ea317b26SKaixu Xia if (attr->type != PERF_TYPE_RAW && 297*ea317b26SKaixu Xia attr->type != PERF_TYPE_HARDWARE) { 298*ea317b26SKaixu Xia perf_event_release_kernel(event); 299*ea317b26SKaixu Xia return ERR_PTR(-EINVAL); 300*ea317b26SKaixu Xia } 301*ea317b26SKaixu Xia return event; 302*ea317b26SKaixu Xia } 303*ea317b26SKaixu Xia 304*ea317b26SKaixu Xia static void perf_event_fd_array_put_ptr(void *ptr) 305*ea317b26SKaixu Xia { 306*ea317b26SKaixu Xia struct perf_event *event = ptr; 307*ea317b26SKaixu Xia 308*ea317b26SKaixu Xia perf_event_release_kernel(event); 309*ea317b26SKaixu Xia } 310*ea317b26SKaixu Xia 311*ea317b26SKaixu Xia static const struct bpf_map_ops perf_event_array_ops = { 312*ea317b26SKaixu Xia .map_alloc = fd_array_map_alloc, 313*ea317b26SKaixu Xia .map_free = perf_event_array_map_free, 314*ea317b26SKaixu Xia .map_get_next_key = array_map_get_next_key, 315*ea317b26SKaixu Xia .map_lookup_elem = fd_array_map_lookup_elem, 316*ea317b26SKaixu Xia .map_update_elem = fd_array_map_update_elem, 317*ea317b26SKaixu Xia .map_delete_elem = fd_array_map_delete_elem, 318*ea317b26SKaixu Xia .map_fd_get_ptr = perf_event_fd_array_get_ptr, 319*ea317b26SKaixu Xia .map_fd_put_ptr = perf_event_fd_array_put_ptr, 320*ea317b26SKaixu Xia }; 321*ea317b26SKaixu Xia 322*ea317b26SKaixu Xia static struct bpf_map_type_list perf_event_array_type __read_mostly = { 323*ea317b26SKaixu Xia .ops = &perf_event_array_ops, 324*ea317b26SKaixu Xia .type = BPF_MAP_TYPE_PERF_EVENT_ARRAY, 325*ea317b26SKaixu Xia }; 326*ea317b26SKaixu Xia 327*ea317b26SKaixu Xia static int __init register_perf_event_array_map(void) 328*ea317b26SKaixu Xia { 329*ea317b26SKaixu Xia bpf_register_map_type(&perf_event_array_type); 330*ea317b26SKaixu Xia return 0; 331*ea317b26SKaixu Xia } 332*ea317b26SKaixu Xia late_initcall(register_perf_event_array_map); 333