diff options
author | Atul Gupta <atul.gupta@chelsio.com> | 2018-12-11 02:20:09 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-12-14 13:39:39 -0800 |
commit | df9d4a1780223c3ddd4bf0810079b5a75251554e (patch) | |
tree | 76930eff88ae5165b3f15fd40d445fbe62242c0b /drivers | |
parent | 6c0563e442528733219afe15c749eb2cc365da3f (diff) | |
download | linux-df9d4a1780223c3ddd4bf0810079b5a75251554e.tar.bz2 |
net/tls: sleeping function from invalid context
HW unhash within mutex for registered tls devices cause sleep
when called from tcp_set_state for TCP_CLOSE. Release lock and
re-acquire after function call with ref count incr/dec.
defined kref and fp release for tls_device to ensure device
is not released outside lock.
BUG: sleeping function called from invalid context at
kernel/locking/mutex.c:748
in_atomic(): 1, irqs_disabled(): 0, pid: 0, name: swapper/7
INFO: lockdep is turned off.
CPU: 7 PID: 0 Comm: swapper/7 Tainted: G W O
Call Trace:
<IRQ>
dump_stack+0x5e/0x8b
___might_sleep+0x222/0x260
__mutex_lock+0x5c/0xa50
? vprintk_emit+0x1f3/0x440
? kmem_cache_free+0x22d/0x2a0
? tls_hw_unhash+0x2f/0x80
? printk+0x52/0x6e
? tls_hw_unhash+0x2f/0x80
tls_hw_unhash+0x2f/0x80
tcp_set_state+0x5f/0x180
tcp_done+0x2e/0xe0
tcp_rcv_state_process+0x92c/0xdd3
? lock_acquire+0xf5/0x1f0
? tcp_v4_rcv+0xa7c/0xbe0
? tcp_v4_do_rcv+0x70/0x1e0
Signed-off-by: Atul Gupta <atul.gupta@chelsio.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/crypto/chelsio/chtls/chtls_main.c | 55 |
1 files changed, 32 insertions, 23 deletions
diff --git a/drivers/crypto/chelsio/chtls/chtls_main.c b/drivers/crypto/chelsio/chtls/chtls_main.c index f472c51abe56..db40ab6dd410 100644 --- a/drivers/crypto/chelsio/chtls/chtls_main.c +++ b/drivers/crypto/chelsio/chtls/chtls_main.c @@ -149,6 +149,30 @@ static void chtls_destroy_hash(struct tls_device *dev, struct sock *sk) chtls_stop_listen(sk); } +static void chtls_free_uld(struct chtls_dev *cdev) +{ + int i; + + tls_unregister_device(&cdev->tlsdev); + kvfree(cdev->kmap.addr); + idr_destroy(&cdev->hwtid_idr); + for (i = 0; i < (1 << RSPQ_HASH_BITS); i++) + kfree_skb(cdev->rspq_skb_cache[i]); + kfree(cdev->lldi); + kfree_skb(cdev->askb); + kfree(cdev); +} + +static inline void chtls_dev_release(struct kref *kref) +{ + struct chtls_dev *cdev; + struct tls_device *dev; + + dev = container_of(kref, struct tls_device, kref); + cdev = to_chtls_dev(dev); + chtls_free_uld(cdev); +} + static void chtls_register_dev(struct chtls_dev *cdev) { struct tls_device *tlsdev = &cdev->tlsdev; @@ -159,15 +183,12 @@ static void chtls_register_dev(struct chtls_dev *cdev) tlsdev->feature = chtls_inline_feature; tlsdev->hash = chtls_create_hash; tlsdev->unhash = chtls_destroy_hash; - tls_register_device(&cdev->tlsdev); + tlsdev->release = chtls_dev_release; + kref_init(&tlsdev->kref); + tls_register_device(tlsdev); cdev->cdev_state = CHTLS_CDEV_STATE_UP; } -static void chtls_unregister_dev(struct chtls_dev *cdev) -{ - tls_unregister_device(&cdev->tlsdev); -} - static void process_deferq(struct work_struct *task_param) { struct chtls_dev *cdev = container_of(task_param, @@ -262,28 +283,16 @@ out: return NULL; } -static void chtls_free_uld(struct chtls_dev *cdev) -{ - int i; - - chtls_unregister_dev(cdev); - kvfree(cdev->kmap.addr); - idr_destroy(&cdev->hwtid_idr); - for (i = 0; i < (1 << RSPQ_HASH_BITS); i++) - kfree_skb(cdev->rspq_skb_cache[i]); - kfree(cdev->lldi); - kfree_skb(cdev->askb); - kfree(cdev); -} - static void chtls_free_all_uld(void) { struct chtls_dev *cdev, *tmp; mutex_lock(&cdev_mutex); list_for_each_entry_safe(cdev, tmp, &cdev_list, list) { - if (cdev->cdev_state == CHTLS_CDEV_STATE_UP) - chtls_free_uld(cdev); + if (cdev->cdev_state == CHTLS_CDEV_STATE_UP) { + list_del(&cdev->list); + kref_put(&cdev->tlsdev.kref, cdev->tlsdev.release); + } } mutex_unlock(&cdev_mutex); } @@ -304,7 +313,7 @@ static int chtls_uld_state_change(void *handle, enum cxgb4_state new_state) mutex_lock(&cdev_mutex); list_del(&cdev->list); mutex_unlock(&cdev_mutex); - chtls_free_uld(cdev); + kref_put(&cdev->tlsdev.kref, cdev->tlsdev.release); break; default: break; |