security: optimize avc_audit() common path
Linus Torvalds [Fri, 23 Mar 2012 00:01:41 +0000 (17:01 -0700)]
avc_audit() did a lot of jumping around and had a big stack frame, all
for the uncommon case.

Split up the uncommon case (which we really can't make go fast anyway)
into its own slow function, and mark the conditional branches
appropriately for the common likely case.

This causes avc_audit() to no longer show up as one of the hottest
functions on the branch profiles (the new "perf -b" thing), and makes
the cycle profiles look really nice and dense too.

The whole audit path is still annoyingly very much one of the biggest
costs of name lookup, so these things are worth optimizing for.  I wish
we could just tell people to turn it off, but realistically we do need
it: we just need to make sure that the overhead of the necessary evil is
as low as possible.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

security/selinux/avc.c

index dca1c22..6989472 100644 (file)
@@ -457,6 +457,42 @@ static void avc_audit_post_callback(struct audit_buffer *ab, void *a)
                           ad->selinux_audit_data.tclass);
 }
 
+/* This is the slow part of avc audit with big stack footprint */
+static noinline int slow_avc_audit(u32 ssid, u32 tsid, u16 tclass,
+               u32 requested, u32 audited, u32 denied,
+               struct av_decision *avd, struct common_audit_data *a,
+               unsigned flags)
+{
+       struct common_audit_data stack_data;
+
+       if (!a) {
+               a = &stack_data;
+               COMMON_AUDIT_DATA_INIT(a, NONE);
+       }
+
+       /*
+        * When in a RCU walk do the audit on the RCU retry.  This is because
+        * the collection of the dname in an inode audit message is not RCU
+        * safe.  Note this may drop some audits when the situation changes
+        * during retry. However this is logically just as if the operation
+        * happened a little later.
+        */
+       if ((a->type == LSM_AUDIT_DATA_INODE) &&
+           (flags & MAY_NOT_BLOCK))
+               return -ECHILD;
+
+       a->selinux_audit_data.tclass = tclass;
+       a->selinux_audit_data.requested = requested;
+       a->selinux_audit_data.ssid = ssid;
+       a->selinux_audit_data.tsid = tsid;
+       a->selinux_audit_data.audited = audited;
+       a->selinux_audit_data.denied = denied;
+       a->lsm_pre_audit = avc_audit_pre_callback;
+       a->lsm_post_audit = avc_audit_post_callback;
+       common_lsm_audit(a);
+       return 0;
+}
+
 /**
  * avc_audit - Audit the granting or denial of permissions.
  * @ssid: source security identifier
@@ -482,10 +518,9 @@ int avc_audit(u32 ssid, u32 tsid,
               struct av_decision *avd, int result, struct common_audit_data *a,
               unsigned flags)
 {
-       struct common_audit_data stack_data;
        u32 denied, audited;
        denied = requested & ~avd->allowed;
-       if (denied) {
+       if (unlikely(denied)) {
                audited = denied & avd->auditdeny;
                /*
                 * a->selinux_audit_data.auditdeny is TRICKY!  Setting a bit in
@@ -511,35 +546,12 @@ int avc_audit(u32 ssid, u32 tsid,
                audited = denied = requested;
        else
                audited = requested & avd->auditallow;
-       if (!audited)
+       if (likely(!audited))
                return 0;
 
-       if (!a) {
-               a = &stack_data;
-               COMMON_AUDIT_DATA_INIT(a, NONE);
-       }
-
-       /*
-        * When in a RCU walk do the audit on the RCU retry.  This is because
-        * the collection of the dname in an inode audit message is not RCU
-        * safe.  Note this may drop some audits when the situation changes
-        * during retry. However this is logically just as if the operation
-        * happened a little later.
-        */
-       if ((a->type == LSM_AUDIT_DATA_INODE) &&
-           (flags & MAY_NOT_BLOCK))
-               return -ECHILD;
-
-       a->selinux_audit_data.tclass = tclass;
-       a->selinux_audit_data.requested = requested;
-       a->selinux_audit_data.ssid = ssid;
-       a->selinux_audit_data.tsid = tsid;
-       a->selinux_audit_data.audited = audited;
-       a->selinux_audit_data.denied = denied;
-       a->lsm_pre_audit = avc_audit_pre_callback;
-       a->lsm_post_audit = avc_audit_post_callback;
-       common_lsm_audit(a);
-       return 0;
+       return slow_avc_audit(ssid, tsid, tclass,
+               requested, audited, denied,
+               avd, a, flags);
 }
 
 /**