On Thu, Mar 3, 2011 at 3:50 AM, "Oleg A. Arkhangelsky" <sysoleg@xxxxxxxxx> wrote: > 02.03.2011, 17:37, "Changli Gao" <xiaosuo@xxxxxxxxx>: > >> t should be NULL here, as offsetof(struct nf_conn, dst.protonum) == 0x36. >> We should free the nf_ct_extend with call_rcu(), since nat ext is >> referenced in the rcu read context. > > Yes, I think the problem is triggered when nf_conntrack_free() is called by > different CPU during net->ipv4.nat_bysource hash traversal. Extensions > framework doesn't have any SLAB_DESTROY_BY_RCU magic. > > I'm not sure, but couldn't this problem be introduced by: > > ea781f197d6a835cbb93a0bf88ee1696296ed8aa > netfilter: nf_conntrack: use SLAB_DESTROY_BY_RCU and get rid of call_rcu() > > ? > There is nothing to do with SLAB_DESTROY_BY_RCU. Here is the comment for this flag: /* * SLAB_DESTROY_BY_RCU - **WARNING** READ THIS! * * This delays freeing the SLAB page by a grace period, it does _NOT_ * delay object freeing. This means that if you do kmem_cache_free() * that memory location is free to be reused at any time. Thus it may * be possible to see another object there in the same RCU grace period. * * This feature only ensures the memory location backing the object * stays valid, the trick to using this is relying on an independent * object validation pass. Something like: ... Please try the patch attached and test if the problem is solved or not. Thanks. -- Regards, Changli Gao(xiaosuo@xxxxxxxxx)
diff --git a/include/net/netfilter/nf_conntrack_extend.h b/include/net/netfilter/nf_conntrack_extend.h index 2dcf317..354cccb9 100644 --- a/include/net/netfilter/nf_conntrack_extend.h +++ b/include/net/netfilter/nf_conntrack_extend.h @@ -66,13 +66,15 @@ static inline void nf_ct_ext_destroy(struct nf_conn *ct) __nf_ct_ext_destroy(ct); } +void __nf_ct_ext_free_rcu(struct rcu_head *head); + /* Free operation. If you want to free a object referred from private area, * please implement __nf_ct_ext_free() and call it. */ static inline void nf_ct_ext_free(struct nf_conn *ct) { if (ct->ext) - kfree(ct->ext); + call_rcu(&ct->ext->rcu, __nf_ct_ext_free_rcu); } /* Add this type, returns pointer to data or NULL. */ diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index 80a23ed..3a47b76 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c @@ -68,11 +68,12 @@ nf_ct_ext_create(struct nf_ct_ext **ext, enum nf_ct_ext_id id, gfp_t gfp) return (void *)(*ext) + off; } -static void __nf_ct_ext_free_rcu(struct rcu_head *head) +void __nf_ct_ext_free_rcu(struct rcu_head *head) { struct nf_ct_ext *ext = container_of(head, struct nf_ct_ext, rcu); kfree(ext); } +EXPORT_SYMBOL_GPL(__nf_ct_ext_free_rcu); void *__nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) {