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