Commit b498a583 authored by Arnaldo Carvalho de Melo's avatar Arnaldo Carvalho de Melo Committed by David S. Miller

o X25: use refcnts and protect x25_neigh structs and list

Simplify some other code.
parent 92acfab5
...@@ -118,7 +118,7 @@ struct x25_route { ...@@ -118,7 +118,7 @@ struct x25_route {
}; };
struct x25_neigh { struct x25_neigh {
struct x25_neigh *next; struct list_head node;
struct net_device *dev; struct net_device *dev;
unsigned int state; unsigned int state;
unsigned int extended; unsigned int extended;
...@@ -126,6 +126,7 @@ struct x25_neigh { ...@@ -126,6 +126,7 @@ struct x25_neigh {
unsigned long t20; unsigned long t20;
struct timer_list t20timer; struct timer_list t20timer;
unsigned long global_facil_mask; unsigned long global_facil_mask;
atomic_t refcnt;
}; };
struct x25_opt { struct x25_opt {
...@@ -199,6 +200,18 @@ extern int x25_subscr_ioctl(unsigned int, void *); ...@@ -199,6 +200,18 @@ extern int x25_subscr_ioctl(unsigned int, void *);
extern struct x25_neigh *x25_get_neigh(struct net_device *); extern struct x25_neigh *x25_get_neigh(struct net_device *);
extern void x25_link_free(void); extern void x25_link_free(void);
/* x25_neigh.c */
static __inline__ void x25_neigh_hold(struct x25_neigh *nb)
{
atomic_inc(&nb->refcnt);
}
static __inline__ void x25_neigh_put(struct x25_neigh *nb)
{
if (atomic_dec_and_test(&nb->refcnt))
kfree(nb);
}
/* x25_out.c */ /* x25_out.c */
extern int x25_output(struct sock *, struct sk_buff *); extern int x25_output(struct sock *, struct sk_buff *);
extern void x25_kick(struct sock *); extern void x25_kick(struct sock *);
......
...@@ -188,7 +188,7 @@ static void x25_kill_by_device(struct net_device *dev) ...@@ -188,7 +188,7 @@ static void x25_kill_by_device(struct net_device *dev)
static int x25_device_event(struct notifier_block *this, unsigned long event, void *ptr) static int x25_device_event(struct notifier_block *this, unsigned long event, void *ptr)
{ {
struct net_device *dev = ptr; struct net_device *dev = ptr;
struct x25_neigh *neigh; struct x25_neigh *nb;
if (dev->type == ARPHRD_X25 if (dev->type == ARPHRD_X25
#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) #if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
...@@ -200,8 +200,11 @@ static int x25_device_event(struct notifier_block *this, unsigned long event, vo ...@@ -200,8 +200,11 @@ static int x25_device_event(struct notifier_block *this, unsigned long event, vo
x25_link_device_up(dev); x25_link_device_up(dev);
break; break;
case NETDEV_GOING_DOWN: case NETDEV_GOING_DOWN:
if ((neigh = x25_get_neigh(dev))) nb = x25_get_neigh(dev);
x25_terminate_link(neigh); if (nb) {
x25_terminate_link(nb);
x25_neigh_put(nb);
}
break; break;
case NETDEV_DOWN: case NETDEV_DOWN:
x25_kill_by_device(dev); x25_kill_by_device(dev);
...@@ -255,7 +258,7 @@ static struct sock *x25_find_listener(struct x25_address *addr) ...@@ -255,7 +258,7 @@ static struct sock *x25_find_listener(struct x25_address *addr)
/* /*
* Find a connected X.25 socket given my LCI and neighbour. * Find a connected X.25 socket given my LCI and neighbour.
*/ */
struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *neigh) struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *nb)
{ {
struct sock *s; struct sock *s;
unsigned long flags; unsigned long flags;
...@@ -264,7 +267,7 @@ struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *neigh) ...@@ -264,7 +267,7 @@ struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *neigh)
cli(); cli();
for (s = x25_list; s; s = s->next) for (s = x25_list; s; s = s->next)
if (x25_sk(s)->lci == lci && x25_sk(s)->neighbour == neigh) if (x25_sk(s)->lci == lci && x25_sk(s)->neighbour == nb)
break; break;
restore_flags(flags); restore_flags(flags);
...@@ -274,11 +277,11 @@ struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *neigh) ...@@ -274,11 +277,11 @@ struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *neigh)
/* /*
* Find a unique LCI for a given device. * Find a unique LCI for a given device.
*/ */
unsigned int x25_new_lci(struct x25_neigh *neigh) unsigned int x25_new_lci(struct x25_neigh *nb)
{ {
unsigned int lci = 1; unsigned int lci = 1;
while (x25_find_socket(lci, neigh)) while (x25_find_socket(lci, nb))
if (++lci == 4096) { if (++lci == 4096) {
lci = 0; lci = 0;
break; break;
...@@ -631,11 +634,11 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len ...@@ -631,11 +634,11 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len
x25->lci = x25_new_lci(x25->neighbour); x25->lci = x25_new_lci(x25->neighbour);
if (!x25->lci) if (!x25->lci)
goto out_put_route; goto out_put_neigh;
rc = -EINVAL; rc = -EINVAL;
if (sk->zapped) /* Must bind first - autobinding does not work */ if (sk->zapped) /* Must bind first - autobinding does not work */
goto out_put_route; goto out_put_neigh;
if (!strcmp(x25->source_addr.x25_addr, null_x25_address.x25_addr)) if (!strcmp(x25->source_addr.x25_addr, null_x25_address.x25_addr))
memset(&x25->source_addr, '\0', X25_ADDR_LEN); memset(&x25->source_addr, '\0', X25_ADDR_LEN);
...@@ -656,7 +659,7 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len ...@@ -656,7 +659,7 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len
/* Now the loop */ /* Now the loop */
rc = -EINPROGRESS; rc = -EINPROGRESS;
if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
goto out_put_route; goto out_put_neigh;
cli(); /* To avoid races on the sleep */ cli(); /* To avoid races on the sleep */
...@@ -681,6 +684,9 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len ...@@ -681,6 +684,9 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len
rc = 0; rc = 0;
out_unlock: out_unlock:
sti(); sti();
out_put_neigh:
if (rc)
x25_neigh_put(x25->neighbour);
out_put_route: out_put_route:
x25_route_put(rt); x25_route_put(rt);
out: out:
...@@ -758,7 +764,7 @@ static int x25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_l ...@@ -758,7 +764,7 @@ static int x25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_l
return 0; return 0;
} }
int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *neigh, unsigned int lci) int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *nb, unsigned int lci)
{ {
struct sock *sk; struct sock *sk;
struct sock *make; struct sock *make;
...@@ -800,7 +806,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *neigh, unsigned i ...@@ -800,7 +806,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *neigh, unsigned i
* on certain facilties * on certain facilties
*/ */
x25_limit_facilities(&facilities,neigh); x25_limit_facilities(&facilities, nb);
/* /*
* Try to create a new socket. * Try to create a new socket.
...@@ -821,7 +827,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *neigh, unsigned i ...@@ -821,7 +827,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *neigh, unsigned i
makex25->lci = lci; makex25->lci = lci;
makex25->dest_addr = dest_addr; makex25->dest_addr = dest_addr;
makex25->source_addr = source_addr; makex25->source_addr = source_addr;
makex25->neighbour = neigh; makex25->neighbour = nb;
makex25->facilities = facilities; makex25->facilities = facilities;
makex25->vc_facil_mask = x25_sk(sk)->vc_facil_mask; makex25->vc_facil_mask = x25_sk(sk)->vc_facil_mask;
...@@ -853,7 +859,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *neigh, unsigned i ...@@ -853,7 +859,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *neigh, unsigned i
return rc; return rc;
out_clear_request: out_clear_request:
rc = 0; rc = 0;
x25_transmit_clear_request(neigh, lci, 0x01); x25_transmit_clear_request(nb, lci, 0x01);
goto out; goto out;
} }
...@@ -1358,12 +1364,12 @@ struct notifier_block x25_dev_notifier = { ...@@ -1358,12 +1364,12 @@ struct notifier_block x25_dev_notifier = {
.notifier_call = x25_device_event, .notifier_call = x25_device_event,
}; };
void x25_kill_by_neigh(struct x25_neigh *neigh) void x25_kill_by_neigh(struct x25_neigh *nb)
{ {
struct sock *s; struct sock *s;
for (s = x25_list; s; s = s->next) for (s = x25_list; s; s = s->next)
if (x25_sk(s)->neighbour == neigh) if (x25_sk(s)->neighbour == nb)
x25_disconnect(s, ENETUNREACH, 0, 0); x25_disconnect(s, ENETUNREACH, 0, 0);
} }
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
#include <linux/if_arp.h> #include <linux/if_arp.h>
#include <net/x25.h> #include <net/x25.h>
static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *neigh) static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *nb)
{ {
struct sock *sk; struct sock *sk;
unsigned short frametype; unsigned short frametype;
...@@ -58,14 +58,14 @@ static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *neigh) ...@@ -58,14 +58,14 @@ static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *neigh)
* frame. * frame.
*/ */
if (lci == 0) { if (lci == 0) {
x25_link_control(skb, neigh, frametype); x25_link_control(skb, nb, frametype);
return 0; return 0;
} }
/* /*
* Find an existing socket. * Find an existing socket.
*/ */
if ((sk = x25_find_socket(lci, neigh)) != NULL) { if ((sk = x25_find_socket(lci, nb)) != NULL) {
int queued = 1; int queued = 1;
skb->h.raw = skb->data; skb->h.raw = skb->data;
...@@ -83,91 +83,87 @@ static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *neigh) ...@@ -83,91 +83,87 @@ static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *neigh)
* Is is a Call Request ? if so process it. * Is is a Call Request ? if so process it.
*/ */
if (frametype == X25_CALL_REQUEST) if (frametype == X25_CALL_REQUEST)
return x25_rx_call_request(skb, neigh, lci); return x25_rx_call_request(skb, nb, lci);
/* /*
* Its not a Call Request, nor is it a control frame. * Its not a Call Request, nor is it a control frame.
* Let caller throw it away. * Let caller throw it away.
*/ */
/* /*
x25_transmit_clear_request(neigh, lci, 0x0D); x25_transmit_clear_request(nb, lci, 0x0D);
*/ */
printk(KERN_DEBUG "x25_receive_data(): unknown frame type %2x\n",frametype); printk(KERN_DEBUG "x25_receive_data(): unknown frame type %2x\n",frametype);
return 0; return 0;
} }
int x25_lapb_receive_frame(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype) int x25_lapb_receive_frame(struct sk_buff *skb, struct net_device *dev,
struct packet_type *ptype)
{ {
struct x25_neigh *neigh; struct x25_neigh *nb;
int queued;
skb->sk = NULL; skb->sk = NULL;
/* /*
* Packet received from unrecognised device, throw it away. * Packet received from unrecognised device, throw it away.
*/ */
if ((neigh = x25_get_neigh(dev)) == NULL) { nb = x25_get_neigh(dev);
if (!nb) {
printk(KERN_DEBUG "X.25: unknown neighbour - %s\n", dev->name); printk(KERN_DEBUG "X.25: unknown neighbour - %s\n", dev->name);
kfree_skb(skb); goto drop;
return 0;
} }
switch (skb->data[0]) { switch (skb->data[0]) {
case 0x00: case 0x00:
skb_pull(skb, 1); skb_pull(skb, 1);
queued = x25_receive_data(skb, neigh); if (x25_receive_data(skb, nb)) {
if( ! queued ) x25_neigh_put(nb);
/* We need to free the skb ourselves because goto out;
* net_bh() won't care about our return code. }
*/ break;
kfree_skb(skb);
return 0;
case 0x01: case 0x01:
x25_link_established(neigh); x25_link_established(nb);
kfree_skb(skb); break;
return 0;
case 0x02: case 0x02:
x25_link_terminated(neigh); x25_link_terminated(nb);
kfree_skb(skb); break;
return 0;
case 0x03:
kfree_skb(skb);
return 0;
default:
kfree_skb(skb);
return 0;
} }
x25_neigh_put(nb);
drop:
kfree_skb(skb);
out:
return 0;
} }
int x25_llc_receive_frame(struct sk_buff *skb, struct net_device *dev, struct packet_type *ptype) int x25_llc_receive_frame(struct sk_buff *skb, struct net_device *dev,
struct packet_type *ptype)
{ {
struct x25_neigh *neigh; struct x25_neigh *nb;
int rc = 0;
skb->sk = NULL; skb->sk = NULL;
/* /*
* Packet received from unrecognised device, throw it away. * Packet received from unrecognised device, throw it away.
*/ */
if ((neigh = x25_get_neigh(dev)) == NULL) { nb = x25_get_neigh(dev);
if (!nb) {
printk(KERN_DEBUG "X.25: unknown_neighbour - %s\n", dev->name); printk(KERN_DEBUG "X.25: unknown_neighbour - %s\n", dev->name);
kfree_skb(skb); kfree_skb(skb);
return 0; } else {
rc = x25_receive_data(skb, nb);
x25_neigh_put(nb);
} }
return x25_receive_data(skb, neigh); return rc;
} }
void x25_establish_link(struct x25_neigh *neigh) void x25_establish_link(struct x25_neigh *nb)
{ {
struct sk_buff *skb; struct sk_buff *skb;
unsigned char *ptr; unsigned char *ptr;
switch (neigh->dev->type) { switch (nb->dev->type) {
case ARPHRD_X25: case ARPHRD_X25:
if ((skb = alloc_skb(1, GFP_ATOMIC)) == NULL) { if ((skb = alloc_skb(1, GFP_ATOMIC)) == NULL) {
printk(KERN_ERR "x25_dev: out of memory\n"); printk(KERN_ERR "x25_dev: out of memory\n");
...@@ -186,47 +182,44 @@ void x25_establish_link(struct x25_neigh *neigh) ...@@ -186,47 +182,44 @@ void x25_establish_link(struct x25_neigh *neigh)
} }
skb->protocol = htons(ETH_P_X25); skb->protocol = htons(ETH_P_X25);
skb->dev = neigh->dev; skb->dev = nb->dev;
dev_queue_xmit(skb); dev_queue_xmit(skb);
} }
void x25_terminate_link(struct x25_neigh *neigh) void x25_terminate_link(struct x25_neigh *nb)
{ {
struct sk_buff *skb; struct sk_buff *skb;
unsigned char *ptr; unsigned char *ptr;
switch (neigh->dev->type) {
case ARPHRD_X25:
if ((skb = alloc_skb(1, GFP_ATOMIC)) == NULL) {
printk(KERN_ERR "x25_dev: out of memory\n");
return;
}
ptr = skb_put(skb, 1);
*ptr = 0x02;
break;
#if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE) #if defined(CONFIG_LLC) || defined(CONFIG_LLC_MODULE)
case ARPHRD_ETHER: if (nb->dev->type == ARPHRD_ETHER)
return; return;
#endif #endif
default: if (nb->dev->type != ARPHRD_X25)
return; return;
skb = alloc_skb(1, GFP_ATOMIC);
if (!skb) {
printk(KERN_ERR "x25_dev: out of memory\n");
return;
} }
skb->protocol = htons(ETH_P_X25); ptr = skb_put(skb, 1);
skb->dev = neigh->dev; *ptr = 0x02;
skb->protocol = htons(ETH_P_X25);
skb->dev = nb->dev;
dev_queue_xmit(skb); dev_queue_xmit(skb);
} }
void x25_send_frame(struct sk_buff *skb, struct x25_neigh *neigh) void x25_send_frame(struct sk_buff *skb, struct x25_neigh *nb)
{ {
unsigned char *dptr; unsigned char *dptr;
skb->nh.raw = skb->data; skb->nh.raw = skb->data;
switch (neigh->dev->type) { switch (nb->dev->type) {
case ARPHRD_X25: case ARPHRD_X25:
dptr = skb_push(skb, 1); dptr = skb_push(skb, 1);
*dptr = 0x00; *dptr = 0x00;
...@@ -243,7 +236,7 @@ void x25_send_frame(struct sk_buff *skb, struct x25_neigh *neigh) ...@@ -243,7 +236,7 @@ void x25_send_frame(struct sk_buff *skb, struct x25_neigh *neigh)
} }
skb->protocol = htons(ETH_P_X25); skb->protocol = htons(ETH_P_X25);
skb->dev = neigh->dev; skb->dev = nb->dev;
dev_queue_xmit(skb); dev_queue_xmit(skb);
} }
...@@ -229,10 +229,10 @@ int x25_negotiate_facilities(struct sk_buff *skb, struct sock *sk, ...@@ -229,10 +229,10 @@ int x25_negotiate_facilities(struct sk_buff *skb, struct sock *sk,
* currently attached x25 link. * currently attached x25 link.
*/ */
void x25_limit_facilities(struct x25_facilities *facilities, void x25_limit_facilities(struct x25_facilities *facilities,
struct x25_neigh *neighbour) struct x25_neigh *nb)
{ {
if (!neighbour->extended) { if (!nb->extended) {
if (facilities->winsize_in > 7) { if (facilities->winsize_in > 7) {
printk(KERN_DEBUG "X.25: incoming winsize limited to 7\n"); printk(KERN_DEBUG "X.25: incoming winsize limited to 7\n");
facilities->winsize_in = 7; facilities->winsize_in = 7;
......
...@@ -43,47 +43,48 @@ ...@@ -43,47 +43,48 @@
#include <linux/init.h> #include <linux/init.h>
#include <net/x25.h> #include <net/x25.h>
static struct x25_neigh *x25_neigh_list; /* = NULL initially */ static struct list_head x25_neigh_list = LIST_HEAD_INIT(x25_neigh_list);
static rwlock_t x25_neigh_list_lock = RW_LOCK_UNLOCKED;
static void x25_t20timer_expiry(unsigned long); static void x25_t20timer_expiry(unsigned long);
/* /*
* Linux set/reset timer routines * Linux set/reset timer routines
*/ */
static void x25_start_t20timer(struct x25_neigh *neigh) static void x25_start_t20timer(struct x25_neigh *nb)
{ {
del_timer(&neigh->t20timer); del_timer(&nb->t20timer);
neigh->t20timer.data = (unsigned long)neigh; nb->t20timer.data = (unsigned long)nb;
neigh->t20timer.function = &x25_t20timer_expiry; nb->t20timer.function = &x25_t20timer_expiry;
neigh->t20timer.expires = jiffies + neigh->t20; nb->t20timer.expires = jiffies + nb->t20;
add_timer(&neigh->t20timer); add_timer(&nb->t20timer);
} }
static void x25_t20timer_expiry(unsigned long param) static void x25_t20timer_expiry(unsigned long param)
{ {
struct x25_neigh *neigh = (struct x25_neigh *)param; struct x25_neigh *nb = (struct x25_neigh *)param;
x25_transmit_restart_request(neigh); x25_transmit_restart_request(nb);
x25_start_t20timer(neigh); x25_start_t20timer(nb);
} }
static void x25_stop_t20timer(struct x25_neigh *neigh) static void x25_stop_t20timer(struct x25_neigh *nb)
{ {
del_timer(&neigh->t20timer); del_timer(&nb->t20timer);
} }
static int x25_t20timer_pending(struct x25_neigh *neigh) static int x25_t20timer_pending(struct x25_neigh *nb)
{ {
return timer_pending(&neigh->t20timer); return timer_pending(&nb->t20timer);
} }
/* /*
* This handles all restart and diagnostic frames. * This handles all restart and diagnostic frames.
*/ */
void x25_link_control(struct sk_buff *skb, struct x25_neigh *neigh, void x25_link_control(struct sk_buff *skb, struct x25_neigh *nb,
unsigned short frametype) unsigned short frametype)
{ {
struct sk_buff *skbn; struct sk_buff *skbn;
...@@ -91,16 +92,16 @@ void x25_link_control(struct sk_buff *skb, struct x25_neigh *neigh, ...@@ -91,16 +92,16 @@ void x25_link_control(struct sk_buff *skb, struct x25_neigh *neigh,
switch (frametype) { switch (frametype) {
case X25_RESTART_REQUEST: case X25_RESTART_REQUEST:
confirm = !x25_t20timer_pending(neigh); confirm = !x25_t20timer_pending(nb);
x25_stop_t20timer(neigh); x25_stop_t20timer(nb);
neigh->state = X25_LINK_STATE_3; nb->state = X25_LINK_STATE_3;
if (confirm) if (confirm)
x25_transmit_restart_confirmation(neigh); x25_transmit_restart_confirmation(nb);
break; break;
case X25_RESTART_CONFIRMATION: case X25_RESTART_CONFIRMATION:
x25_stop_t20timer(neigh); x25_stop_t20timer(nb);
neigh->state = X25_LINK_STATE_3; nb->state = X25_LINK_STATE_3;
break; break;
case X25_DIAGNOSTIC: case X25_DIAGNOSTIC:
...@@ -116,15 +117,15 @@ void x25_link_control(struct sk_buff *skb, struct x25_neigh *neigh, ...@@ -116,15 +117,15 @@ void x25_link_control(struct sk_buff *skb, struct x25_neigh *neigh,
break; break;
} }
if (neigh->state == X25_LINK_STATE_3) if (nb->state == X25_LINK_STATE_3)
while ((skbn = skb_dequeue(&neigh->queue)) != NULL) while ((skbn = skb_dequeue(&nb->queue)) != NULL)
x25_send_frame(skbn, neigh); x25_send_frame(skbn, nb);
} }
/* /*
* This routine is called when a Restart Request is needed * This routine is called when a Restart Request is needed
*/ */
void x25_transmit_restart_request(struct x25_neigh *neigh) void x25_transmit_restart_request(struct x25_neigh *nb)
{ {
unsigned char *dptr; unsigned char *dptr;
int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 2; int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 2;
...@@ -137,7 +138,7 @@ void x25_transmit_restart_request(struct x25_neigh *neigh) ...@@ -137,7 +138,7 @@ void x25_transmit_restart_request(struct x25_neigh *neigh)
dptr = skb_put(skb, X25_STD_MIN_LEN + 2); dptr = skb_put(skb, X25_STD_MIN_LEN + 2);
*dptr++ = neigh->extended ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ; *dptr++ = nb->extended ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ;
*dptr++ = 0x00; *dptr++ = 0x00;
*dptr++ = X25_RESTART_REQUEST; *dptr++ = X25_RESTART_REQUEST;
*dptr++ = 0x00; *dptr++ = 0x00;
...@@ -145,13 +146,13 @@ void x25_transmit_restart_request(struct x25_neigh *neigh) ...@@ -145,13 +146,13 @@ void x25_transmit_restart_request(struct x25_neigh *neigh)
skb->sk = NULL; skb->sk = NULL;
x25_send_frame(skb, neigh); x25_send_frame(skb, nb);
} }
/* /*
* This routine is called when a Restart Confirmation is needed * This routine is called when a Restart Confirmation is needed
*/ */
void x25_transmit_restart_confirmation(struct x25_neigh *neigh) void x25_transmit_restart_confirmation(struct x25_neigh *nb)
{ {
unsigned char *dptr; unsigned char *dptr;
int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN; int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN;
...@@ -164,19 +165,19 @@ void x25_transmit_restart_confirmation(struct x25_neigh *neigh) ...@@ -164,19 +165,19 @@ void x25_transmit_restart_confirmation(struct x25_neigh *neigh)
dptr = skb_put(skb, X25_STD_MIN_LEN); dptr = skb_put(skb, X25_STD_MIN_LEN);
*dptr++ = neigh->extended ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ; *dptr++ = nb->extended ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ;
*dptr++ = 0x00; *dptr++ = 0x00;
*dptr++ = X25_RESTART_CONFIRMATION; *dptr++ = X25_RESTART_CONFIRMATION;
skb->sk = NULL; skb->sk = NULL;
x25_send_frame(skb, neigh); x25_send_frame(skb, nb);
} }
/* /*
* This routine is called when a Diagnostic is required. * This routine is called when a Diagnostic is required.
*/ */
void x25_transmit_diagnostic(struct x25_neigh *neigh, unsigned char diag) void x25_transmit_diagnostic(struct x25_neigh *nb, unsigned char diag)
{ {
unsigned char *dptr; unsigned char *dptr;
int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 1; int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 1;
...@@ -189,21 +190,22 @@ void x25_transmit_diagnostic(struct x25_neigh *neigh, unsigned char diag) ...@@ -189,21 +190,22 @@ void x25_transmit_diagnostic(struct x25_neigh *neigh, unsigned char diag)
dptr = skb_put(skb, X25_STD_MIN_LEN + 1); dptr = skb_put(skb, X25_STD_MIN_LEN + 1);
*dptr++ = neigh->extended ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ; *dptr++ = nb->extended ? X25_GFI_EXTSEQ : X25_GFI_STDSEQ;
*dptr++ = 0x00; *dptr++ = 0x00;
*dptr++ = X25_DIAGNOSTIC; *dptr++ = X25_DIAGNOSTIC;
*dptr++ = diag; *dptr++ = diag;
skb->sk = NULL; skb->sk = NULL;
x25_send_frame(skb, neigh); x25_send_frame(skb, nb);
} }
/* /*
* This routine is called when a Clear Request is needed outside of the context * This routine is called when a Clear Request is needed outside of the context
* of a connected socket. * of a connected socket.
*/ */
void x25_transmit_clear_request(struct x25_neigh *neigh, unsigned int lci, unsigned char cause) void x25_transmit_clear_request(struct x25_neigh *nb, unsigned int lci,
unsigned char cause)
{ {
unsigned char *dptr; unsigned char *dptr;
int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 2; int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 2;
...@@ -216,7 +218,7 @@ void x25_transmit_clear_request(struct x25_neigh *neigh, unsigned int lci, unsig ...@@ -216,7 +218,7 @@ void x25_transmit_clear_request(struct x25_neigh *neigh, unsigned int lci, unsig
dptr = skb_put(skb, X25_STD_MIN_LEN + 2); dptr = skb_put(skb, X25_STD_MIN_LEN + 2);
*dptr++ = ((lci >> 8) & 0x0F) | neigh->extended ? X25_GFI_EXTSEQ : *dptr++ = ((lci >> 8) & 0x0F) | nb->extended ? X25_GFI_EXTSEQ :
X25_GFI_STDSEQ; X25_GFI_STDSEQ;
*dptr++ = (lci >> 0) & 0xFF; *dptr++ = (lci >> 0) & 0xFF;
*dptr++ = X25_CLEAR_REQUEST; *dptr++ = X25_CLEAR_REQUEST;
...@@ -225,23 +227,23 @@ void x25_transmit_clear_request(struct x25_neigh *neigh, unsigned int lci, unsig ...@@ -225,23 +227,23 @@ void x25_transmit_clear_request(struct x25_neigh *neigh, unsigned int lci, unsig
skb->sk = NULL; skb->sk = NULL;
x25_send_frame(skb, neigh); x25_send_frame(skb, nb);
} }
void x25_transmit_link(struct sk_buff *skb, struct x25_neigh *neigh) void x25_transmit_link(struct sk_buff *skb, struct x25_neigh *nb)
{ {
switch (neigh->state) { switch (nb->state) {
case X25_LINK_STATE_0: case X25_LINK_STATE_0:
skb_queue_tail(&neigh->queue, skb); skb_queue_tail(&nb->queue, skb);
neigh->state = X25_LINK_STATE_1; nb->state = X25_LINK_STATE_1;
x25_establish_link(neigh); x25_establish_link(nb);
break; break;
case X25_LINK_STATE_1: case X25_LINK_STATE_1:
case X25_LINK_STATE_2: case X25_LINK_STATE_2:
skb_queue_tail(&neigh->queue, skb); skb_queue_tail(&nb->queue, skb);
break; break;
case X25_LINK_STATE_3: case X25_LINK_STATE_3:
x25_send_frame(skb, neigh); x25_send_frame(skb, nb);
break; break;
} }
} }
...@@ -249,16 +251,16 @@ void x25_transmit_link(struct sk_buff *skb, struct x25_neigh *neigh) ...@@ -249,16 +251,16 @@ void x25_transmit_link(struct sk_buff *skb, struct x25_neigh *neigh)
/* /*
* Called when the link layer has become established. * Called when the link layer has become established.
*/ */
void x25_link_established(struct x25_neigh *neigh) void x25_link_established(struct x25_neigh *nb)
{ {
switch (neigh->state) { switch (nb->state) {
case X25_LINK_STATE_0: case X25_LINK_STATE_0:
neigh->state = X25_LINK_STATE_2; nb->state = X25_LINK_STATE_2;
break; break;
case X25_LINK_STATE_1: case X25_LINK_STATE_1:
x25_transmit_restart_request(neigh); x25_transmit_restart_request(nb);
neigh->state = X25_LINK_STATE_2; nb->state = X25_LINK_STATE_2;
x25_start_t20timer(neigh); x25_start_t20timer(nb);
break; break;
} }
} }
...@@ -268,11 +270,11 @@ void x25_link_established(struct x25_neigh *neigh) ...@@ -268,11 +270,11 @@ void x25_link_established(struct x25_neigh *neigh)
* request has failed. * request has failed.
*/ */
void x25_link_terminated(struct x25_neigh *neigh) void x25_link_terminated(struct x25_neigh *nb)
{ {
neigh->state = X25_LINK_STATE_0; nb->state = X25_LINK_STATE_0;
/* Out of order: clear existing virtual calls (X.25 03/93 4.6.3) */ /* Out of order: clear existing virtual calls (X.25 03/93 4.6.3) */
x25_kill_by_neigh(neigh); x25_kill_by_neigh(nb);
} }
/* /*
...@@ -280,65 +282,50 @@ void x25_link_terminated(struct x25_neigh *neigh) ...@@ -280,65 +282,50 @@ void x25_link_terminated(struct x25_neigh *neigh)
*/ */
void x25_link_device_up(struct net_device *dev) void x25_link_device_up(struct net_device *dev)
{ {
unsigned long flags; struct x25_neigh *nb = kmalloc(sizeof(*nb), GFP_ATOMIC);
struct x25_neigh *x25_neigh = kmalloc(sizeof(*x25_neigh), GFP_ATOMIC);
if (!x25_neigh) if (!nb)
return; return;
skb_queue_head_init(&x25_neigh->queue); skb_queue_head_init(&nb->queue);
init_timer(&x25_neigh->t20timer); init_timer(&nb->t20timer);
dev_hold(dev); dev_hold(dev);
x25_neigh->dev = dev; nb->dev = dev;
x25_neigh->state = X25_LINK_STATE_0; nb->state = X25_LINK_STATE_0;
x25_neigh->extended = 0; nb->extended = 0;
/* /*
* Enables negotiation * Enables negotiation
*/ */
x25_neigh->global_facil_mask = X25_MASK_REVERSE | nb->global_facil_mask = X25_MASK_REVERSE |
X25_MASK_THROUGHPUT | X25_MASK_THROUGHPUT |
X25_MASK_PACKET_SIZE | X25_MASK_PACKET_SIZE |
X25_MASK_WINDOW_SIZE; X25_MASK_WINDOW_SIZE;
x25_neigh->t20 = sysctl_x25_restart_request_timeout; nb->t20 = sysctl_x25_restart_request_timeout;
atomic_set(&nb->refcnt, 1);
save_flags(flags); cli(); write_lock_bh(&x25_neigh_list_lock);
x25_neigh->next = x25_neigh_list; list_add(&nb->node, &x25_neigh_list);
x25_neigh_list = x25_neigh; write_unlock_bh(&x25_neigh_list_lock);
restore_flags(flags);
} }
static void x25_remove_neigh(struct x25_neigh *x25_neigh) /**
* __x25_remove_neigh - remove neighbour from x25_neigh_list
* @nb - neigh to remove
*
* Remove neighbour from x25_neigh_list. If it was there.
* Caller must hold x25_neigh_list_lock.
*/
static void __x25_remove_neigh(struct x25_neigh *nb)
{ {
struct x25_neigh *s; skb_queue_purge(&nb->queue);
unsigned long flags; x25_stop_t20timer(nb);
skb_queue_purge(&x25_neigh->queue);
x25_stop_t20timer(x25_neigh);
save_flags(flags); cli();
if ((s = x25_neigh_list) == x25_neigh) {
x25_neigh_list = x25_neigh->next;
goto out_kfree_neigh;
}
while (s && s->next) {
if (s->next == x25_neigh) {
s->next = x25_neigh->next;
goto out_kfree_neigh;
}
s = s->next; if (nb->node.next) {
list_del(&nb->node);
x25_neigh_put(nb);
} }
out:
restore_flags(flags);
return;
out_kfree_neigh:
kfree(x25_neigh);
goto out;
} }
/* /*
...@@ -346,17 +333,21 @@ static void x25_remove_neigh(struct x25_neigh *x25_neigh) ...@@ -346,17 +333,21 @@ static void x25_remove_neigh(struct x25_neigh *x25_neigh)
*/ */
void x25_link_device_down(struct net_device *dev) void x25_link_device_down(struct net_device *dev)
{ {
struct x25_neigh *neigh, *x25_neigh = x25_neigh_list; struct x25_neigh *nb;
struct list_head *entry, *tmp;
while (x25_neigh) { write_lock_bh(&x25_neigh_list_lock);
neigh = x25_neigh;
x25_neigh = x25_neigh->next;
if (neigh->dev == dev) { list_for_each_safe(entry, tmp, &x25_neigh_list) {
x25_remove_neigh(neigh); nb = list_entry(entry, struct x25_neigh, node);
if (nb->dev == dev) {
__x25_remove_neigh(nb);
dev_put(dev); dev_put(dev);
} }
} }
write_unlock_bh(&x25_neigh_list_lock);
} }
/* /*
...@@ -364,13 +355,23 @@ void x25_link_device_down(struct net_device *dev) ...@@ -364,13 +355,23 @@ void x25_link_device_down(struct net_device *dev)
*/ */
struct x25_neigh *x25_get_neigh(struct net_device *dev) struct x25_neigh *x25_get_neigh(struct net_device *dev)
{ {
struct x25_neigh *x25_neigh = x25_neigh_list; struct x25_neigh *nb, *use = NULL;
struct list_head *entry;
for (; x25_neigh; x25_neigh = x25_neigh->next) read_lock_bh(&x25_neigh_list_lock);
if (x25_neigh->dev == dev) list_for_each(entry, &x25_neigh_list) {
nb = list_entry(entry, struct x25_neigh, node);
if (nb->dev == dev) {
use = nb;
break; break;
}
}
return x25_neigh; if (use)
x25_neigh_hold(use);
read_unlock_bh(&x25_neigh_list_lock);
return use;
} }
/* /*
...@@ -379,7 +380,7 @@ struct x25_neigh *x25_get_neigh(struct net_device *dev) ...@@ -379,7 +380,7 @@ struct x25_neigh *x25_get_neigh(struct net_device *dev)
int x25_subscr_ioctl(unsigned int cmd, void *arg) int x25_subscr_ioctl(unsigned int cmd, void *arg)
{ {
struct x25_subscrip_struct x25_subscr; struct x25_subscrip_struct x25_subscr;
struct x25_neigh *x25_neigh; struct x25_neigh *nb;
struct net_device *dev; struct net_device *dev;
int rc = -EINVAL; int rc = -EINVAL;
...@@ -394,27 +395,28 @@ int x25_subscr_ioctl(unsigned int cmd, void *arg) ...@@ -394,27 +395,28 @@ int x25_subscr_ioctl(unsigned int cmd, void *arg)
if ((dev = x25_dev_get(x25_subscr.device)) == NULL) if ((dev = x25_dev_get(x25_subscr.device)) == NULL)
goto out; goto out;
if ((x25_neigh = x25_get_neigh(dev)) == NULL) if ((nb = x25_get_neigh(dev)) == NULL)
goto out_put; goto out_dev_put;
dev_put(dev); dev_put(dev);
if (cmd == SIOCX25GSUBSCRIP) { if (cmd == SIOCX25GSUBSCRIP) {
x25_subscr.extended = x25_neigh->extended; x25_subscr.extended = nb->extended;
x25_subscr.global_facil_mask = x25_neigh->global_facil_mask; x25_subscr.global_facil_mask = nb->global_facil_mask;
rc = copy_to_user(arg, &x25_subscr, rc = copy_to_user(arg, &x25_subscr,
sizeof(x25_subscr)) ? -EFAULT : 0; sizeof(x25_subscr)) ? -EFAULT : 0;
} else { } else {
rc = -EINVAL; rc = -EINVAL;
if (x25_subscr.extended && x25_subscr.extended != 1) if (!(x25_subscr.extended && x25_subscr.extended != 1)) {
goto out; rc = 0;
rc = 0; nb->extended = x25_subscr.extended;
x25_neigh->extended = x25_subscr.extended; nb->global_facil_mask = x25_subscr.global_facil_mask;
x25_neigh->global_facil_mask = x25_subscr.global_facil_mask; }
} }
x25_neigh_put(nb);
out: out:
return rc; return rc;
out_put: out_dev_put:
dev_put(dev); dev_put(dev);
goto out; goto out;
} }
...@@ -425,12 +427,14 @@ int x25_subscr_ioctl(unsigned int cmd, void *arg) ...@@ -425,12 +427,14 @@ int x25_subscr_ioctl(unsigned int cmd, void *arg)
*/ */
void __exit x25_link_free(void) void __exit x25_link_free(void)
{ {
struct x25_neigh *neigh, *x25_neigh = x25_neigh_list; struct x25_neigh *nb;
struct list_head *entry, *tmp;
while (x25_neigh) { write_lock_bh(&x25_neigh_list_lock);
neigh = x25_neigh;
x25_neigh = x25_neigh->next;
x25_remove_neigh(neigh); list_for_each_safe(entry, tmp, &x25_neigh_list) {
nb = list_entry(entry, struct x25_neigh, node);
__x25_remove_neigh(nb);
} }
write_unlock_bh(&x25_neigh_list_lock);
} }
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