diff options
Diffstat (limited to 'kernel/bpf/devmap.c')
-rw-r--r-- | kernel/bpf/devmap.c | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c new file mode 100644 index 000000000000..1a878356bd37 --- /dev/null +++ b/kernel/bpf/devmap.c | |||
@@ -0,0 +1,264 @@ | |||
1 | /* Copyright (c) 2017 Covalent IO, Inc. http://covalent.io | ||
2 | * | ||
3 | * This program is free software; you can redistribute it and/or | ||
4 | * modify it under the terms of version 2 of the GNU General Public | ||
5 | * License as published by the Free Software Foundation. | ||
6 | * | ||
7 | * This program is distributed in the hope that it will be useful, but | ||
8 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
10 | * General Public License for more details. | ||
11 | */ | ||
12 | |||
13 | /* Devmaps primary use is as a backend map for XDP BPF helper call | ||
14 | * bpf_redirect_map(). Because XDP is mostly concerned with performance we | ||
15 | * spent some effort to ensure the datapath with redirect maps does not use | ||
16 | * any locking. This is a quick note on the details. | ||
17 | * | ||
18 | * We have three possible paths to get into the devmap control plane bpf | ||
19 | * syscalls, bpf programs, and driver side xmit/flush operations. A bpf syscall | ||
20 | * will invoke an update, delete, or lookup operation. To ensure updates and | ||
21 | * deletes appear atomic from the datapath side xchg() is used to modify the | ||
22 | * netdev_map array. Then because the datapath does a lookup into the netdev_map | ||
23 | * array (read-only) from an RCU critical section we use call_rcu() to wait for | ||
24 | * an rcu grace period before free'ing the old data structures. This ensures the | ||
25 | * datapath always has a valid copy. However, the datapath does a "flush" | ||
26 | * operation that pushes any pending packets in the driver outside the RCU | ||
27 | * critical section. Each bpf_dtab_netdev tracks these pending operations using | ||
28 | * an atomic per-cpu bitmap. The bpf_dtab_netdev object will not be destroyed | ||
29 | * until all bits are cleared indicating outstanding flush operations have | ||
30 | * completed. | ||
31 | * | ||
32 | * BPF syscalls may race with BPF program calls on any of the update, delete | ||
33 | * or lookup operations. As noted above the xchg() operation also keep the | ||
34 | * netdev_map consistent in this case. From the devmap side BPF programs | ||
35 | * calling into these operations are the same as multiple user space threads | ||
36 | * making system calls. | ||
37 | */ | ||
38 | #include <linux/bpf.h> | ||
39 | #include <linux/jhash.h> | ||
40 | #include <linux/filter.h> | ||
41 | #include <linux/rculist_nulls.h> | ||
42 | #include "percpu_freelist.h" | ||
43 | #include "bpf_lru_list.h" | ||
44 | #include "map_in_map.h" | ||
45 | |||
46 | struct bpf_dtab_netdev { | ||
47 | struct net_device *dev; | ||
48 | int key; | ||
49 | struct rcu_head rcu; | ||
50 | struct bpf_dtab *dtab; | ||
51 | }; | ||
52 | |||
53 | struct bpf_dtab { | ||
54 | struct bpf_map map; | ||
55 | struct bpf_dtab_netdev **netdev_map; | ||
56 | }; | ||
57 | |||
58 | static struct bpf_map *dev_map_alloc(union bpf_attr *attr) | ||
59 | { | ||
60 | struct bpf_dtab *dtab; | ||
61 | u64 cost; | ||
62 | int err; | ||
63 | |||
64 | /* check sanity of attributes */ | ||
65 | if (attr->max_entries == 0 || attr->key_size != 4 || | ||
66 | attr->value_size != 4 || attr->map_flags) | ||
67 | return ERR_PTR(-EINVAL); | ||
68 | |||
69 | /* if value_size is bigger, the user space won't be able to | ||
70 | * access the elements. | ||
71 | */ | ||
72 | if (attr->value_size > KMALLOC_MAX_SIZE) | ||
73 | return ERR_PTR(-E2BIG); | ||
74 | |||
75 | dtab = kzalloc(sizeof(*dtab), GFP_USER); | ||
76 | if (!dtab) | ||
77 | return ERR_PTR(-ENOMEM); | ||
78 | |||
79 | /* mandatory map attributes */ | ||
80 | dtab->map.map_type = attr->map_type; | ||
81 | dtab->map.key_size = attr->key_size; | ||
82 | dtab->map.value_size = attr->value_size; | ||
83 | dtab->map.max_entries = attr->max_entries; | ||
84 | dtab->map.map_flags = attr->map_flags; | ||
85 | |||
86 | err = -ENOMEM; | ||
87 | |||
88 | /* make sure page count doesn't overflow */ | ||
89 | cost = (u64) dtab->map.max_entries * sizeof(struct bpf_dtab_netdev *); | ||
90 | if (cost >= U32_MAX - PAGE_SIZE) | ||
91 | goto free_dtab; | ||
92 | |||
93 | dtab->map.pages = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT; | ||
94 | |||
95 | /* if map size is larger than memlock limit, reject it early */ | ||
96 | err = bpf_map_precharge_memlock(dtab->map.pages); | ||
97 | if (err) | ||
98 | goto free_dtab; | ||
99 | |||
100 | dtab->netdev_map = bpf_map_area_alloc(dtab->map.max_entries * | ||
101 | sizeof(struct bpf_dtab_netdev *)); | ||
102 | if (!dtab->netdev_map) | ||
103 | goto free_dtab; | ||
104 | |||
105 | return &dtab->map; | ||
106 | |||
107 | free_dtab: | ||
108 | kfree(dtab); | ||
109 | return ERR_PTR(err); | ||
110 | } | ||
111 | |||
112 | static void dev_map_free(struct bpf_map *map) | ||
113 | { | ||
114 | struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); | ||
115 | int i; | ||
116 | |||
117 | /* At this point bpf_prog->aux->refcnt == 0 and this map->refcnt == 0, | ||
118 | * so the programs (can be more than one that used this map) were | ||
119 | * disconnected from events. Wait for outstanding critical sections in | ||
120 | * these programs to complete. The rcu critical section only guarantees | ||
121 | * no further reads against netdev_map. It does __not__ ensure pending | ||
122 | * flush operations (if any) are complete. | ||
123 | */ | ||
124 | synchronize_rcu(); | ||
125 | |||
126 | for (i = 0; i < dtab->map.max_entries; i++) { | ||
127 | struct bpf_dtab_netdev *dev; | ||
128 | |||
129 | dev = dtab->netdev_map[i]; | ||
130 | if (!dev) | ||
131 | continue; | ||
132 | |||
133 | dev_put(dev->dev); | ||
134 | kfree(dev); | ||
135 | } | ||
136 | |||
137 | /* At this point bpf program is detached and all pending operations | ||
138 | * _must_ be complete | ||
139 | */ | ||
140 | bpf_map_area_free(dtab->netdev_map); | ||
141 | kfree(dtab); | ||
142 | } | ||
143 | |||
144 | static int dev_map_get_next_key(struct bpf_map *map, void *key, void *next_key) | ||
145 | { | ||
146 | struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); | ||
147 | u32 index = key ? *(u32 *)key : U32_MAX; | ||
148 | u32 *next = (u32 *)next_key; | ||
149 | |||
150 | if (index >= dtab->map.max_entries) { | ||
151 | *next = 0; | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | if (index == dtab->map.max_entries - 1) | ||
156 | return -ENOENT; | ||
157 | |||
158 | *next = index + 1; | ||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | /* rcu_read_lock (from syscall and BPF contexts) ensures that if a delete and/or | ||
163 | * update happens in parallel here a dev_put wont happen until after reading the | ||
164 | * ifindex. | ||
165 | */ | ||
166 | static void *dev_map_lookup_elem(struct bpf_map *map, void *key) | ||
167 | { | ||
168 | struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); | ||
169 | struct bpf_dtab_netdev *dev; | ||
170 | u32 i = *(u32 *)key; | ||
171 | |||
172 | if (i >= map->max_entries) | ||
173 | return NULL; | ||
174 | |||
175 | dev = READ_ONCE(dtab->netdev_map[i]); | ||
176 | return dev ? &dev->dev->ifindex : NULL; | ||
177 | } | ||
178 | |||
179 | static void __dev_map_entry_free(struct rcu_head *rcu) | ||
180 | { | ||
181 | struct bpf_dtab_netdev *old_dev; | ||
182 | |||
183 | old_dev = container_of(rcu, struct bpf_dtab_netdev, rcu); | ||
184 | dev_put(old_dev->dev); | ||
185 | kfree(old_dev); | ||
186 | } | ||
187 | |||
188 | static int dev_map_delete_elem(struct bpf_map *map, void *key) | ||
189 | { | ||
190 | struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); | ||
191 | struct bpf_dtab_netdev *old_dev; | ||
192 | int k = *(u32 *)key; | ||
193 | |||
194 | if (k >= map->max_entries) | ||
195 | return -EINVAL; | ||
196 | |||
197 | /* Use synchronize_rcu() here to ensure any rcu critical sections | ||
198 | * have completed, but this does not guarantee a flush has happened | ||
199 | * yet. Because driver side rcu_read_lock/unlock only protects the | ||
200 | * running XDP program. However, for pending flush operations the | ||
201 | * dev and ctx are stored in another per cpu map. And additionally, | ||
202 | * the driver tear down ensures all soft irqs are complete before | ||
203 | * removing the net device in the case of dev_put equals zero. | ||
204 | */ | ||
205 | old_dev = xchg(&dtab->netdev_map[k], NULL); | ||
206 | if (old_dev) | ||
207 | call_rcu(&old_dev->rcu, __dev_map_entry_free); | ||
208 | return 0; | ||
209 | } | ||
210 | |||
211 | static int dev_map_update_elem(struct bpf_map *map, void *key, void *value, | ||
212 | u64 map_flags) | ||
213 | { | ||
214 | struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); | ||
215 | struct net *net = current->nsproxy->net_ns; | ||
216 | struct bpf_dtab_netdev *dev, *old_dev; | ||
217 | u32 i = *(u32 *)key; | ||
218 | u32 ifindex = *(u32 *)value; | ||
219 | |||
220 | if (unlikely(map_flags > BPF_EXIST)) | ||
221 | return -EINVAL; | ||
222 | |||
223 | if (unlikely(i >= dtab->map.max_entries)) | ||
224 | return -E2BIG; | ||
225 | |||
226 | if (unlikely(map_flags == BPF_NOEXIST)) | ||
227 | return -EEXIST; | ||
228 | |||
229 | if (!ifindex) { | ||
230 | dev = NULL; | ||
231 | } else { | ||
232 | dev = kmalloc(sizeof(*dev), GFP_ATOMIC | __GFP_NOWARN); | ||
233 | if (!dev) | ||
234 | return -ENOMEM; | ||
235 | |||
236 | dev->dev = dev_get_by_index(net, ifindex); | ||
237 | if (!dev->dev) { | ||
238 | kfree(dev); | ||
239 | return -EINVAL; | ||
240 | } | ||
241 | |||
242 | dev->key = i; | ||
243 | dev->dtab = dtab; | ||
244 | } | ||
245 | |||
246 | /* Use call_rcu() here to ensure rcu critical sections have completed | ||
247 | * Remembering the driver side flush operation will happen before the | ||
248 | * net device is removed. | ||
249 | */ | ||
250 | old_dev = xchg(&dtab->netdev_map[i], dev); | ||
251 | if (old_dev) | ||
252 | call_rcu(&old_dev->rcu, __dev_map_entry_free); | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | const struct bpf_map_ops dev_map_ops = { | ||
258 | .map_alloc = dev_map_alloc, | ||
259 | .map_free = dev_map_free, | ||
260 | .map_get_next_key = dev_map_get_next_key, | ||
261 | .map_lookup_elem = dev_map_lookup_elem, | ||
262 | .map_update_elem = dev_map_update_elem, | ||
263 | .map_delete_elem = dev_map_delete_elem, | ||
264 | }; | ||