/* add new node and rebalance the tree */
        rb_link_node(&sd->s_rb, parent, node);
        rb_insert_color(&sd->s_rb, &sd->s_parent->s_dir.children);
-
-       /* if @sd has ns tag, mark the parent to enable ns filtering */
-       if (sd->s_ns)
-               sd->s_parent->s_flags |= SYSFS_FLAG_HAS_NS;
-
        return 0;
 }
 
                sd->s_parent->s_dir.subdirs--;
 
        rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children);
-
-       /*
-        * Either all or none of the children have tags.  Clearing HAS_NS
-        * when there's no child left is enough to keep the flag synced.
-        */
-       if (RB_EMPTY_ROOT(&sd->s_parent->s_dir.children))
-               sd->s_parent->s_flags &= ~SYSFS_FLAG_HAS_NS;
 }
 
 /**
 static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags)
 {
        struct sysfs_dirent *sd;
+       int type;
 
        if (flags & LOOKUP_RCU)
                return -ECHILD;
                goto out_bad;
 
        /* The sysfs dirent has been moved to a different namespace */
-       if (sd->s_ns && sd->s_ns != sysfs_info(dentry->d_sb)->ns)
-               goto out_bad;
+       type = KOBJ_NS_TYPE_NONE;
+       if (sd->s_parent) {
+               type = sysfs_ns_type(sd->s_parent);
+               if (type != KOBJ_NS_TYPE_NONE &&
+                               sysfs_info(dentry->d_sb)->ns[type] != sd->s_ns)
+                       goto out_bad;
+       }
 
        mutex_unlock(&sysfs_mutex);
 out_valid:
        struct sysfs_inode_attrs *ps_iattr;
        int ret;
 
+       if (!!sysfs_ns_type(parent_sd) != !!sd->s_ns) {
+               WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n",
+                       sysfs_ns_type(parent_sd) ? "required" : "invalid",
+                       parent_sd->s_name, sd->s_name);
+               return -EINVAL;
+       }
+
        sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns);
        sd->s_parent = sysfs_get(parent_sd);
 
        struct rb_node *node = parent_sd->s_dir.children.rb_node;
        unsigned int hash;
 
+       if (!!sysfs_ns_type(parent_sd) != !!ns) {
+               WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n",
+                       sysfs_ns_type(parent_sd) ? "required" : "invalid",
+                       parent_sd->s_name, name);
+               return NULL;
+       }
+
        hash = sysfs_name_hash(name, ns);
        while (node) {
                struct sysfs_dirent *sd;
 EXPORT_SYMBOL_GPL(sysfs_get_dirent_ns);
 
 static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd,
+                     enum kobj_ns_type type,
                      const char *name, const void *ns,
                      struct sysfs_dirent **p_sd)
 {
        if (!sd)
                return -ENOMEM;
 
+       sd->s_flags |= (type << SYSFS_NS_TYPE_SHIFT);
        sd->s_ns = ns;
        sd->s_dir.kobj = kobj;
 
 int sysfs_create_subdir(struct kobject *kobj, const char *name,
                        struct sysfs_dirent **p_sd)
 {
-       return create_dir(kobj, kobj->sd, name, NULL, p_sd);
+       return create_dir(kobj, kobj->sd,
+                         KOBJ_NS_TYPE_NONE, name, NULL, p_sd);
+}
+
+/**
+ *     sysfs_read_ns_type: return associated ns_type
+ *     @kobj: the kobject being queried
+ *
+ *     Each kobject can be tagged with exactly one namespace type
+ *     (i.e. network or user).  Return the ns_type associated with
+ *     this object if any
+ */
+static enum kobj_ns_type sysfs_read_ns_type(struct kobject *kobj)
+{
+       const struct kobj_ns_type_operations *ops;
+       enum kobj_ns_type type;
+
+       ops = kobj_child_ns_ops(kobj);
+       if (!ops)
+               return KOBJ_NS_TYPE_NONE;
+
+       type = ops->type;
+       BUG_ON(type <= KOBJ_NS_TYPE_NONE);
+       BUG_ON(type >= KOBJ_NS_TYPES);
+       BUG_ON(!kobj_ns_type_registered(type));
+
+       return type;
 }
 
 /**
  */
 int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
 {
+       enum kobj_ns_type type;
        struct sysfs_dirent *parent_sd, *sd;
        int error = 0;
 
        if (!parent_sd)
                return -ENOENT;
 
-       error = create_dir(kobj, parent_sd, kobject_name(kobj), ns, &sd);
+       type = sysfs_read_ns_type(kobj);
+
+       error = create_dir(kobj, parent_sd, type, kobject_name(kobj), ns, &sd);
        if (!error)
                kobj->sd = sd;
        return error;
        struct sysfs_dirent *parent_sd = parent->d_fsdata;
        struct sysfs_dirent *sd;
        struct inode *inode;
-       const void *ns = NULL;
+       enum kobj_ns_type type;
+       const void *ns;
 
        mutex_lock(&sysfs_mutex);
 
-       if (parent_sd->s_flags & SYSFS_FLAG_HAS_NS)
-               ns = sysfs_info(dir->i_sb)->ns;
+       type = sysfs_ns_type(parent_sd);
+       ns = sysfs_info(dir->i_sb)->ns[type];
 
        sd = sysfs_find_dirent(parent_sd, dentry->d_name.name, ns);
 
        struct dentry *dentry = file->f_path.dentry;
        struct sysfs_dirent *parent_sd = dentry->d_fsdata;
        struct sysfs_dirent *pos = file->private_data;
-       const void *ns = NULL;
+       enum kobj_ns_type type;
+       const void *ns;
+
+       type = sysfs_ns_type(parent_sd);
+       ns = sysfs_info(dentry->d_sb)->ns[type];
 
        if (!dir_emit_dots(file, ctx))
                return 0;
        mutex_lock(&sysfs_mutex);
-
-       if (parent_sd->s_flags & SYSFS_FLAG_HAS_NS)
-               ns = sysfs_info(dentry->d_sb)->ns;
-
        for (pos = sysfs_dir_pos(ns, parent_sd, ctx->pos, pos);
             pos;
             pos = sysfs_dir_next_pos(ns, parent_sd, ctx->pos, pos)) {
 
 struct sysfs_dirent sysfs_root = {
        .s_name         = "",
        .s_count        = ATOMIC_INIT(1),
-       .s_flags        = SYSFS_DIR,
+       .s_flags        = SYSFS_DIR | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT),
        .s_mode         = S_IFDIR | S_IRUGO | S_IXUGO,
        .s_ino          = 1,
 };
 {
        struct sysfs_super_info *sb_info = sysfs_info(sb);
        struct sysfs_super_info *info = data;
+       enum kobj_ns_type type;
+       int found = 1;
 
-       return sb_info->ns == info->ns;
+       for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) {
+               if (sb_info->ns[type] != info->ns[type])
+                       found = 0;
+       }
+       return found;
 }
 
 static int sysfs_set_super(struct super_block *sb, void *data)
 
 static void free_sysfs_super_info(struct sysfs_super_info *info)
 {
-       kobj_ns_drop(KOBJ_NS_TYPE_NET, info->ns);
+       int type;
+       for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++)
+               kobj_ns_drop(type, info->ns[type]);
        kfree(info);
 }
 
        int flags, const char *dev_name, void *data)
 {
        struct sysfs_super_info *info;
+       enum kobj_ns_type type;
        struct super_block *sb;
        int error;
 
                if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type))
                        return ERR_PTR(-EPERM);
 
-               if (!kobj_ns_current_may_mount(KOBJ_NS_TYPE_NET))
-                       return ERR_PTR(-EPERM);
+               for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) {
+                       if (!kobj_ns_current_may_mount(type))
+                               return ERR_PTR(-EPERM);
+               }
        }
 
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (!info)
                return ERR_PTR(-ENOMEM);
 
-       info->ns = kobj_ns_grab_current(KOBJ_NS_TYPE_NET);
+       for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++)
+               info->ns[type] = kobj_ns_grab_current(type);
 
        sb = sget(fs_type, sysfs_test_super, sysfs_set_super, flags, info);
        if (IS_ERR(sb) || sb->s_fs_info != info)
 
        struct sysfs_dirent *target_sd = NULL;
        struct sysfs_dirent *sd = NULL;
        struct sysfs_addrm_cxt acxt;
+       enum kobj_ns_type ns_type;
        int error;
 
        BUG_ON(!name || !parent_sd);
        if (!sd)
                goto out_put;
 
-       sd->s_ns = target_sd->s_ns;
+       ns_type = sysfs_ns_type(parent_sd);
+       if (ns_type)
+               sd->s_ns = target_sd->s_ns;
        sd->s_symlink.target_sd = target_sd;
        target_sd = NULL;       /* reference is now owned by the symlink */
 
        sysfs_addrm_start(&acxt);
-       if (warn)
-               error = sysfs_add_one(&acxt, sd, parent_sd);
-       else
-               error = __sysfs_add_one(&acxt, sd, parent_sd);
+       /* Symlinks must be between directories with the same ns_type */
+       if (!ns_type ||
+           (ns_type == sysfs_ns_type(sd->s_symlink.target_sd->s_parent))) {
+               if (warn)
+                       error = sysfs_add_one(&acxt, sd, parent_sd);
+               else
+                       error = __sysfs_add_one(&acxt, sd, parent_sd);
+       } else {
+               error = -EINVAL;
+               WARN(1, KERN_WARNING
+                       "sysfs: symlink across ns_types %s/%s -> %s/%s\n",
+                       parent_sd->s_name,
+                       sd->s_name,
+                       sd->s_symlink.target_sd->s_parent->s_name,
+                       sd->s_symlink.target_sd->s_name);
+       }
        sysfs_addrm_finish(&acxt);
 
        if (error)
         * sysfs_remove_dir() for details.
         */
        spin_lock(&sysfs_symlink_target_lock);
-       if (targ->sd)
+       if (targ->sd && sysfs_ns_type(kobj->sd))
                ns = targ->sd->s_ns;
        spin_unlock(&sysfs_symlink_target_lock);
        sysfs_hash_and_remove(kobj->sd, name, ns);
 
 #define SYSFS_COPY_NAME                        (SYSFS_DIR | SYSFS_KOBJ_LINK)
 #define SYSFS_ACTIVE_REF               (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR)
 
-#define SYSFS_FLAG_MASK                        ~SYSFS_TYPE_MASK
-#define SYSFS_FLAG_HAS_NS              0x01000
+/* identify any namespace tag on sysfs_dirents */
+#define SYSFS_NS_TYPE_MASK             0xf00
+#define SYSFS_NS_TYPE_SHIFT            8
+
+#define SYSFS_FLAG_MASK                        ~(SYSFS_NS_TYPE_MASK|SYSFS_TYPE_MASK)
 #define SYSFS_FLAG_REMOVED             0x02000
 
 static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
        return sd->s_flags & SYSFS_TYPE_MASK;
 }
 
+/*
+ * Return any namespace tags on this dirent.
+ * enum kobj_ns_type is defined in linux/kobject.h
+ */
+static inline enum kobj_ns_type sysfs_ns_type(struct sysfs_dirent *sd)
+{
+       return (sd->s_flags & SYSFS_NS_TYPE_MASK) >> SYSFS_NS_TYPE_SHIFT;
+}
+
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 
 #define sysfs_dirent_init_lockdep(sd)                          \
  */
 
 /*
- * Each sb is associated with one namespace tag, currently the network
- * namespace of the task which mounted this sysfs instance.  If multiple
- * tags become necessary, make the following an array and compare
- * sysfs_dirent tag against every entry.
+ * Each sb is associated with a set of namespace tags (i.e.
+ * the network namespace of the task which mounted this sysfs
+ * instance).
  */
 struct sysfs_super_info {
-       void *ns;
+       void *ns[KOBJ_NS_TYPES];
 };
 #define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info))
 extern struct sysfs_dirent sysfs_root;