Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/bpf: Test CGROUP_STORAGE behavior on shared egress + ingress

This mirrors the original egress-only test. The cgroup_storage is
now extended to have two packet counters, one for egress and one
for ingress. We also extend to have two egress programs to test
that egress will always share with other egress origrams in the
same cgroup. The behavior of the counters are exactly the same as
the original egress-only test.

The test is split into two, one "isolated" test that when the key
type is struct bpf_cgroup_storage_key, which contains the attach
type, programs of different attach types will see different
storages. The other, "shared" test that when the key type is u64,
programs of different attach types will see the same storage if
they are attached to the same cgroup.

Signed-off-by: YiFei Zhu <zhuyifei@google.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://lore.kernel.org/bpf/c756f5f1521227b8e6e90a453299dda722d7324d.1595565795.git.zhuyifei@google.com

authored by

YiFei Zhu and committed by
Alexei Starovoitov
3573f384 7d9c3427

+312 -28
+240 -25
tools/testing/selftests/bpf/prog_tests/cg_storage_multi.c
··· 11 11 #include "progs/cg_storage_multi.h" 12 12 13 13 #include "cg_storage_multi_egress_only.skel.h" 14 - #include "cg_storage_multi_egress_ingress.skel.h" 14 + #include "cg_storage_multi_isolated.skel.h" 15 + #include "cg_storage_multi_shared.skel.h" 15 16 16 17 #define PARENT_CGROUP "/cgroup_storage" 17 18 #define CHILD_CGROUP "/cgroup_storage/child" 18 19 19 20 static int duration; 20 21 21 - static bool assert_storage(struct bpf_map *map, const char *cgroup_path, 22 + static bool assert_storage(struct bpf_map *map, const void *key, 22 23 struct cgroup_value *expected) 23 24 { 24 - struct bpf_cgroup_storage_key key = {0}; 25 25 struct cgroup_value value; 26 26 int map_fd; 27 27 28 28 map_fd = bpf_map__fd(map); 29 29 30 - key.cgroup_inode_id = get_cgroup_id(cgroup_path); 31 - key.attach_type = BPF_CGROUP_INET_EGRESS; 32 - if (CHECK(bpf_map_lookup_elem(map_fd, &key, &value) < 0, 30 + if (CHECK(bpf_map_lookup_elem(map_fd, key, &value) < 0, 33 31 "map-lookup", "errno %d", errno)) 34 32 return true; 35 33 if (CHECK(memcmp(&value, expected, sizeof(struct cgroup_value)), ··· 37 39 return false; 38 40 } 39 41 40 - static bool assert_storage_noexist(struct bpf_map *map, const char *cgroup_path) 42 + static bool assert_storage_noexist(struct bpf_map *map, const void *key) 41 43 { 42 - struct bpf_cgroup_storage_key key = {0}; 43 44 struct cgroup_value value; 44 45 int map_fd; 45 46 46 47 map_fd = bpf_map__fd(map); 47 48 48 - key.cgroup_inode_id = get_cgroup_id(cgroup_path); 49 - key.attach_type = BPF_CGROUP_INET_EGRESS; 50 - if (CHECK(bpf_map_lookup_elem(map_fd, &key, &value) == 0, 49 + if (CHECK(bpf_map_lookup_elem(map_fd, key, &value) == 0, 51 50 "map-lookup", "succeeded, expected ENOENT")) 52 51 return true; 53 52 if (CHECK(errno != ENOENT, ··· 85 90 { 86 91 struct cg_storage_multi_egress_only *obj; 87 92 struct cgroup_value expected_cgroup_value; 93 + struct bpf_cgroup_storage_key key; 88 94 struct bpf_link *parent_link = NULL, *child_link = NULL; 89 95 bool err; 96 + 97 + key.attach_type = BPF_CGROUP_INET_EGRESS; 90 98 91 99 obj = cg_storage_multi_egress_only__open_and_load(); 92 100 if (CHECK(!obj, "skel-load", "errno %d", errno)) ··· 111 113 if (CHECK(obj->bss->invocations != 1, 112 114 "first-invoke", "invocations=%d", obj->bss->invocations)) 113 115 goto close_bpf_object; 116 + key.cgroup_inode_id = get_cgroup_id(PARENT_CGROUP); 114 117 expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 1 }; 115 118 if (assert_storage(obj->maps.cgroup_storage, 116 - PARENT_CGROUP, &expected_cgroup_value)) 119 + &key, &expected_cgroup_value)) 117 120 goto close_bpf_object; 118 - if (assert_storage_noexist(obj->maps.cgroup_storage, CHILD_CGROUP)) 121 + key.cgroup_inode_id = get_cgroup_id(CHILD_CGROUP); 122 + if (assert_storage_noexist(obj->maps.cgroup_storage, &key)) 119 123 goto close_bpf_object; 120 124 121 125 /* Attach to parent and child cgroup, trigger packet from child. ··· 135 135 if (CHECK(obj->bss->invocations != 3, 136 136 "second-invoke", "invocations=%d", obj->bss->invocations)) 137 137 goto close_bpf_object; 138 + key.cgroup_inode_id = get_cgroup_id(PARENT_CGROUP); 138 139 expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 2 }; 139 140 if (assert_storage(obj->maps.cgroup_storage, 140 - PARENT_CGROUP, &expected_cgroup_value)) 141 + &key, &expected_cgroup_value)) 141 142 goto close_bpf_object; 143 + key.cgroup_inode_id = get_cgroup_id(CHILD_CGROUP); 142 144 expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 1 }; 143 145 if (assert_storage(obj->maps.cgroup_storage, 144 - CHILD_CGROUP, &expected_cgroup_value)) 146 + &key, &expected_cgroup_value)) 145 147 goto close_bpf_object; 146 148 147 149 close_bpf_object: ··· 153 151 cg_storage_multi_egress_only__destroy(obj); 154 152 } 155 153 156 - static void test_egress_ingress(int parent_cgroup_fd, int child_cgroup_fd) 154 + static void test_isolated(int parent_cgroup_fd, int child_cgroup_fd) 157 155 { 158 - struct cg_storage_multi_egress_ingress *obj; 156 + struct cg_storage_multi_isolated *obj; 157 + struct cgroup_value expected_cgroup_value; 158 + struct bpf_cgroup_storage_key key; 159 + struct bpf_link *parent_egress1_link = NULL, *parent_egress2_link = NULL; 160 + struct bpf_link *child_egress1_link = NULL, *child_egress2_link = NULL; 161 + struct bpf_link *parent_ingress_link = NULL, *child_ingress_link = NULL; 162 + bool err; 159 163 160 - /* Cannot load both programs due to verifier failure: 161 - * "only one cgroup storage of each type is allowed" 164 + obj = cg_storage_multi_isolated__open_and_load(); 165 + if (CHECK(!obj, "skel-load", "errno %d", errno)) 166 + return; 167 + 168 + /* Attach to parent cgroup, trigger packet from child. 169 + * Assert that there is three runs, two with parent cgroup egress and 170 + * one with parent cgroup ingress, stored in separate parent storages. 171 + * Also assert that child cgroup's storages does not exist 162 172 */ 163 - obj = cg_storage_multi_egress_ingress__open_and_load(); 164 - CHECK(obj || errno != EBUSY, 165 - "skel-load", "errno %d, expected EBUSY", errno); 173 + parent_egress1_link = bpf_program__attach_cgroup(obj->progs.egress1, 174 + parent_cgroup_fd); 175 + if (CHECK(IS_ERR(parent_egress1_link), "parent-egress1-cg-attach", 176 + "err %ld", PTR_ERR(parent_egress1_link))) 177 + goto close_bpf_object; 178 + parent_egress2_link = bpf_program__attach_cgroup(obj->progs.egress2, 179 + parent_cgroup_fd); 180 + if (CHECK(IS_ERR(parent_egress2_link), "parent-egress2-cg-attach", 181 + "err %ld", PTR_ERR(parent_egress2_link))) 182 + goto close_bpf_object; 183 + parent_ingress_link = bpf_program__attach_cgroup(obj->progs.ingress, 184 + parent_cgroup_fd); 185 + if (CHECK(IS_ERR(parent_ingress_link), "parent-ingress-cg-attach", 186 + "err %ld", PTR_ERR(parent_ingress_link))) 187 + goto close_bpf_object; 188 + err = connect_send(CHILD_CGROUP); 189 + if (CHECK(err, "first-connect-send", "errno %d", errno)) 190 + goto close_bpf_object; 191 + if (CHECK(obj->bss->invocations != 3, 192 + "first-invoke", "invocations=%d", obj->bss->invocations)) 193 + goto close_bpf_object; 194 + key.cgroup_inode_id = get_cgroup_id(PARENT_CGROUP); 195 + key.attach_type = BPF_CGROUP_INET_EGRESS; 196 + expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 2 }; 197 + if (assert_storage(obj->maps.cgroup_storage, 198 + &key, &expected_cgroup_value)) 199 + goto close_bpf_object; 200 + key.attach_type = BPF_CGROUP_INET_INGRESS; 201 + expected_cgroup_value = (struct cgroup_value) { .ingress_pkts = 1 }; 202 + if (assert_storage(obj->maps.cgroup_storage, 203 + &key, &expected_cgroup_value)) 204 + goto close_bpf_object; 205 + key.cgroup_inode_id = get_cgroup_id(CHILD_CGROUP); 206 + key.attach_type = BPF_CGROUP_INET_EGRESS; 207 + if (assert_storage_noexist(obj->maps.cgroup_storage, &key)) 208 + goto close_bpf_object; 209 + key.attach_type = BPF_CGROUP_INET_INGRESS; 210 + if (assert_storage_noexist(obj->maps.cgroup_storage, &key)) 211 + goto close_bpf_object; 166 212 167 - cg_storage_multi_egress_ingress__destroy(obj); 213 + /* Attach to parent and child cgroup, trigger packet from child. 214 + * Assert that there is six additional runs, parent cgroup egresses and 215 + * ingress, child cgroup egresses and ingress. 216 + * Assert that egree and ingress storages are separate. 217 + */ 218 + child_egress1_link = bpf_program__attach_cgroup(obj->progs.egress1, 219 + child_cgroup_fd); 220 + if (CHECK(IS_ERR(child_egress1_link), "child-egress1-cg-attach", 221 + "err %ld", PTR_ERR(child_egress1_link))) 222 + goto close_bpf_object; 223 + child_egress2_link = bpf_program__attach_cgroup(obj->progs.egress2, 224 + child_cgroup_fd); 225 + if (CHECK(IS_ERR(child_egress2_link), "child-egress2-cg-attach", 226 + "err %ld", PTR_ERR(child_egress2_link))) 227 + goto close_bpf_object; 228 + child_ingress_link = bpf_program__attach_cgroup(obj->progs.ingress, 229 + child_cgroup_fd); 230 + if (CHECK(IS_ERR(child_ingress_link), "child-ingress-cg-attach", 231 + "err %ld", PTR_ERR(child_ingress_link))) 232 + goto close_bpf_object; 233 + err = connect_send(CHILD_CGROUP); 234 + if (CHECK(err, "second-connect-send", "errno %d", errno)) 235 + goto close_bpf_object; 236 + if (CHECK(obj->bss->invocations != 9, 237 + "second-invoke", "invocations=%d", obj->bss->invocations)) 238 + goto close_bpf_object; 239 + key.cgroup_inode_id = get_cgroup_id(PARENT_CGROUP); 240 + key.attach_type = BPF_CGROUP_INET_EGRESS; 241 + expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 4 }; 242 + if (assert_storage(obj->maps.cgroup_storage, 243 + &key, &expected_cgroup_value)) 244 + goto close_bpf_object; 245 + key.attach_type = BPF_CGROUP_INET_INGRESS; 246 + expected_cgroup_value = (struct cgroup_value) { .ingress_pkts = 2 }; 247 + if (assert_storage(obj->maps.cgroup_storage, 248 + &key, &expected_cgroup_value)) 249 + goto close_bpf_object; 250 + key.cgroup_inode_id = get_cgroup_id(CHILD_CGROUP); 251 + key.attach_type = BPF_CGROUP_INET_EGRESS; 252 + expected_cgroup_value = (struct cgroup_value) { .egress_pkts = 2 }; 253 + if (assert_storage(obj->maps.cgroup_storage, 254 + &key, &expected_cgroup_value)) 255 + goto close_bpf_object; 256 + key.attach_type = BPF_CGROUP_INET_INGRESS; 257 + expected_cgroup_value = (struct cgroup_value) { .ingress_pkts = 1 }; 258 + if (assert_storage(obj->maps.cgroup_storage, 259 + &key, &expected_cgroup_value)) 260 + goto close_bpf_object; 261 + 262 + close_bpf_object: 263 + bpf_link__destroy(parent_egress1_link); 264 + bpf_link__destroy(parent_egress2_link); 265 + bpf_link__destroy(parent_ingress_link); 266 + bpf_link__destroy(child_egress1_link); 267 + bpf_link__destroy(child_egress2_link); 268 + bpf_link__destroy(child_ingress_link); 269 + 270 + cg_storage_multi_isolated__destroy(obj); 271 + } 272 + 273 + static void test_shared(int parent_cgroup_fd, int child_cgroup_fd) 274 + { 275 + struct cg_storage_multi_shared *obj; 276 + struct cgroup_value expected_cgroup_value; 277 + __u64 key; 278 + struct bpf_link *parent_egress1_link = NULL, *parent_egress2_link = NULL; 279 + struct bpf_link *child_egress1_link = NULL, *child_egress2_link = NULL; 280 + struct bpf_link *parent_ingress_link = NULL, *child_ingress_link = NULL; 281 + bool err; 282 + 283 + obj = cg_storage_multi_shared__open_and_load(); 284 + if (CHECK(!obj, "skel-load", "errno %d", errno)) 285 + return; 286 + 287 + /* Attach to parent cgroup, trigger packet from child. 288 + * Assert that there is three runs, two with parent cgroup egress and 289 + * one with parent cgroup ingress. 290 + * Also assert that child cgroup's storage does not exist 291 + */ 292 + parent_egress1_link = bpf_program__attach_cgroup(obj->progs.egress1, 293 + parent_cgroup_fd); 294 + if (CHECK(IS_ERR(parent_egress1_link), "parent-egress1-cg-attach", 295 + "err %ld", PTR_ERR(parent_egress1_link))) 296 + goto close_bpf_object; 297 + parent_egress2_link = bpf_program__attach_cgroup(obj->progs.egress2, 298 + parent_cgroup_fd); 299 + if (CHECK(IS_ERR(parent_egress2_link), "parent-egress2-cg-attach", 300 + "err %ld", PTR_ERR(parent_egress2_link))) 301 + goto close_bpf_object; 302 + parent_ingress_link = bpf_program__attach_cgroup(obj->progs.ingress, 303 + parent_cgroup_fd); 304 + if (CHECK(IS_ERR(parent_ingress_link), "parent-ingress-cg-attach", 305 + "err %ld", PTR_ERR(parent_ingress_link))) 306 + goto close_bpf_object; 307 + err = connect_send(CHILD_CGROUP); 308 + if (CHECK(err, "first-connect-send", "errno %d", errno)) 309 + goto close_bpf_object; 310 + if (CHECK(obj->bss->invocations != 3, 311 + "first-invoke", "invocations=%d", obj->bss->invocations)) 312 + goto close_bpf_object; 313 + key = get_cgroup_id(PARENT_CGROUP); 314 + expected_cgroup_value = (struct cgroup_value) { 315 + .egress_pkts = 2, 316 + .ingress_pkts = 1, 317 + }; 318 + if (assert_storage(obj->maps.cgroup_storage, 319 + &key, &expected_cgroup_value)) 320 + goto close_bpf_object; 321 + key = get_cgroup_id(CHILD_CGROUP); 322 + if (assert_storage_noexist(obj->maps.cgroup_storage, &key)) 323 + goto close_bpf_object; 324 + 325 + /* Attach to parent and child cgroup, trigger packet from child. 326 + * Assert that there is six additional runs, parent cgroup egresses and 327 + * ingress, child cgroup egresses and ingress. 328 + */ 329 + child_egress1_link = bpf_program__attach_cgroup(obj->progs.egress1, 330 + child_cgroup_fd); 331 + if (CHECK(IS_ERR(child_egress1_link), "child-egress1-cg-attach", 332 + "err %ld", PTR_ERR(child_egress1_link))) 333 + goto close_bpf_object; 334 + child_egress2_link = bpf_program__attach_cgroup(obj->progs.egress2, 335 + child_cgroup_fd); 336 + if (CHECK(IS_ERR(child_egress2_link), "child-egress2-cg-attach", 337 + "err %ld", PTR_ERR(child_egress2_link))) 338 + goto close_bpf_object; 339 + child_ingress_link = bpf_program__attach_cgroup(obj->progs.ingress, 340 + child_cgroup_fd); 341 + if (CHECK(IS_ERR(child_ingress_link), "child-ingress-cg-attach", 342 + "err %ld", PTR_ERR(child_ingress_link))) 343 + goto close_bpf_object; 344 + err = connect_send(CHILD_CGROUP); 345 + if (CHECK(err, "second-connect-send", "errno %d", errno)) 346 + goto close_bpf_object; 347 + if (CHECK(obj->bss->invocations != 9, 348 + "second-invoke", "invocations=%d", obj->bss->invocations)) 349 + goto close_bpf_object; 350 + key = get_cgroup_id(PARENT_CGROUP); 351 + expected_cgroup_value = (struct cgroup_value) { 352 + .egress_pkts = 4, 353 + .ingress_pkts = 2, 354 + }; 355 + if (assert_storage(obj->maps.cgroup_storage, 356 + &key, &expected_cgroup_value)) 357 + goto close_bpf_object; 358 + key = get_cgroup_id(CHILD_CGROUP); 359 + expected_cgroup_value = (struct cgroup_value) { 360 + .egress_pkts = 2, 361 + .ingress_pkts = 1, 362 + }; 363 + if (assert_storage(obj->maps.cgroup_storage, 364 + &key, &expected_cgroup_value)) 365 + goto close_bpf_object; 366 + 367 + close_bpf_object: 368 + bpf_link__destroy(parent_egress1_link); 369 + bpf_link__destroy(parent_egress2_link); 370 + bpf_link__destroy(parent_ingress_link); 371 + bpf_link__destroy(child_egress1_link); 372 + bpf_link__destroy(child_egress2_link); 373 + bpf_link__destroy(child_ingress_link); 374 + 375 + cg_storage_multi_shared__destroy(obj); 168 376 } 169 377 170 378 void test_cg_storage_multi(void) ··· 391 179 if (test__start_subtest("egress_only")) 392 180 test_egress_only(parent_cgroup_fd, child_cgroup_fd); 393 181 394 - if (test__start_subtest("egress_ingress")) 395 - test_egress_ingress(parent_cgroup_fd, child_cgroup_fd); 182 + if (test__start_subtest("isolated")) 183 + test_isolated(parent_cgroup_fd, child_cgroup_fd); 184 + 185 + if (test__start_subtest("shared")) 186 + test_shared(parent_cgroup_fd, child_cgroup_fd); 396 187 397 188 close_cgroup_fd: 398 189 close(child_cgroup_fd);
+15 -3
tools/testing/selftests/bpf/progs/cg_storage_multi_egress_ingress.c tools/testing/selftests/bpf/progs/cg_storage_multi_shared.c
··· 14 14 15 15 struct { 16 16 __uint(type, BPF_MAP_TYPE_CGROUP_STORAGE); 17 - __type(key, struct bpf_cgroup_storage_key); 17 + __type(key, __u64); 18 18 __type(value, struct cgroup_value); 19 19 } cgroup_storage SEC(".maps"); 20 20 21 21 __u32 invocations = 0; 22 22 23 - SEC("cgroup_skb/egress") 24 - int egress(struct __sk_buff *skb) 23 + SEC("cgroup_skb/egress/1") 24 + int egress1(struct __sk_buff *skb) 25 + { 26 + struct cgroup_value *ptr_cg_storage = 27 + bpf_get_local_storage(&cgroup_storage, 0); 28 + 29 + __sync_fetch_and_add(&ptr_cg_storage->egress_pkts, 1); 30 + __sync_fetch_and_add(&invocations, 1); 31 + 32 + return 1; 33 + } 34 + 35 + SEC("cgroup_skb/egress/2") 36 + int egress2(struct __sk_buff *skb) 25 37 { 26 38 struct cgroup_value *ptr_cg_storage = 27 39 bpf_get_local_storage(&cgroup_storage, 0);
+57
tools/testing/selftests/bpf/progs/cg_storage_multi_isolated.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + 3 + /* 4 + * Copyright 2020 Google LLC. 5 + */ 6 + 7 + #include <errno.h> 8 + #include <linux/bpf.h> 9 + #include <linux/ip.h> 10 + #include <linux/udp.h> 11 + #include <bpf/bpf_helpers.h> 12 + 13 + #include "progs/cg_storage_multi.h" 14 + 15 + struct { 16 + __uint(type, BPF_MAP_TYPE_CGROUP_STORAGE); 17 + __type(key, struct bpf_cgroup_storage_key); 18 + __type(value, struct cgroup_value); 19 + } cgroup_storage SEC(".maps"); 20 + 21 + __u32 invocations = 0; 22 + 23 + SEC("cgroup_skb/egress/1") 24 + int egress1(struct __sk_buff *skb) 25 + { 26 + struct cgroup_value *ptr_cg_storage = 27 + bpf_get_local_storage(&cgroup_storage, 0); 28 + 29 + __sync_fetch_and_add(&ptr_cg_storage->egress_pkts, 1); 30 + __sync_fetch_and_add(&invocations, 1); 31 + 32 + return 1; 33 + } 34 + 35 + SEC("cgroup_skb/egress/2") 36 + int egress2(struct __sk_buff *skb) 37 + { 38 + struct cgroup_value *ptr_cg_storage = 39 + bpf_get_local_storage(&cgroup_storage, 0); 40 + 41 + __sync_fetch_and_add(&ptr_cg_storage->egress_pkts, 1); 42 + __sync_fetch_and_add(&invocations, 1); 43 + 44 + return 1; 45 + } 46 + 47 + SEC("cgroup_skb/ingress") 48 + int ingress(struct __sk_buff *skb) 49 + { 50 + struct cgroup_value *ptr_cg_storage = 51 + bpf_get_local_storage(&cgroup_storage, 0); 52 + 53 + __sync_fetch_and_add(&ptr_cg_storage->ingress_pkts, 1); 54 + __sync_fetch_and_add(&invocations, 1); 55 + 56 + return 1; 57 + }