diff options
Diffstat (limited to 'fs/sysfs')
-rw-r--r-- | fs/sysfs/dir.c | 53 |
1 files changed, 26 insertions, 27 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 953e8432b0ae..1af963e66e3c 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
@@ -565,50 +565,49 @@ void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) | |||
565 | * Drop dentry for @sd. @sd must have been unlinked from its | 565 | * Drop dentry for @sd. @sd must have been unlinked from its |
566 | * parent on entry to this function such that it can't be looked | 566 | * parent on entry to this function such that it can't be looked |
567 | * up anymore. | 567 | * up anymore. |
568 | * | ||
569 | * @sd->s_dentry which is protected with sysfs_assoc_lock points | ||
570 | * to the currently associated dentry but we're not holding a | ||
571 | * reference to it and racing with dput(). Grab dcache_lock and | ||
572 | * verify dentry before dropping it. If @sd->s_dentry is NULL or | ||
573 | * dput() beats us, no need to bother. | ||
574 | */ | 568 | */ |
575 | static void sysfs_drop_dentry(struct sysfs_dirent *sd) | 569 | static void sysfs_drop_dentry(struct sysfs_dirent *sd) |
576 | { | 570 | { |
577 | struct dentry *dentry = NULL; | ||
578 | struct inode *inode; | 571 | struct inode *inode; |
572 | struct dentry *dentry; | ||
573 | |||
574 | inode = ilookup(sysfs_sb, sd->s_ino); | ||
575 | if (!inode) | ||
576 | return; | ||
579 | 577 | ||
580 | /* We're not holding a reference to ->s_dentry dentry but the | 578 | /* Drop any existing dentries associated with sd. |
581 | * field will stay valid as long as sysfs_assoc_lock is held. | 579 | * |
580 | * For the dentry to be properly freed we need to grab a | ||
581 | * reference to the dentry under the dcache lock, unhash it, | ||
582 | * and then put it. The playing with the dentry count allows | ||
583 | * dput to immediately free the dentry if it is not in use. | ||
582 | */ | 584 | */ |
583 | spin_lock(&sysfs_assoc_lock); | 585 | repeat: |
584 | spin_lock(&dcache_lock); | 586 | spin_lock(&dcache_lock); |
585 | 587 | list_for_each_entry(dentry, &inode->i_dentry, d_alias) { | |
586 | /* drop dentry if it's there and dput() didn't kill it yet */ | 588 | if (d_unhashed(dentry)) |
587 | if (sd->s_dentry && sd->s_dentry->d_inode) { | 589 | continue; |
588 | dentry = dget_locked(sd->s_dentry); | 590 | dget_locked(dentry); |
589 | spin_lock(&dentry->d_lock); | 591 | spin_lock(&dentry->d_lock); |
590 | __d_drop(dentry); | 592 | __d_drop(dentry); |
591 | spin_unlock(&dentry->d_lock); | 593 | spin_unlock(&dentry->d_lock); |
594 | spin_unlock(&dcache_lock); | ||
595 | dput(dentry); | ||
596 | goto repeat; | ||
592 | } | 597 | } |
593 | |||
594 | spin_unlock(&dcache_lock); | 598 | spin_unlock(&dcache_lock); |
595 | spin_unlock(&sysfs_assoc_lock); | ||
596 | |||
597 | dput(dentry); | ||
598 | 599 | ||
599 | /* adjust nlink and update timestamp */ | 600 | /* adjust nlink and update timestamp */ |
600 | inode = ilookup(sysfs_sb, sd->s_ino); | 601 | mutex_lock(&inode->i_mutex); |
601 | if (inode) { | ||
602 | mutex_lock(&inode->i_mutex); | ||
603 | 602 | ||
604 | inode->i_ctime = CURRENT_TIME; | 603 | inode->i_ctime = CURRENT_TIME; |
604 | drop_nlink(inode); | ||
605 | if (sysfs_type(sd) == SYSFS_DIR) | ||
605 | drop_nlink(inode); | 606 | drop_nlink(inode); |
606 | if (sysfs_type(sd) == SYSFS_DIR) | ||
607 | drop_nlink(inode); | ||
608 | 607 | ||
609 | mutex_unlock(&inode->i_mutex); | 608 | mutex_unlock(&inode->i_mutex); |
610 | iput(inode); | 609 | |
611 | } | 610 | iput(inode); |
612 | } | 611 | } |
613 | 612 | ||
614 | /** | 613 | /** |