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> 1728fbcfa0SAlexei Starovoitov 1828fbcfa0SAlexei Starovoitov struct bpf_array { 1928fbcfa0SAlexei Starovoitov struct bpf_map map; 2028fbcfa0SAlexei Starovoitov u32 elem_size; 2128fbcfa0SAlexei Starovoitov char value[0] __aligned(8); 2228fbcfa0SAlexei Starovoitov }; 2328fbcfa0SAlexei Starovoitov 2428fbcfa0SAlexei Starovoitov /* Called from syscall */ 2528fbcfa0SAlexei Starovoitov static struct bpf_map *array_map_alloc(union bpf_attr *attr) 2628fbcfa0SAlexei Starovoitov { 2728fbcfa0SAlexei Starovoitov struct bpf_array *array; 28daaf427cSAlexei Starovoitov u32 elem_size, array_size; 2928fbcfa0SAlexei Starovoitov 3028fbcfa0SAlexei Starovoitov /* check sanity of attributes */ 3128fbcfa0SAlexei Starovoitov if (attr->max_entries == 0 || attr->key_size != 4 || 3228fbcfa0SAlexei Starovoitov attr->value_size == 0) 3328fbcfa0SAlexei Starovoitov return ERR_PTR(-EINVAL); 3428fbcfa0SAlexei Starovoitov 3528fbcfa0SAlexei Starovoitov elem_size = round_up(attr->value_size, 8); 3628fbcfa0SAlexei Starovoitov 37daaf427cSAlexei Starovoitov /* check round_up into zero and u32 overflow */ 38daaf427cSAlexei Starovoitov if (elem_size == 0 || 39daaf427cSAlexei Starovoitov attr->max_entries > (U32_MAX - sizeof(*array)) / elem_size) 40daaf427cSAlexei Starovoitov return ERR_PTR(-ENOMEM); 41daaf427cSAlexei Starovoitov 42daaf427cSAlexei Starovoitov array_size = sizeof(*array) + attr->max_entries * elem_size; 43daaf427cSAlexei Starovoitov 4428fbcfa0SAlexei Starovoitov /* allocate all map elements and zero-initialize them */ 45daaf427cSAlexei Starovoitov array = kzalloc(array_size, GFP_USER | __GFP_NOWARN); 4628fbcfa0SAlexei Starovoitov if (!array) { 47daaf427cSAlexei Starovoitov array = vzalloc(array_size); 4828fbcfa0SAlexei Starovoitov if (!array) 4928fbcfa0SAlexei Starovoitov return ERR_PTR(-ENOMEM); 5028fbcfa0SAlexei Starovoitov } 5128fbcfa0SAlexei Starovoitov 5228fbcfa0SAlexei Starovoitov /* copy mandatory map attributes */ 5328fbcfa0SAlexei Starovoitov array->map.key_size = attr->key_size; 5428fbcfa0SAlexei Starovoitov array->map.value_size = attr->value_size; 5528fbcfa0SAlexei Starovoitov array->map.max_entries = attr->max_entries; 5628fbcfa0SAlexei Starovoitov 5728fbcfa0SAlexei Starovoitov array->elem_size = elem_size; 5828fbcfa0SAlexei Starovoitov 5928fbcfa0SAlexei Starovoitov return &array->map; 6028fbcfa0SAlexei Starovoitov } 6128fbcfa0SAlexei Starovoitov 6228fbcfa0SAlexei Starovoitov /* Called from syscall or from eBPF program */ 6328fbcfa0SAlexei Starovoitov static void *array_map_lookup_elem(struct bpf_map *map, void *key) 6428fbcfa0SAlexei Starovoitov { 6528fbcfa0SAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 6628fbcfa0SAlexei Starovoitov u32 index = *(u32 *)key; 6728fbcfa0SAlexei Starovoitov 6828fbcfa0SAlexei Starovoitov if (index >= array->map.max_entries) 6928fbcfa0SAlexei Starovoitov return NULL; 7028fbcfa0SAlexei Starovoitov 7128fbcfa0SAlexei Starovoitov return array->value + array->elem_size * index; 7228fbcfa0SAlexei Starovoitov } 7328fbcfa0SAlexei Starovoitov 7428fbcfa0SAlexei Starovoitov /* Called from syscall */ 7528fbcfa0SAlexei Starovoitov static int array_map_get_next_key(struct bpf_map *map, void *key, void *next_key) 7628fbcfa0SAlexei Starovoitov { 7728fbcfa0SAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 7828fbcfa0SAlexei Starovoitov u32 index = *(u32 *)key; 7928fbcfa0SAlexei Starovoitov u32 *next = (u32 *)next_key; 8028fbcfa0SAlexei Starovoitov 8128fbcfa0SAlexei Starovoitov if (index >= array->map.max_entries) { 8228fbcfa0SAlexei Starovoitov *next = 0; 8328fbcfa0SAlexei Starovoitov return 0; 8428fbcfa0SAlexei Starovoitov } 8528fbcfa0SAlexei Starovoitov 8628fbcfa0SAlexei Starovoitov if (index == array->map.max_entries - 1) 8728fbcfa0SAlexei Starovoitov return -ENOENT; 8828fbcfa0SAlexei Starovoitov 8928fbcfa0SAlexei Starovoitov *next = index + 1; 9028fbcfa0SAlexei Starovoitov return 0; 9128fbcfa0SAlexei Starovoitov } 9228fbcfa0SAlexei Starovoitov 9328fbcfa0SAlexei Starovoitov /* Called from syscall or from eBPF program */ 9428fbcfa0SAlexei Starovoitov static int array_map_update_elem(struct bpf_map *map, void *key, void *value, 9528fbcfa0SAlexei Starovoitov u64 map_flags) 9628fbcfa0SAlexei Starovoitov { 9728fbcfa0SAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 9828fbcfa0SAlexei Starovoitov u32 index = *(u32 *)key; 9928fbcfa0SAlexei Starovoitov 10028fbcfa0SAlexei Starovoitov if (map_flags > BPF_EXIST) 10128fbcfa0SAlexei Starovoitov /* unknown flags */ 10228fbcfa0SAlexei Starovoitov return -EINVAL; 10328fbcfa0SAlexei Starovoitov 10428fbcfa0SAlexei Starovoitov if (index >= array->map.max_entries) 10528fbcfa0SAlexei Starovoitov /* all elements were pre-allocated, cannot insert a new one */ 10628fbcfa0SAlexei Starovoitov return -E2BIG; 10728fbcfa0SAlexei Starovoitov 10828fbcfa0SAlexei Starovoitov if (map_flags == BPF_NOEXIST) 109daaf427cSAlexei Starovoitov /* all elements already exist */ 11028fbcfa0SAlexei Starovoitov return -EEXIST; 11128fbcfa0SAlexei Starovoitov 11228fbcfa0SAlexei Starovoitov memcpy(array->value + array->elem_size * index, value, array->elem_size); 11328fbcfa0SAlexei Starovoitov return 0; 11428fbcfa0SAlexei Starovoitov } 11528fbcfa0SAlexei Starovoitov 11628fbcfa0SAlexei Starovoitov /* Called from syscall or from eBPF program */ 11728fbcfa0SAlexei Starovoitov static int array_map_delete_elem(struct bpf_map *map, void *key) 11828fbcfa0SAlexei Starovoitov { 11928fbcfa0SAlexei Starovoitov return -EINVAL; 12028fbcfa0SAlexei Starovoitov } 12128fbcfa0SAlexei Starovoitov 12228fbcfa0SAlexei Starovoitov /* Called when map->refcnt goes to zero, either from workqueue or from syscall */ 12328fbcfa0SAlexei Starovoitov static void array_map_free(struct bpf_map *map) 12428fbcfa0SAlexei Starovoitov { 12528fbcfa0SAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 12628fbcfa0SAlexei Starovoitov 12728fbcfa0SAlexei Starovoitov /* at this point bpf_prog->aux->refcnt == 0 and this map->refcnt == 0, 12828fbcfa0SAlexei Starovoitov * so the programs (can be more than one that used this map) were 12928fbcfa0SAlexei Starovoitov * disconnected from events. Wait for outstanding programs to complete 13028fbcfa0SAlexei Starovoitov * and free the array 13128fbcfa0SAlexei Starovoitov */ 13228fbcfa0SAlexei Starovoitov synchronize_rcu(); 13328fbcfa0SAlexei Starovoitov 13428fbcfa0SAlexei Starovoitov kvfree(array); 13528fbcfa0SAlexei Starovoitov } 13628fbcfa0SAlexei Starovoitov 137*a2c83fffSDaniel Borkmann static const struct bpf_map_ops array_ops = { 13828fbcfa0SAlexei Starovoitov .map_alloc = array_map_alloc, 13928fbcfa0SAlexei Starovoitov .map_free = array_map_free, 14028fbcfa0SAlexei Starovoitov .map_get_next_key = array_map_get_next_key, 14128fbcfa0SAlexei Starovoitov .map_lookup_elem = array_map_lookup_elem, 14228fbcfa0SAlexei Starovoitov .map_update_elem = array_map_update_elem, 14328fbcfa0SAlexei Starovoitov .map_delete_elem = array_map_delete_elem, 14428fbcfa0SAlexei Starovoitov }; 14528fbcfa0SAlexei Starovoitov 146*a2c83fffSDaniel Borkmann static struct bpf_map_type_list array_type __read_mostly = { 14728fbcfa0SAlexei Starovoitov .ops = &array_ops, 14828fbcfa0SAlexei Starovoitov .type = BPF_MAP_TYPE_ARRAY, 14928fbcfa0SAlexei Starovoitov }; 15028fbcfa0SAlexei Starovoitov 15128fbcfa0SAlexei Starovoitov static int __init register_array_map(void) 15228fbcfa0SAlexei Starovoitov { 153*a2c83fffSDaniel Borkmann bpf_register_map_type(&array_type); 15428fbcfa0SAlexei Starovoitov return 0; 15528fbcfa0SAlexei Starovoitov } 15628fbcfa0SAlexei Starovoitov late_initcall(register_array_map); 157