summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSrividya Desireddy <srividya.dr@samsung.com>2018-01-31 19:15:59 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2018-01-31 20:18:36 -0500
commita85f878b443f8d2b91ba76f09da21ac0af22e07f (patch)
tree6328e7fe0d39c9ba4e3fbc437d2c7291a135f8fe
parent4a01768e9e91082efc9a6384b1ef579fdcbce828 (diff)
zswap: same-filled pages handling
Zswap is a cache which compresses the pages that are being swapped out and stores them into a dynamically allocated RAM-based memory pool. Experiments have shown that around 10-20% of pages stored in zswap are same-filled pages (i.e. contents of the page are all same), but these pages are handled as normal pages by compressing and allocating memory in the pool. This patch adds a check in zswap_frontswap_store() to identify same-filled page before compression of the page. If the page is a same-filled page, set zswap_entry.length to zero, save the same-filled value and skip the compression of the page and alloction of memory in zpool. In zswap_frontswap_load(), check if value of zswap_entry.length is zero corresponding to the page to be loaded. If zswap_entry.length is zero, fill the page with same-filled value. This saves the decompression time during load. On a ARM Quad Core 32-bit device with 1.5GB RAM by launching and relaunching different applications, out of ~64000 pages stored in zswap, ~11000 pages were same-value filled pages (including zero-filled pages) and ~9000 pages were zero-filled pages. An average of 17% of pages(including zero-filled pages) in zswap are same-value filled pages and 14% pages are zero-filled pages. An average of 3% of pages are same-filled non-zero pages. The below table shows the execution time profiling with the patch. Baseline With patch % Improvement ----------------------------------------------------------------- *Zswap Store Time 26.5ms 18ms 32% (of same value pages) *Zswap Load Time (of same value pages) 25.5ms 13ms 49% ----------------------------------------------------------------- On Ubuntu PC with 2GB RAM, while executing kernel build and other test scripts and running multimedia applications, out of 360000 pages stored in zswap 78000(~22%) of pages were found to be same-value filled pages (including zero-filled pages) and 64000(~17%) are zero-filled pages. So an average of %5 of pages are same-filled non-zero pages. The below table shows the execution time profiling with the patch. Baseline With patch % Improvement ----------------------------------------------------------------- *Zswap Store Time 91ms 74ms 19% (of same value pages) *Zswap Load Time 50ms 7.5ms 85% (of same value pages) ----------------------------------------------------------------- *The execution times may vary with test device used. Dan said: : I did test this patch out this week, and I added some instrumentation to : check the performance impact, and tested with a small program to try to : check the best and worst cases. : : When doing a lot of swap where all (or almost all) pages are same-value, I : found this patch does save both time and space, significantly. The exact : improvement in time and space depends on which compressor is being used, : but roughly agrees with the numbers you listed. : : In the worst case situation, where all (or almost all) pages have the : same-value *except* the final long (meaning, zswap will check each long on : the entire page but then still have to pass the page to the compressor), : the same-value check is around 10-15% of the total time spent in : zswap_frontswap_store(). That's a not-insignificant amount of time, but : it's not huge. Considering that most systems will probably be swapping : pages that aren't similar to the worst case (although I don't have any : data to know that), I'd say the improvement is worth the possible : worst-case performance impact. [srividya.dr@samsung.com: add memset_l instead of for loop] Link: http://lkml.kernel.org/r/20171018104832epcms5p1b2232e2236258de3d03d1344dde9fce0@epcms5p1 Signed-off-by: Srividya Desireddy <srividya.dr@samsung.com> Acked-by: Dan Streetman <ddstreet@ieee.org> Cc: Seth Jennings <sjenning@redhat.com> Cc: Pekka Enberg <penberg@kernel.org> Cc: Dinakar Reddy Pathireddy <dinakar.p@samsung.com> Cc: SHARAN ALLUR <sharan.allur@samsung.com> Cc: RAJIB BASU <rajib.basu@samsung.com> Cc: JUHUN KIM <juhunkim@samsung.com> Cc: Matthew Wilcox <willy@infradead.org> Cc: Timofey Titovets <nefelim4ag@gmail.com> Cc: Andi Kleen <ak@linux.intel.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--mm/zswap.c71
1 files changed, 66 insertions, 5 deletions
diff --git a/mm/zswap.c b/mm/zswap.c
index d39581a076c3..1133b4ceb72e 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -49,6 +49,8 @@
49static u64 zswap_pool_total_size; 49static u64 zswap_pool_total_size;
50/* The number of compressed pages currently stored in zswap */ 50/* The number of compressed pages currently stored in zswap */
51static atomic_t zswap_stored_pages = ATOMIC_INIT(0); 51static atomic_t zswap_stored_pages = ATOMIC_INIT(0);
52/* The number of same-value filled pages currently stored in zswap */
53static atomic_t zswap_same_filled_pages = ATOMIC_INIT(0);
52 54
53/* 55/*
54 * The statistics below are not protected from concurrent access for 56 * The statistics below are not protected from concurrent access for
@@ -116,6 +118,11 @@ module_param_cb(zpool, &zswap_zpool_param_ops, &zswap_zpool_type, 0644);
116static unsigned int zswap_max_pool_percent = 20; 118static unsigned int zswap_max_pool_percent = 20;
117module_param_named(max_pool_percent, zswap_max_pool_percent, uint, 0644); 119module_param_named(max_pool_percent, zswap_max_pool_percent, uint, 0644);
118 120
121/* Enable/disable handling same-value filled pages (enabled by default) */
122static bool zswap_same_filled_pages_enabled = true;
123module_param_named(same_filled_pages_enabled, zswap_same_filled_pages_enabled,
124 bool, 0644);
125
119/********************************* 126/*********************************
120* data structures 127* data structures
121**********************************/ 128**********************************/
@@ -145,9 +152,10 @@ struct zswap_pool {
145 * be held while changing the refcount. Since the lock must 152 * be held while changing the refcount. Since the lock must
146 * be held, there is no reason to also make refcount atomic. 153 * be held, there is no reason to also make refcount atomic.
147 * length - the length in bytes of the compressed page data. Needed during 154 * length - the length in bytes of the compressed page data. Needed during
148 * decompression 155 * decompression. For a same value filled page length is 0.
149 * pool - the zswap_pool the entry's data is in 156 * pool - the zswap_pool the entry's data is in
150 * handle - zpool allocation handle that stores the compressed page data 157 * handle - zpool allocation handle that stores the compressed page data
158 * value - value of the same-value filled pages which have same content
151 */ 159 */
152struct zswap_entry { 160struct zswap_entry {
153 struct rb_node rbnode; 161 struct rb_node rbnode;
@@ -155,7 +163,10 @@ struct zswap_entry {
155 int refcount; 163 int refcount;
156 unsigned int length; 164 unsigned int length;
157 struct zswap_pool *pool; 165 struct zswap_pool *pool;
158 unsigned long handle; 166 union {
167 unsigned long handle;
168 unsigned long value;
169 };
159}; 170};
160 171
161struct zswap_header { 172struct zswap_header {
@@ -320,8 +331,12 @@ static void zswap_rb_erase(struct rb_root *root, struct zswap_entry *entry)
320 */ 331 */
321static void zswap_free_entry(struct zswap_entry *entry) 332static void zswap_free_entry(struct zswap_entry *entry)
322{ 333{
323 zpool_free(entry->pool->zpool, entry->handle); 334 if (!entry->length)
324 zswap_pool_put(entry->pool); 335 atomic_dec(&zswap_same_filled_pages);
336 else {
337 zpool_free(entry->pool->zpool, entry->handle);
338 zswap_pool_put(entry->pool);
339 }
325 zswap_entry_cache_free(entry); 340 zswap_entry_cache_free(entry);
326 atomic_dec(&zswap_stored_pages); 341 atomic_dec(&zswap_stored_pages);
327 zswap_update_total_size(); 342 zswap_update_total_size();
@@ -953,6 +968,28 @@ static int zswap_shrink(void)
953 return ret; 968 return ret;
954} 969}
955 970
971static int zswap_is_page_same_filled(void *ptr, unsigned long *value)
972{
973 unsigned int pos;
974 unsigned long *page;
975
976 page = (unsigned long *)ptr;
977 for (pos = 1; pos < PAGE_SIZE / sizeof(*page); pos++) {
978 if (page[pos] != page[0])
979 return 0;
980 }
981 *value = page[0];
982 return 1;
983}
984
985static void zswap_fill_page(void *ptr, unsigned long value)
986{
987 unsigned long *page;
988
989 page = (unsigned long *)ptr;
990 memset_l(page, value, PAGE_SIZE / sizeof(unsigned long));
991}
992
956/********************************* 993/*********************************
957* frontswap hooks 994* frontswap hooks
958**********************************/ 995**********************************/
@@ -965,7 +1002,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
965 struct crypto_comp *tfm; 1002 struct crypto_comp *tfm;
966 int ret; 1003 int ret;
967 unsigned int dlen = PAGE_SIZE, len; 1004 unsigned int dlen = PAGE_SIZE, len;
968 unsigned long handle; 1005 unsigned long handle, value;
969 char *buf; 1006 char *buf;
970 u8 *src, *dst; 1007 u8 *src, *dst;
971 struct zswap_header *zhdr; 1008 struct zswap_header *zhdr;
@@ -993,6 +1030,19 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
993 goto reject; 1030 goto reject;
994 } 1031 }
995 1032
1033 if (zswap_same_filled_pages_enabled) {
1034 src = kmap_atomic(page);
1035 if (zswap_is_page_same_filled(src, &value)) {
1036 kunmap_atomic(src);
1037 entry->offset = offset;
1038 entry->length = 0;
1039 entry->value = value;
1040 atomic_inc(&zswap_same_filled_pages);
1041 goto insert_entry;
1042 }
1043 kunmap_atomic(src);
1044 }
1045
996 /* if entry is successfully added, it keeps the reference */ 1046 /* if entry is successfully added, it keeps the reference */
997 entry->pool = zswap_pool_current_get(); 1047 entry->pool = zswap_pool_current_get();
998 if (!entry->pool) { 1048 if (!entry->pool) {
@@ -1037,6 +1087,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
1037 entry->handle = handle; 1087 entry->handle = handle;
1038 entry->length = dlen; 1088 entry->length = dlen;
1039 1089
1090insert_entry:
1040 /* map */ 1091 /* map */
1041 spin_lock(&tree->lock); 1092 spin_lock(&tree->lock);
1042 do { 1093 do {
@@ -1089,6 +1140,13 @@ static int zswap_frontswap_load(unsigned type, pgoff_t offset,
1089 } 1140 }
1090 spin_unlock(&tree->lock); 1141 spin_unlock(&tree->lock);
1091 1142
1143 if (!entry->length) {
1144 dst = kmap_atomic(page);
1145 zswap_fill_page(dst, entry->value);
1146 kunmap_atomic(dst);
1147 goto freeentry;
1148 }
1149
1092 /* decompress */ 1150 /* decompress */
1093 dlen = PAGE_SIZE; 1151 dlen = PAGE_SIZE;
1094 src = (u8 *)zpool_map_handle(entry->pool->zpool, entry->handle, 1152 src = (u8 *)zpool_map_handle(entry->pool->zpool, entry->handle,
@@ -1101,6 +1159,7 @@ static int zswap_frontswap_load(unsigned type, pgoff_t offset,
1101 zpool_unmap_handle(entry->pool->zpool, entry->handle); 1159 zpool_unmap_handle(entry->pool->zpool, entry->handle);
1102 BUG_ON(ret); 1160 BUG_ON(ret);
1103 1161
1162freeentry:
1104 spin_lock(&tree->lock); 1163 spin_lock(&tree->lock);
1105 zswap_entry_put(tree, entry); 1164 zswap_entry_put(tree, entry);
1106 spin_unlock(&tree->lock); 1165 spin_unlock(&tree->lock);
@@ -1209,6 +1268,8 @@ static int __init zswap_debugfs_init(void)
1209 zswap_debugfs_root, &zswap_pool_total_size); 1268 zswap_debugfs_root, &zswap_pool_total_size);
1210 debugfs_create_atomic_t("stored_pages", S_IRUGO, 1269 debugfs_create_atomic_t("stored_pages", S_IRUGO,
1211 zswap_debugfs_root, &zswap_stored_pages); 1270 zswap_debugfs_root, &zswap_stored_pages);
1271 debugfs_create_atomic_t("same_filled_pages", 0444,
1272 zswap_debugfs_root, &zswap_same_filled_pages);
1212 1273
1213 return 0; 1274 return 0;
1214} 1275}