[LLC]: Use refcounting with struct llc_sap

Signed-off-by: default avatarJochen Friedrich <jochen@scram.de>
Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@mandriva.com>
parent 04e4223f
...@@ -17,6 +17,8 @@ ...@@ -17,6 +17,8 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <asm/atomic.h>
struct net_device; struct net_device;
struct packet_type; struct packet_type;
struct sk_buff; struct sk_buff;
...@@ -44,6 +46,7 @@ struct llc_sap { ...@@ -44,6 +46,7 @@ struct llc_sap {
unsigned char state; unsigned char state;
unsigned char p_bit; unsigned char p_bit;
unsigned char f_bit; unsigned char f_bit;
atomic_t refcnt;
int (*rcv_func)(struct sk_buff *skb, int (*rcv_func)(struct sk_buff *skb,
struct net_device *dev, struct net_device *dev,
struct packet_type *pt, struct packet_type *pt,
...@@ -81,7 +84,18 @@ extern struct llc_sap *llc_sap_open(unsigned char lsap, ...@@ -81,7 +84,18 @@ extern struct llc_sap *llc_sap_open(unsigned char lsap,
struct net_device *dev, struct net_device *dev,
struct packet_type *pt, struct packet_type *pt,
struct net_device *orig_dev)); 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); extern struct llc_sap *llc_sap_find(unsigned char sap_value);
......
...@@ -115,5 +115,4 @@ extern void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk); ...@@ -115,5 +115,4 @@ extern void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk);
extern u8 llc_data_accept_state(u8 state); extern u8 llc_data_accept_state(u8 state);
extern void llc_build_offset_table(void); extern void llc_build_offset_table(void);
extern int llc_release_sockets(struct llc_sap *sap);
#endif /* LLC_CONN_H */ #endif /* LLC_CONN_H */
...@@ -56,7 +56,7 @@ struct datalink_proto *register_8022_client(unsigned char type, ...@@ -56,7 +56,7 @@ struct datalink_proto *register_8022_client(unsigned char type,
void unregister_8022_client(struct datalink_proto *proto) void unregister_8022_client(struct datalink_proto *proto)
{ {
llc_sap_close(proto->sap); llc_sap_put(proto->sap);
kfree(proto); kfree(proto);
} }
......
...@@ -106,7 +106,7 @@ module_init(snap_init); ...@@ -106,7 +106,7 @@ module_init(snap_init);
static void __exit snap_exit(void) static void __exit snap_exit(void)
{ {
llc_sap_close(snap_sap); llc_sap_put(snap_sap);
} }
module_exit(snap_exit); module_exit(snap_exit);
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
* See the GNU General Public License for more details. * See the GNU General Public License for more details.
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/compiler.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/rtnetlink.h> #include <linux/rtnetlink.h>
...@@ -188,10 +189,6 @@ static int llc_ui_release(struct socket *sock) ...@@ -188,10 +189,6 @@ static int llc_ui_release(struct socket *sock)
if (!sock_flag(sk, SOCK_ZAPPED)) if (!sock_flag(sk, SOCK_ZAPPED))
llc_sap_remove_socket(llc->sap, sk); llc_sap_remove_socket(llc->sap, sk);
release_sock(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) if (llc->dev)
dev_put(llc->dev); dev_put(llc->dev);
sock_put(sk); sock_put(sk);
...@@ -220,6 +217,7 @@ static int llc_ui_autoport(void) ...@@ -220,6 +217,7 @@ static int llc_ui_autoport(void)
llc_ui_sap_last_autoport = i + 2; llc_ui_sap_last_autoport = i + 2;
goto out; goto out;
} }
llc_sap_put(sap);
} }
llc_ui_sap_last_autoport = LLC_SAP_DYN_START; llc_ui_sap_last_autoport = LLC_SAP_DYN_START;
tries++; tries++;
...@@ -310,6 +308,7 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen) ...@@ -310,6 +308,7 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen)
rc = -EBUSY; /* some other network layer is using the sap */ rc = -EBUSY; /* some other network layer is using the sap */
if (!sap) if (!sap)
goto out; goto out;
llc_sap_hold(sap);
} else { } else {
struct llc_addr laddr, daddr; struct llc_addr laddr, daddr;
struct sock *ask; struct sock *ask;
...@@ -326,7 +325,7 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen) ...@@ -326,7 +325,7 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen)
ask = llc_lookup_established(sap, &daddr, &laddr); ask = llc_lookup_established(sap, &daddr, &laddr);
if (ask) { if (ask) {
sock_put(ask); sock_put(ask);
goto out; goto out_put;
} }
} }
llc->laddr.lsap = addr->sllc_sap; llc->laddr.lsap = addr->sllc_sap;
...@@ -336,6 +335,8 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen) ...@@ -336,6 +335,8 @@ static int llc_ui_bind(struct socket *sock, struct sockaddr *uaddr, int addrlen)
llc_sap_add_socket(sap, sk); llc_sap_add_socket(sap, sk);
sock_reset_flag(sk, SOCK_ZAPPED); sock_reset_flag(sk, SOCK_ZAPPED);
rc = 0; rc = 0;
out_put:
llc_sap_put(sap);
out: out:
return rc; return rc;
} }
......
...@@ -75,6 +75,7 @@ int llc_conn_ac_conn_ind(struct sock *sk, struct sk_buff *skb) ...@@ -75,6 +75,7 @@ int llc_conn_ac_conn_ind(struct sock *sk, struct sk_buff *skb)
llc->dev = skb->dev; llc->dev = skb->dev;
ev->ind_prim = LLC_CONN_PRIM; ev->ind_prim = LLC_CONN_PRIM;
rc = 0; rc = 0;
llc_sap_put(sap);
} }
return rc; return rc;
} }
......
...@@ -633,6 +633,7 @@ static int llc_find_offset(int state, int ev_type) ...@@ -633,6 +633,7 @@ static int llc_find_offset(int state, int ev_type)
*/ */
void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk) void llc_sap_add_socket(struct llc_sap *sap, struct sock *sk)
{ {
llc_sap_hold(sap);
write_lock_bh(&sap->sk_list.lock); write_lock_bh(&sap->sk_list.lock);
llc_sk(sk)->sap = sap; llc_sk(sk)->sap = sap;
sk_add_node(sk, &sap->sk_list.list); sk_add_node(sk, &sap->sk_list.list);
...@@ -652,6 +653,7 @@ void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk) ...@@ -652,6 +653,7 @@ void llc_sap_remove_socket(struct llc_sap *sap, struct sock *sk)
write_lock_bh(&sap->sk_list.lock); write_lock_bh(&sap->sk_list.lock);
sk_del_node_init(sk); sk_del_node_init(sk);
write_unlock_bh(&sap->sk_list.lock); write_unlock_bh(&sap->sk_list.lock);
llc_sap_put(sap);
} }
/** /**
...@@ -730,32 +732,6 @@ drop: ...@@ -730,32 +732,6 @@ drop:
static atomic_t llc_sock_nr; static atomic_t llc_sock_nr;
#endif #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. * llc_backlog_rcv - Processes rx frames and expired timers.
* @sk: LLC sock (p8022 connection) * @sk: LLC sock (p8022 connection)
......
...@@ -40,6 +40,7 @@ static struct llc_sap *llc_sap_alloc(void) ...@@ -40,6 +40,7 @@ static struct llc_sap *llc_sap_alloc(void)
sap->state = LLC_SAP_STATE_ACTIVE; sap->state = LLC_SAP_STATE_ACTIVE;
memcpy(sap->laddr.mac, llc_station_mac_sa, ETH_ALEN); memcpy(sap->laddr.mac, llc_station_mac_sa, ETH_ALEN);
rwlock_init(&sap->sk_list.lock); rwlock_init(&sap->sk_list.lock);
atomic_set(&sap->refcnt, 1);
} }
return sap; return sap;
} }
...@@ -52,9 +53,7 @@ static struct llc_sap *llc_sap_alloc(void) ...@@ -52,9 +53,7 @@ static struct llc_sap *llc_sap_alloc(void)
*/ */
static void llc_add_sap(struct llc_sap *sap) static void llc_add_sap(struct llc_sap *sap)
{ {
write_lock_bh(&llc_sap_list_lock);
list_add_tail(&sap->node, &llc_sap_list); list_add_tail(&sap->node, &llc_sap_list);
write_unlock_bh(&llc_sap_list_lock);
} }
/** /**
...@@ -70,11 +69,25 @@ static void llc_del_sap(struct llc_sap *sap) ...@@ -70,11 +69,25 @@ static void llc_del_sap(struct llc_sap *sap)
write_unlock_bh(&llc_sap_list_lock); 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 * llc_sap_find - searchs a SAP in station
* @sap_value: sap to be found * @sap_value: sap to be found
* *
* Searchs for a sap in the sap list of the LLC's station upon the sap ID. * 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. * Returns the sap or %NULL if not found.
*/ */
struct llc_sap *llc_sap_find(unsigned char sap_value) struct llc_sap *llc_sap_find(unsigned char sap_value)
...@@ -82,11 +95,9 @@ struct llc_sap *llc_sap_find(unsigned char sap_value) ...@@ -82,11 +95,9 @@ struct llc_sap *llc_sap_find(unsigned char sap_value)
struct llc_sap* sap; struct llc_sap* sap;
read_lock_bh(&llc_sap_list_lock); read_lock_bh(&llc_sap_list_lock);
list_for_each_entry(sap, &llc_sap_list, node) sap = __llc_sap_find(sap_value);
if (sap->laddr.lsap == sap_value) if (sap)
goto out; llc_sap_hold(sap);
sap = NULL;
out:
read_unlock_bh(&llc_sap_list_lock); read_unlock_bh(&llc_sap_list_lock);
return sap; return sap;
} }
...@@ -106,19 +117,20 @@ struct llc_sap *llc_sap_open(unsigned char lsap, ...@@ -106,19 +117,20 @@ struct llc_sap *llc_sap_open(unsigned char lsap,
struct packet_type *pt, struct packet_type *pt,
struct net_device *orig_dev)) struct net_device *orig_dev))
{ {
struct llc_sap *sap = llc_sap_find(lsap); struct llc_sap *sap = NULL;
if (sap) { /* SAP already exists */ write_lock_bh(&llc_sap_list_lock);
sap = NULL; if (__llc_sap_find(lsap)) /* SAP already exists */
goto out; goto out;
}
sap = llc_sap_alloc(); sap = llc_sap_alloc();
if (!sap) if (!sap)
goto out; goto out;
sap->laddr.lsap = lsap; sap->laddr.lsap = lsap;
sap->rcv_func = func; sap->rcv_func = func;
llc_sap_hold(sap);
llc_add_sap(sap); llc_add_sap(sap);
out: out:
write_unlock_bh(&llc_sap_list_lock);
return sap; return sap;
} }
......
...@@ -166,17 +166,22 @@ int llc_rcv(struct sk_buff *skb, struct net_device *dev, ...@@ -166,17 +166,22 @@ int llc_rcv(struct sk_buff *skb, struct net_device *dev,
*/ */
if (sap->rcv_func) { if (sap->rcv_func) {
sap->rcv_func(skb, dev, pt, orig_dev); sap->rcv_func(skb, dev, pt, orig_dev);
goto out; goto out_put;
} }
dest = llc_pdu_type(skb); dest = llc_pdu_type(skb);
if (unlikely(!dest || !llc_type_handlers[dest - 1])) if (unlikely(!dest || !llc_type_handlers[dest - 1]))
goto drop; goto drop_put;
llc_type_handlers[dest - 1](sap, skb); llc_type_handlers[dest - 1](sap, skb);
out_put:
llc_sap_put(sap);
out: out:
return 0; return 0;
drop: drop:
kfree_skb(skb); kfree_skb(skb);
goto out; goto out;
drop_put:
kfree_skb(skb);
goto out_put;
handle_station: handle_station:
if (!llc_station_handler) if (!llc_station_handler)
goto drop; goto drop;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment