diff options
author | Paul Moore <paul@paul-moore.com> | 2017-05-02 10:16:05 -0400 |
---|---|---|
committer | Paul Moore <paul@paul-moore.com> | 2017-05-02 10:16:05 -0400 |
commit | 8cc96382d9a7fe1746286670dd5140c3b12638ae (patch) | |
tree | 22383341416f7ddd97b0f16d259967104ce31739 | |
parent | 2115bb250f260089743e26decfb5f271ba71ca37 (diff) |
audit: use kmem_cache to manage the audit_buffer cache
The audit subsystem implemented its own buffer cache mechanism which
is a bit silly these days when we could use the kmem_cache construct.
Some credit is due to Florian Westphal for originally proposing that
we remove the audit cache implementation in favor of simple
kmalloc()/kfree() calls, but I would rather have a dedicated slab
cache to ease debugging and future stats/performance work.
Cc: Florian Westphal <fw@strlen.de>
Reviewed-by: Richard Guy Briggs <rgb@redhat.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
-rw-r--r-- | kernel/audit.c | 66 |
1 files changed, 17 insertions, 49 deletions
diff --git a/kernel/audit.c b/kernel/audit.c index 41efd2ad1931..10bc2bad2adf 100644 --- a/kernel/audit.c +++ b/kernel/audit.c | |||
@@ -59,6 +59,7 @@ | |||
59 | #include <linux/mutex.h> | 59 | #include <linux/mutex.h> |
60 | #include <linux/gfp.h> | 60 | #include <linux/gfp.h> |
61 | #include <linux/pid.h> | 61 | #include <linux/pid.h> |
62 | #include <linux/slab.h> | ||
62 | 63 | ||
63 | #include <linux/audit.h> | 64 | #include <linux/audit.h> |
64 | 65 | ||
@@ -152,12 +153,7 @@ static atomic_t audit_lost = ATOMIC_INIT(0); | |||
152 | /* Hash for inode-based rules */ | 153 | /* Hash for inode-based rules */ |
153 | struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS]; | 154 | struct list_head audit_inode_hash[AUDIT_INODE_BUCKETS]; |
154 | 155 | ||
155 | /* The audit_freelist is a list of pre-allocated audit buffers (if more | 156 | static struct kmem_cache *audit_buffer_cache; |
156 | * than AUDIT_MAXFREE are in use, the audit buffer is freed instead of | ||
157 | * being placed on the freelist). */ | ||
158 | static DEFINE_SPINLOCK(audit_freelist_lock); | ||
159 | static int audit_freelist_count; | ||
160 | static LIST_HEAD(audit_freelist); | ||
161 | 157 | ||
162 | /* queue msgs to send via kauditd_task */ | 158 | /* queue msgs to send via kauditd_task */ |
163 | static struct sk_buff_head audit_queue; | 159 | static struct sk_buff_head audit_queue; |
@@ -192,17 +188,12 @@ DEFINE_MUTEX(audit_cmd_mutex); | |||
192 | * should be at least that large. */ | 188 | * should be at least that large. */ |
193 | #define AUDIT_BUFSIZ 1024 | 189 | #define AUDIT_BUFSIZ 1024 |
194 | 190 | ||
195 | /* AUDIT_MAXFREE is the number of empty audit_buffers we keep on the | ||
196 | * audit_freelist. Doing so eliminates many kmalloc/kfree calls. */ | ||
197 | #define AUDIT_MAXFREE (2*NR_CPUS) | ||
198 | |||
199 | /* The audit_buffer is used when formatting an audit record. The caller | 191 | /* The audit_buffer is used when formatting an audit record. The caller |
200 | * locks briefly to get the record off the freelist or to allocate the | 192 | * locks briefly to get the record off the freelist or to allocate the |
201 | * buffer, and locks briefly to send the buffer to the netlink layer or | 193 | * buffer, and locks briefly to send the buffer to the netlink layer or |
202 | * to place it on a transmit queue. Multiple audit_buffers can be in | 194 | * to place it on a transmit queue. Multiple audit_buffers can be in |
203 | * use simultaneously. */ | 195 | * use simultaneously. */ |
204 | struct audit_buffer { | 196 | struct audit_buffer { |
205 | struct list_head list; | ||
206 | struct sk_buff *skb; /* formatted skb ready to send */ | 197 | struct sk_buff *skb; /* formatted skb ready to send */ |
207 | struct audit_context *ctx; /* NULL or associated context */ | 198 | struct audit_context *ctx; /* NULL or associated context */ |
208 | gfp_t gfp_mask; | 199 | gfp_t gfp_mask; |
@@ -1486,6 +1477,10 @@ static int __init audit_init(void) | |||
1486 | if (audit_initialized == AUDIT_DISABLED) | 1477 | if (audit_initialized == AUDIT_DISABLED) |
1487 | return 0; | 1478 | return 0; |
1488 | 1479 | ||
1480 | audit_buffer_cache = kmem_cache_create("audit_buffer", | ||
1481 | sizeof(struct audit_buffer), | ||
1482 | 0, SLAB_PANIC, NULL); | ||
1483 | |||
1489 | memset(&auditd_conn, 0, sizeof(auditd_conn)); | 1484 | memset(&auditd_conn, 0, sizeof(auditd_conn)); |
1490 | spin_lock_init(&auditd_conn.lock); | 1485 | spin_lock_init(&auditd_conn.lock); |
1491 | 1486 | ||
@@ -1554,60 +1549,33 @@ __setup("audit_backlog_limit=", audit_backlog_limit_set); | |||
1554 | 1549 | ||
1555 | static void audit_buffer_free(struct audit_buffer *ab) | 1550 | static void audit_buffer_free(struct audit_buffer *ab) |
1556 | { | 1551 | { |
1557 | unsigned long flags; | ||
1558 | |||
1559 | if (!ab) | 1552 | if (!ab) |
1560 | return; | 1553 | return; |
1561 | 1554 | ||
1562 | kfree_skb(ab->skb); | 1555 | kfree_skb(ab->skb); |
1563 | spin_lock_irqsave(&audit_freelist_lock, flags); | 1556 | kmem_cache_free(audit_buffer_cache, ab); |
1564 | if (audit_freelist_count > AUDIT_MAXFREE) | ||
1565 | kfree(ab); | ||
1566 | else { | ||
1567 | audit_freelist_count++; | ||
1568 | list_add(&ab->list, &audit_freelist); | ||
1569 | } | ||
1570 | spin_unlock_irqrestore(&audit_freelist_lock, flags); | ||
1571 | } | 1557 | } |
1572 | 1558 | ||
1573 | static struct audit_buffer * audit_buffer_alloc(struct audit_context *ctx, | 1559 | static struct audit_buffer *audit_buffer_alloc(struct audit_context *ctx, |
1574 | gfp_t gfp_mask, int type) | 1560 | gfp_t gfp_mask, int type) |
1575 | { | 1561 | { |
1576 | unsigned long flags; | 1562 | struct audit_buffer *ab; |
1577 | struct audit_buffer *ab = NULL; | ||
1578 | struct nlmsghdr *nlh; | ||
1579 | |||
1580 | spin_lock_irqsave(&audit_freelist_lock, flags); | ||
1581 | if (!list_empty(&audit_freelist)) { | ||
1582 | ab = list_entry(audit_freelist.next, | ||
1583 | struct audit_buffer, list); | ||
1584 | list_del(&ab->list); | ||
1585 | --audit_freelist_count; | ||
1586 | } | ||
1587 | spin_unlock_irqrestore(&audit_freelist_lock, flags); | ||
1588 | |||
1589 | if (!ab) { | ||
1590 | ab = kmalloc(sizeof(*ab), gfp_mask); | ||
1591 | if (!ab) | ||
1592 | goto err; | ||
1593 | } | ||
1594 | 1563 | ||
1595 | ab->ctx = ctx; | 1564 | ab = kmem_cache_alloc(audit_buffer_cache, gfp_mask); |
1596 | ab->gfp_mask = gfp_mask; | 1565 | if (!ab) |
1566 | return NULL; | ||
1597 | 1567 | ||
1598 | ab->skb = nlmsg_new(AUDIT_BUFSIZ, gfp_mask); | 1568 | ab->skb = nlmsg_new(AUDIT_BUFSIZ, gfp_mask); |
1599 | if (!ab->skb) | 1569 | if (!ab->skb) |
1600 | goto err; | 1570 | goto err; |
1571 | if (!nlmsg_put(ab->skb, 0, 0, type, 0, 0)) | ||
1572 | goto err; | ||
1601 | 1573 | ||
1602 | nlh = nlmsg_put(ab->skb, 0, 0, type, 0, 0); | 1574 | ab->ctx = ctx; |
1603 | if (!nlh) | 1575 | ab->gfp_mask = gfp_mask; |
1604 | goto out_kfree_skb; | ||
1605 | 1576 | ||
1606 | return ab; | 1577 | return ab; |
1607 | 1578 | ||
1608 | out_kfree_skb: | ||
1609 | kfree_skb(ab->skb); | ||
1610 | ab->skb = NULL; | ||
1611 | err: | 1579 | err: |
1612 | audit_buffer_free(ab); | 1580 | audit_buffer_free(ab); |
1613 | return NULL; | 1581 | return NULL; |