summaryrefslogtreecommitdiffstats
path: root/fs/dcache.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2018-04-15 18:21:47 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2018-04-15 23:36:57 -0400
commitff17fa561a04b5fdb25997fda98a2313bb471be6 (patch)
tree02d2fb1cbb1c67cd74268f9c8c256bb38c9053fa /fs/dcache.c
parent60cc43fc888428bb2f18f08997432d426a243338 (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.c62
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
1545struct detach_data { 1545static enum d_walk_ret find_submount(void *_data, struct dentry *dentry)
1546 struct select_data select;
1547 struct dentry *mountpoint;
1548};
1549static 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
1562static 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 */
1580void d_invalidate(struct dentry *dentry) 1560void 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}
1619EXPORT_SYMBOL(d_invalidate); 1589EXPORT_SYMBOL(d_invalidate);