diff options
-rw-r--r-- | include/net/inet_frag.h | 3 | ||||
-rw-r--r-- | net/ipv4/inet_fragment.c | 20 |
2 files changed, 21 insertions, 2 deletions
diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index 94092b1ef22e..e91b79ad4e4a 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -3,6 +3,7 @@ #define __NET_FRAG_H__ #include <linux/rhashtable-types.h> +#include <linux/completion.h> /* Per netns frag queues directory */ struct fqdir { @@ -104,6 +105,8 @@ struct inet_frags { struct kmem_cache *frags_cachep; const char *frags_cache_name; struct rhashtable_params rhash_params; + refcount_t refcnt; + struct completion completion; }; int inet_frags_init(struct inet_frags *); diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 7c07aae969e6..2b816f1ebbb4 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -110,14 +110,18 @@ int inet_frags_init(struct inet_frags *f) if (!f->frags_cachep) return -ENOMEM; + refcount_set(&f->refcnt, 1); + init_completion(&f->completion); return 0; } EXPORT_SYMBOL(inet_frags_init); void inet_frags_fini(struct inet_frags *f) { - /* We must wait that all inet_frag_destroy_rcu() have completed. */ - rcu_barrier(); + if (refcount_dec_and_test(&f->refcnt)) + complete(&f->completion); + + wait_for_completion(&f->completion); kmem_cache_destroy(f->frags_cachep); f->frags_cachep = NULL; @@ -149,8 +153,19 @@ static void fqdir_rwork_fn(struct work_struct *work) { struct fqdir *fqdir = container_of(to_rcu_work(work), struct fqdir, destroy_rwork); + struct inet_frags *f = fqdir->f; rhashtable_free_and_destroy(&fqdir->rhashtable, inet_frags_free_cb, NULL); + + /* We need to make sure all ongoing call_rcu(..., inet_frag_destroy_rcu) + * have completed, since they need to dereference fqdir. + * Would it not be nice to have kfree_rcu_barrier() ? :) + */ + rcu_barrier(); + + if (refcount_dec_and_test(&f->refcnt)) + complete(&f->completion); + kfree(fqdir); } @@ -168,6 +183,7 @@ int fqdir_init(struct fqdir **fqdirp, struct inet_frags *f, struct net *net) kfree(fqdir); return res; } + refcount_inc(&f->refcnt); *fqdirp = fqdir; return 0; } |