diff options
-rw-r--r-- | net/l2tp/l2tp_core.c | 75 | ||||
-rw-r--r-- | net/l2tp/l2tp_core.h | 1 | ||||
-rw-r--r-- | net/l2tp/l2tp_ppp.c | 12 |
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 | */ |
1733 | void l2tp_session_free(struct l2tp_session *session) | 1719 | void 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 | } | ||
1738 | EXPORT_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 | */ | ||
1745 | void __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 | } |
1774 | EXPORT_SYMBOL_GPL(l2tp_session_free); | 1766 | EXPORT_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 | */ |
1779 | int l2tp_session_delete(struct l2tp_session *session) | 1771 | int 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 | } |
1790 | EXPORT_SYMBOL_GPL(l2tp_session_delete); | 1784 | EXPORT_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 | |||
242 | extern void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel); | 242 | extern void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel); |
243 | extern int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel); | 243 | extern int l2tp_tunnel_delete(struct l2tp_tunnel *tunnel); |
244 | extern 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); | 244 | extern 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); |
245 | extern void __l2tp_session_unhash(struct l2tp_session *session); | ||
245 | extern int l2tp_session_delete(struct l2tp_session *session); | 246 | extern int l2tp_session_delete(struct l2tp_session *session); |
246 | extern void l2tp_session_free(struct l2tp_session *session); | 247 | extern void l2tp_session_free(struct l2tp_session *session); |
247 | extern 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)); | 248 | extern 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 | */ |
467 | static void pppol2tp_session_destruct(struct sock *sk) | 467 | static 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 | |||
481 | out: | ||
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 | } |