Blame view
net/ipv4/inet_fragment.c
6.35 KB
7eb95156d [INET]: Collect f... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/* * inet fragments management * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * * Authors: Pavel Emelyanov <xemul@openvz.org> * Started as consolidation of ipv4/ip_fragment.c, * ipv6/reassembly. and ipv6 nf conntrack reassembly */ #include <linux/list.h> #include <linux/spinlock.h> #include <linux/module.h> #include <linux/timer.h> #include <linux/mm.h> |
321a3a99e [INET]: Consolida... |
19 |
#include <linux/random.h> |
1e4b82873 [INET]: Consolida... |
20 21 |
#include <linux/skbuff.h> #include <linux/rtnetlink.h> |
5a0e3ad6a include cleanup: ... |
22 |
#include <linux/slab.h> |
7eb95156d [INET]: Collect f... |
23 24 |
#include <net/inet_frag.h> |
321a3a99e [INET]: Consolida... |
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
static void inet_frag_secret_rebuild(unsigned long dummy) { struct inet_frags *f = (struct inet_frags *)dummy; unsigned long now = jiffies; int i; write_lock(&f->lock); get_random_bytes(&f->rnd, sizeof(u32)); for (i = 0; i < INETFRAGS_HASHSZ; i++) { struct inet_frag_queue *q; struct hlist_node *p, *n; hlist_for_each_entry_safe(q, p, n, &f->hash[i], list) { unsigned int hval = f->hashfn(q); if (hval != i) { hlist_del(&q->list); /* Relink to new hash chain. */ hlist_add_head(&q->list, &f->hash[hval]); } } } write_unlock(&f->lock); |
3b4bc4a2b [NETNS][FRAGS]: I... |
49 |
mod_timer(&f->secret_timer, now + f->secret_interval); |
321a3a99e [INET]: Consolida... |
50 |
} |
7eb95156d [INET]: Collect f... |
51 52 53 54 55 56 |
void inet_frags_init(struct inet_frags *f) { int i; for (i = 0; i < INETFRAGS_HASHSZ; i++) INIT_HLIST_HEAD(&f->hash[i]); |
7eb95156d [INET]: Collect f... |
57 58 59 60 |
rwlock_init(&f->lock); f->rnd = (u32) ((num_physpages ^ (num_physpages>>7)) ^ (jiffies ^ (jiffies >> 6))); |
b24b8a247 [NET]: Convert in... |
61 62 |
setup_timer(&f->secret_timer, inet_frag_secret_rebuild, (unsigned long)f); |
3b4bc4a2b [NETNS][FRAGS]: I... |
63 |
f->secret_timer.expires = jiffies + f->secret_interval; |
321a3a99e [INET]: Consolida... |
64 |
add_timer(&f->secret_timer); |
7eb95156d [INET]: Collect f... |
65 66 |
} EXPORT_SYMBOL(inet_frags_init); |
e5a2bb842 [NETNS][FRAGS]: M... |
67 68 69 |
void inet_frags_init_net(struct netns_frags *nf) { nf->nqueues = 0; |
6ddc08222 [NETNS][FRAGS]: M... |
70 |
atomic_set(&nf->mem, 0); |
3140c25c8 [NETNS][FRAGS]: M... |
71 |
INIT_LIST_HEAD(&nf->lru_list); |
e5a2bb842 [NETNS][FRAGS]: M... |
72 73 |
} EXPORT_SYMBOL(inet_frags_init_net); |
7eb95156d [INET]: Collect f... |
74 75 |
void inet_frags_fini(struct inet_frags *f) { |
321a3a99e [INET]: Consolida... |
76 |
del_timer(&f->secret_timer); |
7eb95156d [INET]: Collect f... |
77 78 |
} EXPORT_SYMBOL(inet_frags_fini); |
277e650dd [INET]: Consolida... |
79 |
|
81566e832 [NETNS][FRAGS]: M... |
80 81 82 |
void inet_frags_exit_net(struct netns_frags *nf, struct inet_frags *f) { nf->low_thresh = 0; |
e8e16b706 [INET]: inet_frag... |
83 84 |
local_bh_disable(); |
81566e832 [NETNS][FRAGS]: M... |
85 |
inet_frag_evictor(nf, f); |
e8e16b706 [INET]: inet_frag... |
86 |
local_bh_enable(); |
81566e832 [NETNS][FRAGS]: M... |
87 88 |
} EXPORT_SYMBOL(inet_frags_exit_net); |
277e650dd [INET]: Consolida... |
89 90 91 92 93 |
static inline void fq_unlink(struct inet_frag_queue *fq, struct inet_frags *f) { write_lock(&f->lock); hlist_del(&fq->list); list_del(&fq->lru_list); |
e5a2bb842 [NETNS][FRAGS]: M... |
94 |
fq->net->nqueues--; |
277e650dd [INET]: Consolida... |
95 96 97 98 99 100 101 |
write_unlock(&f->lock); } void inet_frag_kill(struct inet_frag_queue *fq, struct inet_frags *f) { if (del_timer(&fq->timer)) atomic_dec(&fq->refcnt); |
bc578a54f [NET]: Rename ine... |
102 |
if (!(fq->last_in & INET_FRAG_COMPLETE)) { |
277e650dd [INET]: Consolida... |
103 104 |
fq_unlink(fq, f); atomic_dec(&fq->refcnt); |
bc578a54f [NET]: Rename ine... |
105 |
fq->last_in |= INET_FRAG_COMPLETE; |
277e650dd [INET]: Consolida... |
106 107 |
} } |
277e650dd [INET]: Consolida... |
108 |
EXPORT_SYMBOL(inet_frag_kill); |
1e4b82873 [INET]: Consolida... |
109 |
|
6ddc08222 [NETNS][FRAGS]: M... |
110 111 |
static inline void frag_kfree_skb(struct netns_frags *nf, struct inet_frags *f, struct sk_buff *skb, int *work) |
1e4b82873 [INET]: Consolida... |
112 113 114 |
{ if (work) *work -= skb->truesize; |
6ddc08222 [NETNS][FRAGS]: M... |
115 |
atomic_sub(skb->truesize, &nf->mem); |
1e4b82873 [INET]: Consolida... |
116 117 118 119 120 121 122 123 124 |
if (f->skb_free) f->skb_free(skb); kfree_skb(skb); } void inet_frag_destroy(struct inet_frag_queue *q, struct inet_frags *f, int *work) { struct sk_buff *fp; |
6ddc08222 [NETNS][FRAGS]: M... |
125 |
struct netns_frags *nf; |
1e4b82873 [INET]: Consolida... |
126 |
|
547b792ca net: convert BUG_... |
127 128 |
WARN_ON(!(q->last_in & INET_FRAG_COMPLETE)); WARN_ON(del_timer(&q->timer) != 0); |
1e4b82873 [INET]: Consolida... |
129 130 131 |
/* Release all fragment data. */ fp = q->fragments; |
6ddc08222 [NETNS][FRAGS]: M... |
132 |
nf = q->net; |
1e4b82873 [INET]: Consolida... |
133 134 |
while (fp) { struct sk_buff *xp = fp->next; |
6ddc08222 [NETNS][FRAGS]: M... |
135 |
frag_kfree_skb(nf, f, fp, work); |
1e4b82873 [INET]: Consolida... |
136 137 138 139 140 |
fp = xp; } if (work) *work -= f->qsize; |
6ddc08222 [NETNS][FRAGS]: M... |
141 |
atomic_sub(f->qsize, &nf->mem); |
1e4b82873 [INET]: Consolida... |
142 |
|
c95477090 [INET]: Consolida... |
143 144 145 |
if (f->destructor) f->destructor(q); kfree(q); |
1e4b82873 [INET]: Consolida... |
146 147 148 |
} EXPORT_SYMBOL(inet_frag_destroy); |
8e7999c44 [INET]: Consolida... |
149 |
|
6ddc08222 [NETNS][FRAGS]: M... |
150 |
int inet_frag_evictor(struct netns_frags *nf, struct inet_frags *f) |
8e7999c44 [INET]: Consolida... |
151 152 153 |
{ struct inet_frag_queue *q; int work, evicted = 0; |
e31e0bdc7 [NETNS][FRAGS]: M... |
154 |
work = atomic_read(&nf->mem) - nf->low_thresh; |
8e7999c44 [INET]: Consolida... |
155 156 |
while (work > 0) { read_lock(&f->lock); |
3140c25c8 [NETNS][FRAGS]: M... |
157 |
if (list_empty(&nf->lru_list)) { |
8e7999c44 [INET]: Consolida... |
158 159 160 |
read_unlock(&f->lock); break; } |
3140c25c8 [NETNS][FRAGS]: M... |
161 |
q = list_first_entry(&nf->lru_list, |
8e7999c44 [INET]: Consolida... |
162 163 164 165 166 |
struct inet_frag_queue, lru_list); atomic_inc(&q->refcnt); read_unlock(&f->lock); spin_lock(&q->lock); |
bc578a54f [NET]: Rename ine... |
167 |
if (!(q->last_in & INET_FRAG_COMPLETE)) |
8e7999c44 [INET]: Consolida... |
168 169 170 171 172 173 174 175 176 177 178 |
inet_frag_kill(q, f); spin_unlock(&q->lock); if (atomic_dec_and_test(&q->refcnt)) inet_frag_destroy(q, f, &work); evicted++; } return evicted; } EXPORT_SYMBOL(inet_frag_evictor); |
2588fe1d7 [INET]: Consolida... |
179 |
|
ac18e7509 [NETNS][FRAGS]: M... |
180 181 |
static struct inet_frag_queue *inet_frag_intern(struct netns_frags *nf, struct inet_frag_queue *qp_in, struct inet_frags *f, |
9a375803f inet fragments: f... |
182 |
void *arg) |
2588fe1d7 [INET]: Consolida... |
183 184 185 186 187 |
{ struct inet_frag_queue *qp; #ifdef CONFIG_SMP struct hlist_node *n; #endif |
9a375803f inet fragments: f... |
188 |
unsigned int hash; |
2588fe1d7 [INET]: Consolida... |
189 190 |
write_lock(&f->lock); |
9a375803f inet fragments: f... |
191 192 193 194 195 196 |
/* * While we stayed w/o the lock other CPU could update * the rnd seed, so we need to re-calculate the hash * chain. Fortunatelly the qp_in can be used to get one. */ hash = f->hashfn(qp_in); |
2588fe1d7 [INET]: Consolida... |
197 198 199 200 201 202 |
#ifdef CONFIG_SMP /* With SMP race we have to recheck hash table, because * such entry could be created on other cpu, while we * promoted read lock to write lock. */ hlist_for_each_entry(qp, n, &f->hash[hash], list) { |
ac18e7509 [NETNS][FRAGS]: M... |
203 |
if (qp->net == nf && f->match(qp, arg)) { |
2588fe1d7 [INET]: Consolida... |
204 205 |
atomic_inc(&qp->refcnt); write_unlock(&f->lock); |
bc578a54f [NET]: Rename ine... |
206 |
qp_in->last_in |= INET_FRAG_COMPLETE; |
2588fe1d7 [INET]: Consolida... |
207 208 209 210 211 212 |
inet_frag_put(qp_in, f); return qp; } } #endif qp = qp_in; |
b2fd5321d [NETNS][FRAGS]: M... |
213 |
if (!mod_timer(&qp->timer, jiffies + nf->timeout)) |
2588fe1d7 [INET]: Consolida... |
214 215 216 217 |
atomic_inc(&qp->refcnt); atomic_inc(&qp->refcnt); hlist_add_head(&qp->list, &f->hash[hash]); |
3140c25c8 [NETNS][FRAGS]: M... |
218 |
list_add_tail(&qp->lru_list, &nf->lru_list); |
e5a2bb842 [NETNS][FRAGS]: M... |
219 |
nf->nqueues++; |
2588fe1d7 [INET]: Consolida... |
220 221 222 |
write_unlock(&f->lock); return qp; } |
e521db9d7 [INET]: Consolida... |
223 |
|
ac18e7509 [NETNS][FRAGS]: M... |
224 225 |
static struct inet_frag_queue *inet_frag_alloc(struct netns_frags *nf, struct inet_frags *f, void *arg) |
e521db9d7 [INET]: Consolida... |
226 227 228 229 230 231 |
{ struct inet_frag_queue *q; q = kzalloc(f->qsize, GFP_ATOMIC); if (q == NULL) return NULL; |
c6fda2822 [INET]: Consolida... |
232 |
f->constructor(q, arg); |
6ddc08222 [NETNS][FRAGS]: M... |
233 |
atomic_add(f->qsize, &nf->mem); |
e521db9d7 [INET]: Consolida... |
234 235 236 |
setup_timer(&q->timer, f->frag_expire, (unsigned long)q); spin_lock_init(&q->lock); atomic_set(&q->refcnt, 1); |
ac18e7509 [NETNS][FRAGS]: M... |
237 |
q->net = nf; |
e521db9d7 [INET]: Consolida... |
238 239 240 |
return q; } |
c6fda2822 [INET]: Consolida... |
241 |
|
ac18e7509 [NETNS][FRAGS]: M... |
242 |
static struct inet_frag_queue *inet_frag_create(struct netns_frags *nf, |
9a375803f inet fragments: f... |
243 |
struct inet_frags *f, void *arg) |
c6fda2822 [INET]: Consolida... |
244 245 |
{ struct inet_frag_queue *q; |
ac18e7509 [NETNS][FRAGS]: M... |
246 |
q = inet_frag_alloc(nf, f, arg); |
c6fda2822 [INET]: Consolida... |
247 248 |
if (q == NULL) return NULL; |
9a375803f inet fragments: f... |
249 |
return inet_frag_intern(nf, q, f, arg); |
c6fda2822 [INET]: Consolida... |
250 |
} |
abd6523d1 [INET]: Consolida... |
251 |
|
ac18e7509 [NETNS][FRAGS]: M... |
252 253 |
struct inet_frag_queue *inet_frag_find(struct netns_frags *nf, struct inet_frags *f, void *key, unsigned int hash) |
56bca31ff inet fragments: f... |
254 |
__releases(&f->lock) |
abd6523d1 [INET]: Consolida... |
255 256 257 |
{ struct inet_frag_queue *q; struct hlist_node *n; |
abd6523d1 [INET]: Consolida... |
258 |
hlist_for_each_entry(q, n, &f->hash[hash], list) { |
ac18e7509 [NETNS][FRAGS]: M... |
259 |
if (q->net == nf && f->match(q, key)) { |
abd6523d1 [INET]: Consolida... |
260 261 262 263 264 265 |
atomic_inc(&q->refcnt); read_unlock(&f->lock); return q; } } read_unlock(&f->lock); |
9a375803f inet fragments: f... |
266 |
return inet_frag_create(nf, f, key); |
abd6523d1 [INET]: Consolida... |
267 268 |
} EXPORT_SYMBOL(inet_frag_find); |