118334Speter// SPDX-License-Identifier: GPL-2.0 218334Speter 318334Speter#include <stdio.h> 418334Speter#include <errno.h> 518334Speter#include <string.h> 6169689Skan#include <unistd.h> 718334Speter 890075Sobrien#include <bpf/bpf.h> 918334Speter#include <bpf/libbpf.h> 1090075Sobrien 1190075Sobrien#include <test_maps.h> 1290075Sobrien 1390075Sobrien#define OUTER_MAP_ENTRIES 10 1418334Speter 1590075Sobrienstatic __u32 get_map_id_from_fd(int map_fd) 1690075Sobrien{ 1790075Sobrien struct bpf_map_info map_info = {}; 1890075Sobrien uint32_t info_len = sizeof(map_info); 1918334Speter int ret; 2018334Speter 2190075Sobrien ret = bpf_map_get_info_by_fd(map_fd, &map_info, &info_len); 22169689Skan CHECK(ret < 0, "Finding map info failed", "error:%s\n", 23169689Skan strerror(errno)); 2418334Speter 2518334Speter return map_info.id; 2618334Speter} 2718334Speter 2818334Speter/* This creates number of OUTER_MAP_ENTRIES maps that will be stored 2918334Speter * in outer map and return the created map_fds 3018334Speter */ 3118334Speterstatic void create_inner_maps(enum bpf_map_type map_type, 3218334Speter __u32 *inner_map_fds) 3318334Speter{ 3418334Speter int map_fd, map_index, ret; 3518334Speter __u32 map_key = 0, map_id; 3618334Speter char map_name[16]; 3718334Speter 3818334Speter for (map_index = 0; map_index < OUTER_MAP_ENTRIES; map_index++) { 3918334Speter memset(map_name, 0, sizeof(map_name)); 4018334Speter snprintf(map_name, sizeof(map_name), "inner_map_fd_%d", map_index); 4118334Speter map_fd = bpf_map_create(map_type, map_name, sizeof(__u32), 4218334Speter sizeof(__u32), 1, NULL); 4318334Speter CHECK(map_fd < 0, 4418334Speter "inner bpf_map_create() failed", 4518334Speter "map_type=(%d) map_name(%s), error:%s\n", 4618334Speter map_type, map_name, strerror(errno)); 4718334Speter 4818334Speter /* keep track of the inner map fd as it is required 4918334Speter * to add records in outer map 5018334Speter */ 5118334Speter inner_map_fds[map_index] = map_fd; 5218334Speter 5318334Speter /* Add entry into this created map 5418334Speter * eg: map1 key = 0, value = map1's map id 5518334Speter * map2 key = 0, value = map2's map id 5618334Speter */ 5718334Speter map_id = get_map_id_from_fd(map_fd); 5818334Speter ret = bpf_map_update_elem(map_fd, &map_key, &map_id, 0); 5918334Speter CHECK(ret != 0, 6018334Speter "bpf_map_update_elem failed", 6118334Speter "map_type=(%d) map_name(%s), error:%s\n", 6218334Speter map_type, map_name, strerror(errno)); 6318334Speter } 6418334Speter} 6518334Speter 6618334Speterstatic int create_outer_map(enum bpf_map_type map_type, __u32 inner_map_fd) 6718334Speter{ 6818334Speter int outer_map_fd; 6918334Speter LIBBPF_OPTS(bpf_map_create_opts, attr); 7018334Speter 7118334Speter attr.inner_map_fd = inner_map_fd; 7218334Speter outer_map_fd = bpf_map_create(map_type, "outer_map", sizeof(__u32), 7318334Speter sizeof(__u32), OUTER_MAP_ENTRIES, 7418334Speter &attr); 7518334Speter CHECK(outer_map_fd < 0, 7618334Speter "outer bpf_map_create()", 7718334Speter "map_type=(%d), error:%s\n", 7818334Speter map_type, strerror(errno)); 7918334Speter 8018334Speter return outer_map_fd; 8118334Speter} 8218334Speter 8318334Speterstatic void validate_fetch_results(int outer_map_fd, 8418334Speter __u32 *fetched_keys, __u32 *fetched_values, 8518334Speter __u32 max_entries_fetched) 8618334Speter{ 8718334Speter __u32 inner_map_key, inner_map_value; 8818334Speter int inner_map_fd, entry, err; 8918334Speter __u32 outer_map_value; 9018334Speter 9118334Speter for (entry = 0; entry < max_entries_fetched; ++entry) { 9218334Speter outer_map_value = fetched_values[entry]; 9318334Speter inner_map_fd = bpf_map_get_fd_by_id(outer_map_value); 9418334Speter CHECK(inner_map_fd < 0, 9518334Speter "Failed to get inner map fd", 9618334Speter "from id(%d), error=%s\n", 9718334Speter outer_map_value, strerror(errno)); 9818334Speter err = bpf_map_get_next_key(inner_map_fd, NULL, &inner_map_key); 9918334Speter CHECK(err != 0, 10018334Speter "Failed to get inner map key", 10118334Speter "error=%s\n", strerror(errno)); 10218334Speter 10318334Speter err = bpf_map_lookup_elem(inner_map_fd, &inner_map_key, 10418334Speter &inner_map_value); 10518334Speter 10618334Speter close(inner_map_fd); 10718334Speter 10818334Speter CHECK(err != 0, 10918334Speter "Failed to get inner map value", 11018334Speter "for key(%d), error=%s\n", 11118334Speter inner_map_key, strerror(errno)); 11218334Speter 11318334Speter /* Actual value validation */ 11418334Speter CHECK(outer_map_value != inner_map_value, 11518334Speter "Failed to validate inner map value", 11618334Speter "fetched(%d) and lookedup(%d)!\n", 11718334Speter outer_map_value, inner_map_value); 11818334Speter } 11918334Speter} 12018334Speter 12118334Speterstatic void fetch_and_validate(int outer_map_fd, 12218334Speter struct bpf_map_batch_opts *opts, 12318334Speter __u32 batch_size, bool delete_entries) 12418334Speter{ 12518334Speter __u32 *fetched_keys, *fetched_values, total_fetched = 0; 12618334Speter __u32 batch_key = 0, fetch_count, step_size; 12718334Speter int err, max_entries = OUTER_MAP_ENTRIES; 12818334Speter __u32 value_size = sizeof(__u32); 12918334Speter 13018334Speter /* Total entries needs to be fetched */ 13118334Speter fetched_keys = calloc(max_entries, value_size); 13218334Speter fetched_values = calloc(max_entries, value_size); 13318334Speter CHECK((!fetched_keys || !fetched_values), 13418334Speter "Memory allocation failed for fetched_keys or fetched_values", 13518334Speter "error=%s\n", strerror(errno)); 13618334Speter 13718334Speter for (step_size = batch_size; 13818334Speter step_size <= max_entries; 13918334Speter step_size += batch_size) { 14018334Speter fetch_count = step_size; 14118334Speter err = delete_entries 14218334Speter ? bpf_map_lookup_and_delete_batch(outer_map_fd, 14318334Speter total_fetched ? &batch_key : NULL, 14418334Speter &batch_key, 14518334Speter fetched_keys + total_fetched, 14618334Speter fetched_values + total_fetched, 14718334Speter &fetch_count, opts) 14818334Speter : bpf_map_lookup_batch(outer_map_fd, 14918334Speter total_fetched ? &batch_key : NULL, 15018334Speter &batch_key, 15118334Speter fetched_keys + total_fetched, 15218334Speter fetched_values + total_fetched, 15318334Speter &fetch_count, opts); 15418334Speter 15518334Speter if (err && errno == ENOSPC) { 15618334Speter /* Fetch again with higher batch size */ 15718334Speter total_fetched = 0; 15818334Speter continue; 15918334Speter } 16018334Speter 16118334Speter CHECK((err < 0 && (errno != ENOENT)), 16218334Speter "lookup with steps failed", 16318334Speter "error: %s\n", strerror(errno)); 16418334Speter 16518334Speter /* Update the total fetched number */ 16618334Speter total_fetched += fetch_count; 16718334Speter if (err) 16818334Speter break; 16918334Speter } 17018334Speter 17118334Speter CHECK((total_fetched != max_entries), 17218334Speter "Unable to fetch expected entries !", 17318334Speter "total_fetched(%d) and max_entries(%d) error: (%d):%s\n", 17418334Speter total_fetched, max_entries, errno, strerror(errno)); 17518334Speter 17618334Speter /* validate the fetched entries */ 17718334Speter validate_fetch_results(outer_map_fd, fetched_keys, 17818334Speter fetched_values, total_fetched); 17918334Speter printf("batch_op(%s) is successful with batch_size(%d)\n", 18018334Speter delete_entries ? "LOOKUP_AND_DELETE" : "LOOKUP", batch_size); 18118334Speter 18218334Speter free(fetched_keys); 18350397Sobrien free(fetched_values); 18450397Sobrien} 18550397Sobrien 18650397Sobrienstatic void _map_in_map_batch_ops(enum bpf_map_type outer_map_type, 18750397Sobrien enum bpf_map_type inner_map_type) 18818334Speter{ 18918334Speter __u32 *outer_map_keys, *inner_map_fds; 19018334Speter __u32 max_entries = OUTER_MAP_ENTRIES; 19118334Speter LIBBPF_OPTS(bpf_map_batch_opts, opts); 19218334Speter __u32 value_size = sizeof(__u32); 19318334Speter int batch_size[2] = {5, 10}; 19418334Speter __u32 map_index, op_index; 19518334Speter int outer_map_fd, ret; 19618334Speter 19718334Speter outer_map_keys = calloc(max_entries, value_size); 19818334Speter inner_map_fds = calloc(max_entries, value_size); 19918334Speter CHECK((!outer_map_keys || !inner_map_fds), 20018334Speter "Memory allocation failed for outer_map_keys or inner_map_fds", 20118334Speter "error=%s\n", strerror(errno)); 20218334Speter 20318334Speter create_inner_maps(inner_map_type, inner_map_fds); 20418334Speter 20518334Speter outer_map_fd = create_outer_map(outer_map_type, *inner_map_fds); 20618334Speter /* create outer map keys */ 20718334Speter for (map_index = 0; map_index < max_entries; map_index++) 20818334Speter outer_map_keys[map_index] = 20918334Speter ((outer_map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS) 21018334Speter ? 9 : 1000) - map_index; 21118334Speter 21218334Speter /* batch operation - map_update */ 21318334Speter ret = bpf_map_update_batch(outer_map_fd, outer_map_keys, 21418334Speter inner_map_fds, &max_entries, &opts); 21518334Speter CHECK(ret != 0, 21618334Speter "Failed to update the outer map batch ops", 21718334Speter "error=%s\n", strerror(errno)); 21818334Speter 21918334Speter /* batch operation - map_lookup */ 22018334Speter for (op_index = 0; op_index < 2; ++op_index) 22118334Speter fetch_and_validate(outer_map_fd, &opts, 222169689Skan batch_size[op_index], false); 22318334Speter 22418334Speter /* batch operation - map_lookup_delete */ 22518334Speter if (outer_map_type == BPF_MAP_TYPE_HASH_OF_MAPS) 22618334Speter fetch_and_validate(outer_map_fd, &opts, 22718334Speter max_entries, true /*delete*/); 22818334Speter 22918334Speter /* close all map fds */ 23018334Speter for (map_index = 0; map_index < max_entries; map_index++) 23118334Speter close(inner_map_fds[map_index]); 23218334Speter close(outer_map_fd); 23318334Speter 23418334Speter free(inner_map_fds); 23518334Speter free(outer_map_keys); 23618334Speter} 23718334Speter 23818334Spetervoid test_map_in_map_batch_ops_array(void) 23918334Speter{ 24090075Sobrien _map_in_map_batch_ops(BPF_MAP_TYPE_ARRAY_OF_MAPS, BPF_MAP_TYPE_ARRAY); 24190075Sobrien printf("%s:PASS with inner ARRAY map\n", __func__); 24290075Sobrien _map_in_map_batch_ops(BPF_MAP_TYPE_ARRAY_OF_MAPS, BPF_MAP_TYPE_HASH); 24318334Speter printf("%s:PASS with inner HASH map\n", __func__); 24418334Speter} 24518334Speter 24618334Spetervoid test_map_in_map_batch_ops_hash(void) 24718334Speter{ 24818334Speter _map_in_map_batch_ops(BPF_MAP_TYPE_HASH_OF_MAPS, BPF_MAP_TYPE_ARRAY); 24918334Speter printf("%s:PASS with inner ARRAY map\n", __func__); 25018334Speter _map_in_map_batch_ops(BPF_MAP_TYPE_HASH_OF_MAPS, BPF_MAP_TYPE_HASH); 25118334Speter printf("%s:PASS with inner HASH map\n", __func__); 25218334Speter} 25318334Speter