aboutsummaryrefslogtreecommitdiffstats
path: root/net/l2tp
diff options
context:
space:
mode:
authorTom Parkin <tparkin@katalix.com>2013-03-19 02:11:23 -0400
committerDavid S. Miller <davem@davemloft.net>2013-03-20 12:10:39 -0400
commitf6e16b299bacaa71c6604a784f2d088a966f8c23 (patch)
tree115d199ee9cd9fb9dbc296d3daad259cd9e0d682 /net/l2tp
parent7b7c0719cd7afee725b920d75ec6a500b76107e6 (diff)
l2tp: unhash l2tp sessions on delete, not on free
If we postpone unhashing of l2tp sessions until the structure is freed, we risk: 1. further packets arriving and getting queued while the pseudowire is being closed down 2. the recv path hitting "scheduling while atomic" errors in the case that recv drops the last reference to a session and calls l2tp_session_free while in atomic context As such, l2tp sessions should be unhashed from l2tp_core data structures early in the teardown process prior to calling pseudowire close. For pseudowires like l2tp_ppp which have multiple shutdown codepaths, provide an unhash hook. 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.c75
-rw-r--r--net/l2tp/l2tp_core.h1
-rw-r--r--net/l2tp/l2tp_ppp.c12
3 files changed, 38 insertions, 50 deletions
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
index 97d30ac67c88..8aecf5df6656 100644
--- a/net/l2tp/l2tp_core.c
+++ b/net/l2tp/l2tp_core.c
@@ -1316,26 +1316,12 @@ again:
1316 1316
1317 hlist_del_init(&session->hlist); 1317 hlist_del_init(&session->hlist);
1318 1318
1319 /* Since we should hold the sock lock while
1320 * doing any unbinding, we need to release the
1321 * lock we're holding before taking that lock.
1322 * Hold a reference to the sock so it doesn't
1323 * disappear as we're jumping between locks.
1324 */
1325 if (session->ref != NULL) 1319 if (session->ref != NULL)
1326 (*session->ref)(session); 1320 (*session->ref)(session);
1327 1321
1328 write_unlock_bh(&tunnel->hlist_lock); 1322 write_unlock_bh(&tunnel->hlist_lock);
1329 1323
1330 if (tunnel->version != L2TP_HDR_VER_2) { 1324 __l2tp_session_unhash(session);
1331 struct l2tp_net *pn = l2tp_pernet(tunnel->l2tp_net);
1332
1333 spin_lock_bh(&pn->l2tp_session_hlist_lock);
1334 hlist_del_init_rcu(&session->global_hlist);
1335 spin_unlock_bh(&pn->l2tp_session_hlist_lock);
1336 synchronize_rcu();
1337 }
1338
1339 l2tp_session_queue_purge(session); 1325 l2tp_session_queue_purge(session);
1340 1326
1341 if (session->session_close != NULL) 1327 if (session->session_close != NULL)
@@ -1732,64 +1718,71 @@ EXPORT_SYMBOL_GPL(l2tp_tunnel_delete);
1732 */ 1718 */
1733void l2tp_session_free(struct l2tp_session *session) 1719void l2tp_session_free(struct l2tp_session *session)
1734{ 1720{
1735 struct l2tp_tunnel *tunnel; 1721 struct l2tp_tunnel *tunnel = session->tunnel;
1736 1722
1737 BUG_ON(atomic_read(&session->ref_count) != 0); 1723 BUG_ON(atomic_read(&session->ref_count) != 0);
1738 1724
1739 tunnel = session->tunnel; 1725 if (tunnel) {
1740 if (tunnel != NULL) {
1741 BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC); 1726 BUG_ON(tunnel->magic != L2TP_TUNNEL_MAGIC);
1727 if (session->session_id != 0)
1728 atomic_dec(&l2tp_session_count);
1729 sock_put(tunnel->sock);
1730 session->tunnel = NULL;
1731 l2tp_tunnel_dec_refcount(tunnel);
1732 }
1733
1734 kfree(session);
1735
1736 return;
1737}
1738EXPORT_SYMBOL_GPL(l2tp_session_free);
1739
1740/* Remove an l2tp session from l2tp_core's hash lists.
1741 * Provides a tidyup interface for pseudowire code which can't just route all
1742 * shutdown via. l2tp_session_delete and a pseudowire-specific session_close
1743 * callback.
1744 */
1745void __l2tp_session_unhash(struct l2tp_session *session)
1746{
1747 struct l2tp_tunnel *tunnel = session->tunnel;
1742 1748
1743 /* Delete the session from the hash */ 1749 /* Remove the session from core hashes */
1750 if (tunnel) {
1751 /* Remove from the per-tunnel hash */
1744 write_lock_bh(&tunnel->hlist_lock); 1752 write_lock_bh(&tunnel->hlist_lock);
1745 hlist_del_init(&session->hlist); 1753 hlist_del_init(&session->hlist);
1746 write_unlock_bh(&tunnel->hlist_lock); 1754 write_unlock_bh(&tunnel->hlist_lock);
1747 1755
1748 /* Unlink from the global hash if not L2TPv2 */ 1756 /* For L2TPv3 we have a per-net hash: remove from there, too */
1749 if (tunnel->version != L2TP_HDR_VER_2) { 1757 if (tunnel->version != L2TP_HDR_VER_2) {
1750 struct l2tp_net *pn = l2tp_pernet(tunnel->l2tp_net); 1758 struct l2tp_net *pn = l2tp_pernet(tunnel->l2tp_net);
1751
1752 spin_lock_bh(&pn->l2tp_session_hlist_lock); 1759 spin_lock_bh(&pn->l2tp_session_hlist_lock);
1753 hlist_del_init_rcu(&session->global_hlist); 1760 hlist_del_init_rcu(&session->global_hlist);
1754 spin_unlock_bh(&pn->l2tp_session_hlist_lock); 1761 spin_unlock_bh(&pn->l2tp_session_hlist_lock);
1755 synchronize_rcu(); 1762 synchronize_rcu();
1756 } 1763 }
1757
1758 if (session->session_id != 0)
1759 atomic_dec(&l2tp_session_count);
1760
1761 sock_put(tunnel->sock);
1762
1763 /* This will delete the tunnel context if this
1764 * is the last session on the tunnel.
1765 */
1766 session->tunnel = NULL;
1767 l2tp_tunnel_dec_refcount(tunnel);
1768 } 1764 }
1769
1770 kfree(session);
1771
1772 return;
1773} 1765}
1774EXPORT_SYMBOL_GPL(l2tp_session_free); 1766EXPORT_SYMBOL_GPL(__l2tp_session_unhash);
1775 1767
1776/* This function is used by the netlink SESSION_DELETE command and by 1768/* This function is used by the netlink SESSION_DELETE command and by
1777 pseudowire modules. 1769 pseudowire modules.
1778 */ 1770 */
1779int l2tp_session_delete(struct l2tp_session *session) 1771int l2tp_session_delete(struct l2tp_session *session)
1780{ 1772{
1773 if (session->ref)
1774 (*session->ref)(session);
1775 __l2tp_session_unhash(session);
1781 l2tp_session_queue_purge(session); 1776 l2tp_session_queue_purge(session);
1782
1783 if (session->session_close != NULL) 1777 if (session->session_close != NULL)
1784 (*session->session_close)(session); 1778 (*session->session_close)(session);
1785 1779 if (session->deref)
1780 (*session->ref)(session);
1786 l2tp_session_dec_refcount(session); 1781 l2tp_session_dec_refcount(session);
1787
1788 return 0; 1782 return 0;
1789} 1783}
1790EXPORT_SYMBOL_GPL(l2tp_session_delete); 1784EXPORT_SYMBOL_GPL(l2tp_session_delete);
1791 1785
1792
1793/* We come here whenever a session's send_seq, cookie_len or 1786/* We come here whenever a session's send_seq, cookie_len or
1794 * l2specific_len parameters are set. 1787 * l2specific_len parameters are set.
1795 */ 1788 */
diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h
index 519b013f8b31..485a490fd990 100644
--- a/net/l2tp/l2tp_core.h
+++ b/net/l2tp/l2tp_core.h
@@ -242,6 +242,7 @@ extern int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_i
242extern void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel); 242extern void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel);
243extern int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel); 243extern int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel);
244extern struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg); 244extern struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg);
245extern void __l2tp_session_unhash(struct l2tp_session *session);
245extern int l2tp_session_delete(struct l2tp_session *session); 246extern int l2tp_session_delete(struct l2tp_session *session);
246extern void l2tp_session_free(struct l2tp_session *session); 247extern void l2tp_session_free(struct l2tp_session *session);
247extern void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, unsigned char *ptr, unsigned char *optr, u16 hdrflags, int length, int (*payload_hook)(struct sk_buff *skb)); 248extern void l2tp_recv_common(struct l2tp_session *session, struct sk_buff *skb, unsigned char *ptr, unsigned char *optr, u16 hdrflags, int length, int (*payload_hook)(struct sk_buff *skb));
diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c
index 9d0eb8c13530..637a341c1e2d 100644
--- a/net/l2tp/l2tp_ppp.c
+++ b/net/l2tp/l2tp_ppp.c
@@ -466,19 +466,12 @@ static void pppol2tp_session_close(struct l2tp_session *session)
466 */ 466 */
467static void pppol2tp_session_destruct(struct sock *sk) 467static void pppol2tp_session_destruct(struct sock *sk)
468{ 468{
469 struct l2tp_session *session; 469 struct l2tp_session *session = sk->sk_user_data;
470 470 if (session) {
471 if (sk->sk_user_data != NULL) {
472 session = sk->sk_user_data;
473 if (session == NULL)
474 goto out;
475
476 sk->sk_user_data = NULL; 471 sk->sk_user_data = NULL;
477 BUG_ON(session->magic != L2TP_SESSION_MAGIC); 472 BUG_ON(session->magic != L2TP_SESSION_MAGIC);
478 l2tp_session_dec_refcount(session); 473 l2tp_session_dec_refcount(session);
479 } 474 }
480
481out:
482 return; 475 return;
483} 476}
484 477
@@ -509,6 +502,7 @@ static int pppol2tp_release(struct socket *sock)
509 502
510 /* Purge any queued data */ 503 /* Purge any queued data */
511 if (session != NULL) { 504 if (session != NULL) {
505 __l2tp_session_unhash(session);
512 l2tp_session_queue_purge(session); 506 l2tp_session_queue_purge(session);
513 sock_put(sk); 507 sock_put(sk);
514 } 508 }