diff options
| author | Tom Parkin <tparkin@katalix.com> | 2013-01-31 18:43:00 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2013-02-05 14:20:30 -0500 |
| commit | f8ccac0e44934ff9414b31cc3167a5c828afec73 (patch) | |
| tree | ec40a75d99912502d4bbb3df76f26d8aea96f236 /net/l2tp | |
| parent | 188d1f76d0dd3715ceeadfa31376867c3395eb41 (diff) | |
l2tp: put tunnel socket release on a workqueue
To allow l2tp_tunnel_delete to be called from an atomic context, place the
tunnel socket release calls on a workqueue for asynchronous execution.
Tunnel memory is eventually freed in the tunnel socket destructor.
Signed-off-by: Tom Parkin <tparkin@katalix.com>
Signed-off-by: James Chapman <jchapman@katalix.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/l2tp')
| -rw-r--r-- | net/l2tp/l2tp_core.c | 103 | ||||
| -rw-r--r-- | net/l2tp/l2tp_core.h | 2 |
2 files changed, 61 insertions, 44 deletions
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 06389d5ff120..73988c070561 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c | |||
| @@ -101,6 +101,7 @@ struct l2tp_skb_cb { | |||
| 101 | 101 | ||
| 102 | static atomic_t l2tp_tunnel_count; | 102 | static atomic_t l2tp_tunnel_count; |
| 103 | static atomic_t l2tp_session_count; | 103 | static atomic_t l2tp_session_count; |
| 104 | static struct workqueue_struct *l2tp_wq; | ||
| 104 | 105 | ||
| 105 | /* per-net private data for this module */ | 106 | /* per-net private data for this module */ |
| 106 | static unsigned int l2tp_net_id; | 107 | static unsigned int l2tp_net_id; |
| @@ -122,7 +123,6 @@ static inline struct l2tp_net *l2tp_pernet(struct net *net) | |||
| 122 | return net_generic(net, l2tp_net_id); | 123 | return net_generic(net, l2tp_net_id); |
| 123 | } | 124 | } |
| 124 | 125 | ||
| 125 | |||
| 126 | /* Tunnel reference counts. Incremented per session that is added to | 126 | /* Tunnel reference counts. Incremented per session that is added to |
| 127 | * the tunnel. | 127 | * the tunnel. |
| 128 | */ | 128 | */ |
| @@ -1277,6 +1277,7 @@ EXPORT_SYMBOL_GPL(l2tp_xmit_skb); | |||
| 1277 | static void l2tp_tunnel_destruct(struct sock *sk) | 1277 | static void l2tp_tunnel_destruct(struct sock *sk) |
| 1278 | { | 1278 | { |
| 1279 | struct l2tp_tunnel *tunnel; | 1279 | struct l2tp_tunnel *tunnel; |
| 1280 | struct l2tp_net *pn; | ||
| 1280 | 1281 | ||
| 1281 | tunnel = sk->sk_user_data; | 1282 | tunnel = sk->sk_user_data; |
| 1282 | if (tunnel == NULL) | 1283 | if (tunnel == NULL) |
| @@ -1284,9 +1285,8 @@ static void l2tp_tunnel_destruct(struct sock *sk) | |||
| 1284 | 1285 | ||
| 1285 | l2tp_info(tunnel, L2TP_MSG_CONTROL, "%s: closing...\n", tunnel->name); | 1286 | l2tp_info(tunnel, L2TP_MSG_CONTROL, "%s: closing...\n", tunnel->name); |
| 1286 | 1287 | ||
| 1287 | /* Close all sessions */ | ||
| 1288 | l2tp_tunnel_closeall(tunnel); | ||
| 1289 | 1288 | ||
| 1289 | /* Disable udp encapsulation */ | ||
| 1290 | switch (tunnel->encap) { | 1290 | switch (tunnel->encap) { |
| 1291 | case L2TP_ENCAPTYPE_UDP: | 1291 | case L2TP_ENCAPTYPE_UDP: |
| 1292 | /* No longer an encapsulation socket. See net/ipv4/udp.c */ | 1292 | /* No longer an encapsulation socket. See net/ipv4/udp.c */ |
| @@ -1298,17 +1298,23 @@ static void l2tp_tunnel_destruct(struct sock *sk) | |||
| 1298 | } | 1298 | } |
| 1299 | 1299 | ||
| 1300 | /* Remove hooks into tunnel socket */ | 1300 | /* Remove hooks into tunnel socket */ |
| 1301 | tunnel->sock = NULL; | ||
| 1302 | sk->sk_destruct = tunnel->old_sk_destruct; | 1301 | sk->sk_destruct = tunnel->old_sk_destruct; |
| 1303 | sk->sk_user_data = NULL; | 1302 | sk->sk_user_data = NULL; |
| 1303 | tunnel->sock = NULL; | ||
| 1304 | 1304 | ||
| 1305 | /* Call the original destructor */ | 1305 | /* Remove the tunnel struct from the tunnel list */ |
| 1306 | if (sk->sk_destruct) | 1306 | pn = l2tp_pernet(tunnel->l2tp_net); |
| 1307 | (*sk->sk_destruct)(sk); | 1307 | spin_lock_bh(&pn->l2tp_tunnel_list_lock); |
| 1308 | list_del_rcu(&tunnel->list); | ||
| 1309 | spin_unlock_bh(&pn->l2tp_tunnel_list_lock); | ||
| 1310 | atomic_dec(&l2tp_tunnel_count); | ||
| 1308 | 1311 | ||
| 1309 | /* We're finished with the socket */ | 1312 | l2tp_tunnel_closeall(tunnel); |
| 1310 | l2tp_tunnel_dec_refcount(tunnel); | 1313 | l2tp_tunnel_dec_refcount(tunnel); |
| 1311 | 1314 | ||
| 1315 | /* Call the original destructor */ | ||
| 1316 | if (sk->sk_destruct) | ||
| 1317 | (*sk->sk_destruct)(sk); | ||
| 1312 | end: | 1318 | end: |
| 1313 | return; | 1319 | return; |
| 1314 | } | 1320 | } |
| @@ -1382,20 +1388,41 @@ again: | |||
| 1382 | */ | 1388 | */ |
| 1383 | static void l2tp_tunnel_free(struct l2tp_tunnel *tunnel) | 1389 | static void l2tp_tunnel_free(struct l2tp_tunnel *tunnel) |
| 1384 | { | 1390 | { |
| 1385 | struct l2tp_net *pn = l2tp_pernet(tunnel->l2tp_net); | ||
| 1386 | |||
| 1387 | BUG_ON(atomic_read(&tunnel->ref_count) != 0); | 1391 | BUG_ON(atomic_read(&tunnel->ref_count) != 0); |
| 1388 | BUG_ON(tunnel->sock != NULL); | 1392 | BUG_ON(tunnel->sock != NULL); |
| 1389 | |||
| 1390 | l2tp_info(tunnel, L2TP_MSG_CONTROL, "%s: free...\n", tunnel->name); | 1393 | l2tp_info(tunnel, L2TP_MSG_CONTROL, "%s: free...\n", tunnel->name); |
| 1391 | |||
| 1392 | /* Remove from tunnel list */ | ||
| 1393 | spin_lock_bh(&pn->l2tp_tunnel_list_lock); | ||
| 1394 | list_del_rcu(&tunnel->list); | ||
| 1395 | kfree_rcu(tunnel, rcu); | 1394 | kfree_rcu(tunnel, rcu); |
| 1396 | spin_unlock_bh(&pn->l2tp_tunnel_list_lock); | 1395 | } |
| 1397 | 1396 | ||
| 1398 | atomic_dec(&l2tp_tunnel_count); | 1397 | /* Workqueue tunnel deletion function */ |
| 1398 | static void l2tp_tunnel_del_work(struct work_struct *work) | ||
| 1399 | { | ||
| 1400 | struct l2tp_tunnel *tunnel = NULL; | ||
| 1401 | struct socket *sock = NULL; | ||
| 1402 | struct sock *sk = NULL; | ||
| 1403 | |||
| 1404 | tunnel = container_of(work, struct l2tp_tunnel, del_work); | ||
| 1405 | sk = l2tp_tunnel_sock_lookup(tunnel); | ||
| 1406 | if (!sk) | ||
| 1407 | return; | ||
| 1408 | |||
| 1409 | sock = sk->sk_socket; | ||
| 1410 | BUG_ON(!sock); | ||
| 1411 | |||
| 1412 | /* Force the tunnel socket to close. This will eventually | ||
| 1413 | * cause the tunnel to be deleted via the normal socket close | ||
| 1414 | * mechanisms when userspace closes the tunnel socket. | ||
| 1415 | */ | ||
| 1416 | inet_shutdown(sock, 2); | ||
| 1417 | |||
| 1418 | /* If the tunnel's socket was created by the kernel, | ||
| 1419 | * close the socket here since the socket was not | ||
| 1420 | * created by userspace. | ||
| 1421 | */ | ||
| 1422 | if (sock->file == NULL) | ||
| 1423 | inet_release(sock); | ||
| 1424 | |||
| 1425 | l2tp_tunnel_sock_put(sk); | ||
| 1399 | } | 1426 | } |
| 1400 | 1427 | ||
| 1401 | /* Create a socket for the tunnel, if one isn't set up by | 1428 | /* Create a socket for the tunnel, if one isn't set up by |
| @@ -1657,6 +1684,9 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 | |||
| 1657 | 1684 | ||
| 1658 | sk->sk_allocation = GFP_ATOMIC; | 1685 | sk->sk_allocation = GFP_ATOMIC; |
| 1659 | 1686 | ||
| 1687 | /* Init delete workqueue struct */ | ||
| 1688 | INIT_WORK(&tunnel->del_work, l2tp_tunnel_del_work); | ||
| 1689 | |||
| 1660 | /* Add tunnel to our list */ | 1690 | /* Add tunnel to our list */ |
| 1661 | INIT_LIST_HEAD(&tunnel->list); | 1691 | INIT_LIST_HEAD(&tunnel->list); |
| 1662 | atomic_inc(&l2tp_tunnel_count); | 1692 | atomic_inc(&l2tp_tunnel_count); |
| @@ -1688,33 +1718,7 @@ EXPORT_SYMBOL_GPL(l2tp_tunnel_create); | |||
| 1688 | */ | 1718 | */ |
| 1689 | int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel) | 1719 | int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel) |
| 1690 | { | 1720 | { |
| 1691 | int err = -EBADF; | 1721 | return (false == queue_work(l2tp_wq, &tunnel->del_work)); |
| 1692 | struct socket *sock = NULL; | ||
| 1693 | struct sock *sk = NULL; | ||
| 1694 | |||
| 1695 | sk = l2tp_tunnel_sock_lookup(tunnel); | ||
| 1696 | if (!sk) | ||
| 1697 | goto out; | ||
| 1698 | |||
| 1699 | sock = sk->sk_socket; | ||
| 1700 | BUG_ON(!sock); | ||
| 1701 | |||
| 1702 | /* Force the tunnel socket to close. This will eventually | ||
| 1703 | * cause the tunnel to be deleted via the normal socket close | ||
| 1704 | * mechanisms when userspace closes the tunnel socket. | ||
| 1705 | */ | ||
| 1706 | err = inet_shutdown(sock, 2); | ||
| 1707 | |||
| 1708 | /* If the tunnel's socket was created by the kernel, | ||
| 1709 | * close the socket here since the socket was not | ||
| 1710 | * created by userspace. | ||
| 1711 | */ | ||
| 1712 | if (sock->file == NULL) | ||
| 1713 | err = inet_release(sock); | ||
| 1714 | |||
| 1715 | l2tp_tunnel_sock_put(sk); | ||
| 1716 | out: | ||
| 1717 | return err; | ||
| 1718 | } | 1722 | } |
| 1719 | EXPORT_SYMBOL_GPL(l2tp_tunnel_delete); | 1723 | EXPORT_SYMBOL_GPL(l2tp_tunnel_delete); |
| 1720 | 1724 | ||
| @@ -1912,6 +1916,13 @@ static int __init l2tp_init(void) | |||
| 1912 | if (rc) | 1916 | if (rc) |
| 1913 | goto out; | 1917 | goto out; |
| 1914 | 1918 | ||
| 1919 | l2tp_wq = alloc_workqueue("l2tp", WQ_NON_REENTRANT | WQ_UNBOUND, 0); | ||
| 1920 | if (!l2tp_wq) { | ||
| 1921 | pr_err("alloc_workqueue failed\n"); | ||
| 1922 | rc = -ENOMEM; | ||
| 1923 | goto out; | ||
| 1924 | } | ||
| 1925 | |||
| 1915 | pr_info("L2TP core driver, %s\n", L2TP_DRV_VERSION); | 1926 | pr_info("L2TP core driver, %s\n", L2TP_DRV_VERSION); |
| 1916 | 1927 | ||
| 1917 | out: | 1928 | out: |
| @@ -1921,6 +1932,10 @@ out: | |||
| 1921 | static void __exit l2tp_exit(void) | 1932 | static void __exit l2tp_exit(void) |
| 1922 | { | 1933 | { |
| 1923 | unregister_pernet_device(&l2tp_net_ops); | 1934 | unregister_pernet_device(&l2tp_net_ops); |
| 1935 | if (l2tp_wq) { | ||
| 1936 | destroy_workqueue(l2tp_wq); | ||
| 1937 | l2tp_wq = NULL; | ||
| 1938 | } | ||
| 1924 | } | 1939 | } |
| 1925 | 1940 | ||
| 1926 | module_init(l2tp_init); | 1941 | module_init(l2tp_init); |
diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index e62204cad4fe..8eb8f1d47f3a 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h | |||
| @@ -191,6 +191,8 @@ struct l2tp_tunnel { | |||
| 191 | int fd; /* Parent fd, if tunnel socket | 191 | int fd; /* Parent fd, if tunnel socket |
| 192 | * was created by userspace */ | 192 | * was created by userspace */ |
| 193 | 193 | ||
| 194 | struct work_struct del_work; | ||
| 195 | |||
| 194 | uint8_t priv[0]; /* private data */ | 196 | uint8_t priv[0]; /* private data */ |
| 195 | }; | 197 | }; |
| 196 | 198 | ||
