Extend the allocinfo filtering mechanism to allow users to filter tags based on the total number of bytes allocated [min_size, max_size]. The size range is inclusive. Filtering by size involves retrieving allocinfo per-CPU counters, which is an expensive operation. Hence, the performance of size-based filtering will be worse than other filters. Signed-off-by: Abhishek Bapat --- include/uapi/linux/alloc_tag.h | 8 +++- lib/alloc_tag.c | 68 +++++++++++++++++++++++++++------- 2 files changed, 62 insertions(+), 14 deletions(-) diff --git a/include/uapi/linux/alloc_tag.h b/include/uapi/linux/alloc_tag.h index cffb0c46e0b1..0e648192df4d 100644 --- a/include/uapi/linux/alloc_tag.h +++ b/include/uapi/linux/alloc_tag.h @@ -39,13 +39,17 @@ enum { ALLOCINFO_FILTER_FUNCTION, ALLOCINFO_FILTER_FILENAME, ALLOCINFO_FILTER_LINENO, - __ALLOCINFO_FILTER_LAST = ALLOCINFO_FILTER_LINENO + ALLOCINFO_FILTER_MIN_SIZE, + ALLOCINFO_FILTER_MAX_SIZE, + __ALLOCINFO_FILTER_LAST = ALLOCINFO_FILTER_MAX_SIZE }; #define ALLOCINFO_FILTER_MASK_MODNAME (1 << ALLOCINFO_FILTER_MODNAME) #define ALLOCINFO_FILTER_MASK_FUNCTION (1 << ALLOCINFO_FILTER_FUNCTION) #define ALLOCINFO_FILTER_MASK_FILENAME (1 << ALLOCINFO_FILTER_FILENAME) #define ALLOCINFO_FILTER_MASK_LINENO (1 << ALLOCINFO_FILTER_LINENO) +#define ALLOCINFO_FILTER_MASK_MIN_SIZE (1 << ALLOCINFO_FILTER_MIN_SIZE) +#define ALLOCINFO_FILTER_MASK_MAX_SIZE (1 << ALLOCINFO_FILTER_MAX_SIZE) #define ALLOCINFO_FILTER_MASKS \ ((1 << (__ALLOCINFO_FILTER_LAST + 1)) - 1) @@ -53,6 +57,8 @@ enum { struct allocinfo_filter { __u64 mask; /* bitmask of the filter fields used */ struct allocinfo_tag fields; + __u64 min_size; + __u64 max_size; }; struct allocinfo_get_at { diff --git a/lib/alloc_tag.c b/lib/alloc_tag.c index 93bc976ac505..ddc6946f56ab 100644 --- a/lib/alloc_tag.c +++ b/lib/alloc_tag.c @@ -191,15 +191,26 @@ static int allocinfo_cmp_str(const char *str, const char *template) return strncmp(allocinfo_str(str), template, ALLOCINFO_STR_SIZE); } +/* Fetch the per-CPU counters */ +static inline struct alloc_tag_counters allocinfo_prefetch_counters(struct codetag *ct) +{ + return alloc_tag_read(ct_to_alloc_tag(ct)); +} + /* * Populates the UAPI allocinfo_tag_data structure with active runtime * profiling counters extracted from the given kernel codetag. */ static void allocinfo_to_params(struct codetag *ct, - struct allocinfo_tag_data *data) + struct allocinfo_tag_data *data, + struct alloc_tag_counters *counters) { - struct alloc_tag *tag = ct_to_alloc_tag(ct); - struct alloc_tag_counters counter = alloc_tag_read(tag); + struct alloc_tag_counters local_counters; + + if (!counters) { + local_counters = allocinfo_prefetch_counters(ct); + counters = &local_counters; + } if (ct->modname) allocinfo_copy_str(data->tag.modname, ct->modname); @@ -208,9 +219,9 @@ static void allocinfo_to_params(struct codetag *ct, allocinfo_copy_str(data->tag.function, ct->function); allocinfo_copy_str(data->tag.filename, ct->filename); data->tag.lineno = ct->lineno; - data->counter.bytes = counter.bytes; - data->counter.calls = counter.calls; - data->counter.accurate = !alloc_tag_is_inaccurate(tag); + data->counter.bytes = counters->bytes; + data->counter.calls = counters->calls; + data->counter.accurate = !alloc_tag_is_inaccurate(ct_to_alloc_tag(ct)); } /* @@ -234,7 +245,9 @@ static int allocinfo_ioctl_get_content_id(struct seq_file *m, void __user *arg) * Verifies whether a given codetag satisfies the active filtering criteria by * matching it's characteristics against the specified filter. */ -static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter) +static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter, + struct alloc_tag_counters *counters, + bool *fetched_counters) { if (!filter || !filter->mask) return true; @@ -247,20 +260,34 @@ static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter) return false; } else if (allocinfo_cmp_str(ct->modname, filter->fields.modname)) return false; + } } if ((filter->mask & ALLOCINFO_FILTER_MASK_FUNCTION) && - ct->function && (allocinfo_cmp_str(ct->function, filter->fields.function))) + ct->function && allocinfo_cmp_str(ct->function, filter->fields.function)) return false; if ((filter->mask & ALLOCINFO_FILTER_MASK_FILENAME) && - ct->filename && (allocinfo_cmp_str(ct->filename, filter->fields.filename))) + ct->filename && allocinfo_cmp_str(ct->filename, filter->fields.filename)) return false; if ((filter->mask & ALLOCINFO_FILTER_MASK_LINENO) && ct->lineno != filter->fields.lineno) return false; + if (filter->mask & (ALLOCINFO_FILTER_MASK_MIN_SIZE | ALLOCINFO_FILTER_MASK_MAX_SIZE)) { + if (!*fetched_counters) { + *counters = allocinfo_prefetch_counters(ct); + *fetched_counters = true; + } + if ((filter->mask & ALLOCINFO_FILTER_MASK_MIN_SIZE) && + counters->bytes < filter->min_size) + return false; + if ((filter->mask & ALLOCINFO_FILTER_MASK_MAX_SIZE) && + counters->bytes > filter->max_size) + return false; + } + return true; } @@ -274,6 +301,8 @@ static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg) struct codetag *ct; struct allocinfo_get_at params = {0}; __u64 skip_count; + struct alloc_tag_counters counters; + bool fetched_counters; if (copy_from_user(¶ms, arg, sizeof(params))) return -EFAULT; @@ -281,6 +310,11 @@ static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg) if (params.filter.mask & ~ALLOCINFO_FILTER_MASKS) return -EINVAL; + if ((params.filter.mask & ALLOCINFO_FILTER_MASK_MIN_SIZE) && + (params.filter.mask & ALLOCINFO_FILTER_MASK_MAX_SIZE) && + params.filter.min_size > params.filter.max_size) + return -EINVAL; + priv = m->private; mutex_lock(&priv->ioctl_lock); @@ -304,7 +338,8 @@ static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg) ct = codetag_next_ct(&priv->ioctl_iter); while (ct) { - if (matches_filter(ct, &priv->filter)) { + fetched_counters = false; + if (matches_filter(ct, &priv->filter, &counters, &fetched_counters)) { if (skip_count == 0) break; skip_count--; @@ -313,7 +348,7 @@ static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg) } if (ct) { - allocinfo_to_params(ct, ¶ms.data); + allocinfo_to_params(ct, ¶ms.data, fetched_counters ? &counters : NULL); priv->positioned = true; } @@ -339,6 +374,8 @@ static int allocinfo_ioctl_get_next(struct seq_file *m, void __user *arg) struct codetag *ct; struct allocinfo_tag_data params; int ret = 0; + struct alloc_tag_counters counters; + bool fetched_counters; memset(¶ms, 0, sizeof(params)); priv = m->private; @@ -352,10 +389,15 @@ static int allocinfo_ioctl_get_next(struct seq_file *m, void __user *arg) } ct = codetag_next_ct(&priv->ioctl_iter); - while (ct && !matches_filter(ct, &priv->filter)) + while (ct) { + fetched_counters = false; + if (matches_filter(ct, &priv->filter, &counters, &fetched_counters)) + break; ct = codetag_next_ct(&priv->ioctl_iter); + } + if (ct) - allocinfo_to_params(ct, ¶ms); + allocinfo_to_params(ct, ¶ms, fetched_counters ? &counters : NULL); if (!ct) { priv->positioned = false; -- 2.54.0.1032.g2f8565e1d1-goog