diff options
Diffstat (limited to 'fs/dcache.c')
-rw-r--r-- | fs/dcache.c | 156 |
1 files changed, 113 insertions, 43 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 2bac4ba1d1d3..fd4a428998ef 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -478,11 +478,12 @@ static void prune_dcache(int count, struct super_block *sb) | |||
478 | up_read(s_umount); | 478 | up_read(s_umount); |
479 | } | 479 | } |
480 | spin_unlock(&dentry->d_lock); | 480 | spin_unlock(&dentry->d_lock); |
481 | /* Cannot remove the first dentry, and it isn't appropriate | 481 | /* |
482 | * to move it to the head of the list, so give up, and try | 482 | * Insert dentry at the head of the list as inserting at the |
483 | * later | 483 | * tail leads to a cycle. |
484 | */ | 484 | */ |
485 | break; | 485 | list_add(&dentry->d_lru, &dentry_unused); |
486 | dentry_stat.nr_unused++; | ||
486 | } | 487 | } |
487 | spin_unlock(&dcache_lock); | 488 | spin_unlock(&dcache_lock); |
488 | } | 489 | } |
@@ -556,6 +557,7 @@ repeat: | |||
556 | static void shrink_dcache_for_umount_subtree(struct dentry *dentry) | 557 | static void shrink_dcache_for_umount_subtree(struct dentry *dentry) |
557 | { | 558 | { |
558 | struct dentry *parent; | 559 | struct dentry *parent; |
560 | unsigned detached = 0; | ||
559 | 561 | ||
560 | BUG_ON(!IS_ROOT(dentry)); | 562 | BUG_ON(!IS_ROOT(dentry)); |
561 | 563 | ||
@@ -620,7 +622,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry) | |||
620 | atomic_dec(&parent->d_count); | 622 | atomic_dec(&parent->d_count); |
621 | 623 | ||
622 | list_del(&dentry->d_u.d_child); | 624 | list_del(&dentry->d_u.d_child); |
623 | dentry_stat.nr_dentry--; /* For d_free, below */ | 625 | detached++; |
624 | 626 | ||
625 | inode = dentry->d_inode; | 627 | inode = dentry->d_inode; |
626 | if (inode) { | 628 | if (inode) { |
@@ -638,7 +640,7 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry) | |||
638 | * otherwise we ascend to the parent and move to the | 640 | * otherwise we ascend to the parent and move to the |
639 | * next sibling if there is one */ | 641 | * next sibling if there is one */ |
640 | if (!parent) | 642 | if (!parent) |
641 | return; | 643 | goto out; |
642 | 644 | ||
643 | dentry = parent; | 645 | dentry = parent; |
644 | 646 | ||
@@ -647,6 +649,11 @@ static void shrink_dcache_for_umount_subtree(struct dentry *dentry) | |||
647 | dentry = list_entry(dentry->d_subdirs.next, | 649 | dentry = list_entry(dentry->d_subdirs.next, |
648 | struct dentry, d_u.d_child); | 650 | struct dentry, d_u.d_child); |
649 | } | 651 | } |
652 | out: | ||
653 | /* several dentries were freed, need to correct nr_dentry */ | ||
654 | spin_lock(&dcache_lock); | ||
655 | dentry_stat.nr_dentry -= detached; | ||
656 | spin_unlock(&dcache_lock); | ||
650 | } | 657 | } |
651 | 658 | ||
652 | /* | 659 | /* |
@@ -1469,23 +1476,21 @@ static void switch_names(struct dentry *dentry, struct dentry *target) | |||
1469 | * deleted it. | 1476 | * deleted it. |
1470 | */ | 1477 | */ |
1471 | 1478 | ||
1472 | /** | 1479 | /* |
1473 | * d_move - move a dentry | 1480 | * d_move_locked - move a dentry |
1474 | * @dentry: entry to move | 1481 | * @dentry: entry to move |
1475 | * @target: new dentry | 1482 | * @target: new dentry |
1476 | * | 1483 | * |
1477 | * Update the dcache to reflect the move of a file name. Negative | 1484 | * Update the dcache to reflect the move of a file name. Negative |
1478 | * dcache entries should not be moved in this way. | 1485 | * dcache entries should not be moved in this way. |
1479 | */ | 1486 | */ |
1480 | 1487 | static void d_move_locked(struct dentry * dentry, struct dentry * target) | |
1481 | void d_move(struct dentry * dentry, struct dentry * target) | ||
1482 | { | 1488 | { |
1483 | struct hlist_head *list; | 1489 | struct hlist_head *list; |
1484 | 1490 | ||
1485 | if (!dentry->d_inode) | 1491 | if (!dentry->d_inode) |
1486 | printk(KERN_WARNING "VFS: moving negative dcache entry\n"); | 1492 | printk(KERN_WARNING "VFS: moving negative dcache entry\n"); |
1487 | 1493 | ||
1488 | spin_lock(&dcache_lock); | ||
1489 | write_seqlock(&rename_lock); | 1494 | write_seqlock(&rename_lock); |
1490 | /* | 1495 | /* |
1491 | * XXXX: do we really need to take target->d_lock? | 1496 | * XXXX: do we really need to take target->d_lock? |
@@ -1536,7 +1541,81 @@ already_unhashed: | |||
1536 | fsnotify_d_move(dentry); | 1541 | fsnotify_d_move(dentry); |
1537 | spin_unlock(&dentry->d_lock); | 1542 | spin_unlock(&dentry->d_lock); |
1538 | write_sequnlock(&rename_lock); | 1543 | write_sequnlock(&rename_lock); |
1544 | } | ||
1545 | |||
1546 | /** | ||
1547 | * d_move - move a dentry | ||
1548 | * @dentry: entry to move | ||
1549 | * @target: new dentry | ||
1550 | * | ||
1551 | * Update the dcache to reflect the move of a file name. Negative | ||
1552 | * dcache entries should not be moved in this way. | ||
1553 | */ | ||
1554 | |||
1555 | void d_move(struct dentry * dentry, struct dentry * target) | ||
1556 | { | ||
1557 | spin_lock(&dcache_lock); | ||
1558 | d_move_locked(dentry, target); | ||
1559 | spin_unlock(&dcache_lock); | ||
1560 | } | ||
1561 | |||
1562 | /* | ||
1563 | * Helper that returns 1 if p1 is a parent of p2, else 0 | ||
1564 | */ | ||
1565 | static int d_isparent(struct dentry *p1, struct dentry *p2) | ||
1566 | { | ||
1567 | struct dentry *p; | ||
1568 | |||
1569 | for (p = p2; p->d_parent != p; p = p->d_parent) { | ||
1570 | if (p->d_parent == p1) | ||
1571 | return 1; | ||
1572 | } | ||
1573 | return 0; | ||
1574 | } | ||
1575 | |||
1576 | /* | ||
1577 | * This helper attempts to cope with remotely renamed directories | ||
1578 | * | ||
1579 | * It assumes that the caller is already holding | ||
1580 | * dentry->d_parent->d_inode->i_mutex and the dcache_lock | ||
1581 | * | ||
1582 | * Note: If ever the locking in lock_rename() changes, then please | ||
1583 | * remember to update this too... | ||
1584 | * | ||
1585 | * On return, dcache_lock will have been unlocked. | ||
1586 | */ | ||
1587 | static struct dentry *__d_unalias(struct dentry *dentry, struct dentry *alias) | ||
1588 | { | ||
1589 | struct mutex *m1 = NULL, *m2 = NULL; | ||
1590 | struct dentry *ret; | ||
1591 | |||
1592 | /* If alias and dentry share a parent, then no extra locks required */ | ||
1593 | if (alias->d_parent == dentry->d_parent) | ||
1594 | goto out_unalias; | ||
1595 | |||
1596 | /* Check for loops */ | ||
1597 | ret = ERR_PTR(-ELOOP); | ||
1598 | if (d_isparent(alias, dentry)) | ||
1599 | goto out_err; | ||
1600 | |||
1601 | /* See lock_rename() */ | ||
1602 | ret = ERR_PTR(-EBUSY); | ||
1603 | if (!mutex_trylock(&dentry->d_sb->s_vfs_rename_mutex)) | ||
1604 | goto out_err; | ||
1605 | m1 = &dentry->d_sb->s_vfs_rename_mutex; | ||
1606 | if (!mutex_trylock(&alias->d_parent->d_inode->i_mutex)) | ||
1607 | goto out_err; | ||
1608 | m2 = &alias->d_parent->d_inode->i_mutex; | ||
1609 | out_unalias: | ||
1610 | d_move_locked(alias, dentry); | ||
1611 | ret = alias; | ||
1612 | out_err: | ||
1539 | spin_unlock(&dcache_lock); | 1613 | spin_unlock(&dcache_lock); |
1614 | if (m2) | ||
1615 | mutex_unlock(m2); | ||
1616 | if (m1) | ||
1617 | mutex_unlock(m1); | ||
1618 | return ret; | ||
1540 | } | 1619 | } |
1541 | 1620 | ||
1542 | /* | 1621 | /* |
@@ -1581,7 +1660,7 @@ static void __d_materialise_dentry(struct dentry *dentry, struct dentry *anon) | |||
1581 | */ | 1660 | */ |
1582 | struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode) | 1661 | struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode) |
1583 | { | 1662 | { |
1584 | struct dentry *alias, *actual; | 1663 | struct dentry *actual; |
1585 | 1664 | ||
1586 | BUG_ON(!d_unhashed(dentry)); | 1665 | BUG_ON(!d_unhashed(dentry)); |
1587 | 1666 | ||
@@ -1593,26 +1672,27 @@ struct dentry *d_materialise_unique(struct dentry *dentry, struct inode *inode) | |||
1593 | goto found_lock; | 1672 | goto found_lock; |
1594 | } | 1673 | } |
1595 | 1674 | ||
1596 | /* See if a disconnected directory already exists as an anonymous root | 1675 | if (S_ISDIR(inode->i_mode)) { |
1597 | * that we should splice into the tree instead */ | 1676 | struct dentry *alias; |
1598 | if (S_ISDIR(inode->i_mode) && (alias = __d_find_alias(inode, 1))) { | 1677 | |
1599 | spin_lock(&alias->d_lock); | 1678 | /* Does an aliased dentry already exist? */ |
1600 | 1679 | alias = __d_find_alias(inode, 0); | |
1601 | /* Is this a mountpoint that we could splice into our tree? */ | 1680 | if (alias) { |
1602 | if (IS_ROOT(alias)) | 1681 | actual = alias; |
1603 | goto connect_mountpoint; | 1682 | /* Is this an anonymous mountpoint that we could splice |
1604 | 1683 | * into our tree? */ | |
1605 | if (alias->d_name.len == dentry->d_name.len && | 1684 | if (IS_ROOT(alias)) { |
1606 | alias->d_parent == dentry->d_parent && | 1685 | spin_lock(&alias->d_lock); |
1607 | memcmp(alias->d_name.name, | 1686 | __d_materialise_dentry(dentry, alias); |
1608 | dentry->d_name.name, | 1687 | __d_drop(alias); |
1609 | dentry->d_name.len) == 0) | 1688 | goto found; |
1610 | goto replace_with_alias; | 1689 | } |
1611 | 1690 | /* Nope, but we must(!) avoid directory aliasing */ | |
1612 | spin_unlock(&alias->d_lock); | 1691 | actual = __d_unalias(dentry, alias); |
1613 | 1692 | if (IS_ERR(actual)) | |
1614 | /* Doh! Seem to be aliasing directories for some reason... */ | 1693 | dput(alias); |
1615 | dput(alias); | 1694 | goto out_nolock; |
1695 | } | ||
1616 | } | 1696 | } |
1617 | 1697 | ||
1618 | /* Add a unique reference */ | 1698 | /* Add a unique reference */ |
@@ -1628,7 +1708,7 @@ found: | |||
1628 | _d_rehash(actual); | 1708 | _d_rehash(actual); |
1629 | spin_unlock(&actual->d_lock); | 1709 | spin_unlock(&actual->d_lock); |
1630 | spin_unlock(&dcache_lock); | 1710 | spin_unlock(&dcache_lock); |
1631 | 1711 | out_nolock: | |
1632 | if (actual == dentry) { | 1712 | if (actual == dentry) { |
1633 | security_d_instantiate(dentry, inode); | 1713 | security_d_instantiate(dentry, inode); |
1634 | return NULL; | 1714 | return NULL; |
@@ -1637,16 +1717,6 @@ found: | |||
1637 | iput(inode); | 1717 | iput(inode); |
1638 | return actual; | 1718 | return actual; |
1639 | 1719 | ||
1640 | /* Convert the anonymous/root alias into an ordinary dentry */ | ||
1641 | connect_mountpoint: | ||
1642 | __d_materialise_dentry(dentry, alias); | ||
1643 | |||
1644 | /* Replace the candidate dentry with the alias in the tree */ | ||
1645 | replace_with_alias: | ||
1646 | __d_drop(alias); | ||
1647 | actual = alias; | ||
1648 | goto found; | ||
1649 | |||
1650 | shouldnt_be_hashed: | 1720 | shouldnt_be_hashed: |
1651 | spin_unlock(&dcache_lock); | 1721 | spin_unlock(&dcache_lock); |
1652 | BUG(); | 1722 | BUG(); |