[LLC]: Use refcounting with struct llc_sap
Signed-off-by: Jochen Friedrich <jochen@scram.de>
Signed-off-by: Arnaldo Carvalho de Melo <acme@mandriva.com>
diff --git a/include/net/llc.h b/include/net/llc.h
index 8b8e2be..93e5b44 100644
--- a/include/net/llc.h
+++ b/include/net/llc.h
@@ -17,6 +17,8 @@
#include <linux/list.h>
#include <linux/spinlock.h>
+#include <asm/atomic.h>
+
struct net_device;
struct packet_type;
struct sk_buff;
@@ -44,6 +46,7 @@
unsigned char state;
unsigned char p_bit;
unsigned char f_bit;
+ atomic_t refcnt;
int (*rcv_func)(struct sk_buff *skb,
struct net_device *dev,
struct packet_type *pt,
@@ -81,7 +84,18 @@
struct net_device *dev,
struct packet_type *pt,
struct net_device *orig_dev));
-extern void llc_sap_close(struct llc_sap *sap);
+static inline void llc_sap_hold(struct llc_sap *sap)
+{
+ atomic_inc(&sap->refcnt);
+}
+
+static inline void llc_sap_put(struct llc_sap *sap)
+{
+ extern void llc_sap_close(struct llc_sap *sap);
+
+ if (atomic_dec_and_test(&sap->refcnt))
+ llc_sap_close(sap);
+}
extern struct llc_sap *llc_sap_find(unsigned char sap_value);
diff --git a/include/net/llc_conn.h b/include/net/llc_conn.h
index 8a8ff48..b288921 100644
--- a/include/net/llc_conn.h
+++ b/include/net/llc_conn.h
@@ -115,5 +115,4 @@
extern u8 llc_data_accept_state(u8 state);
extern void llc_build_offset_table(void);
-extern int llc_release_sockets(struct llc_sap *sap);
#endif /* LLC_CONN_H */
diff --git a/net/802/p8022.c b/net/802/p8022.c
index b24817c..2530f35 100644
--- a/net/802/p8022.c
+++ b/net/802/p8022.c
@@ -56,7 +56,7 @@
void unregister_8022_client(struct datalink_proto *proto)
{
- llc_sap_close(proto->sap);
+ llc_sap_put(proto->sap);
kfree(proto);
}
diff --git a/net/802/psnap.c b/net/802/psnap.c
index ab80b1f..4d63894 100644
--- a/net/802/psnap.c
+++ b/net/802/psnap.c
@@ -106,7 +106,7 @@
static void __exit snap_exit(void)
{
- llc_sap_close(snap_sap);
+ llc_sap_put(snap_sap);
}
module_exit(snap_exit);
diff --git a/net/llc/af_llc.c b/net/llc/af_llc.c
index ad9aad8..a75b8f2 100644
--- a/net/llc/af_llc.c
+++ b/net/llc/af_llc.c
@@ -21,6 +21,7 @@
* See the GNU General Public License for more details.
*/
#include <linux/config.h>
+#include <linux/compiler.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/rtnetlink.h>
@@ -188,10 +189,6 @@
if (!sock_flag(sk, SOCK_ZAPPED))
llc_sap_remove_socket(llc->sap, sk);
release_sock(sk);
- if (llc->sap && hlist_empty(&llc->sap->sk_list.list)) {
- llc_release_sockets(llc->sap);
- llc_sap_close(llc->sap);
- }
if (llc->dev)
dev_put(llc->dev);
sock_put(sk);
@@ -220,6 +217,7 @@
llc_ui_sap_last_autoport = i + 2;
goto out;
}
+ llc_sap_put(sap);
}
llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
tries++;
@@ -310,6 +308,7 @@
rc = -EBUSY; /* some other network layer is using the sap */
if (!sap)
goto out;
+ llc_sap_hold(sap);
} else {
struct llc_addr laddr, daddr;
struct sock *ask;
@@ -326,7 +325,7 @@
ask = llc_lookup_established(sap, &daddr, &laddr);
if (ask) {
sock_put(ask);
- goto out;
+ goto out_put;
}
}
llc->laddr.lsap = addr->sllc_sap;
@@ -336,6 +335,8 @@
llc_sap_add_socket(sap, sk);
sock_reset_flag(sk, SOCK_ZAPPED);
rc = 0;
+out_put:
+ llc_sap_put(sap);
out:
return rc;
}
diff --git a/net/llc/llc_c_ac.c b/net/llc/llc_c_ac.c
index c1e7510..05236c2 100644
--- a/net/llc/llc_c_ac.c
+++ b/net/llc/llc_c_ac.c
@@ -75,6 +75,7 @@
llc->dev = skb->dev;
ev->ind_prim = LLC_CONN_PRIM;
rc = 0;
+ llc_sap_put(sap);
}
return rc;
}
diff --git a/net/llc/llc_conn.c b/net/llc/llc_conn.c
index 9f0e10e..ab9e6d7 100644
--- a/net/llc/llc_conn.c
+++ b/net/llc/llc_conn.c
@@ -633,6 +633,7 @@
*/
void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk)
{
+ llc_sap_hold(sap);
write_lock_bh(&sap->sk_list.lock);
llc_sk(sk)->sap = sap;
sk_add_node(sk, &sap->sk_list.list);
@@ -652,6 +653,7 @@
write_lock_bh(&sap->sk_list.lock);
sk_del_node_init(sk);
write_unlock_bh(&sap->sk_list.lock);
+ llc_sap_put(sap);
}
/**
@@ -731,32 +733,6 @@
#endif
/**
- * llc_release_sockets - releases all sockets in a sap
- * @sap: sap to release its sockets
- *
- * Releases all connections of a sap. Returns 0 if all actions complete
- * successfully, nonzero otherwise
- */
-int llc_release_sockets(struct llc_sap *sap)
-{
- int rc = 0;
- struct sock *sk;
- struct hlist_node *node;
-
- write_lock_bh(&sap->sk_list.lock);
-
- sk_for_each(sk, node, &sap->sk_list.list) {
- llc_sk(sk)->state = LLC_CONN_STATE_TEMP;
-
- if (llc_send_disc(sk))
- rc = 1;
- }
-
- write_unlock_bh(&sap->sk_list.lock);
- return rc;
-}
-
-/**
* llc_backlog_rcv - Processes rx frames and expired timers.
* @sk: LLC sock (p8022 connection)
* @skb: queued rx frame or event
diff --git a/net/llc/llc_core.c b/net/llc/llc_core.c
index 9727455..9ccff12 100644
--- a/net/llc/llc_core.c
+++ b/net/llc/llc_core.c
@@ -40,6 +40,7 @@
sap->state = LLC_SAP_STATE_ACTIVE;
memcpy(sap->laddr.mac, llc_station_mac_sa, ETH_ALEN);
rwlock_init(&sap->sk_list.lock);
+ atomic_set(&sap->refcnt, 1);
}
return sap;
}
@@ -52,9 +53,7 @@
*/
static void llc_add_sap(struct llc_sap *sap)
{
- write_lock_bh(&llc_sap_list_lock);
list_add_tail(&sap->node, &llc_sap_list);
- write_unlock_bh(&llc_sap_list_lock);
}
/**
@@ -70,11 +69,25 @@
write_unlock_bh(&llc_sap_list_lock);
}
+struct llc_sap *__llc_sap_find(unsigned char sap_value)
+{
+ struct llc_sap* sap;
+
+ list_for_each_entry(sap, &llc_sap_list, node)
+ if (sap->laddr.lsap == sap_value)
+ goto out;
+ sap = NULL;
+out:
+ return sap;
+}
+
/**
* llc_sap_find - searchs a SAP in station
* @sap_value: sap to be found
*
* Searchs for a sap in the sap list of the LLC's station upon the sap ID.
+ * If the sap is found it will be refcounted and the user will have to do
+ * a llc_sap_put after use.
* Returns the sap or %NULL if not found.
*/
struct llc_sap *llc_sap_find(unsigned char sap_value)
@@ -82,11 +95,9 @@
struct llc_sap* sap;
read_lock_bh(&llc_sap_list_lock);
- list_for_each_entry(sap, &llc_sap_list, node)
- if (sap->laddr.lsap == sap_value)
- goto out;
- sap = NULL;
-out:
+ sap = __llc_sap_find(sap_value);
+ if (sap)
+ llc_sap_hold(sap);
read_unlock_bh(&llc_sap_list_lock);
return sap;
}
@@ -106,19 +117,20 @@
struct packet_type *pt,
struct net_device *orig_dev))
{
- struct llc_sap *sap = llc_sap_find(lsap);
+ struct llc_sap *sap = NULL;
- if (sap) { /* SAP already exists */
- sap = NULL;
+ write_lock_bh(&llc_sap_list_lock);
+ if (__llc_sap_find(lsap)) /* SAP already exists */
goto out;
- }
sap = llc_sap_alloc();
if (!sap)
goto out;
sap->laddr.lsap = lsap;
sap->rcv_func = func;
+ llc_sap_hold(sap);
llc_add_sap(sap);
out:
+ write_unlock_bh(&llc_sap_list_lock);
return sap;
}
diff --git a/net/llc/llc_input.c b/net/llc/llc_input.c
index 60c1aca..789eec4 100644
--- a/net/llc/llc_input.c
+++ b/net/llc/llc_input.c
@@ -166,17 +166,22 @@
*/
if (sap->rcv_func) {
sap->rcv_func(skb, dev, pt, orig_dev);
- goto out;
+ goto out_put;
}
dest = llc_pdu_type(skb);
if (unlikely(!dest || !llc_type_handlers[dest - 1]))
- goto drop;
+ goto drop_put;
llc_type_handlers[dest - 1](sap, skb);
+out_put:
+ llc_sap_put(sap);
out:
return 0;
drop:
kfree_skb(skb);
goto out;
+drop_put:
+ kfree_skb(skb);
+ goto out_put;
handle_station:
if (!llc_station_handler)
goto drop;