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/strsubr.h> 27 #include <inet/led.h> 28 #include <sys/softmac_impl.h> 29 30 /* 31 * Macro to check whether the write-queue of the lower stream is full. 32 * 33 * Because softmac is pushed right above the underlying device and 34 * _I_INSERT/_I_REMOVE is not processed in the lower stream, it is 35 * safe to directly access the q_next pointer. 36 */ 37 #define CANPUTNEXT(q) \ 38 (!((q)->q_next->q_nfsrv->q_flag & QFULL) || canput((q)->q_next)) 39 40 mblk_t * 41 softmac_m_tx(void *arg, mblk_t *mp) 42 { 43 queue_t *wq = ((softmac_t *)arg)->smac_lower->sl_wq; 44 45 /* 46 * Optimize for the most common case. 47 */ 48 if (mp->b_next == NULL) { 49 if (!CANPUTNEXT(wq)) 50 return (mp); 51 52 mp->b_flag |= MSGNOLOOP; 53 putnext(wq, mp); 54 return (NULL); 55 } 56 57 while (mp != NULL) { 58 mblk_t *next = mp->b_next; 59 60 if (!CANPUTNEXT(wq)) 61 break; 62 mp->b_next = NULL; 63 mp->b_flag |= MSGNOLOOP; 64 putnext(wq, mp); 65 mp = next; 66 } 67 return (mp); 68 } 69 70 71 void 72 softmac_rput_process_data(softmac_lower_t *slp, mblk_t *mp) 73 { 74 /* 75 * When packets arrive, the softmac might not be fully started. 76 */ 77 ASSERT((slp->sl_softmac != NULL)); 78 ASSERT((mp->b_next == NULL) && (mp->b_prev == NULL)); 79 80 if (DB_REF(mp) > 1) { 81 mblk_t *tmp; 82 uint32_t start, stuff, end, value, flags; 83 84 if ((tmp = copymsg(mp)) == NULL) { 85 cmn_err(CE_WARN, "softmac_rput_process_data: " 86 "copymsg failed"); 87 goto failed; 88 } 89 hcksum_retrieve(mp, NULL, NULL, &start, &stuff, &end, 90 &value, &flags); 91 VERIFY(hcksum_assoc(tmp, NULL, NULL, start, stuff, end, 92 value, flags, KM_NOSLEEP) == 0); 93 freemsg(mp); 94 mp = tmp; 95 } 96 97 mac_rx(slp->sl_softmac->smac_mh, NULL, mp); 98 return; 99 100 failed: 101 freemsg(mp); 102 } 103 104 #define ACKTIMEOUT (10 * hz) 105 106 /* 107 * Serialize control message processing. 108 */ 109 static void 110 softmac_serialize_enter(softmac_lower_t *slp) 111 { 112 mutex_enter(&slp->sl_ctl_mutex); 113 while (slp->sl_ctl_inprogress) 114 cv_wait(&slp->sl_ctl_cv, &slp->sl_ctl_mutex); 115 116 ASSERT(!slp->sl_ctl_inprogress); 117 ASSERT(!slp->sl_pending_ioctl); 118 ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL); 119 120 slp->sl_ctl_inprogress = B_TRUE; 121 mutex_exit(&slp->sl_ctl_mutex); 122 } 123 124 static void 125 softmac_serialize_exit(softmac_lower_t *slp) 126 { 127 mutex_enter(&slp->sl_ctl_mutex); 128 129 ASSERT(slp->sl_ctl_inprogress); 130 ASSERT(!slp->sl_pending_ioctl); 131 ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL); 132 133 slp->sl_ctl_inprogress = B_FALSE; 134 cv_broadcast(&slp->sl_ctl_cv); 135 mutex_exit(&slp->sl_ctl_mutex); 136 } 137 138 static int 139 dlpi_get_errno(t_uscalar_t error, t_uscalar_t unix_errno) 140 { 141 return (error == DL_SYSERR ? unix_errno : EINVAL); 142 } 143 144 static int 145 softmac_output(softmac_lower_t *slp, mblk_t *mp, t_uscalar_t dl_prim, 146 t_uscalar_t ack, mblk_t **mpp) 147 { 148 union DL_primitives *dlp; 149 int err = 0; 150 151 softmac_serialize_enter(slp); 152 153 /* 154 * Record the pending DLPI primitive. 155 */ 156 mutex_enter(&slp->sl_mutex); 157 slp->sl_pending_prim = dl_prim; 158 mutex_exit(&slp->sl_mutex); 159 160 putnext(slp->sl_wq, mp); 161 162 mutex_enter(&slp->sl_mutex); 163 while (slp->sl_pending_prim != DL_PRIM_INVAL) { 164 if (cv_timedwait(&slp->sl_cv, &slp->sl_mutex, 165 lbolt + ACKTIMEOUT) == -1) 166 break; 167 } 168 169 mp = slp->sl_ack_mp; 170 slp->sl_ack_mp = NULL; 171 172 /* 173 * If we timed out, sl_ack_mp will still be NULL, but sl_pending_prim 174 * won't be set to DL_PRIM_INVAL. 175 */ 176 ASSERT(mp != NULL || slp->sl_pending_prim != DL_PRIM_INVAL); 177 178 slp->sl_pending_prim = DL_PRIM_INVAL; 179 mutex_exit(&slp->sl_mutex); 180 181 if (mp != NULL) { 182 dlp = (union DL_primitives *)mp->b_rptr; 183 184 if (dlp->dl_primitive == DL_ERROR_ACK) { 185 err = dlpi_get_errno(dlp->error_ack.dl_errno, 186 dlp->error_ack.dl_unix_errno); 187 } else { 188 ASSERT(dlp->dl_primitive == ack); 189 } 190 } else { 191 err = ENOMSG; 192 } 193 194 if (mpp != NULL) 195 *mpp = mp; 196 else 197 freemsg(mp); 198 199 softmac_serialize_exit(slp); 200 return (err); 201 } 202 203 void 204 softmac_ioctl_tx(softmac_lower_t *slp, mblk_t *mp, mblk_t **mpp) 205 { 206 softmac_serialize_enter(slp); 207 208 /* 209 * Record that ioctl processing is currently in progress. 210 */ 211 mutex_enter(&slp->sl_mutex); 212 slp->sl_pending_ioctl = B_TRUE; 213 mutex_exit(&slp->sl_mutex); 214 215 putnext(slp->sl_wq, mp); 216 217 mutex_enter(&slp->sl_mutex); 218 while (slp->sl_pending_ioctl) 219 cv_wait(&slp->sl_cv, &slp->sl_mutex); 220 mp = slp->sl_ack_mp; 221 slp->sl_ack_mp = NULL; 222 mutex_exit(&slp->sl_mutex); 223 224 ASSERT(mpp != NULL && mp != NULL); 225 *mpp = mp; 226 227 softmac_serialize_exit(slp); 228 } 229 230 static int 231 softmac_mexchange_error_ack(mblk_t **mpp, t_uscalar_t error_primitive, 232 t_uscalar_t error, t_uscalar_t unix_errno) 233 { 234 union DL_primitives *dlp; 235 236 if ((*mpp = mexchange(NULL, *mpp, sizeof (dl_error_ack_t), M_PCPROTO, 237 DL_ERROR_ACK)) == NULL) 238 return (ENOMEM); 239 240 dlp = (union DL_primitives *)(*mpp)->b_rptr; 241 dlp->error_ack.dl_error_primitive = error_primitive; 242 dlp->error_ack.dl_errno = error; 243 dlp->error_ack.dl_unix_errno = unix_errno; 244 245 return (0); 246 } 247 248 int 249 softmac_proto_tx(softmac_lower_t *slp, mblk_t *mp, mblk_t **mpp) 250 { 251 int err = 0; 252 t_uscalar_t dl_prim; 253 254 dl_prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive; 255 256 ASSERT(slp->sl_softmac != NULL); 257 258 switch (dl_prim) { 259 case DL_ENABMULTI_REQ: 260 case DL_DISABMULTI_REQ: 261 case DL_SET_PHYS_ADDR_REQ: 262 case DL_UNBIND_REQ: 263 case DL_UDQOS_REQ: 264 case DL_PROMISCON_REQ: 265 case DL_PROMISCOFF_REQ: 266 err = softmac_output(slp, mp, dl_prim, DL_OK_ACK, mpp); 267 break; 268 case DL_BIND_REQ: 269 err = softmac_output(slp, mp, dl_prim, DL_BIND_ACK, mpp); 270 break; 271 case DL_NOTIFY_REQ: 272 err = softmac_output(slp, mp, dl_prim, DL_NOTIFY_ACK, mpp); 273 break; 274 case DL_CONTROL_REQ: 275 err = softmac_output(slp, mp, dl_prim, DL_CONTROL_ACK, mpp); 276 break; 277 case DL_CAPABILITY_REQ: 278 err = softmac_output(slp, mp, dl_prim, DL_CAPABILITY_ACK, mpp); 279 break; 280 default: 281 if (mpp != NULL) { 282 *mpp = mp; 283 err = softmac_mexchange_error_ack(mpp, dl_prim, 284 DL_UNSUPPORTED, 0); 285 } 286 break; 287 } 288 return (err); 289 } 290