diff options
Diffstat (limited to 'fs/sysfs/dir.c')
-rw-r--r-- | fs/sysfs/dir.c | 56 |
1 files changed, 56 insertions, 0 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index edb30621b82f..c6f3b697064c 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
@@ -435,6 +435,62 @@ void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) | |||
435 | } | 435 | } |
436 | 436 | ||
437 | /** | 437 | /** |
438 | * sysfs_drop_dentry - drop dentry for the specified sysfs_dirent | ||
439 | * @sd: target sysfs_dirent | ||
440 | * | ||
441 | * Drop dentry for @sd. @sd must have been unlinked from its | ||
442 | * parent on entry to this function such that it can't be looked | ||
443 | * up anymore. | ||
444 | * | ||
445 | * @sd->s_dentry which is protected with sysfs_assoc_lock points | ||
446 | * to the currently associated dentry but we're not holding a | ||
447 | * reference to it and racing with dput(). Grab dcache_lock and | ||
448 | * verify dentry before dropping it. If @sd->s_dentry is NULL or | ||
449 | * dput() beats us, no need to bother. | ||
450 | */ | ||
451 | static void sysfs_drop_dentry(struct sysfs_dirent *sd) | ||
452 | { | ||
453 | struct dentry *dentry = NULL; | ||
454 | struct inode *inode; | ||
455 | |||
456 | /* We're not holding a reference to ->s_dentry dentry but the | ||
457 | * field will stay valid as long as sysfs_assoc_lock is held. | ||
458 | */ | ||
459 | spin_lock(&sysfs_assoc_lock); | ||
460 | spin_lock(&dcache_lock); | ||
461 | |||
462 | /* drop dentry if it's there and dput() didn't kill it yet */ | ||
463 | if (sd->s_dentry && sd->s_dentry->d_inode) { | ||
464 | dentry = dget_locked(sd->s_dentry); | ||
465 | spin_lock(&dentry->d_lock); | ||
466 | __d_drop(dentry); | ||
467 | spin_unlock(&dentry->d_lock); | ||
468 | } | ||
469 | |||
470 | spin_unlock(&dcache_lock); | ||
471 | spin_unlock(&sysfs_assoc_lock); | ||
472 | |||
473 | dput(dentry); | ||
474 | /* XXX: unpin if directory, this will go away soon */ | ||
475 | if (sysfs_type(sd) == SYSFS_DIR) | ||
476 | dput(dentry); | ||
477 | |||
478 | /* adjust nlink and update timestamp */ | ||
479 | inode = ilookup(sysfs_sb, sd->s_ino); | ||
480 | if (inode) { | ||
481 | mutex_lock(&inode->i_mutex); | ||
482 | |||
483 | inode->i_ctime = CURRENT_TIME; | ||
484 | drop_nlink(inode); | ||
485 | if (sysfs_type(sd) == SYSFS_DIR) | ||
486 | drop_nlink(inode); | ||
487 | |||
488 | mutex_unlock(&inode->i_mutex); | ||
489 | iput(inode); | ||
490 | } | ||
491 | } | ||
492 | |||
493 | /** | ||
438 | * sysfs_addrm_finish - finish up sysfs_dirent add/remove | 494 | * sysfs_addrm_finish - finish up sysfs_dirent add/remove |
439 | * @acxt: addrm context to finish up | 495 | * @acxt: addrm context to finish up |
440 | * | 496 | * |