Skip to content

Commit 09d91cd

Browse files
liu-song-6torvalds
authored andcommitted
mm,thp: avoid writes to file with THP in pagecache
In previous patch, an application could put part of its text section in THP via madvise(). These THPs will be protected from writes when the application is still running (TXTBSY). However, after the application exits, the file is available for writes. This patch avoids writes to file THP by dropping page cache for the file when the file is open for write. A new counter nr_thps is added to struct address_space. In do_dentry_open(), if the file is open for write and nr_thps is non-zero, we drop page cache for the whole file. Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Song Liu <[email protected]> Reported-by: kbuild test robot <[email protected]> Acked-by: Rik van Riel <[email protected]> Acked-by: Kirill A. Shutemov <[email protected]> Acked-by: Johannes Weiner <[email protected]> Cc: Hillf Danton <[email protected]> Cc: Hugh Dickins <[email protected]> Cc: William Kucharski <[email protected]> Cc: Oleg Nesterov <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 99cb0db commit 09d91cd

File tree

5 files changed

+47
-1
lines changed

5 files changed

+47
-1
lines changed

fs/inode.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,9 @@ int inode_init_always(struct super_block *sb, struct inode *inode)
181181
mapping->flags = 0;
182182
mapping->wb_err = 0;
183183
atomic_set(&mapping->i_mmap_writable, 0);
184+
#ifdef CONFIG_READ_ONLY_THP_FOR_FS
185+
atomic_set(&mapping->nr_thps, 0);
186+
#endif
184187
mapping_set_gfp_mask(mapping, GFP_HIGHUSER_MOVABLE);
185188
mapping->private_data = NULL;
186189
mapping->writeback_index = 0;

fs/open.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,14 @@ static int do_dentry_open(struct file *f,
818818
if (!f->f_mapping->a_ops || !f->f_mapping->a_ops->direct_IO)
819819
return -EINVAL;
820820
}
821+
822+
/*
823+
* XXX: Huge page cache doesn't support writing yet. Drop all page
824+
* cache for this file before processing writes.
825+
*/
826+
if ((f->f_mode & FMODE_WRITE) && filemap_nr_thps(inode->i_mapping))
827+
truncate_pagecache(inode, 0);
828+
821829
return 0;
822830

823831
cleanup_all:

include/linux/fs.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ int pagecache_write_end(struct file *, struct address_space *mapping,
429429
* @i_pages: Cached pages.
430430
* @gfp_mask: Memory allocation flags to use for allocating pages.
431431
* @i_mmap_writable: Number of VM_SHARED mappings.
432+
* @nr_thps: Number of THPs in the pagecache (non-shmem only).
432433
* @i_mmap: Tree of private and shared mappings.
433434
* @i_mmap_rwsem: Protects @i_mmap and @i_mmap_writable.
434435
* @nrpages: Number of page entries, protected by the i_pages lock.
@@ -446,6 +447,10 @@ struct address_space {
446447
struct xarray i_pages;
447448
gfp_t gfp_mask;
448449
atomic_t i_mmap_writable;
450+
#ifdef CONFIG_READ_ONLY_THP_FOR_FS
451+
/* number of thp, only for non-shmem files */
452+
atomic_t nr_thps;
453+
#endif
449454
struct rb_root_cached i_mmap;
450455
struct rw_semaphore i_mmap_rwsem;
451456
unsigned long nrpages;
@@ -2798,6 +2803,33 @@ static inline errseq_t filemap_sample_wb_err(struct address_space *mapping)
27982803
return errseq_sample(&mapping->wb_err);
27992804
}
28002805

2806+
static inline int filemap_nr_thps(struct address_space *mapping)
2807+
{
2808+
#ifdef CONFIG_READ_ONLY_THP_FOR_FS
2809+
return atomic_read(&mapping->nr_thps);
2810+
#else
2811+
return 0;
2812+
#endif
2813+
}
2814+
2815+
static inline void filemap_nr_thps_inc(struct address_space *mapping)
2816+
{
2817+
#ifdef CONFIG_READ_ONLY_THP_FOR_FS
2818+
atomic_inc(&mapping->nr_thps);
2819+
#else
2820+
WARN_ON_ONCE(1);
2821+
#endif
2822+
}
2823+
2824+
static inline void filemap_nr_thps_dec(struct address_space *mapping)
2825+
{
2826+
#ifdef CONFIG_READ_ONLY_THP_FOR_FS
2827+
atomic_dec(&mapping->nr_thps);
2828+
#else
2829+
WARN_ON_ONCE(1);
2830+
#endif
2831+
}
2832+
28012833
extern int vfs_fsync_range(struct file *file, loff_t start, loff_t end,
28022834
int datasync);
28032835
extern int vfs_fsync(struct file *file, int datasync);

mm/filemap.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ static void unaccount_page_cache_page(struct address_space *mapping,
205205
__dec_node_page_state(page, NR_SHMEM_THPS);
206206
} else if (PageTransHuge(page)) {
207207
__dec_node_page_state(page, NR_FILE_THPS);
208+
filemap_nr_thps_dec(mapping);
208209
}
209210

210211
/*

mm/khugepaged.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1514,8 +1514,10 @@ static void collapse_file(struct mm_struct *mm,
15141514

15151515
if (is_shmem)
15161516
__inc_node_page_state(new_page, NR_SHMEM_THPS);
1517-
else
1517+
else {
15181518
__inc_node_page_state(new_page, NR_FILE_THPS);
1519+
filemap_nr_thps_inc(mapping);
1520+
}
15191521

15201522
if (nr_none) {
15211523
struct zone *zone = page_zone(new_page);

0 commit comments

Comments
 (0)