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