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 {
};
struct x25_neigh {
struct x25_neigh *next;
struct list_head node;
struct net_device *dev;
unsigned int state;
unsigned int extended;
......@@ -126,6 +126,7 @@ struct x25_neigh {
unsigned long t20;
struct timer_list t20timer;
unsigned long global_facil_mask;
atomic_t refcnt;
};
struct x25_opt {
......@@ -199,6 +200,18 @@ extern int x25_subscr_ioctl(unsigned int, void *);
extern struct x25_neigh *x25_get_neigh(struct net_device *);
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 */
extern int x25_output(struct sock *, struct sk_buff *);
extern void x25_kick(struct sock *);
......
......@@ -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)
{
struct net_device *dev = ptr;
struct x25_neigh *neigh;
struct x25_neigh *nb;
if (dev->type == ARPHRD_X25
#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
x25_link_device_up(dev);
break;
case NETDEV_GOING_DOWN:
if ((neigh = x25_get_neigh(dev)))
x25_terminate_link(neigh);
nb = x25_get_neigh(dev);
if (nb) {
x25_terminate_link(nb);
x25_neigh_put(nb);
}
break;
case NETDEV_DOWN:
x25_kill_by_device(dev);
......@@ -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.
*/
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;
unsigned long flags;
......@@ -264,7 +267,7 @@ struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *neigh)
cli();
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;
restore_flags(flags);
......@@ -274,11 +277,11 @@ struct sock *x25_find_socket(unsigned int lci, struct x25_neigh *neigh)
/*
* 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;
while (x25_find_socket(lci, neigh))
while (x25_find_socket(lci, nb))
if (++lci == 4096) {
lci = 0;
break;
......@@ -631,11 +634,11 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len
x25->lci = x25_new_lci(x25->neighbour);
if (!x25->lci)
goto out_put_route;
goto out_put_neigh;
rc = -EINVAL;
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))
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
/* Now the loop */
rc = -EINPROGRESS;
if (sk->state != TCP_ESTABLISHED && (flags & O_NONBLOCK))
goto out_put_route;
goto out_put_neigh;
cli(); /* To avoid races on the sleep */
......@@ -681,6 +684,9 @@ static int x25_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len
rc = 0;
out_unlock:
sti();
out_put_neigh:
if (rc)
x25_neigh_put(x25->neighbour);
out_put_route:
x25_route_put(rt);
out:
......@@ -758,7 +764,7 @@ static int x25_getname(struct socket *sock, struct sockaddr *uaddr, int *uaddr_l
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 *make;
......@@ -800,7 +806,7 @@ int x25_rx_call_request(struct sk_buff *skb, struct x25_neigh *neigh, unsigned i
* on certain facilties
*/
x25_limit_facilities(&facilities,neigh);
x25_limit_facilities(&facilities, nb);
/*
* 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
makex25->lci = lci;
makex25->dest_addr = dest_addr;
makex25->source_addr = source_addr;
makex25->neighbour = neigh;
makex25->neighbour = nb;
makex25->facilities = facilities;
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
return rc;
out_clear_request:
rc = 0;
x25_transmit_clear_request(neigh, lci, 0x01);
x25_transmit_clear_request(nb, lci, 0x01);
goto out;
}
......@@ -1358,12 +1364,12 @@ struct notifier_block x25_dev_notifier = {
.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;
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);
}
......
......@@ -44,7 +44,7 @@
#include <linux/if_arp.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;
unsigned short frametype;
......@@ -58,14 +58,14 @@ static int x25_receive_data(struct sk_buff *skb, struct x25_neigh *neigh)
* frame.
*/
if (lci == 0) {
x25_link_control(skb, neigh, frametype);
x25_link_control(skb, nb, frametype);
return 0;
}
/*
* Find an existing socket.
*/
if ((sk = x25_find_socket(lci, neigh)) != NULL) {
if ((sk = x25_find_socket(lci, nb)) != NULL) {
int queued = 1;
skb->h.raw = skb->data;
......@@ -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.
*/
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.
* 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);
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;
int queued;
struct x25_neigh *nb;
skb->sk = NULL;
/*
* 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);
kfree_skb(skb);
return 0;
goto drop;
}
switch (skb->data[0]) {
case 0x00:
skb_pull(skb, 1);
queued = x25_receive_data(skb, neigh);
if( ! queued )
/* We need to free the skb ourselves because
* net_bh() won't care about our return code.
*/
kfree_skb(skb);
return 0;
if (x25_receive_data(skb, nb)) {
x25_neigh_put(nb);
goto out;
}
break;
case 0x01:
x25_link_established(neigh);
kfree_skb(skb);
return 0;
x25_link_established(nb);
break;
case 0x02:
x25_link_terminated(neigh);
kfree_skb(skb);
return 0;
case 0x03:
kfree_skb(skb);
return 0;
default:
x25_link_terminated(nb);
break;
}
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;
/*
* 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);
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;
unsigned char *ptr;
switch (neigh->dev->type) {
switch (nb->dev->type) {
case ARPHRD_X25:
if ((skb = alloc_skb(1, GFP_ATOMIC)) == NULL) {
printk(KERN_ERR "x25_dev: out of memory\n");
......@@ -186,47 +182,44 @@ void x25_establish_link(struct x25_neigh *neigh)
}
skb->protocol = htons(ETH_P_X25);
skb->dev = neigh->dev;
skb->dev = nb->dev;
dev_queue_xmit(skb);
}
void x25_terminate_link(struct x25_neigh *neigh)
void x25_terminate_link(struct x25_neigh *nb)
{
struct sk_buff *skb;
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)
case ARPHRD_ETHER:
if (nb->dev->type == ARPHRD_ETHER)
return;
#endif
default:
if (nb->dev->type != ARPHRD_X25)
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);
skb->dev = neigh->dev;
ptr = skb_put(skb, 1);
*ptr = 0x02;
skb->protocol = htons(ETH_P_X25);
skb->dev = nb->dev;
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;
skb->nh.raw = skb->data;
switch (neigh->dev->type) {
switch (nb->dev->type) {
case ARPHRD_X25:
dptr = skb_push(skb, 1);
*dptr = 0x00;
......@@ -243,7 +236,7 @@ void x25_send_frame(struct sk_buff *skb, struct x25_neigh *neigh)
}
skb->protocol = htons(ETH_P_X25);
skb->dev = neigh->dev;
skb->dev = nb->dev;
dev_queue_xmit(skb);
}
......@@ -229,10 +229,10 @@ int x25_negotiate_facilities(struct sk_buff *skb, struct sock *sk,
* currently attached x25 link.
*/
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) {
printk(KERN_DEBUG "X.25: incoming winsize limited to 7\n");
facilities->winsize_in = 7;
......
......@@ -43,47 +43,48 @@
#include <linux/init.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);
/*
* 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;
neigh->t20timer.function = &x25_t20timer_expiry;
neigh->t20timer.expires = jiffies + neigh->t20;
nb->t20timer.data = (unsigned long)nb;
nb->t20timer.function = &x25_t20timer_expiry;
nb->t20timer.expires = jiffies + nb->t20;
add_timer(&neigh->t20timer);
add_timer(&nb->t20timer);
}
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.
*/
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)
{
struct sk_buff *skbn;
......@@ -91,16 +92,16 @@ void x25_link_control(struct sk_buff *skb, struct x25_neigh *neigh,
switch (frametype) {
case X25_RESTART_REQUEST:
confirm = !x25_t20timer_pending(neigh);
x25_stop_t20timer(neigh);
neigh->state = X25_LINK_STATE_3;
confirm = !x25_t20timer_pending(nb);
x25_stop_t20timer(nb);
nb->state = X25_LINK_STATE_3;
if (confirm)
x25_transmit_restart_confirmation(neigh);
x25_transmit_restart_confirmation(nb);
break;
case X25_RESTART_CONFIRMATION:
x25_stop_t20timer(neigh);
neigh->state = X25_LINK_STATE_3;
x25_stop_t20timer(nb);
nb->state = X25_LINK_STATE_3;
break;
case X25_DIAGNOSTIC:
......@@ -116,15 +117,15 @@ void x25_link_control(struct sk_buff *skb, struct x25_neigh *neigh,
break;
}
if (neigh->state == X25_LINK_STATE_3)
while ((skbn = skb_dequeue(&neigh->queue)) != NULL)
x25_send_frame(skbn, neigh);
if (nb->state == X25_LINK_STATE_3)
while ((skbn = skb_dequeue(&nb->queue)) != NULL)
x25_send_frame(skbn, nb);
}
/*
* 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;
int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN + 2;
......@@ -137,7 +138,7 @@ void x25_transmit_restart_request(struct x25_neigh *neigh)
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++ = X25_RESTART_REQUEST;
*dptr++ = 0x00;
......@@ -145,13 +146,13 @@ void x25_transmit_restart_request(struct x25_neigh *neigh)
skb->sk = NULL;
x25_send_frame(skb, neigh);
x25_send_frame(skb, nb);
}
/*
* 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;
int len = X25_MAX_L2_LEN + X25_STD_MIN_LEN;
......@@ -164,19 +165,19 @@ void x25_transmit_restart_confirmation(struct x25_neigh *neigh)
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++ = X25_RESTART_CONFIRMATION;
skb->sk = NULL;
x25_send_frame(skb, neigh);
x25_send_frame(skb, nb);
}
/*
* 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;
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)
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++ = X25_DIAGNOSTIC;
*dptr++ = diag;
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
* 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;
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
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;
*dptr++ = (lci >> 0) & 0xFF;
*dptr++ = X25_CLEAR_REQUEST;
......@@ -225,23 +227,23 @@ void x25_transmit_clear_request(struct x25_neigh *neigh, unsigned int lci, unsig
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:
skb_queue_tail(&neigh->queue, skb);
neigh->state = X25_LINK_STATE_1;
x25_establish_link(neigh);
skb_queue_tail(&nb->queue, skb);
nb->state = X25_LINK_STATE_1;
x25_establish_link(nb);
break;
case X25_LINK_STATE_1:
case X25_LINK_STATE_2:
skb_queue_tail(&neigh->queue, skb);
skb_queue_tail(&nb->queue, skb);
break;
case X25_LINK_STATE_3:
x25_send_frame(skb, neigh);
x25_send_frame(skb, nb);
break;
}
}
......@@ -249,16 +251,16 @@ void x25_transmit_link(struct sk_buff *skb, struct x25_neigh *neigh)
/*
* 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:
neigh->state = X25_LINK_STATE_2;
nb->state = X25_LINK_STATE_2;
break;
case X25_LINK_STATE_1:
x25_transmit_restart_request(neigh);
neigh->state = X25_LINK_STATE_2;
x25_start_t20timer(neigh);
x25_transmit_restart_request(nb);
nb->state = X25_LINK_STATE_2;
x25_start_t20timer(nb);
break;
}
}
......@@ -268,11 +270,11 @@ void x25_link_established(struct x25_neigh *neigh)
* 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) */
x25_kill_by_neigh(neigh);
x25_kill_by_neigh(nb);
}
/*
......@@ -280,65 +282,50 @@ void x25_link_terminated(struct x25_neigh *neigh)
*/
void x25_link_device_up(struct net_device *dev)
{
unsigned long flags;
struct x25_neigh *x25_neigh = kmalloc(sizeof(*x25_neigh), GFP_ATOMIC);
struct x25_neigh *nb = kmalloc(sizeof(*nb), GFP_ATOMIC);
if (!x25_neigh)
if (!nb)
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);
x25_neigh->dev = dev;
x25_neigh->state = X25_LINK_STATE_0;
x25_neigh->extended = 0;
nb->dev = dev;
nb->state = X25_LINK_STATE_0;
nb->extended = 0;
/*
* Enables negotiation
*/
x25_neigh->global_facil_mask = X25_MASK_REVERSE |
nb->global_facil_mask = X25_MASK_REVERSE |
X25_MASK_THROUGHPUT |
X25_MASK_PACKET_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();
x25_neigh->next = x25_neigh_list;
x25_neigh_list = x25_neigh;
restore_flags(flags);
write_lock_bh(&x25_neigh_list_lock);
list_add(&nb->node, &x25_neigh_list);
write_unlock_bh(&x25_neigh_list_lock);
}
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;
unsigned long flags;
skb_queue_purge(&x25_neigh->queue);
x25_stop_t20timer(x25_neigh);
save_flags(flags); cli();
skb_queue_purge(&nb->queue);
x25_stop_t20timer(nb);
if ((s = x25_neigh_list) == x25_neigh) {
x25_neigh_list = x25_neigh->next;
goto out_kfree_neigh;
if (nb->node.next) {
list_del(&nb->node);
x25_neigh_put(nb);
}
while (s && s->next) {
if (s->next == x25_neigh) {
s->next = x25_neigh->next;
goto out_kfree_neigh;
}
s = s->next;
}
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)
*/
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;
write_lock_bh(&x25_neigh_list_lock);
while (x25_neigh) {
neigh = x25_neigh;
x25_neigh = x25_neigh->next;
list_for_each_safe(entry, tmp, &x25_neigh_list) {
nb = list_entry(entry, struct x25_neigh, node);
if (neigh->dev == dev) {
x25_remove_neigh(neigh);
if (nb->dev == dev) {
__x25_remove_neigh(nb);
dev_put(dev);
}
}
write_unlock_bh(&x25_neigh_list_lock);
}
/*
......@@ -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_neigh = x25_neigh_list;
struct x25_neigh *nb, *use = NULL;
struct list_head *entry;
read_lock_bh(&x25_neigh_list_lock);
list_for_each(entry, &x25_neigh_list) {
nb = list_entry(entry, struct x25_neigh, node);
for (; x25_neigh; x25_neigh = x25_neigh->next)
if (x25_neigh->dev == dev)
if (nb->dev == dev) {
use = nb;
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)
int x25_subscr_ioctl(unsigned int cmd, void *arg)
{
struct x25_subscrip_struct x25_subscr;
struct x25_neigh *x25_neigh;
struct x25_neigh *nb;
struct net_device *dev;
int rc = -EINVAL;
......@@ -394,27 +395,28 @@ int x25_subscr_ioctl(unsigned int cmd, void *arg)
if ((dev = x25_dev_get(x25_subscr.device)) == NULL)
goto out;
if ((x25_neigh = x25_get_neigh(dev)) == NULL)
goto out_put;
if ((nb = x25_get_neigh(dev)) == NULL)
goto out_dev_put;
dev_put(dev);
if (cmd == SIOCX25GSUBSCRIP) {
x25_subscr.extended = x25_neigh->extended;
x25_subscr.global_facil_mask = x25_neigh->global_facil_mask;
x25_subscr.extended = nb->extended;
x25_subscr.global_facil_mask = nb->global_facil_mask;
rc = copy_to_user(arg, &x25_subscr,
sizeof(x25_subscr)) ? -EFAULT : 0;
} else {
rc = -EINVAL;
if (x25_subscr.extended && x25_subscr.extended != 1)
goto out;
if (!(x25_subscr.extended && x25_subscr.extended != 1)) {
rc = 0;
x25_neigh->extended = x25_subscr.extended;
x25_neigh->global_facil_mask = x25_subscr.global_facil_mask;
nb->extended = x25_subscr.extended;
nb->global_facil_mask = x25_subscr.global_facil_mask;
}
}
x25_neigh_put(nb);
out:
return rc;
out_put:
out_dev_put:
dev_put(dev);
goto out;
}
......@@ -425,12 +427,14 @@ int x25_subscr_ioctl(unsigned int cmd, void *arg)
*/
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) {
neigh = x25_neigh;
x25_neigh = x25_neigh->next;
write_lock_bh(&x25_neigh_list_lock);
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