diff options
| author | Jeroen Vreeken <pe1rxq@amsat.org> | 2003-08-14 07:12:29 -0700 |
|---|---|---|
| committer | David S. Miller <davem@nuts.ninka.net> | 2003-08-14 07:12:29 -0700 |
| commit | 28d0e94e406baea91e2bcb3d8e8aa451081109fd (patch) | |
| tree | a20d8bebc3d2a9360ee2c8cee3e714c63e0db538 | |
| parent | 2a2ff7fc62e028db4312f50e4f32fac20911e980 (diff) | |
[NETROM]: Use hlist for the routing table information.
Note: there is a call to ax25_cb_put commented out, that
can be added back when ax25 refcount patches go in.
| -rw-r--r-- | include/net/netrom.h | 94 | ||||
| -rw-r--r-- | net/netrom/nr_route.c | 439 |
2 files changed, 355 insertions, 178 deletions
diff --git a/include/net/netrom.h b/include/net/netrom.h index 09a0c75d40b3..cde8d0d1a875 100644 --- a/include/net/netrom.h +++ b/include/net/netrom.h @@ -7,6 +7,7 @@ #ifndef _NETROM_H #define _NETROM_H #include <linux/netrom.h> +#include <linux/list.h> #define NR_NETWORK_LEN 15 #define NR_TRANSPORT_LEN 5 @@ -77,16 +78,17 @@ typedef struct { #define nr_sk(__sk) ((nr_cb *)(__sk)->sk_protinfo) struct nr_neigh { - struct nr_neigh *next; - ax25_address callsign; - ax25_digi *digipeat; - ax25_cb *ax25; - struct net_device *dev; - unsigned char quality; - unsigned char locked; - unsigned short count; - unsigned int number; - unsigned char failed; + struct hlist_node neigh_node; + ax25_address callsign; + ax25_digi *digipeat; + ax25_cb *ax25; + struct net_device *dev; + unsigned char quality; + unsigned char locked; + unsigned short count; + unsigned int number; + unsigned char failed; + atomic_t refcount; }; struct nr_route { @@ -96,14 +98,74 @@ struct nr_route { }; struct nr_node { - struct nr_node *next; - ax25_address callsign; - char mnemonic[7]; - unsigned char which; - unsigned char count; - struct nr_route routes[3]; + struct hlist_node node_node; + ax25_address callsign; + char mnemonic[7]; + unsigned char which; + unsigned char count; + struct nr_route routes[3]; + atomic_t refcount; + spinlock_t node_lock; }; +/********************************************************************* + * nr_node & nr_neigh lists, refcounting and locking + *********************************************************************/ + +extern struct hlist_head nr_node_list; +extern struct hlist_head nr_neigh_list; + +#define nr_node_hold(__nr_node) \ + atomic_inc(&((__nr_node)->refcount)) + +static __inline__ void nr_node_put(struct nr_node *nr_node) +{ + if (atomic_dec_and_test(&nr_node->refcount)) { + kfree(nr_node); + } +} + +#define nr_neigh_hold(__nr_neigh) \ + atomic_inc(&((__nr_neigh)->refcount)) + +static __inline__ void nr_neigh_put(struct nr_neigh *nr_neigh) +{ + if (atomic_dec_and_test(&nr_neigh->refcount)) { + if (nr_neigh->digipeat != NULL) + kfree(nr_neigh->digipeat); + kfree(nr_neigh); + } +} + +/* nr_node_lock and nr_node_unlock also hold/put the node's refcounter. + */ +static __inline__ void nr_node_lock(struct nr_node *nr_node) +{ + nr_node_hold(nr_node); + spin_lock_bh(&nr_node->node_lock); +} + +static __inline__ void nr_node_unlock(struct nr_node *nr_node) +{ + spin_unlock_bh(&nr_node->node_lock); + nr_node_put(nr_node); +} + +#define nr_neigh_for_each(__nr_neigh, node, list) \ + hlist_for_each_entry(__nr_neigh, node, list, neigh_node) + +#define nr_neigh_for_each_safe(__nr_neigh, node, node2, list) \ + hlist_for_each_entry_safe(__nr_neigh, node, node2, list, neigh_node) + +#define nr_node_for_each(__nr_node, node, list) \ + hlist_for_each_entry(__nr_node, node, list, node_node) + +#define nr_node_for_each_safe(__nr_node, node, node2, list) \ + hlist_for_each_entry_safe(__nr_node, node, node2, list, node_node) + + +/*********************************************************************/ + /* af_netrom.c */ extern int sysctl_netrom_default_path_quality; extern int sysctl_netrom_obsolescence_count_initialiser; diff --git a/net/netrom/nr_route.c b/net/netrom/nr_route.c index df47a86da3b3..e4067c8fc335 100644 --- a/net/netrom/nr_route.c +++ b/net/netrom/nr_route.c @@ -39,10 +39,45 @@ static unsigned int nr_neigh_no = 1; -static struct nr_node *nr_node_list; -static spinlock_t nr_node_lock; -static struct nr_neigh *nr_neigh_list; -static spinlock_t nr_neigh_lock; +HLIST_HEAD(nr_node_list); +spinlock_t nr_node_list_lock = SPIN_LOCK_UNLOCKED; +HLIST_HEAD(nr_neigh_list); +spinlock_t nr_neigh_list_lock = SPIN_LOCK_UNLOCKED; + +struct nr_node *nr_node_get(ax25_address *callsign) +{ + struct nr_node *found = NULL; + struct nr_node *nr_node; + struct hlist_node *node; + + spin_lock_bh(&nr_node_list_lock); + nr_node_for_each(nr_node, node, &nr_node_list) + if (ax25cmp(callsign, &nr_node->callsign) == 0) { + nr_node_hold(nr_node); + found = nr_node; + break; + } + spin_unlock_bh(&nr_node_list_lock); + return found; +} + +struct nr_neigh *nr_neigh_get_dev(ax25_address *callsign, struct net_device *dev) +{ + struct nr_neigh *found = NULL; + struct nr_neigh *nr_neigh; + struct hlist_node *node; + + spin_lock_bh(&nr_neigh_list_lock); + nr_neigh_for_each(nr_neigh, node, &nr_neigh_list) + if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && + nr_neigh->dev == dev) { + nr_neigh_hold(nr_neigh); + found = nr_neigh; + break; + } + spin_unlock_bh(&nr_neigh_list_lock); + return found; +} static void nr_remove_neigh(struct nr_neigh *); @@ -57,17 +92,16 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 struct nr_neigh *nr_neigh; struct nr_route nr_route; int i, found; + struct net_device *odev; - if (nr_dev_get(nr) != NULL) /* Can't add routes to ourself */ + if ((odev=nr_dev_get(nr)) != NULL) { /* Can't add routes to ourself */ + dev_put(odev); return -EINVAL; + } - for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next) - if (ax25cmp(nr, &nr_node->callsign) == 0) - break; + nr_node = nr_node_get(nr); - for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) - if (ax25cmp(ax25, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) - break; + nr_neigh = nr_neigh_get_dev(ax25, dev); /* * The L2 link to a neighbour has failed in the past @@ -76,24 +110,36 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 * routes now (and not wait for a node broadcast). */ if (nr_neigh != NULL && nr_neigh->failed != 0 && quality == 0) { - struct nr_node *node; - - for (node = nr_node_list; node != NULL; node = node->next) - for (i = 0; i < node->count; i++) - if (node->routes[i].neighbour == nr_neigh) - if (i < node->which) - node->which = i; + struct nr_node *nr_nodet; + struct hlist_node *node; + + spin_lock_bh(&nr_node_list_lock); + nr_node_for_each(nr_nodet, node, &nr_node_list) { + nr_node_lock(nr_nodet); + for (i = 0; i < nr_nodet->count; i++) + if (nr_nodet->routes[i].neighbour == nr_neigh) + if (i < nr_nodet->which) + nr_nodet->which = i; + nr_node_unlock(nr_nodet); + } + spin_unlock_bh(&nr_node_list_lock); } if (nr_neigh != NULL) nr_neigh->failed = 0; - if (quality == 0 && nr_neigh != NULL && nr_node != NULL) + if (quality == 0 && nr_neigh != NULL && nr_node != NULL) { + nr_neigh_put(nr_neigh); + nr_node_put(nr_node); return 0; + } if (nr_neigh == NULL) { - if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL) + if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL) { + if (nr_node) + nr_node_put(nr_node); return -ENOMEM; + } nr_neigh->callsign = *ax25; nr_neigh->digipeat = NULL; @@ -104,48 +150,58 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 nr_neigh->count = 0; nr_neigh->number = nr_neigh_no++; nr_neigh->failed = 0; + atomic_set(&nr_neigh->refcount, 1); if (ax25_digi != NULL && ax25_digi->ndigi > 0) { if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) { kfree(nr_neigh); + if (nr_node) + nr_node_put(nr_node); return -ENOMEM; } memcpy(nr_neigh->digipeat, ax25_digi, sizeof(*ax25_digi)); } - spin_lock_bh(&nr_neigh_lock); - nr_neigh->next = nr_neigh_list; - nr_neigh_list = nr_neigh; - spin_unlock_bh(&nr_neigh_lock); + spin_lock_bh(&nr_neigh_list_lock); + hlist_add_head(&nr_neigh->neigh_node, &nr_neigh_list); + nr_neigh_hold(nr_neigh); + spin_unlock_bh(&nr_neigh_list_lock); } if (quality != 0 && ax25cmp(nr, ax25) == 0 && !nr_neigh->locked) nr_neigh->quality = quality; if (nr_node == NULL) { - if ((nr_node = kmalloc(sizeof(*nr_node), GFP_ATOMIC)) == NULL) + if ((nr_node = kmalloc(sizeof(*nr_node), GFP_ATOMIC)) == NULL) { + if (nr_neigh) + nr_neigh_put(nr_neigh); return -ENOMEM; + } nr_node->callsign = *nr; strcpy(nr_node->mnemonic, mnemonic); nr_node->which = 0; nr_node->count = 1; + atomic_set(&nr_node->refcount, 1); + nr_node->node_lock = SPIN_LOCK_UNLOCKED; nr_node->routes[0].quality = quality; nr_node->routes[0].obs_count = obs_count; nr_node->routes[0].neighbour = nr_neigh; - spin_lock_bh(&nr_node_lock); - nr_node->next = nr_node_list; - nr_node_list = nr_node; - spin_unlock_bh(&nr_node_lock); - + nr_neigh_hold(nr_neigh); nr_neigh->count++; + spin_lock_bh(&nr_node_list_lock); + hlist_add_head(&nr_node->node_node, &nr_node_list); + /* refcount initialized at 1 */ + spin_unlock_bh(&nr_node_list_lock); + return 0; } + nr_node_lock(nr_node); if (quality != 0) strcpy(nr_node->mnemonic, mnemonic); @@ -171,11 +227,13 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 nr_node->which++; nr_node->count++; + nr_neigh_hold(nr_neigh); nr_neigh->count++; } else { /* It must be better than the worst */ if (quality > nr_node->routes[2].quality) { nr_node->routes[2].neighbour->count--; + nr_neigh_put(nr_node->routes[2].neighbour); if (nr_node->routes[2].neighbour->count == 0 && !nr_node->routes[2].neighbour->locked) nr_remove_neigh(nr_node->routes[2].neighbour); @@ -184,6 +242,7 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 nr_node->routes[2].obs_count = obs_count; nr_node->routes[2].neighbour = nr_neigh; + nr_neigh_hold(nr_neigh); nr_neigh->count++; } } @@ -244,62 +303,42 @@ static int nr_add_node(ax25_address *nr, const char *mnemonic, ax25_address *ax2 } } + nr_neigh_put(nr_neigh); + nr_node_unlock(nr_node); + nr_node_put(nr_node); return 0; } -static void nr_remove_node(struct nr_node *nr_node) +static inline void __nr_remove_node(struct nr_node *nr_node) { - struct nr_node *s; - - spin_lock_bh(&nr_node_lock); - if ((s = nr_node_list) == nr_node) { - nr_node_list = nr_node->next; - spin_unlock_bh(&nr_node_lock); - kfree(nr_node); - return; - } - - while (s != NULL && s->next != NULL) { - if (s->next == nr_node) { - s->next = nr_node->next; - spin_unlock_bh(&nr_node_lock); - kfree(nr_node); - return; - } + hlist_del_init(&nr_node->node_node); + nr_node_put(nr_node); +} - s = s->next; - } +#define nr_remove_node_locked(__node) \ + __nr_remove_node(__node) - spin_unlock_bh(&nr_node_lock); +static void nr_remove_node(struct nr_node *nr_node) +{ + spin_lock_bh(&nr_node_list_lock); + __nr_remove_node(nr_node); + spin_unlock_bh(&nr_node_list_lock); } -static void nr_remove_neigh(struct nr_neigh *nr_neigh) +static inline void __nr_remove_neigh(struct nr_neigh *nr_neigh) { - struct nr_neigh *s; + hlist_del_init(&nr_neigh->neigh_node); + nr_neigh_put(nr_neigh); +} - spin_lock_bh(&nr_neigh_lock); - if ((s = nr_neigh_list) == nr_neigh) { - nr_neigh_list = nr_neigh->next; - spin_unlock_bh(&nr_neigh_lock); - if (nr_neigh->digipeat != NULL) - kfree(nr_neigh->digipeat); - kfree(nr_neigh); - return; - } +#define nr_remove_neigh_locked(__neigh) \ + __nr_remove_neigh(__neigh) - while (s != NULL && s->next != NULL) { - if (s->next == nr_neigh) { - s->next = nr_neigh->next; - spin_unlock_bh(&nr_neigh_lock); - if (nr_neigh->digipeat != NULL) - kfree(nr_neigh->digipeat); - kfree(nr_neigh); - return; - } - - s = s->next; - } - spin_unlock_bh(&nr_neigh_lock); +static void nr_remove_neigh(struct nr_neigh *nr_neigh) +{ + spin_lock_bh(&nr_neigh_list_lock); + __nr_remove_neigh(nr_neigh); + spin_unlock_bh(&nr_neigh_list_lock); } /* @@ -312,26 +351,27 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n struct nr_neigh *nr_neigh; int i; - for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next) - if (ax25cmp(callsign, &nr_node->callsign) == 0) - break; + nr_node = nr_node_get(callsign); if (nr_node == NULL) return -EINVAL; - for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) - if (ax25cmp(neighbour, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) - break; + nr_neigh = nr_neigh_get_dev(neighbour, dev); - if (nr_neigh == NULL) + if (nr_neigh == NULL) { + nr_node_put(nr_node); return -EINVAL; + } + nr_node_lock(nr_node); for (i = 0; i < nr_node->count; i++) { if (nr_node->routes[i].neighbour == nr_neigh) { nr_neigh->count--; + nr_neigh_put(nr_neigh); if (nr_neigh->count == 0 && !nr_neigh->locked) nr_remove_neigh(nr_neigh); + nr_neigh_put(nr_neigh); nr_node->count--; @@ -346,11 +386,16 @@ static int nr_del_node(ax25_address *callsign, ax25_address *neighbour, struct n case 2: break; } + nr_node_put(nr_node); } + nr_node_unlock(nr_node); return 0; } } + nr_neigh_put(nr_neigh); + nr_node_unlock(nr_node); + nr_node_put(nr_node); return -EINVAL; } @@ -362,12 +407,12 @@ static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net { struct nr_neigh *nr_neigh; - for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) { - if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) { - nr_neigh->quality = quality; - nr_neigh->locked = 1; - return 0; - } + nr_neigh = nr_neigh_get_dev(callsign, dev); + if (nr_neigh) { + nr_neigh->quality = quality; + nr_neigh->locked = 1; + nr_neigh_put(nr_neigh); + return 0; } if ((nr_neigh = kmalloc(sizeof(*nr_neigh), GFP_ATOMIC)) == NULL) @@ -382,6 +427,7 @@ static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net nr_neigh->count = 0; nr_neigh->number = nr_neigh_no++; nr_neigh->failed = 0; + atomic_set(&nr_neigh->refcount, 1); if (ax25_digi != NULL && ax25_digi->ndigi > 0) { if ((nr_neigh->digipeat = kmalloc(sizeof(*ax25_digi), GFP_KERNEL)) == NULL) { @@ -391,10 +437,10 @@ static int nr_add_neigh(ax25_address *callsign, ax25_digi *ax25_digi, struct net memcpy(nr_neigh->digipeat, ax25_digi, sizeof(*ax25_digi)); } - spin_lock_bh(&nr_neigh_lock); - nr_neigh->next = nr_neigh_list; - nr_neigh_list = nr_neigh; - spin_unlock_bh(&nr_neigh_lock); + spin_lock_bh(&nr_neigh_list_lock); + hlist_add_head(&nr_neigh->neigh_node, &nr_neigh_list); + /* refcount is initialized at 1 */ + spin_unlock_bh(&nr_neigh_list_lock); return 0; } @@ -407,9 +453,7 @@ static int nr_del_neigh(ax25_address *callsign, struct net_device *dev, unsigned { struct nr_neigh *nr_neigh; - for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) - if (ax25cmp(callsign, &nr_neigh->callsign) == 0 && nr_neigh->dev == dev) - break; + nr_neigh = nr_neigh_get_dev(callsign, dev); if (nr_neigh == NULL) return -EINVAL; @@ -418,6 +462,7 @@ static int nr_del_neigh(ax25_address *callsign, struct net_device *dev, unsigned if (nr_neigh->count == 0) nr_remove_neigh(nr_neigh); + nr_neigh_put(nr_neigh); return 0; } @@ -430,15 +475,13 @@ static int nr_del_neigh(ax25_address *callsign, struct net_device *dev, unsigned static int nr_dec_obs(void) { struct nr_neigh *nr_neigh; - struct nr_node *s, *nr_node; + struct nr_node *s; + struct hlist_node *node, *nodet; int i; - nr_node = nr_node_list; - - while (nr_node != NULL) { - s = nr_node; - nr_node = nr_node->next; - + spin_lock_bh(&nr_node_list_lock); + nr_node_for_each_safe(s, node, nodet, &nr_node_list) { + nr_node_lock(s); for (i = 0; i < s->count; i++) { switch (s->routes[i].obs_count) { case 0: /* A locked entry */ @@ -448,6 +491,7 @@ static int nr_dec_obs(void) nr_neigh = s->routes[i].neighbour; nr_neigh->count--; + nr_neigh_put(nr_neigh); if (nr_neigh->count == 0 && !nr_neigh->locked) nr_remove_neigh(nr_neigh); @@ -472,8 +516,10 @@ static int nr_dec_obs(void) } if (s->count <= 0) - nr_remove_node(s); + nr_remove_node_locked(s); + nr_node_unlock(s); } + spin_unlock_bh(&nr_node_list_lock); return 0; } @@ -483,21 +529,17 @@ static int nr_dec_obs(void) */ void nr_rt_device_down(struct net_device *dev) { - struct nr_neigh *s, *nr_neigh = nr_neigh_list; - struct nr_node *t, *nr_node; + struct nr_neigh *s; + struct hlist_node *node, *nodet, *node2, *node2t; + struct nr_node *t; int i; - while (nr_neigh != NULL) { - s = nr_neigh; - nr_neigh = nr_neigh->next; - + spin_lock_bh(&nr_neigh_list_lock); + nr_neigh_for_each_safe(s, node, nodet, &nr_neigh_list) { if (s->dev == dev) { - nr_node = nr_node_list; - - while (nr_node != NULL) { - t = nr_node; - nr_node = nr_node->next; - + spin_lock_bh(&nr_node_list_lock); + nr_node_for_each_safe(t, node2, node2t, &nr_node_list) { + nr_node_lock(t); for (i = 0; i < t->count; i++) { if (t->routes[i].neighbour == s) { t->count--; @@ -514,12 +556,15 @@ void nr_rt_device_down(struct net_device *dev) } if (t->count <= 0) - nr_remove_node(t); + nr_remove_node_locked(t); + nr_node_unlock(t); } + spin_unlock_bh(&nr_node_list_lock); - nr_remove_neigh(s); + nr_remove_neigh_locked(s); } } + spin_unlock_bh(&nr_neigh_list_lock); } /* @@ -553,6 +598,8 @@ struct net_device *nr_dev_first(void) if (first == NULL || strncmp(dev->name, first->name, 3) < 0) first = dev; } + if (first) + dev_hold(first); read_unlock(&dev_base_lock); return first; @@ -603,6 +650,7 @@ int nr_rt_ioctl(unsigned int cmd, void *arg) { struct nr_route_struct nr_route; struct net_device *dev; + int ret; switch (cmd) { case SIOCADDRT: @@ -610,23 +658,29 @@ int nr_rt_ioctl(unsigned int cmd, void *arg) return -EFAULT; if ((dev = nr_ax25_dev_get(nr_route.device)) == NULL) return -EINVAL; - if (nr_route.ndigis < 0 || nr_route.ndigis > AX25_MAX_DIGIS) + if (nr_route.ndigis < 0 || nr_route.ndigis > AX25_MAX_DIGIS) { + dev_put(dev); return -EINVAL; + } switch (nr_route.type) { case NETROM_NODE: - return nr_add_node(&nr_route.callsign, + ret = nr_add_node(&nr_route.callsign, nr_route.mnemonic, &nr_route.neighbour, nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters), dev, nr_route.quality, nr_route.obs_count); + break; case NETROM_NEIGH: - return nr_add_neigh(&nr_route.callsign, + ret = nr_add_neigh(&nr_route.callsign, nr_call_to_digi(nr_route.ndigis, nr_route.digipeaters), dev, nr_route.quality); + break; default: - return -EINVAL; + ret = -EINVAL; } + dev_put(dev); + return ret; case SIOCDELRT: if (copy_from_user(&nr_route, arg, sizeof(struct nr_route_struct))) @@ -635,14 +689,18 @@ int nr_rt_ioctl(unsigned int cmd, void *arg) return -EINVAL; switch (nr_route.type) { case NETROM_NODE: - return nr_del_node(&nr_route.callsign, + ret = nr_del_node(&nr_route.callsign, &nr_route.neighbour, dev); + break; case NETROM_NEIGH: - return nr_del_neigh(&nr_route.callsign, + ret = nr_del_neigh(&nr_route.callsign, dev, nr_route.quality); + break; default: - return -EINVAL; + ret = -EINVAL; } + dev_put(dev); + return ret; case SIOCNRDECOBS: return nr_dec_obs(); @@ -660,22 +718,36 @@ int nr_rt_ioctl(unsigned int cmd, void *arg) */ void nr_link_failed(ax25_cb *ax25, int reason) { - struct nr_neigh *nr_neigh; - struct nr_node *nr_node; - - for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) - if (nr_neigh->ax25 == ax25) + struct nr_neigh *s, *nr_neigh = NULL; + struct hlist_node *node; + struct nr_node *nr_node = NULL; + + spin_lock_bh(&nr_neigh_list_lock); + nr_neigh_for_each(s, node, &nr_neigh_list) + if (s->ax25 == ax25) { + nr_neigh_hold(s); + nr_neigh = s; break; + } + spin_unlock_bh(&nr_neigh_list_lock); if (nr_neigh == NULL) return; nr_neigh->ax25 = NULL; + // ax25_cb_put(ax25); - if (++nr_neigh->failed < sysctl_netrom_link_fails_count) return; - - for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next) + if (++nr_neigh->failed < sysctl_netrom_link_fails_count) { + nr_neigh_put(nr_neigh); + return; + } + spin_lock_bh(&nr_node_list_lock); + nr_node_for_each(nr_node, node, &nr_node_list) + nr_node_lock(nr_node); if (nr_node->which < nr_node->count && nr_node->routes[nr_node->which].neighbour == nr_neigh) nr_node->which++; + nr_node_unlock(nr_node); + spin_unlock_bh(&nr_node_list_lock); + nr_neigh_put(nr_neigh); } /* @@ -689,6 +761,9 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25) struct nr_node *nr_node; struct net_device *dev; unsigned char *dptr; + ax25_cb *ax25s; + int ret; + struct sk_buff *skbn; nr_src = (ax25_address *)(skb->data + 0); @@ -700,50 +775,84 @@ int nr_route_frame(struct sk_buff *skb, ax25_cb *ax25) if ((dev = nr_dev_get(nr_dest)) != NULL) { /* Its for me */ if (ax25 == NULL) /* Its from me */ - return nr_loopback_queue(skb); + ret = nr_loopback_queue(skb); else - return nr_rx_frame(skb, dev); + ret = nr_rx_frame(skb, dev); + dev_put(dev); + return ret; } if (!sysctl_netrom_routing_control && ax25 != NULL) return 0; /* Its Time-To-Live has expired */ - if (--skb->data[14] == 0) + if (skb->data[14] == 1) { return 0; + } - for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next) - if (ax25cmp(nr_dest, &nr_node->callsign) == 0) - break; + nr_node = nr_node_get(nr_dest); + if (nr_node == NULL) + return 0; + nr_node_lock(nr_node); - if (nr_node == NULL || nr_node->which >= nr_node->count) + if (nr_node->which >= nr_node->count) { + nr_node_unlock(nr_node); + nr_node_put(nr_node); return 0; + } nr_neigh = nr_node->routes[nr_node->which].neighbour; - if ((dev = nr_dev_first()) == NULL) + if ((dev = nr_dev_first()) == NULL) { + nr_node_unlock(nr_node); + nr_node_put(nr_node); return 0; + } + + /* We are going to change the netrom headers so we should get our + own skb, we also did not know until now how much header space + we had to reserve... - RXQ */ + if ((skbn=skb_copy_expand(skb, dev->hard_header_len, 0, GFP_ATOMIC)) == NULL) { + nr_node_unlock(nr_node); + nr_node_put(nr_node); + dev_put(dev); + return 0; + } + kfree_skb(skb); + skb=skbn; + skb->data[14]--; dptr = skb_push(skb, 1); *dptr = AX25_P_NETROM; - nr_neigh->ax25 = ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev); + ax25s = ax25_send_frame(skb, 256, (ax25_address *)dev->dev_addr, &nr_neigh->callsign, nr_neigh->digipeat, nr_neigh->dev); + if (nr_neigh->ax25 && ax25s) { + /* We were already holding this ax25_cb */ + // ax25_cb_put(ax25s); + } + nr_neigh->ax25 = ax25s; - return (nr_neigh->ax25 != NULL); + dev_put(dev); + ret = (nr_neigh->ax25 != NULL); + nr_node_unlock(nr_node); + nr_node_put(nr_node); + return ret; } int nr_nodes_get_info(char *buffer, char **start, off_t offset, int length) { struct nr_node *nr_node; + struct hlist_node *node; int len = 0; off_t pos = 0; off_t begin = 0; int i; - spin_lock_bh(&nr_node_lock); + spin_lock_bh(&nr_node_list_lock); len += sprintf(buffer, "callsign mnemonic w n qual obs neigh qual obs neigh qual obs neigh\n"); - for (nr_node = nr_node_list; nr_node != NULL; nr_node = nr_node->next) { + nr_node_for_each(nr_node, node, &nr_node_list) { + nr_node_lock(nr_node); len += sprintf(buffer + len, "%-9s %-7s %d %d", ax2asc(&nr_node->callsign), (nr_node->mnemonic[0] == '\0') ? "*" : nr_node->mnemonic, @@ -756,6 +865,7 @@ int nr_nodes_get_info(char *buffer, char **start, off_t offset, int length) nr_node->routes[i].obs_count, nr_node->routes[i].neighbour->number); } + nr_node_unlock(nr_node); len += sprintf(buffer + len, "\n"); @@ -769,7 +879,7 @@ int nr_nodes_get_info(char *buffer, char **start, off_t offset, int length) if (pos > offset + length) break; } - spin_unlock_bh(&nr_node_lock); + spin_unlock_bh(&nr_node_list_lock); *start = buffer + (offset - begin); len -= (offset - begin); @@ -782,15 +892,16 @@ int nr_nodes_get_info(char *buffer, char **start, off_t offset, int length) int nr_neigh_get_info(char *buffer, char **start, off_t offset, int length) { struct nr_neigh *nr_neigh; + struct hlist_node *node; int len = 0; off_t pos = 0; off_t begin = 0; int i; - spin_lock_bh(&nr_neigh_lock); + spin_lock_bh(&nr_neigh_list_lock); len += sprintf(buffer, "addr callsign dev qual lock count failed digipeaters\n"); - for (nr_neigh = nr_neigh_list; nr_neigh != NULL; nr_neigh = nr_neigh->next) { + nr_neigh_for_each(nr_neigh, node, &nr_neigh_list) { len += sprintf(buffer + len, "%05d %-9s %-4s %3d %d %3d %3d", nr_neigh->number, ax2asc(&nr_neigh->callsign), @@ -818,7 +929,7 @@ int nr_neigh_get_info(char *buffer, char **start, off_t offset, int length) break; } - spin_unlock_bh(&nr_neigh_lock); + spin_unlock_bh(&nr_neigh_list_lock); *start = buffer + (offset - begin); len -= (offset - begin); @@ -833,20 +944,24 @@ int nr_neigh_get_info(char *buffer, char **start, off_t offset, int length) */ void __exit nr_rt_free(void) { - struct nr_neigh *s, *nr_neigh = nr_neigh_list; - struct nr_node *t, *nr_node = nr_node_list; - - while (nr_node != NULL) { - t = nr_node; - nr_node = nr_node->next; - - nr_remove_node(t); + struct nr_neigh *s = NULL; + struct nr_node *t = NULL; + struct hlist_node *node, *nodet; + + spin_lock_bh(&nr_neigh_list_lock); + spin_lock_bh(&nr_node_list_lock); + nr_node_for_each_safe(t, node, nodet, &nr_node_list) { + nr_node_lock(t); + nr_remove_node_locked(t); + nr_node_unlock(t); } - - while (nr_neigh != NULL) { - s = nr_neigh; - nr_neigh = nr_neigh->next; - - nr_remove_neigh(s); + nr_neigh_for_each_safe(s, node, nodet, &nr_neigh_list) { + while(s->count) { + s->count--; + nr_neigh_put(s); + } + nr_remove_neigh_locked(s); } + spin_unlock_bh(&nr_node_list_lock); + spin_unlock_bh(&nr_neigh_list_lock); } |
