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 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* 26 * Copyright 2025 Oxide Computer Company 27 */ 28 29 #include <sys/stropts.h> 30 #include <sys/strsubr.h> 31 #include <sys/callb.h> 32 #include <sys/softmac_impl.h> 33 34 int 35 softmac_send_notify_req(softmac_lower_t *slp, uint32_t notifications) 36 { 37 mblk_t *reqmp; 38 39 /* 40 * create notify req message and send it down 41 */ 42 reqmp = mexchange(NULL, NULL, DL_NOTIFY_REQ_SIZE, M_PROTO, 43 DL_NOTIFY_REQ); 44 if (reqmp == NULL) 45 return (ENOMEM); 46 47 ((dl_notify_req_t *)reqmp->b_rptr)->dl_notifications = notifications; 48 49 return (softmac_proto_tx(slp, reqmp, NULL)); 50 } 51 52 int 53 softmac_send_bind_req(softmac_lower_t *slp, uint_t sap) 54 { 55 dl_bind_req_t *bind; 56 mblk_t *reqmp; 57 58 /* 59 * create bind req message and send it down 60 */ 61 reqmp = mexchange(NULL, NULL, DL_BIND_REQ_SIZE, M_PROTO, DL_BIND_REQ); 62 if (reqmp == NULL) 63 return (ENOMEM); 64 65 bind = (dl_bind_req_t *)reqmp->b_rptr; 66 bind->dl_sap = sap; 67 bind->dl_conn_mgmt = 0; 68 bind->dl_max_conind = 0; 69 bind->dl_xidtest_flg = 0; 70 bind->dl_service_mode = DL_CLDLS; 71 72 return (softmac_proto_tx(slp, reqmp, NULL)); 73 } 74 75 int 76 softmac_send_unbind_req(softmac_lower_t *slp) 77 { 78 mblk_t *reqmp; 79 80 /* 81 * create unbind req message and send it down 82 */ 83 reqmp = mexchange(NULL, NULL, DL_UNBIND_REQ_SIZE, M_PROTO, 84 DL_UNBIND_REQ); 85 if (reqmp == NULL) 86 return (ENOMEM); 87 88 return (softmac_proto_tx(slp, reqmp, NULL)); 89 } 90 91 int 92 softmac_send_promisc_req(softmac_lower_t *slp, t_uscalar_t level, boolean_t on) 93 { 94 mblk_t *reqmp; 95 size_t size; 96 t_uscalar_t dl_prim; 97 98 /* 99 * create promisc message and send it down 100 */ 101 if (on) { 102 dl_prim = DL_PROMISCON_REQ; 103 size = DL_PROMISCON_REQ_SIZE; 104 } else { 105 dl_prim = DL_PROMISCOFF_REQ; 106 size = DL_PROMISCOFF_REQ_SIZE; 107 } 108 109 reqmp = mexchange(NULL, NULL, size, M_PROTO, dl_prim); 110 if (reqmp == NULL) 111 return (ENOMEM); 112 113 if (on) 114 ((dl_promiscon_req_t *)reqmp->b_rptr)->dl_level = level; 115 else 116 ((dl_promiscoff_req_t *)reqmp->b_rptr)->dl_level = level; 117 118 return (softmac_proto_tx(slp, reqmp, NULL)); 119 } 120 121 int 122 softmac_m_promisc(void *arg, boolean_t on) 123 { 124 softmac_t *softmac = arg; 125 softmac_lower_t *slp = softmac->smac_lower; 126 127 ASSERT(MAC_PERIM_HELD(softmac->smac_mh)); 128 ASSERT(slp != NULL); 129 return (softmac_send_promisc_req(slp, DL_PROMISC_PHYS, on)); 130 } 131 132 int 133 softmac_m_multicst(void *arg, boolean_t add, const uint8_t *mca) 134 { 135 softmac_t *softmac = arg; 136 softmac_lower_t *slp; 137 dl_enabmulti_req_t *enabmulti; 138 dl_disabmulti_req_t *disabmulti; 139 mblk_t *reqmp; 140 t_uscalar_t dl_prim; 141 uint32_t size, addr_length; 142 143 ASSERT(MAC_PERIM_HELD(softmac->smac_mh)); 144 /* 145 * create multicst message and send it down 146 */ 147 addr_length = softmac->smac_addrlen; 148 if (add) { 149 size = sizeof (dl_enabmulti_req_t) + addr_length; 150 dl_prim = DL_ENABMULTI_REQ; 151 } else { 152 size = sizeof (dl_disabmulti_req_t) + addr_length; 153 dl_prim = DL_DISABMULTI_REQ; 154 } 155 156 reqmp = mexchange(NULL, NULL, size, M_PROTO, dl_prim); 157 if (reqmp == NULL) 158 return (ENOMEM); 159 160 if (add) { 161 enabmulti = (dl_enabmulti_req_t *)reqmp->b_rptr; 162 enabmulti->dl_addr_offset = sizeof (dl_enabmulti_req_t); 163 enabmulti->dl_addr_length = addr_length; 164 (void) memcpy(&enabmulti[1], mca, addr_length); 165 } else { 166 disabmulti = (dl_disabmulti_req_t *)reqmp->b_rptr; 167 disabmulti->dl_addr_offset = sizeof (dl_disabmulti_req_t); 168 disabmulti->dl_addr_length = addr_length; 169 (void) memcpy(&disabmulti[1], mca, addr_length); 170 } 171 172 slp = softmac->smac_lower; 173 ASSERT(slp != NULL); 174 return (softmac_proto_tx(slp, reqmp, NULL)); 175 } 176 177 int 178 softmac_m_unicst(void *arg, const uint8_t *macaddr) 179 { 180 softmac_t *softmac = arg; 181 softmac_lower_t *slp; 182 dl_set_phys_addr_req_t *phyaddr; 183 mblk_t *reqmp; 184 size_t size; 185 186 ASSERT(MAC_PERIM_HELD(softmac->smac_mh)); 187 /* 188 * create set_phys_addr message and send it down 189 */ 190 size = DL_SET_PHYS_ADDR_REQ_SIZE + softmac->smac_addrlen; 191 reqmp = mexchange(NULL, NULL, size, M_PROTO, DL_SET_PHYS_ADDR_REQ); 192 if (reqmp == NULL) 193 return (ENOMEM); 194 195 phyaddr = (dl_set_phys_addr_req_t *)reqmp->b_rptr; 196 phyaddr->dl_addr_offset = sizeof (dl_set_phys_addr_req_t); 197 phyaddr->dl_addr_length = softmac->smac_addrlen; 198 (void) memcpy(&phyaddr[1], macaddr, softmac->smac_addrlen); 199 200 slp = softmac->smac_lower; 201 ASSERT(slp != NULL); 202 return (softmac_proto_tx(slp, reqmp, NULL)); 203 } 204 205 void 206 softmac_m_ioctl(void *arg, queue_t *wq, mblk_t *mp) 207 { 208 softmac_lower_t *slp = ((softmac_t *)arg)->smac_lower; 209 mblk_t *ackmp; 210 211 ASSERT(slp != NULL); 212 softmac_ioctl_tx(slp, mp, &ackmp); 213 qreply(wq, ackmp); 214 } 215 216 static void 217 softmac_process_notify_ind(softmac_t *softmac, mblk_t *mp) 218 { 219 dl_notify_ind_t *dlnip = (dl_notify_ind_t *)mp->b_rptr; 220 uint_t addroff, addrlen; 221 222 ASSERT(dlnip->dl_primitive == DL_NOTIFY_IND); 223 224 switch (dlnip->dl_notification) { 225 case DL_NOTE_PHYS_ADDR: 226 if (dlnip->dl_data != DL_CURR_PHYS_ADDR) 227 break; 228 229 addroff = dlnip->dl_addr_offset; 230 addrlen = dlnip->dl_addr_length - softmac->smac_saplen; 231 if (addroff == 0 || addrlen != softmac->smac_addrlen || 232 !MBLKIN(mp, addroff, addrlen)) { 233 cmn_err(CE_NOTE, "softmac: got malformed " 234 "DL_NOTIFY_IND; length/offset %d/%d", 235 addrlen, addroff); 236 break; 237 } 238 239 mac_unicst_update(softmac->smac_mh, mp->b_rptr + addroff); 240 break; 241 242 case DL_NOTE_LINK_UP: 243 mac_link_update(softmac->smac_mh, LINK_STATE_UP); 244 break; 245 246 case DL_NOTE_LINK_DOWN: 247 mac_link_update(softmac->smac_mh, LINK_STATE_DOWN); 248 break; 249 } 250 251 freemsg(mp); 252 } 253 254 void 255 softmac_notify_thread(void *arg) 256 { 257 softmac_t *softmac = arg; 258 callb_cpr_t cprinfo; 259 260 CALLB_CPR_INIT(&cprinfo, &softmac->smac_mutex, callb_generic_cpr, 261 "softmac_notify_thread"); 262 263 mutex_enter(&softmac->smac_mutex); 264 265 /* 266 * Quit the thread if smac_mh is unregistered. 267 */ 268 while (softmac->smac_mh != NULL && 269 !(softmac->smac_flags & SOFTMAC_NOTIFY_QUIT)) { 270 mblk_t *mp, *nextmp; 271 272 if ((mp = softmac->smac_notify_head) == NULL) { 273 CALLB_CPR_SAFE_BEGIN(&cprinfo); 274 cv_wait(&softmac->smac_cv, &softmac->smac_mutex); 275 CALLB_CPR_SAFE_END(&cprinfo, &softmac->smac_mutex); 276 continue; 277 } 278 279 softmac->smac_notify_head = softmac->smac_notify_tail = NULL; 280 mutex_exit(&softmac->smac_mutex); 281 282 while (mp != NULL) { 283 nextmp = mp->b_next; 284 mp->b_next = NULL; 285 softmac_process_notify_ind(softmac, mp); 286 mp = nextmp; 287 } 288 mutex_enter(&softmac->smac_mutex); 289 } 290 291 /* 292 * The softmac is being destroyed, simply free all of the DL_NOTIFY_IND 293 * messages left in the queue which did not have the chance to be 294 * processed. 295 */ 296 freemsgchain(softmac->smac_notify_head); 297 softmac->smac_notify_head = softmac->smac_notify_tail = NULL; 298 softmac->smac_flags |= SOFTMAC_NOTIFY_DONE; 299 cv_broadcast(&softmac->smac_cv); 300 CALLB_CPR_EXIT(&cprinfo); 301 thread_exit(); 302 } 303 304 static void 305 softmac_enqueue_notify_ind(queue_t *rq, mblk_t *mp) 306 { 307 softmac_lower_t *slp = rq->q_ptr; 308 softmac_t *softmac = slp->sl_softmac; 309 310 mutex_enter(&softmac->smac_mutex); 311 if (softmac->smac_notify_tail == NULL) { 312 softmac->smac_notify_head = softmac->smac_notify_tail = mp; 313 } else { 314 softmac->smac_notify_tail->b_next = mp; 315 softmac->smac_notify_tail = mp; 316 } 317 cv_broadcast(&softmac->smac_cv); 318 mutex_exit(&softmac->smac_mutex); 319 } 320 321 static void 322 softmac_process_dlpi(softmac_lower_t *slp, mblk_t *mp, uint_t minlen, 323 t_uscalar_t reqprim) 324 { 325 const char *ackname; 326 327 ackname = dl_primstr(((union DL_primitives *)mp->b_rptr)->dl_primitive); 328 329 if (MBLKL(mp) < minlen) { 330 cmn_err(CE_WARN, "softmac: got short %s", ackname); 331 freemsg(mp); 332 return; 333 } 334 335 mutex_enter(&slp->sl_mutex); 336 if (slp->sl_pending_prim != reqprim) { 337 cmn_err(CE_NOTE, "softmac: got unexpected %s", ackname); 338 mutex_exit(&slp->sl_mutex); 339 freemsg(mp); 340 return; 341 } 342 343 slp->sl_pending_prim = DL_PRIM_INVAL; 344 slp->sl_ack_mp = mp; 345 cv_signal(&slp->sl_cv); 346 mutex_exit(&slp->sl_mutex); 347 } 348 349 void 350 softmac_rput_process_proto(queue_t *rq, mblk_t *mp) 351 { 352 softmac_lower_t *slp = rq->q_ptr; 353 union DL_primitives *dlp = (union DL_primitives *)mp->b_rptr; 354 ssize_t len = MBLKL(mp); 355 const char *primstr; 356 357 if (len < sizeof (t_uscalar_t)) { 358 cmn_err(CE_WARN, "softmac: got runt DLPI message"); 359 goto exit; 360 } 361 362 primstr = dl_primstr(dlp->dl_primitive); 363 364 switch (dlp->dl_primitive) { 365 case DL_OK_ACK: 366 if (len < DL_OK_ACK_SIZE) 367 goto runt; 368 369 softmac_process_dlpi(slp, mp, DL_OK_ACK_SIZE, 370 dlp->ok_ack.dl_correct_primitive); 371 return; 372 373 case DL_ERROR_ACK: 374 if (len < DL_ERROR_ACK_SIZE) 375 goto runt; 376 377 softmac_process_dlpi(slp, mp, DL_ERROR_ACK_SIZE, 378 dlp->error_ack.dl_error_primitive); 379 return; 380 381 case DL_NOTIFY_IND: 382 if (len < DL_NOTIFY_IND_SIZE) 383 goto runt; 384 385 /* 386 * Enqueue all the DL_NOTIFY_IND messages and process them 387 * in another separate thread to avoid deadlock. Here is an 388 * example of the deadlock scenario: 389 * 390 * Thread A: mac_promisc_set()->softmac_m_promisc() 391 * 392 * The softmac driver waits for the ACK of the 393 * DL_PROMISC_PHYS request with the MAC perimeter; 394 * 395 * Thread B: 396 * 397 * The driver handles the DL_PROMISC_PHYS request. Before 398 * it sends back the ACK, it could first send a 399 * DL_NOTE_PROMISC_ON_PHYS notification. 400 * 401 * Since DL_NOTIFY_IND could eventually cause softmac to call 402 * mac_xxx_update(), which requires MAC perimeter, this would 403 * cause deadlock between the two threads. Enqueuing the 404 * DL_NOTIFY_IND message and defer its processing would 405 * avoid the potential deadlock. 406 */ 407 softmac_enqueue_notify_ind(rq, mp); 408 return; 409 410 case DL_NOTIFY_ACK: 411 softmac_process_dlpi(slp, mp, DL_NOTIFY_ACK_SIZE, 412 DL_NOTIFY_REQ); 413 return; 414 415 case DL_CAPABILITY_ACK: 416 softmac_process_dlpi(slp, mp, DL_CAPABILITY_ACK_SIZE, 417 DL_CAPABILITY_REQ); 418 return; 419 420 case DL_BIND_ACK: 421 softmac_process_dlpi(slp, mp, DL_BIND_ACK_SIZE, DL_BIND_REQ); 422 return; 423 424 case DL_CONTROL_ACK: 425 softmac_process_dlpi(slp, mp, DL_CONTROL_ACK_SIZE, 426 DL_CONTROL_REQ); 427 return; 428 429 case DL_UNITDATA_IND: 430 case DL_PHYS_ADDR_ACK: 431 /* 432 * a. Because the stream is in DLIOCRAW mode, 433 * DL_UNITDATA_IND messages are not expected. 434 * b. The lower stream should not receive DL_PHYS_ADDR_REQ, 435 * so DL_PHYS_ADDR_ACK messages are also unexpected. 436 */ 437 default: 438 cmn_err(CE_WARN, "softmac: got unexpected %s", primstr); 439 break; 440 } 441 exit: 442 freemsg(mp); 443 return; 444 runt: 445 cmn_err(CE_WARN, "softmac: got runt %s", primstr); 446 freemsg(mp); 447 } 448 449 void 450 softmac_rput_process_notdata(queue_t *rq, softmac_upper_t *sup, mblk_t *mp) 451 { 452 softmac_lower_t *slp = rq->q_ptr; 453 union DL_primitives *dlp; 454 ssize_t len = MBLKL(mp); 455 456 switch (DB_TYPE(mp)) { 457 case M_PROTO: 458 case M_PCPROTO: 459 /* 460 * If this is a shared-lower-stream, pass it to softmac to 461 * process. 462 */ 463 if (sup == NULL) { 464 softmac_rput_process_proto(rq, mp); 465 break; 466 } 467 468 /* 469 * Dedicated-lower-stream. 470 */ 471 dlp = (union DL_primitives *)mp->b_rptr; 472 ASSERT(len >= sizeof (dlp->dl_primitive)); 473 switch (dlp->dl_primitive) { 474 case DL_OK_ACK: 475 if (len < DL_OK_ACK_SIZE) 476 goto runt; 477 478 /* 479 * If this is a DL_OK_ACK for a DL_UNBIND_REQ, pass it 480 * to softmac to process, otherwise directly pass it to 481 * the upper stream. 482 */ 483 if (dlp->ok_ack.dl_correct_primitive == DL_UNBIND_REQ) { 484 softmac_rput_process_proto(rq, mp); 485 break; 486 } 487 488 putnext(sup->su_rq, mp); 489 break; 490 case DL_ERROR_ACK: 491 if (len < DL_ERROR_ACK_SIZE) 492 goto runt; 493 494 /* 495 * If this is a DL_ERROR_ACK for a DL_UNBIND_REQ, pass 496 * it to softmac to process, otherwise directly pass it 497 * to the upper stream. 498 */ 499 if (dlp->error_ack.dl_error_primitive == 500 DL_UNBIND_REQ) { 501 softmac_rput_process_proto(rq, mp); 502 break; 503 } 504 505 putnext(sup->su_rq, mp); 506 break; 507 case DL_BIND_ACK: 508 case DL_CAPABILITY_ACK: 509 softmac_rput_process_proto(rq, mp); 510 break; 511 default: 512 putnext(sup->su_rq, mp); 513 break; 514 } 515 break; 516 case M_FLUSH: 517 if (*mp->b_rptr & FLUSHR) 518 flushq(rq, FLUSHDATA); 519 if (*mp->b_rptr & FLUSHW) 520 flushq(OTHERQ(rq), FLUSHDATA); 521 putnext(rq, mp); 522 break; 523 524 case M_IOCACK: 525 case M_IOCNAK: 526 case M_COPYIN: 527 case M_COPYOUT: 528 if (sup != NULL) { 529 putnext(sup->su_rq, mp); 530 break; 531 } 532 533 mutex_enter(&slp->sl_mutex); 534 if (!slp->sl_pending_ioctl) { 535 mutex_exit(&slp->sl_mutex); 536 cmn_err(CE_NOTE, "softmac: got unexpected mblk " 537 "type 0x%x", DB_TYPE(mp)); 538 freemsg(mp); 539 break; 540 } 541 542 slp->sl_pending_ioctl = B_FALSE; 543 slp->sl_ack_mp = mp; 544 cv_broadcast(&slp->sl_cv); 545 mutex_exit(&slp->sl_mutex); 546 break; 547 548 default: 549 cmn_err(CE_NOTE, "softmac: got unsupported mblk type 0x%x", 550 DB_TYPE(mp)); 551 freemsg(mp); 552 break; 553 } 554 return; 555 runt: 556 cmn_err(CE_WARN, "softmac: got runt %s", dl_primstr(dlp->dl_primitive)); 557 freemsg(mp); 558 } 559