]>
Commit | Line | Data |
---|---|---|
0e87506f ACM |
1 | /* |
2 | * NET Generic infrastructure for Network protocols. | |
3 | * | |
4 | * Authors: Arnaldo Carvalho de Melo <acme@conectiva.com.br> | |
5 | * | |
6 | * From code originally in include/net/tcp.h | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or | |
9 | * modify it under the terms of the GNU General Public License | |
10 | * as published by the Free Software Foundation; either version | |
11 | * 2 of the License, or (at your option) any later version. | |
12 | */ | |
13 | ||
14 | #include <linux/module.h> | |
15 | #include <linux/random.h> | |
16 | #include <linux/slab.h> | |
17 | #include <linux/string.h> | |
72a3effa | 18 | #include <linux/vmalloc.h> |
0e87506f ACM |
19 | |
20 | #include <net/request_sock.h> | |
21 | ||
e52c1f17 DM |
22 | /* |
23 | * Maximum number of SYN_RECV sockets in queue per LISTEN socket. | |
24 | * One SYN_RECV socket costs about 80bytes on a 32bit machine. | |
25 | * It would be better to replace it with a global counter for all sockets | |
26 | * but then some measure against one socket starving all other sockets | |
27 | * would be needed. | |
28 | * | |
29 | * It was 128 by default. Experiments with real servers show, that | |
30 | * it is absolutely not enough even at 100conn/sec. 256 cures most | |
31 | * of problems. This value is adjusted to 128 for very small machines | |
32 | * (<=32Mb of memory) and to 1024 on normal or better ones (>=256Mb). | |
72a3effa | 33 | * Note : Dont forget somaxconn that may limit backlog too. |
e52c1f17 DM |
34 | */ |
35 | int sysctl_max_syn_backlog = 256; | |
493f377d | 36 | EXPORT_SYMBOL(sysctl_max_syn_backlog); |
e52c1f17 | 37 | |
0e87506f | 38 | int reqsk_queue_alloc(struct request_sock_queue *queue, |
72a3effa | 39 | unsigned int nr_table_entries) |
0e87506f | 40 | { |
72a3effa ED |
41 | size_t lopt_size = sizeof(struct listen_sock); |
42 | struct listen_sock *lopt; | |
43 | ||
44 | nr_table_entries = min_t(u32, nr_table_entries, sysctl_max_syn_backlog); | |
45 | nr_table_entries = max_t(u32, nr_table_entries, 8); | |
46 | nr_table_entries = roundup_pow_of_two(nr_table_entries + 1); | |
47 | lopt_size += nr_table_entries * sizeof(struct request_sock *); | |
48 | if (lopt_size > PAGE_SIZE) | |
7a1c8e5a | 49 | lopt = vzalloc(lopt_size); |
72a3effa ED |
50 | else |
51 | lopt = kzalloc(lopt_size, GFP_KERNEL); | |
0e87506f ACM |
52 | if (lopt == NULL) |
53 | return -ENOMEM; | |
54 | ||
72a3effa ED |
55 | for (lopt->max_qlen_log = 3; |
56 | (1 << lopt->max_qlen_log) < nr_table_entries; | |
0e87506f ACM |
57 | lopt->max_qlen_log++); |
58 | ||
59 | get_random_bytes(&lopt->hash_rnd, sizeof(lopt->hash_rnd)); | |
60 | rwlock_init(&queue->syn_wait_lock); | |
3eb4801d | 61 | queue->rskq_accept_head = NULL; |
83e3609e | 62 | lopt->nr_table_entries = nr_table_entries; |
0e87506f ACM |
63 | |
64 | write_lock_bh(&queue->syn_wait_lock); | |
65 | queue->listen_opt = lopt; | |
66 | write_unlock_bh(&queue->syn_wait_lock); | |
67 | ||
68 | return 0; | |
69 | } | |
70 | ||
dab6ba36 PE |
71 | void __reqsk_queue_destroy(struct request_sock_queue *queue) |
72 | { | |
73 | struct listen_sock *lopt; | |
74 | size_t lopt_size; | |
75 | ||
76 | /* | |
77 | * this is an error recovery path only | |
78 | * no locking needed and the lopt is not NULL | |
79 | */ | |
80 | ||
81 | lopt = queue->listen_opt; | |
82 | lopt_size = sizeof(struct listen_sock) + | |
83 | lopt->nr_table_entries * sizeof(struct request_sock *); | |
84 | ||
85 | if (lopt_size > PAGE_SIZE) | |
86 | vfree(lopt); | |
87 | else | |
88 | kfree(lopt); | |
89 | } | |
90 | ||
dab6ba36 PE |
91 | static inline struct listen_sock *reqsk_queue_yank_listen_sk( |
92 | struct request_sock_queue *queue) | |
93 | { | |
94 | struct listen_sock *lopt; | |
95 | ||
96 | write_lock_bh(&queue->syn_wait_lock); | |
97 | lopt = queue->listen_opt; | |
98 | queue->listen_opt = NULL; | |
99 | write_unlock_bh(&queue->syn_wait_lock); | |
100 | ||
101 | return lopt; | |
102 | } | |
103 | ||
83e3609e ACM |
104 | void reqsk_queue_destroy(struct request_sock_queue *queue) |
105 | { | |
106 | /* make all the listen_opt local to us */ | |
107 | struct listen_sock *lopt = reqsk_queue_yank_listen_sk(queue); | |
72a3effa ED |
108 | size_t lopt_size = sizeof(struct listen_sock) + |
109 | lopt->nr_table_entries * sizeof(struct request_sock *); | |
83e3609e ACM |
110 | |
111 | if (lopt->qlen != 0) { | |
72a3effa | 112 | unsigned int i; |
83e3609e ACM |
113 | |
114 | for (i = 0; i < lopt->nr_table_entries; i++) { | |
115 | struct request_sock *req; | |
116 | ||
117 | while ((req = lopt->syn_table[i]) != NULL) { | |
118 | lopt->syn_table[i] = req->dl_next; | |
119 | lopt->qlen--; | |
120 | reqsk_free(req); | |
121 | } | |
122 | } | |
123 | } | |
124 | ||
547b792c | 125 | WARN_ON(lopt->qlen != 0); |
72a3effa ED |
126 | if (lopt_size > PAGE_SIZE) |
127 | vfree(lopt); | |
128 | else | |
129 | kfree(lopt); | |
83e3609e ACM |
130 | } |
131 |