Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mm/zswap: add the flag can_sleep_mapped

Patch series "Fix the compatibility of zsmalloc and zswap".

Patch #1 adds a flag to zpool, then zswap used to determine if zpool
drivers such as zbud/z3fold/zsmalloc will enter an atomic context after
mapping.

The difference between zbud/z3fold and zsmalloc is that zsmalloc requires
an atomic context that since its map function holds a preempt-disabled,
but zbud/z3fold don't require an atomic context. So patch #2 sets flag
sleep_mapped to true indicating that zbud/z3fold can sleep after mapping.
zsmalloc didn't support sleep after mapping, so don't set that flag to
true.

This patch (of 2):

Add a flag to zpool, named is "can_sleep_mapped", and have it set true for
zbud/z3fold, not set this flag for zsmalloc, so its default value is
false. Then zswap could go the current path if the flag is true; and if
it's false, copy data from src to a temporary buffer, then unmap the
handle, take the mutex, process the buffer instead of src to avoid
sleeping function called from atomic context.

[natechancellor@gmail.com: add return value in zswap_frontswap_load]
Link: https://lkml.kernel.org/r/20210121214804.926843-1-natechancellor@gmail.com
[tiantao6@hisilicon.com: fix potential memory leak]
Link: https://lkml.kernel.org/r/1611538365-51811-1-git-send-email-tiantao6@hisilicon.com
[colin.king@canonical.com: fix potential uninitialized pointer read on tmp]
Link: https://lkml.kernel.org/r/20210128141728.639030-1-colin.king@canonical.com
[tiantao6@hisilicon.com: fix variable 'entry' is uninitialized when used]
Link: https://lkml.kernel.org/r/1611223030-58346-1-git-send-email-tiantao6@hisilicon.comLink: https://lkml.kernel.org/r/1611035683-12732-1-git-send-email-tiantao6@hisilicon.com

Link: https://lkml.kernel.org/r/1611035683-12732-2-git-send-email-tiantao6@hisilicon.com
Signed-off-by: Tian Tao <tiantao6@hisilicon.com>
Signed-off-by: Nathan Chancellor <natechancellor@gmail.com>
Signed-off-by: Colin Ian King <colin.king@canonical.com>
Reviewed-by: Vitaly Wool <vitaly.wool@konsulko.com>
Acked-by: Sebastian Andrzej Siewior <bigeasy@linutronix.de>
Reported-by: Mike Galbraith <efault@gmx.de>
Cc: Barry Song <song.bao.hua@hisilicon.com>
Cc: Dan Streetman <ddstreet@ieee.org>
Cc: Seth Jennings <sjenning@redhat.com>
Cc: Dan Carpenter <dan.carpenter@oracle.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Tian Tao and committed by
Linus Torvalds
fc6697a8 c0c641d7

+62 -5
+3
include/linux/zpool.h
··· 73 73 * @malloc: allocate mem from a pool. 74 74 * @free: free mem from a pool. 75 75 * @shrink: shrink the pool. 76 + * @sleep_mapped: whether zpool driver can sleep during map. 76 77 * @map: map a handle. 77 78 * @unmap: unmap a handle. 78 79 * @total_size: get total size of a pool. ··· 101 100 int (*shrink)(void *pool, unsigned int pages, 102 101 unsigned int *reclaimed); 103 102 103 + bool sleep_mapped; 104 104 void *(*map)(void *pool, unsigned long handle, 105 105 enum zpool_mapmode mm); 106 106 void (*unmap)(void *pool, unsigned long handle); ··· 114 112 int zpool_unregister_driver(struct zpool_driver *driver); 115 113 116 114 bool zpool_evictable(struct zpool *pool); 115 + bool zpool_can_sleep_mapped(struct zpool *pool); 117 116 118 117 #endif
+13
mm/zpool.c
··· 23 23 void *pool; 24 24 const struct zpool_ops *ops; 25 25 bool evictable; 26 + bool can_sleep_mapped; 26 27 27 28 struct list_head list; 28 29 }; ··· 184 183 zpool->pool = driver->create(name, gfp, ops, zpool); 185 184 zpool->ops = ops; 186 185 zpool->evictable = driver->shrink && ops && ops->evict; 186 + zpool->can_sleep_mapped = driver->sleep_mapped; 187 187 188 188 if (!zpool->pool) { 189 189 pr_err("couldn't create %s pool\n", type); ··· 393 391 bool zpool_evictable(struct zpool *zpool) 394 392 { 395 393 return zpool->evictable; 394 + } 395 + 396 + /** 397 + * zpool_can_sleep_mapped - Test if zpool can sleep when do mapped. 398 + * @zpool: The zpool to test 399 + * 400 + * Returns: true if zpool can sleep; false otherwise. 401 + */ 402 + bool zpool_can_sleep_mapped(struct zpool *zpool) 403 + { 404 + return zpool->can_sleep_mapped; 396 405 } 397 406 398 407 MODULE_LICENSE("GPL");
+46 -5
mm/zswap.c
··· 935 935 struct scatterlist input, output; 936 936 struct crypto_acomp_ctx *acomp_ctx; 937 937 938 - u8 *src; 938 + u8 *src, *tmp = NULL; 939 939 unsigned int dlen; 940 940 int ret; 941 941 struct writeback_control wbc = { 942 942 .sync_mode = WB_SYNC_NONE, 943 943 }; 944 + 945 + if (!zpool_can_sleep_mapped(pool)) { 946 + tmp = kmalloc(PAGE_SIZE, GFP_ATOMIC); 947 + if (!tmp) 948 + return -ENOMEM; 949 + } 944 950 945 951 /* extract swpentry from data */ 946 952 zhdr = zpool_map_handle(pool, handle, ZPOOL_MM_RO); ··· 961 955 /* entry was invalidated */ 962 956 spin_unlock(&tree->lock); 963 957 zpool_unmap_handle(pool, handle); 958 + kfree(tmp); 964 959 return 0; 965 960 } 966 961 spin_unlock(&tree->lock); ··· 985 978 986 979 dlen = PAGE_SIZE; 987 980 src = (u8 *)zhdr + sizeof(struct zswap_header); 981 + 982 + if (!zpool_can_sleep_mapped(pool)) { 983 + 984 + memcpy(tmp, src, entry->length); 985 + src = tmp; 986 + 987 + zpool_unmap_handle(pool, handle); 988 + } 988 989 989 990 mutex_lock(acomp_ctx->mutex); 990 991 sg_init_one(&input, src, entry->length); ··· 1048 1033 spin_unlock(&tree->lock); 1049 1034 1050 1035 end: 1051 - zpool_unmap_handle(pool, handle); 1036 + if (zpool_can_sleep_mapped(pool)) 1037 + zpool_unmap_handle(pool, handle); 1038 + else 1039 + kfree(tmp); 1040 + 1052 1041 return ret; 1053 1042 } 1054 1043 ··· 1254 1235 struct zswap_entry *entry; 1255 1236 struct scatterlist input, output; 1256 1237 struct crypto_acomp_ctx *acomp_ctx; 1257 - u8 *src, *dst; 1238 + u8 *src, *dst, *tmp; 1258 1239 unsigned int dlen; 1259 1240 int ret; 1260 1241 ··· 1272 1253 dst = kmap_atomic(page); 1273 1254 zswap_fill_page(dst, entry->value); 1274 1255 kunmap_atomic(dst); 1256 + ret = 0; 1275 1257 goto freeentry; 1258 + } 1259 + 1260 + if (!zpool_can_sleep_mapped(entry->pool->zpool)) { 1261 + 1262 + tmp = kmalloc(entry->length, GFP_ATOMIC); 1263 + if (!tmp) { 1264 + ret = -ENOMEM; 1265 + goto freeentry; 1266 + } 1276 1267 } 1277 1268 1278 1269 /* decompress */ ··· 1290 1261 src = zpool_map_handle(entry->pool->zpool, entry->handle, ZPOOL_MM_RO); 1291 1262 if (zpool_evictable(entry->pool->zpool)) 1292 1263 src += sizeof(struct zswap_header); 1264 + 1265 + if (!zpool_can_sleep_mapped(entry->pool->zpool)) { 1266 + 1267 + memcpy(tmp, src, entry->length); 1268 + src = tmp; 1269 + 1270 + zpool_unmap_handle(entry->pool->zpool, entry->handle); 1271 + } 1293 1272 1294 1273 acomp_ctx = raw_cpu_ptr(entry->pool->acomp_ctx); 1295 1274 mutex_lock(acomp_ctx->mutex); ··· 1308 1271 ret = crypto_wait_req(crypto_acomp_decompress(acomp_ctx->req), &acomp_ctx->wait); 1309 1272 mutex_unlock(acomp_ctx->mutex); 1310 1273 1311 - zpool_unmap_handle(entry->pool->zpool, entry->handle); 1274 + if (zpool_can_sleep_mapped(entry->pool->zpool)) 1275 + zpool_unmap_handle(entry->pool->zpool, entry->handle); 1276 + else 1277 + kfree(tmp); 1278 + 1312 1279 BUG_ON(ret); 1313 1280 1314 1281 freeentry: ··· 1320 1279 zswap_entry_put(tree, entry); 1321 1280 spin_unlock(&tree->lock); 1322 1281 1323 - return 0; 1282 + return ret; 1324 1283 } 1325 1284 1326 1285 /* frees an entry in zswap */