From: Greg Kroah-Hartman Date: Mon, 23 Jan 2017 16:06:16 +0000 (+0100) Subject: 4.9-stable patches X-Git-Tag: v4.4.45~4 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=5b0ab96cbfd20abef1b238193a1e49da6796543b;p=thirdparty%2Fkernel%2Fstable-queue.git 4.9-stable patches added patches: libceph-stop-allocating-a-new-cipher-on-every-crypto-request.patch libceph-uninline-ceph_crypto_key_destroy.patch --- diff --git a/queue-4.9/libceph-stop-allocating-a-new-cipher-on-every-crypto-request.patch b/queue-4.9/libceph-stop-allocating-a-new-cipher-on-every-crypto-request.patch new file mode 100644 index 00000000000..1e42202edcb --- /dev/null +++ b/queue-4.9/libceph-stop-allocating-a-new-cipher-on-every-crypto-request.patch @@ -0,0 +1,244 @@ +From 7af3ea189a9a13f090de51c97f676215dabc1205 Mon Sep 17 00:00:00 2001 +From: Ilya Dryomov +Date: Fri, 2 Dec 2016 16:35:08 +0100 +Subject: libceph: stop allocating a new cipher on every crypto request + +From: Ilya Dryomov + +commit 7af3ea189a9a13f090de51c97f676215dabc1205 upstream. + +This is useless and more importantly not allowed on the writeback path, +because crypto_alloc_skcipher() allocates memory with GFP_KERNEL, which +can recurse back into the filesystem: + + kworker/9:3 D ffff92303f318180 0 20732 2 0x00000080 + Workqueue: ceph-msgr ceph_con_workfn [libceph] + ffff923035dd4480 ffff923038f8a0c0 0000000000000001 000000009eb27318 + ffff92269eb28000 ffff92269eb27338 ffff923036b145ac ffff923035dd4480 + 00000000ffffffff ffff923036b145b0 ffffffff951eb4e1 ffff923036b145a8 + Call Trace: + [] ? schedule+0x31/0x80 + [] ? schedule_preempt_disabled+0xa/0x10 + [] ? __mutex_lock_slowpath+0xb4/0x130 + [] ? mutex_lock+0x1b/0x30 + [] ? xfs_reclaim_inodes_ag+0x233/0x2d0 [xfs] + [] ? move_active_pages_to_lru+0x125/0x270 + [] ? radix_tree_gang_lookup_tag+0xc5/0x1c0 + [] ? __list_lru_walk_one.isra.3+0x33/0x120 + [] ? xfs_reclaim_inodes_nr+0x31/0x40 [xfs] + [] ? super_cache_scan+0x17e/0x190 + [] ? shrink_slab.part.38+0x1e3/0x3d0 + [] ? shrink_node+0x10a/0x320 + [] ? do_try_to_free_pages+0xf4/0x350 + [] ? try_to_free_pages+0xea/0x1b0 + [] ? __alloc_pages_nodemask+0x61d/0xe60 + [] ? cache_grow_begin+0x9d/0x560 + [] ? fallback_alloc+0x148/0x1c0 + [] ? __crypto_alloc_tfm+0x37/0x130 + [] ? __kmalloc+0x1eb/0x580 + [] ? crush_choose_firstn+0x3eb/0x470 [libceph] + [] ? __crypto_alloc_tfm+0x37/0x130 + [] ? crypto_spawn_tfm+0x39/0x60 + [] ? crypto_cbc_init_tfm+0x23/0x40 [cbc] + [] ? __crypto_alloc_tfm+0xcc/0x130 + [] ? crypto_skcipher_init_tfm+0x113/0x180 + [] ? crypto_create_tfm+0x43/0xb0 + [] ? crypto_larval_lookup+0x150/0x150 + [] ? crypto_alloc_tfm+0x72/0x120 + [] ? ceph_aes_encrypt2+0x67/0x400 [libceph] + [] ? ceph_pg_to_up_acting_osds+0x84/0x5b0 [libceph] + [] ? release_sock+0x40/0x90 + [] ? tcp_recvmsg+0x4b4/0xae0 + [] ? ceph_encrypt2+0x54/0xc0 [libceph] + [] ? ceph_x_encrypt+0x5d/0x90 [libceph] + [] ? calcu_signature+0x5f/0x90 [libceph] + [] ? ceph_x_sign_message+0x35/0x50 [libceph] + [] ? prepare_write_message_footer+0x5c/0xa0 [libceph] + [] ? ceph_con_workfn+0x2258/0x2dd0 [libceph] + [] ? queue_con_delay+0x33/0xd0 [libceph] + [] ? __submit_request+0x20d/0x2f0 [libceph] + [] ? ceph_osdc_start_request+0x28/0x30 [libceph] + [] ? rbd_queue_workfn+0x2f3/0x350 [rbd] + [] ? process_one_work+0x160/0x410 + [] ? worker_thread+0x4d/0x480 + [] ? process_one_work+0x410/0x410 + [] ? kthread+0xcd/0xf0 + [] ? ret_from_fork+0x1f/0x40 + [] ? kthread_create_on_node+0x190/0x190 + +Allocating the cipher along with the key fixes the issue - as long the +key doesn't change, a single cipher context can be used concurrently in +multiple requests. + +We still can't take that GFP_KERNEL allocation though. Both +ceph_crypto_key_clone() and ceph_crypto_key_decode() are called from +GFP_NOFS context, so resort to memalloc_noio_{save,restore}() here. + +Reported-by: Lucas Stach +Signed-off-by: Ilya Dryomov +Reviewed-by: Sage Weil +Signed-off-by: Greg Kroah-Hartman + +--- + net/ceph/crypto.c | 85 ++++++++++++++++++++++++++++++++++++++---------------- + net/ceph/crypto.h | 1 + 2 files changed, 61 insertions(+), 25 deletions(-) + +--- a/net/ceph/crypto.c ++++ b/net/ceph/crypto.c +@@ -13,14 +13,60 @@ + #include + #include "crypto.h" + ++/* ++ * Set ->key and ->tfm. The rest of the key should be filled in before ++ * this function is called. ++ */ ++static int set_secret(struct ceph_crypto_key *key, void *buf) ++{ ++ unsigned int noio_flag; ++ int ret; ++ ++ key->key = NULL; ++ key->tfm = NULL; ++ ++ switch (key->type) { ++ case CEPH_CRYPTO_NONE: ++ return 0; /* nothing to do */ ++ case CEPH_CRYPTO_AES: ++ break; ++ default: ++ return -ENOTSUPP; ++ } ++ ++ WARN_ON(!key->len); ++ key->key = kmemdup(buf, key->len, GFP_NOIO); ++ if (!key->key) { ++ ret = -ENOMEM; ++ goto fail; ++ } ++ ++ /* crypto_alloc_skcipher() allocates with GFP_KERNEL */ ++ noio_flag = memalloc_noio_save(); ++ key->tfm = crypto_alloc_skcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC); ++ memalloc_noio_restore(noio_flag); ++ if (IS_ERR(key->tfm)) { ++ ret = PTR_ERR(key->tfm); ++ key->tfm = NULL; ++ goto fail; ++ } ++ ++ ret = crypto_skcipher_setkey(key->tfm, key->key, key->len); ++ if (ret) ++ goto fail; ++ ++ return 0; ++ ++fail: ++ ceph_crypto_key_destroy(key); ++ return ret; ++} ++ + int ceph_crypto_key_clone(struct ceph_crypto_key *dst, + const struct ceph_crypto_key *src) + { + memcpy(dst, src, sizeof(struct ceph_crypto_key)); +- dst->key = kmemdup(src->key, src->len, GFP_NOFS); +- if (!dst->key) +- return -ENOMEM; +- return 0; ++ return set_secret(dst, src->key); + } + + int ceph_crypto_key_encode(struct ceph_crypto_key *key, void **p, void *end) +@@ -37,16 +83,16 @@ int ceph_crypto_key_encode(struct ceph_c + + int ceph_crypto_key_decode(struct ceph_crypto_key *key, void **p, void *end) + { ++ int ret; ++ + ceph_decode_need(p, end, 2*sizeof(u16) + sizeof(key->created), bad); + key->type = ceph_decode_16(p); + ceph_decode_copy(p, &key->created, sizeof(key->created)); + key->len = ceph_decode_16(p); + ceph_decode_need(p, end, key->len, bad); +- key->key = kmalloc(key->len, GFP_NOFS); +- if (!key->key) +- return -ENOMEM; +- ceph_decode_copy(p, key->key, key->len); +- return 0; ++ ret = set_secret(key, *p); ++ *p += key->len; ++ return ret; + + bad: + dout("failed to decode crypto key\n"); +@@ -85,14 +131,11 @@ void ceph_crypto_key_destroy(struct ceph + if (key) { + kfree(key->key); + key->key = NULL; ++ crypto_free_skcipher(key->tfm); ++ key->tfm = NULL; + } + } + +-static struct crypto_skcipher *ceph_crypto_alloc_cipher(void) +-{ +- return crypto_alloc_skcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC); +-} +- + static const u8 *aes_iv = (u8 *)CEPH_AES_IV; + + /* +@@ -168,8 +211,7 @@ static void teardown_sgtable(struct sg_t + static int ceph_aes_crypt(const struct ceph_crypto_key *key, bool encrypt, + void *buf, int buf_len, int in_len, int *pout_len) + { +- struct crypto_skcipher *tfm = ceph_crypto_alloc_cipher(); +- SKCIPHER_REQUEST_ON_STACK(req, tfm); ++ SKCIPHER_REQUEST_ON_STACK(req, key->tfm); + struct sg_table sgt; + struct scatterlist prealloc_sg; + char iv[AES_BLOCK_SIZE] __aligned(8); +@@ -177,20 +219,15 @@ static int ceph_aes_crypt(const struct c + int crypt_len = encrypt ? in_len + pad_byte : in_len; + int ret; + +- if (IS_ERR(tfm)) +- return PTR_ERR(tfm); +- + WARN_ON(crypt_len > buf_len); + if (encrypt) + memset(buf + in_len, pad_byte, pad_byte); + ret = setup_sgtable(&sgt, &prealloc_sg, buf, crypt_len); + if (ret) +- goto out_tfm; ++ return ret; + +- crypto_skcipher_setkey((void *)tfm, key->key, key->len); + memcpy(iv, aes_iv, AES_BLOCK_SIZE); +- +- skcipher_request_set_tfm(req, tfm); ++ skcipher_request_set_tfm(req, key->tfm); + skcipher_request_set_callback(req, 0, NULL, NULL); + skcipher_request_set_crypt(req, sgt.sgl, sgt.sgl, crypt_len, iv); + +@@ -232,8 +269,6 @@ static int ceph_aes_crypt(const struct c + + out_sgt: + teardown_sgtable(&sgt); +-out_tfm: +- crypto_free_skcipher(tfm); + return ret; + } + +--- a/net/ceph/crypto.h ++++ b/net/ceph/crypto.h +@@ -12,6 +12,7 @@ struct ceph_crypto_key { + struct ceph_timespec created; + int len; + void *key; ++ struct crypto_skcipher *tfm; + }; + + int ceph_crypto_key_clone(struct ceph_crypto_key *dst, diff --git a/queue-4.9/libceph-uninline-ceph_crypto_key_destroy.patch b/queue-4.9/libceph-uninline-ceph_crypto_key_destroy.patch new file mode 100644 index 00000000000..6c5f99a268c --- /dev/null +++ b/queue-4.9/libceph-uninline-ceph_crypto_key_destroy.patch @@ -0,0 +1,58 @@ +From 6db2304aabb070261ad34923bfd83c43dfb000e3 Mon Sep 17 00:00:00 2001 +From: Ilya Dryomov +Date: Fri, 2 Dec 2016 16:35:08 +0100 +Subject: libceph: uninline ceph_crypto_key_destroy() + +From: Ilya Dryomov + +commit 6db2304aabb070261ad34923bfd83c43dfb000e3 upstream. + +Signed-off-by: Ilya Dryomov +Reviewed-by: Sage Weil +Signed-off-by: Greg Kroah-Hartman + +--- + net/ceph/crypto.c | 8 ++++++++ + net/ceph/crypto.h | 9 +-------- + 2 files changed, 9 insertions(+), 8 deletions(-) + +--- a/net/ceph/crypto.c ++++ b/net/ceph/crypto.c +@@ -80,6 +80,14 @@ int ceph_crypto_key_unarmor(struct ceph_ + return 0; + } + ++void ceph_crypto_key_destroy(struct ceph_crypto_key *key) ++{ ++ if (key) { ++ kfree(key->key); ++ key->key = NULL; ++ } ++} ++ + static struct crypto_skcipher *ceph_crypto_alloc_cipher(void) + { + return crypto_alloc_skcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC); +--- a/net/ceph/crypto.h ++++ b/net/ceph/crypto.h +@@ -14,19 +14,12 @@ struct ceph_crypto_key { + void *key; + }; + +-static inline void ceph_crypto_key_destroy(struct ceph_crypto_key *key) +-{ +- if (key) { +- kfree(key->key); +- key->key = NULL; +- } +-} +- + int ceph_crypto_key_clone(struct ceph_crypto_key *dst, + const struct ceph_crypto_key *src); + int ceph_crypto_key_encode(struct ceph_crypto_key *key, void **p, void *end); + int ceph_crypto_key_decode(struct ceph_crypto_key *key, void **p, void *end); + int ceph_crypto_key_unarmor(struct ceph_crypto_key *key, const char *in); ++void ceph_crypto_key_destroy(struct ceph_crypto_key *key); + + /* crypto.c */ + int ceph_crypt(const struct ceph_crypto_key *key, bool encrypt, diff --git a/queue-4.9/series b/queue-4.9/series index 907189a9db9..b34af6b9c4f 100644 --- a/queue-4.9/series +++ b/queue-4.9/series @@ -124,3 +124,5 @@ spi-pxa2xx-add-missed-break.patch soc-ti-wkup_m3_ipc-fix-error-return-code-in-wkup_m3_ipc_probe.patch selftest-powerpc-wrong-pmc-initialized-in-pmc56_overflow-test.patch tools-virtio-ringtest-fix-run-on-all.sh-for-offline-cpus.patch +libceph-uninline-ceph_crypto_key_destroy.patch +libceph-stop-allocating-a-new-cipher-on-every-crypto-request.patch