xref: /illumos-gate/usr/src/uts/common/io/softmac/softmac_pkt.c (revision c211fc479225fa54805cf480633bf6689ca9a2db)
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