/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <sys/types.h>
#include <sys/mac.h>
#include <sys/softmac_impl.h>
typedef struct softmac_capab_ops {
int (*sc_hcksum_ack)(void *, t_uscalar_t);
int (*sc_zcopy_ack)(void *, t_uscalar_t);
int (*sc_mdt_ack)(void *, dl_capab_mdt_t *);
} softmac_capab_ops_t;
static int dl_capab(ldi_handle_t, mblk_t **);
static int softmac_fill_hcksum_ack(void *, t_uscalar_t);
static int softmac_fill_zcopy_ack(void *, t_uscalar_t);
static int softmac_fill_mdt_ack(void *, dl_capab_mdt_t *);
static int softmac_adv_hcksum_ack(void *, t_uscalar_t);
static int softmac_adv_zcopy_ack(void *, t_uscalar_t);
static int softmac_adv_mdt_ack(void *, dl_capab_mdt_t *);
static int softmac_enable_hcksum_ack(void *, t_uscalar_t);
static int softmac_enable_mdt_ack(void *, dl_capab_mdt_t *);
static int softmac_capab_send(softmac_lower_t *, boolean_t);
static int i_capab_ack(mblk_t *, queue_t *, softmac_capab_ops_t *, void *);
static int i_capab_id_ack(mblk_t *, dl_capability_sub_t *, queue_t *,
softmac_capab_ops_t *, void *);
static int i_capab_sub_ack(mblk_t *, dl_capability_sub_t *, queue_t *,
softmac_capab_ops_t *, void *);
static int i_capab_hcksum_ack(dl_capab_hcksum_t *, queue_t *,
softmac_capab_ops_t *, void *);
static int i_capab_zcopy_ack(dl_capab_zerocopy_t *, queue_t *,
softmac_capab_ops_t *, void *);
static int i_capab_mdt_ack(dl_capab_mdt_t *, queue_t *,
softmac_capab_ops_t *, void *);
static int i_capab_hcksum_verify(dl_capab_hcksum_t *, queue_t *);
static int i_capab_zcopy_verify(dl_capab_zerocopy_t *, queue_t *);
static int i_capab_mdt_verify(dl_capab_mdt_t *, queue_t *);
static softmac_capab_ops_t softmac_fill_capab_ops =
{
softmac_fill_hcksum_ack,
softmac_fill_zcopy_ack,
softmac_fill_mdt_ack,
};
static softmac_capab_ops_t softmac_adv_capab_ops =
{
softmac_adv_hcksum_ack,
softmac_adv_zcopy_ack,
softmac_adv_mdt_ack
};
static softmac_capab_ops_t softmac_enable_capab_ops =
{
softmac_enable_hcksum_ack,
NULL,
softmac_enable_mdt_ack
};
int
softmac_fill_capab(ldi_handle_t lh, softmac_t *softmac)
{
mblk_t *mp = NULL;
union DL_primitives *prim;
int err = 0;
if ((err = dl_capab(lh, &mp)) != 0)
goto exit;
prim = (union DL_primitives *)mp->b_rptr;
if (prim->dl_primitive == DL_ERROR_ACK) {
err = -1;
goto exit;
}
err = i_capab_ack(mp, NULL, &softmac_fill_capab_ops, softmac);
exit:
freemsg(mp);
return (err);
}
static int
dl_capab(ldi_handle_t lh, mblk_t **mpp)
{
dl_capability_req_t *capb;
union DL_primitives *dl_prim;
mblk_t *mp;
int err;
if ((mp = allocb(sizeof (dl_capability_req_t), BPRI_MED)) == NULL)
return (ENOMEM);
mp->b_datap->db_type = M_PROTO;
capb = (dl_capability_req_t *)mp->b_wptr;
mp->b_wptr += sizeof (dl_capability_req_t);
bzero(mp->b_rptr, sizeof (dl_capability_req_t));
capb->dl_primitive = DL_CAPABILITY_REQ;
(void) ldi_putmsg(lh, mp);
if ((err = ldi_getmsg(lh, &mp, (timestruc_t *)NULL)) != 0)
return (err);
dl_prim = (union DL_primitives *)mp->b_rptr;
switch (dl_prim->dl_primitive) {
case DL_CAPABILITY_ACK:
if (MBLKL(mp) < DL_CAPABILITY_ACK_SIZE) {
printf("dl_capability: DL_CAPABILITY_ACK "
"protocol err\n");
break;
}
*mpp = mp;
return (0);
case DL_ERROR_ACK:
if (MBLKL(mp) < DL_ERROR_ACK_SIZE) {
printf("dl_capability: DL_ERROR_ACK protocol err\n");
break;
}
if (((dl_error_ack_t *)dl_prim)->dl_error_primitive !=
DL_CAPABILITY_REQ) {
printf("dl_capability: DL_ERROR_ACK rtnd prim %u\n",
((dl_error_ack_t *)dl_prim)->dl_error_primitive);
break;
}
*mpp = mp;
return (0);
default:
printf("dl_capability: bad ACK header %u\n",
dl_prim->dl_primitive);
break;
}
freemsg(mp);
return (-1);
}
static int
softmac_fill_hcksum_ack(void *arg, t_uscalar_t flags)
{
softmac_t *softmac = (softmac_t *)arg;
/*
* There are two types of acks we process here:
* 1. acks in reply to a (first form) generic capability req
* (no ENABLE flag set)
* 2. acks in reply to a ENABLE capability req.
* (ENABLE flag set)
* Only the first type should be expected here.
*/
if (flags & HCKSUM_ENABLE) {
cmn_err(CE_WARN, "softmac_fill_hcksum_ack: unexpected "
"HCKSUM_ENABLE flag in hardware checksum capability");
} else if (flags & (HCKSUM_INET_PARTIAL | HCKSUM_INET_FULL_V4 |
HCKSUM_INET_FULL_V6 | HCKSUM_IPHDRCKSUM)) {
softmac->smac_capab_flags |= MAC_CAPAB_HCKSUM;
softmac->smac_hcksum_txflags = flags;
}
return (0);
}
static int
softmac_fill_zcopy_ack(void *arg, t_uscalar_t flags)
{
softmac_t *softmac = (softmac_t *)arg;
ASSERT(flags == DL_CAPAB_VMSAFE_MEM);
softmac->smac_capab_flags &= (~MAC_CAPAB_NO_ZCOPY);
return (0);
}
static int
softmac_fill_mdt_ack(void *arg, dl_capab_mdt_t *mdt)
{
softmac_t *softmac = (softmac_t *)arg;
/*
* There are two types of acks we process here:
* 1. acks in reply to a (first form) generic capability req
* (ENABLE flag might be set by some drivers)
* 2. acks in reply to a ENABLE capability req.
* (ENABLE flag set)
*/
ASSERT(mdt->mdt_version == MDT_VERSION_2);
softmac->smac_mdt = B_TRUE;
softmac->smac_mdt_capab.mdt_hdr_head = mdt->mdt_hdr_head;
softmac->smac_mdt_capab.mdt_hdr_tail = mdt->mdt_hdr_tail;
softmac->smac_mdt_capab.mdt_max_pld = mdt->mdt_max_pld;
softmac->smac_mdt_capab.mdt_span_limit = mdt->mdt_span_limit;
return (0);
}
int
softmac_capab_enable(softmac_lower_t *slp)
{
softmac_t *softmac = slp->sl_softmac;
int err;
if (softmac->smac_no_capability_req)
return (0);
/*
* Send DL_CAPABILITY_REQ to get capability advertisement.
*/
if ((err = softmac_capab_send(slp, B_FALSE)) != 0)
return (err);
/*
* Send DL_CAPABILITY_REQ to enable specific capabilities.
*/
if ((err = softmac_capab_send(slp, B_TRUE)) != 0)
return (err);
return (0);
}
static int
softmac_capab_send(softmac_lower_t *slp, boolean_t enable)
{
softmac_t *softmac;
dl_capability_req_t *capb;
dl_capability_sub_t *subcapb;
mblk_t *reqmp, *ackmp;
int err;
size_t size = 0;
softmac = slp->sl_softmac;
if (enable) {
/* No need to enable DL_CAPAB_ZEROCOPY */
if (softmac->smac_capab_flags & MAC_CAPAB_HCKSUM)
size += sizeof (dl_capability_sub_t) +
sizeof (dl_capab_hcksum_t);
if (softmac->smac_mdt) {
if (!(softmac->smac_mdt_capab.mdt_flags &
DL_CAPAB_MDT_ENABLE)) {
/*
* The MDT capability was not enabled for the
* first time, enable it now.
*/
size += sizeof (dl_capability_sub_t) +
sizeof (dl_capab_mdt_t);
}
}
if (size == 0)
return (0);
}
/*
* Create DL_CAPABILITY_REQ message and send it down
*/
reqmp = allocb(sizeof (dl_capability_req_t) + size, BPRI_MED);
if (reqmp == NULL)
return (ENOMEM);
bzero(reqmp->b_rptr, sizeof (dl_capability_req_t) + size);
DB_TYPE(reqmp) = M_PROTO;
reqmp->b_wptr = reqmp->b_rptr + sizeof (dl_capability_req_t) + size;
capb = (dl_capability_req_t *)reqmp->b_rptr;
capb->dl_primitive = DL_CAPABILITY_REQ;
if (!enable)
goto output;
capb->dl_sub_offset = sizeof (dl_capability_req_t);
if (softmac->smac_capab_flags & MAC_CAPAB_HCKSUM) {
dl_capab_hcksum_t *hck_subcapp;
size = sizeof (dl_capability_sub_t) +
sizeof (dl_capab_hcksum_t);
capb->dl_sub_length += size;
subcapb = (dl_capability_sub_t *)(capb + 1);
subcapb->dl_cap = DL_CAPAB_HCKSUM;
subcapb->dl_length = sizeof (dl_capab_hcksum_t);
hck_subcapp = (dl_capab_hcksum_t *)(subcapb + 1);
hck_subcapp->hcksum_version = HCKSUM_VERSION_1;
hck_subcapp->hcksum_txflags =
softmac->smac_hcksum_txflags | HCKSUM_ENABLE;
}
if (softmac->smac_mdt) {
if (!(softmac->smac_mdt_capab.mdt_flags &
DL_CAPAB_MDT_ENABLE)) {
dl_capab_mdt_t *mdt_subcapp;
size = sizeof (dl_capability_sub_t) +
sizeof (dl_capab_mdt_t);
capb->dl_sub_length += size;
subcapb = (dl_capability_sub_t *)
((uint8_t *)(subcapb + 1) + subcapb->dl_length);
subcapb->dl_cap = DL_CAPAB_MDT;
subcapb->dl_length = sizeof (dl_capab_mdt_t);
mdt_subcapp = (dl_capab_mdt_t *)(subcapb + 1);
mdt_subcapp->mdt_version = MDT_VERSION_2;
mdt_subcapp->mdt_flags =
(softmac->smac_mdt_capab.mdt_flags |
DL_CAPAB_MDT_ENABLE);
mdt_subcapp->mdt_hdr_head =
softmac->smac_mdt_capab.mdt_hdr_head;
mdt_subcapp->mdt_hdr_tail =
softmac->smac_mdt_capab.mdt_hdr_tail;
mdt_subcapp->mdt_max_pld =
softmac->smac_mdt_capab.mdt_max_pld;
mdt_subcapp->mdt_span_limit =
softmac->smac_mdt_capab.mdt_span_limit;
}
}
output:
err = softmac_proto_tx(slp, reqmp, &ackmp);
if (err == 0) {
if (enable) {
err = i_capab_ack(ackmp, NULL,
&softmac_enable_capab_ops, softmac);
} else {
err = i_capab_ack(ackmp, NULL,
&softmac_adv_capab_ops, softmac);
}
}
freemsg(ackmp);
return (err);
}
static int
softmac_adv_hcksum_ack(void *arg, t_uscalar_t flags)
{
softmac_t *softmac = (softmac_t *)arg;
/*
* There are two types of acks we process here:
* 1. acks in reply to a (first form) generic capability req
* (no ENABLE flag set)
* 2. acks in reply to a ENABLE capability req.
* (ENABLE flag set)
* Only the first type should be expected here.
*/
if (flags & HCKSUM_ENABLE) {
cmn_err(CE_WARN, "softmac_adv_hcksum_ack: unexpected "
"HCKSUM_ENABLE flag in hardware checksum capability");
return (-1);
} else if (flags & (HCKSUM_INET_PARTIAL | HCKSUM_INET_FULL_V4 |
HCKSUM_INET_FULL_V6 | HCKSUM_IPHDRCKSUM)) {
/*
* The acknowledgement should be the same as we got when
* the softmac is created.
*/
if (!(softmac->smac_capab_flags & MAC_CAPAB_HCKSUM)) {
ASSERT(B_FALSE);
return (-1);
}
if (softmac->smac_hcksum_txflags != flags) {
ASSERT(B_FALSE);
return (-1);
}
}
return (0);
}
static int
softmac_adv_zcopy_ack(void *arg, t_uscalar_t flags)
{
softmac_t *softmac = (softmac_t *)arg;
/*
* The acknowledgement should be the same as we got when
* the softmac is created.
*/
ASSERT(flags == DL_CAPAB_VMSAFE_MEM);
if (softmac->smac_capab_flags & MAC_CAPAB_NO_ZCOPY) {
ASSERT(B_FALSE);
return (-1);
}
return (0);
}
static int
softmac_adv_mdt_ack(void *arg, dl_capab_mdt_t *mdt)
{
softmac_t *softmac = (softmac_t *)arg;
/*
* The acknowledgement should be the same as we got when
* the softmac is created.
*/
if (!softmac->smac_mdt) {
ASSERT(B_FALSE);
return (-1);
}
if ((softmac->smac_mdt_capab.mdt_hdr_head != mdt->mdt_hdr_head) ||
(softmac->smac_mdt_capab.mdt_hdr_tail != mdt->mdt_hdr_tail) ||
(softmac->smac_mdt_capab.mdt_max_pld != mdt->mdt_max_pld) ||
(softmac->smac_mdt_capab.mdt_span_limit != mdt->mdt_span_limit)) {
ASSERT(B_FALSE);
return (-1);
}
/*
* We need the mdt_flags field to know whether an additional
* DL_CAPAB_MDT_ENABLE is necessary.
*/
softmac->smac_mdt_capab.mdt_flags = mdt->mdt_flags;
return (0);
}
static int
softmac_enable_hcksum_ack(void *arg, t_uscalar_t flags)
{
softmac_t *softmac = (softmac_t *)arg;
/*
* There are two types of acks we process here:
* 1. acks in reply to a (first form) generic capability req
* (no ENABLE flag set)
* 2. acks in reply to a ENABLE capability req.
* (ENABLE flag set)
* Only the second type should be expected here.
*/
if (flags & HCKSUM_ENABLE) {
if ((flags & ~HCKSUM_ENABLE) != softmac->smac_hcksum_txflags) {
cmn_err(CE_WARN, "softmac_enable_hcksum_ack: unexpected"
" hardware capability flag value 0x%x", flags);
return (-1);
}
} else {
cmn_err(CE_WARN, "softmac_enable_hcksum_ack: "
"hardware checksum flag HCKSUM_ENABLE is not set");
return (-1);
}
return (0);
}
static int
softmac_enable_mdt_ack(void *arg, dl_capab_mdt_t *mdt)
{
softmac_t *softmac = (softmac_t *)arg;
/*
* There are two types of acks we process here:
* 1. acks in reply to a (first form) generic capability req
* (no ENABLE flag set)
* 2. acks in reply to a ENABLE capability req.
* (ENABLE flag set)
* Only the second type should be expected here.
*/
if (mdt->mdt_flags & DL_CAPAB_MDT_ENABLE) {
if ((softmac->smac_mdt_capab.mdt_hdr_head !=
mdt->mdt_hdr_head) ||
(softmac->smac_mdt_capab.mdt_hdr_tail !=
mdt->mdt_hdr_tail) ||
(softmac->smac_mdt_capab.mdt_max_pld !=
mdt->mdt_max_pld) ||
(softmac->smac_mdt_capab.mdt_span_limit !=
mdt->mdt_span_limit)) {
cmn_err(CE_WARN, "softmac_enable_mdt_ack: "
"unexpected MDT capability value");
return (-1);
}
softmac->smac_mdt_capab.mdt_flags = mdt->mdt_flags;
} else {
cmn_err(CE_WARN, "softmac_enable_mdt_ack: "
"MDT flag DL_CAPAB_MDT_ENABLE is not set");
return (-1);
}
return (0);
}
static int
i_capab_ack(mblk_t *mp, queue_t *q, softmac_capab_ops_t *op, void *arg)
{
union DL_primitives *prim;
dl_capability_ack_t *cap;
dl_capability_sub_t *sub, *end;
int err = 0;
prim = (union DL_primitives *)mp->b_rptr;
ASSERT(prim->dl_primitive == DL_CAPABILITY_ACK);
cap = (dl_capability_ack_t *)prim;
if (cap->dl_sub_length == 0)
goto exit;
/* Is dl_sub_length correct? */
if ((sizeof (*cap) + cap->dl_sub_length) > MBLKL(mp)) {
err = EINVAL;
goto exit;
}
sub = (dl_capability_sub_t *)((caddr_t)cap + cap->dl_sub_offset);
end = (dl_capability_sub_t *)((caddr_t)cap + cap->dl_sub_length
- sizeof (*sub));
for (; (sub <= end) && (err == 0); ) {
switch (sub->dl_cap) {
case DL_CAPAB_ID_WRAPPER:
err = i_capab_id_ack(mp, sub, q, op, arg);
break;
default:
err = i_capab_sub_ack(mp, sub, q, op, arg);
break;
}
sub = (dl_capability_sub_t *)((caddr_t)sub + sizeof (*sub)
+ sub->dl_length);
}
exit:
return (err);
}
static int
i_capab_id_ack(mblk_t *mp, dl_capability_sub_t *outers,
queue_t *q, softmac_capab_ops_t *op, void *arg)
{
dl_capab_id_t *capab_id;
dl_capability_sub_t *inners;
caddr_t capend;
int err = EINVAL;
ASSERT(outers->dl_cap == DL_CAPAB_ID_WRAPPER);
capend = (caddr_t)(outers + 1) + outers->dl_length;
if (capend > (caddr_t)mp->b_wptr) {
cmn_err(CE_WARN, "i_capab_id_ack: malformed "
"sub-capability too long");
return (err);
}
capab_id = (dl_capab_id_t *)(outers + 1);
if (outers->dl_length < sizeof (*capab_id) ||
(inners = &capab_id->id_subcap,
inners->dl_length > (outers->dl_length - sizeof (*inners)))) {
cmn_err(CE_WARN, "i_capab_id_ack: malformed "
"encapsulated capab type %d too long",
inners->dl_cap);
return (err);
}
if ((q != NULL) && (!dlcapabcheckqid(&capab_id->id_mid, q))) {
cmn_err(CE_WARN, "i_capab_id_ack: pass-thru module(s) "
"detected, discarding capab type %d", inners->dl_cap);
return (err);
}
/* Process the encapsulated sub-capability */
return (i_capab_sub_ack(mp, inners, q, op, arg));
}
static int
i_capab_sub_ack(mblk_t *mp, dl_capability_sub_t *sub, queue_t *q,
softmac_capab_ops_t *op, void *arg)
{
caddr_t capend;
dl_capab_hcksum_t *hcksum;
dl_capab_zerocopy_t *zcopy;
dl_capab_mdt_t *mdt;
int err = 0;
capend = (caddr_t)(sub + 1) + sub->dl_length;
if (capend > (caddr_t)mp->b_wptr) {
cmn_err(CE_WARN, "i_capab_sub_ack: "
"malformed sub-capability too long");
return (EINVAL);
}
switch (sub->dl_cap) {
case DL_CAPAB_HCKSUM:
hcksum = (dl_capab_hcksum_t *)(sub + 1);
err = i_capab_hcksum_ack(hcksum, q, op, arg);
break;
case DL_CAPAB_ZEROCOPY:
zcopy = (dl_capab_zerocopy_t *)(sub + 1);
err = i_capab_zcopy_ack(zcopy, q, op, arg);
break;
case DL_CAPAB_MDT:
mdt = (dl_capab_mdt_t *)(sub + 1);
err = i_capab_mdt_ack(mdt, q, op, arg);
break;
default:
cmn_err(CE_WARN, "i_capab_sub_ack: unknown capab type %d",
sub->dl_cap);
err = EINVAL;
}
return (err);
}
static int
i_capab_hcksum_ack(dl_capab_hcksum_t *hcksum, queue_t *q,
softmac_capab_ops_t *op, void *arg)
{
t_uscalar_t flags;
int err = 0;
if ((err = i_capab_hcksum_verify(hcksum, q)) != 0)
return (err);
flags = hcksum->hcksum_txflags;
if (!(flags & (HCKSUM_INET_PARTIAL | HCKSUM_INET_FULL_V4 |
HCKSUM_INET_FULL_V6 | HCKSUM_IPHDRCKSUM | HCKSUM_ENABLE))) {
cmn_err(CE_WARN, "i_capab_hcksum_ack: invalid "
"hardware checksum capability flags 0x%x", flags);
return (EINVAL);
}
if (op->sc_hcksum_ack)
return (op->sc_hcksum_ack(arg, flags));
else {
cmn_err(CE_WARN, "i_capab_hcksum_ack: unexpected hardware "
"checksum acknowledgement");
return (EINVAL);
}
}
static int
i_capab_zcopy_ack(dl_capab_zerocopy_t *zcopy, queue_t *q,
softmac_capab_ops_t *op, void *arg)
{
t_uscalar_t flags;
int err = 0;
if ((err = i_capab_zcopy_verify(zcopy, q)) != 0)
return (err);
flags = zcopy->zerocopy_flags;
if (!(flags & DL_CAPAB_VMSAFE_MEM)) {
cmn_err(CE_WARN, "i_capab_zcopy_ack: invalid zcopy capability "
"flags 0x%x", flags);
return (EINVAL);
}
if (op->sc_zcopy_ack)
return (op->sc_zcopy_ack(arg, flags));
else {
cmn_err(CE_WARN, "i_capab_zcopy_ack: unexpected zcopy "
"acknowledgement");
return (EINVAL);
}
}
static int
i_capab_mdt_ack(dl_capab_mdt_t *mdt, queue_t *q,
softmac_capab_ops_t *op, void *arg)
{
int err;
if ((err = i_capab_mdt_verify(mdt, q)) != 0)
return (err);
if (op->sc_mdt_ack)
return (op->sc_mdt_ack(arg, mdt));
else {
cmn_err(CE_WARN, "i_capab_mdt_ack: unexpected MDT "
"acknowledgement");
return (EINVAL);
}
}
static int
i_capab_hcksum_verify(dl_capab_hcksum_t *hcksum, queue_t *q)
{
if (hcksum->hcksum_version != HCKSUM_VERSION_1) {
cmn_err(CE_WARN, "i_capab_hcksum_verify: "
"unsupported hardware checksum capability (version %d, "
"expected %d)", hcksum->hcksum_version, HCKSUM_VERSION_1);
return (-1);
}
if ((q != NULL) && !dlcapabcheckqid(&hcksum->hcksum_mid, q)) {
cmn_err(CE_WARN, "i_capab_hcksum_verify: unexpected pass-thru "
"module detected; hardware checksum capability discarded");
return (-1);
}
return (0);
}
static int
i_capab_zcopy_verify(dl_capab_zerocopy_t *zcopy, queue_t *q)
{
if (zcopy->zerocopy_version != ZEROCOPY_VERSION_1) {
cmn_err(CE_WARN, "i_capab_zcopy_verify: unsupported zcopy "
"capability (version %d, expected %d)",
zcopy->zerocopy_version, ZEROCOPY_VERSION_1);
return (-1);
}
if ((q != NULL) && !dlcapabcheckqid(&zcopy->zerocopy_mid, q)) {
cmn_err(CE_WARN, "i_capab_zcopy_verify: unexpected pass-thru "
"module detected; zcopy checksum capability discarded");
return (-1);
}
return (0);
}
static int
i_capab_mdt_verify(dl_capab_mdt_t *mdt, queue_t *q)
{
if (mdt->mdt_version != MDT_VERSION_2) {
cmn_err(CE_WARN, "i_capab_mdt_verify: unsupported MDT "
"capability (version %d, expected %d)",
mdt->mdt_version, MDT_VERSION_2);
return (-1);
}
if ((q != NULL) && !dlcapabcheckqid(&mdt->mdt_mid, q)) {
cmn_err(CE_WARN, "i_capab_mdt_verify: unexpected pass-thru "
"module detected; MDT capability discarded");
return (-1);
}
return (0);
}