Skip to contentSkip to conversation

mm/swap: Regular page swap optimizations

PATCH In review Tim Chen 7 participants 33 messages Jan 11, 2017 – Jan 18, 2017

Subsystems (2)
lore
Share & tools
lore

Tim Chen

author
#0 lore raw

v5 cover mm/swap: Regular page swap optimizations

Change Log:
v5:
1. Rebase patch series on 4.10-rc3 kernel. Update patch series to remove usage of obsoleted hot plug functions: cpu_notifier_register_begin(), cpu_notifier_register_done(), and __register_hotcpu_notifier()
2. Fix a bug returning uninitialized swap slot when we run out of swap slots on all swap devices.
3. Minor code style clean ups.

v4:
1. Fix a bug in unlock cluster in add_swap_count_continuation(). We should use unlock_cluster() instead of unlock_cluser_or_swap_info().
2. During swap off, handle race when swap slot is marked unused but allocated, and not yet placed in swap cache. Wait for swap slot to be placed in swap cache and not abort swap off.
3. Initialize n_ret in get_swap_pages().

v3:
1. Fix bug that didn't check for page already in swap cache before skipping read ahead and return null page.
2. Fix bug that didn't try to allocate from global pool if allocation from swap slot cache did not succeed.
3. Fix memory allocation bug for spaces to store split up 64MB radix tree
4. Fix problems caused by races between get_swap_page, cpu online/offline and swap_on/off

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

Andrew,

We're updating this patch series with some minor fixes and rebased to 4.10-rc3. Please consider this patch series for inclusion to the mm kernel.

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, 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-9 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.

We describe below the changes in swap throughput and
lock contentions. Test was done with PMEM block swap device for 32 processes on Xeon E5 v3 system. The swap device used is a RAM simulated PMEM (persistent memory) device. To test the sequential swapping out, the test case created 32 processes, which sequentially allocate and write to the anonymous pages until the RAM and part of the swap device is used. This gives an indication of the effect of each successive patch. Test was done on patch version 4 which is functionally identical to version 5.

Vanilla kernel 4.9-rc8:
Throughput:
vmstat.swap.so: 1428002 kB/sec,
Top lock contentions in %cpu.
perf-profile.calltrace.cycles-pp._raw_spin_lock_irq.__add_to_swap_cache.add_to_swap_cache.add_to_swap.shrink_page_list: 13.94%, perf-profile.calltrace.cycles-pp._raw_spin_lock_irqsave.__remove_mapping.shrink_page_list.shrink_inactive_list.shrink_node_memcg: 13.75%, perf-profile.calltrace.cycles-pp._raw_spin_lock.swap_info_get.swapcache_free.__remove_mapping.shrink_page_list: 7.05%, perf-profile.calltrace.cycles-pp._raw_spin_lock.swap_info_get.page_swapcount.try_to_free_swap.swap_writepage: 7.03%, perf-profile.calltrace.cycles-pp._raw_spin_lock.__swap_duplicate.swap_duplicate.try_to_unmap_one.rmap_walk_anon: 7.02%, perf-profile.calltrace.cycles-pp._raw_spin_lock.get_swap_page.add_to_swap.shrink_page_list.shrink_inactive_list: 6.83%, perf-profile.calltrace.cycles-pp._raw_spin_lock.page_check_address_transhuge.page_referenced_one.rmap_walk_anon.rmap_walk: 0.81%,

Patch 1-2:
Swap throughput slightly improved 4%, swap_info_get and __swap_duplicate contention on swap_info lock eliminated.
Throughput:
vmstat.swap.so: 1481704 kB/sec, (4% increase over vanilla)
Top lock contentions in %cpu:
perf-profile.calltrace.cycles-pp._raw_spin_lock_irq.__add_to_swap_cache.add_to_swap_cache.add_to_swap.shrink_page_list: 27.53%, perf-profile.calltrace.cycles-pp._raw_spin_lock_irqsave.__remove_mapping.shrink_page_list.shrink_inactive_list.shrink_node_memcg: 27.01%, perf-profile.calltrace.cycles-pp._raw_spin_lock.free_pcppages_bulk.drain_pages_zone.drain_pages.drain_local_pages: 1.03%,

Patch 1-3
Swap throughput improved 44%, add_to_swap_cache contention on radix tree lock is eliminated.
Throughput:
vmstat.swap.so: 2050097 kB/sec, (44% increase over vanilla)
Top lock contentions in %cpu:
perf-profile.calltrace.cycles-pp._raw_spin_lock.get_swap_page.add_to_swap.shrink_page_list.shrink_inactive_list: 43.27, perf-profile.calltrace.cycles-pp._raw_spin_lock.get_page_from_freelist.__alloc_pages_nodemask.alloc_pages_vma.handle_mm_fault: 4.84,

Patch 1-9
Swap throughput improved 192%, get_swap_page contention on swap_info lock eliminated.
Throughput:
vmstat.swap.so: 4170746 kB/sec, (192% increase over vanilla)
Top lock contentions in %cpu:
perf-profile.calltrace.cycles-pp._raw_spin_lock.swapcache_free_entries.free_swap_slot.free_swap_and_cache.unmap_page_range: 13.91%, perf-profile.calltrace.cycles-pp._raw_spin_lock.get_page_from_freelist.__alloc_pages_nodemask.alloc_pages_vma.handle_mm_fault: 8.56%, perf-profile.calltrace.cycles-pp._raw_spin_lock.get_page_from_freelist.__alloc_pages_slowpath.__alloc_pages_nodemask.alloc_pages_vma: 2.56%, perf-profile.calltrace.cycles-pp._raw_spin_lock.get_swap_pages.get_swap_page.add_to_swap.shrink_page_list: 2.47%,

Ying Huang & Tim Chen

Footnote:
We tested the patch series for page latency 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.

Huang Ying (1):
mm/swap: Skip readahead only when swap slot cache is enabled

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       |  37 +++-
 include/linux/swap_slots.h |  30 +++
 mm/Makefile                |   2 +-
 mm/swap.c                  |   6 -
 mm/swap_slots.c            | 333 ++++++++++++++++++++++++++++
 mm/swap_state.c            |  80 ++++++-
 mm/swapfile.c              | 540 +++++++++++++++++++++++++++++++++++----------
 7 files changed, 887 insertions(+), 141 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>
author
#1 lore raw

PATCH v5 1/9 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
index 1c6e032..19a7c1d 100644
@@ -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>
author
#2 lore raw

PATCH v5 2/9 mm/swap: Add cluster lock

In reply to Tim Chen

From: "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
index 09f4be1..40716b9 100644
@@ -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>
maintainer
#3 lore raw

Re: [PATCH v5 2/9] mm/swap: Add cluster lock

Quoted 42 lines · On Wed, 11 Jan 2017 09:55:12 -0800 Tim Chen <[email protected]> wrote:

From: "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 +0 -1
@@ -175,11 +175,16 @@ enum {
  * protected by swap_info_struct.lock.
  */
 struct swap_cluster_info {
-	unsigned int data:24;

include/linux/swap.h enum {

Quoted 10 lines
include/linux/swap.h +5 -1
-	unsigned int flags:8;
+	unsigned long data;
 };
 
...
 
+static inline void __lock_cluster(struct swap_cluster_info *ci)
+{
+	bit_spin_lock(CLUSTER_FLAG_LOCK_BIT, &ci->data);
+}

hm, bit_spin_lock() is a nasty thing. It is slow and it doesn't have all the lockdep support.

Would the world end if we added a spinlock to swap_cluster_info? Check my math: for each 1G of wapspace we have 256k pages, hence 1k of swap_cluster_infos, hence 4k of memory. ie, one page of memory for each 256,000 pages of swap. Is increasing that 1/256000 to 2/256000 a big deal?

Also, I note that struct swap_cluster_info is only used in swapfile.c and as a cleanup we could move its definition into that .c file. Perhaps other things could be moved as well..

#4 lore raw

Re: [PATCH v5 2/9] mm/swap: Add cluster lock

Quoted 4 lines · On Wed, 11 Jan 2017 15:00:29 -0800 Andrew Morton <[email protected]>…

hm, bit_spin_lock() is a nasty thing. It is slow and it doesn't have all the lockdep support.

Would the world end if we added a spinlock to swap_cluster_info?

FWIW, I asked the same question in December, this is what I got:

jon

Quoted 79 lines

From: "Huang\, Ying" <[email protected]>
To: Jonathan Corbet <[email protected]>
Cc: Tim Chen <[email protected]>, Andrew Morton <[email protected]>, "Huang\, Ying" <[email protected]>, <[email protected]>, <[email protected]>, <[email protected]>, <[email protected]>, <[email protected]>, Hugh Dickins <[email protected]>, Shaohua Li <[email protected]>, Minchan Kim <[email protected]>, Rik van Riel <[email protected]>, Andrea Arcangeli <[email protected]>, "Kirill A . Shutemov" <[email protected]>, Vladimir Davydov <[email protected]>, Johannes Weiner <[email protected]>, Michal Hocko <[email protected]>, "Hillf Danton" <[email protected]>
Subject: Re: [PATCH v2 2/8] mm/swap: Add cluster lock
Date: Tue, 25 Oct 2016 10:05:39 +0800

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->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.

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.

Quoted 9 lines

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>

maintainer
#5 lore raw

Re: [PATCH v5 2/9] mm/swap: Add cluster lock

Quoted 38 lines · On Wed, 11 Jan 2017 16:07:29 -0700 Jonathan Corbet <[email protected]> wrote:
Quoted 4 lines · On Wed, 11 Jan 2017 15:00:29 -0800 Andrew Morton <[email protected]>…

hm, bit_spin_lock() is a nasty thing. It is slow and it doesn't have all the lockdep support.

Would the world end if we added a spinlock to swap_cluster_info?

FWIW, I asked the same question in December, this is what I got:

...

Quoted 26 lines
Quoted 16 lines

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.

Where did this 80 bytes come from? That swap_cluster_info is 12 bytes and could perhaps be squeezed into 8 bytes if we can get away with a 24-bit "count".

Quoted 6 lines
Quoted 6 lines

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.

--
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>

#6 lore raw

Re: [PATCH v5 2/9] mm/swap: Add cluster lock

Hi, Andrew,

Quoted 41 lines · Andrew Morton
Quoted 38 lines · On Wed, 11 Jan 2017 16:07:29 -0700 Jonathan Corbet <[email protected]> wrote:
Quoted 4 lines · On Wed, 11 Jan 2017 15:00:29 -0800 Andrew Morton <[email protected]>…

hm, bit_spin_lock() is a nasty thing. It is slow and it doesn't have all the lockdep support.

Would the world end if we added a spinlock to swap_cluster_info?

FWIW, I asked the same question in December, this is what I got:

...

Quoted 26 lines
Quoted 16 lines

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.

Quoted 3 lines

Where did this 80 bytes come from? That swap_cluster_info is 12 bytes and could perhaps be squeezed into 8 bytes if we can get away with a 24-bit "count".

Sorry, I made a mistake when measuring the size of swap_cluster_info when I sent that email, because I turned on the lockdep when measuring. I have sent out a correction email to Jonathan when I realized that later.

So the latest size measuring result is:

If we use bit_spin_lock, the size of cluster_swap_info will,

- increased from 4 bytes to 8 bytes on 64 bit platform
- keep as 4 bytes on 32 bit platform

If we use normal spinlock (queue spinlock), the size of cluster_swap_info will,

- increased from 4 bytes to 8 bytes on 64 bit platform
- increased from 4 bytes to 8 bytes on 32 bit platform

So the difference occurs on 32 bit platform. If the size increment on 32 bit platform is OK, then I think it should be good to use normal spinlock instead of bit_spin_lock. Personally, I am OK for that. But I don't know whether there will be some embedded world people don't like it.

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>

maintainer
#7 lore raw

Re: [PATCH v5 2/9] mm/swap: Add cluster lock

Quoted 21 lines · Ying
Quoted 6 lines
Quoted 2 lines
Quoted 2 lines

1MB swap space, so for 1TB swap space, the total size will be 80M compared with 8M of current implementation.

Where did this 80 bytes come from? That swap_cluster_info is 12 bytes and could perhaps be squeezed into 8 bytes if we can get away with a 24-bit "count".

Sorry, I made a mistake when measuring the size of swap_cluster_info when I sent that email, because I turned on the lockdep when measuring. I have sent out a correction email to Jonathan when I realized that later.

So the latest size measuring result is:

If we use bit_spin_lock, the size of cluster_swap_info will,

- increased from 4 bytes to 8 bytes on 64 bit platform
- keep as 4 bytes on 32 bit platform

If we use normal spinlock (queue spinlock), the size of cluster_swap_info will,

Quoted 8 lines

- increased from 4 bytes to 8 bytes on 64 bit platform
- increased from 4 bytes to 8 bytes on 32 bit platform

So the difference occurs on 32 bit platform. If the size increment on 32 bit platform is OK, then I think it should be good to use normal spinlock instead of bit_spin_lock. Personally, I am OK for that. But I don't know whether there will be some embedded world people don't like it.

I think that'll be OK - the difference is small and many small systems disable swap anyway. So can we please try that? Please do describe the additional overhead (with numbers) in the changelog: "additional bytes of RAM per GB of swap", for example. And please also rerun the performance tests, see if we can notice the alleged speed improvements from switching to a spinlock.

--
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>

#8 lore raw

Re: [PATCH v5 2/9] mm/swap: Add cluster lock

Quoted 32 lines · Andrew Morton
Quoted 21 lines · Ying
Quoted 6 lines
Quoted 2 lines
Quoted 2 lines

1MB swap space, so for 1TB swap space, the total size will be 80M compared with 8M of current implementation.

Where did this 80 bytes come from? That swap_cluster_info is 12 bytes and could perhaps be squeezed into 8 bytes if we can get away with a 24-bit "count".

Sorry, I made a mistake when measuring the size of swap_cluster_info when I sent that email, because I turned on the lockdep when measuring. I have sent out a correction email to Jonathan when I realized that later.

So the latest size measuring result is:

If we use bit_spin_lock, the size of cluster_swap_info will,

- increased from 4 bytes to 8 bytes on 64 bit platform
- keep as 4 bytes on 32 bit platform

If we use normal spinlock (queue spinlock), the size of cluster_swap_info will,

Quoted 8 lines

- increased from 4 bytes to 8 bytes on 64 bit platform
- increased from 4 bytes to 8 bytes on 32 bit platform

So the difference occurs on 32 bit platform. If the size increment on 32 bit platform is OK, then I think it should be good to use normal spinlock instead of bit_spin_lock. Personally, I am OK for that. But I don't know whether there will be some embedded world people don't like it.

Quoted 6 lines

I think that'll be OK - the difference is small and many small systems disable swap anyway. So can we please try that? Please do describe the additional overhead (with numbers) in the changelog: "additional bytes of RAM per GB of swap", for example. And please also rerun the performance tests, see if we can notice the alleged speed improvements from switching to a spinlock.

Sure. I will change it and redo the test.

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>

#9 lore raw

PATCH v5 2/9 mm/swap: Add cluster lock

In reply to Andrew Morton

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, a spinlock is added to swap_cluster_info 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.

Because of the added spinlock, the size of swap_cluster_info increases from 4 bytes to 8 bytes on the 64 bit and 32 bit system. This will use additional 4k RAM 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 spinlocks in swap_cluster_info. 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 serious.

Compared with a previous implementation using bit_spin_lock, the sequential swap out throughput improved about 3.2%. Test was done on a Xeon E5 v3 system. The swap device used is a RAM simulated PMEM (persistent memory) device. To test the sequential swapping out, the test case created 32 processes, which sequentially allocate and write to the anonymous pages until the RAM and part of the swap device is used.

---
 include/linux/swap.h |   6 ++
 mm/swapfile.c        | 211 +++++++++++++++++++++++++++++++++++++++++----------
 2 files changed, 177 insertions(+), 40 deletions(-)
include/linux/swap.h +6 -0
index 09f4be179ff3..48e0ed4dc3c8 100644
@@ -175,6 +175,12 @@ enum {
  * protected by swap_info_struct.lock.
  */
 struct swap_cluster_info {
+	spinlock_t lock;	/*
+				 * Protect swap_cluster_info fields
+				 * and swap_info_struct->swap_map
+				 * elements correspond to the swap
+				 * cluster
+				 */
 	unsigned int data:24;
 	unsigned int flags:8;
 };
mm/swapfile.c +171 -40

455 lines not on this page · raw message

Signature · 7 lines
2.11.0

--
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>
#10 lore raw

Re: [PATCH v5 2/9] mm/swap: Add cluster lock

In reply to Jonathan Corbet

Hi, Jonathan,

Quoted 9 lines · Jonathan Corbet
Quoted 4 lines · On Wed, 11 Jan 2017 15:00:29 -0800 Andrew Morton <[email protected]>…

hm, bit_spin_lock() is a nasty thing. It is slow and it doesn't have all the lockdep support.

Would the world end if we added a spinlock to swap_cluster_info?

FWIW, I asked the same question in December, this is what I got:

Sorry I made a mistake in the following email. I have sent another email to correct this before from my another email address, [email protected], have you received it, copied below,

From: huang ying <[email protected]>
Subject: Re: [PATCH v2 2/8] mm/swap: Add cluster lock
To: "Huang, Ying" <[email protected]>
CC: Jonathan Corbet <[email protected]>, Tim Chen <[email protected]>,
Andrew Morton <[email protected]>, <[email protected]>, "Andi
Kleen" <[email protected]>, Aaron Lu <[email protected]>,
<[email protected]>, LKML <[email protected]>, Hugh Dickins <[email protected]>, Shaohua Li <[email protected]>, Minchan Kim <[email protected]>, Rik van Riel <[email protected]>, Andrea Arcangeli <[email protected]>, "Kirill A . Shutemov" <[email protected]>, Vladimir Davydov <[email protected]>, Johannes Weiner <[email protected]>, Michal Hocko <[email protected]>, Hillf
Danton <[email protected]>
Date: Wed, 28 Dec 2016 11:34:01 +0800 (2 weeks, 21 hours, 45 minutes ago)

Hi, Jonathan,

Quoted 14 lines · Ying

Hi, Jonathan,

Thanks for review.

Quoted 8 lines · Jonathan Corbet
Quoted 5 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->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

[...]

Quoted 10 lines
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.

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>

author
#11 lore raw

PATCH v5 3/9 mm/swap: Split swap cache into 64MB trunks

In reply to Tim Chen

From: "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 | 11 +++++++--
 mm/swap.c            |  6 -----
 mm/swap_state.c      | 68 ++++++++++++++++++++++++++++++++++++++++++----------
 mm/swapfile.c        | 16 +++++++++++--
 4 files changed, 79 insertions(+), 22 deletions(-)
include/linux/swap.h +9 -2
index 40716b9..31d89c4 100644
@@ -341,8 +341,13 @@ 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);
@@ -396,6 +401,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
index 844baed..aabf2e9 100644
@@ -971,12 +971,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 +56 -12

91 lines not on this page · raw message

mm/swapfile.c +14 -2
index c3ed37e..6f61fb3 100644
@@ -2075,6 +2075,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)) {
@@ -2398,8 +2399,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,
@@ -2462,7 +2467,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++) {
@@ -2643,6 +2651,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>
maintainer
#12 lore raw

Re: [PATCH v5 3/9] mm/swap: Split swap cache into 64MB trunks

Quoted 16 lines · On Wed, 11 Jan 2017 09:55:13 -0800 Tim Chen <[email protected]> wrote:

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.

Quoted 7 lines

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.

Switching from a single radix-tree to an array of radix-trees to reduce contention seems a bit hacky. That we can do this and have everything continue to work tells me that we're simply using an inappropriate data structure to hold this info.

--
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>

#13 lore raw

Re: [PATCH v5 3/9] mm/swap: Split swap cache into 64MB trunks

Quoted 4 lines

Switching from a single radix-tree to an array of radix-trees to reduce contention seems a bit hacky. That we can do this and have everything continue to work tells me that we're simply using an inappropriate data structure to hold this info.

What would you use instead?

A tree with fine grained locking?

FWIW too fine grained locking (e.g. on every node) is usually a bad idea:

it slows down the single thread performance and it causes much more overhead when there is actual contention because too much time is spent bouncing cache lines around.

So I actually like the "a little bit more fine grained, but not too much" approach.

Or a hash table?

Not sure if this would work here.

-Andi

--
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>

author
#14 lore raw

Re: [PATCH v5 3/9] mm/swap: Split swap cache into 64MB trunks

Quoted 6 lines · Andi Kleen
Quoted 4 lines

Switching from a single radix-tree to an array of radix-trees to reduce contention seems a bit hacky. That we can do this and have everything continue to work tells me that we're simply using an inappropriate data structure to hold this info.

What would you use instead?

I agree that this approach is a bit hacky. However, it is pretty effective and simple. If later on we come up with a better solution to scale modfication of the radix tree, we can collapse the radix trees.

I think developing a scalable radix tree with write modifications will take quite a while and is a non-trivial effort. With almost memory speed SSDs coming on the market soon, I think having a workable solution now and optimizing it for long term is reasonabale.

Tim

author
#15 lore raw

PATCH v5 4/9 mm/swap: skip read ahead for unreferenced swap slots

In reply to Tim Chen

We 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
index 31d89c4..ca66efd 100644
@@ -396,6 +396,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 *);
@@ -490,6 +491,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
index 3863acd..3d76d80 100644
@@ -323,6 +323,10 @@ struct page *__read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask,
 		if (found_page)
 			break;
 
+		/* Just skip read ahead for unused swap slot */
+		if (!__swp_swapcount(entry))
+			return NULL;
+
 		/*
 		 * Get a new page to read into from swap.
 		 */
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>
author
#16 lore raw

PATCH v5 5/9 mm/swap: Allocate swap slots in batches

In reply to Tim Chen

Currently, 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        | 136 +++++++++++++++++++++++++++++++++++++++++----------
 2 files changed, 113 insertions(+), 25 deletions(-)
include/linux/swap.h +2 -0
index ca66efd..980f159 100644
@@ -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 64
 
 static inline int current_is_kswapd(void)
 {
@@ -384,6 +385,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 +111 -25

220 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>
author
#17 lore raw

PATCH v5 6/9 mm/swap: Free swap slots in batch

In reply to Tim Chen

Add 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
index 980f159..f0480c3 100644
@@ -392,6 +392,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>
author
#18 lore raw

PATCH v5 7/9 mm/swap: Add cache for swap slots allocation

In reply to Tim Chen

We 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       |   4 +
 include/linux/swap_slots.h |  28 ++++
 mm/Makefile                |   2 +-
 mm/swap_slots.c            | 333 +++++++++++++++++++++++++++++++++++++++++++++
 mm/swap_state.c            |   1 +
 mm/swapfile.c              |  26 ++--
 6 files changed, 382 insertions(+), 12 deletions(-)
 create mode 100644 include/linux/swap_slots.h
 create mode 100644 mm/swap_slots.c
include/linux/swap.h +4 -0
index f0480c3..b90b17c 100644
@@ -370,6 +370,7 @@ extern struct page *swapin_readahead(swp_entry_t, gfp_t,
 /* linux/mm/swapfile.c */
 extern atomic_long_t nr_swap_pages;
 extern long total_swap_pages;
+extern bool has_usable_swap(void);
 
 /* Swap 50% full? Release swapcache more aggressively.. */
 static inline bool vm_swap_full(void)
@@ -408,6 +409,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 +28 -0
new file mode 100644
index 0000000..a59e6e2
@@ -0,0 +1,28 @@
+#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 {
+	bool		lock_initialized;
+	struct mutex	alloc_lock;
+	swp_entry_t	*slots;
+	int		nr;
+	int		cur;
+	spinlock_t	free_lock;
+	swp_entry_t	*slots_ret;
+	int		n_ret;
+};
+
+void disable_swap_slots_cache_lock(void);
+void reenable_swap_slots_cache_unlock(void);
+int enable_swap_slots_cache(void);
+int free_swap_slot(swp_entry_t entry);
+
+#endif /* _LINUX_SWAP_SLOTS_H */
mm/Makefile +1 -1
index 295bd7a..433eaf9 100644
@@ -35,7 +35,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 +333 -0

333 lines not on this page · raw message

mm/swap_state.c +1 -0
index 3d76d80..e1f07ca 100644
@@ -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 +15 -11
index 50f2688..e6c30ed 100644
@@ -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>
@@ -859,14 +860,6 @@ int get_swap_pages(int n_goal, swp_entry_t swp_entries[])
 	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)
 {
@@ -1057,7 +1050,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(entry);
 	}
 }
 
@@ -1071,7 +1064,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(entry);
 	}
 }
 
@@ -1279,7 +1272,7 @@ int free_swap_and_cache(swp_entry_t entry)
 				page = NULL;
 			}
 		} else if (!count)
-			swapcache_free_entries(&entry, 1);
+			free_swap_slot(entry);
 	}
 	if (page) {
 		/*
@@ -2107,6 +2100,17 @@ static void reinsert_swap_info(struct swap_info_struct *p)
 	spin_unlock(&swap_lock);
 }
 
+bool has_usable_swap(void)
+{
+	bool ret = true;
+
+	spin_lock(&swap_lock);
+	if (plist_head_empty(&swap_active_head))
+		ret = false;
+	spin_unlock(&swap_lock);
+	return ret;
+}
+
 SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
 {
 	struct swap_info_struct *p = 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>
#19 lore raw

PATCH v5 7/9 mm/swap: Add cache for swap slots allocation

Hi, Andrew,

This update patch is to fix the preemption warning raised by Michal Hocko. raw_cpu_ptr() is used to replace this_cpu_ptr() and comments are added for why it is used.

Best Regards,
Huang, Ying

---------------------------------------------->
From: Tim Chen <[email protected]>

We 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       |   4 +
 include/linux/swap_slots.h |  28 ++++
 mm/Makefile                |   2 +-
 mm/swap_slots.c            | 339 +++++++++++++++++++++++++++++++++++++++++++++
 mm/swap_state.c            |   1 +
 mm/swapfile.c              |  26 ++--
 6 files changed, 388 insertions(+), 12 deletions(-)
 create mode 100644 include/linux/swap_slots.h
 create mode 100644 mm/swap_slots.c
include/linux/swap.h +4 -0
index b021478a772d..d101d0e780da 100644
@@ -371,6 +371,7 @@ extern struct page *swapin_readahead(swp_entry_t, gfp_t,
 /* linux/mm/swapfile.c */
 extern atomic_long_t nr_swap_pages;
 extern long total_swap_pages;
+extern bool has_usable_swap(void);
 
 /* Swap 50% full? Release swapcache more aggressively.. */
 static inline bool vm_swap_full(void)
@@ -409,6 +410,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 +28 -0
new file mode 100644
index 000000000000..a59e6e2f2c47
@@ -0,0 +1,28 @@
+#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 {
+	bool		lock_initialized;
+	struct mutex	alloc_lock;
+	swp_entry_t	*slots;
+	int		nr;
+	int		cur;
+	spinlock_t	free_lock;
+	swp_entry_t	*slots_ret;
+	int		n_ret;
+};
+
+void disable_swap_slots_cache_lock(void);
+void reenable_swap_slots_cache_unlock(void);
+int enable_swap_slots_cache(void);
+int free_swap_slot(swp_entry_t entry);
+
+#endif /* _LINUX_SWAP_SLOTS_H */
mm/Makefile +1 -1
index 295bd7a9f76b..433eaf9a876e 100644
@@ -35,7 +35,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 +339 -0

339 lines not on this page · raw message

mm/swap_state.c +1 -0
index 3d76d80c07d6..e1f07cafecaa 100644
@@ -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 +15 -11
index 72dfec7b8035..d4d7baf3c21a 100644
@@ -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>
@@ -859,14 +860,6 @@ int get_swap_pages(int n_goal, swp_entry_t swp_entries[])
 	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)
 {
@@ -1057,7 +1050,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(entry);
 	}
 }
 
@@ -1071,7 +1064,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(entry);
 	}
 }
 
@@ -1279,7 +1272,7 @@ int free_swap_and_cache(swp_entry_t entry)
 				page = NULL;
 			}
 		} else if (!count)
-			swapcache_free_entries(&entry, 1);
+			free_swap_slot(entry);
 	}
 	if (page) {
 		/*
@@ -2107,6 +2100,17 @@ static void reinsert_swap_info(struct swap_info_struct *p)
 	spin_unlock(&swap_lock);
 }
 
+bool has_usable_swap(void)
+{
+	bool ret = true;
+
+	spin_lock(&swap_lock);
+	if (plist_head_empty(&swap_active_head))
+		ret = false;
+	spin_unlock(&swap_lock);
+	return ret;
+}
+
 SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
 {
 	struct swap_info_struct *p = NULL;
Signature · 7 lines
2.11.0

--
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>
#20 lore raw

Re: [Update][PATCH v5 7/9] mm/swap: Add cache for swap slots allocation

On Tue 17-01-17 10:55:47, Huang, Ying wrote:
[...]

Quoted 36 lines
mm/swap_slots.c new file +36 -0
+int free_swap_slot(swp_entry_t entry)
+{
+	struct swap_slots_cache *cache;
+
+	BUG_ON(!swap_slot_cache_initialized);
+
+	cache = &get_cpu_var(swp_slots);
+	if (use_swap_slot_cache && cache->slots_ret) {
+		spin_lock_irq(&cache->free_lock);
+		/* Swap slots cache may be deactivated before acquiring lock */
+		if (!use_swap_slot_cache) {
+			spin_unlock_irq(&cache->free_lock);
+			goto direct_free;
+		}
+		if (cache->n_ret >= SWAP_SLOTS_CACHE_SIZE) {
+			/*
+			 * Return slots to global pool.
+			 * The current swap_map value is SWAP_HAS_CACHE.
+			 * Set it to 0 to indicate it is available for
+			 * allocation in global pool
+			 */
+			swapcache_free_entries(cache->slots_ret, cache->n_ret);
+			cache->n_ret = 0;
+		}
+		cache->slots_ret[cache->n_ret++] = entry;
+		spin_unlock_irq(&cache->free_lock);
+	} else {
+direct_free:
+		swapcache_free_entries(&entry, 1);
+	}
+	put_cpu_var(swp_slots);
+
+	return 0;
+}
+
+swp_entry_t get_swap_page(void)

mm/swap_slots.c line 311

Quoted 10 lines
mm/swap_slots.c new file +10 -0
+{
+	swp_entry_t entry, *pentry;
+	struct swap_slots_cache *cache;
+
+	/*
+	 * Preemption need to be turned on here, because we may sleep
+	 * in refill_swap_slots_cache().  But it is safe, because
+	 * accesses to the per-CPU data structure are protected by a
+	 * mutex.
+	 */

the comment doesn't really explain why it is safe. THere are other users which are not using the lock. E.g. just look at free_swap_slot above. How can

	cache->slots_ret[cache->n_ret++] = entry;
be safe wrt.
	pentry = &cache->slots[cache->cur++];
	entry = *pentry;

Both of them might touch the same slot, no? Btw. I would rather prefer this would be a follow up fix with the trace and the detailed explanation.

Quoted 26 lines
mm/swap_slots.c new file +26 -0
+	cache = raw_cpu_ptr(&swp_slots);
+
+	entry.val = 0;
+	if (check_cache_active()) {
+		mutex_lock(&cache->alloc_lock);
+		if (cache->slots) {
+repeat:
+			if (cache->nr) {
+				pentry = &cache->slots[cache->cur++];
+				entry = *pentry;
+				pentry->val = 0;
+				cache->nr--;
+			} else {
+				if (refill_swap_slots_cache(cache))
+					goto repeat;
+			}
+		}
+		mutex_unlock(&cache->alloc_lock);
+		if (entry.val)
+			return entry;
+	}
+
+	get_swap_pages(1, &entry);
+
+	return entry;
+}
Signature · 8 lines
Michal Hocko
SUSE Labs

--
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>
#21 lore raw

RE: [Update][PATCH v5 7/9] mm/swap: Add cache for swap slots allocation

Quoted 10 lines

mm/swap_slots.c line 311

Quoted 6 lines
mm/swap_slots.c new file +6 -0
+	/*
+	 * Preemption need to be turned on here, because we may sleep
+	 * in refill_swap_slots_cache().  But it is safe, because
+	 * accesses to the per-CPU data structure are protected by a
+	 * mutex.
+	 */

the comment doesn't really explain why it is safe. THere are other users which are not using the lock. E.g. just look at free_swap_slot above. How can

Quoted 7 lines
	cache->slots_ret[cache->n_ret++] = entry; be safe wrt.
	pentry = &cache->slots[cache->cur++];
	entry = *pentry;

Both of them might touch the same slot, no? Btw. I would rather prefer this would be a follow up fix with the trace and the detailed explanation.

The cache->slots_ret is protected by cache->free_lock and cache->slots is protected by cache->free_lock. They are two separate structures, one for caching the slots returned and one for caching the slots allocated. So they do no touch the same slots. We'll update the comments so it is clearer.

Sure. We can issue a follow up fix on top of the current patchset.

Thanks.

Tim

--
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>

#22 lore raw

Re: [Update][PATCH v5 7/9] mm/swap: Add cache for swap slots allocation

Quoted 14 lines · Tim C
Quoted 14 lines

mm/swap_slots.c line 311

Quoted 6 lines
mm/swap_slots.c new file +6 -0
+	/*
+	 * Preemption need to be turned on here, because we may sleep
+	 * in refill_swap_slots_cache().  But it is safe, because
+	 * accesses to the per-CPU data structure are protected by a
+	 * mutex.
+	 */

the comment doesn't really explain why it is safe. THere are other users which are not using the lock. E.g. just look at free_swap_slot above. How can

	cache->slots_ret[cache->n_ret++] = entry; be safe wrt.
	pentry = &cache->slots[cache->cur++];
	entry = *pentry;
Quoted 6 lines
Quoted 3 lines

Both of them might touch the same slot, no? Btw. I would rather prefer this would be a follow up fix with the trace and the detailed explanation.

The cache->slots_ret is protected by cache->free_lock and cache->slots is protected by cache->free_lock.

Ohh, I have misread those names and considered them the same thing. Sorry about the confusion. I will look at code more deeply tomorrow.

Quoted 3 lines

They are two separate structures, one for
caching the slots returned and one for caching the slots allocated. So they do no touch the same slots. We'll update the comments so it is clearer.

That would be really appreciated.

Signature · 8 lines
Michal Hocko
SUSE Labs

--
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>
#23 lore raw

RE: [Update][PATCH v5 7/9] mm/swap: Add cache for swap slots allocation

Quoted 4 lines
Quoted 4 lines
Quoted 1 line

The cache->slots_ret is protected by cache->free_lock and cache->slots is protected by cache->free_lock.

Typo. cache->slots is protected by cache->alloc_lock.

Tim

--
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>

author
#24 lore raw

Re: [Update][PATCH v5 7/9] mm/swap: Add cache for swap slots allocation

In reply to Huang, Ying
Quoted 6 lines · Ying

Hi, Andrew,

This update patch is to fix the preemption warning raised by Michal Hocko. raw_cpu_ptr() is used to replace this_cpu_ptr() and comments are added for why it is used.

Andrew & Michal,

Here's a fix that's a follow on patch instead of an updated patch as Michal has suggested. I've updated the comments a bit to make it clearer.

Thanks.

Tim

--->8---
Date: Tue, 17 Jan 2017 12:57:00 -0800
Subject: [PATCH] mm/swap: Use raw_cpu_ptr over this_cpu_ptr for swap slots
access
To: Andrew Morton <[email protected]>
Cc: Ying Huang <[email protected]>, [email protected], [email protected], [email protected], [email protected], [email protected], Hugh Dickins <[email protected]>, Shaohua Li <[email protected]>, Minchan Kim <[email protected]>, Rik van Riel <[email protected]>, Andrea Arcangeli <[email protected]>, Kirill A . Shutemov <[email protected]>, Vladimir Davydov <[email protected]>, Johannes Weiner <[email protected]>, Michal Hocko <[email protected]>, Hillf Danton <[email protected]>, Christian Borntraeger <[email protected]>, Jonathan Corbet <[email protected]>

From: "Huang, Ying" <[email protected]>

The usage of this_cpu_ptr in get_swap_page causes a bug warning as it is used in pre-emptible code.

[   57.812314] BUG: using smp_processor_id() in preemptible [00000000] code: kswapd0/527
[   57.814360] caller is debug_smp_processor_id+0x17/0x19
[   57.815237] CPU: 1 PID: 527 Comm: kswapd0 Tainted: G        W 4.9.0-mmotm-00135-g4e9a9895ebef #1042
[   57.816019] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.1-1 04/01/2014
[   57.816019]  ffffc900001939c0 ffffffff81329c60 0000000000000001 ffffffff81a0ce06
[   57.816019]  ffffc900001939f0 ffffffff81343c2a 00000000000137a0 ffffea0000dfd2a0
[   57.816019]  ffff88003c49a700 ffffc90000193b10 ffffc90000193a00 ffffffff81343c53
[   57.816019] Call Trace:
[   57.816019]  [<ffffffff81329c60>] dump_stack+0x68/0x92
[   57.816019]  [<ffffffff81343c2a>] check_preemption_disabled+0xce/0xe0
[   57.816019]  [<ffffffff81343c53>] debug_smp_processor_id+0x17/0x19
[   57.816019]  [<ffffffff8115f06f>] get_swap_page+0x19/0x183
[   57.816019]  [<ffffffff8114e01d>] shmem_writepage+0xce/0x38c
[   57.816019]  [<ffffffff81148916>] shrink_page_list+0x81f/0xdbf
[   57.816019]  [<ffffffff81149652>] shrink_inactive_list+0x2ab/0x594
[   57.816019]  [<ffffffff8114a22f>] shrink_node_memcg+0x4c7/0x673
[   57.816019]  [<ffffffff8114a49f>] shrink_node+0xc4/0x282
[   57.816019]  [<ffffffff8114a49f>] ? shrink_node+0xc4/0x282
[   57.816019]  [<ffffffff8114b8cb>] kswapd+0x656/0x834

Logic wise, We do allow pre-emption as per cpu ptr cache->slots is protected by the mutex cache->alloc_lock. We switch the inappropriately used this_cpu_ptr to raw_cpu_ptr for per cpu ptr access of cache->slots.

---
 mm/swap_slots.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)
mm/swap_slots.c +10 -1
index 8cf941e..9b5bc86 100644
@@ -303,7 +303,16 @@ swp_entry_t get_swap_page(void)
 	swp_entry_t entry, *pentry;
 	struct swap_slots_cache *cache;
 
-	cache = this_cpu_ptr(&swp_slots);
+	/*
+	 * Preemption is allowed here, because we may sleep
+	 * in refill_swap_slots_cache().  But it is safe, because
+	 * accesses to the per-CPU data structure are protected by the
+	 * mutex cache->alloc_lock.
+	 *
+	 * The alloc path here does not touch cache->slots_ret
+	 * so cache->free_lock is not taken.
+	 */
+	cache = raw_cpu_ptr(&swp_slots);
 
 	entry.val = 0;
 	if (check_cache_active()) {
Signature · 1 line
2.5.5
Reviewed-by
#25 lore raw

Re: [Update][PATCH v5 7/9] mm/swap: Add cache for swap slots allocation

On Tue 17-01-17 13:42:35, Tim Chen wrote:
[...]

Quoted 31 lines

Date: Tue, 17 Jan 2017 12:57:00 -0800
Subject: [PATCH] mm/swap: Use raw_cpu_ptr over this_cpu_ptr for swap slots
access
To: Andrew Morton <[email protected]>
Cc: Ying Huang <[email protected]>, [email protected], [email protected], [email protected], [email protected], [email protected], Hugh Dickins <[email protected]>, Shaohua Li <[email protected]>, Minchan Kim <[email protected]>, Rik van Riel <[email protected]>, Andrea Arcangeli <[email protected]>, Kirill A . Shutemov <[email protected]>, Vladimir Davydov <[email protected]>, Johannes Weiner <[email protected]>, Michal Hocko <[email protected]>, Hillf Danton <[email protected]>, Christian Borntraeger <[email protected]>, Jonathan Corbet <[email protected]>

From: "Huang, Ying" <[email protected]>

The usage of this_cpu_ptr in get_swap_page causes a bug warning as it is used in pre-emptible code.

[   57.812314] BUG: using smp_processor_id() in preemptible [00000000] code: kswapd0/527
[   57.814360] caller is debug_smp_processor_id+0x17/0x19
[   57.815237] CPU: 1 PID: 527 Comm: kswapd0 Tainted: G        W 4.9.0-mmotm-00135-g4e9a9895ebef #1042
[   57.816019] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.1-1 04/01/2014
[   57.816019]  ffffc900001939c0 ffffffff81329c60 0000000000000001 ffffffff81a0ce06
[   57.816019]  ffffc900001939f0 ffffffff81343c2a 00000000000137a0 ffffea0000dfd2a0
[   57.816019]  ffff88003c49a700 ffffc90000193b10 ffffc90000193a00 ffffffff81343c53
[   57.816019] Call Trace:
[   57.816019]  [<ffffffff81329c60>] dump_stack+0x68/0x92
[   57.816019]  [<ffffffff81343c2a>] check_preemption_disabled+0xce/0xe0
[   57.816019]  [<ffffffff81343c53>] debug_smp_processor_id+0x17/0x19
[   57.816019]  [<ffffffff8115f06f>] get_swap_page+0x19/0x183
[   57.816019]  [<ffffffff8114e01d>] shmem_writepage+0xce/0x38c
[   57.816019]  [<ffffffff81148916>] shrink_page_list+0x81f/0xdbf
[   57.816019]  [<ffffffff81149652>] shrink_inactive_list+0x2ab/0x594
[   57.816019]  [<ffffffff8114a22f>] shrink_node_memcg+0x4c7/0x673
[   57.816019]  [<ffffffff8114a49f>] shrink_node+0xc4/0x282
[   57.816019]  [<ffffffff8114a49f>] ? shrink_node+0xc4/0x282
[   57.816019]  [<ffffffff8114b8cb>] kswapd+0x656/0x834
Quoted 4 lines

Logic wise, We do allow pre-emption as per cpu ptr cache->slots is protected by the mutex cache->alloc_lock. We switch the inappropriately used this_cpu_ptr to raw_cpu_ptr for per cpu ptr access of cache->slots.

OK, that looks better. I would still appreciate something like the following folded in

include/linux/swap_slots.h +1 -0
index fb907346c5c6..0afe748453a7 100644
@@ -11,6 +11,7 @@
 
 struct swap_slots_cache {
 	bool		lock_initialized;
+	/* protects slots, nr, cur */
 	struct mutex	alloc_lock;
 	swp_entry_t	*slots;
 	int		nr;
Quoted 4 lines

Thanks!

Quoted 29 lines
---
 mm/swap_slots.c | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)
mm/swap_slots.c +10 -1
index 8cf941e..9b5bc86 100644
@@ -303,7 +303,16 @@ swp_entry_t get_swap_page(void)
 	swp_entry_t entry, *pentry;
 	struct swap_slots_cache *cache;
 
-	cache = this_cpu_ptr(&swp_slots);
+	/*
+	 * Preemption is allowed here, because we may sleep
+	 * in refill_swap_slots_cache().  But it is safe, because
+	 * accesses to the per-CPU data structure are protected by the
+	 * mutex cache->alloc_lock.
+	 *
+	 * The alloc path here does not touch cache->slots_ret
+	 * so cache->free_lock is not taken.
+	 */
+	cache = raw_cpu_ptr(&swp_slots);
 
 	entry.val = 0;
 	if (check_cache_active()) {
Signature · 1 line
2.5.5
Signature · 8 lines
Michal Hocko
SUSE Labs

--
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>
author
#26 lore raw

Re: [Update][PATCH v5 7/9] mm/swap: Add cache for swap slots allocation

Quoted 15 lines · Michal Hocko

On Tue 17-01-17 13:42:35, Tim Chen wrote:
[...]

Quoted 4 lines

Logic wise, We do allow pre-emption as per cpu ptr cache->slots is protected by the mutex cache->alloc_lock. We switch the inappropriately used this_cpu_ptr to raw_cpu_ptr for per cpu ptr access of cache->slots.

OK, that looks better. I would still appreciate something like the following folded in

include/linux/swap_slots.h +0 -0
index fb907346c5c6..0afe748453a7 100644

include/linux/swap_slots.h line 17

Quoted 7 lines
include/linux/swap_slots.h +1 -0
@@ -11,6 +11,7 @@
 
 struct swap_slots_cache {
 	bool		lock_initialized;
+	/* protects slots, nr, cur */
 	struct mutex	alloc_lock;
 	swp_entry_t	*slots;
 	int		nr;

I've included here a patch for the comments.

Thanks.

Tim

--->8---
From: Tim Chen <[email protected]>
Date: Wed, 18 Jan 2017 09:52:28 -0800
Subject: [PATCH] mm/swap: Add comments on locks in swap_slots.h
To: Andrew Morton <[email protected]>
Cc: Ying Huang <[email protected]>, [email protected], [email protected], [email protected], [email protected], [email protected], Hugh Dickins <[email protected]>, Shaohua Li <[email protected]>, Minchan Kim <[email protected]>, Rik van Riel <[email protected]>, Andrea Arcangeli <[email protected]>, Kirill A . Shutemov <[email protected]>, Vladimir Davydov <[email protected]>, Johannes Weiner <[email protected]>, Michal Hocko <[email protected]>, Hillf Danton <[email protected]>, Christian Borntraeger <[email protected]>, Jonathan Corbet <[email protected]>

Explains what each lock protects in swap_slots_cache structure.

---
 include/linux/swap_slots.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
include/linux/swap_slots.h +2 -2
index fb90734..6ef92d1 100644
@@ -11,11 +11,11 @@
 
 struct swap_slots_cache {
 	bool		lock_initialized;
-	struct mutex	alloc_lock;
+	struct mutex	alloc_lock; /* protects slots, nr, cur */
 	swp_entry_t	*slots;
 	int		nr;
 	int		cur;
-	spinlock_t	free_lock;
+	spinlock_t	free_lock;  /* protects slots_ret, n_ret */
 	swp_entry_t	*slots_ret;
 	int		n_ret;
 };
Signature · 1 line
2.5.5
#27 lore raw

Re: [Update][PATCH v5 7/9] mm/swap: Add cache for swap slots allocation

Quoted 16 lines · Tim Chen
Quoted 15 lines · Michal Hocko

On Tue 17-01-17 13:42:35, Tim Chen wrote:
[...]

Quoted 4 lines

Logic wise, We do allow pre-emption as per cpu ptr cache->slots is protected by the mutex cache->alloc_lock. We switch the inappropriately used this_cpu_ptr to raw_cpu_ptr for per cpu ptr access of cache->slots.

OK, that looks better. I would still appreciate something like the following folded in

include/linux/swap_slots.h +0 -0
index fb907346c5c6..0afe748453a7 100644
@@ -11,6 +11,7 @@
 
Quoted 9 lines

include/linux/swap_slots.h line 16

Quoted 7 lines
include/linux/swap_slots.h +1 -0
 struct swap_slots_cache {
 	bool		lock_initialized;
+	/* protects slots, nr, cur */
 	struct mutex	alloc_lock;
 	swp_entry_t	*slots;
 	int		nr;

I've included here a patch for the comments.

Thanks!

Signature · 8 lines
Michal Hocko
SUSE Labs

--
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>
author
#28 lore raw

PATCH v5 8/9 mm/swap: Enable swap slots cache usage

In reply to Tim Chen

Initialize swap slots cache and enable it on swap on. Drain all swap slots on swap off.

---
 mm/swapfile.c | 7 +++++++
 1 file changed, 7 insertions(+)
mm/swapfile.c +7 -0
index e6c30ed..14d9ea2 100644
@@ -2181,6 +2181,8 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
 	spin_unlock(&p->lock);
 	spin_unlock(&swap_lock);
 
+	disable_swap_slots_cache_lock();
+
 	set_current_oom_origin();
 	err = try_to_unuse(p->type, false, 0); /* force unuse all pages */
 	clear_current_oom_origin();
@@ -2188,9 +2190,12 @@ SYSCALL_DEFINE1(swapoff, const char __user *, specialfile)
 	if (err) {
 		/* re-insert swap space back into swap_list */
 		reinsert_swap_info(p);
+		reenable_swap_slots_cache_unlock();
 		goto out_dput;
 	}
 
+	reenable_swap_slots_cache_unlock();
+
 	flush_work(&p->discard_work);
 
 	destroy_swap_extents(p);
@@ -2868,6 +2873,8 @@ SYSCALL_DEFINE2(swapon, const char __user *, specialfile, int, swap_flags)
 		putname(name);
 	if (inode && S_ISREG(inode->i_mode))
 		inode_unlock(inode);
+	if (!error)
+		enable_swap_slots_cache();
 	return error;
 }
 
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>
author
#29 lore raw

PATCH v5 9/9 mm/swap: Skip readahead only when swap slot cache is enabled

In reply to Tim Chen

From: Huang Ying <[email protected]>

Because during swap off, a swap entry may have swap_map[] == SWAP_HAS_CACHE (for example, just allocated). If we return NULL in __read_swap_cache_async(), the swap off will abort. So when swap slot cache is disabled, (for swap off), we will wait for page to be put into swap cache in such race condition. This should not be a problem for swap slot cache, because swap slot cache should be drained after clearing swap_slot_cache_enabled.

---
 include/linux/swap_slots.h |  2 ++
 mm/swap_slots.c            |  2 +-
 mm/swap_state.c            | 11 +++++++++--
 3 files changed, 12 insertions(+), 3 deletions(-)
include/linux/swap_slots.h +2 -0
index a59e6e2..fb90734 100644
@@ -25,4 +25,6 @@ void reenable_swap_slots_cache_unlock(void);
 int enable_swap_slots_cache(void);
 int free_swap_slot(swp_entry_t entry);
 
+extern bool swap_slot_cache_enabled;
+
 #endif /* _LINUX_SWAP_SLOTS_H */
mm/swap_slots.c +1 -1
index b816839..8cf941e 100644
@@ -36,7 +36,7 @@
 
 static DEFINE_PER_CPU(struct swap_slots_cache, swp_slots);
 static bool	swap_slot_cache_active;
-static bool	swap_slot_cache_enabled;
+bool	swap_slot_cache_enabled;
 static bool	swap_slot_cache_initialized;
 DEFINE_MUTEX(swap_slots_cache_mutex);
 /* Serialize swap slots cache enable/disable operations */
mm/swap_state.c +9 -2
index e1f07ca..2126e9b 100644
@@ -324,8 +324,15 @@ struct page *__read_swap_cache_async(swp_entry_t entry, gfp_t gfp_mask,
 		if (found_page)
 			break;
 
-		/* Just skip read ahead for unused swap slot */
-		if (!__swp_swapcount(entry))
+		/*
+		 * Just skip read ahead for unused swap slot.
+		 * During swap_off when swap_slot_cache is disabled,
+		 * we have to handle the race between putting
+		 * swap entry in swap cache and marking swap slot
+		 * as SWAP_HAS_CACHE.  That's done in later part of code or
+		 * else swap_off will be aborted if we return NULL.
+		 */
+		if (!__swp_swapcount(entry) && swap_slot_cache_enabled)
 			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>
#30 lore raw
In reply to Tim Chen

Hi,
I am seeing a lot of preempt unsafe warnings with the current mmotm and I assume that this patchset has introduced the issue. I haven't checked more closely but get_swap_page didn't use this_cpu_ptr before "mm/swap:
add cache for swap slots allocation"

[   57.812314] BUG: using smp_processor_id() in preemptible [00000000] code: kswapd0/527
[   57.814360] caller is debug_smp_processor_id+0x17/0x19
[   57.815237] CPU: 1 PID: 527 Comm: kswapd0 Tainted: G        W 4.9.0-mmotm-00135-g4e9a9895ebef #1042
[   57.816019] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.1-1 04/01/2014
[   57.816019]  ffffc900001939c0 ffffffff81329c60 0000000000000001 ffffffff81a0ce06
[   57.816019]  ffffc900001939f0 ffffffff81343c2a 00000000000137a0 ffffea0000dfd2a0
[   57.816019]  ffff88003c49a700 ffffc90000193b10 ffffc90000193a00 ffffffff81343c53
[   57.816019] Call Trace:
[   57.816019]  [<ffffffff81329c60>] dump_stack+0x68/0x92
[   57.816019]  [<ffffffff81343c2a>] check_preemption_disabled+0xce/0xe0
[   57.816019]  [<ffffffff81343c53>] debug_smp_processor_id+0x17/0x19
[   57.816019]  [<ffffffff8115f06f>] get_swap_page+0x19/0x183
[   57.816019]  [<ffffffff8114e01d>] shmem_writepage+0xce/0x38c
[   57.816019]  [<ffffffff81148916>] shrink_page_list+0x81f/0xdbf
[   57.816019]  [<ffffffff81149652>] shrink_inactive_list+0x2ab/0x594
[   57.816019]  [<ffffffff8114a22f>] shrink_node_memcg+0x4c7/0x673
[   57.816019]  [<ffffffff8114a49f>] shrink_node+0xc4/0x282
[   57.816019]  [<ffffffff8114a49f>] ? shrink_node+0xc4/0x282
[   57.816019]  [<ffffffff8114b8cb>] kswapd+0x656/0x834
[   57.816019]  [<ffffffff8114b275>] ? mem_cgroup_shrink_node+0x2e1/0x2e1
[   57.816019]  [<ffffffff81069fb4>] ? call_usermodehelper_exec_async+0x124/0x12d
[   57.816019]  [<ffffffff81073621>] kthread+0xf9/0x101
[   57.816019]  [<ffffffff81660198>] ? _raw_spin_unlock_irq+0x2c/0x4a
[   57.816019]  [<ffffffff81073528>] ? kthread_park+0x5a/0x5a
[   57.816019]  [<ffffffff81069e90>] ? umh_complete+0x25/0x25
[   57.816019]  [<ffffffff81660b07>] ret_from_fork+0x27/0x40

I thought a simple

mm/swap_slots.c +4 -2
index 8cf941e09941..732194de58a4 100644
@@ -303,7 +303,7 @@ swp_entry_t get_swap_page(void)
 	swp_entry_t entry, *pentry;
 	struct swap_slots_cache *cache;
 
-	cache = this_cpu_ptr(&swp_slots);
+	cache = &get_cpu_var(swp_slots);
 
 	entry.val = 0;
 	if (check_cache_active()) {
@@ -322,11 +322,13 @@ swp_entry_t get_swap_page(void)
 		}
 		mutex_unlock(&cache->alloc_lock);
 		if (entry.val)
-			return entry;
+			goto out;
 	}
 
 	get_swap_pages(1, &entry);
 
+out:
+	put_cpu_var(swp_slots);
 	return entry;
 }
 

would be a way to go but the function takes a sleeping lock so disabling the preemption is not a way forward. So this is either preempt safe for some reason - which should be IMHO documented in a comment - and raw_cpu_ptr can be used or this needs a deeper thought.

Signature · 8 lines
Michal Hocko
SUSE Labs

--
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>
#31 lore raw
Quoted 24 lines · Michal Hocko

Hi,
I am seeing a lot of preempt unsafe warnings with the current mmotm and I assume that this patchset has introduced the issue. I haven't checked more closely but get_swap_page didn't use this_cpu_ptr before "mm/swap:
add cache for swap slots allocation"

[   57.812314] BUG: using smp_processor_id() in preemptible [00000000] code: kswapd0/527
[   57.814360] caller is debug_smp_processor_id+0x17/0x19
[   57.815237] CPU: 1 PID: 527 Comm: kswapd0 Tainted: G        W 4.9.0-mmotm-00135-g4e9a9895ebef #1042
[   57.816019] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.1-1 04/01/2014
[   57.816019]  ffffc900001939c0 ffffffff81329c60 0000000000000001 ffffffff81a0ce06
[   57.816019]  ffffc900001939f0 ffffffff81343c2a 00000000000137a0 ffffea0000dfd2a0
[   57.816019]  ffff88003c49a700 ffffc90000193b10 ffffc90000193a00 ffffffff81343c53
[   57.816019] Call Trace:
[   57.816019]  [<ffffffff81329c60>] dump_stack+0x68/0x92
[   57.816019]  [<ffffffff81343c2a>] check_preemption_disabled+0xce/0xe0
[   57.816019]  [<ffffffff81343c53>] debug_smp_processor_id+0x17/0x19
[   57.816019]  [<ffffffff8115f06f>] get_swap_page+0x19/0x183
[   57.816019]  [<ffffffff8114e01d>] shmem_writepage+0xce/0x38c
[   57.816019]  [<ffffffff81148916>] shrink_page_list+0x81f/0xdbf
[   57.816019]  [<ffffffff81149652>] shrink_inactive_list+0x2ab/0x594
[   57.816019]  [<ffffffff8114a22f>] shrink_node_memcg+0x4c7/0x673
[   57.816019]  [<ffffffff8114a49f>] shrink_node+0xc4/0x282
[   57.816019]  [<ffffffff8114a49f>] ? shrink_node+0xc4/0x282
Quoted 8 lines
[   57.816019]  [<ffffffff8114b8cb>] kswapd+0x656/0x834
[   57.816019]  [<ffffffff8114b275>] ? mem_cgroup_shrink_node+0x2e1/0x2e1
[   57.816019]  [<ffffffff81069fb4>] ? call_usermodehelper_exec_async+0x124/0x12d
[   57.816019]  [<ffffffff81073621>] kthread+0xf9/0x101
[   57.816019]  [<ffffffff81660198>] ? _raw_spin_unlock_irq+0x2c/0x4a
[   57.816019]  [<ffffffff81073528>] ? kthread_park+0x5a/0x5a
[   57.816019]  [<ffffffff81069e90>] ? umh_complete+0x25/0x25
[   57.816019]  [<ffffffff81660b07>] ret_from_fork+0x27/0x40

Sorry for bothering, we should have tested this before.

Quoted 30 lines

I thought a simple

mm/swap_slots.c +4 -2
index 8cf941e09941..732194de58a4 100644
@@ -303,7 +303,7 @@ swp_entry_t get_swap_page(void)
 	swp_entry_t entry, *pentry;
 	struct swap_slots_cache *cache;
 
-	cache = this_cpu_ptr(&swp_slots);
+	cache = &get_cpu_var(swp_slots);
 
 	entry.val = 0;
 	if (check_cache_active()) {
@@ -322,11 +322,13 @@ swp_entry_t get_swap_page(void)
 		}
 		mutex_unlock(&cache->alloc_lock);
 		if (entry.val)
-			return entry;
+			goto out;
 	}
 
 	get_swap_pages(1, &entry);
 
+out:
+	put_cpu_var(swp_slots);
 	return entry;
 }
 
Quoted 4 lines

would be a way to go but the function takes a sleeping lock so disabling the preemption is not a way forward. So this is either preempt safe for some reason - which should be IMHO documented in a comment - and raw_cpu_ptr can be used or this needs a deeper thought.

Thanks for pointing out this.

We think this is preempt safe. During the development, we have considered the possible preemption between getting the per-CPU pointer and its usage, and implemented the code to make it work at that situation. We will change the code to use raw_cpu_ptr() and add a comment for it.

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>

#32 lore raw
Quoted 35 lines · Ying
Quoted 24 lines · Michal Hocko

Hi,
I am seeing a lot of preempt unsafe warnings with the current mmotm and I assume that this patchset has introduced the issue. I haven't checked more closely but get_swap_page didn't use this_cpu_ptr before "mm/swap:
add cache for swap slots allocation"

[   57.812314] BUG: using smp_processor_id() in preemptible [00000000] code: kswapd0/527
[   57.814360] caller is debug_smp_processor_id+0x17/0x19
[   57.815237] CPU: 1 PID: 527 Comm: kswapd0 Tainted: G        W 4.9.0-mmotm-00135-g4e9a9895ebef #1042
[   57.816019] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.10.1-1 04/01/2014
[   57.816019]  ffffc900001939c0 ffffffff81329c60 0000000000000001 ffffffff81a0ce06
[   57.816019]  ffffc900001939f0 ffffffff81343c2a 00000000000137a0 ffffea0000dfd2a0
[   57.816019]  ffff88003c49a700 ffffc90000193b10 ffffc90000193a00 ffffffff81343c53
[   57.816019] Call Trace:
[   57.816019]  [<ffffffff81329c60>] dump_stack+0x68/0x92
[   57.816019]  [<ffffffff81343c2a>] check_preemption_disabled+0xce/0xe0
[   57.816019]  [<ffffffff81343c53>] debug_smp_processor_id+0x17/0x19
[   57.816019]  [<ffffffff8115f06f>] get_swap_page+0x19/0x183
[   57.816019]  [<ffffffff8114e01d>] shmem_writepage+0xce/0x38c
[   57.816019]  [<ffffffff81148916>] shrink_page_list+0x81f/0xdbf
[   57.816019]  [<ffffffff81149652>] shrink_inactive_list+0x2ab/0x594
[   57.816019]  [<ffffffff8114a22f>] shrink_node_memcg+0x4c7/0x673
[   57.816019]  [<ffffffff8114a49f>] shrink_node+0xc4/0x282
[   57.816019]  [<ffffffff8114a49f>] ? shrink_node+0xc4/0x282
Quoted 8 lines
[   57.816019]  [<ffffffff8114b8cb>] kswapd+0x656/0x834
[   57.816019]  [<ffffffff8114b275>] ? mem_cgroup_shrink_node+0x2e1/0x2e1
[   57.816019]  [<ffffffff81069fb4>] ? call_usermodehelper_exec_async+0x124/0x12d
[   57.816019]  [<ffffffff81073621>] kthread+0xf9/0x101
[   57.816019]  [<ffffffff81660198>] ? _raw_spin_unlock_irq+0x2c/0x4a
[   57.816019]  [<ffffffff81073528>] ? kthread_park+0x5a/0x5a
[   57.816019]  [<ffffffff81069e90>] ? umh_complete+0x25/0x25
[   57.816019]  [<ffffffff81660b07>] ret_from_fork+0x27/0x40
Quoted 1 line

Sorry for bothering, we should have tested this before.

I am always running my tests with CONFIG_DEBUG_PREEMPT=y which is what has caught this one.

[...]

Quoted 5 lines
Quoted 4 lines

would be a way to go but the function takes a sleeping lock so disabling the preemption is not a way forward. So this is either preempt safe for some reason - which should be IMHO documented in a comment - and raw_cpu_ptr can be used or this needs a deeper thought.

Quoted 7 lines

Thanks for pointing out this.

We think this is preempt safe. During the development, we have considered the possible preemption between getting the per-CPU pointer and its usage, and implemented the code to make it work at that situation. We will change the code to use raw_cpu_ptr() and add a comment for it.

FWIW s@this_cpu_ptr@raw_cpu_ptr@ which I am using as a workaround now hasn't seemed to cause any issue. At least nothing observable like a crash.

Thanks!

Signature · 8 lines
Michal Hocko
SUSE Labs

--
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>