1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
|
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
*/
#include <sys/types.h>
#include <inet/common.h>
#include "sctp_impl.h"
/* Control whether SCTP can enter defensive mode when under memory pressure. */
static boolean_t sctp_do_reclaim = B_TRUE;
static void sctp_reclaim_timer(void *);
/* Diagnostic routine used to return a string associated with the sctp state. */
char *
sctp_display(sctp_t *sctp, char *sup_buf)
{
char *buf;
char buf1[30];
static char priv_buf[INET6_ADDRSTRLEN * 2 + 80];
char *cp;
conn_t *connp;
if (sctp == NULL)
return ("NULL_SCTP");
connp = sctp->sctp_connp;
buf = (sup_buf != NULL) ? sup_buf : priv_buf;
switch (sctp->sctp_state) {
case SCTPS_IDLE:
cp = "SCTP_IDLE";
break;
case SCTPS_BOUND:
cp = "SCTP_BOUND";
break;
case SCTPS_LISTEN:
cp = "SCTP_LISTEN";
break;
case SCTPS_COOKIE_WAIT:
cp = "SCTP_COOKIE_WAIT";
break;
case SCTPS_COOKIE_ECHOED:
cp = "SCTP_COOKIE_ECHOED";
break;
case SCTPS_ESTABLISHED:
cp = "SCTP_ESTABLISHED";
break;
case SCTPS_SHUTDOWN_PENDING:
cp = "SCTP_SHUTDOWN_PENDING";
break;
case SCTPS_SHUTDOWN_SENT:
cp = "SCTPS_SHUTDOWN_SENT";
break;
case SCTPS_SHUTDOWN_RECEIVED:
cp = "SCTPS_SHUTDOWN_RECEIVED";
break;
case SCTPS_SHUTDOWN_ACK_SENT:
cp = "SCTPS_SHUTDOWN_ACK_SENT";
break;
default:
(void) mi_sprintf(buf1, "SCTPUnkState(%d)", sctp->sctp_state);
cp = buf1;
break;
}
(void) mi_sprintf(buf, "[%u, %u] %s",
ntohs(connp->conn_lport), ntohs(connp->conn_fport), cp);
return (buf);
}
void
sctp_display_all(sctp_stack_t *sctps)
{
sctp_t *sctp_walker;
mutex_enter(&sctps->sctps_g_lock);
for (sctp_walker = list_head(&sctps->sctps_g_list);
sctp_walker != NULL;
sctp_walker = (sctp_t *)list_next(&sctps->sctps_g_list,
sctp_walker)) {
(void) sctp_display(sctp_walker, NULL);
}
mutex_exit(&sctps->sctps_g_lock);
}
/*
* Given a sctp_stack_t and a port (in host byte order), find a listener
* configuration for that port and return the ratio.
*/
uint32_t
sctp_find_listener_conf(sctp_stack_t *sctps, in_port_t port)
{
sctp_listener_t *sl;
uint32_t ratio = 0;
mutex_enter(&sctps->sctps_listener_conf_lock);
for (sl = list_head(&sctps->sctps_listener_conf); sl != NULL;
sl = list_next(&sctps->sctps_listener_conf, sl)) {
if (sl->sl_port == port) {
ratio = sl->sl_ratio;
break;
}
}
mutex_exit(&sctps->sctps_listener_conf_lock);
return (ratio);
}
/*
* To remove all listener limit configuration in a sctp_stack_t.
*/
void
sctp_listener_conf_cleanup(sctp_stack_t *sctps)
{
sctp_listener_t *sl;
mutex_enter(&sctps->sctps_listener_conf_lock);
while ((sl = list_head(&sctps->sctps_listener_conf)) != NULL) {
list_remove(&sctps->sctps_listener_conf, sl);
kmem_free(sl, sizeof (sctp_listener_t));
}
mutex_destroy(&sctps->sctps_listener_conf_lock);
list_destroy(&sctps->sctps_listener_conf);
}
/*
* Timeout function to reset the SCTP stack variable sctps_reclaim to false.
*/
static void
sctp_reclaim_timer(void *arg)
{
sctp_stack_t *sctps = (sctp_stack_t *)arg;
int64_t tot_assoc = 0;
int i;
extern pgcnt_t lotsfree, needfree;
for (i = 0; i < sctps->sctps_sc_cnt; i++)
tot_assoc += sctps->sctps_sc[i]->sctp_sc_assoc_cnt;
/*
* This happens only when a stack is going away. sctps_reclaim_tid
* should not be reset to 0 when returning in this case.
*/
mutex_enter(&sctps->sctps_reclaim_lock);
if (!sctps->sctps_reclaim) {
mutex_exit(&sctps->sctps_reclaim_lock);
return;
}
if ((freemem >= lotsfree + needfree) || tot_assoc < maxusers) {
sctps->sctps_reclaim = B_FALSE;
sctps->sctps_reclaim_tid = 0;
} else {
/* Stay in defensive mode and restart the timer */
sctps->sctps_reclaim_tid = timeout(sctp_reclaim_timer,
sctps, MSEC_TO_TICK(sctps->sctps_reclaim_period));
}
mutex_exit(&sctps->sctps_reclaim_lock);
}
/*
* Kmem reclaim call back function. When the system is under memory
* pressure, we set the SCTP stack variable sctps_reclaim to true. This
* variable is reset to false after sctps_reclaim_period msecs. During this
* period, SCTP will be more aggressive in aborting connections not making
* progress, meaning retransmitting for shorter time (sctp_pa_early_abort/
* sctp_pp_early_abort number of strikes).
*/
/* ARGSUSED */
void
sctp_conn_reclaim(void *arg)
{
netstack_handle_t nh;
netstack_t *ns;
sctp_stack_t *sctps;
extern pgcnt_t lotsfree, needfree;
if (!sctp_do_reclaim)
return;
/*
* The reclaim function may be called even when the system is not
* really under memory pressure.
*/
if (freemem >= lotsfree + needfree)
return;
netstack_next_init(&nh);
while ((ns = netstack_next(&nh)) != NULL) {
int i;
int64_t tot_assoc = 0;
/*
* During boot time, the first netstack_t is created and
* initialized before SCTP has registered with the netstack
* framework. If this reclaim function is called before SCTP
* has finished its initialization, netstack_next() will
* return the first netstack_t (since its netstack_flags is
* not NSF_UNINIT). And its netstack_sctp will be NULL. We
* need to catch it.
*
* All subsequent netstack_t creation will not have this
* problem since the initialization is not finished until SCTP
* has finished its own sctp_stack_t initialization. Hence
* netstack_next() will not return one with NULL netstack_sctp.
*/
if ((sctps = ns->netstack_sctp) == NULL) {
netstack_rele(ns);
continue;
}
/*
* Even if the system is under memory pressure, the reason may
* not be because of SCTP activity. Check the number of
* associations in each stack. If the number exceeds the
* threshold (maxusers), turn on defensive mode.
*/
for (i = 0; i < sctps->sctps_sc_cnt; i++)
tot_assoc += sctps->sctps_sc[i]->sctp_sc_assoc_cnt;
if (tot_assoc < maxusers) {
netstack_rele(ns);
continue;
}
mutex_enter(&sctps->sctps_reclaim_lock);
if (!sctps->sctps_reclaim) {
sctps->sctps_reclaim = B_TRUE;
sctps->sctps_reclaim_tid = timeout(sctp_reclaim_timer,
sctps, MSEC_TO_TICK(sctps->sctps_reclaim_period));
SCTP_KSTAT(sctps, sctp_reclaim_cnt);
}
mutex_exit(&sctps->sctps_reclaim_lock);
netstack_rele(ns);
}
netstack_next_fini(&nh);
}
/*
* When a CPU is added, we need to allocate the per CPU stats struct.
*/
void
sctp_stack_cpu_add(sctp_stack_t *sctps, processorid_t cpu_seqid)
{
int i;
if (cpu_seqid < sctps->sctps_sc_cnt)
return;
for (i = sctps->sctps_sc_cnt; i <= cpu_seqid; i++) {
ASSERT(sctps->sctps_sc[i] == NULL);
sctps->sctps_sc[i] = kmem_zalloc(sizeof (sctp_stats_cpu_t),
KM_SLEEP);
}
membar_producer();
sctps->sctps_sc_cnt = cpu_seqid + 1;
}
|