xref: /illumos-gate/usr/src/uts/common/io/softmac/softmac_pkt.c (revision a73c0fe4e90b82a478f821ef3adb5cf34f6a9346)
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/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 
83 		if ((tmp = copymsg(mp)) == NULL) {
84 			cmn_err(CE_WARN, "softmac_rput_process_data: "
85 			    "copymsg failed");
86 			goto failed;
87 		}
88 		freemsg(mp);
89 		mp = tmp;
90 	}
91 
92 	mac_rx(slp->sl_softmac->smac_mh, NULL, mp);
93 	return;
94 
95 failed:
96 	freemsg(mp);
97 }
98 
99 #define	ACKTIMEOUT	(10 * hz)
100 
101 /*
102  * Serialize control message processing.
103  */
104 static void
105 softmac_serialize_enter(softmac_lower_t *slp)
106 {
107 	mutex_enter(&slp->sl_ctl_mutex);
108 	while (slp->sl_ctl_inprogress)
109 		cv_wait(&slp->sl_ctl_cv, &slp->sl_ctl_mutex);
110 
111 	ASSERT(!slp->sl_ctl_inprogress);
112 	ASSERT(!slp->sl_pending_ioctl);
113 	ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL);
114 
115 	slp->sl_ctl_inprogress = B_TRUE;
116 	mutex_exit(&slp->sl_ctl_mutex);
117 }
118 
119 static void
120 softmac_serialize_exit(softmac_lower_t *slp)
121 {
122 	mutex_enter(&slp->sl_ctl_mutex);
123 
124 	ASSERT(slp->sl_ctl_inprogress);
125 	ASSERT(!slp->sl_pending_ioctl);
126 	ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL);
127 
128 	slp->sl_ctl_inprogress = B_FALSE;
129 	cv_broadcast(&slp->sl_ctl_cv);
130 	mutex_exit(&slp->sl_ctl_mutex);
131 }
132 
133 static int
134 dlpi_get_errno(t_uscalar_t error, t_uscalar_t unix_errno)
135 {
136 	return (error == DL_SYSERR ? unix_errno : EINVAL);
137 }
138 
139 static int
140 softmac_output(softmac_lower_t *slp, mblk_t *mp, t_uscalar_t dl_prim,
141     t_uscalar_t ack, mblk_t **mpp)
142 {
143 	union DL_primitives	*dlp;
144 	int			err = 0;
145 
146 	softmac_serialize_enter(slp);
147 
148 	/*
149 	 * Record the pending DLPI primitive.
150 	 */
151 	mutex_enter(&slp->sl_mutex);
152 	slp->sl_pending_prim = dl_prim;
153 	mutex_exit(&slp->sl_mutex);
154 
155 	putnext(slp->sl_wq, mp);
156 
157 	mutex_enter(&slp->sl_mutex);
158 	while (slp->sl_pending_prim != DL_PRIM_INVAL) {
159 		if (cv_timedwait(&slp->sl_cv, &slp->sl_mutex,
160 		    lbolt + ACKTIMEOUT) == -1)
161 			break;
162 	}
163 
164 	mp = slp->sl_ack_mp;
165 	slp->sl_ack_mp = NULL;
166 
167 	/*
168 	 * If we timed out, sl_ack_mp will still be NULL, but sl_pending_prim
169 	 * won't be set to DL_PRIM_INVAL.
170 	 */
171 	ASSERT(mp != NULL || slp->sl_pending_prim != DL_PRIM_INVAL);
172 
173 	slp->sl_pending_prim = DL_PRIM_INVAL;
174 	mutex_exit(&slp->sl_mutex);
175 
176 	if (mp != NULL) {
177 		dlp = (union DL_primitives *)mp->b_rptr;
178 
179 		if (dlp->dl_primitive == DL_ERROR_ACK) {
180 			err = dlpi_get_errno(dlp->error_ack.dl_errno,
181 			    dlp->error_ack.dl_unix_errno);
182 		} else {
183 			ASSERT(dlp->dl_primitive == ack);
184 		}
185 	} else {
186 		err = ENOMSG;
187 	}
188 
189 	if (mpp != NULL)
190 		*mpp = mp;
191 	else
192 		freemsg(mp);
193 
194 	softmac_serialize_exit(slp);
195 	return (err);
196 }
197 
198 void
199 softmac_ioctl_tx(softmac_lower_t *slp, mblk_t *mp, mblk_t **mpp)
200 {
201 	softmac_serialize_enter(slp);
202 
203 	/*
204 	 * Record that ioctl processing is currently in progress.
205 	 */
206 	mutex_enter(&slp->sl_mutex);
207 	slp->sl_pending_ioctl = B_TRUE;
208 	mutex_exit(&slp->sl_mutex);
209 
210 	putnext(slp->sl_wq, mp);
211 
212 	mutex_enter(&slp->sl_mutex);
213 	while (slp->sl_pending_ioctl)
214 		cv_wait(&slp->sl_cv, &slp->sl_mutex);
215 	mp = slp->sl_ack_mp;
216 	slp->sl_ack_mp = NULL;
217 	mutex_exit(&slp->sl_mutex);
218 
219 	ASSERT(mpp != NULL && mp != NULL);
220 	*mpp = mp;
221 
222 	softmac_serialize_exit(slp);
223 }
224 
225 static int
226 softmac_mexchange_error_ack(mblk_t **mpp, t_uscalar_t error_primitive,
227 	t_uscalar_t error, t_uscalar_t unix_errno)
228 {
229 	union DL_primitives *dlp;
230 
231 	if ((*mpp = mexchange(NULL, *mpp, sizeof (dl_error_ack_t), M_PCPROTO,
232 	    DL_ERROR_ACK)) == NULL)
233 		return (ENOMEM);
234 
235 	dlp = (union DL_primitives *)(*mpp)->b_rptr;
236 	dlp->error_ack.dl_error_primitive = error_primitive;
237 	dlp->error_ack.dl_errno = error;
238 	dlp->error_ack.dl_unix_errno = unix_errno;
239 
240 	return (0);
241 }
242 
243 int
244 softmac_proto_tx(softmac_lower_t *slp, mblk_t *mp, mblk_t **mpp)
245 {
246 	int err = 0;
247 	t_uscalar_t dl_prim;
248 
249 	dl_prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
250 
251 	ASSERT(slp->sl_softmac != NULL);
252 
253 	switch (dl_prim) {
254 	case DL_ENABMULTI_REQ:
255 	case DL_DISABMULTI_REQ:
256 	case DL_SET_PHYS_ADDR_REQ:
257 	case DL_UNBIND_REQ:
258 	case DL_UDQOS_REQ:
259 	case DL_PROMISCON_REQ:
260 	case DL_PROMISCOFF_REQ:
261 		err = softmac_output(slp, mp, dl_prim, DL_OK_ACK, mpp);
262 		break;
263 	case DL_BIND_REQ:
264 		err = softmac_output(slp, mp, dl_prim, DL_BIND_ACK, mpp);
265 		break;
266 	case DL_NOTIFY_REQ:
267 		err = softmac_output(slp, mp, dl_prim, DL_NOTIFY_ACK, mpp);
268 		break;
269 	case DL_CONTROL_REQ:
270 		err = softmac_output(slp, mp, dl_prim, DL_CONTROL_ACK, mpp);
271 		break;
272 	case DL_CAPABILITY_REQ:
273 		err = softmac_output(slp, mp, dl_prim, DL_CAPABILITY_ACK, mpp);
274 		break;
275 	default:
276 		if (mpp != NULL) {
277 			*mpp = mp;
278 			err = softmac_mexchange_error_ack(mpp, dl_prim,
279 			    DL_UNSUPPORTED, 0);
280 		}
281 		break;
282 	}
283 	return (err);
284 }
285