Holy cow, do we need all those?
> +typedef enum {
> + CONT_REMOVED,
> +} container_flagbits_t;
typedefs are verboten. Fortunately this one is never referred to - only
the values are used, so we can delete it.
Taking dcache_lock in here is unfortunate. A filesystem really shouldn't
be playing with that lock.
The code's a bit short on comments.
> + root = d_alloc_root(inode);
> + if (!root) {
> + iput(inode);
> + return -ENOMEM;
I bet that iput() hasn't been tested ;)
People have hit unpleasant problems before now running iput() against
partially-constructed inodes.
> + if (ret)
> + goto out_unlock;
Did we just leak *root?
>
> +static inline void get_first_subsys(const struct container *cont,
> + struct container_subsys_state **css,
> + int *subsys_id) {
> + const struct containerfs_root *root = cont->root;
> + const struct container_subsys *test_ss;
> + BUG_ON(list_empty(&root->subsys_list));
> + test_ss = list_entry(root->subsys_list.next,
> + struct container_subsys, sibling);
> + if (css) {
> + *css = cont->subsys[test_ss->subsys_id];
> + BUG_ON(!*css);
> + }
> + if (subsys_id)
> + *subsys_id = test_ss->subsys_id;
> +}
This ends up having several callers and its too large to inline.
Do we actually want to support lseek on these things?
If not we can leave this null and use nonseekable_open() in ->open.
> + } else if (S_ISREG(mode)) {
> + inode->i_size = 0;
> + inode->i_fop = &container_file_operations;
> + }
The S_ISREG files have no ->i_ops?