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