mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/
synced 2026-11-10 20:48:12 +00:00
ceph: keep the inode, not the name, when a dentry lease expires
ceph_d_delete() drops a dentry once its lease expires. Since ceph uses
inode_just_drop() for ->drop_inode, that last dput also evicts the
inode, and ceph_evict_inode() throws away a page cache that the caps
still vouch for. A buffered write that outlasts the dentry lease (30s
by default on the MDS side) is therefore flushed and closed, and the
reopen re-reads everything from the OSDs even though both the caps and
the pages were still valid.
Commit 52dd0f1b3f ("ceph: use generic_delete_inode() for
->drop_inode") removed the ->drop_inode hook because "positive dentry
and corresponding inode are always accompanied in MDS reply. So no
need to keep inode in the cache after dropping all its aliases". That
holds for the metadata, but it ties the lifetime of the page cache to
the lifetime of a name, and the two are validated by entirely different
things: a dentry is only as good as its lease, while cached pages are
good for as long as the caps that back them.
So bring the hook back and keep a regular file's inode while it still
has cached pages and still holds real caps. The name is unaffected: an
expired dentry is unhashed exactly as before, and the next lookup goes
to the MDS and finds the retained inode via ceph_get_inode() ->
iget5_locked(), which is keyed on the vino from the reply rather than
on any dentry. ceph_fill_inode() then refills it without touching the
mapping.
Retention is bounded by the page cache itself, and reclaim runs in that
order rather than the other way round. A retained inode holds no
reference of its own, but it does not sit on the inode LRU either:
mapping_shrinkable() is false while real folios are present, so the
inode shrinker never sees it. Page reclaim frees its pages like any
other file's; emptying the mapping is what makes it shrinkable and puts
it on the LRU, and only then can the inode shrinker evict it. Unmount
evicts it regardless, as evict_inodes() walks sb->s_inodes rather than
the LRU.
MDS cap trim needs one more thing. trim_caps_cb() releases an unused
cap indirectly, by pruning the inode's aliases and letting the eviction
that follows call __ceph_remove_caps(). Retaining the inode breaks
that chain in both of its cases: an inode whose aliases are already
gone was never reachable that way, and after d_prune_aliases() the
iput() that ceph_iterate_session_caps() owes the inode no longer evicts
it. Give that path an explicit override in
CEPH_I_EVICT_ON_FINAL_IPUT_BIT, which ceph_drop_inode() consumes with
test_and_clear_bit(). Being one-shot is the point: it lets the MDS
force this inode out now without permanently changing how the inode is
cached afterwards, which is why I_DONTCACHE is not used here.
Signed-off-by: Xiubo Li <xiubo.li@clyso.com>
Reviewed-by: Alex Markuze <amarkuze@redhat.com>
Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
This commit is contained in:
parent
32eacfae3b
commit
6ce5143eea
4 changed files with 93 additions and 7 deletions
Loading…
Add table
Add a link
Reference in a new issue