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