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