@@ -577,11 +577,13 @@ $(OUTPUT)/bench_strncmp.o: $(OUTPUT)/strncmp_bench.skel.h
$(OUTPUT)/bench_bpf_hashmap_full_update.o: $(OUTPUT)/bpf_hashmap_full_update_bench.skel.h
$(OUTPUT)/bench_local_storage.o: $(OUTPUT)/local_storage_bench.skel.h
$(OUTPUT)/bench_local_storage_rcu_tasks_trace.o: $(OUTPUT)/local_storage_rcu_tasks_trace_bench.skel.h
+$(OUTPUT)/bench_qp_trie.o: $(OUTPUT)/qp_trie_bench.skel.h
$(OUTPUT)/bench.o: bench.h testing_helpers.h $(BPFOBJ)
$(OUTPUT)/bench: LDLIBS += -lm
$(OUTPUT)/bench: $(OUTPUT)/bench.o \
$(TESTING_HELPERS) \
$(TRACE_HELPERS) \
+ $(CGROUP_HELPERS) \
$(OUTPUT)/bench_count.o \
$(OUTPUT)/bench_rename.o \
$(OUTPUT)/bench_trigger.o \
@@ -591,7 +593,8 @@ $(OUTPUT)/bench: $(OUTPUT)/bench.o \
$(OUTPUT)/bench_strncmp.o \
$(OUTPUT)/bench_bpf_hashmap_full_update.o \
$(OUTPUT)/bench_local_storage.o \
- $(OUTPUT)/bench_local_storage_rcu_tasks_trace.o
+ $(OUTPUT)/bench_local_storage_rcu_tasks_trace.o \
+ $(OUTPUT)/bench_qp_trie.o
$(call msg,BINARY,,$@)
$(Q)$(CC) $(CFLAGS) $(LDFLAGS) $(filter %.a %.o,$^) $(LDLIBS) -o $@
@@ -275,6 +275,7 @@ extern struct argp bench_bpf_loop_argp;
extern struct argp bench_local_storage_argp;
extern struct argp bench_local_storage_rcu_tasks_trace_argp;
extern struct argp bench_strncmp_argp;
+extern struct argp bench_qp_trie_argp;
static const struct argp_child bench_parsers[] = {
{ &bench_ringbufs_argp, 0, "Ring buffers benchmark", 0 },
@@ -284,6 +285,7 @@ static const struct argp_child bench_parsers[] = {
{ &bench_strncmp_argp, 0, "bpf_strncmp helper benchmark", 0 },
{ &bench_local_storage_rcu_tasks_trace_argp, 0,
"local_storage RCU Tasks Trace slowdown benchmark", 0 },
+ { &bench_qp_trie_argp, 0, "qp-trie benchmark", 0 },
{},
};
@@ -490,6 +492,10 @@ extern const struct bench bench_local_storage_cache_seq_get;
extern const struct bench bench_local_storage_cache_interleaved_get;
extern const struct bench bench_local_storage_cache_hashmap_control;
extern const struct bench bench_local_storage_tasks_trace;
+extern const struct bench bench_htab_lookup;
+extern const struct bench bench_qp_trie_lookup;
+extern const struct bench bench_htab_update;
+extern const struct bench bench_qp_trie_update;
static const struct bench *benchs[] = {
&bench_count_global,
@@ -529,6 +535,10 @@ static const struct bench *benchs[] = {
&bench_local_storage_cache_interleaved_get,
&bench_local_storage_cache_hashmap_control,
&bench_local_storage_tasks_trace,
+ &bench_htab_lookup,
+ &bench_qp_trie_lookup,
+ &bench_htab_update,
+ &bench_qp_trie_update,
};
static void setup_benchmark()
new file mode 100644
@@ -0,0 +1,511 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2022. Huawei Technologies Co., Ltd */
+#include <argp.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "bench.h"
+#include "bpf_util.h"
+#include "cgroup_helpers.h"
+
+#include "qp_trie_bench.skel.h"
+
+enum {
+ FOR_HTAB = 0,
+ FOR_TRIE,
+};
+
+static struct qp_trie_ctx {
+ struct qp_trie_bench *skel;
+ int cgrp_dfd;
+ u64 map_slab_mem;
+} ctx;
+
+static struct {
+ const char *file;
+ __u32 entries;
+} args;
+
+struct qp_trie_key {
+ __u32 len;
+ unsigned char data[0];
+};
+
+struct run_stat {
+ __u64 stats[2];
+};
+
+enum {
+ ARG_DATA_FILE = 8001,
+ ARG_DATA_ENTRIES = 8002,
+};
+
+static const struct argp_option opts[] = {
+ { "file", ARG_DATA_FILE, "DATA-FILE", 0, "Set data file" },
+ { "entries", ARG_DATA_ENTRIES, "DATA-ENTRIES", 0, "Set data entries" },
+ {},
+};
+
+static error_t qp_trie_parse_arg(int key, char *arg, struct argp_state *state)
+{
+ switch (key) {
+ case ARG_DATA_FILE:
+ args.file = strdup(arg);
+ break;
+ case ARG_DATA_ENTRIES:
+ args.entries = strtoul(arg, NULL, 10);
+ break;
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+
+ return 0;
+}
+
+const struct argp bench_qp_trie_argp = {
+ .options = opts,
+ .parser = qp_trie_parse_arg,
+};
+
+static int parse_data_set(const char *name, struct qp_trie_key ***set, unsigned int *nr,
+ unsigned int *max_len)
+{
+#define INT_MAX_DATA_SIZE 1024
+ unsigned int i, nr_items, item_max_len;
+ char line[INT_MAX_DATA_SIZE + 1];
+ struct qp_trie_key **items;
+ struct qp_trie_key *cur;
+ int err = 0;
+ FILE *file;
+ char *got;
+
+ file = fopen(name, "rb");
+ if (!file) {
+ fprintf(stderr, "open %s err %s\n", name, strerror(errno));
+ return -1;
+ }
+
+ got = fgets(line, sizeof(line), file);
+ if (!got) {
+ fprintf(stderr, "empty file ?\n");
+ err = -1;
+ goto out;
+ }
+ if (sscanf(line, "%u", &nr_items) != 1) {
+ fprintf(stderr, "the first line must be the number of items\n");
+ err = -1;
+ goto out;
+ }
+
+ fprintf(stdout, "item %u\n", nr_items);
+
+ items = (struct qp_trie_key **)calloc(nr_items, sizeof(*items) + INT_MAX_DATA_SIZE);
+ if (!items) {
+ fprintf(stderr, "no mem for items\n");
+ err = -1;
+ goto out;
+ }
+
+ i = 0;
+ item_max_len = 0;
+ cur = (void *)items + sizeof(*items) * nr_items;
+ while (true) {
+ unsigned int len;
+
+ got = fgets(line, sizeof(line), file);
+ if (!got) {
+ if (!feof(file)) {
+ fprintf(stderr, "read file %s error\n", name);
+ err = -1;
+ }
+ break;
+ }
+
+ len = strlen(got);
+ if (len && got[len - 1] == '\n') {
+ got[len - 1] = 0;
+ len -= 1;
+ }
+ if (!len) {
+ fprintf(stdout, "#%u empty line\n", i + 2);
+ continue;
+ }
+
+ if (i >= nr_items) {
+ fprintf(stderr, "too many line in %s\n", name);
+ break;
+ }
+
+ if (len > item_max_len)
+ item_max_len = len;
+ cur->len = len;
+ memcpy(cur->data, got, len);
+ items[i++] = cur;
+ cur = (void *)cur + INT_MAX_DATA_SIZE;
+ }
+
+ if (!err) {
+ if (i != nr_items)
+ fprintf(stdout, "few lines in %s (exp %u got %u)\n", name, nr_items, i);
+ *nr = i;
+ *set = items;
+ *max_len = item_max_len;
+ } else {
+ free(items);
+ }
+
+out:
+ fclose(file);
+ return err;
+}
+
+static int gen_data_set(struct qp_trie_key ***set, unsigned int *nr, unsigned int *max_len)
+{
+#define RND_MAX_DATA_SIZE 255
+ struct qp_trie_key **items;
+ size_t ptr_size, data_size;
+ struct qp_trie_key *cur;
+ unsigned int i, nr_items;
+ ssize_t got;
+ int err = 0;
+
+ ptr_size = *nr * sizeof(*items);
+ data_size = *nr * (sizeof(*cur) + RND_MAX_DATA_SIZE);
+ items = (struct qp_trie_key **)malloc(ptr_size + data_size);
+ if (!items) {
+ fprintf(stderr, "no mem for items\n");
+ err = -1;
+ goto out;
+ }
+
+ cur = (void *)items + ptr_size;
+ got = syscall(__NR_getrandom, cur, data_size, 0);
+ if (got != data_size) {
+ fprintf(stderr, "getrandom error %s\n", strerror(errno));
+ err = -1;
+ goto out;
+ }
+
+ nr_items = 0;
+ for (i = 0; i < *nr; i++) {
+ cur->len &= 0xff;
+ if (cur->len) {
+ items[nr_items++] = cur;
+ memset(cur->data + cur->len, 0, RND_MAX_DATA_SIZE - cur->len);
+ }
+ cur = (void *)cur + (sizeof(*cur) + RND_MAX_DATA_SIZE);
+ }
+ if (!nr_items) {
+ fprintf(stderr, "no valid key in random data\n");
+ err = -1;
+ goto out;
+ }
+ fprintf(stdout, "generate %u random keys\n", nr_items);
+
+ *nr = nr_items;
+ *set = items;
+ *max_len = RND_MAX_DATA_SIZE;
+out:
+ if (err && items)
+ free(items);
+ return err;
+}
+
+static void qp_trie_validate(void)
+{
+ if (env.consumer_cnt != 1) {
+ fprintf(stderr, "qp_trie_map benchmark doesn't support multi-consumer!\n");
+ exit(1);
+ }
+
+ if (!args.file && !args.entries) {
+ fprintf(stderr, "must specify entries when use random generated data set\n");
+ exit(1);
+ }
+
+ if (args.file && access(args.file, R_OK)) {
+ fprintf(stderr, "data file is un-accessible\n");
+ exit(1);
+ }
+}
+
+static void qp_trie_init_map_opts(struct qp_trie_bench *skel, unsigned int data_size,
+ unsigned int nr)
+{
+ bpf_map__set_value_size(skel->maps.htab_array, data_size);
+ bpf_map__set_max_entries(skel->maps.htab_array, nr);
+
+ bpf_map__set_key_size(skel->maps.htab, data_size);
+ bpf_map__set_max_entries(skel->maps.htab, nr);
+
+ bpf_map__set_value_size(skel->maps.trie_array, sizeof(struct qp_trie_key) + data_size);
+ bpf_map__set_max_entries(skel->maps.trie_array, nr);
+
+ bpf_map__set_map_extra(skel->maps.qp_trie, data_size);
+ bpf_map__set_max_entries(skel->maps.qp_trie, nr);
+}
+
+static void qp_trie_setup_key_map(struct bpf_map *map, unsigned int map_type,
+ struct qp_trie_key **set, unsigned int nr)
+{
+ int fd = bpf_map__fd(map);
+ unsigned int i;
+
+ for (i = 0; i < nr; i++) {
+ void *value;
+ int err;
+
+ value = (map_type != FOR_HTAB) ? (void *)set[i] : (void *)set[i]->data;
+ err = bpf_map_update_elem(fd, &i, value, 0);
+ if (err) {
+ fprintf(stderr, "add #%u key (%s) on %s error %d\n",
+ i, set[i]->data, bpf_map__name(map), err);
+ exit(1);
+ }
+ }
+}
+
+static u64 qp_trie_get_slab_mem(int dfd)
+{
+ const char *magic = "slab ";
+ const char *name = "memory.stat";
+ int fd;
+ ssize_t nr;
+ char buf[4096];
+ char *from;
+
+ fd = openat(dfd, name, 0);
+ if (fd < 0) {
+ fprintf(stderr, "no %s\n", name);
+ exit(1);
+ }
+
+ nr = read(fd, buf, sizeof(buf));
+ if (nr <= 0) {
+ fprintf(stderr, "empty %s ?\n", name);
+ exit(1);
+ }
+ buf[nr - 1] = 0;
+
+ close(fd);
+
+ from = strstr(buf, magic);
+ if (!from) {
+ fprintf(stderr, "no slab in %s\n", name);
+ exit(1);
+ }
+
+ return strtoull(from + strlen(magic), NULL, 10);
+}
+
+static void qp_trie_setup_lookup_map(struct bpf_map *map, unsigned int map_type,
+ struct qp_trie_key **set, unsigned int nr)
+{
+ int fd = bpf_map__fd(map);
+ unsigned int i;
+
+ for (i = 0; i < nr; i++) {
+ int err;
+
+ if (map_type == FOR_HTAB) {
+ void *key;
+
+ key = set[i]->data;
+ err = bpf_map_update_elem(fd, key, &i, 0);
+ } else {
+ struct bpf_dynptr_user dynptr;
+
+ bpf_dynptr_user_init(set[i]->data, set[i]->len, &dynptr);
+ err = bpf_map_update_elem(fd, &dynptr, &i, 0);
+ }
+ if (err) {
+ fprintf(stderr, "add #%u key (%s) on %s error %d\n",
+ i, set[i]->data, bpf_map__name(map), err);
+ exit(1);
+ }
+ }
+}
+
+static void qp_trie_setup(unsigned int map_type)
+{
+ struct qp_trie_key **set = NULL;
+ struct qp_trie_bench *skel;
+ unsigned int nr = 0, max_len = 0;
+ struct bpf_map *map;
+ u64 before, after;
+ int dfd;
+ int err;
+
+ if (!args.file) {
+ nr = args.entries;
+ err = gen_data_set(&set, &nr, &max_len);
+ } else {
+ err = parse_data_set(args.file, &set, &nr, &max_len);
+ }
+ if (err < 0)
+ exit(1);
+
+ if (args.entries && args.entries < nr)
+ nr = args.entries;
+
+ dfd = cgroup_setup_and_join("/qp_trie");
+ if (dfd < 0) {
+ fprintf(stderr, "failed to setup cgroup env\n");
+ exit(1);
+ }
+
+ setup_libbpf();
+
+ before = qp_trie_get_slab_mem(dfd);
+
+ skel = qp_trie_bench__open();
+ if (!skel) {
+ fprintf(stderr, "failed to open skeleton\n");
+ exit(1);
+ }
+
+ qp_trie_init_map_opts(skel, max_len, nr);
+
+ skel->rodata->qp_trie_key_size = max_len;
+ skel->bss->update_nr = nr;
+ skel->bss->update_chunk = nr / env.producer_cnt;
+
+ err = qp_trie_bench__load(skel);
+ if (err) {
+ fprintf(stderr, "failed to load skeleton\n");
+ exit(1);
+ }
+
+ map = (map_type == FOR_HTAB) ? skel->maps.htab_array : skel->maps.trie_array;
+ qp_trie_setup_key_map(map, map_type, set, nr);
+
+ map = (map_type == FOR_HTAB) ? skel->maps.htab : skel->maps.qp_trie;
+ qp_trie_setup_lookup_map(map, map_type, set, nr);
+
+ after = qp_trie_get_slab_mem(dfd);
+
+ ctx.skel = skel;
+ ctx.cgrp_dfd = dfd;
+ ctx.map_slab_mem = after - before;
+}
+
+static void qp_trie_attach_prog(struct bpf_program *prog)
+{
+ struct bpf_link *link;
+
+ link = bpf_program__attach(prog);
+ if (!link) {
+ fprintf(stderr, "failed to attach program!\n");
+ exit(1);
+ }
+}
+
+static void htab_lookup_setup(void)
+{
+ qp_trie_setup(FOR_HTAB);
+ qp_trie_attach_prog(ctx.skel->progs.htab_lookup);
+}
+
+static void qp_trie_lookup_setup(void)
+{
+ qp_trie_setup(FOR_TRIE);
+ qp_trie_attach_prog(ctx.skel->progs.qp_trie_lookup);
+}
+
+static void htab_update_setup(void)
+{
+ qp_trie_setup(FOR_HTAB);
+ qp_trie_attach_prog(ctx.skel->progs.htab_update);
+}
+
+static void qp_trie_update_setup(void)
+{
+ qp_trie_setup(FOR_TRIE);
+ qp_trie_attach_prog(ctx.skel->progs.qp_trie_update);
+}
+
+static void *qp_trie_producer(void *ctx)
+{
+ while (true)
+ (void)syscall(__NR_getpgid);
+ return NULL;
+}
+
+static void *qp_trie_consumer(void *ctx)
+{
+ return NULL;
+}
+
+static void qp_trie_measure(struct bench_res *res)
+{
+ static __u64 last_hits, last_drops;
+ __u64 total_hits = 0, total_drops = 0;
+ unsigned int i, nr_cpus;
+
+ nr_cpus = bpf_num_possible_cpus();
+ for (i = 0; i < nr_cpus; i++) {
+ struct run_stat *s = (void *)&ctx.skel->bss->percpu_stats[i & 255];
+
+ total_hits += s->stats[0];
+ total_drops += s->stats[1];
+ }
+
+ res->hits = total_hits - last_hits;
+ res->drops = total_drops - last_drops;
+
+ last_hits = total_hits;
+ last_drops = total_drops;
+}
+
+static void qp_trie_report_final(struct bench_res res[], int res_cnt)
+{
+ close(ctx.cgrp_dfd);
+ cleanup_cgroup_environment();
+
+ fprintf(stdout, "Slab: %.3f MiB\n", (float)ctx.map_slab_mem / 1024 / 1024);
+ hits_drops_report_final(res, res_cnt);
+}
+
+const struct bench bench_htab_lookup = {
+ .name = "htab-lookup",
+ .validate = qp_trie_validate,
+ .setup = htab_lookup_setup,
+ .producer_thread = qp_trie_producer,
+ .consumer_thread = qp_trie_consumer,
+ .measure = qp_trie_measure,
+ .report_progress = hits_drops_report_progress,
+ .report_final = qp_trie_report_final,
+};
+
+const struct bench bench_qp_trie_lookup = {
+ .name = "qp-trie-lookup",
+ .validate = qp_trie_validate,
+ .setup = qp_trie_lookup_setup,
+ .producer_thread = qp_trie_producer,
+ .consumer_thread = qp_trie_consumer,
+ .measure = qp_trie_measure,
+ .report_progress = hits_drops_report_progress,
+ .report_final = qp_trie_report_final,
+};
+
+const struct bench bench_htab_update = {
+ .name = "htab-update",
+ .validate = qp_trie_validate,
+ .setup = htab_update_setup,
+ .producer_thread = qp_trie_producer,
+ .consumer_thread = qp_trie_consumer,
+ .measure = qp_trie_measure,
+ .report_progress = hits_drops_report_progress,
+ .report_final = qp_trie_report_final,
+};
+
+const struct bench bench_qp_trie_update = {
+ .name = "qp-trie-update",
+ .validate = qp_trie_validate,
+ .setup = qp_trie_update_setup,
+ .producer_thread = qp_trie_producer,
+ .consumer_thread = qp_trie_consumer,
+ .measure = qp_trie_measure,
+ .report_progress = hits_drops_report_progress,
+ .report_final = qp_trie_report_final,
+};
new file mode 100755
@@ -0,0 +1,55 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# Copyright (C) 2022. Huawei Technologies Co., Ltd
+
+source ./benchs/run_common.sh
+
+set -eufo pipefail
+
+mem()
+{
+ echo "$*" | sed -E "s/.*Slab: ([0-9]+\.[0-9]+ MiB).*/\1/"
+}
+
+run_qp_trie_bench()
+{
+ local title=$1
+ local summary
+
+ shift 1
+ summary=$($RUN_BENCH "$@" | grep "Summary\|Slab:")
+ printf "%s %20s (drops %-16s mem %s)\n" "$title" "$(hits $summary)" \
+ "$(drops $summary)" "$(mem $summary)"
+}
+
+run_qp_trie_benchs()
+{
+ local p
+ local m
+ local b
+ local title
+
+ for m in htab qp-trie
+ do
+ for b in lookup update
+ do
+ for p in 1 2 4 8 16
+ do
+ title=$(printf "%-16s (%-2d thread)" "$m $b" $p)
+ run_qp_trie_bench "$title" ${m}-${b} -p $p "$@"
+ done
+ done
+ done
+ echo
+}
+
+echo "Randomly-generated binary data (16K)"
+run_qp_trie_benchs --entries 16384
+
+echo "Strings in /proc/kallsyms"
+TMP_FILE=/tmp/kallsyms.txt
+SRC_FILE=/proc/kallsyms
+trap 'rm -f $TMP_FILE' EXIT
+wc -l $SRC_FILE | awk '{ print $1}' > $TMP_FILE
+awk '{ print $3 }' $SRC_FILE >> $TMP_FILE
+run_qp_trie_benchs --file $TMP_FILE
new file mode 100644
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2022. Huawei Technologies Co., Ltd */
+#include <linux/types.h>
+#include <linux/bpf.h>
+#include <linux/errno.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+struct bpf_map;
+
+struct qp_trie_key {
+ __u32 len;
+ unsigned char data[0];
+};
+
+/* value_size will be set by benchmark */
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(key_size, 4);
+} htab_array SEC(".maps");
+
+/* value_size will be set by benchmark */
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(key_size, 4);
+} trie_array SEC(".maps");
+
+/* key_size will be set by benchmark */
+struct {
+ __uint(type, BPF_MAP_TYPE_HASH);
+ __uint(value_size, 4);
+ __uint(map_flags, BPF_F_NO_PREALLOC);
+} htab SEC(".maps");
+
+/* map_extra will be set by benchmark */
+struct {
+ __uint(type, BPF_MAP_TYPE_QP_TRIE);
+ __type(key, struct bpf_dynptr);
+ __type(value, unsigned int);
+ __uint(map_flags, BPF_F_NO_PREALLOC | BPF_F_DYNPTR_KEY);
+} qp_trie SEC(".maps");
+
+char _license[] SEC("license") = "GPL";
+
+struct {
+ __u64 stats[2];
+} __attribute__((__aligned__(128))) percpu_stats[256];
+
+struct update_ctx {
+ unsigned int max;
+ unsigned int from;
+};
+
+volatile const unsigned int qp_trie_key_size;
+
+unsigned int update_nr;
+unsigned int update_chunk;
+
+static __always_inline void update_stats(int idx)
+{
+ __u32 cpu = bpf_get_smp_processor_id();
+
+ percpu_stats[cpu & 255].stats[idx]++;
+}
+
+static int lookup_htab(struct bpf_map *map, __u32 *key, void *value, void *data)
+{
+ __u32 *index;
+
+ index = bpf_map_lookup_elem(&htab, value);
+ if (index && *index == *key)
+ update_stats(0);
+ else
+ update_stats(1);
+ return 0;
+}
+
+static int update_htab_loop(unsigned int i, void *ctx)
+{
+ struct update_ctx *update = ctx;
+ void *value;
+ int err;
+
+ if (update->from >= update->max)
+ update->from = 0;
+ value = bpf_map_lookup_elem(&htab_array, &update->from);
+ if (!value)
+ return 1;
+
+ err = bpf_map_update_elem(&htab, value, &update->from, 0);
+ if (!err)
+ update_stats(0);
+ else
+ update_stats(1);
+ update->from++;
+
+ return 0;
+}
+
+static int delete_htab_loop(unsigned int i, void *ctx)
+{
+ struct update_ctx *update = ctx;
+ void *value;
+ int err;
+
+ if (update->from >= update->max)
+ update->from = 0;
+ value = bpf_map_lookup_elem(&htab_array, &update->from);
+ if (!value)
+ return 1;
+
+ err = bpf_map_delete_elem(&htab, value);
+ if (!err)
+ update_stats(0);
+ update->from++;
+
+ return 0;
+}
+
+static int lookup_qp_trie(struct bpf_map *map, __u32 *key, void *value, void *data)
+{
+ struct qp_trie_key *qp_trie_key = value;
+ struct bpf_dynptr dynptr;
+ __u32 *index;
+
+ if (qp_trie_key->len > qp_trie_key_size)
+ return 0;
+
+ bpf_dynptr_from_mem(qp_trie_key->data, qp_trie_key->len, 0, &dynptr);
+ index = bpf_map_lookup_elem(&qp_trie, &dynptr);
+ if (index && *index == *key)
+ update_stats(0);
+ else
+ update_stats(1);
+ return 0;
+}
+
+static int update_qp_trie_loop(unsigned int i, void *ctx)
+{
+ struct update_ctx *update = ctx;
+ struct qp_trie_key *value;
+ struct bpf_dynptr dynptr;
+ int err;
+
+ if (update->from >= update->max)
+ update->from = 0;
+ value = bpf_map_lookup_elem(&trie_array, &update->from);
+ if (!value || value->len > qp_trie_key_size)
+ return 1;
+
+ bpf_dynptr_from_mem(value->data, value->len, 0, &dynptr);
+ err = bpf_map_update_elem(&qp_trie, &dynptr, &update->from, 0);
+ if (!err)
+ update_stats(0);
+ else
+ update_stats(1);
+ update->from++;
+
+ return 0;
+}
+
+static int delete_qp_trie_loop(unsigned int i, void *ctx)
+{
+ struct update_ctx *update = ctx;
+ struct qp_trie_key *value;
+ struct bpf_dynptr dynptr;
+ int err;
+
+ if (update->from >= update->max)
+ update->from = 0;
+ value = bpf_map_lookup_elem(&trie_array, &update->from);
+ if (!value || value->len > qp_trie_key_size)
+ return 1;
+
+ bpf_dynptr_from_mem(value->data, value->len, 0, &dynptr);
+ err = bpf_map_delete_elem(&qp_trie, &dynptr);
+ if (!err)
+ update_stats(0);
+ update->from++;
+
+ return 0;
+}
+
+SEC("tp/syscalls/sys_enter_getpgid")
+int htab_lookup(void *ctx)
+{
+ bpf_for_each_map_elem(&htab_array, lookup_htab, NULL, 0);
+ return 0;
+}
+
+SEC("tp/syscalls/sys_enter_getpgid")
+int qp_trie_lookup(void *ctx)
+{
+ bpf_for_each_map_elem(&trie_array, lookup_qp_trie, NULL, 0);
+ return 0;
+}
+
+SEC("tp/syscalls/sys_enter_getpgid")
+int htab_update(void *ctx)
+{
+ unsigned int index = bpf_get_smp_processor_id() * update_chunk;
+ struct update_ctx update;
+
+ update.max = update_nr;
+ if (update.max && index >= update.max)
+ index %= update.max;
+
+ /* Only operate part of keys according to cpu id */
+ update.from = index;
+ bpf_loop(update_chunk, update_htab_loop, &update, 0);
+
+ update.from = index;
+ bpf_loop(update_chunk, delete_htab_loop, &update, 0);
+
+ return 0;
+}
+
+SEC("tp/syscalls/sys_enter_getpgid")
+int qp_trie_update(void *ctx)
+{
+ unsigned int index = bpf_get_smp_processor_id() * update_chunk;
+ struct update_ctx update;
+
+ update.max = update_nr;
+ if (update.max && index >= update.max)
+ index %= update.max;
+
+ /* Only operate part of keys according to cpu id */
+ update.from = index;
+ bpf_loop(update_chunk, update_qp_trie_loop, &update, 0);
+
+ update.from = index;
+ bpf_loop(update_chunk, delete_qp_trie_loop, &update, 0);
+
+ return 0;
+}