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 #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->xnb_flavour_data; 67 68 ASSERT(mp != NULL); 69 70 if (!xnbop->o_running) { 71 xnbp->xnb_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->xnb_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->xnb_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->xnb_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->xnb_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_copy_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->xnb_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->xnb_mac_addr, 211 sizeof (xnbp->xnb_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->xnb_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->xnb_flavour_data; 244 int err, need_rx_filter, need_setphysaddr, need_promiscuous; 245 const mac_info_t *mi; 246 char *xsname; 247 void (*rx_fn)(void *, mac_resource_handle_t, mblk_t *); 248 uint_t max_sdu; 249 250 xsname = xvdi_get_xsname(xnbp->xnb_devinfo); 251 252 if ((err = mac_open_by_linkname(mac, &xnbop->o_mh)) != 0) { 253 cmn_err(CE_WARN, "xnbo_open_mac: " 254 "cannot open mac for link %s (%d)", mac, err); 255 return (B_FALSE); 256 } 257 ASSERT(xnbop->o_mh != NULL); 258 259 mi = mac_info(xnbop->o_mh); 260 ASSERT(mi != NULL); 261 262 if (mi->mi_media != DL_ETHER) { 263 cmn_err(CE_WARN, "xnbo_open_mac: " 264 "device is not DL_ETHER (%d)", mi->mi_media); 265 xnbo_close_mac(xnbop); 266 return (B_FALSE); 267 } 268 if (mi->mi_media != mi->mi_nativemedia) { 269 cmn_err(CE_WARN, "xnbo_open_mac: " 270 "device media and native media mismatch (%d != %d)", 271 mi->mi_media, mi->mi_nativemedia); 272 xnbo_close_mac(xnbop); 273 return (B_FALSE); 274 } 275 276 mac_sdu_get(xnbop->o_mh, NULL, &max_sdu); 277 if (max_sdu > XNBMAXPKT) { 278 cmn_err(CE_WARN, "xnbo_open_mac: mac device SDU too big (%d)", 279 max_sdu); 280 xnbo_close_mac(xnbop); 281 return (B_FALSE); 282 } 283 284 xnbop->o_mnh = mac_notify_add(xnbop->o_mh, xnbo_notify, xnbp); 285 ASSERT(xnbop->o_mnh != NULL); 286 287 /* 288 * Should the receive path filter packets from the downstream 289 * NIC before passing them to the peer? The default is "no". 290 */ 291 if (xenbus_scanf(XBT_NULL, xsname, 292 "SUNW-need-rx-filter", "%d", &need_rx_filter) != 0) 293 need_rx_filter = 0; 294 if (need_rx_filter > 0) 295 rx_fn = xnbo_from_mac_filter; 296 else 297 rx_fn = xnbo_from_mac; 298 299 xnbop->o_mrh = mac_rx_add(xnbop->o_mh, rx_fn, xnbp); 300 ASSERT(xnbop->o_mrh != NULL); 301 302 xnbop->o_mtx = mac_tx_get(xnbop->o_mh); 303 ASSERT(xnbop->o_mtx != NULL); 304 305 if (!mac_capab_get(xnbop->o_mh, MAC_CAPAB_HCKSUM, 306 &xnbop->o_hcksum_capab)) 307 xnbop->o_hcksum_capab = 0; 308 309 /* 310 * Should we set the physical address of the underlying NIC 311 * to match that assigned to the peer? The default is "no". 312 */ 313 if (xenbus_scanf(XBT_NULL, xsname, 314 "SUNW-need-set-physaddr", "%d", &need_setphysaddr) != 0) 315 need_setphysaddr = 0; 316 if (need_setphysaddr > 0) { 317 struct ether_addr ea; 318 319 err = mac_unicst_set(xnbop->o_mh, xnbp->xnb_mac_addr); 320 /* Warn, but continue on. */ 321 if (err != 0) { 322 bcopy(xnbp->xnb_mac_addr, ea.ether_addr_octet, 323 ETHERADDRL); 324 cmn_err(CE_WARN, "xnbo_open_mac: " 325 "cannot set MAC address of %s to " 326 "%s: %d", mac, ether_sprintf(&ea), 327 err); 328 } 329 } 330 331 /* 332 * Should we set the underlying NIC into promiscuous mode? The 333 * default is "no". 334 */ 335 if (xenbus_scanf(XBT_NULL, xsname, 336 "SUNW-need-promiscuous", "%d", &need_promiscuous) != 0) 337 need_promiscuous = 0; 338 if (need_promiscuous > 0) { 339 err = mac_promisc_set(xnbop->o_mh, B_TRUE, MAC_DEVPROMISC); 340 if (err != 0) { 341 cmn_err(CE_WARN, "xnbo_open_mac: " 342 "cannot enable promiscuous mode of %s: %d", 343 mac, err); 344 xnbo_close_mac(xnbop); 345 return (B_FALSE); 346 } 347 xnbop->o_promiscuous = B_TRUE; 348 } 349 350 if ((err = mac_start(xnbop->o_mh)) != 0) { 351 cmn_err(CE_WARN, "xnbo_open_mac: " 352 "cannot start mac device (%d)", err); 353 xnbo_close_mac(xnbop); 354 return (B_FALSE); 355 } 356 xnbop->o_running = B_TRUE; 357 358 return (B_TRUE); 359 } 360 361 /* 362 * xnb calls back here when the user-level hotplug code reports that 363 * the hotplug has successfully completed. For this flavour that means 364 * that the underlying MAC device that we will use is ready to be 365 * opened. 366 */ 367 static boolean_t 368 xnbo_hotplug(xnb_t *xnbp) 369 { 370 char *xsname; 371 char mac[LIFNAMSIZ]; 372 373 xsname = xvdi_get_xsname(xnbp->xnb_devinfo); 374 if (xenbus_scanf(XBT_NULL, xsname, "nic", "%s", mac) != 0) { 375 cmn_err(CE_WARN, "xnbo_hotplug: " 376 "cannot read nic name from %s", xsname); 377 return (B_FALSE); 378 } 379 380 return (xnbo_open_mac(xnbp, mac)); 381 } 382 383 static void 384 xnbo_close_mac(xnbo_t *xnbop) 385 { 386 if (xnbop->o_mh == NULL) 387 return; 388 389 if (xnbop->o_running) { 390 mac_stop(xnbop->o_mh); 391 xnbop->o_running = B_FALSE; 392 } 393 394 if (xnbop->o_promiscuous) { 395 (void) mac_promisc_set(xnbop->o_mh, B_FALSE, 396 MAC_DEVPROMISC); 397 xnbop->o_promiscuous = B_FALSE; 398 } 399 400 xnbop->o_mtx = NULL; 401 402 if (xnbop->o_mrh != NULL) { 403 mac_rx_remove(xnbop->o_mh, xnbop->o_mrh, B_TRUE); 404 xnbop->o_mrh = NULL; 405 } 406 407 if (xnbop->o_mnh != NULL) { 408 mac_notify_remove(xnbop->o_mh, xnbop->o_mnh); 409 xnbop->o_mnh = NULL; 410 } 411 412 mac_close(xnbop->o_mh); 413 xnbop->o_mh = NULL; 414 } 415 416 /* 417 * xnb calls back here when we successfully synchronize with the 418 * driver in the guest domain. In this flavour there is nothing to do as 419 * we open the underlying MAC device on successful hotplug completion. 420 */ 421 /*ARGSUSED*/ 422 static void 423 xnbo_connected(xnb_t *xnbp) 424 { 425 } 426 427 /* 428 * xnb calls back here when the driver in the guest domain has closed 429 * down the inter-domain connection. We close the underlying MAC device. 430 */ 431 static void 432 xnbo_disconnected(xnb_t *xnbp) 433 { 434 xnbo_close_mac(xnbp->xnb_flavour_data); 435 } 436 437 static int 438 xnbo_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 439 { 440 static xnb_flavour_t flavour = { 441 xnbo_to_mac, xnbo_connected, xnbo_disconnected, xnbo_hotplug, 442 xnbo_cksum_from_peer, xnbo_cksum_to_peer, 443 }; 444 xnbo_t *xnbop; 445 446 switch (cmd) { 447 case DDI_ATTACH: 448 break; 449 case DDI_RESUME: 450 return (DDI_SUCCESS); 451 default: 452 return (DDI_FAILURE); 453 } 454 455 xnbop = kmem_zalloc(sizeof (*xnbop), KM_SLEEP); 456 457 xnbop->o_mh = NULL; 458 xnbop->o_mrh = NULL; 459 xnbop->o_mtx = NULL; 460 xnbop->o_running = B_FALSE; 461 xnbop->o_hcksum_capab = 0; 462 463 if (xnb_attach(dip, &flavour, xnbop) != DDI_SUCCESS) { 464 kmem_free(xnbop, sizeof (*xnbop)); 465 return (DDI_FAILURE); 466 } 467 468 return (DDI_SUCCESS); 469 } 470 471 static int 472 xnbo_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 473 { 474 xnb_t *xnbp = ddi_get_driver_private(dip); 475 xnbo_t *xnbop = xnbp->xnb_flavour_data; 476 477 switch (cmd) { 478 case DDI_DETACH: 479 break; 480 case DDI_SUSPEND: 481 return (DDI_SUCCESS); 482 default: 483 return (DDI_FAILURE); 484 } 485 486 mutex_enter(&xnbp->xnb_tx_lock); 487 mutex_enter(&xnbp->xnb_rx_lock); 488 489 if (!xnbp->xnb_detachable || xnbp->xnb_connected || 490 (xnbp->xnb_rx_buf_count > 0)) { 491 mutex_exit(&xnbp->xnb_rx_lock); 492 mutex_exit(&xnbp->xnb_tx_lock); 493 494 return (DDI_FAILURE); 495 } 496 497 mutex_exit(&xnbp->xnb_rx_lock); 498 mutex_exit(&xnbp->xnb_tx_lock); 499 500 xnbo_close_mac(xnbop); 501 kmem_free(xnbop, sizeof (*xnbop)); 502 503 xnb_detach(dip); 504 505 return (DDI_SUCCESS); 506 } 507 508 static struct cb_ops cb_ops = { 509 nulldev, /* open */ 510 nulldev, /* close */ 511 nodev, /* strategy */ 512 nodev, /* print */ 513 nodev, /* dump */ 514 nodev, /* read */ 515 nodev, /* write */ 516 nodev, /* ioctl */ 517 nodev, /* devmap */ 518 nodev, /* mmap */ 519 nodev, /* segmap */ 520 nochpoll, /* poll */ 521 ddi_prop_op, /* cb_prop_op */ 522 0, /* streamtab */ 523 D_NEW | D_MP | D_64BIT /* Driver compatibility flag */ 524 }; 525 526 static struct dev_ops ops = { 527 DEVO_REV, /* devo_rev */ 528 0, /* devo_refcnt */ 529 nulldev, /* devo_getinfo */ 530 nulldev, /* devo_identify */ 531 nulldev, /* devo_probe */ 532 xnbo_attach, /* devo_attach */ 533 xnbo_detach, /* devo_detach */ 534 nodev, /* devo_reset */ 535 &cb_ops, /* devo_cb_ops */ 536 (struct bus_ops *)0, /* devo_bus_ops */ 537 NULL /* devo_power */ 538 }; 539 540 static struct modldrv modldrv = { 541 &mod_driverops, "xnbo driver %I%", &ops, 542 }; 543 544 static struct modlinkage modlinkage = { 545 MODREV_1, &modldrv, NULL 546 }; 547 548 int 549 _init(void) 550 { 551 return (mod_install(&modlinkage)); 552 } 553 554 int 555 _info(struct modinfo *modinfop) 556 { 557 return (mod_info(&modlinkage, modinfop)); 558 } 559 560 int 561 _fini(void) 562 { 563 return (mod_remove(&modlinkage)); 564 } 565