diff options
Diffstat (limited to 'fs/sysfs/dir.c')
-rw-r--r-- | fs/sysfs/dir.c | 73 |
1 files changed, 46 insertions, 27 deletions
diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 01a9a64ecd4e..9ee130be03be 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c | |||
@@ -298,6 +298,46 @@ void release_sysfs_dirent(struct sysfs_dirent * sd) | |||
298 | goto repeat; | 298 | goto repeat; |
299 | } | 299 | } |
300 | 300 | ||
301 | static int sysfs_dentry_delete(struct dentry *dentry) | ||
302 | { | ||
303 | struct sysfs_dirent *sd = dentry->d_fsdata; | ||
304 | return !!(sd->s_flags & SYSFS_FLAG_REMOVED); | ||
305 | } | ||
306 | |||
307 | static int sysfs_dentry_revalidate(struct dentry *dentry, struct nameidata *nd) | ||
308 | { | ||
309 | struct sysfs_dirent *sd = dentry->d_fsdata; | ||
310 | int is_dir; | ||
311 | |||
312 | mutex_lock(&sysfs_mutex); | ||
313 | |||
314 | /* The sysfs dirent has been deleted */ | ||
315 | if (sd->s_flags & SYSFS_FLAG_REMOVED) | ||
316 | goto out_bad; | ||
317 | |||
318 | mutex_unlock(&sysfs_mutex); | ||
319 | out_valid: | ||
320 | return 1; | ||
321 | out_bad: | ||
322 | /* Remove the dentry from the dcache hashes. | ||
323 | * If this is a deleted dentry we use d_drop instead of d_delete | ||
324 | * so sysfs doesn't need to cope with negative dentries. | ||
325 | */ | ||
326 | is_dir = (sysfs_type(sd) == SYSFS_DIR); | ||
327 | mutex_unlock(&sysfs_mutex); | ||
328 | if (is_dir) { | ||
329 | /* If we have submounts we must allow the vfs caches | ||
330 | * to lie about the state of the filesystem to prevent | ||
331 | * leaks and other nasty things. | ||
332 | */ | ||
333 | if (have_submounts(dentry)) | ||
334 | goto out_valid; | ||
335 | shrink_dcache_parent(dentry); | ||
336 | } | ||
337 | d_drop(dentry); | ||
338 | return 0; | ||
339 | } | ||
340 | |||
301 | static void sysfs_dentry_iput(struct dentry *dentry, struct inode *inode) | 341 | static void sysfs_dentry_iput(struct dentry *dentry, struct inode *inode) |
302 | { | 342 | { |
303 | struct sysfs_dirent * sd = dentry->d_fsdata; | 343 | struct sysfs_dirent * sd = dentry->d_fsdata; |
@@ -307,6 +347,8 @@ static void sysfs_dentry_iput(struct dentry *dentry, struct inode *inode) | |||
307 | } | 347 | } |
308 | 348 | ||
309 | static const struct dentry_operations sysfs_dentry_ops = { | 349 | static const struct dentry_operations sysfs_dentry_ops = { |
350 | .d_revalidate = sysfs_dentry_revalidate, | ||
351 | .d_delete = sysfs_dentry_delete, | ||
310 | .d_iput = sysfs_dentry_iput, | 352 | .d_iput = sysfs_dentry_iput, |
311 | }; | 353 | }; |
312 | 354 | ||
@@ -527,44 +569,21 @@ void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) | |||
527 | } | 569 | } |
528 | 570 | ||
529 | /** | 571 | /** |
530 | * sysfs_drop_dentry - drop dentry for the specified sysfs_dirent | 572 | * sysfs_dec_nlink - Decrement link count for the specified sysfs_dirent |
531 | * @sd: target sysfs_dirent | 573 | * @sd: target sysfs_dirent |
532 | * | 574 | * |
533 | * Drop dentry for @sd. @sd must have been unlinked from its | 575 | * Decrement nlink for @sd. @sd must have been unlinked from its |
534 | * parent on entry to this function such that it can't be looked | 576 | * parent on entry to this function such that it can't be looked |
535 | * up anymore. | 577 | * up anymore. |
536 | */ | 578 | */ |
537 | static void sysfs_drop_dentry(struct sysfs_dirent *sd) | 579 | static void sysfs_dec_nlink(struct sysfs_dirent *sd) |
538 | { | 580 | { |
539 | struct inode *inode; | 581 | struct inode *inode; |
540 | struct dentry *dentry; | ||
541 | 582 | ||
542 | inode = ilookup(sysfs_sb, sd->s_ino); | 583 | inode = ilookup(sysfs_sb, sd->s_ino); |
543 | if (!inode) | 584 | if (!inode) |
544 | return; | 585 | return; |
545 | 586 | ||
546 | /* Drop any existing dentries associated with sd. | ||
547 | * | ||
548 | * For the dentry to be properly freed we need to grab a | ||
549 | * reference to the dentry under the dcache lock, unhash it, | ||
550 | * and then put it. The playing with the dentry count allows | ||
551 | * dput to immediately free the dentry if it is not in use. | ||
552 | */ | ||
553 | repeat: | ||
554 | spin_lock(&dcache_lock); | ||
555 | list_for_each_entry(dentry, &inode->i_dentry, d_alias) { | ||
556 | if (d_unhashed(dentry)) | ||
557 | continue; | ||
558 | dget_locked(dentry); | ||
559 | spin_lock(&dentry->d_lock); | ||
560 | __d_drop(dentry); | ||
561 | spin_unlock(&dentry->d_lock); | ||
562 | spin_unlock(&dcache_lock); | ||
563 | dput(dentry); | ||
564 | goto repeat; | ||
565 | } | ||
566 | spin_unlock(&dcache_lock); | ||
567 | |||
568 | /* adjust nlink and update timestamp */ | 587 | /* adjust nlink and update timestamp */ |
569 | mutex_lock(&inode->i_mutex); | 588 | mutex_lock(&inode->i_mutex); |
570 | 589 | ||
@@ -611,7 +630,7 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) | |||
611 | acxt->removed = sd->s_sibling; | 630 | acxt->removed = sd->s_sibling; |
612 | sd->s_sibling = NULL; | 631 | sd->s_sibling = NULL; |
613 | 632 | ||
614 | sysfs_drop_dentry(sd); | 633 | sysfs_dec_nlink(sd); |
615 | sysfs_deactivate(sd); | 634 | sysfs_deactivate(sd); |
616 | unmap_bin_file(sd); | 635 | unmap_bin_file(sd); |
617 | sysfs_put(sd); | 636 | sysfs_put(sd); |