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> 17*04fd61abSAlexei 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); 152*04fd61abSAlexei Starovoitov 153*04fd61abSAlexei Starovoitov static struct bpf_map *prog_array_map_alloc(union bpf_attr *attr) 154*04fd61abSAlexei Starovoitov { 155*04fd61abSAlexei Starovoitov /* only bpf_prog file descriptors can be stored in prog_array map */ 156*04fd61abSAlexei Starovoitov if (attr->value_size != sizeof(u32)) 157*04fd61abSAlexei Starovoitov return ERR_PTR(-EINVAL); 158*04fd61abSAlexei Starovoitov return array_map_alloc(attr); 159*04fd61abSAlexei Starovoitov } 160*04fd61abSAlexei Starovoitov 161*04fd61abSAlexei Starovoitov static void prog_array_map_free(struct bpf_map *map) 162*04fd61abSAlexei Starovoitov { 163*04fd61abSAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 164*04fd61abSAlexei Starovoitov int i; 165*04fd61abSAlexei Starovoitov 166*04fd61abSAlexei Starovoitov synchronize_rcu(); 167*04fd61abSAlexei Starovoitov 168*04fd61abSAlexei Starovoitov /* make sure it's empty */ 169*04fd61abSAlexei Starovoitov for (i = 0; i < array->map.max_entries; i++) 170*04fd61abSAlexei Starovoitov BUG_ON(array->prog[i] != NULL); 171*04fd61abSAlexei Starovoitov kvfree(array); 172*04fd61abSAlexei Starovoitov } 173*04fd61abSAlexei Starovoitov 174*04fd61abSAlexei Starovoitov static void *prog_array_map_lookup_elem(struct bpf_map *map, void *key) 175*04fd61abSAlexei Starovoitov { 176*04fd61abSAlexei Starovoitov return NULL; 177*04fd61abSAlexei Starovoitov } 178*04fd61abSAlexei Starovoitov 179*04fd61abSAlexei Starovoitov /* only called from syscall */ 180*04fd61abSAlexei Starovoitov static int prog_array_map_update_elem(struct bpf_map *map, void *key, 181*04fd61abSAlexei Starovoitov void *value, u64 map_flags) 182*04fd61abSAlexei Starovoitov { 183*04fd61abSAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 184*04fd61abSAlexei Starovoitov struct bpf_prog *prog, *old_prog; 185*04fd61abSAlexei Starovoitov u32 index = *(u32 *)key, ufd; 186*04fd61abSAlexei Starovoitov 187*04fd61abSAlexei Starovoitov if (map_flags != BPF_ANY) 188*04fd61abSAlexei Starovoitov return -EINVAL; 189*04fd61abSAlexei Starovoitov 190*04fd61abSAlexei Starovoitov if (index >= array->map.max_entries) 191*04fd61abSAlexei Starovoitov return -E2BIG; 192*04fd61abSAlexei Starovoitov 193*04fd61abSAlexei Starovoitov ufd = *(u32 *)value; 194*04fd61abSAlexei Starovoitov prog = bpf_prog_get(ufd); 195*04fd61abSAlexei Starovoitov if (IS_ERR(prog)) 196*04fd61abSAlexei Starovoitov return PTR_ERR(prog); 197*04fd61abSAlexei Starovoitov 198*04fd61abSAlexei Starovoitov if (!bpf_prog_array_compatible(array, prog)) { 199*04fd61abSAlexei Starovoitov bpf_prog_put(prog); 200*04fd61abSAlexei Starovoitov return -EINVAL; 201*04fd61abSAlexei Starovoitov } 202*04fd61abSAlexei Starovoitov 203*04fd61abSAlexei Starovoitov old_prog = xchg(array->prog + index, prog); 204*04fd61abSAlexei Starovoitov if (old_prog) 205*04fd61abSAlexei Starovoitov bpf_prog_put(old_prog); 206*04fd61abSAlexei Starovoitov 207*04fd61abSAlexei Starovoitov return 0; 208*04fd61abSAlexei Starovoitov } 209*04fd61abSAlexei Starovoitov 210*04fd61abSAlexei Starovoitov static int prog_array_map_delete_elem(struct bpf_map *map, void *key) 211*04fd61abSAlexei Starovoitov { 212*04fd61abSAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 213*04fd61abSAlexei Starovoitov struct bpf_prog *old_prog; 214*04fd61abSAlexei Starovoitov u32 index = *(u32 *)key; 215*04fd61abSAlexei Starovoitov 216*04fd61abSAlexei Starovoitov if (index >= array->map.max_entries) 217*04fd61abSAlexei Starovoitov return -E2BIG; 218*04fd61abSAlexei Starovoitov 219*04fd61abSAlexei Starovoitov old_prog = xchg(array->prog + index, NULL); 220*04fd61abSAlexei Starovoitov if (old_prog) { 221*04fd61abSAlexei Starovoitov bpf_prog_put(old_prog); 222*04fd61abSAlexei Starovoitov return 0; 223*04fd61abSAlexei Starovoitov } else { 224*04fd61abSAlexei Starovoitov return -ENOENT; 225*04fd61abSAlexei Starovoitov } 226*04fd61abSAlexei Starovoitov } 227*04fd61abSAlexei Starovoitov 228*04fd61abSAlexei Starovoitov /* decrement refcnt of all bpf_progs that are stored in this map */ 229*04fd61abSAlexei Starovoitov void bpf_prog_array_map_clear(struct bpf_map *map) 230*04fd61abSAlexei Starovoitov { 231*04fd61abSAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 232*04fd61abSAlexei Starovoitov int i; 233*04fd61abSAlexei Starovoitov 234*04fd61abSAlexei Starovoitov for (i = 0; i < array->map.max_entries; i++) 235*04fd61abSAlexei Starovoitov prog_array_map_delete_elem(map, &i); 236*04fd61abSAlexei Starovoitov } 237*04fd61abSAlexei Starovoitov 238*04fd61abSAlexei Starovoitov static const struct bpf_map_ops prog_array_ops = { 239*04fd61abSAlexei Starovoitov .map_alloc = prog_array_map_alloc, 240*04fd61abSAlexei Starovoitov .map_free = prog_array_map_free, 241*04fd61abSAlexei Starovoitov .map_get_next_key = array_map_get_next_key, 242*04fd61abSAlexei Starovoitov .map_lookup_elem = prog_array_map_lookup_elem, 243*04fd61abSAlexei Starovoitov .map_update_elem = prog_array_map_update_elem, 244*04fd61abSAlexei Starovoitov .map_delete_elem = prog_array_map_delete_elem, 245*04fd61abSAlexei Starovoitov }; 246*04fd61abSAlexei Starovoitov 247*04fd61abSAlexei Starovoitov static struct bpf_map_type_list prog_array_type __read_mostly = { 248*04fd61abSAlexei Starovoitov .ops = &prog_array_ops, 249*04fd61abSAlexei Starovoitov .type = BPF_MAP_TYPE_PROG_ARRAY, 250*04fd61abSAlexei Starovoitov }; 251*04fd61abSAlexei Starovoitov 252*04fd61abSAlexei Starovoitov static int __init register_prog_array_map(void) 253*04fd61abSAlexei Starovoitov { 254*04fd61abSAlexei Starovoitov bpf_register_map_type(&prog_array_type); 255*04fd61abSAlexei Starovoitov return 0; 256*04fd61abSAlexei Starovoitov } 257*04fd61abSAlexei Starovoitov late_initcall(register_prog_array_map); 258