Commit 6916457b authored by Ralf Bächle's avatar Ralf Bächle

Implement reference counting and delayed destruction of routes.

Fix arguments to write_lock & co. of which many were passed the lock, not a pointer to one.
parent 9b391b86
...@@ -9,6 +9,8 @@ ...@@ -9,6 +9,8 @@
#include <linux/config.h> #include <linux/config.h>
#include <linux/ax25.h> #include <linux/ax25.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/timer.h>
#include <asm/atomic.h>
#define AX25_T1CLAMPLO 1 #define AX25_T1CLAMPLO 1
#define AX25_T1CLAMPHI (30 * HZ) #define AX25_T1CLAMPHI (30 * HZ)
...@@ -148,10 +150,12 @@ typedef struct { ...@@ -148,10 +150,12 @@ typedef struct {
typedef struct ax25_route { typedef struct ax25_route {
struct ax25_route *next; struct ax25_route *next;
atomic_t ref;
ax25_address callsign; ax25_address callsign;
struct net_device *dev; struct net_device *dev;
ax25_digi *digipeat; ax25_digi *digipeat;
char ip_mode; char ip_mode;
struct timer_list timer;
} ax25_route; } ax25_route;
typedef struct { typedef struct {
......
...@@ -40,10 +40,10 @@ ...@@ -40,10 +40,10 @@
* Jonathan(G4KLX) Support for packet forwarding. * Jonathan(G4KLX) Support for packet forwarding.
* Arnaldo C. Melo s/suser/capable/ * Arnaldo C. Melo s/suser/capable/
*/ */
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/socket.h> #include <linux/socket.h>
#include <linux/timer.h>
#include <linux/in.h> #include <linux/in.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -68,7 +68,7 @@ ...@@ -68,7 +68,7 @@
static ax25_route *ax25_route_list; static ax25_route *ax25_route_list;
static rwlock_t ax25_route_lock = RW_LOCK_UNLOCKED; static rwlock_t ax25_route_lock = RW_LOCK_UNLOCKED;
static ax25_route *ax25_find_route(ax25_address *, struct net_device *); static ax25_route *ax25_get_route(ax25_address *, struct net_device *);
/* /*
* small macro to drop non-digipeated digipeaters and reverse path * small macro to drop non-digipeated digipeaters and reverse path
...@@ -129,7 +129,7 @@ static int ax25_rt_add(struct ax25_routes_struct *route) ...@@ -129,7 +129,7 @@ static int ax25_rt_add(struct ax25_routes_struct *route)
if (route->digi_count > AX25_MAX_DIGIS) if (route->digi_count > AX25_MAX_DIGIS)
return -EINVAL; return -EINVAL;
write_lock(ax25_route_lock); write_lock(&ax25_route_lock);
ax25_rt = ax25_route_list; ax25_rt = ax25_route_list;
while (ax25_rt != NULL) { while (ax25_rt != NULL) {
...@@ -141,7 +141,7 @@ static int ax25_rt_add(struct ax25_routes_struct *route) ...@@ -141,7 +141,7 @@ static int ax25_rt_add(struct ax25_routes_struct *route)
} }
if (route->digi_count != 0) { if (route->digi_count != 0) {
if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
write_unlock(ax25_route_lock); write_unlock(&ax25_route_lock);
return -ENOMEM; return -ENOMEM;
} }
ax25_rt->digipeat->lastrepeat = -1; ax25_rt->digipeat->lastrepeat = -1;
...@@ -157,17 +157,18 @@ static int ax25_rt_add(struct ax25_routes_struct *route) ...@@ -157,17 +157,18 @@ static int ax25_rt_add(struct ax25_routes_struct *route)
} }
if ((ax25_rt = kmalloc(sizeof(ax25_route), GFP_ATOMIC)) == NULL) { if ((ax25_rt = kmalloc(sizeof(ax25_route), GFP_ATOMIC)) == NULL) {
write_unlock(ax25_route_lock); write_unlock(&ax25_route_lock);
return -ENOMEM; return -ENOMEM;
} }
atomic_set(&ax25_rt->ref, 0);
ax25_rt->callsign = route->dest_addr; ax25_rt->callsign = route->dest_addr;
ax25_rt->dev = ax25_dev->dev; ax25_rt->dev = ax25_dev->dev;
ax25_rt->digipeat = NULL; ax25_rt->digipeat = NULL;
ax25_rt->ip_mode = ' '; ax25_rt->ip_mode = ' ';
if (route->digi_count != 0) { if (route->digi_count != 0) {
if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) { if ((ax25_rt->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
write_unlock(ax25_route_lock); write_unlock(&ax25_route_lock);
kfree(ax25_rt); kfree(ax25_rt);
return -ENOMEM; return -ENOMEM;
} }
...@@ -180,11 +181,30 @@ static int ax25_rt_add(struct ax25_routes_struct *route) ...@@ -180,11 +181,30 @@ static int ax25_rt_add(struct ax25_routes_struct *route)
} }
ax25_rt->next = ax25_route_list; ax25_rt->next = ax25_route_list;
ax25_route_list = ax25_rt; ax25_route_list = ax25_rt;
write_unlock(ax25_route_lock); write_unlock(&ax25_route_lock);
return 0; return 0;
} }
static int ax25_rt_destroy(ax25_route *ax25_rt)
{
if (atomic_read(&ax25_rt->ref) == 0) {
if (ax25_rt->digipeat != NULL)
kfree(ax25_rt->digipeat);
kfree(ax25_rt);
}
/*
* Uh... Route is still in use; we can't yet destroy it. Retry later.
*/
init_timer(&ax25_rt->timer);
ax25_rt->timer.data = (unsigned long) ax25_rt;
ax25_rt->timer.function = (void *) ax25_rt_destroy;
ax25_rt->timer.expires = jiffies + 5 * HZ;
add_timer(&ax25_rt->timer);
}
static int ax25_rt_del(struct ax25_routes_struct *route) static int ax25_rt_del(struct ax25_routes_struct *route)
{ {
ax25_route *s, *t, *ax25_rt; ax25_route *s, *t, *ax25_rt;
...@@ -193,32 +213,29 @@ static int ax25_rt_del(struct ax25_routes_struct *route) ...@@ -193,32 +213,29 @@ static int ax25_rt_del(struct ax25_routes_struct *route)
if ((ax25_dev = ax25_addr_ax25dev(&route->port_addr)) == NULL) if ((ax25_dev = ax25_addr_ax25dev(&route->port_addr)) == NULL)
return -EINVAL; return -EINVAL;
write_lock(ax25_route_lock); write_lock(&ax25_route_lock);
ax25_rt = ax25_route_list; ax25_rt = ax25_route_list;
while (ax25_rt != NULL) { while (ax25_rt != NULL) {
s = ax25_rt; s = ax25_rt;
ax25_rt = ax25_rt->next; ax25_rt = ax25_rt->next;
if (s->dev == ax25_dev->dev && ax25cmp(&route->dest_addr, &s->callsign) == 0) { if (s->dev == ax25_dev->dev &&
ax25cmp(&route->dest_addr, &s->callsign) == 0) {
if (ax25_route_list == s) { if (ax25_route_list == s) {
ax25_route_list = s->next; ax25_route_list = s->next;
if (s->digipeat != NULL) ax25_rt_destroy(s);
kfree(s->digipeat);
kfree(s);
} else { } else {
for (t = ax25_route_list; t != NULL; t = t->next) { for (t = ax25_route_list; t != NULL; t = t->next) {
if (t->next == s) { if (t->next == s) {
t->next = s->next; t->next = s->next;
if (s->digipeat != NULL) ax25_rt_destroy(s);
kfree(s->digipeat);
kfree(s);
break; break;
} }
} }
} }
} }
} }
write_unlock(ax25_route_lock); write_unlock(&ax25_route_lock);
return 0; return 0;
} }
...@@ -232,7 +249,7 @@ static int ax25_rt_opt(struct ax25_route_opt_struct *rt_option) ...@@ -232,7 +249,7 @@ static int ax25_rt_opt(struct ax25_route_opt_struct *rt_option)
if ((ax25_dev = ax25_addr_ax25dev(&rt_option->port_addr)) == NULL) if ((ax25_dev = ax25_addr_ax25dev(&rt_option->port_addr)) == NULL)
return -EINVAL; return -EINVAL;
write_lock(ax25_route_lock); write_lock(&ax25_route_lock);
ax25_rt = ax25_route_list; ax25_rt = ax25_route_list;
while (ax25_rt != NULL) { while (ax25_rt != NULL) {
...@@ -260,7 +277,7 @@ static int ax25_rt_opt(struct ax25_route_opt_struct *rt_option) ...@@ -260,7 +277,7 @@ static int ax25_rt_opt(struct ax25_route_opt_struct *rt_option)
} }
out: out:
write_unlock(ax25_route_lock); write_unlock(&ax25_route_lock);
return err; return err;
} }
...@@ -353,8 +370,10 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -353,8 +370,10 @@ int ax25_rt_get_info(char *buffer, char **start, off_t offset, int length)
/* /*
* Find AX.25 route * Find AX.25 route
*
* Only routes with a refernce rout of zero can be destroyed.
*/ */
static ax25_route *ax25_find_route(ax25_address *addr, struct net_device *dev) static ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev)
{ {
ax25_route *ax25_spe_rt = NULL; ax25_route *ax25_spe_rt = NULL;
ax25_route *ax25_def_rt = NULL; ax25_route *ax25_def_rt = NULL;
...@@ -378,12 +397,22 @@ static ax25_route *ax25_find_route(ax25_address *addr, struct net_device *dev) ...@@ -378,12 +397,22 @@ static ax25_route *ax25_find_route(ax25_address *addr, struct net_device *dev)
ax25_def_rt = ax25_rt; ax25_def_rt = ax25_rt;
} }
} }
read_unlock(&ax25_route_lock);
ax25_rt = ax25_def_rt;
if (ax25_spe_rt != NULL) if (ax25_spe_rt != NULL)
return ax25_spe_rt; ax25_rt = ax25_spe_rt;
if (ax25_rt != NULL)
atomic_inc(&ax25_rt->ref);
read_unlock(&ax25_route_lock);
return ax25_def_rt; return ax25_rt;
}
static inline void ax25_put_route(ax25_route *ax25_rt)
{
atomic_dec(&ax25_rt->ref);
} }
/* /*
...@@ -411,24 +440,31 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) ...@@ -411,24 +440,31 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
{ {
ax25_route *ax25_rt; ax25_route *ax25_rt;
ax25_address *call; ax25_address *call;
int err;
if ((ax25_rt = ax25_find_route(addr, NULL)) == NULL) if ((ax25_rt = ax25_get_route(addr, NULL)) == NULL)
return -EHOSTUNREACH; return -EHOSTUNREACH;
if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL) if ((ax25->ax25_dev = ax25_dev_ax25dev(ax25_rt->dev)) == NULL) {
return -EHOSTUNREACH; err = -EHOSTUNREACH;
goto put;
}
if ((call = ax25_findbyuid(current->euid)) == NULL) { if ((call = ax25_findbyuid(current->euid)) == NULL) {
if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) if (ax25_uid_policy && !capable(CAP_NET_BIND_SERVICE)) {
return -EPERM; err = -EPERM;
goto put;
}
call = (ax25_address *)ax25->ax25_dev->dev->dev_addr; call = (ax25_address *)ax25->ax25_dev->dev->dev_addr;
} }
ax25->source_addr = *call; ax25->source_addr = *call;
if (ax25_rt->digipeat != NULL) { if (ax25_rt->digipeat != NULL) {
if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) if ((ax25->digipeat = kmalloc(sizeof(ax25_digi), GFP_ATOMIC)) == NULL) {
return -ENOMEM; err = -ENOMEM;
goto put;
}
memcpy(ax25->digipeat, ax25_rt->digipeat, sizeof(ax25_digi)); memcpy(ax25->digipeat, ax25_rt->digipeat, sizeof(ax25_digi));
ax25_adjust_path(addr, ax25->digipeat); ax25_adjust_path(addr, ax25->digipeat);
} }
...@@ -436,6 +472,9 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr) ...@@ -436,6 +472,9 @@ int ax25_rt_autobind(ax25_cb *ax25, ax25_address *addr)
if (ax25->sk != NULL) if (ax25->sk != NULL)
ax25->sk->zapped = 0; ax25->sk->zapped = 0;
put:
ax25_put_route(ax25_rt);
return 0; return 0;
} }
...@@ -444,10 +483,11 @@ ax25_route *ax25_rt_find_route(ax25_route * route, ax25_address *addr, ...@@ -444,10 +483,11 @@ ax25_route *ax25_rt_find_route(ax25_route * route, ax25_address *addr,
{ {
ax25_route *ax25_rt; ax25_route *ax25_rt;
if (ax25_rt = ax25_find_route(addr, dev)) if ((ax25_rt = ax25_get_route(addr, dev)))
return ax25_rt; return ax25_rt;
route->next = NULL; route->next = NULL;
atomic_set(&route->ref, 1);
route->callsign = *addr; route->callsign = *addr;
route->dev = dev; route->dev = dev;
route->digipeat = NULL; route->digipeat = NULL;
......
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