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