diff options
author | Song Liu <songliubraving@fb.com> | 2019-03-12 01:30:38 -0400 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2019-03-19 15:52:06 -0400 |
commit | 34be16466d4dc06f3d604dafbcdb3327b72e78da (patch) | |
tree | ac000724283e4cf8052a9604b71c8f6885d53c2e | |
parent | 71184c6ab7e60fd59d8dbc8fed62a1c753dc4934 (diff) |
tools lib bpf: Introduce bpf_program__get_prog_info_linear()
Currently, bpf_prog_info includes 9 arrays. The user has the option to
fetch any combination of these arrays. However, this requires a lot of
handling.
This work becomes more tricky when we need to store bpf_prog_info to a
file, because these arrays are allocated independently.
This patch introduces 'struct bpf_prog_info_linear', which stores arrays
of bpf_prog_info in continuous memory.
Helper functions are introduced to unify the work to get different sets
of bpf_prog_info. Specifically, bpf_program__get_prog_info_linear()
allows the user to select which arrays to fetch, and handles details for
the user.
Please see the comments right before 'enum bpf_prog_info_array' for more
details and examples.
Signed-off-by: Song Liu <songliubraving@fb.com>
Reviewed-by: Jiri Olsa <jolsa@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lkml.kernel.org/r/ce92c091-e80d-a0c1-4aa0-987706c42b20@iogearbox.net
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: Alexei Starovoitov <ast@kernel.org>
Cc: kernel-team@fb.com
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stanislav Fomichev <sdf@google.com>
Link: http://lkml.kernel.org/r/20190312053051.2690567-3-songliubraving@fb.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
-rw-r--r-- | tools/lib/bpf/libbpf.c | 251 | ||||
-rw-r--r-- | tools/lib/bpf/libbpf.h | 63 | ||||
-rw-r--r-- | tools/lib/bpf/libbpf.map | 3 |
3 files changed, 317 insertions, 0 deletions
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index 4884557aa17f..8fb6e89b4b2c 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c | |||
@@ -112,6 +112,11 @@ void libbpf_print(enum libbpf_print_level level, const char *format, ...) | |||
112 | # define LIBBPF_ELF_C_READ_MMAP ELF_C_READ | 112 | # define LIBBPF_ELF_C_READ_MMAP ELF_C_READ |
113 | #endif | 113 | #endif |
114 | 114 | ||
115 | static inline __u64 ptr_to_u64(const void *ptr) | ||
116 | { | ||
117 | return (__u64) (unsigned long) ptr; | ||
118 | } | ||
119 | |||
115 | struct bpf_capabilities { | 120 | struct bpf_capabilities { |
116 | /* v4.14: kernel support for program & map names. */ | 121 | /* v4.14: kernel support for program & map names. */ |
117 | __u32 name:1; | 122 | __u32 name:1; |
@@ -2997,3 +3002,249 @@ bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size, | |||
2997 | ring_buffer_write_tail(header, data_tail); | 3002 | ring_buffer_write_tail(header, data_tail); |
2998 | return ret; | 3003 | return ret; |
2999 | } | 3004 | } |
3005 | |||
3006 | struct bpf_prog_info_array_desc { | ||
3007 | int array_offset; /* e.g. offset of jited_prog_insns */ | ||
3008 | int count_offset; /* e.g. offset of jited_prog_len */ | ||
3009 | int size_offset; /* > 0: offset of rec size, | ||
3010 | * < 0: fix size of -size_offset | ||
3011 | */ | ||
3012 | }; | ||
3013 | |||
3014 | static struct bpf_prog_info_array_desc bpf_prog_info_array_desc[] = { | ||
3015 | [BPF_PROG_INFO_JITED_INSNS] = { | ||
3016 | offsetof(struct bpf_prog_info, jited_prog_insns), | ||
3017 | offsetof(struct bpf_prog_info, jited_prog_len), | ||
3018 | -1, | ||
3019 | }, | ||
3020 | [BPF_PROG_INFO_XLATED_INSNS] = { | ||
3021 | offsetof(struct bpf_prog_info, xlated_prog_insns), | ||
3022 | offsetof(struct bpf_prog_info, xlated_prog_len), | ||
3023 | -1, | ||
3024 | }, | ||
3025 | [BPF_PROG_INFO_MAP_IDS] = { | ||
3026 | offsetof(struct bpf_prog_info, map_ids), | ||
3027 | offsetof(struct bpf_prog_info, nr_map_ids), | ||
3028 | -(int)sizeof(__u32), | ||
3029 | }, | ||
3030 | [BPF_PROG_INFO_JITED_KSYMS] = { | ||
3031 | offsetof(struct bpf_prog_info, jited_ksyms), | ||
3032 | offsetof(struct bpf_prog_info, nr_jited_ksyms), | ||
3033 | -(int)sizeof(__u64), | ||
3034 | }, | ||
3035 | [BPF_PROG_INFO_JITED_FUNC_LENS] = { | ||
3036 | offsetof(struct bpf_prog_info, jited_func_lens), | ||
3037 | offsetof(struct bpf_prog_info, nr_jited_func_lens), | ||
3038 | -(int)sizeof(__u32), | ||
3039 | }, | ||
3040 | [BPF_PROG_INFO_FUNC_INFO] = { | ||
3041 | offsetof(struct bpf_prog_info, func_info), | ||
3042 | offsetof(struct bpf_prog_info, nr_func_info), | ||
3043 | offsetof(struct bpf_prog_info, func_info_rec_size), | ||
3044 | }, | ||
3045 | [BPF_PROG_INFO_LINE_INFO] = { | ||
3046 | offsetof(struct bpf_prog_info, line_info), | ||
3047 | offsetof(struct bpf_prog_info, nr_line_info), | ||
3048 | offsetof(struct bpf_prog_info, line_info_rec_size), | ||
3049 | }, | ||
3050 | [BPF_PROG_INFO_JITED_LINE_INFO] = { | ||
3051 | offsetof(struct bpf_prog_info, jited_line_info), | ||
3052 | offsetof(struct bpf_prog_info, nr_jited_line_info), | ||
3053 | offsetof(struct bpf_prog_info, jited_line_info_rec_size), | ||
3054 | }, | ||
3055 | [BPF_PROG_INFO_PROG_TAGS] = { | ||
3056 | offsetof(struct bpf_prog_info, prog_tags), | ||
3057 | offsetof(struct bpf_prog_info, nr_prog_tags), | ||
3058 | -(int)sizeof(__u8) * BPF_TAG_SIZE, | ||
3059 | }, | ||
3060 | |||
3061 | }; | ||
3062 | |||
3063 | static __u32 bpf_prog_info_read_offset_u32(struct bpf_prog_info *info, int offset) | ||
3064 | { | ||
3065 | __u32 *array = (__u32 *)info; | ||
3066 | |||
3067 | if (offset >= 0) | ||
3068 | return array[offset / sizeof(__u32)]; | ||
3069 | return -(int)offset; | ||
3070 | } | ||
3071 | |||
3072 | static __u64 bpf_prog_info_read_offset_u64(struct bpf_prog_info *info, int offset) | ||
3073 | { | ||
3074 | __u64 *array = (__u64 *)info; | ||
3075 | |||
3076 | if (offset >= 0) | ||
3077 | return array[offset / sizeof(__u64)]; | ||
3078 | return -(int)offset; | ||
3079 | } | ||
3080 | |||
3081 | static void bpf_prog_info_set_offset_u32(struct bpf_prog_info *info, int offset, | ||
3082 | __u32 val) | ||
3083 | { | ||
3084 | __u32 *array = (__u32 *)info; | ||
3085 | |||
3086 | if (offset >= 0) | ||
3087 | array[offset / sizeof(__u32)] = val; | ||
3088 | } | ||
3089 | |||
3090 | static void bpf_prog_info_set_offset_u64(struct bpf_prog_info *info, int offset, | ||
3091 | __u64 val) | ||
3092 | { | ||
3093 | __u64 *array = (__u64 *)info; | ||
3094 | |||
3095 | if (offset >= 0) | ||
3096 | array[offset / sizeof(__u64)] = val; | ||
3097 | } | ||
3098 | |||
3099 | struct bpf_prog_info_linear * | ||
3100 | bpf_program__get_prog_info_linear(int fd, __u64 arrays) | ||
3101 | { | ||
3102 | struct bpf_prog_info_linear *info_linear; | ||
3103 | struct bpf_prog_info info = {}; | ||
3104 | __u32 info_len = sizeof(info); | ||
3105 | __u32 data_len = 0; | ||
3106 | int i, err; | ||
3107 | void *ptr; | ||
3108 | |||
3109 | if (arrays >> BPF_PROG_INFO_LAST_ARRAY) | ||
3110 | return ERR_PTR(-EINVAL); | ||
3111 | |||
3112 | /* step 1: get array dimensions */ | ||
3113 | err = bpf_obj_get_info_by_fd(fd, &info, &info_len); | ||
3114 | if (err) { | ||
3115 | pr_debug("can't get prog info: %s", strerror(errno)); | ||
3116 | return ERR_PTR(-EFAULT); | ||
3117 | } | ||
3118 | |||
3119 | /* step 2: calculate total size of all arrays */ | ||
3120 | for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) { | ||
3121 | bool include_array = (arrays & (1UL << i)) > 0; | ||
3122 | struct bpf_prog_info_array_desc *desc; | ||
3123 | __u32 count, size; | ||
3124 | |||
3125 | desc = bpf_prog_info_array_desc + i; | ||
3126 | |||
3127 | /* kernel is too old to support this field */ | ||
3128 | if (info_len < desc->array_offset + sizeof(__u32) || | ||
3129 | info_len < desc->count_offset + sizeof(__u32) || | ||
3130 | (desc->size_offset > 0 && info_len < desc->size_offset)) | ||
3131 | include_array = false; | ||
3132 | |||
3133 | if (!include_array) { | ||
3134 | arrays &= ~(1UL << i); /* clear the bit */ | ||
3135 | continue; | ||
3136 | } | ||
3137 | |||
3138 | count = bpf_prog_info_read_offset_u32(&info, desc->count_offset); | ||
3139 | size = bpf_prog_info_read_offset_u32(&info, desc->size_offset); | ||
3140 | |||
3141 | data_len += count * size; | ||
3142 | } | ||
3143 | |||
3144 | /* step 3: allocate continuous memory */ | ||
3145 | data_len = roundup(data_len, sizeof(__u64)); | ||
3146 | info_linear = malloc(sizeof(struct bpf_prog_info_linear) + data_len); | ||
3147 | if (!info_linear) | ||
3148 | return ERR_PTR(-ENOMEM); | ||
3149 | |||
3150 | /* step 4: fill data to info_linear->info */ | ||
3151 | info_linear->arrays = arrays; | ||
3152 | memset(&info_linear->info, 0, sizeof(info)); | ||
3153 | ptr = info_linear->data; | ||
3154 | |||
3155 | for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) { | ||
3156 | struct bpf_prog_info_array_desc *desc; | ||
3157 | __u32 count, size; | ||
3158 | |||
3159 | if ((arrays & (1UL << i)) == 0) | ||
3160 | continue; | ||
3161 | |||
3162 | desc = bpf_prog_info_array_desc + i; | ||
3163 | count = bpf_prog_info_read_offset_u32(&info, desc->count_offset); | ||
3164 | size = bpf_prog_info_read_offset_u32(&info, desc->size_offset); | ||
3165 | bpf_prog_info_set_offset_u32(&info_linear->info, | ||
3166 | desc->count_offset, count); | ||
3167 | bpf_prog_info_set_offset_u32(&info_linear->info, | ||
3168 | desc->size_offset, size); | ||
3169 | bpf_prog_info_set_offset_u64(&info_linear->info, | ||
3170 | desc->array_offset, | ||
3171 | ptr_to_u64(ptr)); | ||
3172 | ptr += count * size; | ||
3173 | } | ||
3174 | |||
3175 | /* step 5: call syscall again to get required arrays */ | ||
3176 | err = bpf_obj_get_info_by_fd(fd, &info_linear->info, &info_len); | ||
3177 | if (err) { | ||
3178 | pr_debug("can't get prog info: %s", strerror(errno)); | ||
3179 | free(info_linear); | ||
3180 | return ERR_PTR(-EFAULT); | ||
3181 | } | ||
3182 | |||
3183 | /* step 6: verify the data */ | ||
3184 | for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) { | ||
3185 | struct bpf_prog_info_array_desc *desc; | ||
3186 | __u32 v1, v2; | ||
3187 | |||
3188 | if ((arrays & (1UL << i)) == 0) | ||
3189 | continue; | ||
3190 | |||
3191 | desc = bpf_prog_info_array_desc + i; | ||
3192 | v1 = bpf_prog_info_read_offset_u32(&info, desc->count_offset); | ||
3193 | v2 = bpf_prog_info_read_offset_u32(&info_linear->info, | ||
3194 | desc->count_offset); | ||
3195 | if (v1 != v2) | ||
3196 | pr_warning("%s: mismatch in element count\n", __func__); | ||
3197 | |||
3198 | v1 = bpf_prog_info_read_offset_u32(&info, desc->size_offset); | ||
3199 | v2 = bpf_prog_info_read_offset_u32(&info_linear->info, | ||
3200 | desc->size_offset); | ||
3201 | if (v1 != v2) | ||
3202 | pr_warning("%s: mismatch in rec size\n", __func__); | ||
3203 | } | ||
3204 | |||
3205 | /* step 7: update info_len and data_len */ | ||
3206 | info_linear->info_len = sizeof(struct bpf_prog_info); | ||
3207 | info_linear->data_len = data_len; | ||
3208 | |||
3209 | return info_linear; | ||
3210 | } | ||
3211 | |||
3212 | void bpf_program__bpil_addr_to_offs(struct bpf_prog_info_linear *info_linear) | ||
3213 | { | ||
3214 | int i; | ||
3215 | |||
3216 | for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) { | ||
3217 | struct bpf_prog_info_array_desc *desc; | ||
3218 | __u64 addr, offs; | ||
3219 | |||
3220 | if ((info_linear->arrays & (1UL << i)) == 0) | ||
3221 | continue; | ||
3222 | |||
3223 | desc = bpf_prog_info_array_desc + i; | ||
3224 | addr = bpf_prog_info_read_offset_u64(&info_linear->info, | ||
3225 | desc->array_offset); | ||
3226 | offs = addr - ptr_to_u64(info_linear->data); | ||
3227 | bpf_prog_info_set_offset_u64(&info_linear->info, | ||
3228 | desc->array_offset, offs); | ||
3229 | } | ||
3230 | } | ||
3231 | |||
3232 | void bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear) | ||
3233 | { | ||
3234 | int i; | ||
3235 | |||
3236 | for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) { | ||
3237 | struct bpf_prog_info_array_desc *desc; | ||
3238 | __u64 addr, offs; | ||
3239 | |||
3240 | if ((info_linear->arrays & (1UL << i)) == 0) | ||
3241 | continue; | ||
3242 | |||
3243 | desc = bpf_prog_info_array_desc + i; | ||
3244 | offs = bpf_prog_info_read_offset_u64(&info_linear->info, | ||
3245 | desc->array_offset); | ||
3246 | addr = offs + ptr_to_u64(info_linear->data); | ||
3247 | bpf_prog_info_set_offset_u64(&info_linear->info, | ||
3248 | desc->array_offset, addr); | ||
3249 | } | ||
3250 | } | ||
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h index aa1521a51687..c70785cc8ef5 100644 --- a/tools/lib/bpf/libbpf.h +++ b/tools/lib/bpf/libbpf.h | |||
@@ -378,6 +378,69 @@ LIBBPF_API bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex); | |||
378 | LIBBPF_API bool bpf_probe_helper(enum bpf_func_id id, | 378 | LIBBPF_API bool bpf_probe_helper(enum bpf_func_id id, |
379 | enum bpf_prog_type prog_type, __u32 ifindex); | 379 | enum bpf_prog_type prog_type, __u32 ifindex); |
380 | 380 | ||
381 | /* | ||
382 | * Get bpf_prog_info in continuous memory | ||
383 | * | ||
384 | * struct bpf_prog_info has multiple arrays. The user has option to choose | ||
385 | * arrays to fetch from kernel. The following APIs provide an uniform way to | ||
386 | * fetch these data. All arrays in bpf_prog_info are stored in a single | ||
387 | * continuous memory region. This makes it easy to store the info in a | ||
388 | * file. | ||
389 | * | ||
390 | * Before writing bpf_prog_info_linear to files, it is necessary to | ||
391 | * translate pointers in bpf_prog_info to offsets. Helper functions | ||
392 | * bpf_program__bpil_addr_to_offs() and bpf_program__bpil_offs_to_addr() | ||
393 | * are introduced to switch between pointers and offsets. | ||
394 | * | ||
395 | * Examples: | ||
396 | * # To fetch map_ids and prog_tags: | ||
397 | * __u64 arrays = (1UL << BPF_PROG_INFO_MAP_IDS) | | ||
398 | * (1UL << BPF_PROG_INFO_PROG_TAGS); | ||
399 | * struct bpf_prog_info_linear *info_linear = | ||
400 | * bpf_program__get_prog_info_linear(fd, arrays); | ||
401 | * | ||
402 | * # To save data in file | ||
403 | * bpf_program__bpil_addr_to_offs(info_linear); | ||
404 | * write(f, info_linear, sizeof(*info_linear) + info_linear->data_len); | ||
405 | * | ||
406 | * # To read data from file | ||
407 | * read(f, info_linear, <proper_size>); | ||
408 | * bpf_program__bpil_offs_to_addr(info_linear); | ||
409 | */ | ||
410 | enum bpf_prog_info_array { | ||
411 | BPF_PROG_INFO_FIRST_ARRAY = 0, | ||
412 | BPF_PROG_INFO_JITED_INSNS = 0, | ||
413 | BPF_PROG_INFO_XLATED_INSNS, | ||
414 | BPF_PROG_INFO_MAP_IDS, | ||
415 | BPF_PROG_INFO_JITED_KSYMS, | ||
416 | BPF_PROG_INFO_JITED_FUNC_LENS, | ||
417 | BPF_PROG_INFO_FUNC_INFO, | ||
418 | BPF_PROG_INFO_LINE_INFO, | ||
419 | BPF_PROG_INFO_JITED_LINE_INFO, | ||
420 | BPF_PROG_INFO_PROG_TAGS, | ||
421 | BPF_PROG_INFO_LAST_ARRAY, | ||
422 | }; | ||
423 | |||
424 | struct bpf_prog_info_linear { | ||
425 | /* size of struct bpf_prog_info, when the tool is compiled */ | ||
426 | __u32 info_len; | ||
427 | /* total bytes allocated for data, round up to 8 bytes */ | ||
428 | __u32 data_len; | ||
429 | /* which arrays are included in data */ | ||
430 | __u64 arrays; | ||
431 | struct bpf_prog_info info; | ||
432 | __u8 data[]; | ||
433 | }; | ||
434 | |||
435 | LIBBPF_API struct bpf_prog_info_linear * | ||
436 | bpf_program__get_prog_info_linear(int fd, __u64 arrays); | ||
437 | |||
438 | LIBBPF_API void | ||
439 | bpf_program__bpil_addr_to_offs(struct bpf_prog_info_linear *info_linear); | ||
440 | |||
441 | LIBBPF_API void | ||
442 | bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear); | ||
443 | |||
381 | #ifdef __cplusplus | 444 | #ifdef __cplusplus |
382 | } /* extern "C" */ | 445 | } /* extern "C" */ |
383 | #endif | 446 | #endif |
diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map index 778a26702a70..f3ce50500cf2 100644 --- a/tools/lib/bpf/libbpf.map +++ b/tools/lib/bpf/libbpf.map | |||
@@ -153,4 +153,7 @@ LIBBPF_0.0.2 { | |||
153 | xsk_socket__delete; | 153 | xsk_socket__delete; |
154 | xsk_umem__fd; | 154 | xsk_umem__fd; |
155 | xsk_socket__fd; | 155 | xsk_socket__fd; |
156 | bpf_program__get_prog_info_linear; | ||
157 | bpf_program__bpil_addr_to_offs; | ||
158 | bpf_program__bpil_offs_to_addr; | ||
156 | } LIBBPF_0.0.1; | 159 | } LIBBPF_0.0.1; |