diff options
Diffstat (limited to 'fs/sysfs/inode.c')
-rw-r--r-- | fs/sysfs/inode.c | 82 |
1 files changed, 61 insertions, 21 deletions
diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 88857a399d0c..6ad47c13b94d 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c | |||
@@ -191,13 +191,25 @@ int sysfs_create(struct sysfs_dirent *sd, struct dentry *dentry, int mode, | |||
191 | return error; | 191 | return error; |
192 | } | 192 | } |
193 | 193 | ||
194 | /* | 194 | /** |
195 | * Unhashes the dentry corresponding to given sysfs_dirent | 195 | * sysfs_drop_dentry - drop dentry for the specified sysfs_dirent |
196 | * Called with parent inode's i_mutex held. | 196 | * @sd: target sysfs_dirent |
197 | * | ||
198 | * Drop dentry for @sd. @sd must have been unlinked from its | ||
199 | * parent on entry to this function such that it can't be looked | ||
200 | * up anymore. | ||
201 | * | ||
202 | * @sd->s_dentry which is protected with sysfs_lock points to the | ||
203 | * currently associated dentry but we're not holding a reference | ||
204 | * to it and racing with dput(). Grab dcache_lock and verify | ||
205 | * dentry before dropping it. If @sd->s_dentry is NULL or dput() | ||
206 | * beats us, no need to bother. | ||
197 | */ | 207 | */ |
198 | void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) | 208 | void sysfs_drop_dentry(struct sysfs_dirent *sd) |
199 | { | 209 | { |
200 | struct dentry *dentry = NULL; | 210 | struct dentry *dentry = NULL, *parent = NULL; |
211 | struct inode *dir; | ||
212 | struct timespec curtime; | ||
201 | 213 | ||
202 | /* We're not holding a reference to ->s_dentry dentry but the | 214 | /* We're not holding a reference to ->s_dentry dentry but the |
203 | * field will stay valid as long as sysfs_lock is held. | 215 | * field will stay valid as long as sysfs_lock is held. |
@@ -205,30 +217,57 @@ void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) | |||
205 | spin_lock(&sysfs_lock); | 217 | spin_lock(&sysfs_lock); |
206 | spin_lock(&dcache_lock); | 218 | spin_lock(&dcache_lock); |
207 | 219 | ||
208 | /* dget dentry if it's still alive */ | 220 | if (sd->s_dentry && sd->s_dentry->d_inode) { |
209 | if (sd->s_dentry && sd->s_dentry->d_inode) | 221 | /* get dentry if it's there and dput() didn't kill it yet */ |
210 | dentry = dget_locked(sd->s_dentry); | 222 | dentry = dget_locked(sd->s_dentry); |
223 | parent = dentry->d_parent; | ||
224 | } else if (sd->s_parent->s_dentry->d_inode) { | ||
225 | /* We need to update the parent even if dentry for the | ||
226 | * victim itself doesn't exist. | ||
227 | */ | ||
228 | parent = dget_locked(sd->s_parent->s_dentry); | ||
229 | } | ||
230 | |||
231 | /* drop */ | ||
232 | if (dentry) { | ||
233 | spin_lock(&dentry->d_lock); | ||
234 | __d_drop(dentry); | ||
235 | spin_unlock(&dentry->d_lock); | ||
236 | } | ||
211 | 237 | ||
212 | spin_unlock(&dcache_lock); | 238 | spin_unlock(&dcache_lock); |
213 | spin_unlock(&sysfs_lock); | 239 | spin_unlock(&sysfs_lock); |
214 | 240 | ||
215 | /* drop dentry */ | 241 | /* nothing to do if the parent isn't in dcache */ |
242 | if (!parent) | ||
243 | return; | ||
244 | |||
245 | /* adjust nlink and update timestamp */ | ||
246 | dir = parent->d_inode; | ||
247 | mutex_lock(&dir->i_mutex); | ||
248 | |||
249 | curtime = CURRENT_TIME; | ||
250 | |||
251 | dir->i_ctime = dir->i_mtime = curtime; | ||
252 | |||
216 | if (dentry) { | 253 | if (dentry) { |
217 | spin_lock(&dcache_lock); | 254 | dentry->d_inode->i_ctime = curtime; |
218 | spin_lock(&dentry->d_lock); | 255 | drop_nlink(dentry->d_inode); |
219 | if (!d_unhashed(dentry) && dentry->d_inode) { | 256 | if (sd->s_type & SYSFS_DIR) { |
220 | dget_locked(dentry); | 257 | drop_nlink(dentry->d_inode); |
221 | __d_drop(dentry); | 258 | drop_nlink(dir); |
222 | spin_unlock(&dentry->d_lock); | 259 | /* XXX: unpin if directory, this will go away soon */ |
223 | spin_unlock(&dcache_lock); | 260 | dput(dentry); |
224 | simple_unlink(parent->d_inode, dentry); | ||
225 | } else { | ||
226 | spin_unlock(&dentry->d_lock); | ||
227 | spin_unlock(&dcache_lock); | ||
228 | } | 261 | } |
262 | } | ||
263 | |||
264 | mutex_unlock(&dir->i_mutex); | ||
229 | 265 | ||
266 | /* bye bye */ | ||
267 | if (dentry) | ||
230 | dput(dentry); | 268 | dput(dentry); |
231 | } | 269 | else |
270 | dput(parent); | ||
232 | } | 271 | } |
233 | 272 | ||
234 | int sysfs_hash_and_remove(struct dentry * dir, const char * name) | 273 | int sysfs_hash_and_remove(struct dentry * dir, const char * name) |
@@ -251,7 +290,6 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) | |||
251 | continue; | 290 | continue; |
252 | if (!strcmp(sd->s_name, name)) { | 291 | if (!strcmp(sd->s_name, name)) { |
253 | list_del_init(&sd->s_sibling); | 292 | list_del_init(&sd->s_sibling); |
254 | sysfs_drop_dentry(sd, dir); | ||
255 | found = 1; | 293 | found = 1; |
256 | break; | 294 | break; |
257 | } | 295 | } |
@@ -261,7 +299,9 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) | |||
261 | if (!found) | 299 | if (!found) |
262 | return -ENOENT; | 300 | return -ENOENT; |
263 | 301 | ||
302 | sysfs_drop_dentry(sd); | ||
264 | sysfs_deactivate(sd); | 303 | sysfs_deactivate(sd); |
265 | sysfs_put(sd); | 304 | sysfs_put(sd); |
305 | |||
266 | return 0; | 306 | return 0; |
267 | } | 307 | } |