xref: /illumos-gate/usr/src/uts/common/io/aggr/aggr_ctl.c (revision ba2e4443695ee6a6f420a35cd4fc3d3346d22932)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*ba2e4443Sseb  * Common Development and Distribution License (the "License").
6*ba2e4443Sseb  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22*ba2e4443Sseb  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate /*
297c478bd9Sstevel@tonic-gate  * IEEE 802.3ad Link Aggregation -- IOCTL processing.
307c478bd9Sstevel@tonic-gate  */
317c478bd9Sstevel@tonic-gate 
327c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
337c478bd9Sstevel@tonic-gate #include <sys/aggr.h>
347c478bd9Sstevel@tonic-gate #include <sys/aggr_impl.h>
35210db224Sericheng #include <sys/strsun.h>
367c478bd9Sstevel@tonic-gate 
37210db224Sericheng static int aggr_ioc_create(mblk_t *, int);
38210db224Sericheng static int aggr_ioc_delete(mblk_t *, int);
39210db224Sericheng static int aggr_ioc_info(mblk_t *, int);
40210db224Sericheng static int aggr_ioc_add(mblk_t *, int);
41210db224Sericheng static int aggr_ioc_remove(mblk_t *, int);
42210db224Sericheng static int aggr_ioc_status(mblk_t *, int);
43210db224Sericheng static int aggr_ioc_modify(mblk_t *, int);
447c478bd9Sstevel@tonic-gate 
457c478bd9Sstevel@tonic-gate typedef struct ioc_cmd_s {
467c478bd9Sstevel@tonic-gate 	int ic_cmd;
47210db224Sericheng 	int (*ic_func)(mblk_t *, int);
487c478bd9Sstevel@tonic-gate } ioc_cmd_t;
497c478bd9Sstevel@tonic-gate 
507c478bd9Sstevel@tonic-gate static ioc_cmd_t ioc_cmd[] = {
517c478bd9Sstevel@tonic-gate 	{LAIOC_CREATE, aggr_ioc_create},
527c478bd9Sstevel@tonic-gate 	{LAIOC_DELETE, aggr_ioc_delete},
537c478bd9Sstevel@tonic-gate 	{LAIOC_INFO, aggr_ioc_info},
54210db224Sericheng 	{LAIOC_ADD, aggr_ioc_add},
55210db224Sericheng 	{LAIOC_REMOVE, aggr_ioc_remove},
567c478bd9Sstevel@tonic-gate 	{LAIOC_MODIFY, aggr_ioc_modify}};
577c478bd9Sstevel@tonic-gate 
58210db224Sericheng #define	IOC_CMD_SZ	(sizeof (ioc_cmd) / sizeof (ioc_cmd_t))
597c478bd9Sstevel@tonic-gate 
607c478bd9Sstevel@tonic-gate /*
617c478bd9Sstevel@tonic-gate  * Process a LAIOC_MODIFY request.
627c478bd9Sstevel@tonic-gate  */
637c478bd9Sstevel@tonic-gate static int
64210db224Sericheng aggr_ioc_modify(mblk_t *mp, int mode)
657c478bd9Sstevel@tonic-gate {
66210db224Sericheng 	STRUCT_HANDLE(laioc_modify, modify_arg);
677c478bd9Sstevel@tonic-gate 	uint32_t policy;
687c478bd9Sstevel@tonic-gate 	boolean_t mac_fixed;
697c478bd9Sstevel@tonic-gate 	uchar_t mac_addr[ETHERADDRL];
707c478bd9Sstevel@tonic-gate 	uint8_t modify_mask_arg, modify_mask = 0;
71210db224Sericheng 	uint32_t rc, key;
727c478bd9Sstevel@tonic-gate 	aggr_lacp_mode_t lacp_mode;
737c478bd9Sstevel@tonic-gate 	aggr_lacp_timer_t lacp_timer;
747c478bd9Sstevel@tonic-gate 
75210db224Sericheng 	STRUCT_SET_HANDLE(modify_arg, mode, (void *)mp->b_cont->b_rptr);
76210db224Sericheng 	if (MBLKL(mp->b_cont) < STRUCT_SIZE(modify_arg))
77210db224Sericheng 		return (EINVAL);
787c478bd9Sstevel@tonic-gate 
797c478bd9Sstevel@tonic-gate 	key = STRUCT_FGET(modify_arg, lu_key);
807c478bd9Sstevel@tonic-gate 	modify_mask_arg = STRUCT_FGET(modify_arg, lu_modify_mask);
817c478bd9Sstevel@tonic-gate 
827c478bd9Sstevel@tonic-gate 	if (modify_mask_arg & LAIOC_MODIFY_POLICY) {
837c478bd9Sstevel@tonic-gate 		modify_mask |= AGGR_MODIFY_POLICY;
847c478bd9Sstevel@tonic-gate 		policy = STRUCT_FGET(modify_arg, lu_policy);
857c478bd9Sstevel@tonic-gate 	}
867c478bd9Sstevel@tonic-gate 
877c478bd9Sstevel@tonic-gate 	if (modify_mask_arg & LAIOC_MODIFY_MAC) {
887c478bd9Sstevel@tonic-gate 		modify_mask |= AGGR_MODIFY_MAC;
897c478bd9Sstevel@tonic-gate 		bcopy(STRUCT_FGET(modify_arg, lu_mac), mac_addr, ETHERADDRL);
907c478bd9Sstevel@tonic-gate 		mac_fixed = STRUCT_FGET(modify_arg, lu_mac_fixed);
917c478bd9Sstevel@tonic-gate 	}
927c478bd9Sstevel@tonic-gate 
937c478bd9Sstevel@tonic-gate 	if (modify_mask_arg & LAIOC_MODIFY_LACP_MODE) {
947c478bd9Sstevel@tonic-gate 		modify_mask |= AGGR_MODIFY_LACP_MODE;
957c478bd9Sstevel@tonic-gate 		lacp_mode = STRUCT_FGET(modify_arg, lu_lacp_mode);
967c478bd9Sstevel@tonic-gate 	}
977c478bd9Sstevel@tonic-gate 
987c478bd9Sstevel@tonic-gate 	if (modify_mask_arg & LAIOC_MODIFY_LACP_TIMER) {
997c478bd9Sstevel@tonic-gate 		modify_mask |= AGGR_MODIFY_LACP_TIMER;
1007c478bd9Sstevel@tonic-gate 		lacp_timer = STRUCT_FGET(modify_arg, lu_lacp_timer);
1017c478bd9Sstevel@tonic-gate 	}
1027c478bd9Sstevel@tonic-gate 
103210db224Sericheng 	rc = aggr_grp_modify(key, NULL, modify_mask, policy, mac_fixed,
104210db224Sericheng 	    mac_addr, lacp_mode, lacp_timer);
105210db224Sericheng 
106210db224Sericheng 	freemsg(mp->b_cont);
107210db224Sericheng 	mp->b_cont = NULL;
108210db224Sericheng 	return (rc);
1097c478bd9Sstevel@tonic-gate }
1107c478bd9Sstevel@tonic-gate 
1117c478bd9Sstevel@tonic-gate /*
1127c478bd9Sstevel@tonic-gate  * Process a LAIOC_CREATE request.
1137c478bd9Sstevel@tonic-gate  */
1147c478bd9Sstevel@tonic-gate static int
115210db224Sericheng aggr_ioc_create(mblk_t *mp, int mode)
1167c478bd9Sstevel@tonic-gate {
117210db224Sericheng 	STRUCT_HANDLE(laioc_create, create_arg);
1187c478bd9Sstevel@tonic-gate 	uint16_t nports;
1197c478bd9Sstevel@tonic-gate 	laioc_port_t *ports = NULL;
1207c478bd9Sstevel@tonic-gate 	uint32_t policy;
1217c478bd9Sstevel@tonic-gate 	boolean_t mac_fixed;
1227c478bd9Sstevel@tonic-gate 	uchar_t mac_addr[ETHERADDRL];
1237c478bd9Sstevel@tonic-gate 	aggr_lacp_mode_t lacp_mode;
1247c478bd9Sstevel@tonic-gate 	aggr_lacp_timer_t lacp_timer;
125210db224Sericheng 	int rc, len;
1267c478bd9Sstevel@tonic-gate 
127210db224Sericheng 	STRUCT_SET_HANDLE(create_arg, mode, (void *)mp->b_cont->b_rptr);
128210db224Sericheng 	if ((len = MBLKL(mp->b_cont)) < STRUCT_SIZE(create_arg))
129210db224Sericheng 		return (EINVAL);
1307c478bd9Sstevel@tonic-gate 
1317c478bd9Sstevel@tonic-gate 	nports = STRUCT_FGET(create_arg, lc_nports);
1327c478bd9Sstevel@tonic-gate 	if (nports > AGGR_MAX_PORTS)
1337c478bd9Sstevel@tonic-gate 		return (EINVAL);
1347c478bd9Sstevel@tonic-gate 
1357c478bd9Sstevel@tonic-gate 	policy = STRUCT_FGET(create_arg, lc_policy);
1367c478bd9Sstevel@tonic-gate 	lacp_mode = STRUCT_FGET(create_arg, lc_lacp_mode);
1377c478bd9Sstevel@tonic-gate 	lacp_timer = STRUCT_FGET(create_arg, lc_lacp_timer);
1387c478bd9Sstevel@tonic-gate 
139210db224Sericheng 	if (len < STRUCT_SIZE(create_arg) + (nports * sizeof (laioc_port_t)))
140210db224Sericheng 		return (EINVAL);
1417c478bd9Sstevel@tonic-gate 
142210db224Sericheng 	ports = (laioc_port_t *)(STRUCT_BUF(create_arg) + 1);
1437c478bd9Sstevel@tonic-gate 
1447c478bd9Sstevel@tonic-gate 	bcopy(STRUCT_FGET(create_arg, lc_mac), mac_addr, ETHERADDRL);
1457c478bd9Sstevel@tonic-gate 	mac_fixed = STRUCT_FGET(create_arg, lc_mac_fixed);
1467c478bd9Sstevel@tonic-gate 
1477c478bd9Sstevel@tonic-gate 	rc = aggr_grp_create(STRUCT_FGET(create_arg, lc_key),
1487c478bd9Sstevel@tonic-gate 	    nports, ports, policy, mac_fixed, mac_addr, lacp_mode, lacp_timer);
1497c478bd9Sstevel@tonic-gate 
150210db224Sericheng 	freemsg(mp->b_cont);
151210db224Sericheng 	mp->b_cont = NULL;
1527c478bd9Sstevel@tonic-gate 	return (rc);
1537c478bd9Sstevel@tonic-gate }
1547c478bd9Sstevel@tonic-gate 
1557c478bd9Sstevel@tonic-gate static int
156210db224Sericheng aggr_ioc_delete(mblk_t *mp, int mode)
1577c478bd9Sstevel@tonic-gate {
158210db224Sericheng 	STRUCT_HANDLE(laioc_delete, delete_arg);
159210db224Sericheng 	int rc;
1607c478bd9Sstevel@tonic-gate 
161210db224Sericheng 	STRUCT_SET_HANDLE(delete_arg, mode, (void *)mp->b_cont->b_rptr);
162210db224Sericheng 	if (STRUCT_SIZE(delete_arg) > MBLKL(mp))
163210db224Sericheng 		return (EINVAL);
1647c478bd9Sstevel@tonic-gate 
165210db224Sericheng 	rc = aggr_grp_delete(STRUCT_FGET(delete_arg, ld_key));
1667c478bd9Sstevel@tonic-gate 
167210db224Sericheng 	freemsg(mp->b_cont);
168210db224Sericheng 	mp->b_cont = NULL;
169210db224Sericheng 	return (rc);
1707c478bd9Sstevel@tonic-gate }
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate typedef struct aggr_ioc_info_state {
1737c478bd9Sstevel@tonic-gate 	uint32_t bytes_left;
174210db224Sericheng 	uchar_t *where;
1757c478bd9Sstevel@tonic-gate } aggr_ioc_info_state_t;
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate static int
1787c478bd9Sstevel@tonic-gate aggr_ioc_info_new_grp(void *arg, uint32_t key, uchar_t *mac,
1797c478bd9Sstevel@tonic-gate     boolean_t mac_fixed, uint32_t policy, uint32_t nports,
1807c478bd9Sstevel@tonic-gate     aggr_lacp_mode_t lacp_mode, aggr_lacp_timer_t lacp_timer)
1817c478bd9Sstevel@tonic-gate {
1827c478bd9Sstevel@tonic-gate 	aggr_ioc_info_state_t *state = arg;
1837c478bd9Sstevel@tonic-gate 	laioc_info_group_t grp;
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate 	if (state->bytes_left < sizeof (grp))
1867c478bd9Sstevel@tonic-gate 		return (ENOSPC);
1877c478bd9Sstevel@tonic-gate 
1887c478bd9Sstevel@tonic-gate 	grp.lg_key = key;
1897c478bd9Sstevel@tonic-gate 	bcopy(mac, grp.lg_mac, ETHERADDRL);
1907c478bd9Sstevel@tonic-gate 	grp.lg_mac_fixed = mac_fixed;
1917c478bd9Sstevel@tonic-gate 	grp.lg_policy = policy;
1927c478bd9Sstevel@tonic-gate 	grp.lg_nports = nports;
1937c478bd9Sstevel@tonic-gate 	grp.lg_lacp_mode = lacp_mode;
1947c478bd9Sstevel@tonic-gate 	grp.lg_lacp_timer = lacp_timer;
1957c478bd9Sstevel@tonic-gate 
196210db224Sericheng 	bcopy(&grp, state->where, sizeof (grp));
1977c478bd9Sstevel@tonic-gate 	state->where += sizeof (grp);
1987c478bd9Sstevel@tonic-gate 	state->bytes_left -= sizeof (grp);
1997c478bd9Sstevel@tonic-gate 
2007c478bd9Sstevel@tonic-gate 	return (0);
2017c478bd9Sstevel@tonic-gate }
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate static int
204*ba2e4443Sseb aggr_ioc_info_new_port(void *arg, char *devname, uchar_t *mac,
205*ba2e4443Sseb     aggr_port_state_t portstate, aggr_lacp_state_t *lacp_state)
2067c478bd9Sstevel@tonic-gate {
2077c478bd9Sstevel@tonic-gate 	aggr_ioc_info_state_t *state = arg;
2087c478bd9Sstevel@tonic-gate 	laioc_info_port_t port;
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 	if (state->bytes_left < sizeof (port))
2117c478bd9Sstevel@tonic-gate 		return (ENOSPC);
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 	bcopy(devname, port.lp_devname, MAXNAMELEN + 1);
2147c478bd9Sstevel@tonic-gate 	bcopy(mac, port.lp_mac, ETHERADDRL);
2157c478bd9Sstevel@tonic-gate 	port.lp_state = portstate;
2167c478bd9Sstevel@tonic-gate 	port.lp_lacp_state = *lacp_state;
2177c478bd9Sstevel@tonic-gate 
218210db224Sericheng 	bcopy(&port, state->where, sizeof (port));
2197c478bd9Sstevel@tonic-gate 	state->where += sizeof (port);
2207c478bd9Sstevel@tonic-gate 	state->bytes_left -= sizeof (port);
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 	return (0);
2237c478bd9Sstevel@tonic-gate }
2247c478bd9Sstevel@tonic-gate 
2257c478bd9Sstevel@tonic-gate /*ARGSUSED*/
2267c478bd9Sstevel@tonic-gate static int
227210db224Sericheng aggr_ioc_info(mblk_t *mp, int mode)
2287c478bd9Sstevel@tonic-gate {
229210db224Sericheng 	laioc_info_t *info_argp;
2307c478bd9Sstevel@tonic-gate 	uint32_t ngroups, group_key;
231210db224Sericheng 	int rc, len;
2327c478bd9Sstevel@tonic-gate 	aggr_ioc_info_state_t state;
2337c478bd9Sstevel@tonic-gate 
234210db224Sericheng 	if ((len = MBLKL(mp->b_cont)) < sizeof (*info_argp))
235210db224Sericheng 		return (EINVAL);
2367c478bd9Sstevel@tonic-gate 
237210db224Sericheng 	info_argp = (laioc_info_t *)mp->b_cont->b_rptr;
2387c478bd9Sstevel@tonic-gate 	/*
2397c478bd9Sstevel@tonic-gate 	 * Key of the group to return. If zero, the call returns information
2407c478bd9Sstevel@tonic-gate 	 * regarding all groups currently defined.
2417c478bd9Sstevel@tonic-gate 	 */
242210db224Sericheng 	group_key = info_argp->li_group_key;
2437c478bd9Sstevel@tonic-gate 
244210db224Sericheng 	state.bytes_left = len - sizeof (laioc_info_t);
245210db224Sericheng 	state.where = (uchar_t *)(info_argp + 1);
2467c478bd9Sstevel@tonic-gate 
2477c478bd9Sstevel@tonic-gate 	rc = aggr_grp_info(&ngroups, group_key, &state, aggr_ioc_info_new_grp,
2487c478bd9Sstevel@tonic-gate 	    aggr_ioc_info_new_port);
249210db224Sericheng 	if (rc == 0)
250210db224Sericheng 		info_argp->li_ngroups = ngroups;
251210db224Sericheng 
2527c478bd9Sstevel@tonic-gate 	return (rc);
2537c478bd9Sstevel@tonic-gate }
2547c478bd9Sstevel@tonic-gate 
2557c478bd9Sstevel@tonic-gate static int
256210db224Sericheng aggr_ioc_add(mblk_t *mp, int mode)
2577c478bd9Sstevel@tonic-gate {
258210db224Sericheng 	STRUCT_HANDLE(laioc_add_rem, add_arg);
259210db224Sericheng 	uint32_t nports;
2607c478bd9Sstevel@tonic-gate 	laioc_port_t *ports = NULL;
261210db224Sericheng 	int rc, len;
2627c478bd9Sstevel@tonic-gate 
263210db224Sericheng 	STRUCT_SET_HANDLE(add_arg, mode, (void *)mp->b_cont->b_rptr);
264210db224Sericheng 	if ((len = MBLKL(mp->b_cont)) < STRUCT_SIZE(add_arg))
265210db224Sericheng 		return (EINVAL);
2667c478bd9Sstevel@tonic-gate 
267210db224Sericheng 	nports = STRUCT_FGET(add_arg, la_nports);
2687c478bd9Sstevel@tonic-gate 	if (nports > AGGR_MAX_PORTS)
2697c478bd9Sstevel@tonic-gate 		return (EINVAL);
2707c478bd9Sstevel@tonic-gate 
271210db224Sericheng 	if (len < STRUCT_SIZE(add_arg) + (nports * sizeof (laioc_port_t)))
272210db224Sericheng 		return (EINVAL);
2737c478bd9Sstevel@tonic-gate 
274210db224Sericheng 	ports = (laioc_port_t *)(STRUCT_BUF(add_arg) + 1);
2757c478bd9Sstevel@tonic-gate 
276210db224Sericheng 	rc = aggr_grp_add_ports(STRUCT_FGET(add_arg, la_key),
2777c478bd9Sstevel@tonic-gate 	    nports, ports);
2787c478bd9Sstevel@tonic-gate 
279210db224Sericheng 	freemsg(mp->b_cont);
280210db224Sericheng 	mp->b_cont = NULL;
2817c478bd9Sstevel@tonic-gate 	return (rc);
2827c478bd9Sstevel@tonic-gate }
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate static int
285210db224Sericheng aggr_ioc_remove(mblk_t *mp, int mode)
2867c478bd9Sstevel@tonic-gate {
287210db224Sericheng 	STRUCT_HANDLE(laioc_add_rem, rem_arg);
288210db224Sericheng 	uint32_t nports;
2897c478bd9Sstevel@tonic-gate 	laioc_port_t *ports = NULL;
290210db224Sericheng 	int rc, len;
2917c478bd9Sstevel@tonic-gate 
292210db224Sericheng 	STRUCT_SET_HANDLE(rem_arg, mode, (void *)mp->b_cont->b_rptr);
293210db224Sericheng 	if ((len = MBLKL(mp->b_cont)) < STRUCT_SIZE(rem_arg))
294210db224Sericheng 		return (EINVAL);
2957c478bd9Sstevel@tonic-gate 
2967c478bd9Sstevel@tonic-gate 	nports = STRUCT_FGET(rem_arg, la_nports);
2977c478bd9Sstevel@tonic-gate 	if (nports > AGGR_MAX_PORTS)
2987c478bd9Sstevel@tonic-gate 		return (EINVAL);
2997c478bd9Sstevel@tonic-gate 
300210db224Sericheng 	if (len < STRUCT_SIZE(rem_arg) + (nports * sizeof (laioc_port_t)))
301210db224Sericheng 		return (EINVAL);
3027c478bd9Sstevel@tonic-gate 
303210db224Sericheng 	ports = (laioc_port_t *)(STRUCT_BUF(rem_arg) + 1);
3047c478bd9Sstevel@tonic-gate 
3057c478bd9Sstevel@tonic-gate 	rc = aggr_grp_rem_ports(STRUCT_FGET(rem_arg, la_key),
3067c478bd9Sstevel@tonic-gate 	    nports, ports);
3077c478bd9Sstevel@tonic-gate 
308210db224Sericheng 	freemsg(mp->b_cont);
309210db224Sericheng 	mp->b_cont = NULL;
3107c478bd9Sstevel@tonic-gate 	return (rc);
3117c478bd9Sstevel@tonic-gate }
3127c478bd9Sstevel@tonic-gate 
313210db224Sericheng void
314210db224Sericheng aggr_ioctl(queue_t *wq, mblk_t *mp)
3157c478bd9Sstevel@tonic-gate {
316210db224Sericheng 	struct iocblk *iocp = (struct iocblk *)mp->b_rptr;
317210db224Sericheng 	int i, err = EINVAL;
318210db224Sericheng 	mblk_t *nmp;
3197c478bd9Sstevel@tonic-gate 
320210db224Sericheng 	if (mp->b_cont == NULL)
321210db224Sericheng 		goto done;
322210db224Sericheng 
323210db224Sericheng 	/*
324210db224Sericheng 	 * Construct contiguous message
325210db224Sericheng 	 */
326210db224Sericheng 	if ((nmp = msgpullup(mp->b_cont, -1)) == NULL) {
327210db224Sericheng 		err = ENOMEM;
328210db224Sericheng 		goto done;
3297c478bd9Sstevel@tonic-gate 	}
3307c478bd9Sstevel@tonic-gate 
331210db224Sericheng 	freemsg(mp->b_cont);
332210db224Sericheng 	mp->b_cont = nmp;
333210db224Sericheng 
334210db224Sericheng 	for (i = 0; i < IOC_CMD_SZ; i++) {
335210db224Sericheng 		if (iocp->ioc_cmd == ioc_cmd[i].ic_cmd) {
336210db224Sericheng 			err = ioc_cmd[i].ic_func(mp, (int)iocp->ioc_flag);
337210db224Sericheng 			break;
338210db224Sericheng 		}
339210db224Sericheng 	}
340210db224Sericheng 
341210db224Sericheng 	if (err == 0) {
342210db224Sericheng 		int len = 0;
343210db224Sericheng 
344210db224Sericheng 		if (mp->b_cont != NULL) {
345210db224Sericheng 			len = MBLKL(mp->b_cont);
346210db224Sericheng 		}
347210db224Sericheng 		miocack(wq, mp, len, 0);
348210db224Sericheng 		return;
349210db224Sericheng 	}
350210db224Sericheng 
351210db224Sericheng done:
352210db224Sericheng 	miocnak(wq, mp, 0, err);
3537c478bd9Sstevel@tonic-gate }
354