CPU localize dummynet(4) step 1/2
[dragonfly.git] / sys / net / dummynet / ip_dummynet_glue.c
1 /*
2  * Copyright (c) 2007 The DragonFly Project.  All rights reserved.
3  * 
4  * This code is derived from software contributed to The DragonFly Project
5  * by Sepherosa Ziehau <sepherosa@gmail.com>
6  * 
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  * 
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  * 
34  * $DragonFly: src/sys/net/dummynet/ip_dummynet_glue.c,v 1.1 2007/11/16 02:45:45 sephe Exp $
35  */
36
37 #include <sys/param.h>
38 #include <sys/mbuf.h>
39 #include <sys/msgport.h>
40
41 #include <net/if.h>
42 #include <net/if_var.h>
43 #include <net/route.h>
44 #include <net/ethernet.h>
45 #include <net/netisr.h>
46 #include <net/netmsg2.h>
47
48 #include <netinet/in.h>
49 #include <netinet/in_var.h>
50 #include <netinet/ip.h>
51 #include <netinet/ip_var.h>
52
53 #include <net/dummynet/ip_dummynet.h>
54
55 static void     ip_dn_ether_output(struct netmsg *);
56 static void     ip_dn_ether_demux(struct netmsg *);
57 static void     ip_dn_ip_input(struct netmsg *);
58 static void     ip_dn_ip_output(struct netmsg *);
59
60 static void     ip_dn_freepkt_dispatch(struct netmsg *);
61 static void     ip_dn_dispatch(struct netmsg *);
62
63 static void     ip_dn_freepkt(struct dn_pkt *);
64
65 ip_dn_io_t      *ip_dn_io_ptr;
66 int             ip_dn_cpu = 0;
67
68 void
69 ip_dn_queue(struct mbuf *m)
70 {
71         struct netmsg_packet *nmp;
72         lwkt_port_t port;
73
74         KASSERT(m->m_type != MT_TAG, ("mbuf contains old style tag!\n"));
75
76         nmp = &m->m_hdr.mh_netmsg;
77         netmsg_init(&nmp->nm_netmsg, &netisr_apanic_rport, 0,
78                     ip_dn_dispatch);
79         nmp->nm_packet = m;
80
81         port = cpu_portfn(ip_dn_cpu);
82         lwkt_sendmsg(port, &nmp->nm_netmsg.nm_lmsg);
83 }
84
85 void
86 ip_dn_packet_free(struct dn_pkt *pkt)
87 {
88         struct netmsg_packet *nmp;
89         lwkt_port_t port;
90         struct mbuf *m = pkt->dn_m;
91
92         KASSERT(m->m_type != MT_TAG, ("mbuf contains old style tag!\n"));
93
94         if (pkt->cpuid == mycpuid) {
95                 ip_dn_freepkt(pkt);
96                 return;
97         }
98
99         nmp = &m->m_hdr.mh_netmsg;
100         netmsg_init(&nmp->nm_netmsg, &netisr_apanic_rport, 0,
101                     ip_dn_freepkt_dispatch);
102         nmp->nm_packet = m;
103
104         port = cpu_portfn(pkt->cpuid);
105         lwkt_sendmsg(port, &nmp->nm_netmsg.nm_lmsg);
106 }
107
108 void
109 ip_dn_packet_redispatch(struct dn_pkt *pkt)
110 {
111         static const netisr_fn_t dispatches[DN_TO_MAX] = {
112         [DN_TO_IP_OUT]          = ip_dn_ip_output,
113         [DN_TO_IP_IN]           = ip_dn_ip_input,
114         [DN_TO_ETH_DEMUX]       = ip_dn_ether_demux,
115         [DN_TO_ETH_OUT]         = ip_dn_ether_output
116         };
117
118         struct netmsg_packet *nmp;
119         struct mbuf *m;
120         netisr_fn_t dispatch;
121         lwkt_port_t port;
122         int dir;
123
124         dir = (pkt->dn_flags & DN_FLAGS_DIR_MASK);
125         KASSERT(dir < DN_TO_MAX,
126                 ("unknown dummynet redispatch dir %d\n", dir));
127
128         dispatch = dispatches[dir];
129         KASSERT(dispatch != NULL,
130                 ("unsupported dummynet redispatch dir %d\n", dir));
131
132         m = pkt->dn_m;
133         KASSERT(m->m_type != MT_TAG, ("mbuf contains old style tag!\n"));
134
135         nmp = &m->m_hdr.mh_netmsg;
136         netmsg_init(&nmp->nm_netmsg, &netisr_apanic_rport, 0, dispatch);
137         nmp->nm_packet = m;
138
139         port = cpu_portfn(pkt->cpuid);
140         lwkt_sendmsg(port, &nmp->nm_netmsg.nm_lmsg);
141 }
142
143 static void
144 ip_dn_freepkt(struct dn_pkt *pkt)
145 {
146         struct rtentry *rt = pkt->ro.ro_rt;
147
148         /* Unreference route entry */
149         if (rt != NULL) {
150                 if (rt->rt_refcnt <= 0) {       /* XXX assert? */
151                         kprintf("-- warning, refcnt now %ld, decreasing\n",
152                                 rt->rt_refcnt);
153                 }
154                 RTFREE(rt);
155         }
156
157         /* Unreference packet private data */
158         if (pkt->dn_unref_priv)
159                 pkt->dn_unref_priv(pkt->dn_priv);
160
161         /* Free the parent mbuf, this will free 'pkt' as well */
162         m_freem(pkt->dn_m);
163 }
164
165 static void
166 ip_dn_freepkt_dispatch(struct netmsg *nmsg)
167 {
168         struct netmsg_packet *nmp;
169         struct mbuf *m;
170         struct m_tag *mtag;
171         struct dn_pkt *pkt;
172
173         nmp = (struct netmsg_packet *)nmsg;
174         m = nmp->nm_packet;
175
176         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
177         pkt = m_tag_data(mtag);
178
179         KASSERT(pkt->cpuid == mycpuid,
180                 ("%s: dummynet packet was delivered to wrong cpu! "
181                  "target cpuid %d, mycpuid %d\n", __func__,
182                  pkt->cpuid, mycpuid));
183
184         ip_dn_freepkt(pkt);
185 }
186
187 static void
188 ip_dn_dispatch(struct netmsg *nmsg)
189 {
190         struct netmsg_packet *nmp;
191         struct mbuf *m;
192         struct m_tag *mtag;
193         struct dn_pkt *pkt;
194
195         KASSERT(ip_dn_cpu == mycpuid,
196                 ("%s: dummynet packet was delivered to wrong cpu! "
197                  "dummynet cpuid %d, mycpuid %d\n", __func__,
198                  ip_dn_cpu, mycpuid));
199
200         nmp = (struct netmsg_packet *)nmsg;
201         m = nmp->nm_packet;
202
203         if (DUMMYNET_LOADED) {
204                 if (ip_dn_io_ptr(m) == 0)
205                         return;
206         }
207
208         /*
209          * ip_dn_io_ptr() failed or dummynet(4) is not loaded
210          */
211         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
212         pkt = m_tag_data(mtag);
213         ip_dn_packet_free(pkt);
214 }
215
216 static void
217 ip_dn_ip_output(struct netmsg *nmsg)
218 {
219         struct netmsg_packet *nmp;
220         struct mbuf *m;
221         struct m_tag *mtag;
222         struct dn_pkt *pkt;
223         struct rtentry *rt;
224         ip_dn_unref_priv_t unref_priv;
225         void *priv;
226
227         nmp = (struct netmsg_packet *)nmsg;
228         m = nmp->nm_packet;
229
230         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
231         KKASSERT(mtag != NULL);
232         KKASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED);
233
234         pkt = m_tag_data(mtag);
235         KASSERT(pkt->cpuid == mycpuid,
236                 ("%s: dummynet packet was delivered to wrong cpu! "
237                  "target cpuid %d, mycpuid %d\n", __func__,
238                  pkt->cpuid, mycpuid));
239         KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_IP_OUT,
240                 ("wrong direction %d, should be %d\n",
241                  (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_IP_OUT));
242
243         priv = pkt->dn_priv;
244         unref_priv = pkt->dn_unref_priv;
245         rt = pkt->ro.ro_rt;
246
247         if (rt != NULL && !(rt->rt_flags & RTF_UP)) {
248                 /*
249                  * Recorded rtentry is gone, when the packet
250                  * was on delay line.
251                  */
252                 ip_dn_freepkt(pkt);
253                 return;
254         }
255
256         ip_output(pkt->dn_m, NULL, NULL, 0, NULL, NULL);
257
258         if (rt != NULL) {
259                 if (rt->rt_refcnt <= 0) {       /* XXX assert? */
260                         kprintf("-- warning, refcnt now %ld, decreasing\n",
261                                 rt->rt_refcnt);
262                 }
263                 RTFREE(rt);
264         }
265         if (unref_priv)
266                 unref_priv(priv);
267 }
268
269 static void
270 ip_dn_ip_input(struct netmsg *nmsg)
271 {
272         struct netmsg_packet *nmp;
273         struct mbuf *m;
274         struct m_tag *mtag;
275         struct dn_pkt *pkt;
276         ip_dn_unref_priv_t unref_priv;
277         void *priv;
278
279         nmp = (struct netmsg_packet *)nmsg;
280         m = nmp->nm_packet;
281
282         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
283         KKASSERT(mtag != NULL);
284         KKASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED);
285
286         pkt = m_tag_data(mtag);
287         KASSERT(pkt->cpuid == mycpuid,
288                 ("%s: dummynet packet was delivered to wrong cpu! "
289                  "target cpuid %d, mycpuid %d\n", __func__,
290                  pkt->cpuid, mycpuid));
291         KASSERT(pkt->ro.ro_rt == NULL,
292                 ("route entry is not NULL for ip_input\n"));
293         KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_IP_IN,
294                 ("wrong direction %d, should be %d\n",
295                  (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_IP_IN));
296
297         priv = pkt->dn_priv;
298         unref_priv = pkt->dn_unref_priv;
299
300         ip_input(m);
301
302         if (unref_priv)
303                 unref_priv(priv);
304 }
305
306 static void
307 ip_dn_ether_demux(struct netmsg *nmsg)
308 {
309         struct netmsg_packet *nmp;
310         struct mbuf *m;
311         struct m_tag *mtag;
312         struct dn_pkt *pkt;
313         struct ether_header *eh;
314         ip_dn_unref_priv_t unref_priv;
315         void *priv;
316
317         nmp = (struct netmsg_packet *)nmsg;
318         m = nmp->nm_packet;
319
320         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
321         KKASSERT(mtag != NULL);
322         KKASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED);
323
324         pkt = m_tag_data(mtag);
325         KASSERT(pkt->cpuid == mycpuid,
326                 ("%s: dummynet packet was delivered to wrong cpu! "
327                  "target cpuid %d, mycpuid %d\n", __func__,
328                  pkt->cpuid, mycpuid));
329         KASSERT(pkt->ro.ro_rt == NULL,
330                 ("route entry is not NULL for ether_demux\n"));
331         KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_ETH_DEMUX,
332                 ("wrong direction %d, should be %d\n",
333                  (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_ETH_DEMUX));
334
335         priv = pkt->dn_priv;
336         unref_priv = pkt->dn_unref_priv;
337
338         if (m->m_len < ETHER_HDR_LEN &&
339             (m = m_pullup(m, ETHER_HDR_LEN)) == NULL) {
340                 kprintf("%s: pullup fail, dropping pkt\n", __func__);
341                 goto back;
342         }
343
344         /*
345          * Same as ether_input, make eh be a pointer into the mbuf
346          */
347         eh = mtod(m, struct ether_header *);
348         m_adj(m, ETHER_HDR_LEN);
349         ether_demux(NULL, eh, m);
350 back:
351         if (unref_priv)
352                 unref_priv(priv);
353 }
354
355 static void
356 ip_dn_ether_output(struct netmsg *nmsg)
357 {
358         struct netmsg_packet *nmp;
359         struct mbuf *m;
360         struct m_tag *mtag;
361         struct dn_pkt *pkt;
362         ip_dn_unref_priv_t unref_priv;
363         void *priv;
364
365         nmp = (struct netmsg_packet *)nmsg;
366         m = nmp->nm_packet;
367
368         mtag = m_tag_find(m, PACKET_TAG_DUMMYNET, NULL);
369         KKASSERT(mtag != NULL);
370         KKASSERT(m->m_pkthdr.fw_flags & DUMMYNET_MBUF_TAGGED);
371
372         pkt = m_tag_data(mtag);
373         KASSERT(pkt->cpuid == mycpuid,
374                 ("%s: dummynet packet was delivered to wrong cpu! "
375                  "target cpuid %d, mycpuid %d\n", __func__,
376                  pkt->cpuid, mycpuid));
377         KASSERT(pkt->ro.ro_rt == NULL,
378                 ("route entry is not NULL for ether_output_frame\n"));
379         KASSERT((pkt->dn_flags & DN_FLAGS_DIR_MASK) == DN_TO_ETH_OUT,
380                 ("wrong direction %d, should be %d\n",
381                  (pkt->dn_flags & DN_FLAGS_DIR_MASK), DN_TO_ETH_OUT));
382
383         priv = pkt->dn_priv;
384         unref_priv = pkt->dn_unref_priv;
385
386         ether_output_frame(pkt->ifp, m);
387
388         if (unref_priv)
389                 unref_priv(priv);
390 }