Commit 350a19f5 authored by Jeroen Vreeken's avatar Jeroen Vreeken Committed by David S. Miller

[AX25]: Fix ax25_cb locking.

- ax25_cb's use refcounting
- the ax25_cb list uses hlists
- Lots of socket locking.
parent 658dcd3b
......@@ -10,6 +10,7 @@
#include <linux/ax25.h>
#include <linux/spinlock.h>
#include <linux/timer.h>
#include <linux/list.h>
#include <asm/atomic.h>
#define AX25_T1CLAMPLO 1
......@@ -180,7 +181,7 @@ typedef struct ax25_dev {
} ax25_dev;
typedef struct ax25_cb {
struct ax25_cb *next;
struct hlist_node ax25_node;
ax25_address source_addr, dest_addr;
ax25_digi *digipeat;
ax25_dev *ax25_dev;
......@@ -197,17 +198,32 @@ typedef struct ax25_cb {
struct sk_buff_head ack_queue;
struct sk_buff_head frag_queue;
unsigned char window;
struct timer_list timer;
struct timer_list timer, dtimer;
struct sock *sk; /* Backlink to socket */
atomic_t refcount;
} ax25_cb;
#define ax25_sk(__sk) ((ax25_cb *)(__sk)->sk_protinfo)
#define ax25_for_each(__ax25, node, list) \
hlist_for_each_entry(__ax25, node, list, ax25_node)
#define ax25_cb_hold(__ax25) \
atomic_inc(&((__ax25)->refcount))
static __inline__ void ax25_cb_put(ax25_cb *ax25)
{
if (atomic_dec_and_test(&ax25->refcount)) {
if (ax25->digipeat)
kfree(ax25->digipeat);
kfree(ax25);
}
}
/* af_ax25.c */
extern ax25_cb *ax25_list;
extern struct hlist_head ax25_list;
extern spinlock_t ax25_list_lock;
extern void ax25_free_cb(ax25_cb *);
extern void ax25_insert_socket(ax25_cb *);
extern void ax25_cb_add(ax25_cb *);
struct sock *ax25_find_listener(ax25_address *, int, struct net_device *, int);
struct sock *ax25_get_socket(ax25_address *, ax25_address *, int);
extern ax25_cb *ax25_find_cb(ax25_address *, ax25_address *, ax25_digi *, struct net_device *);
......
This diff is collapsed.
......@@ -65,6 +65,7 @@ static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int framet
ax25->state = AX25_STATE_3;
ax25->n2count = 0;
if (ax25->sk != NULL) {
bh_lock_sock(ax25->sk);
ax25->sk->sk_state = TCP_ESTABLISHED;
/*
* For WAIT_SABM connections we will produce an accept
......@@ -72,6 +73,7 @@ static int ax25_ds_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int framet
*/
if (!sock_flag(ax25->sk, SOCK_DEAD))
ax25->sk->sk_state_change(ax25->sk);
bh_unlock_sock(ax25->sk);
}
ax25_dama_on(ax25);
......
......@@ -40,6 +40,7 @@ void ax25_ds_nr_error_recovery(ax25_cb *ax25)
void ax25_ds_enquiry_response(ax25_cb *ax25)
{
ax25_cb *ax25o;
struct hlist_node *node;
/* Please note that neither DK4EGs nor DG2FEFs
* DAMA spec mention the following behaviour as seen
......@@ -80,7 +81,7 @@ void ax25_ds_enquiry_response(ax25_cb *ax25)
ax25_ds_set_timer(ax25->ax25_dev);
spin_lock_bh(&ax25_list_lock);
for (ax25o = ax25_list; ax25o != NULL; ax25o = ax25o->next) {
ax25_for_each(ax25o, node, &ax25_list) {
if (ax25o == ax25)
continue;
......@@ -160,9 +161,10 @@ static int ax25_check_dama_slave(ax25_dev *ax25_dev)
{
ax25_cb *ax25;
int res = 0;
struct hlist_node *node;
spin_lock_bh(&ax25_list_lock);
for (ax25 = ax25_list; ax25 != NULL ; ax25 = ax25->next)
ax25_for_each(ax25, node, &ax25_list)
if (ax25->ax25_dev == ax25_dev && (ax25->condition & AX25_COND_DAMA_MODE) && ax25->state > AX25_STATE_1) {
res = 1;
break;
......
......@@ -74,6 +74,7 @@ static void ax25_ds_timeout(unsigned long arg)
{
ax25_dev *ax25_dev = (struct ax25_dev *) arg;
ax25_cb *ax25;
struct hlist_node *node;
if (ax25_dev == NULL || !ax25_dev->dama.slave)
return; /* Yikes! */
......@@ -84,7 +85,7 @@ static void ax25_ds_timeout(unsigned long arg)
}
spin_lock_bh(&ax25_list_lock);
for (ax25=ax25_list; ax25 != NULL; ax25 = ax25->next) {
ax25_for_each(ax25, node, &ax25_list) {
if (ax25->ax25_dev != ax25_dev || !(ax25->condition & AX25_COND_DAMA_MODE))
continue;
......@@ -98,14 +99,25 @@ static void ax25_ds_timeout(unsigned long arg)
void ax25_ds_heartbeat_expiry(ax25_cb *ax25)
{
struct sock *sk=ax25->sk;
if (sk)
bh_lock_sock(sk);
switch (ax25->state) {
case AX25_STATE_0:
/* Magic here: If we listen() and a new link dies before it
is accepted() it isn't 'dead' so doesn't get removed. */
if (!ax25->sk || sock_flag(ax25->sk, SOCK_DESTROY) ||
(ax25->sk->sk_state == TCP_LISTEN &&
sock_flag(ax25->sk, SOCK_DEAD))) {
if (!sk || sock_flag(sk, SOCK_DESTROY) ||
(sk->sk_state == TCP_LISTEN &&
sock_flag(sk, SOCK_DEAD))) {
if (sk) {
sock_hold(sk);
ax25_destroy_socket(ax25);
sock_put(sk);
bh_unlock_sock(sk);
} else
ax25_destroy_socket(ax25);
return;
}
......@@ -115,9 +127,9 @@ void ax25_ds_heartbeat_expiry(ax25_cb *ax25)
/*
* Check the state of the receive buffer.
*/
if (ax25->sk != NULL) {
if (atomic_read(&ax25->sk->sk_rmem_alloc) <
(ax25->sk->sk_rcvbuf / 2) &&
if (sk != NULL) {
if (atomic_read(&sk->sk_rmem_alloc) <
(sk->sk_rcvbuf / 2) &&
(ax25->condition & AX25_COND_OWN_RX_BUSY)) {
ax25->condition &= ~AX25_COND_OWN_RX_BUSY;
ax25->condition &= ~AX25_COND_ACK_PENDING;
......@@ -127,6 +139,9 @@ void ax25_ds_heartbeat_expiry(ax25_cb *ax25)
break;
}
if (sk)
bh_unlock_sock(sk);
ax25_start_heartbeat(ax25);
}
......@@ -157,6 +172,7 @@ void ax25_ds_idletimer_expiry(ax25_cb *ax25)
ax25_stop_t3timer(ax25);
if (ax25->sk != NULL) {
bh_lock_sock(ax25->sk);
ax25->sk->sk_state = TCP_CLOSE;
ax25->sk->sk_err = 0;
ax25->sk->sk_shutdown |= SEND_SHUTDOWN;
......@@ -164,6 +180,7 @@ void ax25_ds_idletimer_expiry(ax25_cb *ax25)
ax25->sk->sk_state_change(ax25->sk);
sock_set_flag(ax25->sk, SOCK_DEAD);
}
bh_lock_sock(ax25->sk);
}
}
......
......@@ -147,6 +147,7 @@ int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
}
if (ax25->sk != NULL && ax25->ax25_dev->values[AX25_VALUES_CONMODE] == 2) {
bh_lock_sock(ax25->sk);
if ((!ax25->pidincl && ax25->sk->sk_protocol == pid) ||
ax25->pidincl) {
if (sock_queue_rcv_skb(ax25->sk, skb) == 0)
......@@ -154,6 +155,7 @@ int ax25_rx_iframe(ax25_cb *ax25, struct sk_buff *skb)
else
ax25->condition |= AX25_COND_OWN_RX_BUSY;
}
bh_unlock_sock(ax25->sk);
}
return queued;
......@@ -329,6 +331,7 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev,
if (ax25_process_rx_frame(ax25, skb, type, dama) == 0)
kfree_skb(skb);
ax25_cb_put(ax25);
return 0;
}
......@@ -357,11 +360,14 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev,
sk = ax25_find_listener(next_digi, 1, dev, SOCK_SEQPACKET);
if (sk != NULL) {
bh_lock_sock(sk);
if (sk->sk_ack_backlog == sk->sk_max_ack_backlog ||
(make = ax25_make_new(sk, ax25_dev)) == NULL) {
if (mine)
ax25_return_dm(dev, &src, &dest, &dp);
kfree_skb(skb);
bh_unlock_sock(sk);
sock_put(sk);
return 0;
}
......@@ -374,6 +380,8 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev,
make->sk_pair = sk;
sk->sk_ack_backlog++;
bh_unlock_sock(sk);
sock_put(sk);
} else {
if (!mine) {
kfree_skb(skb);
......@@ -429,7 +437,7 @@ static int ax25_rcv(struct sk_buff *skb, struct net_device *dev,
ax25->state = AX25_STATE_3;
ax25_insert_socket(ax25);
ax25_cb_add(ax25);
ax25_start_heartbeat(ax25);
ax25_start_t3timer(ax25);
......
......@@ -107,6 +107,7 @@ int ax25_rebuild_header(struct sk_buff *skb)
ax25_address *src, *dst;
ax25_dev *ax25_dev;
ax25_route _route, *route = &_route;
ax25_cb *ax25;
dst = (ax25_address *)(bp + 1);
src = (ax25_address *)(bp + 8);
......@@ -167,9 +168,14 @@ int ax25_rebuild_header(struct sk_buff *skb)
skb_pull(ourskb, AX25_HEADER_LEN - 1); /* Keep PID */
ourskb->nh.raw = ourskb->data;
ax25_send_frame(ourskb, ax25_dev->values[AX25_VALUES_PACLEN], &src_c,
&dst_c, route->digipeat, dev);
ax25=ax25_send_frame(
ourskb,
ax25_dev->values[AX25_VALUES_PACLEN],
&src_c,
&dst_c, route->digipeat, dev);
if (ax25) {
ax25_cb_put(ax25);
}
goto put;
}
}
......
......@@ -71,7 +71,7 @@ ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax2
if (digi != NULL) {
if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
ax25_free_cb(ax25);
ax25_cb_put(ax25);
return NULL;
}
memcpy(ax25->digipeat, digi, sizeof(ax25_digi));
......@@ -93,7 +93,7 @@ ax25_cb *ax25_send_frame(struct sk_buff *skb, int paclen, ax25_address *src, ax2
#endif
}
ax25_insert_socket(ax25);
ax25_cb_add(ax25);
ax25->state = AX25_STATE_1;
......
......@@ -162,6 +162,7 @@ static void ax25_rt_destroy(ax25_route *ax25_rt)
if (ax25_rt->digipeat != NULL)
kfree(ax25_rt->digipeat);
kfree(ax25_rt);
return;
}
/*
......@@ -434,8 +435,11 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
ax25_adjust_path(addr, ax25->digipeat);
}
if (ax25->sk != NULL)
if (ax25->sk != NULL) {
bh_lock_sock(ax25->sk);
ax25->sk->sk_zapped = 0;
bh_unlock_sock(ax25->sk);
}
put:
ax25_put_route(ax25_rt);
......
......@@ -73,10 +73,12 @@ static int ax25_std_state1_machine(ax25_cb *ax25, struct sk_buff *skb, int frame
ax25->state = AX25_STATE_3;
ax25->n2count = 0;
if (ax25->sk != NULL) {
bh_lock_sock(ax25->sk);
ax25->sk->sk_state = TCP_ESTABLISHED;
/* For WAIT_SABM connections we will produce an accept ready socket here */
if (!sock_flag(ax25->sk, SOCK_DEAD))
ax25->sk->sk_state_change(ax25->sk);
bh_unlock_sock(ax25->sk);
}
}
break;
......
......@@ -33,13 +33,24 @@
void ax25_std_heartbeat_expiry(ax25_cb *ax25)
{
struct sock *sk=ax25->sk;
if (sk)
bh_lock_sock(sk);
switch (ax25->state) {
case AX25_STATE_0:
/* Magic here: If we listen() and a new link dies before it
is accepted() it isn't 'dead' so doesn't get removed. */
if (!ax25->sk || sock_flag(ax25->sk, SOCK_DESTROY) ||
(ax25->sk->sk_state == TCP_LISTEN &&
sock_flag(ax25->sk, SOCK_DEAD))) {
if (!sk || sock_flag(sk, SOCK_DESTROY) ||
(sk->sk_state == TCP_LISTEN &&
sock_flag(sk, SOCK_DEAD))) {
if (sk) {
sock_hold(sk);
ax25_destroy_socket(ax25);
bh_unlock_sock(sk);
sock_put(sk);
} else
ax25_destroy_socket(ax25);
return;
}
......@@ -50,9 +61,9 @@ void ax25_std_heartbeat_expiry(ax25_cb *ax25)
/*
* Check the state of the receive buffer.
*/
if (ax25->sk != NULL) {
if (atomic_read(&ax25->sk->sk_rmem_alloc) <
(ax25->sk->sk_rcvbuf / 2) &&
if (sk != NULL) {
if (atomic_read(&sk->sk_rmem_alloc) <
(sk->sk_rcvbuf / 2) &&
(ax25->condition & AX25_COND_OWN_RX_BUSY)) {
ax25->condition &= ~AX25_COND_OWN_RX_BUSY;
ax25->condition &= ~AX25_COND_ACK_PENDING;
......@@ -62,6 +73,9 @@ void ax25_std_heartbeat_expiry(ax25_cb *ax25)
}
}
if (sk)
bh_unlock_sock(sk);
ax25_start_heartbeat(ax25);
}
......@@ -94,6 +108,7 @@ void ax25_std_idletimer_expiry(ax25_cb *ax25)
ax25_stop_t3timer(ax25);
if (ax25->sk != NULL) {
bh_lock_sock(ax25->sk);
ax25->sk->sk_state = TCP_CLOSE;
ax25->sk->sk_err = 0;
ax25->sk->sk_shutdown |= SEND_SHUTDOWN;
......@@ -101,6 +116,7 @@ void ax25_std_idletimer_expiry(ax25_cb *ax25)
ax25->sk->sk_state_change(ax25->sk);
sock_set_flag(ax25->sk, SOCK_DEAD);
}
bh_unlock_sock(ax25->sk);
}
}
......
......@@ -141,13 +141,10 @@ static void ax25_heartbeat_expiry(unsigned long param)
{
int proto = AX25_PROTO_STD_SIMPLEX;
ax25_cb *ax25 = (ax25_cb *)param;
struct sock *sk = ax25->sk;
if (ax25->ax25_dev)
proto = ax25->ax25_dev->values[AX25_VALUES_PROTOCOL];
bh_lock_sock(sk);
switch (proto) {
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
......@@ -163,15 +160,12 @@ static void ax25_heartbeat_expiry(unsigned long param)
break;
#endif
}
bh_unlock_sock(sk);
}
static void ax25_t1timer_expiry(unsigned long param)
{
ax25_cb *ax25 = (ax25_cb *)param;
struct sock *sk = ax25->sk;
bh_lock_sock(sk);
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
......@@ -185,15 +179,12 @@ static void ax25_t1timer_expiry(unsigned long param)
break;
#endif
}
bh_unlock_sock(sk);
}
static void ax25_t2timer_expiry(unsigned long param)
{
ax25_cb *ax25 = (ax25_cb *)param;
struct sock *sk = ax25->sk;
bh_lock_sock(sk);
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
......@@ -207,15 +198,12 @@ static void ax25_t2timer_expiry(unsigned long param)
break;
#endif
}
bh_unlock_sock(sk);
}
static void ax25_t3timer_expiry(unsigned long param)
{
ax25_cb *ax25 = (ax25_cb *)param;
struct sock *sk = ax25->sk;
bh_lock_sock(sk);
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
......@@ -231,15 +219,12 @@ static void ax25_t3timer_expiry(unsigned long param)
break;
#endif
}
bh_unlock_sock(sk);
}
static void ax25_idletimer_expiry(unsigned long param)
{
ax25_cb *ax25 = (ax25_cb *)param;
struct sock *sk = ax25->sk;
bh_lock_sock(sk);
switch (ax25->ax25_dev->values[AX25_VALUES_PROTOCOL]) {
case AX25_PROTO_STD_SIMPLEX:
case AX25_PROTO_STD_DUPLEX:
......@@ -255,5 +240,4 @@ static void ax25_idletimer_expiry(unsigned long param)
break;
#endif
}
bh_unlock_sock(sk);
}
......@@ -204,8 +204,10 @@ void ax25_register_sysctl(void)
for (ax25_table_size = sizeof(ctl_table), ax25_dev = ax25_dev_list; ax25_dev != NULL; ax25_dev = ax25_dev->next)
ax25_table_size += sizeof(ctl_table);
if ((ax25_table = kmalloc(ax25_table_size, GFP_ATOMIC)) == NULL)
if ((ax25_table = kmalloc(ax25_table_size, GFP_ATOMIC)) == NULL) {
spin_unlock_bh(&ax25_dev_lock);
return;
}
memset(ax25_table, 0x00, ax25_table_size);
......
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