From c46225ea802a013cf9741c2e72e5842a93aaa5bd Mon Sep 17 00:00:00 2001 From: Abhishek Vijeev Date: Fri, 24 Jul 2020 21:51:13 +0530 Subject: [PATCH] criu: dumping BPF map data This commit enables CRIU to dump data(key-value) pairs stored in BPF maps Source files modified: * bpfmap.c - Function dump_one_bpfmap_data() reads the map's keys and values into two buffers using bpf_map_lookup_batch() and then writes them out to a protobuf image along with the number of key-value pairs read - Function dump_one_bpfmap() now dumps the data as well before returning * include/bpfmap.h - Includes headers and declares functions needed to dump BPF map data Signed-off-by: Abhishek Vijeev --- criu/bpfmap.c | 103 +++++++++++++++++++++++++++++++++++++++++- criu/include/bpfmap.h | 3 ++ 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/criu/bpfmap.c b/criu/bpfmap.c index e97c827e7..4eb9fc559 100644 --- a/criu/bpfmap.c +++ b/criu/bpfmap.c @@ -1,5 +1,6 @@ #include #include +#include #include "common/compiler.h" #include "imgset.h" @@ -22,6 +23,103 @@ static void pr_info_bpfmap(char *action, BpfmapFileEntry *bpf) action, bpf->id, bpf->map_id, bpf->map_type, bpf->map_flags); } +int dump_one_bpfmap_data(BpfmapFileEntry *bpf, int lfd, const struct fd_parms *p) +{ + /* + * Linux kernel patch notes for bpf_map_*_batch(): + * + * in_batch/out_batch are opaque values use to communicate between + * user/kernel space, in_batch/out_batch must be of key_size length. + * To start iterating from the beginning in_batch must be null, + * count is the # of key/value elements to retrieve. Note that the 'keys' + * buffer must be a buffer of key_size * count size and the 'values' buffer + * must be value_size * count, where value_size must be aligned to 8 bytes + * by userspace if it's dealing with percpu maps. 'count' will contain the + * number of keys/values successfully retrieved. Note that 'count' is an + * input/output variable and it can contain a lower value after a call. + * + * If there's no more entries to retrieve, ENOENT will be returned. If error + * is ENOENT, count might be > 0 in case it copied some values but there were + * no more entries to retrieve. + * + * Note that if the return code is an error and not -EFAULT, + * count indicates the number of elements successfully processed. + */ + + struct cr_img *img; + uint32_t key_size, value_size, max_entries, count; + void *keys = NULL, *values = NULL; + void *in_batch = NULL, *out_batch = NULL; + BpfmapDataEntry bde = BPFMAP_DATA_ENTRY__INIT; + DECLARE_LIBBPF_OPTS(bpf_map_batch_opts, opts, + .elem_flags = 0, + .flags = 0, + ); + int ret; + + key_size = bpf->key_size; + value_size = bpf->value_size; + max_entries = bpf->max_entries; + count = max_entries; + + keys = mmap(NULL, key_size * max_entries, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (keys == MAP_FAILED) { + pr_perror("Can't map memory for BPF map keys"); + goto err; + } + + values = mmap(NULL, value_size * max_entries, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (values == MAP_FAILED) { + pr_perror("Can't map memory for BPF map values"); + goto err; + } + + out_batch = mmap(NULL, key_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (out_batch == MAP_FAILED) { + pr_perror("Can't map memory for BPF map out_batch"); + goto err; + } + + ret = bpf_map_lookup_batch(lfd, in_batch, out_batch, keys, values, &count, &opts); + if (ret && errno != ENOENT) { + pr_perror("Can't perform a batch lookup on BPF map"); + goto err; + } + + img = img_from_set(glob_imgset, CR_FD_BPFMAP_DATA); + + bde.map_id = bpf->map_id; + bde.keys_bytes = (key_size * count); + bde.values_bytes = (value_size * count); + bde.count = count; + + if (pb_write_one(img, &bde, PB_BPFMAP_DATA)) + goto err; + + if (write(img_raw_fd(img), keys, key_size * count) != (key_size * count)) { + pr_perror("Can't write BPF map's keys"); + goto err; + } + if (write(img_raw_fd(img), values, value_size * count) != (value_size * count)) { + pr_perror("Can't write BPF map's values"); + goto err; + } + + munmap(keys, key_size * max_entries); + munmap(values, value_size * max_entries); + munmap(out_batch, key_size); + return 0; + +err: + munmap(keys, key_size * max_entries); + munmap(values, value_size * max_entries); + munmap(out_batch, key_size); + return -1; +} + static int dump_one_bpfmap(int lfd, u32 id, const struct fd_parms *p) { BpfmapFileEntry bpf = BPFMAP_FILE_ENTRY__INIT; @@ -44,7 +142,10 @@ static int dump_one_bpfmap(int lfd, u32 id, const struct fd_parms *p) fe.bpf = &bpf; pr_info_bpfmap("Dumping ", &bpf); - ret = pb_write_one(img_from_set(glob_imgset, CR_FD_FILES), &fe, PB_FILE); + if (pb_write_one(img_from_set(glob_imgset, CR_FD_FILES), &fe, PB_FILE)) + return -1; + pr_info_bpfmap("Dumping data for ", &bpf); + ret = dump_one_bpfmap_data(&bpf, lfd, p); break; default: diff --git a/criu/include/bpfmap.h b/criu/include/bpfmap.h index 881a94554..cfeeb2745 100644 --- a/criu/include/bpfmap.h +++ b/criu/include/bpfmap.h @@ -3,8 +3,11 @@ #include "files.h" #include "bpfmap-file.pb-c.h" +#include "bpfmap-data.pb-c.h" extern int is_bpfmap_link(char *link); +extern int dump_one_bpfmap_data(BpfmapFileEntry *bpf, int lfd, const struct fd_parms *p); + extern const struct fdtype_ops bpfmap_dump_ops; #endif /* __CR_BPFMAP_H__ */