mirror of
https://github.com/torvalds/linux.git
synced 2026-09-12 20:53:03 +02:00
alloc_tag: add size-based filtering to ioctl
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. Link: https://lore.kernel.org/0a7653b70ae0d64e967fbea0e933bc35f8ac656e.1783532853.git.abhishekbapat@google.com Signed-off-by: Abhishek Bapat <abhishekbapat@google.com> Acked-by: Hao Ge <hao.ge@linux.dev> Acked-by: Suren Baghdasaryan <surenb@google.com> Cc: Jonathan Corbet <corbet@lwn.net> Cc: Kent Overstreet <kent.overstreet@linux.dev> Cc: Sourav Panda <souravpanda@google.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
This commit is contained in:
parent
5732a4e4c1
commit
6f6769ea88
|
|
@ -50,13 +50,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)
|
||||
|
|
@ -64,6 +68,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 {
|
||||
|
|
|
|||
|
|
@ -211,16 +211,20 @@ 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);
|
||||
|
||||
if (ct->modname)
|
||||
allocinfo_copy_str(data->tag.modname, ct->modname);
|
||||
else
|
||||
|
|
@ -228,9 +232,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));
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -254,7 +258,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 its 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;
|
||||
|
|
@ -281,6 +287,19 @@ static bool matches_filter(struct codetag *ct, struct allocinfo_filter *filter)
|
|||
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;
|
||||
}
|
||||
|
||||
|
|
@ -294,6 +313,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;
|
||||
|
|
@ -301,6 +322,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);
|
||||
|
|
@ -324,7 +350,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--;
|
||||
|
|
@ -333,7 +360,9 @@ static int allocinfo_ioctl_get_at(struct seq_file *m, void __user *arg)
|
|||
}
|
||||
|
||||
if (ct) {
|
||||
allocinfo_to_params(ct, ¶ms.data);
|
||||
if (!fetched_counters)
|
||||
counters = allocinfo_prefetch_counters(ct);
|
||||
allocinfo_to_params(ct, ¶ms.data, &counters);
|
||||
priv->positioned = true;
|
||||
}
|
||||
|
||||
|
|
@ -359,6 +388,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;
|
||||
|
|
@ -372,11 +403,18 @@ 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);
|
||||
}
|
||||
|
||||
if (ct) {
|
||||
if (!fetched_counters)
|
||||
counters = allocinfo_prefetch_counters(ct);
|
||||
allocinfo_to_params(ct, ¶ms, &counters);
|
||||
}
|
||||
if (!ct) {
|
||||
priv->positioned = false;
|
||||
ret = -ENOENT;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user