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 @@ | |||
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 */ |
8 | struct fqdir { | 9 | struct 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 | ||
109 | int inet_frags_init(struct inet_frags *); | 112 | 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) | |||
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 | } |
115 | EXPORT_SYMBOL(inet_frags_init); | 117 | EXPORT_SYMBOL(inet_frags_init); |
116 | 118 | ||
117 | void inet_frags_fini(struct inet_frags *f) | 119 | void 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 | } |