xref: /illumos-gate/usr/src/uts/common/io/softmac/softmac_capab.c (revision 55d6cb5d63bcf69dfa47b8c41c770a2d34f169b0)
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/types.h>
29 #include <sys/mac.h>
30 #include <sys/softmac_impl.h>
31 
32 typedef struct softmac_capab_ops {
33 	int	(*sc_hcksum_ack)(void *, t_uscalar_t);
34 	int	(*sc_zcopy_ack)(void *, t_uscalar_t);
35 	int	(*sc_mdt_ack)(void *, dl_capab_mdt_t *);
36 } softmac_capab_ops_t;
37 
38 static int	dl_capab(ldi_handle_t, mblk_t **);
39 static int	softmac_fill_hcksum_ack(void *, t_uscalar_t);
40 static int	softmac_fill_zcopy_ack(void *, t_uscalar_t);
41 static int	softmac_fill_mdt_ack(void *, dl_capab_mdt_t *);
42 static int	softmac_adv_hcksum_ack(void *, t_uscalar_t);
43 static int	softmac_adv_zcopy_ack(void *, t_uscalar_t);
44 static int	softmac_adv_mdt_ack(void *, dl_capab_mdt_t *);
45 static int	softmac_enable_hcksum_ack(void *, t_uscalar_t);
46 static int	softmac_enable_mdt_ack(void *, dl_capab_mdt_t *);
47 static int	softmac_capab_send(softmac_lower_t *, boolean_t);
48 static int	i_capab_ack(mblk_t *, queue_t *, softmac_capab_ops_t *, void *);
49 static int	i_capab_id_ack(mblk_t *, dl_capability_sub_t *, queue_t *,
50     softmac_capab_ops_t *, void *);
51 static int	i_capab_sub_ack(mblk_t *, dl_capability_sub_t *, queue_t *,
52     softmac_capab_ops_t *, void *);
53 static int	i_capab_hcksum_ack(dl_capab_hcksum_t *, queue_t *,
54     softmac_capab_ops_t *, void *);
55 static int	i_capab_zcopy_ack(dl_capab_zerocopy_t *, queue_t *,
56     softmac_capab_ops_t *, void *);
57 static int	i_capab_mdt_ack(dl_capab_mdt_t *, queue_t *,
58     softmac_capab_ops_t *, void *);
59 static int	i_capab_hcksum_verify(dl_capab_hcksum_t *, queue_t *);
60 static int	i_capab_zcopy_verify(dl_capab_zerocopy_t *, queue_t *);
61 static int	i_capab_mdt_verify(dl_capab_mdt_t *, queue_t *);
62 
63 static softmac_capab_ops_t softmac_fill_capab_ops =
64 {
65 	softmac_fill_hcksum_ack,
66 	softmac_fill_zcopy_ack,
67 	softmac_fill_mdt_ack,
68 };
69 
70 static softmac_capab_ops_t softmac_adv_capab_ops =
71 {
72 	softmac_adv_hcksum_ack,
73 	softmac_adv_zcopy_ack,
74 	softmac_adv_mdt_ack
75 };
76 
77 static softmac_capab_ops_t softmac_enable_capab_ops =
78 {
79 	softmac_enable_hcksum_ack,
80 	NULL,
81 	softmac_enable_mdt_ack
82 };
83 
84 int
85 softmac_fill_capab(ldi_handle_t lh, softmac_t *softmac)
86 {
87 	mblk_t			*mp = NULL;
88 	union DL_primitives	*prim;
89 	int			err = 0;
90 
91 	if ((err = dl_capab(lh, &mp)) != 0)
92 		goto exit;
93 
94 	prim = (union DL_primitives *)mp->b_rptr;
95 	if (prim->dl_primitive == DL_ERROR_ACK) {
96 		err = -1;
97 		goto exit;
98 	}
99 
100 	err = i_capab_ack(mp, NULL, &softmac_fill_capab_ops, softmac);
101 
102 exit:
103 	freemsg(mp);
104 	return (err);
105 }
106 
107 static int
108 dl_capab(ldi_handle_t lh, mblk_t **mpp)
109 {
110 	dl_capability_req_t	*capb;
111 	union DL_primitives	*dl_prim;
112 	mblk_t			*mp;
113 	int			err;
114 
115 	if ((mp = allocb(sizeof (dl_capability_req_t), BPRI_MED)) == NULL)
116 		return (ENOMEM);
117 	mp->b_datap->db_type = M_PROTO;
118 
119 	capb = (dl_capability_req_t *)mp->b_wptr;
120 	mp->b_wptr += sizeof (dl_capability_req_t);
121 	bzero(mp->b_rptr, sizeof (dl_capability_req_t));
122 	capb->dl_primitive = DL_CAPABILITY_REQ;
123 
124 	(void) ldi_putmsg(lh, mp);
125 	if ((err = ldi_getmsg(lh, &mp, (timestruc_t *)NULL)) != 0)
126 		return (err);
127 
128 	dl_prim = (union DL_primitives *)mp->b_rptr;
129 	switch (dl_prim->dl_primitive) {
130 	case DL_CAPABILITY_ACK:
131 		if (MBLKL(mp) < DL_CAPABILITY_ACK_SIZE) {
132 			printf("dl_capability: DL_CAPABILITY_ACK "
133 			    "protocol err\n");
134 			break;
135 		}
136 		*mpp = mp;
137 		return (0);
138 
139 	case DL_ERROR_ACK:
140 		if (MBLKL(mp) < DL_ERROR_ACK_SIZE) {
141 			printf("dl_capability: DL_ERROR_ACK protocol err\n");
142 			break;
143 		}
144 		if (((dl_error_ack_t *)dl_prim)->dl_error_primitive !=
145 		    DL_CAPABILITY_REQ) {
146 			printf("dl_capability: DL_ERROR_ACK rtnd prim %u\n",
147 			    ((dl_error_ack_t *)dl_prim)->dl_error_primitive);
148 			break;
149 		}
150 
151 		*mpp = mp;
152 		return (0);
153 
154 	default:
155 		printf("dl_capability: bad ACK header %u\n",
156 		    dl_prim->dl_primitive);
157 		break;
158 	}
159 
160 	freemsg(mp);
161 	return (-1);
162 }
163 
164 static int
165 softmac_fill_hcksum_ack(void *arg, t_uscalar_t flags)
166 {
167 	softmac_t	*softmac = (softmac_t *)arg;
168 
169 	/*
170 	 * There are two types of acks we process here:
171 	 * 1. acks in reply to a (first form) generic capability req
172 	 *    (no ENABLE flag set)
173 	 * 2. acks in reply to a ENABLE capability req.
174 	 *    (ENABLE flag set)
175 	 * Only the first type should be expected here.
176 	 */
177 
178 	if (flags & HCKSUM_ENABLE) {
179 		cmn_err(CE_WARN, "softmac_fill_hcksum_ack: unexpected "
180 		    "HCKSUM_ENABLE flag in hardware checksum capability");
181 	} else if (flags & (HCKSUM_INET_PARTIAL | HCKSUM_INET_FULL_V4 |
182 	    HCKSUM_INET_FULL_V6 | HCKSUM_IPHDRCKSUM)) {
183 		softmac->smac_capab_flags |= MAC_CAPAB_HCKSUM;
184 		softmac->smac_hcksum_txflags = flags;
185 	}
186 	return (0);
187 }
188 
189 static int
190 softmac_fill_zcopy_ack(void *arg, t_uscalar_t flags)
191 {
192 	softmac_t	*softmac = (softmac_t *)arg;
193 
194 	ASSERT(flags == DL_CAPAB_VMSAFE_MEM);
195 	softmac->smac_capab_flags &= (~MAC_CAPAB_NO_ZCOPY);
196 	return (0);
197 }
198 
199 static int
200 softmac_fill_mdt_ack(void *arg, dl_capab_mdt_t *mdt)
201 {
202 	softmac_t *softmac = (softmac_t *)arg;
203 
204 	/*
205 	 * There are two types of acks we process here:
206 	 * 1. acks in reply to a (first form) generic capability req
207 	 *    (ENABLE flag might be set by some drivers)
208 	 * 2. acks in reply to a ENABLE capability req.
209 	 *    (ENABLE flag set)
210 	 */
211 
212 	ASSERT(mdt->mdt_version == MDT_VERSION_2);
213 	softmac->smac_mdt = B_TRUE;
214 	softmac->smac_mdt_capab.mdt_hdr_head = mdt->mdt_hdr_head;
215 	softmac->smac_mdt_capab.mdt_hdr_tail = mdt->mdt_hdr_tail;
216 	softmac->smac_mdt_capab.mdt_max_pld = mdt->mdt_max_pld;
217 	softmac->smac_mdt_capab.mdt_span_limit = mdt->mdt_span_limit;
218 	return (0);
219 }
220 
221 int
222 softmac_capab_enable(softmac_lower_t *slp)
223 {
224 	softmac_t	*softmac = slp->sl_softmac;
225 	int		err;
226 
227 	if (softmac->smac_no_capability_req)
228 		return (0);
229 
230 	/*
231 	 * Send DL_CAPABILITY_REQ to get capability advertisement.
232 	 */
233 	if ((err = softmac_capab_send(slp, B_FALSE)) != 0)
234 		return (err);
235 
236 	/*
237 	 * Send DL_CAPABILITY_REQ to enable specific capabilities.
238 	 */
239 	if ((err = softmac_capab_send(slp, B_TRUE)) != 0)
240 		return (err);
241 
242 	return (0);
243 }
244 
245 static int
246 softmac_capab_send(softmac_lower_t *slp, boolean_t enable)
247 {
248 	softmac_t		*softmac;
249 	dl_capability_req_t	*capb;
250 	dl_capability_sub_t	*subcapb;
251 	mblk_t			*reqmp, *ackmp;
252 	int			err;
253 	size_t			size = 0;
254 
255 	softmac = slp->sl_softmac;
256 
257 	if (enable) {
258 		/* No need to enable DL_CAPAB_ZEROCOPY */
259 		if (softmac->smac_capab_flags & MAC_CAPAB_HCKSUM)
260 			size += sizeof (dl_capability_sub_t) +
261 			    sizeof (dl_capab_hcksum_t);
262 
263 		if (softmac->smac_mdt) {
264 			if (!(softmac->smac_mdt_capab.mdt_flags &
265 			    DL_CAPAB_MDT_ENABLE)) {
266 				/*
267 				 * The MDT capability was not enabled for the
268 				 * first time, enable it now.
269 				 */
270 				size += sizeof (dl_capability_sub_t) +
271 				    sizeof (dl_capab_mdt_t);
272 			}
273 		}
274 
275 		if (size == 0)
276 			return (0);
277 	}
278 
279 	/*
280 	 * Create DL_CAPABILITY_REQ message and send it down
281 	 */
282 	reqmp = allocb(sizeof (dl_capability_req_t) + size, BPRI_MED);
283 	if (reqmp == NULL)
284 		return (ENOMEM);
285 
286 	bzero(reqmp->b_rptr, sizeof (dl_capability_req_t) + size);
287 
288 	DB_TYPE(reqmp) = M_PROTO;
289 	reqmp->b_wptr = reqmp->b_rptr + sizeof (dl_capability_req_t) + size;
290 
291 	capb = (dl_capability_req_t *)reqmp->b_rptr;
292 	capb->dl_primitive = DL_CAPABILITY_REQ;
293 
294 	if (!enable)
295 		goto output;
296 
297 	capb->dl_sub_offset = sizeof (dl_capability_req_t);
298 
299 	if (softmac->smac_capab_flags & MAC_CAPAB_HCKSUM) {
300 		dl_capab_hcksum_t *hck_subcapp;
301 
302 		size = sizeof (dl_capability_sub_t) +
303 		    sizeof (dl_capab_hcksum_t);
304 		capb->dl_sub_length += size;
305 
306 		subcapb = (dl_capability_sub_t *)(capb + 1);
307 		subcapb->dl_cap = DL_CAPAB_HCKSUM;
308 		subcapb->dl_length = sizeof (dl_capab_hcksum_t);
309 		hck_subcapp = (dl_capab_hcksum_t *)(subcapb + 1);
310 		hck_subcapp->hcksum_version = HCKSUM_VERSION_1;
311 		hck_subcapp->hcksum_txflags =
312 		    softmac->smac_hcksum_txflags | HCKSUM_ENABLE;
313 	}
314 
315 	if (softmac->smac_mdt) {
316 		if (!(softmac->smac_mdt_capab.mdt_flags &
317 		    DL_CAPAB_MDT_ENABLE)) {
318 			dl_capab_mdt_t *mdt_subcapp;
319 
320 			size = sizeof (dl_capability_sub_t) +
321 			    sizeof (dl_capab_mdt_t);
322 			capb->dl_sub_length += size;
323 
324 			subcapb = (dl_capability_sub_t *)
325 			    ((uint8_t *)(subcapb + 1) + subcapb->dl_length);
326 
327 			subcapb->dl_cap = DL_CAPAB_MDT;
328 			subcapb->dl_length = sizeof (dl_capab_mdt_t);
329 			mdt_subcapp = (dl_capab_mdt_t *)(subcapb + 1);
330 			mdt_subcapp->mdt_version = MDT_VERSION_2;
331 			mdt_subcapp->mdt_flags =
332 			    (softmac->smac_mdt_capab.mdt_flags |
333 			    DL_CAPAB_MDT_ENABLE);
334 			mdt_subcapp->mdt_hdr_head =
335 			    softmac->smac_mdt_capab.mdt_hdr_head;
336 			mdt_subcapp->mdt_hdr_tail =
337 			    softmac->smac_mdt_capab.mdt_hdr_tail;
338 			mdt_subcapp->mdt_max_pld =
339 			    softmac->smac_mdt_capab.mdt_max_pld;
340 			mdt_subcapp->mdt_span_limit =
341 			    softmac->smac_mdt_capab.mdt_span_limit;
342 		}
343 	}
344 
345 output:
346 	err = softmac_proto_tx(slp, reqmp, &ackmp);
347 	if (err == 0) {
348 		if (enable) {
349 			err = i_capab_ack(ackmp, NULL,
350 			    &softmac_enable_capab_ops, softmac);
351 		} else {
352 			err = i_capab_ack(ackmp, NULL,
353 			    &softmac_adv_capab_ops, softmac);
354 		}
355 	}
356 	freemsg(ackmp);
357 
358 	return (err);
359 }
360 
361 static int
362 softmac_adv_hcksum_ack(void *arg, t_uscalar_t flags)
363 {
364 	softmac_t	*softmac = (softmac_t *)arg;
365 
366 	/*
367 	 * There are two types of acks we process here:
368 	 * 1. acks in reply to a (first form) generic capability req
369 	 *    (no ENABLE flag set)
370 	 * 2. acks in reply to a ENABLE capability req.
371 	 *    (ENABLE flag set)
372 	 * Only the first type should be expected here.
373 	 */
374 
375 	if (flags & HCKSUM_ENABLE) {
376 		cmn_err(CE_WARN, "softmac_adv_hcksum_ack: unexpected "
377 		    "HCKSUM_ENABLE flag in hardware checksum capability");
378 		return (-1);
379 	} else if (flags & (HCKSUM_INET_PARTIAL | HCKSUM_INET_FULL_V4 |
380 	    HCKSUM_INET_FULL_V6 | HCKSUM_IPHDRCKSUM)) {
381 		/*
382 		 * The acknowledgement should be the same as we got when
383 		 * the softmac is created.
384 		 */
385 		if (!(softmac->smac_capab_flags & MAC_CAPAB_HCKSUM)) {
386 			ASSERT(B_FALSE);
387 			return (-1);
388 		}
389 		if (softmac->smac_hcksum_txflags != flags) {
390 			ASSERT(B_FALSE);
391 			return (-1);
392 		}
393 	}
394 
395 	return (0);
396 }
397 
398 static int
399 softmac_adv_zcopy_ack(void *arg, t_uscalar_t flags)
400 {
401 	softmac_t	*softmac = (softmac_t *)arg;
402 
403 	/*
404 	 * The acknowledgement should be the same as we got when
405 	 * the softmac is created.
406 	 */
407 	ASSERT(flags == DL_CAPAB_VMSAFE_MEM);
408 	if (softmac->smac_capab_flags & MAC_CAPAB_NO_ZCOPY) {
409 		ASSERT(B_FALSE);
410 		return (-1);
411 	}
412 
413 	return (0);
414 }
415 
416 static int
417 softmac_adv_mdt_ack(void *arg, dl_capab_mdt_t *mdt)
418 {
419 	softmac_t *softmac = (softmac_t *)arg;
420 
421 	/*
422 	 * The acknowledgement should be the same as we got when
423 	 * the softmac is created.
424 	 */
425 	if (!softmac->smac_mdt) {
426 		ASSERT(B_FALSE);
427 		return (-1);
428 	}
429 
430 	if ((softmac->smac_mdt_capab.mdt_hdr_head != mdt->mdt_hdr_head) ||
431 	    (softmac->smac_mdt_capab.mdt_hdr_tail != mdt->mdt_hdr_tail) ||
432 	    (softmac->smac_mdt_capab.mdt_max_pld != mdt->mdt_max_pld) ||
433 	    (softmac->smac_mdt_capab.mdt_span_limit != mdt->mdt_span_limit)) {
434 		ASSERT(B_FALSE);
435 		return (-1);
436 	}
437 	/*
438 	 * We need the mdt_flags field to know whether an additional
439 	 * DL_CAPAB_MDT_ENABLE is necessary.
440 	 */
441 	softmac->smac_mdt_capab.mdt_flags = mdt->mdt_flags;
442 	return (0);
443 }
444 
445 static int
446 softmac_enable_hcksum_ack(void *arg, t_uscalar_t flags)
447 {
448 	softmac_t	*softmac = (softmac_t *)arg;
449 
450 	/*
451 	 * There are two types of acks we process here:
452 	 * 1. acks in reply to a (first form) generic capability req
453 	 *    (no ENABLE flag set)
454 	 * 2. acks in reply to a ENABLE capability req.
455 	 *    (ENABLE flag set)
456 	 * Only the second type should be expected here.
457 	 */
458 
459 	if (flags & HCKSUM_ENABLE) {
460 		if ((flags & ~HCKSUM_ENABLE) != softmac->smac_hcksum_txflags) {
461 			cmn_err(CE_WARN, "softmac_enable_hcksum_ack: unexpected"
462 			    " hardware capability flag value 0x%x", flags);
463 			return (-1);
464 		}
465 	} else {
466 		cmn_err(CE_WARN, "softmac_enable_hcksum_ack: "
467 		    "hardware checksum flag HCKSUM_ENABLE is not set");
468 		return (-1);
469 	}
470 
471 	return (0);
472 }
473 
474 static int
475 softmac_enable_mdt_ack(void *arg, dl_capab_mdt_t *mdt)
476 {
477 	softmac_t	*softmac = (softmac_t *)arg;
478 
479 	/*
480 	 * There are two types of acks we process here:
481 	 * 1. acks in reply to a (first form) generic capability req
482 	 *    (no ENABLE flag set)
483 	 * 2. acks in reply to a ENABLE capability req.
484 	 *    (ENABLE flag set)
485 	 * Only the second type should be expected here.
486 	 */
487 
488 	if (mdt->mdt_flags & DL_CAPAB_MDT_ENABLE) {
489 		if ((softmac->smac_mdt_capab.mdt_hdr_head !=
490 		    mdt->mdt_hdr_head) ||
491 		    (softmac->smac_mdt_capab.mdt_hdr_tail !=
492 		    mdt->mdt_hdr_tail) ||
493 		    (softmac->smac_mdt_capab.mdt_max_pld !=
494 		    mdt->mdt_max_pld) ||
495 		    (softmac->smac_mdt_capab.mdt_span_limit !=
496 		    mdt->mdt_span_limit)) {
497 			cmn_err(CE_WARN, "softmac_enable_mdt_ack: "
498 			    "unexpected MDT capability value");
499 			return (-1);
500 		}
501 		softmac->smac_mdt_capab.mdt_flags = mdt->mdt_flags;
502 	} else {
503 		cmn_err(CE_WARN, "softmac_enable_mdt_ack: "
504 		    "MDT flag DL_CAPAB_MDT_ENABLE is not set");
505 		return (-1);
506 	}
507 
508 	return (0);
509 }
510 
511 static int
512 i_capab_ack(mblk_t *mp, queue_t *q, softmac_capab_ops_t *op, void *arg)
513 {
514 	union DL_primitives	*prim;
515 	dl_capability_ack_t	*cap;
516 	dl_capability_sub_t	*sub, *end;
517 	int			err = 0;
518 
519 	prim = (union DL_primitives *)mp->b_rptr;
520 	ASSERT(prim->dl_primitive == DL_CAPABILITY_ACK);
521 
522 	cap = (dl_capability_ack_t *)prim;
523 	if (cap->dl_sub_length == 0)
524 		goto exit;
525 
526 	/* Is dl_sub_length correct? */
527 	if ((sizeof (*cap) + cap->dl_sub_length) > MBLKL(mp)) {
528 		err = EINVAL;
529 		goto exit;
530 	}
531 
532 	sub = (dl_capability_sub_t *)((caddr_t)cap + cap->dl_sub_offset);
533 	end = (dl_capability_sub_t *)((caddr_t)cap + cap->dl_sub_length
534 	    - sizeof (*sub));
535 	for (; (sub <= end) && (err == 0); ) {
536 		switch (sub->dl_cap) {
537 		case DL_CAPAB_ID_WRAPPER:
538 			err = i_capab_id_ack(mp, sub, q, op, arg);
539 			break;
540 		default:
541 			err = i_capab_sub_ack(mp, sub, q, op, arg);
542 			break;
543 		}
544 		sub = (dl_capability_sub_t *)((caddr_t)sub + sizeof (*sub)
545 		    + sub->dl_length);
546 	}
547 
548 exit:
549 	return (err);
550 }
551 
552 static int
553 i_capab_id_ack(mblk_t *mp, dl_capability_sub_t *outers,
554     queue_t *q, softmac_capab_ops_t *op, void *arg)
555 {
556 	dl_capab_id_t		*capab_id;
557 	dl_capability_sub_t	*inners;
558 	caddr_t			capend;
559 	int			err = EINVAL;
560 
561 	ASSERT(outers->dl_cap == DL_CAPAB_ID_WRAPPER);
562 
563 	capend = (caddr_t)(outers + 1) + outers->dl_length;
564 	if (capend > (caddr_t)mp->b_wptr) {
565 		cmn_err(CE_WARN, "i_capab_id_ack: malformed "
566 		    "sub-capability too long");
567 		return (err);
568 	}
569 
570 	capab_id = (dl_capab_id_t *)(outers + 1);
571 
572 	if (outers->dl_length < sizeof (*capab_id) ||
573 	    (inners = &capab_id->id_subcap,
574 	    inners->dl_length > (outers->dl_length - sizeof (*inners)))) {
575 		cmn_err(CE_WARN, "i_capab_id_ack: malformed "
576 		    "encapsulated capab type %d too long",
577 		    inners->dl_cap);
578 		return (err);
579 	}
580 
581 	if ((q != NULL) && (!dlcapabcheckqid(&capab_id->id_mid, q))) {
582 		cmn_err(CE_WARN, "i_capab_id_ack: pass-thru module(s) "
583 		    "detected, discarding capab type %d", inners->dl_cap);
584 		return (err);
585 	}
586 
587 	/* Process the encapsulated sub-capability */
588 	return (i_capab_sub_ack(mp, inners, q, op, arg));
589 }
590 
591 static int
592 i_capab_sub_ack(mblk_t *mp, dl_capability_sub_t *sub, queue_t *q,
593     softmac_capab_ops_t *op, void *arg)
594 {
595 	caddr_t			capend;
596 	dl_capab_hcksum_t	*hcksum;
597 	dl_capab_zerocopy_t	*zcopy;
598 	dl_capab_mdt_t		*mdt;
599 	int			err = 0;
600 
601 	capend = (caddr_t)(sub + 1) + sub->dl_length;
602 	if (capend > (caddr_t)mp->b_wptr) {
603 		cmn_err(CE_WARN, "i_capab_sub_ack: "
604 		    "malformed sub-capability too long");
605 		return (EINVAL);
606 	}
607 
608 	switch (sub->dl_cap) {
609 	case DL_CAPAB_HCKSUM:
610 		hcksum = (dl_capab_hcksum_t *)(sub + 1);
611 		err = i_capab_hcksum_ack(hcksum, q, op, arg);
612 		break;
613 
614 	case DL_CAPAB_ZEROCOPY:
615 		zcopy = (dl_capab_zerocopy_t *)(sub + 1);
616 		err = i_capab_zcopy_ack(zcopy, q, op, arg);
617 		break;
618 
619 	case DL_CAPAB_MDT:
620 		mdt = (dl_capab_mdt_t *)(sub + 1);
621 		err = i_capab_mdt_ack(mdt, q, op, arg);
622 		break;
623 
624 	default:
625 		cmn_err(CE_WARN, "i_capab_sub_ack: unknown capab type %d",
626 		    sub->dl_cap);
627 		err = EINVAL;
628 	}
629 
630 	return (err);
631 }
632 
633 static int
634 i_capab_hcksum_ack(dl_capab_hcksum_t *hcksum, queue_t *q,
635     softmac_capab_ops_t *op, void *arg)
636 {
637 	t_uscalar_t		flags;
638 	int			err = 0;
639 
640 	if ((err = i_capab_hcksum_verify(hcksum, q)) != 0)
641 		return (err);
642 
643 	flags = hcksum->hcksum_txflags;
644 
645 	if (!(flags & (HCKSUM_INET_PARTIAL | HCKSUM_INET_FULL_V4 |
646 	    HCKSUM_INET_FULL_V6 | HCKSUM_IPHDRCKSUM | HCKSUM_ENABLE))) {
647 		cmn_err(CE_WARN, "i_capab_hcksum_ack: invalid "
648 		    "hardware checksum capability flags 0x%x", flags);
649 		return (EINVAL);
650 	}
651 
652 	if (op->sc_hcksum_ack)
653 		return (op->sc_hcksum_ack(arg, flags));
654 	else {
655 		cmn_err(CE_WARN, "i_capab_hcksum_ack: unexpected hardware "
656 		    "checksum acknowledgement");
657 		return (EINVAL);
658 	}
659 }
660 
661 static int
662 i_capab_zcopy_ack(dl_capab_zerocopy_t *zcopy, queue_t *q,
663     softmac_capab_ops_t *op, void *arg)
664 {
665 	t_uscalar_t		flags;
666 	int			err = 0;
667 
668 	if ((err = i_capab_zcopy_verify(zcopy, q)) != 0)
669 		return (err);
670 
671 	flags = zcopy->zerocopy_flags;
672 	if (!(flags & DL_CAPAB_VMSAFE_MEM)) {
673 		cmn_err(CE_WARN, "i_capab_zcopy_ack: invalid zcopy capability "
674 		    "flags 0x%x", flags);
675 		return (EINVAL);
676 	}
677 	if (op->sc_zcopy_ack)
678 		return (op->sc_zcopy_ack(arg, flags));
679 	else {
680 		cmn_err(CE_WARN, "i_capab_zcopy_ack: unexpected zcopy "
681 		    "acknowledgement");
682 		return (EINVAL);
683 	}
684 }
685 
686 static int
687 i_capab_mdt_ack(dl_capab_mdt_t *mdt, queue_t *q,
688     softmac_capab_ops_t *op, void *arg)
689 {
690 	int	err;
691 
692 	if ((err = i_capab_mdt_verify(mdt, q)) != 0)
693 		return (err);
694 
695 	if (op->sc_mdt_ack)
696 		return (op->sc_mdt_ack(arg, mdt));
697 	else {
698 		cmn_err(CE_WARN, "i_capab_mdt_ack: unexpected MDT "
699 		    "acknowledgement");
700 		return (EINVAL);
701 	}
702 }
703 
704 static int
705 i_capab_hcksum_verify(dl_capab_hcksum_t *hcksum, queue_t *q)
706 {
707 	if (hcksum->hcksum_version != HCKSUM_VERSION_1) {
708 		cmn_err(CE_WARN, "i_capab_hcksum_verify: "
709 		    "unsupported hardware checksum capability (version %d, "
710 		    "expected %d)", hcksum->hcksum_version, HCKSUM_VERSION_1);
711 		return (-1);
712 	}
713 
714 	if ((q != NULL) && !dlcapabcheckqid(&hcksum->hcksum_mid, q)) {
715 		cmn_err(CE_WARN, "i_capab_hcksum_verify: unexpected pass-thru "
716 		    "module detected; hardware checksum capability discarded");
717 		return (-1);
718 	}
719 	return (0);
720 }
721 
722 static int
723 i_capab_zcopy_verify(dl_capab_zerocopy_t *zcopy, queue_t *q)
724 {
725 	if (zcopy->zerocopy_version != ZEROCOPY_VERSION_1) {
726 		cmn_err(CE_WARN, "i_capab_zcopy_verify: unsupported zcopy "
727 		    "capability (version %d, expected %d)",
728 		    zcopy->zerocopy_version, ZEROCOPY_VERSION_1);
729 		return (-1);
730 	}
731 
732 	if ((q != NULL) && !dlcapabcheckqid(&zcopy->zerocopy_mid, q)) {
733 		cmn_err(CE_WARN, "i_capab_zcopy_verify: unexpected pass-thru "
734 		    "module detected; zcopy checksum capability discarded");
735 		return (-1);
736 	}
737 	return (0);
738 }
739 
740 static int
741 i_capab_mdt_verify(dl_capab_mdt_t *mdt, queue_t *q)
742 {
743 	if (mdt->mdt_version != MDT_VERSION_2) {
744 		cmn_err(CE_WARN, "i_capab_mdt_verify: unsupported MDT "
745 		    "capability (version %d, expected %d)",
746 		    mdt->mdt_version, MDT_VERSION_2);
747 		return (-1);
748 	}
749 
750 	if ((q != NULL) && !dlcapabcheckqid(&mdt->mdt_mid, q)) {
751 		cmn_err(CE_WARN, "i_capab_mdt_verify: unexpected pass-thru "
752 		    "module detected; MDT capability discarded");
753 		return (-1);
754 	}
755 	return (0);
756 }
757