From 87ea9546ed2b3ad71e6c5d6b73de7f8ecd5ea31d Mon Sep 17 00:00:00 2001 From: Abhishek Vijeev Date: Sat, 25 Jul 2020 17:56:26 +0530 Subject: [PATCH] criu: restoring BPF map data This commit restores the data of BPF maps. A hash table (indexed by the map's id) is used to store data objects for multiple BPF map files that a process may have opened. Collisions are resolved with chaining using a linked list. Source files modified: * bpfmap.c - Structure and function definitions needed to: (a) collect the protobuf image containing BPF map data (b) read the BPF map's data from the image and store it in the hash table (c) restore the map's data using bpf_map_update_batch() * include/bpfmap.h - Defines the size of the hash table and maks to be used while indexing into it - Structure and function declarations that are used while restoring BPF map data * cr-restore.c - Collects the protobuf image containing BPF map data during the restoration phase Signed-off-by: Abhishek Vijeev --- criu/bpfmap.c | 109 ++++++++++++++++++++++++++++++++++++++++++ criu/cr-restore.c | 4 ++ criu/include/bpfmap.h | 14 ++++++ 3 files changed, 127 insertions(+) diff --git a/criu/bpfmap.c b/criu/bpfmap.c index c5bde6ebb..dc8edb09c 100644 --- a/criu/bpfmap.c +++ b/criu/bpfmap.c @@ -23,6 +23,112 @@ static void pr_info_bpfmap(char *action, BpfmapFileEntry *bpf) action, bpf->id, bpf->map_id, bpf->map_type, bpf->map_flags); } +struct bpfmap_data_rst *bpfmap_data_hash_table[BPFMAP_DATA_TABLE_SIZE]; + +static int bpfmap_data_read(struct cr_img *img, struct bpfmap_data_rst *r) +{ + unsigned long bytes = r->bde->keys_bytes + r->bde->values_bytes; + if (!bytes) + return 0; + + r->data = mmap(NULL, bytes, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, 0, 0); + if (r->data == MAP_FAILED) { + pr_perror("Can't map mem for bpfmap buffers"); + return -1; + } + + return read_img_buf(img, r->data, bytes); +} + +int do_collect_bpfmap_data(struct bpfmap_data_rst *r, ProtobufCMessage *msg, + struct cr_img *img, struct bpfmap_data_rst **bpf_hash_table) +{ + int ret; + int table_index; + + r->bde = pb_msg(msg, BpfmapDataEntry); + ret = bpfmap_data_read(img, r); + if (ret < 0) + return ret; + + table_index = r->bde->map_id & BPFMAP_DATA_HASH_MASK; + r->next = bpf_hash_table[table_index]; + bpf_hash_table[table_index] = r; + + pr_info("Collected bpfmap data for %#x\n", r->bde->map_id); + return 0; +} + +int restore_bpfmap_data(int map_fd, uint32_t map_id, struct bpfmap_data_rst **bpf_hash_table) +{ + struct bpfmap_data_rst *map_data; + BpfmapDataEntry *bde; + void *keys = NULL; + void *values = NULL; + unsigned int count; + DECLARE_LIBBPF_OPTS(bpf_map_batch_opts, opts, + .elem_flags = 0, + .flags = 0, + ); + + for (map_data = bpf_hash_table[map_id & BPFMAP_DATA_HASH_MASK]; map_data != NULL; + map_data = map_data->next) { + + if (map_data->bde->map_id == map_id) + break; + } + + if (!map_data || map_data->bde->count == 0) { + pr_info("No data for BPF map %#x\n", map_id); + return 0; + } + + bde = map_data->bde; + count = bde->count; + + keys = mmap(NULL, bde->keys_bytes, 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; + } + memcpy(keys, map_data->data, bde->keys_bytes); + + values = mmap(NULL, bde->values_bytes, 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; + } + memcpy(values, map_data->data + bde->keys_bytes, bde->values_bytes); + + if (bpf_map_update_batch(map_fd, keys, values, &count, &opts)) { + pr_perror("Can't load key-value pairs to BPF map"); + goto err; + } + munmap(keys, bde->keys_bytes); + munmap(values, bde->values_bytes); + return 0; + +err: + munmap(keys, bde->keys_bytes); + munmap(values, bde->values_bytes); + return -1; +} + +static int collect_bpfmap_data(void *obj, ProtobufCMessage *msg, struct cr_img *img) +{ + return do_collect_bpfmap_data(obj, msg, img, bpfmap_data_hash_table); +} + +struct collect_image_info bpfmap_data_cinfo = { + .fd_type = CR_FD_BPFMAP_DATA, + .pb_type = PB_BPFMAP_DATA, + .priv_size = sizeof(struct bpfmap_data_rst), + .collect = collect_bpfmap_data, +}; + int dump_one_bpfmap_data(BpfmapFileEntry *bpf, int lfd, const struct fd_parms *p) { /* @@ -179,6 +285,9 @@ static int bpfmap_open(struct file_desc *d, int *new_fd) return -1; } + if (restore_bpfmap_data(bpfmap_fd, bpfe->map_id, bpfmap_data_hash_table)) + return -1; + if (rst_file_params(bpfmap_fd, bpfe->fown, bpfe->flags)) { pr_perror("Can't restore params on bpfmap %#08x", bpfe->id); goto err_close; diff --git a/criu/cr-restore.c b/criu/cr-restore.c index d926b66a4..641ac4a65 100644 --- a/criu/cr-restore.c +++ b/criu/cr-restore.c @@ -78,6 +78,7 @@ #include "string.h" #include "memfd.h" #include "timens.h" +#include "bpfmap.h" #include "parasite-syscall.h" #include "files-reg.h" @@ -272,6 +273,9 @@ static struct collect_image_info *cinfos[] = { &pipe_data_cinfo, &fifo_data_cinfo, &sk_queues_cinfo, +#ifdef CONFIG_HAS_LIBBPF + &bpfmap_data_cinfo, +#endif }; static struct collect_image_info *cinfos_files[] = { diff --git a/criu/include/bpfmap.h b/criu/include/bpfmap.h index bf5bbfec5..ad065dfc6 100644 --- a/criu/include/bpfmap.h +++ b/criu/include/bpfmap.h @@ -10,10 +10,24 @@ struct bpfmap_file_info { struct file_desc d; }; +struct bpfmap_data_rst { + BpfmapDataEntry *bde; + void *data; + struct bpfmap_data_rst *next; +}; + +#define BPFMAP_DATA_HASH_BITS 5 +#define BPFMAP_DATA_TABLE_SIZE (1 << BPFMAP_DATA_HASH_BITS) +#define BPFMAP_DATA_HASH_MASK (BPFMAP_DATA_TABLE_SIZE - 1) + extern int is_bpfmap_link(char *link); extern int dump_one_bpfmap_data(BpfmapFileEntry *bpf, int lfd, const struct fd_parms *p); +extern int do_collect_bpfmap_data(struct bpfmap_data_rst *, ProtobufCMessage *, + struct cr_img *, struct bpfmap_data_rst **); +extern int restore_bpfmap_data(int, uint32_t, struct bpfmap_data_rst **); extern const struct fdtype_ops bpfmap_dump_ops; extern struct collect_image_info bpfmap_cinfo; +extern struct collect_image_info bpfmap_data_cinfo; #endif /* __CR_BPFMAP_H__ */