mm: Protect operations adding pages to page cache with invalidate_lock
authorJan Kara <jack@suse.cz>
Thu, 28 Jan 2021 18:19:45 +0000 (19:19 +0100)
committerJan Kara <jack@suse.cz>
Tue, 13 Jul 2021 11:14:27 +0000 (13:14 +0200)
commit730633f0b7f951726e87f912a6323641f674ae34
tree1c4a6eb5ddbc0c28e6d37a1418ec259cb6daef27
parentc625b4cc57d078b03fd8aa4d86c99d584a1782be
mm: Protect operations adding pages to page cache with invalidate_lock

Currently, serializing operations such as page fault, read, or readahead
against hole punching is rather difficult. The basic race scheme is
like:

fallocate(FALLOC_FL_PUNCH_HOLE) read / fault / ..
  truncate_inode_pages_range()
  <create pages in page
   cache here>
  <update fs block mapping and free blocks>

Now the problem is in this way read / page fault / readahead can
instantiate pages in page cache with potentially stale data (if blocks
get quickly reused). Avoiding this race is not simple - page locks do
not work because we want to make sure there are *no* pages in given
range. inode->i_rwsem does not work because page fault happens under
mmap_sem which ranks below inode->i_rwsem. Also using it for reads makes
the performance for mixed read-write workloads suffer.

So create a new rw_semaphore in the address_space - invalidate_lock -
that protects adding of pages to page cache for page faults / reads /
readahead.

Reviewed-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Jan Kara <jack@suse.cz>
Documentation/filesystems/locking.rst
fs/inode.c
include/linux/fs.h
mm/filemap.c
mm/readahead.c
mm/rmap.c
mm/truncate.c