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