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 | * | |
99b53bdd | 29 | * The minimum value of it is 128. Experiments with real servers show that |
e52c1f17 | 30 | * it is absolutely not enough even at 100conn/sec. 256 cures most |
99b53bdd PP |
31 | * of problems. |
32 | * This value is adjusted to 128 for low memory machines, | |
33 | * and it will increase in proportion to the memory of machine. | |
72a3effa | 34 | * Note : Dont forget somaxconn that may limit backlog too. |
e52c1f17 DM |
35 | */ |
36 | int sysctl_max_syn_backlog = 256; | |
493f377d | 37 | EXPORT_SYMBOL(sysctl_max_syn_backlog); |
e52c1f17 | 38 | |
0e87506f | 39 | int reqsk_queue_alloc(struct request_sock_queue *queue, |
72a3effa | 40 | unsigned int nr_table_entries) |
0e87506f | 41 | { |
72a3effa ED |
42 | size_t lopt_size = sizeof(struct listen_sock); |
43 | struct listen_sock *lopt; | |
44 | ||
45 | nr_table_entries = min_t(u32, nr_table_entries, sysctl_max_syn_backlog); | |
46 | nr_table_entries = max_t(u32, nr_table_entries, 8); | |
47 | nr_table_entries = roundup_pow_of_two(nr_table_entries + 1); | |
48 | lopt_size += nr_table_entries * sizeof(struct request_sock *); | |
49 | if (lopt_size > PAGE_SIZE) | |
7a1c8e5a | 50 | lopt = vzalloc(lopt_size); |
72a3effa ED |
51 | else |
52 | lopt = kzalloc(lopt_size, GFP_KERNEL); | |
0e87506f ACM |
53 | if (lopt == NULL) |
54 | return -ENOMEM; | |
55 | ||
72a3effa ED |
56 | for (lopt->max_qlen_log = 3; |
57 | (1 << lopt->max_qlen_log) < nr_table_entries; | |
0e87506f ACM |
58 | lopt->max_qlen_log++); |
59 | ||
60 | get_random_bytes(&lopt->hash_rnd, sizeof(lopt->hash_rnd)); | |
61 | rwlock_init(&queue->syn_wait_lock); | |
3eb4801d | 62 | queue->rskq_accept_head = NULL; |
83e3609e | 63 | lopt->nr_table_entries = nr_table_entries; |
0e87506f ACM |
64 | |
65 | write_lock_bh(&queue->syn_wait_lock); | |
66 | queue->listen_opt = lopt; | |
67 | write_unlock_bh(&queue->syn_wait_lock); | |
68 | ||
69 | return 0; | |
70 | } | |
71 | ||
dab6ba36 PE |
72 | void __reqsk_queue_destroy(struct request_sock_queue *queue) |
73 | { | |
74 | struct listen_sock *lopt; | |
75 | size_t lopt_size; | |
76 | ||
77 | /* | |
78 | * this is an error recovery path only | |
79 | * no locking needed and the lopt is not NULL | |
80 | */ | |
81 | ||
82 | lopt = queue->listen_opt; | |
83 | lopt_size = sizeof(struct listen_sock) + | |
84 | lopt->nr_table_entries * sizeof(struct request_sock *); | |
85 | ||
86 | if (lopt_size > PAGE_SIZE) | |
87 | vfree(lopt); | |
88 | else | |
89 | kfree(lopt); | |
90 | } | |
91 | ||
dab6ba36 PE |
92 | static inline struct listen_sock *reqsk_queue_yank_listen_sk( |
93 | struct request_sock_queue *queue) | |
94 | { | |
95 | struct listen_sock *lopt; | |
96 | ||
97 | write_lock_bh(&queue->syn_wait_lock); | |
98 | lopt = queue->listen_opt; | |
99 | queue->listen_opt = NULL; | |
100 | write_unlock_bh(&queue->syn_wait_lock); | |
101 | ||
102 | return lopt; | |
103 | } | |
104 | ||
83e3609e ACM |
105 | void reqsk_queue_destroy(struct request_sock_queue *queue) |
106 | { | |
107 | /* make all the listen_opt local to us */ | |
108 | struct listen_sock *lopt = reqsk_queue_yank_listen_sk(queue); | |
72a3effa ED |
109 | size_t lopt_size = sizeof(struct listen_sock) + |
110 | lopt->nr_table_entries * sizeof(struct request_sock *); | |
83e3609e ACM |
111 | |
112 | if (lopt->qlen != 0) { | |
72a3effa | 113 | unsigned int i; |
83e3609e ACM |
114 | |
115 | for (i = 0; i < lopt->nr_table_entries; i++) { | |
116 | struct request_sock *req; | |
117 | ||
118 | while ((req = lopt->syn_table[i]) != NULL) { | |
119 | lopt->syn_table[i] = req->dl_next; | |
120 | lopt->qlen--; | |
121 | reqsk_free(req); | |
122 | } | |
123 | } | |
124 | } | |
125 | ||
547b792c | 126 | WARN_ON(lopt->qlen != 0); |
72a3effa ED |
127 | if (lopt_size > PAGE_SIZE) |
128 | vfree(lopt); | |
129 | else | |
130 | kfree(lopt); | |
83e3609e ACM |
131 | } |
132 |