From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 49C4FC433F5 for ; Fri, 6 May 2022 01:03:11 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1387752AbiEFBGu (ORCPT ); Thu, 5 May 2022 21:06:50 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33866 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232783AbiEFBGt (ORCPT ); Thu, 5 May 2022 21:06:49 -0400 Received: from mx0a-00082601.pphosted.com (mx0b-00082601.pphosted.com [67.231.153.30]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id BED5B220C0 for ; Thu, 5 May 2022 18:03:07 -0700 (PDT) Received: from pps.filterd (m0001303.ppops.net [127.0.0.1]) by m0001303.ppops.net (8.17.1.5/8.17.1.5) with ESMTP id 2460mRkP030890 for ; Thu, 5 May 2022 18:03:07 -0700 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=fb.com; h=from : to : cc : subject : date : message-id : in-reply-to : references : mime-version : content-transfer-encoding : content-type; s=facebook; bh=t5aP8MQyUVPY9ZOiIJRUuQp+DHe4O2ev7Y3cMWHotFI=; b=Syv4Nl5auwoCgmy69cr/n/4S1ClANd7dO9Xm0Kc0ljgJOiu1CzIPUNN9AK6l7qannjUL 0Inuqa+vdP4rVXo/bztec0ggdsKG1mXSiAFCu+oTvvjImQ/tDw0rYi/gayrw/Yjf5OIG k2HSGHraW4gdLBVhzaDp+nR4agloeaMD9sc= Received: from mail.thefacebook.com ([163.114.132.120]) by m0001303.ppops.net (PPS) with ESMTPS id 3fuqp24693-5 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128 verify=NOT) for ; Thu, 05 May 2022 18:03:06 -0700 Received: from twshared16483.05.ash9.facebook.com (2620:10d:c085:208::11) by mail.thefacebook.com (2620:10d:c085:11d::5) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.2375.24; Thu, 5 May 2022 18:03:03 -0700 Received: by devvm2896.atn0.facebook.com (Postfix, from userid 153359) id 8E160148D3BED; Thu, 5 May 2022 18:00:52 -0700 (PDT) From: Takshak Chahande To: , CC: , , , , , , , , Subject: [PATCH bpf-next v5 2/2] selftests/bpf: handle batch operations for map-in-map bpf-maps Date: Thu, 5 May 2022 18:00:49 -0700 Message-ID: <20220506010049.1980482-2-ctakshak@fb.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20220506010049.1980482-1-ctakshak@fb.com> References: <20220506010049.1980482-1-ctakshak@fb.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-FB-Internal: Safe Content-Type: text/plain X-Proofpoint-ORIG-GUID: 50vhyp8DK50XjAjaQCiTRkv9wj7eXUbP X-Proofpoint-GUID: 50vhyp8DK50XjAjaQCiTRkv9wj7eXUbP X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.205,Aquarius:18.0.858,Hydra:6.0.486,FMLib:17.11.64.514 definitions=2022-05-05_10,2022-05-05_01,2022-02-23_01 Precedence: bulk List-ID: X-Mailing-List: bpf@vger.kernel.org This patch adds up test cases that handles 4 combinations: a) outer map: BPF_MAP_TYPE_ARRAY_OF_MAPS inner maps: BPF_MAP_TYPE_ARRAY and BPF_MAP_TYPE_HASH b) outer map: BPF_MAP_TYPE_HASH_OF_MAPS inner maps: BPF_MAP_TYPE_ARRAY and BPF_MAP_TYPE_HASH Signed-off-by: Takshak Chahande Acked-by: Yonghong Song --- .../bpf/map_tests/map_in_map_batch_ops.c | 250 ++++++++++++++++++ 1 file changed, 250 insertions(+) create mode 100644 tools/testing/selftests/bpf/map_tests/map_in_map_batc= h_ops.c v4->v5: - close all (inner and outer) map fds (Martin)=20 v3->v4: - Addressed nits; kept this map test together in map_tests/ (Yonghong, A= ndrii)=20 v2->v3: - Handled transient ENOSPC correctly, bug was found in BPF CI (Daniel) v1->v2: - Fixed no format arguments error (Andrii) diff --git a/tools/testing/selftests/bpf/map_tests/map_in_map_batch_ops.c= b/tools/testing/selftests/bpf/map_tests/map_in_map_batch_ops.c new file mode 100644 index 000000000000..62702101f4ba --- /dev/null +++ b/tools/testing/selftests/bpf/map_tests/map_in_map_batch_ops.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include +#include +#include +#include + +#include +#include + +#include + +#define OUTER_MAP_ENTRIES 10 + +static __u32 get_map_id_from_fd(int map_fd) +{ + struct bpf_map_info map_info =3D {}; + uint32_t info_len =3D sizeof(map_info); + int ret; + + ret =3D bpf_obj_get_info_by_fd(map_fd, &map_info, &info_len); + CHECK(ret < 0, "Finding map info failed", "error:%s\n", + strerror(errno)); + + return map_info.id; +} + +/* This creates number of OUTER_MAP_ENTRIES maps that will be stored + * in outer map and return the created map_fds + */ +static void create_inner_maps(enum bpf_map_type map_type, + __u32 *inner_map_fds) +{ + int map_fd, map_index, ret; + __u32 map_key =3D 0, map_id; + char map_name[15]; + + for (map_index =3D 0; map_index < OUTER_MAP_ENTRIES; map_index++) { + memset(map_name, 0, sizeof(map_name)); + sprintf(map_name, "inner_map_fd_%d", map_index); + map_fd =3D bpf_map_create(map_type, map_name, sizeof(__u32), + sizeof(__u32), 1, NULL); + CHECK(map_fd < 0, + "inner bpf_map_create() failed", + "map_type=3D(%d) map_name(%s), error:%s\n", + map_type, map_name, strerror(errno)); + + /* keep track of the inner map fd as it is required + * to add records in outer map + */ + inner_map_fds[map_index] =3D map_fd; + + /* Add entry into this created map + * eg: map1 key =3D 0, value =3D map1's map id + * map2 key =3D 0, value =3D map2's map id + */ + map_id =3D get_map_id_from_fd(map_fd); + ret =3D bpf_map_update_elem(map_fd, &map_key, &map_id, 0); + CHECK(ret !=3D 0, + "bpf_map_update_elem failed", + "map_type=3D(%d) map_name(%s), error:%s\n", + map_type, map_name, strerror(errno)); + } +} + +static int create_outer_map(enum bpf_map_type map_type, __u32 inner_map_= fd) +{ + int outer_map_fd; + LIBBPF_OPTS(bpf_map_create_opts, attr); + + attr.inner_map_fd =3D inner_map_fd; + outer_map_fd =3D bpf_map_create(map_type, "outer_map", sizeof(__u32), + sizeof(__u32), OUTER_MAP_ENTRIES, + &attr); + CHECK(outer_map_fd < 0, + "outer bpf_map_create()", + "map_type=3D(%d), error:%s\n", + map_type, strerror(errno)); + + return outer_map_fd; +} + +static void validate_fetch_results(int outer_map_fd, __u32 *inner_map_fd= s, + __u32 *fetched_keys, __u32 *fetched_values, + __u32 max_entries_fetched) +{ + __u32 inner_map_key, inner_map_value; + int inner_map_fd, entry, err; + __u32 outer_map_value; + + for (entry =3D 0; entry < max_entries_fetched; ++entry) { + outer_map_value =3D fetched_values[entry]; + inner_map_fd =3D bpf_map_get_fd_by_id(outer_map_value); + CHECK(inner_map_fd < 0, + "Failed to get inner map fd", + "from id(%d), error=3D%s\n", + outer_map_value, strerror(errno)); + err =3D bpf_map_get_next_key(inner_map_fd, NULL, &inner_map_key); + CHECK(err !=3D 0, + "Failed to get inner map key", + "error=3D%s\n", strerror(errno)); + + err =3D bpf_map_lookup_elem(inner_map_fd, &inner_map_key, + &inner_map_value); + CHECK(err !=3D 0, + "Failed to get inner map value", + "for key(%d), error=3D%s\n", + inner_map_key, strerror(errno)); + + /* Actual value validation */ + CHECK(outer_map_value !=3D inner_map_value, + "Failed to validate inner map value", + "fetched(%d) and lookedup(%d)!\n", + outer_map_value, inner_map_value); + } +} + +static void fetch_and_validate(int outer_map_fd, + __u32 *inner_map_fds, + struct bpf_map_batch_opts *opts, + __u32 batch_size, bool delete_entries) +{ + __u32 *fetched_keys, *fetched_values, total_fetched =3D 0; + __u32 batch_key =3D 0, fetch_count, step_size; + int err, max_entries =3D OUTER_MAP_ENTRIES; + __u32 value_size =3D sizeof(__u32); + + /* Total entries needs to be fetched */ + fetched_keys =3D calloc(max_entries, value_size); + fetched_values =3D calloc(max_entries, value_size); + CHECK((!fetched_keys || !fetched_values), + "Memory allocation failed for fetched_keys or fetched_values", + "error=3D%s\n", strerror(errno)); + + for (step_size =3D batch_size; + step_size <=3D max_entries; + step_size +=3D batch_size) { + fetch_count =3D step_size; + err =3D delete_entries + ? bpf_map_lookup_and_delete_batch(outer_map_fd, + total_fetched ? &batch_key : NULL, + &batch_key, + fetched_keys + total_fetched, + fetched_values + total_fetched, + &fetch_count, opts) + : bpf_map_lookup_batch(outer_map_fd, + total_fetched ? &batch_key : NULL, + &batch_key, + fetched_keys + total_fetched, + fetched_values + total_fetched, + &fetch_count, opts); + + if (err && errno =3D=3D ENOSPC) { + /* Fetch again with higher batch size */ + total_fetched =3D 0; + continue; + } + + CHECK((err < 0 && (errno !=3D ENOENT)), + "lookup with steps failed", + "error: %s\n", strerror(errno)); + + /* Update the total fetched number */ + total_fetched +=3D fetch_count; + if (err) + break; + } + + CHECK((total_fetched !=3D max_entries), + "Unable to fetch expected entries !", + "total_fetched(%d) and max_entries(%d) error: (%d):%s\n", + total_fetched, max_entries, errno, strerror(errno)); + + /* validate the fetched entries */ + validate_fetch_results(outer_map_fd, inner_map_fds, fetched_keys, + fetched_values, total_fetched); + printf("batch_op(%s) is successful with batch_size(%d)\n", + delete_entries ? "LOOKUP_AND_DELETE" : "LOOKUP", batch_size); + + free(fetched_keys); + free(fetched_values); +} + +static void _map_in_map_batch_ops(enum bpf_map_type outer_map_type, + enum bpf_map_type inner_map_type) +{ + __u32 *outer_map_keys, *inner_map_fds; + __u32 max_entries =3D OUTER_MAP_ENTRIES; + LIBBPF_OPTS(bpf_map_batch_opts, opts); + __u32 value_size =3D sizeof(__u32); + int batch_size[2] =3D {5, 10}; + __u32 map_index, op_index; + int outer_map_fd, ret; + + outer_map_keys =3D calloc(max_entries, value_size); + inner_map_fds =3D calloc(max_entries, value_size); + CHECK((!outer_map_keys || !inner_map_fds), + "Memory allocation failed for outer_map_keys or inner_map_fds", + "error=3D%s\n", strerror(errno)); + + create_inner_maps(inner_map_type, inner_map_fds); + + outer_map_fd =3D create_outer_map(outer_map_type, *inner_map_fds); + /* create outer map keys */ + for (map_index =3D 0; map_index < max_entries; map_index++) + outer_map_keys[map_index] =3D + ((outer_map_type =3D=3D BPF_MAP_TYPE_ARRAY_OF_MAPS) + ? 9 : 1000) - map_index; + + /* batch operation - map_update */ + ret =3D bpf_map_update_batch(outer_map_fd, outer_map_keys, + inner_map_fds, &max_entries, &opts); + CHECK(ret !=3D 0, + "Failed to update the outer map batch ops", + "error=3D%s\n", strerror(errno)); + + /* batch operation - map_lookup */ + for (op_index =3D 0; op_index < 2; ++op_index) + fetch_and_validate(outer_map_fd, inner_map_fds, &opts, + batch_size[op_index], false); + + /* batch operation - map_lookup_delete */ + if (outer_map_type =3D=3D BPF_MAP_TYPE_HASH_OF_MAPS) + fetch_and_validate(outer_map_fd, inner_map_fds, &opts, + max_entries, true /*delete*/); + + /* close all map fds */ + for (map_index =3D 0; map_index < max_entries; map_index++) + close(inner_map_fds[map_index]); + close(outer_map_fd); + + free(inner_map_fds); + free(outer_map_keys); +} + +void test_map_in_map_batch_ops_array(void) +{ + _map_in_map_batch_ops(BPF_MAP_TYPE_ARRAY_OF_MAPS, BPF_MAP_TYPE_ARRAY); + printf("%s:PASS with inner ARRAY map\n", __func__); + _map_in_map_batch_ops(BPF_MAP_TYPE_ARRAY_OF_MAPS, BPF_MAP_TYPE_HASH); + printf("%s:PASS with inner HASH map\n", __func__); +} + +void test_map_in_map_batch_ops_hash(void) +{ + _map_in_map_batch_ops(BPF_MAP_TYPE_HASH_OF_MAPS, BPF_MAP_TYPE_ARRAY); + printf("%s:PASS with inner ARRAY map\n", __func__); + _map_in_map_batch_ops(BPF_MAP_TYPE_HASH_OF_MAPS, BPF_MAP_TYPE_HASH); + printf("%s:PASS with inner HASH map\n", __func__); +} --=20 2.30.2