kernfs: add exportfs operations
authorShaohua Li <shli@fb.com>
Wed, 12 Jul 2017 18:49:51 +0000 (11:49 -0700)
committerJens Axboe <axboe@kernel.dk>
Sat, 29 Jul 2017 15:00:03 +0000 (09:00 -0600)
Now we have the facilities to implement exportfs operations. The idea is
cgroup can export the fhandle info to userspace, then userspace uses
fhandle to find the cgroup name. Another example is userspace can get
fhandle for a cgroup and BPF uses the fhandle to filter info for the
cgroup.

Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: Shaohua Li <shli@fb.com>
Signed-off-by: Jens Axboe <axboe@kernel.dk>
fs/kernfs/mount.c
include/linux/kernfs.h
kernel/cgroup/cgroup.c

index acd542625fd8a645772f7a91dedf3e4fa0e62d4f..fa323589704f828c808127304af1615ca0f3f6df 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/pagemap.h>
 #include <linux/namei.h>
 #include <linux/seq_file.h>
+#include <linux/exportfs.h>
 
 #include "kernfs-internal.h"
 
@@ -64,6 +65,59 @@ const struct super_operations kernfs_sops = {
        .show_path      = kernfs_sop_show_path,
 };
 
+static struct inode *kernfs_fh_get_inode(struct super_block *sb,
+               u64 ino, u32 generation)
+{
+       struct kernfs_super_info *info = kernfs_info(sb);
+       struct inode *inode;
+       struct kernfs_node *kn;
+
+       if (ino == 0)
+               return ERR_PTR(-ESTALE);
+
+       kn = kernfs_find_and_get_node_by_ino(info->root, ino);
+       if (!kn)
+               return ERR_PTR(-ESTALE);
+       inode = kernfs_get_inode(sb, kn);
+       kernfs_put(kn);
+       if (IS_ERR(inode))
+               return ERR_CAST(inode);
+
+       if (generation && inode->i_generation != generation) {
+               /* we didn't find the right inode.. */
+               iput(inode);
+               return ERR_PTR(-ESTALE);
+       }
+       return inode;
+}
+
+static struct dentry *kernfs_fh_to_dentry(struct super_block *sb, struct fid *fid,
+               int fh_len, int fh_type)
+{
+       return generic_fh_to_dentry(sb, fid, fh_len, fh_type,
+                                   kernfs_fh_get_inode);
+}
+
+static struct dentry *kernfs_fh_to_parent(struct super_block *sb, struct fid *fid,
+               int fh_len, int fh_type)
+{
+       return generic_fh_to_parent(sb, fid, fh_len, fh_type,
+                                   kernfs_fh_get_inode);
+}
+
+static struct dentry *kernfs_get_parent_dentry(struct dentry *child)
+{
+       struct kernfs_node *kn = kernfs_dentry_node(child);
+
+       return d_obtain_alias(kernfs_get_inode(child->d_sb, kn->parent));
+}
+
+static const struct export_operations kernfs_export_ops = {
+       .fh_to_dentry   = kernfs_fh_to_dentry,
+       .fh_to_parent   = kernfs_fh_to_parent,
+       .get_parent     = kernfs_get_parent_dentry,
+};
+
 /**
  * kernfs_root_from_sb - determine kernfs_root associated with a super_block
  * @sb: the super_block in question
@@ -159,6 +213,8 @@ static int kernfs_fill_super(struct super_block *sb, unsigned long magic)
        sb->s_magic = magic;
        sb->s_op = &kernfs_sops;
        sb->s_xattr = kernfs_xattr_handlers;
+       if (info->root->flags & KERNFS_ROOT_SUPPORT_EXPORTOP)
+               sb->s_export_op = &kernfs_export_ops;
        sb->s_time_gran = 1;
 
        /* get root inode, initialize and unlock it */
index 06a0c5913e1dbf7367bdb2dc47cb2d3e1a91e7bb..d149361e58756d0ea93deb014121e965a3fe8097 100644 (file)
@@ -69,6 +69,12 @@ enum kernfs_root_flag {
         * following flag enables that behavior.
         */
        KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK       = 0x0002,
+
+       /*
+        * The filesystem supports exportfs operation, so userspace can use
+        * fhandle to access nodes of the fs.
+        */
+       KERNFS_ROOT_SUPPORT_EXPORTOP            = 0x0004,
 };
 
 /* type-specific structures for kernfs_node union members */
@@ -98,6 +104,12 @@ struct kernfs_elem_attr {
 /* represent a kernfs node */
 union kernfs_node_id {
        struct {
+               /*
+                * blktrace will export this struct as a simplified 'struct
+                * fid' (which is a big data struction), so userspace can use
+                * it to find kernfs node. The layout must match the first two
+                * fields of 'struct fid' exactly.
+                */
                u32             ino;
                u32             generation;
        };
index 620794a20a339c7e10948da829fae5a6818e598b..6cefa277f39c61b3ad1004bec9331bd2e883f317 100644 (file)
@@ -1737,7 +1737,8 @@ int cgroup_setup_root(struct cgroup_root *root, u16 ss_mask, int ref_flags)
                &cgroup_kf_syscall_ops : &cgroup1_kf_syscall_ops;
 
        root->kf_root = kernfs_create_root(kf_sops,
-                                          KERNFS_ROOT_CREATE_DEACTIVATED,
+                                          KERNFS_ROOT_CREATE_DEACTIVATED |
+                                          KERNFS_ROOT_SUPPORT_EXPORTOP,
                                           root_cgrp);
        if (IS_ERR(root->kf_root)) {
                ret = PTR_ERR(root->kf_root);