cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

htab_map_batch_ops.c (7751B)


      1// SPDX-License-Identifier: GPL-2.0
      2/* Copyright (c) 2019 Facebook  */
      3#include <stdio.h>
      4#include <errno.h>
      5#include <string.h>
      6
      7#include <bpf/bpf.h>
      8#include <bpf/libbpf.h>
      9
     10#include <bpf_util.h>
     11#include <test_maps.h>
     12
     13static void map_batch_update(int map_fd, __u32 max_entries, int *keys,
     14			     void *values, bool is_pcpu)
     15{
     16	typedef BPF_DECLARE_PERCPU(int, value);
     17	value *v = NULL;
     18	int i, j, err;
     19	DECLARE_LIBBPF_OPTS(bpf_map_batch_opts, opts,
     20		.elem_flags = 0,
     21		.flags = 0,
     22	);
     23
     24	if (is_pcpu)
     25		v = (value *)values;
     26
     27	for (i = 0; i < max_entries; i++) {
     28		keys[i] = i + 1;
     29		if (is_pcpu)
     30			for (j = 0; j < bpf_num_possible_cpus(); j++)
     31				bpf_percpu(v[i], j) = i + 2 + j;
     32		else
     33			((int *)values)[i] = i + 2;
     34	}
     35
     36	err = bpf_map_update_batch(map_fd, keys, values, &max_entries, &opts);
     37	CHECK(err, "bpf_map_update_batch()", "error:%s\n", strerror(errno));
     38}
     39
     40static void map_batch_verify(int *visited, __u32 max_entries,
     41			     int *keys, void *values, bool is_pcpu)
     42{
     43	typedef BPF_DECLARE_PERCPU(int, value);
     44	value *v = NULL;
     45	int i, j;
     46
     47	if (is_pcpu)
     48		v = (value *)values;
     49
     50	memset(visited, 0, max_entries * sizeof(*visited));
     51	for (i = 0; i < max_entries; i++) {
     52
     53		if (is_pcpu) {
     54			for (j = 0; j < bpf_num_possible_cpus(); j++) {
     55				CHECK(keys[i] + 1 + j != bpf_percpu(v[i], j),
     56				      "key/value checking",
     57				      "error: i %d j %d key %d value %d\n",
     58				      i, j, keys[i], bpf_percpu(v[i],  j));
     59			}
     60		} else {
     61			CHECK(keys[i] + 1 != ((int *)values)[i],
     62			      "key/value checking",
     63			      "error: i %d key %d value %d\n", i, keys[i],
     64			      ((int *)values)[i]);
     65		}
     66
     67		visited[i] = 1;
     68
     69	}
     70	for (i = 0; i < max_entries; i++) {
     71		CHECK(visited[i] != 1, "visited checking",
     72		      "error: keys array at index %d missing\n", i);
     73	}
     74}
     75
     76void __test_map_lookup_and_delete_batch(bool is_pcpu)
     77{
     78	__u32 batch, count, total, total_success;
     79	typedef BPF_DECLARE_PERCPU(int, value);
     80	int map_fd, *keys, *visited, key;
     81	const __u32 max_entries = 10;
     82	value pcpu_values[max_entries];
     83	int err, step, value_size;
     84	bool nospace_err;
     85	void *values;
     86	DECLARE_LIBBPF_OPTS(bpf_map_batch_opts, opts,
     87		.elem_flags = 0,
     88		.flags = 0,
     89	);
     90
     91	map_fd = bpf_map_create(is_pcpu ? BPF_MAP_TYPE_PERCPU_HASH : BPF_MAP_TYPE_HASH,
     92				"hash_map", sizeof(int), sizeof(int), max_entries, NULL);
     93	CHECK(map_fd == -1,
     94	      "bpf_map_create()", "error:%s\n", strerror(errno));
     95
     96	value_size = is_pcpu ? sizeof(value) : sizeof(int);
     97	keys = malloc(max_entries * sizeof(int));
     98	if (is_pcpu)
     99		values = pcpu_values;
    100	else
    101		values = malloc(max_entries * sizeof(int));
    102	visited = malloc(max_entries * sizeof(int));
    103	CHECK(!keys || !values || !visited, "malloc()",
    104	      "error:%s\n", strerror(errno));
    105
    106	/* test 1: lookup/delete an empty hash table, -ENOENT */
    107	count = max_entries;
    108	err = bpf_map_lookup_and_delete_batch(map_fd, NULL, &batch, keys,
    109					      values, &count, &opts);
    110	CHECK((err && errno != ENOENT), "empty map",
    111	      "error: %s\n", strerror(errno));
    112
    113	/* populate elements to the map */
    114	map_batch_update(map_fd, max_entries, keys, values, is_pcpu);
    115
    116	/* test 2: lookup/delete with count = 0, success */
    117	count = 0;
    118	err = bpf_map_lookup_and_delete_batch(map_fd, NULL, &batch, keys,
    119					      values, &count, &opts);
    120	CHECK(err, "count = 0", "error: %s\n", strerror(errno));
    121
    122	/* test 3: lookup/delete with count = max_entries, success */
    123	memset(keys, 0, max_entries * sizeof(*keys));
    124	memset(values, 0, max_entries * value_size);
    125	count = max_entries;
    126	err = bpf_map_lookup_and_delete_batch(map_fd, NULL, &batch, keys,
    127					      values, &count, &opts);
    128	CHECK((err && errno != ENOENT), "count = max_entries",
    129	       "error: %s\n", strerror(errno));
    130	CHECK(count != max_entries, "count = max_entries",
    131	      "count = %u, max_entries = %u\n", count, max_entries);
    132	map_batch_verify(visited, max_entries, keys, values, is_pcpu);
    133
    134	/* bpf_map_get_next_key() should return -ENOENT for an empty map. */
    135	err = bpf_map_get_next_key(map_fd, NULL, &key);
    136	CHECK(!err, "bpf_map_get_next_key()", "error: %s\n", strerror(errno));
    137
    138	/* test 4: lookup/delete in a loop with various steps. */
    139	total_success = 0;
    140	for (step = 1; step < max_entries; step++) {
    141		map_batch_update(map_fd, max_entries, keys, values, is_pcpu);
    142		memset(keys, 0, max_entries * sizeof(*keys));
    143		memset(values, 0, max_entries * value_size);
    144		total = 0;
    145		/* iteratively lookup/delete elements with 'step'
    146		 * elements each
    147		 */
    148		count = step;
    149		nospace_err = false;
    150		while (true) {
    151			err = bpf_map_lookup_batch(map_fd,
    152						   total ? &batch : NULL,
    153						   &batch, keys + total,
    154						   values +
    155						   total * value_size,
    156						   &count, &opts);
    157			/* It is possible that we are failing due to buffer size
    158			 * not big enough. In such cases, let us just exit and
    159			 * go with large steps. Not that a buffer size with
    160			 * max_entries should always work.
    161			 */
    162			if (err && errno == ENOSPC) {
    163				nospace_err = true;
    164				break;
    165			}
    166
    167			CHECK((err && errno != ENOENT), "lookup with steps",
    168			      "error: %s\n", strerror(errno));
    169
    170			total += count;
    171			if (err)
    172				break;
    173
    174		}
    175		if (nospace_err == true)
    176			continue;
    177
    178		CHECK(total != max_entries, "lookup with steps",
    179		      "total = %u, max_entries = %u\n", total, max_entries);
    180		map_batch_verify(visited, max_entries, keys, values, is_pcpu);
    181
    182		total = 0;
    183		count = step;
    184		while (total < max_entries) {
    185			if (max_entries - total < step)
    186				count = max_entries - total;
    187			err = bpf_map_delete_batch(map_fd,
    188						   keys + total,
    189						   &count, &opts);
    190			CHECK((err && errno != ENOENT), "delete batch",
    191			      "error: %s\n", strerror(errno));
    192			total += count;
    193			if (err)
    194				break;
    195		}
    196		CHECK(total != max_entries, "delete with steps",
    197		      "total = %u, max_entries = %u\n", total, max_entries);
    198
    199		/* check map is empty, errono == ENOENT */
    200		err = bpf_map_get_next_key(map_fd, NULL, &key);
    201		CHECK(!err || errno != ENOENT, "bpf_map_get_next_key()",
    202		      "error: %s\n", strerror(errno));
    203
    204		/* iteratively lookup/delete elements with 'step'
    205		 * elements each
    206		 */
    207		map_batch_update(map_fd, max_entries, keys, values, is_pcpu);
    208		memset(keys, 0, max_entries * sizeof(*keys));
    209		memset(values, 0, max_entries * value_size);
    210		total = 0;
    211		count = step;
    212		nospace_err = false;
    213		while (true) {
    214			err = bpf_map_lookup_and_delete_batch(map_fd,
    215							total ? &batch : NULL,
    216							&batch, keys + total,
    217							values +
    218							total * value_size,
    219							&count, &opts);
    220			/* It is possible that we are failing due to buffer size
    221			 * not big enough. In such cases, let us just exit and
    222			 * go with large steps. Not that a buffer size with
    223			 * max_entries should always work.
    224			 */
    225			if (err && errno == ENOSPC) {
    226				nospace_err = true;
    227				break;
    228			}
    229
    230			CHECK((err && errno != ENOENT), "lookup with steps",
    231			      "error: %s\n", strerror(errno));
    232
    233			total += count;
    234			if (err)
    235				break;
    236		}
    237
    238		if (nospace_err == true)
    239			continue;
    240
    241		CHECK(total != max_entries, "lookup/delete with steps",
    242		      "total = %u, max_entries = %u\n", total, max_entries);
    243
    244		map_batch_verify(visited, max_entries, keys, values, is_pcpu);
    245		err = bpf_map_get_next_key(map_fd, NULL, &key);
    246		CHECK(!err, "bpf_map_get_next_key()", "error: %s\n",
    247		      strerror(errno));
    248
    249		total_success++;
    250	}
    251
    252	CHECK(total_success == 0, "check total_success",
    253	      "unexpected failure\n");
    254	free(keys);
    255	free(visited);
    256	if (!is_pcpu)
    257		free(values);
    258}
    259
    260void htab_map_batch_ops(void)
    261{
    262	__test_map_lookup_and_delete_batch(false);
    263	printf("test_%s:PASS\n", __func__);
    264}
    265
    266void htab_percpu_map_batch_ops(void)
    267{
    268	__test_map_lookup_and_delete_batch(true);
    269	printf("test_%s:PASS\n", __func__);
    270}
    271
    272void test_htab_map_batch_ops(void)
    273{
    274	htab_map_batch_ops();
    275	htab_percpu_map_batch_ops();
    276}