xref: /illumos-gate/usr/src/uts/common/xen/io/xnbu.c (revision 99dda20867d903eec23291ba1ecb18a82d70096b)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * Xen inter-domain backend - GLDv3 driver edition.
31  *
32  * A traditional GLDv3 driver used to communicate with a guest
33  * domain.  This driver is typically plumbed underneath the IP stack
34  * or a software ethernet bridge.
35  */
36 
37 #include "xnb.h"
38 
39 #include <sys/sunddi.h>
40 #include <sys/conf.h>
41 #include <sys/modctl.h>
42 #include <sys/strsubr.h>
43 #include <sys/dlpi.h>
44 #include <sys/pattr.h>
45 #include <sys/mac.h>
46 #include <sys/mac_ether.h>
47 #include <xen/sys/xendev.h>
48 
49 /* Required driver entry points for GLDv3 */
50 static int	xnbu_m_start(void *);
51 static void	xnbu_m_stop(void *);
52 static int	xnbu_m_set_mac_addr(void *, const uint8_t *);
53 static int	xnbu_m_set_multicast(void *, boolean_t, const uint8_t *);
54 static int	xnbu_m_set_promiscuous(void *, boolean_t);
55 static int	xnbu_m_stat(void *, uint_t, uint64_t *);
56 static void	xnbu_m_blank(void *, time_t, uint_t);
57 static void	xnbu_m_resources(void *);
58 static boolean_t xnbu_m_getcapab(void *, mac_capab_t, void *);
59 static mblk_t	*xnbu_m_send(void *, mblk_t *);
60 
61 typedef struct xnbu {
62 	mac_handle_t		u_mh;
63 	mac_resource_handle_t	u_rx_handle;
64 	boolean_t		u_need_sched;
65 } xnbu_t;
66 
67 static mac_callbacks_t xnb_callbacks = {
68 	MC_RESOURCES | MC_GETCAPAB,
69 	xnbu_m_stat,
70 	xnbu_m_start,
71 	xnbu_m_stop,
72 	xnbu_m_set_promiscuous,
73 	xnbu_m_set_multicast,
74 	xnbu_m_set_mac_addr,
75 	xnbu_m_send,
76 	xnbu_m_resources,
77 	NULL,
78 	xnbu_m_getcapab
79 };
80 
81 static void
82 xnbu_to_host(xnb_t *xnbp, mblk_t *mp)
83 {
84 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
85 	boolean_t sched = B_FALSE;
86 
87 	ASSERT(mp != NULL);
88 
89 	mac_rx(xnbup->u_mh, xnbup->u_rx_handle, mp);
90 
91 	mutex_enter(&xnbp->xnb_tx_lock);
92 
93 	/*
94 	 * If a transmit attempt failed because we ran out of ring
95 	 * space and there is now some space, re-enable the transmit
96 	 * path.
97 	 */
98 	if (xnbup->u_need_sched &&
99 	    RING_HAS_UNCONSUMED_REQUESTS(&xnbp->xnb_rx_ring)) {
100 		sched = B_TRUE;
101 		xnbup->u_need_sched = B_FALSE;
102 	}
103 
104 	mutex_exit(&xnbp->xnb_tx_lock);
105 
106 	if (sched)
107 		mac_tx_update(xnbup->u_mh);
108 }
109 
110 static mblk_t *
111 xnbu_cksum_from_peer(xnb_t *xnbp, mblk_t *mp, uint16_t flags)
112 {
113 	/*
114 	 * Take a conservative approach - if the checksum is blank
115 	 * then we fill it in.
116 	 *
117 	 * If the consumer of the packet is IP then we might actually
118 	 * only need fill it in if the data is not validated, but how
119 	 * do we know who might end up with the packet?
120 	 */
121 
122 	if ((flags & NETTXF_csum_blank) != 0) {
123 		/*
124 		 * The checksum is blank.  We must fill it in here.
125 		 */
126 		mp = xnb_process_cksum_flags(xnbp, mp, 0);
127 
128 		/*
129 		 * Because we calculated the checksum ourselves we
130 		 * know that it must be good, so we assert this.
131 		 */
132 		flags |= NETTXF_data_validated;
133 	}
134 
135 	if ((flags & NETTXF_data_validated) != 0) {
136 		/*
137 		 * The checksum is asserted valid.
138 		 *
139 		 * The hardware checksum offload specification says
140 		 * that we must provide the actual checksum as well as
141 		 * an assertion that it is valid, but the protocol
142 		 * stack doesn't actually use it so we don't bother.
143 		 * If it was necessary we could grovel in the packet
144 		 * to find it.
145 		 */
146 		(void) hcksum_assoc(mp, NULL, NULL, 0, 0, 0, 0,
147 		    HCK_FULLCKSUM | HCK_FULLCKSUM_OK, KM_NOSLEEP);
148 	}
149 
150 	return (mp);
151 }
152 
153 static uint16_t
154 xnbu_cksum_to_peer(xnb_t *xnbp, mblk_t *mp)
155 {
156 	uint16_t r = 0;
157 
158 	if (xnbp->xnb_cksum_offload) {
159 		uint32_t pflags;
160 
161 		hcksum_retrieve(mp, NULL, NULL, NULL, NULL,
162 		    NULL, NULL, &pflags);
163 
164 		/*
165 		 * If the protocol stack has requested checksum
166 		 * offload, inform the peer that we have not
167 		 * calculated the checksum.
168 		 */
169 		if ((pflags & HCK_FULLCKSUM) != 0)
170 			r |= NETRXF_csum_blank;
171 	}
172 
173 	return (r);
174 }
175 
176 static void
177 xnbu_connected(xnb_t *xnbp)
178 {
179 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
180 
181 	mac_link_update(xnbup->u_mh, LINK_STATE_UP);
182 	/*
183 	 * We are able to send packets now - bring them on.
184 	 */
185 	mac_tx_update(xnbup->u_mh);
186 }
187 
188 static void
189 xnbu_disconnected(xnb_t *xnbp)
190 {
191 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
192 
193 	mac_link_update(xnbup->u_mh, LINK_STATE_DOWN);
194 }
195 
196 /*ARGSUSED*/
197 static boolean_t
198 xnbu_hotplug(xnb_t *xnbp)
199 {
200 	return (B_TRUE);
201 }
202 
203 static mblk_t *
204 xnbu_m_send(void *arg, mblk_t *mp)
205 {
206 	xnb_t *xnbp = arg;
207 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
208 
209 	mp = xnb_copy_to_peer(arg, mp);
210 
211 	/* XXPV dme: playing with need_sched without txlock? */
212 
213 	/*
214 	 * If we consumed all of the mblk_t's offered, perhaps we need
215 	 * to indicate that we can accept more.  Otherwise we are full
216 	 * and need to wait for space.
217 	 */
218 	if (mp == NULL) {
219 		/*
220 		 * If a previous transmit attempt failed because the ring
221 		 * was full, try again now.
222 		 */
223 		if (xnbup->u_need_sched) {
224 			xnbup->u_need_sched = B_FALSE;
225 			mac_tx_update(xnbup->u_mh);
226 		}
227 	} else {
228 		xnbup->u_need_sched = B_TRUE;
229 	}
230 
231 	return (mp);
232 }
233 
234 /*
235  *  xnbu_m_set_mac_addr() -- set the physical network address on the board
236  */
237 /* ARGSUSED */
238 static int
239 xnbu_m_set_mac_addr(void *arg, const uint8_t *macaddr)
240 {
241 	xnb_t *xnbp = arg;
242 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
243 
244 	bcopy(macaddr, xnbp->xnb_mac_addr, ETHERADDRL);
245 	mac_unicst_update(xnbup->u_mh, xnbp->xnb_mac_addr);
246 
247 	return (0);
248 }
249 
250 /*
251  *  xnbu_m_set_multicast() -- set (enable) or disable a multicast address
252  */
253 /*ARGSUSED*/
254 static int
255 xnbu_m_set_multicast(void *arg, boolean_t add, const uint8_t *mca)
256 {
257 	/*
258 	 * We always accept all packets from the peer, so nothing to
259 	 * do for enable or disable.
260 	 */
261 	return (0);
262 }
263 
264 
265 /*
266  * xnbu_m_set_promiscuous() -- set or reset promiscuous mode on the board
267  */
268 /* ARGSUSED */
269 static int
270 xnbu_m_set_promiscuous(void *arg, boolean_t on)
271 {
272 	/*
273 	 * We always accept all packets from the peer, so nothing to
274 	 * do for enable or disable.
275 	 */
276 	return (0);
277 }
278 
279 /*
280  *  xnbu_m_start() -- start the board receiving and enable interrupts.
281  */
282 /*ARGSUSED*/
283 static int
284 xnbu_m_start(void *arg)
285 {
286 	return (0);
287 }
288 
289 /*
290  * xnbu_m_stop() - disable hardware
291  */
292 /*ARGSUSED*/
293 static void
294 xnbu_m_stop(void *arg)
295 {
296 }
297 
298 static int
299 xnbu_m_stat(void *arg, uint_t stat, uint64_t *val)
300 {
301 	xnb_t *xnbp = arg;
302 
303 	mutex_enter(&xnbp->xnb_tx_lock);
304 	mutex_enter(&xnbp->xnb_rx_lock);
305 
306 #define	map_stat(q, r)				\
307 	case (MAC_STAT_##q):			\
308 		*val = xnbp->xnb_stat_##r;		\
309 		break
310 
311 	switch (stat) {
312 
313 	map_stat(IPACKETS, ipackets);
314 	map_stat(OPACKETS, opackets);
315 	map_stat(RBYTES, rbytes);
316 	map_stat(OBYTES, obytes);
317 
318 	default:
319 		mutex_exit(&xnbp->xnb_rx_lock);
320 		mutex_exit(&xnbp->xnb_tx_lock);
321 
322 		return (ENOTSUP);
323 	}
324 
325 #undef map_stat
326 
327 	mutex_exit(&xnbp->xnb_rx_lock);
328 	mutex_exit(&xnbp->xnb_tx_lock);
329 
330 	return (0);
331 }
332 
333 /*ARGSUSED*/
334 static void
335 xnbu_m_blank(void *arg, time_t ticks, uint_t count)
336 {
337 	/*
338 	 * XXPV dme: blanking is not currently implemented.
339 	 */
340 }
341 
342 static void
343 xnbu_m_resources(void *arg)
344 {
345 	xnb_t *xnbp = arg;
346 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
347 	mac_rx_fifo_t mrf;
348 
349 	mrf.mrf_type = MAC_RX_FIFO;
350 	mrf.mrf_blank = xnbu_m_blank;
351 	mrf.mrf_arg = (void *)xnbp;
352 	mrf.mrf_normal_blank_time = 128; /* XXPV dme: see xnbu_m_blank() */
353 	mrf.mrf_normal_pkt_count = 8;    /* XXPV dme: see xnbu_m_blank() */
354 
355 	xnbup->u_rx_handle = mac_resource_add(xnbup->u_mh,
356 	    (mac_resource_t *)&mrf);
357 }
358 
359 static boolean_t
360 xnbu_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
361 {
362 	xnb_t *xnbp = arg;
363 
364 	switch (cap) {
365 	case MAC_CAPAB_HCKSUM: {
366 		uint32_t *capab = cap_data;
367 
368 		if (xnbp->xnb_cksum_offload)
369 			*capab = HCKSUM_INET_PARTIAL;
370 		else
371 			*capab = 0;
372 		break;
373 	}
374 
375 	case MAC_CAPAB_POLL:
376 		/* Just return B_TRUE. */
377 		break;
378 
379 	default:
380 		return (B_FALSE);
381 	}
382 
383 	return (B_TRUE);
384 }
385 
386 static int
387 xnbu_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
388 {
389 	static xnb_flavour_t flavour = {
390 		xnbu_to_host, xnbu_connected, xnbu_disconnected, xnbu_hotplug,
391 		xnbu_cksum_from_peer, xnbu_cksum_to_peer,
392 	};
393 	xnbu_t *xnbup;
394 	xnb_t *xnbp;
395 	mac_register_t *mr;
396 	int err;
397 
398 	switch (cmd) {
399 	case DDI_ATTACH:
400 		break;
401 	case DDI_RESUME:
402 		return (DDI_SUCCESS);
403 	default:
404 		return (DDI_FAILURE);
405 	}
406 
407 	xnbup = kmem_zalloc(sizeof (*xnbup), KM_SLEEP);
408 
409 	if ((mr = mac_alloc(MAC_VERSION)) == NULL) {
410 		kmem_free(xnbup, sizeof (*xnbup));
411 		return (DDI_FAILURE);
412 	}
413 
414 	if (xnb_attach(dip, &flavour, xnbup) != DDI_SUCCESS) {
415 		mac_free(mr);
416 		kmem_free(xnbup, sizeof (*xnbup));
417 		return (DDI_FAILURE);
418 	}
419 
420 	xnbp = ddi_get_driver_private(dip);
421 	ASSERT(xnbp != NULL);
422 
423 	mr->m_dip = dip;
424 	mr->m_driver = xnbp;
425 
426 	/*
427 	 *  Initialize pointers to device specific functions which will be
428 	 *  used by the generic layer.
429 	 */
430 	mr->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
431 	mr->m_src_addr = xnbp->xnb_mac_addr;
432 	mr->m_callbacks = &xnb_callbacks;
433 	mr->m_min_sdu = 0;
434 	mr->m_max_sdu = XNBMAXPKT;
435 	/*
436 	 * xnbu is a virtual device, and it is not associated with any
437 	 * physical device. Its margin size is determined by the maximum
438 	 * packet size it can handle, which is PAGESIZE.
439 	 */
440 	mr->m_margin = PAGESIZE - XNBMAXPKT - sizeof (struct ether_header);
441 
442 	(void) memset(xnbp->xnb_mac_addr, 0xff, ETHERADDRL);
443 	xnbp->xnb_mac_addr[0] &= 0xfe;
444 	xnbup->u_need_sched = B_FALSE;
445 
446 	/*
447 	 * Register ourselves with the GLDv3 interface.
448 	 */
449 	err = mac_register(mr, &xnbup->u_mh);
450 	mac_free(mr);
451 	if (err != 0) {
452 		xnb_detach(dip);
453 		kmem_free(xnbup, sizeof (*xnbup));
454 		return (DDI_FAILURE);
455 	}
456 
457 	mac_link_update(xnbup->u_mh, LINK_STATE_DOWN);
458 
459 	return (DDI_SUCCESS);
460 }
461 
462 /*ARGSUSED*/
463 int
464 xnbu_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
465 {
466 	xnb_t *xnbp = ddi_get_driver_private(dip);
467 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
468 
469 	switch (cmd) {
470 	case DDI_DETACH:
471 		break;
472 	case DDI_SUSPEND:
473 		return (DDI_SUCCESS);
474 	default:
475 		return (DDI_FAILURE);
476 	}
477 
478 	ASSERT(xnbp != NULL);
479 	ASSERT(xnbup != NULL);
480 
481 	mutex_enter(&xnbp->xnb_tx_lock);
482 	mutex_enter(&xnbp->xnb_rx_lock);
483 
484 	if (!xnbp->xnb_detachable || xnbp->xnb_connected ||
485 	    (xnbp->xnb_rx_buf_count > 0)) {
486 		mutex_exit(&xnbp->xnb_rx_lock);
487 		mutex_exit(&xnbp->xnb_tx_lock);
488 
489 		return (DDI_FAILURE);
490 	}
491 
492 	mutex_exit(&xnbp->xnb_rx_lock);
493 	mutex_exit(&xnbp->xnb_tx_lock);
494 
495 	/*
496 	 * Attempt to unregister the mac.
497 	 */
498 	if ((xnbup->u_mh != NULL) && (mac_unregister(xnbup->u_mh) != 0))
499 		return (DDI_FAILURE);
500 	kmem_free(xnbup, sizeof (*xnbup));
501 
502 	xnb_detach(dip);
503 
504 	return (DDI_SUCCESS);
505 }
506 
507 DDI_DEFINE_STREAM_OPS(ops, nulldev, nulldev, xnbu_attach, xnbu_detach,
508     nodev, NULL, D_MP, NULL);
509 
510 static struct modldrv modldrv = {
511 	&mod_driverops, "xnbu driver %I%", &ops
512 };
513 
514 static struct modlinkage modlinkage = {
515 	MODREV_1, &modldrv, NULL
516 };
517 
518 int
519 _init(void)
520 {
521 	int i;
522 
523 	mac_init_ops(&ops, "xnbu");
524 
525 	i = mod_install(&modlinkage);
526 	if (i != DDI_SUCCESS)
527 		mac_fini_ops(&ops);
528 
529 	return (i);
530 }
531 
532 int
533 _fini(void)
534 {
535 	int i;
536 
537 	i = mod_remove(&modlinkage);
538 	if (i == DDI_SUCCESS)
539 		mac_fini_ops(&ops);
540 
541 	return (i);
542 }
543 
544 int
545 _info(struct modinfo *modinfop)
546 {
547 	return (mod_info(&modlinkage, modinfop));
548 }
549