15b497af4SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2546ac1ffSJohn Fastabend /* Copyright (c) 2017 Covalent IO, Inc. http://covalent.io 3546ac1ffSJohn Fastabend */ 4546ac1ffSJohn Fastabend 5546ac1ffSJohn Fastabend /* Devmaps primary use is as a backend map for XDP BPF helper call 6546ac1ffSJohn Fastabend * bpf_redirect_map(). Because XDP is mostly concerned with performance we 7546ac1ffSJohn Fastabend * spent some effort to ensure the datapath with redirect maps does not use 8546ac1ffSJohn Fastabend * any locking. This is a quick note on the details. 9546ac1ffSJohn Fastabend * 10546ac1ffSJohn Fastabend * We have three possible paths to get into the devmap control plane bpf 11546ac1ffSJohn Fastabend * syscalls, bpf programs, and driver side xmit/flush operations. A bpf syscall 12546ac1ffSJohn Fastabend * will invoke an update, delete, or lookup operation. To ensure updates and 13546ac1ffSJohn Fastabend * deletes appear atomic from the datapath side xchg() is used to modify the 14546ac1ffSJohn Fastabend * netdev_map array. Then because the datapath does a lookup into the netdev_map 15546ac1ffSJohn Fastabend * array (read-only) from an RCU critical section we use call_rcu() to wait for 16546ac1ffSJohn Fastabend * an rcu grace period before free'ing the old data structures. This ensures the 17546ac1ffSJohn Fastabend * datapath always has a valid copy. However, the datapath does a "flush" 18546ac1ffSJohn Fastabend * operation that pushes any pending packets in the driver outside the RCU 19546ac1ffSJohn Fastabend * critical section. Each bpf_dtab_netdev tracks these pending operations using 20d5df2830SToke Høiland-Jørgensen * a per-cpu flush list. The bpf_dtab_netdev object will not be destroyed until 21d5df2830SToke Høiland-Jørgensen * this list is empty, indicating outstanding flush operations have completed. 22546ac1ffSJohn Fastabend * 23546ac1ffSJohn Fastabend * BPF syscalls may race with BPF program calls on any of the update, delete 24546ac1ffSJohn Fastabend * or lookup operations. As noted above the xchg() operation also keep the 25546ac1ffSJohn Fastabend * netdev_map consistent in this case. From the devmap side BPF programs 26546ac1ffSJohn Fastabend * calling into these operations are the same as multiple user space threads 27546ac1ffSJohn Fastabend * making system calls. 282ddf71e2SJohn Fastabend * 292ddf71e2SJohn Fastabend * Finally, any of the above may race with a netdev_unregister notifier. The 302ddf71e2SJohn Fastabend * unregister notifier must search for net devices in the map structure that 312ddf71e2SJohn Fastabend * contain a reference to the net device and remove them. This is a two step 322ddf71e2SJohn Fastabend * process (a) dereference the bpf_dtab_netdev object in netdev_map and (b) 332ddf71e2SJohn Fastabend * check to see if the ifindex is the same as the net_device being removed. 344cc7b954SJohn Fastabend * When removing the dev a cmpxchg() is used to ensure the correct dev is 354cc7b954SJohn Fastabend * removed, in the case of a concurrent update or delete operation it is 364cc7b954SJohn Fastabend * possible that the initially referenced dev is no longer in the map. As the 374cc7b954SJohn Fastabend * notifier hook walks the map we know that new dev references can not be 384cc7b954SJohn Fastabend * added by the user because core infrastructure ensures dev_get_by_index() 394cc7b954SJohn Fastabend * calls will fail at this point. 406f9d451aSToke Høiland-Jørgensen * 416f9d451aSToke Høiland-Jørgensen * The devmap_hash type is a map type which interprets keys as ifindexes and 426f9d451aSToke Høiland-Jørgensen * indexes these using a hashmap. This allows maps that use ifindex as key to be 436f9d451aSToke Høiland-Jørgensen * densely packed instead of having holes in the lookup array for unused 446f9d451aSToke Høiland-Jørgensen * ifindexes. The setup and packet enqueue/send code is shared between the two 456f9d451aSToke Høiland-Jørgensen * types of devmap; only the lookup and insertion is different. 46546ac1ffSJohn Fastabend */ 47546ac1ffSJohn Fastabend #include <linux/bpf.h> 4867f29e07SJesper Dangaard Brouer #include <net/xdp.h> 49546ac1ffSJohn Fastabend #include <linux/filter.h> 5067f29e07SJesper Dangaard Brouer #include <trace/events/xdp.h> 51546ac1ffSJohn Fastabend 526e71b04aSChenbo Feng #define DEV_CREATE_FLAG_MASK \ 536e71b04aSChenbo Feng (BPF_F_NUMA_NODE | BPF_F_RDONLY | BPF_F_WRONLY) 546e71b04aSChenbo Feng 5575ccae62SToke Høiland-Jørgensen struct xdp_dev_bulk_queue { 565d053f9dSJesper Dangaard Brouer struct xdp_frame *q[DEV_MAP_BULK_SIZE]; 57d5df2830SToke Høiland-Jørgensen struct list_head flush_node; 5875ccae62SToke Høiland-Jørgensen struct net_device *dev; 5938edddb8SJesper Dangaard Brouer struct net_device *dev_rx; 60*cb261b59SJesper Dangaard Brouer struct bpf_prog *xdp_prog; 615d053f9dSJesper Dangaard Brouer unsigned int count; 625d053f9dSJesper Dangaard Brouer }; 635d053f9dSJesper Dangaard Brouer 64546ac1ffSJohn Fastabend struct bpf_dtab_netdev { 6567f29e07SJesper Dangaard Brouer struct net_device *dev; /* must be first member, due to tracepoint */ 666f9d451aSToke Høiland-Jørgensen struct hlist_node index_hlist; 67546ac1ffSJohn Fastabend struct bpf_dtab *dtab; 68fbee97feSDavid Ahern struct bpf_prog *xdp_prog; 69af4d045cSDaniel Borkmann struct rcu_head rcu; 7075ccae62SToke Høiland-Jørgensen unsigned int idx; 717f1c0426SDavid Ahern struct bpf_devmap_val val; 72546ac1ffSJohn Fastabend }; 73546ac1ffSJohn Fastabend 74546ac1ffSJohn Fastabend struct bpf_dtab { 75546ac1ffSJohn Fastabend struct bpf_map map; 76071cdeceSToke Høiland-Jørgensen struct bpf_dtab_netdev **netdev_map; /* DEVMAP type only */ 772ddf71e2SJohn Fastabend struct list_head list; 786f9d451aSToke Høiland-Jørgensen 796f9d451aSToke Høiland-Jørgensen /* these are only used for DEVMAP_HASH type maps */ 806f9d451aSToke Høiland-Jørgensen struct hlist_head *dev_index_head; 816f9d451aSToke Høiland-Jørgensen spinlock_t index_lock; 826f9d451aSToke Høiland-Jørgensen unsigned int items; 836f9d451aSToke Høiland-Jørgensen u32 n_buckets; 84546ac1ffSJohn Fastabend }; 85546ac1ffSJohn Fastabend 861d233886SToke Høiland-Jørgensen static DEFINE_PER_CPU(struct list_head, dev_flush_list); 874cc7b954SJohn Fastabend static DEFINE_SPINLOCK(dev_map_lock); 882ddf71e2SJohn Fastabend static LIST_HEAD(dev_map_list); 892ddf71e2SJohn Fastabend 9099c51064SToke Høiland-Jørgensen static struct hlist_head *dev_map_create_hash(unsigned int entries, 9199c51064SToke Høiland-Jørgensen int numa_node) 926f9d451aSToke Høiland-Jørgensen { 936f9d451aSToke Høiland-Jørgensen int i; 946f9d451aSToke Høiland-Jørgensen struct hlist_head *hash; 956f9d451aSToke Høiland-Jørgensen 9699c51064SToke Høiland-Jørgensen hash = bpf_map_area_alloc(entries * sizeof(*hash), numa_node); 976f9d451aSToke Høiland-Jørgensen if (hash != NULL) 986f9d451aSToke Høiland-Jørgensen for (i = 0; i < entries; i++) 996f9d451aSToke Høiland-Jørgensen INIT_HLIST_HEAD(&hash[i]); 1006f9d451aSToke Høiland-Jørgensen 1016f9d451aSToke Høiland-Jørgensen return hash; 1026f9d451aSToke Høiland-Jørgensen } 1036f9d451aSToke Høiland-Jørgensen 104071cdeceSToke Høiland-Jørgensen static inline struct hlist_head *dev_map_index_hash(struct bpf_dtab *dtab, 105071cdeceSToke Høiland-Jørgensen int idx) 106071cdeceSToke Høiland-Jørgensen { 107071cdeceSToke Høiland-Jørgensen return &dtab->dev_index_head[idx & (dtab->n_buckets - 1)]; 108071cdeceSToke Høiland-Jørgensen } 109071cdeceSToke Høiland-Jørgensen 110fca16e51SToke Høiland-Jørgensen static int dev_map_init_map(struct bpf_dtab *dtab, union bpf_attr *attr) 111546ac1ffSJohn Fastabend { 112fbee97feSDavid Ahern u32 valsize = attr->value_size; 113546ac1ffSJohn Fastabend 114fbee97feSDavid Ahern /* check sanity of attributes. 2 value sizes supported: 115fbee97feSDavid Ahern * 4 bytes: ifindex 116fbee97feSDavid Ahern * 8 bytes: ifindex + prog fd 117fbee97feSDavid Ahern */ 118546ac1ffSJohn Fastabend if (attr->max_entries == 0 || attr->key_size != 4 || 119fbee97feSDavid Ahern (valsize != offsetofend(struct bpf_devmap_val, ifindex) && 120fbee97feSDavid Ahern valsize != offsetofend(struct bpf_devmap_val, bpf_prog.fd)) || 121fbee97feSDavid Ahern attr->map_flags & ~DEV_CREATE_FLAG_MASK) 122fca16e51SToke Høiland-Jørgensen return -EINVAL; 123546ac1ffSJohn Fastabend 1240cdbb4b0SToke Høiland-Jørgensen /* Lookup returns a pointer straight to dev->ifindex, so make sure the 1250cdbb4b0SToke Høiland-Jørgensen * verifier prevents writes from the BPF side 1260cdbb4b0SToke Høiland-Jørgensen */ 1270cdbb4b0SToke Høiland-Jørgensen attr->map_flags |= BPF_F_RDONLY_PROG; 1280cdbb4b0SToke Høiland-Jørgensen 129546ac1ffSJohn Fastabend 130bd475643SJakub Kicinski bpf_map_init_from_attr(&dtab->map, attr); 131546ac1ffSJohn Fastabend 1326f9d451aSToke Høiland-Jørgensen if (attr->map_type == BPF_MAP_TYPE_DEVMAP_HASH) { 1336f9d451aSToke Høiland-Jørgensen dtab->n_buckets = roundup_pow_of_two(dtab->map.max_entries); 1346f9d451aSToke Høiland-Jørgensen 1356f9d451aSToke Høiland-Jørgensen if (!dtab->n_buckets) /* Overflow check */ 1366f9d451aSToke Høiland-Jørgensen return -EINVAL; 1376f9d451aSToke Høiland-Jørgensen } 1386f9d451aSToke Høiland-Jørgensen 139071cdeceSToke Høiland-Jørgensen if (attr->map_type == BPF_MAP_TYPE_DEVMAP_HASH) { 14099c51064SToke Høiland-Jørgensen dtab->dev_index_head = dev_map_create_hash(dtab->n_buckets, 14199c51064SToke Høiland-Jørgensen dtab->map.numa_node); 142071cdeceSToke Høiland-Jørgensen if (!dtab->dev_index_head) 143844f157fSRoman Gushchin return -ENOMEM; 144071cdeceSToke Høiland-Jørgensen 145071cdeceSToke Høiland-Jørgensen spin_lock_init(&dtab->index_lock); 146071cdeceSToke Høiland-Jørgensen } else { 147546ac1ffSJohn Fastabend dtab->netdev_map = bpf_map_area_alloc(dtab->map.max_entries * 14896eabe7aSMartin KaFai Lau sizeof(struct bpf_dtab_netdev *), 14996eabe7aSMartin KaFai Lau dtab->map.numa_node); 150546ac1ffSJohn Fastabend if (!dtab->netdev_map) 151844f157fSRoman Gushchin return -ENOMEM; 1526f9d451aSToke Høiland-Jørgensen } 1536f9d451aSToke Høiland-Jørgensen 154fca16e51SToke Høiland-Jørgensen return 0; 155fca16e51SToke Høiland-Jørgensen } 156fca16e51SToke Høiland-Jørgensen 157fca16e51SToke Høiland-Jørgensen static struct bpf_map *dev_map_alloc(union bpf_attr *attr) 158fca16e51SToke Høiland-Jørgensen { 159fca16e51SToke Høiland-Jørgensen struct bpf_dtab *dtab; 160fca16e51SToke Høiland-Jørgensen int err; 161fca16e51SToke Høiland-Jørgensen 162fca16e51SToke Høiland-Jørgensen if (!capable(CAP_NET_ADMIN)) 163fca16e51SToke Høiland-Jørgensen return ERR_PTR(-EPERM); 164fca16e51SToke Høiland-Jørgensen 1651440290aSRoman Gushchin dtab = kzalloc(sizeof(*dtab), GFP_USER | __GFP_ACCOUNT); 166fca16e51SToke Høiland-Jørgensen if (!dtab) 167fca16e51SToke Høiland-Jørgensen return ERR_PTR(-ENOMEM); 168fca16e51SToke Høiland-Jørgensen 169fca16e51SToke Høiland-Jørgensen err = dev_map_init_map(dtab, attr); 170fca16e51SToke Høiland-Jørgensen if (err) { 171546ac1ffSJohn Fastabend kfree(dtab); 172582db7e0STobias Klauser return ERR_PTR(err); 173546ac1ffSJohn Fastabend } 174546ac1ffSJohn Fastabend 175fca16e51SToke Høiland-Jørgensen spin_lock(&dev_map_lock); 176fca16e51SToke Høiland-Jørgensen list_add_tail_rcu(&dtab->list, &dev_map_list); 177fca16e51SToke Høiland-Jørgensen spin_unlock(&dev_map_lock); 178fca16e51SToke Høiland-Jørgensen 179fca16e51SToke Høiland-Jørgensen return &dtab->map; 180fca16e51SToke Høiland-Jørgensen } 181fca16e51SToke Høiland-Jørgensen 182546ac1ffSJohn Fastabend static void dev_map_free(struct bpf_map *map) 183546ac1ffSJohn Fastabend { 184546ac1ffSJohn Fastabend struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); 1850536b852SBjörn Töpel int i; 186546ac1ffSJohn Fastabend 187546ac1ffSJohn Fastabend /* At this point bpf_prog->aux->refcnt == 0 and this map->refcnt == 0, 188546ac1ffSJohn Fastabend * so the programs (can be more than one that used this map) were 18942a84a8cSJohn Fastabend * disconnected from events. The following synchronize_rcu() guarantees 19042a84a8cSJohn Fastabend * both rcu read critical sections complete and waits for 19142a84a8cSJohn Fastabend * preempt-disable regions (NAPI being the relevant context here) so we 19242a84a8cSJohn Fastabend * are certain there will be no further reads against the netdev_map and 19342a84a8cSJohn Fastabend * all flush operations are complete. Flush operations can only be done 19442a84a8cSJohn Fastabend * from NAPI context for this reason. 195546ac1ffSJohn Fastabend */ 196274043c6SDaniel Borkmann 197274043c6SDaniel Borkmann spin_lock(&dev_map_lock); 198274043c6SDaniel Borkmann list_del_rcu(&dtab->list); 199274043c6SDaniel Borkmann spin_unlock(&dev_map_lock); 200274043c6SDaniel Borkmann 201546ac1ffSJohn Fastabend synchronize_rcu(); 202546ac1ffSJohn Fastabend 2032baae354SEric Dumazet /* Make sure prior __dev_map_entry_free() have completed. */ 2042baae354SEric Dumazet rcu_barrier(); 2052baae354SEric Dumazet 206071cdeceSToke Høiland-Jørgensen if (dtab->map.map_type == BPF_MAP_TYPE_DEVMAP_HASH) { 207071cdeceSToke Høiland-Jørgensen for (i = 0; i < dtab->n_buckets; i++) { 208071cdeceSToke Høiland-Jørgensen struct bpf_dtab_netdev *dev; 209071cdeceSToke Høiland-Jørgensen struct hlist_head *head; 210071cdeceSToke Høiland-Jørgensen struct hlist_node *next; 211071cdeceSToke Høiland-Jørgensen 212071cdeceSToke Høiland-Jørgensen head = dev_map_index_hash(dtab, i); 213071cdeceSToke Høiland-Jørgensen 214071cdeceSToke Høiland-Jørgensen hlist_for_each_entry_safe(dev, next, head, index_hlist) { 215071cdeceSToke Høiland-Jørgensen hlist_del_rcu(&dev->index_hlist); 216fbee97feSDavid Ahern if (dev->xdp_prog) 217fbee97feSDavid Ahern bpf_prog_put(dev->xdp_prog); 218071cdeceSToke Høiland-Jørgensen dev_put(dev->dev); 219071cdeceSToke Høiland-Jørgensen kfree(dev); 220071cdeceSToke Høiland-Jørgensen } 221071cdeceSToke Høiland-Jørgensen } 222071cdeceSToke Høiland-Jørgensen 22399c51064SToke Høiland-Jørgensen bpf_map_area_free(dtab->dev_index_head); 224071cdeceSToke Høiland-Jørgensen } else { 225546ac1ffSJohn Fastabend for (i = 0; i < dtab->map.max_entries; i++) { 226546ac1ffSJohn Fastabend struct bpf_dtab_netdev *dev; 227546ac1ffSJohn Fastabend 228546ac1ffSJohn Fastabend dev = dtab->netdev_map[i]; 229546ac1ffSJohn Fastabend if (!dev) 230546ac1ffSJohn Fastabend continue; 231546ac1ffSJohn Fastabend 232fbee97feSDavid Ahern if (dev->xdp_prog) 233fbee97feSDavid Ahern bpf_prog_put(dev->xdp_prog); 234546ac1ffSJohn Fastabend dev_put(dev->dev); 235546ac1ffSJohn Fastabend kfree(dev); 236546ac1ffSJohn Fastabend } 237546ac1ffSJohn Fastabend 238546ac1ffSJohn Fastabend bpf_map_area_free(dtab->netdev_map); 239071cdeceSToke Høiland-Jørgensen } 240071cdeceSToke Høiland-Jørgensen 241546ac1ffSJohn Fastabend kfree(dtab); 242546ac1ffSJohn Fastabend } 243546ac1ffSJohn Fastabend 244546ac1ffSJohn Fastabend static int dev_map_get_next_key(struct bpf_map *map, void *key, void *next_key) 245546ac1ffSJohn Fastabend { 246546ac1ffSJohn Fastabend struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); 247546ac1ffSJohn Fastabend u32 index = key ? *(u32 *)key : U32_MAX; 248af4d045cSDaniel Borkmann u32 *next = next_key; 249546ac1ffSJohn Fastabend 250546ac1ffSJohn Fastabend if (index >= dtab->map.max_entries) { 251546ac1ffSJohn Fastabend *next = 0; 252546ac1ffSJohn Fastabend return 0; 253546ac1ffSJohn Fastabend } 254546ac1ffSJohn Fastabend 255546ac1ffSJohn Fastabend if (index == dtab->map.max_entries - 1) 256546ac1ffSJohn Fastabend return -ENOENT; 257546ac1ffSJohn Fastabend *next = index + 1; 258546ac1ffSJohn Fastabend return 0; 259546ac1ffSJohn Fastabend } 260546ac1ffSJohn Fastabend 261e6a4750fSBjörn Töpel static void *__dev_map_hash_lookup_elem(struct bpf_map *map, u32 key) 2626f9d451aSToke Høiland-Jørgensen { 2636f9d451aSToke Høiland-Jørgensen struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); 2646f9d451aSToke Høiland-Jørgensen struct hlist_head *head = dev_map_index_hash(dtab, key); 2656f9d451aSToke Høiland-Jørgensen struct bpf_dtab_netdev *dev; 2666f9d451aSToke Høiland-Jørgensen 267485ec2eaSAmol Grover hlist_for_each_entry_rcu(dev, head, index_hlist, 268485ec2eaSAmol Grover lockdep_is_held(&dtab->index_lock)) 2696f9d451aSToke Høiland-Jørgensen if (dev->idx == key) 2706f9d451aSToke Høiland-Jørgensen return dev; 2716f9d451aSToke Høiland-Jørgensen 2726f9d451aSToke Høiland-Jørgensen return NULL; 2736f9d451aSToke Høiland-Jørgensen } 2746f9d451aSToke Høiland-Jørgensen 2756f9d451aSToke Høiland-Jørgensen static int dev_map_hash_get_next_key(struct bpf_map *map, void *key, 2766f9d451aSToke Høiland-Jørgensen void *next_key) 2776f9d451aSToke Høiland-Jørgensen { 2786f9d451aSToke Høiland-Jørgensen struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); 2796f9d451aSToke Høiland-Jørgensen u32 idx, *next = next_key; 2806f9d451aSToke Høiland-Jørgensen struct bpf_dtab_netdev *dev, *next_dev; 2816f9d451aSToke Høiland-Jørgensen struct hlist_head *head; 2826f9d451aSToke Høiland-Jørgensen int i = 0; 2836f9d451aSToke Høiland-Jørgensen 2846f9d451aSToke Høiland-Jørgensen if (!key) 2856f9d451aSToke Høiland-Jørgensen goto find_first; 2866f9d451aSToke Høiland-Jørgensen 2876f9d451aSToke Høiland-Jørgensen idx = *(u32 *)key; 2886f9d451aSToke Høiland-Jørgensen 2896f9d451aSToke Høiland-Jørgensen dev = __dev_map_hash_lookup_elem(map, idx); 2906f9d451aSToke Høiland-Jørgensen if (!dev) 2916f9d451aSToke Høiland-Jørgensen goto find_first; 2926f9d451aSToke Høiland-Jørgensen 2936f9d451aSToke Høiland-Jørgensen next_dev = hlist_entry_safe(rcu_dereference_raw(hlist_next_rcu(&dev->index_hlist)), 2946f9d451aSToke Høiland-Jørgensen struct bpf_dtab_netdev, index_hlist); 2956f9d451aSToke Høiland-Jørgensen 2966f9d451aSToke Høiland-Jørgensen if (next_dev) { 2976f9d451aSToke Høiland-Jørgensen *next = next_dev->idx; 2986f9d451aSToke Høiland-Jørgensen return 0; 2996f9d451aSToke Høiland-Jørgensen } 3006f9d451aSToke Høiland-Jørgensen 3016f9d451aSToke Høiland-Jørgensen i = idx & (dtab->n_buckets - 1); 3026f9d451aSToke Høiland-Jørgensen i++; 3036f9d451aSToke Høiland-Jørgensen 3046f9d451aSToke Høiland-Jørgensen find_first: 3056f9d451aSToke Høiland-Jørgensen for (; i < dtab->n_buckets; i++) { 3066f9d451aSToke Høiland-Jørgensen head = dev_map_index_hash(dtab, i); 3076f9d451aSToke Høiland-Jørgensen 3086f9d451aSToke Høiland-Jørgensen next_dev = hlist_entry_safe(rcu_dereference_raw(hlist_first_rcu(head)), 3096f9d451aSToke Høiland-Jørgensen struct bpf_dtab_netdev, 3106f9d451aSToke Høiland-Jørgensen index_hlist); 3116f9d451aSToke Høiland-Jørgensen if (next_dev) { 3126f9d451aSToke Høiland-Jørgensen *next = next_dev->idx; 3136f9d451aSToke Høiland-Jørgensen return 0; 3146f9d451aSToke Høiland-Jørgensen } 3156f9d451aSToke Høiland-Jørgensen } 3166f9d451aSToke Høiland-Jørgensen 3176f9d451aSToke Høiland-Jørgensen return -ENOENT; 3186f9d451aSToke Høiland-Jørgensen } 3196f9d451aSToke Høiland-Jørgensen 320fbee97feSDavid Ahern bool dev_map_can_have_prog(struct bpf_map *map) 321fbee97feSDavid Ahern { 322fbee97feSDavid Ahern if ((map->map_type == BPF_MAP_TYPE_DEVMAP || 323fbee97feSDavid Ahern map->map_type == BPF_MAP_TYPE_DEVMAP_HASH) && 324fbee97feSDavid Ahern map->value_size != offsetofend(struct bpf_devmap_val, ifindex)) 325fbee97feSDavid Ahern return true; 326fbee97feSDavid Ahern 327fbee97feSDavid Ahern return false; 328fbee97feSDavid Ahern } 329fbee97feSDavid Ahern 330*cb261b59SJesper Dangaard Brouer static int dev_map_bpf_prog_run(struct bpf_prog *xdp_prog, 331*cb261b59SJesper Dangaard Brouer struct xdp_frame **frames, int n, 332*cb261b59SJesper Dangaard Brouer struct net_device *dev) 333*cb261b59SJesper Dangaard Brouer { 334*cb261b59SJesper Dangaard Brouer struct xdp_txq_info txq = { .dev = dev }; 335*cb261b59SJesper Dangaard Brouer struct xdp_buff xdp; 336*cb261b59SJesper Dangaard Brouer int i, nframes = 0; 337*cb261b59SJesper Dangaard Brouer 338*cb261b59SJesper Dangaard Brouer for (i = 0; i < n; i++) { 339*cb261b59SJesper Dangaard Brouer struct xdp_frame *xdpf = frames[i]; 340*cb261b59SJesper Dangaard Brouer u32 act; 341*cb261b59SJesper Dangaard Brouer int err; 342*cb261b59SJesper Dangaard Brouer 343*cb261b59SJesper Dangaard Brouer xdp_convert_frame_to_buff(xdpf, &xdp); 344*cb261b59SJesper Dangaard Brouer xdp.txq = &txq; 345*cb261b59SJesper Dangaard Brouer 346*cb261b59SJesper Dangaard Brouer act = bpf_prog_run_xdp(xdp_prog, &xdp); 347*cb261b59SJesper Dangaard Brouer switch (act) { 348*cb261b59SJesper Dangaard Brouer case XDP_PASS: 349*cb261b59SJesper Dangaard Brouer err = xdp_update_frame_from_buff(&xdp, xdpf); 350*cb261b59SJesper Dangaard Brouer if (unlikely(err < 0)) 351*cb261b59SJesper Dangaard Brouer xdp_return_frame_rx_napi(xdpf); 352*cb261b59SJesper Dangaard Brouer else 353*cb261b59SJesper Dangaard Brouer frames[nframes++] = xdpf; 354*cb261b59SJesper Dangaard Brouer break; 355*cb261b59SJesper Dangaard Brouer default: 356*cb261b59SJesper Dangaard Brouer bpf_warn_invalid_xdp_action(act); 357*cb261b59SJesper Dangaard Brouer fallthrough; 358*cb261b59SJesper Dangaard Brouer case XDP_ABORTED: 359*cb261b59SJesper Dangaard Brouer trace_xdp_exception(dev, xdp_prog, act); 360*cb261b59SJesper Dangaard Brouer fallthrough; 361*cb261b59SJesper Dangaard Brouer case XDP_DROP: 362*cb261b59SJesper Dangaard Brouer xdp_return_frame_rx_napi(xdpf); 363*cb261b59SJesper Dangaard Brouer break; 364*cb261b59SJesper Dangaard Brouer } 365*cb261b59SJesper Dangaard Brouer } 366*cb261b59SJesper Dangaard Brouer return nframes; /* sent frames count */ 367*cb261b59SJesper Dangaard Brouer } 368*cb261b59SJesper Dangaard Brouer 369ebc4ecd4SBjörn Töpel static void bq_xmit_all(struct xdp_dev_bulk_queue *bq, u32 flags) 3705d053f9dSJesper Dangaard Brouer { 37175ccae62SToke Høiland-Jørgensen struct net_device *dev = bq->dev; 372*cb261b59SJesper Dangaard Brouer int sent = 0, drops = 0, err = 0; 373*cb261b59SJesper Dangaard Brouer unsigned int cnt = bq->count; 374*cb261b59SJesper Dangaard Brouer int to_send = cnt; 3755d053f9dSJesper Dangaard Brouer int i; 3765d053f9dSJesper Dangaard Brouer 377*cb261b59SJesper Dangaard Brouer if (unlikely(!cnt)) 378ebc4ecd4SBjörn Töpel return; 3795d053f9dSJesper Dangaard Brouer 380*cb261b59SJesper Dangaard Brouer for (i = 0; i < cnt; i++) { 3815d053f9dSJesper Dangaard Brouer struct xdp_frame *xdpf = bq->q[i]; 3825d053f9dSJesper Dangaard Brouer 3835d053f9dSJesper Dangaard Brouer prefetch(xdpf); 3845d053f9dSJesper Dangaard Brouer } 3855d053f9dSJesper Dangaard Brouer 386*cb261b59SJesper Dangaard Brouer if (bq->xdp_prog) { 387*cb261b59SJesper Dangaard Brouer to_send = dev_map_bpf_prog_run(bq->xdp_prog, bq->q, cnt, dev); 388*cb261b59SJesper Dangaard Brouer if (!to_send) 389*cb261b59SJesper Dangaard Brouer goto out; 390*cb261b59SJesper Dangaard Brouer 391*cb261b59SJesper Dangaard Brouer drops = cnt - to_send; 392*cb261b59SJesper Dangaard Brouer } 393*cb261b59SJesper Dangaard Brouer 394*cb261b59SJesper Dangaard Brouer sent = dev->netdev_ops->ndo_xdp_xmit(dev, to_send, bq->q, flags); 395735fc405SJesper Dangaard Brouer if (sent < 0) { 396fdc13979SLorenzo Bianconi /* If ndo_xdp_xmit fails with an errno, no frames have 397fdc13979SLorenzo Bianconi * been xmit'ed. 398fdc13979SLorenzo Bianconi */ 399e74de52eSJesper Dangaard Brouer err = sent; 400735fc405SJesper Dangaard Brouer sent = 0; 40138edddb8SJesper Dangaard Brouer } 4025d053f9dSJesper Dangaard Brouer 403fdc13979SLorenzo Bianconi /* If not all frames have been transmitted, it is our 404fdc13979SLorenzo Bianconi * responsibility to free them 405735fc405SJesper Dangaard Brouer */ 406*cb261b59SJesper Dangaard Brouer for (i = sent; unlikely(i < to_send); i++) 407fdc13979SLorenzo Bianconi xdp_return_frame_rx_napi(bq->q[i]); 408735fc405SJesper Dangaard Brouer 409*cb261b59SJesper Dangaard Brouer out: 410*cb261b59SJesper Dangaard Brouer drops = cnt - sent; 411fdc13979SLorenzo Bianconi bq->count = 0; 412*cb261b59SJesper Dangaard Brouer trace_xdp_devmap_xmit(bq->dev_rx, dev, sent, drops, err); 4135d053f9dSJesper Dangaard Brouer } 4145d053f9dSJesper Dangaard Brouer 4151d233886SToke Høiland-Jørgensen /* __dev_flush is called from xdp_do_flush() which _must_ be signaled 41611393cc9SJohn Fastabend * from the driver before returning from its napi->poll() routine. The poll() 41711393cc9SJohn Fastabend * routine is called either from busy_poll context or net_rx_action signaled 41811393cc9SJohn Fastabend * from NET_RX_SOFTIRQ. Either way the poll routine must complete before the 419d5df2830SToke Høiland-Jørgensen * net device can be torn down. On devmap tear down we ensure the flush list 420d5df2830SToke Høiland-Jørgensen * is empty before completing to ensure all flush operations have completed. 421b23bfa56SJohn Fastabend * When drivers update the bpf program they may need to ensure any flush ops 422b23bfa56SJohn Fastabend * are also complete. Using synchronize_rcu or call_rcu will suffice for this 423b23bfa56SJohn Fastabend * because both wait for napi context to exit. 42411393cc9SJohn Fastabend */ 4251d233886SToke Høiland-Jørgensen void __dev_flush(void) 42611393cc9SJohn Fastabend { 4271d233886SToke Høiland-Jørgensen struct list_head *flush_list = this_cpu_ptr(&dev_flush_list); 42875ccae62SToke Høiland-Jørgensen struct xdp_dev_bulk_queue *bq, *tmp; 42911393cc9SJohn Fastabend 430*cb261b59SJesper Dangaard Brouer list_for_each_entry_safe(bq, tmp, flush_list, flush_node) { 4310536b852SBjörn Töpel bq_xmit_all(bq, XDP_XMIT_FLUSH); 432*cb261b59SJesper Dangaard Brouer bq->dev_rx = NULL; 433*cb261b59SJesper Dangaard Brouer bq->xdp_prog = NULL; 434*cb261b59SJesper Dangaard Brouer __list_del_clearprev(&bq->flush_node); 435*cb261b59SJesper Dangaard Brouer } 43611393cc9SJohn Fastabend } 43711393cc9SJohn Fastabend 438546ac1ffSJohn Fastabend /* rcu_read_lock (from syscall and BPF contexts) ensures that if a delete and/or 4398fb33b60SZhen Lei * update happens in parallel here a dev_put won't happen until after reading 4408fb33b60SZhen Lei * the ifindex. 441546ac1ffSJohn Fastabend */ 442e6a4750fSBjörn Töpel static void *__dev_map_lookup_elem(struct bpf_map *map, u32 key) 443546ac1ffSJohn Fastabend { 444546ac1ffSJohn Fastabend struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); 44567f29e07SJesper Dangaard Brouer struct bpf_dtab_netdev *obj; 446546ac1ffSJohn Fastabend 447af4d045cSDaniel Borkmann if (key >= map->max_entries) 448546ac1ffSJohn Fastabend return NULL; 449546ac1ffSJohn Fastabend 45067f29e07SJesper Dangaard Brouer obj = READ_ONCE(dtab->netdev_map[key]); 45167f29e07SJesper Dangaard Brouer return obj; 45267f29e07SJesper Dangaard Brouer } 45367f29e07SJesper Dangaard Brouer 4545d053f9dSJesper Dangaard Brouer /* Runs under RCU-read-side, plus in softirq under NAPI protection. 4555d053f9dSJesper Dangaard Brouer * Thus, safe percpu variable access. 4565d053f9dSJesper Dangaard Brouer */ 457ebc4ecd4SBjörn Töpel static void bq_enqueue(struct net_device *dev, struct xdp_frame *xdpf, 458*cb261b59SJesper Dangaard Brouer struct net_device *dev_rx, struct bpf_prog *xdp_prog) 4595d053f9dSJesper Dangaard Brouer { 4601d233886SToke Høiland-Jørgensen struct list_head *flush_list = this_cpu_ptr(&dev_flush_list); 46175ccae62SToke Høiland-Jørgensen struct xdp_dev_bulk_queue *bq = this_cpu_ptr(dev->xdp_bulkq); 4625d053f9dSJesper Dangaard Brouer 4635d053f9dSJesper Dangaard Brouer if (unlikely(bq->count == DEV_MAP_BULK_SIZE)) 4640536b852SBjörn Töpel bq_xmit_all(bq, 0); 4655d053f9dSJesper Dangaard Brouer 46638edddb8SJesper Dangaard Brouer /* Ingress dev_rx will be the same for all xdp_frame's in 46738edddb8SJesper Dangaard Brouer * bulk_queue, because bq stored per-CPU and must be flushed 46838edddb8SJesper Dangaard Brouer * from net_device drivers NAPI func end. 469*cb261b59SJesper Dangaard Brouer * 470*cb261b59SJesper Dangaard Brouer * Do the same with xdp_prog and flush_list since these fields 471*cb261b59SJesper Dangaard Brouer * are only ever modified together. 47238edddb8SJesper Dangaard Brouer */ 473*cb261b59SJesper Dangaard Brouer if (!bq->dev_rx) { 47438edddb8SJesper Dangaard Brouer bq->dev_rx = dev_rx; 475*cb261b59SJesper Dangaard Brouer bq->xdp_prog = xdp_prog; 476d5df2830SToke Høiland-Jørgensen list_add(&bq->flush_node, flush_list); 4775d053f9dSJesper Dangaard Brouer } 4785d053f9dSJesper Dangaard Brouer 479*cb261b59SJesper Dangaard Brouer bq->q[bq->count++] = xdpf; 480*cb261b59SJesper Dangaard Brouer } 481*cb261b59SJesper Dangaard Brouer 4821d233886SToke Høiland-Jørgensen static inline int __xdp_enqueue(struct net_device *dev, struct xdp_buff *xdp, 483*cb261b59SJesper Dangaard Brouer struct net_device *dev_rx, 484*cb261b59SJesper Dangaard Brouer struct bpf_prog *xdp_prog) 48567f29e07SJesper Dangaard Brouer { 48667f29e07SJesper Dangaard Brouer struct xdp_frame *xdpf; 487d8d7218aSToshiaki Makita int err; 48867f29e07SJesper Dangaard Brouer 48967f29e07SJesper Dangaard Brouer if (!dev->netdev_ops->ndo_xdp_xmit) 49067f29e07SJesper Dangaard Brouer return -EOPNOTSUPP; 49167f29e07SJesper Dangaard Brouer 492d8d7218aSToshiaki Makita err = xdp_ok_fwd_dev(dev, xdp->data_end - xdp->data); 493d8d7218aSToshiaki Makita if (unlikely(err)) 494d8d7218aSToshiaki Makita return err; 495d8d7218aSToshiaki Makita 4961b698fa5SLorenzo Bianconi xdpf = xdp_convert_buff_to_frame(xdp); 49767f29e07SJesper Dangaard Brouer if (unlikely(!xdpf)) 49867f29e07SJesper Dangaard Brouer return -EOVERFLOW; 49967f29e07SJesper Dangaard Brouer 500*cb261b59SJesper Dangaard Brouer bq_enqueue(dev, xdpf, dev_rx, xdp_prog); 501ebc4ecd4SBjörn Töpel return 0; 502546ac1ffSJohn Fastabend } 503546ac1ffSJohn Fastabend 5041d233886SToke Høiland-Jørgensen int dev_xdp_enqueue(struct net_device *dev, struct xdp_buff *xdp, 5051d233886SToke Høiland-Jørgensen struct net_device *dev_rx) 5061d233886SToke Høiland-Jørgensen { 507*cb261b59SJesper Dangaard Brouer return __xdp_enqueue(dev, xdp, dev_rx, NULL); 5081d233886SToke Høiland-Jørgensen } 5091d233886SToke Høiland-Jørgensen 5101d233886SToke Høiland-Jørgensen int dev_map_enqueue(struct bpf_dtab_netdev *dst, struct xdp_buff *xdp, 5111d233886SToke Høiland-Jørgensen struct net_device *dev_rx) 5121d233886SToke Høiland-Jørgensen { 5131d233886SToke Høiland-Jørgensen struct net_device *dev = dst->dev; 5141d233886SToke Høiland-Jørgensen 515*cb261b59SJesper Dangaard Brouer return __xdp_enqueue(dev, xdp, dev_rx, dst->xdp_prog); 5161d233886SToke Høiland-Jørgensen } 5171d233886SToke Høiland-Jørgensen 5186d5fc195SToshiaki Makita int dev_map_generic_redirect(struct bpf_dtab_netdev *dst, struct sk_buff *skb, 5196d5fc195SToshiaki Makita struct bpf_prog *xdp_prog) 5206d5fc195SToshiaki Makita { 5216d5fc195SToshiaki Makita int err; 5226d5fc195SToshiaki Makita 523d8d7218aSToshiaki Makita err = xdp_ok_fwd_dev(dst->dev, skb->len); 5246d5fc195SToshiaki Makita if (unlikely(err)) 5256d5fc195SToshiaki Makita return err; 5266d5fc195SToshiaki Makita skb->dev = dst->dev; 5276d5fc195SToshiaki Makita generic_xdp_tx(skb, xdp_prog); 5286d5fc195SToshiaki Makita 5296d5fc195SToshiaki Makita return 0; 5306d5fc195SToshiaki Makita } 5316d5fc195SToshiaki Makita 532af4d045cSDaniel Borkmann static void *dev_map_lookup_elem(struct bpf_map *map, void *key) 53311393cc9SJohn Fastabend { 53467f29e07SJesper Dangaard Brouer struct bpf_dtab_netdev *obj = __dev_map_lookup_elem(map, *(u32 *)key); 535af4d045cSDaniel Borkmann 5367f1c0426SDavid Ahern return obj ? &obj->val : NULL; 537af4d045cSDaniel Borkmann } 538af4d045cSDaniel Borkmann 5396f9d451aSToke Høiland-Jørgensen static void *dev_map_hash_lookup_elem(struct bpf_map *map, void *key) 5406f9d451aSToke Høiland-Jørgensen { 5416f9d451aSToke Høiland-Jørgensen struct bpf_dtab_netdev *obj = __dev_map_hash_lookup_elem(map, 5426f9d451aSToke Høiland-Jørgensen *(u32 *)key); 5437f1c0426SDavid Ahern return obj ? &obj->val : NULL; 5446f9d451aSToke Høiland-Jørgensen } 5456f9d451aSToke Høiland-Jørgensen 546546ac1ffSJohn Fastabend static void __dev_map_entry_free(struct rcu_head *rcu) 547546ac1ffSJohn Fastabend { 548af4d045cSDaniel Borkmann struct bpf_dtab_netdev *dev; 549546ac1ffSJohn Fastabend 550af4d045cSDaniel Borkmann dev = container_of(rcu, struct bpf_dtab_netdev, rcu); 551fbee97feSDavid Ahern if (dev->xdp_prog) 552fbee97feSDavid Ahern bpf_prog_put(dev->xdp_prog); 553af4d045cSDaniel Borkmann dev_put(dev->dev); 554af4d045cSDaniel Borkmann kfree(dev); 555546ac1ffSJohn Fastabend } 556546ac1ffSJohn Fastabend 557546ac1ffSJohn Fastabend static int dev_map_delete_elem(struct bpf_map *map, void *key) 558546ac1ffSJohn Fastabend { 559546ac1ffSJohn Fastabend struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); 560546ac1ffSJohn Fastabend struct bpf_dtab_netdev *old_dev; 561546ac1ffSJohn Fastabend int k = *(u32 *)key; 562546ac1ffSJohn Fastabend 563546ac1ffSJohn Fastabend if (k >= map->max_entries) 564546ac1ffSJohn Fastabend return -EINVAL; 565546ac1ffSJohn Fastabend 566af4d045cSDaniel Borkmann /* Use call_rcu() here to ensure any rcu critical sections have 56742a84a8cSJohn Fastabend * completed as well as any flush operations because call_rcu 56842a84a8cSJohn Fastabend * will wait for preempt-disable region to complete, NAPI in this 56942a84a8cSJohn Fastabend * context. And additionally, the driver tear down ensures all 57042a84a8cSJohn Fastabend * soft irqs are complete before removing the net device in the 57142a84a8cSJohn Fastabend * case of dev_put equals zero. 572546ac1ffSJohn Fastabend */ 573546ac1ffSJohn Fastabend old_dev = xchg(&dtab->netdev_map[k], NULL); 574546ac1ffSJohn Fastabend if (old_dev) 575546ac1ffSJohn Fastabend call_rcu(&old_dev->rcu, __dev_map_entry_free); 576546ac1ffSJohn Fastabend return 0; 577546ac1ffSJohn Fastabend } 578546ac1ffSJohn Fastabend 5796f9d451aSToke Høiland-Jørgensen static int dev_map_hash_delete_elem(struct bpf_map *map, void *key) 5806f9d451aSToke Høiland-Jørgensen { 5816f9d451aSToke Høiland-Jørgensen struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); 5826f9d451aSToke Høiland-Jørgensen struct bpf_dtab_netdev *old_dev; 5836f9d451aSToke Høiland-Jørgensen int k = *(u32 *)key; 5846f9d451aSToke Høiland-Jørgensen unsigned long flags; 5856f9d451aSToke Høiland-Jørgensen int ret = -ENOENT; 5866f9d451aSToke Høiland-Jørgensen 5876f9d451aSToke Høiland-Jørgensen spin_lock_irqsave(&dtab->index_lock, flags); 5886f9d451aSToke Høiland-Jørgensen 5896f9d451aSToke Høiland-Jørgensen old_dev = __dev_map_hash_lookup_elem(map, k); 5906f9d451aSToke Høiland-Jørgensen if (old_dev) { 5916f9d451aSToke Høiland-Jørgensen dtab->items--; 5926f9d451aSToke Høiland-Jørgensen hlist_del_init_rcu(&old_dev->index_hlist); 5936f9d451aSToke Høiland-Jørgensen call_rcu(&old_dev->rcu, __dev_map_entry_free); 5946f9d451aSToke Høiland-Jørgensen ret = 0; 5956f9d451aSToke Høiland-Jørgensen } 5966f9d451aSToke Høiland-Jørgensen spin_unlock_irqrestore(&dtab->index_lock, flags); 5976f9d451aSToke Høiland-Jørgensen 5986f9d451aSToke Høiland-Jørgensen return ret; 5996f9d451aSToke Høiland-Jørgensen } 6006f9d451aSToke Høiland-Jørgensen 601fca16e51SToke Høiland-Jørgensen static struct bpf_dtab_netdev *__dev_map_alloc_node(struct net *net, 602fca16e51SToke Høiland-Jørgensen struct bpf_dtab *dtab, 6037f1c0426SDavid Ahern struct bpf_devmap_val *val, 604fca16e51SToke Høiland-Jørgensen unsigned int idx) 605546ac1ffSJohn Fastabend { 606fbee97feSDavid Ahern struct bpf_prog *prog = NULL; 607fca16e51SToke Høiland-Jørgensen struct bpf_dtab_netdev *dev; 608546ac1ffSJohn Fastabend 6091440290aSRoman Gushchin dev = bpf_map_kmalloc_node(&dtab->map, sizeof(*dev), 6101440290aSRoman Gushchin GFP_ATOMIC | __GFP_NOWARN, 61175ccae62SToke Høiland-Jørgensen dtab->map.numa_node); 612546ac1ffSJohn Fastabend if (!dev) 613fca16e51SToke Høiland-Jørgensen return ERR_PTR(-ENOMEM); 614546ac1ffSJohn Fastabend 6157f1c0426SDavid Ahern dev->dev = dev_get_by_index(net, val->ifindex); 6167f1c0426SDavid Ahern if (!dev->dev) 6177f1c0426SDavid Ahern goto err_out; 618546ac1ffSJohn Fastabend 619281920b7SJesper Dangaard Brouer if (val->bpf_prog.fd > 0) { 620fbee97feSDavid Ahern prog = bpf_prog_get_type_dev(val->bpf_prog.fd, 621fbee97feSDavid Ahern BPF_PROG_TYPE_XDP, false); 622fbee97feSDavid Ahern if (IS_ERR(prog)) 623fbee97feSDavid Ahern goto err_put_dev; 624fbee97feSDavid Ahern if (prog->expected_attach_type != BPF_XDP_DEVMAP) 625fbee97feSDavid Ahern goto err_put_prog; 626fbee97feSDavid Ahern } 627fbee97feSDavid Ahern 628fca16e51SToke Høiland-Jørgensen dev->idx = idx; 629546ac1ffSJohn Fastabend dev->dtab = dtab; 630fbee97feSDavid Ahern if (prog) { 631fbee97feSDavid Ahern dev->xdp_prog = prog; 632fbee97feSDavid Ahern dev->val.bpf_prog.id = prog->aux->id; 633fbee97feSDavid Ahern } else { 634fbee97feSDavid Ahern dev->xdp_prog = NULL; 635fbee97feSDavid Ahern dev->val.bpf_prog.id = 0; 636fbee97feSDavid Ahern } 6377f1c0426SDavid Ahern dev->val.ifindex = val->ifindex; 638fca16e51SToke Høiland-Jørgensen 639fca16e51SToke Høiland-Jørgensen return dev; 640fbee97feSDavid Ahern err_put_prog: 641fbee97feSDavid Ahern bpf_prog_put(prog); 642fbee97feSDavid Ahern err_put_dev: 643fbee97feSDavid Ahern dev_put(dev->dev); 6447f1c0426SDavid Ahern err_out: 6457f1c0426SDavid Ahern kfree(dev); 6467f1c0426SDavid Ahern return ERR_PTR(-EINVAL); 647fca16e51SToke Høiland-Jørgensen } 648fca16e51SToke Høiland-Jørgensen 649fca16e51SToke Høiland-Jørgensen static int __dev_map_update_elem(struct net *net, struct bpf_map *map, 650fca16e51SToke Høiland-Jørgensen void *key, void *value, u64 map_flags) 651fca16e51SToke Høiland-Jørgensen { 652fca16e51SToke Høiland-Jørgensen struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); 653fca16e51SToke Høiland-Jørgensen struct bpf_dtab_netdev *dev, *old_dev; 654281920b7SJesper Dangaard Brouer struct bpf_devmap_val val = {}; 655fca16e51SToke Høiland-Jørgensen u32 i = *(u32 *)key; 656fca16e51SToke Høiland-Jørgensen 657fca16e51SToke Høiland-Jørgensen if (unlikely(map_flags > BPF_EXIST)) 658fca16e51SToke Høiland-Jørgensen return -EINVAL; 659fca16e51SToke Høiland-Jørgensen if (unlikely(i >= dtab->map.max_entries)) 660fca16e51SToke Høiland-Jørgensen return -E2BIG; 661fca16e51SToke Høiland-Jørgensen if (unlikely(map_flags == BPF_NOEXIST)) 662fca16e51SToke Høiland-Jørgensen return -EEXIST; 663fca16e51SToke Høiland-Jørgensen 6647f1c0426SDavid Ahern /* already verified value_size <= sizeof val */ 6657f1c0426SDavid Ahern memcpy(&val, value, map->value_size); 6667f1c0426SDavid Ahern 6677f1c0426SDavid Ahern if (!val.ifindex) { 668fca16e51SToke Høiland-Jørgensen dev = NULL; 669fbee97feSDavid Ahern /* can not specify fd if ifindex is 0 */ 670281920b7SJesper Dangaard Brouer if (val.bpf_prog.fd > 0) 671fbee97feSDavid Ahern return -EINVAL; 672fca16e51SToke Høiland-Jørgensen } else { 6737f1c0426SDavid Ahern dev = __dev_map_alloc_node(net, dtab, &val, i); 674fca16e51SToke Høiland-Jørgensen if (IS_ERR(dev)) 675fca16e51SToke Høiland-Jørgensen return PTR_ERR(dev); 676546ac1ffSJohn Fastabend } 677546ac1ffSJohn Fastabend 678546ac1ffSJohn Fastabend /* Use call_rcu() here to ensure rcu critical sections have completed 679546ac1ffSJohn Fastabend * Remembering the driver side flush operation will happen before the 680546ac1ffSJohn Fastabend * net device is removed. 681546ac1ffSJohn Fastabend */ 682546ac1ffSJohn Fastabend old_dev = xchg(&dtab->netdev_map[i], dev); 683546ac1ffSJohn Fastabend if (old_dev) 684546ac1ffSJohn Fastabend call_rcu(&old_dev->rcu, __dev_map_entry_free); 685546ac1ffSJohn Fastabend 686546ac1ffSJohn Fastabend return 0; 687546ac1ffSJohn Fastabend } 688546ac1ffSJohn Fastabend 689fca16e51SToke Høiland-Jørgensen static int dev_map_update_elem(struct bpf_map *map, void *key, void *value, 690fca16e51SToke Høiland-Jørgensen u64 map_flags) 691fca16e51SToke Høiland-Jørgensen { 692fca16e51SToke Høiland-Jørgensen return __dev_map_update_elem(current->nsproxy->net_ns, 693fca16e51SToke Høiland-Jørgensen map, key, value, map_flags); 694fca16e51SToke Høiland-Jørgensen } 695fca16e51SToke Høiland-Jørgensen 6966f9d451aSToke Høiland-Jørgensen static int __dev_map_hash_update_elem(struct net *net, struct bpf_map *map, 6976f9d451aSToke Høiland-Jørgensen void *key, void *value, u64 map_flags) 6986f9d451aSToke Høiland-Jørgensen { 6996f9d451aSToke Høiland-Jørgensen struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); 7006f9d451aSToke Høiland-Jørgensen struct bpf_dtab_netdev *dev, *old_dev; 701281920b7SJesper Dangaard Brouer struct bpf_devmap_val val = {}; 7026f9d451aSToke Høiland-Jørgensen u32 idx = *(u32 *)key; 7036f9d451aSToke Høiland-Jørgensen unsigned long flags; 704af58e7eeSToke Høiland-Jørgensen int err = -EEXIST; 7056f9d451aSToke Høiland-Jørgensen 7067f1c0426SDavid Ahern /* already verified value_size <= sizeof val */ 7077f1c0426SDavid Ahern memcpy(&val, value, map->value_size); 7087f1c0426SDavid Ahern 7097f1c0426SDavid Ahern if (unlikely(map_flags > BPF_EXIST || !val.ifindex)) 7106f9d451aSToke Høiland-Jørgensen return -EINVAL; 7116f9d451aSToke Høiland-Jørgensen 712af58e7eeSToke Høiland-Jørgensen spin_lock_irqsave(&dtab->index_lock, flags); 713af58e7eeSToke Høiland-Jørgensen 7146f9d451aSToke Høiland-Jørgensen old_dev = __dev_map_hash_lookup_elem(map, idx); 7156f9d451aSToke Høiland-Jørgensen if (old_dev && (map_flags & BPF_NOEXIST)) 716af58e7eeSToke Høiland-Jørgensen goto out_err; 7176f9d451aSToke Høiland-Jørgensen 7187f1c0426SDavid Ahern dev = __dev_map_alloc_node(net, dtab, &val, idx); 719af58e7eeSToke Høiland-Jørgensen if (IS_ERR(dev)) { 720af58e7eeSToke Høiland-Jørgensen err = PTR_ERR(dev); 721af58e7eeSToke Høiland-Jørgensen goto out_err; 722af58e7eeSToke Høiland-Jørgensen } 7236f9d451aSToke Høiland-Jørgensen 7246f9d451aSToke Høiland-Jørgensen if (old_dev) { 7256f9d451aSToke Høiland-Jørgensen hlist_del_rcu(&old_dev->index_hlist); 7266f9d451aSToke Høiland-Jørgensen } else { 7276f9d451aSToke Høiland-Jørgensen if (dtab->items >= dtab->map.max_entries) { 7286f9d451aSToke Høiland-Jørgensen spin_unlock_irqrestore(&dtab->index_lock, flags); 7296f9d451aSToke Høiland-Jørgensen call_rcu(&dev->rcu, __dev_map_entry_free); 7306f9d451aSToke Høiland-Jørgensen return -E2BIG; 7316f9d451aSToke Høiland-Jørgensen } 7326f9d451aSToke Høiland-Jørgensen dtab->items++; 7336f9d451aSToke Høiland-Jørgensen } 7346f9d451aSToke Høiland-Jørgensen 7356f9d451aSToke Høiland-Jørgensen hlist_add_head_rcu(&dev->index_hlist, 7366f9d451aSToke Høiland-Jørgensen dev_map_index_hash(dtab, idx)); 7376f9d451aSToke Høiland-Jørgensen spin_unlock_irqrestore(&dtab->index_lock, flags); 7386f9d451aSToke Høiland-Jørgensen 7396f9d451aSToke Høiland-Jørgensen if (old_dev) 7406f9d451aSToke Høiland-Jørgensen call_rcu(&old_dev->rcu, __dev_map_entry_free); 7416f9d451aSToke Høiland-Jørgensen 7426f9d451aSToke Høiland-Jørgensen return 0; 743af58e7eeSToke Høiland-Jørgensen 744af58e7eeSToke Høiland-Jørgensen out_err: 745af58e7eeSToke Høiland-Jørgensen spin_unlock_irqrestore(&dtab->index_lock, flags); 746af58e7eeSToke Høiland-Jørgensen return err; 7476f9d451aSToke Høiland-Jørgensen } 7486f9d451aSToke Høiland-Jørgensen 7496f9d451aSToke Høiland-Jørgensen static int dev_map_hash_update_elem(struct bpf_map *map, void *key, void *value, 7506f9d451aSToke Høiland-Jørgensen u64 map_flags) 7516f9d451aSToke Høiland-Jørgensen { 7526f9d451aSToke Høiland-Jørgensen return __dev_map_hash_update_elem(current->nsproxy->net_ns, 7536f9d451aSToke Høiland-Jørgensen map, key, value, map_flags); 7546f9d451aSToke Høiland-Jørgensen } 7556f9d451aSToke Høiland-Jørgensen 756e6a4750fSBjörn Töpel static int dev_map_redirect(struct bpf_map *map, u32 ifindex, u64 flags) 757e6a4750fSBjörn Töpel { 758e6a4750fSBjörn Töpel return __bpf_xdp_redirect_map(map, ifindex, flags, __dev_map_lookup_elem); 759e6a4750fSBjörn Töpel } 760e6a4750fSBjörn Töpel 761e6a4750fSBjörn Töpel static int dev_hash_map_redirect(struct bpf_map *map, u32 ifindex, u64 flags) 762e6a4750fSBjörn Töpel { 763e6a4750fSBjörn Töpel return __bpf_xdp_redirect_map(map, ifindex, flags, __dev_map_hash_lookup_elem); 764e6a4750fSBjörn Töpel } 765e6a4750fSBjörn Töpel 7662872e9acSAndrey Ignatov static int dev_map_btf_id; 767546ac1ffSJohn Fastabend const struct bpf_map_ops dev_map_ops = { 768f4d05259SMartin KaFai Lau .map_meta_equal = bpf_map_meta_equal, 769546ac1ffSJohn Fastabend .map_alloc = dev_map_alloc, 770546ac1ffSJohn Fastabend .map_free = dev_map_free, 771546ac1ffSJohn Fastabend .map_get_next_key = dev_map_get_next_key, 772546ac1ffSJohn Fastabend .map_lookup_elem = dev_map_lookup_elem, 773546ac1ffSJohn Fastabend .map_update_elem = dev_map_update_elem, 774546ac1ffSJohn Fastabend .map_delete_elem = dev_map_delete_elem, 775e8d2bec0SDaniel Borkmann .map_check_btf = map_check_no_btf, 7762872e9acSAndrey Ignatov .map_btf_name = "bpf_dtab", 7772872e9acSAndrey Ignatov .map_btf_id = &dev_map_btf_id, 778e6a4750fSBjörn Töpel .map_redirect = dev_map_redirect, 779546ac1ffSJohn Fastabend }; 7802ddf71e2SJohn Fastabend 7812872e9acSAndrey Ignatov static int dev_map_hash_map_btf_id; 7826f9d451aSToke Høiland-Jørgensen const struct bpf_map_ops dev_map_hash_ops = { 783f4d05259SMartin KaFai Lau .map_meta_equal = bpf_map_meta_equal, 7846f9d451aSToke Høiland-Jørgensen .map_alloc = dev_map_alloc, 7856f9d451aSToke Høiland-Jørgensen .map_free = dev_map_free, 7866f9d451aSToke Høiland-Jørgensen .map_get_next_key = dev_map_hash_get_next_key, 7876f9d451aSToke Høiland-Jørgensen .map_lookup_elem = dev_map_hash_lookup_elem, 7886f9d451aSToke Høiland-Jørgensen .map_update_elem = dev_map_hash_update_elem, 7896f9d451aSToke Høiland-Jørgensen .map_delete_elem = dev_map_hash_delete_elem, 7906f9d451aSToke Høiland-Jørgensen .map_check_btf = map_check_no_btf, 7912872e9acSAndrey Ignatov .map_btf_name = "bpf_dtab", 7922872e9acSAndrey Ignatov .map_btf_id = &dev_map_hash_map_btf_id, 793e6a4750fSBjörn Töpel .map_redirect = dev_hash_map_redirect, 7946f9d451aSToke Høiland-Jørgensen }; 7956f9d451aSToke Høiland-Jørgensen 796ce197d83SToke Høiland-Jørgensen static void dev_map_hash_remove_netdev(struct bpf_dtab *dtab, 797ce197d83SToke Høiland-Jørgensen struct net_device *netdev) 798ce197d83SToke Høiland-Jørgensen { 799ce197d83SToke Høiland-Jørgensen unsigned long flags; 800ce197d83SToke Høiland-Jørgensen u32 i; 801ce197d83SToke Høiland-Jørgensen 802ce197d83SToke Høiland-Jørgensen spin_lock_irqsave(&dtab->index_lock, flags); 803ce197d83SToke Høiland-Jørgensen for (i = 0; i < dtab->n_buckets; i++) { 804ce197d83SToke Høiland-Jørgensen struct bpf_dtab_netdev *dev; 805ce197d83SToke Høiland-Jørgensen struct hlist_head *head; 806ce197d83SToke Høiland-Jørgensen struct hlist_node *next; 807ce197d83SToke Høiland-Jørgensen 808ce197d83SToke Høiland-Jørgensen head = dev_map_index_hash(dtab, i); 809ce197d83SToke Høiland-Jørgensen 810ce197d83SToke Høiland-Jørgensen hlist_for_each_entry_safe(dev, next, head, index_hlist) { 811ce197d83SToke Høiland-Jørgensen if (netdev != dev->dev) 812ce197d83SToke Høiland-Jørgensen continue; 813ce197d83SToke Høiland-Jørgensen 814ce197d83SToke Høiland-Jørgensen dtab->items--; 815ce197d83SToke Høiland-Jørgensen hlist_del_rcu(&dev->index_hlist); 816ce197d83SToke Høiland-Jørgensen call_rcu(&dev->rcu, __dev_map_entry_free); 817ce197d83SToke Høiland-Jørgensen } 818ce197d83SToke Høiland-Jørgensen } 819ce197d83SToke Høiland-Jørgensen spin_unlock_irqrestore(&dtab->index_lock, flags); 820ce197d83SToke Høiland-Jørgensen } 821ce197d83SToke Høiland-Jørgensen 8222ddf71e2SJohn Fastabend static int dev_map_notification(struct notifier_block *notifier, 8232ddf71e2SJohn Fastabend ulong event, void *ptr) 8242ddf71e2SJohn Fastabend { 8252ddf71e2SJohn Fastabend struct net_device *netdev = netdev_notifier_info_to_dev(ptr); 8262ddf71e2SJohn Fastabend struct bpf_dtab *dtab; 82775ccae62SToke Høiland-Jørgensen int i, cpu; 8282ddf71e2SJohn Fastabend 8292ddf71e2SJohn Fastabend switch (event) { 83075ccae62SToke Høiland-Jørgensen case NETDEV_REGISTER: 83175ccae62SToke Høiland-Jørgensen if (!netdev->netdev_ops->ndo_xdp_xmit || netdev->xdp_bulkq) 83275ccae62SToke Høiland-Jørgensen break; 83375ccae62SToke Høiland-Jørgensen 83475ccae62SToke Høiland-Jørgensen /* will be freed in free_netdev() */ 8357d4553b6SJun'ichi Nomura netdev->xdp_bulkq = alloc_percpu(struct xdp_dev_bulk_queue); 83675ccae62SToke Høiland-Jørgensen if (!netdev->xdp_bulkq) 83775ccae62SToke Høiland-Jørgensen return NOTIFY_BAD; 83875ccae62SToke Høiland-Jørgensen 83975ccae62SToke Høiland-Jørgensen for_each_possible_cpu(cpu) 84075ccae62SToke Høiland-Jørgensen per_cpu_ptr(netdev->xdp_bulkq, cpu)->dev = netdev; 84175ccae62SToke Høiland-Jørgensen break; 8422ddf71e2SJohn Fastabend case NETDEV_UNREGISTER: 8434cc7b954SJohn Fastabend /* This rcu_read_lock/unlock pair is needed because 8444cc7b954SJohn Fastabend * dev_map_list is an RCU list AND to ensure a delete 8454cc7b954SJohn Fastabend * operation does not free a netdev_map entry while we 8464cc7b954SJohn Fastabend * are comparing it against the netdev being unregistered. 8474cc7b954SJohn Fastabend */ 8484cc7b954SJohn Fastabend rcu_read_lock(); 8494cc7b954SJohn Fastabend list_for_each_entry_rcu(dtab, &dev_map_list, list) { 850ce197d83SToke Høiland-Jørgensen if (dtab->map.map_type == BPF_MAP_TYPE_DEVMAP_HASH) { 851ce197d83SToke Høiland-Jørgensen dev_map_hash_remove_netdev(dtab, netdev); 852ce197d83SToke Høiland-Jørgensen continue; 853ce197d83SToke Høiland-Jørgensen } 854ce197d83SToke Høiland-Jørgensen 8552ddf71e2SJohn Fastabend for (i = 0; i < dtab->map.max_entries; i++) { 8564cc7b954SJohn Fastabend struct bpf_dtab_netdev *dev, *odev; 8572ddf71e2SJohn Fastabend 8584cc7b954SJohn Fastabend dev = READ_ONCE(dtab->netdev_map[i]); 859f592f804STaehee Yoo if (!dev || netdev != dev->dev) 8602ddf71e2SJohn Fastabend continue; 8614cc7b954SJohn Fastabend odev = cmpxchg(&dtab->netdev_map[i], dev, NULL); 8624cc7b954SJohn Fastabend if (dev == odev) 8632ddf71e2SJohn Fastabend call_rcu(&dev->rcu, 8642ddf71e2SJohn Fastabend __dev_map_entry_free); 8652ddf71e2SJohn Fastabend } 8662ddf71e2SJohn Fastabend } 8674cc7b954SJohn Fastabend rcu_read_unlock(); 8682ddf71e2SJohn Fastabend break; 8692ddf71e2SJohn Fastabend default: 8702ddf71e2SJohn Fastabend break; 8712ddf71e2SJohn Fastabend } 8722ddf71e2SJohn Fastabend return NOTIFY_OK; 8732ddf71e2SJohn Fastabend } 8742ddf71e2SJohn Fastabend 8752ddf71e2SJohn Fastabend static struct notifier_block dev_map_notifier = { 8762ddf71e2SJohn Fastabend .notifier_call = dev_map_notification, 8772ddf71e2SJohn Fastabend }; 8782ddf71e2SJohn Fastabend 8792ddf71e2SJohn Fastabend static int __init dev_map_init(void) 8802ddf71e2SJohn Fastabend { 88196360004SBjörn Töpel int cpu; 88296360004SBjörn Töpel 88367f29e07SJesper Dangaard Brouer /* Assure tracepoint shadow struct _bpf_dtab_netdev is in sync */ 88467f29e07SJesper Dangaard Brouer BUILD_BUG_ON(offsetof(struct bpf_dtab_netdev, dev) != 88567f29e07SJesper Dangaard Brouer offsetof(struct _bpf_dtab_netdev, dev)); 8862ddf71e2SJohn Fastabend register_netdevice_notifier(&dev_map_notifier); 88796360004SBjörn Töpel 88896360004SBjörn Töpel for_each_possible_cpu(cpu) 8891d233886SToke Høiland-Jørgensen INIT_LIST_HEAD(&per_cpu(dev_flush_list, cpu)); 8902ddf71e2SJohn Fastabend return 0; 8912ddf71e2SJohn Fastabend } 8922ddf71e2SJohn Fastabend 8932ddf71e2SJohn Fastabend subsys_initcall(dev_map_init); 894