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 2007 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 network backend - mac client edition. 31 * 32 * A driver that sits above an existing GLDv3/Nemo MAC driver and 33 * relays packets to/from that driver from/to a guest domain. 34 */ 35 36 #include "xnb.h" 37 38 #include <sys/sunddi.h> 39 #include <sys/modctl.h> 40 #include <sys/strsubr.h> 41 #include <sys/mac.h> 42 #include <net/if.h> 43 #include <sys/dlpi.h> 44 #include <sys/pattr.h> 45 #include <xen/sys/xenbus_impl.h> 46 #include <xen/sys/xendev.h> 47 48 typedef struct xnbo { 49 mac_handle_t o_mh; 50 mac_rx_handle_t o_mrh; 51 const mac_txinfo_t *o_mtx; 52 mac_notify_handle_t o_mnh; 53 boolean_t o_running; 54 boolean_t o_promiscuous; 55 uint32_t o_hcksum_capab; 56 } xnbo_t; 57 58 static void xnbo_close_mac(xnbo_t *); 59 60 /* 61 * Packets from the peer come here. We pass them to the mac device. 62 */ 63 static void 64 xnbo_to_mac(xnb_t *xnbp, mblk_t *mp) 65 { 66 xnbo_t *xnbop = xnbp->x_flavour_data; 67 68 ASSERT(mp != NULL); 69 70 if (!xnbop->o_running) { 71 xnbp->x_stat_rx_too_early++; 72 goto fail; 73 } 74 75 mp = xnbop->o_mtx->mt_fn(xnbop->o_mtx->mt_arg, mp); 76 77 if (mp != NULL) { 78 xnbp->x_stat_mac_full++; 79 goto fail; 80 } 81 82 return; 83 84 fail: 85 freemsgchain(mp); 86 } 87 88 static mblk_t * 89 xnbo_cksum_from_peer(xnb_t *xnbp, mblk_t *mp, uint16_t flags) 90 { 91 xnbo_t *xnbop = xnbp->x_flavour_data; 92 93 ASSERT(mp->b_next == NULL); 94 95 if ((flags & NETTXF_csum_blank) != 0) { 96 /* 97 * It would be nice to ASSERT that xnbp->x_cksum_offload 98 * is TRUE here, but some peers insist on assuming 99 * that it is available even when they have been told 100 * otherwise. 101 * 102 * The checksum in the packet is blank. Determine 103 * whether we can do hardware offload and, if so, 104 * update the flags on the mblk according. If not, 105 * calculate and insert the checksum using software. 106 */ 107 mp = xnb_process_cksum_flags(xnbp, mp, 108 xnbop->o_hcksum_capab); 109 } 110 111 return (mp); 112 } 113 114 static uint16_t 115 xnbo_cksum_to_peer(xnb_t *xnbp, mblk_t *mp) 116 { 117 uint16_t r = 0; 118 119 /* 120 * We might also check for HCK_PARTIALCKSUM here and, 121 * providing that the partial checksum covers the TCP/UDP 122 * payload, return NETRXF_data_validated. 123 * 124 * It seems that it's probably not worthwhile, as even MAC 125 * devices which advertise HCKSUM_INET_PARTIAL in their 126 * capabilities tend to use HCK_FULLCKSUM on the receive side 127 * - they are actually saying that in the output path the 128 * caller must use HCK_PARTIALCKSUM. 129 */ 130 131 if (xnbp->x_cksum_offload) { 132 uint32_t pflags, csum; 133 134 /* 135 * XXPV dme: Pull in improved hcksum_retrieve() from 136 * Crossbow, which gives back the csum in the seventh 137 * argument for HCK_FULLCKSUM. 138 */ 139 hcksum_retrieve(mp, NULL, NULL, NULL, NULL, 140 NULL, NULL, &pflags); 141 csum = DB_CKSUM16(mp); 142 143 /* 144 * If the MAC driver has asserted that the checksum is 145 * good, let the peer know. 146 */ 147 if (((pflags & HCK_FULLCKSUM) != 0) && 148 (((pflags & HCK_FULLCKSUM_OK) != 0) || 149 (csum == 0xffff))) 150 r |= NETRXF_data_validated; 151 } 152 153 return (r); 154 } 155 156 /* 157 * Packets from the mac device come here. We pass them to the peer. 158 */ 159 /*ARGSUSED*/ 160 static void 161 xnbo_from_mac(void *arg, mac_resource_handle_t mrh, mblk_t *mp) 162 { 163 xnb_t *xnbp = arg; 164 165 mp = xnb_to_peer(xnbp, mp); 166 167 if (mp != NULL) 168 freemsgchain(mp); 169 } 170 171 /* 172 * Packets from the mac device come here. We pass them to the peer if 173 * the destination mac address matches or it's a multicast/broadcast 174 * address. 175 */ 176 /*ARGSUSED*/ 177 static void 178 xnbo_from_mac_filter(void *arg, mac_resource_handle_t mrh, mblk_t *mp) 179 { 180 xnb_t *xnbp = arg; 181 xnbo_t *xnbop = xnbp->x_flavour_data; 182 mblk_t *next, *keep, *keep_head, *free, *free_head; 183 184 keep = keep_head = free = free_head = NULL; 185 186 #define ADD(list, bp) \ 187 if (list != NULL) \ 188 list->b_next = bp; \ 189 else \ 190 list##_head = bp; \ 191 list = bp; 192 193 for (; mp != NULL; mp = next) { 194 mac_header_info_t hdr_info; 195 196 next = mp->b_next; 197 mp->b_next = NULL; 198 199 if (mac_header_info(xnbop->o_mh, mp, &hdr_info) != 0) { 200 ADD(free, mp); 201 continue; 202 } 203 204 if ((hdr_info.mhi_dsttype == MAC_ADDRTYPE_BROADCAST) || 205 (hdr_info.mhi_dsttype == MAC_ADDRTYPE_MULTICAST)) { 206 ADD(keep, mp); 207 continue; 208 } 209 210 if (bcmp(hdr_info.mhi_daddr, xnbp->x_mac_addr, 211 sizeof (xnbp->x_mac_addr)) == 0) { 212 ADD(keep, mp); 213 continue; 214 } 215 216 ADD(free, mp); 217 } 218 #undef ADD 219 220 if (keep_head != NULL) 221 xnbo_from_mac(xnbp, mrh, keep_head); 222 223 if (free_head != NULL) 224 freemsgchain(free_head); 225 } 226 227 static void 228 xnbo_notify(void *arg, mac_notify_type_t type) 229 { 230 xnb_t *xnbp = arg; 231 xnbo_t *xnbop = xnbp->x_flavour_data; 232 233 switch (type) { 234 case MAC_NOTE_PROMISC: 235 xnbop->o_mtx = mac_tx_get(xnbop->o_mh); 236 break; 237 } 238 } 239 240 static boolean_t 241 xnbo_open_mac(xnb_t *xnbp, char *mac) 242 { 243 xnbo_t *xnbop = xnbp->x_flavour_data; 244 char drv[LIFNAMSIZ]; 245 uint_t ddi_instance; 246 int err, need_rx_filter, need_setphysaddr, need_promiscuous; 247 const mac_info_t *mi; 248 char *xsname; 249 void (*rx_fn)(void *, mac_resource_handle_t, mblk_t *); 250 251 xsname = xvdi_get_xsname(xnbp->x_devinfo); 252 253 if (ddi_parse(mac, drv, &ddi_instance) != DDI_SUCCESS) { 254 cmn_err(CE_WARN, "xnbo_hotplug: " 255 "invalid device name %s", mac); 256 return (B_FALSE); 257 } 258 259 if ((err = mac_open(mac, ddi_instance, &xnbop->o_mh)) != 0) { 260 cmn_err(CE_WARN, "xnbo_open_mac: " 261 "cannot open mac device %s (%d)", mac, err); 262 return (B_FALSE); 263 } 264 ASSERT(xnbop->o_mh != NULL); 265 266 mi = mac_info(xnbop->o_mh); 267 ASSERT(mi != NULL); 268 269 if (mi->mi_media != DL_ETHER) { 270 cmn_err(CE_WARN, "xnbo_open_mac: " 271 "mac device is not DL_ETHER (%d)", mi->mi_media); 272 xnbo_close_mac(xnbop); 273 return (B_FALSE); 274 } 275 if (mi->mi_media != mi->mi_nativemedia) { 276 cmn_err(CE_WARN, "xnbo_open_mac: " 277 "mac device media and native media mismatch (%d != %d)", 278 mi->mi_media, mi->mi_nativemedia); 279 xnbo_close_mac(xnbop); 280 return (B_FALSE); 281 } 282 if (mi->mi_sdu_max > XNBMAXPKT) { 283 cmn_err(CE_WARN, "xnbo_open_mac: " 284 "mac device SDU too big (%d)", mi->mi_sdu_max); 285 xnbo_close_mac(xnbop); 286 return (B_FALSE); 287 } 288 289 xnbop->o_mnh = mac_notify_add(xnbop->o_mh, xnbo_notify, xnbp); 290 ASSERT(xnbop->o_mnh != NULL); 291 292 /* 293 * Should the receive path filter packets from the downstream 294 * NIC before passing them to the peer? The default is "no". 295 */ 296 if (xenbus_scanf(XBT_NULL, xsname, 297 "SUNW-need-rx-filter", "%d", &need_rx_filter) != 0) 298 need_rx_filter = 0; 299 if (need_rx_filter > 0) 300 rx_fn = xnbo_from_mac_filter; 301 else 302 rx_fn = xnbo_from_mac; 303 304 xnbop->o_mrh = mac_rx_add(xnbop->o_mh, rx_fn, xnbp); 305 ASSERT(xnbop->o_mrh != NULL); 306 307 xnbop->o_mtx = mac_tx_get(xnbop->o_mh); 308 ASSERT(xnbop->o_mtx != NULL); 309 310 if (!mac_capab_get(xnbop->o_mh, MAC_CAPAB_HCKSUM, 311 &xnbop->o_hcksum_capab)) 312 xnbop->o_hcksum_capab = 0; 313 314 /* 315 * Should we set the physical address of the underlying NIC 316 * to match that assigned to the peer? The default is "no". 317 */ 318 if (xenbus_scanf(XBT_NULL, xsname, 319 "SUNW-need-set-physaddr", "%d", &need_setphysaddr) != 0) 320 need_setphysaddr = 0; 321 if (need_setphysaddr > 0) { 322 struct ether_addr ea; 323 324 err = mac_unicst_set(xnbop->o_mh, xnbp->x_mac_addr); 325 /* Warn, but continue on. */ 326 if (err != 0) { 327 bcopy(xnbp->x_mac_addr, ea.ether_addr_octet, 328 ETHERADDRL); 329 cmn_err(CE_WARN, "xnbo_open_mac: " 330 "cannot set MAC address of %s to " 331 "%s: %d", mac, ether_sprintf(&ea), 332 err); 333 } 334 } 335 336 /* 337 * Should we set the underlying NIC into promiscuous mode? The 338 * default is "no". 339 */ 340 if (xenbus_scanf(XBT_NULL, xsname, 341 "SUNW-need-promiscuous", "%d", &need_promiscuous) != 0) 342 need_promiscuous = 0; 343 if (need_promiscuous > 0) { 344 err = mac_promisc_set(xnbop->o_mh, B_TRUE, MAC_DEVPROMISC); 345 if (err != 0) { 346 cmn_err(CE_WARN, "xnbo_open_mac: " 347 "cannot enable promiscuous mode of %s: %d", 348 mac, err); 349 xnbo_close_mac(xnbop); 350 return (B_FALSE); 351 } 352 xnbop->o_promiscuous = B_TRUE; 353 } 354 355 if ((err = mac_start(xnbop->o_mh)) != 0) { 356 cmn_err(CE_WARN, "xnbo_open_mac: " 357 "cannot start mac device (%d)", err); 358 xnbo_close_mac(xnbop); 359 return (B_FALSE); 360 } 361 xnbop->o_running = B_TRUE; 362 363 return (B_TRUE); 364 } 365 366 /* 367 * xnb calls back here when the user-level hotplug code reports that 368 * the hotplug has successfully completed. For this flavour that means 369 * that the underlying MAC device that we will use is ready to be 370 * opened. 371 */ 372 static boolean_t 373 xnbo_hotplug(xnb_t *xnbp) 374 { 375 char *xsname; 376 char mac[LIFNAMSIZ]; 377 378 xsname = xvdi_get_xsname(xnbp->x_devinfo); 379 if (xenbus_scanf(XBT_NULL, xsname, "nic", "%s", mac) != 0) { 380 cmn_err(CE_WARN, "xnbo_hotplug: " 381 "cannot read nic name from %s", xsname); 382 return (B_FALSE); 383 } 384 385 return (xnbo_open_mac(xnbp, mac)); 386 } 387 388 static void 389 xnbo_close_mac(xnbo_t *xnbop) 390 { 391 if (xnbop->o_mh == NULL) 392 return; 393 394 if (xnbop->o_running) { 395 mac_stop(xnbop->o_mh); 396 xnbop->o_running = B_FALSE; 397 } 398 399 if (xnbop->o_promiscuous) { 400 (void) mac_promisc_set(xnbop->o_mh, B_FALSE, 401 MAC_DEVPROMISC); 402 xnbop->o_promiscuous = B_FALSE; 403 } 404 405 xnbop->o_mtx = NULL; 406 407 if (xnbop->o_mrh != NULL) { 408 mac_rx_remove(xnbop->o_mh, xnbop->o_mrh, B_TRUE); 409 xnbop->o_mrh = NULL; 410 } 411 412 if (xnbop->o_mnh != NULL) { 413 mac_notify_remove(xnbop->o_mh, xnbop->o_mnh); 414 xnbop->o_mnh = NULL; 415 } 416 417 mac_close(xnbop->o_mh); 418 xnbop->o_mh = NULL; 419 } 420 421 /* 422 * xnb calls back here when we successfully synchronize with the 423 * driver in the guest domain. In this flavour there is nothing to do as 424 * we open the underlying MAC device on successful hotplug completion. 425 */ 426 /*ARGSUSED*/ 427 static void 428 xnbo_connected(xnb_t *xnbp) 429 { 430 } 431 432 /* 433 * xnb calls back here when the driver in the guest domain has closed 434 * down the inter-domain connection. We close the underlying MAC device. 435 */ 436 static void 437 xnbo_disconnected(xnb_t *xnbp) 438 { 439 xnbo_close_mac(xnbp->x_flavour_data); 440 } 441 442 static int 443 xnbo_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 444 { 445 static xnb_flavour_t flavour = { 446 xnbo_to_mac, xnbo_connected, xnbo_disconnected, xnbo_hotplug, 447 xnbo_cksum_from_peer, xnbo_cksum_to_peer, 448 }; 449 xnbo_t *xnbop; 450 451 switch (cmd) { 452 case DDI_ATTACH: 453 break; 454 case DDI_RESUME: 455 return (DDI_SUCCESS); 456 default: 457 return (DDI_FAILURE); 458 } 459 460 xnbop = kmem_zalloc(sizeof (*xnbop), KM_SLEEP); 461 462 xnbop->o_mh = NULL; 463 xnbop->o_mrh = NULL; 464 xnbop->o_mtx = NULL; 465 xnbop->o_running = B_FALSE; 466 xnbop->o_hcksum_capab = 0; 467 468 if (xnb_attach(dip, &flavour, xnbop) != DDI_SUCCESS) { 469 kmem_free(xnbop, sizeof (*xnbop)); 470 return (DDI_FAILURE); 471 } 472 473 return (DDI_SUCCESS); 474 } 475 476 static int 477 xnbo_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 478 { 479 xnb_t *xnbp = ddi_get_driver_private(dip); 480 xnbo_t *xnbop = xnbp->x_flavour_data; 481 482 switch (cmd) { 483 case DDI_DETACH: 484 break; 485 case DDI_SUSPEND: 486 return (DDI_SUCCESS); 487 default: 488 return (DDI_FAILURE); 489 } 490 491 mutex_enter(&xnbp->x_tx_lock); 492 mutex_enter(&xnbp->x_rx_lock); 493 494 if (!xnbp->x_detachable || xnbp->x_connected || 495 (xnbp->x_rx_buf_count > 0)) { 496 mutex_exit(&xnbp->x_rx_lock); 497 mutex_exit(&xnbp->x_tx_lock); 498 499 return (DDI_FAILURE); 500 } 501 502 mutex_exit(&xnbp->x_rx_lock); 503 mutex_exit(&xnbp->x_tx_lock); 504 505 xnbo_close_mac(xnbop); 506 kmem_free(xnbop, sizeof (*xnbop)); 507 508 xnb_detach(dip); 509 510 return (DDI_SUCCESS); 511 } 512 513 static struct cb_ops cb_ops = { 514 nulldev, /* open */ 515 nulldev, /* close */ 516 nodev, /* strategy */ 517 nodev, /* print */ 518 nodev, /* dump */ 519 nodev, /* read */ 520 nodev, /* write */ 521 nodev, /* ioctl */ 522 nodev, /* devmap */ 523 nodev, /* mmap */ 524 nodev, /* segmap */ 525 nochpoll, /* poll */ 526 ddi_prop_op, /* cb_prop_op */ 527 0, /* streamtab */ 528 D_NEW | D_MP | D_64BIT /* Driver compatibility flag */ 529 }; 530 531 static struct dev_ops ops = { 532 DEVO_REV, /* devo_rev */ 533 0, /* devo_refcnt */ 534 nulldev, /* devo_getinfo */ 535 nulldev, /* devo_identify */ 536 nulldev, /* devo_probe */ 537 xnbo_attach, /* devo_attach */ 538 xnbo_detach, /* devo_detach */ 539 nodev, /* devo_reset */ 540 &cb_ops, /* devo_cb_ops */ 541 (struct bus_ops *)0, /* devo_bus_ops */ 542 NULL /* devo_power */ 543 }; 544 545 static struct modldrv modldrv = { 546 &mod_driverops, "xnbo driver %I%", &ops, 547 }; 548 549 static struct modlinkage modlinkage = { 550 MODREV_1, &modldrv, NULL 551 }; 552 553 int 554 _init(void) 555 { 556 return (mod_install(&modlinkage)); 557 } 558 559 int 560 _info(struct modinfo *modinfop) 561 { 562 return (mod_info(&modlinkage, modinfop)); 563 } 564 565 int 566 _fini(void) 567 { 568 return (mod_remove(&modlinkage)); 569 } 570