]> git.saurik.com Git - apple/xnu.git/blame - bsd/net/kpi_protocol.c
xnu-792.10.96.tar.gz
[apple/xnu.git] / bsd / net / kpi_protocol.c
CommitLineData
91447636
A
1/*
2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
37839358
A
6 * The contents of this file constitute Original Code as defined in and
7 * are subject to the Apple Public Source License Version 1.1 (the
8 * "License"). You may not use this file except in compliance with the
9 * License. Please obtain a copy of the License at
10 * http://www.apple.com/publicsource and read it before using this file.
91447636 11 *
37839358
A
12 * This Original Code and all software distributed under the License are
13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
91447636
A
14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
37839358
A
16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
17 * License for the specific language governing rights and limitations
18 * under the License.
91447636
A
19 *
20 * @APPLE_LICENSE_HEADER_END@
21 */
22
23#include "kpi_protocol.h"
24
25#include <sys/param.h>
26#include <sys/malloc.h>
27#include <sys/socket.h>
28#include <sys/systm.h>
29#include <sys/kpi_mbuf.h>
30#include <sys/domain.h>
31#include <net/if.h>
32#include <net/dlil.h>
33#include <libkern/OSAtomic.h>
34
35void proto_kpi_init(void);
36void proto_input_run(void);
37
38typedef int (*attach_t)(struct ifnet *ifp, u_long protocol_family);
39typedef int (*detach_t)(struct ifnet *ifp, u_long protocol_family);
40
41/****************************************************************************/
42/* WARNING: Big assumption made here - there can be only one input thread */
43struct proto_input_entry {
44 struct proto_input_entry *next;
45 int detach;
46 struct domain *domain;
47
48 protocol_family_t protocol;
49 proto_input_handler input;
50 proto_input_detached_handler detached;
51
52 mbuf_t first_packet;
53 mbuf_t last_packet;
54};
55
56#define PROTO_HASH_SLOTS 5
57
58static struct proto_input_entry *proto_hash[PROTO_HASH_SLOTS];
59static struct proto_input_entry *proto_input_add_list;
60static lck_mtx_t *proto_input_lock = 0;
b36670ce 61__private_extern__ u_int32_t inject_buckets = 0;
91447636
A
62
63extern thread_t dlil_input_thread_ptr;
64extern int dlil_input_thread_wakeup;
65
66static int
67proto_hash_value(
68 protocol_family_t protocol)
69{
70 switch(protocol) {
71 case PF_INET:
72 return 0;
73 case PF_INET6:
74 return 1;
75 case PF_APPLETALK:
76 return 2;
77 case PF_VLAN:
78 return 3;
79 }
80 return 4;
81}
82
83__private_extern__ void
84proto_kpi_init(void)
85{
86 lck_grp_attr_t *grp_attrib = 0;
87 lck_attr_t *lck_attrib = 0;
88 lck_grp_t *lck_group = 0;
89
90 /* Allocate a mtx lock */
91 grp_attrib = lck_grp_attr_alloc_init();
91447636
A
92 lck_group = lck_grp_alloc_init("protocol kpi", grp_attrib);
93 lck_grp_attr_free(grp_attrib);
94 lck_attrib = lck_attr_alloc_init();
91447636
A
95 proto_input_lock = lck_mtx_alloc_init(lck_group, lck_attrib);
96 lck_grp_free(lck_group);
97 lck_attr_free(lck_attrib);
98}
99
100__private_extern__ errno_t
101proto_register_input(
102 protocol_family_t protocol,
103 proto_input_handler input,
104 proto_input_detached_handler detached)
105{
106
107 struct proto_input_entry *entry;
108
109 entry = _MALLOC(sizeof(*entry), M_IFADDR, M_WAITOK);
110
111 if (entry == NULL)
112 return ENOMEM;
113
114 bzero(entry, sizeof(*entry));
115 entry->protocol = protocol;
116 entry->input = input;
117 entry->detached = detached;
118
119 {
120 struct domain *dp = domains;
121 extern lck_mtx_t *domain_proto_mtx;
122
123 lck_mtx_assert(domain_proto_mtx, LCK_MTX_ASSERT_NOTOWNED);
124 lck_mtx_lock(domain_proto_mtx);
125 while (dp && dp->dom_family != protocol)
126 dp = dp->dom_next;
127 entry->domain = dp;
128 lck_mtx_unlock(domain_proto_mtx);
129 }
130
131
132 do {
133 entry->next = proto_input_add_list;
134 } while(!OSCompareAndSwap((UInt32)entry->next, (UInt32)entry, (UInt32*)&proto_input_add_list));
135
136 wakeup((caddr_t)&dlil_input_thread_wakeup);
137
138 return 0;
139}
140
141
142__private_extern__ void
143proto_unregister_input(
144 protocol_family_t protocol)
145{
146 struct proto_input_entry *entry = NULL;
147
148 for (entry = proto_hash[proto_hash_value(protocol)]; entry; entry = entry->next)
149 if (entry->protocol == protocol)
150 break;
151
152 if (entry)
153 entry->detach = 1;
154}
155
156
157static void
158proto_delayed_attach(
159 struct proto_input_entry *entry)
160{
161 struct proto_input_entry *next_entry;
162 for (next_entry = entry->next; entry; entry = next_entry) {
163 struct proto_input_entry *exist;
164 int hash_slot;
165
166 hash_slot = proto_hash_value(entry->protocol);
167 next_entry = entry->next;
168
169 for (exist = proto_hash[hash_slot]; exist; exist = exist->next)
170 if (exist->protocol == entry->protocol)
171 break;
172
173 /* If the entry already exists, call detached and dispose */
174 if (exist) {
175 if (entry->detached)
176 entry->detached(entry->protocol);
177 FREE(entry, M_IFADDR);
178 }
179 else {
180 entry->next = proto_hash[hash_slot];
181 proto_hash[hash_slot] = entry;
182 }
183 }
184}
185
186static void
187proto_delayed_inject(
188 struct proto_input_entry *entry)
189{
190 mbuf_t packet_list;
191 mbuf_t packet;
192 int locked = 0;
193
194 lck_mtx_lock(proto_input_lock);
195 packet_list = entry->first_packet;
196 entry->first_packet = entry->last_packet = 0;
197 lck_mtx_unlock(proto_input_lock);
198
199 if (packet_list == NULL)
200 return;
201
202 if (entry->domain && (entry->domain->dom_flags & DOM_REENTRANT) == 0) {
203 lck_mtx_lock(entry->domain->dom_mtx);
204 locked = 1;
205 }
206
207 for (packet = packet_list; packet; packet = packet_list) {
208 packet_list = mbuf_nextpkt(packet);
209 mbuf_setnextpkt(packet, NULL);
210 entry->input(entry->protocol, packet);
211 }
212
213 if (locked) {
214 lck_mtx_unlock(entry->domain->dom_mtx);
215 }
216}
217
218/* This function must be called from a single dlil input thread */
219__private_extern__ void
220proto_input_run(void)
221{
222 struct proto_input_entry *entry;
223 u_int32_t inject;
224 int i;
225
226 if (current_thread() != dlil_input_thread_ptr)
227 panic("proto_input_run called from a thread other than dlil_input_thread!\n");
228
229 do {
230 entry = proto_input_add_list;
231 } while (entry && !OSCompareAndSwap((UInt32)entry, 0, (UInt32*)&proto_input_add_list));
232
233 if (entry)
234 proto_delayed_attach(entry);
235
236 do {
237 inject = inject_buckets;
238 } while (inject && !OSCompareAndSwap(inject, 0, (UInt32*)&inject_buckets));
239
240 if (inject) {
241 for (i = 0; i < PROTO_HASH_SLOTS; i++) {
242 if ((inject & (1L << i)) != 0) {
243 for (entry = proto_hash[i]; entry; entry = entry->next) {
244 if (entry->first_packet) {
245 proto_delayed_inject(entry);
246 }
247 }
248 }
249 }
250 }
251}
252
253errno_t
254proto_input(
255 protocol_family_t protocol,
256 mbuf_t packet_list)
257{
258 struct proto_input_entry *entry;
259
260 if (current_thread() != dlil_input_thread_ptr)
261 panic("proto_input called from a thread other than dlil_input_thread!\n");
262
263 for (entry = proto_hash[proto_hash_value(protocol)]; entry; entry = entry->next) {
264 if (entry->protocol == protocol)
265 break;
266 }
267
268 if (entry) {
269 mbuf_t packet;
270#if DIRECT_PROTO_INPUT
271 // See <rdar://problem/3687868> for why this is disabled
272 // We need to release the dlil lock before taking the protocol lock
273 for (packet = packet_list; packet; packet = packet_list) {
274 packet_list = mbuf_nextpkt(packet);
275 mbuf_setnextpkt(packet, NULL);
276 entry->input(entry->protocol, packet);
277 }
278#else
279 mbuf_t last_packet;
280 int hash_slot = proto_hash_value(protocol);
281
282 for (last_packet = packet_list; mbuf_nextpkt(last_packet);
283 last_packet = mbuf_nextpkt(last_packet))
284 /* find the last packet */;
285
286 lck_mtx_lock(proto_input_lock);
287 if (entry->first_packet == NULL) {
288 entry->first_packet = packet_list;
289 }
290 else {
291 mbuf_setnextpkt(entry->last_packet, packet_list);
292 }
293 entry->last_packet = last_packet;
294 lck_mtx_unlock(proto_input_lock);
295
296 OSBitOrAtomic((1L << hash_slot), (UInt32*)&inject_buckets);
297#endif
298 }
299 else
300 {
301 return ENOENT;
302 }
303
304 return 0;
305}
306
307errno_t
308proto_inject(
309 protocol_family_t protocol,
310 mbuf_t packet_list)
311{
312 struct proto_input_entry *entry;
313 mbuf_t last_packet;
314 int hash_slot = proto_hash_value(protocol);
315
316 for (last_packet = packet_list; mbuf_nextpkt(last_packet);
317 last_packet = mbuf_nextpkt(last_packet))
318 /* find the last packet */;
319
320 for (entry = proto_hash[hash_slot]; entry; entry = entry->next) {
321 if (entry->protocol == protocol)
322 break;
323 }
324
325 if (entry) {
326 lck_mtx_lock(proto_input_lock);
327 if (entry->first_packet == NULL) {
328 entry->first_packet = packet_list;
329 }
330 else {
331 mbuf_setnextpkt(entry->last_packet, packet_list);
332 }
333 entry->last_packet = last_packet;
334 lck_mtx_unlock(proto_input_lock);
335
336 OSBitOrAtomic((1L << hash_slot), (UInt32*)&inject_buckets);
337
338 wakeup((caddr_t)&dlil_input_thread_wakeup);
339 }
340 else
341 {
342 return ENOENT;
343 }
344
345 return 0;
346}
347
348errno_t
349proto_register_plumber(
350 protocol_family_t proto_fam,
351 ifnet_family_t if_fam,
352 proto_plumb_handler plumb,
353 proto_unplumb_handler unplumb)
354{
355 return dlil_reg_proto_module(proto_fam, if_fam, (attach_t)plumb, (detach_t)unplumb);
356}
357
358void
359proto_unregister_plumber(
360 protocol_family_t proto_fam,
361 ifnet_family_t if_fam)
362{
363 (void)dlil_dereg_proto_module(proto_fam, if_fam);
364}