#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <linux/bpf.h>
#include "scx_test_map.h"
enum {
SCX_MAP_TYPE_NORMAL,
SCX_MAP_TYPE_PERCPU,
};
struct scx_map_type {
void *map_ptr;
int map_type;
};
struct scx_map_entry {
void *map_ptr;
struct scx_test_map *map;
};
struct scx_percpu_map_entry {
void *map_ptr;
struct scx_percpu_test_map *map;
};
static __thread struct scx_map_entry *scx_map_entries = NULL;
static __thread int scx_map_entries_count = 0;
static __thread struct scx_percpu_map_entry *scx_percpu_map_entries = NULL;
static __thread int scx_percpu_map_entries_count = 0;
static __thread struct scx_map_type *scx_map_types = NULL;
static __thread int scx_map_types_count = 0;
static void scx_regsiter_map_type(void *map_ptr, int map_type)
{
int index = scx_map_types_count;
scx_map_types_count++;
scx_map_types = reallocarray(scx_map_types, scx_map_types_count,
sizeof(struct scx_map_type));
if (!scx_map_types) {
perror("Failed to allocate memory for scx_map_types");
exit(EXIT_FAILURE);
}
scx_map_types[index].map_ptr = map_ptr;
scx_map_types[index].map_type = map_type;
}
static struct scx_test_map *scx_percpu_entry(const void *map_ptr, int cpu)
{
for (int i = 0; i < scx_percpu_map_entries_count; i++) {
if (scx_percpu_map_entries[i].map_ptr == map_ptr) {
return &scx_percpu_map_entries[i].map->per_cpu_maps[cpu];
}
}
return NULL;
}
static struct scx_test_map *scx_normal_entry(const void *map_ptr)
{
for (int i = 0; i < scx_map_entries_count; i++) {
if (scx_map_entries[i].map_ptr == map_ptr) {
return scx_map_entries[i].map;
}
}
return NULL;
}
static struct scx_test_map *scx_test_map_lookup(const void *map_ptr, int cpu)
{
for (int i = 0; i < scx_map_types_count; i++) {
if (scx_map_types[i].map_ptr == map_ptr) {
if (scx_map_types[i].map_type == SCX_MAP_TYPE_PERCPU) {
return scx_percpu_entry(map_ptr, cpu);
} else if (scx_map_types[i].map_type == SCX_MAP_TYPE_NORMAL) {
return scx_normal_entry(map_ptr);
}
}
}
return NULL;
}
void *scx_test_map_lookup_percpu_elem(void *map, const void *key, int cpu)
{
struct scx_test_map *test_map = scx_test_map_lookup(map, cpu);
if (!test_map) {
return NULL;
}
for (int i = 0; i < test_map->nr; i++) {
if (memcmp(&test_map->keys[i], key, test_map->key_size) == 0) {
return &test_map->values[i];
}
}
return NULL;
}
void *scx_test_map_lookup_elem(void *map, const void *key)
{
struct scx_test_map *test_map = scx_test_map_lookup(map, 0);
if (!test_map) {
return NULL;
}
for (int i = 0; i < test_map->nr; i++) {
if (memcmp(&test_map->keys[i], key, test_map->key_size) == 0) {
return &test_map->values[i];
}
}
return NULL;
}
static int map_update_elem(struct scx_test_map *test_map, const void *key,
const void *value, unsigned long flags)
{
int index;
for (int i = 0; i < test_map->nr; i++) {
if (memcmp(&test_map->keys[i], key, test_map->key_size) == 0) {
if (flags & BPF_NOEXIST) {
return -1;
}
memcpy(&test_map->values[i], value, test_map->value_size);
return 0;
}
}
if (flags & BPF_EXIST) {
return -1;
}
if (test_map->nr < 0) {
return -1;
}
if ((unsigned int)test_map->nr >= test_map->max_entries) {
return -1;
}
index = test_map->nr;
test_map->nr++;
test_map->keys = reallocarray(test_map->keys, test_map->nr, test_map->key_size);
if (!test_map->keys) {
perror("Failed to allocate memory for keys");
exit(EXIT_FAILURE);
}
test_map->values = reallocarray(test_map->values, test_map->nr, test_map->value_size);
if (!test_map->values) {
perror("Failed to allocate memory for values");
exit(EXIT_FAILURE);
}
memcpy(&test_map->keys[index], key, test_map->key_size);
memcpy(&test_map->values[index], value, test_map->value_size);
return 0;
}
void *scx_test_task_storage_get(void *map, const void *key, void *value,
unsigned long flags)
{
void *newvalue = NULL;
struct scx_test_map *test_map;
void *ret = scx_test_map_lookup_elem(map, key);
if (ret)
return ret;
if (!(flags & BPF_LOCAL_STORAGE_GET_F_CREATE))
return NULL;
test_map = scx_test_map_lookup(map, 0);
if (!value) {
newvalue = calloc(1, test_map->value_size);
if (!newvalue) {
perror("Failed to allocate empty value for scx_test_task_storage_get");
exit(EXIT_FAILURE);
}
value = newvalue;
}
map_update_elem(test_map, key, value, BPF_ANY);
if (newvalue)
free(newvalue);
return scx_test_map_lookup_elem(map, key);
}
int scx_test_map_update_elem(void *map, const void *key, const void *value,
unsigned long flags)
{
struct scx_test_map *test_map = scx_test_map_lookup(map, 0);
if (!test_map) {
return -1;
}
return map_update_elem(test_map, key, value, flags);
}
int scx_test_map_update_percpu_elem(void *map, const void *key, const void *value,
int cpu, unsigned long flags)
{
struct scx_test_map *test_map = scx_test_map_lookup(map, cpu);
if (!test_map) {
return -1;
}
return map_update_elem(test_map, key, value, flags);
}
void scx_test_map_register(struct scx_test_map *map, void *map_ptr)
{
int index = scx_map_entries_count;
scx_map_entries_count++;
scx_map_entries = reallocarray(scx_map_entries, scx_map_entries_count,
sizeof(struct scx_map_entry));
if (!scx_map_entries) {
perror("Failed to allocate memory for scx_map_entries");
exit(EXIT_FAILURE);
}
scx_map_entries[index].map_ptr = map_ptr;
scx_map_entries[index].map = map;
scx_regsiter_map_type(map_ptr, SCX_MAP_TYPE_NORMAL);
}
struct scx_percpu_test_map *scx_alloc_percpu_test_map(int nr_cpus)
{
struct scx_percpu_test_map *map = malloc(sizeof(struct scx_percpu_test_map));
if (!map) {
perror("Failed to allocate memory for scx_percpu_test_map");
exit(EXIT_FAILURE);
}
map->per_cpu_maps = calloc(nr_cpus, sizeof(struct scx_test_map));
if (!map->per_cpu_maps) {
perror("Failed to allocate memory for per_cpu_maps");
free(map);
exit(EXIT_FAILURE);
}
map->nr_cpus = nr_cpus;
return map;
}
void scx_init_percpu_test_map(struct scx_percpu_test_map *map, unsigned int max_entries,
unsigned int key_size, unsigned int value_size)
{
for (int i = 0; i < map->nr_cpus; i++) {
map->per_cpu_maps[i].keys = NULL;
map->per_cpu_maps[i].values = NULL;
map->per_cpu_maps[i].max_entries = max_entries;
map->per_cpu_maps[i].key_size = key_size;
map->per_cpu_maps[i].value_size = value_size;
map->per_cpu_maps[i].nr = 0;
}
}
void scx_register_percpu_test_map(struct scx_percpu_test_map *map, void *map_ptr)
{
int index = scx_percpu_map_entries_count;
scx_percpu_map_entries_count++;
scx_percpu_map_entries = reallocarray(scx_percpu_map_entries, scx_percpu_map_entries_count,
sizeof(struct scx_percpu_map_entry));
if (!scx_percpu_map_entries) {
perror("Failed to allocate memory for scx_percpu_map_entries");
exit(EXIT_FAILURE);
}
scx_percpu_map_entries[index].map_ptr = map_ptr;
scx_percpu_map_entries[index].map = map;
scx_regsiter_map_type(map_ptr, SCX_MAP_TYPE_PERCPU);
}