mm/swap: Regular page swap optimizations
Subsystems (2)
v2 cover mm/swap: Regular page swap optimizations
We appreciate feedback about this patch series from the community. Historically, neither the performance nor latency of the swap path mattered. The underlying I/O was slow enough to hide any latency coming from software and the low IOPS kept the overall CPU impact low.
Times have changed. Coming generation of Solid state Block device latencies are getting down to sub 100 usec, which is within an order of magnitude of DRAM, and their performance is orders of magnitude higher than the single- spindle rotational media we've swapped to historically.
This could benefit many usage scenearios. For example cloud providers who overcommit their memory (as VM don't use all the memory provisioned). Having a fast swap will allow them to be more aggressive in memory overcommit and fit more VMs to a platform.
In our testing [see footnote], the median latency that the kernel adds to a page fault is 15 usec, which comes quite close to the amount that will be contributed by the underlying I/O devices.
The software latency comes mostly from contentions on the locks protecting the radix tree of the swap cache and also the locks protecting the individual swap devices. The lock contentions already consumed 35% of cpu cycles in our test. In the very near future, software latency will become the bottleneck to swap performnace as block device I/O latency gets within the shouting distance of DRAM speed.
This patch set, plus a previous patch Ying already posted (commit: f6498b3f) reduced the median page fault latency from 15 usec to 4 usec (375% reduction) for DRAM based pmem block device.
Patch 1 is a clean up patch.
Patch 2 creates a lock per cluster, this gives us a more fine graind lock
that can be used for accessing swap_map, and not lock the whole swap device
Patch 3 splits the swap cache radix tree into 64MB chunks, reducing
the rate that we have to contende for the radix tree.
Patch 4 eliminates unnecessary page allocation for read ahead. Patch 5-8 create a per cpu cache of the swap slots, so we don't have
to contend on the swap device to get a swap slot or to release a swap slot. And we allocate and release the swap slots in batches for better efficiency.
Ying Huang & Tim Chen
Footnote:
Testing was done on 4.8-rc3-mm1 kernel with/without optimizations from this patche series plus one additional patch Ying posted earlier on removing radix tree write back tag in swap cache. Eight threads performed random memory access on a 2 socket Haswell using swap mounted on RAM based PMEM block device. This emulated a moderate load and a SWAP device unbounded by I/O speed. The aggregate working set is twice the RAM size. We instrumented the kernel to measure the page fault latency.
Change Log:
v2:
1. Fix bug in the index limit used in scan_swap_map_try_ssd_cluster when searching for empty slots in cluster.
2. Fix bug in swap off that incorrectly determines if we still have swap devices left.
3. Port patches to mmotm-2016-10-11-15-46 branch
Huang, Ying (3):
mm/swap: Fix kernel message in swap_info_get()
mm/swap: Add cluster lock
mm/swap: Split swap cache into 64MB trunks
Tim Chen (5):
mm/swap: skip read ahead for unreferenced swap slots mm/swap: Allocate swap slots in batches mm/swap: Free swap slots in batch
mm/swap: Add cache for swap slots allocation mm/swap: Enable swap slots cache usage
include/linux/swap.h | 35 ++- include/linux/swap_slots.h | 37 +++ mm/Makefile | 2 +- mm/swap.c | 6 - mm/swap_slots.c | 306 +++++++++++++++++++++++++ mm/swap_state.c | 76 +++++- mm/swapfile.c | 558 +++++++++++++++++++++++++++++++++++---------- 7 files changed, 875 insertions(+), 145 deletions(-) create mode 100644 include/linux/swap_slots.h create mode 100644 mm/swap_slots.c
Signature · 7 lines
2.5.5 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v2 1/8 mm/swap: Fix kernel message in swap_info_get()
From: "Huang, Ying" <[email protected]>
swap_info_get() is used not only in swap free code path but also in page_swapcount(), etc. So the original kernel message in swap_info_get() is not correct now. Fix it via replacing "swap_free" to "swap_info_get" in the message.
--- mm/swapfile.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
mm/swapfile.c +4 -4
@@ -753,16 +753,16 @@ static struct swap_info_struct *swap_info_get(swp_entry_t entry)return p; bad_free: -pr_err ("swap_free: %s%08lx\n" , Unused_offset, entry.val); +pr_err ("swap_info_get: %s%08lx\n" , Unused_offset, entry.val); goto out; bad_offset: -pr_err ("swap_free: %s%08lx\n" , Bad_offset, entry.val); +pr_err ("swap_info_get: %s%08lx\n" , Bad_offset, entry.val); goto out; bad_device: -pr_err ("swap_free: %s%08lx\n" , Unused_file, entry.val); +pr_err ("swap_info_get: %s%08lx\n" , Unused_file, entry.val); goto out; bad_nofile: -pr_err ("swap_free: %s%08lx\n" , Bad_file, entry.val); +pr_err ("swap_info_get: %s%08lx\n" , Bad_file, entry.val); out:return NULL ; }
Signature · 7 lines
2.5.5 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v2 2/8 mm/swap: Add cluster lock
In reply to Tim ChenFrom: "Huang, Ying" <[email protected]>
This patch is to reduce the lock contention of swap_info_struct->lock via using a more fine grained lock in swap_cluster_info for some swap operations. swap_info_struct->lock is heavily contended if multiple processes reclaim pages simultaneously. Because there is only one lock for each swap device. While in common configuration, there is only one or several swap devices in the system. The lock protects almost all swap related operations.
In fact, many swap operations only access one element of swap_info_struct->swap_map array. And there is no dependency between different elements of swap_info_struct->swap_map. So a fine grained lock can be used to allow parallel access to the different elements of swap_info_struct->swap_map.
In this patch, one bit of swap_cluster_info is used as the bin spinlock to protect the elements of swap_info_struct->swap_map in the swap cluster and the fields of swap_cluster_info. This reduced locking contention for swap_info_struct->swap_map access greatly.
To use the bin spinlock, the size of swap_cluster_info needs to increase from 4 bytes to 8 bytes on the 64bit system. This will use 4k more memory for every 1G swap space.
Because the size of swap_cluster_info is much smaller than the size of the cache line (8 vs 64 on x86_64 architecture), there may be false cache line sharing between swap_cluster_info bit spinlocks. To avoid the false sharing in the first round of the swap cluster allocation, the order of the swap clusters in the free clusters list is changed. So that, the swap_cluster_info sharing the same cache line will be placed as far as possible. After the first round of allocation, the order of the clusters in free clusters list is expected to be random. So the false sharing should be not noticeable.
--- include/linux/swap.h | 13 ++- mm/swapfile.c | 239 +++++++++++++++++++++++++++++++++++++++------------ 2 files changed, 194 insertions(+), 58 deletions(-)
include/linux/swap.h +9 -4
@@ -175,11 +175,16 @@ enum {* protected by swap_info_struct.lock. */ struct swap_cluster_info { -unsigned int data:24 ; -unsigned int flags:8 ; +unsigned long data; }; -#define CLUSTER_FLAG_FREE 1/* This cluster is free */ -#define CLUSTER_FLAG_NEXT_NULL 2/* This cluster has no next cluster */ +#define CLUSTER_COUNT_SHIFT 8 +#define CLUSTER_FLAG_MASK ((1UL << CLUSTER_COUNT_SHIFT) - 1) +#define CLUSTER_COUNT_MASK (~CLUSTER_FLAG_MASK) +#define CLUSTER_FLAG_FREE 1/* This cluster is free */ +#define CLUSTER_FLAG_NEXT_NULL 2/* This cluster has no next cluster */ +/* cluster lock, protect cluster_info contents and sis->swap_map */ +#define CLUSTER_FLAG_LOCK_BIT 2 +#define CLUSTER_FLAG_LOCK (1 << CLUSTER_FLAG_LOCK_BIT)/* * We assign a cluster to each CPU, so each CPU can allocate swap entry from
mm/swapfile.c +185 -54
524 lines not on this page · raw message
Signature · 7 lines
2.5.5 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v2 2/8] mm/swap: Add cluster lock
Quoted 9 lines · On Thu, 20 Oct 2016 16:31:41 -0700 Tim Chen <[email protected]> wrote:
From: "Huang, Ying" <[email protected]>
This patch is to reduce the lock contention of
swap_info_struct->lockvia using a more fine grained lock inswap_cluster_infofor some swap operations.swap_info_struct->lockis heavily contended if multiple processes reclaim pages simultaneously. Because there is only one lock for each swap device. While in common configuration, there is only one or several swap devices in the system. The lock protects almost all swap related operations.
So I'm looking at this a bit. Overall it seems like a good thing to do (from my limited understanding of this area) but I have a probably silly question...
Quoted 5 lines
struct
swap_cluster_info{include/linux/swap.h +1
-2- unsigned int data:24; - unsigned int flags:8; +unsigned long data; };
include/linux/swap.h enum { · line 187
Quoted 10 lines
include/linux/swap.h +8
-2-#define CLUSTER_FLAG_FREE 1/* This cluster is free */ -#define CLUSTER_FLAG_NEXT_NULL 2/* This cluster has no next cluster */ +#define CLUSTER_COUNT_SHIFT 8 +#define CLUSTER_FLAG_MASK ((1UL << CLUSTER_COUNT_SHIFT) - 1) +#define CLUSTER_COUNT_MASK (~CLUSTER_FLAG_MASK) +#define CLUSTER_FLAG_FREE 1/* This cluster is free */ +#define CLUSTER_FLAG_NEXT_NULL 2/* This cluster has no next cluster */ +/* cluster lock, protect cluster_info contents and sis->swap_map */ +#define CLUSTER_FLAG_LOCK_BIT 2 +#define CLUSTER_FLAG_LOCK (1 << CLUSTER_FLAG_LOCK_BIT)
Why the roll-your-own locking and data structures here? To my naive understanding, it seems like you could do something like:
struct swap_cluster_info {
spinlock_t lock; atomic_t count; unsigned int flags;
};
Then you could use proper spinlock operations which, among other things, would make the realtime folks happier. That might well help with the cache-line sharing issues as well. Some of the count manipulations could perhaps be done without the lock entirely; similarly, atomic bitops might save you the locking for some of the flag tweaks - though I'd have to look more closely to be really sure of that.
The cost, of course, is the growth of this structure, but you've already noted that the overhead isn't all that high; seems like it could be worth it.
I assume that I'm missing something obvious here?
Thanks,
jon
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v2 2/8] mm/swap: Add cluster lock
Hi, Jonathan,
Thanks for review.
Quoted 49 lines · Jonathan Corbet
Quoted 9 lines · On Thu, 20 Oct 2016 16:31:41 -0700 Tim Chen <[email protected]> wrote:
From: "Huang, Ying" <[email protected]>
This patch is to reduce the lock contention of
swap_info_struct->lockvia using a more fine grained lock inswap_cluster_infofor some swap operations.swap_info_struct->lockis heavily contended if multiple processes reclaim pages simultaneously. Because there is only one lock for each swap device. While in common configuration, there is only one or several swap devices in the system. The lock protects almost all swap related operations.So I'm looking at this a bit. Overall it seems like a good thing to do (from my limited understanding of this area) but I have a probably silly question...
Quoted 5 lines
struct
swap_cluster_info{include/linux/swap.h +1
-2- unsigned int data:24; - unsigned int flags:8; +unsigned long data; };include/linux/swap.h enum { · line 187
Quoted 10 lines
include/linux/swap.h +8
-2-#define CLUSTER_FLAG_FREE 1/* This cluster is free */ -#define CLUSTER_FLAG_NEXT_NULL 2/* This cluster has no next cluster */ +#define CLUSTER_COUNT_SHIFT 8 +#define CLUSTER_FLAG_MASK ((1UL << CLUSTER_COUNT_SHIFT) - 1) +#define CLUSTER_COUNT_MASK (~CLUSTER_FLAG_MASK) +#define CLUSTER_FLAG_FREE 1/* This cluster is free */ +#define CLUSTER_FLAG_NEXT_NULL 2/* This cluster has no next cluster */ +/* cluster lock, protect cluster_info contents and sis->swap_map */ +#define CLUSTER_FLAG_LOCK_BIT 2 +#define CLUSTER_FLAG_LOCK (1 << CLUSTER_FLAG_LOCK_BIT)Why the roll-your-own locking and data structures here? To my naive understanding, it seems like you could do something like:
struct
swap_cluster_info{spinlock_t lock; atomic_t count; unsigned int flags;};
Then you could use proper spinlock operations which, among other things, would make the realtime folks happier. That might well help with the cache-line sharing issues as well. Some of the count manipulations could perhaps be done without the lock entirely; similarly, atomic bitops might save you the locking for some of the flag tweaks - though I'd have to look more closely to be really sure of that.
Quoted 3 lines
The cost, of course, is the growth of this structure, but you've already noted that the overhead isn't all that high; seems like it could be worth it.
Yes. The data structure you proposed is much easier to be used than the current one. The main concern is the RAM usage. The size of the data structure you proposed is about 80 bytes, while that of the current one is about 8 bytes. There will be one struct swap_cluster_info for every 1MB swap space, so for 1TB swap space, the total size will be 80M compared with 8M of current implementation.
In the other hand, the return of the increased size is not overwhelming. The bit spinlock on cluster will not be heavy contended because it is a quite fine-grained lock. So the benefit will be little to use lockless operations. I guess the realtime issue isn't serious given the lock is not heavy contended and the operations protected by the lock is light-weight too.
Best Regards,
Huang, Ying
Quoted 5 lines
I assume that I'm missing something obvious here?
Thanks,
jon
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v2 2/8] mm/swap: Add cluster lock
Hi, Jonathan,
Quoted 59 lines · Ying
Hi, Jonathan,
Thanks for review.
Quoted 49 lines · Jonathan Corbet
Quoted 9 lines · On Thu, 20 Oct 2016 16:31:41 -0700 Tim Chen <[email protected]> wrote:
From: "Huang, Ying" <[email protected]>
This patch is to reduce the lock contention of
swap_info_struct->lockvia using a more fine grained lock inswap_cluster_infofor some swap operations.swap_info_struct->lockis heavily contended if multiple processes reclaim pages simultaneously. Because there is only one lock for each swap device. While in common configuration, there is only one or several swap devices in the system. The lock protects almost all swap related operations.So I'm looking at this a bit. Overall it seems like a good thing to do (from my limited understanding of this area) but I have a probably silly question...
Quoted 5 lines
struct
swap_cluster_info{include/linux/swap.h +1
-2- unsigned int data:24; - unsigned int flags:8; +unsigned long data; };include/linux/swap.h enum { · line 187
Quoted 10 lines
include/linux/swap.h +8
-2-#define CLUSTER_FLAG_FREE 1/* This cluster is free */ -#define CLUSTER_FLAG_NEXT_NULL 2/* This cluster has no next cluster */ +#define CLUSTER_COUNT_SHIFT 8 +#define CLUSTER_FLAG_MASK ((1UL << CLUSTER_COUNT_SHIFT) - 1) +#define CLUSTER_COUNT_MASK (~CLUSTER_FLAG_MASK) +#define CLUSTER_FLAG_FREE 1/* This cluster is free */ +#define CLUSTER_FLAG_NEXT_NULL 2/* This cluster has no next cluster */ +/* cluster lock, protect cluster_info contents and sis->swap_map */ +#define CLUSTER_FLAG_LOCK_BIT 2 +#define CLUSTER_FLAG_LOCK (1 << CLUSTER_FLAG_LOCK_BIT)Why the roll-your-own locking and data structures here? To my naive understanding, it seems like you could do something like:
struct swap_cluster_info { spinlock_t lock; atomic_t count; unsigned int flags; };Then you could use proper spinlock operations which, among other things, would make the realtime folks happier. That might well help with the cache-line sharing issues as well. Some of the count manipulations could perhaps be done without the lock entirely; similarly, atomic bitops might save you the locking for some of the flag tweaks - though I'd have to look more closely to be really sure of that.
Quoted 3 lines
The cost, of course, is the growth of this structure, but you've already noted that the overhead isn't all that high; seems like it could be worth it.
Quoted 6 lines
Yes. The data structure you proposed is much easier to be used than the current one. The main concern is the RAM usage. The size of the data structure you proposed is about 80 bytes, while that of the current one is about 8 bytes. There will be one struct
swap_cluster_infofor every 1MB swap space, so for 1TB swap space, the total size will be 80M compared with 8M of current implementation.
Sorry, I turned on the lockdep when measure the size change, so the previous size change data is wrong. The size of the data structure you proposed is 12 bytes. While that of the current one is 8 bytes on 64 bit platform and 4 bytes on 32 bit platform.
Best Regards,
Huang, Ying
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v2 3/8 mm/swap: Split swap cache into 64MB trunks
In reply to Tim ChenFrom: "Huang, Ying" <[email protected]>
The patch is to improve the scalability of the swap out/in via using fine grained locks for the swap cache. In current kernel, one address space will be used for each swap device. And in the common configuration, the number of the swap device is very small (one is typical). This causes the heavy lock contention on the radix tree of the address space if multiple tasks swap out/in concurrently. But in fact, there is no dependency between pages in the swap cache. So that, we can split the one shared address space for each swap device into several address spaces to reduce the lock contention. In the patch, the shared address space is split into 64MB trunks. 64MB is chosen to balance the memory space usage and effect of lock contention reduction.
The size of struct address_space on x86_64 architecture is 408B, so with the patch, 6528B more memory will be used for every 1GB swap space on x86_64 architecture.
One address space is still shared for the swap entries in the same 64M trunks. To avoid lock contention for the first round of swap space allocation, the order of the swap clusters in the initial free clusters list is changed. The swap space distance between the consecutive swap clusters in the free cluster list is at least 64M. After the first round of allocation, the swap clusters are expected to be freed randomly, so the lock contention should be reduced effectively.
--- include/linux/swap.h | 10 ++++++-- mm/swap.c | 6 ----- mm/swap_state.c | 71 +++++++++++++++++++++++++++++++++++++++++++--------- mm/swapfile.c | 16 ++++++++++-- 4 files changed, 81 insertions(+), 22 deletions(-)
include/linux/swap.h +8 -2
@@ -370,8 +370,12 @@ int generic_swapfile_activate(struct swap_info_struct *, struct file *, sector_t *);/* linux/mm/swap_state.c */ -extern struct address_space swapper_spaces[]; -#define swap_address_space (entry) (&swapper_spaces[swp_type(entry)]) +/* One swap address space for each 64M swap space */ +#define SWAP_ADDRESS_SPACE_SHIFT 14 +#define SWAP_ADDRESS_SPACE_PAGES (1 << SWAP_ADDRESS_SPACE_SHIFT) +extern struct address_space * swapper_spaces[]; +#define swap_address_space (entry) \ + (&swapper_spaces[swp_type(entry)][swp_offset(entry) >> SWAP_ADDRESS_SPACE_SHIFT])extern unsigned long total_swapcache_pages (void );extern void show_swap_cache_info (void );extern int add_to_swap (struct page *,struct list_head *list);
@@ -425,6 +429,8 @@ extern struct swap_info_struct *page_swap_info(struct page *);extern bool reuse_swap_page (struct page *,int *);extern int try_to_free_swap (struct page *);struct backing_dev_info ; +extern int init_swap_address_space (unsigned int type,unsigned long nr_pages); +extern void exit_swap_address_space (unsigned int type);#else /* CONFIG_SWAP */
mm/swap.c +0 -6
@@ -969,12 +969,6 @@ EXPORT_SYMBOL(pagevec_lookup_tag);void __init swap_setup (void ) {unsigned long megs = totalram_pages >> (20 -PAGE_SHIFT ); -#ifdef CONFIG_SWAP -int i; - -for (i =0 ; i <MAX_SWAPFILES ; i++) -spin_lock_init (&swapper_spaces[i].tree_lock); -#endif /* Use a smaller cluster for small-memory machines */ if (megs <16 )
mm/swap_state.c +59 -12
94 lines not on this page · raw message
mm/swapfile.c +14 -2
@@ -2076,6 +2076,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)vfree (frontswap_map);/* Destroy swap account information */ swap_cgroup_swapoff (p->type); +exit_swap_address_space (p->type); inode = mapping->host;if (S_ISBLK (inode->i_mode)) {
@@ -2397,8 +2398,12 @@ static unsigned long read_swap_header(struct swap_info_struct *p,return maxpages; } -#define SWAP_CLUSTER_COLS \ +#define SWAP_CLUSTER_INFO_COLS \ DIV_ROUND_UP(L1_CACHE_BYTES, sizeof(struct swap_cluster_info)) +#define SWAP_CLUSTER_SPACE_COLS \ + DIV_ROUND_UP(SWAP_ADDRESS_SPACE_PAGES, SWAPFILE_CLUSTER) +#define SWAP_CLUSTER_COLS \ + max_t(unsigned int, SWAP_CLUSTER_INFO_COLS, SWAP_CLUSTER_SPACE_COLS) static int setup_swap_map_and_extents(struct swap_info_struct *p, union swap_header *swap_header,
@@ -2461,7 +2466,10 @@ static int setup_swap_map_and_extents(struct swap_info_struct *p,return nr_extents; -/* Reduce false cache line sharing between cluster_info */ +/* +* Reduce false cache line sharing between cluster_info and +* sharing same address space. +*/ for (k =0 ; k <SWAP_CLUSTER_COLS ; k++) { j = (k + col) %SWAP_CLUSTER_COLS ;for (i =0 ; i <DIV_ROUND_UP (nr_clusters,SWAP_CLUSTER_COLS ); i++) {
@@ -2642,6 +2650,10 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags) } } + error =init_swap_address_space (p->type, maxpages); +if (error) + goto bad_swap; +mutex_lock (&swapon_mutex); prio =-1 ;if (swap_flags &SWAP_FLAG_PREFER )
Signature · 7 lines
2.5.5 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v2 4/8 mm/swap: skip read ahead for unreferenced swap slots
In reply to Tim ChenWe can avoid needlessly allocating page for swap slots that are not used by anyone. No pages have to be read in for these slots.
--- include/linux/swap.h | 6 ++++++ mm/swap_state.c | 4 ++++ mm/swapfile.c | 47 +++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 51 insertions(+), 6 deletions(-)
include/linux/swap.h +6 -0
@@ -424,6 +424,7 @@ extern unsigned int count_swap_pages(int, int);extern sector_t map_swap_page (struct page *,struct block_device **);extern sector_t swapdev_block (int ,pgoff_t );extern int page_swapcount (struct page *); +extern int __swp_swapcount (swp_entry_t entry);extern int swp_swapcount (swp_entry_t entry);extern struct swap_info_struct *page_swap_info (struct page *);extern bool reuse_swap_page (struct page *,int *);
@@ -518,6 +519,11 @@ static inline int page_swapcount(struct page *page)return 0 ; } +static inline int __swp_swapcount (swp_entry_t entry) +{ +return 0 ; +} +static inline int swp_swapcount (swp_entry_t entry) {return 0 ;
mm/swap_state.c +4 -0
@@ -314,6 +314,10 @@ struct page *__read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask, *new_page_allocated = false; do { + /* Just skip read ahead for unused swap slot */ + if (!__swp_swapcount(entry)) + return NULL; + /* * First check the swap cache. Since this is normally * called after lookup_swap_cache() failed, re-calling
mm/swapfile.c +41 -6
73 lines not on this page · raw message
Signature · 7 lines
2.5.5 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v2 5/8 mm/swap: Allocate swap slots in batches
In reply to Tim ChenCurrently, the swap slots are allocated one page at a time, causing contention to the swap_info lock protecting the swap partition on every page being swapped.
This patch adds new functions get_swap_pages and scan_swap_map_slots to request multiple swap slots at once. This will reduces the lock contention on the swap_info lock. Also scan_swap_map_slots can operate more efficiently as swap slots often occurs in clusters close to each other on a swap device and it is quicker to allocate them together.
--- include/linux/swap.h | 2 + mm/swapfile.c | 161 ++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 136 insertions(+), 27 deletions(-)
include/linux/swap.h +2 -0
@@ -27,6 +27,7 @@ struct bio;#define SWAP_FLAGS_VALID (SWAP_FLAG_PRIO_MASK | SWAP_FLAG_PREFER | \ SWAP_FLAG_DISCARD | SWAP_FLAG_DISCARD_ONCE | \ SWAP_FLAG_DISCARD_PAGES) +#define SWAP_BATCH 64static inline int current_is_kswapd (void ) {
@@ -412,6 +413,7 @@ static inline long get_nr_swap_pages(void)extern void si_swapinfo (struct sysinfo *);extern swp_entry_t get_swap_page (void );extern swp_entry_t get_swap_page_of_type (int ); +extern int get_swap_pages (int n,swp_entry_t swp_entries[]);extern int add_swap_count_continuation (swp_entry_t ,gfp_t );extern void swap_shmem_alloc (swp_entry_t );extern int swap_duplicate (swp_entry_t );
mm/swapfile.c +134 -27
244 lines not on this page · raw message
Signature · 7 lines
2.5.5 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v2 6/8 mm/swap: Free swap slots in batch
In reply to Tim ChenAdd new functions that free unused swap slots in batches without the need to reacquire swap info lock. This improves scalability and reduce lock contention.
--- include/linux/swap.h | 1 + mm/swapfile.c | 155 +++++++++++++++++++++++++++++++-------------------- 2 files changed, 95 insertions(+), 61 deletions(-)
include/linux/swap.h +1 -0
@@ -420,6 +420,7 @@ extern int swap_duplicate(swp_entry_t);extern int swapcache_prepare (swp_entry_t );extern void swap_free (swp_entry_t );extern void swapcache_free (swp_entry_t ); +extern void swapcache_free_entries (swp_entry_t *entries,int n);extern int free_swap_and_cache (swp_entry_t );extern int swap_type_of (dev_t ,sector_t ,struct block_device **);extern unsigned int count_swap_pages (int ,int );
mm/swapfile.c +94 -61
212 lines not on this page · raw message
Signature · 7 lines
2.5.5 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v2 7/8 mm/swap: Add cache for swap slots allocation
In reply to Tim ChenWe add per cpu caches for swap slots that can be allocated and freed quickly without the need to touch the swap info lock.
Two separate caches are maintained for swap slots allocated and swap slots returned. This is to allow the swap slots to be returned to the global pool in a batch so they will have a chance to be coaelesced with other slots in a cluster. We do not reuse the slots that are returned right away, as it may increase fragmentation of the slots.
The swap allocation cache is protected by a mutex as we may sleep when searching for empty slots in cache. The swap free cache is protected by a spin lock as we cannot sleep in the free path.
We refill the swap slots cache when we run out of slots, and we disable the swap slots cache and drain the slots if the global number of slots fall below a low watermark threshold. We re-enable the cache agian when the slots available are above a high watermark.
--- include/linux/swap.h | 3 + include/linux/swap_slots.h | 37 ++++++ mm/Makefile | 2 +- mm/swap_slots.c | 306 +++++++++++++++++++++++++++++++++++++++++++++ mm/swap_state.c | 1 + mm/swapfile.c | 15 +-- 6 files changed, 352 insertions(+), 12 deletions(-) create mode 100644 include/linux/swap_slots.h create mode 100644 mm/swap_slots.c
include/linux/swap.h +3 -0
@@ -436,6 +436,9 @@ struct backing_dev_info;extern int init_swap_address_space (unsigned int type,unsigned long nr_pages);extern void exit_swap_address_space (unsigned int type); +extern int get_swap_slots (int n,swp_entry_t *slots); +extern void swapcache_free_batch (swp_entry_t *entries,int n); +#else /* CONFIG_SWAP */ #define swap_address_space (entry) (NULL)
include/linux/swap_slots.h new file +37 -0
@@ -0,0 +1,37 @@ +#ifndef _LINUX_SWAP_SLOTS_H +#define _LINUX_SWAP_SLOTS_H + +#include <linux/swap.h> +#include <linux/spinlock.h> +#include <linux/mutex.h> + +#define SWAP_SLOTS_CACHE_SIZE SWAP_BATCH +#define THRESHOLD_ACTIVATE_SWAP_SLOTS_CACHE (5*SWAP_SLOTS_CACHE_SIZE) +#define THRESHOLD_DEACTIVATE_SWAP_SLOTS_CACHE (2*SWAP_SLOTS_CACHE_SIZE) + +struct swap_slots_cache { +struct mutex alloc_lock; +swp_entry_t *slots; +int nr; +int cur; +spinlock_t free_lock; +swp_entry_t *slots_ret; +int n_ret; +}; + +DECLARE_PER_CPU(struct swap_slots_cache, swp_slots); +extern bool swap_slot_cache_enabled; + +void drain_swap_slots_cache (unsigned int type); +void deactivate_swap_slots_cache (void ); +void disable_swap_slots_cache (void ); +void reenable_swap_slots_cache (void ); +void reactivate_swap_slots_cache (void ); + +int free_swap_slot_to_cache (swp_entry_t entry); +swp_entry_t get_swap_slot (void ); +int get_swap_slots (int n,swp_entry_t *entries); +void free_swap_slot_cache (int cpu); +int enable_swap_slot_caches (void ); + +#endif /* _LINUX_SWAP_SLOTS_H */
mm/Makefile +1 -1
@@ -38,7 +38,7 @@ obj-y := filemap.o mempool.o oom_kill.o \ readahead.o swap.o truncate.o vmscan.o shmem.o \ util.o mmzone.o vmstat.o backing-dev.o \ mm_init.o mmu_context.o percpu.o slab_common.o \ - compaction.o vmacache.o \ + compaction.o vmacache.o swap_slots.o \ interval_tree.o list_lru.o workingset.o \ debug.o $(mmu-y)
mm/swap_slots.c new file +306 -0
306 lines not on this page · raw message
mm/swap_state.c +1 -0
@@ -18,6 +18,7 @@#include <linux/pagevec.h> #include <linux/migrate.h> #include <linux/vmalloc.h> +#include <linux/swap_slots.h> #include <asm/pgtable.h>
mm/swapfile.c +4 -11
@@ -34,6 +34,7 @@#include <linux/frontswap.h> #include <linux/swapfile.h> #include <linux/export.h> +#include <linux/swap_slots.h> #include <asm/pgtable.h> #include <asm/tlbflush.h>
@@ -880,14 +881,6 @@ noswap:return n_ret; } -swp_entry_t get_swap_page (void ) -{ -swp_entry_t entry; - -get_swap_pages (1 , &entry); -return entry; -} -/* The only caller of this function is now suspend routine */ swp_entry_t get_swap_page_of_type (int type) {
@@ -1078,7 +1071,7 @@ void swap_free(swp_entry_t entry) p =_swap_info_get (entry);if (p) {if (!__swap_entry_free (p, entry,1 )) -swapcache_free_entries (&entry,1 ); +free_swap_slot_to_cache (entry); } }
@@ -1092,7 +1085,7 @@ void swapcache_free(swp_entry_t entry) p =_swap_info_get (entry);if (p) {if (!__swap_entry_free (p, entry,SWAP_HAS_CACHE )) -swapcache_free_entries (&entry,1 ); +free_swap_slot_to_cache (entry); } }
@@ -1300,7 +1293,7 @@ int free_swap_and_cache(swp_entry_t entry) page =NULL ; } }else if (!count) -swapcache_free_entries (&entry,1 ); +free_swap_slot_to_cache (entry); }if (page) {/*
Signature · 7 lines
2.5.5 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v2 8/8 mm/swap: Enable swap slots cache usage
In reply to Tim ChenInitialize swap slots cache and enable it on swap on. Drain swap slots on swap off.
--- mm/swapfile.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-)
mm/swapfile.c +10 -3
@@ -2132,7 +2132,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)struct address_space *mapping;struct inode *inode;struct filename *pathname; -int err, found =0 ; +int err, found =0 , has_swap =0 ;unsigned int old_block_size;if (!capable (CAP_SYS_ADMIN ))
@@ -2144,6 +2144,7 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)if (IS_ERR (pathname))return PTR_ERR (pathname); +disable_swap_slots_cache (); victim =file_open_name (pathname,O_RDWR |O_LARGEFILE ,0 ); err =PTR_ERR (victim);if (IS_ERR (victim))
@@ -2153,10 +2154,13 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)spin_lock (&swap_lock);plist_for_each_entry (p, &swap_active_head, list) {if (p->flags &SWP_WRITEOK ) { -if (p->swap_file->f_mapping == mapping) { +if (p->swap_file->f_mapping == mapping) found =1 ; +else + ++has_swap; +/* there is another swap device left? */ +if (found && has_swap)break ; - } } }if (!found) {
@@ -2275,6 +2279,8 @@ out_dput:filp_close (victim,NULL ); out:putname (pathname); +if (has_swap) +reenable_swap_slots_cache ();return err; }
@@ -2692,6 +2698,7 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)if (!capable (CAP_SYS_ADMIN ))return -EPERM ; +enable_swap_slot_caches (); p =alloc_swap_info ();if (IS_ERR (p))return PTR_ERR (p);
Signature · 7 lines
2.5.5 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
On s390 4.9-rc1 + this patch set
I get the following on swapon
[ 308.206195] ------------[ cut here ]------------ [ 308.206203] WARNING: CPU: 5 PID: 20745 at mm/page_alloc.c:3511 __alloc_pages_nodemask+0x884/0xdf8 [ 308.206205] Modules linked in: xt_CHECKSUM iptable_mangle ipt_MASQUERADE nf_nat_masquerade_ipv4 iptable_nat nf_nat_ipv4 nf_nat nf_conntrack_ipv4 nf_defrag_ipv4 xt_conntrack nf_conntrack ipt_REJECT nf_reject_ipv4 bridge stp llc ebtable_filter ebtables ip6table_filter ip6_tables iptable_filter btrfs raid6_pq xor dm_service_time ghash_s390 prng aes_s390 des_s390 des_generic qeth_l2 sha512_s390 sha256_s390 sha1_s390 sha_common nfsd eadm_sch auth_rpcgss qeth ccwgroup oid_registry nfs_acl lockd grace vhost_net vhost sunrpc macvtap macvlan kvm sch_fq_codel dm_multipath ip_tables [ 308.206240] CPU: 5 PID: 20745 Comm: swapon Tainted: G W 4.9.0-rc1+ #23 [ 308.206241] Hardware name: IBM 2964 NC9 704 (LPAR) [ 308.206243] task: 00000000e3bb8000 task.stack: 00000000d4270000 [ 308.206244] Krnl PSW : 0704c00180000000 000000000025db3c [ 308.206246] (__alloc_pages_nodemask+0x884/0xdf8)
[ 308.206248] R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:3 CC:0 PM:0
[ 308.206250] RI:0 EA:3
[ 308.206251]
Krnl GPRS: 000000000000000a 0000000000b2b0ec 0000000100000312 0000000000000001
[ 308.206267] 000000000025d3ec 0000000000000008 0000000000000075 000000000240c0c0
[ 308.206269] 0000000000000001 0000000000000000 000000000000000a 0000000000000000
[ 308.206270] 000000000000000a 000000000079d8c8 000000000025d3ec 00000000d4273ba0
[ 308.206280] Krnl Code: 000000000025db30: a774fd27 brc 7,25d57e
[ 308.206282]
000000000025db34: 92011000 mvi 0(%r1),1
[ 308.206285]
#000000000025db38: a7f40001 brc 15,25db3a
[ 308.206286]
>000000000025db3c: a7f4fd21 brc 15,25d57e
[ 308.206289]
000000000025db40: 4130f150 la %r3,336(%r15)
[ 308.206291]
000000000025db44: b904002c lgr %r2,%r12
[ 308.206293]
000000000025db48: c0e5ffffe11c brasl %r14,259d80
[ 308.206294]
000000000025db4e: a7f4fda3 brc 15,25d694
[ 308.206297] Call Trace:
[ 308.206299] ([<000000000025d3ec>] __alloc_pages_nodemask+0x134/0xdf8)
[ 308.206303] ([<0000000000280d6a>] kmalloc_order+0x42/0x70)
[ 308.206305] ([<0000000000280dd8>] kmalloc_order_trace+0x40/0xf0)
[ 308.206310] ([<00000000002a7090>] init_swap_address_space+0x68/0x138)
[ 308.206312] ([<00000000002ac858>] SyS_swapon+0xbd0/0xf80)
[ 308.206317] ([<0000000000785476>] system_call+0xd6/0x264)
[ 308.206318] Last Breaking-Event-Address:
[ 308.206319] [<000000000025db38>] __alloc_pages_nodemask+0x880/0xdf8
[ 308.206320] ---[ end trace aaeca736f47ac05b ]-----
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 36 lines · Christian Borntraeger
On s390 4.9-rc1 + this patch set
I get the following on swapon[ 308.206195] ------------[ cut here ]------------ [ 308.206203] WARNING: CPU: 5 PID: 20745 at mm/page_alloc.c:3511 __alloc_pages_nodemask+0x884/0xdf8 [ 308.206205] Modules linked in: xt_CHECKSUM iptable_mangle ipt_MASQUERADE nf_nat_masquerade_ipv4 iptable_nat nf_nat_ipv4 nf_nat nf_conntrack_ipv4 nf_defrag_ipv4 xt_conntrack nf_conntrack ipt_REJECT nf_reject_ipv4 bridge stp llc ebtable_filter ebtables ip6table_filter ip6_tables iptable_filter btrfs raid6_pq xor dm_service_time ghash_s390 prng aes_s390 des_s390 des_generic qeth_l2 sha512_s390 sha256_s390 sha1_s390 sha_common nfsd eadm_sch auth_rpcgss qeth ccwgroup oid_registry nfs_acl lockd grace vhost_net vhost sunrpc macvtap macvlan kvm sch_fq_codel dm_multipath ip_tables [ 308.206240] CPU: 5 PID: 20745 Comm: swapon Tainted: G W 4.9.0-rc1+ #23 [ 308.206241] Hardware name: IBM 2964 NC9 704 (LPAR) [ 308.206243] task: 00000000e3bb8000 task.stack: 00000000d4270000 [ 308.206244] Krnl PSW : 0704c00180000000 000000000025db3c [ 308.206246] (__alloc_pages_nodemask+0x884/0xdf8)[ 308.206248] R:0 T:1 IO:1 EX:1 Key:0 M:1 W:0 P:0 AS:3 CC:0 PM:0 [ 308.206250] RI:0 EA:3 [ 308.206251] Krnl GPRS: 000000000000000a 0000000000b2b0ec 0000000100000312 0000000000000001 [ 308.206267] 000000000025d3ec 0000000000000008 0000000000000075 000000000240c0c0 [ 308.206269] 0000000000000001 0000000000000000 000000000000000a 0000000000000000 [ 308.206270] 000000000000000a 000000000079d8c8 000000000025d3ec 00000000d4273ba0 [ 308.206280] Krnl Code: 000000000025db30: a774fd27 brc 7,25d57e [ 308.206282] 000000000025db34: 92011000 mvi 0(%r1),1 [ 308.206285] #000000000025db38: a7f40001 brc 15,25db3a [ 308.206286] >000000000025db3c: a7f4fd21 brc 15,25d57e [ 308.206289] 000000000025db40: 4130f150 la %r3,336(%r15) [ 308.206291] 000000000025db44: b904002c lgr %r2,%r12 [ 308.206293] 000000000025db48: c0e5ffffe11c brasl %r14,259d80 [ 308.206294] 000000000025db4e: a7f4fda3 brc 15,25d694 [ 308.206297] Call Trace: [ 308.206299] ([<000000000025d3ec>] __alloc_pages_nodemask+0x134/0xdf8)
Quoted 9 lines
[ 308.206303] ([<0000000000280d6a>] kmalloc_order+0x42/0x70) [ 308.206305] ([<0000000000280dd8>] kmalloc_order_trace+0x40/0xf0) [ 308.206310] ([<00000000002a7090>] init_swap_address_space+0x68/0x138) [ 308.206312] ([<00000000002ac858>] SyS_swapon+0xbd0/0xf80) [ 308.206317] ([<0000000000785476>] system_call+0xd6/0x264) [ 308.206318] Last Breaking-Event-Address: [ 308.206319] [<000000000025db38>] __alloc_pages_nodemask+0x880/0xdf8 [ 308.206320] ---[ end trace aaeca736f47ac05b ]---
Looks like that 1TB of swap is just too big for your logic (you try kmalloc without checking the size).
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 13 lines · Christian Borntraeger
Quoted 11 lines · Christian Borntraeger
[ 308.206297] Call Trace: [ 308.206299] ([<000000000025d3ec>] __alloc_pages_nodemask+0x134/0xdf8) [ 308.206303] ([<0000000000280d6a>] kmalloc_order+0x42/0x70) [ 308.206305] ([<0000000000280dd8>] kmalloc_order_trace+0x40/0xf0) [ 308.206310] ([<00000000002a7090>] init_swap_address_space+0x68/0x138) [ 308.206312] ([<00000000002ac858>] SyS_swapon+0xbd0/0xf80) [ 308.206317] ([<0000000000785476>] system_call+0xd6/0x264) [ 308.206318] Last Breaking-Event-Address: [ 308.206319] [<000000000025db38>] __alloc_pages_nodemask+0x880/0xdf8 [ 308.206320] ---[ end trace aaeca736f47ac05b ]---
Quoted 2 lines
Looks like that 1TB of swap is just too big for your logic (you try kmalloc without checking the size).
Thanks for giving this patch series a spin.
Let's use vzalloc instead. Can you try the following change.
Thanks.
Tim
--->8---
mm/swap_state.c +2 -4
@@ -526,11 +526,9 @@ int init_swap_address_space(unsigned int type, unsigned long nr_pages)unsigned int i, nr; nr =DIV_ROUND_UP (nr_pages,SWAP_ADDRESS_SPACE_PAGES ); - spaces =kzalloc (sizeof (struct address_space ) * nr,GFP_KERNEL ); + spaces =vzalloc (sizeof (struct address_space ) * nr);if (!spaces) { - spaces =vzalloc (sizeof (struct address_space ) * nr); -if (!spaces) -return -ENOMEM ; +return -ENOMEM ; }for (i =0 ; i < nr; i++) { space = spaces + i;
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>