Maintain per-namespace running totals of the resident bytes and non-null
profile count of the namespace plus all its descendants, walking the
parent chain at the existing charge/uncharge sites.

Expose them as read-only .subtree_size and .subtree_count.

Signed-off-by: Maxime Bélair <[email protected]>
---
 security/apparmor/apparmorfs.c         |  7 +++++
 security/apparmor/include/apparmorfs.h |  2 ++
 security/apparmor/include/policy_ns.h  |  4 +++
 security/apparmor/policy_ns.c          | 38 +++++++++++++++++++++++---
 4 files changed, 47 insertions(+), 4 deletions(-)

diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c
index 98029e7f9d29..d5aa7a23e4ab 100644
--- a/security/apparmor/apparmorfs.c
+++ b/security/apparmor/apparmorfs.c
@@ -1494,6 +1494,9 @@ SEQ_NS_FOPS(NAME)
 
 SEQ_NS_ACCT(acct_count, atomic_long_read(&ns->acct.profile_count));
 SEQ_NS_ACCT(acct_size, atomic_long_read(&ns->acct.resident));
+SEQ_NS_ACCT(acct_subtree_count,
+           atomic_long_read(&ns->acct.subtree_profile_count));
+SEQ_NS_ACCT(acct_subtree_size, atomic_long_read(&ns->acct.subtree_resident));
 SEQ_NS_ACCT(acct_max_count, ns->acct.caps.limits.profiles);
 SEQ_NS_ACCT(acct_max_size, ns->acct.caps.limits.memory);
 SEQ_NS_ACCT(acct_max_profile, ns->acct.caps.limits.max_profile);
@@ -2262,6 +2265,10 @@ static const struct aa_ns_acct_file {
        { ".max_count",   &seq_ns_acct_max_count_fops,  AAFS_NS_MAX_COUNT },
        { ".size",        &seq_ns_acct_size_fops,       AAFS_NS_SIZE },
        { ".max_size",    &seq_ns_acct_max_size_fops,   AAFS_NS_MAX_SIZE },
+       { ".subtree_count", &seq_ns_acct_subtree_count_fops,
+                                               AAFS_NS_SUBTREE_COUNT },
+       { ".subtree_size", &seq_ns_acct_subtree_size_fops,
+                                               AAFS_NS_SUBTREE_SIZE },
        { ".max_profile", &seq_ns_acct_max_profile_fops, AAFS_NS_MAX_PROFILE },
        { ".namespaces",  &seq_ns_acct_namespaces_fops, AAFS_NS_NAMESPACES },
        { ".depth",       &seq_ns_acct_depth_fops,      AAFS_NS_DEPTH },
diff --git a/security/apparmor/include/apparmorfs.h 
b/security/apparmor/include/apparmorfs.h
index faf37b48df72..b8e198b529b4 100644
--- a/security/apparmor/include/apparmorfs.h
+++ b/security/apparmor/include/apparmorfs.h
@@ -83,6 +83,8 @@ enum aafs_ns_type {
        AAFS_NS_NAMESPACES,
        AAFS_NS_DEPTH,
        AAFS_NS_CRIU,
+       AAFS_NS_SUBTREE_COUNT,
+       AAFS_NS_SUBTREE_SIZE,
        AAFS_NS_SIZEOF,
 };
 
diff --git a/security/apparmor/include/policy_ns.h 
b/security/apparmor/include/policy_ns.h
index dce753feb91f..7398e9ab7f1e 100644
--- a/security/apparmor/include/policy_ns.h
+++ b/security/apparmor/include/policy_ns.h
@@ -51,6 +51,8 @@ static inline void aa_ns_capset_init_unset(struct 
aa_ns_capset *caps)
  * @resident: current resident policy bytes charged to this ns (local scope)
  * @profile_count: current count of non-null profiles in this ns (local)
  * @ns_count: current number of direct child namespaces
+ * @subtree_resident: resident policy bytes of this ns plus all descendants
+ * @subtree_profile_count: non-null profiles of this ns plus all descendants
  * @ratelimit: bounds OP_NS_QUOTA audit emission
  */
 struct aa_ns_acct {
@@ -58,6 +60,8 @@ struct aa_ns_acct {
        atomic_long_t resident;
        atomic_long_t profile_count;
        atomic_long_t ns_count;
+       atomic_long_t subtree_resident;
+       atomic_long_t subtree_profile_count;
        struct ratelimit_state ratelimit;
 };
 
diff --git a/security/apparmor/policy_ns.c b/security/apparmor/policy_ns.c
index fb67557548b8..38234a21c570 100644
--- a/security/apparmor/policy_ns.c
+++ b/security/apparmor/policy_ns.c
@@ -210,6 +210,8 @@ void aa_ns_acct_init(struct aa_ns *ns)
        atomic_long_set(&acct->resident, 0);
        atomic_long_set(&acct->profile_count, 0);
        atomic_long_set(&acct->ns_count, 0);
+       atomic_long_set(&acct->subtree_resident, 0);
+       atomic_long_set(&acct->subtree_profile_count, 0);
        ratelimit_state_init(&acct->ratelimit,
                             AA_NS_QUOTA_RATELIMIT_INTERVAL,
                             AA_NS_QUOTA_RATELIMIT_BURST);
@@ -449,6 +451,22 @@ int aa_ns_admit_load_set(struct aa_ns *ns, struct 
list_head *lh,
        return 0;
 }
 
+/*
+ * acct_rollup - add a usage delta to @ns's and every ancestor's subtree totals
+ * @ns: the namespace the delta was charged to  (NOT NULL)
+ * @bytes: resident byte delta (may be negative)
+ * @profiles: non-null profile count delta (may be negative)
+ *
+ * The parent chain is stable for the life of @ns.
+ */
+static void acct_rollup(struct aa_ns *ns, long bytes, long profiles)
+{
+       for (; ns; ns = ns->parent) {
+               atomic_long_add(bytes, &ns->acct.subtree_resident);
+               atomic_long_add(profiles, &ns->acct.subtree_profile_count);
+       }
+}
+
 /**
  * aa_ns_charge_profile - charge a profile's resident policy to its ns
  * @profile: the profile being made live  (NOT NULL)
@@ -458,6 +476,7 @@ int aa_ns_admit_load_set(struct aa_ns *ns, struct list_head 
*lh,
 void aa_ns_charge_profile(struct aa_profile *profile)
 {
        struct aa_ns *ns = profile->ns;
+       bool counted;
        long bytes;
 
        if (!ns || profile->acct_resident)
@@ -465,9 +484,11 @@ void aa_ns_charge_profile(struct aa_profile *profile)
 
        bytes = profile->resident_size;
        profile->acct_resident = bytes;
+       counted = !(profile->label.flags & FLAG_NULL);
        atomic_long_add(bytes, &ns->acct.resident);
-       if (!(profile->label.flags & FLAG_NULL))
+       if (counted)
                atomic_long_inc(&ns->acct.profile_count);
+       acct_rollup(ns, bytes, counted ? 1 : 0);
 }
 
 /**
@@ -479,7 +500,10 @@ void aa_ns_charge_profile(struct aa_profile *profile)
  */
 void aa_ns_charge_rawdata(struct aa_ns *ns, struct aa_loaddata *data)
 {
-       atomic_long_add(aa_loaddata_resident_size(data), &ns->acct.resident);
+       long bytes = aa_loaddata_resident_size(data);
+
+       atomic_long_add(bytes, &ns->acct.resident);
+       acct_rollup(ns, bytes, 0);
 }
 
 /**
@@ -491,7 +515,10 @@ void aa_ns_charge_rawdata(struct aa_ns *ns, struct 
aa_loaddata *data)
  */
 void aa_ns_uncharge_rawdata(struct aa_ns *ns, struct aa_loaddata *data)
 {
-       atomic_long_sub(aa_loaddata_resident_size(data), &ns->acct.resident);
+       long bytes = aa_loaddata_resident_size(data);
+
+       atomic_long_sub(bytes, &ns->acct.resident);
+       acct_rollup(ns, -bytes, 0);
 }
 
 /**
@@ -503,13 +530,16 @@ void aa_ns_uncharge_rawdata(struct aa_ns *ns, struct 
aa_loaddata *data)
 void aa_ns_uncharge_profile(struct aa_profile *profile)
 {
        struct aa_ns *ns = profile->ns;
+       bool counted;
 
        if (!ns || !profile->acct_resident)
                return;
 
+       counted = !(profile->label.flags & FLAG_NULL);
        atomic_long_sub(profile->acct_resident, &ns->acct.resident);
-       if (!(profile->label.flags & FLAG_NULL))
+       if (counted)
                atomic_long_dec(&ns->acct.profile_count);
+       acct_rollup(ns, -profile->acct_resident, counted ? -1 : 0);
        profile->acct_resident = 0;
 }
 
-- 
2.51.0


Reply via email to