diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2018-04-15 18:21:47 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2018-04-15 23:36:57 -0400 |
commit | ff17fa561a04b5fdb25997fda98a2313bb471be6 (patch) | |
tree | 02d2fb1cbb1c67cd74268f9c8c256bb38c9053fa /fs/dcache.c | |
parent | 60cc43fc888428bb2f18f08997432d426a243338 (diff) |
d_invalidate(): unhash immediately
Once that is done, we can just hunt mountpoints down one by one;
no new mountpoints can be added from now on, so we don't need
anything tricky in finish() callback, etc.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/dcache.c')
-rw-r--r-- | fs/dcache.c | 62 |
1 files changed, 16 insertions, 46 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 86d2de63461e..449d0f895e6f 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -1542,78 +1542,48 @@ void shrink_dcache_for_umount(struct super_block *sb) | |||
1542 | } | 1542 | } |
1543 | } | 1543 | } |
1544 | 1544 | ||
1545 | struct detach_data { | 1545 | static enum d_walk_ret find_submount(void *_data, struct dentry *dentry) |
1546 | struct select_data select; | ||
1547 | struct dentry *mountpoint; | ||
1548 | }; | ||
1549 | static enum d_walk_ret detach_and_collect(void *_data, struct dentry *dentry) | ||
1550 | { | 1546 | { |
1551 | struct detach_data *data = _data; | 1547 | struct dentry **victim = _data; |
1552 | |||
1553 | if (d_mountpoint(dentry)) { | 1548 | if (d_mountpoint(dentry)) { |
1554 | __dget_dlock(dentry); | 1549 | __dget_dlock(dentry); |
1555 | data->mountpoint = dentry; | 1550 | *victim = dentry; |
1556 | return D_WALK_QUIT; | 1551 | return D_WALK_QUIT; |
1557 | } | 1552 | } |
1558 | 1553 | return D_WALK_CONTINUE; | |
1559 | return select_collect(&data->select, dentry); | ||
1560 | } | ||
1561 | |||
1562 | static void check_and_drop(void *_data) | ||
1563 | { | ||
1564 | struct detach_data *data = _data; | ||
1565 | |||
1566 | if (!data->mountpoint && list_empty(&data->select.dispose)) | ||
1567 | __d_drop(data->select.start); | ||
1568 | } | 1554 | } |
1569 | 1555 | ||
1570 | /** | 1556 | /** |
1571 | * d_invalidate - detach submounts, prune dcache, and drop | 1557 | * d_invalidate - detach submounts, prune dcache, and drop |
1572 | * @dentry: dentry to invalidate (aka detach, prune and drop) | 1558 | * @dentry: dentry to invalidate (aka detach, prune and drop) |
1573 | * | ||
1574 | * no dcache lock. | ||
1575 | * | ||
1576 | * The final d_drop is done as an atomic operation relative to | ||
1577 | * rename_lock ensuring there are no races with d_set_mounted. This | ||
1578 | * ensures there are no unhashed dentries on the path to a mountpoint. | ||
1579 | */ | 1559 | */ |
1580 | void d_invalidate(struct dentry *dentry) | 1560 | void d_invalidate(struct dentry *dentry) |
1581 | { | 1561 | { |
1582 | /* | 1562 | bool had_submounts = false; |
1583 | * If it's already been dropped, return OK. | ||
1584 | */ | ||
1585 | spin_lock(&dentry->d_lock); | 1563 | spin_lock(&dentry->d_lock); |
1586 | if (d_unhashed(dentry)) { | 1564 | if (d_unhashed(dentry)) { |
1587 | spin_unlock(&dentry->d_lock); | 1565 | spin_unlock(&dentry->d_lock); |
1588 | return; | 1566 | return; |
1589 | } | 1567 | } |
1568 | __d_drop(dentry); | ||
1590 | spin_unlock(&dentry->d_lock); | 1569 | spin_unlock(&dentry->d_lock); |
1591 | 1570 | ||
1592 | /* Negative dentries can be dropped without further checks */ | 1571 | /* Negative dentries can be dropped without further checks */ |
1593 | if (!dentry->d_inode) { | 1572 | if (!dentry->d_inode) |
1594 | d_drop(dentry); | ||
1595 | return; | 1573 | return; |
1596 | } | ||
1597 | 1574 | ||
1575 | shrink_dcache_parent(dentry); | ||
1598 | for (;;) { | 1576 | for (;;) { |
1599 | struct detach_data data; | 1577 | struct dentry *victim = NULL; |
1600 | 1578 | d_walk(dentry, &victim, find_submount, NULL); | |
1601 | data.mountpoint = NULL; | 1579 | if (!victim) { |
1602 | INIT_LIST_HEAD(&data.select.dispose); | 1580 | if (had_submounts) |
1603 | data.select.start = dentry; | 1581 | shrink_dcache_parent(dentry); |
1604 | data.select.found = 0; | ||
1605 | |||
1606 | d_walk(dentry, &data, detach_and_collect, check_and_drop); | ||
1607 | |||
1608 | if (!list_empty(&data.select.dispose)) | ||
1609 | shrink_dentry_list(&data.select.dispose); | ||
1610 | else if (!data.mountpoint) | ||
1611 | return; | 1582 | return; |
1612 | |||
1613 | if (data.mountpoint) { | ||
1614 | detach_mounts(data.mountpoint); | ||
1615 | dput(data.mountpoint); | ||
1616 | } | 1583 | } |
1584 | had_submounts = true; | ||
1585 | detach_mounts(victim); | ||
1586 | dput(victim); | ||
1617 | } | 1587 | } |
1618 | } | 1588 | } |
1619 | EXPORT_SYMBOL(d_invalidate); | 1589 | EXPORT_SYMBOL(d_invalidate); |