From: jamal <[EMAIL PROTECTED]> Date: Mon, 19 Dec 2005 08:17:19 -0500
> Just an addendum: If this works it should be sysctl controlled i hope. There is absolutely no reason for that, so no :) > A second approach inspired from your current patch: > Just delete the route cache entry for the one specific SA on update. I > could attempt such a patch when i get back from work tonight. Jamal, you seem to be asleep at the wheel. This is the whole difficult problem to solve, finding cache entries from a specific SA, it's hard stuff :-) I have a design by which to do this, which I'll code up this afternoon, so don't waste any cycles on it :) Meanwhile I also coded up an alternative way to do the flush, by entry matching in the bundle purger, it looks something like the patch below. It's not as fast as the "SA --> dst" mapping thing will be, but it's better than the full flush we'll need to eat for the more straightforward 2.6.15 fix. diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 5beae1c..2cf7c32 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -889,7 +889,7 @@ struct xfrm_state * xfrm_find_acq(u8 mod int create, unsigned short family); extern void xfrm_policy_flush(void); extern int xfrm_sk_policy_insert(struct sock *sk, int dir, struct xfrm_policy *pol); -extern int xfrm_flush_bundles(void); +extern void xfrm_flush_bundles_by_state(struct xfrm_state *x, int id_match); extern int xfrm_bundle_ok(struct xfrm_dst *xdst, struct flowi *fl, int family); extern void xfrm_init_pmtu(struct dst_entry *dst); diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 0db9e57..ffa0e0f 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -724,7 +724,7 @@ static inline int policy_to_flow_dir(int }; } -static int stale_bundle(struct dst_entry *dst); +static int stale_bundle(struct dst_entry *dst, void *); /* Main function: finds/creates a bundle for given flow. * @@ -835,7 +835,7 @@ restart: } write_lock_bh(&policy->lock); - if (unlikely(policy->dead || stale_bundle(dst))) { + if (unlikely(policy->dead || stale_bundle(dst, NULL))) { /* Wow! While we worked on resolving, this * policy has gone. Retry. It is not paranoia, * we just cannot enlist new bundle to dead object. @@ -1014,17 +1014,16 @@ int __xfrm_route_forward(struct sk_buff } EXPORT_SYMBOL(__xfrm_route_forward); -/* Optimize later using cookies and generation ids. */ - static struct dst_entry *xfrm_dst_check(struct dst_entry *dst, u32 cookie) { - if (!stale_bundle(dst)) - return dst; - + /* If it is marked obsolete, which is how we even get here, + * then we have purged it from the policy bundle list and we + * did that for a good reason. + */ return NULL; } -static int stale_bundle(struct dst_entry *dst) +static int stale_bundle(struct dst_entry *dst, void *__unused) { return !xfrm_bundle_ok((struct xfrm_dst *)dst, NULL, AF_UNSPEC); } @@ -1056,7 +1055,7 @@ static struct dst_entry *xfrm_negative_a return dst; } -static void xfrm_prune_bundles(int (*func)(struct dst_entry *)) +static void xfrm_prune_bundles(int (*func)(struct dst_entry *, void *), void *arg) { int i; struct xfrm_policy *pol; @@ -1068,7 +1067,7 @@ static void xfrm_prune_bundles(int (*fun write_lock(&pol->lock); dstp = &pol->bundles; while ((dst=*dstp) != NULL) { - if (func(dst)) { + if (func(dst, arg)) { *dstp = dst->next; dst->next = gc_list; gc_list = dst; @@ -1088,22 +1087,65 @@ static void xfrm_prune_bundles(int (*fun } } -static int unused_bundle(struct dst_entry *dst) +static int unused_bundle(struct dst_entry *dst, void *__unused) { return !atomic_read(&dst->__refcnt); } static void __xfrm_garbage_collect(void) { - xfrm_prune_bundles(unused_bundle); + xfrm_prune_bundles(unused_bundle, NULL); } int xfrm_flush_bundles(void) { - xfrm_prune_bundles(stale_bundle); + xfrm_prune_bundles(stale_bundle, NULL); + return 0; +} + +static int matches_id(struct dst_entry *dst, void *__x0) +{ + struct xfrm_state *x0 = __x0; + unsigned short family = x0->props.family; + xfrm_address_t *daddr = &x0->id.daddr; + xfrm_address_t *saddr = &x0->props.saddr; + __u8 proto = x0->id.proto; + + do { + struct xfrm_state *x = dst->xfrm; + + if (!x) + continue; + + if (x->props.family == family && + x->id.proto == proto && + xfrm_state_addr_check(x, daddr, saddr, family)) + return 1; + } while ((dst = dst->child)); + + return 0; +} + +static int uses_state(struct dst_entry *dst, void *__x) +{ + struct xfrm_state *x = __x; + + do { + if (dst->xfrm == x) + return 1; + } while ((dst = dst->child)); + return 0; } +void xfrm_flush_bundles_by_state(struct xfrm_state *x, int id_match) +{ + if (id_match) + xfrm_prune_bundles(matches_id, x); + else + xfrm_prune_bundles(uses_state, x); +} + void xfrm_init_pmtu(struct dst_entry *dst) { do { @@ -1267,11 +1309,20 @@ static void xfrm_policy_put_afinfo(struc read_unlock(&afinfo->lock); } +static int mentions_dev(struct dst_entry *dst, void *__dev) +{ + struct net_device *dev = __dev; + + return (dst->dev == dev); +} + static int xfrm_dev_event(struct notifier_block *this, unsigned long event, void *ptr) { + struct net_device *dev = (struct net_device *) ptr; + switch (event) { case NETDEV_DOWN: - xfrm_flush_bundles(); + xfrm_prune_bundles(mentions_dev, dev); } return NOTIFY_DONE; } diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 7cf48aa..00f0a41 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -48,8 +48,6 @@ static struct work_struct xfrm_state_gc_ static struct list_head xfrm_state_gc_list = LIST_HEAD_INIT(xfrm_state_gc_list); static DEFINE_SPINLOCK(xfrm_state_gc_lock); -static int xfrm_state_gc_flush_bundles; - static int __xfrm_state_delete(struct xfrm_state *x); static struct xfrm_state_afinfo *xfrm_state_get_afinfo(unsigned short family); @@ -79,11 +77,6 @@ static void xfrm_state_gc_task(void *dat struct list_head *entry, *tmp; struct list_head gc_list = LIST_HEAD_INIT(gc_list); - if (xfrm_state_gc_flush_bundles) { - xfrm_state_gc_flush_bundles = 0; - xfrm_flush_bundles(); - } - spin_lock_bh(&xfrm_state_gc_lock); list_splice_init(&xfrm_state_gc_list, &gc_list); spin_unlock_bh(&xfrm_state_gc_lock); @@ -233,10 +226,8 @@ static int __xfrm_state_delete(struct xf * our caller holds. A larger value means that * there are DSTs attached to this xfrm_state. */ - if (atomic_read(&x->refcnt) > 2) { - xfrm_state_gc_flush_bundles = 1; - schedule_work(&xfrm_state_gc_work); - } + if (atomic_read(&x->refcnt) > 2) + xfrm_flush_bundles_by_state(x, 0); /* All xfrm_state objects are created by xfrm_state_alloc. * The xfrm_state_alloc call gives a reference, and that @@ -431,6 +422,8 @@ void xfrm_state_insert(struct xfrm_state spin_lock_bh(&xfrm_state_lock); __xfrm_state_insert(x); spin_unlock_bh(&xfrm_state_lock); + + xfrm_flush_bundles_by_state(x, 1); } EXPORT_SYMBOL(xfrm_state_insert); @@ -478,6 +471,9 @@ out: spin_unlock_bh(&xfrm_state_lock); xfrm_state_put_afinfo(afinfo); + if (!err) + xfrm_flush_bundles_by_state(x, 1); + if (x1) { xfrm_state_delete(x1); xfrm_state_put(x1); - To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html