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(ðerbroadcast, 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, ðerbroadcast) == 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(ðerbroadcast, &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 *)∈
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