aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/sysfs/dir.c98
-rw-r--r--fs/sysfs/sysfs.h1
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 */
80struct 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 {
54extern struct vfsmount * sysfs_mount; 54extern struct vfsmount * sysfs_mount;
55extern struct kmem_cache *sysfs_dir_cachep; 55extern struct kmem_cache *sysfs_dir_cachep;
56 56
57extern struct dentry *sysfs_get_dentry(struct sysfs_dirent *sd);
57extern void sysfs_link_sibling(struct sysfs_dirent *sd); 58extern void sysfs_link_sibling(struct sysfs_dirent *sd);
58extern void sysfs_unlink_sibling(struct sysfs_dirent *sd); 59extern void sysfs_unlink_sibling(struct sysfs_dirent *sd);
59extern struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd); 60extern struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd);