From 7a1d55b987dfcbddecdb67eecc76fe555d4348ba Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 May 2021 18:04:45 -0700 Subject: gcov: combine common code There's a lot of duplicated code between gcc and clang implementations, move it over to fs.c to simplify the code, there's no reason to believe that for small data like this one would not just implement the simple convert_to_gcda() function. Link: https://lkml.kernel.org/r/20210315235453.e3fbb86e99a0.I08a3ee6dbe47ea3e8024956083f162884a958e40@changeid Signed-off-by: Johannes Berg Acked-by: Peter Oberparleiter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/gcov/fs.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) (limited to 'kernel/gcov/fs.c') diff --git a/kernel/gcov/fs.c b/kernel/gcov/fs.c index 82babf5aa077..2d29e1d1225d 100644 --- a/kernel/gcov/fs.c +++ b/kernel/gcov/fs.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "gcov.h" /** @@ -85,6 +86,121 @@ static int __init gcov_persist_setup(char *str) } __setup("gcov_persist=", gcov_persist_setup); +#define ITER_STRIDE PAGE_SIZE + +/** + * struct gcov_iterator - specifies current file position in logical records + * @info: associated profiling data + * @buffer: buffer containing file data + * @size: size of buffer + * @pos: current position in file + */ +struct gcov_iterator { + struct gcov_info *info; + void *buffer; + size_t size; + loff_t pos; +}; + +/** + * gcov_iter_new - allocate and initialize profiling data iterator + * @info: profiling data set to be iterated + * + * Return file iterator on success, %NULL otherwise. + */ +static struct gcov_iterator *gcov_iter_new(struct gcov_info *info) +{ + struct gcov_iterator *iter; + + iter = kzalloc(sizeof(struct gcov_iterator), GFP_KERNEL); + if (!iter) + goto err_free; + + iter->info = info; + /* Dry-run to get the actual buffer size. */ + iter->size = convert_to_gcda(NULL, info); + iter->buffer = vmalloc(iter->size); + if (!iter->buffer) + goto err_free; + + convert_to_gcda(iter->buffer, info); + + return iter; + +err_free: + kfree(iter); + return NULL; +} + + +/** + * gcov_iter_free - free iterator data + * @iter: file iterator + */ +static void gcov_iter_free(struct gcov_iterator *iter) +{ + vfree(iter->buffer); + kfree(iter); +} + +/** + * gcov_iter_get_info - return profiling data set for given file iterator + * @iter: file iterator + */ +static struct gcov_info *gcov_iter_get_info(struct gcov_iterator *iter) +{ + return iter->info; +} + +/** + * gcov_iter_start - reset file iterator to starting position + * @iter: file iterator + */ +static void gcov_iter_start(struct gcov_iterator *iter) +{ + iter->pos = 0; +} + +/** + * gcov_iter_next - advance file iterator to next logical record + * @iter: file iterator + * + * Return zero if new position is valid, non-zero if iterator has reached end. + */ +static int gcov_iter_next(struct gcov_iterator *iter) +{ + if (iter->pos < iter->size) + iter->pos += ITER_STRIDE; + + if (iter->pos >= iter->size) + return -EINVAL; + + return 0; +} + +/** + * gcov_iter_write - write data for current pos to seq_file + * @iter: file iterator + * @seq: seq_file handle + * + * Return zero on success, non-zero otherwise. + */ +static int gcov_iter_write(struct gcov_iterator *iter, struct seq_file *seq) +{ + size_t len; + + if (iter->pos >= iter->size) + return -EINVAL; + + len = ITER_STRIDE; + if (iter->pos + len > iter->size) + len = iter->size - iter->pos; + + seq_write(seq, iter->buffer + iter->pos, len); + + return 0; +} + /* * seq_file.start() implementation for gcov data files. Note that the * gcov_iterator interface is designed to be more restrictive than seq_file -- cgit v1.2.3 From 3180c44fe1baf14fc876a4cdad77ea7b51ddc387 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 May 2021 18:04:48 -0700 Subject: gcov: simplify buffer allocation Use just a single vmalloc() with struct_size() instead of a separate kmalloc() for the iter struct. Link: https://lkml.kernel.org/r/20210315235453.b6de4a92096e.Iac40a5166589cefbff8449e466bd1b38ea7a17af@changeid Signed-off-by: Johannes Berg Cc: Peter Oberparleiter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/gcov/fs.c | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) (limited to 'kernel/gcov/fs.c') diff --git a/kernel/gcov/fs.c b/kernel/gcov/fs.c index 2d29e1d1225d..40ea81c0475b 100644 --- a/kernel/gcov/fs.c +++ b/kernel/gcov/fs.c @@ -97,9 +97,9 @@ __setup("gcov_persist=", gcov_persist_setup); */ struct gcov_iterator { struct gcov_info *info; - void *buffer; size_t size; loff_t pos; + char buffer[]; }; /** @@ -111,25 +111,20 @@ struct gcov_iterator { static struct gcov_iterator *gcov_iter_new(struct gcov_info *info) { struct gcov_iterator *iter; + size_t size; + + /* Dry-run to get the actual buffer size. */ + size = convert_to_gcda(NULL, info); - iter = kzalloc(sizeof(struct gcov_iterator), GFP_KERNEL); + iter = vmalloc(struct_size(iter, buffer, size)); if (!iter) - goto err_free; + return NULL; iter->info = info; - /* Dry-run to get the actual buffer size. */ - iter->size = convert_to_gcda(NULL, info); - iter->buffer = vmalloc(iter->size); - if (!iter->buffer) - goto err_free; - + iter->size = size; convert_to_gcda(iter->buffer, info); return iter; - -err_free: - kfree(iter); - return NULL; } @@ -139,8 +134,7 @@ err_free: */ static void gcov_iter_free(struct gcov_iterator *iter) { - vfree(iter->buffer); - kfree(iter); + vfree(iter); } /** -- cgit v1.2.3 From 1391efa952e8b22088f8626fc63ade26767b92d6 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Thu, 6 May 2021 18:04:51 -0700 Subject: gcov: use kvmalloc() Using vmalloc() in gcov is really quite wasteful, many of the objects allocated are really small (e.g. I've seen 24 bytes.) Use kvmalloc() to automatically pick the better of kmalloc() or vmalloc() depending on the size. [johannes.berg@intel.com: fix clang-11+ build] Link: https://lkml.kernel.org/r/20210412214210.6e1ecca9cdc5.I24459763acf0591d5e6b31c7e3a59890d802f79c@changeid Link: https://lkml.kernel.org/r/20210315235453.799e7a9d627d.I741d0db096c6f312910f7f1bcdfde0fda20801a4@changeid Signed-off-by: Johannes Berg Reviewed-by: Nick Desaulniers Tested-by: Nick Desaulniers Cc: Peter Oberparleiter Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- kernel/gcov/clang.c | 12 ++++++------ kernel/gcov/fs.c | 6 +++--- kernel/gcov/gcc_4_7.c | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'kernel/gcov/fs.c') diff --git a/kernel/gcov/clang.c b/kernel/gcov/clang.c index 1741c65b4fb2..d43ffd0c5ddb 100644 --- a/kernel/gcov/clang.c +++ b/kernel/gcov/clang.c @@ -49,7 +49,7 @@ #include #include #include -#include +#include #include "gcov.h" typedef void (*llvm_gcov_callback)(void); @@ -333,8 +333,8 @@ void gcov_info_add(struct gcov_info *dst, struct gcov_info *src) static struct gcov_fn_info *gcov_fn_info_dup(struct gcov_fn_info *fn) { size_t cv_size; /* counter values size */ - struct gcov_fn_info *fn_dup = kmemdup(fn, sizeof(*fn), - GFP_KERNEL); + struct gcov_fn_info *fn_dup = kmemdup(fn, sizeof(*fn), GFP_KERNEL); + if (!fn_dup) return NULL; INIT_LIST_HEAD(&fn_dup->head); @@ -344,7 +344,7 @@ static struct gcov_fn_info *gcov_fn_info_dup(struct gcov_fn_info *fn) goto err_name; cv_size = fn->num_counters * sizeof(fn->counters[0]); - fn_dup->counters = vmalloc(cv_size); + fn_dup->counters = kvmalloc(cv_size, GFP_KERNEL); if (!fn_dup->counters) goto err_counters; memcpy(fn_dup->counters, fn->counters, cv_size); @@ -368,7 +368,7 @@ static struct gcov_fn_info *gcov_fn_info_dup(struct gcov_fn_info *fn) INIT_LIST_HEAD(&fn_dup->head); cv_size = fn->num_counters * sizeof(fn->counters[0]); - fn_dup->counters = vmalloc(cv_size); + fn_dup->counters = kvmalloc(cv_size, GFP_KERNEL); if (!fn_dup->counters) { kfree(fn_dup); return NULL; @@ -439,7 +439,7 @@ void gcov_info_free(struct gcov_info *info) struct gcov_fn_info *fn, *tmp; list_for_each_entry_safe(fn, tmp, &info->functions, head) { - vfree(fn->counters); + kvfree(fn->counters); list_del(&fn->head); kfree(fn); } diff --git a/kernel/gcov/fs.c b/kernel/gcov/fs.c index 40ea81c0475b..5c3086cad8f9 100644 --- a/kernel/gcov/fs.c +++ b/kernel/gcov/fs.c @@ -26,7 +26,7 @@ #include #include #include -#include +#include #include "gcov.h" /** @@ -116,7 +116,7 @@ static struct gcov_iterator *gcov_iter_new(struct gcov_info *info) /* Dry-run to get the actual buffer size. */ size = convert_to_gcda(NULL, info); - iter = vmalloc(struct_size(iter, buffer, size)); + iter = kvmalloc(struct_size(iter, buffer, size), GFP_KERNEL); if (!iter) return NULL; @@ -134,7 +134,7 @@ static struct gcov_iterator *gcov_iter_new(struct gcov_info *info) */ static void gcov_iter_free(struct gcov_iterator *iter) { - vfree(iter); + kvfree(iter); } /** diff --git a/kernel/gcov/gcc_4_7.c b/kernel/gcov/gcc_4_7.c index 1251f2434e90..460c12b7dfea 100644 --- a/kernel/gcov/gcc_4_7.c +++ b/kernel/gcov/gcc_4_7.c @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include "gcov.h" #if (__GNUC__ >= 10) @@ -309,7 +309,7 @@ struct gcov_info *gcov_info_dup(struct gcov_info *info) cv_size = sizeof(gcov_type) * sci_ptr->num; - dci_ptr->values = vmalloc(cv_size); + dci_ptr->values = kvmalloc(cv_size, GFP_KERNEL); if (!dci_ptr->values) goto err_free; @@ -351,7 +351,7 @@ void gcov_info_free(struct gcov_info *info) ci_ptr = info->functions[fi_idx]->ctrs; for (ct_idx = 0; ct_idx < active; ct_idx++, ci_ptr++) - vfree(ci_ptr->values); + kvfree(ci_ptr->values); kfree(info->functions[fi_idx]); } -- cgit v1.2.3