diff options
-rw-r--r-- | fs/sysfs/dir.c | 98 | ||||
-rw-r--r-- | fs/sysfs/sysfs.h | 1 |
2 files changed, 99 insertions, 0 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index c6f3b697064c..987211296106 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
@@ -63,6 +63,104 @@ void sysfs_unlink_sibling(struct sysfs_dirent *sd) | |||
63 | } | 63 | } |
64 | 64 | ||
65 | /** | 65 | /** |
66 | * sysfs_get_dentry - get dentry for the given sysfs_dirent | ||
67 | * @sd: sysfs_dirent of interest | ||
68 | * | ||
69 | * Get dentry for @sd. Dentry is looked up if currently not | ||
70 | * present. This function climbs sysfs_dirent tree till it | ||
71 | * reaches a sysfs_dirent with valid dentry attached and descends | ||
72 | * down from there looking up dentry for each step. | ||
73 | * | ||
74 | * LOCKING: | ||
75 | * Kernel thread context (may sleep) | ||
76 | * | ||
77 | * RETURNS: | ||
78 | * Pointer to found dentry on success, ERR_PTR() value on error. | ||
79 | */ | ||
80 | struct dentry *sysfs_get_dentry(struct sysfs_dirent *sd) | ||
81 | { | ||
82 | struct sysfs_dirent *cur; | ||
83 | struct dentry *parent_dentry, *dentry; | ||
84 | int i, depth; | ||
85 | |||
86 | /* Find the first parent which has valid s_dentry and get the | ||
87 | * dentry. | ||
88 | */ | ||
89 | mutex_lock(&sysfs_mutex); | ||
90 | restart0: | ||
91 | spin_lock(&sysfs_assoc_lock); | ||
92 | restart1: | ||
93 | spin_lock(&dcache_lock); | ||
94 | |||
95 | dentry = NULL; | ||
96 | depth = 0; | ||
97 | cur = sd; | ||
98 | while (!cur->s_dentry || !cur->s_dentry->d_inode) { | ||
99 | if (cur->s_flags & SYSFS_FLAG_REMOVED) { | ||
100 | dentry = ERR_PTR(-ENOENT); | ||
101 | depth = 0; | ||
102 | break; | ||
103 | } | ||
104 | cur = cur->s_parent; | ||
105 | depth++; | ||
106 | } | ||
107 | if (!IS_ERR(dentry)) | ||
108 | dentry = dget_locked(cur->s_dentry); | ||
109 | |||
110 | spin_unlock(&dcache_lock); | ||
111 | spin_unlock(&sysfs_assoc_lock); | ||
112 | |||
113 | /* from the found dentry, look up depth times */ | ||
114 | while (depth--) { | ||
115 | /* find and get depth'th ancestor */ | ||
116 | for (cur = sd, i = 0; cur && i < depth; i++) | ||
117 | cur = cur->s_parent; | ||
118 | |||
119 | /* This can happen if tree structure was modified due | ||
120 | * to move/rename. Restart. | ||
121 | */ | ||
122 | if (i != depth) { | ||
123 | dput(dentry); | ||
124 | goto restart0; | ||
125 | } | ||
126 | |||
127 | sysfs_get(cur); | ||
128 | |||
129 | mutex_unlock(&sysfs_mutex); | ||
130 | |||
131 | /* look it up */ | ||
132 | parent_dentry = dentry; | ||
133 | dentry = lookup_one_len_kern(cur->s_name, parent_dentry, | ||
134 | strlen(cur->s_name)); | ||
135 | dput(parent_dentry); | ||
136 | |||
137 | if (IS_ERR(dentry)) { | ||
138 | sysfs_put(cur); | ||
139 | return dentry; | ||
140 | } | ||
141 | |||
142 | mutex_lock(&sysfs_mutex); | ||
143 | spin_lock(&sysfs_assoc_lock); | ||
144 | |||
145 | /* This, again, can happen if tree structure has | ||
146 | * changed and we looked up the wrong thing. Restart. | ||
147 | */ | ||
148 | if (cur->s_dentry != dentry) { | ||
149 | dput(dentry); | ||
150 | sysfs_put(cur); | ||
151 | goto restart1; | ||
152 | } | ||
153 | |||
154 | spin_unlock(&sysfs_assoc_lock); | ||
155 | |||
156 | sysfs_put(cur); | ||
157 | } | ||
158 | |||
159 | mutex_unlock(&sysfs_mutex); | ||
160 | return dentry; | ||
161 | } | ||
162 | |||
163 | /** | ||
66 | * sysfs_get_active - get an active reference to sysfs_dirent | 164 | * sysfs_get_active - get an active reference to sysfs_dirent |
67 | * @sd: sysfs_dirent to get an active reference to | 165 | * @sd: sysfs_dirent to get an active reference to |
68 | * | 166 | * |
diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 92fe1e51a29b..72530dc666fd 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h | |||
@@ -54,6 +54,7 @@ struct sysfs_addrm_cxt { | |||
54 | extern struct vfsmount * sysfs_mount; | 54 | extern struct vfsmount * sysfs_mount; |
55 | extern struct kmem_cache *sysfs_dir_cachep; | 55 | extern struct kmem_cache *sysfs_dir_cachep; |
56 | 56 | ||
57 | extern struct dentry *sysfs_get_dentry(struct sysfs_dirent *sd); | ||
57 | extern void sysfs_link_sibling(struct sysfs_dirent *sd); | 58 | extern void sysfs_link_sibling(struct sysfs_dirent *sd); |
58 | extern void sysfs_unlink_sibling(struct sysfs_dirent *sd); | 59 | extern void sysfs_unlink_sibling(struct sysfs_dirent *sd); |
59 | extern struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd); | 60 | extern struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd); |