summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/inet_frag.h3
-rw-r--r--net/ipv4/inet_fragment.c20
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 @@
3#define __NET_FRAG_H__ 3#define __NET_FRAG_H__
4 4
5#include <linux/rhashtable-types.h> 5#include <linux/rhashtable-types.h>
6#include <linux/completion.h>
6 7
7/* Per netns frag queues directory */ 8/* Per netns frag queues directory */
8struct fqdir { 9struct fqdir {
@@ -104,6 +105,8 @@ struct inet_frags {
104 struct kmem_cache *frags_cachep; 105 struct kmem_cache *frags_cachep;
105 const char *frags_cache_name; 106 const char *frags_cache_name;
106 struct rhashtable_params rhash_params; 107 struct rhashtable_params rhash_params;
108 refcount_t refcnt;
109 struct completion completion;
107}; 110};
108 111
109int inet_frags_init(struct inet_frags *); 112int 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)
110 if (!f->frags_cachep) 110 if (!f->frags_cachep)
111 return -ENOMEM; 111 return -ENOMEM;
112 112
113 refcount_set(&f->refcnt, 1);
114 init_completion(&f->completion);
113 return 0; 115 return 0;
114} 116}
115EXPORT_SYMBOL(inet_frags_init); 117EXPORT_SYMBOL(inet_frags_init);
116 118
117void inet_frags_fini(struct inet_frags *f) 119void inet_frags_fini(struct inet_frags *f)
118{ 120{
119 /* We must wait that all inet_frag_destroy_rcu() have completed. */ 121 if (refcount_dec_and_test(&f->refcnt))
120 rcu_barrier(); 122 complete(&f->completion);
123
124 wait_for_completion(&f->completion);
121 125
122 kmem_cache_destroy(f->frags_cachep); 126 kmem_cache_destroy(f->frags_cachep);
123 f->frags_cachep = NULL; 127 f->frags_cachep = NULL;
@@ -149,8 +153,19 @@ static void fqdir_rwork_fn(struct work_struct *work)
149{ 153{
150 struct fqdir *fqdir = container_of(to_rcu_work(work), 154 struct fqdir *fqdir = container_of(to_rcu_work(work),
151 struct fqdir, destroy_rwork); 155 struct fqdir, destroy_rwork);
156 struct inet_frags *f = fqdir->f;
152 157
153 rhashtable_free_and_destroy(&fqdir->rhashtable, inet_frags_free_cb, NULL); 158 rhashtable_free_and_destroy(&fqdir->rhashtable, inet_frags_free_cb, NULL);
159
160 /* We need to make sure all ongoing call_rcu(..., inet_frag_destroy_rcu)
161 * have completed, since they need to dereference fqdir.
162 * Would it not be nice to have kfree_rcu_barrier() ? :)
163 */
164 rcu_barrier();
165
166 if (refcount_dec_and_test(&f->refcnt))
167 complete(&f->completion);
168
154 kfree(fqdir); 169 kfree(fqdir);
155} 170}
156 171
@@ -168,6 +183,7 @@ int fqdir_init(struct fqdir **fqdirp, struct inet_frags *f, struct net *net)
168 kfree(fqdir); 183 kfree(fqdir);
169 return res; 184 return res;
170 } 185 }
186 refcount_inc(&f->refcnt);
171 *fqdirp = fqdir; 187 *fqdirp = fqdir;
172 return 0; 188 return 0;
173} 189}