Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /* Copyright (c) 2019 Facebook  */
0003 #include <stdio.h>
0004 #include <errno.h>
0005 #include <string.h>
0006 
0007 #include <bpf/bpf.h>
0008 #include <bpf/libbpf.h>
0009 
0010 #include <bpf_util.h>
0011 #include <test_maps.h>
0012 
0013 static void map_batch_update(int map_fd, __u32 max_entries, int *keys,
0014                  void *values, bool is_pcpu)
0015 {
0016     typedef BPF_DECLARE_PERCPU(int, value);
0017     value *v = NULL;
0018     int i, j, err;
0019     DECLARE_LIBBPF_OPTS(bpf_map_batch_opts, opts,
0020         .elem_flags = 0,
0021         .flags = 0,
0022     );
0023 
0024     if (is_pcpu)
0025         v = (value *)values;
0026 
0027     for (i = 0; i < max_entries; i++) {
0028         keys[i] = i + 1;
0029         if (is_pcpu)
0030             for (j = 0; j < bpf_num_possible_cpus(); j++)
0031                 bpf_percpu(v[i], j) = i + 2 + j;
0032         else
0033             ((int *)values)[i] = i + 2;
0034     }
0035 
0036     err = bpf_map_update_batch(map_fd, keys, values, &max_entries, &opts);
0037     CHECK(err, "bpf_map_update_batch()", "error:%s\n", strerror(errno));
0038 }
0039 
0040 static void map_batch_verify(int *visited, __u32 max_entries,
0041                  int *keys, void *values, bool is_pcpu)
0042 {
0043     typedef BPF_DECLARE_PERCPU(int, value);
0044     value *v = NULL;
0045     int i, j;
0046 
0047     if (is_pcpu)
0048         v = (value *)values;
0049 
0050     memset(visited, 0, max_entries * sizeof(*visited));
0051     for (i = 0; i < max_entries; i++) {
0052 
0053         if (is_pcpu) {
0054             for (j = 0; j < bpf_num_possible_cpus(); j++) {
0055                 CHECK(keys[i] + 1 + j != bpf_percpu(v[i], j),
0056                       "key/value checking",
0057                       "error: i %d j %d key %d value %d\n",
0058                       i, j, keys[i], bpf_percpu(v[i],  j));
0059             }
0060         } else {
0061             CHECK(keys[i] + 1 != ((int *)values)[i],
0062                   "key/value checking",
0063                   "error: i %d key %d value %d\n", i, keys[i],
0064                   ((int *)values)[i]);
0065         }
0066 
0067         visited[i] = 1;
0068 
0069     }
0070     for (i = 0; i < max_entries; i++) {
0071         CHECK(visited[i] != 1, "visited checking",
0072               "error: keys array at index %d missing\n", i);
0073     }
0074 }
0075 
0076 void __test_map_lookup_and_delete_batch(bool is_pcpu)
0077 {
0078     __u32 batch, count, total, total_success;
0079     typedef BPF_DECLARE_PERCPU(int, value);
0080     int map_fd, *keys, *visited, key;
0081     const __u32 max_entries = 10;
0082     value pcpu_values[max_entries];
0083     int err, step, value_size;
0084     bool nospace_err;
0085     void *values;
0086     DECLARE_LIBBPF_OPTS(bpf_map_batch_opts, opts,
0087         .elem_flags = 0,
0088         .flags = 0,
0089     );
0090 
0091     map_fd = bpf_map_create(is_pcpu ? BPF_MAP_TYPE_PERCPU_HASH : BPF_MAP_TYPE_HASH,
0092                 "hash_map", sizeof(int), sizeof(int), max_entries, NULL);
0093     CHECK(map_fd == -1,
0094           "bpf_map_create()", "error:%s\n", strerror(errno));
0095 
0096     value_size = is_pcpu ? sizeof(value) : sizeof(int);
0097     keys = malloc(max_entries * sizeof(int));
0098     if (is_pcpu)
0099         values = pcpu_values;
0100     else
0101         values = malloc(max_entries * sizeof(int));
0102     visited = malloc(max_entries * sizeof(int));
0103     CHECK(!keys || !values || !visited, "malloc()",
0104           "error:%s\n", strerror(errno));
0105 
0106     /* test 1: lookup/delete an empty hash table, -ENOENT */
0107     count = max_entries;
0108     err = bpf_map_lookup_and_delete_batch(map_fd, NULL, &batch, keys,
0109                           values, &count, &opts);
0110     CHECK((err && errno != ENOENT), "empty map",
0111           "error: %s\n", strerror(errno));
0112 
0113     /* populate elements to the map */
0114     map_batch_update(map_fd, max_entries, keys, values, is_pcpu);
0115 
0116     /* test 2: lookup/delete with count = 0, success */
0117     count = 0;
0118     err = bpf_map_lookup_and_delete_batch(map_fd, NULL, &batch, keys,
0119                           values, &count, &opts);
0120     CHECK(err, "count = 0", "error: %s\n", strerror(errno));
0121 
0122     /* test 3: lookup/delete with count = max_entries, success */
0123     memset(keys, 0, max_entries * sizeof(*keys));
0124     memset(values, 0, max_entries * value_size);
0125     count = max_entries;
0126     err = bpf_map_lookup_and_delete_batch(map_fd, NULL, &batch, keys,
0127                           values, &count, &opts);
0128     CHECK((err && errno != ENOENT), "count = max_entries",
0129            "error: %s\n", strerror(errno));
0130     CHECK(count != max_entries, "count = max_entries",
0131           "count = %u, max_entries = %u\n", count, max_entries);
0132     map_batch_verify(visited, max_entries, keys, values, is_pcpu);
0133 
0134     /* bpf_map_get_next_key() should return -ENOENT for an empty map. */
0135     err = bpf_map_get_next_key(map_fd, NULL, &key);
0136     CHECK(!err, "bpf_map_get_next_key()", "error: %s\n", strerror(errno));
0137 
0138     /* test 4: lookup/delete in a loop with various steps. */
0139     total_success = 0;
0140     for (step = 1; step < max_entries; step++) {
0141         map_batch_update(map_fd, max_entries, keys, values, is_pcpu);
0142         memset(keys, 0, max_entries * sizeof(*keys));
0143         memset(values, 0, max_entries * value_size);
0144         total = 0;
0145         /* iteratively lookup/delete elements with 'step'
0146          * elements each
0147          */
0148         count = step;
0149         nospace_err = false;
0150         while (true) {
0151             err = bpf_map_lookup_batch(map_fd,
0152                            total ? &batch : NULL,
0153                            &batch, keys + total,
0154                            values +
0155                            total * value_size,
0156                            &count, &opts);
0157             /* It is possible that we are failing due to buffer size
0158              * not big enough. In such cases, let us just exit and
0159              * go with large steps. Not that a buffer size with
0160              * max_entries should always work.
0161              */
0162             if (err && errno == ENOSPC) {
0163                 nospace_err = true;
0164                 break;
0165             }
0166 
0167             CHECK((err && errno != ENOENT), "lookup with steps",
0168                   "error: %s\n", strerror(errno));
0169 
0170             total += count;
0171             if (err)
0172                 break;
0173 
0174         }
0175         if (nospace_err == true)
0176             continue;
0177 
0178         CHECK(total != max_entries, "lookup with steps",
0179               "total = %u, max_entries = %u\n", total, max_entries);
0180         map_batch_verify(visited, max_entries, keys, values, is_pcpu);
0181 
0182         total = 0;
0183         count = step;
0184         while (total < max_entries) {
0185             if (max_entries - total < step)
0186                 count = max_entries - total;
0187             err = bpf_map_delete_batch(map_fd,
0188                            keys + total,
0189                            &count, &opts);
0190             CHECK((err && errno != ENOENT), "delete batch",
0191                   "error: %s\n", strerror(errno));
0192             total += count;
0193             if (err)
0194                 break;
0195         }
0196         CHECK(total != max_entries, "delete with steps",
0197               "total = %u, max_entries = %u\n", total, max_entries);
0198 
0199         /* check map is empty, errono == ENOENT */
0200         err = bpf_map_get_next_key(map_fd, NULL, &key);
0201         CHECK(!err || errno != ENOENT, "bpf_map_get_next_key()",
0202               "error: %s\n", strerror(errno));
0203 
0204         /* iteratively lookup/delete elements with 'step'
0205          * elements each
0206          */
0207         map_batch_update(map_fd, max_entries, keys, values, is_pcpu);
0208         memset(keys, 0, max_entries * sizeof(*keys));
0209         memset(values, 0, max_entries * value_size);
0210         total = 0;
0211         count = step;
0212         nospace_err = false;
0213         while (true) {
0214             err = bpf_map_lookup_and_delete_batch(map_fd,
0215                             total ? &batch : NULL,
0216                             &batch, keys + total,
0217                             values +
0218                             total * value_size,
0219                             &count, &opts);
0220             /* It is possible that we are failing due to buffer size
0221              * not big enough. In such cases, let us just exit and
0222              * go with large steps. Not that a buffer size with
0223              * max_entries should always work.
0224              */
0225             if (err && errno == ENOSPC) {
0226                 nospace_err = true;
0227                 break;
0228             }
0229 
0230             CHECK((err && errno != ENOENT), "lookup with steps",
0231                   "error: %s\n", strerror(errno));
0232 
0233             total += count;
0234             if (err)
0235                 break;
0236         }
0237 
0238         if (nospace_err == true)
0239             continue;
0240 
0241         CHECK(total != max_entries, "lookup/delete with steps",
0242               "total = %u, max_entries = %u\n", total, max_entries);
0243 
0244         map_batch_verify(visited, max_entries, keys, values, is_pcpu);
0245         err = bpf_map_get_next_key(map_fd, NULL, &key);
0246         CHECK(!err, "bpf_map_get_next_key()", "error: %s\n",
0247               strerror(errno));
0248 
0249         total_success++;
0250     }
0251 
0252     CHECK(total_success == 0, "check total_success",
0253           "unexpected failure\n");
0254     free(keys);
0255     free(visited);
0256     if (!is_pcpu)
0257         free(values);
0258 }
0259 
0260 void htab_map_batch_ops(void)
0261 {
0262     __test_map_lookup_and_delete_batch(false);
0263     printf("test_%s:PASS\n", __func__);
0264 }
0265 
0266 void htab_percpu_map_batch_ops(void)
0267 {
0268     __test_map_lookup_and_delete_batch(true);
0269     printf("test_%s:PASS\n", __func__);
0270 }
0271 
0272 void test_htab_map_batch_ops(void)
0273 {
0274     htab_map_batch_ops();
0275     htab_percpu_map_batch_ops();
0276 }