1
2
3
4
5
6
7
8
9
10
11#include <linux/module.h>
12#include <linux/types.h>
13#include <linux/slab.h>
14#include <linux/interrupt.h>
15#include <linux/spinlock.h>
16#include <linux/random.h>
17#include <linux/sched.h>
18#include <linux/timer.h>
19#include <linux/time.h>
20#include <linux/kernel.h>
21#include <linux/mm.h>
22#include <linux/net.h>
23#include <net/inetpeer.h>
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73spinlock_t inet_peer_idlock = SPIN_LOCK_UNLOCKED;
74
75static kmem_cache_t *peer_cachep;
76
77#define node_height(x) x->avl_height
78static struct inet_peer peer_fake_node = {
79 .avl_left = &peer_fake_node,
80 .avl_right = &peer_fake_node,
81 .avl_height = 0
82};
83#define peer_avl_empty (&peer_fake_node)
84static struct inet_peer *peer_root = peer_avl_empty;
85static rwlock_t peer_pool_lock = RW_LOCK_UNLOCKED;
86#define PEER_MAXDEPTH 40
87
88static volatile int peer_total;
89
90int inet_peer_threshold = 65536 + 128;
91
92int inet_peer_minttl = 120 * HZ;
93int inet_peer_maxttl = 10 * 60 * HZ;
94
95
96struct inet_peer *inet_peer_unused_head,
97 **inet_peer_unused_tailp = &inet_peer_unused_head;
98spinlock_t inet_peer_unused_lock = SPIN_LOCK_UNLOCKED;
99#define PEER_MAX_CLEANUP_WORK 30
100
101static void peer_check_expire(unsigned long dummy);
102static struct timer_list peer_periodic_timer =
103 TIMER_INITIALIZER(peer_check_expire, 0, 0);
104
105
106int inet_peer_gc_mintime = 10 * HZ,
107 inet_peer_gc_maxtime = 120 * HZ;
108
109
110void __init inet_initpeers(void)
111{
112 struct sysinfo si;
113
114
115 si_meminfo(&si);
116
117
118
119
120 if (si.totalram <= (32768*1024)/PAGE_SIZE)
121 inet_peer_threshold >>= 1;
122 if (si.totalram <= (16384*1024)/PAGE_SIZE)
123 inet_peer_threshold >>= 1;
124 if (si.totalram <= (8192*1024)/PAGE_SIZE)
125 inet_peer_threshold >>= 2;
126
127 peer_cachep = kmem_cache_create("inet_peer_cache",
128 sizeof(struct inet_peer),
129 0, SLAB_HWCACHE_ALIGN,
130 NULL, NULL);
131
132
133
134
135 peer_periodic_timer.expires = jiffies
136 + net_random() % inet_peer_gc_maxtime
137 + inet_peer_gc_maxtime;
138 add_timer(&peer_periodic_timer);
139}
140
141
142static void unlink_from_unused(struct inet_peer *p)
143{
144 spin_lock_bh(&inet_peer_unused_lock);
145 if (p->unused_prevp != NULL) {
146
147 *p->unused_prevp = p->unused_next;
148 if (p->unused_next != NULL)
149 p->unused_next->unused_prevp = p->unused_prevp;
150 else
151 inet_peer_unused_tailp = p->unused_prevp;
152 p->unused_prevp = NULL;
153 }
154 spin_unlock_bh(&inet_peer_unused_lock);
155}
156
157
158#define lookup(daddr) \
159({ \
160 struct inet_peer *u, **v; \
161 stackptr = stack; \
162 *stackptr++ = &peer_root; \
163 for (u = peer_root; u != peer_avl_empty; ) { \
164 if (daddr == u->v4daddr) \
165 break; \
166 if (daddr < u->v4daddr) \
167 v = &u->avl_left; \
168 else \
169 v = &u->avl_right; \
170 *stackptr++ = v; \
171 u = *v; \
172 } \
173 u; \
174})
175
176
177#define lookup_rightempty(start) \
178({ \
179 struct inet_peer *u, **v; \
180 *stackptr++ = &start->avl_left; \
181 v = &start->avl_left; \
182 for (u = *v; u->avl_right != peer_avl_empty; ) { \
183 v = &u->avl_right; \
184 *stackptr++ = v; \
185 u = *v; \
186 } \
187 u; \
188})
189
190
191
192
193static void peer_avl_rebalance(struct inet_peer **stack[],
194 struct inet_peer ***stackend)
195{
196 struct inet_peer **nodep, *node, *l, *r;
197 int lh, rh;
198
199 while (stackend > stack) {
200 nodep = *--stackend;
201 node = *nodep;
202 l = node->avl_left;
203 r = node->avl_right;
204 lh = node_height(l);
205 rh = node_height(r);
206 if (lh > rh + 1) {
207 struct inet_peer *ll, *lr, *lrl, *lrr;
208 int lrh;
209 ll = l->avl_left;
210 lr = l->avl_right;
211 lrh = node_height(lr);
212 if (lrh <= node_height(ll)) {
213 node->avl_left = lr;
214 node->avl_right = r;
215 node->avl_height = lrh + 1;
216 l->avl_left = ll;
217 l->avl_right = node;
218 l->avl_height = node->avl_height + 1;
219 *nodep = l;
220 } else {
221 lrl = lr->avl_left;
222 lrr = lr->avl_right;
223 node->avl_left = lrr;
224 node->avl_right = r;
225 node->avl_height = rh + 1;
226 l->avl_left = ll;
227 l->avl_right = lrl;
228 l->avl_height = rh + 1;
229 lr->avl_left = l;
230 lr->avl_right = node;
231 lr->avl_height = rh + 2;
232 *nodep = lr;
233 }
234 } else if (rh > lh + 1) {
235 struct inet_peer *rr, *rl, *rlr, *rll;
236 int rlh;
237 rr = r->avl_right;
238 rl = r->avl_left;
239 rlh = node_height(rl);
240 if (rlh <= node_height(rr)) {
241 node->avl_right = rl;
242 node->avl_left = l;
243 node->avl_height = rlh + 1;
244 r->avl_right = rr;
245 r->avl_left = node;
246 r->avl_height = node->avl_height + 1;
247 *nodep = r;
248 } else {
249 rlr = rl->avl_right;
250 rll = rl->avl_left;
251 node->avl_right = rll;
252 node->avl_left = l;
253 node->avl_height = lh + 1;
254 r->avl_right = rr;
255 r->avl_left = rlr;
256 r->avl_height = lh + 1;
257 rl->avl_right = r;
258 rl->avl_left = node;
259 rl->avl_height = lh + 2;
260 *nodep = rl;
261 }
262 } else {
263 node->avl_height = (lh > rh ? lh : rh) + 1;
264 }
265 }
266}
267
268
269#define link_to_pool(n) \
270do { \
271 n->avl_height = 1; \
272 n->avl_left = peer_avl_empty; \
273 n->avl_right = peer_avl_empty; \
274 **--stackptr = n; \
275 peer_avl_rebalance(stack, stackptr); \
276} while(0)
277
278
279static void unlink_from_pool(struct inet_peer *p)
280{
281 int do_free;
282
283 do_free = 0;
284
285 write_lock_bh(&peer_pool_lock);
286
287
288
289
290
291 if (atomic_read(&p->refcnt) == 1) {
292 struct inet_peer **stack[PEER_MAXDEPTH];
293 struct inet_peer ***stackptr, ***delp;
294 if (lookup(p->v4daddr) != p)
295 BUG();
296 delp = stackptr - 1;
297 if (p->avl_left == peer_avl_empty) {
298 *delp[0] = p->avl_right;
299 --stackptr;
300 } else {
301
302 struct inet_peer *t;
303 t = lookup_rightempty(p);
304 if (*stackptr[-1] != t)
305 BUG();
306 **--stackptr = t->avl_left;
307
308
309
310 *delp[0] = t;
311 t->avl_left = p->avl_left;
312 t->avl_right = p->avl_right;
313 t->avl_height = p->avl_height;
314 if (delp[1] != &p->avl_left)
315 BUG();
316 delp[1] = &t->avl_left;
317 }
318 peer_avl_rebalance(stack, stackptr);
319 peer_total--;
320 do_free = 1;
321 }
322 write_unlock_bh(&peer_pool_lock);
323
324 if (do_free)
325 kmem_cache_free(peer_cachep, p);
326 else
327
328
329
330
331
332
333 inet_putpeer(p);
334}
335
336
337static int cleanup_once(unsigned long ttl)
338{
339 struct inet_peer *p;
340
341
342 spin_lock_bh(&inet_peer_unused_lock);
343 p = inet_peer_unused_head;
344 if (p != NULL) {
345 if (time_after(p->dtime + ttl, jiffies)) {
346
347 spin_unlock_bh(&inet_peer_unused_lock);
348 return -1;
349 }
350 inet_peer_unused_head = p->unused_next;
351 if (p->unused_next != NULL)
352 p->unused_next->unused_prevp = p->unused_prevp;
353 else
354 inet_peer_unused_tailp = p->unused_prevp;
355 p->unused_prevp = NULL;
356
357
358 atomic_inc(&p->refcnt);
359 }
360 spin_unlock_bh(&inet_peer_unused_lock);
361
362 if (p == NULL)
363
364
365
366 return -1;
367
368 unlink_from_pool(p);
369 return 0;
370}
371
372
373struct inet_peer *inet_getpeer(__u32 daddr, int create)
374{
375 struct inet_peer *p, *n;
376 struct inet_peer **stack[PEER_MAXDEPTH], ***stackptr;
377
378
379 read_lock_bh(&peer_pool_lock);
380 p = lookup(daddr);
381 if (p != peer_avl_empty)
382 atomic_inc(&p->refcnt);
383 read_unlock_bh(&peer_pool_lock);
384
385 if (p != peer_avl_empty) {
386
387
388 unlink_from_unused(p);
389 return p;
390 }
391
392 if (!create)
393 return NULL;
394
395
396 n = kmem_cache_alloc(peer_cachep, GFP_ATOMIC);
397 if (n == NULL)
398 return NULL;
399 n->v4daddr = daddr;
400 atomic_set(&n->refcnt, 1);
401 n->ip_id_count = secure_ip_id(daddr);
402 n->tcp_ts_stamp = 0;
403
404 write_lock_bh(&peer_pool_lock);
405
406 p = lookup(daddr);
407 if (p != peer_avl_empty)
408 goto out_free;
409
410
411 link_to_pool(n);
412 n->unused_prevp = NULL;
413 peer_total++;
414 write_unlock_bh(&peer_pool_lock);
415
416 if (peer_total >= inet_peer_threshold)
417
418 cleanup_once(0);
419
420 return n;
421
422out_free:
423
424 atomic_inc(&p->refcnt);
425 write_unlock_bh(&peer_pool_lock);
426
427 unlink_from_unused(p);
428
429 kmem_cache_free(peer_cachep, n);
430 return p;
431}
432
433
434static void peer_check_expire(unsigned long dummy)
435{
436 int i;
437 int ttl;
438
439 if (peer_total >= inet_peer_threshold)
440 ttl = inet_peer_minttl;
441 else
442 ttl = inet_peer_maxttl
443 - (inet_peer_maxttl - inet_peer_minttl) / HZ *
444 peer_total / inet_peer_threshold * HZ;
445 for (i = 0; i < PEER_MAX_CLEANUP_WORK && !cleanup_once(ttl); i++);
446
447
448
449
450 peer_periodic_timer.expires = jiffies
451 + inet_peer_gc_maxtime
452 - (inet_peer_gc_maxtime - inet_peer_gc_mintime) / HZ *
453 peer_total / inet_peer_threshold * HZ;
454 add_timer(&peer_periodic_timer);
455}
456
457EXPORT_SYMBOL(inet_peer_idlock);
458