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 | ||