We allocate struct audit_entry using kzalloc() which aligns the
structure at its natural boundary and so uses the kmalloc-512
SLAB.

That means that the lower order 9 bits are equal for these allocations.
Which on architectures with limited L1D cache-sets width would cause
conflict misses.

With STIG compliant audit rules, up-to 26 L1D misses per syscall have
been observed. This, of course, varies from boot to boot.

Fix this by using a kmem_cache aligned at cacheline width, which
greatly increases the entropy in the lower order VA bits. With this
fix, we observe a maximum of 0.8 misses per syscall using the same set
of audit rules.

Testing: run "perf stat -d -r 5 ./syscall_lat", where syscall_lat is a
simple getpid() loop, running 100 million rounds. This test is run 10
times, with a reboot in between. After each reboot, we wait until
the last minute load is less than 1.0.

We boot the kernel, v6.6-rc4, with "mitigations=off", in order to
amplify the changes in the audit system.

Base vs. base + this commit gives:

ns per call:
  min avg max   pstdev
- 205 210 227 6.402000
+ 203 203 209 0.954149

L1d misses per syscall:
    min    avg    max   pstdev
- 3.147 12.017 26.045 6.568284
+ 0.012  0.103  0.817 0.238352

ipc:
    min    avg    max   pstdev
- 2.090  2.259  2.300 0.060075
+ 2.320  2.329  2.330 0.003000

The above metrics are all improved, and the L1D misses per syscall has
a significant reduction.

Co-developed-by: Ankur Arora <[email protected]>
Signed-off-by: Ankur Arora <[email protected]>
Signed-off-by: Håkon Bugge <[email protected]>
---
 kernel/auditfilter.c | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c
index 8317a37dea0bb..b7597a63a3950 100644
--- a/kernel/auditfilter.c
+++ b/kernel/auditfilter.c
@@ -80,6 +80,7 @@ static void audit_free_lsm_field(struct audit_field *f)
        }
 }
 
+static struct kmem_cache *entry_cache;
 static inline void audit_free_rule(struct audit_entry *e)
 {
        int i;
@@ -93,7 +94,7 @@ static inline void audit_free_rule(struct audit_entry *e)
                        audit_free_lsm_field(&erule->fields[i]);
        kfree(erule->fields);
        kfree(erule->filterkey);
-       kfree(e);
+       kmem_cache_free(entry_cache, e);
 }
 
 void audit_free_rule_rcu(struct rcu_head *head)
@@ -108,13 +109,20 @@ static inline struct audit_entry *audit_init_entry(u32 
field_count)
        struct audit_entry *entry;
        struct audit_field *fields;
 
-       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+       if (!entry_cache) {
+               entry_cache = kmem_cache_create("audit_entry", sizeof(*entry), 
0,
+                                               SLAB_HWCACHE_ALIGN, NULL);
+               if (!entry_cache)
+                       return NULL;
+       }
+
+       entry = kmem_cache_zalloc(entry_cache, GFP_KERNEL);
        if (unlikely(!entry))
                return NULL;
 
        fields = kcalloc(field_count, sizeof(*fields), GFP_KERNEL);
        if (unlikely(!fields)) {
-               kfree(entry);
+               kmem_cache_free(entry_cache, entry);
                return NULL;
        }
        entry->rule.fields = fields;
-- 
2.39.3


Reply via email to