audit: merge loops in __audit_inode_child()

Whenever there's audit context, __audit_inode_child() gets called
numerous times, which can lead to high latency in scenarios that
create too many sysfs/debugfs entries at once, for instance, upon
device_add_disk() invocation.

   # uname -r
   6.18.0-rc2+

   # auditctl -a always,exit -F path=/tmp -k foo
   # time insmod loop max_loop=1000
   real 0m46.676s
   user 0m0.000s
   sys 0m46.405s

   # perf record -a insmod loop max_loop=1000
   # perf report --stdio |grep __audit_inode_child
   32.73%  insmod [kernel.kallsyms] [k] __audit_inode_child

__audit_inode_child() searches for both the parent and the child
in two different loops that iterate over the same list. This
process can be optimized by merging these into a single loop,
without changing the function behavior or affecting the code's
readability.

This patch merges the two loops that walk through the list
context->names_list into a single loop. This optimization resulted
in around 51% performance enhancement for the benchmark.

   # uname -r
   6.18.0-rc2-enhancedv3+

   # auditctl -a always,exit -F path=/tmp -k foo
   # time insmod loop max_loop=1000
   real 0m22.899s
   user 0m0.001s
   sys 0m22.652s

Signed-off-by: Ricardo Robaina <rrobaina@redhat.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
This commit is contained in:
Ricardo Robaina 2025-10-31 09:33:28 -03:00 committed by Paul Moore
parent 77563f3d47
commit c8a3dfe731
1 changed files with 21 additions and 26 deletions

View File

@ -2416,40 +2416,35 @@ void __audit_inode_child(struct inode *parent,
if (inode) if (inode)
handle_one(inode); handle_one(inode);
/* look for a parent entry first */
list_for_each_entry(n, &context->names_list, list) {
if (!n->name ||
(n->type != AUDIT_TYPE_PARENT &&
n->type != AUDIT_TYPE_UNKNOWN))
continue;
if (n->ino == parent->i_ino && n->dev == parent->i_sb->s_dev &&
!audit_compare_dname_path(dname,
n->name->name, n->name_len)) {
if (n->type == AUDIT_TYPE_UNKNOWN)
n->type = AUDIT_TYPE_PARENT;
found_parent = n;
break;
}
}
cond_resched();
/* is there a matching child entry? */
list_for_each_entry(n, &context->names_list, list) { list_for_each_entry(n, &context->names_list, list) {
/* can only match entries that have a name */ /* can only match entries that have a name */
if (!n->name || if (!n->name)
(n->type != type && n->type != AUDIT_TYPE_UNKNOWN))
continue; continue;
if (!strcmp(dname->name, n->name->name) || /* look for a parent entry first */
if (!found_parent &&
(n->type == AUDIT_TYPE_PARENT || n->type == AUDIT_TYPE_UNKNOWN) &&
(n->ino == parent->i_ino && n->dev == parent->i_sb->s_dev &&
!audit_compare_dname_path(dname, n->name->name, n->name_len))) {
n->type = AUDIT_TYPE_PARENT;
found_parent = n;
if (found_child)
break;
continue;
}
/* is there a matching child entry? */
if (!found_child &&
(n->type == type || n->type == AUDIT_TYPE_UNKNOWN) &&
(!strcmp(dname->name, n->name->name) ||
!audit_compare_dname_path(dname, n->name->name, !audit_compare_dname_path(dname, n->name->name,
found_parent ? found_parent ?
found_parent->name_len : found_parent->name_len :
AUDIT_NAME_FULL)) { AUDIT_NAME_FULL))) {
if (n->type == AUDIT_TYPE_UNKNOWN) if (n->type == AUDIT_TYPE_UNKNOWN)
n->type = type; n->type = type;
found_child = n; found_child = n;
if (found_parent)
break; break;
} }
} }