History

cgroup, kernfs: make mountinfo show properly scoped path for cgroup namespaces

Patch summary: When showing a cgroupfs entry in mountinfo, show the path of the mount root dentry relative to the reader's cgroup namespace root. Short explanation (courtesy of mkerrisk): If we create a new cgroup namespace, then we want both /proc/self/cgroup and /proc/self/mountinfo to show cgroup paths that are correctly virtualized with respect to the cgroup mount point. Previous to this patch, /proc/self/cgroup shows the right info, but /proc/self/mountinfo does not. Long version: When a uid 0 task which is in freezer cgroup /a/b, unshares a new cgroup namespace, and then mounts a new instance of the freezer cgroup, the new mount will be rooted at /a/b. The root dentry field of the mountinfo entry will show '/a/b'. cat > /tmp/do1 << EOF mount -t cgroup -o freezer freezer /mnt grep freezer /proc/self/mountinfo EOF unshare -Gm bash /tmp/do1 > 330 160 0:34 / /sys/fs/cgroup/freezer rw,nosuid,nodev,noexec,relatime - cgroup cgroup rw,freezer > 355 133 0:34 /a/b /mnt rw,relatime - cgroup freezer rw,freezer The task's freezer cgroup entry in /proc/self/cgroup will simply show '/': grep freezer /proc/self/cgroup 9:freezer:/ If instead the same task simply bind mounts the /a/b cgroup directory, the resulting mountinfo entry will again show /a/b for the dentry root. However in this case the task will find its own cgroup at /mnt/a/b, not at /mnt: mount --bind /sys/fs/cgroup/freezer/a/b /mnt 130 25 0:34 /a/b /mnt rw,nosuid,nodev,noexec,relatime shared:21 - cgroup cgroup rw,freezer In other words, there is no way for the task to know, based on what is in mountinfo, which cgroup directory is its own. Example (by mkerrisk): First, a little script to save some typing and verbiage: echo -e "\t/proc/self/cgroup:\t$(cat /proc/self/cgroup | grep freezer)" cat /proc/self/mountinfo | grep freezer | awk '{print "\tmountinfo:\t\t" $4 "\t" $5}' Create cgroup, place this shell into the cgroup, and look at the state of the /proc files: 2653 2653 # Our shell 14254 # cat(1) /proc/self/cgroup: 10:freezer:/a/b mountinfo: / /sys/fs/cgroup/freezer Create a shell in new cgroup and mount namespaces. The act of creating a new cgroup namespace causes the process's current cgroups directories to become its cgroup root directories. (Here, I'm using my own version of the "unshare" utility, which takes the same options as the util-linux version): Look at the state of the /proc files: /proc/self/cgroup: 10:freezer:/ mountinfo: / /sys/fs/cgroup/freezer The third entry in /proc/self/cgroup (the pathname of the cgroup inside the hierarchy) is correctly virtualized w.r.t. the cgroup namespace, which is rooted at /a/b in the outer namespace. However, the info in /proc/self/mountinfo is not for this cgroup namespace, since we are seeing a duplicate of the mount from the old mount namespace, and the info there does not correspond to the new cgroup namespace. However, trying to create a new mount still doesn't show us the right information in mountinfo: # propagating to other mountns /proc/self/cgroup: 7:freezer:/ mountinfo: /a/b /mnt/freezer The act of creating a new cgroup namespace caused the process's current freezer directory, "/a/b", to become its cgroup freezer root directory. In other words, the pathname directory of the directory within the newly mounted cgroup filesystem should be "/", but mountinfo wrongly shows us "/a/b". The consequence of this is that the process in the cgroup namespace cannot correctly construct the pathname of its cgroup root directory from the information in /proc/PID/mountinfo. With this patch, the dentry root field in mountinfo is shown relative to the reader's cgroup namespace. So the same steps as above: /proc/self/cgroup: 10:freezer:/a/b mountinfo: / /sys/fs/cgroup/freezer /proc/self/cgroup: 10:freezer:/ mountinfo: /../.. /sys/fs/cgroup/freezer /proc/self/cgroup: 10:freezer:/ mountinfo: / /mnt/freezer cgroup.clone_children freezer.parent_freezing freezer.state tasks cgroup.procs freezer.self_freezing notify_on_release 3164 2653 # First shell that placed in this cgroup 3164 # Shell started by 'unshare' 14197 # cat(1)

Serge E. Hallyn committed 10 years ago

Commit 4f41fc51 parent e99ed4d3 files · +79 −0Browse files

Unified · Split

3 files
  1. fs/kernfs/mount.c +14 −0
  2. include/linux/kernfs.h +2 −0
  3. kernel/cgroup.c +63 −0
fs/kernfs/mount.c+14−0
@@ -15,6 +15,7 @@ · 14 unmodified lines
15#include <linux/slab.h>15#include <linux/slab.h>
16#include <linux/pagemap.h>16#include <linux/pagemap.h>
17#include <linux/namei.h>17#include <linux/namei.h>
18#include <linux/seq_file.h>
18 19
19#include "kernfs-internal.h"20#include "kernfs-internal.h"
20 21
@@ -40,6 +41,18 @@ static int kernfs_sop_show_options(struct seq_file *sf, struct dentry *dentry) · 19 unmodified lines
40 return 0;41 return 0;
41}42}
42 43
44static int kernfs_sop_show_path(struct seq_file *sf, struct dentry *dentry)
45{
46 struct kernfs_node *node = dentry->d_fsdata;
47 struct kernfs_root *root = kernfs_root(node);
48 struct kernfs_syscall_ops *scops = root->syscall_ops;
49
50 if (scops && scops->show_path)
51 return scops->show_path(sf, node, root);
52
53 return seq_dentry(sf, dentry, " \t\n\\");
54}
55
43const struct super_operations kernfs_sops = {56const struct super_operations kernfs_sops = {
44 .statfs = simple_statfs,57 .statfs = simple_statfs,
45 .drop_inode = generic_delete_inode,58 .drop_inode = generic_delete_inode,
@@ -47,6 +60,7 @@ const struct super_operations kernfs_sops = { · 1 unmodified line
47 60
48 .remount_fs = kernfs_sop_remount_fs,61 .remount_fs = kernfs_sop_remount_fs,
49 .show_options = kernfs_sop_show_options,62 .show_options = kernfs_sop_show_options,
63 .show_path = kernfs_sop_show_path,
50};64};
51 65
52/**66/**
include/linux/kernfs.h+2−0
@@ -152,6 +152,8 @@ struct kernfs_syscall_ops { · 151 unmodified lines
152 int (*rmdir)(struct kernfs_node *kn);152 int (*rmdir)(struct kernfs_node *kn);
153 int (*rename)(struct kernfs_node *kn, struct kernfs_node *new_parent,153 int (*rename)(struct kernfs_node *kn, struct kernfs_node *new_parent,
154 const char *new_name);154 const char *new_name);
155 int (*show_path)(struct seq_file *sf, struct kernfs_node *kn,
156 struct kernfs_root *root);
155};157};
156 158
157struct kernfs_root {159struct kernfs_root {
kernel/cgroup.c+63−0
@@ -1215,6 +1215,41 @@ static void cgroup_destroy_root(struct cgroup_root *root) · 1214 unmodified lines
1215 cgroup_free_root(root);1215 cgroup_free_root(root);
1216}1216}
1217 1217
1218/*
1219 * look up cgroup associated with current task's cgroup namespace on the
1220 * specified hierarchy
1221 */
1222static struct cgroup *
1223current_cgns_cgroup_from_root(struct cgroup_root *root)
1224{
1225 struct cgroup *res = NULL;
1226 struct css_set *cset;
1227
1228 lockdep_assert_held(&css_set_lock);
1229
1230 rcu_read_lock();
1231
1232 cset = current->nsproxy->cgroup_ns->root_cset;
1233 if (cset == &init_css_set) {
1234 res = &root->cgrp;
1235 } else {
1236 struct cgrp_cset_link *link;
1237
1238 list_for_each_entry(link, &cset->cgrp_links, cgrp_link) {
1239 struct cgroup *c = link->cgrp;
1240
1241 if (c->root == root) {
1242 res = c;
1243 break;
1244 }
1245 }
1246 }
1247 rcu_read_unlock();
1248
1249 BUG_ON(!res);
1250 return res;
1251}
1252
1218/* look up cgroup associated with given css_set on the specified hierarchy */1253/* look up cgroup associated with given css_set on the specified hierarchy */
1219static struct cgroup *cset_cgroup_from_root(struct css_set *cset,1254static struct cgroup *cset_cgroup_from_root(struct css_set *cset,
1220 struct cgroup_root *root)1255 struct cgroup_root *root)
@@ -1593,6 +1628,33 @@ static int rebind_subsystems(struct cgroup_root *dst_root, u16 ss_mask) · 372 unmodified lines
1593 return 0;1628 return 0;
1594}1629}
1595 1630
1631static int cgroup_show_path(struct seq_file *sf, struct kernfs_node *kf_node,
1632 struct kernfs_root *kf_root)
1633{
1634 int len = 0, ret = 0;
1635 char *buf = NULL;
1636 struct cgroup_root *kf_cgroot = cgroup_root_from_kf(kf_root);
1637 struct cgroup *ns_cgroup;
1638
1639 buf = kmalloc(PATH_MAX, GFP_KERNEL);
1640 if (!buf)
1641 return -ENOMEM;
1642
1643 spin_lock_bh(&css_set_lock);
1644 ns_cgroup = current_cgns_cgroup_from_root(kf_cgroot);
1645 len = kernfs_path_from_node(kf_node, ns_cgroup->kn, buf, PATH_MAX);
1646 spin_unlock_bh(&css_set_lock);
1647
1648 if (len >= PATH_MAX)
1649 len = -ERANGE;
1650 else if (len > 0) {
1651 seq_escape(sf, buf, " \t\n\\");
1652 len = 0;
1653 }
1654 kfree(buf);
1655 return len;
1656}
1657
1596static int cgroup_show_options(struct seq_file *seq,1658static int cgroup_show_options(struct seq_file *seq,
1597 struct kernfs_root *kf_root)1659 struct kernfs_root *kf_root)
1598{1660{
@@ -5433,6 +5495,7 @@ static struct kernfs_syscall_ops cgroup_kf_syscall_ops = { · 3834 unmodified lines
5433 .mkdir = cgroup_mkdir,5495 .mkdir = cgroup_mkdir,
5434 .rmdir = cgroup_rmdir,5496 .rmdir = cgroup_rmdir,
5435 .rename = cgroup_rename,5497 .rename = cgroup_rename,
5498 .show_path = cgroup_show_path,
5436};5499};
5437 5500
5438static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early)5501static void __init cgroup_init_subsys(struct cgroup_subsys *ss, bool early)