Subject: Re: [PATCH 1/8] sysfs: Implement sysfs tagged directory support.

From: Benjamin Thery <[email-masked]>
Date: Wed, 27 Aug 2008 17:18:48 +0200

Eric W. Biederman wrote:
> The problem.  When implementing a network namespace I need to be able
> to have multiple network devices with the same name.  Currently this
> is a problem for /sys/class/net/*, /sys/devices/virtual/net/*, and
> potentially a few other directories of the form /sys/ ... /net/*.
> 
> What this patch does is to add an additional tag field to the
> sysfs dirent structure.  For directories that should show different
> contents depending on the context such as /sys/class/net/, and
> /sys/devices/virtual/net/ this tag field is used to specify the
> context in which those directories should be visible.  Effectively
> this is the same as creating multiple distinct directories with
> the same name but internally to sysfs the result is nicer.
> 
> I am calling the concept of a single directory that looks like multiple
> directories all at the same path in the filesystem tagged directories.
> 
> For the networking namespace the set of directories whose contents I need
> to filter with tags can depend on the presence or absence of hotplug
> hardware or which modules are currently loaded.  Which means I need
> a simple race free way to setup those directories as tagged.
> 
> To achieve a reace free design all tagged directories are created
> and managed by sysfs itself.
> 
> Users of this interface:
> - define a type in the sysfs_tag_type enumeration.
> - call sysfs_register_tag_types with the type and it's operations
> - call sysfs_make_tagged_dir with the tag type on directories
>   to be managed by this tag type
> - sysfs_exit_tag when an individual tag is no longer valid
> 
> - Implement mount_tag() which returns the tag of the calling process
>   so we can attach it to a sysfs superblock.
> - Implement ktype.sysfs_tag() which returns the tag of a syfs kobject.
> 
> Everything else is left up to sysfs and the driver layer.
> 
> For the network namespace mount_tag and sysfs_tag are essentially
> one line functions, and look to remain that.
> 
> Tags are currently represented a const void * pointers as that is
> both generic, prevides enough information for equality comparisons,
> and is trivial to create for current users, as it is just the
> existing namespace pointer.
> 
> The work needed in sysfs is more extensive.  At each directory
> or symlink creating I need to check if the directory it is being
> created in is a tagged directory and if so generate the appropriate
> tag to place on the sysfs_dirent.  Likewise at each symlink or
> directory removal I need to check if the sysfs directory it is
> being removed from is a tagged directory and if so figure out
> which tag goes along with the name I am deleting.
> 
> Currently only directories which hold kobjects, and
> symlinks are supported.  There is not enough information
> in the current file attribute interfaces to give us anything
> to discriminate on which makes it useless, and there are
> no potential users which makes it an uninteresting problem
> to solve.
> 
> Signed-off-by: Eric W. Biederman <[email-masked]>
> Signed-off-by: Benjamin Thery <[email-masked]>
> ---
>  fs/sysfs/bin.c          |    2 +-
>  fs/sysfs/dir.c          |  139 ++++++++++++++++++++++++++++++++++++++++++-----
>  fs/sysfs/file.c         |   11 +++--
>  fs/sysfs/group.c        |    4 +-
>  fs/sysfs/inode.c        |    7 ++-
>  fs/sysfs/mount.c        |  115 +++++++++++++++++++++++++++++++++++++--
>  fs/sysfs/symlink.c      |    2 +-
>  fs/sysfs/sysfs.h        |   19 ++++++-
>  include/linux/kobject.h |    1 +
>  include/linux/sysfs.h   |   31 +++++++++++
>  10 files changed, 298 insertions(+), 33 deletions(-)
> 
> diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c
> index 006fc64..86e1128 100644
> --- a/fs/sysfs/bin.c
> +++ b/fs/sysfs/bin.c
> @@ -252,7 +252,7 @@ int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
>  
>  void sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr)
>  {
> -	sysfs_hash_and_remove(kobj->sd, attr->attr.name);
> +	sysfs_hash_and_remove(kobj, kobj->sd, attr->attr.name);
>  }
>  
>  EXPORT_SYMBOL_GPL(sysfs_create_bin_file);
> diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
> index 4ffcfd2..dec7586 100644
> --- a/fs/sysfs/dir.c
> +++ b/fs/sysfs/dir.c
> @@ -30,6 +30,30 @@ DEFINE_SPINLOCK(sysfs_assoc_lock);
>  static DEFINE_SPINLOCK(sysfs_ino_lock);
>  static DEFINE_IDA(sysfs_ino_ida);
>  
> +static const void *sysfs_creation_tag(struct sysfs_dirent *parent_sd,
> +				      struct sysfs_dirent *sd)
> +{
> +	const void *tag = NULL;
> +
> +	if (sysfs_tag_type(parent_sd)) {
> +		struct kobject *kobj;
> +		switch (sysfs_type(sd)) {
> +		case SYSFS_DIR:
> +			kobj = sd->s_dir.kobj;
> +			break;
> +		case SYSFS_KOBJ_LINK:
> +			kobj = sd->s_symlink.target_sd->s_dir.kobj;
> +			break;
> +		default:
> +			BUG();
> +		}
> +		tag = kobj->ktype->sysfs_tag(kobj);
> +		/* NULL tags are reserved for internal use */
> +		BUG_ON(tag == NULL);
> +	}
> +	return tag;
> +}
> +
>  /**
>   *	sysfs_link_sibling - link sysfs_dirent into sibling list
>   *	@sd: sysfs_dirent of interest
> @@ -101,8 +125,19 @@ static void sysfs_unlink_sibling(struct sysfs_dirent *sd)
>  struct dentry *sysfs_get_dentry(struct super_block *sb,
>  				struct sysfs_dirent *sd)
>  {
> -	struct dentry *dentry = dget(sb->s_root);
> +	struct dentry *dentry;
> +
> +	/* Bail if this sd won't show up in this superblock */
> +	if (sd->s_parent) {
> +		enum sysfs_tag_type type;
> +		const void *tag;
> +		type = sysfs_tag_type(sd->s_parent);
> +		tag = sysfs_info(sb)->tag[type];
> +		if (sd->s_tag != tag)
> +			return ERR_PTR(-EXDEV);
> +	}
>  
> +	dentry = dget(sb->s_root);
>  	while (dentry->d_fsdata != sd) {
>  		struct sysfs_dirent *cur;
>  		struct dentry *parent;
> @@ -421,10 +456,15 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt,
>   */
>  int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
>  {
> -	if (sysfs_find_dirent(acxt->parent_sd, sd->s_name))
> +	const void *tag = NULL;
> +
> +	tag = sysfs_creation_tag(acxt->parent_sd, sd);
> +
> +	if (sysfs_find_dirent(acxt->parent_sd, tag, sd->s_name))
>  		return -EEXIST;
>  
>  	sd->s_parent = sysfs_get(acxt->parent_sd);
> +	sd->s_tag = tag;
>  
>  	if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode)
>  		inc_nlink(acxt->parent_inode);
> @@ -602,13 +642,17 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt)
>   *	Pointer to sysfs_dirent if found, NULL if not.
>   */
>  struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
> +				       const void *tag,
>  				       const unsigned char *name)
>  {
>  	struct sysfs_dirent *sd;
>  
> -	for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling)
> +	for (sd = parent_sd->s_dir.children; sd; sd = sd->s_sibling) {
> +		if (sd->s_tag != tag)
> +			continue;
>  		if (!strcmp(sd->s_name, name))
>  			return sd;
> +	}
>  	return NULL;
>  }
>  
> @@ -632,7 +676,7 @@ struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd,
>  	struct sysfs_dirent *sd;
>  
>  	mutex_lock(&sysfs_mutex);
> -	sd = sysfs_find_dirent(parent_sd, name);
> +	sd = sysfs_find_dirent(parent_sd, NULL, name);
>  	sysfs_get(sd);
>  	mutex_unlock(&sysfs_mutex);
>  
> @@ -699,13 +743,18 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,
>  				struct nameidata *nd)
>  {
>  	struct dentry *ret = NULL;
> -	struct sysfs_dirent *parent_sd = dentry->d_parent->d_fsdata;
> +	struct dentry *parent = dentry->d_parent;
> +	struct sysfs_dirent *parent_sd = parent->d_fsdata;
>  	struct sysfs_dirent *sd;
>  	struct inode *inode;
> +	enum sysfs_tag_type type;
> +	const void *tag;
>  
>  	mutex_lock(&sysfs_mutex);
>  
> -	sd = sysfs_find_dirent(parent_sd, dentry->d_name.name);
> +	type = sysfs_tag_type(parent_sd);
> +	tag = sysfs_info(parent->d_sb)->tag[type];
> +	sd = sysfs_find_dirent(parent_sd, tag, dentry->d_name.name);
>  
>  	/* no such entry */
>  	if (!sd) {
> @@ -913,19 +962,24 @@ int sysfs_rename_dir(struct kobject * kobj, const char *new_name)
>  	struct sysfs_rename_struct *srs;
>  	struct inode *parent_inode = NULL;
>  	const char *dup_name = NULL;
> +	const void *old_tag, *tag;
>  	int error;
>  
>  	INIT_LIST_HEAD(&todo);
>  	mutex_lock(&sysfs_rename_mutex);
> +	old_tag = sd->s_tag;
> +	tag = sysfs_creation_tag(sd->s_parent, sd);
>  
>  	error = 0;
> -	if (strcmp(sd->s_name, new_name) == 0)
> +	if ((old_tag == tag) && (strcmp(sd->s_name, new_name) == 0))
>  		goto out;	/* nothing to rename */
>  
>  	sysfs_grab_supers();
> -	error = prep_rename(&todo, sd, sd->s_parent, new_name);
> -	if (error)
> -		goto out_release;
> +	if (old_tag == tag) {
> +		error = prep_rename(&todo, sd, sd->s_parent, new_name);
> +		if (error)
> +			goto out_release;
> +	}
>  
>  	error = -ENOMEM;
>  	mutex_lock(&sysfs_mutex);
> @@ -938,7 +992,7 @@ int sysfs_rename_dir(struct kobject * kobj, const char *new_name)
>  	mutex_lock(&sysfs_mutex);
>  
>  	error = -EEXIST;
> -	if (sysfs_find_dirent(sd->s_parent, new_name))
> +	if (sysfs_find_dirent(sd->s_parent, tag, new_name))
>  		goto out_unlock;
>  
>  	/* rename sysfs_dirent */
> @@ -949,6 +1003,7 @@ int sysfs_rename_dir(struct kobject * kobj, const char *new_name)
>  
>  	dup_name = sd->s_name;
>  	sd->s_name = new_name;
> +	sd->s_tag = tag;
>  
>  	/* rename */
>  	list_for_each_entry(srs, &todo, list) {
> @@ -956,6 +1011,20 @@ int sysfs_rename_dir(struct kobject * kobj, const char *new_name)
>  		d_move(srs->old_dentry, srs->new_dentry);
>  	}
>  
> +	/* If we are moving across superblocks drop the dcache entries */
> +	if (old_tag != tag) {
> +		struct super_block *sb;
> +		struct dentry *dentry;
> +		list_for_each_entry(sb, &sysfs_fs_type.fs_supers, s_instances) {
> +			dentry = __sysfs_get_dentry(sb, sd);
> +			if (!dentry)
> +				continue;
> +			shrink_dcache_parent(dentry);
> +			d_drop(dentry);
> +			dput(dentry);
> +		}
> +	}
> +
>  	error = 0;
>  out_unlock:
>  	mutex_unlock(&sysfs_mutex);
> @@ -978,11 +1047,13 @@ int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj)
>  	struct sysfs_rename_struct *srs;
>  	struct inode *old_parent_inode = NULL, *new_parent_inode = NULL;
>  	int error;
> +	const void *tag;
>  
>  	INIT_LIST_HEAD(&todo);
>  	mutex_lock(&sysfs_rename_mutex);
>  	BUG_ON(!sd->s_parent);
>  	new_parent_sd = new_parent_kobj->sd ? new_parent_kobj->sd : &sysfs_root;
> +	tag = sd->s_tag;
>  
>  	error = 0;
>  	if (sd->s_parent == new_parent_sd)
> @@ -1016,7 +1087,7 @@ again:
>  	mutex_lock(&sysfs_mutex);
>  
>  	error = -EEXIST;
> -	if (sysfs_find_dirent(new_parent_sd, sd->s_name))
> +	if (sysfs_find_dirent(new_parent_sd, tag, sd->s_name))
>  		goto out_unlock;
>  
>  	error = 0;
> @@ -1055,10 +1126,12 @@ static inline unsigned char dt_type(struct sysfs_dirent *sd)
>  
>  static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
>  {
> -	struct dentry *dentry = filp->f_path.dentry;
> -	struct sysfs_dirent * parent_sd = dentry->d_fsdata;
> +	struct dentry *parent = filp->f_path.dentry;
> +	struct sysfs_dirent *parent_sd = parent->d_fsdata;
>  	struct sysfs_dirent *pos;
>  	ino_t ino;
> +	enum sysfs_tag_type type;
> +	const void *tag;
>  
>  	if (filp->f_pos == 0) {
>  		ino = parent_sd->s_ino;
> @@ -1076,6 +1149,9 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
>  	if ((filp->f_pos > 1) && (filp->f_pos < INT_MAX)) {
>  		mutex_lock(&sysfs_mutex);
>  
> +		type = sysfs_tag_type(parent_sd);
> +		tag = sysfs_info(parent->d_sb)->tag[type];
> +
>  		/* Skip the dentries we have already reported */
>  		pos = parent_sd->s_dir.children;
>  		while (pos && (filp->f_pos > pos->s_ino))
> @@ -1085,6 +1161,9 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir)
>  			const char * name;
>  			int len;
>  
> +			if (pos->s_tag != tag)
> +				continue;
> +
>  			name = pos->s_name;
>  			len = strlen(name);
>  			filp->f_pos = ino = pos->s_ino;
> @@ -1105,3 +1184,35 @@ const struct file_operations sysfs_dir_operations = {
>  	.read		= generic_read_dir,
>  	.readdir	= sysfs_readdir,
>  };
> +
> +/**
> + *	sysfs_make_tagged_dir - Require tags of all the entries in a directory.
> + *	@kobj:	object whose children should be filtered by tags
> + *
> + *	Once tagging has been enabled on a directory the contents
> + *	of the directory become dependent upon context captured when
> + *	sysfs was mounted.
> + */
> +int sysfs_make_tagged_dir(struct kobject *kobj, enum sysfs_tag_type type)
> +{
> +	struct sysfs_dirent *sd;
> +	int err;
> +
> +	err = -ENOENT;
> +	sd = kobj->sd;
> +
> +	mutex_lock(&sysfs_mutex);
> +	err = -EINVAL;
> +	/* We can only enable tagging when we have a valid tag type
> +	 * on empty directories where taggint has not already been
> +	 * enabled.
> +	 */
> +	if ((type > SYSFS_TAG_TYPE_NONE) && (type < SYSFS_TAG_TYPES) &&
> +	    tag_ops[type] && !sysfs_tag_type(sd) &&
> +	    (sysfs_type(sd) == SYSFS_DIR) && !sd->s_dir.children) {
> +		err = 0;
> +		sd->s_flags |= (type << SYSFS_TAG_TYPE_SHIFT);
> +	}
> +	mutex_unlock(&sysfs_mutex);
> +	return err;
> +}
> diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
> index 61c3476..091c0de 100644
> --- a/fs/sysfs/file.c
> +++ b/fs/sysfs/file.c
> @@ -476,9 +476,12 @@ void sysfs_notify(struct kobject *k, char *dir, char *attr)
>  	mutex_lock(&sysfs_mutex);
>  
>  	if (sd && dir)
> -		sd = sysfs_find_dirent(sd, dir);
> +		/* Only directories are tagged, so no need to pass
> +		 * a tag explicitly.
> +		 */
> +		sd = sysfs_find_dirent(sd, NULL, dir);
>  	if (sd && attr)
> -		sd = sysfs_find_dirent(sd, attr);
> +		sd = sysfs_find_dirent(sd, NULL, attr);
>  	if (sd)
>  		sysfs_notify_dirent(sd);
>  
> @@ -640,7 +643,7 @@ EXPORT_SYMBOL_GPL(sysfs_chmod_file);
>  
>  void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr)
>  {
> -	sysfs_hash_and_remove(kobj->sd, attr->name);
> +	sysfs_hash_and_remove(kobj, kobj->sd, attr->name);
>  }
>  
>  
> @@ -660,7 +663,7 @@ void sysfs_remove_file_from_group(struct kobject *kobj,
>  	else
>  		dir_sd = sysfs_get(kobj->sd);
>  	if (dir_sd) {
> -		sysfs_hash_and_remove(dir_sd, attr->name);
> +		sysfs_hash_and_remove(kobj, dir_sd, attr->name);
>  		sysfs_put(dir_sd);
>  	}
>  }
> diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c
> index fe61194..5fba6f2 100644
> --- a/fs/sysfs/group.c
> +++ b/fs/sysfs/group.c
> @@ -23,7 +23,7 @@ static void remove_files(struct sysfs_dirent *dir_sd, struct kobject *kobj,
>  	int i;
>  
>  	for (i = 0, attr = grp->attrs; *attr; i++, attr++)
> -		sysfs_hash_and_remove(dir_sd, (*attr)->name);
> +		sysfs_hash_and_remove(kobj, dir_sd, (*attr)->name);
>  }
>  
>  static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj,
> @@ -39,7 +39,7 @@ static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj,
>  		 * visibility.  Do this by first removing then
>  		 * re-adding (if required) the file */
>  		if (update)
> -			sysfs_hash_and_remove(dir_sd, (*attr)->name);
> +			sysfs_hash_and_remove(kobj, dir_sd, (*attr)->name);
>  		if (grp->is_visible) {
>  			mode = grp->is_visible(kobj, *attr, i);
>  			if (!mode)
> diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c
> index 80f8fd4..b5fc78a 100644
> --- a/fs/sysfs/inode.c
> +++ b/fs/sysfs/inode.c
> @@ -226,17 +226,20 @@ struct inode * sysfs_get_inode(struct sysfs_dirent *sd)
>  	return inode;
>  }
>  
> -int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name)
> +int sysfs_hash_and_remove(struct kobject *kobj, struct sysfs_dirent *dir_sd,
> +			  const char *name)
>  {
>  	struct sysfs_addrm_cxt acxt;
>  	struct sysfs_dirent *sd;
> +	const void *tag;
>  
>  	if (!dir_sd)
>  		return -ENOENT;
>  
>  	sysfs_addrm_start(&acxt, dir_sd);
> +	tag = kobj->sd->s_tag;
>  
> -	sd = sysfs_find_dirent(dir_sd, name);
> +	sd = sysfs_find_dirent(dir_sd, tag, name);
>  	if (sd)
>  		sysfs_remove_one(&acxt, sd);
>  
> diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c
> index 6ebda1a..8f2237a 100644
> --- a/fs/sysfs/mount.c
> +++ b/fs/sysfs/mount.c
> @@ -35,12 +35,15 @@ static const struct super_operations sysfs_ops = {
>  struct sysfs_dirent sysfs_root = {
>  	.s_name		= "",
>  	.s_count	= ATOMIC_INIT(1),
> -	.s_flags	= SYSFS_DIR,
> +	.s_flags	= SYSFS_DIR | (SYSFS_TAG_TYPE_NONE << SYSFS_TAG_TYPE_SHIFT),
>  	.s_mode		= S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO,
>  	.s_ino		= 1,
>  };
>  
> -static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
> +struct sysfs_tag_type_operations *tag_ops[SYSFS_TAG_TYPES];
> +
> +static int sysfs_fill_super(struct super_block *sb, void *data, int silent,
> +	const void *tags[SYSFS_TAG_TYPES])
>  {
>  	struct sysfs_super_info *info = NULL;
>  	struct inode *inode = NULL;
> @@ -76,8 +79,10 @@ static int sysfs_fill_super(struct super_block *sb, void *data, int silent)
>  		goto out_err;
>  	}
>  	root->d_fsdata = &sysfs_root;
> +	root->d_sb = sb;
>  	sb->s_root = root;
>  	sb->s_fs_info = info;
> +	memcpy(info->tag, tags, sizeof(info->tag[0])*SYSFS_TAG_TYPES);
>  	return 0;
>  
>  out_err:
> @@ -89,20 +94,74 @@ out_err:
>  	return error;
>  }
>  
> +static int sysfs_test_super(struct super_block *sb, void *ptr)
> +{
> +	const void **tag = ptr;
> +	struct sysfs_super_info *info = sysfs_info(sb);
> +	enum sysfs_tag_type type;
> +	int found = 1;
> +
> +	for (type = SYSFS_TAG_TYPE_NONE; type < SYSFS_TAG_TYPES; type++) {
> +		if (info->tag[type] != tag[type]) {
> +			found = 0;
> +			break;
> +		}
> +	}
> +
> +	return found;
> +}
> +
>  static int sysfs_get_sb(struct file_system_type *fs_type,
>  	int flags, const char *dev_name, void *data, struct vfsmount *mnt)
>  {
> -	int rc;
> +	const void *tag[SYSFS_TAG_TYPES];
> +	struct super_block *sb;
> +	int error;
> +	enum sysfs_tag_type type;
> +
> +	for (type = SYSFS_TAG_TYPE_NONE; type < SYSFS_TAG_TYPES; type++) {
> +		tag[type] = NULL;
> +		if (!tag_ops[type])
> +			continue;
> +		tag[type] = tag_ops[type]->mount_tag();
> +	}
> +
>  	mutex_lock(&sysfs_rename_mutex);
> -	rc = get_sb_single(fs_type, flags, data, sysfs_fill_super, mnt);
> +	sb = sget(fs_type, sysfs_test_super, set_anon_super, tag);
> +	if (IS_ERR(sb)) {
> +		error = PTR_ERR(sb);
> +		goto out;
> +	}
> +	if (!sb->s_root) {
> +		sb->s_flags = flags;
> +		error = sysfs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0,
> +					tag);
> +		if (error) {
> +			up_write(&sb->s_umount);
> +			deactivate_super(sb);
> +			goto out;
> +		}
> +		sb->s_flags |= MS_ACTIVE;
> +	}
> +	do_remount_sb(sb, flags, data, 0);
> +	error = simple_set_mnt(mnt, sb);
> +out:
>  	mutex_unlock(&sysfs_rename_mutex);
> -	return rc;
> +	return error;
> +}
> +
> +static void sysfs_kill_sb(struct super_block *sb)
> +{
> +	struct sysfs_super_info *info = sysfs_info(sb);
> +
> +	kill_anon_super(sb);
> +	kfree(info);
>  }
>  
>  struct file_system_type sysfs_fs_type = {
>  	.name		= "sysfs",
>  	.get_sb		= sysfs_get_sb,
> -	.kill_sb	= kill_anon_super,
> +	.kill_sb	= sysfs_kill_sb,
>  };
>  
>  void sysfs_grab_supers(void)
> @@ -146,6 +205,50 @@ restart:
>  	spin_unlock(&sb_lock);
>  }
>  
> +int sysfs_register_tag_type(enum sysfs_tag_type type, struct sysfs_tag_type_operations *ops)
> +{
> +	int error;
> +
> +	mutex_lock(&sysfs_rename_mutex);
> +
> +	error = -EINVAL;
> +	if (type >= SYSFS_TAG_TYPES)
> +		goto out;
> +
> +	error = -EINVAL;
> +	if (type <= SYSFS_TAG_TYPE_NONE)
> +		goto out;
> +
> +	error = -EBUSY;
> +	if (tag_ops[type])
> +		goto out;
> +
> +	error = 0;
> +	tag_ops[type] = ops;
> +
> +out:
> +	mutex_unlock(&sysfs_rename_mutex);
> +	return error;
> +}
> +
> +void sysfs_exit_tag(enum sysfs_tag_type type, const void *tag)
> +{
> +	/* Allow the tag to go away while sysfs is still mounted. */
> +	struct super_block *sb;
> +	mutex_lock(&sysfs_rename_mutex);
> +	sysfs_grab_supers();
> +	mutex_lock(&sysfs_mutex);
> +	list_for_each_entry(sb, &sysfs_fs_type.fs_supers, s_instances) {
> +		struct sysfs_super_info *info = sysfs_info(sb);
> +		if (info->tag[type] != tag)
> +			continue;
> +		info->tag[type] = NULL;
> +	}
> +	mutex_unlock(&sysfs_mutex);
> +	sysfs_release_supers();
> +	mutex_unlock(&sysfs_rename_mutex);
> +}
> +
>  int __init sysfs_init(void)
>  {
>  	int err = -ENOMEM;
> diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c
> index a3ba217..54b2e5f 100644
> --- a/fs/sysfs/symlink.c
> +++ b/fs/sysfs/symlink.c
> @@ -119,7 +119,7 @@ void sysfs_remove_link(struct kobject * kobj, const char * name)
>  	else
>  		parent_sd = kobj->sd;
>  
> -	sysfs_hash_and_remove(parent_sd, name);
> +	sysfs_hash_and_remove(kobj, parent_sd, name);
>  }
>  
>  static int sysfs_get_target_path(struct sysfs_dirent *parent_sd,
> diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h
> index f0e5ecb..67115ec 100644
> --- a/fs/sysfs/sysfs.h
> +++ b/fs/sysfs/sysfs.h
> @@ -45,6 +45,7 @@ struct sysfs_dirent {
>  	struct sysfs_dirent	*s_sibling;
>  	const char		*s_name;
>  
> +	const void		*s_tag;
>  	union {
>  		struct sysfs_elem_dir		s_dir;
>  		struct sysfs_elem_symlink	s_symlink;
> @@ -67,14 +68,22 @@ struct sysfs_dirent {
>  #define SYSFS_KOBJ_LINK			0x0008
>  #define SYSFS_COPY_NAME			(SYSFS_DIR | SYSFS_KOBJ_LINK)
>  
> -#define SYSFS_FLAG_MASK			~SYSFS_TYPE_MASK
> -#define SYSFS_FLAG_REMOVED		0x0200
> +#define SYSFS_TAG_TYPE_MASK		0xff00
> +#define SYSFS_TAG_TYPE_SHIFT		8
> +
> +#define SYSFS_FLAG_MASK			~(SYSFS_TYPE_MASK | SYSFS_TAG_TYPE_MASK)
> +#define SYSFS_FLAG_REMOVED		0x020000
>  
>  static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
>  {
>  	return sd->s_flags & SYSFS_TYPE_MASK;
>  }
>  
> +static inline enum sysfs_tag_type sysfs_tag_type(struct sysfs_dirent *sd)
> +{
> +	return (sd->s_flags & SYSFS_TAG_TYPE_MASK) >> SYSFS_TAG_TYPE_SHIFT;
> +}
> +
>  /*
>   * Context structure to be used while adding/removing nodes.
>   */
> @@ -87,6 +96,7 @@ struct sysfs_addrm_cxt {
>  
>  struct sysfs_super_info {
>  	int	grabbed;
> +	const void *tag[SYSFS_TAG_TYPES];
>  };
>  
>  #define sysfs_info(SB) ((struct sysfs_super_info *)(SB)->s_fs_info)
> @@ -98,6 +108,7 @@ extern struct sysfs_dirent sysfs_root;
>  extern struct super_block *sysfs_sb;
>  extern struct kmem_cache *sysfs_dir_cachep;
>  extern struct file_system_type sysfs_fs_type;
> +extern struct sysfs_tag_type_operations *tag_ops[SYSFS_TAG_TYPES];
>  
>  void sysfs_grab_supers(void);
>  void sysfs_release_supers(void);
> @@ -124,6 +135,7 @@ void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd);
>  void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt);
>  
>  struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
> +				       const void *tag,
>  				       const unsigned char *name);
>  struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd,
>  				      const unsigned char *name);
> @@ -158,7 +170,8 @@ static inline void __sysfs_put(struct sysfs_dirent *sd)
>  struct inode *sysfs_get_inode(struct sysfs_dirent *sd);
>  int sysfs_sd_setattr(struct sysfs_dirent *sd, struct inode *inode, struct iattr *iattr);
>  int sysfs_setattr(struct dentry *dentry, struct iattr *iattr);
> -int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name);
> +int sysfs_hash_and_remove(struct kobject *kobj, struct sysfs_dirent *dir_sd,
> +			  const char *name);
>  int sysfs_inode_init(void);
>  
>  /*
> diff --git a/include/linux/kobject.h b/include/linux/kobject.h
> index 5437ac0..beb3573 100644
> --- a/include/linux/kobject.h
> +++ b/include/linux/kobject.h
> @@ -105,6 +105,7 @@ struct kobj_type {
>  	void (*release)(struct kobject *kobj);
>  	struct sysfs_ops *sysfs_ops;
>  	struct attribute **default_attrs;
> +	const void *(*sysfs_tag)(struct kobject *kobj);
>  };
>  
>  struct kobj_uevent_env {
> diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h
> index d8e0230..ba68829 100644
> --- a/include/linux/sysfs.h
> +++ b/include/linux/sysfs.h
> @@ -80,6 +80,15 @@ struct sysfs_ops {
>  
>  struct sysfs_dirent;
>  
> +enum sysfs_tag_type {
> +	SYSFS_TAG_TYPE_NONE = 0,
> +	SYSFS_TAG_TYPES
> +};
> +
> +struct sysfs_tag_type_operations {
> +	const void *(*mount_tag)(void);
> +};
> +
>  #ifdef CONFIG_SYSFS
>  
>  int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *),
> @@ -126,6 +135,12 @@ struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd,
>  struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd);
>  void sysfs_put(struct sysfs_dirent *sd);
>  void sysfs_printk_last_file(void);
> +
> +int sysfs_make_tagged_dir(struct kobject *, enum sysfs_tag_type tag_type);
> +int sysfs_register_tag_type(enum sysfs_tag_type type,
> +			    struct sysfs_tag_type_operations *ops);
> +void sysfs_exit_tag(enum sysfs_tag_type type, const void *tag);
> +
>  int __must_check sysfs_init(void);
>  
>  #else /* CONFIG_SYSFS */
> @@ -249,6 +264,22 @@ static inline void sysfs_put(struct sysfs_dirent *sd)
>  {
>  }
>  
> +staticn inline int sysfs_make_tagged_dir(struct kobject *kobj,
    ______^

This typo is still present in your patch in the CONFIG_SYSFS=n case.

Otherwise the patchset, combined with the patches Greg has already
merged in his tree, still works great for me with network namespaces.

--Benjamin

> +						enum sysfs_tag_type tag_type)
> +{
> +	return 0;
> +}
> +
> +static inline int sysfs_register_tag_type(enum sysfs_tag_type type,
> +					struct sysfs_tag_type_operations *ops)
> +{
> +	return 0;
> +}
> +
> +static inline void sysfs_exit_tag(enum sysfs_tag_type type, const void *tag)
> +{
> +}
> +
>  static inline int __must_check sysfs_init(void)
>  {
>  	return 0;

-- 
B e n j a m i n   T h e r y  - BULL/DT/Open Software R&D

    http://www.bull.com