]>
Commit | Line | Data |
---|---|---|
39236c6e A |
1 | /* |
2 | * Copyright (c) 2012-2013 Apple Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * This file contains Original Code and/or Modifications of Original Code | |
7 | * as defined in and that are subject to the Apple Public Source License | |
8 | * Version 2.0 (the 'License'). You may not use this file except in | |
9 | * compliance with the License. The rights granted to you under the License | |
10 | * may not be used to create, or enable the creation or redistribution of, | |
11 | * unlawful or unlicensed copies of an Apple operating system, or to | |
12 | * circumvent, violate, or enable the circumvention or violation of, any | |
13 | * terms of an Apple operating system software license agreement. | |
14 | * | |
15 | * Please obtain a copy of the License at | |
16 | * http://www.opensource.apple.com/apsl/ and read it before using this file. | |
17 | * | |
18 | * The Original Code and all software distributed under the License are | |
19 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
20 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
21 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
22 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
23 | * Please see the License for the specific language governing rights and | |
24 | * limitations under the License. | |
25 | * | |
26 | * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ | |
27 | */ | |
28 | ||
29 | #include <sys/param.h> | |
30 | #include <sys/systm.h> | |
31 | #include <sys/kernel.h> | |
32 | #include <sys/mbuf.h> | |
33 | #include <sys/mcache.h> | |
34 | #include <sys/syslog.h> | |
35 | #include <sys/socket.h> | |
36 | #include <sys/socketvar.h> | |
37 | #include <sys/protosw.h> | |
38 | #include <sys/proc_internal.h> | |
39 | ||
40 | #include <mach/boolean.h> | |
41 | #include <kern/zalloc.h> | |
42 | #include <kern/locks.h> | |
43 | ||
44 | #include <netinet/mp_pcb.h> | |
45 | #include <netinet/mptcp_var.h> | |
46 | ||
47 | static lck_grp_t *mp_lock_grp; | |
48 | static lck_attr_t *mp_lock_attr; | |
49 | static lck_grp_attr_t *mp_lock_grp_attr; | |
50 | decl_lck_mtx_data(static, mp_lock); /* global MULTIPATH lock */ | |
51 | decl_lck_mtx_data(static, mp_timeout_lock); | |
52 | ||
53 | static TAILQ_HEAD(, mppcbinfo) mppi_head = TAILQ_HEAD_INITIALIZER(mppi_head); | |
54 | ||
55 | static boolean_t mp_timeout_run; /* MP timer is scheduled to run */ | |
56 | static boolean_t mp_garbage_collecting; | |
57 | static boolean_t mp_ticking; | |
58 | static void mp_sched_timeout(void); | |
59 | static void mp_timeout(void *); | |
60 | ||
61 | void | |
62 | mp_pcbinit(void) | |
63 | { | |
64 | static int mp_initialized = 0; | |
65 | ||
66 | VERIFY(!mp_initialized); | |
67 | mp_initialized = 1; | |
68 | ||
69 | mp_lock_grp_attr = lck_grp_attr_alloc_init(); | |
70 | mp_lock_grp = lck_grp_alloc_init("multipath", mp_lock_grp_attr); | |
71 | mp_lock_attr = lck_attr_alloc_init(); | |
72 | lck_mtx_init(&mp_lock, mp_lock_grp, mp_lock_attr); | |
73 | lck_mtx_init(&mp_timeout_lock, mp_lock_grp, mp_lock_attr); | |
74 | } | |
75 | ||
76 | static void | |
77 | mp_timeout(void *arg) | |
78 | { | |
79 | #pragma unused(arg) | |
80 | struct mppcbinfo *mppi; | |
81 | boolean_t t, gc; | |
82 | uint32_t t_act = 0; | |
83 | uint32_t gc_act = 0; | |
84 | ||
85 | /* | |
86 | * Update coarse-grained networking timestamp (in sec.); the idea | |
87 | * is to piggy-back on the timeout callout to update the counter | |
88 | * returnable via net_uptime(). | |
89 | */ | |
90 | net_update_uptime(); | |
91 | ||
92 | lck_mtx_lock_spin(&mp_timeout_lock); | |
93 | gc = mp_garbage_collecting; | |
94 | mp_garbage_collecting = FALSE; | |
95 | ||
96 | t = mp_ticking; | |
97 | mp_ticking = FALSE; | |
98 | ||
99 | if (gc || t) { | |
100 | lck_mtx_unlock(&mp_timeout_lock); | |
101 | ||
102 | lck_mtx_lock(&mp_lock); | |
103 | TAILQ_FOREACH(mppi, &mppi_head, mppi_entry) { | |
104 | if ((gc && mppi->mppi_gc != NULL) || | |
105 | (t && mppi->mppi_timer != NULL)) { | |
106 | lck_mtx_lock(&mppi->mppi_lock); | |
107 | if (gc && mppi->mppi_gc != NULL) | |
108 | gc_act += mppi->mppi_gc(mppi); | |
109 | if (t && mppi->mppi_timer != NULL) | |
110 | t_act += mppi->mppi_timer(mppi); | |
111 | lck_mtx_unlock(&mppi->mppi_lock); | |
112 | } | |
113 | } | |
114 | lck_mtx_unlock(&mp_lock); | |
115 | ||
116 | lck_mtx_lock_spin(&mp_timeout_lock); | |
117 | } | |
118 | ||
119 | /* lock was dropped above, so check first before overriding */ | |
120 | if (!mp_garbage_collecting) | |
121 | mp_garbage_collecting = (gc_act != 0); | |
122 | if (!mp_ticking) | |
123 | mp_ticking = (t_act != 0); | |
124 | ||
125 | /* re-arm the timer if there's work to do */ | |
126 | mp_timeout_run = FALSE; | |
127 | mp_sched_timeout(); | |
128 | lck_mtx_unlock(&mp_timeout_lock); | |
129 | } | |
130 | ||
131 | static void | |
132 | mp_sched_timeout(void) | |
133 | { | |
134 | lck_mtx_assert(&mp_timeout_lock, LCK_MTX_ASSERT_OWNED); | |
135 | ||
136 | if (!mp_timeout_run && (mp_garbage_collecting || mp_ticking)) { | |
137 | lck_mtx_convert_spin(&mp_timeout_lock); | |
138 | mp_timeout_run = TRUE; | |
139 | timeout(mp_timeout, NULL, hz); | |
140 | } | |
141 | } | |
142 | ||
143 | void | |
144 | mp_gc_sched(void) | |
145 | { | |
146 | lck_mtx_lock_spin(&mp_timeout_lock); | |
147 | mp_garbage_collecting = TRUE; | |
148 | mp_sched_timeout(); | |
149 | lck_mtx_unlock(&mp_timeout_lock); | |
150 | } | |
151 | ||
152 | void | |
153 | mptcp_timer_sched(void) | |
154 | { | |
155 | lck_mtx_lock_spin(&mp_timeout_lock); | |
156 | mp_ticking = TRUE; | |
157 | mp_sched_timeout(); | |
158 | lck_mtx_unlock(&mp_timeout_lock); | |
159 | } | |
160 | ||
161 | void | |
162 | mp_pcbinfo_attach(struct mppcbinfo *mppi) | |
163 | { | |
164 | struct mppcbinfo *mppi0; | |
165 | ||
166 | lck_mtx_lock(&mp_lock); | |
167 | TAILQ_FOREACH(mppi0, &mppi_head, mppi_entry) { | |
168 | if (mppi0 == mppi) { | |
169 | panic("%s: mppi %p already in the list\n", | |
170 | __func__, mppi); | |
171 | /* NOTREACHED */ | |
172 | } | |
173 | } | |
174 | TAILQ_INSERT_TAIL(&mppi_head, mppi, mppi_entry); | |
175 | lck_mtx_unlock(&mp_lock); | |
176 | } | |
177 | ||
178 | int | |
179 | mp_pcbinfo_detach(struct mppcbinfo *mppi) | |
180 | { | |
181 | struct mppcbinfo *mppi0; | |
182 | int error = 0; | |
183 | ||
184 | lck_mtx_lock(&mp_lock); | |
185 | TAILQ_FOREACH(mppi0, &mppi_head, mppi_entry) { | |
186 | if (mppi0 == mppi) | |
187 | break; | |
188 | } | |
189 | if (mppi0 != NULL) | |
190 | TAILQ_REMOVE(&mppi_head, mppi0, mppi_entry); | |
191 | else | |
192 | error = ENXIO; | |
193 | lck_mtx_unlock(&mp_lock); | |
194 | ||
195 | return (error); | |
196 | } | |
197 | ||
198 | int | |
199 | mp_pcballoc(struct socket *so, struct mppcbinfo *mppi) | |
200 | { | |
201 | struct mppcb *mpp; | |
202 | ||
203 | VERIFY(sotomppcb(so) == NULL); | |
204 | ||
205 | lck_mtx_lock(&mppi->mppi_lock); | |
206 | if (mppi->mppi_count >= mptcp_socket_limit) { | |
207 | lck_mtx_unlock(&mppi->mppi_lock); | |
208 | mptcplog((LOG_ERR, "Reached MPTCP socket limit.")); | |
209 | return (ENOBUFS); | |
210 | } | |
211 | lck_mtx_unlock(&mppi->mppi_lock); | |
212 | ||
213 | mpp = zalloc(mppi->mppi_zone); | |
214 | if (mpp == NULL) | |
215 | return (ENOBUFS); | |
216 | ||
217 | bzero(mpp, mppi->mppi_size); | |
218 | lck_mtx_init(&mpp->mpp_lock, mppi->mppi_lock_grp, mppi->mppi_lock_attr); | |
219 | mpp->mpp_pcbinfo = mppi; | |
220 | mpp->mpp_state = MPPCB_STATE_INUSE; | |
221 | mpp->mpp_socket = so; | |
222 | so->so_pcb = mpp; | |
223 | ||
224 | lck_mtx_lock(&mppi->mppi_lock); | |
225 | mpp->mpp_flags |= MPP_ATTACHED; | |
226 | TAILQ_INSERT_TAIL(&mppi->mppi_pcbs, mpp, mpp_entry); | |
227 | mppi->mppi_count++; | |
228 | lck_mtx_unlock(&mppi->mppi_lock); | |
229 | ||
230 | return (0); | |
231 | } | |
232 | ||
233 | void | |
234 | mp_pcbdetach(struct mppcb *mpp) | |
235 | { | |
236 | struct socket *so = mpp->mpp_socket; | |
237 | ||
238 | VERIFY(so->so_pcb == mpp); | |
239 | ||
240 | mpp->mpp_state = MPPCB_STATE_DEAD; | |
241 | if (!(so->so_flags & SOF_PCBCLEARING)) | |
242 | so->so_flags |= SOF_PCBCLEARING; | |
243 | ||
244 | mp_gc_sched(); | |
245 | } | |
246 | ||
247 | void | |
248 | mp_pcbdispose(struct mppcb *mpp) | |
249 | { | |
250 | struct mppcbinfo *mppi = mpp->mpp_pcbinfo; | |
251 | ||
252 | VERIFY(mppi != NULL); | |
253 | ||
254 | lck_mtx_assert(&mppi->mppi_lock, LCK_MTX_ASSERT_OWNED); | |
255 | lck_mtx_assert(&mpp->mpp_lock, LCK_MTX_ASSERT_OWNED); | |
256 | ||
257 | VERIFY(mpp->mpp_state == MPPCB_STATE_DEAD); | |
258 | ||
259 | VERIFY(mpp->mpp_flags & MPP_ATTACHED); | |
260 | mpp->mpp_flags &= ~MPP_ATTACHED; | |
261 | TAILQ_REMOVE(&mppi->mppi_pcbs, mpp, mpp_entry); | |
262 | VERIFY(mppi->mppi_count != 0); | |
263 | mppi->mppi_count--; | |
264 | ||
265 | VERIFY(mpp->mpp_socket != NULL); | |
266 | VERIFY(mpp->mpp_socket->so_usecount == 0); | |
267 | mpp->mpp_socket->so_pcb = NULL; | |
268 | mpp->mpp_socket = NULL; | |
269 | ||
270 | lck_mtx_unlock(&mpp->mpp_lock); | |
271 | lck_mtx_destroy(&mpp->mpp_lock, mppi->mppi_lock_grp); | |
272 | zfree(mppi->mppi_zone, mpp); | |
273 | } |