diff options
Diffstat (limited to 'net/ipv4/inet_fragment.c')
-rw-r--r-- | net/ipv4/inet_fragment.c | 29 |
1 files changed, 15 insertions, 14 deletions
diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index bcb11f3a27c0..760a9e52e02b 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c | |||
@@ -178,21 +178,22 @@ static struct inet_frag_queue *inet_frag_alloc(struct netns_frags *nf, | |||
178 | } | 178 | } |
179 | 179 | ||
180 | static struct inet_frag_queue *inet_frag_create(struct netns_frags *nf, | 180 | static struct inet_frag_queue *inet_frag_create(struct netns_frags *nf, |
181 | void *arg) | 181 | void *arg, |
182 | struct inet_frag_queue **prev) | ||
182 | { | 183 | { |
183 | struct inet_frags *f = nf->f; | 184 | struct inet_frags *f = nf->f; |
184 | struct inet_frag_queue *q; | 185 | struct inet_frag_queue *q; |
185 | int err; | ||
186 | 186 | ||
187 | q = inet_frag_alloc(nf, f, arg); | 187 | q = inet_frag_alloc(nf, f, arg); |
188 | if (!q) | 188 | if (!q) { |
189 | *prev = ERR_PTR(-ENOMEM); | ||
189 | return NULL; | 190 | return NULL; |
190 | 191 | } | |
191 | mod_timer(&q->timer, jiffies + nf->timeout); | 192 | mod_timer(&q->timer, jiffies + nf->timeout); |
192 | 193 | ||
193 | err = rhashtable_insert_fast(&nf->rhashtable, &q->node, | 194 | *prev = rhashtable_lookup_get_insert_key(&nf->rhashtable, &q->key, |
194 | f->rhash_params); | 195 | &q->node, f->rhash_params); |
195 | if (err < 0) { | 196 | if (*prev) { |
196 | q->flags |= INET_FRAG_COMPLETE; | 197 | q->flags |= INET_FRAG_COMPLETE; |
197 | inet_frag_kill(q); | 198 | inet_frag_kill(q); |
198 | inet_frag_destroy(q); | 199 | inet_frag_destroy(q); |
@@ -204,22 +205,22 @@ static struct inet_frag_queue *inet_frag_create(struct netns_frags *nf, | |||
204 | /* TODO : call from rcu_read_lock() and no longer use refcount_inc_not_zero() */ | 205 | /* TODO : call from rcu_read_lock() and no longer use refcount_inc_not_zero() */ |
205 | struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, void *key) | 206 | struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, void *key) |
206 | { | 207 | { |
207 | struct inet_frag_queue *fq; | 208 | struct inet_frag_queue *fq = NULL, *prev; |
208 | 209 | ||
209 | if (!nf->high_thresh || frag_mem_limit(nf) > nf->high_thresh) | 210 | if (!nf->high_thresh || frag_mem_limit(nf) > nf->high_thresh) |
210 | return NULL; | 211 | return NULL; |
211 | 212 | ||
212 | rcu_read_lock(); | 213 | rcu_read_lock(); |
213 | 214 | ||
214 | fq = rhashtable_lookup(&nf->rhashtable, key, nf->f->rhash_params); | 215 | prev = rhashtable_lookup(&nf->rhashtable, key, nf->f->rhash_params); |
215 | if (fq) { | 216 | if (!prev) |
217 | fq = inet_frag_create(nf, key, &prev); | ||
218 | if (prev && !IS_ERR(prev)) { | ||
219 | fq = prev; | ||
216 | if (!refcount_inc_not_zero(&fq->refcnt)) | 220 | if (!refcount_inc_not_zero(&fq->refcnt)) |
217 | fq = NULL; | 221 | fq = NULL; |
218 | rcu_read_unlock(); | ||
219 | return fq; | ||
220 | } | 222 | } |
221 | rcu_read_unlock(); | 223 | rcu_read_unlock(); |
222 | 224 | return fq; | |
223 | return inet_frag_create(nf, key); | ||
224 | } | 225 | } |
225 | EXPORT_SYMBOL(inet_frag_find); | 226 | EXPORT_SYMBOL(inet_frag_find); |