[AX.25]: Reference counting for AX.25 routes.
Ralf Baechle DL5RB [Tue, 4 Jul 2006 02:30:18 +0000 (19:30 -0700)]
In the past routes could be freed even though the were possibly in use ...

Signed-off-by: Ralf Baechle DL5RB <ralf@linux-mips.org>
Signed-off-by: David S. Miller <davem@davemloft.net>

include/net/ax25.h
net/ax25/ax25_ip.c
net/ax25/ax25_route.c

index 7cd528e..69374cd 100644 (file)
@@ -182,14 +182,26 @@ typedef struct {
 
 typedef struct ax25_route {
        struct ax25_route       *next;
-       atomic_t                ref;
+       atomic_t                refcount;
        ax25_address            callsign;
        struct net_device       *dev;
        ax25_digi               *digipeat;
        char                    ip_mode;
-       struct timer_list       timer;
 } ax25_route;
 
+static inline void ax25_hold_route(ax25_route *ax25_rt)
+{
+       atomic_inc(&ax25_rt->refcount);
+}
+
+extern void __ax25_put_route(ax25_route *ax25_rt);
+
+static inline void ax25_put_route(ax25_route *ax25_rt)
+{
+       if (atomic_dec_and_test(&ax25_rt->refcount))
+               __ax25_put_route(ax25_rt);
+}
+
 typedef struct {
        char                    slave;                  /* slave_mode?   */
        struct timer_list       slave_timer;            /* timeout timer */
@@ -348,17 +360,11 @@ extern int  ax25_check_iframes_acked(ax25_cb *, unsigned short);
 extern void ax25_rt_device_down(struct net_device *);
 extern int  ax25_rt_ioctl(unsigned int, void __user *);
 extern struct file_operations ax25_route_fops;
+extern ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev);
 extern int  ax25_rt_autobind(ax25_cb *, ax25_address *);
-extern ax25_route *ax25_rt_find_route(ax25_route *, ax25_address *,
-       struct net_device *);
 extern struct sk_buff *ax25_rt_build_path(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *);
 extern void ax25_rt_free(void);
 
-static inline void ax25_put_route(ax25_route *ax25_rt)
-{
-       atomic_dec(&ax25_rt->ref);
-}
-
 /* ax25_std_in.c */
 extern int  ax25_std_frame_in(ax25_cb *, struct sk_buff *, int);
 
index 9be5c15..136c3ae 100644 (file)
@@ -103,11 +103,13 @@ int ax25_rebuild_header(struct sk_buff *skb)
 {
        struct sk_buff *ourskb;
        unsigned char *bp  = skb->data;
-       struct net_device *dev;
+       ax25_route *route;
+       struct net_device *dev = NULL;
        ax25_address *src, *dst;
+       ax25_digi *digipeat = NULL;
        ax25_dev *ax25_dev;
-       ax25_route _route, *route = &_route;
        ax25_cb *ax25;
+       char ip_mode = ' ';
 
        dst = (ax25_address *)(bp + 1);
        src = (ax25_address *)(bp + 8);
@@ -115,8 +117,12 @@ int ax25_rebuild_header(struct sk_buff *skb)
        if (arp_find(bp + 1, skb))
                return 1;
 
-       route = ax25_rt_find_route(route, dst, NULL);
-       dev      = route->dev;
+       route = ax25_get_route(dst, NULL);
+       if (route) {
+               digipeat = route->digipeat;
+               dev = route->dev;
+               ip_mode = route->ip_mode;
+       };
 
        if (dev == NULL)
                dev = skb->dev;
@@ -126,7 +132,7 @@ int ax25_rebuild_header(struct sk_buff *skb)
        }
 
        if (bp[16] == AX25_P_IP) {
-               if (route->ip_mode == 'V' || (route->ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) {
+               if (ip_mode == 'V' || (ip_mode == ' ' && ax25_dev->values[AX25_VALUES_IPDEFMODE])) {
                        /*
                         *      We copy the buffer and release the original thereby
                         *      keeping it straight
@@ -172,7 +178,7 @@ int ax25_rebuild_header(struct sk_buff *skb)
                            ourskb, 
                            ax25_dev->values[AX25_VALUES_PACLEN], 
                            &src_c,
-                           &dst_c, route->digipeat, dev);
+                           &dst_c, digipeat, dev);
                        if (ax25) {
                                ax25_cb_put(ax25);
                        }
@@ -190,7 +196,7 @@ int ax25_rebuild_header(struct sk_buff *skb)
 
        skb_pull(skb, AX25_KISS_HEADER_LEN);
 
-       if (route->digipeat != NULL) {
+       if (digipeat != NULL) {
                if ((ourskb = ax25_rt_build_path(skb, src, dst, route->digipeat)) == NULL) {
                        kfree_skb(skb);
                        goto put;
@@ -202,7 +208,8 @@ int ax25_rebuild_header(struct sk_buff *skb)
        ax25_queue_xmit(skb, dev);
 
 put:
-       ax25_put_route(route);
+       if (route)
+               ax25_put_route(route);
 
        return 1;
 }
index 5ac9825..51b7bda 100644 (file)
@@ -41,8 +41,6 @@
 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;
@@ -115,7 +113,7 @@ static int ax25_rt_add(struct ax25_routes_struct *route)
                return -ENOMEM;
        }
 
-       atomic_set(&ax25_rt->ref, 0);
+       atomic_set(&ax25_rt->refcount, 1);
        ax25_rt->callsign     = route->dest_addr;
        ax25_rt->dev          = ax25_dev->dev;
        ax25_rt->digipeat     = NULL;
@@ -140,23 +138,10 @@ static int ax25_rt_add(struct ax25_routes_struct *route)
        return 0;
 }
 
-static void ax25_rt_destroy(ax25_route *ax25_rt)
+void __ax25_put_route(ax25_route *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);
+       kfree(ax25_rt->digipeat);
+       kfree(ax25_rt);
 }
 
 static int ax25_rt_del(struct ax25_routes_struct *route)
@@ -177,12 +162,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_rt_destroy(s);
+                               ax25_put_route(s);
                        } else {
                                for (t = ax25_route_list; t != NULL; t = t->next) {
                                        if (t->next == s) {
                                                t->next = s->next;
-                                               ax25_rt_destroy(s);
+                                               ax25_put_route(s);
                                                break;
                                        }
                                }
@@ -362,7 +347,7 @@ struct file_operations ax25_route_fops = {
  *
  *     Only routes with a reference count of zero can be destroyed.
  */
-static ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev)
+ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev)
 {
        ax25_route *ax25_spe_rt = NULL;
        ax25_route *ax25_def_rt = NULL;
@@ -392,7 +377,7 @@ static ax25_route *ax25_get_route(ax25_address *addr, struct net_device *dev)
                ax25_rt = ax25_spe_rt;
 
        if (ax25_rt != NULL)
-               atomic_inc(&ax25_rt->ref);
+               ax25_hold_route(ax25_rt);
 
        read_unlock(&ax25_route_lock);
 
@@ -467,24 +452,6 @@ 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)
 {