@@ -3481,66 +3481,107 @@ static int cgroup_cpu_pressure_show(struct seq_file *seq, void *v)
static int cgroup_file_open(struct kernfs_open_file *of)
{
struct cftype *cft = of->kn->priv;
+ struct cftype *n;
+ int ret = 0;
+ list_for_each_cft(cft, n) {
+ if (cft->open)
+ ret = cft->open(of);
+ /*
+ * If there has been an error with the open function of one of
+ * the cft associated with the file, we call the release
+ * function of all the cftype associated to cft whose open
+ * function succeded.
+ */
+ if (ret) {
+ struct cftype *c = of->kn->priv;
+ struct cftype *n;
+
+ list_for_each_cft(c, n) {
+ if (cft == c)
+ break;
+ if (c->release)
+ c->release(of);
+ }
+ break;
+ }
+ }
- if (cft->open)
- return cft->open(of);
- return 0;
+ return ret;
}
static void cgroup_file_release(struct kernfs_open_file *of)
{
struct cftype *cft = of->kn->priv;
+ struct cftype *n;
- if (cft->release)
- cft->release(of);
+ list_for_each_cft(cft, n)
+ if (cft->release)
+ cft->release(of);
}
+/*
+ * Call all the write functions of the cftypes associated with the file.
+ *
+ * When a write fails, don't keep trying to write into the file via the write
+ * functions of the other cftypes associated with it.
+ */
static ssize_t cgroup_file_write(struct kernfs_open_file *of, char *buf,
size_t nbytes, loff_t off)
{
struct cgroup_namespace *ns = current->nsproxy->cgroup_ns;
struct cgroup *cgrp = of->kn->parent->priv;
struct cftype *cft = of->kn->priv;
+ struct cftype *n;
struct cgroup_subsys_state *css;
- int ret;
+ int ret = 0;
- /*
- * If namespaces are delegation boundaries, disallow writes to
- * files in an non-init namespace root from inside the namespace
- * except for the files explicitly marked delegatable -
- * cgroup.procs and cgroup.subtree_control.
- */
- if ((cgrp->root->flags & CGRP_ROOT_NS_DELEGATE) &&
- !(cft->flags & CFTYPE_NS_DELEGATABLE) &&
- ns != &init_cgroup_ns && ns->root_cset->dfl_cgrp == cgrp)
- return -EPERM;
+ list_for_each_cft(cft, n) {
+ /*
+ * If namespaces are delegation boundaries, disallow writes to
+ * files in an non-init namespace root from inside the
+ * namespace except for the files explicitly marked
+ * delegatable - cgroup.procs and cgroup.subtree_control.
+ */
+ if ((cgrp->root->flags & CGRP_ROOT_NS_DELEGATE) &&
+ !(cft->flags & CFTYPE_NS_DELEGATABLE) &&
+ ns != &init_cgroup_ns && ns->root_cset->dfl_cgrp == cgrp)
+ return -EPERM;
- if (cft->write)
- return cft->write(of, buf, nbytes, off);
+ if (cft->write) {
+ ret = cft->write(of, buf, nbytes, off);
- /*
- * kernfs guarantees that a file isn't deleted with operations in
- * flight, which means that the matching css is and stays alive and
- * doesn't need to be pinned. The RCU locking is not necessary
- * either. It's just for the convenience of using cgroup_css().
- */
- rcu_read_lock();
- css = cgroup_css(cgrp, cft->ss);
- rcu_read_unlock();
+ if (ret)
+ break;
+ continue;
+ }
- if (cft->write_u64) {
- unsigned long long v;
- ret = kstrtoull(buf, 0, &v);
- if (!ret)
- ret = cft->write_u64(css, cft, v);
- } else if (cft->write_s64) {
- long long v;
- ret = kstrtoll(buf, 0, &v);
- if (!ret)
- ret = cft->write_s64(css, cft, v);
- } else {
- ret = -EINVAL;
+ /*
+ * kernfs guarantees that a file isn't deleted with operations
+ * in flight, which means that the matching css is and stays
+ * alive and doesn't need to be pinned. The RCU locking is not
+ * necessary either. It's just for the convenience of using
+ * cgroup_css().
+ */
+ rcu_read_lock();
+ css = cgroup_css(cgrp, cft->ss);
+ rcu_read_unlock();
+
+ if (cft->write_u64) {
+ unsigned long long v;
+
+ ret = kstrtoull(buf, 0, &v);
+ if (!ret)
+ ret = cft->write_u64(css, cft, v);
+ } else if (cft->write_s64) {
+ long long v;
+
+ ret = kstrtoll(buf, 0, &v);
+ if (!ret)
+ ret = cft->write_s64(css, cft, v);
+ } else {
+ return -EINVAL;
+ }
}
return ret ?: nbytes;
@@ -3562,22 +3603,64 @@ static void cgroup_seqfile_stop(struct seq_file *seq, void *v)
seq_cft(seq)->seq_stop(seq, v);
}
+/*
+ * A file shared by more cftypes may be showing different values. In that case
+ * call all the show functions and print the name of the owner that defined
+ * them.
+ */
static int cgroup_seqfile_show(struct seq_file *m, void *arg)
{
struct cftype *cft = seq_cft(m);
+ struct cftype *n;
struct cgroup_subsys_state *css = seq_css(m);
+ char *first_seqshow_str = NULL;
+ size_t first_str_size = 0;
+ size_t current_str_size = 0;
int ret = 0;
- if (cft->seq_show)
- ret = cft->seq_show(m, arg);
- else if (cft->seq_show_cft)
- ret = cft->seq_show_cft(m, cft, arg);
- else if (cft->read_u64)
- seq_printf(m, "%llu\n", cft->read_u64(css, cft));
- else if (cft->read_s64)
- seq_printf(m, "%lld\n", cft->read_s64(css, cft));
- else
- ret = -EINVAL;
+ list_for_each_cft(cft, n) {
+ if (cft->seq_show) {
+ ret = cft->seq_show(m, arg);
+ if (ret)
+ break;
+ } else if (cft->seq_show_cft) {
+ ret = cft->seq_show_cft(m, cft, arg);
+ if (ret)
+ break;
+ } else if (cft->read_u64) {
+ seq_printf(m, "%llu\n", cft->read_u64(css, cft));
+ } else if (cft->read_s64) {
+ seq_printf(m, "%lld\n", cft->read_s64(css, cft));
+ } else {
+ ret = -EINVAL;
+ break;
+ }
+ current_str_size = m->count - current_str_size;
+
+ if (first_seqshow_str == NULL) {
+ first_seqshow_str = kmalloc(m->size, GFP_KERNEL);
+ first_str_size = m->count;
+ strcpy(first_seqshow_str, m->buf);
+ first_str_size = m->count;
+ } else if (strcmp(first_seqshow_str,
+ m->buf + m->count - current_str_size)) {
+ first_str_size = -1;
+ }
+
+ if (current_str_size) {
+ seq_printf(m, " - %s\n", cft->owner_name);
+ current_str_size = m->count;
+ }
+ }
+
+ /*
+ * If all the cft->seqfile_show/read are equal, truncate the
+ * output of the seqfile to the length of the first string.
+ */
+ if (first_str_size != -1)
+ m->count = first_str_size;
+
+ kfree(first_seqshow_str);
return ret;
}