Okay, here's a different implementation of tagged nodes. I just
compile tested it so I can guarantee it's broken. I don't think
there's anything fundamentally wrong but I'm wrong fairly often, so if
you find something moronic, feel free to scream at me.
1. there's no enable_tagging() or fundamental restrictions on which
types of nodes can be tagged.
2. no callback. tags are set by sysfs_rename_dir_tagged(). symlinks
follow the tag of its target_sd. This limits taggable node types
to dirs and symlinks.
3. in all paths between the root and leaf nodes, only zero or one
tagged sd exists. all children of a tagged sd have NULL s_tag but
are considered tagged the same as the tagged ancestor.
4. untagged entries are visible in all supers unless there's a
matching tagged entry overshadowing it. tagged entry is only
visible in the matching tagged super.
5. due to #3, children in a directory which belong to the same tag can
be looked up using NULL tag. Most leaf node ops don't need to be
changed.
6. symlink removal is different. we either need to modify the
interface to take target_sd or implement new one. actually, I
think linking symlinks to target_sd and renaming/removing them
automagically would be pretty good.
Thanks. What do you think?
JUST FOR BRAIN STORMING. DO NOT APPLY.
---
fs/sysfs/bin.c | 2
fs/sysfs/dir.c | 122 ++++++++++++++++++++++++++++++++++++++++----------
fs/sysfs/file.c | 4 -
fs/sysfs/group.c | 2
fs/sysfs/inode.c | 5 +-
fs/sysfs/mount.c | 44 ++++++++++++++++--
fs/sysfs/symlink.c | 8 ++-
fs/sysfs/sysfs.h | 11 +++-
include/linux/sysfs.h | 4 +
9 files changed, 167 insertions(+), 35 deletions(-)
Index: work/fs/sysfs/dir.c
===================================================================
--- work.orig/fs/sysfs/dir.c
+++ work/fs/sysfs/dir.c
@@ -387,7 +387,7 @@ void sysfs_addrm_start(struct sysfs_addr
*/
int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd)
{
- if (sysfs_find_dirent(acxt->parent_sd, sd->s_name))
+ if (__sysfs_find_dirent(acxt->parent_sd, sd->s_name, sd->s_tag, 0))
return -EEXIST;
sd->s_parent = sysfs_get(acxt->parent_sd);
@@ -526,6 +526,24 @@ void sysfs_addrm_finish(struct sysfs_add
}
}
+struct sysfs_dirent *__sysfs_find_dirent(struct sysfs_dirent *parent_sd,
+ const unsigned char *name,
+ const void *tag, int match_null)
+{
+ struct sysfs_dirent *null_sd = NULL, *sd;
+
+ for (sd = parent_sd->s_children; sd; sd = sd->s_sibling) {
+ if (strcmp(sd->s_name, name))
+ continue;
+ if (sd->s_tag == tag)
+ return sd;
+ if (!sd->s_tag && match_null)
+ null_sd = sd;
+ }
+
+ return null_sd;
+}
+
/**
* sysfs_find_dirent - find sysfs_dirent with the given name
* @parent_sd: sysfs_dirent to search under
@@ -542,12 +560,7 @@ void sysfs_addrm_finish(struct sysfs_add
struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd,
const unsigned char *name)
{
- struct sysfs_dirent *sd;
-
- for (sd = parent_sd->s_children; sd; sd = sd->s_sibling)
- if (!strcmp(sd->s_name, name))
- return sd;
- return NULL;
+ return __sysfs_find_dirent(parent_sd, name, NULL, 0);
}
/**
@@ -642,7 +655,8 @@ static struct dentry * sysfs_lookup(stru
mutex_lock(&sysfs_mutex);
- sd = sysfs_find_dirent(parent_sd, dentry->d_name.name);
+ sd = __sysfs_find_dirent(parent_sd, dentry->d_name.name,
+ sysfs_info(dentry->d_sb)->tag, 1);
/* no such entry */
if (!sd)
@@ -803,9 +817,19 @@ out:
return dentry;
}
+const void *sysfs_dirent_tag(struct sysfs_dirent *sd)
+{
+ while (sd) {
+ if (sd->s_tag)
+ return sd->s_tag;
+ sd = sd->s_parent;
+ }
+ return sd->s_tag;
+}
+
/**
* sysfs_get_dentry - get dentry for the given sysfs_dirent
- * @sb: superblock of the dentry to return
+ * @super: superblock of the dentry to return
* @sd: sysfs_dirent of interest
*
* Get dentry for @sd. Dentry is looked up if currently not
@@ -820,16 +844,23 @@ out:
* Pointer to found dentry on success, ERR_PTR() value on error.
* NULL if the sysfs dentry does not appear in the specified superblock
*/
-struct dentry *sysfs_get_dentry(struct super_block *sb, struct sysfs_dirent *sd)
+struct dentry *sysfs_get_dentry(struct super_block *super,
+ struct sysfs_dirent *sd)
{
+ const void *tag;
struct sysfs_dirent *cur;
struct dentry *parent_dentry, *dentry;
+ /* bail if this sd won't show up in this superblock */
+ tag = sysfs_dirent_tag(sd);
+ if (tag && tag != sysfs_info(super)->tag)
+ return ERR_PTR(-ENOENT);
+
/* Find the first parent which has valid dentry.
*/
dentry = NULL;
cur = sd;
- while (!(dentry = __sysfs_get_dentry(sb, cur))) {
+ while (!(dentry = __sysfs_get_dentry(super, cur))) {
if (cur->s_flags & SYSFS_FLAG_REMOVED) {
dentry = ERR_PTR(-ENOENT);
break;
@@ -926,27 +957,44 @@ err_out:
return error;
}
-
-int sysfs_rename_dir(struct kobject * kobj, const char *new_name)
+int sysfs_rename_dir_tagged(struct kobject * kobj, const char *new_name,
+ const void *new_tag)
{
struct sysfs_dirent *sd = kobj->sd;
struct list_head todo;
struct sysfs_rename_struct *srs;
struct inode *parent_inode = NULL;
const char *dup_name = NULL;
- int error;
+ const void *old_tag;
+ int tag = 0, error;
INIT_LIST_HEAD(&todo);
mutex_lock(&sysfs_rename_mutex);
+ old_tag = sysfs_dirent_tag(sd);
+
+ /* need tagging? */
+ if (old_tag != new_tag) {
+ tag = 1;
+
+ /* can't tag again in tagged subtree */
+ error = -EINVAL;
+ if (old_tag != new_tag)
+ goto out;
+ } else
+ /* sd->s_tag can be either old_tag or NULL */
+ new_tag = sd->s_tag;
+
error = 0;
- if (strcmp(sd->s_name, new_name) == 0)
+ if (!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 (!tag) {
+ error = prep_rename(&todo, sd, sd->s_parent, new_name);
+ if (error)
+ goto out_release;
+ }
error = -ENOMEM;
mutex_lock(&sysfs_mutex);
@@ -959,7 +1007,7 @@ int sysfs_rename_dir(struct kobject * ko
mutex_lock(&sysfs_mutex);
error = -EEXIST;
- if (sysfs_find_dirent(sd->s_parent, new_name))
+ if (__sysfs_find_dirent(sd->s_parent, new_name, new_tag, 0))
goto out_unlock;
/* rename kobject and sysfs_dirent */
@@ -974,6 +1022,7 @@ int sysfs_rename_dir(struct kobject * ko
dup_name = sd->s_name;
sd->s_name = new_name;
+ sd->s_tag = new_tag;
/* rename */
list_for_each_entry(srs, &todo, list) {
@@ -981,6 +1030,21 @@ int sysfs_rename_dir(struct kobject * ko
d_move(srs->old_dentry, srs->new_dentry);
}
+ /* If we are moving across superblocks drop the dcache entries */
+ if (tag) {
+ struct super_block *super;
+ struct dentry *dentry;
+
+ list_for_each_entry(super, &sysfs_fs_type.fs_supers, s_instances) {
+ dentry = __sysfs_get_dentry(super, sd);
+ if (!dentry)
+ continue;
+ shrink_dcache_parent(dentry);
+ d_drop(dentry);
+ dput(dentry);
+ }
+ }
+
error = 0;
out_unlock:
mutex_unlock(&sysfs_mutex);
@@ -995,6 +1059,11 @@ out:
return error;
}
+int sysfs_rename_dir(struct kobject * kobj, const char *new_name)
+{
+ return sysfs_rename_dir_tagged(kobj, new_name, kobj->sd->s_tag);
+}
+
int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj)
{
struct sysfs_dirent *sd = kobj->sd;
@@ -1013,6 +1082,11 @@ int sysfs_move_dir(struct kobject *kobj,
if (sd->s_parent == new_parent_sd)
goto out; /* nothing to move */
+ /* can't move between parents belonging to different tags */
+ error = -EINVAL;
+ if (sysfs_dirent_tag(sd->s_parent) != sysfs_dirent_tag(new_parent_sd))
+ goto out;
+
sysfs_grab_supers();
error = prep_rename(&todo, sd, new_parent_sd, sd->s_name);
if (error)
@@ -1041,7 +1115,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, sd->s_name, sd->s_tag, 0))
goto out_unlock;
error = 0;
@@ -1080,8 +1154,9 @@ static inline unsigned char dt_type(stru
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;
+ const void *tag = sysfs_info(filp->f_dentry->d_sb)->tag;
struct sysfs_dirent *pos;
ino_t ino;
@@ -1110,6 +1185,9 @@ static int sysfs_readdir(struct file * f
const char * name;
int len;
+ if (pos->s_tag && pos->s_tag != tag)
+ continue;
+
name = pos->s_name;
len = strlen(name);
filp->f_pos = ino = pos->s_ino;
Index: work/fs/sysfs/sysfs.h
===================================================================
--- work.orig/fs/sysfs/sysfs.h
+++ work/fs/sysfs/sysfs.h
@@ -26,6 +26,7 @@ struct sysfs_dirent {
struct sysfs_dirent * s_sibling;
struct sysfs_dirent * s_children;
const char * s_name;
+ const void * s_tag;
union {
struct sysfs_elem_dir dir;
@@ -51,7 +52,8 @@ struct sysfs_addrm_cxt {
};
struct sysfs_super_info {
- int grabbed;
+ int grabbed;
+ const void *tag;
};
#define sysfs_info(SB) ((struct sysfs_super_info *)(SB)->s_fs_info)
@@ -63,6 +65,7 @@ extern struct file_system_type sysfs_fs_
void sysfs_grab_supers(void);
void sysfs_release_supers(void);
+extern const void *sysfs_dirent_tag(struct sysfs_dirent *sd);
extern struct dentry *sysfs_get_dentry(struct super_block *sb, struct sysfs_dirent *sd);
extern struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd);
extern void sysfs_put_active(struct sysfs_dirent *sd);
@@ -79,6 +
...