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