struct in6_addr *saddr,
int oif, int flags);
-extern struct rt6_info *ip6_dst_alloc(void);
+extern struct dst_entry *ndisc_dst_alloc(struct net_device *dev,
+ struct neighbour *neigh,
+ int (*output)(struct sk_buff *));
+extern int ndisc_dst_gc(int *more);
+extern void fib6_force_start_gc(void);
/*
* support functions for ND
mod_timer(&ip6_fib_timer, jiffies + ip6_rt_gc_interval);
}
+void fib6_force_start_gc(void)
+{
+ if (ip6_fib_timer.expires == 0)
+ mod_timer(&ip6_fib_timer, jiffies + ip6_rt_gc_interval);
+}
+
/*
* Add routing information to the routing tree.
* <destination addr>/<source addr>
write_lock_bh(&rt6_lock);
+ ndisc_dst_gc(&gc_args.more);
fib6_clean_tree(&ip6_routing_table, fib6_age, 0, NULL);
write_unlock_bh(&rt6_lock);
return -EINVAL;
}
-static inline struct dst_entry *ndisc_dst_alloc(struct net_device *dev,
- struct neighbour *neigh)
-{
- struct rt6_info *rt = ip6_dst_alloc();
-
- if (unlikely(rt == NULL))
- goto out;
-
- rt->rt6i_dev = dev;
- rt->rt6i_nexthop = neigh;
- rt->rt6i_expires = 0;
- rt->rt6i_flags = RTF_LOCAL;
- rt->rt6i_metric = 0;
- rt->u.dst.metrics[RTAX_HOPLIMIT-1] = 255;
- rt->u.dst.output = ndisc_output;
-out:
- return (struct dst_entry *)rt;
-}
-
static inline void ndisc_flow_init(struct flowi *fl, u8 type,
struct in6_addr *saddr, struct in6_addr *daddr)
{
ndisc_flow_init(&fl, NDISC_NEIGHBOUR_ADVERTISEMENT, src_addr, daddr);
- dst = ndisc_dst_alloc(dev, neigh);
+ dst = ndisc_dst_alloc(dev, neigh, ndisc_output);
if (!dst)
return;
err = xfrm_lookup(&dst, &fl, NULL, 0);
if (err < 0) {
- dst_free(dst);
+ dst_release(dst);
return;
}
if (skb == NULL) {
ND_PRINTK1("send_na: alloc skb failed\n");
- dst_free(dst);
+ dst_release(dst);
return;
}
csum_partial((__u8 *) msg,
len, 0));
- dst_clone(dst);
skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb);
ndisc_flow_init(&fl, NDISC_NEIGHBOUR_SOLICITATION, saddr, daddr);
- dst = ndisc_dst_alloc(dev, neigh);
+ dst = ndisc_dst_alloc(dev, neigh, ndisc_output);
if (!dst)
return;
- dst_clone(dst);
err = xfrm_lookup(&dst, &fl, NULL, 0);
if (err < 0) {
1, &err);
if (skb == NULL) {
ND_PRINTK1("send_ns: alloc skb failed\n");
+ dst_release(dst);
return;
}
csum_partial((__u8 *) msg,
len, 0));
/* send it! */
- dst_clone(dst);
skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb);
ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr);
- dst = ndisc_dst_alloc(dev, NULL);
+ dst = ndisc_dst_alloc(dev, NULL, ndisc_output);
if (!dst)
return;
- dst_clone(dst);
err = xfrm_lookup(&dst, &fl, NULL, 0);
if (err < 0) {
csum_partial((__u8 *) hdr, len, 0));
/* send it! */
- dst_clone(dst);
skb->dst = dst;
idev = in6_dev_get(dst->dev);
dst_output(skb);
/* allocate dst with ip6_dst_ops */
-static __inline__ struct rt6_info *__ip6_dst_alloc(void)
+static __inline__ struct rt6_info *ip6_dst_alloc(void)
{
return dst_alloc(&ip6_dst_ops);
}
-struct rt6_info *ip6_dst_alloc(void)
-{
- return __ip6_dst_alloc();
-}
-
/*
* Route lookup. Any rt6_lock is implied.
*/
}
}
+/* Protected by rt6_lock. */
+static struct dst_entry *ndisc_dst_gc_list;
+
+struct dst_entry *ndisc_dst_alloc(struct net_device *dev,
+ struct neighbour *neigh,
+ int (*output)(struct sk_buff *))
+{
+ struct rt6_info *rt = ip6_dst_alloc();
+
+ if (unlikely(rt == NULL))
+ goto out;
+
+ rt->rt6i_dev = dev;
+ rt->rt6i_nexthop = neigh;
+ rt->rt6i_expires = 0;
+ rt->rt6i_flags = RTF_LOCAL;
+ rt->rt6i_metric = 0;
+ atomic_set(&rt->u.dst.__refcnt, 1);
+ rt->u.dst.metrics[RTAX_HOPLIMIT-1] = 255;
+ rt->u.dst.output = output;
+
+ write_lock_bh(&rt6_lock);
+ rt->u.dst.next = ndisc_dst_gc_list;
+ ndisc_dst_gc_list = &rt->u.dst;
+ write_unlock_bh(&rt6_lock);
+
+ fib6_force_start_gc();
+
+out:
+ return (struct dst_entry *)rt;
+}
+
+int ndisc_dst_gc(int *more)
+{
+ struct dst_entry *dst, *next, **pprev;
+ int freed;
+
+ next = NULL;
+ pprev = &ndisc_dst_gc_list;
+ freed = 0;
+ while ((dst = *pprev) != NULL) {
+ if (!atomic_read(&dst->__refcnt)) {
+ *pprev = dst->next;
+ dst_free(dst);
+ freed++;
+ } else {
+ pprev = &dst->next;
+ (*more)++;
+ }
+ }
+
+ return freed;
+}
+
static int ip6_dst_gc(void)
{
static unsigned expire = 30*HZ;
if (rtmsg->rtmsg_metric == 0)
rtmsg->rtmsg_metric = IP6_RT_PRIO_USER;
- rt = __ip6_dst_alloc();
+ rt = ip6_dst_alloc();
if (rt == NULL)
return -ENOMEM;
static struct rt6_info * ip6_rt_copy(struct rt6_info *ort)
{
- struct rt6_info *rt = __ip6_dst_alloc();
+ struct rt6_info *rt = ip6_dst_alloc();
if (rt) {
rt->u.dst.input = ort->u.dst.input;
int ip6_rt_addr_add(struct in6_addr *addr, struct net_device *dev)
{
- struct rt6_info *rt = __ip6_dst_alloc();
+ struct rt6_info *rt = ip6_dst_alloc();
if (rt == NULL)
return -ENOMEM;