summaryrefslogtreecommitdiffstats
path: root/mm/swapfile.c
diff options
context:
space:
mode:
authorTim Chen <tim.c.chen@linux.intel.com>2017-02-22 18:45:36 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2017-02-22 19:41:30 -0500
commit7c00bafee87c7bac7ed9eced7c161f8e5332cb4e (patch)
tree05833c1d7a5c40bcee488a19d72292b3fab06f5c /mm/swapfile.c
parent36005bae205da3eef0016a5c96a34f10a68afa1e (diff)
mm/swap: free swap slots in batch
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. Link: http://lkml.kernel.org/r/c25e0fcdfd237ec4ca7db91631d3b9f6ed23824e.1484082593.git.tim.c.chen@linux.intel.com Signed-off-by: Tim Chen <tim.c.chen@linux.intel.com> Signed-off-by: "Huang, Ying" <ying.huang@intel.com> Cc: Aaron Lu <aaron.lu@intel.com> Cc: Andi Kleen <ak@linux.intel.com> Cc: Andrea Arcangeli <aarcange@redhat.com> Cc: Christian Borntraeger <borntraeger@de.ibm.com> Cc: Dave Hansen <dave.hansen@intel.com> Cc: Hillf Danton <hillf.zj@alibaba-inc.com> Cc: Huang Ying <ying.huang@intel.com> Cc: Hugh Dickins <hughd@google.com> Cc: Johannes Weiner <hannes@cmpxchg.org> Cc: Jonathan Corbet <corbet@lwn.net> escreveu: Cc: Kirill A. Shutemov <kirill.shutemov@linux.intel.com> Cc: Michal Hocko <mhocko@kernel.org> Cc: Minchan Kim <minchan@kernel.org> Cc: Rik van Riel <riel@redhat.com> Cc: Shaohua Li <shli@kernel.org> Cc: Vladimir Davydov <vdavydov.dev@gmail.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/swapfile.c')
-rw-r--r--mm/swapfile.c155
1 files changed, 94 insertions, 61 deletions
diff --git a/mm/swapfile.c b/mm/swapfile.c
index e73b5441055b..8b5bd34b1a00 100644
--- a/mm/swapfile.c
+++ b/mm/swapfile.c
@@ -942,35 +942,34 @@ static struct swap_info_struct *swap_info_get(swp_entry_t entry)
942 return p; 942 return p;
943} 943}
944 944
945static unsigned char swap_entry_free(struct swap_info_struct *p, 945static struct swap_info_struct *swap_info_get_cont(swp_entry_t entry,
946 swp_entry_t entry, unsigned char usage, 946 struct swap_info_struct *q)
947 bool swap_info_locked) 947{
948 struct swap_info_struct *p;
949
950 p = _swap_info_get(entry);
951
952 if (p != q) {
953 if (q != NULL)
954 spin_unlock(&q->lock);
955 if (p != NULL)
956 spin_lock(&p->lock);
957 }
958 return p;
959}
960
961static unsigned char __swap_entry_free(struct swap_info_struct *p,
962 swp_entry_t entry, unsigned char usage)
948{ 963{
949 struct swap_cluster_info *ci; 964 struct swap_cluster_info *ci;
950 unsigned long offset = swp_offset(entry); 965 unsigned long offset = swp_offset(entry);
951 unsigned char count; 966 unsigned char count;
952 unsigned char has_cache; 967 unsigned char has_cache;
953 bool lock_swap_info = false;
954
955 if (!swap_info_locked) {
956 count = p->swap_map[offset];
957 if (!p->cluster_info || count == usage || count == SWAP_MAP_SHMEM) {
958lock_swap_info:
959 swap_info_locked = true;
960 lock_swap_info = true;
961 spin_lock(&p->lock);
962 }
963 }
964 968
965 ci = lock_cluster(p, offset); 969 ci = lock_cluster_or_swap_info(p, offset);
966 970
967 count = p->swap_map[offset]; 971 count = p->swap_map[offset];
968 972
969 if (!swap_info_locked && (count == usage || count == SWAP_MAP_SHMEM)) {
970 unlock_cluster(ci);
971 goto lock_swap_info;
972 }
973
974 has_cache = count & SWAP_HAS_CACHE; 973 has_cache = count & SWAP_HAS_CACHE;
975 count &= ~SWAP_HAS_CACHE; 974 count &= ~SWAP_HAS_CACHE;
976 975
@@ -994,46 +993,52 @@ lock_swap_info:
994 } 993 }
995 994
996 usage = count | has_cache; 995 usage = count | has_cache;
997 p->swap_map[offset] = usage; 996 p->swap_map[offset] = usage ? : SWAP_HAS_CACHE;
997
998 unlock_cluster_or_swap_info(p, ci);
999
1000 return usage;
1001}
998 1002
1003static void swap_entry_free(struct swap_info_struct *p, swp_entry_t entry)
1004{
1005 struct swap_cluster_info *ci;
1006 unsigned long offset = swp_offset(entry);
1007 unsigned char count;
1008
1009 ci = lock_cluster(p, offset);
1010 count = p->swap_map[offset];
1011 VM_BUG_ON(count != SWAP_HAS_CACHE);
1012 p->swap_map[offset] = 0;
1013 dec_cluster_info_page(p, p->cluster_info, offset);
999 unlock_cluster(ci); 1014 unlock_cluster(ci);
1000 1015
1001 /* free if no reference */ 1016 mem_cgroup_uncharge_swap(entry);
1002 if (!usage) { 1017 if (offset < p->lowest_bit)
1003 VM_BUG_ON(!swap_info_locked); 1018 p->lowest_bit = offset;
1004 mem_cgroup_uncharge_swap(entry); 1019 if (offset > p->highest_bit) {
1005 ci = lock_cluster(p, offset); 1020 bool was_full = !p->highest_bit;
1006 dec_cluster_info_page(p, p->cluster_info, offset); 1021
1007 unlock_cluster(ci); 1022 p->highest_bit = offset;
1008 if (offset < p->lowest_bit) 1023 if (was_full && (p->flags & SWP_WRITEOK)) {
1009 p->lowest_bit = offset; 1024 spin_lock(&swap_avail_lock);
1010 if (offset > p->highest_bit) { 1025 WARN_ON(!plist_node_empty(&p->avail_list));
1011 bool was_full = !p->highest_bit; 1026 if (plist_node_empty(&p->avail_list))
1012 p->highest_bit = offset; 1027 plist_add(&p->avail_list,
1013 if (was_full && (p->flags & SWP_WRITEOK)) { 1028 &swap_avail_head);
1014 spin_lock(&swap_avail_lock); 1029 spin_unlock(&swap_avail_lock);
1015 WARN_ON(!plist_node_empty(&p->avail_list));
1016 if (plist_node_empty(&p->avail_list))
1017 plist_add(&p->avail_list,
1018 &swap_avail_head);
1019 spin_unlock(&swap_avail_lock);
1020 }
1021 }
1022 atomic_long_inc(&nr_swap_pages);
1023 p->inuse_pages--;
1024 frontswap_invalidate_page(p->type, offset);
1025 if (p->flags & SWP_BLKDEV) {
1026 struct gendisk *disk = p->bdev->bd_disk;
1027 if (disk->fops->swap_slot_free_notify)
1028 disk->fops->swap_slot_free_notify(p->bdev,
1029 offset);
1030 } 1030 }
1031 } 1031 }
1032 atomic_long_inc(&nr_swap_pages);
1033 p->inuse_pages--;
1034 frontswap_invalidate_page(p->type, offset);
1035 if (p->flags & SWP_BLKDEV) {
1036 struct gendisk *disk = p->bdev->bd_disk;
1032 1037
1033 if (lock_swap_info) 1038 if (disk->fops->swap_slot_free_notify)
1034 spin_unlock(&p->lock); 1039 disk->fops->swap_slot_free_notify(p->bdev,
1035 1040 offset);
1036 return usage; 1041 }
1037} 1042}
1038 1043
1039/* 1044/*
@@ -1045,8 +1050,10 @@ void swap_free(swp_entry_t entry)
1045 struct swap_info_struct *p; 1050 struct swap_info_struct *p;
1046 1051
1047 p = _swap_info_get(entry); 1052 p = _swap_info_get(entry);
1048 if (p) 1053 if (p) {
1049 swap_entry_free(p, entry, 1, false); 1054 if (!__swap_entry_free(p, entry, 1))
1055 swapcache_free_entries(&entry, 1);
1056 }
1050} 1057}
1051 1058
1052/* 1059/*
@@ -1057,8 +1064,32 @@ void swapcache_free(swp_entry_t entry)
1057 struct swap_info_struct *p; 1064 struct swap_info_struct *p;
1058 1065
1059 p = _swap_info_get(entry); 1066 p = _swap_info_get(entry);
1067 if (p) {
1068 if (!__swap_entry_free(p, entry, SWAP_HAS_CACHE))
1069 swapcache_free_entries(&entry, 1);
1070 }
1071}
1072
1073void swapcache_free_entries(swp_entry_t *entries, int n)
1074{
1075 struct swap_info_struct *p, *prev;
1076 int i;
1077
1078 if (n <= 0)
1079 return;
1080
1081 prev = NULL;
1082 p = NULL;
1083 for (i = 0; i < n; ++i) {
1084 p = swap_info_get_cont(entries[i], prev);
1085 if (p)
1086 swap_entry_free(p, entries[i]);
1087 else
1088 break;
1089 prev = p;
1090 }
1060 if (p) 1091 if (p)
1061 swap_entry_free(p, entry, SWAP_HAS_CACHE, false); 1092 spin_unlock(&p->lock);
1062} 1093}
1063 1094
1064/* 1095/*
@@ -1241,21 +1272,23 @@ int free_swap_and_cache(swp_entry_t entry)
1241{ 1272{
1242 struct swap_info_struct *p; 1273 struct swap_info_struct *p;
1243 struct page *page = NULL; 1274 struct page *page = NULL;
1275 unsigned char count;
1244 1276
1245 if (non_swap_entry(entry)) 1277 if (non_swap_entry(entry))
1246 return 1; 1278 return 1;
1247 1279
1248 p = swap_info_get(entry); 1280 p = _swap_info_get(entry);
1249 if (p) { 1281 if (p) {
1250 if (swap_entry_free(p, entry, 1, true) == SWAP_HAS_CACHE) { 1282 count = __swap_entry_free(p, entry, 1);
1283 if (count == SWAP_HAS_CACHE) {
1251 page = find_get_page(swap_address_space(entry), 1284 page = find_get_page(swap_address_space(entry),
1252 swp_offset(entry)); 1285 swp_offset(entry));
1253 if (page && !trylock_page(page)) { 1286 if (page && !trylock_page(page)) {
1254 put_page(page); 1287 put_page(page);
1255 page = NULL; 1288 page = NULL;
1256 } 1289 }
1257 } 1290 } else if (!count)
1258 spin_unlock(&p->lock); 1291 swapcache_free_entries(&entry, 1);
1259 } 1292 }
1260 if (page) { 1293 if (page) {
1261 /* 1294 /*