1*28fbcfa0SAlexei Starovoitov /* Copyright (c) 2011-2014 PLUMgrid, http://plumgrid.com 2*28fbcfa0SAlexei Starovoitov * 3*28fbcfa0SAlexei Starovoitov * This program is free software; you can redistribute it and/or 4*28fbcfa0SAlexei Starovoitov * modify it under the terms of version 2 of the GNU General Public 5*28fbcfa0SAlexei Starovoitov * License as published by the Free Software Foundation. 6*28fbcfa0SAlexei Starovoitov * 7*28fbcfa0SAlexei Starovoitov * This program is distributed in the hope that it will be useful, but 8*28fbcfa0SAlexei Starovoitov * WITHOUT ANY WARRANTY; without even the implied warranty of 9*28fbcfa0SAlexei Starovoitov * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 10*28fbcfa0SAlexei Starovoitov * General Public License for more details. 11*28fbcfa0SAlexei Starovoitov */ 12*28fbcfa0SAlexei Starovoitov #include <linux/bpf.h> 13*28fbcfa0SAlexei Starovoitov #include <linux/err.h> 14*28fbcfa0SAlexei Starovoitov #include <linux/vmalloc.h> 15*28fbcfa0SAlexei Starovoitov #include <linux/slab.h> 16*28fbcfa0SAlexei Starovoitov #include <linux/mm.h> 17*28fbcfa0SAlexei Starovoitov 18*28fbcfa0SAlexei Starovoitov struct bpf_array { 19*28fbcfa0SAlexei Starovoitov struct bpf_map map; 20*28fbcfa0SAlexei Starovoitov u32 elem_size; 21*28fbcfa0SAlexei Starovoitov char value[0] __aligned(8); 22*28fbcfa0SAlexei Starovoitov }; 23*28fbcfa0SAlexei Starovoitov 24*28fbcfa0SAlexei Starovoitov /* Called from syscall */ 25*28fbcfa0SAlexei Starovoitov static struct bpf_map *array_map_alloc(union bpf_attr *attr) 26*28fbcfa0SAlexei Starovoitov { 27*28fbcfa0SAlexei Starovoitov struct bpf_array *array; 28*28fbcfa0SAlexei Starovoitov u32 elem_size; 29*28fbcfa0SAlexei Starovoitov 30*28fbcfa0SAlexei Starovoitov /* check sanity of attributes */ 31*28fbcfa0SAlexei Starovoitov if (attr->max_entries == 0 || attr->key_size != 4 || 32*28fbcfa0SAlexei Starovoitov attr->value_size == 0) 33*28fbcfa0SAlexei Starovoitov return ERR_PTR(-EINVAL); 34*28fbcfa0SAlexei Starovoitov 35*28fbcfa0SAlexei Starovoitov elem_size = round_up(attr->value_size, 8); 36*28fbcfa0SAlexei Starovoitov 37*28fbcfa0SAlexei Starovoitov /* allocate all map elements and zero-initialize them */ 38*28fbcfa0SAlexei Starovoitov array = kzalloc(sizeof(*array) + attr->max_entries * elem_size, 39*28fbcfa0SAlexei Starovoitov GFP_USER | __GFP_NOWARN); 40*28fbcfa0SAlexei Starovoitov if (!array) { 41*28fbcfa0SAlexei Starovoitov array = vzalloc(array->map.max_entries * array->elem_size); 42*28fbcfa0SAlexei Starovoitov if (!array) 43*28fbcfa0SAlexei Starovoitov return ERR_PTR(-ENOMEM); 44*28fbcfa0SAlexei Starovoitov } 45*28fbcfa0SAlexei Starovoitov 46*28fbcfa0SAlexei Starovoitov /* copy mandatory map attributes */ 47*28fbcfa0SAlexei Starovoitov array->map.key_size = attr->key_size; 48*28fbcfa0SAlexei Starovoitov array->map.value_size = attr->value_size; 49*28fbcfa0SAlexei Starovoitov array->map.max_entries = attr->max_entries; 50*28fbcfa0SAlexei Starovoitov 51*28fbcfa0SAlexei Starovoitov array->elem_size = elem_size; 52*28fbcfa0SAlexei Starovoitov 53*28fbcfa0SAlexei Starovoitov return &array->map; 54*28fbcfa0SAlexei Starovoitov 55*28fbcfa0SAlexei Starovoitov } 56*28fbcfa0SAlexei Starovoitov 57*28fbcfa0SAlexei Starovoitov /* Called from syscall or from eBPF program */ 58*28fbcfa0SAlexei Starovoitov static void *array_map_lookup_elem(struct bpf_map *map, void *key) 59*28fbcfa0SAlexei Starovoitov { 60*28fbcfa0SAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 61*28fbcfa0SAlexei Starovoitov u32 index = *(u32 *)key; 62*28fbcfa0SAlexei Starovoitov 63*28fbcfa0SAlexei Starovoitov if (index >= array->map.max_entries) 64*28fbcfa0SAlexei Starovoitov return NULL; 65*28fbcfa0SAlexei Starovoitov 66*28fbcfa0SAlexei Starovoitov return array->value + array->elem_size * index; 67*28fbcfa0SAlexei Starovoitov } 68*28fbcfa0SAlexei Starovoitov 69*28fbcfa0SAlexei Starovoitov /* Called from syscall */ 70*28fbcfa0SAlexei Starovoitov static int array_map_get_next_key(struct bpf_map *map, void *key, void *next_key) 71*28fbcfa0SAlexei Starovoitov { 72*28fbcfa0SAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 73*28fbcfa0SAlexei Starovoitov u32 index = *(u32 *)key; 74*28fbcfa0SAlexei Starovoitov u32 *next = (u32 *)next_key; 75*28fbcfa0SAlexei Starovoitov 76*28fbcfa0SAlexei Starovoitov if (index >= array->map.max_entries) { 77*28fbcfa0SAlexei Starovoitov *next = 0; 78*28fbcfa0SAlexei Starovoitov return 0; 79*28fbcfa0SAlexei Starovoitov } 80*28fbcfa0SAlexei Starovoitov 81*28fbcfa0SAlexei Starovoitov if (index == array->map.max_entries - 1) 82*28fbcfa0SAlexei Starovoitov return -ENOENT; 83*28fbcfa0SAlexei Starovoitov 84*28fbcfa0SAlexei Starovoitov *next = index + 1; 85*28fbcfa0SAlexei Starovoitov return 0; 86*28fbcfa0SAlexei Starovoitov } 87*28fbcfa0SAlexei Starovoitov 88*28fbcfa0SAlexei Starovoitov /* Called from syscall or from eBPF program */ 89*28fbcfa0SAlexei Starovoitov static int array_map_update_elem(struct bpf_map *map, void *key, void *value, 90*28fbcfa0SAlexei Starovoitov u64 map_flags) 91*28fbcfa0SAlexei Starovoitov { 92*28fbcfa0SAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 93*28fbcfa0SAlexei Starovoitov u32 index = *(u32 *)key; 94*28fbcfa0SAlexei Starovoitov 95*28fbcfa0SAlexei Starovoitov if (map_flags > BPF_EXIST) 96*28fbcfa0SAlexei Starovoitov /* unknown flags */ 97*28fbcfa0SAlexei Starovoitov return -EINVAL; 98*28fbcfa0SAlexei Starovoitov 99*28fbcfa0SAlexei Starovoitov if (index >= array->map.max_entries) 100*28fbcfa0SAlexei Starovoitov /* all elements were pre-allocated, cannot insert a new one */ 101*28fbcfa0SAlexei Starovoitov return -E2BIG; 102*28fbcfa0SAlexei Starovoitov 103*28fbcfa0SAlexei Starovoitov if (map_flags == BPF_NOEXIST) 104*28fbcfa0SAlexei Starovoitov /* all elemenets already exist */ 105*28fbcfa0SAlexei Starovoitov return -EEXIST; 106*28fbcfa0SAlexei Starovoitov 107*28fbcfa0SAlexei Starovoitov memcpy(array->value + array->elem_size * index, value, array->elem_size); 108*28fbcfa0SAlexei Starovoitov return 0; 109*28fbcfa0SAlexei Starovoitov } 110*28fbcfa0SAlexei Starovoitov 111*28fbcfa0SAlexei Starovoitov /* Called from syscall or from eBPF program */ 112*28fbcfa0SAlexei Starovoitov static int array_map_delete_elem(struct bpf_map *map, void *key) 113*28fbcfa0SAlexei Starovoitov { 114*28fbcfa0SAlexei Starovoitov return -EINVAL; 115*28fbcfa0SAlexei Starovoitov } 116*28fbcfa0SAlexei Starovoitov 117*28fbcfa0SAlexei Starovoitov /* Called when map->refcnt goes to zero, either from workqueue or from syscall */ 118*28fbcfa0SAlexei Starovoitov static void array_map_free(struct bpf_map *map) 119*28fbcfa0SAlexei Starovoitov { 120*28fbcfa0SAlexei Starovoitov struct bpf_array *array = container_of(map, struct bpf_array, map); 121*28fbcfa0SAlexei Starovoitov 122*28fbcfa0SAlexei Starovoitov /* at this point bpf_prog->aux->refcnt == 0 and this map->refcnt == 0, 123*28fbcfa0SAlexei Starovoitov * so the programs (can be more than one that used this map) were 124*28fbcfa0SAlexei Starovoitov * disconnected from events. Wait for outstanding programs to complete 125*28fbcfa0SAlexei Starovoitov * and free the array 126*28fbcfa0SAlexei Starovoitov */ 127*28fbcfa0SAlexei Starovoitov synchronize_rcu(); 128*28fbcfa0SAlexei Starovoitov 129*28fbcfa0SAlexei Starovoitov kvfree(array); 130*28fbcfa0SAlexei Starovoitov } 131*28fbcfa0SAlexei Starovoitov 132*28fbcfa0SAlexei Starovoitov static struct bpf_map_ops array_ops = { 133*28fbcfa0SAlexei Starovoitov .map_alloc = array_map_alloc, 134*28fbcfa0SAlexei Starovoitov .map_free = array_map_free, 135*28fbcfa0SAlexei Starovoitov .map_get_next_key = array_map_get_next_key, 136*28fbcfa0SAlexei Starovoitov .map_lookup_elem = array_map_lookup_elem, 137*28fbcfa0SAlexei Starovoitov .map_update_elem = array_map_update_elem, 138*28fbcfa0SAlexei Starovoitov .map_delete_elem = array_map_delete_elem, 139*28fbcfa0SAlexei Starovoitov }; 140*28fbcfa0SAlexei Starovoitov 141*28fbcfa0SAlexei Starovoitov static struct bpf_map_type_list tl = { 142*28fbcfa0SAlexei Starovoitov .ops = &array_ops, 143*28fbcfa0SAlexei Starovoitov .type = BPF_MAP_TYPE_ARRAY, 144*28fbcfa0SAlexei Starovoitov }; 145*28fbcfa0SAlexei Starovoitov 146*28fbcfa0SAlexei Starovoitov static int __init register_array_map(void) 147*28fbcfa0SAlexei Starovoitov { 148*28fbcfa0SAlexei Starovoitov bpf_register_map_type(&tl); 149*28fbcfa0SAlexei Starovoitov return 0; 150*28fbcfa0SAlexei Starovoitov } 151*28fbcfa0SAlexei Starovoitov late_initcall(register_array_map); 152