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