linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / net / ax25 / ax25_route.c
index 51b7bda..f04f863 100644 (file)
@@ -41,6 +41,8 @@
 static ax25_route *ax25_route_list;
 static DEFINE_RWLOCK(ax25_route_lock);
 
+static ax25_route *ax25_get_route(ax25_address *, struct net_device *);
+
 void ax25_rt_device_down(struct net_device *dev)
 {
        ax25_route *s, *t, *ax25_rt;
@@ -113,7 +115,7 @@ static int ax25_rt_add(struct ax25_routes_struct *route)
                return -ENOMEM;
        }
 
-       atomic_set(&ax25_rt->refcount, 1);
+       atomic_set(&ax25_rt->ref, 0);
        ax25_rt->callsign     = route->dest_addr;
        ax25_rt->dev          = ax25_dev->dev;
        ax25_rt->digipeat     = NULL;
@@ -138,10 +140,23 @@ static int ax25_rt_add(struct ax25_routes_struct *route)
        return 0;
 }
 
-void __ax25_put_route(ax25_route *ax25_rt)
+static void ax25_rt_destroy(ax25_route *ax25_rt)
 {
-       kfree(ax25_rt->digipeat);
-       kfree(ax25_rt);
+       if (atomic_read(&ax25_rt->ref) == 0) {
+               kfree(ax25_rt->digipeat);
+               kfree(ax25_rt);
+               return;
+       }
+
+       /*
+        * 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)
@@ -162,12 +177,12 @@ static int ax25_rt_del(struct ax25_routes_struct *route)
                    ax25cmp(&route->dest_addr, &s->callsign) == 0) {
                        if (ax25_route_list == s) {
                                ax25_route_list = s->next;
-                               ax25_put_route(s);
+                               ax25_rt_destroy(s);
                        } else {
                                for (t = ax25_route_list; t != NULL; t = t->next) {
                                        if (t->next == s) {
                                                t->next = s->next;
-                                               ax25_put_route(s);
+                                               ax25_rt_destroy(s);
                                                break;
                                        }
                                }
@@ -345,9 +360,9 @@ struct file_operations ax25_route_fops = {
 /*
  *     Find AX.25 route
  *
- *     Only routes with a reference count of zero can be destroyed.
+ *     Only routes with a refernce rout of zero can be destroyed.
  */
-ax25_route *ax25_get_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_def_rt = NULL;
@@ -377,7 +392,7 @@ ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev)
                ax25_rt = ax25_spe_rt;
 
        if (ax25_rt != NULL)
-               ax25_hold_route(ax25_rt);
+               atomic_inc(&ax25_rt->ref);
 
        read_unlock(&ax25_route_lock);
 
@@ -452,6 +467,24 @@ put:
        return 0;
 }
 
+ax25_route *ax25_rt_find_route(ax25_route * route, ax25_address *addr,
+       struct net_device *dev)
+{
+       ax25_route *ax25_rt;
+
+       if ((ax25_rt = ax25_get_route(addr, dev)))
+               return ax25_rt;
+
+       route->next     = NULL;
+       atomic_set(&route->ref, 1);
+       route->callsign = *addr;
+       route->dev      = dev;
+       route->digipeat = NULL;
+       route->ip_mode  = ' ';
+
+       return route;
+}
+
 struct sk_buff *ax25_rt_build_path(struct sk_buff *skb, ax25_address *src,
        ax25_address *dest, ax25_digi *digi)
 {