cgroup: Improve cftype add/rm error handling
authorTejun Heo <tj@kernel.org>
Tue, 6 Sep 2022 19:38:42 +0000 (09:38 -1000)
committerTejun Heo <tj@kernel.org>
Tue, 6 Sep 2022 19:38:42 +0000 (09:38 -1000)
Let's track whether a cftype is currently added or not using a new flag
__CFTYPE_ADDED so that duplicate operations can be failed safely and
consistently allow using empty cftypes.

Signed-off-by: Tejun Heo <tj@kernel.org>
include/linux/cgroup-defs.h
kernel/cgroup/cgroup.c

index 1283993..9fa1652 100644 (file)
@@ -131,6 +131,7 @@ enum {
        /* internal flags, do not use outside cgroup core proper */
        __CFTYPE_ONLY_ON_DFL    = (1 << 16),    /* only on default hierarchy */
        __CFTYPE_NOT_ON_DFL     = (1 << 17),    /* not on default hierarchy */
+       __CFTYPE_ADDED          = (1 << 18),
 };
 
 /*
index e0b72eb..bd9fe6e 100644 (file)
@@ -4198,19 +4198,26 @@ static void cgroup_exit_cftypes(struct cftype *cfts)
                cft->ss = NULL;
 
                /* revert flags set by cgroup core while adding @cfts */
-               cft->flags &= ~(__CFTYPE_ONLY_ON_DFL | __CFTYPE_NOT_ON_DFL);
+               cft->flags &= ~(__CFTYPE_ONLY_ON_DFL | __CFTYPE_NOT_ON_DFL |
+                               __CFTYPE_ADDED);
        }
 }
 
 static int cgroup_init_cftypes(struct cgroup_subsys *ss, struct cftype *cfts)
 {
        struct cftype *cft;
+       int ret = 0;
 
        for (cft = cfts; cft->name[0] != '\0'; cft++) {
                struct kernfs_ops *kf_ops;
 
                WARN_ON(cft->ss || cft->kf_ops);
 
+               if (cft->flags & __CFTYPE_ADDED) {
+                       ret = -EBUSY;
+                       break;
+               }
+
                if ((cft->flags & CFTYPE_PRESSURE) && !cgroup_psi_enabled())
                        continue;
 
@@ -4226,26 +4233,26 @@ static int cgroup_init_cftypes(struct cgroup_subsys *ss, struct cftype *cfts)
                if (cft->max_write_len && cft->max_write_len != PAGE_SIZE) {
                        kf_ops = kmemdup(kf_ops, sizeof(*kf_ops), GFP_KERNEL);
                        if (!kf_ops) {
-                               cgroup_exit_cftypes(cfts);
-                               return -ENOMEM;
+                               ret = -ENOMEM;
+                               break;
                        }
                        kf_ops->atomic_write_len = cft->max_write_len;
                }
 
                cft->kf_ops = kf_ops;
                cft->ss = ss;
+               cft->flags |= __CFTYPE_ADDED;
        }
 
-       return 0;
+       if (ret)
+               cgroup_exit_cftypes(cfts);
+       return ret;
 }
 
 static int cgroup_rm_cftypes_locked(struct cftype *cfts)
 {
        lockdep_assert_held(&cgroup_mutex);
 
-       if (!cfts || !cfts[0].ss)
-               return -ENOENT;
-
        list_del(&cfts->node);
        cgroup_apply_cftypes(cfts, false);
        cgroup_exit_cftypes(cfts);
@@ -4267,6 +4274,12 @@ int cgroup_rm_cftypes(struct cftype *cfts)
 {
        int ret;
 
+       if (!cfts || cfts[0].name[0] == '\0')
+               return 0;
+
+       if (!(cfts[0].flags & __CFTYPE_ADDED))
+               return -ENOENT;
+
        mutex_lock(&cgroup_mutex);
        ret = cgroup_rm_cftypes_locked(cfts);
        mutex_unlock(&cgroup_mutex);