diff options
author | Andrii Nakryiko <andriin@fb.com> | 2019-06-17 15:26:53 -0400 |
---|---|---|
committer | Daniel Borkmann <daniel@iogearbox.net> | 2019-06-17 18:10:39 -0400 |
commit | bf82927125dd25003d76ed5541da704df21de57a (patch) | |
tree | 991f621f7ec18f306ed65911d89212b007c8cbf0 | |
parent | 01b29d1dc9a06e2cdc6a0172d35605eb0300ecb2 (diff) |
libbpf: refactor map initialization
User and global data maps initialization has gotten pretty complicated
and unnecessarily convoluted. This patch splits out the logic for global
data map and user-defined map initialization. It also removes the
restriction of pre-calculating how many maps will be initialized,
instead allowing to keep adding new maps as they are discovered, which
will be used later for BTF-defined map definitions.
Signed-off-by: Andrii Nakryiko <andriin@fb.com>
Acked-by: Song Liu <songliubraving@fb.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
-rw-r--r-- | tools/lib/bpf/libbpf.c | 247 |
1 files changed, 133 insertions, 114 deletions
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 7ee44d8877c5..88609dca4f7d 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c | |||
@@ -234,6 +234,7 @@ struct bpf_object { | |||
234 | size_t nr_programs; | 234 | size_t nr_programs; |
235 | struct bpf_map *maps; | 235 | struct bpf_map *maps; |
236 | size_t nr_maps; | 236 | size_t nr_maps; |
237 | size_t maps_cap; | ||
237 | struct bpf_secdata sections; | 238 | struct bpf_secdata sections; |
238 | 239 | ||
239 | bool loaded; | 240 | bool loaded; |
@@ -763,21 +764,51 @@ int bpf_object__variable_offset(const struct bpf_object *obj, const char *name, | |||
763 | return -ENOENT; | 764 | return -ENOENT; |
764 | } | 765 | } |
765 | 766 | ||
766 | static bool bpf_object__has_maps(const struct bpf_object *obj) | 767 | static struct bpf_map *bpf_object__add_map(struct bpf_object *obj) |
767 | { | 768 | { |
768 | return obj->efile.maps_shndx >= 0 || | 769 | struct bpf_map *new_maps; |
769 | obj->efile.data_shndx >= 0 || | 770 | size_t new_cap; |
770 | obj->efile.rodata_shndx >= 0 || | 771 | int i; |
771 | obj->efile.bss_shndx >= 0; | 772 | |
773 | if (obj->nr_maps < obj->maps_cap) | ||
774 | return &obj->maps[obj->nr_maps++]; | ||
775 | |||
776 | new_cap = max(4ul, obj->maps_cap * 3 / 2); | ||
777 | new_maps = realloc(obj->maps, new_cap * sizeof(*obj->maps)); | ||
778 | if (!new_maps) { | ||
779 | pr_warning("alloc maps for object failed\n"); | ||
780 | return ERR_PTR(-ENOMEM); | ||
781 | } | ||
782 | |||
783 | obj->maps_cap = new_cap; | ||
784 | obj->maps = new_maps; | ||
785 | |||
786 | /* zero out new maps */ | ||
787 | memset(obj->maps + obj->nr_maps, 0, | ||
788 | (obj->maps_cap - obj->nr_maps) * sizeof(*obj->maps)); | ||
789 | /* | ||
790 | * fill all fd with -1 so won't close incorrect fd (fd=0 is stdin) | ||
791 | * when failure (zclose won't close negative fd)). | ||
792 | */ | ||
793 | for (i = obj->nr_maps; i < obj->maps_cap; i++) { | ||
794 | obj->maps[i].fd = -1; | ||
795 | obj->maps[i].inner_map_fd = -1; | ||
796 | } | ||
797 | |||
798 | return &obj->maps[obj->nr_maps++]; | ||
772 | } | 799 | } |
773 | 800 | ||
774 | static int | 801 | static int |
775 | bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map, | 802 | bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type, |
776 | enum libbpf_map_type type, Elf_Data *data, | 803 | Elf_Data *data, void **data_buff) |
777 | void **data_buff) | ||
778 | { | 804 | { |
779 | struct bpf_map_def *def = &map->def; | ||
780 | char map_name[BPF_OBJ_NAME_LEN]; | 805 | char map_name[BPF_OBJ_NAME_LEN]; |
806 | struct bpf_map_def *def; | ||
807 | struct bpf_map *map; | ||
808 | |||
809 | map = bpf_object__add_map(obj); | ||
810 | if (IS_ERR(map)) | ||
811 | return PTR_ERR(map); | ||
781 | 812 | ||
782 | map->libbpf_type = type; | 813 | map->libbpf_type = type; |
783 | map->offset = ~(typeof(map->offset))0; | 814 | map->offset = ~(typeof(map->offset))0; |
@@ -789,6 +820,7 @@ bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map, | |||
789 | return -ENOMEM; | 820 | return -ENOMEM; |
790 | } | 821 | } |
791 | 822 | ||
823 | def = &map->def; | ||
792 | def->type = BPF_MAP_TYPE_ARRAY; | 824 | def->type = BPF_MAP_TYPE_ARRAY; |
793 | def->key_size = sizeof(int); | 825 | def->key_size = sizeof(int); |
794 | def->value_size = data->d_size; | 826 | def->value_size = data->d_size; |
@@ -808,29 +840,58 @@ bpf_object__init_internal_map(struct bpf_object *obj, struct bpf_map *map, | |||
808 | return 0; | 840 | return 0; |
809 | } | 841 | } |
810 | 842 | ||
811 | static int bpf_object__init_maps(struct bpf_object *obj, int flags) | 843 | static int bpf_object__init_global_data_maps(struct bpf_object *obj) |
844 | { | ||
845 | int err; | ||
846 | |||
847 | if (!obj->caps.global_data) | ||
848 | return 0; | ||
849 | /* | ||
850 | * Populate obj->maps with libbpf internal maps. | ||
851 | */ | ||
852 | if (obj->efile.data_shndx >= 0) { | ||
853 | err = bpf_object__init_internal_map(obj, LIBBPF_MAP_DATA, | ||
854 | obj->efile.data, | ||
855 | &obj->sections.data); | ||
856 | if (err) | ||
857 | return err; | ||
858 | } | ||
859 | if (obj->efile.rodata_shndx >= 0) { | ||
860 | err = bpf_object__init_internal_map(obj, LIBBPF_MAP_RODATA, | ||
861 | obj->efile.rodata, | ||
862 | &obj->sections.rodata); | ||
863 | if (err) | ||
864 | return err; | ||
865 | } | ||
866 | if (obj->efile.bss_shndx >= 0) { | ||
867 | err = bpf_object__init_internal_map(obj, LIBBPF_MAP_BSS, | ||
868 | obj->efile.bss, NULL); | ||
869 | if (err) | ||
870 | return err; | ||
871 | } | ||
872 | return 0; | ||
873 | } | ||
874 | |||
875 | static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict) | ||
812 | { | 876 | { |
813 | int i, map_idx, map_def_sz = 0, nr_syms, nr_maps = 0, nr_maps_glob = 0; | ||
814 | bool strict = !(flags & MAPS_RELAX_COMPAT); | ||
815 | Elf_Data *symbols = obj->efile.symbols; | 877 | Elf_Data *symbols = obj->efile.symbols; |
878 | int i, map_def_sz = 0, nr_maps = 0, nr_syms; | ||
816 | Elf_Data *data = NULL; | 879 | Elf_Data *data = NULL; |
817 | int ret = 0; | 880 | Elf_Scn *scn; |
881 | |||
882 | if (obj->efile.maps_shndx < 0) | ||
883 | return 0; | ||
818 | 884 | ||
819 | if (!symbols) | 885 | if (!symbols) |
820 | return -EINVAL; | 886 | return -EINVAL; |
821 | nr_syms = symbols->d_size / sizeof(GElf_Sym); | ||
822 | |||
823 | if (obj->efile.maps_shndx >= 0) { | ||
824 | Elf_Scn *scn = elf_getscn(obj->efile.elf, | ||
825 | obj->efile.maps_shndx); | ||
826 | 887 | ||
827 | if (scn) | 888 | scn = elf_getscn(obj->efile.elf, obj->efile.maps_shndx); |
828 | data = elf_getdata(scn, NULL); | 889 | if (scn) |
829 | if (!scn || !data) { | 890 | data = elf_getdata(scn, NULL); |
830 | pr_warning("failed to get Elf_Data from map section %d\n", | 891 | if (!scn || !data) { |
831 | obj->efile.maps_shndx); | 892 | pr_warning("failed to get Elf_Data from map section %d\n", |
832 | return -EINVAL; | 893 | obj->efile.maps_shndx); |
833 | } | 894 | return -EINVAL; |
834 | } | 895 | } |
835 | 896 | ||
836 | /* | 897 | /* |
@@ -840,16 +901,8 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags) | |||
840 | * | 901 | * |
841 | * TODO: Detect array of map and report error. | 902 | * TODO: Detect array of map and report error. |
842 | */ | 903 | */ |
843 | if (obj->caps.global_data) { | 904 | nr_syms = symbols->d_size / sizeof(GElf_Sym); |
844 | if (obj->efile.data_shndx >= 0) | 905 | for (i = 0; i < nr_syms; i++) { |
845 | nr_maps_glob++; | ||
846 | if (obj->efile.rodata_shndx >= 0) | ||
847 | nr_maps_glob++; | ||
848 | if (obj->efile.bss_shndx >= 0) | ||
849 | nr_maps_glob++; | ||
850 | } | ||
851 | |||
852 | for (i = 0; data && i < nr_syms; i++) { | ||
853 | GElf_Sym sym; | 906 | GElf_Sym sym; |
854 | 907 | ||
855 | if (!gelf_getsym(symbols, i, &sym)) | 908 | if (!gelf_getsym(symbols, i, &sym)) |
@@ -858,79 +911,56 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags) | |||
858 | continue; | 911 | continue; |
859 | nr_maps++; | 912 | nr_maps++; |
860 | } | 913 | } |
861 | |||
862 | if (!nr_maps && !nr_maps_glob) | ||
863 | return 0; | ||
864 | |||
865 | /* Assume equally sized map definitions */ | 914 | /* Assume equally sized map definitions */ |
866 | if (data) { | 915 | pr_debug("maps in %s: %d maps in %zd bytes\n", |
867 | pr_debug("maps in %s: %d maps in %zd bytes\n", obj->path, | 916 | obj->path, nr_maps, data->d_size); |
868 | nr_maps, data->d_size); | 917 | |
869 | 918 | map_def_sz = data->d_size / nr_maps; | |
870 | map_def_sz = data->d_size / nr_maps; | 919 | if (!data->d_size || (data->d_size % nr_maps) != 0) { |
871 | if (!data->d_size || (data->d_size % nr_maps) != 0) { | 920 | pr_warning("unable to determine map definition size " |
872 | pr_warning("unable to determine map definition size " | 921 | "section %s, %d maps in %zd bytes\n", |
873 | "section %s, %d maps in %zd bytes\n", | 922 | obj->path, nr_maps, data->d_size); |
874 | obj->path, nr_maps, data->d_size); | 923 | return -EINVAL; |
875 | return -EINVAL; | ||
876 | } | ||
877 | } | ||
878 | |||
879 | nr_maps += nr_maps_glob; | ||
880 | obj->maps = calloc(nr_maps, sizeof(obj->maps[0])); | ||
881 | if (!obj->maps) { | ||
882 | pr_warning("alloc maps for object failed\n"); | ||
883 | return -ENOMEM; | ||
884 | } | ||
885 | obj->nr_maps = nr_maps; | ||
886 | |||
887 | for (i = 0; i < nr_maps; i++) { | ||
888 | /* | ||
889 | * fill all fd with -1 so won't close incorrect | ||
890 | * fd (fd=0 is stdin) when failure (zclose won't close | ||
891 | * negative fd)). | ||
892 | */ | ||
893 | obj->maps[i].fd = -1; | ||
894 | obj->maps[i].inner_map_fd = -1; | ||
895 | } | 924 | } |
896 | 925 | ||
897 | /* | 926 | /* Fill obj->maps using data in "maps" section. */ |
898 | * Fill obj->maps using data in "maps" section. | 927 | for (i = 0; i < nr_syms; i++) { |
899 | */ | ||
900 | for (i = 0, map_idx = 0; data && i < nr_syms; i++) { | ||
901 | GElf_Sym sym; | 928 | GElf_Sym sym; |
902 | const char *map_name; | 929 | const char *map_name; |
903 | struct bpf_map_def *def; | 930 | struct bpf_map_def *def; |
931 | struct bpf_map *map; | ||
904 | 932 | ||
905 | if (!gelf_getsym(symbols, i, &sym)) | 933 | if (!gelf_getsym(symbols, i, &sym)) |
906 | continue; | 934 | continue; |
907 | if (sym.st_shndx != obj->efile.maps_shndx) | 935 | if (sym.st_shndx != obj->efile.maps_shndx) |
908 | continue; | 936 | continue; |
909 | 937 | ||
910 | map_name = elf_strptr(obj->efile.elf, | 938 | map = bpf_object__add_map(obj); |
911 | obj->efile.strtabidx, | 939 | if (IS_ERR(map)) |
940 | return PTR_ERR(map); | ||
941 | |||
942 | map_name = elf_strptr(obj->efile.elf, obj->efile.strtabidx, | ||
912 | sym.st_name); | 943 | sym.st_name); |
913 | if (!map_name) { | 944 | if (!map_name) { |
914 | pr_warning("failed to get map #%d name sym string for obj %s\n", | 945 | pr_warning("failed to get map #%d name sym string for obj %s\n", |
915 | map_idx, obj->path); | 946 | i, obj->path); |
916 | return -LIBBPF_ERRNO__FORMAT; | 947 | return -LIBBPF_ERRNO__FORMAT; |
917 | } | 948 | } |
918 | 949 | ||
919 | obj->maps[map_idx].libbpf_type = LIBBPF_MAP_UNSPEC; | 950 | map->libbpf_type = LIBBPF_MAP_UNSPEC; |
920 | obj->maps[map_idx].offset = sym.st_value; | 951 | map->offset = sym.st_value; |
921 | if (sym.st_value + map_def_sz > data->d_size) { | 952 | if (sym.st_value + map_def_sz > data->d_size) { |
922 | pr_warning("corrupted maps section in %s: last map \"%s\" too small\n", | 953 | pr_warning("corrupted maps section in %s: last map \"%s\" too small\n", |
923 | obj->path, map_name); | 954 | obj->path, map_name); |
924 | return -EINVAL; | 955 | return -EINVAL; |
925 | } | 956 | } |
926 | 957 | ||
927 | obj->maps[map_idx].name = strdup(map_name); | 958 | map->name = strdup(map_name); |
928 | if (!obj->maps[map_idx].name) { | 959 | if (!map->name) { |
929 | pr_warning("failed to alloc map name\n"); | 960 | pr_warning("failed to alloc map name\n"); |
930 | return -ENOMEM; | 961 | return -ENOMEM; |
931 | } | 962 | } |
932 | pr_debug("map %d is \"%s\"\n", map_idx, | 963 | pr_debug("map %d is \"%s\"\n", i, map->name); |
933 | obj->maps[map_idx].name); | ||
934 | def = (struct bpf_map_def *)(data->d_buf + sym.st_value); | 964 | def = (struct bpf_map_def *)(data->d_buf + sym.st_value); |
935 | /* | 965 | /* |
936 | * If the definition of the map in the object file fits in | 966 | * If the definition of the map in the object file fits in |
@@ -939,7 +969,7 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags) | |||
939 | * calloc above. | 969 | * calloc above. |
940 | */ | 970 | */ |
941 | if (map_def_sz <= sizeof(struct bpf_map_def)) { | 971 | if (map_def_sz <= sizeof(struct bpf_map_def)) { |
942 | memcpy(&obj->maps[map_idx].def, def, map_def_sz); | 972 | memcpy(&map->def, def, map_def_sz); |
943 | } else { | 973 | } else { |
944 | /* | 974 | /* |
945 | * Here the map structure being read is bigger than what | 975 | * Here the map structure being read is bigger than what |
@@ -959,37 +989,30 @@ static int bpf_object__init_maps(struct bpf_object *obj, int flags) | |||
959 | return -EINVAL; | 989 | return -EINVAL; |
960 | } | 990 | } |
961 | } | 991 | } |
962 | memcpy(&obj->maps[map_idx].def, def, | 992 | memcpy(&map->def, def, sizeof(struct bpf_map_def)); |
963 | sizeof(struct bpf_map_def)); | ||
964 | } | 993 | } |
965 | map_idx++; | ||
966 | } | 994 | } |
995 | return 0; | ||
996 | } | ||
967 | 997 | ||
968 | if (!obj->caps.global_data) | 998 | static int bpf_object__init_maps(struct bpf_object *obj, int flags) |
969 | goto finalize; | 999 | { |
1000 | bool strict = !(flags & MAPS_RELAX_COMPAT); | ||
1001 | int err; | ||
970 | 1002 | ||
971 | /* | 1003 | err = bpf_object__init_user_maps(obj, strict); |
972 | * Populate rest of obj->maps with libbpf internal maps. | 1004 | if (err) |
973 | */ | 1005 | return err; |
974 | if (obj->efile.data_shndx >= 0) | 1006 | |
975 | ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++], | 1007 | err = bpf_object__init_global_data_maps(obj); |
976 | LIBBPF_MAP_DATA, | 1008 | if (err) |
977 | obj->efile.data, | 1009 | return err; |
978 | &obj->sections.data); | 1010 | |
979 | if (!ret && obj->efile.rodata_shndx >= 0) | 1011 | if (obj->nr_maps) { |
980 | ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++], | ||
981 | LIBBPF_MAP_RODATA, | ||
982 | obj->efile.rodata, | ||
983 | &obj->sections.rodata); | ||
984 | if (!ret && obj->efile.bss_shndx >= 0) | ||
985 | ret = bpf_object__init_internal_map(obj, &obj->maps[map_idx++], | ||
986 | LIBBPF_MAP_BSS, | ||
987 | obj->efile.bss, NULL); | ||
988 | finalize: | ||
989 | if (!ret) | ||
990 | qsort(obj->maps, obj->nr_maps, sizeof(obj->maps[0]), | 1012 | qsort(obj->maps, obj->nr_maps, sizeof(obj->maps[0]), |
991 | compare_bpf_map); | 1013 | compare_bpf_map); |
992 | return ret; | 1014 | } |
1015 | return 0; | ||
993 | } | 1016 | } |
994 | 1017 | ||
995 | static bool section_have_execinstr(struct bpf_object *obj, int idx) | 1018 | static bool section_have_execinstr(struct bpf_object *obj, int idx) |
@@ -1262,14 +1285,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj, int flags) | |||
1262 | return -LIBBPF_ERRNO__FORMAT; | 1285 | return -LIBBPF_ERRNO__FORMAT; |
1263 | } | 1286 | } |
1264 | err = bpf_object__load_btf(obj, btf_data, btf_ext_data); | 1287 | err = bpf_object__load_btf(obj, btf_data, btf_ext_data); |
1265 | if (err) | 1288 | if (!err) |
1266 | return err; | ||
1267 | if (bpf_object__has_maps(obj)) { | ||
1268 | err = bpf_object__init_maps(obj, flags); | 1289 | err = bpf_object__init_maps(obj, flags); |
1269 | if (err) | 1290 | if (!err) |
1270 | return err; | 1291 | err = bpf_object__init_prog_names(obj); |
1271 | } | ||
1272 | err = bpf_object__init_prog_names(obj); | ||
1273 | return err; | 1292 | return err; |
1274 | } | 1293 | } |
1275 | 1294 | ||