xref: /onnv-gate/usr/src/uts/sun4u/starcat/io/dman.c (revision 11311:639e7bc0b42f)
11708Sstevel /*
21708Sstevel  * CDDL HEADER START
31708Sstevel  *
41708Sstevel  * The contents of this file are subject to the terms of the
51708Sstevel  * Common Development and Distribution License (the "License").
61708Sstevel  * You may not use this file except in compliance with the License.
71708Sstevel  *
81708Sstevel  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
91708Sstevel  * or http://www.opensolaris.org/os/licensing.
101708Sstevel  * See the License for the specific language governing permissions
111708Sstevel  * and limitations under the License.
121708Sstevel  *
131708Sstevel  * When distributing Covered Code, include this CDDL HEADER in each
141708Sstevel  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
151708Sstevel  * If applicable, add the following below this CDDL HEADER, with the
161708Sstevel  * fields enclosed by brackets "[]" replaced with your own identifying
171708Sstevel  * information: Portions Copyright [yyyy] [name of copyright owner]
181708Sstevel  *
191708Sstevel  * CDDL HEADER END
201708Sstevel  */
211708Sstevel 
221708Sstevel /*
2311066Srafael.vanoni@sun.com  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
241708Sstevel  * Use is subject to license terms.
251708Sstevel  */
261708Sstevel 
271708Sstevel 
281708Sstevel /*
291708Sstevel  * Starcat Management Network Driver
301708Sstevel  *
311708Sstevel  * ****** NOTICE **** This file also resides in the SSC gate as
321708Sstevel  * ****** NOTICE **** usr/src/uts/sun4u/scman/scman.c. Any changes
331708Sstevel  * ****** NOTICE **** made here must be propogated there as well.
341708Sstevel  *
351708Sstevel  */
361708Sstevel 
371708Sstevel #include <sys/types.h>
381708Sstevel #include <sys/proc.h>
391708Sstevel #include <sys/disp.h>
401708Sstevel #include <sys/kmem.h>
411708Sstevel #include <sys/stat.h>
421708Sstevel #include <sys/kstat.h>
431708Sstevel #include <sys/ksynch.h>
441708Sstevel #include <sys/stream.h>
451708Sstevel #include <sys/dlpi.h>
461708Sstevel #include <sys/stropts.h>
471708Sstevel #include <sys/strsubr.h>
481708Sstevel #include <sys/debug.h>
491708Sstevel #include <sys/conf.h>
501708Sstevel #include <sys/kstr.h>
511708Sstevel #include <sys/errno.h>
521708Sstevel #include <sys/ethernet.h>
531708Sstevel #include <sys/byteorder.h>
541708Sstevel #include <sys/ddi.h>
551708Sstevel #include <sys/sunddi.h>
561708Sstevel #include <sys/sunldi.h>
571708Sstevel #include <sys/modctl.h>
581708Sstevel #include <sys/strsun.h>
591708Sstevel #include <sys/callb.h>
601708Sstevel #include <sys/pci.h>
611708Sstevel #include <netinet/in.h>
621708Sstevel #include <inet/common.h>
631708Sstevel #include <inet/mi.h>
641708Sstevel #include <inet/nd.h>
651708Sstevel #include <sys/socket.h>
661708Sstevel #include <netinet/igmp_var.h>
671708Sstevel #include <netinet/ip6.h>
681708Sstevel #include <netinet/icmp6.h>
691708Sstevel #include <inet/ip.h>
701708Sstevel #include <inet/ip6.h>
711708Sstevel #include <sys/file.h>
721708Sstevel #include <sys/dman.h>
731708Sstevel #include <sys/autoconf.h>
741708Sstevel #include <sys/zone.h>
751708Sstevel 
761708Sstevel extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t);
771708Sstevel 
781708Sstevel #define	MAN_IDNAME	"dman"
791708Sstevel #define	DMAN_INT_PATH	"/devices/pseudo/dman@0:dman"
801708Sstevel #define	DMAN_PATH	"/devices/pseudo/clone@0:dman"
811708Sstevel #define	ERI_IDNAME	"eri"
821708Sstevel #define	ERI_PATH	"/devices/pseudo/clone@0:eri"
831708Sstevel 
841708Sstevel #if defined(DEBUG)
851708Sstevel 
861708Sstevel static void man_print_msp(manstr_t *);
871708Sstevel static void man_print_man(man_t *);
881708Sstevel static void man_print_mdp(man_dest_t *);
891708Sstevel static void man_print_dev(man_dev_t *);
901708Sstevel static void man_print_mip(mi_path_t *);
911708Sstevel static void man_print_mtp(mi_time_t *);
921708Sstevel static void man_print_mpg(man_pg_t *);
931708Sstevel static void man_print_path(man_path_t *);
941708Sstevel static void man_print_work(man_work_t *);
951708Sstevel 
961708Sstevel /*
971708Sstevel  * Set manstr_t dlpistate (upper half of multiplexor)
981708Sstevel  */
991708Sstevel #define	SETSTATE(msp, state) \
1001708Sstevel 	MAN_DBG(MAN_DLPI, ("msp=0x%p @ %d state %s=>%s\n",		\
1011708Sstevel 		    (void *)msp, __LINE__, dss[msp->ms_dlpistate],	\
1021708Sstevel 		    dss[(state)]));					\
1031708Sstevel 		    msp->ms_dlpistate = (state);
1041708Sstevel /*
1051708Sstevel  * Set man_dest_t dlpistate (lower half of multiplexor)
1061708Sstevel  */
1071708Sstevel #define	D_SETSTATE(mdp, state) \
1081708Sstevel 	MAN_DBG(MAN_DLPI, ("dst=0x%p @ %d state %s=>%s\n",	   \
1091708Sstevel 		    (void *)mdp, __LINE__, dss[mdp->md_dlpistate], \
1101708Sstevel 		    dss[(state)]));				   \
1111708Sstevel 		    mdp->md_dlpistate = (state);
1121708Sstevel 
1131708Sstevel static char *promisc[] = {	/* DLPI promisc Strings */
1141708Sstevel 	"not used",		/* 0x00 */
1151708Sstevel 	"DL_PROMISC_PHYS",	/* 0x01 */
1161708Sstevel 	"DL_PROMISC_SAP",	/* 0x02 */
1171708Sstevel 	"DL_PROMISC_MULTI"	/* 0x03 */
1181708Sstevel };
1191708Sstevel 
1201708Sstevel static char *dps[] = {			/* DLPI Primitive Strings */
1211708Sstevel 	"DL_INFO_REQ",			/* 0x00 */
1221708Sstevel 	"DL_BIND_REQ",			/* 0x01 */
1231708Sstevel 	"DL_UNBIND_REQ",		/* 0x02 */
1241708Sstevel 	"DL_INFO_ACK",			/* 0x03 */
1251708Sstevel 	"DL_BIND_ACK",			/* 0x04 */
1261708Sstevel 	"DL_ERROR_ACK",			/* 0x05 */
1271708Sstevel 	"DL_OK_ACK",			/* 0x06 */
1281708Sstevel 	"DL_UNITDATA_REQ",		/* 0x07 */
1291708Sstevel 	"DL_UNITDATA_IND",		/* 0x08 */
1301708Sstevel 	"DL_UDERROR_IND",		/* 0x09 */
1311708Sstevel 	"DL_UDQOS_REQ",			/* 0x0a */
1321708Sstevel 	"DL_ATTACH_REQ",		/* 0x0b */
1331708Sstevel 	"DL_DETACH_REQ",		/* 0x0c */
1341708Sstevel 	"DL_CONNECT_REQ",		/* 0x0d */
1351708Sstevel 	"DL_CONNECT_IND",		/* 0x0e */
1361708Sstevel 	"DL_CONNECT_RES",		/* 0x0f */
1371708Sstevel 	"DL_CONNECT_CON",		/* 0x10 */
1381708Sstevel 	"DL_TOKEN_REQ",			/* 0x11 */
1391708Sstevel 	"DL_TOKEN_ACK",			/* 0x12 */
1401708Sstevel 	"DL_DISCONNECT_REQ",		/* 0x13 */
1411708Sstevel 	"DL_DISCONNECT_IND",		/* 0x14 */
1421708Sstevel 	"DL_SUBS_UNBIND_REQ",		/* 0x15 */
1431708Sstevel 	"DL_LIARLIARPANTSONFIRE",	/* 0x16 */
1441708Sstevel 	"DL_RESET_REQ",			/* 0x17 */
1451708Sstevel 	"DL_RESET_IND",			/* 0x18 */
1461708Sstevel 	"DL_RESET_RES",			/* 0x19 */
1471708Sstevel 	"DL_RESET_CON",			/* 0x1a */
1481708Sstevel 	"DL_SUBS_BIND_REQ",		/* 0x1b */
1491708Sstevel 	"DL_SUBS_BIND_ACK",		/* 0x1c */
1501708Sstevel 	"DL_ENABMULTI_REQ",		/* 0x1d */
1511708Sstevel 	"DL_DISABMULTI_REQ",		/* 0x1e */
1521708Sstevel 	"DL_PROMISCON_REQ",		/* 0x1f */
1531708Sstevel 	"DL_PROMISCOFF_REQ",		/* 0x20 */
1541708Sstevel 	"DL_DATA_ACK_REQ",		/* 0x21 */
1551708Sstevel 	"DL_DATA_ACK_IND",		/* 0x22 */
1561708Sstevel 	"DL_DATA_ACK_STATUS_IND",	/* 0x23 */
1571708Sstevel 	"DL_REPLY_REQ",			/* 0x24 */
1581708Sstevel 	"DL_REPLY_IND",			/* 0x25 */
1591708Sstevel 	"DL_REPLY_STATUS_IND",		/* 0x26 */
1601708Sstevel 	"DL_REPLY_UPDATE_REQ",		/* 0x27 */
1611708Sstevel 	"DL_REPLY_UPDATE_STATUS_IND",	/* 0x28 */
1621708Sstevel 	"DL_XID_REQ",			/* 0x29 */
1631708Sstevel 	"DL_XID_IND",			/* 0x2a */
1641708Sstevel 	"DL_XID_RES",			/* 0x2b */
1651708Sstevel 	"DL_XID_CON",			/* 0x2c */
1661708Sstevel 	"DL_TEST_REQ",			/* 0x2d */
1671708Sstevel 	"DL_TEST_IND",			/* 0x2e */
1681708Sstevel 	"DL_TEST_RES",			/* 0x2f */
1691708Sstevel 	"DL_TEST_CON",			/* 0x30 */
1701708Sstevel 	"DL_PHYS_ADDR_REQ",		/* 0x31 */
1711708Sstevel 	"DL_PHYS_ADDR_ACK",		/* 0x32 */
1721708Sstevel 	"DL_SET_PHYS_ADDR_REQ",		/* 0x33 */
1731708Sstevel 	"DL_GET_STATISTICS_REQ",	/* 0x34 */
1741708Sstevel 	"DL_GET_STATISTICS_ACK",	/* 0x35 */
1751708Sstevel };
1761708Sstevel 
1771708Sstevel #define	MAN_DLPI_MAX_PRIM	0x35
1781708Sstevel 
1791708Sstevel static char *dss[] = {			/* DLPI State Strings */
1801708Sstevel 	"DL_UNBOUND",			/* 0x00	*/
1811708Sstevel 	"DL_BIND_PENDING",		/* 0x01	*/
1821708Sstevel 	"DL_UNBIND_PENDING",		/* 0x02	*/
1831708Sstevel 	"DL_IDLE",			/* 0x03	*/
1841708Sstevel 	"DL_UNATTACHED",		/* 0x04	*/
1851708Sstevel 	"DL_ATTACH_PENDING",		/* 0x05	*/
1861708Sstevel 	"DL_DETACH_PENDING",		/* 0x06	*/
1871708Sstevel 	"DL_UDQOS_PENDING",		/* 0x07	*/
1881708Sstevel 	"DL_OUTCON_PENDING",		/* 0x08	*/
1891708Sstevel 	"DL_INCON_PENDING",		/* 0x09	*/
1901708Sstevel 	"DL_CONN_RES_PENDING",		/* 0x0a	*/
1911708Sstevel 	"DL_DATAXFER",			/* 0x0b	*/
1921708Sstevel 	"DL_USER_RESET_PENDING",	/* 0x0c	*/
1931708Sstevel 	"DL_PROV_RESET_PENDING",	/* 0x0d	*/
1941708Sstevel 	"DL_RESET_RES_PENDING",		/* 0x0e	*/
1951708Sstevel 	"DL_DISCON8_PENDING",		/* 0x0f	*/
1961708Sstevel 	"DL_DISCON9_PENDING",		/* 0x10	*/
1971708Sstevel 	"DL_DISCON11_PENDING",		/* 0x11	*/
1981708Sstevel 	"DL_DISCON12_PENDING",		/* 0x12	*/
1991708Sstevel 	"DL_DISCON13_PENDING",		/* 0x13	*/
2001708Sstevel 	"DL_SUBS_BIND_PND",		/* 0x14	*/
2011708Sstevel 	"DL_SUBS_UNBIND_PND",		/* 0x15	*/
2021708Sstevel };
2031708Sstevel 
2041708Sstevel static const char *lss[] = {
2051708Sstevel 	"UNKNOWN",	/* 0x0 */
2061708Sstevel 	"INIT",		/* 0x1 */
2071708Sstevel 	"GOOD",		/* 0x2 */
2081708Sstevel 	"STALE",	/* 0x3 */
2091708Sstevel 	"FAIL",		/* 0x4 */
2101708Sstevel };
2111708Sstevel 
2121708Sstevel static char *_mw_type[] = {
2131708Sstevel 	"OPEN_CTL",		/* 0x0 */
2141708Sstevel 	"CLOSE_CTL",		/* 0x1 */
2151708Sstevel 	"SWITCH",		/* 0x2 */
2161708Sstevel 	"PATH_UPDATE",		/* 0x3 */
2171708Sstevel 	"CLOSE",		/* 0x4 */
2181708Sstevel 	"CLOSE_STREAM",	/* 0x5 */
2191708Sstevel 	"DRATTACH",		/* 0x6 */
2201708Sstevel 	"DRDETACH",		/* 0x7 */
2211708Sstevel 	"STOP",			/* 0x8 */
2221708Sstevel 	"DRSWITCH",		/* 0x9 */
2231708Sstevel 	"KSTAT_UPDATE"		/* 0xA */
2241708Sstevel };
2251708Sstevel 
2261708Sstevel uint32_t		man_debug = MAN_WARN;
2271708Sstevel 
2281708Sstevel #define	man_kzalloc(a, b)	man_dbg_kzalloc(__LINE__, a, b)
2291708Sstevel #define	man_kfree(a, b)		man_dbg_kfree(__LINE__, a, b)
2301708Sstevel void	*man_dbg_kzalloc(int line, size_t size, int kmflags);
2311708Sstevel void	man_dbg_kfree(int line, void *buf, size_t size);
2321708Sstevel 
2331708Sstevel #else	/* DEBUG */
2341708Sstevel 
2351708Sstevel uint32_t		man_debug = 0;
2361708Sstevel /*
2371708Sstevel  * Set manstr_t dlpistate (upper half of multiplexor)
2381708Sstevel  */
2391708Sstevel #define	SETSTATE(msp, state) msp->ms_dlpistate = (state);
2401708Sstevel /*
2411708Sstevel  * Set man_dest_t dlpistate (lower half of multiplexor)
2421708Sstevel  */
2431708Sstevel #define	D_SETSTATE(mdp, state) mdp->md_dlpistate = (state);
2441708Sstevel 
2451708Sstevel #define	man_kzalloc(a, b)	kmem_zalloc(a, b)
2461708Sstevel #define	man_kfree(a, b)		kmem_free(a, b)
2471708Sstevel 
2481708Sstevel #endif	/* DEBUG */
2491708Sstevel 
2501708Sstevel #define	DL_PRIM(mp)	(((union DL_primitives *)(mp)->b_rptr)->dl_primitive)
2511708Sstevel #define	DL_PROMISCON_TYPE(mp)	\
2521708Sstevel 		(((union DL_primitives *)(mp)->b_rptr)->promiscon_req.dl_level)
2531708Sstevel #define	IOC_CMD(mp)	(((struct iocblk *)(mp)->b_rptr)->ioc_cmd)
2541708Sstevel 
2551708Sstevel /*
2561708Sstevel  * Start of kstat-related declarations
2571708Sstevel  */
2581708Sstevel #define	MK_NOT_COUNTER		(1<<0)	/* is it a counter? */
2591708Sstevel #define	MK_ERROR		(1<<2)	/* for error statistics */
2601708Sstevel #define	MK_NOT_PHYSICAL		(1<<3)	/* no matching physical stat */
2611708Sstevel 
2621708Sstevel typedef struct man_kstat_info_s {
2631708Sstevel 	char		*mk_name;	/* e.g. align_errors */
2641708Sstevel 	char		*mk_physname;	/* e.g. framing (NULL for same) */
2651708Sstevel 	char		*mk_physalias;	/* e.g. framing (NULL for same) */
2661708Sstevel 	uchar_t		mk_type;	/* e.g. KSTAT_DATA_UINT32 */
2671708Sstevel 	int		mk_flags;
2681708Sstevel } man_kstat_info_t;
2691708Sstevel 
2701708Sstevel /*
2711708Sstevel  * Master declaration macro, note that it uses token pasting
2721708Sstevel  */
2731708Sstevel #define	MK_DECLARE(name, pname, palias, bits, flags) \
2741708Sstevel 	{ name,		pname,	palias,	KSTAT_DATA_UINT ## bits, flags }
2751708Sstevel 
2761708Sstevel /*
2771708Sstevel  * Obsolete forms don't have the _sinceswitch forms, they are all errors
2781708Sstevel  */
2791708Sstevel #define	MK_OBSOLETE32(name, alias) MK_DECLARE(alias, name, alias, 32, MK_ERROR)
2801708Sstevel #define	MK_OBSOLETE64(name, alias) MK_DECLARE(alias, name, alias, 64, MK_ERROR)
2811708Sstevel 
2821708Sstevel /*
2831708Sstevel  * The only non-counters don't have any other aliases
2841708Sstevel  */
2851708Sstevel #define	MK_NOTCOUNTER32(name) MK_DECLARE(name, name, NULL, 32, MK_NOT_COUNTER)
2861708Sstevel #define	MK_NOTCOUNTER64(name) MK_DECLARE(name, name, NULL, 64, MK_NOT_COUNTER)
2871708Sstevel 
2881708Sstevel /*
2891708Sstevel  * Normal counter forms
2901708Sstevel  */
2911708Sstevel #define	MK_DECLARE32(name, alias) \
2921708Sstevel 	MK_DECLARE(name, name, alias, 32, 0)
2931708Sstevel #define	MK_DECLARE64(name, alias) \
2941708Sstevel 	MK_DECLARE(name, name, alias, 64, 0)
2951708Sstevel 
2961708Sstevel /*
2971708Sstevel  * Error counters need special MK_ERROR flag only for the non-AP form
2981708Sstevel  */
2991708Sstevel #define	MK_ERROR32(name, alias) \
3001708Sstevel 	MK_DECLARE(name, name, alias, 32, MK_ERROR)
3011708Sstevel #define	MK_ERROR64(name, alias) \
3021708Sstevel 	MK_DECLARE(name, name, alias, 64, MK_ERROR)
3031708Sstevel 
3041708Sstevel /*
3051708Sstevel  * These AP-specific stats are not backed by physical statistics
3061708Sstevel  */
3071708Sstevel #define	MK_NOTPHYS32(name) MK_DECLARE(name, NULL, NULL, 32, MK_NOT_PHYSICAL)
3081708Sstevel #define	MK_NOTPHYS64(name) MK_DECLARE(name, NULL, NULL, 64, MK_NOT_PHYSICAL)
3091708Sstevel 
3101708Sstevel /*
3111708Sstevel  * START of the actual man_kstat_info declaration using above macros
3121708Sstevel  */
3131708Sstevel static man_kstat_info_t man_kstat_info[] = {
3141708Sstevel 	/*
3151708Sstevel 	 * Link Input/Output stats
3161708Sstevel 	 */
3171708Sstevel 	MK_DECLARE32("ipackets", NULL),
3181708Sstevel 	MK_ERROR32("ierrors", NULL),
3191708Sstevel 	MK_DECLARE32("opackets", NULL),
3201708Sstevel 	MK_ERROR32("oerrors", NULL),
3211708Sstevel 	MK_ERROR32("collisions", NULL),
3221708Sstevel 	MK_NOTCOUNTER64("ifspeed"),
3231708Sstevel 	/*
3241708Sstevel 	 * These are new MIB-II stats, per PSARC 1997/198
3251708Sstevel 	 */
3261708Sstevel 	MK_DECLARE32("rbytes", NULL),
3271708Sstevel 	MK_DECLARE32("obytes", NULL),
3281708Sstevel 	MK_DECLARE32("multircv", NULL),
3291708Sstevel 	MK_DECLARE32("multixmt", NULL),
3301708Sstevel 	MK_DECLARE32("brdcstrcv", NULL),
3311708Sstevel 	MK_DECLARE32("brdcstxmt", NULL),
3321708Sstevel 	/*
3331708Sstevel 	 * Error values
3341708Sstevel 	 */
3351708Sstevel 	MK_ERROR32("norcvbuf", NULL),
3361708Sstevel 	MK_ERROR32("noxmtbuf", NULL),
3371708Sstevel 	MK_ERROR32("unknowns", NULL),
3381708Sstevel 	/*
3391708Sstevel 	 * These are the 64-bit values, they fallback to 32-bit values
3401708Sstevel 	 */
3411708Sstevel 	MK_DECLARE64("ipackets64", "ipackets"),
3421708Sstevel 	MK_DECLARE64("opackets64", "opackets"),
3431708Sstevel 	MK_DECLARE64("rbytes64", "rbytes"),
3441708Sstevel 	MK_DECLARE64("obytes64", "obytes"),
3451708Sstevel 
3461708Sstevel 	/* New AP switching statistics */
3471708Sstevel 	MK_NOTPHYS64("man_switches"),
3481708Sstevel 	MK_NOTPHYS64("man_link_fails"),
3491708Sstevel 	MK_NOTPHYS64("man_link_stales"),
3501708Sstevel 	MK_NOTPHYS64("man_icmpv4_probes"),
3511708Sstevel 	MK_NOTPHYS64("man_icmpv6_probes"),
3521708Sstevel 
3531708Sstevel 	MK_ERROR32("align_errors", "framing"),
3541708Sstevel 	MK_ERROR32("fcs_errors", "crc"),
3551708Sstevel 	MK_ERROR32("first_collisions", NULL),
3561708Sstevel 	MK_ERROR32("multi_collisions", NULL),
3571708Sstevel 	MK_ERROR32("sqe_errors", "sqe"),
3581708Sstevel 
3591708Sstevel 	MK_ERROR32("tx_late_collisions", NULL),
3601708Sstevel 	MK_ERROR32("ex_collisions", "excollisions"),
3611708Sstevel 	MK_ERROR32("macxmt_errors", NULL),
3621708Sstevel 	MK_ERROR32("carrier_errors", "nocarrier"),
3631708Sstevel 	MK_ERROR32("toolong_errors", "buff"),
3641708Sstevel 	MK_ERROR32("macrcv_errors", NULL),
3651708Sstevel 
3661708Sstevel 	MK_OBSOLETE32("framing", "align_errors"),
3671708Sstevel 	MK_OBSOLETE32("crc", "fcs_errors"),
3681708Sstevel 	MK_OBSOLETE32("sqe", "sqe_errors"),
3691708Sstevel 	MK_OBSOLETE32("excollisions", "ex_collisions"),
3701708Sstevel 	MK_OBSOLETE32("nocarrier", "carrier_errors"),
3711708Sstevel 	MK_OBSOLETE32("buff", "toolong_errors"),
3721708Sstevel };
3731708Sstevel 
3741708Sstevel #define	MAN_NUMSTATS (sizeof (man_kstat_info) / sizeof (man_kstat_info_t))
3751708Sstevel 
3761708Sstevel /*
3771708Sstevel  * Miscellaneous ethernet stuff.
3781708Sstevel  *
3791708Sstevel  * MANs DL_INFO_ACK template.
3801708Sstevel  */
3811708Sstevel static	dl_info_ack_t man_infoack = {
3821708Sstevel 	DL_INFO_ACK,				/* dl_primitive */
3831708Sstevel 	ETHERMTU,				/* dl_max_sdu */
3841708Sstevel 	0,					/* dl_min_sdu */
3851708Sstevel 	MAN_ADDRL,				/* dl_addr_length */
3861708Sstevel 	DL_ETHER,				/* dl_mac_type */
3871708Sstevel 	0,					/* dl_reserved */
3881708Sstevel 	0,					/* dl_current_state */
3891708Sstevel 	-2,					/* dl_sap_length */
3901708Sstevel 	DL_CLDLS,				/* dl_service_mode */
3911708Sstevel 	0,					/* dl_qos_length */
3921708Sstevel 	0,					/* dl_qos_offset */
3931708Sstevel 	0,					/* dl_range_length */
3941708Sstevel 	0,					/* dl_range_offset */
3951708Sstevel 	DL_STYLE2,				/* dl_provider_style */
3961708Sstevel 	sizeof (dl_info_ack_t),			/* dl_addr_offset */
3971708Sstevel 	DL_VERSION_2,				/* dl_version */
3981708Sstevel 	ETHERADDRL,				/* dl_brdcst_addr_length */
3991708Sstevel 	sizeof (dl_info_ack_t) + MAN_ADDRL,	/* dl_brdcst_addr_offset */
4001708Sstevel 	0					/* dl_growth */
4011708Sstevel };
4021708Sstevel 
4031708Sstevel /*
4041708Sstevel  * Ethernet broadcast address definition.
4051708Sstevel  */
4061708Sstevel static	struct ether_addr	etherbroadcast = {
4071708Sstevel 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff
4081708Sstevel };
4091708Sstevel 
4101708Sstevel static struct ether_addr zero_ether_addr = {
4111708Sstevel 	0x00, 0x00, 0x00, 0x00, 0x00, 0x00
4121708Sstevel };
4131708Sstevel 
4141708Sstevel /*
4151708Sstevel  * Set via MAN_SET_SC_IPADDRS ioctl.
4161708Sstevel  */
4171708Sstevel man_sc_ipaddrs_t	man_sc_ipaddrs = { 0xffffffffU, 0xffffffffU };
4181708Sstevel 
4191708Sstevel /*
4201708Sstevel  * Set via MAN_SET_SC_IP6ADDRS ioctl.
4211708Sstevel  */
4221708Sstevel man_sc_ip6addrs_t	man_sc_ip6addrs = { 0, 0, 0, 0, 0, 0, 0, 0 };
4231708Sstevel 
4241708Sstevel /*
4251708Sstevel  * IP & ICMP constants
4261708Sstevel  */
4271708Sstevel #ifndef	ETHERTYPE_IPV6
4281708Sstevel #define	ETHERTYPE_IPV6 0x86DD
4291708Sstevel #endif
4301708Sstevel 
4311708Sstevel /*
4321708Sstevel  * Function prototypes.
4331708Sstevel  *
4341708Sstevel  * Upper multiplexor functions.
4351708Sstevel  */
4361708Sstevel static int	man_attach(dev_info_t *, ddi_attach_cmd_t);
4371708Sstevel static int	man_detach(dev_info_t *, ddi_detach_cmd_t);
4381708Sstevel static int	man_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
4391708Sstevel static int	man_open(register queue_t *, dev_t *, int, int, cred_t *);
4401708Sstevel static int	man_configure(queue_t *);
4411708Sstevel static int	man_deconfigure(void);
4421708Sstevel static int	man_init_dests(man_t *, manstr_t *);
4431708Sstevel static void	man_start_dest(man_dest_t *, manstr_t *, man_pg_t *);
4441708Sstevel static void	man_set_optimized_dest(manstr_t *);
4451708Sstevel static int	man_close(queue_t *);
4461708Sstevel static void	man_cancel_timers(man_adest_t *);
4471708Sstevel static int	man_uwput(queue_t *, mblk_t *);
4481708Sstevel static int	man_start(queue_t *, mblk_t *, eaddr_t *);
4491708Sstevel static void	man_ioctl(queue_t *, mblk_t *);
4501708Sstevel static void	man_set_linkcheck_time(queue_t *, mblk_t *);
4511708Sstevel static void	man_setpath(queue_t *, mblk_t *);
4521708Sstevel static void	man_geteaddr(queue_t *, mblk_t *);
4531708Sstevel static void	man_set_sc_ipaddrs(queue_t *, mblk_t *);
4541708Sstevel static void	man_set_sc_ip6addrs(queue_t *, mblk_t *);
4551708Sstevel static int	man_get_our_etheraddr(eaddr_t *eap);
4561708Sstevel static void	man_nd_getset(queue_t *, mblk_t *);
4571708Sstevel static void	man_dl_ioc_hdr_info(queue_t *, mblk_t *);
4581708Sstevel static int	man_uwsrv(queue_t *);
4591708Sstevel static int	man_proto(queue_t *, mblk_t *);
4601708Sstevel static int	man_udreq(queue_t *, mblk_t *);
4611708Sstevel static void	man_areq(queue_t *, mblk_t *);
4621708Sstevel static mblk_t	*man_alloc_physreq_mp(eaddr_t *);
4631708Sstevel static void	man_dreq(queue_t *, mblk_t *);
4641708Sstevel static void	man_dodetach(manstr_t *, man_work_t *);
4651708Sstevel static void	man_dl_clean(mblk_t **);
4661708Sstevel static void	man_breq(queue_t *, mblk_t *);
4671708Sstevel static void	man_ubreq(queue_t *, mblk_t *);
4681708Sstevel static void	man_ireq(queue_t *, mblk_t *);
4691708Sstevel static void	man_ponreq(queue_t *, mblk_t *);
4701708Sstevel static void	man_poffreq(queue_t *, mblk_t *);
4711708Sstevel static void	man_emreq(queue_t *, mblk_t *);
4721708Sstevel static void	man_dmreq(queue_t *, mblk_t *);
4731708Sstevel static void	man_pareq(queue_t *, mblk_t *);
4741708Sstevel static void	man_spareq(queue_t *, mblk_t *);
4751708Sstevel static int	man_dlpi(manstr_t *, mblk_t *);
4761708Sstevel static int	man_dlioc(manstr_t *, mblk_t *);
4771708Sstevel static int	man_dl_catch(mblk_t **, mblk_t *);
4781708Sstevel static void	man_dl_release(mblk_t **, mblk_t *);
4791708Sstevel static int	man_match_proto(mblk_t *, mblk_t *);
4801708Sstevel static int	man_open_ctl();
4811708Sstevel static void	man_close_ctl();
4821708Sstevel /*
4831708Sstevel  * upper/lower multiplexor functions.
4841708Sstevel  */
4851708Sstevel static int	man_dlpi_senddown(manstr_t *, mblk_t *);
4861708Sstevel static int	man_start_lower(man_dest_t *, mblk_t *, queue_t *, int caller);
4871708Sstevel static int	man_lrput(queue_t *, mblk_t *);
4881708Sstevel /*
4891708Sstevel  * Lower multiplexor functions.
4901708Sstevel  */
4911708Sstevel static int	man_lwsrv(queue_t *);
4921708Sstevel static int	man_lrsrv(queue_t *);
4931708Sstevel static void	man_dlpi_replay(man_dest_t *, mblk_t *);
4941708Sstevel static int	man_dlioc_replay(man_dest_t *);
4951708Sstevel /*
4961708Sstevel  * Link failover routines.
4971708Sstevel  */
4981708Sstevel static int	man_gettimer(int, man_dest_t *);
4991708Sstevel static void	man_linkcheck_timer(void *);
5001708Sstevel static int	man_needs_linkcheck(man_dest_t *);
5011708Sstevel static int	man_do_autoswitch(man_dest_t *);
5021708Sstevel static int	man_autoswitch(man_pg_t *, man_dev_t *, man_work_t *);
5031708Sstevel static int	man_prep_dests_for_switch(man_pg_t *, man_dest_t **, int *);
5041708Sstevel static int	man_str_uses_pg(manstr_t *, man_pg_t *);
5051708Sstevel static void	man_do_icmp_bcast(man_dest_t *, t_uscalar_t);
5061708Sstevel static mblk_t	*man_alloc_udreq(int, man_dladdr_t *);
5071708Sstevel static mblk_t	*man_pinger(t_uscalar_t);
5081708Sstevel /*
5091708Sstevel  * Functions normally executing outside of the STREAMs perimeter.
5101708Sstevel  */
5111708Sstevel /*
5121708Sstevel  * Functions supporting/processing work requests.
5131708Sstevel  */
5141708Sstevel static void	man_bwork(void);
5151708Sstevel static void	man_iwork(void);		/* inside perimeter */
5161708Sstevel void		man_work_add(man_workq_t *, man_work_t *);
5171708Sstevel man_work_t	*man_work_alloc(int, int);
5181708Sstevel void		man_work_free(man_work_t *);
5191708Sstevel /*
5201708Sstevel  * Functions implementing/supporting failover.
5211708Sstevel  *
5221708Sstevel  * Executed inside perimeter.
5231708Sstevel  */
5241708Sstevel static int	man_do_dr_attach(man_work_t *);
5251708Sstevel static int	man_do_dr_switch(man_work_t *);
5261708Sstevel static void	man_do_dr_detach(man_work_t *);
5271708Sstevel static int	man_iswitch(man_work_t *);
5281708Sstevel static void	man_ifail_dest(man_dest_t *);
5291708Sstevel static man_dest_t *man_switch_match(man_dest_t *, int, void *);
5301708Sstevel static void	man_add_dests(man_pg_t *);
5311708Sstevel static void	man_reset_dlpi(void *);
5321708Sstevel static mblk_t	*man_dup_mplist(mblk_t *);
5331708Sstevel static mblk_t	*man_alloc_ubreq_dreq();
5341708Sstevel /*
5351708Sstevel  * Executed outside perimeter (us man_lock for synchronization).
5361708Sstevel  */
5371708Sstevel static void	man_bclose(man_adest_t *);
5381708Sstevel static void	man_bswitch(man_adest_t *, man_work_t *);
5391708Sstevel static int	man_plumb(man_dest_t *);
5401708Sstevel static void	man_unplumb(man_dest_t *);
5411708Sstevel static void	man_plink(queue_t *, mblk_t *);
5421708Sstevel static void	man_unplink(queue_t *, mblk_t *);
5431708Sstevel static void	man_linkrec_insert(man_linkrec_t *);
5441708Sstevel static queue_t	*man_linkrec_find(int);
5451708Sstevel /*
5461708Sstevel  * Functions supporting pathgroups
5471708Sstevel  */
5481708Sstevel int	man_pg_cmd(mi_path_t *, man_work_t *);
5491708Sstevel static int	man_pg_assign(man_pg_t **, mi_path_t *, int);
5501708Sstevel static int	man_pg_create(man_pg_t **, man_pg_t **, mi_path_t *);
5511708Sstevel static int	man_pg_unassign(man_pg_t **, mi_path_t *);
5521708Sstevel static int	man_pg_activate(man_t *, mi_path_t *, man_work_t *);
5531708Sstevel static int	man_pg_read(man_pg_t *, mi_path_t *);
5541708Sstevel static man_pg_t	*man_find_path_by_dev(man_pg_t *, man_dev_t *, man_path_t **);
5551708Sstevel static man_pg_t	*man_find_pg_by_id(man_pg_t *, int);
5561708Sstevel static man_path_t	*man_find_path_by_ppa(man_path_t *, int);
5571708Sstevel static man_path_t	*man_find_active_path(man_path_t *);
5581708Sstevel static man_path_t	*man_find_alternate_path(man_path_t *);
5591708Sstevel static void	man_path_remove(man_path_t **, man_path_t *);
5601708Sstevel static void	man_path_insert(man_path_t **, man_path_t *);
5611708Sstevel static void	man_path_merge(man_path_t **, man_path_t *);
5621708Sstevel static int	man_path_kstat_init(man_path_t *);
5631708Sstevel static void	man_path_kstat_uninit(man_path_t *);
5641708Sstevel /*
5651708Sstevel  * Functions supporting kstat reporting.
5661708Sstevel  */
5671708Sstevel static int	man_kstat_update(kstat_t *, int);
5681708Sstevel static void	man_do_kstats(man_work_t *);
5691708Sstevel static void	man_update_path_kstats(man_t *);
5701708Sstevel static void 	man_update_dev_kstats(kstat_named_t *, man_path_t *);
5711708Sstevel static void	man_sum_dests_kstats(kstat_named_t *, man_pg_t *);
5721708Sstevel static void	man_kstat_named_init(kstat_named_t *, int);
5731708Sstevel static int	man_kstat_byname(kstat_t *, char *, kstat_named_t *);
5741708Sstevel static void	man_sum_kstats(kstat_named_t *, kstat_t *, kstat_named_t *);
5751708Sstevel /*
5761708Sstevel  * Functions supporting ndd.
5771708Sstevel  */
5781708Sstevel static int	man_param_register(param_t *, int);
5791708Sstevel static int	man_pathgroups_report(queue_t *, mblk_t *, caddr_t, cred_t *);
5801708Sstevel static void	man_preport(man_path_t *, mblk_t *);
5811708Sstevel static int	man_set_active_path(queue_t *, mblk_t *, char *, caddr_t,
5821708Sstevel 			cred_t *);
5831708Sstevel static int	man_get_hostinfo(queue_t *, mblk_t *, caddr_t, cred_t *);
5841708Sstevel static char	*man_inet_ntoa(in_addr_t);
5851708Sstevel static int	man_param_get(queue_t *, mblk_t *, caddr_t, cred_t *);
5861708Sstevel static int	man_param_set(queue_t *, mblk_t *, char *, caddr_t, cred_t *);
5871708Sstevel static  void    man_param_cleanup(void);
5881708Sstevel static  void    man_nd_free(caddr_t *nd_pparam);
5891708Sstevel /*
5901708Sstevel  * MAN SSC/Domain specific externs.
5911708Sstevel  */
5921708Sstevel extern int	man_get_iosram(manc_t *);
5931708Sstevel extern int	man_domain_configure(void);
5941708Sstevel extern int	man_domain_deconfigure(void);
5951708Sstevel extern int	man_dossc_switch(uint32_t);
5961708Sstevel extern int	man_is_on_domain;
5971708Sstevel 
5981708Sstevel /*
5991708Sstevel  * Driver Globals protected by inner perimeter.
6001708Sstevel  */
6011708Sstevel static manstr_t	*man_strup = NULL;	/* list of MAN STREAMS */
6021708Sstevel static caddr_t	man_ndlist = NULL;	/* head of ndd var list */
6031708Sstevel void		*man_softstate = NULL;
6041708Sstevel 
6051708Sstevel /*
6061708Sstevel  * Driver globals protected by man_lock.
6071708Sstevel  */
6081708Sstevel kmutex_t		man_lock;		/* lock protecting vars below */
6091708Sstevel static kthread_id_t	man_bwork_id = NULL;	/* background thread ID */
6101708Sstevel man_workq_t		*man_bwork_q;		/* bgthread work q */
6111708Sstevel man_workq_t		*man_iwork_q;		/* inner perim (uwsrv) work q */
6121708Sstevel static man_linkrec_t	*man_linkrec_head = NULL;	/* list of linkblks */
6131708Sstevel ldi_handle_t		man_ctl_lh = NULL;	/* MAN control handle */
6141708Sstevel queue_t			*man_ctl_wq = NULL;	/* MAN control rq */
6151708Sstevel static int		man_config_state = MAN_UNCONFIGURED;
6161708Sstevel static int		man_config_error = ENODEV;
6171708Sstevel 
6181708Sstevel /*
6191708Sstevel  * These parameters are accessed via ndd to report the link configuration
6201708Sstevel  * for the MAN driver. They can also be used to force configuration changes.
6211708Sstevel  */
6221708Sstevel #define	MAN_NOTUSR	0x0f000000
6231708Sstevel 
6241708Sstevel /* ------------------------------------------------------------------------- */
6251708Sstevel 
6261708Sstevel static  param_t	man_param_arr[] = {
6271708Sstevel 	/* min		max		value		name */
6281708Sstevel 	{  0,		0xFFFF,		0,		"man_debug_level"},
6291708Sstevel };
6301708Sstevel 
6311708Sstevel #define	MAN_NDD_GETABLE	1
6321708Sstevel #define	MAN_NDD_SETABLE	2
6331708Sstevel 
6341708Sstevel static  uint32_t	man_param_display[] = {
6351708Sstevel /* DISPLAY */
6361708Sstevel MAN_NDD_SETABLE,	/* man_debug_level */
6371708Sstevel };
6381708Sstevel 
6391708Sstevel /*
6401708Sstevel  * STREAMs information.
6411708Sstevel  */
6421708Sstevel static struct module_info man_m_info = {
6431708Sstevel 	MAN_IDNUM,			/* mi_idnum */
6441708Sstevel 	MAN_IDNAME,			/* mi_idname */
6451708Sstevel 	MAN_MINPSZ,			/* mi_minpsz */
6461708Sstevel 	MAN_MAXPSZ,			/* mi_maxpsz */
6471708Sstevel 	MAN_HIWAT,			/* mi_hiwat */
6481708Sstevel 	MAN_LOWAT			/* mi_lowat */
6491708Sstevel };
6501708Sstevel 
6511708Sstevel /*
6521708Sstevel  * Upper read queue does not do anything.
6531708Sstevel  */
6541708Sstevel static struct qinit man_urinit = {
6551708Sstevel 	NULL,				/* qi_putp */
6561708Sstevel 	NULL,				/* qi_srvp */
6571708Sstevel 	man_open,			/* qi_qopen */
6581708Sstevel 	man_close,			/* qi_qclose */
6591708Sstevel 	NULL,				/* qi_qadmin */
6601708Sstevel 	&man_m_info,			/* qi_minfo */
6611708Sstevel 	NULL				/* qi_mstat */
6621708Sstevel };
6631708Sstevel 
6641708Sstevel static struct qinit man_lrinit = {
6651708Sstevel 	man_lrput,			/* qi_putp */
6661708Sstevel 	man_lrsrv,			/* qi_srvp */
6671708Sstevel 	man_open,			/* qi_qopen */
6681708Sstevel 	man_close,			/* qi_qclose */
6691708Sstevel 	NULL,				/* qi_qadmin */
6701708Sstevel 	&man_m_info,			/* qi_minfo */
6711708Sstevel 	NULL				/* qi_mstat */
6721708Sstevel };
6731708Sstevel 
6741708Sstevel static struct qinit man_uwinit = {
6751708Sstevel 	man_uwput,			/* qi_putp */
6761708Sstevel 	man_uwsrv,			/* qi_srvp */
6771708Sstevel 	man_open,			/* qi_qopen */
6781708Sstevel 	man_close,			/* qi_qclose */
6791708Sstevel 	NULL,				/* qi_qadmin */
6801708Sstevel 	&man_m_info,			/* qi_minfo */
6811708Sstevel 	NULL				/* qi_mstat */
6821708Sstevel };
6831708Sstevel 
6841708Sstevel static struct qinit man_lwinit = {
6851708Sstevel 	NULL,				/* qi_putp */
6861708Sstevel 	man_lwsrv,			/* qi_srvp */
6871708Sstevel 	man_open,			/* qi_qopen */
6881708Sstevel 	man_close,			/* qi_qclose */
6891708Sstevel 	NULL,				/* qi_qadmin */
6901708Sstevel 	&man_m_info,			/* qi_minfo */
6911708Sstevel 	NULL				/* qi_mstat */
6921708Sstevel };
6931708Sstevel 
6941708Sstevel static struct streamtab man_maninfo = {
6951708Sstevel 	&man_urinit,			/* st_rdinit */
6961708Sstevel 	&man_uwinit,			/* st_wrinit */
6971708Sstevel 	&man_lrinit,			/* st_muxrinit */
6981708Sstevel 	&man_lwinit			/* st_muxwrinit */
6991708Sstevel };
7001708Sstevel 
7011708Sstevel 
7021708Sstevel /*
7031708Sstevel  * Module linkage information for the kernel.
7041708Sstevel  *
7051708Sstevel  * Locking Theory:
7061708Sstevel  * 	D_MTPERMOD -	Only an inner perimeter: All routines single
7071708Sstevel  * 			threaded (except put, see below).
7081708Sstevel  *	D_MTPUTSHARED -	Put routines enter inner perimeter shared (not
7091708Sstevel  *			exclusive) for concurrency/performance reasons.
7101708Sstevel  *
7111708Sstevel  *	Anyone who needs exclusive outer perimeter permission (changing
7121708Sstevel  *	global data structures) does so via qwriter() calls. The
7131708Sstevel  *	background thread does all his work outside of perimeter and
7141708Sstevel  *	submits work via qtimeout() when data structures need to be
7151708Sstevel  *	modified.
7161708Sstevel  */
7171708Sstevel 
7181708Sstevel #define	MAN_MDEV_FLAGS	(D_MP|D_MTPERMOD|D_MTPUTSHARED)
7191708Sstevel 
7201708Sstevel DDI_DEFINE_STREAM_OPS(man_ops, nulldev, nulldev, man_attach,
7217656SSherry.Moore@Sun.COM     man_detach, nodev, man_info, MAN_MDEV_FLAGS, &man_maninfo,
7227656SSherry.Moore@Sun.COM     ddi_quiesce_not_supported);
7231708Sstevel 
7241708Sstevel extern int nodev(), nulldev();
7251708Sstevel 
7261708Sstevel static struct modldrv modldrv = {
7271708Sstevel 	&mod_driverops, 	/* Module type.  This one is a pseudo driver */
7287656SSherry.Moore@Sun.COM 	"MAN MetaDriver",
7291708Sstevel 	&man_ops,		/* driver ops */
7301708Sstevel };
7311708Sstevel 
7321708Sstevel static struct modlinkage modlinkage = {
7331708Sstevel 	MODREV_1,
7341708Sstevel 	(void *) &modldrv,
7351708Sstevel 	NULL
7361708Sstevel };
7371708Sstevel 
7381708Sstevel 
7391708Sstevel /* Virtual Driver loader entry points */
7401708Sstevel 
7411708Sstevel int
_init(void)7421708Sstevel _init(void)
7431708Sstevel {
7441708Sstevel 	int		status = DDI_FAILURE;
7451708Sstevel 
7461708Sstevel 	MAN_DBG(MAN_INIT, ("_init:"));
7471708Sstevel 
7481708Sstevel 	status = mod_install(&modlinkage);
7491708Sstevel 	if (status != 0) {
7501708Sstevel 		cmn_err(CE_WARN, "man_init: mod_install failed"
7517656SSherry.Moore@Sun.COM 		    " error = %d", status);
7521708Sstevel 		return (status);
7531708Sstevel 	}
7541708Sstevel 
7551708Sstevel 	status = ddi_soft_state_init(&man_softstate, sizeof (man_t), 4);
7561708Sstevel 	if (status != 0) {
7571708Sstevel 		cmn_err(CE_WARN, "man_init: ddi_soft_state_init failed"
7587656SSherry.Moore@Sun.COM 		    " error = %d", status);
759*11311SSurya.Prakki@Sun.COM 		(void) mod_remove(&modlinkage);
7601708Sstevel 		return (status);
7611708Sstevel 	}
7621708Sstevel 
7631708Sstevel 	man_bwork_q = man_kzalloc(sizeof (man_workq_t), KM_SLEEP);
7641708Sstevel 	man_iwork_q = man_kzalloc(sizeof (man_workq_t), KM_SLEEP);
7651708Sstevel 
7661708Sstevel 	mutex_init(&man_lock, NULL, MUTEX_DRIVER, NULL);
7671708Sstevel 	cv_init(&man_bwork_q->q_cv, NULL, CV_DRIVER, NULL);
7681708Sstevel 	cv_init(&man_iwork_q->q_cv, NULL, CV_DRIVER, NULL);
7691708Sstevel 
7701708Sstevel 	return (0);
7711708Sstevel }
7721708Sstevel 
7731708Sstevel /*
7741708Sstevel  * _info is called by modinfo().
7751708Sstevel  */
7761708Sstevel int
_info(struct modinfo * modinfop)7771708Sstevel _info(struct modinfo *modinfop)
7781708Sstevel {
7791708Sstevel 	int	status;
7801708Sstevel 
7811708Sstevel 	MAN_DBG(MAN_INIT, ("_info:"));
7821708Sstevel 
7831708Sstevel 	status = mod_info(&modlinkage, modinfop);
7841708Sstevel 
7851708Sstevel 	MAN_DBG(MAN_INIT, ("_info: returns %d", status));
7861708Sstevel 
7871708Sstevel 	return (status);
7881708Sstevel }
7891708Sstevel 
7901708Sstevel /*
7911708Sstevel  * _fini called by modunload() just before driver is unloaded from memory.
7921708Sstevel  */
7931708Sstevel int
_fini(void)7941708Sstevel _fini(void)
7951708Sstevel {
7961708Sstevel 	int status = 0;
7971708Sstevel 
7981708Sstevel 	MAN_DBG(MAN_INIT, ("_fini:"));
7991708Sstevel 
8001708Sstevel 
8011708Sstevel 	/*
8021708Sstevel 	 * The only upper stream left should be man_ctl_lh. Note that
8031708Sstevel 	 * man_close (upper stream) is synchronous (i.e. it waits for
8041708Sstevel 	 * all STREAMS framework associated with the upper stream to be
8051708Sstevel 	 * torn down). This guarantees that man_ctl_lh will never become
8061708Sstevel 	 * NULL until noone is around to notice. This assumption is made
8071708Sstevel 	 * in a few places like man_plumb, man_unplumb, etc.
8081708Sstevel 	 */
8091708Sstevel 	if (man_strup && (man_strup->ms_next != NULL))
8101708Sstevel 		return (EBUSY);
8111708Sstevel 
8121708Sstevel 	/*
8131708Sstevel 	 * Deconfigure the driver.
8141708Sstevel 	 */
8151708Sstevel 	status = man_deconfigure();
8161708Sstevel 	if (status)
8171708Sstevel 		goto exit;
8181708Sstevel 
8191708Sstevel 	/*
8201708Sstevel 	 * need to detach every instance of the driver
8211708Sstevel 	 */
8221708Sstevel 	status = mod_remove(&modlinkage);
8231708Sstevel 	if (status != 0)
8241708Sstevel 		goto exit;
8251708Sstevel 
8261708Sstevel 	ddi_soft_state_fini(&man_softstate);
8271708Sstevel 
8281708Sstevel 	/*
8291708Sstevel 	 * Free up locks.
8301708Sstevel 	 */
8311708Sstevel 	mutex_destroy(&man_lock);
8321708Sstevel 	cv_destroy(&man_bwork_q->q_cv);
8331708Sstevel 	cv_destroy(&man_iwork_q->q_cv);
8341708Sstevel 
8351708Sstevel 	man_kfree(man_bwork_q, sizeof (man_workq_t));
8361708Sstevel 	man_kfree(man_iwork_q, sizeof (man_workq_t));
8371708Sstevel 
8381708Sstevel exit:
8391708Sstevel 
8401708Sstevel 	MAN_DBG(MAN_INIT, ("_fini: returns %d", status));
8411708Sstevel 
8421708Sstevel 	return (status);
8431708Sstevel }
8441708Sstevel 
8451708Sstevel /*
8461708Sstevel  * Deconfigure the MAN driver.
8471708Sstevel  */
8481708Sstevel static int
man_deconfigure()8491708Sstevel man_deconfigure()
8501708Sstevel {
8511708Sstevel 	man_work_t	*wp;
8521708Sstevel 	int		status = 0;
8531708Sstevel 
8541708Sstevel 	MAN_DBG(MAN_CONFIG, ("man_deconfigure:\n"));
8551708Sstevel 
8561708Sstevel 	mutex_enter(&man_lock);
8571708Sstevel 
8581708Sstevel 	if (man_is_on_domain) {
8591708Sstevel 		status = man_domain_deconfigure();
8601708Sstevel 		if (status != 0)
8611708Sstevel 			goto exit;
8621708Sstevel 	}
8631708Sstevel 
8641708Sstevel 	man_param_cleanup();	/* Free up NDD resources */
8651708Sstevel 
8661708Sstevel 	/*
8671708Sstevel 	 * I may have to handle straggling work requests. Just qwait?
8681708Sstevel 	 * or cvwait? Called from _fini - TBD
8691708Sstevel 	 */
8701708Sstevel 	ASSERT(man_bwork_q->q_work == NULL);
8711708Sstevel 	ASSERT(man_iwork_q->q_work == NULL);
8721708Sstevel 
8731708Sstevel 	MAN_DBG(MAN_CONFIG, ("man_deconfigure: submitting CLOSE_CTL\n"));
8741708Sstevel 
8751708Sstevel 	if (man_ctl_lh != NULL) {
8761708Sstevel 		wp = man_work_alloc(MAN_WORK_CLOSE_CTL, KM_SLEEP);
8771708Sstevel 		wp->mw_flags = MAN_WFLAGS_CVWAITER;
8781708Sstevel 		man_work_add(man_bwork_q, wp);
8791708Sstevel 
8801708Sstevel 		while (!(wp->mw_flags & MAN_WFLAGS_DONE)) {
8811708Sstevel 			cv_wait(&wp->mw_cv, &man_lock);
8821708Sstevel 		}
8831708Sstevel 		man_work_free(wp);
8841708Sstevel 	}
8851708Sstevel 
8861708Sstevel 	MAN_DBG(MAN_CONFIG, ("man_deconfigure: submitting STOP\n"));
8871708Sstevel 	if (man_bwork_id != NULL) {
8881708Sstevel 
8891708Sstevel 		wp = man_work_alloc(MAN_WORK_STOP, KM_SLEEP);
8901708Sstevel 		wp->mw_flags = MAN_WFLAGS_CVWAITER;
8911708Sstevel 		man_work_add(man_bwork_q, wp);
8921708Sstevel 
8931708Sstevel 		while (!(wp->mw_flags & MAN_WFLAGS_DONE)) {
8941708Sstevel 			cv_wait(&wp->mw_cv, &man_lock);
8951708Sstevel 		}
8961708Sstevel 		man_work_free(wp);
8971708Sstevel 	}
8981708Sstevel 	man_config_state = MAN_UNCONFIGURED;
8991708Sstevel 
9001708Sstevel exit:
9011708Sstevel 	mutex_exit(&man_lock);
9021708Sstevel 
9031708Sstevel 	MAN_DBG(MAN_CONFIG, ("man_deconfigure: returns %d\n", status));
9041708Sstevel 
9051708Sstevel 	return (status);
9061708Sstevel }
9071708Sstevel 
9081708Sstevel /*
9091708Sstevel  * man_attach - allocate resources and attach an instance of the MAN driver
9101708Sstevel  * The <man>.conf file controls how many instances of the MAN driver are
9111708Sstevel  * available.
9121708Sstevel  *
9131708Sstevel  *	dip - devinfo of node
9141708Sstevel  * 	cmd - one of DDI_ATTACH | DDI_RESUME
9151708Sstevel  *
9161708Sstevel  *	returns	- success - DDI_SUCCESS
9171708Sstevel  *		- failure - DDI_FAILURE
9181708Sstevel  */
9191708Sstevel static int
man_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)9201708Sstevel man_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
9211708Sstevel {
9221708Sstevel 	man_t		*manp;		/* per instance data */
9231708Sstevel 	uchar_t		flag = KSTAT_FLAG_WRITABLE; /* support netstat -kc */
9241708Sstevel 	kstat_t		*ksp;
9251708Sstevel 	int		minor_node_created = 0;
9261708Sstevel 	int		instance;
9271708Sstevel 	eaddr_t		man_eaddr;
9281708Sstevel 
9291708Sstevel 	MAN_DBG(MAN_INIT, ("man_attach: \n"));
9301708Sstevel 
9311708Sstevel 	if (cmd != DDI_ATTACH) {
9321708Sstevel 		MAN_DBG(MAN_INIT, ("man_attach: bad command %d\n", cmd));
9331708Sstevel 		return (DDI_FAILURE);
9341708Sstevel 	}
9351708Sstevel 
9361708Sstevel 	if (man_get_our_etheraddr(&man_eaddr))
9371708Sstevel 		return (DDI_FAILURE);
9381708Sstevel 
9391708Sstevel 	instance = ddi_get_instance(dip);
9401708Sstevel 
9411708Sstevel 	/*
9421708Sstevel 	 * we assume that instance is always equal to zero.
9431708Sstevel 	 * and there will always only be one instance.
9441708Sstevel 	 * this is done because when dman opens itself via DMAN_INT_PATH,
9451708Sstevel 	 * the path assumes that the instance number is zero.
9461708Sstevel 	 * if we ever need to support multiple instances of the dman
9471708Sstevel 	 * driver or non-zero instances, this will have to change.
9481708Sstevel 	 */
9491708Sstevel 	ASSERT(instance == 0);
9501708Sstevel 
9511708Sstevel 	/*
9521708Sstevel 	 * Allocate per device info pointer and link in to global list of
9531708Sstevel 	 * MAN devices.
9541708Sstevel 	 */
9551708Sstevel 	if ((ddi_soft_state_zalloc(man_softstate, instance) != DDI_SUCCESS) ||
9561708Sstevel 	    ((manp = ddi_get_soft_state(man_softstate, instance)) == NULL)) {
9571708Sstevel 		cmn_err(CE_WARN, "man_attach: cannot zalloc soft state!");
9581708Sstevel 		return (DDI_FAILURE);
9591708Sstevel 	}
9601708Sstevel 
9611708Sstevel 	ddi_set_driver_private(dip, manp);
9621708Sstevel 	manp->man_dip = dip;
9638459SJerry.Gilliam@Sun.COM 	manp->man_meta_major = ddi_driver_major(dip);
9641708Sstevel 	manp->man_meta_ppa = instance;
9651708Sstevel 
9661708Sstevel 	/*
9671708Sstevel 	 * Set ethernet address. Note that this address is duplicated
9681708Sstevel 	 * at md_src_eaddr.
9691708Sstevel 	 */
9701708Sstevel 	ether_copy(&man_eaddr, &manp->man_eaddr);
9711708Sstevel 	manp->man_eaddr_v = 1;
9721708Sstevel 
9731708Sstevel 	MAN_DBG(MAN_INIT, ("man_attach: set ether to %s",
9747656SSherry.Moore@Sun.COM 	    ether_sprintf(&manp->man_eaddr)));
9751708Sstevel 
9761708Sstevel 	/*
9771708Sstevel 	 * Initialize failover-related fields (timers and such),
9781708Sstevel 	 * taking values from properties if present.
9791708Sstevel 	 */
9801708Sstevel 	manp->man_init_time = ddi_getprop(DDI_DEV_T_ANY, dip, 0,
9811708Sstevel 	    "init_time", MAN_INIT_TIME);
9821708Sstevel 
9831708Sstevel 	manp->man_linkcheck_time = ddi_getprop(DDI_DEV_T_ANY, dip, 0,
9841708Sstevel 	    "linkcheck_time", MAN_LINKCHECK_TIME);
9851708Sstevel 
9861708Sstevel 	manp->man_linkstale_time = ddi_getprop(DDI_DEV_T_ANY, dip, 0,
9871708Sstevel 	    "man_linkstale_time", MAN_LINKSTALE_TIME);
9881708Sstevel 
9891708Sstevel 	manp->man_linkstale_retries = ddi_getprop(DDI_DEV_T_ANY, dip, 0,
9901708Sstevel 	    "man_linkstale_retries", MAN_LINKSTALE_RETRIES);
9911708Sstevel 
9921708Sstevel 	manp->man_dr_delay = ddi_getprop(DDI_DEV_T_ANY, dip, 0,
9931708Sstevel 	    "man_dr_delay", MAN_DR_DELAY);
9941708Sstevel 
9951708Sstevel 	manp->man_dr_retries = ddi_getprop(DDI_DEV_T_ANY, dip, 0,
9961708Sstevel 	    "man_dr_retries", MAN_DR_RETRIES);
9971708Sstevel 
9981708Sstevel 	manp->man_kstat_waittime = ddi_getprop(DDI_DEV_T_ANY, dip, 0,
9991708Sstevel 	    "man_kstat_waittime", MAN_KSTAT_WAITTIME);
10001708Sstevel 
10011708Sstevel 	manp->man_dlpireset_time = ddi_getprop(DDI_DEV_T_ANY, dip, 0,
10021708Sstevel 	    "man_dlpireset_time", MAN_DLPIRESET_TIME);
10031708Sstevel 
10041708Sstevel 	if (ddi_create_internal_pathname(dip, MAN_IDNAME, S_IFCHR,
10057656SSherry.Moore@Sun.COM 	    ddi_get_instance(dip)) == DDI_SUCCESS) {
10061708Sstevel 		minor_node_created = 1;
10071708Sstevel 	} else {
10081708Sstevel 		cmn_err(CE_WARN, "man_attach: failed for instance %d",
10091708Sstevel 		    ddi_get_instance(dip));
10101708Sstevel 		goto exit;
10111708Sstevel 	}
10121708Sstevel 
10131708Sstevel 	if (ddi_create_minor_node(dip, MAN_IDNAME, S_IFCHR,
10147656SSherry.Moore@Sun.COM 	    ddi_get_instance(dip), DDI_NT_NET, CLONE_DEV) == DDI_SUCCESS) {
10151708Sstevel 		minor_node_created = 1;
10161708Sstevel 	} else {
10171708Sstevel 		cmn_err(CE_WARN, "man_attach: failed for instance %d",
10181708Sstevel 		    ddi_get_instance(dip));
10191708Sstevel 		goto exit;
10201708Sstevel 	}
10211708Sstevel 
10221708Sstevel 	/*
10231708Sstevel 	 * Allocate meta kstat_t for this instance of the driver.
10241708Sstevel 	 * Note that each of man_path_t keeps track of the kstats
10251708Sstevel 	 * for the real devices via mp_last_knp.
10261708Sstevel 	 */
10271708Sstevel #ifdef	kstat
10281708Sstevel 	flag |= KSTAT_FLAG_PERSISTENT;
10291708Sstevel #endif
10301708Sstevel 	ksp = kstat_create(MAN_IDNAME, ddi_get_instance(dip), NULL, "net",
10317656SSherry.Moore@Sun.COM 	    KSTAT_TYPE_NAMED, MAN_NUMSTATS, flag);
10321708Sstevel 
10331708Sstevel 	if (ksp == NULL) {
10341708Sstevel 		cmn_err(CE_WARN, "man_attach(%d): kstat_create failed"
10357656SSherry.Moore@Sun.COM 		    " - manp(0x%p)", manp->man_meta_ppa,
10367656SSherry.Moore@Sun.COM 		    (void *)manp);
10371708Sstevel 		goto exit;
10381708Sstevel 	}
10391708Sstevel 
10401708Sstevel 	man_kstat_named_init(ksp->ks_data, MAN_NUMSTATS);
10411708Sstevel 	ksp->ks_update = man_kstat_update;
10421708Sstevel 	ksp->ks_private = (void *) manp;
10431708Sstevel 	manp->man_ksp = ksp;
10441708Sstevel 	kstat_install(manp->man_ksp);
10451708Sstevel 
10461708Sstevel 	ddi_report_dev(dip);
10471708Sstevel 
10481708Sstevel 	MAN_DBG(MAN_INIT, ("man_attach(%d) returns DDI_SUCCESS",
10497656SSherry.Moore@Sun.COM 	    ddi_get_instance(dip)));
10501708Sstevel 
10511708Sstevel 	return (DDI_SUCCESS);
10521708Sstevel 
10531708Sstevel exit:
10541708Sstevel 	if (minor_node_created)
10551708Sstevel 		ddi_remove_minor_node(dip, NULL);
10561708Sstevel 	ddi_set_driver_private(dip, NULL);
10571708Sstevel 	ddi_soft_state_free(man_softstate, instance);
10581708Sstevel 
10591708Sstevel 	MAN_DBG(MAN_INIT, ("man_attach(%d) eaddr returns DDI_FAILIRE",
10607656SSherry.Moore@Sun.COM 	    ddi_get_instance(dip)));
10611708Sstevel 
10621708Sstevel 	return (DDI_FAILURE);
10631708Sstevel 
10641708Sstevel }
10651708Sstevel 
10661708Sstevel static int
man_get_our_etheraddr(eaddr_t * eap)10671708Sstevel man_get_our_etheraddr(eaddr_t *eap)
10681708Sstevel {
10691708Sstevel 	manc_t	manc;
10701708Sstevel 	int	status = 0;
10711708Sstevel 
10721708Sstevel 	if (man_is_on_domain) {
10731708Sstevel 		if (status = man_get_iosram(&manc))
10741708Sstevel 			return (status);
10751708Sstevel 		ether_copy(&manc.manc_dom_eaddr, eap);
10761708Sstevel 	} else {
10771708Sstevel 		(void) localetheraddr((struct ether_addr *)NULL, eap);
10781708Sstevel 	}
10791708Sstevel 
10801708Sstevel 	return (status);
10811708Sstevel }
10821708Sstevel 
10831708Sstevel /*
10841708Sstevel  * man_detach - detach an instance of a driver
10851708Sstevel  *
10861708Sstevel  *	dip - devinfo of node
10871708Sstevel  * 	cmd - one of DDI_DETACH | DDI_SUSPEND
10881708Sstevel  *
10891708Sstevel  *	returns	- success - DDI_SUCCESS
10901708Sstevel  *		- failure - DDI_FAILURE
10911708Sstevel  */
10921708Sstevel static int
man_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)10931708Sstevel man_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
10941708Sstevel {
10951708Sstevel 	register man_t	*manp;		/* per instance data */
10961708Sstevel 	int		instance;
10971708Sstevel 
10981708Sstevel 	MAN_DBG(MAN_INIT, ("man_detach(%d):\n", ddi_get_instance(dip)));
10991708Sstevel 
11001708Sstevel 	if (cmd != DDI_DETACH) {
11011708Sstevel 		MAN_DBG(MAN_INIT, ("man_detach: bad command %d\n", cmd));
11021708Sstevel 		return (DDI_FAILURE);
11031708Sstevel 	}
11041708Sstevel 
11051708Sstevel 	if (dip == NULL) {
11061708Sstevel 		MAN_DBG(MAN_INIT, ("man_detach: dip == NULL\n"));
11071708Sstevel 		return (DDI_FAILURE);
11081708Sstevel 	}
11091708Sstevel 
11101708Sstevel 	instance = ddi_get_instance(dip);
11111708Sstevel 
11121708Sstevel 	mutex_enter(&man_lock);
11131708Sstevel 
11141708Sstevel 	manp = (man_t *)ddi_get_soft_state(man_softstate, instance);
11151708Sstevel 	if (manp == NULL) {
11161708Sstevel 		mutex_exit(&man_lock);
11171708Sstevel 
11181708Sstevel 		cmn_err(CE_WARN, "man_detach: unable to get softstate"
11197656SSherry.Moore@Sun.COM 		    " for instance = %d, dip = 0x%p!\n", instance,
11207656SSherry.Moore@Sun.COM 		    (void *)dip);
11211708Sstevel 		return (DDI_FAILURE);
11221708Sstevel 	}
11231708Sstevel 
11241708Sstevel 	if (manp->man_refcnt != 0) {
11251708Sstevel 		mutex_exit(&man_lock);
11261708Sstevel 
11271708Sstevel 		cmn_err(CE_WARN, "man_detach: %s%d refcnt %d", MAN_IDNAME,
11287656SSherry.Moore@Sun.COM 		    instance, manp->man_refcnt);
11291708Sstevel 		MAN_DBGCALL(MAN_INIT, man_print_man(manp));
11301708Sstevel 
11311708Sstevel 		return (DDI_FAILURE);
11321708Sstevel 	}
11331708Sstevel 
11341708Sstevel 	ddi_remove_minor_node(dip, NULL);
11351708Sstevel 
11361708Sstevel 	mutex_exit(&man_lock);
11371708Sstevel 
11381708Sstevel 	kstat_delete(manp->man_ksp);
11391708Sstevel 	ddi_soft_state_free(man_softstate, instance);
11401708Sstevel 	ddi_set_driver_private(dip, NULL);
11411708Sstevel 
11421708Sstevel 	MAN_DBG(MAN_INIT, ("man_detach returns DDI_SUCCESS"));
11431708Sstevel 
11441708Sstevel 	return (DDI_SUCCESS);
11451708Sstevel }
11461708Sstevel 
11471708Sstevel /*
11481708Sstevel  * man_info:
11491708Sstevel  *	As a standard DLPI style-2, man_info() should always return
11501708Sstevel  *	DDI_FAILURE.
11511708Sstevel  *
11521708Sstevel  *	However, man_open() has special treatment for a direct open
11531708Sstevel  *	via kstr_open() without going through the CLONE driver.
11541708Sstevel  *	To make this special kstr_open() work, we need to map
11551708Sstevel  *	minor of 0 to instance 0.
11561708Sstevel  */
11571708Sstevel /*ARGSUSED*/
11581708Sstevel static int
man_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)11591708Sstevel man_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
11601708Sstevel {
11611708Sstevel 	minor_t minor;
11621708Sstevel 
11631708Sstevel 	switch (infocmd) {
11641708Sstevel 	case DDI_INFO_DEVT2DEVINFO:
11651708Sstevel 		break;
11661708Sstevel 
11671708Sstevel 	case DDI_INFO_DEVT2INSTANCE:
11681708Sstevel 		minor = getminor((dev_t)arg);
11691708Sstevel 		if (minor == 0) {
11701708Sstevel 			*result = (void *)(uintptr_t)minor;
11711708Sstevel 			return (DDI_SUCCESS);
11721708Sstevel 		}
11731708Sstevel 		break;
11741708Sstevel 	default:
11751708Sstevel 		break;
11761708Sstevel 	}
11771708Sstevel 	return (DDI_FAILURE);
11781708Sstevel }
11791708Sstevel 
11801708Sstevel /* Standard Device Driver entry points */
11811708Sstevel 
11821708Sstevel /*
11831708Sstevel  * man_open - open the device
11841708Sstevel  *
11851708Sstevel  *	rq - upper read queue of the stream
11861708Sstevel  *	devp - pointer to a device number
11871708Sstevel  *	flag - information passed from the user program open(2) system call
11881708Sstevel  *	sflag - stream flags
11891708Sstevel  *	credp - pointer to the cred(9S) user credential structure
11901708Sstevel  *
11911708Sstevel  *	returns	- success - 0
11921708Sstevel  *		- failure - errno value for failure
11931708Sstevel  */
11941708Sstevel /*ARGSUSED*/
11951708Sstevel static int
man_open(queue_t * rq,dev_t * devp,int flag,int sflag,cred_t * credp)11961708Sstevel man_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
11971708Sstevel {
11981708Sstevel 	int			minordev = -1;
11991708Sstevel 	manstr_t		*msp;
12001708Sstevel 	manstr_t		*tsp;
12011708Sstevel 	manstr_t		**prevmsp;
12021708Sstevel 	int			status = 0;
12031708Sstevel 
12041708Sstevel 	MAN_DBG(MAN_OCLOSE, ("man_open: rq(0x%p) sflag(0x%x)\n",
12051708Sstevel 	    (void *)rq, sflag));
12061708Sstevel 
12071708Sstevel 	ASSERT(rq);
12081708Sstevel 	ASSERT(sflag != MODOPEN);
12091708Sstevel 
12101708Sstevel 	/*
12111708Sstevel 	 * reopen; q_ptr set to msp at open completion.
12121708Sstevel 	 */
12131708Sstevel 	if (rq->q_ptr) {
12141708Sstevel 		return (0);
12151708Sstevel 	}
12161708Sstevel 
12171708Sstevel 	/*
12181708Sstevel 	 * Allocate and initialize manstr_t for this device.
12191708Sstevel 	 */
12201708Sstevel 	msp = man_kzalloc(sizeof (manstr_t), KM_SLEEP);
12211708Sstevel 	SETSTATE(msp, DL_UNATTACHED);
12221708Sstevel 	msp->ms_meta_ppa = -1;
12231708Sstevel 	msp->ms_rq = rq;
12241708Sstevel 	rq->q_ptr = WR(rq)->q_ptr = msp;
12251708Sstevel 
12261708Sstevel 	/*
12271708Sstevel 	 * Get the MAN driver configured on 1st open.  Note that the only way
12281708Sstevel 	 * we get sflag != CLONEOPEN is via the call in man_plumbctl().  All
12291708Sstevel 	 * CLONEOPEN calls to man_open will be via the file system
12301708Sstevel 	 * device node /dev/man, a pseudo clone device.
12311708Sstevel 	 */
12321708Sstevel 
12331708Sstevel 	qprocson(rq);
12341708Sstevel 
12351708Sstevel 	if (sflag == CLONEOPEN && man_config_state != MAN_CONFIGURED) {
12361708Sstevel 		/*
12371708Sstevel 		 * First open calls man_configure. Everyone qwaits until
12381708Sstevel 		 * we get it open. See man_open_ctl() comments for mutex
12391708Sstevel 		 * lock/synchronization info.
12401708Sstevel 		 */
12411708Sstevel 
12421708Sstevel 		mutex_enter(&man_lock);
12431708Sstevel 
12441708Sstevel 		if (man_config_state == MAN_UNCONFIGURED) {
12451708Sstevel 			man_config_state = MAN_CONFIGURING;
12461708Sstevel 			mutex_exit(&man_lock);
12471708Sstevel 			status = man_configure(rq);
12481708Sstevel 			if (status != 0)
12491708Sstevel 				goto exit;
12501708Sstevel 		} else {
12511708Sstevel 			while (man_config_state == MAN_CONFIGURING) {
12521708Sstevel 
12531708Sstevel 				mutex_exit(&man_lock);
12541708Sstevel 				status = qwait_sig(rq);
12551708Sstevel 
12561708Sstevel 				if (status == 0) {
12571708Sstevel 					status = EINTR;
12581708Sstevel 					goto exit;
12591708Sstevel 				}
12601708Sstevel 
12611708Sstevel 				mutex_enter(&man_lock);
12621708Sstevel 			}
12631708Sstevel 			mutex_exit(&man_lock);
12641708Sstevel 
12651708Sstevel 			if (man_config_error) {
12661708Sstevel 				status = man_config_error;
12671708Sstevel 				goto exit;
12681708Sstevel 			}
12691708Sstevel 		}
12701708Sstevel 	}
12711708Sstevel 
12721708Sstevel 	/*
12731708Sstevel 	 * Determine minor device number. man_open serialized by
12741708Sstevel 	 * D_MTPERMOD.
12751708Sstevel 	 */
12761708Sstevel 	prevmsp = &man_strup;
12771708Sstevel 	if (sflag == CLONEOPEN) {
12781708Sstevel 
12791708Sstevel 		minordev = 0;
12801708Sstevel 		for (; (tsp = *prevmsp) != NULL; prevmsp = &tsp->ms_next) {
12811708Sstevel 			if (minordev < tsp->ms_minor)
12821708Sstevel 				break;
12831708Sstevel 			minordev++;
12841708Sstevel 		}
12851708Sstevel 		*devp = makedevice(getmajor(*devp), minordev);
12861708Sstevel 
12871708Sstevel 	} else {
12881708Sstevel 		/*
12891708Sstevel 		 * Should only get here from man_plumbctl().
12901708Sstevel 		 */
12911708Sstevel 		/*LINTED E_ASSIGN_UINT_TO_SIGNED_INT*/
12921708Sstevel 		minordev = getminor(*devp);
12931708Sstevel 
12941708Sstevel 		/*
12951708Sstevel 		 * No need to protect this here as all opens are
12961708Sstevel 		 * qwaiting, and the bgthread (who is doing this open)
12971708Sstevel 		 * is the only one who mucks with this variable.
12981708Sstevel 		 */
12991708Sstevel 		man_ctl_wq = WR(rq);
13001708Sstevel 
13011708Sstevel 		ASSERT(minordev == 0);	/* TBD delete this */
13021708Sstevel 	}
13031708Sstevel 
13041708Sstevel 	msp->ms_meta_maj = getmajor(*devp);
13051708Sstevel 	msp->ms_minor = minordev;
13061708Sstevel 	if (minordev == 0)
13071708Sstevel 		msp->ms_flags = MAN_SFLAG_CONTROL;
13081708Sstevel 
13091708Sstevel 	/*
13101708Sstevel 	 * Link new entry into global list of active entries.
13111708Sstevel 	 */
13121708Sstevel 	msp->ms_next = *prevmsp;
13131708Sstevel 	*prevmsp = msp;
13141708Sstevel 
13151708Sstevel 
13161708Sstevel 	/*
13171708Sstevel 	 * Disable automatic enabling of our write service procedure.
13181708Sstevel 	 * We control this explicitly.
13191708Sstevel 	 */
13201708Sstevel 	noenable(WR(rq));
13211708Sstevel 
13221708Sstevel exit:
13231708Sstevel 	MAN_DBG(MAN_OCLOSE, ("man_open: exit rq(0x%p) minor %d errno %d\n",
13247656SSherry.Moore@Sun.COM 	    (void *)rq, minordev, status));
13251708Sstevel 
13261708Sstevel 	/*
13271708Sstevel 	 * Clean up on error.
13281708Sstevel 	 */
13291708Sstevel 	if (status) {
13301708Sstevel 		qprocsoff(rq);
13311708Sstevel 		rq->q_ptr = WR(rq)->q_ptr = NULL;
13321708Sstevel 		man_kfree((char *)msp, sizeof (manstr_t));
13331708Sstevel 	} else
13341708Sstevel 		(void) qassociate(rq, -1);
13351708Sstevel 
13361708Sstevel 	return (status);
13371708Sstevel }
13381708Sstevel 
13391708Sstevel /*
13401708Sstevel  * Get the driver configured.  Called from first man_open with exclusive
13411708Sstevel  * inner perimeter.
13421708Sstevel  */
13431708Sstevel static int
man_configure(queue_t * rq)13441708Sstevel man_configure(queue_t *rq)
13451708Sstevel {
13461708Sstevel 	man_work_t	*wp;
13471708Sstevel 	int		status = 0;
13481708Sstevel 
13491708Sstevel 	MAN_DBG(MAN_CONFIG, ("man_configure:"));
13501708Sstevel 
13511708Sstevel 	/*
13521708Sstevel 	 * Initialize NDD parameters.
13531708Sstevel 	 */
13541708Sstevel 	if (!man_ndlist &&
13551708Sstevel 	    !man_param_register(man_param_arr, A_CNT(man_param_arr))) {
13561708Sstevel 		cmn_err(CE_WARN, "man_configure: man_param_register failed!");
13571708Sstevel 		man_config_error = ENOMEM;
13581708Sstevel 		goto exit;
13591708Sstevel 	}
13601708Sstevel 
13611708Sstevel 	mutex_enter(&man_lock);
13621708Sstevel 
13631708Sstevel 	/*
13641708Sstevel 	 * Start up background thread.
13651708Sstevel 	 */
13661708Sstevel 	if (man_bwork_id == NULL)
13671708Sstevel 		man_bwork_id = thread_create(NULL, 2 * DEFAULTSTKSZ,
13681708Sstevel 		    man_bwork, NULL, 0, &p0, TS_RUN, minclsyspri);
13691708Sstevel 
13701708Sstevel 	/*
13711708Sstevel 	 * Submit work to get control stream opened. Qwait until its
13721708Sstevel 	 * done. See man_open_ctl for mutex lock/synchronization info.
13731708Sstevel 	 */
13741708Sstevel 
13751708Sstevel 	if (man_ctl_lh == NULL) {
13761708Sstevel 		wp = man_work_alloc(MAN_WORK_OPEN_CTL, KM_SLEEP);
13771708Sstevel 		wp->mw_flags |= MAN_WFLAGS_QWAITER;
13781708Sstevel 		wp->mw_q = WR(rq);
13791708Sstevel 
13801708Sstevel 		/*
13811708Sstevel 		 * Submit work and wait. When man_open_ctl exits
13821708Sstevel 		 * man_open, it will cause qwait below to return.
13831708Sstevel 		 */
13841708Sstevel 		man_work_add(man_bwork_q, wp);
13851708Sstevel 		while (!(wp->mw_flags & MAN_WFLAGS_DONE)) {
13861708Sstevel 			mutex_exit(&man_lock);
13871708Sstevel 			qwait(rq);
13881708Sstevel 			mutex_enter(&man_lock);
13891708Sstevel 		}
13901708Sstevel 		status = wp->mw_status;
13911708Sstevel 		man_work_free(wp);
13921708Sstevel 
13931708Sstevel 	}
13941708Sstevel 	mutex_exit(&man_lock);
13951708Sstevel 
13961708Sstevel 	/*
13971708Sstevel 	 * If on domain, setup IOSRAM and build the pathgroups
13981708Sstevel 	 * automatically.
13991708Sstevel 	 */
14001708Sstevel 	if ((status == 0) && man_is_on_domain)
14011708Sstevel 		status = man_domain_configure();
14021708Sstevel 
14031708Sstevel exit:
14041708Sstevel 	mutex_enter(&man_lock);
14051708Sstevel 
14061708Sstevel 	man_config_error = status;
14071708Sstevel 	if (status != 0)
14081708Sstevel 		man_config_state = MAN_UNCONFIGURED;
14091708Sstevel 	else
14101708Sstevel 		man_config_state = MAN_CONFIGURED;
14111708Sstevel 
14121708Sstevel 	mutex_exit(&man_lock);
14131708Sstevel 
14141708Sstevel 	MAN_DBG(MAN_CONFIG, ("man_configure: returns %d\n", status));
14151708Sstevel 
14161708Sstevel 	return (status);
14171708Sstevel }
14181708Sstevel 
14191708Sstevel /*
14201708Sstevel  * man_close - close the device
14211708Sstevel  *
14221708Sstevel  *	rq - upper read queue of the stream
14231708Sstevel  *
14241708Sstevel  *	returns	- success - 0
14251708Sstevel  *		- failure - errno value for failure
14261708Sstevel  */
14271708Sstevel static int
man_close(queue_t * rq)14281708Sstevel man_close(queue_t *rq)
14291708Sstevel {
14301708Sstevel 	manstr_t		*close_msp;
14311708Sstevel 	manstr_t		*msp;
14321708Sstevel 
14331708Sstevel 	MAN_DBG(MAN_OCLOSE, ("man_close: rq(0x%p)\n", (void *)rq));
14341708Sstevel 
14351708Sstevel 	qprocsoff(rq);
14361708Sstevel 	close_msp = (manstr_t *)rq->q_ptr;
14371708Sstevel 
14381708Sstevel 	/*
14391708Sstevel 	 * Unlink the per-Stream entry from the active list and free it.
14401708Sstevel 	 */
14411708Sstevel 	if (close_msp == man_strup)
14421708Sstevel 		man_strup = close_msp->ms_next;
14431708Sstevel 	else {
14441708Sstevel 		for (msp = man_strup; msp && msp->ms_next != close_msp; )
14451708Sstevel 			msp = msp->ms_next;
14461708Sstevel 
14471708Sstevel 		if (msp == NULL) {
14481708Sstevel 			cmn_err(CE_WARN, "man_close: no stream!");
14491708Sstevel 			return (ENODEV);
14501708Sstevel 		}
14511708Sstevel 
14521708Sstevel 		msp->ms_next = close_msp->ms_next;
14531708Sstevel 	}
14541708Sstevel 
14551708Sstevel 	if (close_msp->ms_dests != NULL) {
14561708Sstevel 		/*
14571708Sstevel 		 * Still DL_ATTACHED
14581708Sstevel 		 */
14591708Sstevel 		man_work_t *wp;
14601708Sstevel 
14611708Sstevel 		wp = man_work_alloc(MAN_WORK_CLOSE_STREAM, KM_SLEEP);
14621708Sstevel 		man_dodetach(close_msp, wp);
14631708Sstevel 	}
14641708Sstevel 
14651708Sstevel 	if (close_msp->ms_flags & MAN_SFLAG_CONTROL) {
14661708Sstevel 		/*
14671708Sstevel 		 * Driver about to unload.
14681708Sstevel 		 */
14691708Sstevel 		man_ctl_wq = NULL;
14701708Sstevel 	}
14711708Sstevel 
14721708Sstevel 	rq->q_ptr = WR(rq)->q_ptr = NULL;
14731708Sstevel 	man_kfree((char *)close_msp, sizeof (manstr_t));
14741708Sstevel 	(void) qassociate(rq, -1);
14751708Sstevel 
14761708Sstevel 	MAN_DBG(MAN_OCLOSE, ("man_close: exit\n"));
14771708Sstevel 
14781708Sstevel 	return (0);
14791708Sstevel }
14801708Sstevel 
14811708Sstevel /*
14821708Sstevel  * Ask bgthread to tear down lower stream and qwait
14831708Sstevel  * until its done.
14841708Sstevel  */
14851708Sstevel static void
man_dodetach(manstr_t * msp,man_work_t * wp)14861708Sstevel man_dodetach(manstr_t *msp, man_work_t *wp)
14871708Sstevel {
14881708Sstevel 	man_dest_t	*mdp;
14891708Sstevel 	int		i;
14901708Sstevel 	mblk_t		*mp;
14911708Sstevel 
14921708Sstevel 	mdp = msp->ms_dests;
14931708Sstevel 	msp->ms_dests = NULL;
14941708Sstevel 	msp->ms_destp = NULL;
14951708Sstevel 
14961708Sstevel 	/*
14971708Sstevel 	 * Excise lower dests array, set it closing and hand it to
14981708Sstevel 	 * background thread to dispose of.
14991708Sstevel 	 */
15001708Sstevel 	for (i = 0; i < MAN_MAX_DESTS; i++) {
15011708Sstevel 
15021708Sstevel 		mdp[i].md_state |= MAN_DSTATE_CLOSING;
15031708Sstevel 		mdp[i].md_msp = NULL;
15041708Sstevel 		mdp[i].md_rq = NULL;
15051708Sstevel 
15061708Sstevel 		if (mdp[i].md_lc_timer_id != 0) {
15071708Sstevel 			(void) quntimeout(man_ctl_wq, mdp[i].md_lc_timer_id);
15081708Sstevel 			mdp[i].md_lc_timer_id = 0;
15091708Sstevel 		}
15101708Sstevel 		if (mdp[i].md_bc_id != 0) {
15111708Sstevel 			qunbufcall(man_ctl_wq, mdp[i].md_bc_id);
15121708Sstevel 			mdp[i].md_bc_id = 0;
15131708Sstevel 		}
15141708Sstevel 
15151708Sstevel 		mutex_enter(&mdp[i].md_lock);
15161708Sstevel 		while ((mp = mdp[i].md_dmp_head) != NULL) {
15171708Sstevel 			mdp[i].md_dmp_head = mp->b_next;
15181708Sstevel 			mp->b_next = NULL;
15191708Sstevel 			freemsg(mp);
15201708Sstevel 		}
15211708Sstevel 		mdp[i].md_dmp_count = 0;
15221708Sstevel 		mdp[i].md_dmp_tail = NULL;
15231708Sstevel 		mutex_exit(&mdp[i].md_lock);
15241708Sstevel 	}
15251708Sstevel 
15261708Sstevel 	/*
15271708Sstevel 	 * Dump any DL type messages previously caught.
15281708Sstevel 	 */
15291708Sstevel 	man_dl_clean(&msp->ms_dl_mp);
15301708Sstevel 	man_dl_clean(&msp->ms_dlioc_mp);
15311708Sstevel 
15321708Sstevel 	/*
15331708Sstevel 	 * We need to clear fast path flag when dlioc messages are cleaned.
15341708Sstevel 	 */
15351708Sstevel 	msp->ms_flags &= ~MAN_SFLAG_FAST;
15361708Sstevel 
15371708Sstevel 	/*
15381708Sstevel 	 * MAN_WORK_CLOSE_STREAM work request preallocated by caller.
15391708Sstevel 	 */
15401708Sstevel 	ASSERT(wp->mw_type == MAN_WORK_CLOSE_STREAM);
15411708Sstevel 	ASSERT(mdp != NULL);
15421708Sstevel 	wp->mw_arg.a_mdp = mdp;
15431708Sstevel 	wp->mw_arg.a_ndests = MAN_MAX_DESTS;
15441708Sstevel 	wp->mw_arg.a_pg_id = -1;	/* Don't care */
15451708Sstevel 
15461708Sstevel 	mutex_enter(&man_lock);
15471708Sstevel 	man_work_add(man_bwork_q, wp);
15481708Sstevel 	msp->ms_manp->man_refcnt--;
15491708Sstevel 	mutex_exit(&man_lock);
15501708Sstevel 
15511708Sstevel 	msp->ms_manp = NULL;
15521708Sstevel 
15531708Sstevel }
15541708Sstevel 
15551708Sstevel 
15561708Sstevel /*
15571708Sstevel  * man_uwput - handle DLPI messages issued from upstream, the write
15581708Sstevel  * side of the upper half of multiplexor. Called with shared access to
15591708Sstevel  * the inner perimeter.
15601708Sstevel  *
15611708Sstevel  *	wq - upper write queue of mxx
15621708Sstevel  *	mp - mblk ptr to DLPI request
15631708Sstevel  */
15641708Sstevel static int
man_uwput(register queue_t * wq,register mblk_t * mp)15651708Sstevel man_uwput(register queue_t *wq, register mblk_t *mp)
15661708Sstevel {
15671708Sstevel 	register manstr_t	*msp;		/* per stream data */
15681708Sstevel 	register man_t		*manp;		/* per instance data */
15691708Sstevel 
15701708Sstevel 	msp = (manstr_t *)wq->q_ptr;
15711708Sstevel 
15721708Sstevel 	MAN_DBG(MAN_UWPUT, ("man_uwput: wq(0x%p) mp(0x%p) db_type(0x%x)"
15737656SSherry.Moore@Sun.COM 	    " msp(0x%p)\n",
15747656SSherry.Moore@Sun.COM 	    (void *)wq, (void *)mp, DB_TYPE(mp), (void *)msp));
15751708Sstevel #if DEBUG
15761708Sstevel 	if (man_debug & MAN_UWPUT) {
15771708Sstevel 		if (DB_TYPE(mp) == M_IOCTL) {
15781708Sstevel 			struct iocblk	*iocp = (struct iocblk *)mp->b_rptr;
15791708Sstevel 			MAN_DBG(MAN_UWPUT,
15801708Sstevel 			    ("man_uwput: M_IOCTL ioc_cmd(0x%x)\n",
15811708Sstevel 			    iocp->ioc_cmd));
15821708Sstevel 		} else if (DB_TYPE(mp) == M_CTL) {
15831708Sstevel 			struct iocblk	*iocp = (struct iocblk *)mp->b_rptr;
15841708Sstevel 			MAN_DBG(MAN_UWPUT,
15851708Sstevel 			    ("man_uwput: M_CTL ioc_cmd(0x%x)\n",
15861708Sstevel 			    iocp->ioc_cmd));
15871708Sstevel 		}
15881708Sstevel 	}
15891708Sstevel #endif	/* DEBUG */
15901708Sstevel 
15911708Sstevel 
15921708Sstevel 	switch (DB_TYPE(mp)) {
15931708Sstevel 	case M_DATA:
15941708Sstevel 		manp = msp->ms_manp;
15951708Sstevel 
15961708Sstevel 		if (((msp->ms_flags & (MAN_SFLAG_FAST | MAN_SFLAG_RAW)) == 0) ||
15977656SSherry.Moore@Sun.COM 		    (msp->ms_dlpistate != DL_IDLE) ||
15987656SSherry.Moore@Sun.COM 		    (manp == NULL)) {
15991708Sstevel 
16001708Sstevel 			merror(wq, mp, EPROTO);
16011708Sstevel 			break;
16021708Sstevel 		}
16031708Sstevel 
16041708Sstevel 		if (wq->q_first) {
16051708Sstevel 			(void) putq(wq, mp);
16061708Sstevel 			qenable(wq);
16071708Sstevel 		} else {
16081708Sstevel 			ehdr_t	*ep = (ehdr_t *)mp->b_rptr;
16091708Sstevel 
16101708Sstevel 			(void) man_start(wq, mp, &ep->ether_dhost);
16111708Sstevel 		}
16121708Sstevel 		break;
16131708Sstevel 
16141708Sstevel 	case M_PROTO:
16151708Sstevel 	case M_PCPROTO:
16161708Sstevel 		if ((DL_PRIM(mp) == DL_UNITDATA_IND) && !wq->q_first) {
16171708Sstevel 			(void) man_udreq(wq, mp);
16181708Sstevel 		} else {
16191708Sstevel 			(void) putq(wq, mp);
16201708Sstevel 			qenable(wq);
16211708Sstevel 		}
16221708Sstevel 		break;
16231708Sstevel 
16241708Sstevel 	case M_IOCTL:
16251708Sstevel 	case M_IOCDATA:
16261708Sstevel 		qwriter(wq, mp, man_ioctl, PERIM_INNER);
16271708Sstevel 		break;
16281708Sstevel 
16291708Sstevel 	case M_CTL:
16301708Sstevel 		freemsg(mp);
16311708Sstevel 		break;
16321708Sstevel 
16331708Sstevel 	case M_FLUSH:
16341708Sstevel 		MAN_DBG(MAN_UWPUT, ("man_wput: M_FLUSH\n"));
16351708Sstevel 		if (*mp->b_rptr & FLUSHW)
16361708Sstevel 			flushq(wq, FLUSHDATA);
16371708Sstevel 		if (*mp->b_rptr & FLUSHR) {
16381708Sstevel 			flushq(RD(wq), FLUSHDATA);
16391708Sstevel 			*mp->b_rptr &= ~FLUSHW;
16401708Sstevel 			qreply(wq, mp);
16411708Sstevel 		} else {
16421708Sstevel 			freemsg(mp);
16431708Sstevel 		}
16441708Sstevel 		break;
16451708Sstevel 
16461708Sstevel 	default:
16471708Sstevel 		MAN_DBG(MAN_WARN,
16481708Sstevel 		    ("man_uwput: illegal mblk(0x%p) type(0x%x)\n",
16491708Sstevel 		    (void *)mp, DB_TYPE(mp)));
16501708Sstevel 		freemsg(mp);
16511708Sstevel 		break;
16521708Sstevel 	} /* End switch */
16531708Sstevel 
16541708Sstevel 	MAN_DBG(MAN_UWPUT, ("man_uwput: exit wq(0x%p) mp(0x%p)\n",
16551708Sstevel 	    (void *)wq, (void *)mp));
16561708Sstevel 
16571708Sstevel 	return (0);
16581708Sstevel }
16591708Sstevel 
16601708Sstevel /*
16611708Sstevel  * man_start - handle data messages issued from upstream.  Send down
16621708Sstevel  * to particular man_dest based on ether_addr, otherwise send out to all
16631708Sstevel  * valid man_dests.
16641708Sstevel  *
16651708Sstevel  *	wq - upper write queue of mxx
16661708Sstevel  *	mp - mblk ptr to DLPI request
16671708Sstevel  * 	caller - Caller ID for decision making on canput failure
16681708Sstevel  *
16691708Sstevel  * Returns:
16701708Sstevel  *	0	- Data xmitted or No flow control situation detected.
16711708Sstevel  *	1	- Flow control situation detected.
16721708Sstevel  *
16731708Sstevel  * STREAMS Flow Control: can be used if there is only one destination
16741708Sstevel  * for a stream (1 to 1 multiplexor). In this case, we will use the upper
16751708Sstevel  * write queue to store mblks when in flow control. If there are multiple
16761708Sstevel  * destinations, we cannot use the STREAMs based flow control (1 to many
16771708Sstevel  * multiplexor). In this case, we will use the lower write queue to store
16781708Sstevel  * mblks when in flow control. Since destinations come and go, we may
16791708Sstevel  * transition between 1-to-1 and 1-to-m. So it may be the case that we have
16801708Sstevel  * some mblks stored on the upper queue, and some on the lower queue. However,
16811708Sstevel  * we will never send mblks out of order. See man_uwput and man_start_lower().
16821708Sstevel  *
16831708Sstevel  * A simple flow control mechanism is implemented for the deferred mblk list,
16841708Sstevel  * as this list is expected to be used temporarily for a very short
16851708Sstevel  * period required for switching paths. This flow control mechanism is
16861708Sstevel  * used only as a defensive approach to avoid infinite growth of this list.
16871708Sstevel  */
16881708Sstevel static int
man_start(register queue_t * wq,register mblk_t * mp,eaddr_t * eap)16891708Sstevel man_start(register queue_t *wq, register mblk_t *mp, eaddr_t *eap)
16901708Sstevel {
16911708Sstevel 	register manstr_t	*msp;		/* per stream data */
16921708Sstevel 	register man_dest_t	*mdp = NULL;	/* destination */
16931708Sstevel 	mblk_t			*tmp;
16941708Sstevel 	int			i;
16951708Sstevel 	int			status = 0;
16961708Sstevel 
16971708Sstevel 	msp = (manstr_t *)wq->q_ptr;
16981708Sstevel 
16991708Sstevel 	MAN_DBG(MAN_DATA, ("man_start: msp(0x%p) ether_addr(%s)\n",
17007656SSherry.Moore@Sun.COM 	    (void *)msp, ether_sprintf(eap)));
17011708Sstevel 
17021708Sstevel 	if (msp->ms_dests == NULL) {
17031708Sstevel 		cmn_err(CE_WARN, "man_start: no destinations");
17041708Sstevel 		freemsg(mp);
17051708Sstevel 		return (0);
17061708Sstevel 	}
17071708Sstevel 
17081708Sstevel 	/*
17091708Sstevel 	 * Optimization if only one valid destination.
17101708Sstevel 	 */
17111708Sstevel 	mdp = msp->ms_destp;
17121708Sstevel 
17131708Sstevel 	if (IS_UNICAST(eap)) {
17141708Sstevel 		queue_t			*flow_wq = NULL;
17151708Sstevel 
17161708Sstevel 		if (mdp == NULL) {
17171708Sstevel 			/*
17181708Sstevel 			 * TDB - This needs to be optimized (some bits in
17191708Sstevel 			 * ehp->dhost will act as an index.
17201708Sstevel 			 */
17211708Sstevel 			for (i = 0; i < MAN_MAX_DESTS; i++) {
17221708Sstevel 
17231708Sstevel 				mdp = &msp->ms_dests[i];
17241708Sstevel 
17251708Sstevel 				if ((mdp->md_state == MAN_DSTATE_READY) &&
17261708Sstevel 				    (ether_cmp(eap, &mdp->md_dst_eaddr) == 0))
17271708Sstevel 					break;
17281708Sstevel 				mdp = NULL;
17291708Sstevel 			}
17301708Sstevel 		} else {
17311708Sstevel 			/*
17321708Sstevel 			 * 1 to 1 multiplexing, use upper wq for flow control.
17331708Sstevel 			 */
17341708Sstevel 			flow_wq = wq;
17351708Sstevel 		}
17361708Sstevel 
17371708Sstevel 		if (mdp != NULL) {
17381708Sstevel 			/*
17391708Sstevel 			 * Its going somewhere specific
17401708Sstevel 			 */
17411708Sstevel 			status =  man_start_lower(mdp, mp, flow_wq, MAN_UPPER);
17421708Sstevel 
17431708Sstevel 		} else {
17441708Sstevel 			MAN_DBG(MAN_DATA, ("man_start: no destination"
17457656SSherry.Moore@Sun.COM 			    " for eaddr %s\n", ether_sprintf(eap)));
17461708Sstevel 			freemsg(mp);
17471708Sstevel 		}
17481708Sstevel 	} else {
17491708Sstevel 		/*
17501708Sstevel 		 * Broadcast or multicast - send everone a copy.
17511708Sstevel 		 */
17521708Sstevel 		if (mdp == NULL) {
17531708Sstevel 			for (i = 0; i < MAN_MAX_DESTS; i++) {
17541708Sstevel 				mdp = &msp->ms_dests[i];
17551708Sstevel 
17561708Sstevel 				if (mdp->md_state != MAN_DSTATE_READY)
17571708Sstevel 					continue;
17581708Sstevel 
17591708Sstevel 				if ((tmp = copymsg(mp)) != NULL) {
17601708Sstevel 					(void) man_start_lower(mdp, tmp,
17617656SSherry.Moore@Sun.COM 					    NULL, MAN_UPPER);
17621708Sstevel 				} else {
17631708Sstevel 					MAN_DBG(MAN_DATA, ("man_start: copymsg"
17647656SSherry.Moore@Sun.COM 					    " failed!"));
17651708Sstevel 				}
17661708Sstevel 			}
17671708Sstevel 			freemsg(mp);
17681708Sstevel 		} else {
17691708Sstevel 			if (mdp->md_state == MAN_DSTATE_READY)
17701708Sstevel 				status =  man_start_lower(mdp, mp, wq,
17717656SSherry.Moore@Sun.COM 				    MAN_UPPER);
17721708Sstevel 			else
17731708Sstevel 				freemsg(mp);
17741708Sstevel 		}
17751708Sstevel 	}
17761708Sstevel 	return (status);
17771708Sstevel }
17781708Sstevel 
17791708Sstevel /*
17801708Sstevel  * Send a DL_UNITDATA or M_DATA fastpath data mblk to a particular
17811708Sstevel  * destination. Others mblk types sent down via * man_dlpi_senddown().
17821708Sstevel  *
17831708Sstevel  * Returns:
17841708Sstevel  *	0	- Data xmitted
17851708Sstevel  *	1	- Data not xmitted due to flow control.
17861708Sstevel  */
17871708Sstevel static int
man_start_lower(man_dest_t * mdp,mblk_t * mp,queue_t * flow_wq,int caller)17881708Sstevel man_start_lower(man_dest_t *mdp, mblk_t *mp, queue_t *flow_wq, int caller)
17891708Sstevel {
17901708Sstevel 	queue_t		*wq = mdp->md_wq;
17911708Sstevel 	int		status = 0;
17921708Sstevel 
17931708Sstevel 	/*
17941708Sstevel 	 * Lower stream ready for data transmit.
17951708Sstevel 	 */
17961708Sstevel 	if (mdp->md_state == MAN_DSTATE_READY &&
17971708Sstevel 	    mdp->md_dlpistate == DL_IDLE) {
17981708Sstevel 
17991708Sstevel 		ASSERT(mdp->md_wq != NULL);
18001708Sstevel 
18011708Sstevel 		if (caller == MAN_UPPER) {
18021708Sstevel 			/*
18031708Sstevel 			 * Check for flow control conditions for lower
18041708Sstevel 			 * stream.
18051708Sstevel 			 */
18061708Sstevel 			if (mdp->md_dmp_head == NULL &&
18077656SSherry.Moore@Sun.COM 			    wq->q_first == NULL && canputnext(wq)) {
18081708Sstevel 
18091708Sstevel 				(void) putnext(wq, mp);
18101708Sstevel 
18111708Sstevel 			} else {
18121708Sstevel 				mutex_enter(&mdp->md_lock);
18131708Sstevel 				if (mdp->md_dmp_head != NULL) {
18141708Sstevel 					/*
18151708Sstevel 					 * A simple flow control mechanism.
18161708Sstevel 					 */
18171708Sstevel 					if (mdp->md_dmp_count >= MAN_HIWAT) {
18181708Sstevel 						freemsg(mp);
18191708Sstevel 					} else {
18201708Sstevel 						/*
18211708Sstevel 						 * Add 'mp' to the deferred
18221708Sstevel 						 * msg list.
18231708Sstevel 						 */
18241708Sstevel 						mdp->md_dmp_tail->b_next = mp;
18251708Sstevel 						mdp->md_dmp_tail = mp;
18261708Sstevel 						mdp->md_dmp_count +=
18277656SSherry.Moore@Sun.COM 						    msgsize(mp);
18281708Sstevel 					}
18291708Sstevel 					mutex_exit(&mdp->md_lock);
18301708Sstevel 					/*
18311708Sstevel 					 * Inform flow control situation
18321708Sstevel 					 * to the caller.
18331708Sstevel 					 */
18341708Sstevel 					status = 1;
18351708Sstevel 					qenable(wq);
18361708Sstevel 					goto exit;
18371708Sstevel 				}
18381708Sstevel 				mutex_exit(&mdp->md_lock);
18391708Sstevel 				/*
18401708Sstevel 				 * If 1 to 1 mux, use upper write queue for
18411708Sstevel 				 * flow control.
18421708Sstevel 				 */
18431708Sstevel 				if (flow_wq != NULL) {
18441708Sstevel 					/*
18451708Sstevel 					 * putbq() message and indicate
18461708Sstevel 					 * flow control situation to the
18471708Sstevel 					 * caller.
18481708Sstevel 					 */
1849*11311SSurya.Prakki@Sun.COM 					(void) putbq(flow_wq, mp);
18501708Sstevel 					qenable(flow_wq);
18511708Sstevel 					status = 1;
18521708Sstevel 					goto exit;
18531708Sstevel 				}
18541708Sstevel 				/*
18551708Sstevel 				 * 1 to many mux, use lower write queue for
18561708Sstevel 				 * flow control. Be mindful not to overflow
18571708Sstevel 				 * the lower MAN STREAM q.
18581708Sstevel 				 */
18591708Sstevel 				if (canput(wq)) {
18601708Sstevel 					(void) putq(wq, mp);
18611708Sstevel 					qenable(wq);
18621708Sstevel 				} else {
18631708Sstevel 					MAN_DBG(MAN_DATA, ("man_start_lower:"
18647656SSherry.Moore@Sun.COM 					    " lower q flow controlled -"
18657656SSherry.Moore@Sun.COM 					    " discarding packet"));
18661708Sstevel 					freemsg(mp);
18671708Sstevel 					goto exit;
18681708Sstevel 				}
18691708Sstevel 			}
18701708Sstevel 
18711708Sstevel 		} else {
18721708Sstevel 			/*
18731708Sstevel 			 * man_lwsrv  is draining flow controlled mblks.
18741708Sstevel 			 */
18751708Sstevel 			if (canputnext(wq))
18761708Sstevel 				(void) putnext(wq, mp);
18771708Sstevel 			else
18781708Sstevel 				status = 1;
18791708Sstevel 		}
18801708Sstevel 		goto exit;
18811708Sstevel 	}
18821708Sstevel 
18831708Sstevel 	/*
18841708Sstevel 	 * Lower stream in transition, do flow control.
18851708Sstevel 	 */
18861708Sstevel 	status = 1;
18871708Sstevel 
18881708Sstevel 	if (mdp->md_state == MAN_DSTATE_NOTPRESENT) {
18891708Sstevel nodest:
18901708Sstevel 		cmn_err(CE_WARN,
18917656SSherry.Moore@Sun.COM 		    "man_start_lower: no dest for mdp(0x%p), caller(%d)!",
18927656SSherry.Moore@Sun.COM 		    (void *)mdp, caller);
18931708Sstevel 		if (caller == MAN_UPPER)
18941708Sstevel 			freemsg(mp);
18951708Sstevel 		goto exit;
18961708Sstevel 	}
18971708Sstevel 
18981708Sstevel 	if (mdp->md_state & MAN_DSTATE_CLOSING) {
18991708Sstevel 		MAN_DBG(MAN_DATA, ("man_start_lower: mdp(0x%p) closing",
19007656SSherry.Moore@Sun.COM 		    (void *)mdp));
19011708Sstevel 		if (caller == MAN_UPPER)
19021708Sstevel 			freemsg(mp);
19031708Sstevel 		goto exit;
19041708Sstevel 	}
19051708Sstevel 
19061708Sstevel 	if ((mdp->md_state & MAN_DSTATE_PLUMBING) ||
19071708Sstevel 	    (mdp->md_state == MAN_DSTATE_INITIALIZING) ||
19081708Sstevel 	    (mdp->md_dlpistate != DL_IDLE)) {
19091708Sstevel 		/*
19101708Sstevel 		 * Defer until PLUMBED and DL_IDLE. See man_lwsrv().
19111708Sstevel 		 */
19121708Sstevel 		if (caller == MAN_UPPER) {
19131708Sstevel 			/*
19141708Sstevel 			 * Upper stream sending data down, add to defered mblk
19151708Sstevel 			 * list for stream.
19161708Sstevel 			 */
19171708Sstevel 			mutex_enter(&mdp->md_lock);
19181708Sstevel 			if (mdp->md_dmp_count >= MAN_HIWAT) {
19191708Sstevel 				freemsg(mp);
19201708Sstevel 			} else {
19211708Sstevel 				if (mdp->md_dmp_head == NULL) {
19221708Sstevel 					ASSERT(mdp->md_dmp_tail == NULL);
19231708Sstevel 					mdp->md_dmp_head = mp;
19241708Sstevel 					mdp->md_dmp_tail = mp;
19251708Sstevel 				} else {
19261708Sstevel 					mdp->md_dmp_tail->b_next = mp;
19271708Sstevel 					mdp->md_dmp_tail = mp;
19281708Sstevel 				}
19291708Sstevel 				mdp->md_dmp_count += msgsize(mp);
19301708Sstevel 			}
19311708Sstevel 			mutex_exit(&mdp->md_lock);
19321708Sstevel 		}
19331708Sstevel 
19341708Sstevel 		goto exit;
19351708Sstevel 	}
19361708Sstevel 
19371708Sstevel exit:
19381708Sstevel 	return (status);
19391708Sstevel }
19401708Sstevel 
19411708Sstevel /*
19421708Sstevel  * man_ioctl - handle ioctl requests for this driver (I_PLINK/I_PUNLINK)
19431708Sstevel  * or pass thru to the physical driver below.  Note that most M_IOCTLs we
19441708Sstevel  * care about come down the control msp, but the IOC ones come down the IP.
19451708Sstevel  * Called with exclusive inner perimeter.
19461708Sstevel  *
19471708Sstevel  *	wq - upper write queue of mxx
19481708Sstevel  *	mp - mblk ptr to DLPI ioctl request
19491708Sstevel  */
19501708Sstevel static void
man_ioctl(register queue_t * wq,register mblk_t * mp)19511708Sstevel man_ioctl(register queue_t *wq, register mblk_t *mp)
19521708Sstevel {
19531708Sstevel 	manstr_t		*msp;
19541708Sstevel 	struct iocblk		*iocp;
19551708Sstevel 
19561708Sstevel 	iocp = (struct iocblk *)mp->b_rptr;
19571708Sstevel 	msp = (manstr_t *)wq->q_ptr;
19581708Sstevel 
19591708Sstevel #ifdef DEBUG
19601708Sstevel 	{
19611708Sstevel 		char			ioc_cmd[30];
19621708Sstevel 
1963*11311SSurya.Prakki@Sun.COM 		(void) sprintf(ioc_cmd, "not handled IOCTL 0x%x",
1964*11311SSurya.Prakki@Sun.COM 		    iocp->ioc_cmd);
19651708Sstevel 		MAN_DBG((MAN_SWITCH | MAN_PATH | MAN_DLPI),
19667656SSherry.Moore@Sun.COM 		    ("man_ioctl: wq(0x%p) mp(0x%p) cmd(%s)\n",
19677656SSherry.Moore@Sun.COM 		    (void *)wq, (void *)mp,
19687656SSherry.Moore@Sun.COM 		    (iocp->ioc_cmd == I_PLINK) ? "I_PLINK" :
19697656SSherry.Moore@Sun.COM 		    (iocp->ioc_cmd == I_PUNLINK) ? "I_PUNLINK" :
19707656SSherry.Moore@Sun.COM 		    (iocp->ioc_cmd == MAN_SETPATH) ? "MAN_SETPATH" :
19717656SSherry.Moore@Sun.COM 		    (iocp->ioc_cmd == DL_IOC_HDR_INFO) ? "DL_IOC_HDR_INFO" :
19727656SSherry.Moore@Sun.COM 		    (iocp->ioc_cmd == DLIOCRAW) ? "DLIOCRAW" : ioc_cmd));
19731708Sstevel 	}
19741708Sstevel #endif /* DEBUG */
19751708Sstevel 
19761708Sstevel 
19771708Sstevel 	/*
19781708Sstevel 	 *  Handle the requests...
19791708Sstevel 	 */
19801708Sstevel 	switch ((unsigned int)iocp->ioc_cmd) {
19811708Sstevel 
19821708Sstevel 	case I_PLINK:
19831708Sstevel 		man_plink(wq, mp);
19841708Sstevel 		break;
19851708Sstevel 
19861708Sstevel 	case I_PUNLINK:
19871708Sstevel 		man_unplink(wq, mp);
19881708Sstevel 		break;
19891708Sstevel 
19901708Sstevel 	case MAN_SETPATH:
19911708Sstevel 		man_setpath(wq, mp);
19921708Sstevel 		break;
19931708Sstevel 
19941708Sstevel 	case MAN_GETEADDR:
19951708Sstevel 		man_geteaddr(wq, mp);
19961708Sstevel 		break;
19971708Sstevel 
19981708Sstevel 	case MAN_SET_LINKCHECK_TIME:
19991708Sstevel 		man_set_linkcheck_time(wq, mp);
20001708Sstevel 		break;
20011708Sstevel 
20021708Sstevel 	case MAN_SET_SC_IPADDRS:
20031708Sstevel 		man_set_sc_ipaddrs(wq, mp);
20041708Sstevel 		break;
20051708Sstevel 
20061708Sstevel 	case MAN_SET_SC_IP6ADDRS:
20071708Sstevel 		man_set_sc_ip6addrs(wq, mp);
20081708Sstevel 		break;
20091708Sstevel 
20101708Sstevel 	case DLIOCRAW:
20111708Sstevel 		if (man_dlioc(msp, mp))
20121708Sstevel 			miocnak(wq, mp, 0, ENOMEM);
20131708Sstevel 		else {
20141708Sstevel 			msp->ms_flags |= MAN_SFLAG_RAW;
20151708Sstevel 			miocack(wq, mp, 0, 0);
20161708Sstevel 		}
20171708Sstevel 		break;
20181708Sstevel 
20191708Sstevel 	case DL_IOC_HDR_INFO:
20201708Sstevel 		man_dl_ioc_hdr_info(wq, mp);
20211708Sstevel 		break;
20221708Sstevel 
20231708Sstevel 	case MAN_ND_GET:
20241708Sstevel 	case MAN_ND_SET:
20251708Sstevel 		man_nd_getset(wq, mp);
20261708Sstevel 		break;
20271708Sstevel 
20281708Sstevel 	default:
20291708Sstevel 		MAN_DBG(MAN_DDI, ("man_ioctl: unknown ioc_cmd %d\n",
20307656SSherry.Moore@Sun.COM 		    (unsigned int)iocp->ioc_cmd));
20311708Sstevel 		miocnak(wq, mp, 0, EINVAL);
20321708Sstevel 		break;
20331708Sstevel 	}
20341708Sstevel exit:
20351708Sstevel 	MAN_DBG((MAN_SWITCH | MAN_PATH | MAN_DLPI), ("man_ioctl: exit\n"));
20361708Sstevel 
20371708Sstevel }
20381708Sstevel 
20391708Sstevel /*
20401708Sstevel  * man_plink: handle I_PLINK requests on the control stream
20411708Sstevel  */
20421708Sstevel void
man_plink(queue_t * wq,mblk_t * mp)20431708Sstevel man_plink(queue_t *wq, mblk_t *mp)
20441708Sstevel {
20451708Sstevel 	struct linkblk	*linkp;
20461708Sstevel 	man_linkrec_t	*lrp;
20471708Sstevel 	int		status = 0;
20481708Sstevel 
20491708Sstevel 	linkp = (struct linkblk *)mp->b_cont->b_rptr;
20501708Sstevel 
20511708Sstevel 	/*
20521708Sstevel 	 * Create a record to hold lower stream info. man_plumb will
20531708Sstevel 	 * retrieve it after calling ldi_ioctl(I_PLINK)
20541708Sstevel 	 */
20551708Sstevel 	lrp = man_kzalloc(sizeof (man_linkrec_t), KM_NOSLEEP);
20561708Sstevel 	if (lrp == NULL) {
20571708Sstevel 		status = ENOMEM;
20581708Sstevel 		goto exit;
20591708Sstevel 	}
20601708Sstevel 
20611708Sstevel 	lrp->l_muxid = linkp->l_index;
20621708Sstevel 	lrp->l_wq = linkp->l_qbot;
20631708Sstevel 	lrp->l_rq = RD(linkp->l_qbot);
20641708Sstevel 
20651708Sstevel 	man_linkrec_insert(lrp);
20661708Sstevel 
20671708Sstevel exit:
20681708Sstevel 	if (status)
20691708Sstevel 		miocnak(wq, mp, 0, status);
20701708Sstevel 	else
20711708Sstevel 		miocack(wq, mp, 0, 0);
20721708Sstevel 
20731708Sstevel }
20741708Sstevel 
20751708Sstevel /*
20761708Sstevel  * man_unplink - handle I_PUNLINK requests on the control stream
20771708Sstevel  */
20781708Sstevel void
man_unplink(queue_t * wq,mblk_t * mp)20791708Sstevel man_unplink(queue_t *wq, mblk_t *mp)
20801708Sstevel {
20811708Sstevel 	struct linkblk	*linkp;
20821708Sstevel 
20831708Sstevel 	linkp = (struct linkblk *)mp->b_cont->b_rptr;
20841708Sstevel 	RD(linkp->l_qbot)->q_ptr = NULL;
20851708Sstevel 	WR(linkp->l_qbot)->q_ptr = NULL;
20861708Sstevel 	miocack(wq, mp, 0, 0);
20871708Sstevel }
20881708Sstevel 
20891708Sstevel void
man_linkrec_insert(man_linkrec_t * lrp)20901708Sstevel man_linkrec_insert(man_linkrec_t *lrp)
20911708Sstevel {
20921708Sstevel 	mutex_enter(&man_lock);
20931708Sstevel 
20941708Sstevel 	lrp->l_next = man_linkrec_head;
20951708Sstevel 	man_linkrec_head = lrp;
20961708Sstevel 
20971708Sstevel 	mutex_exit(&man_lock);
20981708Sstevel 
20991708Sstevel }
21001708Sstevel 
21011708Sstevel static queue_t *
man_linkrec_find(int muxid)21021708Sstevel man_linkrec_find(int muxid)
21031708Sstevel {
21041708Sstevel 	man_linkrec_t	*lpp;
21051708Sstevel 	man_linkrec_t	*lp;
21061708Sstevel 	queue_t		*wq = NULL;
21071708Sstevel 
21081708Sstevel 	mutex_enter(&man_lock);
21091708Sstevel 
21101708Sstevel 	if (man_linkrec_head == NULL)
21111708Sstevel 		goto exit;
21121708Sstevel 
21131708Sstevel 	lp = lpp = man_linkrec_head;
21141708Sstevel 	if (lpp->l_muxid == muxid) {
21151708Sstevel 		man_linkrec_head = lpp->l_next;
21161708Sstevel 	} else {
21171708Sstevel 		for (lp = lpp->l_next; lp; lp = lp->l_next) {
21181708Sstevel 			if (lp->l_muxid == muxid)
21191708Sstevel 				break;
21201708Sstevel 			lpp = lp;
21211708Sstevel 		}
21221708Sstevel 	}
21231708Sstevel 
21241708Sstevel 	if (lp == NULL)
21251708Sstevel 		goto exit;
21261708Sstevel 
21271708Sstevel 	wq = lp->l_wq;
21281708Sstevel 	ASSERT(wq != NULL);
21291708Sstevel 
21301708Sstevel 	lpp->l_next = lp->l_next;
21311708Sstevel 	man_kfree(lp, sizeof (man_linkrec_t));
21321708Sstevel 
21331708Sstevel exit:
21341708Sstevel 	mutex_exit(&man_lock);
21351708Sstevel 
21361708Sstevel 	return (wq);
21371708Sstevel }
21381708Sstevel 
21391708Sstevel /*
21401708Sstevel  * Set instance linkcheck timer value.
21411708Sstevel  */
21421708Sstevel static void
man_set_linkcheck_time(queue_t * wq,mblk_t * mp)21431708Sstevel man_set_linkcheck_time(queue_t *wq, mblk_t *mp)
21441708Sstevel {
21451708Sstevel 	mi_time_t	*mtp;
21461708Sstevel 	int		error;
21471708Sstevel 	man_t		*manp;
21481708Sstevel 
21491708Sstevel 	MAN_DBG(MAN_LINK, ("man_set_linkcheck_time: enter"));
21501708Sstevel 
21511708Sstevel 	error = miocpullup(mp, sizeof (mi_time_t));
21521708Sstevel 	if (error != 0)
21531708Sstevel 		goto exit;
21541708Sstevel 
21551708Sstevel 	mtp = (mi_time_t *)mp->b_cont->b_rptr;
21561708Sstevel 
21571708Sstevel 	MAN_DBG(MAN_LINK, ("man_set_linkcheck_time: mtp"));
21581708Sstevel 	MAN_DBGCALL(MAN_LINK, man_print_mtp(mtp));
21591708Sstevel 
21601708Sstevel 	manp = ddi_get_soft_state(man_softstate, mtp->mtp_man_ppa);
21611708Sstevel 	if (manp == NULL) {
21621708Sstevel 		error = ENODEV;
21631708Sstevel 		goto exit;
21641708Sstevel 	}
21651708Sstevel 
21661708Sstevel 	manp->man_linkcheck_time = mtp->mtp_time;
21671708Sstevel exit:
21681708Sstevel 	if (error)
21691708Sstevel 		miocnak(wq, mp, 0, error);
21701708Sstevel 	else
21711708Sstevel 		miocack(wq, mp, sizeof (mi_time_t), 0);
21721708Sstevel }
21731708Sstevel 
21741708Sstevel /*
21751708Sstevel  * Man path ioctl processing. Should only happen on the SSC. Called
21761708Sstevel  * with exclusive inner perimeter.
21771708Sstevel  */
21781708Sstevel static void
man_setpath(queue_t * wq,mblk_t * mp)21791708Sstevel man_setpath(queue_t *wq, mblk_t *mp)
21801708Sstevel {
21811708Sstevel 	mi_path_t		*mip;
21821708Sstevel 	int			error;
21831708Sstevel 
21841708Sstevel 	error = miocpullup(mp, sizeof (mi_path_t));
21851708Sstevel 	if (error != 0)
21861708Sstevel 		goto exit;
21871708Sstevel 
21881708Sstevel 	mip = (mi_path_t *)mp->b_cont->b_rptr;
21891708Sstevel 	mutex_enter(&man_lock);
21901708Sstevel 	error = man_pg_cmd(mip, NULL);
21911708Sstevel 	mutex_exit(&man_lock);
21921708Sstevel 
21931708Sstevel exit:
21941708Sstevel 	if (error)
21951708Sstevel 		miocnak(wq, mp, 0, error);
21961708Sstevel 	else
21971708Sstevel 		miocack(wq, mp, sizeof (mi_path_t), 0);
21981708Sstevel }
21991708Sstevel 
22001708Sstevel /*
22011708Sstevel  * Get the local ethernet address of this machine.
22021708Sstevel  */
22031708Sstevel static void
man_geteaddr(queue_t * wq,mblk_t * mp)22041708Sstevel man_geteaddr(queue_t *wq, mblk_t *mp)
22051708Sstevel {
22061708Sstevel 	eaddr_t			*eap;
22071708Sstevel 	int			error;
22081708Sstevel 
22091708Sstevel 	error = miocpullup(mp, sizeof (eaddr_t));
22101708Sstevel 	if (error != 0) {
22111708Sstevel 		miocnak(wq, mp, 0, error);
22121708Sstevel 		return;
22131708Sstevel 	}
22141708Sstevel 
22151708Sstevel 	eap = (eaddr_t *)mp->b_cont->b_rptr;
22161708Sstevel 	(void) localetheraddr(NULL, eap);
22171708Sstevel 	miocack(wq, mp, sizeof (eaddr_t), 0);
22181708Sstevel }
22191708Sstevel 
22201708Sstevel /*
22211708Sstevel  * Set my SC and other SC IPv4 addresses for use in man_pinger routine.
22221708Sstevel  */
22231708Sstevel static void
man_set_sc_ipaddrs(queue_t * wq,mblk_t * mp)22241708Sstevel man_set_sc_ipaddrs(queue_t *wq, mblk_t *mp)
22251708Sstevel {
22261708Sstevel 	int			error;
22271708Sstevel 
22281708Sstevel 	error = miocpullup(mp, sizeof (man_sc_ipaddrs_t));
22291708Sstevel 	if (error != 0)
22301708Sstevel 		goto exit;
22311708Sstevel 
22321708Sstevel 	man_sc_ipaddrs = *(man_sc_ipaddrs_t *)mp->b_cont->b_rptr;
22331708Sstevel 
22341708Sstevel #ifdef DEBUG
22351708Sstevel 	{
22361708Sstevel 		char	buf[INET_ADDRSTRLEN];
22371708Sstevel 
22381708Sstevel 		(void) inet_ntop(AF_INET,
22397656SSherry.Moore@Sun.COM 		    (void *) &man_sc_ipaddrs.ip_other_sc_ipaddr,
22407656SSherry.Moore@Sun.COM 		    buf, INET_ADDRSTRLEN);
22411708Sstevel 		MAN_DBG(MAN_CONFIG, ("ip_other_sc_ipaddr = %s", buf));
22421708Sstevel 		(void) inet_ntop(AF_INET,
22437656SSherry.Moore@Sun.COM 		    (void *) &man_sc_ipaddrs.ip_my_sc_ipaddr,
22447656SSherry.Moore@Sun.COM 		    buf, INET_ADDRSTRLEN);
22451708Sstevel 		MAN_DBG(MAN_CONFIG, ("ip_my_sc_ipaddr = %s", buf));
22461708Sstevel 	}
22471708Sstevel #endif /* DEBUG */
22481708Sstevel exit:
22491708Sstevel 	if (error)
22501708Sstevel 		miocnak(wq, mp, 0, error);
22511708Sstevel 	else
22521708Sstevel 		miocack(wq, mp, sizeof (man_sc_ipaddrs_t), 0);
22531708Sstevel }
22541708Sstevel 
22551708Sstevel /*
22561708Sstevel  * Set my SC and other SC IPv6 addresses for use in man_pinger routine.
22571708Sstevel  */
22581708Sstevel static void
man_set_sc_ip6addrs(queue_t * wq,mblk_t * mp)22591708Sstevel man_set_sc_ip6addrs(queue_t *wq, mblk_t *mp)
22601708Sstevel {
22611708Sstevel 	int			error;
22621708Sstevel 
22631708Sstevel 	error = miocpullup(mp, sizeof (man_sc_ip6addrs_t));
22641708Sstevel 	if (error != 0)
22651708Sstevel 		goto exit;
22661708Sstevel 
22671708Sstevel 	man_sc_ip6addrs = *(man_sc_ip6addrs_t *)mp->b_cont->b_rptr;
22681708Sstevel 
22691708Sstevel #ifdef DEBUG
22701708Sstevel 	{
22711708Sstevel 		char	buf[INET6_ADDRSTRLEN];
22721708Sstevel 
22731708Sstevel 		(void) inet_ntop(AF_INET6,
22747656SSherry.Moore@Sun.COM 		    (void *) &man_sc_ip6addrs.ip6_other_sc_ipaddr,
22757656SSherry.Moore@Sun.COM 		    buf, INET6_ADDRSTRLEN);
22761708Sstevel 		MAN_DBG(MAN_CONFIG, ("ip6_other_sc_ipaddr = %s", buf));
22771708Sstevel 		(void) inet_ntop(AF_INET6,
22787656SSherry.Moore@Sun.COM 		    (void *) &man_sc_ip6addrs.ip6_my_sc_ipaddr,
22797656SSherry.Moore@Sun.COM 		    buf, INET6_ADDRSTRLEN);
22801708Sstevel 		MAN_DBG(MAN_CONFIG, ("ip6_my_sc_ipaddr = %s", buf));
22811708Sstevel 	}
22821708Sstevel #endif /* DEBUG */
22831708Sstevel exit:
22841708Sstevel 	if (error)
22851708Sstevel 		miocnak(wq, mp, 0, error);
22861708Sstevel 	else
22871708Sstevel 		miocack(wq, mp, sizeof (man_sc_ip6addrs_t), 0);
22881708Sstevel }
22891708Sstevel 
22901708Sstevel /*
22911708Sstevel  * M_DATA fastpath info request.
22921708Sstevel  */
22931708Sstevel static void
man_dl_ioc_hdr_info(queue_t * wq,mblk_t * mp)22941708Sstevel man_dl_ioc_hdr_info(queue_t *wq, mblk_t *mp)
22951708Sstevel {
22961708Sstevel 	manstr_t		*msp;
22971708Sstevel 	man_t			*manp;
22981708Sstevel 	mblk_t			*nmp;
22991708Sstevel 	man_dladdr_t		*dlap;
23001708Sstevel 	dl_unitdata_req_t	*dludp;
23011708Sstevel 	struct	ether_header	*headerp;
23021708Sstevel 	t_uscalar_t		off, len;
23031708Sstevel 	int			status = 0;
23041708Sstevel 
23051708Sstevel 	MAN_DBG(MAN_DLPI, ("man_dl_ioc_hdr_info: enter"));
23061708Sstevel 
23071708Sstevel 	msp = (manstr_t *)wq->q_ptr;
23081708Sstevel 	manp = msp->ms_manp;
23091708Sstevel 	if (manp == NULL) {
23101708Sstevel 		status = EINVAL;
23111708Sstevel 		goto exit;
23121708Sstevel 	}
23131708Sstevel 
23141708Sstevel 	status = miocpullup(mp, sizeof (dl_unitdata_req_t) + MAN_ADDRL);
23151708Sstevel 	if (status != 0)
23161708Sstevel 		goto exit;
23171708Sstevel 
23181708Sstevel 	/*
23191708Sstevel 	 * Sanity check the DL_UNITDATA_REQ destination address
23201708Sstevel 	 * offset and length values.
23211708Sstevel 	 */
23221708Sstevel 	dludp = (dl_unitdata_req_t *)mp->b_cont->b_rptr;
23231708Sstevel 	off = dludp->dl_dest_addr_offset;
23241708Sstevel 	len = dludp->dl_dest_addr_length;
23251708Sstevel 	if (dludp->dl_primitive != DL_UNITDATA_REQ ||
23261708Sstevel 	    !MBLKIN(mp->b_cont, off, len) || len != MAN_ADDRL) {
23271708Sstevel 		status = EINVAL;
23281708Sstevel 		goto exit;
23291708Sstevel 	}
23301708Sstevel 
23311708Sstevel 	dlap = (man_dladdr_t  *)(mp->b_cont->b_rptr + off);
23321708Sstevel 
23331708Sstevel 	/*
23341708Sstevel 	 * Allocate a new mblk to hold the ether header.
23351708Sstevel 	 */
23361708Sstevel 	if ((nmp = allocb(ETHERHEADER_SIZE, BPRI_MED)) == NULL) {
23371708Sstevel 		status = ENOMEM;
23381708Sstevel 		goto exit;
23391708Sstevel 	}
23401708Sstevel 
23411708Sstevel 	/* We only need one dl_ioc_hdr mblk for replay */
23421708Sstevel 	if (!(msp->ms_flags & MAN_SFLAG_FAST))
23431708Sstevel 		status = man_dl_catch(&msp->ms_dlioc_mp, mp);
23441708Sstevel 
23451708Sstevel 	/* Forward the packet to all lower destinations. */
23461708Sstevel 	if ((status != 0) || ((status = man_dlpi_senddown(msp, mp)) != 0)) {
23471708Sstevel 		freemsg(nmp);
23481708Sstevel 		goto exit;
23491708Sstevel 	}
23501708Sstevel 
23511708Sstevel 	nmp->b_wptr += ETHERHEADER_SIZE;
23521708Sstevel 
23531708Sstevel 	/*
23541708Sstevel 	 * Fill in the ether header.
23551708Sstevel 	 */
23561708Sstevel 	headerp = (struct ether_header *)nmp->b_rptr;
23571708Sstevel 	ether_copy(&dlap->dl_phys, &headerp->ether_dhost);
23581708Sstevel 	ether_copy(&manp->man_eaddr, &headerp->ether_shost);
23591708Sstevel 	put_ether_type(headerp, dlap->dl_sap);
23601708Sstevel 
23611708Sstevel 	/*
23621708Sstevel 	 * Link new mblk in after the "request" mblks.
23631708Sstevel 	 */
23641708Sstevel 	linkb(mp, nmp);
23651708Sstevel 
23661708Sstevel exit:
23671708Sstevel 	MAN_DBG(MAN_DLPI, ("man_dl_ioc_hdr_info: returns, status = %d",
23687656SSherry.Moore@Sun.COM 	    status));
23691708Sstevel 
23701708Sstevel 	if (status) {
23711708Sstevel 		miocnak(wq, mp, 0, status);
23721708Sstevel 	} else {
23731708Sstevel 		msp = (manstr_t *)wq->q_ptr;
23741708Sstevel 		msp->ms_flags |= MAN_SFLAG_FAST;
23751708Sstevel 		miocack(wq, mp, msgsize(mp->b_cont), 0);
23761708Sstevel 	}
23771708Sstevel 
23781708Sstevel }
23791708Sstevel 
23801708Sstevel /*
23811708Sstevel  * man_uwsrv - Upper write queue service routine to handle deferred
23821708Sstevel  * DLPI messages issued from upstream, the write side of the upper half
23831708Sstevel  * of multiplexor. It is also used by man_bwork to switch the lower
23841708Sstevel  * multiplexor.
23851708Sstevel  *
23861708Sstevel  *	wq - upper write queue of mxx
23871708Sstevel  */
23881708Sstevel static int
man_uwsrv(queue_t * wq)23891708Sstevel man_uwsrv(queue_t *wq)
23901708Sstevel {
23911708Sstevel 	register mblk_t		*mp;
23921708Sstevel 	manstr_t		*msp;		/* per stream data */
23931708Sstevel 	man_t			*manp;		/* per instance data */
23941708Sstevel 	ehdr_t			*ep;
23951708Sstevel 	int			status;
23961708Sstevel 
23971708Sstevel 	msp = (manstr_t *)wq->q_ptr;
23981708Sstevel 
23991708Sstevel 	MAN_DBG(MAN_UWSRV, ("man_uwsrv: wq(0x%p) msp", (void *)wq));
24001708Sstevel 	MAN_DBGCALL(MAN_UWSRV, man_print_msp(msp));
24011708Sstevel 
24021708Sstevel 	if (msp == NULL)
24031708Sstevel 		goto done;
24041708Sstevel 
24051708Sstevel 	manp = msp->ms_manp;
24061708Sstevel 
24071708Sstevel 	while (mp = getq(wq)) {
24081708Sstevel 
24091708Sstevel 		switch (DB_TYPE(mp)) {
24101708Sstevel 		/*
24111708Sstevel 		 * Can probably remove this as I never put data messages
24121708Sstevel 		 * here.
24131708Sstevel 		 */
24141708Sstevel 		case M_DATA:
24151708Sstevel 			if (manp) {
24161708Sstevel 				ep = (ehdr_t *)mp->b_rptr;
24171708Sstevel 				status = man_start(wq, mp, &ep->ether_dhost);
24181708Sstevel 				if (status) {
24191708Sstevel 					/*
24201708Sstevel 					 * man_start() indicated flow control
24211708Sstevel 					 * situation, stop processing now.
24221708Sstevel 					 */
24231708Sstevel 					goto break_loop;
24241708Sstevel 				}
24251708Sstevel 			} else
24261708Sstevel 				freemsg(mp);
24271708Sstevel 			break;
24281708Sstevel 
24291708Sstevel 		case M_PROTO:
24301708Sstevel 		case M_PCPROTO:
24311708Sstevel 			status = man_proto(wq, mp);
24321708Sstevel 			if (status) {
24331708Sstevel 				/*
24341708Sstevel 				 * man_proto() indicated flow control
24351708Sstevel 				 * situation detected by man_start(),
24361708Sstevel 				 * stop processing now.
24371708Sstevel 				 */
24381708Sstevel 				goto break_loop;
24391708Sstevel 			}
24401708Sstevel 			break;
24411708Sstevel 
24421708Sstevel 		default:
24431708Sstevel 			MAN_DBG(MAN_UWSRV, ("man_uwsrv: discarding mp(0x%p)",
24447656SSherry.Moore@Sun.COM 			    (void *)mp));
24451708Sstevel 			freemsg(mp);
24461708Sstevel 			break;
24471708Sstevel 		}
24481708Sstevel 	}
24491708Sstevel 
24501708Sstevel break_loop:
24511708Sstevel 	/*
24521708Sstevel 	 * Check to see if bgthread wants us to do something inside the
24531708Sstevel 	 * perimeter.
24541708Sstevel 	 */
24551708Sstevel 	if ((msp->ms_flags & MAN_SFLAG_CONTROL) &&
24567656SSherry.Moore@Sun.COM 	    man_iwork_q->q_work != NULL) {
24571708Sstevel 
24581708Sstevel 		man_iwork();
24591708Sstevel 	}
24601708Sstevel 
24611708Sstevel done:
24621708Sstevel 
24631708Sstevel 	MAN_DBG(MAN_UWSRV, ("man_uwsrv: returns"));
24641708Sstevel 
24651708Sstevel 	return (0);
24661708Sstevel }
24671708Sstevel 
24681708Sstevel 
24691708Sstevel /*
24701708Sstevel  * man_proto - handle DLPI protocol requests issued from upstream.
24711708Sstevel  * Called by man_uwsrv().  We disassociate upper and lower multiplexor
24721708Sstevel  * DLPI state transitions. The upper stream here (manstr_t) transitions
24731708Sstevel  * appropriately, saves the DLPI requests via man_dlpi(), and then
24741708Sstevel  * arranges for the DLPI request to be sent down via man_dlpi_senddown() if
24751708Sstevel  * appropriate.
24761708Sstevel  *
24771708Sstevel  *	wq - upper write queue of mxx
24781708Sstevel  *	mp - mbl ptr to protocol request
24791708Sstevel  */
24801708Sstevel static int
man_proto(queue_t * wq,mblk_t * mp)24811708Sstevel man_proto(queue_t *wq, mblk_t *mp)
24821708Sstevel {
24831708Sstevel 	union DL_primitives	*dlp;
24841708Sstevel 	int			flow_status = 0;
24851708Sstevel 
24861708Sstevel 	dlp = (union DL_primitives *)mp->b_rptr;
24871708Sstevel 
24881708Sstevel 	MAN_DBG((MAN_UWSRV | MAN_DLPI),
24897656SSherry.Moore@Sun.COM 	    ("man_proto: mp(0x%p) prim(%s)\n", (void *)mp,
24907656SSherry.Moore@Sun.COM 	    dps[dlp->dl_primitive]));
24911708Sstevel 
24921708Sstevel 	switch (dlp->dl_primitive) {
24931708Sstevel 	case DL_UNITDATA_REQ:
24941708Sstevel 		flow_status = man_udreq(wq, mp);
24951708Sstevel 		break;
24961708Sstevel 
24971708Sstevel 	case DL_ATTACH_REQ:
24981708Sstevel 		man_areq(wq, mp);
24991708Sstevel 		break;
25001708Sstevel 
25011708Sstevel 	case DL_DETACH_REQ:
25021708Sstevel 		man_dreq(wq, mp);
25031708Sstevel 		break;
25041708Sstevel 
25051708Sstevel 	case DL_BIND_REQ:
25061708Sstevel 		man_breq(wq, mp);
25071708Sstevel 		break;
25081708Sstevel 
25091708Sstevel 	case DL_UNBIND_REQ:
25101708Sstevel 		man_ubreq(wq, mp);
25111708Sstevel 		break;
25121708Sstevel 
25131708Sstevel 	case DL_INFO_REQ:
25141708Sstevel 		man_ireq(wq, mp);
25151708Sstevel 		break;
25161708Sstevel 
25171708Sstevel 	case DL_PROMISCON_REQ:
25181708Sstevel 		man_ponreq(wq, mp);
25191708Sstevel 		break;
25201708Sstevel 
25211708Sstevel 	case DL_PROMISCOFF_REQ:
25221708Sstevel 		man_poffreq(wq, mp);
25231708Sstevel 		break;
25241708Sstevel 
25251708Sstevel 	case DL_ENABMULTI_REQ:
25261708Sstevel 		man_emreq(wq, mp);
25271708Sstevel 		break;
25281708Sstevel 
25291708Sstevel 	case DL_DISABMULTI_REQ:
25301708Sstevel 		man_dmreq(wq, mp);
25311708Sstevel 		break;
25321708Sstevel 
25331708Sstevel 	case DL_PHYS_ADDR_REQ:
25341708Sstevel 		man_pareq(wq, mp);
25351708Sstevel 		break;
25361708Sstevel 
25371708Sstevel 	case DL_SET_PHYS_ADDR_REQ:
25381708Sstevel 		man_spareq(wq, mp);
25391708Sstevel 		break;
25401708Sstevel 
25411708Sstevel 	default:
25421708Sstevel 		MAN_DBG((MAN_UWSRV | MAN_DLPI), ("man_proto: prim(%d)\n",
25437656SSherry.Moore@Sun.COM 		    dlp->dl_primitive));
25441708Sstevel 		dlerrorack(wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0);
25451708Sstevel 		break;
25461708Sstevel 
25471708Sstevel 	} /* End switch */
25481708Sstevel 
25491708Sstevel 	MAN_DBG((MAN_UWSRV | MAN_DLPI), ("man_proto: exit\n"));
25501708Sstevel 	return (flow_status);
25511708Sstevel 
25521708Sstevel }
25531708Sstevel 
25541708Sstevel static int
man_udreq(queue_t * wq,mblk_t * mp)25551708Sstevel man_udreq(queue_t *wq, mblk_t *mp)
25561708Sstevel {
25571708Sstevel 	manstr_t		*msp;
25581708Sstevel 	dl_unitdata_req_t	*dludp;
25591708Sstevel 	mblk_t	*nmp;
25601708Sstevel 	man_dladdr_t		*dlap;
25611708Sstevel 	t_uscalar_t 		off, len;
25621708Sstevel 	int 			flow_status = 0;
25631708Sstevel 
25641708Sstevel 	msp = (manstr_t *)wq->q_ptr;
25651708Sstevel 
25661708Sstevel 
25671708Sstevel 	if (msp->ms_dlpistate != DL_IDLE) {
25681708Sstevel 		dlerrorack(wq, mp, DL_UNITDATA_REQ, DL_OUTSTATE, 0);
25691708Sstevel 		return (flow_status);
25701708Sstevel 	}
25711708Sstevel 	dludp = (dl_unitdata_req_t *)mp->b_rptr;
25721708Sstevel 	off = dludp->dl_dest_addr_offset;
25731708Sstevel 	len = dludp->dl_dest_addr_length;
25741708Sstevel 
25751708Sstevel 	/*
25761708Sstevel 	 * Validate destination address format.
25771708Sstevel 	 */
25781708Sstevel 	if (!MBLKIN(mp, off, len) || (len != MAN_ADDRL)) {
25791708Sstevel 		dluderrorind(wq, mp, mp->b_rptr + off, len, DL_BADADDR, 0);
25801708Sstevel 		return (flow_status);
25811708Sstevel 	}
25821708Sstevel 
25831708Sstevel 	/*
25841708Sstevel 	 * Error if no M_DATA follows.
25851708Sstevel 	 */
25861708Sstevel 	nmp = mp->b_cont;
25871708Sstevel 	if (nmp == NULL) {
25881708Sstevel 		dluderrorind(wq, mp, mp->b_rptr + off, len, DL_BADDATA, 0);
25891708Sstevel 		return (flow_status);
25901708Sstevel 	}
25911708Sstevel 
25921708Sstevel 	dlap = (man_dladdr_t *)(mp->b_rptr + off);
25931708Sstevel 
25941708Sstevel 	flow_status = man_start(wq, mp, &dlap->dl_phys);
25951708Sstevel 	return (flow_status);
25961708Sstevel }
25971708Sstevel 
25981708Sstevel /*
25991708Sstevel  * Handle DL_ATTACH_REQ.
26001708Sstevel  */
26011708Sstevel static void
man_areq(queue_t * wq,mblk_t * mp)26021708Sstevel man_areq(queue_t *wq, mblk_t *mp)
26031708Sstevel {
26041708Sstevel 	man_t			*manp;	/* per instance data */
26051708Sstevel 	manstr_t		*msp;	/* per stream data */
26061708Sstevel 	short			ppa;
26071708Sstevel 	union DL_primitives	*dlp;
26081708Sstevel 	mblk_t			*preq = NULL;
26091708Sstevel 	int			did_refcnt = FALSE;
26101708Sstevel 	int			dlerror = 0;
26111708Sstevel 	int			status = 0;
26121708Sstevel 
26131708Sstevel 	msp = (manstr_t *)wq->q_ptr;
26141708Sstevel 	dlp = (union DL_primitives *)mp->b_rptr;
26151708Sstevel 
26161708Sstevel 	/*
26171708Sstevel 	 * Attach us to MAN PPA (device instance).
26181708Sstevel 	 */
26191708Sstevel 	if (MBLKL(mp) < DL_ATTACH_REQ_SIZE) {
26201708Sstevel 		dlerror = DL_BADPRIM;
26211708Sstevel 		goto exit;
26221708Sstevel 	}
26231708Sstevel 
26241708Sstevel 	if (msp->ms_dlpistate != DL_UNATTACHED) {
26251708Sstevel 		dlerror = DL_OUTSTATE;
26261708Sstevel 		goto exit;
26271708Sstevel 	}
26281708Sstevel 
26291708Sstevel 	ppa = dlp->attach_req.dl_ppa;
26301708Sstevel 	if (ppa == -1 || qassociate(wq, ppa) != 0) {
26311708Sstevel 		dlerror = DL_BADPPA;
26321708Sstevel 		MAN_DBG(MAN_WARN, ("man_areq: bad PPA %d", ppa));
26331708Sstevel 		goto exit;
26341708Sstevel 	}
26351708Sstevel 
26361708Sstevel 	mutex_enter(&man_lock);
26371708Sstevel 	manp = ddi_get_soft_state(man_softstate, ppa);
26381708Sstevel 	ASSERT(manp != NULL);	/* qassociate() succeeded */
26391708Sstevel 
26401708Sstevel 	manp->man_refcnt++;
26411708Sstevel 	did_refcnt = TRUE;
26421708Sstevel 	mutex_exit(&man_lock);
26431708Sstevel 
26441708Sstevel 	/*
26451708Sstevel 	 * Create a DL replay list for the lower stream. These wont
26461708Sstevel 	 * actually be sent down until the lower streams are made active
26471708Sstevel 	 * (sometime after the call to man_init_dests below).
26481708Sstevel 	 */
26491708Sstevel 	preq = man_alloc_physreq_mp(&manp->man_eaddr);
26501708Sstevel 	if (preq == NULL) {
26511708Sstevel 		dlerror = DL_SYSERR;
26521708Sstevel 		status = ENOMEM;
26531708Sstevel 		goto exit;
26541708Sstevel 	}
26551708Sstevel 
26561708Sstevel 	/*
26571708Sstevel 	 * Make copy for dlpi resync of upper and lower streams.
26581708Sstevel 	 */
26591708Sstevel 	if (man_dlpi(msp, mp)) {
26601708Sstevel 		dlerror = DL_SYSERR;
26611708Sstevel 		status = ENOMEM;
26621708Sstevel 		goto exit;
26631708Sstevel 	}
26641708Sstevel 
26651708Sstevel 	/* TBD - need to clean off ATTACH req on failure here. */
26661708Sstevel 	if (man_dlpi(msp, preq)) {
26671708Sstevel 		dlerror = DL_SYSERR;
26681708Sstevel 		status = ENOMEM;
26691708Sstevel 		goto exit;
26701708Sstevel 	}
26711708Sstevel 
26721708Sstevel 	/*
26731708Sstevel 	 * man_init_dests/man_start_dest needs these set before call.
26741708Sstevel 	 */
26751708Sstevel 	msp->ms_manp = manp;
26761708Sstevel 	msp->ms_meta_ppa = ppa;
26771708Sstevel 
26781708Sstevel 	/*
26791708Sstevel 	 *  Allocate and init lower destination structures.
26801708Sstevel 	 */
26811708Sstevel 	ASSERT(msp->ms_dests == NULL);
26821708Sstevel 	if (man_init_dests(manp, msp)) {
26831708Sstevel 		mblk_t	 *tmp;
26841708Sstevel 
26851708Sstevel 		/*
26861708Sstevel 		 * If we cant get the lower streams ready, then
26871708Sstevel 		 * remove the messages from the DL replay list and
26881708Sstevel 		 * fail attach.
26891708Sstevel 		 */
26901708Sstevel 		while ((tmp = msp->ms_dl_mp) != NULL) {
26911708Sstevel 			msp->ms_dl_mp = msp->ms_dl_mp->b_next;
26921708Sstevel 			tmp->b_next = tmp->b_prev = NULL;
26931708Sstevel 			freemsg(tmp);
26941708Sstevel 		}
26951708Sstevel 
26961708Sstevel 		msp->ms_manp = NULL;
26971708Sstevel 		msp->ms_meta_ppa = -1;
26981708Sstevel 
26991708Sstevel 		dlerror = DL_SYSERR;
27001708Sstevel 		status = ENOMEM;
27011708Sstevel 		goto exit;
27021708Sstevel 	}
27031708Sstevel 
27041708Sstevel 	MAN_DBG(MAN_DLPI, ("man_areq: ppa 0x%x man_refcnt: %d\n",
27057656SSherry.Moore@Sun.COM 	    ppa, manp->man_refcnt));
27061708Sstevel 
27071708Sstevel 	SETSTATE(msp, DL_UNBOUND);
27081708Sstevel 
27091708Sstevel exit:
27101708Sstevel 	if (dlerror == 0) {
27111708Sstevel 		dlokack(wq, mp, DL_ATTACH_REQ);
27121708Sstevel 	} else {
27131708Sstevel 		if (did_refcnt) {
27141708Sstevel 			mutex_enter(&man_lock);
27151708Sstevel 			manp->man_refcnt--;
27161708Sstevel 			mutex_exit(&man_lock);
27171708Sstevel 		}
27181708Sstevel 		dlerrorack(wq, mp, DL_ATTACH_REQ, dlerror, status);
27191708Sstevel 		(void) qassociate(wq, -1);
27201708Sstevel 	}
27211708Sstevel 	if (preq != NULL)
27221708Sstevel 		freemsg(preq);
27231708Sstevel 
27241708Sstevel }
27251708Sstevel 
27261708Sstevel /*
27271708Sstevel  * Called at DL_ATTACH time.
27281708Sstevel  * Man_lock is held to protect pathgroup list(man_pg).
27291708Sstevel  */
27301708Sstevel static int
man_init_dests(man_t * manp,manstr_t * msp)27311708Sstevel man_init_dests(man_t *manp, manstr_t *msp)
27321708Sstevel {
27331708Sstevel 	man_dest_t	*mdp;
27341708Sstevel 	man_pg_t	*mpg;
27351708Sstevel 	int		i;
27361708Sstevel 
27371708Sstevel 	mdp = man_kzalloc(MAN_DEST_ARRAY_SIZE, KM_NOSLEEP);
27381708Sstevel 	if (mdp == NULL)
27391708Sstevel 		return (ENOMEM);
27401708Sstevel 
27411708Sstevel 	msp->ms_dests = mdp;
27421708Sstevel 
27431708Sstevel 	mutex_enter(&man_lock);
27441708Sstevel 	for (i = 0; i < MAN_MAX_DESTS; i++) {
27451708Sstevel 
27461708Sstevel 		mdp[i].md_muxid = -1;	/* muxid 0 is valid */
27471708Sstevel 		mutex_init(&mdp->md_lock, NULL, MUTEX_DRIVER, NULL);
27481708Sstevel 
27491708Sstevel 		mpg = man_find_pg_by_id(manp->man_pg, i);
27501708Sstevel 
27511708Sstevel 		if (mpg && man_find_active_path(mpg->mpg_pathp))
27521708Sstevel 			man_start_dest(&mdp[i], msp, mpg);
27531708Sstevel 	}
27541708Sstevel 	mutex_exit(&man_lock);
27551708Sstevel 
27561708Sstevel 	return (0);
27571708Sstevel }
27581708Sstevel 
27591708Sstevel /*
27601708Sstevel  * Get a destination ready for use.
27611708Sstevel  */
27621708Sstevel static void
man_start_dest(man_dest_t * mdp,manstr_t * msp,man_pg_t * mpg)27631708Sstevel man_start_dest(man_dest_t *mdp, manstr_t *msp, man_pg_t *mpg)
27641708Sstevel {
27651708Sstevel 	man_path_t	*ap;
27661708Sstevel 
27671708Sstevel 	mdp->md_muxid = -1;
27681708Sstevel 	mdp->md_dlpistate = DL_UNATTACHED;
27691708Sstevel 	mdp->md_msp = msp;
27701708Sstevel 	mdp->md_rq = msp->ms_rq;
27711708Sstevel 	mdp->md_pg_id = mpg->mpg_pg_id;
27721708Sstevel 
27731708Sstevel 	ASSERT(msp->ms_manp);
27741708Sstevel 
27751708Sstevel 	ether_copy(&msp->ms_manp->man_eaddr, &mdp->md_src_eaddr);
27761708Sstevel 	ether_copy(&mpg->mpg_dst_eaddr, &mdp->md_dst_eaddr);
27771708Sstevel 
27781708Sstevel 	ap = man_find_active_path(mpg->mpg_pathp);
27791708Sstevel 	ASSERT(ap);
27801708Sstevel 	mdp->md_device = ap->mp_device;
27811708Sstevel 
27821708Sstevel 	/*
27831708Sstevel 	 * Set up linktimers so that first time through, we will do
27841708Sstevel 	 * a failover.
27851708Sstevel 	 */
27861708Sstevel 	mdp->md_linkstate = MAN_LINKFAIL;
27871708Sstevel 	mdp->md_state = MAN_DSTATE_INITIALIZING;
27881708Sstevel 	mdp->md_lc_timer_id = qtimeout(man_ctl_wq, man_linkcheck_timer,
27897656SSherry.Moore@Sun.COM 	    (void *)mdp, man_gettimer(MAN_TIMER_INIT, mdp));
27901708Sstevel 
27911708Sstevel 	/*
27921708Sstevel 	 * As an optimization, if there is only one destination,
27931708Sstevel 	 * remember the destination pointer. Used by man_start().
27941708Sstevel 	 */
27951708Sstevel 	man_set_optimized_dest(msp);
27961708Sstevel 
27971708Sstevel 	MAN_DBG(MAN_DEST, ("man_start_dest: mdp"));
27981708Sstevel 	MAN_DBGCALL(MAN_DEST, man_print_mdp(mdp));
27991708Sstevel }
28001708Sstevel 
28011708Sstevel static void
man_set_optimized_dest(manstr_t * msp)28021708Sstevel man_set_optimized_dest(manstr_t *msp)
28031708Sstevel {
28041708Sstevel 	int		count = 0;
28051708Sstevel 	int		i;
28061708Sstevel 	man_dest_t	*mdp = NULL;
28071708Sstevel 
28081708Sstevel 	for (i = 0; i < MAN_MAX_DESTS; i++) {
28091708Sstevel 		if (msp->ms_dests[i].md_msp != NULL) {
28101708Sstevel 			count++;
28111708Sstevel 			mdp = &msp->ms_dests[i];
28121708Sstevel 		}
28131708Sstevel 	}
28141708Sstevel 
28151708Sstevel 	if (count == 1)
28161708Sstevel 		msp->ms_destp = mdp;
28171708Sstevel 	else
28181708Sstevel 		msp->ms_destp = NULL;
28191708Sstevel 
28201708Sstevel }
28211708Sstevel 
28221708Sstevel /*
28231708Sstevel  * Catch dlpi message for replaying, and arrange to send it down
28241708Sstevel  * to any destinations not PLUMBING. See man_dlpi_replay().
28251708Sstevel  */
28261708Sstevel static int
man_dlpi(manstr_t * msp,mblk_t * mp)28271708Sstevel man_dlpi(manstr_t *msp, mblk_t *mp)
28281708Sstevel {
28291708Sstevel 	int	status;
28301708Sstevel 
28311708Sstevel 	status = man_dl_catch(&msp->ms_dl_mp, mp);
28321708Sstevel 	if (status == 0)
28331708Sstevel 		status = man_dlpi_senddown(msp, mp);
28341708Sstevel 
28351708Sstevel 	return (status);
28361708Sstevel }
28371708Sstevel 
28381708Sstevel /*
28391708Sstevel  * Catch IOCTL type DL_ messages.
28401708Sstevel  */
28411708Sstevel static int
man_dlioc(manstr_t * msp,mblk_t * mp)28421708Sstevel man_dlioc(manstr_t *msp, mblk_t *mp)
28431708Sstevel {
28441708Sstevel 	int status;
28451708Sstevel 
28461708Sstevel 	status = man_dl_catch(&msp->ms_dlioc_mp, mp);
28471708Sstevel 	if (status == 0)
28481708Sstevel 		status = man_dlpi_senddown(msp, mp);
28491708Sstevel 
28501708Sstevel 	return (status);
28511708Sstevel }
28521708Sstevel 
28531708Sstevel /*
28541708Sstevel  * We catch all DLPI messages that we have to resend to a new AP'ed
28551708Sstevel  * device to put him in the right state.  We link these messages together
28561708Sstevel  * w/ their b_next fields and hang it off of msp->ms_dl_mp.  We
28571708Sstevel  * must be careful to restore b_next fields before doing dupmsg/freemsg!
28581708Sstevel  *
28591708Sstevel  *	msp - pointer of stream struct to process
28601708Sstevel  *	mblk - pointer to DLPI request to catch
28611708Sstevel  */
28621708Sstevel static int
man_dl_catch(mblk_t ** mplist,mblk_t * mp)28631708Sstevel man_dl_catch(mblk_t **mplist, mblk_t *mp)
28641708Sstevel {
28651708Sstevel 	mblk_t			*dupmp;
28661708Sstevel 	mblk_t			*tmp;
28671708Sstevel 	unsigned		prim;
28681708Sstevel 	int			status = 0;
28691708Sstevel 
28701708Sstevel 	dupmp = copymsg(mp);
28711708Sstevel 	if (dupmp == NULL) {
28721708Sstevel 		status = ENOMEM;
28731708Sstevel 		goto exit;
28741708Sstevel 	}
28751708Sstevel 
28761708Sstevel 
28771708Sstevel 	if (*mplist == NULL)
28781708Sstevel 		*mplist = dupmp;
28791708Sstevel 	else {
28801708Sstevel 		for (tmp = *mplist; tmp->b_next; )
28811708Sstevel 			tmp = tmp->b_next;
28821708Sstevel 
28831708Sstevel 		tmp->b_next = dupmp;
28841708Sstevel 	}
28851708Sstevel 
28861708Sstevel 	prim = DL_PRIM(mp);
28871708Sstevel 	MAN_DBG(MAN_DLPI,
28887656SSherry.Moore@Sun.COM 	    ("man_dl_catch: adding %s\n",
28897656SSherry.Moore@Sun.COM 	    (prim == DL_IOC_HDR_INFO) ? "DL_IOC_HDR_INFO" :
28907656SSherry.Moore@Sun.COM 	    (prim == DLIOCRAW) ? "DLIOCRAW" :
28917656SSherry.Moore@Sun.COM 	    (prim == DL_PROMISCON_REQ) ? promisc[DL_PROMISCON_TYPE(mp)] :
28927656SSherry.Moore@Sun.COM 	    dps[prim]));
28931708Sstevel 
28941708Sstevel exit:
28951708Sstevel 
28961708Sstevel 	return (status);
28971708Sstevel }
28981708Sstevel 
28991708Sstevel /*
29001708Sstevel  * Send down a single DLPI M_[PC]PROTO to all currently valid dests.
29011708Sstevel  *
29021708Sstevel  *	msp - ptr to NDM stream structure DL_ messages was received on.
29031708Sstevel  *	mp - ptr to mblk containing DL_ request.
29041708Sstevel  */
29051708Sstevel static int
man_dlpi_senddown(manstr_t * msp,mblk_t * mp)29061708Sstevel man_dlpi_senddown(manstr_t *msp, mblk_t *mp)
29071708Sstevel {
29081708Sstevel 	man_dest_t	*mdp;
29091708Sstevel 	int		i;
29101708Sstevel 	mblk_t		*rmp[MAN_MAX_DESTS];	/* Copy to replay */
29111708Sstevel 	int		dstate[MAN_MAX_DESTS];
29121708Sstevel 	int		no_dests = TRUE;
29131708Sstevel 	int		status = 0;
29141708Sstevel 
29151708Sstevel 	if (msp->ms_dests == NULL)
29161708Sstevel 		goto exit;
29171708Sstevel 
29181708Sstevel 	for (i = 0; i < MAN_MAX_DESTS; i++) {
29191708Sstevel 		mdp = &msp->ms_dests[i];
29201708Sstevel 		if (mdp->md_state == MAN_DSTATE_READY) {
29211708Sstevel 			dstate[i] = TRUE;
29221708Sstevel 			no_dests = FALSE;
29231708Sstevel 		} else {
29241708Sstevel 			dstate[i] = FALSE;
29251708Sstevel 		}
29261708Sstevel 		rmp[i] = NULL;
29271708Sstevel 	}
29281708Sstevel 
29291708Sstevel 	if (no_dests)
29301708Sstevel 		goto exit;
29311708Sstevel 
29321708Sstevel 	/*
29331708Sstevel 	 * Build replay and duplicate list for all possible destinations.
29341708Sstevel 	 */
29351708Sstevel 	for (i = 0; i < MAN_MAX_DESTS; i++) {
29361708Sstevel 		if (dstate[i]) {
29371708Sstevel 			rmp[i] = copymsg(mp);
29381708Sstevel 			if (rmp[i] == NULL) {
29391708Sstevel 				status = ENOMEM;
29401708Sstevel 				break;
29411708Sstevel 			}
29421708Sstevel 		}
29431708Sstevel 	}
29441708Sstevel 
29451708Sstevel 	if (status == 0) {
29461708Sstevel 		for (i = 0; i < MAN_MAX_DESTS; i++)
29471708Sstevel 			if (dstate[i]) {
29481708Sstevel 				mdp = &msp->ms_dests[i];
29491708Sstevel 
29501708Sstevel 				ASSERT(mdp->md_wq != NULL);
29511708Sstevel 				ASSERT(mp->b_next == NULL);
29521708Sstevel 				ASSERT(mp->b_prev == NULL);
29531708Sstevel 
29541708Sstevel 				man_dlpi_replay(mdp, rmp[i]);
29551708Sstevel 			}
29561708Sstevel 	} else {
29571708Sstevel 		for (; i >= 0; i--)
29581708Sstevel 			if (dstate[i] && rmp[i])
29591708Sstevel 				freemsg(rmp[i]);
29601708Sstevel 	}
29611708Sstevel 
29621708Sstevel exit:
29631708Sstevel 	return (status);
29641708Sstevel }
29651708Sstevel 
29661708Sstevel /*
29671708Sstevel  * man_dlpi_replay - traverse the list of DLPI requests and reapply them to
29681708Sstevel  * get the upper and lower streams into the same state. Called holding inner
29691708Sstevel  * perimeter lock exclusive. Note thet we defer M_IOCTL type dlpi messages
29701708Sstevel  * until we get an OK_ACK to our ATTACH (see man_lrsrv and
29711708Sstevel  * man_dlioc_replay).
29721708Sstevel  *
29731708Sstevel  * 	mdp - pointer to lower queue (destination)
29741708Sstevel  *	rmp - list of mblks to send down stream.
29751708Sstevel  */
29761708Sstevel static void
man_dlpi_replay(man_dest_t * mdp,mblk_t * rmp)29771708Sstevel man_dlpi_replay(man_dest_t *mdp, mblk_t *rmp)
29781708Sstevel {
29791708Sstevel 	mblk_t			*mp;
29801708Sstevel 	union DL_primitives	*dlp = NULL;
29811708Sstevel 
29821708Sstevel 	MAN_DBG(MAN_DLPI, ("man_dlpi_replay: mdp(0x%p)", (void *)mdp));
29831708Sstevel 
29841708Sstevel 	while (rmp) {
29851708Sstevel 		mp = rmp;
29861708Sstevel 		rmp = rmp->b_next;
29871708Sstevel 		mp->b_prev = mp->b_next = NULL;
29881708Sstevel 
29891708Sstevel 		dlp = (union DL_primitives *)mp->b_rptr;
29901708Sstevel 		MAN_DBG(MAN_DLPI,
29917656SSherry.Moore@Sun.COM 		    ("man_dlpi_replay: mdp(0x%p) sending %s\n",
29927656SSherry.Moore@Sun.COM 		    (void *)mdp,
29937656SSherry.Moore@Sun.COM 		    (dlp->dl_primitive == DL_IOC_HDR_INFO) ?
29947656SSherry.Moore@Sun.COM 		    "DL_IOC_HDR_INFO" : (dlp->dl_primitive == DLIOCRAW) ?
29957656SSherry.Moore@Sun.COM 		    "DLIOCRAW" : dps[(unsigned)(dlp->dl_primitive)]));
29961708Sstevel 
29971708Sstevel 		if (dlp->dl_primitive == DL_ATTACH_REQ) {
29981708Sstevel 			/*
29991708Sstevel 			 * insert the lower devices ppa.
30001708Sstevel 			 */
30011708Sstevel 			dlp->attach_req.dl_ppa = mdp->md_device.mdev_ppa;
30021708Sstevel 		}
30031708Sstevel 
30041708Sstevel 		(void) putnext(mdp->md_wq, mp);
30051708Sstevel 	}
30061708Sstevel 
30071708Sstevel }
30081708Sstevel 
30091708Sstevel static void
man_dreq(queue_t * wq,mblk_t * mp)30101708Sstevel man_dreq(queue_t *wq, mblk_t *mp)
30111708Sstevel {
30121708Sstevel 	manstr_t	*msp;	/* per stream data */
30131708Sstevel 	man_work_t	*wp;
30141708Sstevel 
30151708Sstevel 	msp = (manstr_t *)wq->q_ptr;
30161708Sstevel 
30171708Sstevel 	if (MBLKL(mp) < DL_DETACH_REQ_SIZE) {
30181708Sstevel 		dlerrorack(wq, mp, DL_DETACH_REQ, DL_BADPRIM, 0);
30191708Sstevel 		return;
30201708Sstevel 	}
30211708Sstevel 
30221708Sstevel 	if (msp->ms_dlpistate != DL_UNBOUND) {
30231708Sstevel 		dlerrorack(wq, mp, DL_DETACH_REQ, DL_OUTSTATE, 0);
30241708Sstevel 		return;
30251708Sstevel 	}
30261708Sstevel 
30271708Sstevel 	ASSERT(msp->ms_dests != NULL);
30281708Sstevel 
30291708Sstevel 	wp = man_work_alloc(MAN_WORK_CLOSE_STREAM, KM_NOSLEEP);
30301708Sstevel 	if (wp == NULL) {
30311708Sstevel 		dlerrorack(wq, mp, DL_DETACH_REQ, DL_SYSERR, ENOMEM);
30321708Sstevel 		return;
30331708Sstevel 	}
30341708Sstevel 	man_dodetach(msp, wp);
30351708Sstevel 	(void) qassociate(wq, -1);
30361708Sstevel 
30371708Sstevel 	SETSTATE(msp, DL_UNATTACHED);
30381708Sstevel 
30391708Sstevel 	dlokack(wq, mp, DL_DETACH_REQ);
30401708Sstevel }
30411708Sstevel 
30421708Sstevel static void
man_dl_clean(mblk_t ** mplist)30431708Sstevel man_dl_clean(mblk_t **mplist)
30441708Sstevel {
30451708Sstevel 	mblk_t	*tmp;
30461708Sstevel 
30471708Sstevel 	/*
30481708Sstevel 	 * Toss everything.
30491708Sstevel 	 */
30501708Sstevel 	while ((tmp = *mplist) != NULL) {
30511708Sstevel 		*mplist = (*mplist)->b_next;
30521708Sstevel 		tmp->b_next = tmp->b_prev = NULL;
30531708Sstevel 		freemsg(tmp);
30541708Sstevel 	}
30551708Sstevel 
30561708Sstevel }
30571708Sstevel 
30581708Sstevel /*
30591708Sstevel  * man_dl_release - Remove the corresponding DLPI request from the
30601708Sstevel  * catch list. Walk thru the catch list looking for the other half of
30611708Sstevel  * the pair and delete it.  If we are detaching, delete the entire list.
30621708Sstevel  *
30631708Sstevel  *	msp - pointer of stream struct to process
30641708Sstevel  *	mp  - pointer to mblk to first half of pair.  We will delete other
30651708Sstevel  * 		half of pair based on this.
30661708Sstevel  */
30671708Sstevel static void
man_dl_release(mblk_t ** mplist,mblk_t * mp)30681708Sstevel man_dl_release(mblk_t **mplist, mblk_t *mp)
30691708Sstevel {
30701708Sstevel 	uchar_t			match_dbtype;
30711708Sstevel 	mblk_t			*tmp;
30721708Sstevel 	mblk_t			*tmpp;
30731708Sstevel 	int			matched = FALSE;
30741708Sstevel 
30751708Sstevel 	if (*mplist == NULL)
30761708Sstevel 		goto exit;
30771708Sstevel 
30781708Sstevel 	match_dbtype = DB_TYPE(mp);
30791708Sstevel 
30801708Sstevel 	/*
30811708Sstevel 	 * Currently we only clean DL_ PROTO type messages. There is
30821708Sstevel 	 * no way to turn off M_CTL or DL_IOC stuff other than sending
30831708Sstevel 	 * down a DL_DETACH, which resets everything.
30841708Sstevel 	 */
30851708Sstevel 	if (match_dbtype != M_PROTO && match_dbtype != M_PCPROTO) {
30861708Sstevel 		goto exit;
30871708Sstevel 	}
30881708Sstevel 
30891708Sstevel 	/*
30901708Sstevel 	 * Selectively find a caught mblk that matches this one and
30911708Sstevel 	 * remove it from the list
30921708Sstevel 	 */
30931708Sstevel 	tmp = tmpp = *mplist;
30941708Sstevel 	matched = man_match_proto(mp, tmp);
30951708Sstevel 	if (matched) {
30961708Sstevel 		*mplist = tmp->b_next;
30971708Sstevel 		tmp->b_next = tmp->b_prev = NULL;
30981708Sstevel 	} else {
30991708Sstevel 		for (tmp = tmp->b_next; tmp != NULL; tmp = tmp->b_next) {
31001708Sstevel 			if (matched = man_match_proto(mp, tmp))
31011708Sstevel 				break;
31021708Sstevel 			tmpp = tmp;
31031708Sstevel 		}
31041708Sstevel 
31051708Sstevel 		if (matched) {
31061708Sstevel 			tmpp->b_next = tmp->b_next;
31071708Sstevel 			tmp->b_next = tmp->b_prev = NULL;
31081708Sstevel 		}
31091708Sstevel 	}
31101708Sstevel 
31111708Sstevel exit:
31121708Sstevel 	if (matched) {
31131708Sstevel 
31141708Sstevel 		MAN_DBG(MAN_DLPI, ("man_dl_release: release %s",
31157656SSherry.Moore@Sun.COM 		    (DL_PRIM(mp) == DL_IOC_HDR_INFO) ? "DL_IOC_HDR_INFO" :
31167656SSherry.Moore@Sun.COM 		    (DL_PRIM(mp) == DLIOCRAW) ? "DLIOCRAW" :
31177656SSherry.Moore@Sun.COM 		    dps[(int)DL_PRIM(mp)]));
31181708Sstevel 
31191708Sstevel 		freemsg(tmp);
31201708Sstevel 	}
31211708Sstevel 	MAN_DBG(MAN_DLPI, ("man_dl_release: returns"));
31221708Sstevel 
31231708Sstevel }
31241708Sstevel 
31251708Sstevel /*
31261708Sstevel  * Compare two DL_ messages. If they are complimentary (e.g. DL_UNBIND
31271708Sstevel  * compliments DL_BIND), return true.
31281708Sstevel  */
31291708Sstevel static int
man_match_proto(mblk_t * mp1,mblk_t * mp2)31301708Sstevel man_match_proto(mblk_t *mp1, mblk_t *mp2)
31311708Sstevel {
31321708Sstevel 	t_uscalar_t	prim1;
31331708Sstevel 	t_uscalar_t	prim2;
31341708Sstevel 	int		matched = FALSE;
31351708Sstevel 
31361708Sstevel 	/*
31371708Sstevel 	 * Primitive to clean off list.
31381708Sstevel 	 */
31391708Sstevel 	prim1 = DL_PRIM(mp1);
31401708Sstevel 	prim2 = DL_PRIM(mp2);
31411708Sstevel 
31421708Sstevel 	switch (prim1) {
31431708Sstevel 	case DL_UNBIND_REQ:
31441708Sstevel 		if (prim2 == DL_BIND_REQ)
31451708Sstevel 			matched = TRUE;
31461708Sstevel 		break;
31471708Sstevel 
31481708Sstevel 	case DL_PROMISCOFF_REQ:
31491708Sstevel 		if (prim2 == DL_PROMISCON_REQ) {
31501708Sstevel 			dl_promiscoff_req_t	*poff1;
31511708Sstevel 			dl_promiscoff_req_t	*poff2;
31521708Sstevel 
31531708Sstevel 			poff1 = (dl_promiscoff_req_t *)mp1->b_rptr;
31541708Sstevel 			poff2 = (dl_promiscoff_req_t *)mp2->b_rptr;
31551708Sstevel 
31561708Sstevel 			if (poff1->dl_level == poff2->dl_level)
31571708Sstevel 				matched = TRUE;
31581708Sstevel 		}
31591708Sstevel 		break;
31601708Sstevel 
31611708Sstevel 	case DL_DISABMULTI_REQ:
31621708Sstevel 		if (prim2 == DL_ENABMULTI_REQ) {
31631708Sstevel 			union DL_primitives	*dlp;
31641708Sstevel 			t_uscalar_t		off;
31651708Sstevel 			eaddr_t			*addrp1;
31661708Sstevel 			eaddr_t			*addrp2;
31671708Sstevel 
31681708Sstevel 			dlp = (union DL_primitives *)mp1->b_rptr;
31691708Sstevel 			off = dlp->disabmulti_req.dl_addr_offset;
31701708Sstevel 			addrp1 = (eaddr_t *)(mp1->b_rptr + off);
31711708Sstevel 
31721708Sstevel 			dlp = (union DL_primitives *)mp2->b_rptr;
31731708Sstevel 			off = dlp->disabmulti_req.dl_addr_offset;
31741708Sstevel 			addrp2 = (eaddr_t *)(mp2->b_rptr + off);
31751708Sstevel 
31761708Sstevel 			if (ether_cmp(addrp1, addrp2) == 0)
31771708Sstevel 				matched = 1;
31781708Sstevel 		}
31791708Sstevel 		break;
31801708Sstevel 
31811708Sstevel 	default:
31821708Sstevel 		break;
31831708Sstevel 	}
31841708Sstevel 
31851708Sstevel 	MAN_DBG(MAN_DLPI, ("man_match_proto returns %d", matched));
31861708Sstevel 
31871708Sstevel 	return (matched);
31881708Sstevel }
31891708Sstevel 
31901708Sstevel /*
31911708Sstevel  * Bind upper stream to a particular SAP. Called with exclusive innerperim
31921708Sstevel  * QPAIR, shared outerperim.
31931708Sstevel  */
31941708Sstevel static void
man_breq(queue_t * wq,mblk_t * mp)31951708Sstevel man_breq(queue_t *wq, mblk_t *mp)
31961708Sstevel {
31971708Sstevel 	man_t			*manp;	/* per instance data */
31981708Sstevel 	manstr_t		*msp;	/* per stream data */
31991708Sstevel 	union DL_primitives	*dlp;
32001708Sstevel 	man_dladdr_t		man_addr;
32011708Sstevel 	t_uscalar_t		sap;
32021708Sstevel 	t_uscalar_t		xidtest;
32031708Sstevel 
32041708Sstevel 	msp = (manstr_t *)wq->q_ptr;
32051708Sstevel 
32061708Sstevel 	if (MBLKL(mp) < DL_BIND_REQ_SIZE) {
32071708Sstevel 		dlerrorack(wq, mp, DL_BIND_REQ, DL_BADPRIM, 0);
32081708Sstevel 		return;
32091708Sstevel 	}
32101708Sstevel 
32111708Sstevel 	if (msp->ms_dlpistate != DL_UNBOUND) {
32121708Sstevel 		dlerrorack(wq, mp, DL_BIND_REQ, DL_OUTSTATE, 0);
32131708Sstevel 		return;
32141708Sstevel 	}
32151708Sstevel 
32161708Sstevel 	dlp = (union DL_primitives *)mp->b_rptr;
32171708Sstevel 	manp = msp->ms_manp;			/* valid after attach */
32181708Sstevel 	sap = dlp->bind_req.dl_sap;
32191708Sstevel 	xidtest = dlp->bind_req.dl_xidtest_flg;
32201708Sstevel 
32211708Sstevel 	ASSERT(manp);
32221708Sstevel 
32231708Sstevel 	if (xidtest) {
32241708Sstevel 		dlerrorack(wq, mp, DL_BIND_REQ, DL_NOAUTO, 0);
32251708Sstevel 		return;
32261708Sstevel 	}
32271708Sstevel 
32281708Sstevel 	if (sap > ETHERTYPE_MAX) {
32291708Sstevel 		dlerrorack(wq, mp, DL_BIND_REQ, DL_BADSAP, 0);
32301708Sstevel 		return;
32311708Sstevel 	}
32321708Sstevel 
32331708Sstevel 	if (man_dlpi(msp, mp)) {
32341708Sstevel 		dlerrorack(wq, mp, DL_BIND_REQ, DL_SYSERR, ENOMEM);
32351708Sstevel 		return;
32361708Sstevel 	}
32371708Sstevel 
32381708Sstevel 	msp->ms_sap = sap;
32391708Sstevel 
32401708Sstevel 	SETSTATE(msp, DL_IDLE);
32411708Sstevel 
32421708Sstevel 	man_addr.dl_sap = msp->ms_sap;
32431708Sstevel 	ether_copy(&msp->ms_manp->man_eaddr, &man_addr.dl_phys);
32441708Sstevel 
32451708Sstevel 	dlbindack(wq, mp, msp->ms_sap, &man_addr, MAN_ADDRL, 0, 0);
32461708Sstevel 
32471708Sstevel }
32481708Sstevel 
32491708Sstevel static void
man_ubreq(queue_t * wq,mblk_t * mp)32501708Sstevel man_ubreq(queue_t *wq, mblk_t *mp)
32511708Sstevel {
32521708Sstevel 	manstr_t		*msp;	/* per stream data */
32531708Sstevel 
32541708Sstevel 	msp = (manstr_t *)wq->q_ptr;
32551708Sstevel 
32561708Sstevel 	if (MBLKL(mp) < DL_UNBIND_REQ_SIZE) {
32571708Sstevel 		dlerrorack(wq, mp, DL_UNBIND_REQ, DL_BADPRIM, 0);
32581708Sstevel 		return;
32591708Sstevel 	}
32601708Sstevel 
32611708Sstevel 	if (msp->ms_dlpistate != DL_IDLE) {
32621708Sstevel 		dlerrorack(wq, mp, DL_UNBIND_REQ, DL_OUTSTATE, 0);
32631708Sstevel 		return;
32641708Sstevel 	}
32651708Sstevel 
32661708Sstevel 	if (man_dlpi_senddown(msp, mp)) {
32671708Sstevel 		dlerrorack(wq, mp, DL_UNBIND_REQ, DL_SYSERR, ENOMEM);
32681708Sstevel 		return;
32691708Sstevel 	}
32701708Sstevel 
32711708Sstevel 	man_dl_release(&msp->ms_dl_mp, mp);
32721708Sstevel 
32731708Sstevel 	SETSTATE(msp, DL_UNBOUND);
32741708Sstevel 
32751708Sstevel 	dlokack(wq, mp, DL_UNBIND_REQ);
32761708Sstevel 
32771708Sstevel }
32781708Sstevel 
32791708Sstevel static void
man_ireq(queue_t * wq,mblk_t * mp)32801708Sstevel man_ireq(queue_t *wq, mblk_t *mp)
32811708Sstevel {
32821708Sstevel 	manstr_t	*msp;
32831708Sstevel 	dl_info_ack_t	*dlip;
32841708Sstevel 	man_dladdr_t	*dlap;
32851708Sstevel 	eaddr_t		*ep;
32861708Sstevel 	size_t	size;
32871708Sstevel 
32881708Sstevel 	msp = (manstr_t *)wq->q_ptr;
32891708Sstevel 
32901708Sstevel 	if (MBLKL(mp) < DL_INFO_REQ_SIZE) {
32911708Sstevel 		dlerrorack(wq, mp, DL_INFO_REQ, DL_BADPRIM, 0);
32921708Sstevel 		return;
32931708Sstevel 	}
32941708Sstevel 
32951708Sstevel 	/* Exchange current msg for a DL_INFO_ACK. */
32961708Sstevel 	size = sizeof (dl_info_ack_t) + MAN_ADDRL + ETHERADDRL;
32971708Sstevel 	mp = mexchange(wq, mp, size, M_PCPROTO, DL_INFO_ACK);
32981708Sstevel 	if (mp == NULL) {
32991708Sstevel 		MAN_DBG(MAN_DLPI, ("man_ireq: man_ireq: mp == NULL."));
33001708Sstevel 		return;
33011708Sstevel 	}
33021708Sstevel 
33031708Sstevel 	/* Fill in the DL_INFO_ACK fields and reply. */
33041708Sstevel 	dlip = (dl_info_ack_t *)mp->b_rptr;
33051708Sstevel 	*dlip = man_infoack;
33061708Sstevel 	dlip->dl_current_state = msp->ms_dlpistate;
33071708Sstevel 	dlap = (man_dladdr_t *)(mp->b_rptr + dlip->dl_addr_offset);
33081708Sstevel 	dlap->dl_sap = msp->ms_sap;
33091708Sstevel 
33101708Sstevel 	/*
33111708Sstevel 	 * If attached, return physical address.
33121708Sstevel 	 */
33131708Sstevel 	if (msp->ms_manp != NULL) {
33141708Sstevel 		ether_copy(&msp->ms_manp->man_eaddr, &dlap->dl_phys);
33151708Sstevel 	} else {
33161708Sstevel 		bzero((caddr_t)&dlap->dl_phys, ETHERADDRL);
33171708Sstevel 	}
33181708Sstevel 
33191708Sstevel 	ep = (struct ether_addr *)(mp->b_rptr + dlip->dl_brdcst_addr_offset);
33201708Sstevel 	ether_copy(&etherbroadcast, ep);
33211708Sstevel 
33221708Sstevel 	qreply(wq, mp);
33231708Sstevel 
33241708Sstevel }
33251708Sstevel 
33261708Sstevel 
33271708Sstevel static void
man_ponreq(queue_t * wq,mblk_t * mp)33281708Sstevel man_ponreq(queue_t *wq, mblk_t *mp)
33291708Sstevel {
33301708Sstevel 	manstr_t	*msp;
33311708Sstevel 	int		flag;
33321708Sstevel 
33331708Sstevel 	msp = (manstr_t *)wq->q_ptr;
33341708Sstevel 
33351708Sstevel 	if (MBLKL(mp) < DL_PROMISCON_REQ_SIZE) {
33361708Sstevel 		dlerrorack(wq, mp, DL_PROMISCON_REQ, DL_BADPRIM, 0);
33371708Sstevel 		return;
33381708Sstevel 	}
33391708Sstevel 
33401708Sstevel 	switch (((dl_promiscon_req_t *)mp->b_rptr)->dl_level) {
33411708Sstevel 	case DL_PROMISC_PHYS:
33421708Sstevel 		flag = MAN_SFLAG_ALLPHYS;
33431708Sstevel 		break;
33441708Sstevel 
33451708Sstevel 	case DL_PROMISC_SAP:
33461708Sstevel 		flag = MAN_SFLAG_ALLSAP;
33471708Sstevel 		break;
33481708Sstevel 
33491708Sstevel 	case DL_PROMISC_MULTI:
33501708Sstevel 		flag = MAN_SFLAG_ALLMULTI;
33511708Sstevel 		break;
33521708Sstevel 
33531708Sstevel 	default:
33541708Sstevel 		dlerrorack(wq, mp, DL_PROMISCON_REQ, DL_NOTSUPPORTED, 0);
33551708Sstevel 		return;
33561708Sstevel 	}
33571708Sstevel 
33581708Sstevel 	/*
33591708Sstevel 	 * Catch request for replay, and forward down to any lower
33601708Sstevel 	 * lower stream.
33611708Sstevel 	 */
33621708Sstevel 	if (man_dlpi(msp, mp)) {
33631708Sstevel 		dlerrorack(wq, mp, DL_PROMISCON_REQ, DL_SYSERR, ENOMEM);
33641708Sstevel 		return;
33651708Sstevel 	}
33661708Sstevel 
33671708Sstevel 	msp->ms_flags |= flag;
33681708Sstevel 
33691708Sstevel 	dlokack(wq, mp, DL_PROMISCON_REQ);
33701708Sstevel 
33711708Sstevel }
33721708Sstevel 
33731708Sstevel static void
man_poffreq(queue_t * wq,mblk_t * mp)33741708Sstevel man_poffreq(queue_t *wq, mblk_t *mp)
33751708Sstevel {
33761708Sstevel 	manstr_t		*msp;
33771708Sstevel 	int			flag;
33781708Sstevel 
33791708Sstevel 	msp = (manstr_t *)wq->q_ptr;
33801708Sstevel 
33811708Sstevel 	if (MBLKL(mp) < DL_PROMISCOFF_REQ_SIZE) {
33821708Sstevel 		dlerrorack(wq, mp, DL_PROMISCOFF_REQ, DL_BADPRIM, 0);
33831708Sstevel 		return;
33841708Sstevel 	}
33851708Sstevel 
33861708Sstevel 	switch (((dl_promiscoff_req_t *)mp->b_rptr)->dl_level) {
33871708Sstevel 	case DL_PROMISC_PHYS:
33881708Sstevel 		flag = MAN_SFLAG_ALLPHYS;
33891708Sstevel 		break;
33901708Sstevel 
33911708Sstevel 	case DL_PROMISC_SAP:
33921708Sstevel 		flag = MAN_SFLAG_ALLSAP;
33931708Sstevel 		break;
33941708Sstevel 
33951708Sstevel 	case DL_PROMISC_MULTI:
33961708Sstevel 		flag = MAN_SFLAG_ALLMULTI;
33971708Sstevel 		break;
33981708Sstevel 
33991708Sstevel 	default:
34001708Sstevel 		dlerrorack(wq, mp, DL_PROMISCOFF_REQ, DL_NOTSUPPORTED, 0);
34011708Sstevel 		return;
34021708Sstevel 	}
34031708Sstevel 
34041708Sstevel 	if ((msp->ms_flags & flag) == 0) {
34051708Sstevel 		dlerrorack(wq, mp, DL_PROMISCOFF_REQ, DL_NOTENAB, 0);
34061708Sstevel 		return;
34071708Sstevel 	}
34081708Sstevel 
34091708Sstevel 	if (man_dlpi_senddown(msp, mp)) {
34101708Sstevel 		dlerrorack(wq, mp, DL_PROMISCOFF_REQ, DL_SYSERR, ENOMEM);
34111708Sstevel 		return;
34121708Sstevel 	}
34131708Sstevel 
34141708Sstevel 	man_dl_release(&msp->ms_dl_mp, mp);
34151708Sstevel 
34161708Sstevel 	msp->ms_flags &= ~flag;
34171708Sstevel 
34181708Sstevel 	dlokack(wq, mp, DL_PROMISCOFF_REQ);
34191708Sstevel 
34201708Sstevel }
34211708Sstevel 
34221708Sstevel /*
34231708Sstevel  * Enable multicast requests. We might need to track addresses instead of
34241708Sstevel  * just passing things through (see eri_dmreq) - TBD.
34251708Sstevel  */
34261708Sstevel static void
man_emreq(queue_t * wq,mblk_t * mp)34271708Sstevel man_emreq(queue_t *wq, mblk_t *mp)
34281708Sstevel {
34291708Sstevel 	manstr_t		*msp;
34301708Sstevel 	union DL_primitives	*dlp;
34311708Sstevel 	eaddr_t			*addrp;
34321708Sstevel 	t_uscalar_t		off;
34331708Sstevel 	t_uscalar_t		len;
34341708Sstevel 
34351708Sstevel 	msp = (manstr_t *)wq->q_ptr;
34361708Sstevel 
34371708Sstevel 	if (MBLKL(mp) < DL_ENABMULTI_REQ_SIZE) {
34381708Sstevel 		dlerrorack(wq, mp, DL_ENABMULTI_REQ, DL_BADPRIM, 0);
34391708Sstevel 		return;
34401708Sstevel 	}
34411708Sstevel 
34421708Sstevel 	if (msp->ms_dlpistate == DL_UNATTACHED) {
34431708Sstevel 		dlerrorack(wq, mp, DL_ENABMULTI_REQ, DL_OUTSTATE, 0);
34441708Sstevel 		return;
34451708Sstevel 	}
34461708Sstevel 
34471708Sstevel 	dlp = (union DL_primitives *)mp->b_rptr;
34481708Sstevel 	len = dlp->enabmulti_req.dl_addr_length;
34491708Sstevel 	off = dlp->enabmulti_req.dl_addr_offset;
34501708Sstevel 	addrp = (struct ether_addr *)(mp->b_rptr + off);
34511708Sstevel 
34521708Sstevel 	if ((len != ETHERADDRL) ||
34537656SSherry.Moore@Sun.COM 	    !MBLKIN(mp, off, len) ||
34547656SSherry.Moore@Sun.COM 	    ((addrp->ether_addr_octet[0] & 01) == 0)) {
34551708Sstevel 		dlerrorack(wq, mp, DL_ENABMULTI_REQ, DL_BADADDR, 0);
34561708Sstevel 		return;
34571708Sstevel 	}
34581708Sstevel 
34591708Sstevel 	/*
34601708Sstevel 	 * Catch request for replay, and forward down to any lower
34611708Sstevel 	 * lower stream.
34621708Sstevel 	 */
34631708Sstevel 	if (man_dlpi(msp, mp)) {
34641708Sstevel 		dlerrorack(wq, mp, DL_ENABMULTI_REQ, DL_SYSERR, ENOMEM);
34651708Sstevel 		return;
34661708Sstevel 	}
34671708Sstevel 
34681708Sstevel 	dlokack(wq, mp, DL_ENABMULTI_REQ);
34691708Sstevel 
34701708Sstevel }
34711708Sstevel 
34721708Sstevel static void
man_dmreq(queue_t * wq,mblk_t * mp)34731708Sstevel man_dmreq(queue_t *wq, mblk_t *mp)
34741708Sstevel {
34751708Sstevel 	manstr_t		*msp;
34761708Sstevel 	union DL_primitives	*dlp;
34771708Sstevel 	eaddr_t			*addrp;
34781708Sstevel 	t_uscalar_t		off;
34791708Sstevel 	t_uscalar_t		len;
34801708Sstevel 
34811708Sstevel 	msp = (manstr_t *)wq->q_ptr;
34821708Sstevel 
34831708Sstevel 	if (MBLKL(mp) < DL_DISABMULTI_REQ_SIZE) {
34841708Sstevel 		dlerrorack(wq, mp, DL_DISABMULTI_REQ, DL_BADPRIM, 0);
34851708Sstevel 		return;
34861708Sstevel 	}
34871708Sstevel 
34881708Sstevel 	if (msp->ms_dlpistate == DL_UNATTACHED) {
34891708Sstevel 		dlerrorack(wq, mp, DL_ENABMULTI_REQ, DL_OUTSTATE, 0);
34901708Sstevel 		return;
34911708Sstevel 	}
34921708Sstevel 
34931708Sstevel 	dlp = (union DL_primitives *)mp->b_rptr;
34941708Sstevel 	len = dlp->enabmulti_req.dl_addr_length;
34951708Sstevel 	off = dlp->enabmulti_req.dl_addr_offset;
34961708Sstevel 	addrp = (struct ether_addr *)(mp->b_rptr + off);
34971708Sstevel 
34981708Sstevel 	if ((len != ETHERADDRL) ||
34997656SSherry.Moore@Sun.COM 	    !MBLKIN(mp, off, len) ||
35007656SSherry.Moore@Sun.COM 	    ((addrp->ether_addr_octet[0] & 01) == 0)) {
35011708Sstevel 		dlerrorack(wq, mp, DL_ENABMULTI_REQ, DL_BADADDR, 0);
35021708Sstevel 		return;
35031708Sstevel 	}
35041708Sstevel 
35051708Sstevel 	if (man_dlpi_senddown(msp, mp)) {
35061708Sstevel 		dlerrorack(wq, mp, DL_ENABMULTI_REQ, DL_SYSERR, ENOMEM);
35071708Sstevel 		return;
35081708Sstevel 	}
35091708Sstevel 
35101708Sstevel 	man_dl_release(&msp->ms_dl_mp, mp);
35111708Sstevel 
35121708Sstevel 	dlokack(wq, mp, DL_DISABMULTI_REQ);
35131708Sstevel 
35141708Sstevel }
35151708Sstevel 
35161708Sstevel static void
man_pareq(queue_t * wq,mblk_t * mp)35171708Sstevel man_pareq(queue_t *wq, mblk_t *mp)
35181708Sstevel {
35191708Sstevel 	manstr_t		*msp;
35201708Sstevel 	union	DL_primitives	*dlp;
35211708Sstevel 	uint32_t		type;
35221708Sstevel 	struct	ether_addr	addr;
35231708Sstevel 
35241708Sstevel 	msp = (manstr_t *)wq->q_ptr;
35251708Sstevel 
35261708Sstevel 	if (MBLKL(mp) < DL_PHYS_ADDR_REQ_SIZE) {
35271708Sstevel 		dlerrorack(wq, mp, DL_PHYS_ADDR_REQ, DL_BADPRIM, 0);
35281708Sstevel 		return;
35291708Sstevel 	}
35301708Sstevel 
35311708Sstevel 	dlp = (union DL_primitives *)mp->b_rptr;
35321708Sstevel 	type = dlp->physaddr_req.dl_addr_type;
35331708Sstevel 	if (msp->ms_manp == NULL) {
35341708Sstevel 		dlerrorack(wq, mp, DL_PHYS_ADDR_REQ, DL_OUTSTATE, 0);
35351708Sstevel 		return;
35361708Sstevel 	}
35371708Sstevel 
35381708Sstevel 	switch (type) {
35391708Sstevel 	case	DL_FACT_PHYS_ADDR:
35401708Sstevel 		(void) localetheraddr((struct ether_addr *)NULL, &addr);
35411708Sstevel 		break;
35421708Sstevel 
35431708Sstevel 	case	DL_CURR_PHYS_ADDR:
35441708Sstevel 		ether_bcopy(&msp->ms_manp->man_eaddr, &addr);
35451708Sstevel 		break;
35461708Sstevel 
35471708Sstevel 	default:
35481708Sstevel 		dlerrorack(wq, mp, DL_PHYS_ADDR_REQ, DL_NOTSUPPORTED, 0);
35491708Sstevel 		return;
35501708Sstevel 	}
35511708Sstevel 
35521708Sstevel 	dlphysaddrack(wq, mp, &addr, ETHERADDRL);
35531708Sstevel }
35541708Sstevel 
35551708Sstevel /*
35561708Sstevel  * TBD - this routine probably should be protected w/ an ndd
35571708Sstevel  * tuneable, or a man.conf parameter.
35581708Sstevel  */
35591708Sstevel static void
man_spareq(queue_t * wq,mblk_t * mp)35601708Sstevel man_spareq(queue_t *wq, mblk_t *mp)
35611708Sstevel {
35621708Sstevel 	manstr_t		*msp;
35631708Sstevel 	union DL_primitives	*dlp;
35641708Sstevel 	t_uscalar_t		off;
35651708Sstevel 	t_uscalar_t		len;
35661708Sstevel 	eaddr_t			*addrp;
35671708Sstevel 
35681708Sstevel 	msp = (manstr_t *)wq->q_ptr;
35691708Sstevel 
35701708Sstevel 	if (MBLKL(mp) < DL_SET_PHYS_ADDR_REQ_SIZE) {
35711708Sstevel 		dlerrorack(wq, mp, DL_SET_PHYS_ADDR_REQ, DL_BADPRIM, 0);
35721708Sstevel 		return;
35731708Sstevel 	}
35741708Sstevel 
35751708Sstevel 	dlp = (union DL_primitives *)mp->b_rptr;
35761708Sstevel 	len = dlp->set_physaddr_req.dl_addr_length;
35771708Sstevel 	off = dlp->set_physaddr_req.dl_addr_offset;
35781708Sstevel 
35791708Sstevel 	if (!MBLKIN(mp, off, len)) {
35801708Sstevel 		dlerrorack(wq, mp, DL_SET_PHYS_ADDR_REQ, DL_BADPRIM, 0);
35811708Sstevel 		return;
35821708Sstevel 	}
35831708Sstevel 
35841708Sstevel 	addrp = (struct ether_addr *)(mp->b_rptr + off);
35851708Sstevel 
35861708Sstevel 	/*
35871708Sstevel 	 * Error if length of address isn't right or the address
35881708Sstevel 	 * specified is a multicast or broadcast address.
35891708Sstevel 	 */
35901708Sstevel 	if ((len != ETHERADDRL) ||
35917656SSherry.Moore@Sun.COM 	    ((addrp->ether_addr_octet[0] & 01) == 1) ||
35927656SSherry.Moore@Sun.COM 	    (ether_cmp(addrp, &etherbroadcast) == 0)) {
35931708Sstevel 		dlerrorack(wq, mp, DL_SET_PHYS_ADDR_REQ, DL_BADADDR, 0);
35941708Sstevel 		return;
35951708Sstevel 	}
35961708Sstevel 	/*
35971708Sstevel 	 * Error if this stream is not attached to a device.
35981708Sstevel 	 */
35991708Sstevel 	if (msp->ms_manp == NULL) {
36001708Sstevel 		dlerrorack(wq, mp, DL_SET_PHYS_ADDR_REQ, DL_OUTSTATE, 0);
36011708Sstevel 		return;
36021708Sstevel 	}
36031708Sstevel 
36041708Sstevel 	/*
36051708Sstevel 	 * We will also resend DL_SET_PHYS_ADDR_REQ for each dest
36061708Sstevel 	 * when it is linked under us.
36071708Sstevel 	 */
36081708Sstevel 	if (man_dlpi_senddown(msp, mp)) {
36091708Sstevel 		dlerrorack(wq, mp, DL_SET_PHYS_ADDR_REQ, DL_SYSERR, ENOMEM);
36101708Sstevel 		return;
36111708Sstevel 	}
36121708Sstevel 
36131708Sstevel 	ether_copy(addrp, msp->ms_manp->man_eaddr.ether_addr_octet);
36141708Sstevel 
36151708Sstevel 	MAN_DBG(MAN_DLPI, ("man_sareq: snagged %s\n",
36167656SSherry.Moore@Sun.COM 	    ether_sprintf(&msp->ms_manp->man_eaddr)));
36171708Sstevel 
36181708Sstevel 	dlokack(wq, mp, DL_SET_PHYS_ADDR_REQ);
36191708Sstevel 
36201708Sstevel }
36211708Sstevel 
36221708Sstevel /*
36231708Sstevel  * These routines make up the lower part of the MAN streams framework.
36241708Sstevel  */
36251708Sstevel 
36261708Sstevel /*
36271708Sstevel  * man_lwsrv - Deferred mblks for down stream. We end up here when
36281708Sstevel  * the destination is not DL_IDLE when traffic comes downstream.
36291708Sstevel  *
36301708Sstevel  *	wq - lower write queue of mxx
36311708Sstevel  */
36321708Sstevel static int
man_lwsrv(queue_t * wq)36331708Sstevel man_lwsrv(queue_t *wq)
36341708Sstevel {
36351708Sstevel 	mblk_t		*mp;
36361708Sstevel 	mblk_t		*mlistp;
36371708Sstevel 	man_dest_t	*mdp;
36381708Sstevel 	size_t		count;
36391708Sstevel 
36401708Sstevel 	mdp = (man_dest_t *)wq->q_ptr;
36411708Sstevel 
36421708Sstevel 	MAN_DBG(MAN_LWSRV, ("man_lwsrv: wq(0x%p) mdp(0x%p)"
36437656SSherry.Moore@Sun.COM 	    " md_rq(0x%p)\n", (void *)wq, (void *)mdp,
36447656SSherry.Moore@Sun.COM 	    mdp ? (void *)mdp->md_rq : NULL));
36451708Sstevel 
36461708Sstevel 	if (mdp == NULL)
36471708Sstevel 		goto exit;
36481708Sstevel 
36491708Sstevel 	if (mdp->md_state & MAN_DSTATE_CLOSING) {
36501708Sstevel 			flushq(wq, FLUSHDATA);
36511708Sstevel 			flushq(RD(wq), FLUSHDATA);
36521708Sstevel 			goto exit;
36531708Sstevel 	}
36541708Sstevel 
36551708Sstevel 	/*
36561708Sstevel 	 * Arrange to send deferred mp's first, then mblks on the
36571708Sstevel 	 * service queue. Since we are exclusive in the inner perimeter,
36581708Sstevel 	 * we dont have to worry about md_lock, like the put procedures,
36591708Sstevel 	 * which are MTPUTSHARED.
36601708Sstevel 	 */
36611708Sstevel 	mutex_enter(&mdp->md_lock);
36621708Sstevel 	mlistp = mdp->md_dmp_head;
36631708Sstevel 	mdp->md_dmp_head = NULL;
36641708Sstevel 	count = mdp->md_dmp_count;
36651708Sstevel 	mdp->md_dmp_count = 0;
36661708Sstevel 	mutex_exit(&mdp->md_lock);
36671708Sstevel 
36681708Sstevel 	while (mlistp != NULL) {
36691708Sstevel 		mp = mlistp;
36701708Sstevel 		mlistp = mp->b_next;
36711708Sstevel 		mp->b_next = NULL;
36721708Sstevel 		count -= msgsize(mp);
36731708Sstevel 		if (man_start_lower(mdp, mp, NULL, MAN_LOWER)) {
36741708Sstevel 
36751708Sstevel 			mutex_enter(&mdp->md_lock);
36761708Sstevel 			mdp->md_dmp_count += count + msgsize(mp);
36771708Sstevel 			mp->b_next = mlistp;
36781708Sstevel 			mdp->md_dmp_head = mp;
36791708Sstevel 			mutex_exit(&mdp->md_lock);
36801708Sstevel 			goto exit;
36811708Sstevel 		}
36821708Sstevel 	}
36831708Sstevel 	mdp->md_dmp_tail = NULL;
36841708Sstevel 
36851708Sstevel 	while (mp = getq(wq)) {
36861708Sstevel 		if (man_start_lower(mdp, mp, NULL, MAN_LOWER)) {
36871708Sstevel 			/*
36881708Sstevel 			 * Put it back on queue, making sure to avoid
36891708Sstevel 			 * infinite loop mentioned in putbq(9F)
36901708Sstevel 			 */
36911708Sstevel 			noenable(wq);
3692*11311SSurya.Prakki@Sun.COM 			(void) putbq(wq, mp);
36931708Sstevel 			enableok(wq);
36941708Sstevel 
36951708Sstevel 			break;
36961708Sstevel 		}
36971708Sstevel 	}
36981708Sstevel 
36991708Sstevel exit:
37001708Sstevel 
37011708Sstevel 	return (0);
37021708Sstevel }
37031708Sstevel 
37041708Sstevel /*
37051708Sstevel  * man_lrput - handle DLPI messages issued from downstream.
37061708Sstevel  *
37071708Sstevel  *	rq - lower read queue of mxx
37081708Sstevel  *	mp - mblk ptr to DLPI request
37091708Sstevel  *
37101708Sstevel  *	returns 0
37111708Sstevel  */
37121708Sstevel static int
man_lrput(queue_t * rq,mblk_t * mp)37131708Sstevel man_lrput(queue_t *rq, mblk_t *mp)
37141708Sstevel {
37151708Sstevel 	man_dest_t	*mdp;
37161708Sstevel 	manstr_t	*msp;
37171708Sstevel 
37181708Sstevel #if defined(DEBUG)
37191708Sstevel 	union DL_primitives	*dlp;
37201708Sstevel 	t_uscalar_t		prim = MAN_DLPI_MAX_PRIM + 1;
37211708Sstevel 	char			*prim_str;
37221708Sstevel #endif  /* DEBUG */
37231708Sstevel 
37241708Sstevel 	mdp = (man_dest_t *)rq->q_ptr;
37251708Sstevel 
37261708Sstevel #if defined(DEBUG)
37271708Sstevel 	if (DB_TYPE(mp) == M_PROTO) {
37281708Sstevel 		dlp = (union DL_primitives *)mp->b_rptr;
37291708Sstevel 		prim = dlp->dl_primitive;
37301708Sstevel 	}
37311708Sstevel 
37321708Sstevel 	prim_str = (prim > MAN_DLPI_MAX_PRIM) ? "NON DLPI" :
37337656SSherry.Moore@Sun.COM 	    (prim == DL_IOC_HDR_INFO) ? "DL_IOC_HDR_INFO" :
37347656SSherry.Moore@Sun.COM 	    (prim == DLIOCRAW) ? "DLIOCRAW" :
37357656SSherry.Moore@Sun.COM 	    dps[(unsigned int)prim];
37361708Sstevel 	MAN_DBG(MAN_LRPUT, ("man_lrput: rq(0x%p) mp(0x%p) mdp(0x%p)"
37377656SSherry.Moore@Sun.COM 	    " db_type(0x%x) dl_prim %s", (void *)rq,
37387656SSherry.Moore@Sun.COM 	    (void *)mp, (void *)mdp, DB_TYPE(mp), prim_str));
37391708Sstevel 	MAN_DBGCALL(MAN_LRPUT2, man_print_mdp(mdp));
37401708Sstevel #endif  /* DEBUG */
37411708Sstevel 
37421708Sstevel 	if (DB_TYPE(mp) == M_FLUSH) {
37431708Sstevel 		/* Turn around */
37441708Sstevel 		if (*mp->b_rptr & FLUSHW) {
37451708Sstevel 			*mp->b_rptr &= ~FLUSHR;
37461708Sstevel 			qreply(rq, mp);
37471708Sstevel 		} else
37481708Sstevel 			freemsg(mp);
37491708Sstevel 		return (0);
37501708Sstevel 	}
37511708Sstevel 
37521708Sstevel 	if (mdp == NULL || mdp->md_state != MAN_DSTATE_READY) {
37531708Sstevel 
37541708Sstevel 		MAN_DBG(MAN_LRPUT, ("man_lrput: not ready mdp(0x%p),"
37557656SSherry.Moore@Sun.COM 		    " state(%d)", (void *)mdp, mdp ? mdp->md_state : -1));
37561708Sstevel 		freemsg(mp);
37571708Sstevel 		return (0);
37581708Sstevel 	}
37591708Sstevel 
37601708Sstevel 	/*
37611708Sstevel 	 * If we have a destination in the right state, forward on datagrams.
37621708Sstevel 	 */
37631708Sstevel 	if (MAN_IS_DATA(mp)) {
37641708Sstevel 		if (mdp->md_dlpistate == DL_IDLE && canputnext(mdp->md_rq)) {
37651708Sstevel 
37661708Sstevel 			msp = mdp->md_msp;
37671708Sstevel 			if (!(msp->ms_flags & MAN_SFLAG_PROMISC))
37681708Sstevel 				mdp->md_rcvcnt++; /* Count for failover */
37691708Sstevel 			/*
37701708Sstevel 			 * go put mblk_t directly up to next queue.
37711708Sstevel 			 */
37721708Sstevel 			MAN_DBG(MAN_LRPUT, ("man_lrput: putnext to rq(0x%p)",
37737656SSherry.Moore@Sun.COM 			    (void *)mdp->md_rq));
37741708Sstevel 			(void) putnext(mdp->md_rq, mp);
37751708Sstevel 		} else {
37761708Sstevel 			freemsg(mp);
37771708Sstevel 		}
37781708Sstevel 	} else {
37791708Sstevel 		/*
37801708Sstevel 		 * Handle in man_lrsrv with exclusive inner perimeter lock.
37811708Sstevel 		 */
3782*11311SSurya.Prakki@Sun.COM 		(void) putq(rq, mp);
37831708Sstevel 	}
37841708Sstevel 
37851708Sstevel 	return (0);
37861708Sstevel }
37871708Sstevel 
37881708Sstevel /*
37891708Sstevel  * Either this is a response from our attempt to sync the upper and lower
37901708Sstevel  * stream states, or its data. If its not data. Do DL_* response processing
37911708Sstevel  * and transition md_dlpistate accordingly. If its data, toss it.
37921708Sstevel  */
37931708Sstevel static int
man_lrsrv(queue_t * rq)37941708Sstevel man_lrsrv(queue_t *rq)
37951708Sstevel {
37961708Sstevel 	man_dest_t		*mdp;
37971708Sstevel 	mblk_t			*mp;
37981708Sstevel 	union DL_primitives	*dlp;
37991708Sstevel 	ulong_t			prim;
38001708Sstevel 	ulong_t			cprim;
38011708Sstevel 	int			need_dl_reset = FALSE;
38021708Sstevel 
38031708Sstevel #if defined(DEBUG)
38041708Sstevel 		struct iocblk	*iocp;
38051708Sstevel 		char		ioc_cmd[256];
38061708Sstevel #endif  /* DEBUG */
38071708Sstevel 
38081708Sstevel 	MAN_DBG(MAN_LRSRV, ("man_lrsrv: rq(0x%p)", (void *)rq));
38091708Sstevel 
38101708Sstevel 	mdp = (man_dest_t *)rq->q_ptr;
38111708Sstevel 
38121708Sstevel 	if ((mdp == NULL) || (mdp->md_state & MAN_DSTATE_CLOSING)) {
38131708Sstevel 			flushq(rq, FLUSHDATA);
38141708Sstevel 			flushq(WR(rq), FLUSHDATA);
38151708Sstevel 			goto exit;
38161708Sstevel 	}
38171708Sstevel 
38181708Sstevel 	while (mp = getq(rq)) {
38191708Sstevel 
38201708Sstevel 
38211708Sstevel 	/*
38221708Sstevel 	 * If we're not connected, or its a datagram, toss it.
38231708Sstevel 	 */
38241708Sstevel 	if (MAN_IS_DATA(mp) || mdp->md_state != MAN_DSTATE_READY) {
38251708Sstevel 
38261708Sstevel 		MAN_DBG(MAN_LRSRV, ("man_lrsrv: dropping mblk mdp(0x%p)"
38277656SSherry.Moore@Sun.COM 		    " is_data(%d)", (void *)mdp, MAN_IS_DATA(mp)));
38281708Sstevel 		freemsg(mp);
38291708Sstevel 		continue;
38301708Sstevel 	}
38311708Sstevel 
38321708Sstevel 	/*
38331708Sstevel 	 * Should be response to man_dlpi_replay. Discard unless there
38341708Sstevel 	 * is a failure we care about.
38351708Sstevel 	 */
38361708Sstevel 
38371708Sstevel 	switch (DB_TYPE(mp)) {
38381708Sstevel 	case M_PROTO:
38391708Sstevel 	case M_PCPROTO:
38401708Sstevel 		/* Do proto processing below. */
38411708Sstevel 		break;
38421708Sstevel 
38431708Sstevel 	case M_IOCNAK:
38441708Sstevel 		/*
38451708Sstevel 		 * DL_IOC* failed for some reason.
38461708Sstevel 		 */
38471708Sstevel 		need_dl_reset = TRUE;
38481708Sstevel 
38491708Sstevel #if defined(DEBUG)
38501708Sstevel 		iocp = (struct iocblk *)mp->b_rptr;
38511708Sstevel 
3852*11311SSurya.Prakki@Sun.COM 		(void) sprintf(ioc_cmd, "0x%x", iocp->ioc_cmd);
38531708Sstevel 		MAN_DBG(MAN_LRSRV, ("man_lrsrv: M_IOCNAK err %d for cmd(%s)\n",
38547656SSherry.Moore@Sun.COM 		    iocp->ioc_error,
38557656SSherry.Moore@Sun.COM 		    (iocp->ioc_cmd == DL_IOC_HDR_INFO) ? "DL_IOC_HDR_INFO" :
38567656SSherry.Moore@Sun.COM 		    (iocp->ioc_cmd == DLIOCRAW) ? "DLIOCRAW" : ioc_cmd));
38571708Sstevel #endif  /* DEBUG */
38581708Sstevel 
38591708Sstevel 		/* FALLTHRU */
38601708Sstevel 
38611708Sstevel 	case M_IOCACK:
38621708Sstevel 	case M_CTL:
38631708Sstevel 		/*
38641708Sstevel 		 * OK response from DL_IOC*, ignore.
38651708Sstevel 		 */
38661708Sstevel 		goto dl_reset;
38671708Sstevel 	}
38681708Sstevel 
38691708Sstevel 	dlp = (union DL_primitives *)mp->b_rptr;
38701708Sstevel 	prim = dlp->dl_primitive;
38711708Sstevel 
38721708Sstevel 	MAN_DBG(MAN_LRSRV, ("man_lrsrv: prim %s", dps[(int)prim]));
38731708Sstevel 
38741708Sstevel 	/*
38751708Sstevel 	 * DLPI state processing big theory: We do not rigorously check
38761708Sstevel 	 * DLPI states (e.g. PENDING stuff). Simple rules:
38771708Sstevel 	 *
38781708Sstevel 	 * 	1) If we see an OK_ACK to an ATTACH_REQ, dlpistate = DL_UNBOUND.
38791708Sstevel 	 *	2) If we see an BIND_ACK to a BIND_REQ, dlpistate = DL_IDLE.
38801708Sstevel 	 *	3) If we see a OK_ACK response to an UNBIND_REQ
38811708Sstevel 	 *	   dlpistate = DL_UNBOUND.
38821708Sstevel 	 *	4) If we see a OK_ACK response to a DETACH_REQ,
38831708Sstevel 	 *	   dlpistate = DL_UNATTACHED.
38841708Sstevel 	 *
38851708Sstevel 	 * Everything that isn't handle by 1-4 above is handled by 5)
38861708Sstevel 	 *
38871708Sstevel 	 *	5) A NAK to any DL_* messages we care about causes
38881708Sstevel 	 *	   dlpistate = DL_UNATTACHED and man_reset_dlpi to run
38891708Sstevel 	 *
38901708Sstevel 	 * TBD - need a reset counter so we can try a switch if it gets
38911708Sstevel 	 * too high.
38921708Sstevel 	 */
38931708Sstevel 
38941708Sstevel 	switch (prim) {
38951708Sstevel 	case DL_OK_ACK:
38961708Sstevel 		cprim = dlp->ok_ack.dl_correct_primitive;
38971708Sstevel 
38981708Sstevel 		switch (cprim) {
38991708Sstevel 		case DL_ATTACH_REQ:
39001708Sstevel 			if (man_dlioc_replay(mdp)) {
39011708Sstevel 				D_SETSTATE(mdp, DL_UNBOUND);
39021708Sstevel 			} else {
39031708Sstevel 				need_dl_reset = TRUE;
39041708Sstevel 				break;
39051708Sstevel 			}
39061708Sstevel 			break;
39071708Sstevel 
39081708Sstevel 		case DL_DETACH_REQ:
39091708Sstevel 			D_SETSTATE(mdp, DL_UNATTACHED);
39101708Sstevel 			break;
39111708Sstevel 
39121708Sstevel 		case DL_UNBIND_REQ:
39131708Sstevel 			/*
39141708Sstevel 			 * Cancel timer and set md_dlpistate.
39151708Sstevel 			 */
39161708Sstevel 			D_SETSTATE(mdp, DL_UNBOUND);
39171708Sstevel 
39181708Sstevel 			ASSERT(mdp->md_bc_id == 0);
39191708Sstevel 			if (mdp->md_lc_timer_id != 0) {
39201708Sstevel 				(void) quntimeout(man_ctl_wq,
39217656SSherry.Moore@Sun.COM 				    mdp->md_lc_timer_id);
39221708Sstevel 				mdp->md_lc_timer_id = 0;
39231708Sstevel 			}
39241708Sstevel 		}
39251708Sstevel 		MAN_DBG(MAN_DLPI,
39267656SSherry.Moore@Sun.COM 		    ("		cprim %s", dps[(int)cprim]));
39271708Sstevel 		break;
39281708Sstevel 
39291708Sstevel 	case DL_BIND_ACK:
39301708Sstevel 		/*
39311708Sstevel 		 * We're ready for data. Get man_lwsrv to run to
39321708Sstevel 		 * process any defered data and start linkcheck timer.
39331708Sstevel 		 */
39341708Sstevel 		D_SETSTATE(mdp, DL_IDLE);
39351708Sstevel 		qenable(mdp->md_wq);
39361708Sstevel 		mdp->md_linkstate = MAN_LINKGOOD;
39371708Sstevel 		if (man_needs_linkcheck(mdp)) {
39381708Sstevel 			mdp->md_lc_timer_id = qtimeout(man_ctl_wq,
39397656SSherry.Moore@Sun.COM 			    man_linkcheck_timer, (void *)mdp,
39407656SSherry.Moore@Sun.COM 			    man_gettimer(MAN_TIMER_LINKCHECK, mdp));
39411708Sstevel 		}
39421708Sstevel 
39431708Sstevel 		break;
39441708Sstevel 
39451708Sstevel 	case DL_ERROR_ACK:
39461708Sstevel 		cprim = dlp->error_ack.dl_error_primitive;
39471708Sstevel 		switch (cprim) {
39481708Sstevel 		case DL_ATTACH_REQ:
39491708Sstevel 		case DL_BIND_REQ:
39501708Sstevel 		case DL_DISABMULTI_REQ:
39511708Sstevel 		case DL_ENABMULTI_REQ:
39521708Sstevel 		case DL_PROMISCON_REQ:
39531708Sstevel 		case DL_PROMISCOFF_REQ:
39541708Sstevel 		case DL_SET_PHYS_ADDR_REQ:
39551708Sstevel 			need_dl_reset = TRUE;
39561708Sstevel 			break;
39571708Sstevel 
39581708Sstevel 		/*
39591708Sstevel 		 * ignore error TBD (better comment)
39601708Sstevel 		 */
39611708Sstevel 		case DL_UNBIND_REQ:
39621708Sstevel 		case DL_DETACH_REQ:
39631708Sstevel 			break;
39641708Sstevel 		}
39651708Sstevel 
39661708Sstevel 		MAN_DBG(MAN_DLPI,
39677656SSherry.Moore@Sun.COM 		    ("\tdl_errno %d dl_unix_errno %d cprim %s",
39687656SSherry.Moore@Sun.COM 		    dlp->error_ack.dl_errno, dlp->error_ack.dl_unix_errno,
39697656SSherry.Moore@Sun.COM 		    dps[(int)cprim]));
39701708Sstevel 		break;
39711708Sstevel 
39721708Sstevel 	case DL_UDERROR_IND:
39731708Sstevel 		MAN_DBG(MAN_DLPI,
39747656SSherry.Moore@Sun.COM 		    ("\tdl_errno %d unix_errno %d",
39757656SSherry.Moore@Sun.COM 		    dlp->uderror_ind.dl_errno,
39767656SSherry.Moore@Sun.COM 		    dlp->uderror_ind.dl_unix_errno));
39771708Sstevel 		break;
39781708Sstevel 
39791708Sstevel 	case DL_INFO_ACK:
39801708Sstevel 		break;
39811708Sstevel 
39821708Sstevel 	default:
39831708Sstevel 		/*
39841708Sstevel 		 * We should not get here.
39851708Sstevel 		 */
39861708Sstevel 		cmn_err(CE_WARN, "man_lrsrv: unexpected DL prim 0x%lx!",
39877656SSherry.Moore@Sun.COM 		    prim);
39881708Sstevel 		need_dl_reset = TRUE;
39891708Sstevel 		break;
39901708Sstevel 	}
39911708Sstevel 
39921708Sstevel dl_reset:
39931708Sstevel 	freemsg(mp);
39941708Sstevel 
39951708Sstevel 	if (need_dl_reset) {
39961708Sstevel 		man_pg_t	*mpg;
39971708Sstevel 		man_path_t	*mp;
39981708Sstevel 
39991708Sstevel 		if (qsize(rq)) {	/* Dump all messages. */
40001708Sstevel 			flushq(rq, FLUSHDATA);
40011708Sstevel 			flushq(WR(rq), FLUSHDATA);
40021708Sstevel 		}
40031708Sstevel 
40041708Sstevel 		mdp->md_dlpierrors++;
40051708Sstevel 		D_SETSTATE(mdp, DL_UNATTACHED);
40061708Sstevel 		if (mdp->md_lc_timer_id != 0) {
40071708Sstevel 			(void) quntimeout(man_ctl_wq, mdp->md_lc_timer_id);
40081708Sstevel 			mdp->md_lc_timer_id = 0;
40091708Sstevel 		}
40101708Sstevel 
40111708Sstevel 		mutex_enter(&man_lock);
40121708Sstevel 		ASSERT(mdp->md_msp != NULL);
40131708Sstevel 		ASSERT(mdp->md_msp->ms_manp != NULL);
40141708Sstevel 		mpg = man_find_pg_by_id(mdp->md_msp->ms_manp->man_pg,
40157656SSherry.Moore@Sun.COM 		    mdp->md_pg_id);
40161708Sstevel 		ASSERT(mpg != NULL);
40171708Sstevel 		mp = man_find_path_by_ppa(mpg->mpg_pathp,
40187656SSherry.Moore@Sun.COM 		    mdp->md_device.mdev_ppa);
40191708Sstevel 		ASSERT(mp != NULL);
40201708Sstevel 		mp->mp_device.mdev_state |= MDEV_FAILED;
40211708Sstevel 		if ((mdp->md_dlpierrors >= MAN_MAX_DLPIERRORS) &&
40227656SSherry.Moore@Sun.COM 		    (man_is_on_domain ||
40237656SSherry.Moore@Sun.COM 		    mdp->md_msp->ms_manp->man_meta_ppa == 1)) {
40241708Sstevel 			/*
40251708Sstevel 			 * Autoswitching is disabled for instance 0
40261708Sstevel 			 * on the SC as we expect the domain to
40271708Sstevel 			 * initiate the path switching.
40281708Sstevel 			 */
40291708Sstevel 			(void) man_do_autoswitch((man_dest_t *)mdp);
40301708Sstevel 			MAN_DBG(MAN_WARN, ("man_lrsrv: dlpi failure(%d,%d),"
40317656SSherry.Moore@Sun.COM 			    " switching path", mdp->md_device.mdev_major,
40327656SSherry.Moore@Sun.COM 			    mdp->md_device.mdev_ppa));
40331708Sstevel 		} else {
40341708Sstevel 			mdp->md_lc_timer_id = qtimeout(man_ctl_wq,
40357656SSherry.Moore@Sun.COM 			    man_reset_dlpi, (void *)mdp,
40367656SSherry.Moore@Sun.COM 			    man_gettimer(MAN_TIMER_DLPIRESET, mdp));
40371708Sstevel 		}
40381708Sstevel 		mutex_exit(&man_lock);
40391708Sstevel 	}
40401708Sstevel 
40411708Sstevel 
40421708Sstevel 	} /* End while (getq()) */
40431708Sstevel 
40441708Sstevel exit:
40451708Sstevel 	MAN_DBG(MAN_DLPI, ("man_lrsrv: returns"));
40461708Sstevel 
40471708Sstevel 	return (0);
40481708Sstevel }
40491708Sstevel 
40501708Sstevel static int
man_needs_linkcheck(man_dest_t * mdp)40511708Sstevel man_needs_linkcheck(man_dest_t *mdp)
40521708Sstevel {
40531708Sstevel 	/*
40541708Sstevel 	 * Not ready for linkcheck.
40551708Sstevel 	 */
40561708Sstevel 	if (mdp->md_msp == NULL || mdp->md_msp->ms_manp == NULL)
40571708Sstevel 		return (0);
40581708Sstevel 
40591708Sstevel 	/*
40601708Sstevel 	 * Linkchecking needs to be done on IP streams. For domain, all
40611708Sstevel 	 * driver instances need checking, for SC only instance 1 needs it.
40621708Sstevel 	 */
40631708Sstevel 	if ((man_is_on_domain || mdp->md_msp->ms_manp->man_meta_ppa == 1) &&
40641708Sstevel 	    (mdp->md_msp->ms_sap == ETHERTYPE_IP ||
40651708Sstevel 	    mdp->md_msp->ms_sap == ETHERTYPE_IPV6))
40661708Sstevel 
40671708Sstevel 		return (1);
40681708Sstevel 
40691708Sstevel 	/*
40701708Sstevel 	 * Linkcheck not need on this link.
40711708Sstevel 	 */
40721708Sstevel 	return (0);
40731708Sstevel }
40741708Sstevel 
40751708Sstevel /*
40761708Sstevel  * The following routines process work requests posted to man_iwork_q
40771708Sstevel  * from the non-STREAMS half of the driver (see man_bwork.c). The work
40781708Sstevel  * requires access to the inner perimeter lock of the driver. This
40791708Sstevel  * lock is acquired by man_uwsrv, who calls man_iwork to process the
40801708Sstevel  * man_iwork_q->
40811708Sstevel  */
40821708Sstevel 
40831708Sstevel /*
40841708Sstevel  * The man_bwork has posted some work for us to do inside the
40851708Sstevel  * perimeter. This mainly involves updating lower multiplexor data
40861708Sstevel  * structures (non-blocking type stuff). So, we can hold the man_lock
40871708Sstevel  * until we are done processing all work items. Note that some of these
40881708Sstevel  * routines in turn submit work back to the bgthread, which they can do
40891708Sstevel  * since we hold the man_lock.
40901708Sstevel  */
40911708Sstevel static void
man_iwork()40921708Sstevel man_iwork()
40931708Sstevel {
40941708Sstevel 	man_work_t	*wp;
40951708Sstevel 	int		wp_finished;
40961708Sstevel 
40971708Sstevel 	MAN_DBG(MAN_SWITCH, ("man_iwork: q_work(0x%p)",
40987656SSherry.Moore@Sun.COM 	    (void *)man_iwork_q->q_work));
40991708Sstevel 
41001708Sstevel 	mutex_enter(&man_lock);
41011708Sstevel 
41021708Sstevel 	while (man_iwork_q->q_work) {
41031708Sstevel 
41041708Sstevel 		wp = man_iwork_q->q_work;
41051708Sstevel 		man_iwork_q->q_work = wp->mw_next;
41061708Sstevel 		wp->mw_next = NULL;
41071708Sstevel 
41081708Sstevel 		mutex_exit(&man_lock);
41091708Sstevel 
41101708Sstevel 		MAN_DBG(MAN_SWITCH, ("man_iwork: type %s",
41117656SSherry.Moore@Sun.COM 		    _mw_type[wp->mw_type]));
41121708Sstevel 
41131708Sstevel 		wp_finished = TRUE;
41141708Sstevel 
41151708Sstevel 		switch (wp->mw_type) {
41161708Sstevel 		case MAN_WORK_DRATTACH:
41171708Sstevel 			(void) man_do_dr_attach(wp);
41181708Sstevel 			break;
41191708Sstevel 
41201708Sstevel 		case MAN_WORK_DRSWITCH:
41211708Sstevel 			/*
41221708Sstevel 			 * Return status to man_dr_detach immediately. If
41231708Sstevel 			 * no error submitting SWITCH request, man_iswitch
41241708Sstevel 			 * or man_bclose will cv_signal man_dr_detach on
41251708Sstevel 			 * completion of SWITCH work request.
41261708Sstevel 			 */
41271708Sstevel 			if (man_do_dr_switch(wp) == 0)
41281708Sstevel 				wp_finished = FALSE;
41291708Sstevel 			break;
41301708Sstevel 
41311708Sstevel 		case MAN_WORK_DRDETACH:
41321708Sstevel 			man_do_dr_detach(wp);
41331708Sstevel 			break;
41341708Sstevel 
41351708Sstevel 		case MAN_WORK_SWITCH:
41361708Sstevel 			if (man_iswitch(wp))
41371708Sstevel 				wp_finished = FALSE;
41381708Sstevel 			break;
41391708Sstevel 
41401708Sstevel 		case MAN_WORK_KSTAT_UPDATE:
41411708Sstevel 			man_do_kstats(wp);
41421708Sstevel 			break;
41431708Sstevel 
41441708Sstevel 		default:
41451708Sstevel 			cmn_err(CE_WARN, "man_iwork: "
41461708Sstevel 			    "illegal work type(%d)", wp->mw_type);
41471708Sstevel 			break;
41481708Sstevel 		}
41491708Sstevel 
41501708Sstevel 		mutex_enter(&man_lock);
41511708Sstevel 
41521708Sstevel 		/*
41531708Sstevel 		 * If we've completed the work request, delete, or
41541708Sstevel 		 * cv_signal waiter.
41551708Sstevel 		 */
41561708Sstevel 		if (wp_finished) {
41571708Sstevel 			wp->mw_flags |= MAN_WFLAGS_DONE;
41581708Sstevel 
41591708Sstevel 			if (wp->mw_flags & MAN_WFLAGS_CVWAITER)
41601708Sstevel 				cv_signal(&wp->mw_cv);
41611708Sstevel 			else
41621708Sstevel 				man_work_free(wp);
41631708Sstevel 		}
41641708Sstevel 	}
41651708Sstevel 
41661708Sstevel 	mutex_exit(&man_lock);
41671708Sstevel }
41681708Sstevel 
41691708Sstevel /*
41701708Sstevel  * man_dr_detach has submitted a request to DRSWITCH a path.
41711708Sstevel  * He is in cv_wait_sig(wp->mw_cv). We forward the work request on to
41721708Sstevel  * man_bwork as a switch request. It should end up back at
41731708Sstevel  * man_iwork, who will cv_signal(wp->mw_cv) man_dr_detach.
41741708Sstevel  *
41751708Sstevel  * Called holding inner perimeter lock.
41761708Sstevel  * man_lock is held to synchronize access to pathgroup list(man_pg).
41771708Sstevel  */
41781708Sstevel static int
man_do_dr_switch(man_work_t * wp)41791708Sstevel man_do_dr_switch(man_work_t *wp)
41801708Sstevel {
41811708Sstevel 	man_t		*manp;
41821708Sstevel 	man_pg_t	*mpg;
41831708Sstevel 	man_path_t	*mp;
41841708Sstevel 	man_path_t	*ap;
41851708Sstevel 	man_adest_t	*adp;
41861708Sstevel 	mi_path_t	mpath;
41871708Sstevel 	int		status = 0;
41881708Sstevel 
41891708Sstevel 	adp = &wp->mw_arg;
41901708Sstevel 
41911708Sstevel 	MAN_DBG(MAN_SWITCH, ("man_do_dr_switch: pg_id %d work:", adp->a_pg_id));
41921708Sstevel 	MAN_DBGCALL(MAN_SWITCH, man_print_work(wp));
41931708Sstevel 
41941708Sstevel 	mutex_enter(&man_lock);
41951708Sstevel 	manp = ddi_get_soft_state(man_softstate, adp->a_man_ppa);
41961708Sstevel 	if (manp == NULL || manp->man_pg == NULL) {
41971708Sstevel 		status = ENODEV;
41981708Sstevel 		goto exit;
41991708Sstevel 	}
42001708Sstevel 
42011708Sstevel 	mpg = man_find_pg_by_id(manp->man_pg, adp->a_pg_id);
42021708Sstevel 	if (mpg == NULL) {
42031708Sstevel 		status = ENODEV;
42041708Sstevel 		goto exit;
42051708Sstevel 	}
42061708Sstevel 
42071708Sstevel 	if (mpg->mpg_flags & MAN_PG_SWITCHING) {
42081708Sstevel 		status = EAGAIN;
42091708Sstevel 		goto exit;
42101708Sstevel 	}
42111708Sstevel 
42121708Sstevel 	/*
42131708Sstevel 	 * Check to see if detaching device is active. If so, activate
42141708Sstevel 	 * an alternate.
42151708Sstevel 	 */
42161708Sstevel 	mp = man_find_active_path(mpg->mpg_pathp);
42171708Sstevel 	if (mp && mp->mp_device.mdev_ppa == adp->a_sf_dev.mdev_ppa) {
42181708Sstevel 
42191708Sstevel 		ap = man_find_alternate_path(mpg->mpg_pathp);
42201708Sstevel 		if (ap == NULL) {
42211708Sstevel 			status = EBUSY;
42221708Sstevel 			goto exit;
42231708Sstevel 		}
42241708Sstevel 
42251708Sstevel 		bzero((char *)&mpath, sizeof (mi_path_t));
42261708Sstevel 
42271708Sstevel 		mpath.mip_cmd = MI_PATH_ACTIVATE;
42281708Sstevel 		mpath.mip_man_ppa = 0;
42291708Sstevel 		mpath.mip_pg_id = 0;
42301708Sstevel 		mpath.mip_devs[0] = ap->mp_device;
42311708Sstevel 		mpath.mip_ndevs = 1;
42321708Sstevel 		ether_copy(&manp->man_eaddr, &mpath.mip_eaddr);
42331708Sstevel 
42341708Sstevel 		/*
42351708Sstevel 		 * DR thread is sleeping on wp->mw_cv. We change the work
42361708Sstevel 		 * request from DRSWITCH to SWITCH and submit it to
42371708Sstevel 		 * for processing by man_bwork (via man_pg_cmd). At
42381708Sstevel 		 * completion the SWITCH work request is processed by
42391708Sstevel 		 * man_iswitch() or man_bclose and the DR thread will
42401708Sstevel 		 * be cv_signal'd.
42411708Sstevel 		 */
42421708Sstevel 		wp->mw_type = MAN_WORK_SWITCH;
42431708Sstevel 		if (status = man_pg_cmd(&mpath, wp))
42441708Sstevel 			goto exit;
42451708Sstevel 
42461708Sstevel 	} else {
42471708Sstevel 		/*
42481708Sstevel 		 * Tell man_dr_detach that detaching device is not currently
42491708Sstevel 		 * in use.
42501708Sstevel 		 */
42511708Sstevel 		status = ENODEV;
42521708Sstevel 	}
42531708Sstevel 
42541708Sstevel exit:
42551708Sstevel 	if (status) {
42561708Sstevel 		/*
42571708Sstevel 		 * ENODEV is a noop, not really an error.
42581708Sstevel 		 */
42591708Sstevel 		if (status != ENODEV)
42601708Sstevel 			wp->mw_status = status;
42611708Sstevel 	}
42621708Sstevel 	mutex_exit(&man_lock);
42631708Sstevel 
42641708Sstevel 	return (status);
42651708Sstevel }
42661708Sstevel 
42671708Sstevel /*
42681708Sstevel  * man_dr_attach has submitted a request to DRATTACH a path,
42691708Sstevel  * add that path to the path list.
42701708Sstevel  *
42711708Sstevel  * Called holding perimeter lock.
42721708Sstevel  */
42731708Sstevel static int
man_do_dr_attach(man_work_t * wp)42741708Sstevel man_do_dr_attach(man_work_t *wp)
42751708Sstevel {
42761708Sstevel 	man_t		*manp;
42771708Sstevel 	man_adest_t	*adp;
42781708Sstevel 	mi_path_t	mpath;
42791708Sstevel 	manc_t		manc;
42801708Sstevel 	int		status = 0;
42811708Sstevel 
42821708Sstevel 	adp = &wp->mw_arg;
42831708Sstevel 
42841708Sstevel 	MAN_DBG(MAN_SWITCH, ("man_do_dr_attach: pg_id %d work:", adp->a_pg_id));
42851708Sstevel 	MAN_DBGCALL(MAN_SWITCH, man_print_work(wp));
42861708Sstevel 
42871708Sstevel 	mutex_enter(&man_lock);
42881708Sstevel 	manp = ddi_get_soft_state(man_softstate, adp->a_man_ppa);
42891708Sstevel 	if (manp == NULL || manp->man_pg == NULL) {
42901708Sstevel 		status = ENODEV;
42911708Sstevel 		goto exit;
42921708Sstevel 	}
42931708Sstevel 
42941708Sstevel 	if (status = man_get_iosram(&manc)) {
42951708Sstevel 		goto exit;
42961708Sstevel 	}
42971708Sstevel 	/*
42981708Sstevel 	 * Extract SC ethernet address from IOSRAM.
42991708Sstevel 	 */
43001708Sstevel 	ether_copy(&manc.manc_sc_eaddr, &mpath.mip_eaddr);
43011708Sstevel 
43021708Sstevel 	mpath.mip_pg_id = adp->a_pg_id;
43031708Sstevel 	mpath.mip_man_ppa = adp->a_man_ppa;
43041708Sstevel 	/*
43051708Sstevel 	 * man_dr_attach passes the new device info in a_sf_dev.
43061708Sstevel 	 */
43071708Sstevel 	MAN_DBG(MAN_DR, ("man_do_dr_attach: "));
43081708Sstevel 	MAN_DBGCALL(MAN_DR, man_print_dev(&adp->a_sf_dev));
43091708Sstevel 	mpath.mip_devs[0] = adp->a_sf_dev;
43101708Sstevel 	mpath.mip_ndevs = 1;
43111708Sstevel 	mpath.mip_cmd = MI_PATH_ADD;
43121708Sstevel 	status = man_pg_cmd(&mpath, NULL);
43131708Sstevel 
43141708Sstevel exit:
43151708Sstevel 	mutex_exit(&man_lock);
43161708Sstevel 	return (status);
43171708Sstevel }
43181708Sstevel 
43191708Sstevel /*
43201708Sstevel  * man_dr_detach has submitted a request to DRDETACH a path.
43211708Sstevel  * He is in cv_wait_sig(wp->mw_cv). We remove the path and
43221708Sstevel  * cv_signal(wp->mw_cv) man_dr_detach.
43231708Sstevel  *
43241708Sstevel  * Called holding perimeter lock.
43251708Sstevel  */
43261708Sstevel static void
man_do_dr_detach(man_work_t * wp)43271708Sstevel man_do_dr_detach(man_work_t *wp)
43281708Sstevel {
43291708Sstevel 	man_t		*manp;
43301708Sstevel 	man_pg_t	*mpg;
43311708Sstevel 	man_path_t	*mp;
43321708Sstevel 	man_adest_t	*adp;
43331708Sstevel 	manc_t		manc;
43341708Sstevel 	mi_path_t	mpath;
43351708Sstevel 	int		i;
43361708Sstevel 	int		found;
43371708Sstevel 	int		status = 0;
43381708Sstevel 
43391708Sstevel 	adp = &wp->mw_arg;
43401708Sstevel 
43411708Sstevel 	MAN_DBG(MAN_SWITCH, ("man_do_dr_detach: pg_id %d work:", adp->a_pg_id));
43421708Sstevel 	MAN_DBGCALL(MAN_SWITCH, man_print_work(wp));
43431708Sstevel 
43441708Sstevel 	mutex_enter(&man_lock);
43451708Sstevel 	manp = ddi_get_soft_state(man_softstate, adp->a_man_ppa);
43461708Sstevel 	if (manp == NULL || manp->man_pg == NULL) {
43471708Sstevel 		status = ENODEV;
43481708Sstevel 		goto exit;
43491708Sstevel 	}
43501708Sstevel 
43511708Sstevel 	mpg = man_find_pg_by_id(manp->man_pg, adp->a_pg_id);
43521708Sstevel 	if (mpg == NULL) {
43531708Sstevel 		status = ENODEV;
43541708Sstevel 		goto exit;
43551708Sstevel 	}
43561708Sstevel 
43571708Sstevel 	if (mpg->mpg_flags & MAN_PG_SWITCHING) {
43581708Sstevel 		status = EAGAIN;
43591708Sstevel 		goto exit;
43601708Sstevel 	}
43611708Sstevel 
43621708Sstevel 	/*
43631708Sstevel 	 * We should have switched detaching path if it was active.
43641708Sstevel 	 */
43651708Sstevel 	mp = man_find_active_path(mpg->mpg_pathp);
43661708Sstevel 	if (mp && mp->mp_device.mdev_ppa == adp->a_sf_dev.mdev_ppa) {
43671708Sstevel 		status = EAGAIN;
43681708Sstevel 		goto exit;
43691708Sstevel 	}
43701708Sstevel 
43711708Sstevel 	/*
43721708Sstevel 	 * Submit an ASSIGN command, minus the detaching device.
43731708Sstevel 	 */
43741708Sstevel 	bzero((char *)&mpath, sizeof (mi_path_t));
43751708Sstevel 
43761708Sstevel 	if (status = man_get_iosram(&manc)) {
43771708Sstevel 		goto exit;
43781708Sstevel 	}
43791708Sstevel 
43801708Sstevel 	mpath.mip_cmd = MI_PATH_ASSIGN;
43811708Sstevel 	mpath.mip_man_ppa = 0;
43821708Sstevel 	mpath.mip_pg_id = 0;
43831708Sstevel 
43841708Sstevel 	mp = mpg->mpg_pathp;
43851708Sstevel 	i = 0;
43861708Sstevel 	found = FALSE;
43871708Sstevel 	while (mp != NULL) {
43881708Sstevel 		if (mp->mp_device.mdev_ppa != adp->a_sf_dev.mdev_ppa) {
43891708Sstevel 			mpath.mip_devs[i] = mp->mp_device;
43901708Sstevel 			i++;
43911708Sstevel 		} else {
43921708Sstevel 			found = TRUE;
43931708Sstevel 		}
43941708Sstevel 		mp = mp->mp_next;
43951708Sstevel 	}
43961708Sstevel 
43971708Sstevel 	if (found) {
43981708Sstevel 		/*
43991708Sstevel 		 * Need to include SCs ethernet address in command.
44001708Sstevel 		 */
44011708Sstevel 		mpath.mip_ndevs = i;
44021708Sstevel 		ether_copy(&manc.manc_sc_eaddr, &mpath.mip_eaddr);
44031708Sstevel 
44041708Sstevel 		status = man_pg_cmd(&mpath, NULL);
44051708Sstevel 	}
44061708Sstevel 
44071708Sstevel 	/*
44081708Sstevel 	 * Hand back status to man_dr_detach request.
44091708Sstevel 	 */
44101708Sstevel exit:
44111708Sstevel 	if (status != ENODEV)
44121708Sstevel 		wp->mw_status = status;
44131708Sstevel 
44141708Sstevel 	mutex_exit(&man_lock);
44151708Sstevel 
44161708Sstevel }
44171708Sstevel 
44181708Sstevel 
44191708Sstevel /*
44201708Sstevel  * The background thread has configured new lower multiplexor streams for
44211708Sstevel  * the given destinations. Update the appropriate destination data structures
44221708Sstevel  * inside the inner perimeter. We must take care to deal with destinations
44231708Sstevel  * whose upper stream has closed or detached from lower streams.
44241708Sstevel  *
44251708Sstevel  * Returns
44261708Sstevel  *	0		Done with work request.
44271708Sstevel  *	1		Reused work request.
44281708Sstevel  */
44291708Sstevel static int
man_iswitch(man_work_t * wp)44301708Sstevel man_iswitch(man_work_t *wp)
44311708Sstevel {
44321708Sstevel 	man_adest_t	*adp;
44331708Sstevel 	man_t		*manp;
44341708Sstevel 	man_pg_t	*mpg;
44351708Sstevel 	man_path_t	*mp = NULL;
44361708Sstevel 	man_dest_t	*mdp;
44371708Sstevel 	man_dest_t	*tdp;
44381708Sstevel 	int		i;
44391708Sstevel 	int		switch_ok = TRUE;
44401708Sstevel 
44411708Sstevel 	adp = &wp->mw_arg;
44421708Sstevel 
44431708Sstevel 	if (wp->mw_status != 0) {
44441708Sstevel 		switch_ok = FALSE;	/* Never got things opened */
44451708Sstevel 	}
44461708Sstevel 
44471708Sstevel 	/*
44481708Sstevel 	 * Update destination structures as appropriate.
44491708Sstevel 	 */
44501708Sstevel 	for (i = 0; i < adp->a_ndests; i++) {
44511708Sstevel 		man_dest_t	tmp;
44521708Sstevel 
44531708Sstevel 		/*
44541708Sstevel 		 * Check to see if lower stream we just switch is still
44551708Sstevel 		 * around.
44561708Sstevel 		 */
44571708Sstevel 		tdp = &adp->a_mdp[i];
44581708Sstevel 		mdp = man_switch_match(tdp, adp->a_pg_id, tdp->md_switch_id);
44591708Sstevel 
44601708Sstevel 		if (mdp == NULL)
44611708Sstevel 			continue;
44621708Sstevel 
44631708Sstevel 		if (switch_ok == FALSE) {
44641708Sstevel 			/*
44651708Sstevel 			 * Switch failed for some reason.  Clear
44661708Sstevel 			 * PLUMBING flag and retry switch again later.
44671708Sstevel 			 */
44681708Sstevel 			man_ifail_dest(mdp);
44691708Sstevel 			continue;
44701708Sstevel 		}
44711708Sstevel 
44721708Sstevel 		/*
44731708Sstevel 		 * Swap new info, for old. We return the old info to
44741708Sstevel 		 * man_bwork to close things up below.
44751708Sstevel 		 */
44761708Sstevel 		bcopy((char *)mdp, (char *)&tmp, sizeof (man_dest_t));
44771708Sstevel 
44781708Sstevel 		ASSERT(mdp->md_state & MAN_DSTATE_PLUMBING);
44791708Sstevel 		ASSERT(mdp->md_state == tdp->md_state);
44801708Sstevel 
44811708Sstevel 		mdp->md_state = tdp->md_state;
44821708Sstevel 
44831708Sstevel 		/*
44841708Sstevel 		 * save the wq from the destination passed(tdp).
44851708Sstevel 		 */
44861708Sstevel 		mdp->md_wq = tdp->md_wq;
44871708Sstevel 		RD(mdp->md_wq)->q_ptr = (void *)(mdp);
44881708Sstevel 		WR(mdp->md_wq)->q_ptr = (void *)(mdp);
44891708Sstevel 
44901708Sstevel 		mdp->md_state &= ~MAN_DSTATE_INITIALIZING;
44911708Sstevel 		mdp->md_state |= MAN_DSTATE_READY;
44921708Sstevel 
44931708Sstevel 		ASSERT(mdp->md_device.mdev_major == adp->a_sf_dev.mdev_major);
44941708Sstevel 
44951708Sstevel 		ASSERT(tdp->md_device.mdev_ppa == adp->a_st_dev.mdev_ppa);
44961708Sstevel 		ASSERT(tdp->md_device.mdev_major == adp->a_st_dev.mdev_major);
44971708Sstevel 
44981708Sstevel 		mdp->md_device = tdp->md_device;
44991708Sstevel 		mdp->md_muxid = tdp->md_muxid;
45001708Sstevel 		mdp->md_linkstate = MAN_LINKUNKNOWN;
45011708Sstevel 		(void) drv_getparm(TIME, &mdp->md_lastswitch);
45021708Sstevel 		mdp->md_state &= ~MAN_DSTATE_PLUMBING;
45031708Sstevel 		mdp->md_switch_id = 0;
45041708Sstevel 		mdp->md_switches++;
45051708Sstevel 		mdp->md_dlpierrors = 0;
45061708Sstevel 		D_SETSTATE(mdp, DL_UNATTACHED);
45071708Sstevel 
45081708Sstevel 		/*
45091708Sstevel 		 * Resync lower w/ upper dlpi state. This will start link
45101708Sstevel 		 * timer if/when lower stream goes to DL_IDLE (see man_lrsrv).
45111708Sstevel 		 */
45121708Sstevel 		man_reset_dlpi((void *)mdp);
45131708Sstevel 
45141708Sstevel 		bcopy((char *)&tmp, (char *)tdp, sizeof (man_dest_t));
45151708Sstevel 	}
45161708Sstevel 
45171708Sstevel 	if (switch_ok) {
45181708Sstevel 		for (i = 0; i < adp->a_ndests; i++) {
45191708Sstevel 			tdp = &adp->a_mdp[i];
45201708Sstevel 
45211708Sstevel 			tdp->md_state &= ~MAN_DSTATE_PLUMBING;
45221708Sstevel 			tdp->md_state &= ~MAN_DSTATE_INITIALIZING;
45231708Sstevel 			tdp->md_state |= MAN_DSTATE_READY;
45241708Sstevel 		}
45251708Sstevel 	} else {
45261708Sstevel 		/*
45271708Sstevel 		 * Never got switch-to destinations open, free them.
45281708Sstevel 		 */
45291708Sstevel 		man_kfree(adp->a_mdp,
45307656SSherry.Moore@Sun.COM 		    sizeof (man_dest_t) * adp->a_ndests);
45311708Sstevel 	}
45321708Sstevel 
45331708Sstevel 	/*
45341708Sstevel 	 * Clear pathgroup switching flag and update path flags.
45351708Sstevel 	 */
45361708Sstevel 	mutex_enter(&man_lock);
45371708Sstevel 	manp = ddi_get_soft_state(man_softstate, adp->a_man_ppa);
45381708Sstevel 
45391708Sstevel 	ASSERT(manp != NULL);
45401708Sstevel 	ASSERT(manp->man_pg != NULL);
45411708Sstevel 
45421708Sstevel 	mpg = man_find_pg_by_id(manp->man_pg, adp->a_pg_id);
45431708Sstevel 	ASSERT(mpg != NULL);
45441708Sstevel 	ASSERT(mpg->mpg_flags & MAN_PG_SWITCHING);
45451708Sstevel 	mpg->mpg_flags &= ~MAN_PG_SWITCHING;
45461708Sstevel 
45471708Sstevel 	/*
45481708Sstevel 	 * Switch succeeded, mark path we switched from as failed, and
45491708Sstevel 	 * device we switch to as active and clear its failed flag (if set).
45501708Sstevel 	 * Sync up kstats.
45511708Sstevel 	 */
45521708Sstevel 	if (switch_ok) {
45531708Sstevel 		mp = man_find_active_path(mpg->mpg_pathp);
45541708Sstevel 		if (mp != NULL) {
45551708Sstevel 
45561708Sstevel 			ASSERT(adp->a_sf_dev.mdev_major != 0);
45571708Sstevel 
45581708Sstevel 			MAN_DBG(MAN_SWITCH, ("man_iswitch: switch from dev:"));
45591708Sstevel 			MAN_DBGCALL(MAN_SWITCH, man_print_dev(&adp->a_sf_dev));
45601708Sstevel 
45611708Sstevel 			mp->mp_device.mdev_state &= ~MDEV_ACTIVE;
45621708Sstevel 		} else
45631708Sstevel 			ASSERT(adp->a_sf_dev.mdev_major == 0);
45641708Sstevel 
45651708Sstevel 		MAN_DBG(MAN_SWITCH, ("man_iswitch: switch to dev:"));
45661708Sstevel 		MAN_DBGCALL(MAN_SWITCH, man_print_dev(&adp->a_st_dev));
45671708Sstevel 
45681708Sstevel 		ASSERT(adp->a_st_dev.mdev_major != 0);
45691708Sstevel 
45701708Sstevel 		mp = man_find_path_by_ppa(mpg->mpg_pathp,
45717656SSherry.Moore@Sun.COM 		    adp->a_st_dev.mdev_ppa);
45721708Sstevel 
45731708Sstevel 		ASSERT(mp != NULL);
45741708Sstevel 
45751708Sstevel 		mp->mp_device.mdev_state |= MDEV_ACTIVE;
45761708Sstevel 	}
45771708Sstevel 
45781708Sstevel 	/*
45791708Sstevel 	 * Decrement manp reference count and hand back work request if
45801708Sstevel 	 * needed.
45811708Sstevel 	 */
45821708Sstevel 	manp->man_refcnt--;
45831708Sstevel 
45841708Sstevel 	if (switch_ok) {
45851708Sstevel 		wp->mw_type = MAN_WORK_CLOSE;
45861708Sstevel 		man_work_add(man_bwork_q, wp);
45871708Sstevel 	}
45881708Sstevel 
45891708Sstevel 	mutex_exit(&man_lock);
45901708Sstevel 
45911708Sstevel 	return (switch_ok);
45921708Sstevel }
45931708Sstevel 
45941708Sstevel /*
45951708Sstevel  * Find the destination in the upper stream that we just switched.
45961708Sstevel  */
45971708Sstevel man_dest_t *
man_switch_match(man_dest_t * sdp,int pg_id,void * sid)45981708Sstevel man_switch_match(man_dest_t *sdp, int pg_id, void *sid)
45991708Sstevel {
46001708Sstevel 	man_dest_t	*mdp = NULL;
46011708Sstevel 	manstr_t	*msp;
46021708Sstevel 
46031708Sstevel 	for (msp = man_strup; msp != NULL; msp = msp->ms_next) {
46041708Sstevel 		/*
46051708Sstevel 		 * Check if upper stream closed, or detached.
46061708Sstevel 		 */
46071708Sstevel 		if (msp != sdp->md_msp)
46081708Sstevel 			continue;
46091708Sstevel 
46101708Sstevel 		if (msp->ms_dests == NULL)
46111708Sstevel 			break;
46121708Sstevel 
46131708Sstevel 		mdp = &msp->ms_dests[pg_id];
46141708Sstevel 
46151708Sstevel 		/*
46161708Sstevel 		 * Upper stream detached and reattached while we were
46171708Sstevel 		 * switching.
46181708Sstevel 		 */
46191708Sstevel 		if (mdp->md_switch_id != sid) {
46201708Sstevel 			mdp = NULL;
46211708Sstevel 			break;
46221708Sstevel 		}
46231708Sstevel 	}
46241708Sstevel 
46251708Sstevel 	return (mdp);
46261708Sstevel }
46271708Sstevel 
46281708Sstevel /*
46291708Sstevel  * bg_thread cant complete the switch for some reason. (Re)start the
46301708Sstevel  * linkcheck timer again.
46311708Sstevel  */
46321708Sstevel static void
man_ifail_dest(man_dest_t * mdp)46331708Sstevel man_ifail_dest(man_dest_t *mdp)
46341708Sstevel {
46351708Sstevel 	ASSERT(mdp->md_lc_timer_id == 0);
46361708Sstevel 	ASSERT(mdp->md_bc_id == 0);
46371708Sstevel 	ASSERT(mdp->md_state & MAN_DSTATE_PLUMBING);
46381708Sstevel 
46391708Sstevel 	MAN_DBG(MAN_SWITCH, ("man_ifail_dest"));
46401708Sstevel 	MAN_DBGCALL(MAN_SWITCH, man_print_mdp(mdp));
46411708Sstevel 
46421708Sstevel 	mdp->md_state &= ~MAN_DSTATE_PLUMBING;
46431708Sstevel 	mdp->md_linkstate = MAN_LINKFAIL;
46441708Sstevel 
46451708Sstevel 	/*
46461708Sstevel 	 * If we have not yet initialized link, or the upper stream is
46471708Sstevel 	 * DL_IDLE, restart the linktimer.
46481708Sstevel 	 */
46491708Sstevel 	if ((mdp->md_state & MAN_DSTATE_INITIALIZING) ||
46507656SSherry.Moore@Sun.COM 	    ((mdp->md_msp->ms_sap == ETHERTYPE_IPV6 ||
46517656SSherry.Moore@Sun.COM 	    mdp->md_msp->ms_sap == ETHERTYPE_IP) &&
46527656SSherry.Moore@Sun.COM 	    mdp->md_msp->ms_dlpistate == DL_IDLE)) {
46531708Sstevel 
46541708Sstevel 		mdp->md_lc_timer_id = qtimeout(man_ctl_wq, man_linkcheck_timer,
46557656SSherry.Moore@Sun.COM 		    (void *)mdp, man_gettimer(MAN_TIMER_LINKCHECK, mdp));
46561708Sstevel 	}
46571708Sstevel 
46581708Sstevel }
46591708Sstevel 
46601708Sstevel /*
46611708Sstevel  * Arrange to replay all of ms_dl_mp on the new lower stream to get it
46621708Sstevel  * in sync with the upper stream. Note that this includes setting the
46631708Sstevel  * physical address.
46641708Sstevel  *
46651708Sstevel  * Called from qtimeout with inner perimeter lock.
46661708Sstevel  */
46671708Sstevel static void
man_reset_dlpi(void * argp)46681708Sstevel man_reset_dlpi(void *argp)
46691708Sstevel {
46701708Sstevel 	man_dest_t	*mdp = (man_dest_t *)argp;
46711708Sstevel 	manstr_t	*msp;
46721708Sstevel 	mblk_t		*mp;
46731708Sstevel 	mblk_t		*rmp = NULL;
46741708Sstevel 	mblk_t		*tmp;
46751708Sstevel 
46761708Sstevel 	mdp->md_lc_timer_id = 0;
46771708Sstevel 
46781708Sstevel 	if (mdp->md_state != MAN_DSTATE_READY) {
46791708Sstevel 		MAN_DBG(MAN_DLPI, ("man_reset_dlpi: not ready!"));
46801708Sstevel 		return;
46811708Sstevel 	}
46821708Sstevel 
46831708Sstevel 	msp = mdp->md_msp;
46841708Sstevel 
46851708Sstevel 	rmp = man_dup_mplist(msp->ms_dl_mp);
46861708Sstevel 	if (rmp == NULL)
46871708Sstevel 		goto fail;
46881708Sstevel 
46891708Sstevel 	/*
46901708Sstevel 	 * Send down an unbind and detach request, just to clean things
46911708Sstevel 	 * out, we ignore ERROR_ACKs for unbind and detach in man_lrsrv.
46921708Sstevel 	 */
46931708Sstevel 	tmp = man_alloc_ubreq_dreq();
46941708Sstevel 	if (tmp == NULL) {
46951708Sstevel 		goto fail;
46961708Sstevel 	}
46971708Sstevel 	mp = tmp;
46981708Sstevel 	while (mp->b_next != NULL)
46991708Sstevel 		mp = mp->b_next;
47001708Sstevel 	mp->b_next = rmp;
47011708Sstevel 	rmp = tmp;
47021708Sstevel 
47031708Sstevel 	man_dlpi_replay(mdp, rmp);
47041708Sstevel 
47051708Sstevel 	return;
47061708Sstevel 
47071708Sstevel fail:
47081708Sstevel 
47091708Sstevel 	while (rmp) {
47101708Sstevel 		mp = rmp;
47111708Sstevel 		rmp = rmp->b_next;
47121708Sstevel 		mp->b_next = mp->b_prev = NULL;
47131708Sstevel 		freemsg(mp);
47141708Sstevel 	}
47151708Sstevel 
47161708Sstevel 	ASSERT(mdp->md_lc_timer_id == 0);
47171708Sstevel 	ASSERT(mdp->md_bc_id == 0);
47181708Sstevel 
47191708Sstevel 	/*
47201708Sstevel 	 * If low on memory, try again later. I Could use qbufcall, but that
47211708Sstevel 	 * could fail and I would have to try and recover from that w/
47221708Sstevel 	 * qtimeout anyway.
47231708Sstevel 	 */
47241708Sstevel 	mdp->md_lc_timer_id = qtimeout(man_ctl_wq, man_reset_dlpi,
47257656SSherry.Moore@Sun.COM 	    (void *)mdp, man_gettimer(MAN_TIMER_LINKCHECK, mdp));
47261708Sstevel }
47271708Sstevel 
47281708Sstevel /*
47291708Sstevel  * Once we receive acknowledgement that DL_ATTACH_REQ was successful,
47301708Sstevel  * we can send down the DL_* related IOCTLs (e.g. DL_IOC_HDR). If we
47311708Sstevel  * try and send them downsteam w/o waiting, the ioctl's get processed before
47321708Sstevel  * the ATTACH_REQ and they are rejected. TBD - could just do the lower
47331708Sstevel  * dlpi state change in lock step. TBD
47341708Sstevel  */
47351708Sstevel static int
man_dlioc_replay(man_dest_t * mdp)47361708Sstevel man_dlioc_replay(man_dest_t *mdp)
47371708Sstevel {
47381708Sstevel 	mblk_t		*rmp;
47391708Sstevel 	int		status = 1;
47401708Sstevel 
47411708Sstevel 	if (mdp->md_msp->ms_dlioc_mp == NULL)
47421708Sstevel 		goto exit;
47431708Sstevel 
47441708Sstevel 	rmp = man_dup_mplist(mdp->md_msp->ms_dlioc_mp);
47451708Sstevel 	if (rmp == NULL) {
47461708Sstevel 		status = 0;
47471708Sstevel 		goto exit;
47481708Sstevel 	}
47491708Sstevel 
47501708Sstevel 	man_dlpi_replay(mdp, rmp);
47511708Sstevel exit:
47521708Sstevel 	return (status);
47531708Sstevel }
47541708Sstevel 
47551708Sstevel static mblk_t *
man_alloc_ubreq_dreq()47561708Sstevel man_alloc_ubreq_dreq()
47571708Sstevel {
47581708Sstevel 	mblk_t			*dreq;
47591708Sstevel 	mblk_t			*ubreq = NULL;
47601708Sstevel 	union DL_primitives	*dlp;
47611708Sstevel 
47621708Sstevel 	dreq = allocb(DL_DETACH_REQ_SIZE, BPRI_MED);
47631708Sstevel 	if (dreq == NULL)
47641708Sstevel 		goto exit;
47651708Sstevel 
47661708Sstevel 	dreq->b_datap->db_type = M_PROTO;
47671708Sstevel 	dlp = (union DL_primitives *)dreq->b_rptr;
47681708Sstevel 	dlp->dl_primitive = DL_DETACH_REQ;
47691708Sstevel 	dreq->b_wptr += DL_DETACH_REQ_SIZE;
47701708Sstevel 
47711708Sstevel 	ubreq = allocb(DL_UNBIND_REQ_SIZE, BPRI_MED);
47721708Sstevel 	if (ubreq == NULL) {
47731708Sstevel 		freemsg(dreq);
47741708Sstevel 		goto exit;
47751708Sstevel 	}
47761708Sstevel 
47771708Sstevel 	ubreq->b_datap->db_type = M_PROTO;
47781708Sstevel 	dlp = (union DL_primitives *)ubreq->b_rptr;
47791708Sstevel 	dlp->dl_primitive = DL_UNBIND_REQ;
47801708Sstevel 	ubreq->b_wptr += DL_UNBIND_REQ_SIZE;
47811708Sstevel 
47821708Sstevel 	ubreq->b_next = dreq;
47831708Sstevel 
47841708Sstevel exit:
47851708Sstevel 
47861708Sstevel 	return (ubreq);
47871708Sstevel }
47881708Sstevel 
47891708Sstevel static mblk_t *
man_dup_mplist(mblk_t * mp)47901708Sstevel man_dup_mplist(mblk_t *mp)
47911708Sstevel {
47921708Sstevel 	mblk_t	*listp = NULL;
47931708Sstevel 	mblk_t	*tailp = NULL;
47941708Sstevel 
47951708Sstevel 	for (; mp != NULL; mp = mp->b_next) {
47961708Sstevel 
47971708Sstevel 		mblk_t	*nmp;
47981708Sstevel 		mblk_t	*prev;
47991708Sstevel 		mblk_t	*next;
48001708Sstevel 
48011708Sstevel 		prev = mp->b_prev;
48021708Sstevel 		next = mp->b_next;
48031708Sstevel 		mp->b_prev = mp->b_next = NULL;
48041708Sstevel 
48051708Sstevel 		nmp = copymsg(mp);
48061708Sstevel 
48071708Sstevel 		mp->b_prev = prev;
48081708Sstevel 		mp->b_next = next;
48091708Sstevel 
48101708Sstevel 		if (nmp == NULL)
48111708Sstevel 			goto nomem;
48121708Sstevel 
48131708Sstevel 		if (listp == NULL) {
48141708Sstevel 			listp = tailp = nmp;
48151708Sstevel 		} else {
48161708Sstevel 			tailp->b_next = nmp;
48171708Sstevel 			tailp = nmp;
48181708Sstevel 		}
48191708Sstevel 	}
48201708Sstevel 
48211708Sstevel 	return (listp);
48221708Sstevel nomem:
48231708Sstevel 
48241708Sstevel 	while (listp) {
48251708Sstevel 		mp = listp;
48261708Sstevel 		listp = mp->b_next;
48271708Sstevel 		mp->b_next = mp->b_prev = NULL;
48281708Sstevel 		freemsg(mp);
48291708Sstevel 	}
48301708Sstevel 
48311708Sstevel 	return (NULL);
48321708Sstevel 
48331708Sstevel }
48341708Sstevel 
48351708Sstevel static mblk_t *
man_alloc_physreq_mp(eaddr_t * man_eap)48361708Sstevel man_alloc_physreq_mp(eaddr_t *man_eap)
48371708Sstevel {
48381708Sstevel 
48391708Sstevel 	mblk_t			*mp;
48401708Sstevel 	union DL_primitives	*dlp;
48411708Sstevel 	t_uscalar_t		off;
48421708Sstevel 	eaddr_t			*eap;
48431708Sstevel 
48441708Sstevel 	mp = allocb(DL_SET_PHYS_ADDR_REQ_SIZE + ETHERADDRL, BPRI_MED);
48451708Sstevel 	if (mp == NULL)
48461708Sstevel 		goto exit;
48471708Sstevel 
48481708Sstevel 	mp->b_datap->db_type = M_PROTO;
48491708Sstevel 	dlp = (union DL_primitives *)mp->b_wptr;
48501708Sstevel 	dlp->set_physaddr_req.dl_primitive = DL_SET_PHYS_ADDR_REQ;
48511708Sstevel 	dlp->set_physaddr_req.dl_addr_length = ETHERADDRL;
48521708Sstevel 	off = DL_SET_PHYS_ADDR_REQ_SIZE;
48531708Sstevel 	dlp->set_physaddr_req.dl_addr_offset =  off;
48541708Sstevel 	mp->b_wptr += DL_SET_PHYS_ADDR_REQ_SIZE + ETHERADDRL;
48551708Sstevel 
48561708Sstevel 	eap = (eaddr_t *)(mp->b_rptr + off);
48571708Sstevel 	ether_copy(man_eap, eap);
48581708Sstevel 
48591708Sstevel exit:
48601708Sstevel 	MAN_DBG(MAN_DLPI, ("man_alloc_physreq: physaddr %s\n",
48617656SSherry.Moore@Sun.COM 	    ether_sprintf(eap)));
48621708Sstevel 
48631708Sstevel 	return (mp);
48641708Sstevel }
48651708Sstevel 
48661708Sstevel /*
48671708Sstevel  * A new path in a pathgroup has become active for the first time. Setup
48681708Sstevel  * the lower destinations in prepartion for man_pg_activate to call
48691708Sstevel  * man_autoswitch.
48701708Sstevel  */
48711708Sstevel static void
man_add_dests(man_pg_t * mpg)48721708Sstevel man_add_dests(man_pg_t *mpg)
48731708Sstevel {
48741708Sstevel 	manstr_t	*msp;
48751708Sstevel 	man_dest_t	*mdp;
48761708Sstevel 
48771708Sstevel 	for (msp = man_strup; msp != NULL; msp = msp->ms_next) {
48781708Sstevel 
48791708Sstevel 		if (!man_str_uses_pg(msp, mpg))
48801708Sstevel 			continue;
48811708Sstevel 
48821708Sstevel 		mdp = &msp->ms_dests[mpg->mpg_pg_id];
48831708Sstevel 
48841708Sstevel /*
48851708Sstevel  * TBD - Take out
48861708Sstevel  *		ASSERT(mdp->md_device.mdev_state == MDEV_UNASSIGNED);
48871708Sstevel  *		ASSERT(mdp->md_state == MAN_DSTATE_NOTPRESENT);
48881708Sstevel  */
48891708Sstevel 		if (mdp->md_device.mdev_state != MDEV_UNASSIGNED) {
48901708Sstevel 			cmn_err(CE_NOTE, "man_add_dests mdev !unassigned");
48911708Sstevel 			MAN_DBGCALL(MAN_PATH, man_print_mdp(mdp));
48921708Sstevel 		}
48931708Sstevel 
48941708Sstevel 		man_start_dest(mdp, msp, mpg);
48951708Sstevel 	}
48961708Sstevel 
48971708Sstevel }
48981708Sstevel 
48991708Sstevel static int
man_remove_dests(man_pg_t * mpg)49001708Sstevel man_remove_dests(man_pg_t *mpg)
49011708Sstevel {
49021708Sstevel 	manstr_t	*msp;
49031708Sstevel 	int		close_cnt = 0;
49041708Sstevel 	man_dest_t	*cdp;
49051708Sstevel 	man_dest_t	*mdp;
49061708Sstevel 	man_dest_t	*tdp;
49071708Sstevel 	man_work_t	*wp;
49081708Sstevel 	mblk_t		*mp;
49091708Sstevel 	int		status = 0;
49101708Sstevel 
49111708Sstevel 	wp = man_work_alloc(MAN_WORK_CLOSE, KM_NOSLEEP);
49121708Sstevel 	if (wp == NULL) {
49131708Sstevel 		status = ENOMEM;
49141708Sstevel 		goto exit;
49151708Sstevel 	}
49161708Sstevel 
49171708Sstevel 	/*
49181708Sstevel 	 * Count up number of destinations we need to close.
49191708Sstevel 	 */
49201708Sstevel 	for (msp = man_strup; msp != NULL; msp = msp->ms_next) {
49211708Sstevel 		if (!man_str_uses_pg(msp, mpg))
49221708Sstevel 			continue;
49231708Sstevel 
49241708Sstevel 		close_cnt++;
49251708Sstevel 	}
49261708Sstevel 
49271708Sstevel 	if (close_cnt == 0)
49281708Sstevel 		goto exit;
49291708Sstevel 
49301708Sstevel 	cdp = man_kzalloc(sizeof (man_dest_t) * close_cnt, KM_NOSLEEP);
49311708Sstevel 	if (cdp == NULL) {
49321708Sstevel 		status = ENOMEM;
49331708Sstevel 		man_work_free(wp);
49341708Sstevel 		goto exit;
49351708Sstevel 	}
49361708Sstevel 
49371708Sstevel 	tdp = cdp;
49381708Sstevel 	for (msp = man_strup; msp != NULL; msp = msp->ms_next) {
49391708Sstevel 		if (!man_str_uses_pg(msp, mpg))
49401708Sstevel 			continue;
49411708Sstevel 
49421708Sstevel 		mdp = &msp->ms_dests[mpg->mpg_pg_id];
49431708Sstevel 
49441708Sstevel 		mdp->md_state |= MAN_DSTATE_CLOSING;
49451708Sstevel 		mdp->md_device.mdev_state = MDEV_UNASSIGNED;
49461708Sstevel 		mdp->md_msp = NULL;
49471708Sstevel 		mdp->md_rq = NULL;
49481708Sstevel 
49491708Sstevel 		/*
49501708Sstevel 		 * Clean up optimized destination pointer if we are
49511708Sstevel 		 * closing it.
49521708Sstevel 		 */
49531708Sstevel 		man_set_optimized_dest(msp);
49541708Sstevel 
49551708Sstevel 		if (mdp->md_lc_timer_id != 0) {
49561708Sstevel 			(void) quntimeout(man_ctl_wq, mdp->md_lc_timer_id);
49571708Sstevel 			mdp->md_lc_timer_id = 0;
49581708Sstevel 		}
49591708Sstevel 		if (mdp->md_bc_id != 0) {
49601708Sstevel 			qunbufcall(man_ctl_wq, mdp->md_bc_id);
49611708Sstevel 			mdp->md_bc_id = 0;
49621708Sstevel 		}
49631708Sstevel 
49641708Sstevel 		mutex_enter(&mdp->md_lock);
49651708Sstevel 		while ((mp = mdp->md_dmp_head) != NULL) {
49661708Sstevel 			mdp->md_dmp_head = mp->b_next;
49671708Sstevel 			mp->b_next = NULL;
49681708Sstevel 			freemsg(mp);
49691708Sstevel 		}
49701708Sstevel 		mdp->md_dmp_count = 0;
49711708Sstevel 		mdp->md_dmp_tail = NULL;
49721708Sstevel 		mutex_exit(&mdp->md_lock);
49731708Sstevel 
49741708Sstevel 		*tdp++ = *mdp;
49751708Sstevel 
49761708Sstevel 		mdp->md_state = MAN_DSTATE_NOTPRESENT;
49771708Sstevel 		mdp->md_muxid = -1;
49781708Sstevel 	}
49791708Sstevel 
49801708Sstevel 	wp->mw_arg.a_mdp = cdp;
49811708Sstevel 	wp->mw_arg.a_ndests = close_cnt;
49821708Sstevel 	man_work_add(man_bwork_q, wp);
49831708Sstevel 
49841708Sstevel exit:
49851708Sstevel 	return (status);
49861708Sstevel 
49871708Sstevel }
49881708Sstevel 
49891708Sstevel /*
49901708Sstevel  * Returns TRUE if stream uses pathgroup, FALSE otherwise.
49911708Sstevel  */
49921708Sstevel static int
man_str_uses_pg(manstr_t * msp,man_pg_t * mpg)49931708Sstevel man_str_uses_pg(manstr_t *msp, man_pg_t *mpg)
49941708Sstevel {
49951708Sstevel 	int	status;
49961708Sstevel 
49971708Sstevel 	status = ((msp->ms_flags & MAN_SFLAG_CONTROL)	||
49987656SSherry.Moore@Sun.COM 	    (msp->ms_dests == NULL)	||
49997656SSherry.Moore@Sun.COM 	    (msp->ms_manp == NULL)	||
50007656SSherry.Moore@Sun.COM 	    (msp->ms_manp->man_meta_ppa != mpg->mpg_man_ppa));
50011708Sstevel 
50021708Sstevel 	return (!status);
50031708Sstevel }
50041708Sstevel 
50051708Sstevel static int
man_gettimer(int timer,man_dest_t * mdp)50061708Sstevel man_gettimer(int timer, man_dest_t *mdp)
50071708Sstevel {
50081708Sstevel 
50091708Sstevel 	int attached = TRUE;
50101708Sstevel 	int time = 0;
50111708Sstevel 
50121708Sstevel 	if (mdp == NULL || mdp->md_msp == NULL || mdp->md_msp->ms_manp == NULL)
50131708Sstevel 		attached = FALSE;
50141708Sstevel 
50151708Sstevel 	switch (timer) {
50161708Sstevel 	case MAN_TIMER_INIT:
50171708Sstevel 		if (attached)
50181708Sstevel 			time = mdp->md_msp->ms_manp->man_init_time;
50191708Sstevel 		else
50201708Sstevel 			time = MAN_INIT_TIME;
50211708Sstevel 		break;
50221708Sstevel 
50231708Sstevel 	case MAN_TIMER_LINKCHECK:
50241708Sstevel 		if (attached) {
50251708Sstevel 			if (mdp->md_linkstate == MAN_LINKSTALE)
50261708Sstevel 				time = mdp->md_msp->ms_manp->man_linkstale_time;
50271708Sstevel 			else
50281708Sstevel 				time = mdp->md_msp->ms_manp->man_linkcheck_time;
50291708Sstevel 		} else
50301708Sstevel 			time = MAN_LINKCHECK_TIME;
50311708Sstevel 		break;
50321708Sstevel 
50331708Sstevel 	case MAN_TIMER_DLPIRESET:
50341708Sstevel 		if (attached)
50351708Sstevel 			time = mdp->md_msp->ms_manp->man_dlpireset_time;
50361708Sstevel 		else
50371708Sstevel 			time = MAN_DLPIRESET_TIME;
50381708Sstevel 		break;
50391708Sstevel 
50401708Sstevel 	default:
50411708Sstevel 		MAN_DBG(MAN_LINK, ("man_gettimer: unknown timer %d", timer));
50421708Sstevel 		time = MAN_LINKCHECK_TIME;
50431708Sstevel 		break;
50441708Sstevel 	}
50451708Sstevel 
50461708Sstevel 	return (drv_usectohz(time));
50471708Sstevel }
50481708Sstevel 
50491708Sstevel /*
50501708Sstevel  * Check the links for each active destination. Called inside inner
50511708Sstevel  * perimeter via qtimeout. This timer only runs on the domain side of the
50521708Sstevel  * driver. It should never run on the SC side.
50531708Sstevel  *
50541708Sstevel  * On a MAN_LINKGOOD link, we check/probe the link health every
50551708Sstevel  * MAN_LINKCHECK_TIME seconds. If the link goes MAN_LINKSTALE, the we probe
50561708Sstevel  * the link every MAN_LINKSTALE_TIME seconds, and fail the link after probing
50571708Sstevel  * the link MAN_LINKSTALE_RETRIES times.
50581708Sstevel  * The man_lock is held to synchronize access pathgroup list(man_pg).
50591708Sstevel  */
50601708Sstevel void
man_linkcheck_timer(void * argp)50611708Sstevel man_linkcheck_timer(void *argp)
50621708Sstevel {
50631708Sstevel 	man_dest_t		*mdp = (man_dest_t *)argp;
50641708Sstevel 	int			restart_timer = TRUE;
50651708Sstevel 	int			send_ping = TRUE;
50661708Sstevel 	int			newstate;
50671708Sstevel 	int			oldstate;
50681708Sstevel 	man_pg_t		*mpg;
50691708Sstevel 	man_path_t		*mp;
50701708Sstevel 
50711708Sstevel 	MAN_DBG(MAN_LINK, ("man_linkcheck_timer: mdp"));
50721708Sstevel 	MAN_DBGCALL(MAN_LINK, man_print_mdp(mdp));
50731708Sstevel 
50741708Sstevel 	/*
50751708Sstevel 	 * Clear timeout id and check if someones waiting on us to
50761708Sstevel 	 * complete a close.
50771708Sstevel 	 */
50781708Sstevel 	mdp->md_lc_timer_id = 0;
50791708Sstevel 
50801708Sstevel 	if (mdp->md_state == MAN_DSTATE_NOTPRESENT ||
50811708Sstevel 	    mdp->md_state & MAN_DSTATE_BUSY) {
50821708Sstevel 
50831708Sstevel 		MAN_DBG(MAN_LINK, ("man_linkcheck_timer: not ready mdp"));
50841708Sstevel 		MAN_DBGCALL(MAN_LINK, man_print_mdp(mdp));
50851708Sstevel 		goto exit;
50861708Sstevel 	}
50871708Sstevel 
50881708Sstevel 	mutex_enter(&man_lock);
50891708Sstevel 	/*
50901708Sstevel 	 * If the lower stream needs initializing, just go straight to
50911708Sstevel 	 * switch code. As the linkcheck timer is started for all
50921708Sstevel 	 * SAPs, do not send ping packets during the initialization.
50931708Sstevel 	 */
50941708Sstevel 	if (mdp->md_state == MAN_DSTATE_INITIALIZING) {
50951708Sstevel 		send_ping = FALSE;
50961708Sstevel 		goto do_switch;
50971708Sstevel 	}
50981708Sstevel 
50991708Sstevel 	newstate = oldstate = mdp->md_linkstate;
51001708Sstevel 
51011708Sstevel 	if (!man_needs_linkcheck(mdp)) {
51021708Sstevel 		cmn_err(CE_NOTE,
51037656SSherry.Moore@Sun.COM 		    "man_linkcheck_timer: unneeded linkcheck on mdp(0x%p)",
51047656SSherry.Moore@Sun.COM 		    (void *)mdp);
51051708Sstevel 		mutex_exit(&man_lock);
51061708Sstevel 		return;
51071708Sstevel 	}
51081708Sstevel 
51091708Sstevel 	/*
51101708Sstevel 	 * The above call to  man_needs_linkcheck() validates
51111708Sstevel 	 * mdp->md_msp and mdp->md_msp->ms_manp pointers.
51121708Sstevel 	 */
51131708Sstevel 	mpg = man_find_pg_by_id(mdp->md_msp->ms_manp->man_pg, mdp->md_pg_id);
51141708Sstevel 	ASSERT(mpg != NULL);
51151708Sstevel 	mp = man_find_path_by_ppa(mpg->mpg_pathp, mdp->md_device.mdev_ppa);
51161708Sstevel 	ASSERT(mp != NULL);
51171708Sstevel 
51181708Sstevel 	/*
51191708Sstevel 	 * This is the most common case, when traffic is flowing.
51201708Sstevel 	 */
51211708Sstevel 	if (mdp->md_rcvcnt != mdp->md_lastrcvcnt) {
51221708Sstevel 
51231708Sstevel 		newstate = MAN_LINKGOOD;
51241708Sstevel 		mdp->md_lastrcvcnt = mdp->md_rcvcnt;
51251708Sstevel 		send_ping = FALSE;
51261708Sstevel 
51271708Sstevel 		/*
51281708Sstevel 		 * Clear the FAILED flag and update lru.
51291708Sstevel 		 */
51301708Sstevel 		mp->mp_device.mdev_state &= ~MDEV_FAILED;
51311708Sstevel 		(void) drv_getparm(TIME, &mp->mp_lru);
51321708Sstevel 
51331708Sstevel 		if (mdp->md_link_updown_msg == MAN_LINK_DOWN_MSG) {
51341708Sstevel 			man_t *manp = mdp->md_msp->ms_manp;
51351708Sstevel 
51361708Sstevel 			cmn_err(CE_NOTE, "%s%d Link up",
51371708Sstevel 			    ddi_major_to_name(manp->man_meta_major),
51381708Sstevel 			    manp->man_meta_ppa);
51391708Sstevel 
51401708Sstevel 			mdp->md_link_updown_msg = MAN_LINK_UP_MSG;
51411708Sstevel 		}
51421708Sstevel 
51431708Sstevel 		goto done;
51441708Sstevel 	}
51451708Sstevel 
51461708Sstevel 	/*
51471708Sstevel 	 * If we're here, it means we have not seen any traffic
51481708Sstevel 	 */
51491708Sstevel 	switch (oldstate) {
51501708Sstevel 	case MAN_LINKINIT:
51511708Sstevel 	case MAN_LINKGOOD:
51521708Sstevel 		newstate = MAN_LINKSTALE;
51531708Sstevel 		mdp->md_linkstales++;
51541708Sstevel 		mdp->md_linkstale_retries =
51557656SSherry.Moore@Sun.COM 		    mdp->md_msp->ms_manp->man_linkstale_retries;
51561708Sstevel 		break;
51571708Sstevel 
51581708Sstevel 	case MAN_LINKSTALE:
51591708Sstevel 	case MAN_LINKFAIL:
51601708Sstevel 		mdp->md_linkstales++;
51611708Sstevel 		mdp->md_linkstale_retries--;
51621708Sstevel 		if (mdp->md_linkstale_retries < 0) {
51631708Sstevel 			newstate = MAN_LINKFAIL;
51641708Sstevel 			mdp->md_linkfails++;
51651708Sstevel 			mdp->md_linkstale_retries =
51667656SSherry.Moore@Sun.COM 			    mdp->md_msp->ms_manp->man_linkstale_retries;
51671708Sstevel 			/*
51681708Sstevel 			 * Mark the destination as FAILED and
51691708Sstevel 			 * update lru.
51701708Sstevel 			 */
51711708Sstevel 			if (oldstate != MAN_LINKFAIL) {
51721708Sstevel 				mp->mp_device.mdev_state |= MDEV_FAILED;
51731708Sstevel 				(void) drv_getparm(TIME, &mp->mp_lru);
51741708Sstevel 			}
51751708Sstevel 		}
51761708Sstevel 		break;
51771708Sstevel 
51781708Sstevel 	default:
51791708Sstevel 		cmn_err(CE_WARN, "man_linkcheck_timer: illegal link"
51807656SSherry.Moore@Sun.COM 		    " state %d", oldstate);
51811708Sstevel 		break;
51821708Sstevel 	}
51831708Sstevel done:
51841708Sstevel 
51851708Sstevel 	if (oldstate != newstate) {
51861708Sstevel 
51871708Sstevel 		MAN_DBG(MAN_LINK, ("man_linkcheck_timer"
51887656SSherry.Moore@Sun.COM 		    " link state %s -> %s", lss[oldstate],
51897656SSherry.Moore@Sun.COM 		    lss[newstate]));
51901708Sstevel 
51911708Sstevel 		mdp->md_linkstate = newstate;
51921708Sstevel 	}
51931708Sstevel 
51941708Sstevel 	/*
51951708Sstevel 	 * Do any work required from state transitions above.
51961708Sstevel 	 */
51971708Sstevel 	if (newstate == MAN_LINKFAIL) {
51981708Sstevel do_switch:
51991708Sstevel 		if (!man_do_autoswitch(mdp)) {
52001708Sstevel 			/*
52011708Sstevel 			 * Stop linkcheck timer until switch completes.
52021708Sstevel 			 */
52031708Sstevel 			restart_timer = FALSE;
52041708Sstevel 			send_ping = FALSE;
52051708Sstevel 		}
52061708Sstevel 	}
52071708Sstevel 
52081708Sstevel 	mutex_exit(&man_lock);
52091708Sstevel 	if (send_ping)
52101708Sstevel 		man_do_icmp_bcast(mdp, mdp->md_msp->ms_sap);
52111708Sstevel 
52121708Sstevel 	if (restart_timer)
52131708Sstevel 		mdp->md_lc_timer_id = qtimeout(man_ctl_wq, man_linkcheck_timer,
52147656SSherry.Moore@Sun.COM 		    (void *)mdp, man_gettimer(MAN_TIMER_LINKCHECK, mdp));
52151708Sstevel 
52161708Sstevel exit:
52171708Sstevel 	MAN_DBG(MAN_LINK, ("man_linkcheck_timer: returns"));
52181708Sstevel 
52191708Sstevel }
52201708Sstevel 
52211708Sstevel /*
52221708Sstevel  * Handle linkcheck initiated autoswitching.
52231708Sstevel  * Called with man_lock held.
52241708Sstevel  */
52251708Sstevel static int
man_do_autoswitch(man_dest_t * mdp)52261708Sstevel man_do_autoswitch(man_dest_t *mdp)
52271708Sstevel {
52281708Sstevel 	man_pg_t	*mpg;
52291708Sstevel 	man_path_t	*ap;
52301708Sstevel 	int		status = 0;
52311708Sstevel 
52321708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
52331708Sstevel 	/*
52341708Sstevel 	 * Set flags and refcnt. Cleared in man_iswitch when SWITCH completes.
52351708Sstevel 	 */
52361708Sstevel 	mdp->md_msp->ms_manp->man_refcnt++;
52371708Sstevel 
52381708Sstevel 	mpg = man_find_pg_by_id(mdp->md_msp->ms_manp->man_pg, mdp->md_pg_id);
52391708Sstevel 	ASSERT(mpg);
52401708Sstevel 
52411708Sstevel 	if (mpg->mpg_flags & MAN_PG_SWITCHING)
52421708Sstevel 		return (EBUSY);
52431708Sstevel 
52441708Sstevel 	mpg->mpg_flags |= MAN_PG_SWITCHING;
52451708Sstevel 
52461708Sstevel 	if (mdp->md_state == MAN_DSTATE_INITIALIZING) {
52471708Sstevel 		/*
52481708Sstevel 		 * We're initializing, ask for a switch to our currently
52491708Sstevel 		 * active device.
52501708Sstevel 		 */
52511708Sstevel 		status = man_autoswitch(mpg, &mdp->md_device, NULL);
52521708Sstevel 	} else {
52531708Sstevel 
52541708Sstevel 		if (mdp->md_msp != NULL && mdp->md_msp->ms_manp != NULL &&
52551708Sstevel 		    mdp->md_link_updown_msg == MAN_LINK_UP_MSG) {
52561708Sstevel 
52571708Sstevel 			man_t *manp = mdp->md_msp->ms_manp;
52581708Sstevel 
52591708Sstevel 			cmn_err(CE_NOTE, "%s%d Link down",
52601708Sstevel 			    ddi_major_to_name(manp->man_meta_major),
52611708Sstevel 			    manp->man_meta_ppa);
52621708Sstevel 		}
52631708Sstevel 		mdp->md_link_updown_msg = MAN_LINK_DOWN_MSG;
52641708Sstevel 
52651708Sstevel 		MAN_DBG(MAN_LINK, ("man_linkcheck_timer: link failure on %s%d",
52667656SSherry.Moore@Sun.COM 		    ddi_major_to_name(mdp->md_device.mdev_major),
52677656SSherry.Moore@Sun.COM 		    mdp->md_device.mdev_ppa));
52681708Sstevel 
52691708Sstevel 		ap = man_find_alternate_path(mpg->mpg_pathp);
52701708Sstevel 
52711708Sstevel 		if (ap == NULL) {
52721708Sstevel 			status = ENODEV;
52731708Sstevel 			goto exit;
52741708Sstevel 		}
52751708Sstevel 		status = man_autoswitch(mpg, &ap->mp_device, NULL);
52761708Sstevel 	}
52771708Sstevel exit:
52781708Sstevel 	if (status != 0) {
52791708Sstevel 		/*
52801708Sstevel 		 * man_iswitch not going to run, clean up.
52811708Sstevel 		 */
52821708Sstevel 		mpg->mpg_flags &= ~MAN_PG_SWITCHING;
52831708Sstevel 		mdp->md_msp->ms_manp->man_refcnt--;
52841708Sstevel 	}
52851708Sstevel 
52861708Sstevel 	return (status);
52871708Sstevel }
52881708Sstevel 
52891708Sstevel /*
52901708Sstevel  * Gather up all lower multiplexor streams that have this link open and
52911708Sstevel  * try to switch them. Called from inner perimeter and holding man_lock.
52921708Sstevel  *
52931708Sstevel  *	pg_id		- Pathgroup to do switch for.
52941708Sstevel  *	st_devp		- New device to switch to.
52951708Sstevel  *	wait_for_switch	- whether or not to qwait for completion.
52961708Sstevel  */
52971708Sstevel static int
man_autoswitch(man_pg_t * mpg,man_dev_t * st_devp,man_work_t * waiter_wp)52981708Sstevel man_autoswitch(man_pg_t *mpg, man_dev_t *st_devp, man_work_t *waiter_wp)
52991708Sstevel {
53001708Sstevel 	man_work_t	*wp;
53011708Sstevel 	int		sdp_cnt = 0;
53021708Sstevel 	man_dest_t	*sdp;
53031708Sstevel 	int		status = 0;
53041708Sstevel 
53051708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
53061708Sstevel 	if (waiter_wp == NULL) {
53071708Sstevel 		wp = man_work_alloc(MAN_WORK_SWITCH, KM_NOSLEEP);
53081708Sstevel 		if (wp == NULL) {
53091708Sstevel 			status = ENOMEM;
53101708Sstevel 			goto exit;
53111708Sstevel 		}
53121708Sstevel 	} else {
53131708Sstevel 		ASSERT(waiter_wp->mw_type == MAN_WORK_SWITCH);
53141708Sstevel 		wp = waiter_wp;
53151708Sstevel 	}
53161708Sstevel 
53171708Sstevel 	/*
53181708Sstevel 	 * Set dests as PLUMBING, cancel timers and return array of dests
53191708Sstevel 	 * that need a switch.
53201708Sstevel 	 */
53211708Sstevel 	status = man_prep_dests_for_switch(mpg, &sdp, &sdp_cnt);
53221708Sstevel 	if (status) {
53231708Sstevel 		if (waiter_wp == NULL)
53241708Sstevel 			man_work_free(wp);
53251708Sstevel 		goto exit;
53261708Sstevel 	}
53271708Sstevel 
53281708Sstevel 	/*
53291708Sstevel 	 * If no streams are active, there are no streams to switch.
53301708Sstevel 	 * Return ENODEV (see man_pg_activate).
53311708Sstevel 	 */
53321708Sstevel 	if (sdp_cnt == 0) {
53331708Sstevel 		if (waiter_wp == NULL)
53341708Sstevel 			man_work_free(wp);
53351708Sstevel 		status = ENODEV;
53361708Sstevel 		goto exit;
53371708Sstevel 	}
53381708Sstevel 
53391708Sstevel 	/*
53401708Sstevel 	 * Ask the bgthread to switch. See man_bwork.
53411708Sstevel 	 */
53421708Sstevel 	wp->mw_arg.a_sf_dev = sdp->md_device;
53431708Sstevel 	wp->mw_arg.a_st_dev = *st_devp;
53441708Sstevel 	wp->mw_arg.a_pg_id = mpg->mpg_pg_id;
53451708Sstevel 	wp->mw_arg.a_man_ppa = mpg->mpg_man_ppa;
53461708Sstevel 
53471708Sstevel 	wp->mw_arg.a_mdp = sdp;
53481708Sstevel 	wp->mw_arg.a_ndests = sdp_cnt;
53491708Sstevel 	man_work_add(man_bwork_q, wp);
53501708Sstevel 
53511708Sstevel exit:
53521708Sstevel 
53531708Sstevel 	return (status);
53541708Sstevel }
53551708Sstevel 
53561708Sstevel /*
53571708Sstevel  * If an alternate path exists for pathgroup, arrange for switch to
53581708Sstevel  * happen. Note that we need to switch each of msp->dests[pg_id], for
53591708Sstevel  * all on man_strup. We must:
53601708Sstevel  *
53611708Sstevel  *		Cancel any timers
53621708Sstevel  *		Mark dests as PLUMBING
53631708Sstevel  *		Submit switch request to man_bwork_q->
53641708Sstevel  */
53651708Sstevel static int
man_prep_dests_for_switch(man_pg_t * mpg,man_dest_t ** mdpp,int * cntp)53661708Sstevel man_prep_dests_for_switch(man_pg_t *mpg, man_dest_t **mdpp, int *cntp)
53671708Sstevel {
53681708Sstevel 	manstr_t	*msp;
53691708Sstevel 	man_dest_t	*mdp;
53701708Sstevel 	int		sdp_cnt = 0;
53711708Sstevel 	man_dest_t	*sdp = NULL;
53721708Sstevel 	man_dest_t	*tdp;
53731708Sstevel 	int		status = 0;
53741708Sstevel 
53751708Sstevel 	MAN_DBG(MAN_SWITCH, ("man_prep_dests_for_switch: pg_id %d",
53767656SSherry.Moore@Sun.COM 	    mpg->mpg_pg_id));
53771708Sstevel 
53781708Sstevel 	/*
53791708Sstevel 	 * Count up number of streams, there is one destination that needs
53801708Sstevel 	 * switching per stream.
53811708Sstevel 	 */
53821708Sstevel 	for (msp = man_strup; msp != NULL; msp = msp->ms_next) {
53831708Sstevel 		if (man_str_uses_pg(msp, mpg))
53841708Sstevel 			sdp_cnt++;
53851708Sstevel 	}
53861708Sstevel 
53871708Sstevel 	if (sdp_cnt == 0)
53881708Sstevel 		goto exit;
53891708Sstevel 
53901708Sstevel 	sdp = man_kzalloc(sizeof (man_dest_t) * sdp_cnt, KM_NOSLEEP);
53911708Sstevel 	if (sdp == NULL) {
53921708Sstevel 		status = ENOMEM;
53931708Sstevel 		goto exit;
53941708Sstevel 	}
53951708Sstevel 	tdp = sdp;
53961708Sstevel 	/*
53971708Sstevel 	 * Mark each destination as unusable.
53981708Sstevel 	 */
53991708Sstevel 	for (msp = man_strup; msp != NULL; msp = msp->ms_next) {
54001708Sstevel 		if (man_str_uses_pg(msp, mpg)) {
54011708Sstevel 
54021708Sstevel 			/*
54031708Sstevel 			 * Mark destination as plumbing and store the
54041708Sstevel 			 * address of sdp as a way to identify the
54051708Sstevel 			 * SWITCH request when it comes back (see man_iswitch).
54061708Sstevel 			 */
54071708Sstevel 			mdp = &msp->ms_dests[mpg->mpg_pg_id];
54081708Sstevel 			mdp->md_state |= MAN_DSTATE_PLUMBING;
54091708Sstevel 			mdp->md_switch_id = sdp;
54101708Sstevel 
54111708Sstevel 			/*
54121708Sstevel 			 * Copy destination info.
54131708Sstevel 			 */
54141708Sstevel 			bcopy(mdp, tdp, sizeof (man_dest_t));
54151708Sstevel 			tdp++;
54161708Sstevel 
54171708Sstevel 			/*
54181708Sstevel 			 * Cancel timers.
54191708Sstevel 			 */
54201708Sstevel 			if (mdp->md_lc_timer_id) {
54211708Sstevel 				(void) quntimeout(man_ctl_wq,
54227656SSherry.Moore@Sun.COM 				    mdp->md_lc_timer_id);
54231708Sstevel 				mdp->md_lc_timer_id = 0;
54241708Sstevel 			}
54251708Sstevel 			if (mdp->md_bc_id) {
54261708Sstevel 				qunbufcall(man_ctl_wq, mdp->md_bc_id);
54271708Sstevel 				mdp->md_bc_id = 0;
54281708Sstevel 			}
54291708Sstevel 		}
54301708Sstevel 	}
54311708Sstevel 
54321708Sstevel 	*mdpp = sdp;
54331708Sstevel 	*cntp = sdp_cnt;
54341708Sstevel 	status = 0;
54351708Sstevel exit:
54361708Sstevel 
54371708Sstevel 	MAN_DBG(MAN_SWITCH, ("man_prep_dests_for_switch: returns %d"
54387656SSherry.Moore@Sun.COM 	    " sdp(0x%p) sdp_cnt(%d)", status, (void *)sdp, sdp_cnt));
54391708Sstevel 
54401708Sstevel 	return (status);
54411708Sstevel 
54421708Sstevel }
54431708Sstevel 
54441708Sstevel /*
54451708Sstevel  * The code below generates an ICMP echo packet and sends it to the
54461708Sstevel  * broadcast address in the hopes that the other end will respond
54471708Sstevel  * and the man_linkcheck_timer logic will see the traffic.
54481708Sstevel  *
54491708Sstevel  * This assumes ethernet-like media.
54501708Sstevel  */
54511708Sstevel /*
54521708Sstevel  * Generate an ICMP packet. Called exclusive inner perimeter.
54531708Sstevel  *
54541708Sstevel  *	mdp - destination to send packet to.
54551708Sstevel  *	sap - either ETHERTYPE_ARP or ETHERTYPE_IPV6
54561708Sstevel  */
54571708Sstevel static void
man_do_icmp_bcast(man_dest_t * mdp,t_uscalar_t sap)54581708Sstevel man_do_icmp_bcast(man_dest_t *mdp, t_uscalar_t sap)
54591708Sstevel {
54601708Sstevel 	mblk_t			*mp = NULL;
54611708Sstevel 
54621708Sstevel 	/* TBD - merge pinger and this routine. */
54631708Sstevel 
54641708Sstevel 	ASSERT(sap == ETHERTYPE_IPV6 || sap == ETHERTYPE_IP);
54651708Sstevel 
54661708Sstevel 	if (sap == ETHERTYPE_IPV6) {
54671708Sstevel 		mdp->md_icmpv6probes++;
54681708Sstevel 	} else {
54691708Sstevel 		mdp->md_icmpv4probes++;
54701708Sstevel 	}
54711708Sstevel 	/*
54721708Sstevel 	 * Send the ICMP message
54731708Sstevel 	 */
54741708Sstevel 	mp = man_pinger(sap);
54751708Sstevel 
54761708Sstevel 	MAN_DBG(MAN_LINK, ("man_do_icmp_bcast: sap=0x%x mp=0x%p",
54777656SSherry.Moore@Sun.COM 	    sap, (void *)mp));
54781708Sstevel 	if (mp == NULL)
54791708Sstevel 		return;
54801708Sstevel 
54811708Sstevel 	/*
54821708Sstevel 	 * Send it out.
54831708Sstevel 	 */
54841708Sstevel 	if (man_start_lower(mdp, mp, NULL, MAN_LOWER)) {
54851708Sstevel 
54861708Sstevel 		MAN_DBG(MAN_LINK, ("man_do_icmp_broadcast: xmit failed"));
54871708Sstevel 
54881708Sstevel 		freemsg(mp);
54891708Sstevel 	}
54901708Sstevel 
54911708Sstevel }
54921708Sstevel 
54931708Sstevel static mblk_t *
man_pinger(t_uscalar_t sap)54941708Sstevel man_pinger(t_uscalar_t sap)
54951708Sstevel {
54961708Sstevel 	mblk_t		*mp = NULL;
54971708Sstevel 	man_dladdr_t	dlsap;
54981708Sstevel 	icmph_t		*icmph;
54991708Sstevel 	int		ipver;
55001708Sstevel 	ipha_t		*ipha;
55011708Sstevel 	ip6_t		*ip6h;
55021708Sstevel 	int		iph_hdr_len;
55031708Sstevel 	int		datalen = 64;
55041708Sstevel 	uchar_t		*datap;
55051708Sstevel 	uint16_t	size;
55061708Sstevel 	uchar_t		i;
55071708Sstevel 
55081708Sstevel 	dlsap.dl_sap = htons(sap);
55091708Sstevel 	bcopy(&etherbroadcast, &dlsap.dl_phys, sizeof (dlsap.dl_phys));
55101708Sstevel 
55111708Sstevel 	if (sap == ETHERTYPE_IPV6) {
55121708Sstevel 		ipver = IPV6_VERSION;
55131708Sstevel 		iph_hdr_len = sizeof (ip6_t);
55141708Sstevel 		size = ICMP6_MINLEN;
55151708Sstevel 	} else {
55161708Sstevel 		ipver = IPV4_VERSION;
55171708Sstevel 		iph_hdr_len = sizeof (ipha_t);
55181708Sstevel 		size = ICMPH_SIZE;
55191708Sstevel 	}
55201708Sstevel 	size += (uint16_t)iph_hdr_len;
55211708Sstevel 	size += datalen;
55221708Sstevel 
55231708Sstevel 	mp = man_alloc_udreq(size, &dlsap);
55241708Sstevel 	if (mp == NULL)
55251708Sstevel 		goto exit;
55261708Sstevel 
55271708Sstevel 	/*
55281708Sstevel 	 * fill out the ICMP echo packet headers
55291708Sstevel 	 */
55301708Sstevel 	mp->b_cont->b_wptr += iph_hdr_len;
55311708Sstevel 	if (ipver == IPV4_VERSION) {
55321708Sstevel 		ipha = (ipha_t *)mp->b_cont->b_rptr;
55331708Sstevel 		ipha->ipha_version_and_hdr_length = (IP_VERSION << 4)
55347656SSherry.Moore@Sun.COM 		    | IP_SIMPLE_HDR_LENGTH_IN_WORDS;
55351708Sstevel 		ipha->ipha_type_of_service = 0;
55361708Sstevel 		ipha->ipha_length = size;
55371708Sstevel 		ipha->ipha_fragment_offset_and_flags = IPH_DF;
55381708Sstevel 		ipha->ipha_ttl = 1;
55391708Sstevel 		ipha->ipha_protocol = IPPROTO_ICMP;
55401708Sstevel 		if (man_is_on_domain) {
55411708Sstevel 			manc_t		manc;
55421708Sstevel 
55431708Sstevel 			if (man_get_iosram(&manc)) {
55441708Sstevel 				freemsg(mp);
55451708Sstevel 				mp = NULL;
55461708Sstevel 				goto exit;
55471708Sstevel 			}
55481708Sstevel 
55491708Sstevel 			/*
55501708Sstevel 			 * Domain generates ping packets for domain to
55511708Sstevel 			 * SC network (dman0 <--> scman0).
55521708Sstevel 			 */
55531708Sstevel 			ipha->ipha_dst = manc.manc_sc_ipaddr;
55541708Sstevel 			ipha->ipha_src = manc.manc_dom_ipaddr;
55551708Sstevel 		} else {
55561708Sstevel 			/*
55571708Sstevel 			 * Note that ping packets are only generated
55581708Sstevel 			 * by the SC across scman1 (SC to SC network).
55591708Sstevel 			 */
55601708Sstevel 			ipha->ipha_dst = man_sc_ipaddrs.ip_other_sc_ipaddr;
55611708Sstevel 			ipha->ipha_src = man_sc_ipaddrs.ip_my_sc_ipaddr;
55621708Sstevel 		}
55631708Sstevel 
55641708Sstevel 		ipha->ipha_ident = 0;
55651708Sstevel 
55661708Sstevel 		ipha->ipha_hdr_checksum = 0;
55671708Sstevel 		ipha->ipha_hdr_checksum = IP_CSUM(mp->b_cont, 0, 0);
55681708Sstevel 
55691708Sstevel 	} else {
55701708Sstevel 		ip6h = (ip6_t *)mp->b_cont->b_rptr;
55711708Sstevel 		/*
55721708Sstevel 		 * IP version = 6, priority = 0, flow = 0
55731708Sstevel 		 */
55741708Sstevel 		ip6h->ip6_flow = (IPV6_VERSION << 28);
55751708Sstevel 		ip6h->ip6_plen =
55767656SSherry.Moore@Sun.COM 		    htons((short)(size - iph_hdr_len));
55771708Sstevel 		ip6h->ip6_nxt = IPPROTO_ICMPV6;
55781708Sstevel 		ip6h->ip6_hlim = 1;	/* stay on link */
55791708Sstevel 
55801708Sstevel 		if (man_is_on_domain) {
55811708Sstevel 			manc_t		manc;
55821708Sstevel 
55831708Sstevel 			if (man_get_iosram(&manc)) {
55841708Sstevel 				freemsg(mp);
55851708Sstevel 				mp = NULL;
55861708Sstevel 				goto exit;
55871708Sstevel 			}
55881708Sstevel 
55891708Sstevel 			/*
55901708Sstevel 			 * Domain generates ping packets for domain to
55911708Sstevel 			 * SC network (dman0 <--> scman0).
55921708Sstevel 			 */
55931708Sstevel 			ip6h->ip6_src = manc.manc_dom_ipv6addr;
55941708Sstevel 			ip6h->ip6_dst = manc.manc_sc_ipv6addr;
55951708Sstevel 		} else {
55961708Sstevel 			/*
55971708Sstevel 			 * Note that ping packets are only generated
55981708Sstevel 			 * by the SC across scman1 (SC to SC network).
55991708Sstevel 			 */
56001708Sstevel 			ip6h->ip6_src = man_sc_ip6addrs.ip6_my_sc_ipaddr;
56011708Sstevel 			ip6h->ip6_dst = man_sc_ip6addrs.ip6_other_sc_ipaddr;
56021708Sstevel 		}
56031708Sstevel 	}
56041708Sstevel 
56051708Sstevel 	/*
56061708Sstevel 	 * IPv6 and IP are the same for ICMP as far as I'm concerned.
56071708Sstevel 	 */
56081708Sstevel 	icmph = (icmph_t *)mp->b_cont->b_wptr;
56091708Sstevel 	if (ipver == IPV4_VERSION) {
56101708Sstevel 		mp->b_cont->b_wptr += ICMPH_SIZE;
56111708Sstevel 		icmph->icmph_type = ICMP_ECHO_REQUEST;
56121708Sstevel 		icmph->icmph_code = 0;
56131708Sstevel 	} else {
56141708Sstevel 		mp->b_cont->b_wptr += ICMP6_MINLEN;
56151708Sstevel 		icmph->icmph_type = ICMP6_ECHO_REQUEST;
56161708Sstevel 		icmph->icmph_code = 0;
56171708Sstevel 	}
56181708Sstevel 
56191708Sstevel 	datap = mp->b_cont->b_wptr;
56201708Sstevel 	mp->b_cont->b_wptr += datalen;
56211708Sstevel 
56221708Sstevel 	for (i = 0; i < datalen; i++)
56231708Sstevel 		*datap++ = i;
56241708Sstevel 
56251708Sstevel 	if (ipver == IPV4_VERSION) {
56261708Sstevel 		icmph->icmph_checksum = IP_CSUM(mp->b_cont, iph_hdr_len, 0);
56271708Sstevel 	} else {
56281708Sstevel 		uint32_t	sum;
56291708Sstevel 
56301708Sstevel 		sum = htons(IPPROTO_ICMPV6) + ip6h->ip6_plen;
56311708Sstevel 		icmph->icmph_checksum = IP_CSUM(mp->b_cont, iph_hdr_len - 32,
56327656SSherry.Moore@Sun.COM 		    (sum & 0xffff) + (sum >> 16));
56331708Sstevel 	}
56341708Sstevel 
56351708Sstevel /*
56361708Sstevel  * TBD
56371708Sstevel  *	icp->icmp_time =  ???;
56381708Sstevel  */
56391708Sstevel 
56401708Sstevel exit:
56411708Sstevel 	return (mp);
56421708Sstevel }
56431708Sstevel 
56441708Sstevel static mblk_t *
man_alloc_udreq(int size,man_dladdr_t * dlsap)56451708Sstevel man_alloc_udreq(int size, man_dladdr_t *dlsap)
56461708Sstevel {
56471708Sstevel 	dl_unitdata_req_t	*udreq;
56481708Sstevel 	mblk_t			*bp;
56491708Sstevel 	mblk_t			*mp;
56501708Sstevel 
56511708Sstevel 	mp = allocb(sizeof (dl_unitdata_req_t) + sizeof (*dlsap), BPRI_MED);
56521708Sstevel 
56531708Sstevel 	if (mp == NULL) {
56541708Sstevel 		cmn_err(CE_NOTE, "man_preparepkt: allocb failed");
56551708Sstevel 		return (NULL);
56561708Sstevel 	}
56571708Sstevel 
56581708Sstevel 	if ((bp = allocb(size, BPRI_MED)) == NULL) {
56591708Sstevel 		freemsg(mp);
56601708Sstevel 		cmn_err(CE_NOTE, "man_preparepkts: allocb failed");
56611708Sstevel 		return (NULL);
56621708Sstevel 	}
56631708Sstevel 	bzero(bp->b_rptr, size);
56641708Sstevel 
56651708Sstevel 	mp->b_cont = bp;
56661708Sstevel 	mp->b_datap->db_type = M_PROTO;
56671708Sstevel 	udreq = (dl_unitdata_req_t *)mp->b_wptr;
56681708Sstevel 	mp->b_wptr += sizeof (dl_unitdata_req_t);
56691708Sstevel 
56701708Sstevel 	/*
56711708Sstevel 	 * phys addr first - TBD
56721708Sstevel 	 */
56731708Sstevel 	bcopy((char *)dlsap, mp->b_wptr, sizeof (*dlsap));
56741708Sstevel 	mp->b_wptr += sizeof (*dlsap);
56751708Sstevel 
56761708Sstevel 	udreq->dl_primitive = DL_UNITDATA_REQ;
56771708Sstevel 	udreq->dl_dest_addr_length = sizeof (*dlsap);
56781708Sstevel 	udreq->dl_dest_addr_offset = sizeof (*udreq);
56791708Sstevel 	udreq->dl_priority.dl_min = 0;
56801708Sstevel 	udreq->dl_priority.dl_max = 0;
56811708Sstevel 
56821708Sstevel 	return (mp);
56831708Sstevel }
56841708Sstevel 
56851708Sstevel 
56861708Sstevel /*
56871708Sstevel  * The routines in this file are executed by the MAN background thread,
56881708Sstevel  * which executes outside of the STREAMS framework (see man_str.c). It is
56891708Sstevel  * allowed to do the things required to modify the STREAMS driver (things
56901708Sstevel  * that are normally done from a user process). These routines do things like
56911708Sstevel  * open and close drivers, PLINK and PUNLINK streams to/from the multiplexor,
56921708Sstevel  * etc.
56931708Sstevel  *
56941708Sstevel  * The mechanism of communication between the STREAMS portion of the driver
56951708Sstevel  * and the background thread portion are two work queues, man_bwork_q
56961708Sstevel  * and man_iwork_q (background work q and streams work q).  Work
56971708Sstevel  * requests are placed on those queues when one half of the driver wants
56981708Sstevel  * the other half to do some work for it.
56991708Sstevel  *
57001708Sstevel  * The MAN background thread executes the man_bwork routine. Its sole
57011708Sstevel  * job is to process work requests placed on this work q. The MAN upper
57021708Sstevel  * write service routine is responsible for processing work requests posted
57031708Sstevel  * to the man_iwork_q->
57041708Sstevel  *
57051708Sstevel  * Both work queues are protected by the global mutex man_lock. The
57061708Sstevel  * man_bwork is signalged via the condvarman_bwork_q->q_cv. The man_uwsrv
57071708Sstevel  * routine is signaled by calling qenable (forcing man_uwsrv to run).
57081708Sstevel  */
57091708Sstevel 
57101708Sstevel /*
57111708Sstevel  * man_bwork - Work thread for this device.  It is responsible for
57121708Sstevel  * performing operations which can't occur within the STREAMS framework.
57131708Sstevel  *
57141708Sstevel  * Locking:
57151708Sstevel  *	- Called holding no locks
57161708Sstevel  *	- Obtains the global mutex man_lock to remove work from
57171708Sstevel  *	  man_bwork_q, and post work to man_iwork_q->
57181708Sstevel  *	- Note that we do not want to hold any locks when making
57191708Sstevel  *	  any ldi_ calls.
57201708Sstevel  */
57211708Sstevel void
man_bwork()57221708Sstevel man_bwork()
57231708Sstevel {
57241708Sstevel 	man_work_t	*wp;
57251708Sstevel 	int		done = 0;
57261708Sstevel 	callb_cpr_t	cprinfo;
57271708Sstevel 	int		wp_finished;
57281708Sstevel 
57291708Sstevel 	CALLB_CPR_INIT(&cprinfo, &man_lock, callb_generic_cpr,
57301708Sstevel 	    "mn_work_thrd");
57311708Sstevel 
57321708Sstevel 	MAN_DBG(MAN_CONFIG, ("man_bwork: enter"));
57331708Sstevel 
57341708Sstevel 	while (done == 0) {
57351708Sstevel 
57361708Sstevel 		mutex_enter(&man_lock);
57371708Sstevel 		/*
57381708Sstevel 		 * While there is nothing to do, sit in cv_wait.  If work
57391708Sstevel 		 * request is made, requester will signal.
57401708Sstevel 		 */
57411708Sstevel 		while (man_bwork_q->q_work == NULL) {
57421708Sstevel 
57431708Sstevel 			CALLB_CPR_SAFE_BEGIN(&cprinfo);
57441708Sstevel 
57451708Sstevel 			cv_wait(&man_bwork_q->q_cv, &man_lock);
57461708Sstevel 
57471708Sstevel 			CALLB_CPR_SAFE_END(&cprinfo, &man_lock);
57481708Sstevel 		}
57491708Sstevel 
57501708Sstevel 		wp = man_bwork_q->q_work;
57511708Sstevel 		man_bwork_q->q_work = wp->mw_next;
57521708Sstevel 		wp->mw_next = NULL;
57531708Sstevel 		mutex_exit(&man_lock);
57541708Sstevel 
57551708Sstevel 		wp_finished = TRUE;
57561708Sstevel 
57571708Sstevel 		MAN_DBG(MAN_SWITCH, ("man_bwork: type %s",
57587656SSherry.Moore@Sun.COM 		    _mw_type[wp->mw_type]));
57591708Sstevel 
57601708Sstevel 		switch (wp->mw_type) {
57611708Sstevel 		case MAN_WORK_OPEN_CTL:
57621708Sstevel 			wp->mw_status = man_open_ctl();
57631708Sstevel 			break;
57641708Sstevel 
57651708Sstevel 		case MAN_WORK_CLOSE_CTL:
57661708Sstevel 			man_close_ctl();
57671708Sstevel 			break;
57681708Sstevel 
57691708Sstevel 		case MAN_WORK_CLOSE:
57701708Sstevel 		case MAN_WORK_CLOSE_STREAM:
57711708Sstevel 			man_bclose(&wp->mw_arg);
57721708Sstevel 			break;
57731708Sstevel 
57741708Sstevel 		case MAN_WORK_SWITCH:
57751708Sstevel 			man_bswitch(&wp->mw_arg, wp);
57761708Sstevel 			wp_finished = FALSE;
57771708Sstevel 			break;
57781708Sstevel 
57791708Sstevel 		case MAN_WORK_STOP:		/* man_bwork_stop() */
57801708Sstevel 			done = 1;
57811708Sstevel 			mutex_enter(&man_lock);
57821708Sstevel 			CALLB_CPR_EXIT(&cprinfo); /* Unlocks man_lock */
57831708Sstevel 			break;
57841708Sstevel 
57851708Sstevel 		default:
57861708Sstevel 			cmn_err(CE_WARN, "man_bwork: "
57871708Sstevel 			    "illegal work type(%d)", wp->mw_type);
57881708Sstevel 			break;
57891708Sstevel 		}
57901708Sstevel 
57911708Sstevel 		mutex_enter(&man_lock);
57921708Sstevel 
57931708Sstevel 		if (wp_finished) {
57941708Sstevel 			wp->mw_flags |= MAN_WFLAGS_DONE;
57951708Sstevel 			if (wp->mw_flags & MAN_WFLAGS_CVWAITER)
57961708Sstevel 				cv_signal(&wp->mw_cv);
57971708Sstevel 			else if (wp->mw_flags & MAN_WFLAGS_QWAITER)
57981708Sstevel 				qenable(wp->mw_q);
57991708Sstevel 			else
58001708Sstevel 				man_work_free(wp);
58011708Sstevel 		}
58021708Sstevel 
58031708Sstevel 		mutex_exit(&man_lock);
58041708Sstevel 	}
58051708Sstevel 
58061708Sstevel 	MAN_DBG(MAN_CONFIG, ("man_bwork: thread_exit"));
58071708Sstevel 
58081708Sstevel 	mutex_enter(&man_lock);
58091708Sstevel 	man_bwork_id = NULL;
58101708Sstevel 	mutex_exit(&man_lock);
58111708Sstevel 
58121708Sstevel 	thread_exit();
58131708Sstevel }
58141708Sstevel 
58151708Sstevel /*
58161708Sstevel  * man_open_ctl - Open the control stream.
58171708Sstevel  *
58181708Sstevel  *	returns	- success - 0
58191708Sstevel  *		- failure - errno code
58201708Sstevel  *
58211708Sstevel  * Mutex Locking Notes:
58221708Sstevel  *	We need a way to keep the CLONE_OPEN qwaiters in man_open from
58231708Sstevel  *	checking the man_config variables after the ldi_open call below
58241708Sstevel  *	returns from man_open, leaving the inner perimeter. So, we use the
58251708Sstevel  *	man_lock to synchronize the threads in man_open_ctl and man_open.  We
58261708Sstevel  *	hold man_lock across this call into man_open, which in general is a
58271708Sstevel  *	no-no. But, the STREAMs portion of the driver (other than open)
58281708Sstevel  *	doesn't use it. So, if ldi_open gets hijacked to run any part of
58291708Sstevel  *	the MAN streams driver, it wont end up recursively trying to acquire
58301708Sstevel  *	man_lock. Note that the non-CLONE_OPEN portion of man_open doesnt
58311708Sstevel  *	acquire it either, so again no recursive mutex.
58321708Sstevel  */
58331708Sstevel static int
man_open_ctl()58341708Sstevel man_open_ctl()
58351708Sstevel {
58361708Sstevel 	int		status = 0;
58371708Sstevel 	ldi_handle_t	ctl_lh = NULL;
58381708Sstevel 	ldi_ident_t	li = NULL;
58391708Sstevel 
58401708Sstevel 	MAN_DBG(MAN_CONFIG, ("man_open_ctl: plumbing control stream\n"));
58411708Sstevel 
58421708Sstevel 	/*
58431708Sstevel 	 * Get eri driver loaded and kstats initialized. Is there a better
58441708Sstevel 	 * way to do this? - TBD.
58451708Sstevel 	 */
58461708Sstevel 	status = ldi_ident_from_mod(&modlinkage, &li);
58471708Sstevel 	if (status) {
58481708Sstevel 		cmn_err(CE_WARN,
58497656SSherry.Moore@Sun.COM 		    "man_open_ctl: ident alloc failed, error %d", status);
58501708Sstevel 		goto exit;
58511708Sstevel 	}
58521708Sstevel 
58531708Sstevel 	status = ldi_open_by_name(ERI_PATH, FREAD | FWRITE | FNOCTTY,
58541708Sstevel 	    kcred, &ctl_lh, li);
58551708Sstevel 	if (status) {
58561708Sstevel 		cmn_err(CE_WARN,
58577656SSherry.Moore@Sun.COM 		    "man_open_ctl: eri open failed, error %d", status);
58581708Sstevel 		ctl_lh = NULL;
58591708Sstevel 		goto exit;
58601708Sstevel 	}
58611708Sstevel 	(void) ldi_close(ctl_lh, NULL, kcred);
58621708Sstevel 	ctl_lh = NULL;
58631708Sstevel 
58641708Sstevel 	mutex_enter(&man_lock);
58651708Sstevel 
58661708Sstevel 	if (man_ctl_lh != NULL) {
58671708Sstevel 		mutex_exit(&man_lock);
58681708Sstevel 		goto exit;
58691708Sstevel 	}
58701708Sstevel 
58711708Sstevel 	ASSERT(man_ctl_wq == NULL);
58721708Sstevel 	mutex_exit(&man_lock);
58731708Sstevel 
58741708Sstevel 	status = ldi_open_by_name(DMAN_INT_PATH, FREAD | FWRITE | FNOCTTY,
58751708Sstevel 	    kcred, &ctl_lh, li);
58761708Sstevel 	if (status) {
58771708Sstevel 		cmn_err(CE_WARN,
58787656SSherry.Moore@Sun.COM 		    "man_open_ctl: man control dev open failed, "
58797656SSherry.Moore@Sun.COM 		    "error %d", status);
58801708Sstevel 		goto exit;
58811708Sstevel 	}
58821708Sstevel 
58831708Sstevel 	/*
58841708Sstevel 	 * Update global config state. TBD - dont need lock here, since
58851708Sstevel 	 * everyone is stuck in open until we finish. Only other modifier
58861708Sstevel 	 * is man_deconfigure via _fini, which returns EBUSY if there is
58871708Sstevel 	 * any open streams (other than control). Do need to signal qwaiters
58881708Sstevel 	 * on error.
58891708Sstevel 	 */
58901708Sstevel 	mutex_enter(&man_lock);
58911708Sstevel 	ASSERT(man_config_state == MAN_CONFIGURING);
58921708Sstevel 	ASSERT(man_ctl_lh == NULL);
58931708Sstevel 	man_ctl_lh = ctl_lh;
58941708Sstevel 	mutex_exit(&man_lock);
58951708Sstevel 
58961708Sstevel exit:
58971708Sstevel 	if (li)
58981708Sstevel 		ldi_ident_release(li);
58991708Sstevel 
59001708Sstevel 	MAN_DBG(MAN_CONFIG, ("man_open_ctl: man_ctl_lh(0x%p) errno = %d\n",
59017656SSherry.Moore@Sun.COM 	    (void *)man_ctl_lh, status));
59021708Sstevel 
59031708Sstevel 	return (status);
59041708Sstevel }
59051708Sstevel 
59061708Sstevel /*
59071708Sstevel  * man_close_ctl - Close control stream, we are about to unload driver.
59081708Sstevel  *
59091708Sstevel  * Locking:
59101708Sstevel  *	- Called holding no locks.
59111708Sstevel  */
59121708Sstevel static void
man_close_ctl()59131708Sstevel man_close_ctl()
59141708Sstevel {
59151708Sstevel 	ldi_handle_t tlh;
59161708Sstevel 
59171708Sstevel 	MAN_DBG(MAN_CONFIG, ("man_close_ctl: unplumbing control stream\n"));
59181708Sstevel 
59191708Sstevel 	mutex_enter(&man_lock);
59201708Sstevel 	if ((tlh = man_ctl_lh) != NULL)
59211708Sstevel 		man_ctl_lh = NULL;
59221708Sstevel 	mutex_exit(&man_lock);
59231708Sstevel 
59241708Sstevel 	if (tlh != NULL) {
59251708Sstevel 		(void) ldi_close(tlh, NULL, kcred);
59261708Sstevel 	}
59271708Sstevel 
59281708Sstevel }
59291708Sstevel 
59301708Sstevel /*
59311708Sstevel  * Close the lower streams. Get all the timers canceled, close the lower
59321708Sstevel  * stream and delete the dest array.
59331708Sstevel  *
59341708Sstevel  * Returns:
59351708Sstevel  *	0	Closed all streams.
59361708Sstevel  *	1	Couldn't close one or more streams, timers still running.
59371708Sstevel  *
59381708Sstevel  * Locking:
59391708Sstevel  *	- Called holding no locks.
59401708Sstevel  */
59411708Sstevel static void
man_bclose(man_adest_t * adp)59421708Sstevel man_bclose(man_adest_t *adp)
59431708Sstevel {
59441708Sstevel 	int		i;
59451708Sstevel 	man_dest_t	*mdp;
59461708Sstevel 
59471708Sstevel 	man_cancel_timers(adp);
59481708Sstevel 
59491708Sstevel 	for (i = 0; i < adp->a_ndests; i++) {
59501708Sstevel 		mdp = &adp->a_mdp[i];
59511708Sstevel 
59521708Sstevel 		if (mdp->md_muxid != -1)
59531708Sstevel 			man_unplumb(mdp);
59541708Sstevel 	}
59551708Sstevel 
59561708Sstevel 	mutex_destroy(&mdp->md_lock);
59571708Sstevel 	man_kfree(adp->a_mdp, sizeof (man_dest_t) * adp->a_ndests);
59581708Sstevel 	adp->a_mdp = NULL;
59591708Sstevel }
59601708Sstevel 
59611708Sstevel /*
59621708Sstevel  * We want to close down all lower streams. Need to wait until all
59631708Sstevel  * timers and work related to these lower streams is quiesced.
59641708Sstevel  *
59651708Sstevel  * Returns 1 if lower streams are quiesced, 0 if we need to wait
59661708Sstevel  * a bit longer.
59671708Sstevel  */
59681708Sstevel static void
man_cancel_timers(man_adest_t * adp)59691708Sstevel man_cancel_timers(man_adest_t *adp)
59701708Sstevel {
59711708Sstevel 	man_dest_t	*mdp;
59721708Sstevel 	int		cnt;
59731708Sstevel 	int		i;
59741708Sstevel 
59751708Sstevel 	mdp = adp->a_mdp;
59761708Sstevel 	cnt = adp->a_ndests;
59771708Sstevel 
59781708Sstevel 	MAN_DBG(MAN_SWITCH, ("man_cancel_timers: mdp(0x%p) cnt %d",
59797656SSherry.Moore@Sun.COM 	    (void *)mdp, cnt));
59801708Sstevel 
59811708Sstevel 	for (i = 0; i < cnt; i++) {
59821708Sstevel 
59831708Sstevel 		if (mdp[i].md_lc_timer_id != 0) {
59841708Sstevel 			(void) quntimeout(man_ctl_wq, mdp[i].md_lc_timer_id);
59851708Sstevel 			mdp[i].md_lc_timer_id = 0;
59861708Sstevel 		}
59871708Sstevel 
59881708Sstevel 		if (mdp[i].md_bc_id != 0) {
59891708Sstevel 			qunbufcall(man_ctl_wq, mdp[i].md_bc_id);
59901708Sstevel 			mdp[i].md_bc_id = 0;
59911708Sstevel 		}
59921708Sstevel 	}
59931708Sstevel 
59941708Sstevel 	MAN_DBG(MAN_SWITCH, ("man_cancel_timers: returns"));
59951708Sstevel }
59961708Sstevel 
59971708Sstevel /*
59981708Sstevel  * A failover is started at start of day, when the driver detects a
59991708Sstevel  * link failure (see man_linkcheck_timer), or when DR detaches
60001708Sstevel  * the IO board containing the current active link between SC and
60011708Sstevel  * domain (see man_dr_detach, man_iwork, and man_do_dr_detach). A
60021708Sstevel  * MAN_WORK_SWITCH work request containing all the lower streams that
60031708Sstevel  * should be switched is posted on the man_bwork_q-> This work request is
60041708Sstevel  * processed here. Once all lower streams have been switched to an
60051708Sstevel  * alternate path, the MAN_WORK_SWITCH work request is passed back to
60061708Sstevel  * man_iwork_q where it is processed within the inner perimeter of the
60071708Sstevel  * STREAMS framework (see man_iswitch).
60081708Sstevel  *
60091708Sstevel  * Note that when the switch fails for whatever reason, we just hand
60101708Sstevel  * back the lower streams untouched and let another failover happen.
60111708Sstevel  * Hopefully we will sooner or later succeed at the failover.
60121708Sstevel  */
60131708Sstevel static void
man_bswitch(man_adest_t * adp,man_work_t * wp)60141708Sstevel man_bswitch(man_adest_t *adp, man_work_t *wp)
60151708Sstevel {
60161708Sstevel 	man_dest_t	*tdp;
60171708Sstevel 	man_t		*manp;
60181708Sstevel 	int		i;
60191708Sstevel 	int		status = 0;
60201708Sstevel 
60211708Sstevel 	/*
60221708Sstevel 	 * Make a temporary copy of dest array, updating device to the
60231708Sstevel 	 * alternate and try to open all lower streams. bgthread can sleep.
60241708Sstevel 	 */
60251708Sstevel 
60261708Sstevel 	tdp = man_kzalloc(sizeof (man_dest_t) * adp->a_ndests,
60277656SSherry.Moore@Sun.COM 	    KM_SLEEP);
60281708Sstevel 	bcopy(adp->a_mdp, tdp, sizeof (man_dest_t) * adp->a_ndests);
60291708Sstevel 
60301708Sstevel 	/*
60311708Sstevel 	 * Before we switch to the new path, lets sync the kstats.
60321708Sstevel 	 */
60331708Sstevel 	mutex_enter(&man_lock);
60341708Sstevel 
60351708Sstevel 	manp = ddi_get_soft_state(man_softstate, adp->a_man_ppa);
60361708Sstevel 	if (manp != NULL) {
60371708Sstevel 		man_update_path_kstats(manp);
60381708Sstevel 	} else
60391708Sstevel 		status = ENODEV;
60401708Sstevel 
60411708Sstevel 	mutex_exit(&man_lock);
60421708Sstevel 
60431708Sstevel 	if (status != 0)
60441708Sstevel 		goto exit;
60451708Sstevel 
60461708Sstevel 	for (i = 0; i < adp->a_ndests; i++) {
60471708Sstevel 
60481708Sstevel 		tdp[i].md_device = adp->a_st_dev;
60491708Sstevel 		tdp[i].md_muxid = -1;
60501708Sstevel 
60511708Sstevel 		if (man_plumb(&tdp[i]))
60521708Sstevel 			break;
60531708Sstevel 	}
60541708Sstevel 
60551708Sstevel 	/*
60561708Sstevel 	 * Didn't plumb everyone, unplumb new lower stuff and return.
60571708Sstevel 	 */
60581708Sstevel 	if (i < adp->a_ndests) {
60591708Sstevel 		int	j;
60601708Sstevel 
60611708Sstevel 		for (j = 0; j <= i; j++)
60621708Sstevel 			man_unplumb(&tdp[j]);
60631708Sstevel 		status = EAGAIN;
60641708Sstevel 		goto exit;
60651708Sstevel 	}
60661708Sstevel 
60671708Sstevel 	if (man_is_on_domain && man_dossc_switch(adp->a_st_dev.mdev_exp_id)) {
60681708Sstevel 		/*
60691708Sstevel 		 * If we cant set new path on the SSC, then fail the
60701708Sstevel 		 * failover.
60711708Sstevel 		 */
60721708Sstevel 		for (i = 0; i < adp->a_ndests; i++)
60731708Sstevel 			man_unplumb(&tdp[i]);
60741708Sstevel 		status = EAGAIN;
60751708Sstevel 		goto exit;
60761708Sstevel 	}
60771708Sstevel 
60781708Sstevel 	man_kfree(adp->a_mdp, sizeof (man_dest_t) * adp->a_ndests);
60791708Sstevel 	adp->a_mdp = tdp;
60801708Sstevel 
60811708Sstevel exit:
60821708Sstevel 	if (status)
60831708Sstevel 		man_kfree(tdp, sizeof (man_dest_t) * adp->a_ndests);
60841708Sstevel 
60851708Sstevel 
60861708Sstevel 	MAN_DBG(MAN_SWITCH, ("man_bswitch: returns %d", status));
60871708Sstevel 
60881708Sstevel 	/*
60891708Sstevel 	 * Hand processed switch request back to man_iwork for
60901708Sstevel 	 * processing in man_iswitch.
60911708Sstevel 	 */
60921708Sstevel 	wp->mw_status = status;
60931708Sstevel 
60941708Sstevel 	mutex_enter(&man_lock);
60951708Sstevel 	man_work_add(man_iwork_q, wp);
60961708Sstevel 	mutex_exit(&man_lock);
60971708Sstevel 
60981708Sstevel }
60991708Sstevel 
61001708Sstevel /*
61011708Sstevel  * man_plumb - Configure a lower stream for this destination.
61021708Sstevel  *
61031708Sstevel  * Locking:
61041708Sstevel  * 	- Called holding no locks.
61051708Sstevel  *
61061708Sstevel  * Returns:
61071708Sstevel  *	- success - 0
61081708Sstevel  *	- failure - error code of failure
61091708Sstevel  */
61101708Sstevel static int
man_plumb(man_dest_t * mdp)61111708Sstevel man_plumb(man_dest_t *mdp)
61121708Sstevel {
61131708Sstevel 	int		status;
61141708Sstevel 	int		muxid;
61151708Sstevel 	ldi_handle_t	lh;
61161708Sstevel 	ldi_ident_t	li = NULL;
61171708Sstevel 
61181708Sstevel 	MAN_DBG(MAN_SWITCH, ("man_plumb: mdp(0x%p) %s%d exp(%d)",
61197656SSherry.Moore@Sun.COM 	    (void *)mdp, ddi_major_to_name(mdp->md_device.mdev_major),
61207656SSherry.Moore@Sun.COM 	    mdp->md_device.mdev_ppa, mdp->md_device.mdev_exp_id));
61211708Sstevel 
61221708Sstevel 	/*
61231708Sstevel 	 * Control stream should already be open.
61241708Sstevel 	 */
61251708Sstevel 	if (man_ctl_lh == NULL) {
61261708Sstevel 		status = EAGAIN;
61271708Sstevel 		goto exit;
61281708Sstevel 	}
61291708Sstevel 
61301708Sstevel 	mutex_enter(&man_lock);
61311708Sstevel 	ASSERT(man_ctl_wq != NULL);
61321708Sstevel 	status = ldi_ident_from_stream(man_ctl_wq, &li);
61331708Sstevel 	if (status != 0) {
61341708Sstevel 		cmn_err(CE_WARN,
61351708Sstevel 		    "man_plumb: ident alloc failed, error %d", status);
61361708Sstevel 		goto exit;
61371708Sstevel 	}
61381708Sstevel 	mutex_exit(&man_lock);
61391708Sstevel 
61401708Sstevel 	/*
61411708Sstevel 	 * previously opens were done by a dev_t of makedev(clone_major,
61421708Sstevel 	 * mdev_major) which should always map to /devices/pseudo/clone@0:eri
61431708Sstevel 	 */
61441708Sstevel 	ASSERT(strcmp(ERI_IDNAME,
61457656SSherry.Moore@Sun.COM 	    ddi_major_to_name(mdp->md_device.mdev_major)) == 0);
61461708Sstevel 
61471708Sstevel 	status = ldi_open_by_name(ERI_PATH, FREAD | FWRITE | FNOCTTY,
61481708Sstevel 	    kcred, &lh, li);
61491708Sstevel 	if (status) {
61501708Sstevel 		cmn_err(CE_WARN,
61511708Sstevel 		    "man_plumb: eri open failed, error %d", status);
61521708Sstevel 		goto exit;
61531708Sstevel 	}
61541708Sstevel 
61551708Sstevel 	/*
61561708Sstevel 	 * Link netdev under MAN.
61571708Sstevel 	 */
61581708Sstevel 	ASSERT(mdp->md_muxid == -1);
61591708Sstevel 
61601708Sstevel 	status = ldi_ioctl(man_ctl_lh, I_PLINK, (intptr_t)lh,
61617656SSherry.Moore@Sun.COM 	    FREAD+FWRITE+FNOCTTY+FKIOCTL, kcred, &muxid);
61621708Sstevel 	if (status) {
61631708Sstevel 		cmn_err(CE_WARN,
61641708Sstevel 		    "man_plumb: ldi_ioctl(I_PLINK) failed, error %d", status);
61651708Sstevel 		(void) ldi_close(lh, NULL, kcred);
61661708Sstevel 		goto exit;
61671708Sstevel 
61681708Sstevel 	}
61691708Sstevel 	mdp->md_muxid = muxid;
61701708Sstevel 	mdp->md_wq = man_linkrec_find(muxid);
61711708Sstevel 	/*
61721708Sstevel 	 * If we can't find the linkrec then return an
61731708Sstevel 	 * error. It will be automatically unplumbed on failure.
61741708Sstevel 	 */
61751708Sstevel 	if (mdp->md_wq == NULL)
61761708Sstevel 		status = EAGAIN;
61771708Sstevel 
61781708Sstevel 	(void) ldi_close(lh, NULL, kcred);
61791708Sstevel exit:
61801708Sstevel 	if (li)
61811708Sstevel 		ldi_ident_release(li);
61821708Sstevel 
61831708Sstevel 	MAN_DBG(MAN_SWITCH, ("man_plumb: exit\n"));
61841708Sstevel 
61851708Sstevel 	return (status);
61861708Sstevel }
61871708Sstevel 
61881708Sstevel /*
61891708Sstevel  * man_unplumb - tear down the STREAMs framework for the lower multiplexor.
61901708Sstevel  *
61911708Sstevel  *	mdp - destination struct of interest
61921708Sstevel  *
61931708Sstevel  *	returns	- success - 0
61941708Sstevel  *		- failure - return error from ldi_ioctl
61951708Sstevel  */
61961708Sstevel static void
man_unplumb(man_dest_t * mdp)61971708Sstevel man_unplumb(man_dest_t *mdp)
61981708Sstevel {
61991708Sstevel 	int	status, rval;
62001708Sstevel 
62011708Sstevel 	MAN_DBG(MAN_SWITCH, ("man_unplumb: mdp"));
62021708Sstevel 	MAN_DBGCALL(MAN_SWITCH, man_print_mdp(mdp));
62031708Sstevel 
62041708Sstevel 	if (mdp->md_muxid == -1)
62051708Sstevel 		return;
62061708Sstevel 
62071708Sstevel 	ASSERT(man_ctl_lh != NULL);
62081708Sstevel 
62091708Sstevel 	/*
62101708Sstevel 	 * I_PUNLINK causes the multiplexor resources to be freed.
62111708Sstevel 	 */
62121708Sstevel 	status = ldi_ioctl(man_ctl_lh, I_PUNLINK, (intptr_t)mdp->md_muxid,
62137656SSherry.Moore@Sun.COM 	    FREAD+FWRITE+FNOCTTY+FKIOCTL, kcred, &rval);
62141708Sstevel 	if (status) {
62151708Sstevel 		cmn_err(CE_WARN, "man_unplumb: ldi_ioctl(I_PUNLINK) failed"
62167656SSherry.Moore@Sun.COM 		    " errno %d\n", status);
62171708Sstevel 	}
62181708Sstevel 	/*
62191708Sstevel 	 * Delete linkrec if it exists.
62201708Sstevel 	 */
62211708Sstevel 	(void) man_linkrec_find(mdp->md_muxid);
62221708Sstevel 	mdp->md_muxid = -1;
62231708Sstevel 
62241708Sstevel }
62251708Sstevel 
62261708Sstevel /*
62271708Sstevel  * The routines below deal with paths and pathgroups. These data structures
62281708Sstevel  * are used to track the physical devices connecting the domain and SSC.
62291708Sstevel  * These devices make up the lower streams of the MAN multiplexor. The
62301708Sstevel  * routines all expect the man_lock to be held.
62311708Sstevel  *
62321708Sstevel  * A pathgroup consists of all paths that connect a particular domain and the
62331708Sstevel  * SSC. The concept of a pathgroup id (pg_id) is used to uniquely identify
62341708Sstevel  * a pathgroup.  For Domains, there is just one pathgroup, that connecting
62351708Sstevel  * the domain to the SSC (pg_id == 0). On the SSC, there is one pathgroup per
62361708Sstevel  * domain. The pg_id field corresponds to the domain tags A-R. A pg_id of
62371708Sstevel  * 0 means domain tag A, a pg_id of 1 means domain B, etc.
62381708Sstevel  *
62391708Sstevel  * The path data structure identifies one path between the SSC and a domain.
62401708Sstevel  * It describes the information for the path: the major and minor number of
62411708Sstevel  * the physical device; kstat pointers; and ethernet address of the
62421708Sstevel  * other end of the path.
62431708Sstevel  *
62441708Sstevel  * The pathgroups are anchored at man_pg_head and are protected by the
62451708Sstevel  * by the inner perimeter. The routines are only called by the STREAMs
62461708Sstevel  * portion of the driver.
62471708Sstevel  */
62481708Sstevel 
62491708Sstevel /*
62501708Sstevel  * Update man instance pathgroup info. Exclusive inner perimeter assures
62511708Sstevel  * this code is single threaded. man_refcnt assures man_t wont detach
62521708Sstevel  * while we are playing with man_pg stuff.
62531708Sstevel  *
62541708Sstevel  * Returns 0 on success, errno on failure.
62551708Sstevel  */
62561708Sstevel int
man_pg_cmd(mi_path_t * mip,man_work_t * waiter_wp)62571708Sstevel man_pg_cmd(mi_path_t *mip, man_work_t *waiter_wp)
62581708Sstevel {
62591708Sstevel 	int		status = 0;
62601708Sstevel 	man_t		*manp;
62611708Sstevel 
62621708Sstevel 	if (mip->mip_ndevs < 0) {
62631708Sstevel 		status = EINVAL;
62641708Sstevel 		cmn_err(CE_WARN, "man_pg_cmd: EINVAL: mip_ndevs %d",
62657656SSherry.Moore@Sun.COM 		    mip->mip_ndevs);
62661708Sstevel 		goto exit;
62671708Sstevel 	}
62681708Sstevel 
62691708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
62701708Sstevel 	manp = ddi_get_soft_state(man_softstate, mip->mip_man_ppa);
62711708Sstevel 	if (manp == NULL) {
62721708Sstevel 		status = ENODEV;
62731708Sstevel 		goto exit;
62741708Sstevel 	}
62751708Sstevel 
62761708Sstevel 	MAN_DBG(MAN_PATH, ("man_pg_cmd: mip"));
62771708Sstevel 	MAN_DBGCALL(MAN_PATH, man_print_mip(mip));
62781708Sstevel 
62791708Sstevel 	MAN_DBG(MAN_PATH, ("\tman_t"));
62801708Sstevel 	MAN_DBGCALL(MAN_PATH, man_print_man(manp));
62811708Sstevel 
62821708Sstevel 	switch (mip->mip_cmd) {
62831708Sstevel 	case MI_PATH_ASSIGN:
62841708Sstevel 		status = man_pg_assign(&manp->man_pg, mip, FALSE);
62851708Sstevel 		break;
62861708Sstevel 
62871708Sstevel 	case MI_PATH_ADD:
62881708Sstevel 		status = man_pg_assign(&manp->man_pg, mip, TRUE);
62891708Sstevel 		break;
62901708Sstevel 
62911708Sstevel 	case MI_PATH_UNASSIGN:
62921708Sstevel 		status = man_pg_unassign(&manp->man_pg, mip);
62931708Sstevel 		break;
62941708Sstevel 
62951708Sstevel 	case MI_PATH_ACTIVATE:
62961708Sstevel 		status = man_pg_activate(manp, mip, waiter_wp);
62971708Sstevel 		break;
62981708Sstevel 
62991708Sstevel 	case MI_PATH_READ:
63001708Sstevel 		status = man_pg_read(manp->man_pg, mip);
63011708Sstevel 		break;
63021708Sstevel 
63031708Sstevel 	default:
63041708Sstevel 		status = EINVAL;
63051708Sstevel 		cmn_err(CE_NOTE, "man_pg_cmd: invalid command");
63061708Sstevel 		break;
63071708Sstevel 	}
63081708Sstevel 
63091708Sstevel exit:
63101708Sstevel 	MAN_DBG(MAN_PATH, ("man_pg_cmd: returns %d", status));
63111708Sstevel 
63121708Sstevel 	return (status);
63131708Sstevel }
63141708Sstevel 
63151708Sstevel /*
63161708Sstevel  * Assign paths to a pathgroup. If pathgroup doesnt exists, create it.
63171708Sstevel  * If path doesnt exist, create it. If ethernet address of existing
63181708Sstevel  * pathgroup different, change it. If an existing path is not in the new
63191708Sstevel  * list, remove it.  If anything changed, send PATH_UPDATE request to
63201708Sstevel  * man_iwork to update all man_dest_t's.
63211708Sstevel  *
63221708Sstevel  * 	mplpp	- man pathgroup list point to point.
63231708Sstevel  *	mip	- new/updated pathgroup info to assign.
63241708Sstevel  */
63251708Sstevel static int
man_pg_assign(man_pg_t ** mplpp,mi_path_t * mip,int add_only)63261708Sstevel man_pg_assign(man_pg_t **mplpp, mi_path_t *mip, int add_only)
63271708Sstevel {
63281708Sstevel 	man_pg_t	*mpg;
63291708Sstevel 	man_path_t	*mp;
63301708Sstevel 	man_path_t	*add_paths = NULL;
63311708Sstevel 	int		cnt;
63321708Sstevel 	int		i;
63331708Sstevel 	int		first_pass = TRUE;
63341708Sstevel 	int		status = 0;
63351708Sstevel 
63361708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
63371708Sstevel 
63381708Sstevel 	cnt = mip->mip_ndevs;
63391708Sstevel 	if (cnt == 0) {
63401708Sstevel 		status = EINVAL;
63411708Sstevel 		cmn_err(CE_NOTE, "man_pg_assign: mip_ndevs == 0");
63421708Sstevel 		goto exit;
63431708Sstevel 	}
63441708Sstevel 
63451708Sstevel 	/*
63461708Sstevel 	 * Assure the devices to be assigned are not assigned to some other
63471708Sstevel 	 * pathgroup.
63481708Sstevel 	 */
63491708Sstevel 	for (i = 0; i < cnt; i++) {
63501708Sstevel 		mpg = man_find_path_by_dev(*mplpp, &mip->mip_devs[i], NULL);
63511708Sstevel 
63521708Sstevel 		if (mpg == NULL)
63531708Sstevel 			continue;
63541708Sstevel 
63551708Sstevel 		if ((mpg->mpg_man_ppa != mip->mip_man_ppa) ||
63561708Sstevel 		    (mpg->mpg_pg_id != mip->mip_pg_id)) {
63571708Sstevel 			/*
63581708Sstevel 			 * Already assigned to some other man instance
63591708Sstevel 			 * or pathgroup.
63601708Sstevel 			 */
63611708Sstevel 			status = EEXIST;
63621708Sstevel 			goto exit;
63631708Sstevel 		}
63641708Sstevel 	}
63651708Sstevel 
63661708Sstevel 	/*
63671708Sstevel 	 * Find pathgroup, or allocate new one if it doesnt exist and
63681708Sstevel 	 * add it to list at mplpp. Result is that mpg points to
63691708Sstevel 	 * pathgroup to modify.
63701708Sstevel 	 */
63711708Sstevel 	mpg = man_find_pg_by_id(*mplpp, mip->mip_pg_id);
63721708Sstevel 	if (mpg == NULL) {
63731708Sstevel 
63741708Sstevel 		status = man_pg_create(mplpp, &mpg, mip);
63751708Sstevel 		if (status)
63761708Sstevel 			goto exit;
63771708Sstevel 
63781708Sstevel 	} else if (ether_cmp(&mip->mip_eaddr, &mpg->mpg_dst_eaddr) != 0) {
63791708Sstevel 
63801708Sstevel 		cmn_err(CE_WARN, "man_pg_assign: ethernet address mismatch");
63811708Sstevel 		cmn_err(CE_CONT, "existing %s",
63827656SSherry.Moore@Sun.COM 		    ether_sprintf(&mpg->mpg_dst_eaddr));
63831708Sstevel 		cmn_err(CE_CONT, "new %s",
63847656SSherry.Moore@Sun.COM 		    ether_sprintf(&mip->mip_eaddr));
63851708Sstevel 
63861708Sstevel 		status = EINVAL;
63871708Sstevel 		goto exit;
63881708Sstevel 	}
63891708Sstevel 
63901708Sstevel 	/*
63911708Sstevel 	 * Create list of new paths to add to pathgroup.
63921708Sstevel 	 */
63931708Sstevel 	for (i = 0; i < cnt; i++) {
63941708Sstevel 
63951708Sstevel 		if (man_find_path_by_dev(*mplpp, &mip->mip_devs[i], NULL))
63961708Sstevel 			continue;	/* Already exists in this pathgroup */
63971708Sstevel 
63981708Sstevel 		mp = man_kzalloc(sizeof (man_path_t), KM_NOSLEEP);
63991708Sstevel 		if (mp == NULL) {
64001708Sstevel 			status = ENOMEM;
64011708Sstevel 			goto exit;
64021708Sstevel 		}
64031708Sstevel 
64041708Sstevel 		mp->mp_device = mip->mip_devs[i];
64051708Sstevel 		mp->mp_device.mdev_state = MDEV_ASSIGNED;
64061708Sstevel 
64071708Sstevel 		MAN_DBG(MAN_PATH, ("man_pg_assign: assigning mdp"));
64081708Sstevel 		MAN_DBGCALL(MAN_PATH, man_print_dev(&mp->mp_device));
64091708Sstevel 
64101708Sstevel 		status = man_path_kstat_init(mp);
64111708Sstevel 		if (status) {
64121708Sstevel 			man_kfree(mp, sizeof (man_path_t));
64131708Sstevel 			goto exit;
64141708Sstevel 		}
64151708Sstevel 
64161708Sstevel 		man_path_insert(&add_paths, mp);
64171708Sstevel 	}
64181708Sstevel 
64191708Sstevel 	/*
64201708Sstevel 	 * man_dr_attach passes only the path which is being DRd in.
64211708Sstevel 	 * So just add the path and don't worry about removing paths.
64221708Sstevel 	 */
64231708Sstevel 	if (add_only == TRUE)
64241708Sstevel 		goto exit;
64251708Sstevel 
64261708Sstevel 
64271708Sstevel 	/*
64281708Sstevel 	 * Check if any paths we want to remove are ACTIVE. If not,
64291708Sstevel 	 * do a second pass and remove them.
64301708Sstevel 	 */
64311708Sstevel again:
64321708Sstevel 	mp = mpg->mpg_pathp;
64331708Sstevel 	while (mp != NULL) {
64341708Sstevel 		int		in_new_list;
64351708Sstevel 		man_path_t	*rp;
64361708Sstevel 
64371708Sstevel 		rp = NULL;
64381708Sstevel 		in_new_list = FALSE;
64391708Sstevel 
64401708Sstevel 		for (i = 0; i < cnt; i++) {
64411708Sstevel 			if (mp->mp_device.mdev_ppa ==
64421708Sstevel 			    mip->mip_devs[i].mdev_ppa) {
64431708Sstevel 
64441708Sstevel 				in_new_list = TRUE;
64451708Sstevel 				break;
64461708Sstevel 			}
64471708Sstevel 		}
64481708Sstevel 
64491708Sstevel 		if (!in_new_list) {
64501708Sstevel 			if (first_pass) {
64511708Sstevel 				if (mp->mp_device.mdev_state & MDEV_ACTIVE) {
64521708Sstevel 					status = EBUSY;
64531708Sstevel 					goto exit;
64541708Sstevel 				}
64551708Sstevel 			} else {
64561708Sstevel 				rp = mp;
64571708Sstevel 			}
64581708Sstevel 		}
64591708Sstevel 		mp = mp->mp_next;
64601708Sstevel 
64611708Sstevel 		if (rp != NULL)
64621708Sstevel 			man_path_remove(&mpg->mpg_pathp, rp);
64631708Sstevel 	}
64641708Sstevel 
64651708Sstevel 	if (first_pass == TRUE) {
64661708Sstevel 		first_pass = FALSE;
64671708Sstevel 		goto again;
64681708Sstevel 	}
64691708Sstevel 
64701708Sstevel exit:
64711708Sstevel 	if (status == 0) {
64721708Sstevel 		if (add_paths)
64731708Sstevel 			man_path_merge(&mpg->mpg_pathp, add_paths);
64741708Sstevel 	} else {
64751708Sstevel 		while (add_paths != NULL) {
64761708Sstevel 			mp = add_paths;
64771708Sstevel 			add_paths = mp->mp_next;
64781708Sstevel 			mp->mp_next = NULL;
64791708Sstevel 
64801708Sstevel 			man_path_kstat_uninit(mp);
64811708Sstevel 			man_kfree(mp, sizeof (man_path_t));
64821708Sstevel 		}
64831708Sstevel 	}
64841708Sstevel 
64851708Sstevel 	return (status);
64861708Sstevel }
64871708Sstevel 
64881708Sstevel /*
64891708Sstevel  * Remove all paths from a pathgroup (domain shutdown). If there is an
64901708Sstevel  * active path in the group, shut down all destinations referencing it
64911708Sstevel  * first.
64921708Sstevel  */
64931708Sstevel static int
man_pg_unassign(man_pg_t ** plpp,mi_path_t * mip)64941708Sstevel man_pg_unassign(man_pg_t **plpp, mi_path_t *mip)
64951708Sstevel {
64961708Sstevel 	man_pg_t	*mpg;
64971708Sstevel 	man_pg_t	*tpg;
64981708Sstevel 	man_pg_t	*tppg;
64991708Sstevel 	man_path_t	*mp = NULL;
65001708Sstevel 	int		status = 0;
65011708Sstevel 
65021708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
65031708Sstevel 
65041708Sstevel 	/*
65051708Sstevel 	 * Check for existence of pathgroup.
65061708Sstevel 	 */
65071708Sstevel 	if ((mpg = man_find_pg_by_id(*plpp, mip->mip_pg_id)) == NULL)
65081708Sstevel 		goto exit;
65091708Sstevel 
65101708Sstevel 	if (man_find_active_path(mpg->mpg_pathp) != NULL) {
65111708Sstevel 		status = man_remove_dests(mpg);
65121708Sstevel 		if (status)
65131708Sstevel 			goto exit;
65141708Sstevel 	}
65151708Sstevel 
65161708Sstevel 	/*
65171708Sstevel 	 * Free all the paths for this pathgroup.
65181708Sstevel 	 */
65191708Sstevel 	while (mpg->mpg_pathp) {
65201708Sstevel 		mp = mpg->mpg_pathp;
65211708Sstevel 		mpg->mpg_pathp = mp->mp_next;
65221708Sstevel 		mp->mp_next = NULL;
65231708Sstevel 
65241708Sstevel 		man_path_kstat_uninit(mp);
65251708Sstevel 		man_kfree(mp, sizeof (man_path_t));
65261708Sstevel 	}
65271708Sstevel 
65281708Sstevel 	/*
65291708Sstevel 	 * Remove this pathgroup from the list, and free it.
65301708Sstevel 	 */
65311708Sstevel 	tpg = tppg = *plpp;
65321708Sstevel 	if (tpg == mpg) {
65331708Sstevel 		*plpp = tpg->mpg_next;
65341708Sstevel 		goto free_pg;
65351708Sstevel 	}
65361708Sstevel 
65371708Sstevel 	for (tpg = tpg->mpg_next; tpg != NULL; tpg = tpg->mpg_next) {
65381708Sstevel 		if (tpg == mpg)
65391708Sstevel 			break;
65401708Sstevel 		tppg = tpg;
65411708Sstevel 	}
65421708Sstevel 
65431708Sstevel 	ASSERT(tpg != NULL);
65441708Sstevel 
65451708Sstevel 	tppg->mpg_next = tpg->mpg_next;
65461708Sstevel 	tpg->mpg_next = NULL;
65471708Sstevel 
65481708Sstevel free_pg:
65491708Sstevel 	man_kfree(tpg, sizeof (man_pg_t));
65501708Sstevel 
65511708Sstevel exit:
65521708Sstevel 	return (status);
65531708Sstevel 
65541708Sstevel }
65551708Sstevel 
65561708Sstevel /*
65571708Sstevel  * Set a new active path. This is done via man_ioctl so we are
65581708Sstevel  * exclusive in the inner perimeter.
65591708Sstevel  */
65601708Sstevel static int
man_pg_activate(man_t * manp,mi_path_t * mip,man_work_t * waiter_wp)65611708Sstevel man_pg_activate(man_t *manp, mi_path_t *mip, man_work_t *waiter_wp)
65621708Sstevel {
65631708Sstevel 	man_pg_t	*mpg1;
65641708Sstevel 	man_pg_t	*mpg2;
65651708Sstevel 	man_pg_t	*plp;
65661708Sstevel 	man_path_t	*mp;
65671708Sstevel 	man_path_t	*ap;
65681708Sstevel 	int		status = 0;
65691708Sstevel 
65701708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
65711708Sstevel 	MAN_DBG(MAN_PATH, ("man_pg_activate: dev"));
65721708Sstevel 	MAN_DBGCALL(MAN_PATH, man_print_dev(mip->mip_devs));
65731708Sstevel 
65741708Sstevel 	if (mip->mip_ndevs != 1) {
65751708Sstevel 		status = EINVAL;
65761708Sstevel 		goto exit;
65771708Sstevel 	}
65781708Sstevel 
65791708Sstevel 	plp = manp->man_pg;
65801708Sstevel 	mpg1 = man_find_pg_by_id(plp, mip->mip_pg_id);
65811708Sstevel 	if (mpg1 == NULL) {
65821708Sstevel 		status = EINVAL;
65831708Sstevel 		goto exit;
65841708Sstevel 	}
65851708Sstevel 
65861708Sstevel 	mpg2 = man_find_path_by_dev(plp, mip->mip_devs, &mp);
65871708Sstevel 	if (mpg2 == NULL) {
65881708Sstevel 		status = ENODEV;
65891708Sstevel 		goto exit;
65901708Sstevel 	}
65911708Sstevel 
65921708Sstevel 	if (mpg1 != mpg2) {
65931708Sstevel 		status = EINVAL;
65941708Sstevel 		goto exit;
65951708Sstevel 	}
65961708Sstevel 
65971708Sstevel 	ASSERT(mp->mp_device.mdev_ppa == mip->mip_devs->mdev_ppa);
65981708Sstevel 
65991708Sstevel 	if (mpg1->mpg_flags & MAN_PG_SWITCHING) {
66001708Sstevel 		status = EAGAIN;
66011708Sstevel 		goto exit;
66021708Sstevel 	}
66031708Sstevel 
66041708Sstevel 	ap = man_find_active_path(mpg1->mpg_pathp);
66051708Sstevel 	if (ap == NULL) {
66061708Sstevel 		/*
66071708Sstevel 		 * This is the first time a path has been activated for
66081708Sstevel 		 * this pathgroup. Initialize all upper streams dest
66091708Sstevel 		 * structure for this pathgroup so autoswitch will find
66101708Sstevel 		 * them.
66111708Sstevel 		 */
66121708Sstevel 		mp->mp_device.mdev_state |= MDEV_ACTIVE;
66131708Sstevel 		man_add_dests(mpg1);
66141708Sstevel 		goto exit;
66151708Sstevel 	}
66161708Sstevel 
66171708Sstevel 	/*
66181708Sstevel 	 * Path already active, nothing to do.
66191708Sstevel 	 */
66201708Sstevel 	if (ap == mp)
66211708Sstevel 		goto exit;
66221708Sstevel 
66231708Sstevel 	/*
66241708Sstevel 	 * Try to autoswitch to requested device. Set flags and refcnt.
66251708Sstevel 	 * Cleared in man_iswitch when SWITCH completes.
66261708Sstevel 	 */
66271708Sstevel 	manp->man_refcnt++;
66281708Sstevel 	mpg1->mpg_flags |= MAN_PG_SWITCHING;
66291708Sstevel 
66301708Sstevel 	/*
66311708Sstevel 	 * Switch to path specified.
66321708Sstevel 	 */
66331708Sstevel 	status = man_autoswitch(mpg1, mip->mip_devs, waiter_wp);
66341708Sstevel 
66351708Sstevel 	if (status != 0) {
66361708Sstevel 		/*
66371708Sstevel 		 * man_iswitch not going to run, clean up.
66381708Sstevel 		 */
66391708Sstevel 		manp->man_refcnt--;
66401708Sstevel 		mpg1->mpg_flags &= ~MAN_PG_SWITCHING;
66411708Sstevel 
66421708Sstevel 		if (status == ENODEV) {
66431708Sstevel 			/*
66441708Sstevel 			 * Device not plumbed isn't really an error. Change
66451708Sstevel 			 * active device setting here, since man_iswitch isn't
66461708Sstevel 			 * going to be run to do it.
66471708Sstevel 			 */
66481708Sstevel 			status = 0;
66491708Sstevel 			ap->mp_device.mdev_state &= ~MDEV_ACTIVE;
66501708Sstevel 			mp->mp_device.mdev_state |= MDEV_ACTIVE;
66511708Sstevel 		}
66521708Sstevel 	}
66531708Sstevel 
66541708Sstevel exit:
66551708Sstevel 	MAN_DBG(MAN_PATH, ("man_pg_activate: returns %d", status));
66561708Sstevel 
66571708Sstevel 	return (status);
66581708Sstevel }
66591708Sstevel 
66601708Sstevel static int
man_pg_read(man_pg_t * plp,mi_path_t * mip)66611708Sstevel man_pg_read(man_pg_t *plp, mi_path_t *mip)
66621708Sstevel {
66631708Sstevel 	man_pg_t	*mpg;
66641708Sstevel 	man_path_t	*mp;
66651708Sstevel 	int		cnt;
66661708Sstevel 	int		status = 0;
66671708Sstevel 
66681708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
66691708Sstevel 
66701708Sstevel 	if ((mpg = man_find_pg_by_id(plp, mip->mip_pg_id)) == NULL) {
66711708Sstevel 		status = ENODEV;
66721708Sstevel 		goto exit;
66731708Sstevel 	}
66741708Sstevel 
66751708Sstevel 	cnt = 0;
66761708Sstevel 	for (mp = mpg->mpg_pathp; mp != NULL; mp = mp->mp_next) {
66771708Sstevel 		bcopy(&mp->mp_device, &mip->mip_devs[cnt], sizeof (man_dev_t));
66781708Sstevel 		if (cnt == mip->mip_ndevs)
66791708Sstevel 			break;
66801708Sstevel 		cnt++;
66811708Sstevel 	}
66821708Sstevel 
66831708Sstevel 	MAN_DBG(MAN_PATH, ("man_pg_read: pg(0x%p) id(%d) found %d paths",
66847656SSherry.Moore@Sun.COM 	    (void *)mpg, mpg->mpg_pg_id, cnt));
66851708Sstevel 
66861708Sstevel 	mip->mip_ndevs = cnt;
66871708Sstevel 
66881708Sstevel 	/*
66891708Sstevel 	 * TBD - What should errno be if user buffer too small ?
66901708Sstevel 	 */
66911708Sstevel 	if (mp != NULL) {
66921708Sstevel 		status = ENOMEM;
66931708Sstevel 	}
66941708Sstevel 
66951708Sstevel exit:
66961708Sstevel 
66971708Sstevel 	return (status);
66981708Sstevel }
66991708Sstevel 
67001708Sstevel /*
67011708Sstevel  * return existing pathgroup, or create it. TBD - Need to update
67021708Sstevel  * all of destinations if we added a pathgroup. Also, need to update
67031708Sstevel  * all of man_strup if we add a path.
67041708Sstevel  *
67051708Sstevel  * 	mplpp	- man pathgroup list point to pointer.
67061708Sstevel  * 	mpgp	- returns newly created man pathgroup.
67071708Sstevel  *	mip	- info to fill in mpgp.
67081708Sstevel  */
67091708Sstevel static int
man_pg_create(man_pg_t ** mplpp,man_pg_t ** mpgp,mi_path_t * mip)67101708Sstevel man_pg_create(man_pg_t **mplpp, man_pg_t **mpgp, mi_path_t *mip)
67111708Sstevel {
67121708Sstevel 	man_pg_t	*mpg;
67131708Sstevel 	man_pg_t	*tpg;
67141708Sstevel 	int		status = 0;
67151708Sstevel 
67161708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
67171708Sstevel 
67181708Sstevel 	if (ether_cmp(&mip->mip_eaddr, &zero_ether_addr) == 0) {
67191708Sstevel 		cmn_err(CE_NOTE, "man_ioctl: man_pg_create: ether"
67207656SSherry.Moore@Sun.COM 		    " addresss not set!");
67211708Sstevel 		status = EINVAL;
67221708Sstevel 		goto exit;
67231708Sstevel 	}
67241708Sstevel 
67251708Sstevel 	mpg = man_kzalloc(sizeof (man_pg_t), KM_NOSLEEP);
67261708Sstevel 	if (mpg == NULL) {
67271708Sstevel 		status = ENOMEM;
67281708Sstevel 		goto exit;
67291708Sstevel 	}
67301708Sstevel 
67311708Sstevel 	mpg->mpg_flags = MAN_PG_IDLE;
67321708Sstevel 	mpg->mpg_pg_id = mip->mip_pg_id;
67331708Sstevel 	mpg->mpg_man_ppa = mip->mip_man_ppa;
67341708Sstevel 	ether_copy(&mip->mip_eaddr, &mpg->mpg_dst_eaddr);
67351708Sstevel 
67361708Sstevel 	MAN_DBG(MAN_PATH, ("man_pg_create: new mpg"));
67371708Sstevel 	MAN_DBGCALL(MAN_PATH, man_print_mpg(mpg));
67381708Sstevel 
67391708Sstevel 	tpg = *mplpp;
67401708Sstevel 	if (tpg == NULL) {
67411708Sstevel 		*mplpp = mpg;
67421708Sstevel 	} else {
67431708Sstevel 		while (tpg->mpg_next != NULL)
67441708Sstevel 			tpg = tpg->mpg_next;
67451708Sstevel 		tpg->mpg_next = mpg;
67461708Sstevel 	}
67471708Sstevel 
67481708Sstevel exit:
67491708Sstevel 	*mpgp = mpg;
67501708Sstevel 
67511708Sstevel 	return (status);
67521708Sstevel }
67531708Sstevel 
67541708Sstevel /*
67551708Sstevel  * Return pointer to pathgroup containing mdevp, null otherwise. Also,
67561708Sstevel  * if a path pointer is passed in, set it to matching path in pathgroup.
67571708Sstevel  *
67581708Sstevel  * Called holding man_lock.
67591708Sstevel  */
67601708Sstevel static man_pg_t *
man_find_path_by_dev(man_pg_t * plp,man_dev_t * mdevp,man_path_t ** mpp)67611708Sstevel man_find_path_by_dev(man_pg_t *plp, man_dev_t *mdevp, man_path_t **mpp)
67621708Sstevel {
67631708Sstevel 	man_pg_t	*mpg;
67641708Sstevel 	man_path_t	*mp;
67651708Sstevel 
67661708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
67671708Sstevel 	for (mpg = plp; mpg != NULL; mpg = mpg->mpg_next) {
67681708Sstevel 		for (mp  = mpg->mpg_pathp; mp != NULL; mp = mp->mp_next) {
67691708Sstevel 			if (mp->mp_device.mdev_major == mdevp->mdev_major &&
67701708Sstevel 			    mp->mp_device.mdev_ppa == mdevp->mdev_ppa) {
67711708Sstevel 
67721708Sstevel 				if (mpp != NULL)
67731708Sstevel 					*mpp = mp;
67741708Sstevel 				return (mpg);
67751708Sstevel 			}
67761708Sstevel 		}
67771708Sstevel 	}
67781708Sstevel 
67791708Sstevel 	return (NULL);
67801708Sstevel }
67811708Sstevel 
67821708Sstevel /*
67831708Sstevel  * Return pointer to pathgroup assigned to destination, null if not found.
67841708Sstevel  *
67851708Sstevel  * Called holding man_lock.
67861708Sstevel  */
67871708Sstevel static man_pg_t *
man_find_pg_by_id(man_pg_t * mpg,int pg_id)67881708Sstevel man_find_pg_by_id(man_pg_t *mpg, int pg_id)
67891708Sstevel {
67901708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
67911708Sstevel 	for (; mpg != NULL; mpg = mpg->mpg_next) {
67921708Sstevel 		if (mpg->mpg_pg_id == pg_id)
67931708Sstevel 			return (mpg);
67941708Sstevel 	}
67951708Sstevel 
67961708Sstevel 	return (NULL);
67971708Sstevel }
67981708Sstevel 
67991708Sstevel static man_path_t *
man_find_path_by_ppa(man_path_t * mplist,int ppa)68001708Sstevel man_find_path_by_ppa(man_path_t *mplist, int ppa)
68011708Sstevel {
68021708Sstevel 	man_path_t	*mp;
68031708Sstevel 
68041708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
68051708Sstevel 	for (mp = mplist; mp != NULL; mp = mp->mp_next) {
68061708Sstevel 		if (mp->mp_device.mdev_ppa == ppa)
68071708Sstevel 			return (mp);
68081708Sstevel 	}
68091708Sstevel 
68101708Sstevel 	return (NULL);
68111708Sstevel }
68121708Sstevel 
68131708Sstevel static man_path_t *
man_find_active_path(man_path_t * mplist)68141708Sstevel man_find_active_path(man_path_t *mplist)
68151708Sstevel {
68161708Sstevel 	man_path_t	*mp;
68171708Sstevel 
68181708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
68191708Sstevel 	for (mp = mplist; mp != NULL; mp = mp->mp_next)
68201708Sstevel 		if (mp->mp_device.mdev_state & MDEV_ACTIVE)
68211708Sstevel 			return (mp);
68221708Sstevel 
68231708Sstevel 	return (NULL);
68241708Sstevel }
68251708Sstevel 
68261708Sstevel /*
68271708Sstevel  * Try and find an alternate path.
68281708Sstevel  */
68291708Sstevel static man_path_t *
man_find_alternate_path(man_path_t * mlp)68301708Sstevel man_find_alternate_path(man_path_t *mlp)
68311708Sstevel {
68321708Sstevel 	man_path_t	*ap;		/* Active path */
68331708Sstevel 	man_path_t	*np;		/* New alternate path */
68341708Sstevel 	man_path_t	*fp = NULL;	/* LRU failed path */
68351708Sstevel 
68361708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
68371708Sstevel 	ap = man_find_active_path(mlp);
68381708Sstevel 
68391708Sstevel 	/*
68401708Sstevel 	 * Find a non-failed path, or the lru failed path and switch to it.
68411708Sstevel 	 */
68421708Sstevel 	for (np = mlp; np != NULL; np = np->mp_next) {
68431708Sstevel 		if (np == ap)
68441708Sstevel 			continue;
68451708Sstevel 
68461708Sstevel 		if (np->mp_device.mdev_state == MDEV_ASSIGNED)
68471708Sstevel 			goto exit;
68481708Sstevel 
68491708Sstevel 		if (np->mp_device.mdev_state & MDEV_FAILED) {
68501708Sstevel 			if (fp == NULL)
68511708Sstevel 				fp = np;
68521708Sstevel 			else
68531708Sstevel 				if (fp->mp_lru > np->mp_lru)
68541708Sstevel 						fp = np;
68551708Sstevel 		}
68561708Sstevel 	}
68571708Sstevel 
68581708Sstevel 	/*
68591708Sstevel 	 * Nowhere to switch to.
68601708Sstevel 	 */
68611708Sstevel 	if (np == NULL && (np =  fp) == NULL)
68621708Sstevel 		goto exit;
68631708Sstevel 
68641708Sstevel exit:
68651708Sstevel 	return (np);
68661708Sstevel }
68671708Sstevel 
68681708Sstevel /*
68691708Sstevel  * Assumes caller has verified existence.
68701708Sstevel  */
68711708Sstevel static void
man_path_remove(man_path_t ** lpp,man_path_t * mp)68721708Sstevel man_path_remove(man_path_t **lpp, man_path_t *mp)
68731708Sstevel {
68741708Sstevel 	man_path_t	*tp;
68751708Sstevel 	man_path_t	*tpp;
68761708Sstevel 
68771708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
68781708Sstevel 	MAN_DBG(MAN_PATH, ("man_path_remove: removing path"));
68791708Sstevel 	MAN_DBGCALL(MAN_PATH, man_print_path(mp));
68801708Sstevel 
68811708Sstevel 	tp = tpp = *lpp;
68821708Sstevel 	if (tp == mp) {
68831708Sstevel 		*lpp = tp->mp_next;
68841708Sstevel 		goto exit;
68851708Sstevel 	}
68861708Sstevel 
68871708Sstevel 	for (tp = tp->mp_next; tp != NULL; tp = tp->mp_next) {
68881708Sstevel 		if (tp == mp)
68891708Sstevel 			break;
68901708Sstevel 		tpp = tp;
68911708Sstevel 	}
68921708Sstevel 
68931708Sstevel 	ASSERT(tp != NULL);
68941708Sstevel 
68951708Sstevel 	tpp->mp_next = tp->mp_next;
68961708Sstevel 	tp->mp_next = NULL;
68971708Sstevel 
68981708Sstevel exit:
68991708Sstevel 	man_path_kstat_uninit(tp);
69001708Sstevel 	man_kfree(tp, sizeof (man_path_t));
69011708Sstevel 
69021708Sstevel }
69031708Sstevel 
69041708Sstevel /*
69051708Sstevel  * Insert path into list, ascending order by ppa.
69061708Sstevel  */
69071708Sstevel static void
man_path_insert(man_path_t ** lpp,man_path_t * mp)69081708Sstevel man_path_insert(man_path_t **lpp, man_path_t *mp)
69091708Sstevel {
69101708Sstevel 	man_path_t	*tp;
69111708Sstevel 	man_path_t	*tpp;
69121708Sstevel 
69131708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
69141708Sstevel 	if (*lpp == NULL) {
69151708Sstevel 		*lpp = mp;
69161708Sstevel 		return;
69171708Sstevel 	}
69181708Sstevel 
69191708Sstevel 	tp = tpp = *lpp;
69201708Sstevel 	if (tp->mp_device.mdev_ppa > mp->mp_device.mdev_ppa) {
69211708Sstevel 		mp->mp_next = tp;
69221708Sstevel 		*lpp = mp;
69231708Sstevel 		return;
69241708Sstevel 	}
69251708Sstevel 
69261708Sstevel 	for (tp = tp->mp_next; tp != NULL; tp =  tp->mp_next) {
69271708Sstevel 		if (tp->mp_device.mdev_ppa > mp->mp_device.mdev_ppa)
69281708Sstevel 			break;
69291708Sstevel 		tpp = tp;
69301708Sstevel 	}
69311708Sstevel 
69321708Sstevel 	if (tp == NULL) {
69331708Sstevel 		tpp->mp_next = mp;
69341708Sstevel 	} else {
69351708Sstevel 		tpp->mp_next = mp;
69361708Sstevel 		mp->mp_next = tp;
69371708Sstevel 	}
69381708Sstevel }
69391708Sstevel 
69401708Sstevel /*
69411708Sstevel  * Merge npp into lpp, ascending order by ppa. Assumes no
69421708Sstevel  * duplicates in either list.
69431708Sstevel  */
69441708Sstevel static void
man_path_merge(man_path_t ** lpp,man_path_t * np)69451708Sstevel man_path_merge(man_path_t **lpp, man_path_t *np)
69461708Sstevel {
69471708Sstevel 	man_path_t	*tmp;
69481708Sstevel 
69491708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
69501708Sstevel 	while (np != NULL) {
69511708Sstevel 		tmp = np;
69521708Sstevel 		np = np->mp_next;
69531708Sstevel 		tmp->mp_next = NULL;
69541708Sstevel 
69551708Sstevel 		man_path_insert(lpp, tmp);
69561708Sstevel 	}
69571708Sstevel 
69581708Sstevel }
69591708Sstevel 
69601708Sstevel static int
man_path_kstat_init(man_path_t * mpp)69611708Sstevel man_path_kstat_init(man_path_t *mpp)
69621708Sstevel {
69631708Sstevel 
69641708Sstevel 	kstat_named_t	*dev_knp;
69651708Sstevel 	int		status = 0;
69661708Sstevel 
69671708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
69681708Sstevel 	MAN_DBG(MAN_PATH, ("man_path_kstat_init: mpp(0x%p)\n", (void *)mpp));
69691708Sstevel 
69701708Sstevel 	/*
69711708Sstevel 	 * Create named kstats for accounting purposes.
69721708Sstevel 	 */
69731708Sstevel 	dev_knp = man_kzalloc(MAN_NUMSTATS * sizeof (kstat_named_t),
69747656SSherry.Moore@Sun.COM 	    KM_NOSLEEP);
69751708Sstevel 	if (dev_knp == NULL) {
69761708Sstevel 		status = ENOMEM;
69771708Sstevel 		goto exit;
69781708Sstevel 	}
69791708Sstevel 	man_kstat_named_init(dev_knp, MAN_NUMSTATS);
69801708Sstevel 	mpp->mp_last_knp = dev_knp;
69811708Sstevel 
69821708Sstevel exit:
69831708Sstevel 
69841708Sstevel 	MAN_DBG(MAN_PATH, ("man_path_kstat_init: returns %d\n", status));
69851708Sstevel 
69861708Sstevel 	return (status);
69871708Sstevel }
69881708Sstevel 
69891708Sstevel static void
man_path_kstat_uninit(man_path_t * mp)69901708Sstevel man_path_kstat_uninit(man_path_t *mp)
69911708Sstevel {
69921708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
69931708Sstevel 	man_kfree(mp->mp_last_knp, MAN_NUMSTATS * sizeof (kstat_named_t));
69941708Sstevel }
69951708Sstevel 
69961708Sstevel /*
69971708Sstevel  * man_work_alloc - allocate and initiate a work request structure
69981708Sstevel  *
69991708Sstevel  *	type - type of request to allocate
70001708Sstevel  *	returns	- success - ptr to an initialized work structure
70011708Sstevel  *		- failure - NULL
70021708Sstevel  */
70031708Sstevel man_work_t *
man_work_alloc(int type,int kmflag)70041708Sstevel man_work_alloc(int type, int kmflag)
70051708Sstevel {
70061708Sstevel 	man_work_t	*wp;
70071708Sstevel 
70081708Sstevel 	wp = man_kzalloc(sizeof (man_work_t), kmflag);
70091708Sstevel 	if (wp == NULL)
70101708Sstevel 		goto exit;
70111708Sstevel 
70121708Sstevel 	cv_init(&wp->mw_cv, NULL, CV_DRIVER, NULL); \
70131708Sstevel 	wp->mw_type = type;
70141708Sstevel 
70151708Sstevel exit:
70161708Sstevel 	return (wp);
70171708Sstevel }
70181708Sstevel 
70191708Sstevel /*
70201708Sstevel  * man_work_free - deallocate a work request structure
70211708Sstevel  *
70221708Sstevel  *	wp - ptr to work structure to be freed
70231708Sstevel  */
70241708Sstevel void
man_work_free(man_work_t * wp)70251708Sstevel man_work_free(man_work_t *wp)
70261708Sstevel {
70271708Sstevel 	cv_destroy(&wp->mw_cv);
70281708Sstevel 	man_kfree((void *)wp, sizeof (man_work_t));
70291708Sstevel }
70301708Sstevel 
70311708Sstevel /*
70321708Sstevel  * Post work to a work queue.  The man_bwork sleeps on
70331708Sstevel  * man_bwork_q->q_cv, and work requesters may sleep on mw_cv.
70341708Sstevel  * The man_lock is used to protect both cv's.
70351708Sstevel  */
70361708Sstevel void
man_work_add(man_workq_t * q,man_work_t * wp)70371708Sstevel man_work_add(man_workq_t *q, man_work_t *wp)
70381708Sstevel {
70391708Sstevel 	man_work_t	*lp = q->q_work;
70401708Sstevel 
70411708Sstevel 	if (lp) {
70421708Sstevel 		while (lp->mw_next != NULL)
70431708Sstevel 			lp = lp->mw_next;
70441708Sstevel 
70451708Sstevel 		lp->mw_next = wp;
70461708Sstevel 
70471708Sstevel 	} else {
70481708Sstevel 		q->q_work = wp;
70491708Sstevel 	}
70501708Sstevel 
70511708Sstevel 	/*
70521708Sstevel 	 * cv_signal for man_bwork_q, qenable for man_iwork_q
70531708Sstevel 	 */
70541708Sstevel 	if (q == man_bwork_q) {
70551708Sstevel 		cv_signal(&q->q_cv);
70561708Sstevel 
70571708Sstevel 	} else {	/* q == man_iwork_q */
70581708Sstevel 
70591708Sstevel 		if (man_ctl_wq != NULL)
70601708Sstevel 			qenable(man_ctl_wq);
70611708Sstevel 	}
70621708Sstevel 
70631708Sstevel }
70641708Sstevel 
70651708Sstevel /* <<<<<<<<<<<<<<<<<<<<<<< NDD SUPPORT FUNCTIONS	>>>>>>>>>>>>>>>>>>> */
70661708Sstevel /*
70671708Sstevel  * ndd support functions to get/set parameters
70681708Sstevel  */
70691708Sstevel 
70701708Sstevel /*
70711708Sstevel  * Register each element of the parameter array with the
70721708Sstevel  * named dispatch handler. Each element is loaded using
70731708Sstevel  * nd_load()
70741708Sstevel  *
70751708Sstevel  * 	cnt	- the number of elements present in the parameter array
70761708Sstevel  */
70771708Sstevel static int
man_param_register(param_t * manpa,int cnt)70781708Sstevel man_param_register(param_t *manpa, int cnt)
70791708Sstevel {
70801708Sstevel 	int	i;
70811708Sstevel 	ndgetf_t getp;
70821708Sstevel 	ndsetf_t setp;
70831708Sstevel 	int	status = B_TRUE;
70841708Sstevel 
70851708Sstevel 	MAN_DBG(MAN_CONFIG, ("man_param_register: manpa(0x%p) cnt %d\n",
70867656SSherry.Moore@Sun.COM 	    (void *)manpa, cnt));
70871708Sstevel 
70881708Sstevel 	getp = man_param_get;
70891708Sstevel 
70901708Sstevel 	for (i = 0; i < cnt; i++, manpa++) {
70911708Sstevel 		switch (man_param_display[i]) {
70921708Sstevel 		case MAN_NDD_GETABLE:
70931708Sstevel 			setp = NULL;
70941708Sstevel 			break;
70951708Sstevel 
70961708Sstevel 		case MAN_NDD_SETABLE:
70971708Sstevel 			setp = man_param_set;
70981708Sstevel 			break;
70991708Sstevel 
71001708Sstevel 		default:
71011708Sstevel 			continue;
71021708Sstevel 		}
71031708Sstevel 
71041708Sstevel 		if (!nd_load(&man_ndlist, manpa->param_name, getp,
71057656SSherry.Moore@Sun.COM 		    setp, (caddr_t)manpa)) {
71061708Sstevel 
71071708Sstevel 			(void) man_nd_free(&man_ndlist);
71081708Sstevel 			status = B_FALSE;
71091708Sstevel 			goto exit;
71101708Sstevel 		}
71111708Sstevel 	}
71121708Sstevel 
71131708Sstevel 	if (!nd_load(&man_ndlist, "man_pathgroups_report",
71147656SSherry.Moore@Sun.COM 	    man_pathgroups_report, NULL, NULL)) {
71151708Sstevel 
71161708Sstevel 		(void) man_nd_free(&man_ndlist);
71171708Sstevel 		status = B_FALSE;
71181708Sstevel 		goto exit;
71191708Sstevel 	}
71201708Sstevel 
71211708Sstevel 	if (!nd_load(&man_ndlist, "man_set_active_path",
71227656SSherry.Moore@Sun.COM 	    NULL, man_set_active_path, NULL)) {
71231708Sstevel 
71241708Sstevel 		(void) man_nd_free(&man_ndlist);
71251708Sstevel 		status = B_FALSE;
71261708Sstevel 		goto exit;
71271708Sstevel 	}
71281708Sstevel 
71291708Sstevel 	if (!nd_load(&man_ndlist, "man_get_hostinfo",
71307656SSherry.Moore@Sun.COM 	    man_get_hostinfo, NULL, NULL)) {
71311708Sstevel 
71321708Sstevel 		(void) man_nd_free(&man_ndlist);
71331708Sstevel 		status = B_FALSE;
71341708Sstevel 		goto exit;
71351708Sstevel 	}
71361708Sstevel 
71371708Sstevel exit:
71381708Sstevel 
71391708Sstevel 	MAN_DBG(MAN_CONFIG, ("man_param_register: returns %d\n", status));
71401708Sstevel 
71411708Sstevel 	return (status);
71421708Sstevel }
71431708Sstevel 
71441708Sstevel static void
man_nd_getset(queue_t * wq,mblk_t * mp)71451708Sstevel man_nd_getset(queue_t *wq, mblk_t *mp)
71461708Sstevel {
71471708Sstevel 
71481708Sstevel 	if (!nd_getset(wq, man_ndlist, mp))
71491708Sstevel 		miocnak(wq, mp, 0, ENOENT);
71501708Sstevel 	else
71511708Sstevel 		qreply(wq, mp);
71521708Sstevel }
71531708Sstevel 
71541708Sstevel /*ARGSUSED*/
71551708Sstevel static int
man_pathgroups_report(queue_t * wq,mblk_t * mp,caddr_t cp,cred_t * cr)71561708Sstevel man_pathgroups_report(queue_t *wq, mblk_t *mp, caddr_t cp, cred_t *cr)
71571708Sstevel {
71581708Sstevel 
71591708Sstevel 	man_t		*manp;
71601708Sstevel 	man_pg_t	*mpg;
71611708Sstevel 	int		i;
71621708Sstevel 	char		pad[] = "                 "; /* 17 spaces */
71631708Sstevel 	int		pad_end;
71641708Sstevel 
71651708Sstevel 
71661708Sstevel 	MAN_DBG(MAN_PATH, ("man_pathgroups_report: wq(0x%p) mp(0x%p)"
71677656SSherry.Moore@Sun.COM 	    " caddr 0x%p", (void *)wq, (void *)mp, (void *)cp));
71681708Sstevel 
71691708Sstevel 	(void) mi_mpprintf(mp, "MAN Pathgroup report: (* == failed)");
71701708Sstevel 	(void) mi_mpprintf(mp, "====================================="
71717656SSherry.Moore@Sun.COM 	    "==========================================");
71721708Sstevel 
71731708Sstevel 	mutex_enter(&man_lock);
71741708Sstevel 
71751708Sstevel 	for (i = 0; i < 2; i++) {
71761708Sstevel 		manp = ddi_get_soft_state(man_softstate, i);
71771708Sstevel 		if (manp == NULL)
71781708Sstevel 			continue;
71791708Sstevel 
71801708Sstevel 	(void) mi_mpprintf(mp,
71817656SSherry.Moore@Sun.COM 	    "Interface\tDestination\t\tActive Path\tAlternate Paths");
71821708Sstevel 	(void) mi_mpprintf(mp, "---------------------------------------"
71837656SSherry.Moore@Sun.COM 	    "----------------------------------------");
71841708Sstevel 
71851708Sstevel 		for (mpg = manp->man_pg; mpg != NULL; mpg = mpg->mpg_next) {
71861708Sstevel 
71871708Sstevel 			(void) mi_mpprintf(mp, "%s%d\t\t",
71887656SSherry.Moore@Sun.COM 			    ddi_major_to_name(manp->man_meta_major),
71897656SSherry.Moore@Sun.COM 			    manp->man_meta_ppa);
71901708Sstevel 
71911708Sstevel 			if (man_is_on_domain) {
71921708Sstevel 				(void) mi_mpprintf_nr(mp, "Master SSC\t");
71931708Sstevel 				man_preport(mpg->mpg_pathp, mp);
71941708Sstevel 			} else {
71951708Sstevel 				if (i == 0) {
71967656SSherry.Moore@Sun.COM 					pad_end = 17 - strlen(ether_sprintf(
71977656SSherry.Moore@Sun.COM 					    &mpg->mpg_dst_eaddr));
71987656SSherry.Moore@Sun.COM 					if (pad_end < 0 || pad_end > 16)
71991708Sstevel 					pad_end = 0;
72007656SSherry.Moore@Sun.COM 					pad[pad_end] = '\0';
72017656SSherry.Moore@Sun.COM 
72027656SSherry.Moore@Sun.COM 					(void) mi_mpprintf_nr(mp, "%c %s%s",
72037656SSherry.Moore@Sun.COM 					    mpg->mpg_pg_id + 'A',
72047656SSherry.Moore@Sun.COM 					    ether_sprintf(&mpg->mpg_dst_eaddr),
72057656SSherry.Moore@Sun.COM 					    pad);
72067656SSherry.Moore@Sun.COM 
72077656SSherry.Moore@Sun.COM 					pad[pad_end] = ' ';
72081708Sstevel 				} else {
72097656SSherry.Moore@Sun.COM 					(void) mi_mpprintf_nr(mp,
72107656SSherry.Moore@Sun.COM 					    "Other SSC\t");
72111708Sstevel 				}
72121708Sstevel 				man_preport(mpg->mpg_pathp, mp);
72131708Sstevel 			}
72141708Sstevel 			(void) mi_mpprintf_nr(mp, "\n");
72151708Sstevel 		}
72161708Sstevel 	}
72171708Sstevel 
72181708Sstevel 	mutex_exit(&man_lock);
72191708Sstevel 	MAN_DBG(MAN_PATH, ("man_pathgroups_report: returns"));
72201708Sstevel 
72211708Sstevel 	return (0);
72221708Sstevel }
72231708Sstevel 
72241708Sstevel static void
man_preport(man_path_t * plist,mblk_t * mp)72251708Sstevel man_preport(man_path_t *plist, mblk_t *mp)
72261708Sstevel {
72271708Sstevel 	man_path_t	*ap;
72281708Sstevel 
72291708Sstevel 	ap = man_find_active_path(plist);
72301708Sstevel 	/*
72311708Sstevel 	 * Active path
72321708Sstevel 	 */
72331708Sstevel 	if (ap != NULL) {
72341708Sstevel 		(void) mi_mpprintf_nr(mp, "\t%s%d\t\t",
72357656SSherry.Moore@Sun.COM 		    ddi_major_to_name(ap->mp_device.mdev_major),
72367656SSherry.Moore@Sun.COM 		    ap->mp_device.mdev_ppa);
72371708Sstevel 	} else {
72381708Sstevel 		(void) mi_mpprintf_nr(mp, "None \t");
72391708Sstevel 	}
72401708Sstevel 
72411708Sstevel 	/*
72421708Sstevel 	 * Alternate Paths.
72431708Sstevel 	 */
72441708Sstevel 	while (plist != NULL) {
72451708Sstevel 		(void) mi_mpprintf_nr(mp, "%s%d exp %d",
72467656SSherry.Moore@Sun.COM 		    ddi_major_to_name(plist->mp_device.mdev_major),
72477656SSherry.Moore@Sun.COM 		    plist->mp_device.mdev_ppa,
72487656SSherry.Moore@Sun.COM 		    plist->mp_device.mdev_exp_id);
72491708Sstevel 		if (plist->mp_device.mdev_state & MDEV_FAILED)
72501708Sstevel 			(void) mi_mpprintf_nr(mp, "*");
72511708Sstevel 		plist = plist->mp_next;
72521708Sstevel 		if (plist)
72531708Sstevel 			(void) mi_mpprintf_nr(mp, ", ");
72541708Sstevel 	}
72551708Sstevel }
72561708Sstevel 
72571708Sstevel /*
72581708Sstevel  * NDD request to set active path. Calling context is man_ioctl, so we are
72591708Sstevel  * exclusive in the inner perimeter.
72601708Sstevel  *
72611708Sstevel  *	Syntax is "ndd -set /dev/dman <man ppa> <pg_id> <phys ppa>"
72621708Sstevel  */
72631708Sstevel /* ARGSUSED3 */
72641708Sstevel static int
man_set_active_path(queue_t * wq,mblk_t * mp,char * value,caddr_t cp,cred_t * cr)72651708Sstevel man_set_active_path(queue_t *wq, mblk_t *mp, char *value, caddr_t cp,
72661708Sstevel     cred_t *cr)
72671708Sstevel {
72681708Sstevel 	char		*end, *meta_ppap, *phys_ppap, *pg_idp;
72691708Sstevel 	int		meta_ppa;
72701708Sstevel 	int		phys_ppa;
72711708Sstevel 	int		pg_id;
72721708Sstevel 	man_t		*manp;
72731708Sstevel 	man_pg_t	*mpg;
72741708Sstevel 	man_path_t	*np;
72751708Sstevel 	mi_path_t	mpath;
72761708Sstevel 	int		status = 0;
72771708Sstevel 
72781708Sstevel 	MAN_DBG(MAN_PATH, ("man_set_active_path: wq(0x%p) mp(0x%p)"
72797656SSherry.Moore@Sun.COM 	    " args %s", (void *)wq, (void *)mp, value));
72801708Sstevel 
72811708Sstevel 	meta_ppap = value;
72821708Sstevel 
72831708Sstevel 	if ((pg_idp = strchr(value, ' ')) == NULL) {
72841708Sstevel 		status = EINVAL;
72851708Sstevel 		goto exit;
72861708Sstevel 	}
72871708Sstevel 
72881708Sstevel 	*pg_idp++ = '\0';
72891708Sstevel 
72901708Sstevel 	if ((phys_ppap = strchr(pg_idp, ' ')) == NULL) {
72911708Sstevel 		status = EINVAL;
72921708Sstevel 		goto exit;
72931708Sstevel 	}
72941708Sstevel 
72951708Sstevel 	*phys_ppap++ = '\0';
72961708Sstevel 
72971708Sstevel 	meta_ppa = (int)mi_strtol(meta_ppap, &end, 10);
72981708Sstevel 	pg_id = (int)mi_strtol(pg_idp, &end, 10);
72991708Sstevel 	phys_ppa = (int)mi_strtol(phys_ppap, &end, 10);
73001708Sstevel 
73011708Sstevel 	mutex_enter(&man_lock);
73021708Sstevel 	manp = ddi_get_soft_state(man_softstate, meta_ppa);
73031708Sstevel 	if (manp == NULL || manp->man_pg == NULL) {
73041708Sstevel 		status = EINVAL;
73051708Sstevel 		mutex_exit(&man_lock);
73061708Sstevel 		goto exit;
73071708Sstevel 	}
73081708Sstevel 
73091708Sstevel 	mpg = man_find_pg_by_id(manp->man_pg, pg_id);
73101708Sstevel 	if (mpg == NULL) {
73111708Sstevel 		status = EINVAL;
73121708Sstevel 		mutex_exit(&man_lock);
73131708Sstevel 		goto exit;
73141708Sstevel 	}
73151708Sstevel 
73161708Sstevel 	np = man_find_path_by_ppa(mpg->mpg_pathp, phys_ppa);
73171708Sstevel 
73181708Sstevel 	if (np == NULL) {
73191708Sstevel 		status = EINVAL;
73201708Sstevel 		mutex_exit(&man_lock);
73211708Sstevel 		goto exit;
73221708Sstevel 	}
73231708Sstevel 
73241708Sstevel 	mpath.mip_cmd = MI_PATH_ACTIVATE;
73251708Sstevel 	mpath.mip_pg_id = pg_id;
73261708Sstevel 	mpath.mip_man_ppa = meta_ppa;
73271708Sstevel 	mpath.mip_devs[0] = np->mp_device;
73281708Sstevel 	mpath.mip_ndevs = 1;
73291708Sstevel 
73301708Sstevel 	status = man_pg_cmd(&mpath, NULL);
73311708Sstevel 	mutex_exit(&man_lock);
73321708Sstevel 
73331708Sstevel exit:
73341708Sstevel 
73351708Sstevel 	MAN_DBG(MAN_PATH, ("man_set_active_path: returns %d", status));
73361708Sstevel 
73371708Sstevel 	return (status);
73381708Sstevel }
73391708Sstevel 
73401708Sstevel /*
73411708Sstevel  * Dump out the contents of the IOSRAM handoff structure. Note that if
73421708Sstevel  * anything changes here, you must make sure that the sysinit script
73431708Sstevel  * stays in sync with this output.
73441708Sstevel  */
73451708Sstevel /* ARGSUSED */
73461708Sstevel static int
man_get_hostinfo(queue_t * wq,mblk_t * mp,caddr_t cp,cred_t * cr)73471708Sstevel man_get_hostinfo(queue_t *wq, mblk_t *mp, caddr_t cp, cred_t *cr)
73481708Sstevel {
73491708Sstevel 	manc_t	manc;
73501708Sstevel 	char	*ipaddr;
73511708Sstevel 	char	ipv6addr[INET6_ADDRSTRLEN];
73521708Sstevel 	int	i;
73531708Sstevel 	int	status;
73541708Sstevel 
73551708Sstevel 	if (!man_is_on_domain)
73561708Sstevel 		return (0);
73571708Sstevel 
73581708Sstevel 	if (status = man_get_iosram(&manc)) {
73591708Sstevel 		return (status);
73601708Sstevel 	}
73611708Sstevel 
7362*11311SSurya.Prakki@Sun.COM 	(void) mi_mpprintf(mp, "manc_magic = 0x%x", manc.manc_magic);
7363*11311SSurya.Prakki@Sun.COM 	(void) mi_mpprintf(mp, "manc_version = 0%d", manc.manc_version);
7364*11311SSurya.Prakki@Sun.COM 	(void) mi_mpprintf(mp, "manc_csum = 0x%x", manc.manc_csum);
73651708Sstevel 
73661708Sstevel 	if (manc.manc_ip_type == AF_INET) {
73671708Sstevel 		in_addr_t	netnum;
73681708Sstevel 
7369*11311SSurya.Prakki@Sun.COM 		(void) mi_mpprintf(mp, "manc_ip_type = AF_INET");
73701708Sstevel 
73711708Sstevel 		ipaddr = man_inet_ntoa(manc.manc_dom_ipaddr);
7372*11311SSurya.Prakki@Sun.COM 		(void) mi_mpprintf(mp, "manc_dom_ipaddr = %s", ipaddr);
73731708Sstevel 
73741708Sstevel 		ipaddr = man_inet_ntoa(manc.manc_dom_ip_netmask);
7375*11311SSurya.Prakki@Sun.COM 		(void) mi_mpprintf(mp, "manc_dom_ip_netmask = %s", ipaddr);
73761708Sstevel 
73771708Sstevel 		netnum = manc.manc_dom_ipaddr & manc.manc_dom_ip_netmask;
73781708Sstevel 		ipaddr = man_inet_ntoa(netnum);
7379*11311SSurya.Prakki@Sun.COM 		(void) mi_mpprintf(mp, "manc_dom_ip_netnum = %s", ipaddr);
73801708Sstevel 
73811708Sstevel 		ipaddr = man_inet_ntoa(manc.manc_sc_ipaddr);
7382*11311SSurya.Prakki@Sun.COM 		(void) mi_mpprintf(mp, "manc_sc_ipaddr = %s", ipaddr);
73831708Sstevel 
73841708Sstevel 	} else if (manc.manc_ip_type == AF_INET6) {
73851708Sstevel 
7386*11311SSurya.Prakki@Sun.COM 		(void) mi_mpprintf(mp, "manc_ip_type = AF_INET6");
73871708Sstevel 
73881708Sstevel 		(void) inet_ntop(AF_INET6, (void *)&manc.manc_dom_ipv6addr,
73897656SSherry.Moore@Sun.COM 		    ipv6addr, INET6_ADDRSTRLEN);
7390*11311SSurya.Prakki@Sun.COM 		(void) mi_mpprintf(mp, "manc_dom_ipv6addr = %s", ipv6addr);
7391*11311SSurya.Prakki@Sun.COM 
7392*11311SSurya.Prakki@Sun.COM 		(void) mi_mpprintf(mp, "manc_dom_ipv6_netmask = %d",
73937656SSherry.Moore@Sun.COM 		    manc.manc_dom_ipv6_netmask.s6_addr[0]);
73941708Sstevel 
73951708Sstevel 		(void) inet_ntop(AF_INET6, (void *)&manc.manc_sc_ipv6addr,
73967656SSherry.Moore@Sun.COM 		    ipv6addr, INET6_ADDRSTRLEN);
7397*11311SSurya.Prakki@Sun.COM 		(void) mi_mpprintf(mp, "manc_sc_ipv6addr = %s", ipv6addr);
73981708Sstevel 
73991708Sstevel 	} else {
74001708Sstevel 
7401*11311SSurya.Prakki@Sun.COM 		(void) mi_mpprintf(mp, "manc_ip_type = NONE");
7402*11311SSurya.Prakki@Sun.COM 	}
7403*11311SSurya.Prakki@Sun.COM 
7404*11311SSurya.Prakki@Sun.COM 	(void) mi_mpprintf(mp, "manc_dom_eaddr = %s",
74057656SSherry.Moore@Sun.COM 	    ether_sprintf(&manc.manc_dom_eaddr));
7406*11311SSurya.Prakki@Sun.COM 	(void) mi_mpprintf(mp, "manc_sc_eaddr = %s",
74077656SSherry.Moore@Sun.COM 	    ether_sprintf(&manc.manc_sc_eaddr));
74081708Sstevel 
7409*11311SSurya.Prakki@Sun.COM 	(void) mi_mpprintf(mp, "manc_iob_bitmap = 0x%x\tio boards = ",
74107656SSherry.Moore@Sun.COM 	    manc.manc_iob_bitmap);
74111708Sstevel 	for (i = 0; i < MAN_MAX_EXPANDERS; i++) {
74121708Sstevel 		if ((manc.manc_iob_bitmap >> i) & 0x1) {
7413*11311SSurya.Prakki@Sun.COM 			(void) mi_mpprintf_nr(mp, "%d.1, ", i);
74141708Sstevel 		}
74151708Sstevel 	}
7416*11311SSurya.Prakki@Sun.COM 	(void) mi_mpprintf(mp, "manc_golden_iob = %d", manc.manc_golden_iob);
74171708Sstevel 
74181708Sstevel 	return (0);
74191708Sstevel }
74201708Sstevel 
74211708Sstevel static char *
man_inet_ntoa(in_addr_t in)74221708Sstevel man_inet_ntoa(in_addr_t in)
74231708Sstevel {
74241708Sstevel 	static char b[18];
74251708Sstevel 	unsigned char *p;
74261708Sstevel 
74271708Sstevel 	p = (unsigned char *)&in;
74281708Sstevel 	(void) sprintf(b, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]);
74291708Sstevel 	return (b);
74301708Sstevel }
74311708Sstevel 
74321708Sstevel /*
74331708Sstevel  * parameter value. cp points to the required parameter.
74341708Sstevel  */
74351708Sstevel /* ARGSUSED */
74361708Sstevel static int
man_param_get(queue_t * q,mblk_t * mp,caddr_t cp,cred_t * cr)74371708Sstevel man_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr)
74381708Sstevel {
74391708Sstevel 	param_t	*manpa = (param_t *)cp;
74401708Sstevel 
74411708Sstevel 	(void) mi_mpprintf(mp, "%u", manpa->param_val);
74421708Sstevel 	return (0);
74431708Sstevel }
74441708Sstevel 
74451708Sstevel /*
74461708Sstevel  * Sets the man parameter to the value in the param_register using
74471708Sstevel  * nd_load().
74481708Sstevel  */
74491708Sstevel /* ARGSUSED */
74501708Sstevel static int
man_param_set(queue_t * q,mblk_t * mp,char * value,caddr_t cp,cred_t * cr)74511708Sstevel man_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, cred_t *cr)
74521708Sstevel {
74531708Sstevel 	char *end;
74541708Sstevel 	size_t new_value;
74551708Sstevel 	param_t	*manpa = (param_t *)cp;
74561708Sstevel 
74571708Sstevel 	new_value = mi_strtol(value, &end, 10);
74581708Sstevel 
74591708Sstevel 	if (end == value || new_value < manpa->param_min ||
74601708Sstevel 	    new_value > manpa->param_max) {
74611708Sstevel 			return (EINVAL);
74621708Sstevel 	}
74631708Sstevel 
74641708Sstevel 	manpa->param_val = new_value;
74651708Sstevel 
74661708Sstevel 	return (0);
74671708Sstevel 
74681708Sstevel }
74691708Sstevel 
74701708Sstevel /*
74711708Sstevel  * Free the Named Dispatch Table by calling man_nd_free
74721708Sstevel  */
74731708Sstevel static void
man_param_cleanup()74741708Sstevel man_param_cleanup()
74751708Sstevel {
74761708Sstevel 	if (man_ndlist != NULL)
74771708Sstevel 		nd_free(&man_ndlist);
74781708Sstevel }
74791708Sstevel 
74801708Sstevel /*
74811708Sstevel  * Free the table pointed to by 'ndp'
74821708Sstevel  */
74831708Sstevel static void
man_nd_free(caddr_t * nd_pparam)74841708Sstevel man_nd_free(caddr_t *nd_pparam)
74851708Sstevel {
74861708Sstevel 	ND	*nd;
74871708Sstevel 
74881708Sstevel 	if ((nd = (ND *)(*nd_pparam)) != NULL) {
74891708Sstevel 		if (nd->nd_tbl)
74901708Sstevel 			mi_free((char *)nd->nd_tbl);
74911708Sstevel 		mi_free((char *)nd);
74921708Sstevel 		*nd_pparam = NULL;
74931708Sstevel 	}
74941708Sstevel }
74951708Sstevel 
74961708Sstevel 
74971708Sstevel /*
74981708Sstevel  * man_kstat_update - update the statistics for a meta-interface.
74991708Sstevel  *
75001708Sstevel  *	ksp - kstats struct
75011708Sstevel  *	rw - flag indicating whether stats are to be read or written.
75021708Sstevel  *
75031708Sstevel  *	returns	0
75041708Sstevel  *
75051708Sstevel  * The destination specific kstat information is protected by the
75061708Sstevel  * perimeter lock, so we submit a work request to get the stats
75071708Sstevel  * updated (see man_do_kstats()), and then collect the results
75081708Sstevel  * when cv_signal'd. Note that we are doing cv_timedwait_sig()
75091708Sstevel  * as a precautionary measure only.
75101708Sstevel  */
75111708Sstevel static int
man_kstat_update(kstat_t * ksp,int rw)75121708Sstevel man_kstat_update(kstat_t *ksp, int rw)
75131708Sstevel {
75141708Sstevel 	man_t			*manp;		/* per instance data */
75151708Sstevel 	man_work_t		*wp;
75161708Sstevel 	int			status = 0;
75171708Sstevel 	kstat_named_t		*knp;
75181708Sstevel 	kstat_named_t		*man_knp;
75191708Sstevel 	int			i;
75201708Sstevel 
75211708Sstevel 	MAN_DBG(MAN_KSTAT, ("man_kstat_update: %s\n", rw ? "KSTAT_WRITE" :
75227656SSherry.Moore@Sun.COM 	    "KSTAT_READ"));
75231708Sstevel 
75241708Sstevel 	mutex_enter(&man_lock);
75251708Sstevel 	manp = (man_t *)ksp->ks_private;
75261708Sstevel 	manp->man_refcnt++;
75271708Sstevel 
75281708Sstevel 	/*
75291708Sstevel 	 * If the driver has been configured, get kstats updated by inner
75301708Sstevel 	 * perimeter prior to retrieving.
75311708Sstevel 	 */
75321708Sstevel 	if (man_config_state == MAN_CONFIGURED) {
75331708Sstevel 		clock_t wait_status;
75341708Sstevel 
75351708Sstevel 		man_update_path_kstats(manp);
75361708Sstevel 		wp = man_work_alloc(MAN_WORK_KSTAT_UPDATE, KM_SLEEP);
75371708Sstevel 		wp->mw_arg.a_man_ppa = manp->man_meta_ppa;
75381708Sstevel 		wp->mw_flags = MAN_WFLAGS_CVWAITER;
75391708Sstevel 		man_work_add(man_iwork_q, wp);
75401708Sstevel 
754111066Srafael.vanoni@sun.com 		wait_status = cv_reltimedwait_sig(&wp->mw_cv, &man_lock,
754211066Srafael.vanoni@sun.com 		    drv_usectohz(manp->man_kstat_waittime), TR_CLOCK_TICK);
75431708Sstevel 
75441708Sstevel 		if (wp->mw_flags & MAN_WFLAGS_DONE) {
75451708Sstevel 			status = wp->mw_status;
75461708Sstevel 			man_work_free(wp);
75471708Sstevel 		} else {
75481708Sstevel 			ASSERT(wait_status <= 0);
75491708Sstevel 			wp->mw_flags &= ~MAN_WFLAGS_CVWAITER;
75501708Sstevel 			if (wait_status == 0)
75511708Sstevel 				status = EINTR;
75521708Sstevel 			else {
75531708Sstevel 				MAN_DBG(MAN_KSTAT, ("man_kstat_update: "
75547656SSherry.Moore@Sun.COM 				    "timedout, returning stale stats."));
75551708Sstevel 				status = 0;
75561708Sstevel 			}
75571708Sstevel 		}
75581708Sstevel 		if (status)
75591708Sstevel 			goto exit;
75601708Sstevel 	}
75611708Sstevel 
75621708Sstevel 	knp = (kstat_named_t *)ksp->ks_data;
75631708Sstevel 	man_knp = (kstat_named_t *)manp->man_ksp->ks_data;
75641708Sstevel 
75651708Sstevel 	if (rw == KSTAT_READ) {
75661708Sstevel 		for (i = 0; i < MAN_NUMSTATS; i++) {
75671708Sstevel 			knp[i].value.ui64 = man_knp[i].value.ui64;
75681708Sstevel 		}
75691708Sstevel 	} else {
75701708Sstevel 		for (i = 0; i < MAN_NUMSTATS; i++) {
75711708Sstevel 			man_knp[i].value.ui64 = knp[i].value.ui64;
75721708Sstevel 		}
75731708Sstevel 	}
75741708Sstevel 
75751708Sstevel exit:
75761708Sstevel 	manp->man_refcnt--;
75771708Sstevel 	mutex_exit(&man_lock);
75781708Sstevel 
75791708Sstevel 	MAN_DBG(MAN_KSTAT, ("man_kstat_update: returns %d", status));
75801708Sstevel 
75811708Sstevel 	return (status);
75821708Sstevel }
75831708Sstevel 
75841708Sstevel /*
75851708Sstevel  * Sum destination kstats for all active paths for a given instance of the
75861708Sstevel  * MAN driver. Called with perimeter lock.
75871708Sstevel  */
75881708Sstevel static void
man_do_kstats(man_work_t * wp)75891708Sstevel man_do_kstats(man_work_t *wp)
75901708Sstevel {
75911708Sstevel 	man_t		*manp;
75921708Sstevel 	man_pg_t	*mpg;
75931708Sstevel 	man_path_t	*mp;
75941708Sstevel 
75951708Sstevel 	MAN_DBG(MAN_KSTAT, ("man_do_kstats:"));
75961708Sstevel 
75971708Sstevel 	mutex_enter(&man_lock);
75981708Sstevel 	/*
75991708Sstevel 	 * Sync mp_last_knp for each path associated with the MAN instance.
76001708Sstevel 	 */
76011708Sstevel 	manp = (man_t *)ddi_get_soft_state(man_softstate,
76027656SSherry.Moore@Sun.COM 	    wp->mw_arg.a_man_ppa);
76031708Sstevel 	for (mpg = manp->man_pg; mpg != NULL; mpg = mpg->mpg_next) {
76041708Sstevel 
76051708Sstevel 		ASSERT(mpg->mpg_man_ppa == manp->man_meta_ppa);
76061708Sstevel 
76071708Sstevel 		if ((mp = man_find_active_path(mpg->mpg_pathp)) != NULL) {
76081708Sstevel 
76091708Sstevel 			MAN_DBG(MAN_KSTAT, ("\tkstat: path"));
76101708Sstevel 			MAN_DBGCALL(MAN_KSTAT, man_print_path(mp));
76111708Sstevel 
76121708Sstevel 			/*
76131708Sstevel 			 * We just to update the destination statistics here.
76141708Sstevel 			 */
76151708Sstevel 			man_sum_dests_kstats(mp->mp_last_knp, mpg);
76161708Sstevel 		}
76171708Sstevel 	}
76181708Sstevel 	mutex_exit(&man_lock);
76191708Sstevel 	MAN_DBG(MAN_KSTAT, ("man_do_kstats: returns"));
76201708Sstevel }
76211708Sstevel 
76221708Sstevel /*
76231708Sstevel  * Sum device kstats for all active paths for a given instance of the
76241708Sstevel  * MAN driver. Called with man_lock.
76251708Sstevel  */
76261708Sstevel static void
man_update_path_kstats(man_t * manp)76271708Sstevel man_update_path_kstats(man_t *manp)
76281708Sstevel {
76291708Sstevel 	kstat_named_t	*man_knp;
76301708Sstevel 	man_pg_t	*mpg;
76311708Sstevel 	man_path_t	*mp;
76321708Sstevel 
76331708Sstevel 	ASSERT(MUTEX_HELD(&man_lock));
76341708Sstevel 	MAN_DBG(MAN_KSTAT, ("man_update_path_kstats:"));
76351708Sstevel 
76361708Sstevel 	man_knp = (kstat_named_t *)manp->man_ksp->ks_data;
76371708Sstevel 
76381708Sstevel 	for (mpg = manp->man_pg; mpg != NULL; mpg = mpg->mpg_next) {
76391708Sstevel 
76401708Sstevel 		ASSERT(mpg->mpg_man_ppa == manp->man_meta_ppa);
76411708Sstevel 
76421708Sstevel 		if ((mp = man_find_active_path(mpg->mpg_pathp)) != NULL) {
76431708Sstevel 
76441708Sstevel 			man_update_dev_kstats(man_knp, mp);
76451708Sstevel 
76461708Sstevel 		}
76471708Sstevel 	}
76481708Sstevel 	MAN_DBG(MAN_KSTAT, ("man_update_path_kstats: returns"));
76491708Sstevel }
76501708Sstevel 
76511708Sstevel /*
76521708Sstevel  * Update the device kstats.
76531708Sstevel  * As man_kstat_update() is called with kstat_chain_lock held,
76541708Sstevel  * we can safely update the statistics from the underlying driver here.
76551708Sstevel  */
76561708Sstevel static void
man_update_dev_kstats(kstat_named_t * man_knp,man_path_t * mp)76571708Sstevel man_update_dev_kstats(kstat_named_t *man_knp, man_path_t *mp)
76581708Sstevel {
76591708Sstevel 	kstat_t		*dev_ksp;
76601708Sstevel 	major_t		major;
76611708Sstevel 	int		instance;
76621708Sstevel 	char		buf[KSTAT_STRLEN];
76631708Sstevel 
76641708Sstevel 
76651708Sstevel 	major = mp->mp_device.mdev_major;
76661708Sstevel 	instance = mp->mp_device.mdev_ppa;
76671708Sstevel 	(void) sprintf(buf, "%s%d", ddi_major_to_name(major), instance);
76681708Sstevel 
76691708Sstevel 	dev_ksp = kstat_hold_byname(ddi_major_to_name(major), instance, buf,
76701708Sstevel 	    ALL_ZONES);
76711708Sstevel 	if (dev_ksp != NULL) {
76721708Sstevel 
76731708Sstevel 		KSTAT_ENTER(dev_ksp);
76741708Sstevel 		KSTAT_UPDATE(dev_ksp, KSTAT_READ);
76751708Sstevel 		man_sum_kstats(man_knp, dev_ksp, mp->mp_last_knp);
76761708Sstevel 		KSTAT_EXIT(dev_ksp);
76771708Sstevel 		kstat_rele(dev_ksp);
76781708Sstevel 
76791708Sstevel 	} else {
76801708Sstevel 		MAN_DBG(MAN_KSTAT,
76817656SSherry.Moore@Sun.COM 		    ("man_update_dev_kstats: no kstat data found for %s(%d,%d)",
76827656SSherry.Moore@Sun.COM 		    buf, major, instance));
76831708Sstevel 	}
76841708Sstevel }
76851708Sstevel 
76861708Sstevel static void
man_sum_dests_kstats(kstat_named_t * knp,man_pg_t * mpg)76871708Sstevel man_sum_dests_kstats(kstat_named_t *knp, man_pg_t *mpg)
76881708Sstevel {
76891708Sstevel 	int		i;
76901708Sstevel 	int		flags;
76911708Sstevel 	char		*statname;
76921708Sstevel 	manstr_t	*msp;
76931708Sstevel 	man_dest_t	*mdp;
76941708Sstevel 	uint64_t	switches = 0;
76951708Sstevel 	uint64_t	linkfails = 0;
76961708Sstevel 	uint64_t	linkstales = 0;
76971708Sstevel 	uint64_t	icmpv4probes = 0;
76981708Sstevel 	uint64_t	icmpv6probes = 0;
76991708Sstevel 
77001708Sstevel 	MAN_DBG(MAN_KSTAT, ("man_sum_dests_kstats: mpg 0x%p", (void *)mpg));
77011708Sstevel 
77021708Sstevel 	for (msp = man_strup; msp != NULL; msp = msp->ms_next) {
77031708Sstevel 
77041708Sstevel 		if (!man_str_uses_pg(msp, mpg))
77051708Sstevel 			continue;
77061708Sstevel 
77071708Sstevel 		mdp = &msp->ms_dests[mpg->mpg_pg_id];
77081708Sstevel 
77091708Sstevel 		switches += mdp->md_switches;
77101708Sstevel 		linkfails += mdp->md_linkfails;
77111708Sstevel 		linkstales += mdp->md_linkstales;
77121708Sstevel 		icmpv4probes += mdp->md_icmpv4probes;
77131708Sstevel 		icmpv6probes += mdp->md_icmpv6probes;
77141708Sstevel 	}
77151708Sstevel 
77161708Sstevel 	for (i = 0; i < MAN_NUMSTATS; i++) {
77171708Sstevel 
77181708Sstevel 		statname = man_kstat_info[i].mk_name;
77191708Sstevel 		flags = man_kstat_info[i].mk_flags;
77201708Sstevel 
77211708Sstevel 		if (!(flags & MK_NOT_PHYSICAL))
77221708Sstevel 			continue;
77231708Sstevel 
77241708Sstevel 		if (strcmp(statname, "man_switches") == 0) {
77251708Sstevel 			knp[i].value.ui64 = switches;
77261708Sstevel 		} else if (strcmp(statname, "man_link_fails") == 0) {
77271708Sstevel 			knp[i].value.ui64 = linkfails;
77281708Sstevel 		} else if (strcmp(statname, "man_link_stales") == 0) {
77291708Sstevel 			knp[i].value.ui64 = linkstales;
77301708Sstevel 		} else if (strcmp(statname, "man_icmpv4_probes") == 0) {
77311708Sstevel 			knp[i].value.ui64 = icmpv4probes;
77321708Sstevel 		} else if (strcmp(statname, "man_icmpv6_probes") == 0) {
77331708Sstevel 			knp[i].value.ui64 = icmpv6probes;
77341708Sstevel 		}
77351708Sstevel 	}
77361708Sstevel 
77371708Sstevel 	MAN_DBG(MAN_KSTAT, ("man_sum_dests_kstats: returns"));
77381708Sstevel }
77391708Sstevel 
77401708Sstevel /*
77411708Sstevel  * Initialize MAN named kstats in the space provided.
77421708Sstevel  */
77431708Sstevel static void
man_kstat_named_init(kstat_named_t * knp,int num_stats)77441708Sstevel man_kstat_named_init(kstat_named_t *knp, int num_stats)
77451708Sstevel {
77461708Sstevel 	int	i;
77471708Sstevel 
77481708Sstevel 	MAN_DBG(MAN_KSTAT, ("man_kstat_named_init: knp(0x%p) num_stats = %d",
77497656SSherry.Moore@Sun.COM 	    (void *)knp, num_stats));
77501708Sstevel 
77511708Sstevel 	for (i = 0; i < num_stats; i++) {
77521708Sstevel 		kstat_named_init(&knp[i], man_kstat_info[i].mk_name,
77537656SSherry.Moore@Sun.COM 		    man_kstat_info[i].mk_type);
77541708Sstevel 	}
77551708Sstevel 
77561708Sstevel 	MAN_DBG(MAN_KSTAT, ("man_kstat_named_init: returns"));
77571708Sstevel 
77581708Sstevel }
77591708Sstevel 
77601708Sstevel /*
77611708Sstevel  * man_kstat_byname - get a kernel stat value from its structure
77621708Sstevel  *
77631708Sstevel  *	ksp - kstat_t structure to play with
77641708Sstevel  *	s   - string to match names with
77651708Sstevel  *	res - in/out result data pointer
77661708Sstevel  *
77671708Sstevel  *	returns	- success - 1 (found)
77681708Sstevel  *		- failure - 0 (not found)
77691708Sstevel  */
77701708Sstevel static int
man_kstat_byname(kstat_t * ksp,char * s,kstat_named_t * res)77711708Sstevel man_kstat_byname(kstat_t *ksp, char *s, kstat_named_t *res)
77721708Sstevel {
77731708Sstevel 	int		found = 0;
77741708Sstevel 
77751708Sstevel 	MAN_DBG(MAN_KSTAT2, ("man_kstat_byname: GETTING %s\n", s));
77761708Sstevel 
77771708Sstevel 	if (ksp->ks_type == KSTAT_TYPE_NAMED) {
77781708Sstevel 		kstat_named_t *knp;
77791708Sstevel 
77801708Sstevel 		for (knp = KSTAT_NAMED_PTR(ksp);
77811708Sstevel 		    (caddr_t)knp < ((caddr_t)ksp->ks_data+ksp->ks_data_size);
77821708Sstevel 		    knp++) {
77831708Sstevel 
77841708Sstevel 			if (strcmp(s, knp->name) == NULL) {
77851708Sstevel 
77861708Sstevel 				res->data_type = knp->data_type;
77871708Sstevel 				res->value = knp->value;
77881708Sstevel 				found++;
77891708Sstevel 
77901708Sstevel 				MAN_DBG(MAN_KSTAT2, ("\t%s: %d\n", knp->name,
77917656SSherry.Moore@Sun.COM 				    (int)knp->value.ul));
77921708Sstevel 			}
77931708Sstevel 		}
77941708Sstevel 	} else {
77951708Sstevel 		MAN_DBG(MAN_KSTAT2, ("\tbad kstats type %d\n", ksp->ks_type));
77961708Sstevel 	}
77971708Sstevel 
77981708Sstevel 	/*
77991708Sstevel 	 * if getting a value but couldn't find the namestring, result = 0.
78001708Sstevel 	 */
78011708Sstevel 	if (!found) {
78021708Sstevel 		/*
78031708Sstevel 		 * a reasonable default
78041708Sstevel 		 */
78051708Sstevel 		res->data_type = KSTAT_DATA_ULONG;
78061708Sstevel 		res->value.l = 0;
78071708Sstevel 		MAN_DBG(MAN_KSTAT2, ("\tcouldn't find, using defaults\n"));
78081708Sstevel 	}
78091708Sstevel 
78101708Sstevel 	MAN_DBG(MAN_KSTAT2, ("man_kstat_byname: returns\n"));
78111708Sstevel 
78121708Sstevel 	return (found);
78131708Sstevel }
78141708Sstevel 
78151708Sstevel 
78161708Sstevel /*
78171708Sstevel  *
78181708Sstevel  * Accumulate MAN driver kstats from the incremental values of the underlying
78191708Sstevel  * physical interfaces.
78201708Sstevel  *
78211708Sstevel  * Parameters:
78221708Sstevel  *	sum_knp		- The named kstat area to put cumulative value,
78231708Sstevel  *			  NULL if we just want to sync next two params.
78241708Sstevel  *	phys_ksp	- Physical interface kstat_t pointer. Contains
78251708Sstevel  *			  more current counts.
78261708Sstevel  * 	phys_last_knp	- counts from the last time we were called for this
78271708Sstevel  *			  physical interface. Note that the name kstats
78281708Sstevel  *			  pointed to are actually in MAN format, but they
78291708Sstevel  *			  hold the mirrored physical devices last read
78301708Sstevel  *			  kstats.
78311708Sstevel  * Basic algorithm is:
78321708Sstevel  *
78331708Sstevel  * 	for each named kstat variable {
78341708Sstevel  *	    sum_knp[i] += (phys_ksp->ksp_data[i] - phys_last_knp[i]);
78351708Sstevel  *	    phys_last_knp[i] = phys_ksp->ksp_data[i];
78361708Sstevel  *	}
78371708Sstevel  *
78381708Sstevel  */
78391708Sstevel static void
man_sum_kstats(kstat_named_t * sum_knp,kstat_t * phys_ksp,kstat_named_t * phys_last_knp)78401708Sstevel man_sum_kstats(kstat_named_t *sum_knp, kstat_t *phys_ksp,
78411708Sstevel 	kstat_named_t *phys_last_knp)
78421708Sstevel {
78431708Sstevel 	char		*physname;
78441708Sstevel 	char		*physalias;
78451708Sstevel 	char		*statname;
78461708Sstevel 	kstat_named_t	phys_kn_entry;
78471708Sstevel 	uint64_t	delta64;
78481708Sstevel 	int		i;
78491708Sstevel 
78501708Sstevel 	MAN_DBG(MAN_KSTAT, ("man_sum_kstats: sum_knp(0x%p) phys_ksp(0x%p)"
78517656SSherry.Moore@Sun.COM 	    " phys_last_knp(0x%p)\n", (void *)sum_knp, (void *)phys_ksp,
78527656SSherry.Moore@Sun.COM 	    (void *)phys_last_knp));
78531708Sstevel 
78541708Sstevel 	/*
78551708Sstevel 	 * Now for each entry in man_kstat_info, sum the named kstat.
78561708Sstevel 	 * Not that all MAN specific kstats will end up !found.
78571708Sstevel 	 */
78581708Sstevel 	for (i = 0; i < MAN_NUMSTATS; i++) {
78591708Sstevel 		int	found = 0;
78601708Sstevel 		int	flags = 0;
78611708Sstevel 
78621708Sstevel 		delta64 = 0;
78631708Sstevel 
78641708Sstevel 		statname = man_kstat_info[i].mk_name;
78651708Sstevel 		physname = man_kstat_info[i].mk_physname;
78661708Sstevel 		physalias = man_kstat_info[i].mk_physalias;
78671708Sstevel 		flags = man_kstat_info[i].mk_flags;
78681708Sstevel 
78691708Sstevel 		/*
78701708Sstevel 		 * Update MAN private kstats.
78711708Sstevel 		 */
78721708Sstevel 		if (flags & MK_NOT_PHYSICAL) {
78731708Sstevel 
78741708Sstevel 			kstat_named_t	*knp = phys_last_knp;
78751708Sstevel 
78761708Sstevel 			if (sum_knp == NULL)
78771708Sstevel 				continue;
78781708Sstevel 
78791708Sstevel 			if (strcmp(statname, "man_switches") == 0) {
78801708Sstevel 				sum_knp[i].value.ui64 = knp[i].value.ui64;
78811708Sstevel 			} else if (strcmp(statname, "man_link_fails") == 0) {
78821708Sstevel 				sum_knp[i].value.ui64 = knp[i].value.ui64;
78831708Sstevel 			} else if (strcmp(statname, "man_link_stales") == 0) {
78841708Sstevel 				sum_knp[i].value.ui64 = knp[i].value.ui64;
78851708Sstevel 			} else if (strcmp(statname, "man_icmpv4_probes") == 0) {
78861708Sstevel 				sum_knp[i].value.ui64 = knp[i].value.ui64;
78871708Sstevel 			} else if (strcmp(statname, "man_icmpv6_probes") == 0) {
78881708Sstevel 				sum_knp[i].value.ui64 = knp[i].value.ui64;
78891708Sstevel 			}
78901708Sstevel 
78911708Sstevel 			continue;	/* phys_ksp doesnt have this stat */
78921708Sstevel 		}
78931708Sstevel 
78941708Sstevel 		/*
78951708Sstevel 		 * first try it by the "official" name
78961708Sstevel 		 */
78971708Sstevel 		if (phys_ksp) {
78981708Sstevel 			if (man_kstat_byname(phys_ksp, physname,
78997656SSherry.Moore@Sun.COM 			    &phys_kn_entry)) {
79001708Sstevel 
79011708Sstevel 				found = 1;
79021708Sstevel 
79031708Sstevel 			} else if ((physalias) && (man_kstat_byname(phys_ksp,
79047656SSherry.Moore@Sun.COM 			    physalias, &phys_kn_entry))) {
79051708Sstevel 
79061708Sstevel 				found = 1;
79071708Sstevel 			}
79081708Sstevel 		}
79091708Sstevel 
79101708Sstevel 		if (!found) {
79111708Sstevel 			/*
79121708Sstevel 			 * clear up the "last" value, no change to the sum
79131708Sstevel 			 */
79141708Sstevel 			phys_last_knp[i].value.ui64 = 0;
79151708Sstevel 			continue;
79161708Sstevel 		}
79171708Sstevel 
79181708Sstevel 		/*
79191708Sstevel 		 * at this point, we should have the good underlying
79201708Sstevel 		 * kstat value stored in phys_kn_entry
79211708Sstevel 		 */
79221708Sstevel 		if (flags & MK_NOT_COUNTER) {
79231708Sstevel 			/*
79241708Sstevel 			 * it isn't a counter, so store the value and
79251708Sstevel 			 * move on (e.g. ifspeed)
79261708Sstevel 			 */
79271708Sstevel 			phys_last_knp[i].value = phys_kn_entry.value;
79281708Sstevel 			continue;
79291708Sstevel 		}
79301708Sstevel 
79311708Sstevel 		switch (phys_kn_entry.data_type) {
79321708Sstevel 		case KSTAT_DATA_UINT32:
79331708Sstevel 
79341708Sstevel 			/*
79351708Sstevel 			 * this handles 32-bit wrapping
79361708Sstevel 			 */
79371708Sstevel 			if (phys_kn_entry.value.ui32 <
79387656SSherry.Moore@Sun.COM 			    phys_last_knp[i].value.ui32) {
79391708Sstevel 
79401708Sstevel 				/*
79411708Sstevel 				 * we've wrapped!
79421708Sstevel 				 */
79431708Sstevel 				delta64 += (UINT_MAX -
79447656SSherry.Moore@Sun.COM 				    phys_last_knp[i].value.ui32);
79451708Sstevel 				phys_last_knp[i].value.ui32 = 0;
79461708Sstevel 			}
79471708Sstevel 
79481708Sstevel 			delta64 += phys_kn_entry.value.ui32 -
79497656SSherry.Moore@Sun.COM 			    phys_last_knp[i].value.ui32;
79501708Sstevel 			phys_last_knp[i].value.ui32 = phys_kn_entry.value.ui32;
79511708Sstevel 			break;
79521708Sstevel 
79531708Sstevel 		default:
79541708Sstevel 			/*
79551708Sstevel 			 * must be a 64-bit value, we ignore 64-bit
79561708Sstevel 			 * wraps, since they shouldn't ever happen
79571708Sstevel 			 * within the life of a machine (if we assume
79581708Sstevel 			 * machines don't stay up for more than a few
79591708Sstevel 			 * hundred years without a reboot...)
79601708Sstevel 			 */
79611708Sstevel 			delta64 = phys_kn_entry.value.ui64 -
79627656SSherry.Moore@Sun.COM 			    phys_last_knp[i].value.ui64;
79631708Sstevel 			phys_last_knp[i].value.ui64 = phys_kn_entry.value.ui64;
79641708Sstevel 		}
79651708Sstevel 
79661708Sstevel 		if (sum_knp != NULL) {
79671708Sstevel 			/*
79681708Sstevel 			 * now we need to save the value
79691708Sstevel 			 */
79701708Sstevel 			switch (sum_knp[i].data_type) {
79711708Sstevel 			case KSTAT_DATA_UINT32:
79721708Sstevel 				/* trunk down to 32 bits, possibly lossy */
79731708Sstevel 				sum_knp[i].value.ui32 += (uint32_t)delta64;
79741708Sstevel 				break;
79751708Sstevel 
79761708Sstevel 			default:
79771708Sstevel 				sum_knp[i].value.ui64 += delta64;
79781708Sstevel 				break;
79791708Sstevel 			}
79801708Sstevel 		}
79811708Sstevel 	}
79821708Sstevel 
79831708Sstevel 	MAN_DBG(MAN_KSTAT, ("man_sum_kstats: returns\n"));
79841708Sstevel }
79851708Sstevel 
79861708Sstevel 
79871708Sstevel #if defined(DEBUG)
79881708Sstevel 
79891708Sstevel 
79901708Sstevel static char *_ms_flags[] = {
79911708Sstevel 	"NONE",
79921708Sstevel 	"FAST", 	/* 0x1 */
79931708Sstevel 	"RAW",		/* 0x2 */
79941708Sstevel 	"ALLPHYS",	/* 0x4 */
79951708Sstevel 	"ALLMULTI",	/* 0x8 */
79961708Sstevel 	"ALLSAP",	/* 0x10 */
79971708Sstevel 	"CKSUM",	/* 0x20 */
79981708Sstevel 	"MULTI",	/* 0x40 */
79991708Sstevel 	"SERLPBK",	/* 0x80 */
80001708Sstevel 	"MACLPBK",	/* 0x100 */
80011708Sstevel 	"CLOSING",	/* 0x200 */
80021708Sstevel 	"CLOSE_DONE",	/* 0x400 */
80031708Sstevel 	"CONTROL"	/* 0x800 */
80041708Sstevel };
80051708Sstevel 
80061708Sstevel static void
man_print_msp(manstr_t * msp)80071708Sstevel man_print_msp(manstr_t *msp)
80081708Sstevel {
80091708Sstevel 	char	buf[512];
80101708Sstevel 	char	prbuf[512];
80111708Sstevel 	uint_t	flags;
80121708Sstevel 	int	i;
80131708Sstevel 
80141708Sstevel 	cmn_err(CE_CONT, "\tmsp(0x%p)\n", (void *)msp);
80151708Sstevel 
80161708Sstevel 	if (msp == NULL)
80171708Sstevel 		return;
80181708Sstevel 
80191708Sstevel 	cmn_err(CE_CONT, "\t%s%d SAP(0x%x):\n",
80207656SSherry.Moore@Sun.COM 	    ddi_major_to_name(msp->ms_meta_maj), msp->ms_meta_ppa,
80217656SSherry.Moore@Sun.COM 	    msp->ms_sap);
80221708Sstevel 
80231708Sstevel 	buf[0] = '\0';
80241708Sstevel 	prbuf[0] = '\0';
80251708Sstevel 	flags = msp->ms_flags;
80261708Sstevel 	for (i = 0; i < A_CNT(_ms_flags); i++) {
80271708Sstevel 		if ((flags >> i) & 0x1) {
8028*11311SSurya.Prakki@Sun.COM 			(void) sprintf(buf, " %s |", _ms_flags[i+1]);
8029*11311SSurya.Prakki@Sun.COM 			(void) strcat(prbuf, buf);
80301708Sstevel 		}
80311708Sstevel 	}
80321708Sstevel 	prbuf[strlen(prbuf) - 1] = '\0';
80331708Sstevel 	cmn_err(CE_CONT, "\tms_flags: %s\n", prbuf);
80341708Sstevel 
80351708Sstevel 	cmn_err(CE_CONT, "\tms_dlpistate: %s\n", dss[msp->ms_dlpistate]);
80361708Sstevel 
80371708Sstevel 	cmn_err(CE_CONT, "\tms_dl_mp: 0x%p\n", (void *)msp->ms_dl_mp);
80381708Sstevel 
80391708Sstevel 	cmn_err(CE_CONT, "\tms_manp: 0x%p\n", (void *)msp->ms_manp);
80401708Sstevel 
80411708Sstevel 	cmn_err(CE_CONT, "\tms_dests: 0x%p\n", (void *)msp->ms_dests);
80421708Sstevel 
80431708Sstevel }
80441708Sstevel 
80451708Sstevel static char *_md_state[] = {
80461708Sstevel 	"NOTPRESENT",		/* 0x0 */
80471708Sstevel 	"INITIALIZING",		/* 0x1 */
80481708Sstevel 	"READY",		/* 0x2 */
80491708Sstevel 	"PLUMBING",		/* 0x4 */
80501708Sstevel 	"CLOSING"		/* 0x8 */
80511708Sstevel };
80521708Sstevel 
80531708Sstevel static void
man_print_mdp(man_dest_t * mdp)80541708Sstevel man_print_mdp(man_dest_t *mdp)
80551708Sstevel {
80561708Sstevel 	uint_t		state;
80571708Sstevel 	int		i;
80581708Sstevel 	char		buf[64];
80591708Sstevel 	char		prbuf[512];
80601708Sstevel 
80611708Sstevel 	buf[0] = '\0';
80621708Sstevel 	prbuf[0] = '\0';
80631708Sstevel 
80641708Sstevel 	cmn_err(CE_CONT, "\tmdp(0x%p)\n", (void *)mdp);
80651708Sstevel 
80661708Sstevel 	if (mdp == NULL)
80671708Sstevel 		return;
80681708Sstevel 
80691708Sstevel 	cmn_err(CE_CONT, "\tmd_pg_id: %d\n", mdp->md_pg_id);
80701708Sstevel 	cmn_err(CE_CONT, "\tmd_dst_eaddr: %s\n",
80717656SSherry.Moore@Sun.COM 	    ether_sprintf(&mdp->md_dst_eaddr));
80721708Sstevel 	cmn_err(CE_CONT, "\tmd_src_eaddr: %s\n",
80737656SSherry.Moore@Sun.COM 	    ether_sprintf(&mdp->md_src_eaddr));
80741708Sstevel 	cmn_err(CE_CONT, "\tmd_dlpistate: %s", dss[mdp->md_dlpistate]);
80751708Sstevel 	cmn_err(CE_CONT, "\tmd_muxid: 0x%u", mdp->md_muxid);
80761708Sstevel 	cmn_err(CE_CONT, "\tmd_rcvcnt %lu md_lastrcvcnt %lu", mdp->md_rcvcnt,
80777656SSherry.Moore@Sun.COM 	    mdp->md_lastrcvcnt);
80781708Sstevel 
80791708Sstevel 	/*
80801708Sstevel 	 * Print out state as text.
80811708Sstevel 	 */
80821708Sstevel 	state = mdp->md_state;
80831708Sstevel 
80841708Sstevel 	if (state == 0) {
8085*11311SSurya.Prakki@Sun.COM 		(void) strcat(prbuf, _md_state[0]);
80861708Sstevel 	} else {
80871708Sstevel 
80881708Sstevel 		for (i = 0; i < A_CNT(_md_state); i++) {
80891708Sstevel 			if ((state >> i) & 0x1)  {
8090*11311SSurya.Prakki@Sun.COM 				(void) sprintf(buf, " %s |", _md_state[i+1]);
8091*11311SSurya.Prakki@Sun.COM 				(void) strcat(prbuf, buf);
80921708Sstevel 			}
80931708Sstevel 		}
80941708Sstevel 		prbuf[strlen(prbuf) -1] = '\0';
80951708Sstevel 	}
80961708Sstevel 	cmn_err(CE_CONT, "\tmd_state: %s", prbuf);
80971708Sstevel 
80981708Sstevel 	cmn_err(CE_CONT, "\tmd_device:\n");
80991708Sstevel 	man_print_dev(&mdp->md_device);
81001708Sstevel 
81011708Sstevel }
81021708Sstevel 
81031708Sstevel static void
man_print_man(man_t * manp)81041708Sstevel man_print_man(man_t *manp)
81051708Sstevel {
81061708Sstevel 	char	buf[512];
81071708Sstevel 	char	prbuf[512];
81081708Sstevel 
81091708Sstevel 	buf[0] = '\0';
81101708Sstevel 	prbuf[0] = '\0';
81111708Sstevel 
81121708Sstevel 	if (manp == NULL)
81131708Sstevel 		return;
81141708Sstevel 
81151708Sstevel 	if (ddi_major_to_name(manp->man_meta_major)) {
8116*11311SSurya.Prakki@Sun.COM 		(void) sprintf(buf, "\t man_device: %s%d\n",
81177656SSherry.Moore@Sun.COM 		    ddi_major_to_name(manp->man_meta_major),
81187656SSherry.Moore@Sun.COM 		    manp->man_meta_ppa);
81191708Sstevel 	} else {
8120*11311SSurya.Prakki@Sun.COM 		(void) sprintf(buf, "\t major: %d", manp->man_meta_major);
8121*11311SSurya.Prakki@Sun.COM 		(void) sprintf(buf, "\t ppa: %d", manp->man_meta_ppa);
81221708Sstevel 	}
81231708Sstevel 
81241708Sstevel 	cmn_err(CE_CONT, "%s", buf);
81251708Sstevel 
81261708Sstevel }
81271708Sstevel 
81281708Sstevel static char *_mdev_state[] = {
81291708Sstevel 	"UNASSIGNED  ",
81301708Sstevel 	"ASSIGNED",
81311708Sstevel 	"ACTIVE",
81321708Sstevel 	"FAILED"
81331708Sstevel };
81341708Sstevel 
81351708Sstevel static void
man_print_dev(man_dev_t * mdevp)81361708Sstevel man_print_dev(man_dev_t *mdevp)
81371708Sstevel {
81381708Sstevel 	char	buf[512];
81391708Sstevel 	char	prbuf[512];
81401708Sstevel 	int	i;
81411708Sstevel 	uint_t	state;
81421708Sstevel 
81431708Sstevel 	buf[0] = '\0';
81441708Sstevel 	prbuf[0] = '\0';
81451708Sstevel 
81461708Sstevel 	if (mdevp == NULL)
81471708Sstevel 		return;
81481708Sstevel 
81491708Sstevel 	if (mdevp->mdev_major == 0) {
81501708Sstevel number:
8151*11311SSurya.Prakki@Sun.COM 		(void) sprintf(buf, "\t mdev_major: %d\n", mdevp->mdev_major);
81521708Sstevel 	} else if (ddi_major_to_name(mdevp->mdev_major)) {
8153*11311SSurya.Prakki@Sun.COM 		(void) sprintf(buf, "\t mdev_device: %s%d\n",
81547656SSherry.Moore@Sun.COM 		    ddi_major_to_name(mdevp->mdev_major),
81557656SSherry.Moore@Sun.COM 		    mdevp->mdev_ppa);
81561708Sstevel 	} else
81571708Sstevel 		goto number;
81581708Sstevel 
81591708Sstevel 	cmn_err(CE_CONT, "%s", buf);
81601708Sstevel 
81611708Sstevel 	cmn_err(CE_CONT, "\t mdev_exp_id: %d\n", mdevp->mdev_exp_id);
81621708Sstevel 
81631708Sstevel 	buf[0] = '\0';
81641708Sstevel 	prbuf[0] = '\0';
81651708Sstevel 	state = mdevp->mdev_state;
81661708Sstevel 
81671708Sstevel 	if (state == 0) {
8168*11311SSurya.Prakki@Sun.COM 		(void) strcat(prbuf, _mdev_state[0]);
81691708Sstevel 	} else {
81701708Sstevel 		for (i = 0; i < A_CNT(_mdev_state); i++) {
81711708Sstevel 			if ((state >> i) & 0x1) {
8172*11311SSurya.Prakki@Sun.COM 				(void) sprintf(buf, " %s |", _mdev_state[i+1]);
8173*11311SSurya.Prakki@Sun.COM 				(void) strcat(prbuf, buf);
81741708Sstevel 			}
81751708Sstevel 		}
81761708Sstevel 	}
81771708Sstevel 
81781708Sstevel 	prbuf[strlen(prbuf) - 2] = '\0';
81791708Sstevel 
81801708Sstevel 	cmn_err(CE_CONT, "\t mdev_state: %s\n", prbuf);
81811708Sstevel 
81821708Sstevel }
81831708Sstevel 
81841708Sstevel static char *_mip_cmd[] = {
81851708Sstevel 	"MI_PATH_READ",
81861708Sstevel 	"MI_PATH_ASSIGN",
81871708Sstevel 	"MI_PATH_ACTIVATE",
81881708Sstevel 	"MI_PATH_DEACTIVATE",
81891708Sstevel 	"MI_PATH_UNASSIGN"
81901708Sstevel };
81911708Sstevel 
81921708Sstevel static void
man_print_mtp(mi_time_t * mtp)81931708Sstevel man_print_mtp(mi_time_t *mtp)
81941708Sstevel {
81951708Sstevel 	cmn_err(CE_CONT, "\tmtp(0x%p)\n", (void *)mtp);
81961708Sstevel 
81971708Sstevel 	if (mtp == NULL)
81981708Sstevel 		return;
81991708Sstevel 
82001708Sstevel 	cmn_err(CE_CONT, "\tmtp_instance: %d\n", mtp->mtp_man_ppa);
82011708Sstevel 
82021708Sstevel 	cmn_err(CE_CONT, "\tmtp_time: %d\n", mtp->mtp_time);
82031708Sstevel 
82041708Sstevel }
82051708Sstevel 
82061708Sstevel static void
man_print_mip(mi_path_t * mip)82071708Sstevel man_print_mip(mi_path_t *mip)
82081708Sstevel {
82091708Sstevel 	cmn_err(CE_CONT, "\tmip(0x%p)\n", (void *)mip);
82101708Sstevel 
82111708Sstevel 	if (mip == NULL)
82121708Sstevel 		return;
82131708Sstevel 
82141708Sstevel 	cmn_err(CE_CONT, "\tmip_pg_id: %d\n", mip->mip_pg_id);
82151708Sstevel 
82161708Sstevel 	cmn_err(CE_CONT, "\tmip_cmd: %s\n", _mip_cmd[mip->mip_cmd]);
82171708Sstevel 
82181708Sstevel 	cmn_err(CE_CONT, "\tmip_eaddr: %s\n", ether_sprintf(&mip->mip_eaddr));
82191708Sstevel 
82201708Sstevel 	cmn_err(CE_CONT, "\tmip_devs: 0x%p\n", (void *)mip->mip_devs);
82211708Sstevel 
82221708Sstevel 	cmn_err(CE_CONT, "\tmip_ndevs: %d\n", mip->mip_ndevs);
82231708Sstevel 
82241708Sstevel }
82251708Sstevel 
82261708Sstevel static void
man_print_mpg(man_pg_t * mpg)82271708Sstevel man_print_mpg(man_pg_t *mpg)
82281708Sstevel {
82291708Sstevel 	cmn_err(CE_CONT, "\tmpg(0x%p)\n", (void *)mpg);
82301708Sstevel 
82311708Sstevel 	if (mpg == NULL)
82321708Sstevel 		return;
82331708Sstevel 
82341708Sstevel 	cmn_err(CE_CONT, "\tmpg_next: 0x%p\n", (void *)mpg->mpg_next);
82351708Sstevel 
82361708Sstevel 	cmn_err(CE_CONT, "\tmpg_pg_id: %d\n", mpg->mpg_pg_id);
82371708Sstevel 
82381708Sstevel 	cmn_err(CE_CONT, "\tmpg_man_ppa: %d\n", mpg->mpg_man_ppa);
82391708Sstevel 
82401708Sstevel 	cmn_err(CE_CONT, "\tmpg_dst_eaddr: %s\n",
82417656SSherry.Moore@Sun.COM 	    ether_sprintf(&mpg->mpg_dst_eaddr));
82421708Sstevel 
82431708Sstevel 	cmn_err(CE_CONT, "\tmpg_pathp: 0x%p\n", (void *)mpg->mpg_pathp);
82441708Sstevel 
82451708Sstevel }
82461708Sstevel 
82471708Sstevel static char *_mw_flags[] = {
82481708Sstevel 	"NOWAITER",		/* 0x0 */
82491708Sstevel 	"CVWAITER",		/* 0x1 */
82501708Sstevel 	"QWAITER",		/* 0x2 */
82511708Sstevel 	"DONE"		/* 0x3 */
82521708Sstevel };
82531708Sstevel 
82541708Sstevel static void
man_print_work(man_work_t * wp)82551708Sstevel man_print_work(man_work_t *wp)
82561708Sstevel {
82571708Sstevel 	int 	i;
82581708Sstevel 
82591708Sstevel 	cmn_err(CE_CONT, "\twp(0x%p)\n\n", (void *)wp);
82601708Sstevel 
82611708Sstevel 	if (wp == NULL)
82621708Sstevel 		return;
82631708Sstevel 
82641708Sstevel 	cmn_err(CE_CONT, "\tmw_type: %s\n", _mw_type[wp->mw_type]);
82651708Sstevel 
82661708Sstevel 	cmn_err(CE_CONT, "\tmw_flags: ");
82671708Sstevel 	for (i = 0; i < A_CNT(_mw_flags); i++) {
82681708Sstevel 		if ((wp->mw_flags >> i) & 0x1)
82691708Sstevel 			cmn_err(CE_CONT, "%s", _mw_flags[i]);
82701708Sstevel 	}
82711708Sstevel 	cmn_err(CE_CONT, "\n");
82721708Sstevel 
82731708Sstevel 	cmn_err(CE_CONT, "\twp_status: %d\n", wp->mw_status);
82741708Sstevel 
82751708Sstevel 	cmn_err(CE_CONT, "\twp_arg: 0x%p\n", (void *)&wp->mw_arg);
82761708Sstevel 
82771708Sstevel 	cmn_err(CE_CONT, "\tmw_next: 0x%p\n", (void *)wp->mw_next);
82781708Sstevel 
82791708Sstevel 	cmn_err(CE_CONT, "\twp_q: 0x%p", (void *)wp->mw_q);
82801708Sstevel 
82811708Sstevel }
82821708Sstevel 
82831708Sstevel static void
man_print_path(man_path_t * mp)82841708Sstevel man_print_path(man_path_t *mp)
82851708Sstevel {
82861708Sstevel 	cmn_err(CE_CONT, "\tmp(0x%p)\n\n", (void *)mp);
82871708Sstevel 
82881708Sstevel 	if (mp == NULL)
82891708Sstevel 		return;
82901708Sstevel 
82911708Sstevel 	cmn_err(CE_CONT, "\tmp_device:");
82921708Sstevel 	man_print_dev(&mp->mp_device);
82931708Sstevel 
82941708Sstevel 	cmn_err(CE_CONT, "\tmp_next: 0x%p\n", (void *)mp->mp_next);
82951708Sstevel 
82961708Sstevel 	cmn_err(CE_CONT, "\tmp_last_knp: 0x%p\n", (void *)mp->mp_last_knp);
82971708Sstevel 
82981708Sstevel 	cmn_err(CE_CONT, "\tmp_lru: 0x%lx", mp->mp_lru);
82991708Sstevel 
83001708Sstevel }
83011708Sstevel 
83021708Sstevel void *
man_dbg_kzalloc(int line,size_t size,int kmflags)83031708Sstevel man_dbg_kzalloc(int line, size_t size, int kmflags)
83041708Sstevel {
83051708Sstevel 	void *tmp;
83061708Sstevel 
83071708Sstevel 	tmp = kmem_zalloc(size, kmflags);
83081708Sstevel 	MAN_DBG(MAN_KMEM, ("0x%p %lu\tzalloc'd @ %d\n", (void *)tmp,
83097656SSherry.Moore@Sun.COM 	    size, line));
83101708Sstevel 
83111708Sstevel 	return (tmp);
83121708Sstevel 
83131708Sstevel }
83141708Sstevel 
83151708Sstevel void
man_dbg_kfree(int line,void * buf,size_t size)83161708Sstevel man_dbg_kfree(int line, void *buf, size_t size)
83171708Sstevel {
83181708Sstevel 
83191708Sstevel 	MAN_DBG(MAN_KMEM, ("0x%p %lu\tfree'd @ %d\n", (void *)buf, size, line));
83201708Sstevel 
83211708Sstevel 	kmem_free(buf, size);
83221708Sstevel 
83231708Sstevel }
83241708Sstevel 
83251708Sstevel #endif  /* DEBUG */
8326