103831d35Sstevel /* 203831d35Sstevel * CDDL HEADER START 303831d35Sstevel * 403831d35Sstevel * The contents of this file are subject to the terms of the 503831d35Sstevel * Common Development and Distribution License (the "License"). 603831d35Sstevel * You may not use this file except in compliance with the License. 703831d35Sstevel * 803831d35Sstevel * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 903831d35Sstevel * or http://www.opensolaris.org/os/licensing. 1003831d35Sstevel * See the License for the specific language governing permissions 1103831d35Sstevel * and limitations under the License. 1203831d35Sstevel * 1303831d35Sstevel * When distributing Covered Code, include this CDDL HEADER in each 1403831d35Sstevel * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1503831d35Sstevel * If applicable, add the following below this CDDL HEADER, with the 1603831d35Sstevel * fields enclosed by brackets "[]" replaced with your own identifying 1703831d35Sstevel * information: Portions Copyright [yyyy] [name of copyright owner] 1803831d35Sstevel * 1903831d35Sstevel * CDDL HEADER END 2003831d35Sstevel */ 2103831d35Sstevel 2203831d35Sstevel /* 23d3d50737SRafael Vanoni * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 2403831d35Sstevel * Use is subject to license terms. 2503831d35Sstevel */ 2603831d35Sstevel 2703831d35Sstevel 2803831d35Sstevel /* 2903831d35Sstevel * Starcat Management Network Driver 3003831d35Sstevel * 3103831d35Sstevel * ****** NOTICE **** This file also resides in the SSC gate as 3203831d35Sstevel * ****** NOTICE **** usr/src/uts/sun4u/scman/scman.c. Any changes 3303831d35Sstevel * ****** NOTICE **** made here must be propogated there as well. 3403831d35Sstevel * 3503831d35Sstevel */ 3603831d35Sstevel 3703831d35Sstevel #include <sys/types.h> 3803831d35Sstevel #include <sys/proc.h> 3903831d35Sstevel #include <sys/disp.h> 4003831d35Sstevel #include <sys/kmem.h> 4103831d35Sstevel #include <sys/stat.h> 4203831d35Sstevel #include <sys/kstat.h> 4303831d35Sstevel #include <sys/ksynch.h> 4403831d35Sstevel #include <sys/stream.h> 4503831d35Sstevel #include <sys/dlpi.h> 4603831d35Sstevel #include <sys/stropts.h> 4703831d35Sstevel #include <sys/strsubr.h> 4803831d35Sstevel #include <sys/debug.h> 4903831d35Sstevel #include <sys/conf.h> 5003831d35Sstevel #include <sys/kstr.h> 5103831d35Sstevel #include <sys/errno.h> 5203831d35Sstevel #include <sys/ethernet.h> 5303831d35Sstevel #include <sys/byteorder.h> 5403831d35Sstevel #include <sys/ddi.h> 5503831d35Sstevel #include <sys/sunddi.h> 5603831d35Sstevel #include <sys/sunldi.h> 5703831d35Sstevel #include <sys/modctl.h> 5803831d35Sstevel #include <sys/strsun.h> 5903831d35Sstevel #include <sys/callb.h> 6003831d35Sstevel #include <sys/pci.h> 6103831d35Sstevel #include <netinet/in.h> 6203831d35Sstevel #include <inet/common.h> 6303831d35Sstevel #include <inet/mi.h> 6403831d35Sstevel #include <inet/nd.h> 6503831d35Sstevel #include <sys/socket.h> 6603831d35Sstevel #include <netinet/igmp_var.h> 6703831d35Sstevel #include <netinet/ip6.h> 6803831d35Sstevel #include <netinet/icmp6.h> 6903831d35Sstevel #include <inet/ip.h> 7003831d35Sstevel #include <inet/ip6.h> 7103831d35Sstevel #include <sys/file.h> 7203831d35Sstevel #include <sys/dman.h> 7303831d35Sstevel #include <sys/autoconf.h> 7403831d35Sstevel #include <sys/zone.h> 7503831d35Sstevel 7603831d35Sstevel extern int ddi_create_internal_pathname(dev_info_t *, char *, int, minor_t); 7703831d35Sstevel 7803831d35Sstevel #define MAN_IDNAME "dman" 7903831d35Sstevel #define DMAN_INT_PATH "/devices/pseudo/dman@0:dman" 8003831d35Sstevel #define DMAN_PATH "/devices/pseudo/clone@0:dman" 8103831d35Sstevel #define ERI_IDNAME "eri" 8203831d35Sstevel #define ERI_PATH "/devices/pseudo/clone@0:eri" 8303831d35Sstevel 8403831d35Sstevel #if defined(DEBUG) 8503831d35Sstevel 8603831d35Sstevel static void man_print_msp(manstr_t *); 8703831d35Sstevel static void man_print_man(man_t *); 8803831d35Sstevel static void man_print_mdp(man_dest_t *); 8903831d35Sstevel static void man_print_dev(man_dev_t *); 9003831d35Sstevel static void man_print_mip(mi_path_t *); 9103831d35Sstevel static void man_print_mtp(mi_time_t *); 9203831d35Sstevel static void man_print_mpg(man_pg_t *); 9303831d35Sstevel static void man_print_path(man_path_t *); 9403831d35Sstevel static void man_print_work(man_work_t *); 9503831d35Sstevel 9603831d35Sstevel /* 9703831d35Sstevel * Set manstr_t dlpistate (upper half of multiplexor) 9803831d35Sstevel */ 9903831d35Sstevel #define SETSTATE(msp, state) \ 10003831d35Sstevel MAN_DBG(MAN_DLPI, ("msp=0x%p @ %d state %s=>%s\n", \ 10103831d35Sstevel (void *)msp, __LINE__, dss[msp->ms_dlpistate], \ 10203831d35Sstevel dss[(state)])); \ 10303831d35Sstevel msp->ms_dlpistate = (state); 10403831d35Sstevel /* 10503831d35Sstevel * Set man_dest_t dlpistate (lower half of multiplexor) 10603831d35Sstevel */ 10703831d35Sstevel #define D_SETSTATE(mdp, state) \ 10803831d35Sstevel MAN_DBG(MAN_DLPI, ("dst=0x%p @ %d state %s=>%s\n", \ 10903831d35Sstevel (void *)mdp, __LINE__, dss[mdp->md_dlpistate], \ 11003831d35Sstevel dss[(state)])); \ 11103831d35Sstevel mdp->md_dlpistate = (state); 11203831d35Sstevel 11303831d35Sstevel static char *promisc[] = { /* DLPI promisc Strings */ 11403831d35Sstevel "not used", /* 0x00 */ 11503831d35Sstevel "DL_PROMISC_PHYS", /* 0x01 */ 11603831d35Sstevel "DL_PROMISC_SAP", /* 0x02 */ 11703831d35Sstevel "DL_PROMISC_MULTI" /* 0x03 */ 11803831d35Sstevel }; 11903831d35Sstevel 12003831d35Sstevel static char *dps[] = { /* DLPI Primitive Strings */ 12103831d35Sstevel "DL_INFO_REQ", /* 0x00 */ 12203831d35Sstevel "DL_BIND_REQ", /* 0x01 */ 12303831d35Sstevel "DL_UNBIND_REQ", /* 0x02 */ 12403831d35Sstevel "DL_INFO_ACK", /* 0x03 */ 12503831d35Sstevel "DL_BIND_ACK", /* 0x04 */ 12603831d35Sstevel "DL_ERROR_ACK", /* 0x05 */ 12703831d35Sstevel "DL_OK_ACK", /* 0x06 */ 12803831d35Sstevel "DL_UNITDATA_REQ", /* 0x07 */ 12903831d35Sstevel "DL_UNITDATA_IND", /* 0x08 */ 13003831d35Sstevel "DL_UDERROR_IND", /* 0x09 */ 13103831d35Sstevel "DL_UDQOS_REQ", /* 0x0a */ 13203831d35Sstevel "DL_ATTACH_REQ", /* 0x0b */ 13303831d35Sstevel "DL_DETACH_REQ", /* 0x0c */ 13403831d35Sstevel "DL_CONNECT_REQ", /* 0x0d */ 13503831d35Sstevel "DL_CONNECT_IND", /* 0x0e */ 13603831d35Sstevel "DL_CONNECT_RES", /* 0x0f */ 13703831d35Sstevel "DL_CONNECT_CON", /* 0x10 */ 13803831d35Sstevel "DL_TOKEN_REQ", /* 0x11 */ 13903831d35Sstevel "DL_TOKEN_ACK", /* 0x12 */ 14003831d35Sstevel "DL_DISCONNECT_REQ", /* 0x13 */ 14103831d35Sstevel "DL_DISCONNECT_IND", /* 0x14 */ 14203831d35Sstevel "DL_SUBS_UNBIND_REQ", /* 0x15 */ 14303831d35Sstevel "DL_LIARLIARPANTSONFIRE", /* 0x16 */ 14403831d35Sstevel "DL_RESET_REQ", /* 0x17 */ 14503831d35Sstevel "DL_RESET_IND", /* 0x18 */ 14603831d35Sstevel "DL_RESET_RES", /* 0x19 */ 14703831d35Sstevel "DL_RESET_CON", /* 0x1a */ 14803831d35Sstevel "DL_SUBS_BIND_REQ", /* 0x1b */ 14903831d35Sstevel "DL_SUBS_BIND_ACK", /* 0x1c */ 15003831d35Sstevel "DL_ENABMULTI_REQ", /* 0x1d */ 15103831d35Sstevel "DL_DISABMULTI_REQ", /* 0x1e */ 15203831d35Sstevel "DL_PROMISCON_REQ", /* 0x1f */ 15303831d35Sstevel "DL_PROMISCOFF_REQ", /* 0x20 */ 15403831d35Sstevel "DL_DATA_ACK_REQ", /* 0x21 */ 15503831d35Sstevel "DL_DATA_ACK_IND", /* 0x22 */ 15603831d35Sstevel "DL_DATA_ACK_STATUS_IND", /* 0x23 */ 15703831d35Sstevel "DL_REPLY_REQ", /* 0x24 */ 15803831d35Sstevel "DL_REPLY_IND", /* 0x25 */ 15903831d35Sstevel "DL_REPLY_STATUS_IND", /* 0x26 */ 16003831d35Sstevel "DL_REPLY_UPDATE_REQ", /* 0x27 */ 16103831d35Sstevel "DL_REPLY_UPDATE_STATUS_IND", /* 0x28 */ 16203831d35Sstevel "DL_XID_REQ", /* 0x29 */ 16303831d35Sstevel "DL_XID_IND", /* 0x2a */ 16403831d35Sstevel "DL_XID_RES", /* 0x2b */ 16503831d35Sstevel "DL_XID_CON", /* 0x2c */ 16603831d35Sstevel "DL_TEST_REQ", /* 0x2d */ 16703831d35Sstevel "DL_TEST_IND", /* 0x2e */ 16803831d35Sstevel "DL_TEST_RES", /* 0x2f */ 16903831d35Sstevel "DL_TEST_CON", /* 0x30 */ 17003831d35Sstevel "DL_PHYS_ADDR_REQ", /* 0x31 */ 17103831d35Sstevel "DL_PHYS_ADDR_ACK", /* 0x32 */ 17203831d35Sstevel "DL_SET_PHYS_ADDR_REQ", /* 0x33 */ 17303831d35Sstevel "DL_GET_STATISTICS_REQ", /* 0x34 */ 17403831d35Sstevel "DL_GET_STATISTICS_ACK", /* 0x35 */ 17503831d35Sstevel }; 17603831d35Sstevel 17703831d35Sstevel #define MAN_DLPI_MAX_PRIM 0x35 17803831d35Sstevel 17903831d35Sstevel static char *dss[] = { /* DLPI State Strings */ 18003831d35Sstevel "DL_UNBOUND", /* 0x00 */ 18103831d35Sstevel "DL_BIND_PENDING", /* 0x01 */ 18203831d35Sstevel "DL_UNBIND_PENDING", /* 0x02 */ 18303831d35Sstevel "DL_IDLE", /* 0x03 */ 18403831d35Sstevel "DL_UNATTACHED", /* 0x04 */ 18503831d35Sstevel "DL_ATTACH_PENDING", /* 0x05 */ 18603831d35Sstevel "DL_DETACH_PENDING", /* 0x06 */ 18703831d35Sstevel "DL_UDQOS_PENDING", /* 0x07 */ 18803831d35Sstevel "DL_OUTCON_PENDING", /* 0x08 */ 18903831d35Sstevel "DL_INCON_PENDING", /* 0x09 */ 19003831d35Sstevel "DL_CONN_RES_PENDING", /* 0x0a */ 19103831d35Sstevel "DL_DATAXFER", /* 0x0b */ 19203831d35Sstevel "DL_USER_RESET_PENDING", /* 0x0c */ 19303831d35Sstevel "DL_PROV_RESET_PENDING", /* 0x0d */ 19403831d35Sstevel "DL_RESET_RES_PENDING", /* 0x0e */ 19503831d35Sstevel "DL_DISCON8_PENDING", /* 0x0f */ 19603831d35Sstevel "DL_DISCON9_PENDING", /* 0x10 */ 19703831d35Sstevel "DL_DISCON11_PENDING", /* 0x11 */ 19803831d35Sstevel "DL_DISCON12_PENDING", /* 0x12 */ 19903831d35Sstevel "DL_DISCON13_PENDING", /* 0x13 */ 20003831d35Sstevel "DL_SUBS_BIND_PND", /* 0x14 */ 20103831d35Sstevel "DL_SUBS_UNBIND_PND", /* 0x15 */ 20203831d35Sstevel }; 20303831d35Sstevel 20403831d35Sstevel static const char *lss[] = { 20503831d35Sstevel "UNKNOWN", /* 0x0 */ 20603831d35Sstevel "INIT", /* 0x1 */ 20703831d35Sstevel "GOOD", /* 0x2 */ 20803831d35Sstevel "STALE", /* 0x3 */ 20903831d35Sstevel "FAIL", /* 0x4 */ 21003831d35Sstevel }; 21103831d35Sstevel 21203831d35Sstevel static char *_mw_type[] = { 21303831d35Sstevel "OPEN_CTL", /* 0x0 */ 21403831d35Sstevel "CLOSE_CTL", /* 0x1 */ 21503831d35Sstevel "SWITCH", /* 0x2 */ 21603831d35Sstevel "PATH_UPDATE", /* 0x3 */ 21703831d35Sstevel "CLOSE", /* 0x4 */ 21803831d35Sstevel "CLOSE_STREAM", /* 0x5 */ 21903831d35Sstevel "DRATTACH", /* 0x6 */ 22003831d35Sstevel "DRDETACH", /* 0x7 */ 22103831d35Sstevel "STOP", /* 0x8 */ 22203831d35Sstevel "DRSWITCH", /* 0x9 */ 22303831d35Sstevel "KSTAT_UPDATE" /* 0xA */ 22403831d35Sstevel }; 22503831d35Sstevel 22603831d35Sstevel uint32_t man_debug = MAN_WARN; 22703831d35Sstevel 22803831d35Sstevel #define man_kzalloc(a, b) man_dbg_kzalloc(__LINE__, a, b) 22903831d35Sstevel #define man_kfree(a, b) man_dbg_kfree(__LINE__, a, b) 23003831d35Sstevel void *man_dbg_kzalloc(int line, size_t size, int kmflags); 23103831d35Sstevel void man_dbg_kfree(int line, void *buf, size_t size); 23203831d35Sstevel 23303831d35Sstevel #else /* DEBUG */ 23403831d35Sstevel 23503831d35Sstevel uint32_t man_debug = 0; 23603831d35Sstevel /* 23703831d35Sstevel * Set manstr_t dlpistate (upper half of multiplexor) 23803831d35Sstevel */ 23903831d35Sstevel #define SETSTATE(msp, state) msp->ms_dlpistate = (state); 24003831d35Sstevel /* 24103831d35Sstevel * Set man_dest_t dlpistate (lower half of multiplexor) 24203831d35Sstevel */ 24303831d35Sstevel #define D_SETSTATE(mdp, state) mdp->md_dlpistate = (state); 24403831d35Sstevel 24503831d35Sstevel #define man_kzalloc(a, b) kmem_zalloc(a, b) 24603831d35Sstevel #define man_kfree(a, b) kmem_free(a, b) 24703831d35Sstevel 24803831d35Sstevel #endif /* DEBUG */ 24903831d35Sstevel 25003831d35Sstevel #define DL_PRIM(mp) (((union DL_primitives *)(mp)->b_rptr)->dl_primitive) 25103831d35Sstevel #define DL_PROMISCON_TYPE(mp) \ 25203831d35Sstevel (((union DL_primitives *)(mp)->b_rptr)->promiscon_req.dl_level) 25303831d35Sstevel #define IOC_CMD(mp) (((struct iocblk *)(mp)->b_rptr)->ioc_cmd) 25403831d35Sstevel 25503831d35Sstevel /* 25603831d35Sstevel * Start of kstat-related declarations 25703831d35Sstevel */ 25803831d35Sstevel #define MK_NOT_COUNTER (1<<0) /* is it a counter? */ 25903831d35Sstevel #define MK_ERROR (1<<2) /* for error statistics */ 26003831d35Sstevel #define MK_NOT_PHYSICAL (1<<3) /* no matching physical stat */ 26103831d35Sstevel 26203831d35Sstevel typedef struct man_kstat_info_s { 26303831d35Sstevel char *mk_name; /* e.g. align_errors */ 26403831d35Sstevel char *mk_physname; /* e.g. framing (NULL for same) */ 26503831d35Sstevel char *mk_physalias; /* e.g. framing (NULL for same) */ 26603831d35Sstevel uchar_t mk_type; /* e.g. KSTAT_DATA_UINT32 */ 26703831d35Sstevel int mk_flags; 26803831d35Sstevel } man_kstat_info_t; 26903831d35Sstevel 27003831d35Sstevel /* 27103831d35Sstevel * Master declaration macro, note that it uses token pasting 27203831d35Sstevel */ 27303831d35Sstevel #define MK_DECLARE(name, pname, palias, bits, flags) \ 27403831d35Sstevel { name, pname, palias, KSTAT_DATA_UINT ## bits, flags } 27503831d35Sstevel 27603831d35Sstevel /* 27703831d35Sstevel * Obsolete forms don't have the _sinceswitch forms, they are all errors 27803831d35Sstevel */ 27903831d35Sstevel #define MK_OBSOLETE32(name, alias) MK_DECLARE(alias, name, alias, 32, MK_ERROR) 28003831d35Sstevel #define MK_OBSOLETE64(name, alias) MK_DECLARE(alias, name, alias, 64, MK_ERROR) 28103831d35Sstevel 28203831d35Sstevel /* 28303831d35Sstevel * The only non-counters don't have any other aliases 28403831d35Sstevel */ 28503831d35Sstevel #define MK_NOTCOUNTER32(name) MK_DECLARE(name, name, NULL, 32, MK_NOT_COUNTER) 28603831d35Sstevel #define MK_NOTCOUNTER64(name) MK_DECLARE(name, name, NULL, 64, MK_NOT_COUNTER) 28703831d35Sstevel 28803831d35Sstevel /* 28903831d35Sstevel * Normal counter forms 29003831d35Sstevel */ 29103831d35Sstevel #define MK_DECLARE32(name, alias) \ 29203831d35Sstevel MK_DECLARE(name, name, alias, 32, 0) 29303831d35Sstevel #define MK_DECLARE64(name, alias) \ 29403831d35Sstevel MK_DECLARE(name, name, alias, 64, 0) 29503831d35Sstevel 29603831d35Sstevel /* 29703831d35Sstevel * Error counters need special MK_ERROR flag only for the non-AP form 29803831d35Sstevel */ 29903831d35Sstevel #define MK_ERROR32(name, alias) \ 30003831d35Sstevel MK_DECLARE(name, name, alias, 32, MK_ERROR) 30103831d35Sstevel #define MK_ERROR64(name, alias) \ 30203831d35Sstevel MK_DECLARE(name, name, alias, 64, MK_ERROR) 30303831d35Sstevel 30403831d35Sstevel /* 30503831d35Sstevel * These AP-specific stats are not backed by physical statistics 30603831d35Sstevel */ 30703831d35Sstevel #define MK_NOTPHYS32(name) MK_DECLARE(name, NULL, NULL, 32, MK_NOT_PHYSICAL) 30803831d35Sstevel #define MK_NOTPHYS64(name) MK_DECLARE(name, NULL, NULL, 64, MK_NOT_PHYSICAL) 30903831d35Sstevel 31003831d35Sstevel /* 31103831d35Sstevel * START of the actual man_kstat_info declaration using above macros 31203831d35Sstevel */ 31303831d35Sstevel static man_kstat_info_t man_kstat_info[] = { 31403831d35Sstevel /* 31503831d35Sstevel * Link Input/Output stats 31603831d35Sstevel */ 31703831d35Sstevel MK_DECLARE32("ipackets", NULL), 31803831d35Sstevel MK_ERROR32("ierrors", NULL), 31903831d35Sstevel MK_DECLARE32("opackets", NULL), 32003831d35Sstevel MK_ERROR32("oerrors", NULL), 32103831d35Sstevel MK_ERROR32("collisions", NULL), 32203831d35Sstevel MK_NOTCOUNTER64("ifspeed"), 32303831d35Sstevel /* 32403831d35Sstevel * These are new MIB-II stats, per PSARC 1997/198 32503831d35Sstevel */ 32603831d35Sstevel MK_DECLARE32("rbytes", NULL), 32703831d35Sstevel MK_DECLARE32("obytes", NULL), 32803831d35Sstevel MK_DECLARE32("multircv", NULL), 32903831d35Sstevel MK_DECLARE32("multixmt", NULL), 33003831d35Sstevel MK_DECLARE32("brdcstrcv", NULL), 33103831d35Sstevel MK_DECLARE32("brdcstxmt", NULL), 33203831d35Sstevel /* 33303831d35Sstevel * Error values 33403831d35Sstevel */ 33503831d35Sstevel MK_ERROR32("norcvbuf", NULL), 33603831d35Sstevel MK_ERROR32("noxmtbuf", NULL), 33703831d35Sstevel MK_ERROR32("unknowns", NULL), 33803831d35Sstevel /* 33903831d35Sstevel * These are the 64-bit values, they fallback to 32-bit values 34003831d35Sstevel */ 34103831d35Sstevel MK_DECLARE64("ipackets64", "ipackets"), 34203831d35Sstevel MK_DECLARE64("opackets64", "opackets"), 34303831d35Sstevel MK_DECLARE64("rbytes64", "rbytes"), 34403831d35Sstevel MK_DECLARE64("obytes64", "obytes"), 34503831d35Sstevel 34603831d35Sstevel /* New AP switching statistics */ 34703831d35Sstevel MK_NOTPHYS64("man_switches"), 34803831d35Sstevel MK_NOTPHYS64("man_link_fails"), 34903831d35Sstevel MK_NOTPHYS64("man_link_stales"), 35003831d35Sstevel MK_NOTPHYS64("man_icmpv4_probes"), 35103831d35Sstevel MK_NOTPHYS64("man_icmpv6_probes"), 35203831d35Sstevel 35303831d35Sstevel MK_ERROR32("align_errors", "framing"), 35403831d35Sstevel MK_ERROR32("fcs_errors", "crc"), 35503831d35Sstevel MK_ERROR32("first_collisions", NULL), 35603831d35Sstevel MK_ERROR32("multi_collisions", NULL), 35703831d35Sstevel MK_ERROR32("sqe_errors", "sqe"), 35803831d35Sstevel 35903831d35Sstevel MK_ERROR32("tx_late_collisions", NULL), 36003831d35Sstevel MK_ERROR32("ex_collisions", "excollisions"), 36103831d35Sstevel MK_ERROR32("macxmt_errors", NULL), 36203831d35Sstevel MK_ERROR32("carrier_errors", "nocarrier"), 36303831d35Sstevel MK_ERROR32("toolong_errors", "buff"), 36403831d35Sstevel MK_ERROR32("macrcv_errors", NULL), 36503831d35Sstevel 36603831d35Sstevel MK_OBSOLETE32("framing", "align_errors"), 36703831d35Sstevel MK_OBSOLETE32("crc", "fcs_errors"), 36803831d35Sstevel MK_OBSOLETE32("sqe", "sqe_errors"), 36903831d35Sstevel MK_OBSOLETE32("excollisions", "ex_collisions"), 37003831d35Sstevel MK_OBSOLETE32("nocarrier", "carrier_errors"), 37103831d35Sstevel MK_OBSOLETE32("buff", "toolong_errors"), 37203831d35Sstevel }; 37303831d35Sstevel 37403831d35Sstevel #define MAN_NUMSTATS (sizeof (man_kstat_info) / sizeof (man_kstat_info_t)) 37503831d35Sstevel 37603831d35Sstevel /* 37703831d35Sstevel * Miscellaneous ethernet stuff. 37803831d35Sstevel * 37903831d35Sstevel * MANs DL_INFO_ACK template. 38003831d35Sstevel */ 38103831d35Sstevel static dl_info_ack_t man_infoack = { 38203831d35Sstevel DL_INFO_ACK, /* dl_primitive */ 38303831d35Sstevel ETHERMTU, /* dl_max_sdu */ 38403831d35Sstevel 0, /* dl_min_sdu */ 38503831d35Sstevel MAN_ADDRL, /* dl_addr_length */ 38603831d35Sstevel DL_ETHER, /* dl_mac_type */ 38703831d35Sstevel 0, /* dl_reserved */ 38803831d35Sstevel 0, /* dl_current_state */ 38903831d35Sstevel -2, /* dl_sap_length */ 39003831d35Sstevel DL_CLDLS, /* dl_service_mode */ 39103831d35Sstevel 0, /* dl_qos_length */ 39203831d35Sstevel 0, /* dl_qos_offset */ 39303831d35Sstevel 0, /* dl_range_length */ 39403831d35Sstevel 0, /* dl_range_offset */ 39503831d35Sstevel DL_STYLE2, /* dl_provider_style */ 39603831d35Sstevel sizeof (dl_info_ack_t), /* dl_addr_offset */ 39703831d35Sstevel DL_VERSION_2, /* dl_version */ 39803831d35Sstevel ETHERADDRL, /* dl_brdcst_addr_length */ 39903831d35Sstevel sizeof (dl_info_ack_t) + MAN_ADDRL, /* dl_brdcst_addr_offset */ 40003831d35Sstevel 0 /* dl_growth */ 40103831d35Sstevel }; 40203831d35Sstevel 40303831d35Sstevel /* 40403831d35Sstevel * Ethernet broadcast address definition. 40503831d35Sstevel */ 40603831d35Sstevel static struct ether_addr etherbroadcast = { 40703831d35Sstevel 0xff, 0xff, 0xff, 0xff, 0xff, 0xff 40803831d35Sstevel }; 40903831d35Sstevel 41003831d35Sstevel static struct ether_addr zero_ether_addr = { 41103831d35Sstevel 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 41203831d35Sstevel }; 41303831d35Sstevel 41403831d35Sstevel /* 41503831d35Sstevel * Set via MAN_SET_SC_IPADDRS ioctl. 41603831d35Sstevel */ 41703831d35Sstevel man_sc_ipaddrs_t man_sc_ipaddrs = { 0xffffffffU, 0xffffffffU }; 41803831d35Sstevel 41903831d35Sstevel /* 42003831d35Sstevel * Set via MAN_SET_SC_IP6ADDRS ioctl. 42103831d35Sstevel */ 42203831d35Sstevel man_sc_ip6addrs_t man_sc_ip6addrs = { 0, 0, 0, 0, 0, 0, 0, 0 }; 42303831d35Sstevel 42403831d35Sstevel /* 42503831d35Sstevel * IP & ICMP constants 42603831d35Sstevel */ 42703831d35Sstevel #ifndef ETHERTYPE_IPV6 42803831d35Sstevel #define ETHERTYPE_IPV6 0x86DD 42903831d35Sstevel #endif 43003831d35Sstevel 43103831d35Sstevel /* 43203831d35Sstevel * Function prototypes. 43303831d35Sstevel * 43403831d35Sstevel * Upper multiplexor functions. 43503831d35Sstevel */ 43603831d35Sstevel static int man_attach(dev_info_t *, ddi_attach_cmd_t); 43703831d35Sstevel static int man_detach(dev_info_t *, ddi_detach_cmd_t); 43803831d35Sstevel static int man_info(dev_info_t *, ddi_info_cmd_t, void *, void **); 43903831d35Sstevel static int man_open(register queue_t *, dev_t *, int, int, cred_t *); 44003831d35Sstevel static int man_configure(queue_t *); 44103831d35Sstevel static int man_deconfigure(void); 44203831d35Sstevel static int man_init_dests(man_t *, manstr_t *); 44303831d35Sstevel static void man_start_dest(man_dest_t *, manstr_t *, man_pg_t *); 44403831d35Sstevel static void man_set_optimized_dest(manstr_t *); 44503831d35Sstevel static int man_close(queue_t *); 44603831d35Sstevel static void man_cancel_timers(man_adest_t *); 44703831d35Sstevel static int man_uwput(queue_t *, mblk_t *); 44803831d35Sstevel static int man_start(queue_t *, mblk_t *, eaddr_t *); 44903831d35Sstevel static void man_ioctl(queue_t *, mblk_t *); 45003831d35Sstevel static void man_set_linkcheck_time(queue_t *, mblk_t *); 45103831d35Sstevel static void man_setpath(queue_t *, mblk_t *); 45203831d35Sstevel static void man_geteaddr(queue_t *, mblk_t *); 45303831d35Sstevel static void man_set_sc_ipaddrs(queue_t *, mblk_t *); 45403831d35Sstevel static void man_set_sc_ip6addrs(queue_t *, mblk_t *); 45503831d35Sstevel static int man_get_our_etheraddr(eaddr_t *eap); 45603831d35Sstevel static void man_nd_getset(queue_t *, mblk_t *); 45703831d35Sstevel static void man_dl_ioc_hdr_info(queue_t *, mblk_t *); 45803831d35Sstevel static int man_uwsrv(queue_t *); 45903831d35Sstevel static int man_proto(queue_t *, mblk_t *); 46003831d35Sstevel static int man_udreq(queue_t *, mblk_t *); 46103831d35Sstevel static void man_areq(queue_t *, mblk_t *); 46203831d35Sstevel static mblk_t *man_alloc_physreq_mp(eaddr_t *); 46303831d35Sstevel static void man_dreq(queue_t *, mblk_t *); 46403831d35Sstevel static void man_dodetach(manstr_t *, man_work_t *); 46503831d35Sstevel static void man_dl_clean(mblk_t **); 46603831d35Sstevel static void man_breq(queue_t *, mblk_t *); 46703831d35Sstevel static void man_ubreq(queue_t *, mblk_t *); 46803831d35Sstevel static void man_ireq(queue_t *, mblk_t *); 46903831d35Sstevel static void man_ponreq(queue_t *, mblk_t *); 47003831d35Sstevel static void man_poffreq(queue_t *, mblk_t *); 47103831d35Sstevel static void man_emreq(queue_t *, mblk_t *); 47203831d35Sstevel static void man_dmreq(queue_t *, mblk_t *); 47303831d35Sstevel static void man_pareq(queue_t *, mblk_t *); 47403831d35Sstevel static void man_spareq(queue_t *, mblk_t *); 47503831d35Sstevel static int man_dlpi(manstr_t *, mblk_t *); 47603831d35Sstevel static int man_dlioc(manstr_t *, mblk_t *); 47703831d35Sstevel static int man_dl_catch(mblk_t **, mblk_t *); 47803831d35Sstevel static void man_dl_release(mblk_t **, mblk_t *); 47903831d35Sstevel static int man_match_proto(mblk_t *, mblk_t *); 48003831d35Sstevel static int man_open_ctl(); 48103831d35Sstevel static void man_close_ctl(); 48203831d35Sstevel /* 48303831d35Sstevel * upper/lower multiplexor functions. 48403831d35Sstevel */ 48503831d35Sstevel static int man_dlpi_senddown(manstr_t *, mblk_t *); 48603831d35Sstevel static int man_start_lower(man_dest_t *, mblk_t *, queue_t *, int caller); 48703831d35Sstevel static int man_lrput(queue_t *, mblk_t *); 48803831d35Sstevel /* 48903831d35Sstevel * Lower multiplexor functions. 49003831d35Sstevel */ 49103831d35Sstevel static int man_lwsrv(queue_t *); 49203831d35Sstevel static int man_lrsrv(queue_t *); 49303831d35Sstevel static void man_dlpi_replay(man_dest_t *, mblk_t *); 49403831d35Sstevel static int man_dlioc_replay(man_dest_t *); 49503831d35Sstevel /* 49603831d35Sstevel * Link failover routines. 49703831d35Sstevel */ 49803831d35Sstevel static int man_gettimer(int, man_dest_t *); 49903831d35Sstevel static void man_linkcheck_timer(void *); 50003831d35Sstevel static int man_needs_linkcheck(man_dest_t *); 50103831d35Sstevel static int man_do_autoswitch(man_dest_t *); 50203831d35Sstevel static int man_autoswitch(man_pg_t *, man_dev_t *, man_work_t *); 50303831d35Sstevel static int man_prep_dests_for_switch(man_pg_t *, man_dest_t **, int *); 50403831d35Sstevel static int man_str_uses_pg(manstr_t *, man_pg_t *); 50503831d35Sstevel static void man_do_icmp_bcast(man_dest_t *, t_uscalar_t); 50603831d35Sstevel static mblk_t *man_alloc_udreq(int, man_dladdr_t *); 50703831d35Sstevel static mblk_t *man_pinger(t_uscalar_t); 50803831d35Sstevel /* 50903831d35Sstevel * Functions normally executing outside of the STREAMs perimeter. 51003831d35Sstevel */ 51103831d35Sstevel /* 51203831d35Sstevel * Functions supporting/processing work requests. 51303831d35Sstevel */ 51403831d35Sstevel static void man_bwork(void); 51503831d35Sstevel static void man_iwork(void); /* inside perimeter */ 51603831d35Sstevel void man_work_add(man_workq_t *, man_work_t *); 51703831d35Sstevel man_work_t *man_work_alloc(int, int); 51803831d35Sstevel void man_work_free(man_work_t *); 51903831d35Sstevel /* 52003831d35Sstevel * Functions implementing/supporting failover. 52103831d35Sstevel * 52203831d35Sstevel * Executed inside perimeter. 52303831d35Sstevel */ 52403831d35Sstevel static int man_do_dr_attach(man_work_t *); 52503831d35Sstevel static int man_do_dr_switch(man_work_t *); 52603831d35Sstevel static void man_do_dr_detach(man_work_t *); 52703831d35Sstevel static int man_iswitch(man_work_t *); 52803831d35Sstevel static void man_ifail_dest(man_dest_t *); 52903831d35Sstevel static man_dest_t *man_switch_match(man_dest_t *, int, void *); 53003831d35Sstevel static void man_add_dests(man_pg_t *); 53103831d35Sstevel static void man_reset_dlpi(void *); 53203831d35Sstevel static mblk_t *man_dup_mplist(mblk_t *); 53303831d35Sstevel static mblk_t *man_alloc_ubreq_dreq(); 53403831d35Sstevel /* 53503831d35Sstevel * Executed outside perimeter (us man_lock for synchronization). 53603831d35Sstevel */ 53703831d35Sstevel static void man_bclose(man_adest_t *); 53803831d35Sstevel static void man_bswitch(man_adest_t *, man_work_t *); 53903831d35Sstevel static int man_plumb(man_dest_t *); 54003831d35Sstevel static void man_unplumb(man_dest_t *); 54103831d35Sstevel static void man_plink(queue_t *, mblk_t *); 54203831d35Sstevel static void man_unplink(queue_t *, mblk_t *); 54303831d35Sstevel static void man_linkrec_insert(man_linkrec_t *); 54403831d35Sstevel static queue_t *man_linkrec_find(int); 54503831d35Sstevel /* 54603831d35Sstevel * Functions supporting pathgroups 54703831d35Sstevel */ 54803831d35Sstevel int man_pg_cmd(mi_path_t *, man_work_t *); 54903831d35Sstevel static int man_pg_assign(man_pg_t **, mi_path_t *, int); 55003831d35Sstevel static int man_pg_create(man_pg_t **, man_pg_t **, mi_path_t *); 55103831d35Sstevel static int man_pg_unassign(man_pg_t **, mi_path_t *); 55203831d35Sstevel static int man_pg_activate(man_t *, mi_path_t *, man_work_t *); 55303831d35Sstevel static int man_pg_read(man_pg_t *, mi_path_t *); 55403831d35Sstevel static man_pg_t *man_find_path_by_dev(man_pg_t *, man_dev_t *, man_path_t **); 55503831d35Sstevel static man_pg_t *man_find_pg_by_id(man_pg_t *, int); 55603831d35Sstevel static man_path_t *man_find_path_by_ppa(man_path_t *, int); 55703831d35Sstevel static man_path_t *man_find_active_path(man_path_t *); 55803831d35Sstevel static man_path_t *man_find_alternate_path(man_path_t *); 55903831d35Sstevel static void man_path_remove(man_path_t **, man_path_t *); 56003831d35Sstevel static void man_path_insert(man_path_t **, man_path_t *); 56103831d35Sstevel static void man_path_merge(man_path_t **, man_path_t *); 56203831d35Sstevel static int man_path_kstat_init(man_path_t *); 56303831d35Sstevel static void man_path_kstat_uninit(man_path_t *); 56403831d35Sstevel /* 56503831d35Sstevel * Functions supporting kstat reporting. 56603831d35Sstevel */ 56703831d35Sstevel static int man_kstat_update(kstat_t *, int); 56803831d35Sstevel static void man_do_kstats(man_work_t *); 56903831d35Sstevel static void man_update_path_kstats(man_t *); 57003831d35Sstevel static void man_update_dev_kstats(kstat_named_t *, man_path_t *); 57103831d35Sstevel static void man_sum_dests_kstats(kstat_named_t *, man_pg_t *); 57203831d35Sstevel static void man_kstat_named_init(kstat_named_t *, int); 57303831d35Sstevel static int man_kstat_byname(kstat_t *, char *, kstat_named_t *); 57403831d35Sstevel static void man_sum_kstats(kstat_named_t *, kstat_t *, kstat_named_t *); 57503831d35Sstevel /* 57603831d35Sstevel * Functions supporting ndd. 57703831d35Sstevel */ 57803831d35Sstevel static int man_param_register(param_t *, int); 57903831d35Sstevel static int man_pathgroups_report(queue_t *, mblk_t *, caddr_t, cred_t *); 58003831d35Sstevel static void man_preport(man_path_t *, mblk_t *); 58103831d35Sstevel static int man_set_active_path(queue_t *, mblk_t *, char *, caddr_t, 58203831d35Sstevel cred_t *); 58303831d35Sstevel static int man_get_hostinfo(queue_t *, mblk_t *, caddr_t, cred_t *); 58403831d35Sstevel static char *man_inet_ntoa(in_addr_t); 58503831d35Sstevel static int man_param_get(queue_t *, mblk_t *, caddr_t, cred_t *); 58603831d35Sstevel static int man_param_set(queue_t *, mblk_t *, char *, caddr_t, cred_t *); 58703831d35Sstevel static void man_param_cleanup(void); 58803831d35Sstevel static void man_nd_free(caddr_t *nd_pparam); 58903831d35Sstevel /* 59003831d35Sstevel * MAN SSC/Domain specific externs. 59103831d35Sstevel */ 59203831d35Sstevel extern int man_get_iosram(manc_t *); 59303831d35Sstevel extern int man_domain_configure(void); 59403831d35Sstevel extern int man_domain_deconfigure(void); 59503831d35Sstevel extern int man_dossc_switch(uint32_t); 59603831d35Sstevel extern int man_is_on_domain; 59703831d35Sstevel 59803831d35Sstevel /* 59903831d35Sstevel * Driver Globals protected by inner perimeter. 60003831d35Sstevel */ 60103831d35Sstevel static manstr_t *man_strup = NULL; /* list of MAN STREAMS */ 60203831d35Sstevel static caddr_t man_ndlist = NULL; /* head of ndd var list */ 60303831d35Sstevel void *man_softstate = NULL; 60403831d35Sstevel 60503831d35Sstevel /* 60603831d35Sstevel * Driver globals protected by man_lock. 60703831d35Sstevel */ 60803831d35Sstevel kmutex_t man_lock; /* lock protecting vars below */ 60903831d35Sstevel static kthread_id_t man_bwork_id = NULL; /* background thread ID */ 61003831d35Sstevel man_workq_t *man_bwork_q; /* bgthread work q */ 61103831d35Sstevel man_workq_t *man_iwork_q; /* inner perim (uwsrv) work q */ 61203831d35Sstevel static man_linkrec_t *man_linkrec_head = NULL; /* list of linkblks */ 61303831d35Sstevel ldi_handle_t man_ctl_lh = NULL; /* MAN control handle */ 61403831d35Sstevel queue_t *man_ctl_wq = NULL; /* MAN control rq */ 61503831d35Sstevel static int man_config_state = MAN_UNCONFIGURED; 61603831d35Sstevel static int man_config_error = ENODEV; 61703831d35Sstevel 61803831d35Sstevel /* 61903831d35Sstevel * These parameters are accessed via ndd to report the link configuration 62003831d35Sstevel * for the MAN driver. They can also be used to force configuration changes. 62103831d35Sstevel */ 62203831d35Sstevel #define MAN_NOTUSR 0x0f000000 62303831d35Sstevel 62403831d35Sstevel /* ------------------------------------------------------------------------- */ 62503831d35Sstevel 62603831d35Sstevel static param_t man_param_arr[] = { 62703831d35Sstevel /* min max value name */ 62803831d35Sstevel { 0, 0xFFFF, 0, "man_debug_level"}, 62903831d35Sstevel }; 63003831d35Sstevel 63103831d35Sstevel #define MAN_NDD_GETABLE 1 63203831d35Sstevel #define MAN_NDD_SETABLE 2 63303831d35Sstevel 63403831d35Sstevel static uint32_t man_param_display[] = { 63503831d35Sstevel /* DISPLAY */ 63603831d35Sstevel MAN_NDD_SETABLE, /* man_debug_level */ 63703831d35Sstevel }; 63803831d35Sstevel 63903831d35Sstevel /* 64003831d35Sstevel * STREAMs information. 64103831d35Sstevel */ 64203831d35Sstevel static struct module_info man_m_info = { 64303831d35Sstevel MAN_IDNUM, /* mi_idnum */ 64403831d35Sstevel MAN_IDNAME, /* mi_idname */ 64503831d35Sstevel MAN_MINPSZ, /* mi_minpsz */ 64603831d35Sstevel MAN_MAXPSZ, /* mi_maxpsz */ 64703831d35Sstevel MAN_HIWAT, /* mi_hiwat */ 64803831d35Sstevel MAN_LOWAT /* mi_lowat */ 64903831d35Sstevel }; 65003831d35Sstevel 65103831d35Sstevel /* 65203831d35Sstevel * Upper read queue does not do anything. 65303831d35Sstevel */ 65403831d35Sstevel static struct qinit man_urinit = { 65503831d35Sstevel NULL, /* qi_putp */ 65603831d35Sstevel NULL, /* qi_srvp */ 65703831d35Sstevel man_open, /* qi_qopen */ 65803831d35Sstevel man_close, /* qi_qclose */ 65903831d35Sstevel NULL, /* qi_qadmin */ 66003831d35Sstevel &man_m_info, /* qi_minfo */ 66103831d35Sstevel NULL /* qi_mstat */ 66203831d35Sstevel }; 66303831d35Sstevel 66403831d35Sstevel static struct qinit man_lrinit = { 66503831d35Sstevel man_lrput, /* qi_putp */ 66603831d35Sstevel man_lrsrv, /* qi_srvp */ 66703831d35Sstevel man_open, /* qi_qopen */ 66803831d35Sstevel man_close, /* qi_qclose */ 66903831d35Sstevel NULL, /* qi_qadmin */ 67003831d35Sstevel &man_m_info, /* qi_minfo */ 67103831d35Sstevel NULL /* qi_mstat */ 67203831d35Sstevel }; 67303831d35Sstevel 67403831d35Sstevel static struct qinit man_uwinit = { 67503831d35Sstevel man_uwput, /* qi_putp */ 67603831d35Sstevel man_uwsrv, /* qi_srvp */ 67703831d35Sstevel man_open, /* qi_qopen */ 67803831d35Sstevel man_close, /* qi_qclose */ 67903831d35Sstevel NULL, /* qi_qadmin */ 68003831d35Sstevel &man_m_info, /* qi_minfo */ 68103831d35Sstevel NULL /* qi_mstat */ 68203831d35Sstevel }; 68303831d35Sstevel 68403831d35Sstevel static struct qinit man_lwinit = { 68503831d35Sstevel NULL, /* qi_putp */ 68603831d35Sstevel man_lwsrv, /* qi_srvp */ 68703831d35Sstevel man_open, /* qi_qopen */ 68803831d35Sstevel man_close, /* qi_qclose */ 68903831d35Sstevel NULL, /* qi_qadmin */ 69003831d35Sstevel &man_m_info, /* qi_minfo */ 69103831d35Sstevel NULL /* qi_mstat */ 69203831d35Sstevel }; 69303831d35Sstevel 69403831d35Sstevel static struct streamtab man_maninfo = { 69503831d35Sstevel &man_urinit, /* st_rdinit */ 69603831d35Sstevel &man_uwinit, /* st_wrinit */ 69703831d35Sstevel &man_lrinit, /* st_muxrinit */ 69803831d35Sstevel &man_lwinit /* st_muxwrinit */ 69903831d35Sstevel }; 70003831d35Sstevel 70103831d35Sstevel 70203831d35Sstevel /* 70303831d35Sstevel * Module linkage information for the kernel. 70403831d35Sstevel * 70503831d35Sstevel * Locking Theory: 70603831d35Sstevel * D_MTPERMOD - Only an inner perimeter: All routines single 70703831d35Sstevel * threaded (except put, see below). 70803831d35Sstevel * D_MTPUTSHARED - Put routines enter inner perimeter shared (not 70903831d35Sstevel * exclusive) for concurrency/performance reasons. 71003831d35Sstevel * 71103831d35Sstevel * Anyone who needs exclusive outer perimeter permission (changing 71203831d35Sstevel * global data structures) does so via qwriter() calls. The 71303831d35Sstevel * background thread does all his work outside of perimeter and 71403831d35Sstevel * submits work via qtimeout() when data structures need to be 71503831d35Sstevel * modified. 71603831d35Sstevel */ 71703831d35Sstevel 71803831d35Sstevel #define MAN_MDEV_FLAGS (D_MP|D_MTPERMOD|D_MTPUTSHARED) 71903831d35Sstevel 72003831d35Sstevel DDI_DEFINE_STREAM_OPS(man_ops, nulldev, nulldev, man_attach, 72119397407SSherry Moore man_detach, nodev, man_info, MAN_MDEV_FLAGS, &man_maninfo, 72219397407SSherry Moore ddi_quiesce_not_supported); 72303831d35Sstevel 72403831d35Sstevel extern int nodev(), nulldev(); 72503831d35Sstevel 72603831d35Sstevel static struct modldrv modldrv = { 72703831d35Sstevel &mod_driverops, /* Module type. This one is a pseudo driver */ 72819397407SSherry Moore "MAN MetaDriver", 72903831d35Sstevel &man_ops, /* driver ops */ 73003831d35Sstevel }; 73103831d35Sstevel 73203831d35Sstevel static struct modlinkage modlinkage = { 73303831d35Sstevel MODREV_1, 73403831d35Sstevel (void *) &modldrv, 73503831d35Sstevel NULL 73603831d35Sstevel }; 73703831d35Sstevel 73803831d35Sstevel 73903831d35Sstevel /* Virtual Driver loader entry points */ 74003831d35Sstevel 74103831d35Sstevel int 74203831d35Sstevel _init(void) 74303831d35Sstevel { 74403831d35Sstevel int status = DDI_FAILURE; 74503831d35Sstevel 74603831d35Sstevel MAN_DBG(MAN_INIT, ("_init:")); 74703831d35Sstevel 74803831d35Sstevel status = mod_install(&modlinkage); 74903831d35Sstevel if (status != 0) { 75003831d35Sstevel cmn_err(CE_WARN, "man_init: mod_install failed" 75103831d35Sstevel " error = %d", status); 75203831d35Sstevel return (status); 75303831d35Sstevel } 75403831d35Sstevel 75503831d35Sstevel status = ddi_soft_state_init(&man_softstate, sizeof (man_t), 4); 75603831d35Sstevel if (status != 0) { 75703831d35Sstevel cmn_err(CE_WARN, "man_init: ddi_soft_state_init failed" 75803831d35Sstevel " error = %d", status); 759*07d06da5SSurya Prakki (void) mod_remove(&modlinkage); 76003831d35Sstevel return (status); 76103831d35Sstevel } 76203831d35Sstevel 76303831d35Sstevel man_bwork_q = man_kzalloc(sizeof (man_workq_t), KM_SLEEP); 76403831d35Sstevel man_iwork_q = man_kzalloc(sizeof (man_workq_t), KM_SLEEP); 76503831d35Sstevel 76603831d35Sstevel mutex_init(&man_lock, NULL, MUTEX_DRIVER, NULL); 76703831d35Sstevel cv_init(&man_bwork_q->q_cv, NULL, CV_DRIVER, NULL); 76803831d35Sstevel cv_init(&man_iwork_q->q_cv, NULL, CV_DRIVER, NULL); 76903831d35Sstevel 77003831d35Sstevel return (0); 77103831d35Sstevel } 77203831d35Sstevel 77303831d35Sstevel /* 77403831d35Sstevel * _info is called by modinfo(). 77503831d35Sstevel */ 77603831d35Sstevel int 77703831d35Sstevel _info(struct modinfo *modinfop) 77803831d35Sstevel { 77903831d35Sstevel int status; 78003831d35Sstevel 78103831d35Sstevel MAN_DBG(MAN_INIT, ("_info:")); 78203831d35Sstevel 78303831d35Sstevel status = mod_info(&modlinkage, modinfop); 78403831d35Sstevel 78503831d35Sstevel MAN_DBG(MAN_INIT, ("_info: returns %d", status)); 78603831d35Sstevel 78703831d35Sstevel return (status); 78803831d35Sstevel } 78903831d35Sstevel 79003831d35Sstevel /* 79103831d35Sstevel * _fini called by modunload() just before driver is unloaded from memory. 79203831d35Sstevel */ 79303831d35Sstevel int 79403831d35Sstevel _fini(void) 79503831d35Sstevel { 79603831d35Sstevel int status = 0; 79703831d35Sstevel 79803831d35Sstevel MAN_DBG(MAN_INIT, ("_fini:")); 79903831d35Sstevel 80003831d35Sstevel 80103831d35Sstevel /* 80203831d35Sstevel * The only upper stream left should be man_ctl_lh. Note that 80303831d35Sstevel * man_close (upper stream) is synchronous (i.e. it waits for 80403831d35Sstevel * all STREAMS framework associated with the upper stream to be 80503831d35Sstevel * torn down). This guarantees that man_ctl_lh will never become 80603831d35Sstevel * NULL until noone is around to notice. This assumption is made 80703831d35Sstevel * in a few places like man_plumb, man_unplumb, etc. 80803831d35Sstevel */ 80903831d35Sstevel if (man_strup && (man_strup->ms_next != NULL)) 81003831d35Sstevel return (EBUSY); 81103831d35Sstevel 81203831d35Sstevel /* 81303831d35Sstevel * Deconfigure the driver. 81403831d35Sstevel */ 81503831d35Sstevel status = man_deconfigure(); 81603831d35Sstevel if (status) 81703831d35Sstevel goto exit; 81803831d35Sstevel 81903831d35Sstevel /* 82003831d35Sstevel * need to detach every instance of the driver 82103831d35Sstevel */ 82203831d35Sstevel status = mod_remove(&modlinkage); 82303831d35Sstevel if (status != 0) 82403831d35Sstevel goto exit; 82503831d35Sstevel 82603831d35Sstevel ddi_soft_state_fini(&man_softstate); 82703831d35Sstevel 82803831d35Sstevel /* 82903831d35Sstevel * Free up locks. 83003831d35Sstevel */ 83103831d35Sstevel mutex_destroy(&man_lock); 83203831d35Sstevel cv_destroy(&man_bwork_q->q_cv); 83303831d35Sstevel cv_destroy(&man_iwork_q->q_cv); 83403831d35Sstevel 83503831d35Sstevel man_kfree(man_bwork_q, sizeof (man_workq_t)); 83603831d35Sstevel man_kfree(man_iwork_q, sizeof (man_workq_t)); 83703831d35Sstevel 83803831d35Sstevel exit: 83903831d35Sstevel 84003831d35Sstevel MAN_DBG(MAN_INIT, ("_fini: returns %d", status)); 84103831d35Sstevel 84203831d35Sstevel return (status); 84303831d35Sstevel } 84403831d35Sstevel 84503831d35Sstevel /* 84603831d35Sstevel * Deconfigure the MAN driver. 84703831d35Sstevel */ 84803831d35Sstevel static int 84903831d35Sstevel man_deconfigure() 85003831d35Sstevel { 85103831d35Sstevel man_work_t *wp; 85203831d35Sstevel int status = 0; 85303831d35Sstevel 85403831d35Sstevel MAN_DBG(MAN_CONFIG, ("man_deconfigure:\n")); 85503831d35Sstevel 85603831d35Sstevel mutex_enter(&man_lock); 85703831d35Sstevel 85803831d35Sstevel if (man_is_on_domain) { 85903831d35Sstevel status = man_domain_deconfigure(); 86003831d35Sstevel if (status != 0) 86103831d35Sstevel goto exit; 86203831d35Sstevel } 86303831d35Sstevel 86403831d35Sstevel man_param_cleanup(); /* Free up NDD resources */ 86503831d35Sstevel 86603831d35Sstevel /* 86703831d35Sstevel * I may have to handle straggling work requests. Just qwait? 86803831d35Sstevel * or cvwait? Called from _fini - TBD 86903831d35Sstevel */ 87003831d35Sstevel ASSERT(man_bwork_q->q_work == NULL); 87103831d35Sstevel ASSERT(man_iwork_q->q_work == NULL); 87203831d35Sstevel 87303831d35Sstevel MAN_DBG(MAN_CONFIG, ("man_deconfigure: submitting CLOSE_CTL\n")); 87403831d35Sstevel 87503831d35Sstevel if (man_ctl_lh != NULL) { 87603831d35Sstevel wp = man_work_alloc(MAN_WORK_CLOSE_CTL, KM_SLEEP); 87703831d35Sstevel wp->mw_flags = MAN_WFLAGS_CVWAITER; 87803831d35Sstevel man_work_add(man_bwork_q, wp); 87903831d35Sstevel 88003831d35Sstevel while (!(wp->mw_flags & MAN_WFLAGS_DONE)) { 88103831d35Sstevel cv_wait(&wp->mw_cv, &man_lock); 88203831d35Sstevel } 88303831d35Sstevel man_work_free(wp); 88403831d35Sstevel } 88503831d35Sstevel 88603831d35Sstevel MAN_DBG(MAN_CONFIG, ("man_deconfigure: submitting STOP\n")); 88703831d35Sstevel if (man_bwork_id != NULL) { 88803831d35Sstevel 88903831d35Sstevel wp = man_work_alloc(MAN_WORK_STOP, KM_SLEEP); 89003831d35Sstevel wp->mw_flags = MAN_WFLAGS_CVWAITER; 89103831d35Sstevel man_work_add(man_bwork_q, wp); 89203831d35Sstevel 89303831d35Sstevel while (!(wp->mw_flags & MAN_WFLAGS_DONE)) { 89403831d35Sstevel cv_wait(&wp->mw_cv, &man_lock); 89503831d35Sstevel } 89603831d35Sstevel man_work_free(wp); 89703831d35Sstevel } 89803831d35Sstevel man_config_state = MAN_UNCONFIGURED; 89903831d35Sstevel 90003831d35Sstevel exit: 90103831d35Sstevel mutex_exit(&man_lock); 90203831d35Sstevel 90303831d35Sstevel MAN_DBG(MAN_CONFIG, ("man_deconfigure: returns %d\n", status)); 90403831d35Sstevel 90503831d35Sstevel return (status); 90603831d35Sstevel } 90703831d35Sstevel 90803831d35Sstevel /* 90903831d35Sstevel * man_attach - allocate resources and attach an instance of the MAN driver 91003831d35Sstevel * The <man>.conf file controls how many instances of the MAN driver are 91103831d35Sstevel * available. 91203831d35Sstevel * 91303831d35Sstevel * dip - devinfo of node 91403831d35Sstevel * cmd - one of DDI_ATTACH | DDI_RESUME 91503831d35Sstevel * 91603831d35Sstevel * returns - success - DDI_SUCCESS 91703831d35Sstevel * - failure - DDI_FAILURE 91803831d35Sstevel */ 91903831d35Sstevel static int 92003831d35Sstevel man_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 92103831d35Sstevel { 92203831d35Sstevel man_t *manp; /* per instance data */ 92303831d35Sstevel uchar_t flag = KSTAT_FLAG_WRITABLE; /* support netstat -kc */ 92403831d35Sstevel kstat_t *ksp; 92503831d35Sstevel int minor_node_created = 0; 92603831d35Sstevel int instance; 92703831d35Sstevel eaddr_t man_eaddr; 92803831d35Sstevel 92903831d35Sstevel MAN_DBG(MAN_INIT, ("man_attach: \n")); 93003831d35Sstevel 93103831d35Sstevel if (cmd != DDI_ATTACH) { 93203831d35Sstevel MAN_DBG(MAN_INIT, ("man_attach: bad command %d\n", cmd)); 93303831d35Sstevel return (DDI_FAILURE); 93403831d35Sstevel } 93503831d35Sstevel 93603831d35Sstevel if (man_get_our_etheraddr(&man_eaddr)) 93703831d35Sstevel return (DDI_FAILURE); 93803831d35Sstevel 93903831d35Sstevel instance = ddi_get_instance(dip); 94003831d35Sstevel 94103831d35Sstevel /* 94203831d35Sstevel * we assume that instance is always equal to zero. 94303831d35Sstevel * and there will always only be one instance. 94403831d35Sstevel * this is done because when dman opens itself via DMAN_INT_PATH, 94503831d35Sstevel * the path assumes that the instance number is zero. 94603831d35Sstevel * if we ever need to support multiple instances of the dman 94703831d35Sstevel * driver or non-zero instances, this will have to change. 94803831d35Sstevel */ 94903831d35Sstevel ASSERT(instance == 0); 95003831d35Sstevel 95103831d35Sstevel /* 95203831d35Sstevel * Allocate per device info pointer and link in to global list of 95303831d35Sstevel * MAN devices. 95403831d35Sstevel */ 95503831d35Sstevel if ((ddi_soft_state_zalloc(man_softstate, instance) != DDI_SUCCESS) || 95603831d35Sstevel ((manp = ddi_get_soft_state(man_softstate, instance)) == NULL)) { 95703831d35Sstevel cmn_err(CE_WARN, "man_attach: cannot zalloc soft state!"); 95803831d35Sstevel return (DDI_FAILURE); 95903831d35Sstevel } 96003831d35Sstevel 96103831d35Sstevel ddi_set_driver_private(dip, manp); 96203831d35Sstevel manp->man_dip = dip; 9635c066ec2SJerry Gilliam manp->man_meta_major = ddi_driver_major(dip); 96403831d35Sstevel manp->man_meta_ppa = instance; 96503831d35Sstevel 96603831d35Sstevel /* 96703831d35Sstevel * Set ethernet address. Note that this address is duplicated 96803831d35Sstevel * at md_src_eaddr. 96903831d35Sstevel */ 97003831d35Sstevel ether_copy(&man_eaddr, &manp->man_eaddr); 97103831d35Sstevel manp->man_eaddr_v = 1; 97203831d35Sstevel 97303831d35Sstevel MAN_DBG(MAN_INIT, ("man_attach: set ether to %s", 97403831d35Sstevel ether_sprintf(&manp->man_eaddr))); 97503831d35Sstevel 97603831d35Sstevel /* 97703831d35Sstevel * Initialize failover-related fields (timers and such), 97803831d35Sstevel * taking values from properties if present. 97903831d35Sstevel */ 98003831d35Sstevel manp->man_init_time = ddi_getprop(DDI_DEV_T_ANY, dip, 0, 98103831d35Sstevel "init_time", MAN_INIT_TIME); 98203831d35Sstevel 98303831d35Sstevel manp->man_linkcheck_time = ddi_getprop(DDI_DEV_T_ANY, dip, 0, 98403831d35Sstevel "linkcheck_time", MAN_LINKCHECK_TIME); 98503831d35Sstevel 98603831d35Sstevel manp->man_linkstale_time = ddi_getprop(DDI_DEV_T_ANY, dip, 0, 98703831d35Sstevel "man_linkstale_time", MAN_LINKSTALE_TIME); 98803831d35Sstevel 98903831d35Sstevel manp->man_linkstale_retries = ddi_getprop(DDI_DEV_T_ANY, dip, 0, 99003831d35Sstevel "man_linkstale_retries", MAN_LINKSTALE_RETRIES); 99103831d35Sstevel 99203831d35Sstevel manp->man_dr_delay = ddi_getprop(DDI_DEV_T_ANY, dip, 0, 99303831d35Sstevel "man_dr_delay", MAN_DR_DELAY); 99403831d35Sstevel 99503831d35Sstevel manp->man_dr_retries = ddi_getprop(DDI_DEV_T_ANY, dip, 0, 99603831d35Sstevel "man_dr_retries", MAN_DR_RETRIES); 99703831d35Sstevel 99803831d35Sstevel manp->man_kstat_waittime = ddi_getprop(DDI_DEV_T_ANY, dip, 0, 99903831d35Sstevel "man_kstat_waittime", MAN_KSTAT_WAITTIME); 100003831d35Sstevel 100103831d35Sstevel manp->man_dlpireset_time = ddi_getprop(DDI_DEV_T_ANY, dip, 0, 100203831d35Sstevel "man_dlpireset_time", MAN_DLPIRESET_TIME); 100303831d35Sstevel 100403831d35Sstevel if (ddi_create_internal_pathname(dip, MAN_IDNAME, S_IFCHR, 100503831d35Sstevel ddi_get_instance(dip)) == DDI_SUCCESS) { 100603831d35Sstevel minor_node_created = 1; 100703831d35Sstevel } else { 100803831d35Sstevel cmn_err(CE_WARN, "man_attach: failed for instance %d", 100903831d35Sstevel ddi_get_instance(dip)); 101003831d35Sstevel goto exit; 101103831d35Sstevel } 101203831d35Sstevel 101303831d35Sstevel if (ddi_create_minor_node(dip, MAN_IDNAME, S_IFCHR, 101403831d35Sstevel ddi_get_instance(dip), DDI_NT_NET, CLONE_DEV) == DDI_SUCCESS) { 101503831d35Sstevel minor_node_created = 1; 101603831d35Sstevel } else { 101703831d35Sstevel cmn_err(CE_WARN, "man_attach: failed for instance %d", 101803831d35Sstevel ddi_get_instance(dip)); 101903831d35Sstevel goto exit; 102003831d35Sstevel } 102103831d35Sstevel 102203831d35Sstevel /* 102303831d35Sstevel * Allocate meta kstat_t for this instance of the driver. 102403831d35Sstevel * Note that each of man_path_t keeps track of the kstats 102503831d35Sstevel * for the real devices via mp_last_knp. 102603831d35Sstevel */ 102703831d35Sstevel #ifdef kstat 102803831d35Sstevel flag |= KSTAT_FLAG_PERSISTENT; 102903831d35Sstevel #endif 103003831d35Sstevel ksp = kstat_create(MAN_IDNAME, ddi_get_instance(dip), NULL, "net", 103103831d35Sstevel KSTAT_TYPE_NAMED, MAN_NUMSTATS, flag); 103203831d35Sstevel 103303831d35Sstevel if (ksp == NULL) { 103403831d35Sstevel cmn_err(CE_WARN, "man_attach(%d): kstat_create failed" 103503831d35Sstevel " - manp(0x%p)", manp->man_meta_ppa, 103603831d35Sstevel (void *)manp); 103703831d35Sstevel goto exit; 103803831d35Sstevel } 103903831d35Sstevel 104003831d35Sstevel man_kstat_named_init(ksp->ks_data, MAN_NUMSTATS); 104103831d35Sstevel ksp->ks_update = man_kstat_update; 104203831d35Sstevel ksp->ks_private = (void *) manp; 104303831d35Sstevel manp->man_ksp = ksp; 104403831d35Sstevel kstat_install(manp->man_ksp); 104503831d35Sstevel 104603831d35Sstevel ddi_report_dev(dip); 104703831d35Sstevel 104803831d35Sstevel MAN_DBG(MAN_INIT, ("man_attach(%d) returns DDI_SUCCESS", 104903831d35Sstevel ddi_get_instance(dip))); 105003831d35Sstevel 105103831d35Sstevel return (DDI_SUCCESS); 105203831d35Sstevel 105303831d35Sstevel exit: 105403831d35Sstevel if (minor_node_created) 105503831d35Sstevel ddi_remove_minor_node(dip, NULL); 105603831d35Sstevel ddi_set_driver_private(dip, NULL); 105703831d35Sstevel ddi_soft_state_free(man_softstate, instance); 105803831d35Sstevel 105903831d35Sstevel MAN_DBG(MAN_INIT, ("man_attach(%d) eaddr returns DDI_FAILIRE", 106003831d35Sstevel ddi_get_instance(dip))); 106103831d35Sstevel 106203831d35Sstevel return (DDI_FAILURE); 106303831d35Sstevel 106403831d35Sstevel } 106503831d35Sstevel 106603831d35Sstevel static int 106703831d35Sstevel man_get_our_etheraddr(eaddr_t *eap) 106803831d35Sstevel { 106903831d35Sstevel manc_t manc; 107003831d35Sstevel int status = 0; 107103831d35Sstevel 107203831d35Sstevel if (man_is_on_domain) { 107303831d35Sstevel if (status = man_get_iosram(&manc)) 107403831d35Sstevel return (status); 107503831d35Sstevel ether_copy(&manc.manc_dom_eaddr, eap); 107603831d35Sstevel } else { 107703831d35Sstevel (void) localetheraddr((struct ether_addr *)NULL, eap); 107803831d35Sstevel } 107903831d35Sstevel 108003831d35Sstevel return (status); 108103831d35Sstevel } 108203831d35Sstevel 108303831d35Sstevel /* 108403831d35Sstevel * man_detach - detach an instance of a driver 108503831d35Sstevel * 108603831d35Sstevel * dip - devinfo of node 108703831d35Sstevel * cmd - one of DDI_DETACH | DDI_SUSPEND 108803831d35Sstevel * 108903831d35Sstevel * returns - success - DDI_SUCCESS 109003831d35Sstevel * - failure - DDI_FAILURE 109103831d35Sstevel */ 109203831d35Sstevel static int 109303831d35Sstevel man_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 109403831d35Sstevel { 109503831d35Sstevel register man_t *manp; /* per instance data */ 109603831d35Sstevel int instance; 109703831d35Sstevel 109803831d35Sstevel MAN_DBG(MAN_INIT, ("man_detach(%d):\n", ddi_get_instance(dip))); 109903831d35Sstevel 110003831d35Sstevel if (cmd != DDI_DETACH) { 110103831d35Sstevel MAN_DBG(MAN_INIT, ("man_detach: bad command %d\n", cmd)); 110203831d35Sstevel return (DDI_FAILURE); 110303831d35Sstevel } 110403831d35Sstevel 110503831d35Sstevel if (dip == NULL) { 110603831d35Sstevel MAN_DBG(MAN_INIT, ("man_detach: dip == NULL\n")); 110703831d35Sstevel return (DDI_FAILURE); 110803831d35Sstevel } 110903831d35Sstevel 111003831d35Sstevel instance = ddi_get_instance(dip); 111103831d35Sstevel 111203831d35Sstevel mutex_enter(&man_lock); 111303831d35Sstevel 111403831d35Sstevel manp = (man_t *)ddi_get_soft_state(man_softstate, instance); 111503831d35Sstevel if (manp == NULL) { 111603831d35Sstevel mutex_exit(&man_lock); 111703831d35Sstevel 111803831d35Sstevel cmn_err(CE_WARN, "man_detach: unable to get softstate" 111903831d35Sstevel " for instance = %d, dip = 0x%p!\n", instance, 112003831d35Sstevel (void *)dip); 112103831d35Sstevel return (DDI_FAILURE); 112203831d35Sstevel } 112303831d35Sstevel 112403831d35Sstevel if (manp->man_refcnt != 0) { 112503831d35Sstevel mutex_exit(&man_lock); 112603831d35Sstevel 112703831d35Sstevel cmn_err(CE_WARN, "man_detach: %s%d refcnt %d", MAN_IDNAME, 112803831d35Sstevel instance, manp->man_refcnt); 112903831d35Sstevel MAN_DBGCALL(MAN_INIT, man_print_man(manp)); 113003831d35Sstevel 113103831d35Sstevel return (DDI_FAILURE); 113203831d35Sstevel } 113303831d35Sstevel 113403831d35Sstevel ddi_remove_minor_node(dip, NULL); 113503831d35Sstevel 113603831d35Sstevel mutex_exit(&man_lock); 113703831d35Sstevel 113803831d35Sstevel kstat_delete(manp->man_ksp); 113903831d35Sstevel ddi_soft_state_free(man_softstate, instance); 114003831d35Sstevel ddi_set_driver_private(dip, NULL); 114103831d35Sstevel 114203831d35Sstevel MAN_DBG(MAN_INIT, ("man_detach returns DDI_SUCCESS")); 114303831d35Sstevel 114403831d35Sstevel return (DDI_SUCCESS); 114503831d35Sstevel } 114603831d35Sstevel 114703831d35Sstevel /* 114803831d35Sstevel * man_info: 114903831d35Sstevel * As a standard DLPI style-2, man_info() should always return 115003831d35Sstevel * DDI_FAILURE. 115103831d35Sstevel * 115203831d35Sstevel * However, man_open() has special treatment for a direct open 115303831d35Sstevel * via kstr_open() without going through the CLONE driver. 115403831d35Sstevel * To make this special kstr_open() work, we need to map 115503831d35Sstevel * minor of 0 to instance 0. 115603831d35Sstevel */ 115703831d35Sstevel /*ARGSUSED*/ 115803831d35Sstevel static int 115903831d35Sstevel man_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result) 116003831d35Sstevel { 116103831d35Sstevel minor_t minor; 116203831d35Sstevel 116303831d35Sstevel switch (infocmd) { 116403831d35Sstevel case DDI_INFO_DEVT2DEVINFO: 116503831d35Sstevel break; 116603831d35Sstevel 116703831d35Sstevel case DDI_INFO_DEVT2INSTANCE: 116803831d35Sstevel minor = getminor((dev_t)arg); 116903831d35Sstevel if (minor == 0) { 117003831d35Sstevel *result = (void *)(uintptr_t)minor; 117103831d35Sstevel return (DDI_SUCCESS); 117203831d35Sstevel } 117303831d35Sstevel break; 117403831d35Sstevel default: 117503831d35Sstevel break; 117603831d35Sstevel } 117703831d35Sstevel return (DDI_FAILURE); 117803831d35Sstevel } 117903831d35Sstevel 118003831d35Sstevel /* Standard Device Driver entry points */ 118103831d35Sstevel 118203831d35Sstevel /* 118303831d35Sstevel * man_open - open the device 118403831d35Sstevel * 118503831d35Sstevel * rq - upper read queue of the stream 118603831d35Sstevel * devp - pointer to a device number 118703831d35Sstevel * flag - information passed from the user program open(2) system call 118803831d35Sstevel * sflag - stream flags 118903831d35Sstevel * credp - pointer to the cred(9S) user credential structure 119003831d35Sstevel * 119103831d35Sstevel * returns - success - 0 119203831d35Sstevel * - failure - errno value for failure 119303831d35Sstevel */ 119403831d35Sstevel /*ARGSUSED*/ 119503831d35Sstevel static int 119603831d35Sstevel man_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp) 119703831d35Sstevel { 119803831d35Sstevel int minordev = -1; 119903831d35Sstevel manstr_t *msp; 120003831d35Sstevel manstr_t *tsp; 120103831d35Sstevel manstr_t **prevmsp; 120203831d35Sstevel int status = 0; 120303831d35Sstevel 120403831d35Sstevel MAN_DBG(MAN_OCLOSE, ("man_open: rq(0x%p) sflag(0x%x)\n", 120503831d35Sstevel (void *)rq, sflag)); 120603831d35Sstevel 120703831d35Sstevel ASSERT(rq); 120803831d35Sstevel ASSERT(sflag != MODOPEN); 120903831d35Sstevel 121003831d35Sstevel /* 121103831d35Sstevel * reopen; q_ptr set to msp at open completion. 121203831d35Sstevel */ 121303831d35Sstevel if (rq->q_ptr) { 121403831d35Sstevel return (0); 121503831d35Sstevel } 121603831d35Sstevel 121703831d35Sstevel /* 121803831d35Sstevel * Allocate and initialize manstr_t for this device. 121903831d35Sstevel */ 122003831d35Sstevel msp = man_kzalloc(sizeof (manstr_t), KM_SLEEP); 122103831d35Sstevel SETSTATE(msp, DL_UNATTACHED); 122203831d35Sstevel msp->ms_meta_ppa = -1; 122303831d35Sstevel msp->ms_rq = rq; 122403831d35Sstevel rq->q_ptr = WR(rq)->q_ptr = msp; 122503831d35Sstevel 122603831d35Sstevel /* 122703831d35Sstevel * Get the MAN driver configured on 1st open. Note that the only way 122803831d35Sstevel * we get sflag != CLONEOPEN is via the call in man_plumbctl(). All 122903831d35Sstevel * CLONEOPEN calls to man_open will be via the file system 123003831d35Sstevel * device node /dev/man, a pseudo clone device. 123103831d35Sstevel */ 123203831d35Sstevel 123303831d35Sstevel qprocson(rq); 123403831d35Sstevel 123503831d35Sstevel if (sflag == CLONEOPEN && man_config_state != MAN_CONFIGURED) { 123603831d35Sstevel /* 123703831d35Sstevel * First open calls man_configure. Everyone qwaits until 123803831d35Sstevel * we get it open. See man_open_ctl() comments for mutex 123903831d35Sstevel * lock/synchronization info. 124003831d35Sstevel */ 124103831d35Sstevel 124203831d35Sstevel mutex_enter(&man_lock); 124303831d35Sstevel 124403831d35Sstevel if (man_config_state == MAN_UNCONFIGURED) { 124503831d35Sstevel man_config_state = MAN_CONFIGURING; 124603831d35Sstevel mutex_exit(&man_lock); 124703831d35Sstevel status = man_configure(rq); 124803831d35Sstevel if (status != 0) 124903831d35Sstevel goto exit; 125003831d35Sstevel } else { 125103831d35Sstevel while (man_config_state == MAN_CONFIGURING) { 125203831d35Sstevel 125303831d35Sstevel mutex_exit(&man_lock); 125403831d35Sstevel status = qwait_sig(rq); 125503831d35Sstevel 125603831d35Sstevel if (status == 0) { 125703831d35Sstevel status = EINTR; 125803831d35Sstevel goto exit; 125903831d35Sstevel } 126003831d35Sstevel 126103831d35Sstevel mutex_enter(&man_lock); 126203831d35Sstevel } 126303831d35Sstevel mutex_exit(&man_lock); 126403831d35Sstevel 126503831d35Sstevel if (man_config_error) { 126603831d35Sstevel status = man_config_error; 126703831d35Sstevel goto exit; 126803831d35Sstevel } 126903831d35Sstevel } 127003831d35Sstevel } 127103831d35Sstevel 127203831d35Sstevel /* 127303831d35Sstevel * Determine minor device number. man_open serialized by 127403831d35Sstevel * D_MTPERMOD. 127503831d35Sstevel */ 127603831d35Sstevel prevmsp = &man_strup; 127703831d35Sstevel if (sflag == CLONEOPEN) { 127803831d35Sstevel 127903831d35Sstevel minordev = 0; 128003831d35Sstevel for (; (tsp = *prevmsp) != NULL; prevmsp = &tsp->ms_next) { 128103831d35Sstevel if (minordev < tsp->ms_minor) 128203831d35Sstevel break; 128303831d35Sstevel minordev++; 128403831d35Sstevel } 128503831d35Sstevel *devp = makedevice(getmajor(*devp), minordev); 128603831d35Sstevel 128703831d35Sstevel } else { 128803831d35Sstevel /* 128903831d35Sstevel * Should only get here from man_plumbctl(). 129003831d35Sstevel */ 129103831d35Sstevel /*LINTED E_ASSIGN_UINT_TO_SIGNED_INT*/ 129203831d35Sstevel minordev = getminor(*devp); 129303831d35Sstevel 129403831d35Sstevel /* 129503831d35Sstevel * No need to protect this here as all opens are 129603831d35Sstevel * qwaiting, and the bgthread (who is doing this open) 129703831d35Sstevel * is the only one who mucks with this variable. 129803831d35Sstevel */ 129903831d35Sstevel man_ctl_wq = WR(rq); 130003831d35Sstevel 130103831d35Sstevel ASSERT(minordev == 0); /* TBD delete this */ 130203831d35Sstevel } 130303831d35Sstevel 130403831d35Sstevel msp->ms_meta_maj = getmajor(*devp); 130503831d35Sstevel msp->ms_minor = minordev; 130603831d35Sstevel if (minordev == 0) 130703831d35Sstevel msp->ms_flags = MAN_SFLAG_CONTROL; 130803831d35Sstevel 130903831d35Sstevel /* 131003831d35Sstevel * Link new entry into global list of active entries. 131103831d35Sstevel */ 131203831d35Sstevel msp->ms_next = *prevmsp; 131303831d35Sstevel *prevmsp = msp; 131403831d35Sstevel 131503831d35Sstevel 131603831d35Sstevel /* 131703831d35Sstevel * Disable automatic enabling of our write service procedure. 131803831d35Sstevel * We control this explicitly. 131903831d35Sstevel */ 132003831d35Sstevel noenable(WR(rq)); 132103831d35Sstevel 132203831d35Sstevel exit: 132303831d35Sstevel MAN_DBG(MAN_OCLOSE, ("man_open: exit rq(0x%p) minor %d errno %d\n", 132403831d35Sstevel (void *)rq, minordev, status)); 132503831d35Sstevel 132603831d35Sstevel /* 132703831d35Sstevel * Clean up on error. 132803831d35Sstevel */ 132903831d35Sstevel if (status) { 133003831d35Sstevel qprocsoff(rq); 133103831d35Sstevel rq->q_ptr = WR(rq)->q_ptr = NULL; 133203831d35Sstevel man_kfree((char *)msp, sizeof (manstr_t)); 133303831d35Sstevel } else 133403831d35Sstevel (void) qassociate(rq, -1); 133503831d35Sstevel 133603831d35Sstevel return (status); 133703831d35Sstevel } 133803831d35Sstevel 133903831d35Sstevel /* 134003831d35Sstevel * Get the driver configured. Called from first man_open with exclusive 134103831d35Sstevel * inner perimeter. 134203831d35Sstevel */ 134303831d35Sstevel static int 134403831d35Sstevel man_configure(queue_t *rq) 134503831d35Sstevel { 134603831d35Sstevel man_work_t *wp; 134703831d35Sstevel int status = 0; 134803831d35Sstevel 134903831d35Sstevel MAN_DBG(MAN_CONFIG, ("man_configure:")); 135003831d35Sstevel 135103831d35Sstevel /* 135203831d35Sstevel * Initialize NDD parameters. 135303831d35Sstevel */ 135403831d35Sstevel if (!man_ndlist && 135503831d35Sstevel !man_param_register(man_param_arr, A_CNT(man_param_arr))) { 135603831d35Sstevel cmn_err(CE_WARN, "man_configure: man_param_register failed!"); 135703831d35Sstevel man_config_error = ENOMEM; 135803831d35Sstevel goto exit; 135903831d35Sstevel } 136003831d35Sstevel 136103831d35Sstevel mutex_enter(&man_lock); 136203831d35Sstevel 136303831d35Sstevel /* 136403831d35Sstevel * Start up background thread. 136503831d35Sstevel */ 136603831d35Sstevel if (man_bwork_id == NULL) 136703831d35Sstevel man_bwork_id = thread_create(NULL, 2 * DEFAULTSTKSZ, 136803831d35Sstevel man_bwork, NULL, 0, &p0, TS_RUN, minclsyspri); 136903831d35Sstevel 137003831d35Sstevel /* 137103831d35Sstevel * Submit work to get control stream opened. Qwait until its 137203831d35Sstevel * done. See man_open_ctl for mutex lock/synchronization info. 137303831d35Sstevel */ 137403831d35Sstevel 137503831d35Sstevel if (man_ctl_lh == NULL) { 137603831d35Sstevel wp = man_work_alloc(MAN_WORK_OPEN_CTL, KM_SLEEP); 137703831d35Sstevel wp->mw_flags |= MAN_WFLAGS_QWAITER; 137803831d35Sstevel wp->mw_q = WR(rq); 137903831d35Sstevel 138003831d35Sstevel /* 138103831d35Sstevel * Submit work and wait. When man_open_ctl exits 138203831d35Sstevel * man_open, it will cause qwait below to return. 138303831d35Sstevel */ 138403831d35Sstevel man_work_add(man_bwork_q, wp); 138503831d35Sstevel while (!(wp->mw_flags & MAN_WFLAGS_DONE)) { 138603831d35Sstevel mutex_exit(&man_lock); 138703831d35Sstevel qwait(rq); 138803831d35Sstevel mutex_enter(&man_lock); 138903831d35Sstevel } 139003831d35Sstevel status = wp->mw_status; 139103831d35Sstevel man_work_free(wp); 139203831d35Sstevel 139303831d35Sstevel } 139403831d35Sstevel mutex_exit(&man_lock); 139503831d35Sstevel 139603831d35Sstevel /* 139703831d35Sstevel * If on domain, setup IOSRAM and build the pathgroups 139803831d35Sstevel * automatically. 139903831d35Sstevel */ 140003831d35Sstevel if ((status == 0) && man_is_on_domain) 140103831d35Sstevel status = man_domain_configure(); 140203831d35Sstevel 140303831d35Sstevel exit: 140403831d35Sstevel mutex_enter(&man_lock); 140503831d35Sstevel 140603831d35Sstevel man_config_error = status; 140703831d35Sstevel if (status != 0) 140803831d35Sstevel man_config_state = MAN_UNCONFIGURED; 140903831d35Sstevel else 141003831d35Sstevel man_config_state = MAN_CONFIGURED; 141103831d35Sstevel 141203831d35Sstevel mutex_exit(&man_lock); 141303831d35Sstevel 141403831d35Sstevel MAN_DBG(MAN_CONFIG, ("man_configure: returns %d\n", status)); 141503831d35Sstevel 141603831d35Sstevel return (status); 141703831d35Sstevel } 141803831d35Sstevel 141903831d35Sstevel /* 142003831d35Sstevel * man_close - close the device 142103831d35Sstevel * 142203831d35Sstevel * rq - upper read queue of the stream 142303831d35Sstevel * 142403831d35Sstevel * returns - success - 0 142503831d35Sstevel * - failure - errno value for failure 142603831d35Sstevel */ 142703831d35Sstevel static int 142803831d35Sstevel man_close(queue_t *rq) 142903831d35Sstevel { 143003831d35Sstevel manstr_t *close_msp; 143103831d35Sstevel manstr_t *msp; 143203831d35Sstevel 143303831d35Sstevel MAN_DBG(MAN_OCLOSE, ("man_close: rq(0x%p)\n", (void *)rq)); 143403831d35Sstevel 143503831d35Sstevel qprocsoff(rq); 143603831d35Sstevel close_msp = (manstr_t *)rq->q_ptr; 143703831d35Sstevel 143803831d35Sstevel /* 143903831d35Sstevel * Unlink the per-Stream entry from the active list and free it. 144003831d35Sstevel */ 144103831d35Sstevel if (close_msp == man_strup) 144203831d35Sstevel man_strup = close_msp->ms_next; 144303831d35Sstevel else { 144403831d35Sstevel for (msp = man_strup; msp && msp->ms_next != close_msp; ) 144503831d35Sstevel msp = msp->ms_next; 144603831d35Sstevel 144703831d35Sstevel if (msp == NULL) { 144803831d35Sstevel cmn_err(CE_WARN, "man_close: no stream!"); 144903831d35Sstevel return (ENODEV); 145003831d35Sstevel } 145103831d35Sstevel 145203831d35Sstevel msp->ms_next = close_msp->ms_next; 145303831d35Sstevel } 145403831d35Sstevel 145503831d35Sstevel if (close_msp->ms_dests != NULL) { 145603831d35Sstevel /* 145703831d35Sstevel * Still DL_ATTACHED 145803831d35Sstevel */ 145903831d35Sstevel man_work_t *wp; 146003831d35Sstevel 146103831d35Sstevel wp = man_work_alloc(MAN_WORK_CLOSE_STREAM, KM_SLEEP); 146203831d35Sstevel man_dodetach(close_msp, wp); 146303831d35Sstevel } 146403831d35Sstevel 146503831d35Sstevel if (close_msp->ms_flags & MAN_SFLAG_CONTROL) { 146603831d35Sstevel /* 146703831d35Sstevel * Driver about to unload. 146803831d35Sstevel */ 146903831d35Sstevel man_ctl_wq = NULL; 147003831d35Sstevel } 147103831d35Sstevel 147203831d35Sstevel rq->q_ptr = WR(rq)->q_ptr = NULL; 147303831d35Sstevel man_kfree((char *)close_msp, sizeof (manstr_t)); 147403831d35Sstevel (void) qassociate(rq, -1); 147503831d35Sstevel 147603831d35Sstevel MAN_DBG(MAN_OCLOSE, ("man_close: exit\n")); 147703831d35Sstevel 147803831d35Sstevel return (0); 147903831d35Sstevel } 148003831d35Sstevel 148103831d35Sstevel /* 148203831d35Sstevel * Ask bgthread to tear down lower stream and qwait 148303831d35Sstevel * until its done. 148403831d35Sstevel */ 148503831d35Sstevel static void 148603831d35Sstevel man_dodetach(manstr_t *msp, man_work_t *wp) 148703831d35Sstevel { 148803831d35Sstevel man_dest_t *mdp; 148903831d35Sstevel int i; 149003831d35Sstevel mblk_t *mp; 149103831d35Sstevel 149203831d35Sstevel mdp = msp->ms_dests; 149303831d35Sstevel msp->ms_dests = NULL; 149403831d35Sstevel msp->ms_destp = NULL; 149503831d35Sstevel 149603831d35Sstevel /* 149703831d35Sstevel * Excise lower dests array, set it closing and hand it to 149803831d35Sstevel * background thread to dispose of. 149903831d35Sstevel */ 150003831d35Sstevel for (i = 0; i < MAN_MAX_DESTS; i++) { 150103831d35Sstevel 150203831d35Sstevel mdp[i].md_state |= MAN_DSTATE_CLOSING; 150303831d35Sstevel mdp[i].md_msp = NULL; 150403831d35Sstevel mdp[i].md_rq = NULL; 150503831d35Sstevel 150603831d35Sstevel if (mdp[i].md_lc_timer_id != 0) { 150703831d35Sstevel (void) quntimeout(man_ctl_wq, mdp[i].md_lc_timer_id); 150803831d35Sstevel mdp[i].md_lc_timer_id = 0; 150903831d35Sstevel } 151003831d35Sstevel if (mdp[i].md_bc_id != 0) { 151103831d35Sstevel qunbufcall(man_ctl_wq, mdp[i].md_bc_id); 151203831d35Sstevel mdp[i].md_bc_id = 0; 151303831d35Sstevel } 151403831d35Sstevel 151503831d35Sstevel mutex_enter(&mdp[i].md_lock); 151603831d35Sstevel while ((mp = mdp[i].md_dmp_head) != NULL) { 151703831d35Sstevel mdp[i].md_dmp_head = mp->b_next; 151803831d35Sstevel mp->b_next = NULL; 151903831d35Sstevel freemsg(mp); 152003831d35Sstevel } 152103831d35Sstevel mdp[i].md_dmp_count = 0; 152203831d35Sstevel mdp[i].md_dmp_tail = NULL; 152303831d35Sstevel mutex_exit(&mdp[i].md_lock); 152403831d35Sstevel } 152503831d35Sstevel 152603831d35Sstevel /* 152703831d35Sstevel * Dump any DL type messages previously caught. 152803831d35Sstevel */ 152903831d35Sstevel man_dl_clean(&msp->ms_dl_mp); 153003831d35Sstevel man_dl_clean(&msp->ms_dlioc_mp); 153103831d35Sstevel 153203831d35Sstevel /* 153303831d35Sstevel * We need to clear fast path flag when dlioc messages are cleaned. 153403831d35Sstevel */ 153503831d35Sstevel msp->ms_flags &= ~MAN_SFLAG_FAST; 153603831d35Sstevel 153703831d35Sstevel /* 153803831d35Sstevel * MAN_WORK_CLOSE_STREAM work request preallocated by caller. 153903831d35Sstevel */ 154003831d35Sstevel ASSERT(wp->mw_type == MAN_WORK_CLOSE_STREAM); 154103831d35Sstevel ASSERT(mdp != NULL); 154203831d35Sstevel wp->mw_arg.a_mdp = mdp; 154303831d35Sstevel wp->mw_arg.a_ndests = MAN_MAX_DESTS; 154403831d35Sstevel wp->mw_arg.a_pg_id = -1; /* Don't care */ 154503831d35Sstevel 154603831d35Sstevel mutex_enter(&man_lock); 154703831d35Sstevel man_work_add(man_bwork_q, wp); 154803831d35Sstevel msp->ms_manp->man_refcnt--; 154903831d35Sstevel mutex_exit(&man_lock); 155003831d35Sstevel 155103831d35Sstevel msp->ms_manp = NULL; 155203831d35Sstevel 155303831d35Sstevel } 155403831d35Sstevel 155503831d35Sstevel 155603831d35Sstevel /* 155703831d35Sstevel * man_uwput - handle DLPI messages issued from upstream, the write 155803831d35Sstevel * side of the upper half of multiplexor. Called with shared access to 155903831d35Sstevel * the inner perimeter. 156003831d35Sstevel * 156103831d35Sstevel * wq - upper write queue of mxx 156203831d35Sstevel * mp - mblk ptr to DLPI request 156303831d35Sstevel */ 156403831d35Sstevel static int 156503831d35Sstevel man_uwput(register queue_t *wq, register mblk_t *mp) 156603831d35Sstevel { 156703831d35Sstevel register manstr_t *msp; /* per stream data */ 156803831d35Sstevel register man_t *manp; /* per instance data */ 156903831d35Sstevel 157003831d35Sstevel msp = (manstr_t *)wq->q_ptr; 157103831d35Sstevel 157203831d35Sstevel MAN_DBG(MAN_UWPUT, ("man_uwput: wq(0x%p) mp(0x%p) db_type(0x%x)" 157303831d35Sstevel " msp(0x%p)\n", 157403831d35Sstevel (void *)wq, (void *)mp, DB_TYPE(mp), (void *)msp)); 157503831d35Sstevel #if DEBUG 157603831d35Sstevel if (man_debug & MAN_UWPUT) { 157703831d35Sstevel if (DB_TYPE(mp) == M_IOCTL) { 157803831d35Sstevel struct iocblk *iocp = (struct iocblk *)mp->b_rptr; 157903831d35Sstevel MAN_DBG(MAN_UWPUT, 158003831d35Sstevel ("man_uwput: M_IOCTL ioc_cmd(0x%x)\n", 158103831d35Sstevel iocp->ioc_cmd)); 158203831d35Sstevel } else if (DB_TYPE(mp) == M_CTL) { 158303831d35Sstevel struct iocblk *iocp = (struct iocblk *)mp->b_rptr; 158403831d35Sstevel MAN_DBG(MAN_UWPUT, 158503831d35Sstevel ("man_uwput: M_CTL ioc_cmd(0x%x)\n", 158603831d35Sstevel iocp->ioc_cmd)); 158703831d35Sstevel } 158803831d35Sstevel } 158903831d35Sstevel #endif /* DEBUG */ 159003831d35Sstevel 159103831d35Sstevel 159203831d35Sstevel switch (DB_TYPE(mp)) { 159303831d35Sstevel case M_DATA: 159403831d35Sstevel manp = msp->ms_manp; 159503831d35Sstevel 159603831d35Sstevel if (((msp->ms_flags & (MAN_SFLAG_FAST | MAN_SFLAG_RAW)) == 0) || 159703831d35Sstevel (msp->ms_dlpistate != DL_IDLE) || 159803831d35Sstevel (manp == NULL)) { 159903831d35Sstevel 160003831d35Sstevel merror(wq, mp, EPROTO); 160103831d35Sstevel break; 160203831d35Sstevel } 160303831d35Sstevel 160403831d35Sstevel if (wq->q_first) { 160503831d35Sstevel (void) putq(wq, mp); 160603831d35Sstevel qenable(wq); 160703831d35Sstevel } else { 160803831d35Sstevel ehdr_t *ep = (ehdr_t *)mp->b_rptr; 160903831d35Sstevel 161003831d35Sstevel (void) man_start(wq, mp, &ep->ether_dhost); 161103831d35Sstevel } 161203831d35Sstevel break; 161303831d35Sstevel 161403831d35Sstevel case M_PROTO: 161503831d35Sstevel case M_PCPROTO: 161603831d35Sstevel if ((DL_PRIM(mp) == DL_UNITDATA_IND) && !wq->q_first) { 161703831d35Sstevel (void) man_udreq(wq, mp); 161803831d35Sstevel } else { 161903831d35Sstevel (void) putq(wq, mp); 162003831d35Sstevel qenable(wq); 162103831d35Sstevel } 162203831d35Sstevel break; 162303831d35Sstevel 162403831d35Sstevel case M_IOCTL: 162503831d35Sstevel case M_IOCDATA: 162603831d35Sstevel qwriter(wq, mp, man_ioctl, PERIM_INNER); 162703831d35Sstevel break; 162803831d35Sstevel 162903831d35Sstevel case M_CTL: 163003831d35Sstevel freemsg(mp); 163103831d35Sstevel break; 163203831d35Sstevel 163303831d35Sstevel case M_FLUSH: 163403831d35Sstevel MAN_DBG(MAN_UWPUT, ("man_wput: M_FLUSH\n")); 163503831d35Sstevel if (*mp->b_rptr & FLUSHW) 163603831d35Sstevel flushq(wq, FLUSHDATA); 163703831d35Sstevel if (*mp->b_rptr & FLUSHR) { 163803831d35Sstevel flushq(RD(wq), FLUSHDATA); 163903831d35Sstevel *mp->b_rptr &= ~FLUSHW; 164003831d35Sstevel qreply(wq, mp); 164103831d35Sstevel } else { 164203831d35Sstevel freemsg(mp); 164303831d35Sstevel } 164403831d35Sstevel break; 164503831d35Sstevel 164603831d35Sstevel default: 164703831d35Sstevel MAN_DBG(MAN_WARN, 164803831d35Sstevel ("man_uwput: illegal mblk(0x%p) type(0x%x)\n", 164903831d35Sstevel (void *)mp, DB_TYPE(mp))); 165003831d35Sstevel freemsg(mp); 165103831d35Sstevel break; 165203831d35Sstevel } /* End switch */ 165303831d35Sstevel 165403831d35Sstevel MAN_DBG(MAN_UWPUT, ("man_uwput: exit wq(0x%p) mp(0x%p)\n", 165503831d35Sstevel (void *)wq, (void *)mp)); 165603831d35Sstevel 165703831d35Sstevel return (0); 165803831d35Sstevel } 165903831d35Sstevel 166003831d35Sstevel /* 166103831d35Sstevel * man_start - handle data messages issued from upstream. Send down 166203831d35Sstevel * to particular man_dest based on ether_addr, otherwise send out to all 166303831d35Sstevel * valid man_dests. 166403831d35Sstevel * 166503831d35Sstevel * wq - upper write queue of mxx 166603831d35Sstevel * mp - mblk ptr to DLPI request 166703831d35Sstevel * caller - Caller ID for decision making on canput failure 166803831d35Sstevel * 166903831d35Sstevel * Returns: 167003831d35Sstevel * 0 - Data xmitted or No flow control situation detected. 167103831d35Sstevel * 1 - Flow control situation detected. 167203831d35Sstevel * 167303831d35Sstevel * STREAMS Flow Control: can be used if there is only one destination 167403831d35Sstevel * for a stream (1 to 1 multiplexor). In this case, we will use the upper 167503831d35Sstevel * write queue to store mblks when in flow control. If there are multiple 167603831d35Sstevel * destinations, we cannot use the STREAMs based flow control (1 to many 167703831d35Sstevel * multiplexor). In this case, we will use the lower write queue to store 167803831d35Sstevel * mblks when in flow control. Since destinations come and go, we may 167903831d35Sstevel * transition between 1-to-1 and 1-to-m. So it may be the case that we have 168003831d35Sstevel * some mblks stored on the upper queue, and some on the lower queue. However, 168103831d35Sstevel * we will never send mblks out of order. See man_uwput and man_start_lower(). 168203831d35Sstevel * 168303831d35Sstevel * A simple flow control mechanism is implemented for the deferred mblk list, 168403831d35Sstevel * as this list is expected to be used temporarily for a very short 168503831d35Sstevel * period required for switching paths. This flow control mechanism is 168603831d35Sstevel * used only as a defensive approach to avoid infinite growth of this list. 168703831d35Sstevel */ 168803831d35Sstevel static int 168903831d35Sstevel man_start(register queue_t *wq, register mblk_t *mp, eaddr_t *eap) 169003831d35Sstevel { 169103831d35Sstevel register manstr_t *msp; /* per stream data */ 169203831d35Sstevel register man_dest_t *mdp = NULL; /* destination */ 169303831d35Sstevel mblk_t *tmp; 169403831d35Sstevel int i; 169503831d35Sstevel int status = 0; 169603831d35Sstevel 169703831d35Sstevel msp = (manstr_t *)wq->q_ptr; 169803831d35Sstevel 169903831d35Sstevel MAN_DBG(MAN_DATA, ("man_start: msp(0x%p) ether_addr(%s)\n", 170003831d35Sstevel (void *)msp, ether_sprintf(eap))); 170103831d35Sstevel 170203831d35Sstevel if (msp->ms_dests == NULL) { 170303831d35Sstevel cmn_err(CE_WARN, "man_start: no destinations"); 170403831d35Sstevel freemsg(mp); 170503831d35Sstevel return (0); 170603831d35Sstevel } 170703831d35Sstevel 170803831d35Sstevel /* 170903831d35Sstevel * Optimization if only one valid destination. 171003831d35Sstevel */ 171103831d35Sstevel mdp = msp->ms_destp; 171203831d35Sstevel 171303831d35Sstevel if (IS_UNICAST(eap)) { 171403831d35Sstevel queue_t *flow_wq = NULL; 171503831d35Sstevel 171603831d35Sstevel if (mdp == NULL) { 171703831d35Sstevel /* 171803831d35Sstevel * TDB - This needs to be optimized (some bits in 171903831d35Sstevel * ehp->dhost will act as an index. 172003831d35Sstevel */ 172103831d35Sstevel for (i = 0; i < MAN_MAX_DESTS; i++) { 172203831d35Sstevel 172303831d35Sstevel mdp = &msp->ms_dests[i]; 172403831d35Sstevel 172503831d35Sstevel if ((mdp->md_state == MAN_DSTATE_READY) && 172603831d35Sstevel (ether_cmp(eap, &mdp->md_dst_eaddr) == 0)) 172703831d35Sstevel break; 172803831d35Sstevel mdp = NULL; 172903831d35Sstevel } 173003831d35Sstevel } else { 173103831d35Sstevel /* 173203831d35Sstevel * 1 to 1 multiplexing, use upper wq for flow control. 173303831d35Sstevel */ 173403831d35Sstevel flow_wq = wq; 173503831d35Sstevel } 173603831d35Sstevel 173703831d35Sstevel if (mdp != NULL) { 173803831d35Sstevel /* 173903831d35Sstevel * Its going somewhere specific 174003831d35Sstevel */ 174103831d35Sstevel status = man_start_lower(mdp, mp, flow_wq, MAN_UPPER); 174203831d35Sstevel 174303831d35Sstevel } else { 174403831d35Sstevel MAN_DBG(MAN_DATA, ("man_start: no destination" 174503831d35Sstevel " for eaddr %s\n", ether_sprintf(eap))); 174603831d35Sstevel freemsg(mp); 174703831d35Sstevel } 174803831d35Sstevel } else { 174903831d35Sstevel /* 175003831d35Sstevel * Broadcast or multicast - send everone a copy. 175103831d35Sstevel */ 175203831d35Sstevel if (mdp == NULL) { 175303831d35Sstevel for (i = 0; i < MAN_MAX_DESTS; i++) { 175403831d35Sstevel mdp = &msp->ms_dests[i]; 175503831d35Sstevel 175603831d35Sstevel if (mdp->md_state != MAN_DSTATE_READY) 175703831d35Sstevel continue; 175803831d35Sstevel 175903831d35Sstevel if ((tmp = copymsg(mp)) != NULL) { 176003831d35Sstevel (void) man_start_lower(mdp, tmp, 176103831d35Sstevel NULL, MAN_UPPER); 176203831d35Sstevel } else { 176303831d35Sstevel MAN_DBG(MAN_DATA, ("man_start: copymsg" 176403831d35Sstevel " failed!")); 176503831d35Sstevel } 176603831d35Sstevel } 176703831d35Sstevel freemsg(mp); 176803831d35Sstevel } else { 176903831d35Sstevel if (mdp->md_state == MAN_DSTATE_READY) 177003831d35Sstevel status = man_start_lower(mdp, mp, wq, 177103831d35Sstevel MAN_UPPER); 177203831d35Sstevel else 177303831d35Sstevel freemsg(mp); 177403831d35Sstevel } 177503831d35Sstevel } 177603831d35Sstevel return (status); 177703831d35Sstevel } 177803831d35Sstevel 177903831d35Sstevel /* 178003831d35Sstevel * Send a DL_UNITDATA or M_DATA fastpath data mblk to a particular 178103831d35Sstevel * destination. Others mblk types sent down via * man_dlpi_senddown(). 178203831d35Sstevel * 178303831d35Sstevel * Returns: 178403831d35Sstevel * 0 - Data xmitted 178503831d35Sstevel * 1 - Data not xmitted due to flow control. 178603831d35Sstevel */ 178703831d35Sstevel static int 178803831d35Sstevel man_start_lower(man_dest_t *mdp, mblk_t *mp, queue_t *flow_wq, int caller) 178903831d35Sstevel { 179003831d35Sstevel queue_t *wq = mdp->md_wq; 179103831d35Sstevel int status = 0; 179203831d35Sstevel 179303831d35Sstevel /* 179403831d35Sstevel * Lower stream ready for data transmit. 179503831d35Sstevel */ 179603831d35Sstevel if (mdp->md_state == MAN_DSTATE_READY && 179703831d35Sstevel mdp->md_dlpistate == DL_IDLE) { 179803831d35Sstevel 179903831d35Sstevel ASSERT(mdp->md_wq != NULL); 180003831d35Sstevel 180103831d35Sstevel if (caller == MAN_UPPER) { 180203831d35Sstevel /* 180303831d35Sstevel * Check for flow control conditions for lower 180403831d35Sstevel * stream. 180503831d35Sstevel */ 180603831d35Sstevel if (mdp->md_dmp_head == NULL && 180703831d35Sstevel wq->q_first == NULL && canputnext(wq)) { 180803831d35Sstevel 180903831d35Sstevel (void) putnext(wq, mp); 181003831d35Sstevel 181103831d35Sstevel } else { 181203831d35Sstevel mutex_enter(&mdp->md_lock); 181303831d35Sstevel if (mdp->md_dmp_head != NULL) { 181403831d35Sstevel /* 181503831d35Sstevel * A simple flow control mechanism. 181603831d35Sstevel */ 181703831d35Sstevel if (mdp->md_dmp_count >= MAN_HIWAT) { 181803831d35Sstevel freemsg(mp); 181903831d35Sstevel } else { 182003831d35Sstevel /* 182103831d35Sstevel * Add 'mp' to the deferred 182203831d35Sstevel * msg list. 182303831d35Sstevel */ 182403831d35Sstevel mdp->md_dmp_tail->b_next = mp; 182503831d35Sstevel mdp->md_dmp_tail = mp; 182603831d35Sstevel mdp->md_dmp_count += 182703831d35Sstevel msgsize(mp); 182803831d35Sstevel } 182903831d35Sstevel mutex_exit(&mdp->md_lock); 183003831d35Sstevel /* 183103831d35Sstevel * Inform flow control situation 183203831d35Sstevel * to the caller. 183303831d35Sstevel */ 183403831d35Sstevel status = 1; 183503831d35Sstevel qenable(wq); 183603831d35Sstevel goto exit; 183703831d35Sstevel } 183803831d35Sstevel mutex_exit(&mdp->md_lock); 183903831d35Sstevel /* 184003831d35Sstevel * If 1 to 1 mux, use upper write queue for 184103831d35Sstevel * flow control. 184203831d35Sstevel */ 184303831d35Sstevel if (flow_wq != NULL) { 184403831d35Sstevel /* 184503831d35Sstevel * putbq() message and indicate 184603831d35Sstevel * flow control situation to the 184703831d35Sstevel * caller. 184803831d35Sstevel */ 1849*07d06da5SSurya Prakki (void) putbq(flow_wq, mp); 185003831d35Sstevel qenable(flow_wq); 185103831d35Sstevel status = 1; 185203831d35Sstevel goto exit; 185303831d35Sstevel } 185403831d35Sstevel /* 185503831d35Sstevel * 1 to many mux, use lower write queue for 185603831d35Sstevel * flow control. Be mindful not to overflow 185703831d35Sstevel * the lower MAN STREAM q. 185803831d35Sstevel */ 185903831d35Sstevel if (canput(wq)) { 186003831d35Sstevel (void) putq(wq, mp); 186103831d35Sstevel qenable(wq); 186203831d35Sstevel } else { 186303831d35Sstevel MAN_DBG(MAN_DATA, ("man_start_lower:" 186403831d35Sstevel " lower q flow controlled -" 186503831d35Sstevel " discarding packet")); 186603831d35Sstevel freemsg(mp); 186703831d35Sstevel goto exit; 186803831d35Sstevel } 186903831d35Sstevel } 187003831d35Sstevel 187103831d35Sstevel } else { 187203831d35Sstevel /* 187303831d35Sstevel * man_lwsrv is draining flow controlled mblks. 187403831d35Sstevel */ 187503831d35Sstevel if (canputnext(wq)) 187603831d35Sstevel (void) putnext(wq, mp); 187703831d35Sstevel else 187803831d35Sstevel status = 1; 187903831d35Sstevel } 188003831d35Sstevel goto exit; 188103831d35Sstevel } 188203831d35Sstevel 188303831d35Sstevel /* 188403831d35Sstevel * Lower stream in transition, do flow control. 188503831d35Sstevel */ 188603831d35Sstevel status = 1; 188703831d35Sstevel 188803831d35Sstevel if (mdp->md_state == MAN_DSTATE_NOTPRESENT) { 188903831d35Sstevel nodest: 189003831d35Sstevel cmn_err(CE_WARN, 189103831d35Sstevel "man_start_lower: no dest for mdp(0x%p), caller(%d)!", 189203831d35Sstevel (void *)mdp, caller); 189303831d35Sstevel if (caller == MAN_UPPER) 189403831d35Sstevel freemsg(mp); 189503831d35Sstevel goto exit; 189603831d35Sstevel } 189703831d35Sstevel 189803831d35Sstevel if (mdp->md_state & MAN_DSTATE_CLOSING) { 189903831d35Sstevel MAN_DBG(MAN_DATA, ("man_start_lower: mdp(0x%p) closing", 190003831d35Sstevel (void *)mdp)); 190103831d35Sstevel if (caller == MAN_UPPER) 190203831d35Sstevel freemsg(mp); 190303831d35Sstevel goto exit; 190403831d35Sstevel } 190503831d35Sstevel 190603831d35Sstevel if ((mdp->md_state & MAN_DSTATE_PLUMBING) || 190703831d35Sstevel (mdp->md_state == MAN_DSTATE_INITIALIZING) || 190803831d35Sstevel (mdp->md_dlpistate != DL_IDLE)) { 190903831d35Sstevel /* 191003831d35Sstevel * Defer until PLUMBED and DL_IDLE. See man_lwsrv(). 191103831d35Sstevel */ 191203831d35Sstevel if (caller == MAN_UPPER) { 191303831d35Sstevel /* 191403831d35Sstevel * Upper stream sending data down, add to defered mblk 191503831d35Sstevel * list for stream. 191603831d35Sstevel */ 191703831d35Sstevel mutex_enter(&mdp->md_lock); 191803831d35Sstevel if (mdp->md_dmp_count >= MAN_HIWAT) { 191903831d35Sstevel freemsg(mp); 192003831d35Sstevel } else { 192103831d35Sstevel if (mdp->md_dmp_head == NULL) { 192203831d35Sstevel ASSERT(mdp->md_dmp_tail == NULL); 192303831d35Sstevel mdp->md_dmp_head = mp; 192403831d35Sstevel mdp->md_dmp_tail = mp; 192503831d35Sstevel } else { 192603831d35Sstevel mdp->md_dmp_tail->b_next = mp; 192703831d35Sstevel mdp->md_dmp_tail = mp; 192803831d35Sstevel } 192903831d35Sstevel mdp->md_dmp_count += msgsize(mp); 193003831d35Sstevel } 193103831d35Sstevel mutex_exit(&mdp->md_lock); 193203831d35Sstevel } 193303831d35Sstevel 193403831d35Sstevel goto exit; 193503831d35Sstevel } 193603831d35Sstevel 193703831d35Sstevel exit: 193803831d35Sstevel return (status); 193903831d35Sstevel } 194003831d35Sstevel 194103831d35Sstevel /* 194203831d35Sstevel * man_ioctl - handle ioctl requests for this driver (I_PLINK/I_PUNLINK) 194303831d35Sstevel * or pass thru to the physical driver below. Note that most M_IOCTLs we 194403831d35Sstevel * care about come down the control msp, but the IOC ones come down the IP. 194503831d35Sstevel * Called with exclusive inner perimeter. 194603831d35Sstevel * 194703831d35Sstevel * wq - upper write queue of mxx 194803831d35Sstevel * mp - mblk ptr to DLPI ioctl request 194903831d35Sstevel */ 195003831d35Sstevel static void 195103831d35Sstevel man_ioctl(register queue_t *wq, register mblk_t *mp) 195203831d35Sstevel { 195303831d35Sstevel manstr_t *msp; 195403831d35Sstevel struct iocblk *iocp; 195503831d35Sstevel 195603831d35Sstevel iocp = (struct iocblk *)mp->b_rptr; 195703831d35Sstevel msp = (manstr_t *)wq->q_ptr; 195803831d35Sstevel 195903831d35Sstevel #ifdef DEBUG 196003831d35Sstevel { 196103831d35Sstevel char ioc_cmd[30]; 196203831d35Sstevel 1963*07d06da5SSurya Prakki (void) sprintf(ioc_cmd, "not handled IOCTL 0x%x", 1964*07d06da5SSurya Prakki iocp->ioc_cmd); 196503831d35Sstevel MAN_DBG((MAN_SWITCH | MAN_PATH | MAN_DLPI), 196603831d35Sstevel ("man_ioctl: wq(0x%p) mp(0x%p) cmd(%s)\n", 196703831d35Sstevel (void *)wq, (void *)mp, 196803831d35Sstevel (iocp->ioc_cmd == I_PLINK) ? "I_PLINK" : 196903831d35Sstevel (iocp->ioc_cmd == I_PUNLINK) ? "I_PUNLINK" : 197003831d35Sstevel (iocp->ioc_cmd == MAN_SETPATH) ? "MAN_SETPATH" : 197103831d35Sstevel (iocp->ioc_cmd == DL_IOC_HDR_INFO) ? "DL_IOC_HDR_INFO" : 197203831d35Sstevel (iocp->ioc_cmd == DLIOCRAW) ? "DLIOCRAW" : ioc_cmd)); 197303831d35Sstevel } 197403831d35Sstevel #endif /* DEBUG */ 197503831d35Sstevel 197603831d35Sstevel 197703831d35Sstevel /* 197803831d35Sstevel * Handle the requests... 197903831d35Sstevel */ 198003831d35Sstevel switch ((unsigned int)iocp->ioc_cmd) { 198103831d35Sstevel 198203831d35Sstevel case I_PLINK: 198303831d35Sstevel man_plink(wq, mp); 198403831d35Sstevel break; 198503831d35Sstevel 198603831d35Sstevel case I_PUNLINK: 198703831d35Sstevel man_unplink(wq, mp); 198803831d35Sstevel break; 198903831d35Sstevel 199003831d35Sstevel case MAN_SETPATH: 199103831d35Sstevel man_setpath(wq, mp); 199203831d35Sstevel break; 199303831d35Sstevel 199403831d35Sstevel case MAN_GETEADDR: 199503831d35Sstevel man_geteaddr(wq, mp); 199603831d35Sstevel break; 199703831d35Sstevel 199803831d35Sstevel case MAN_SET_LINKCHECK_TIME: 199903831d35Sstevel man_set_linkcheck_time(wq, mp); 200003831d35Sstevel break; 200103831d35Sstevel 200203831d35Sstevel case MAN_SET_SC_IPADDRS: 200303831d35Sstevel man_set_sc_ipaddrs(wq, mp); 200403831d35Sstevel break; 200503831d35Sstevel 200603831d35Sstevel case MAN_SET_SC_IP6ADDRS: 200703831d35Sstevel man_set_sc_ip6addrs(wq, mp); 200803831d35Sstevel break; 200903831d35Sstevel 201003831d35Sstevel case DLIOCRAW: 201103831d35Sstevel if (man_dlioc(msp, mp)) 201203831d35Sstevel miocnak(wq, mp, 0, ENOMEM); 201303831d35Sstevel else { 201403831d35Sstevel msp->ms_flags |= MAN_SFLAG_RAW; 201503831d35Sstevel miocack(wq, mp, 0, 0); 201603831d35Sstevel } 201703831d35Sstevel break; 201803831d35Sstevel 201903831d35Sstevel case DL_IOC_HDR_INFO: 202003831d35Sstevel man_dl_ioc_hdr_info(wq, mp); 202103831d35Sstevel break; 202203831d35Sstevel 202303831d35Sstevel case MAN_ND_GET: 202403831d35Sstevel case MAN_ND_SET: 202503831d35Sstevel man_nd_getset(wq, mp); 202603831d35Sstevel break; 202703831d35Sstevel 202803831d35Sstevel default: 202903831d35Sstevel MAN_DBG(MAN_DDI, ("man_ioctl: unknown ioc_cmd %d\n", 203003831d35Sstevel (unsigned int)iocp->ioc_cmd)); 203103831d35Sstevel miocnak(wq, mp, 0, EINVAL); 203203831d35Sstevel break; 203303831d35Sstevel } 203403831d35Sstevel exit: 203503831d35Sstevel MAN_DBG((MAN_SWITCH | MAN_PATH | MAN_DLPI), ("man_ioctl: exit\n")); 203603831d35Sstevel 203703831d35Sstevel } 203803831d35Sstevel 203903831d35Sstevel /* 204003831d35Sstevel * man_plink: handle I_PLINK requests on the control stream 204103831d35Sstevel */ 204203831d35Sstevel void 204303831d35Sstevel man_plink(queue_t *wq, mblk_t *mp) 204403831d35Sstevel { 204503831d35Sstevel struct linkblk *linkp; 204603831d35Sstevel man_linkrec_t *lrp; 204703831d35Sstevel int status = 0; 204803831d35Sstevel 204903831d35Sstevel linkp = (struct linkblk *)mp->b_cont->b_rptr; 205003831d35Sstevel 205103831d35Sstevel /* 205203831d35Sstevel * Create a record to hold lower stream info. man_plumb will 205303831d35Sstevel * retrieve it after calling ldi_ioctl(I_PLINK) 205403831d35Sstevel */ 205503831d35Sstevel lrp = man_kzalloc(sizeof (man_linkrec_t), KM_NOSLEEP); 205603831d35Sstevel if (lrp == NULL) { 205703831d35Sstevel status = ENOMEM; 205803831d35Sstevel goto exit; 205903831d35Sstevel } 206003831d35Sstevel 206103831d35Sstevel lrp->l_muxid = linkp->l_index; 206203831d35Sstevel lrp->l_wq = linkp->l_qbot; 206303831d35Sstevel lrp->l_rq = RD(linkp->l_qbot); 206403831d35Sstevel 206503831d35Sstevel man_linkrec_insert(lrp); 206603831d35Sstevel 206703831d35Sstevel exit: 206803831d35Sstevel if (status) 206903831d35Sstevel miocnak(wq, mp, 0, status); 207003831d35Sstevel else 207103831d35Sstevel miocack(wq, mp, 0, 0); 207203831d35Sstevel 207303831d35Sstevel } 207403831d35Sstevel 207503831d35Sstevel /* 207603831d35Sstevel * man_unplink - handle I_PUNLINK requests on the control stream 207703831d35Sstevel */ 207803831d35Sstevel void 207903831d35Sstevel man_unplink(queue_t *wq, mblk_t *mp) 208003831d35Sstevel { 208103831d35Sstevel struct linkblk *linkp; 208203831d35Sstevel 208303831d35Sstevel linkp = (struct linkblk *)mp->b_cont->b_rptr; 208403831d35Sstevel RD(linkp->l_qbot)->q_ptr = NULL; 208503831d35Sstevel WR(linkp->l_qbot)->q_ptr = NULL; 208603831d35Sstevel miocack(wq, mp, 0, 0); 208703831d35Sstevel } 208803831d35Sstevel 208903831d35Sstevel void 209003831d35Sstevel man_linkrec_insert(man_linkrec_t *lrp) 209103831d35Sstevel { 209203831d35Sstevel mutex_enter(&man_lock); 209303831d35Sstevel 209403831d35Sstevel lrp->l_next = man_linkrec_head; 209503831d35Sstevel man_linkrec_head = lrp; 209603831d35Sstevel 209703831d35Sstevel mutex_exit(&man_lock); 209803831d35Sstevel 209903831d35Sstevel } 210003831d35Sstevel 210103831d35Sstevel static queue_t * 210203831d35Sstevel man_linkrec_find(int muxid) 210303831d35Sstevel { 210403831d35Sstevel man_linkrec_t *lpp; 210503831d35Sstevel man_linkrec_t *lp; 210603831d35Sstevel queue_t *wq = NULL; 210703831d35Sstevel 210803831d35Sstevel mutex_enter(&man_lock); 210903831d35Sstevel 211003831d35Sstevel if (man_linkrec_head == NULL) 211103831d35Sstevel goto exit; 211203831d35Sstevel 211303831d35Sstevel lp = lpp = man_linkrec_head; 211403831d35Sstevel if (lpp->l_muxid == muxid) { 211503831d35Sstevel man_linkrec_head = lpp->l_next; 211603831d35Sstevel } else { 211703831d35Sstevel for (lp = lpp->l_next; lp; lp = lp->l_next) { 211803831d35Sstevel if (lp->l_muxid == muxid) 211903831d35Sstevel break; 212003831d35Sstevel lpp = lp; 212103831d35Sstevel } 212203831d35Sstevel } 212303831d35Sstevel 212403831d35Sstevel if (lp == NULL) 212503831d35Sstevel goto exit; 212603831d35Sstevel 212703831d35Sstevel wq = lp->l_wq; 212803831d35Sstevel ASSERT(wq != NULL); 212903831d35Sstevel 213003831d35Sstevel lpp->l_next = lp->l_next; 213103831d35Sstevel man_kfree(lp, sizeof (man_linkrec_t)); 213203831d35Sstevel 213303831d35Sstevel exit: 213403831d35Sstevel mutex_exit(&man_lock); 213503831d35Sstevel 213603831d35Sstevel return (wq); 213703831d35Sstevel } 213803831d35Sstevel 213903831d35Sstevel /* 214003831d35Sstevel * Set instance linkcheck timer value. 214103831d35Sstevel */ 214203831d35Sstevel static void 214303831d35Sstevel man_set_linkcheck_time(queue_t *wq, mblk_t *mp) 214403831d35Sstevel { 214503831d35Sstevel mi_time_t *mtp; 214603831d35Sstevel int error; 214703831d35Sstevel man_t *manp; 214803831d35Sstevel 214903831d35Sstevel MAN_DBG(MAN_LINK, ("man_set_linkcheck_time: enter")); 215003831d35Sstevel 215103831d35Sstevel error = miocpullup(mp, sizeof (mi_time_t)); 215203831d35Sstevel if (error != 0) 215303831d35Sstevel goto exit; 215403831d35Sstevel 215503831d35Sstevel mtp = (mi_time_t *)mp->b_cont->b_rptr; 215603831d35Sstevel 215703831d35Sstevel MAN_DBG(MAN_LINK, ("man_set_linkcheck_time: mtp")); 215803831d35Sstevel MAN_DBGCALL(MAN_LINK, man_print_mtp(mtp)); 215903831d35Sstevel 216003831d35Sstevel manp = ddi_get_soft_state(man_softstate, mtp->mtp_man_ppa); 216103831d35Sstevel if (manp == NULL) { 216203831d35Sstevel error = ENODEV; 216303831d35Sstevel goto exit; 216403831d35Sstevel } 216503831d35Sstevel 216603831d35Sstevel manp->man_linkcheck_time = mtp->mtp_time; 216703831d35Sstevel exit: 216803831d35Sstevel if (error) 216903831d35Sstevel miocnak(wq, mp, 0, error); 217003831d35Sstevel else 217103831d35Sstevel miocack(wq, mp, sizeof (mi_time_t), 0); 217203831d35Sstevel } 217303831d35Sstevel 217403831d35Sstevel /* 217503831d35Sstevel * Man path ioctl processing. Should only happen on the SSC. Called 217603831d35Sstevel * with exclusive inner perimeter. 217703831d35Sstevel */ 217803831d35Sstevel static void 217903831d35Sstevel man_setpath(queue_t *wq, mblk_t *mp) 218003831d35Sstevel { 218103831d35Sstevel mi_path_t *mip; 218203831d35Sstevel int error; 218303831d35Sstevel 218403831d35Sstevel error = miocpullup(mp, sizeof (mi_path_t)); 218503831d35Sstevel if (error != 0) 218603831d35Sstevel goto exit; 218703831d35Sstevel 218803831d35Sstevel mip = (mi_path_t *)mp->b_cont->b_rptr; 218903831d35Sstevel mutex_enter(&man_lock); 219003831d35Sstevel error = man_pg_cmd(mip, NULL); 219103831d35Sstevel mutex_exit(&man_lock); 219203831d35Sstevel 219303831d35Sstevel exit: 219403831d35Sstevel if (error) 219503831d35Sstevel miocnak(wq, mp, 0, error); 219603831d35Sstevel else 219703831d35Sstevel miocack(wq, mp, sizeof (mi_path_t), 0); 219803831d35Sstevel } 219903831d35Sstevel 220003831d35Sstevel /* 220103831d35Sstevel * Get the local ethernet address of this machine. 220203831d35Sstevel */ 220303831d35Sstevel static void 220403831d35Sstevel man_geteaddr(queue_t *wq, mblk_t *mp) 220503831d35Sstevel { 220603831d35Sstevel eaddr_t *eap; 220703831d35Sstevel int error; 220803831d35Sstevel 220903831d35Sstevel error = miocpullup(mp, sizeof (eaddr_t)); 221003831d35Sstevel if (error != 0) { 221103831d35Sstevel miocnak(wq, mp, 0, error); 221203831d35Sstevel return; 221303831d35Sstevel } 221403831d35Sstevel 221503831d35Sstevel eap = (eaddr_t *)mp->b_cont->b_rptr; 221603831d35Sstevel (void) localetheraddr(NULL, eap); 221703831d35Sstevel miocack(wq, mp, sizeof (eaddr_t), 0); 221803831d35Sstevel } 221903831d35Sstevel 222003831d35Sstevel /* 222103831d35Sstevel * Set my SC and other SC IPv4 addresses for use in man_pinger routine. 222203831d35Sstevel */ 222303831d35Sstevel static void 222403831d35Sstevel man_set_sc_ipaddrs(queue_t *wq, mblk_t *mp) 222503831d35Sstevel { 222603831d35Sstevel int error; 222703831d35Sstevel 222803831d35Sstevel error = miocpullup(mp, sizeof (man_sc_ipaddrs_t)); 222903831d35Sstevel if (error != 0) 223003831d35Sstevel goto exit; 223103831d35Sstevel 223203831d35Sstevel man_sc_ipaddrs = *(man_sc_ipaddrs_t *)mp->b_cont->b_rptr; 223303831d35Sstevel 223403831d35Sstevel #ifdef DEBUG 223503831d35Sstevel { 223603831d35Sstevel char buf[INET_ADDRSTRLEN]; 223703831d35Sstevel 223803831d35Sstevel (void) inet_ntop(AF_INET, 223903831d35Sstevel (void *) &man_sc_ipaddrs.ip_other_sc_ipaddr, 224003831d35Sstevel buf, INET_ADDRSTRLEN); 224103831d35Sstevel MAN_DBG(MAN_CONFIG, ("ip_other_sc_ipaddr = %s", buf)); 224203831d35Sstevel (void) inet_ntop(AF_INET, 224303831d35Sstevel (void *) &man_sc_ipaddrs.ip_my_sc_ipaddr, 224403831d35Sstevel buf, INET_ADDRSTRLEN); 224503831d35Sstevel MAN_DBG(MAN_CONFIG, ("ip_my_sc_ipaddr = %s", buf)); 224603831d35Sstevel } 224703831d35Sstevel #endif /* DEBUG */ 224803831d35Sstevel exit: 224903831d35Sstevel if (error) 225003831d35Sstevel miocnak(wq, mp, 0, error); 225103831d35Sstevel else 225203831d35Sstevel miocack(wq, mp, sizeof (man_sc_ipaddrs_t), 0); 225303831d35Sstevel } 225403831d35Sstevel 225503831d35Sstevel /* 225603831d35Sstevel * Set my SC and other SC IPv6 addresses for use in man_pinger routine. 225703831d35Sstevel */ 225803831d35Sstevel static void 225903831d35Sstevel man_set_sc_ip6addrs(queue_t *wq, mblk_t *mp) 226003831d35Sstevel { 226103831d35Sstevel int error; 226203831d35Sstevel 226303831d35Sstevel error = miocpullup(mp, sizeof (man_sc_ip6addrs_t)); 226403831d35Sstevel if (error != 0) 226503831d35Sstevel goto exit; 226603831d35Sstevel 226703831d35Sstevel man_sc_ip6addrs = *(man_sc_ip6addrs_t *)mp->b_cont->b_rptr; 226803831d35Sstevel 226903831d35Sstevel #ifdef DEBUG 227003831d35Sstevel { 227103831d35Sstevel char buf[INET6_ADDRSTRLEN]; 227203831d35Sstevel 227303831d35Sstevel (void) inet_ntop(AF_INET6, 227403831d35Sstevel (void *) &man_sc_ip6addrs.ip6_other_sc_ipaddr, 227503831d35Sstevel buf, INET6_ADDRSTRLEN); 227603831d35Sstevel MAN_DBG(MAN_CONFIG, ("ip6_other_sc_ipaddr = %s", buf)); 227703831d35Sstevel (void) inet_ntop(AF_INET6, 227803831d35Sstevel (void *) &man_sc_ip6addrs.ip6_my_sc_ipaddr, 227903831d35Sstevel buf, INET6_ADDRSTRLEN); 228003831d35Sstevel MAN_DBG(MAN_CONFIG, ("ip6_my_sc_ipaddr = %s", buf)); 228103831d35Sstevel } 228203831d35Sstevel #endif /* DEBUG */ 228303831d35Sstevel exit: 228403831d35Sstevel if (error) 228503831d35Sstevel miocnak(wq, mp, 0, error); 228603831d35Sstevel else 228703831d35Sstevel miocack(wq, mp, sizeof (man_sc_ip6addrs_t), 0); 228803831d35Sstevel } 228903831d35Sstevel 229003831d35Sstevel /* 229103831d35Sstevel * M_DATA fastpath info request. 229203831d35Sstevel */ 229303831d35Sstevel static void 229403831d35Sstevel man_dl_ioc_hdr_info(queue_t *wq, mblk_t *mp) 229503831d35Sstevel { 229603831d35Sstevel manstr_t *msp; 229703831d35Sstevel man_t *manp; 229803831d35Sstevel mblk_t *nmp; 229903831d35Sstevel man_dladdr_t *dlap; 230003831d35Sstevel dl_unitdata_req_t *dludp; 230103831d35Sstevel struct ether_header *headerp; 230203831d35Sstevel t_uscalar_t off, len; 230303831d35Sstevel int status = 0; 230403831d35Sstevel 230503831d35Sstevel MAN_DBG(MAN_DLPI, ("man_dl_ioc_hdr_info: enter")); 230603831d35Sstevel 230703831d35Sstevel msp = (manstr_t *)wq->q_ptr; 230803831d35Sstevel manp = msp->ms_manp; 230903831d35Sstevel if (manp == NULL) { 231003831d35Sstevel status = EINVAL; 231103831d35Sstevel goto exit; 231203831d35Sstevel } 231303831d35Sstevel 231403831d35Sstevel status = miocpullup(mp, sizeof (dl_unitdata_req_t) + MAN_ADDRL); 231503831d35Sstevel if (status != 0) 231603831d35Sstevel goto exit; 231703831d35Sstevel 231803831d35Sstevel /* 231903831d35Sstevel * Sanity check the DL_UNITDATA_REQ destination address 232003831d35Sstevel * offset and length values. 232103831d35Sstevel */ 232203831d35Sstevel dludp = (dl_unitdata_req_t *)mp->b_cont->b_rptr; 232303831d35Sstevel off = dludp->dl_dest_addr_offset; 232403831d35Sstevel len = dludp->dl_dest_addr_length; 232503831d35Sstevel if (dludp->dl_primitive != DL_UNITDATA_REQ || 232603831d35Sstevel !MBLKIN(mp->b_cont, off, len) || len != MAN_ADDRL) { 232703831d35Sstevel status = EINVAL; 232803831d35Sstevel goto exit; 232903831d35Sstevel } 233003831d35Sstevel 233103831d35Sstevel dlap = (man_dladdr_t *)(mp->b_cont->b_rptr + off); 233203831d35Sstevel 233303831d35Sstevel /* 233403831d35Sstevel * Allocate a new mblk to hold the ether header. 233503831d35Sstevel */ 233603831d35Sstevel if ((nmp = allocb(ETHERHEADER_SIZE, BPRI_MED)) == NULL) { 233703831d35Sstevel status = ENOMEM; 233803831d35Sstevel goto exit; 233903831d35Sstevel } 234003831d35Sstevel 234103831d35Sstevel /* We only need one dl_ioc_hdr mblk for replay */ 234203831d35Sstevel if (!(msp->ms_flags & MAN_SFLAG_FAST)) 234303831d35Sstevel status = man_dl_catch(&msp->ms_dlioc_mp, mp); 234403831d35Sstevel 234503831d35Sstevel /* Forward the packet to all lower destinations. */ 234603831d35Sstevel if ((status != 0) || ((status = man_dlpi_senddown(msp, mp)) != 0)) { 234703831d35Sstevel freemsg(nmp); 234803831d35Sstevel goto exit; 234903831d35Sstevel } 235003831d35Sstevel 235103831d35Sstevel nmp->b_wptr += ETHERHEADER_SIZE; 235203831d35Sstevel 235303831d35Sstevel /* 235403831d35Sstevel * Fill in the ether header. 235503831d35Sstevel */ 235603831d35Sstevel headerp = (struct ether_header *)nmp->b_rptr; 235703831d35Sstevel ether_copy(&dlap->dl_phys, &headerp->ether_dhost); 235803831d35Sstevel ether_copy(&manp->man_eaddr, &headerp->ether_shost); 235903831d35Sstevel put_ether_type(headerp, dlap->dl_sap); 236003831d35Sstevel 236103831d35Sstevel /* 236203831d35Sstevel * Link new mblk in after the "request" mblks. 236303831d35Sstevel */ 236403831d35Sstevel linkb(mp, nmp); 236503831d35Sstevel 236603831d35Sstevel exit: 236703831d35Sstevel MAN_DBG(MAN_DLPI, ("man_dl_ioc_hdr_info: returns, status = %d", 236803831d35Sstevel status)); 236903831d35Sstevel 237003831d35Sstevel if (status) { 237103831d35Sstevel miocnak(wq, mp, 0, status); 237203831d35Sstevel } else { 237303831d35Sstevel msp = (manstr_t *)wq->q_ptr; 237403831d35Sstevel msp->ms_flags |= MAN_SFLAG_FAST; 237503831d35Sstevel miocack(wq, mp, msgsize(mp->b_cont), 0); 237603831d35Sstevel } 237703831d35Sstevel 237803831d35Sstevel } 237903831d35Sstevel 238003831d35Sstevel /* 238103831d35Sstevel * man_uwsrv - Upper write queue service routine to handle deferred 238203831d35Sstevel * DLPI messages issued from upstream, the write side of the upper half 238303831d35Sstevel * of multiplexor. It is also used by man_bwork to switch the lower 238403831d35Sstevel * multiplexor. 238503831d35Sstevel * 238603831d35Sstevel * wq - upper write queue of mxx 238703831d35Sstevel */ 238803831d35Sstevel static int 238903831d35Sstevel man_uwsrv(queue_t *wq) 239003831d35Sstevel { 239103831d35Sstevel register mblk_t *mp; 239203831d35Sstevel manstr_t *msp; /* per stream data */ 239303831d35Sstevel man_t *manp; /* per instance data */ 239403831d35Sstevel ehdr_t *ep; 239503831d35Sstevel int status; 239603831d35Sstevel 239703831d35Sstevel msp = (manstr_t *)wq->q_ptr; 239803831d35Sstevel 239903831d35Sstevel MAN_DBG(MAN_UWSRV, ("man_uwsrv: wq(0x%p) msp", (void *)wq)); 240003831d35Sstevel MAN_DBGCALL(MAN_UWSRV, man_print_msp(msp)); 240103831d35Sstevel 240203831d35Sstevel if (msp == NULL) 240303831d35Sstevel goto done; 240403831d35Sstevel 240503831d35Sstevel manp = msp->ms_manp; 240603831d35Sstevel 240703831d35Sstevel while (mp = getq(wq)) { 240803831d35Sstevel 240903831d35Sstevel switch (DB_TYPE(mp)) { 241003831d35Sstevel /* 241103831d35Sstevel * Can probably remove this as I never put data messages 241203831d35Sstevel * here. 241303831d35Sstevel */ 241403831d35Sstevel case M_DATA: 241503831d35Sstevel if (manp) { 241603831d35Sstevel ep = (ehdr_t *)mp->b_rptr; 241703831d35Sstevel status = man_start(wq, mp, &ep->ether_dhost); 241803831d35Sstevel if (status) { 241903831d35Sstevel /* 242003831d35Sstevel * man_start() indicated flow control 242103831d35Sstevel * situation, stop processing now. 242203831d35Sstevel */ 242303831d35Sstevel goto break_loop; 242403831d35Sstevel } 242503831d35Sstevel } else 242603831d35Sstevel freemsg(mp); 242703831d35Sstevel break; 242803831d35Sstevel 242903831d35Sstevel case M_PROTO: 243003831d35Sstevel case M_PCPROTO: 243103831d35Sstevel status = man_proto(wq, mp); 243203831d35Sstevel if (status) { 243303831d35Sstevel /* 243403831d35Sstevel * man_proto() indicated flow control 243503831d35Sstevel * situation detected by man_start(), 243603831d35Sstevel * stop processing now. 243703831d35Sstevel */ 243803831d35Sstevel goto break_loop; 243903831d35Sstevel } 244003831d35Sstevel break; 244103831d35Sstevel 244203831d35Sstevel default: 244303831d35Sstevel MAN_DBG(MAN_UWSRV, ("man_uwsrv: discarding mp(0x%p)", 244403831d35Sstevel (void *)mp)); 244503831d35Sstevel freemsg(mp); 244603831d35Sstevel break; 244703831d35Sstevel } 244803831d35Sstevel } 244903831d35Sstevel 245003831d35Sstevel break_loop: 245103831d35Sstevel /* 245203831d35Sstevel * Check to see if bgthread wants us to do something inside the 245303831d35Sstevel * perimeter. 245403831d35Sstevel */ 245503831d35Sstevel if ((msp->ms_flags & MAN_SFLAG_CONTROL) && 245603831d35Sstevel man_iwork_q->q_work != NULL) { 245703831d35Sstevel 245803831d35Sstevel man_iwork(); 245903831d35Sstevel } 246003831d35Sstevel 246103831d35Sstevel done: 246203831d35Sstevel 246303831d35Sstevel MAN_DBG(MAN_UWSRV, ("man_uwsrv: returns")); 246403831d35Sstevel 246503831d35Sstevel return (0); 246603831d35Sstevel } 246703831d35Sstevel 246803831d35Sstevel 246903831d35Sstevel /* 247003831d35Sstevel * man_proto - handle DLPI protocol requests issued from upstream. 247103831d35Sstevel * Called by man_uwsrv(). We disassociate upper and lower multiplexor 247203831d35Sstevel * DLPI state transitions. The upper stream here (manstr_t) transitions 247303831d35Sstevel * appropriately, saves the DLPI requests via man_dlpi(), and then 247403831d35Sstevel * arranges for the DLPI request to be sent down via man_dlpi_senddown() if 247503831d35Sstevel * appropriate. 247603831d35Sstevel * 247703831d35Sstevel * wq - upper write queue of mxx 247803831d35Sstevel * mp - mbl ptr to protocol request 247903831d35Sstevel */ 248003831d35Sstevel static int 248103831d35Sstevel man_proto(queue_t *wq, mblk_t *mp) 248203831d35Sstevel { 248303831d35Sstevel union DL_primitives *dlp; 248403831d35Sstevel int flow_status = 0; 248503831d35Sstevel 248603831d35Sstevel dlp = (union DL_primitives *)mp->b_rptr; 248703831d35Sstevel 248803831d35Sstevel MAN_DBG((MAN_UWSRV | MAN_DLPI), 248903831d35Sstevel ("man_proto: mp(0x%p) prim(%s)\n", (void *)mp, 249003831d35Sstevel dps[dlp->dl_primitive])); 249103831d35Sstevel 249203831d35Sstevel switch (dlp->dl_primitive) { 249303831d35Sstevel case DL_UNITDATA_REQ: 249403831d35Sstevel flow_status = man_udreq(wq, mp); 249503831d35Sstevel break; 249603831d35Sstevel 249703831d35Sstevel case DL_ATTACH_REQ: 249803831d35Sstevel man_areq(wq, mp); 249903831d35Sstevel break; 250003831d35Sstevel 250103831d35Sstevel case DL_DETACH_REQ: 250203831d35Sstevel man_dreq(wq, mp); 250303831d35Sstevel break; 250403831d35Sstevel 250503831d35Sstevel case DL_BIND_REQ: 250603831d35Sstevel man_breq(wq, mp); 250703831d35Sstevel break; 250803831d35Sstevel 250903831d35Sstevel case DL_UNBIND_REQ: 251003831d35Sstevel man_ubreq(wq, mp); 251103831d35Sstevel break; 251203831d35Sstevel 251303831d35Sstevel case DL_INFO_REQ: 251403831d35Sstevel man_ireq(wq, mp); 251503831d35Sstevel break; 251603831d35Sstevel 251703831d35Sstevel case DL_PROMISCON_REQ: 251803831d35Sstevel man_ponreq(wq, mp); 251903831d35Sstevel break; 252003831d35Sstevel 252103831d35Sstevel case DL_PROMISCOFF_REQ: 252203831d35Sstevel man_poffreq(wq, mp); 252303831d35Sstevel break; 252403831d35Sstevel 252503831d35Sstevel case DL_ENABMULTI_REQ: 252603831d35Sstevel man_emreq(wq, mp); 252703831d35Sstevel break; 252803831d35Sstevel 252903831d35Sstevel case DL_DISABMULTI_REQ: 253003831d35Sstevel man_dmreq(wq, mp); 253103831d35Sstevel break; 253203831d35Sstevel 253303831d35Sstevel case DL_PHYS_ADDR_REQ: 253403831d35Sstevel man_pareq(wq, mp); 253503831d35Sstevel break; 253603831d35Sstevel 253703831d35Sstevel case DL_SET_PHYS_ADDR_REQ: 253803831d35Sstevel man_spareq(wq, mp); 253903831d35Sstevel break; 254003831d35Sstevel 254103831d35Sstevel default: 254203831d35Sstevel MAN_DBG((MAN_UWSRV | MAN_DLPI), ("man_proto: prim(%d)\n", 254303831d35Sstevel dlp->dl_primitive)); 254403831d35Sstevel dlerrorack(wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0); 254503831d35Sstevel break; 254603831d35Sstevel 254703831d35Sstevel } /* End switch */ 254803831d35Sstevel 254903831d35Sstevel MAN_DBG((MAN_UWSRV | MAN_DLPI), ("man_proto: exit\n")); 255003831d35Sstevel return (flow_status); 255103831d35Sstevel 255203831d35Sstevel } 255303831d35Sstevel 255403831d35Sstevel static int 255503831d35Sstevel man_udreq(queue_t *wq, mblk_t *mp) 255603831d35Sstevel { 255703831d35Sstevel manstr_t *msp; 255803831d35Sstevel dl_unitdata_req_t *dludp; 255903831d35Sstevel mblk_t *nmp; 256003831d35Sstevel man_dladdr_t *dlap; 256103831d35Sstevel t_uscalar_t off, len; 256203831d35Sstevel int flow_status = 0; 256303831d35Sstevel 256403831d35Sstevel msp = (manstr_t *)wq->q_ptr; 256503831d35Sstevel 256603831d35Sstevel 256703831d35Sstevel if (msp->ms_dlpistate != DL_IDLE) { 256803831d35Sstevel dlerrorack(wq, mp, DL_UNITDATA_REQ, DL_OUTSTATE, 0); 256903831d35Sstevel return (flow_status); 257003831d35Sstevel } 257103831d35Sstevel dludp = (dl_unitdata_req_t *)mp->b_rptr; 257203831d35Sstevel off = dludp->dl_dest_addr_offset; 257303831d35Sstevel len = dludp->dl_dest_addr_length; 257403831d35Sstevel 257503831d35Sstevel /* 257603831d35Sstevel * Validate destination address format. 257703831d35Sstevel */ 257803831d35Sstevel if (!MBLKIN(mp, off, len) || (len != MAN_ADDRL)) { 257903831d35Sstevel dluderrorind(wq, mp, mp->b_rptr + off, len, DL_BADADDR, 0); 258003831d35Sstevel return (flow_status); 258103831d35Sstevel } 258203831d35Sstevel 258303831d35Sstevel /* 258403831d35Sstevel * Error if no M_DATA follows. 258503831d35Sstevel */ 258603831d35Sstevel nmp = mp->b_cont; 258703831d35Sstevel if (nmp == NULL) { 258803831d35Sstevel dluderrorind(wq, mp, mp->b_rptr + off, len, DL_BADDATA, 0); 258903831d35Sstevel return (flow_status); 259003831d35Sstevel } 259103831d35Sstevel 259203831d35Sstevel dlap = (man_dladdr_t *)(mp->b_rptr + off); 259303831d35Sstevel 259403831d35Sstevel flow_status = man_start(wq, mp, &dlap->dl_phys); 259503831d35Sstevel return (flow_status); 259603831d35Sstevel } 259703831d35Sstevel 259803831d35Sstevel /* 259903831d35Sstevel * Handle DL_ATTACH_REQ. 260003831d35Sstevel */ 260103831d35Sstevel static void 260203831d35Sstevel man_areq(queue_t *wq, mblk_t *mp) 260303831d35Sstevel { 260403831d35Sstevel man_t *manp; /* per instance data */ 260503831d35Sstevel manstr_t *msp; /* per stream data */ 260603831d35Sstevel short ppa; 260703831d35Sstevel union DL_primitives *dlp; 260803831d35Sstevel mblk_t *preq = NULL; 260903831d35Sstevel int did_refcnt = FALSE; 261003831d35Sstevel int dlerror = 0; 261103831d35Sstevel int status = 0; 261203831d35Sstevel 261303831d35Sstevel msp = (manstr_t *)wq->q_ptr; 261403831d35Sstevel dlp = (union DL_primitives *)mp->b_rptr; 261503831d35Sstevel 261603831d35Sstevel /* 261703831d35Sstevel * Attach us to MAN PPA (device instance). 261803831d35Sstevel */ 261903831d35Sstevel if (MBLKL(mp) < DL_ATTACH_REQ_SIZE) { 262003831d35Sstevel dlerror = DL_BADPRIM; 262103831d35Sstevel goto exit; 262203831d35Sstevel } 262303831d35Sstevel 262403831d35Sstevel if (msp->ms_dlpistate != DL_UNATTACHED) { 262503831d35Sstevel dlerror = DL_OUTSTATE; 262603831d35Sstevel goto exit; 262703831d35Sstevel } 262803831d35Sstevel 262903831d35Sstevel ppa = dlp->attach_req.dl_ppa; 263003831d35Sstevel if (ppa == -1 || qassociate(wq, ppa) != 0) { 263103831d35Sstevel dlerror = DL_BADPPA; 263203831d35Sstevel MAN_DBG(MAN_WARN, ("man_areq: bad PPA %d", ppa)); 263303831d35Sstevel goto exit; 263403831d35Sstevel } 263503831d35Sstevel 263603831d35Sstevel mutex_enter(&man_lock); 263703831d35Sstevel manp = ddi_get_soft_state(man_softstate, ppa); 263803831d35Sstevel ASSERT(manp != NULL); /* qassociate() succeeded */ 263903831d35Sstevel 264003831d35Sstevel manp->man_refcnt++; 264103831d35Sstevel did_refcnt = TRUE; 264203831d35Sstevel mutex_exit(&man_lock); 264303831d35Sstevel 264403831d35Sstevel /* 264503831d35Sstevel * Create a DL replay list for the lower stream. These wont 264603831d35Sstevel * actually be sent down until the lower streams are made active 264703831d35Sstevel * (sometime after the call to man_init_dests below). 264803831d35Sstevel */ 264903831d35Sstevel preq = man_alloc_physreq_mp(&manp->man_eaddr); 265003831d35Sstevel if (preq == NULL) { 265103831d35Sstevel dlerror = DL_SYSERR; 265203831d35Sstevel status = ENOMEM; 265303831d35Sstevel goto exit; 265403831d35Sstevel } 265503831d35Sstevel 265603831d35Sstevel /* 265703831d35Sstevel * Make copy for dlpi resync of upper and lower streams. 265803831d35Sstevel */ 265903831d35Sstevel if (man_dlpi(msp, mp)) { 266003831d35Sstevel dlerror = DL_SYSERR; 266103831d35Sstevel status = ENOMEM; 266203831d35Sstevel goto exit; 266303831d35Sstevel } 266403831d35Sstevel 266503831d35Sstevel /* TBD - need to clean off ATTACH req on failure here. */ 266603831d35Sstevel if (man_dlpi(msp, preq)) { 266703831d35Sstevel dlerror = DL_SYSERR; 266803831d35Sstevel status = ENOMEM; 266903831d35Sstevel goto exit; 267003831d35Sstevel } 267103831d35Sstevel 267203831d35Sstevel /* 267303831d35Sstevel * man_init_dests/man_start_dest needs these set before call. 267403831d35Sstevel */ 267503831d35Sstevel msp->ms_manp = manp; 267603831d35Sstevel msp->ms_meta_ppa = ppa; 267703831d35Sstevel 267803831d35Sstevel /* 267903831d35Sstevel * Allocate and init lower destination structures. 268003831d35Sstevel */ 268103831d35Sstevel ASSERT(msp->ms_dests == NULL); 268203831d35Sstevel if (man_init_dests(manp, msp)) { 268303831d35Sstevel mblk_t *tmp; 268403831d35Sstevel 268503831d35Sstevel /* 268603831d35Sstevel * If we cant get the lower streams ready, then 268703831d35Sstevel * remove the messages from the DL replay list and 268803831d35Sstevel * fail attach. 268903831d35Sstevel */ 269003831d35Sstevel while ((tmp = msp->ms_dl_mp) != NULL) { 269103831d35Sstevel msp->ms_dl_mp = msp->ms_dl_mp->b_next; 269203831d35Sstevel tmp->b_next = tmp->b_prev = NULL; 269303831d35Sstevel freemsg(tmp); 269403831d35Sstevel } 269503831d35Sstevel 269603831d35Sstevel msp->ms_manp = NULL; 269703831d35Sstevel msp->ms_meta_ppa = -1; 269803831d35Sstevel 269903831d35Sstevel dlerror = DL_SYSERR; 270003831d35Sstevel status = ENOMEM; 270103831d35Sstevel goto exit; 270203831d35Sstevel } 270303831d35Sstevel 270403831d35Sstevel MAN_DBG(MAN_DLPI, ("man_areq: ppa 0x%x man_refcnt: %d\n", 270503831d35Sstevel ppa, manp->man_refcnt)); 270603831d35Sstevel 270703831d35Sstevel SETSTATE(msp, DL_UNBOUND); 270803831d35Sstevel 270903831d35Sstevel exit: 271003831d35Sstevel if (dlerror == 0) { 271103831d35Sstevel dlokack(wq, mp, DL_ATTACH_REQ); 271203831d35Sstevel } else { 271303831d35Sstevel if (did_refcnt) { 271403831d35Sstevel mutex_enter(&man_lock); 271503831d35Sstevel manp->man_refcnt--; 271603831d35Sstevel mutex_exit(&man_lock); 271703831d35Sstevel } 271803831d35Sstevel dlerrorack(wq, mp, DL_ATTACH_REQ, dlerror, status); 271903831d35Sstevel (void) qassociate(wq, -1); 272003831d35Sstevel } 272103831d35Sstevel if (preq != NULL) 272203831d35Sstevel freemsg(preq); 272303831d35Sstevel 272403831d35Sstevel } 272503831d35Sstevel 272603831d35Sstevel /* 272703831d35Sstevel * Called at DL_ATTACH time. 272803831d35Sstevel * Man_lock is held to protect pathgroup list(man_pg). 272903831d35Sstevel */ 273003831d35Sstevel static int 273103831d35Sstevel man_init_dests(man_t *manp, manstr_t *msp) 273203831d35Sstevel { 273303831d35Sstevel man_dest_t *mdp; 273403831d35Sstevel man_pg_t *mpg; 273503831d35Sstevel int i; 273603831d35Sstevel 273703831d35Sstevel mdp = man_kzalloc(MAN_DEST_ARRAY_SIZE, KM_NOSLEEP); 273803831d35Sstevel if (mdp == NULL) 273903831d35Sstevel return (ENOMEM); 274003831d35Sstevel 274103831d35Sstevel msp->ms_dests = mdp; 274203831d35Sstevel 274303831d35Sstevel mutex_enter(&man_lock); 274403831d35Sstevel for (i = 0; i < MAN_MAX_DESTS; i++) { 274503831d35Sstevel 274603831d35Sstevel mdp[i].md_muxid = -1; /* muxid 0 is valid */ 274703831d35Sstevel mutex_init(&mdp->md_lock, NULL, MUTEX_DRIVER, NULL); 274803831d35Sstevel 274903831d35Sstevel mpg = man_find_pg_by_id(manp->man_pg, i); 275003831d35Sstevel 275103831d35Sstevel if (mpg && man_find_active_path(mpg->mpg_pathp)) 275203831d35Sstevel man_start_dest(&mdp[i], msp, mpg); 275303831d35Sstevel } 275403831d35Sstevel mutex_exit(&man_lock); 275503831d35Sstevel 275603831d35Sstevel return (0); 275703831d35Sstevel } 275803831d35Sstevel 275903831d35Sstevel /* 276003831d35Sstevel * Get a destination ready for use. 276103831d35Sstevel */ 276203831d35Sstevel static void 276303831d35Sstevel man_start_dest(man_dest_t *mdp, manstr_t *msp, man_pg_t *mpg) 276403831d35Sstevel { 276503831d35Sstevel man_path_t *ap; 276603831d35Sstevel 276703831d35Sstevel mdp->md_muxid = -1; 276803831d35Sstevel mdp->md_dlpistate = DL_UNATTACHED; 276903831d35Sstevel mdp->md_msp = msp; 277003831d35Sstevel mdp->md_rq = msp->ms_rq; 277103831d35Sstevel mdp->md_pg_id = mpg->mpg_pg_id; 277203831d35Sstevel 277303831d35Sstevel ASSERT(msp->ms_manp); 277403831d35Sstevel 277503831d35Sstevel ether_copy(&msp->ms_manp->man_eaddr, &mdp->md_src_eaddr); 277603831d35Sstevel ether_copy(&mpg->mpg_dst_eaddr, &mdp->md_dst_eaddr); 277703831d35Sstevel 277803831d35Sstevel ap = man_find_active_path(mpg->mpg_pathp); 277903831d35Sstevel ASSERT(ap); 278003831d35Sstevel mdp->md_device = ap->mp_device; 278103831d35Sstevel 278203831d35Sstevel /* 278303831d35Sstevel * Set up linktimers so that first time through, we will do 278403831d35Sstevel * a failover. 278503831d35Sstevel */ 278603831d35Sstevel mdp->md_linkstate = MAN_LINKFAIL; 278703831d35Sstevel mdp->md_state = MAN_DSTATE_INITIALIZING; 278803831d35Sstevel mdp->md_lc_timer_id = qtimeout(man_ctl_wq, man_linkcheck_timer, 278903831d35Sstevel (void *)mdp, man_gettimer(MAN_TIMER_INIT, mdp)); 279003831d35Sstevel 279103831d35Sstevel /* 279203831d35Sstevel * As an optimization, if there is only one destination, 279303831d35Sstevel * remember the destination pointer. Used by man_start(). 279403831d35Sstevel */ 279503831d35Sstevel man_set_optimized_dest(msp); 279603831d35Sstevel 279703831d35Sstevel MAN_DBG(MAN_DEST, ("man_start_dest: mdp")); 279803831d35Sstevel MAN_DBGCALL(MAN_DEST, man_print_mdp(mdp)); 279903831d35Sstevel } 280003831d35Sstevel 280103831d35Sstevel static void 280203831d35Sstevel man_set_optimized_dest(manstr_t *msp) 280303831d35Sstevel { 280403831d35Sstevel int count = 0; 280503831d35Sstevel int i; 280603831d35Sstevel man_dest_t *mdp = NULL; 280703831d35Sstevel 280803831d35Sstevel for (i = 0; i < MAN_MAX_DESTS; i++) { 280903831d35Sstevel if (msp->ms_dests[i].md_msp != NULL) { 281003831d35Sstevel count++; 281103831d35Sstevel mdp = &msp->ms_dests[i]; 281203831d35Sstevel } 281303831d35Sstevel } 281403831d35Sstevel 281503831d35Sstevel if (count == 1) 281603831d35Sstevel msp->ms_destp = mdp; 281703831d35Sstevel else 281803831d35Sstevel msp->ms_destp = NULL; 281903831d35Sstevel 282003831d35Sstevel } 282103831d35Sstevel 282203831d35Sstevel /* 282303831d35Sstevel * Catch dlpi message for replaying, and arrange to send it down 282403831d35Sstevel * to any destinations not PLUMBING. See man_dlpi_replay(). 282503831d35Sstevel */ 282603831d35Sstevel static int 282703831d35Sstevel man_dlpi(manstr_t *msp, mblk_t *mp) 282803831d35Sstevel { 282903831d35Sstevel int status; 283003831d35Sstevel 283103831d35Sstevel status = man_dl_catch(&msp->ms_dl_mp, mp); 283203831d35Sstevel if (status == 0) 283303831d35Sstevel status = man_dlpi_senddown(msp, mp); 283403831d35Sstevel 283503831d35Sstevel return (status); 283603831d35Sstevel } 283703831d35Sstevel 283803831d35Sstevel /* 283903831d35Sstevel * Catch IOCTL type DL_ messages. 284003831d35Sstevel */ 284103831d35Sstevel static int 284203831d35Sstevel man_dlioc(manstr_t *msp, mblk_t *mp) 284303831d35Sstevel { 284403831d35Sstevel int status; 284503831d35Sstevel 284603831d35Sstevel status = man_dl_catch(&msp->ms_dlioc_mp, mp); 284703831d35Sstevel if (status == 0) 284803831d35Sstevel status = man_dlpi_senddown(msp, mp); 284903831d35Sstevel 285003831d35Sstevel return (status); 285103831d35Sstevel } 285203831d35Sstevel 285303831d35Sstevel /* 285403831d35Sstevel * We catch all DLPI messages that we have to resend to a new AP'ed 285503831d35Sstevel * device to put him in the right state. We link these messages together 285603831d35Sstevel * w/ their b_next fields and hang it off of msp->ms_dl_mp. We 285703831d35Sstevel * must be careful to restore b_next fields before doing dupmsg/freemsg! 285803831d35Sstevel * 285903831d35Sstevel * msp - pointer of stream struct to process 286003831d35Sstevel * mblk - pointer to DLPI request to catch 286103831d35Sstevel */ 286203831d35Sstevel static int 286303831d35Sstevel man_dl_catch(mblk_t **mplist, mblk_t *mp) 286403831d35Sstevel { 286503831d35Sstevel mblk_t *dupmp; 286603831d35Sstevel mblk_t *tmp; 286703831d35Sstevel unsigned prim; 286803831d35Sstevel int status = 0; 286903831d35Sstevel 287003831d35Sstevel dupmp = copymsg(mp); 287103831d35Sstevel if (dupmp == NULL) { 287203831d35Sstevel status = ENOMEM; 287303831d35Sstevel goto exit; 287403831d35Sstevel } 287503831d35Sstevel 287603831d35Sstevel 287703831d35Sstevel if (*mplist == NULL) 287803831d35Sstevel *mplist = dupmp; 287903831d35Sstevel else { 288003831d35Sstevel for (tmp = *mplist; tmp->b_next; ) 288103831d35Sstevel tmp = tmp->b_next; 288203831d35Sstevel 288303831d35Sstevel tmp->b_next = dupmp; 288403831d35Sstevel } 288503831d35Sstevel 288603831d35Sstevel prim = DL_PRIM(mp); 288703831d35Sstevel MAN_DBG(MAN_DLPI, 288803831d35Sstevel ("man_dl_catch: adding %s\n", 288903831d35Sstevel (prim == DL_IOC_HDR_INFO) ? "DL_IOC_HDR_INFO" : 289003831d35Sstevel (prim == DLIOCRAW) ? "DLIOCRAW" : 289103831d35Sstevel (prim == DL_PROMISCON_REQ) ? promisc[DL_PROMISCON_TYPE(mp)] : 289203831d35Sstevel dps[prim])); 289303831d35Sstevel 289403831d35Sstevel exit: 289503831d35Sstevel 289603831d35Sstevel return (status); 289703831d35Sstevel } 289803831d35Sstevel 289903831d35Sstevel /* 290003831d35Sstevel * Send down a single DLPI M_[PC]PROTO to all currently valid dests. 290103831d35Sstevel * 290203831d35Sstevel * msp - ptr to NDM stream structure DL_ messages was received on. 290303831d35Sstevel * mp - ptr to mblk containing DL_ request. 290403831d35Sstevel */ 290503831d35Sstevel static int 290603831d35Sstevel man_dlpi_senddown(manstr_t *msp, mblk_t *mp) 290703831d35Sstevel { 290803831d35Sstevel man_dest_t *mdp; 290903831d35Sstevel int i; 291003831d35Sstevel mblk_t *rmp[MAN_MAX_DESTS]; /* Copy to replay */ 291103831d35Sstevel int dstate[MAN_MAX_DESTS]; 291203831d35Sstevel int no_dests = TRUE; 291303831d35Sstevel int status = 0; 291403831d35Sstevel 291503831d35Sstevel if (msp->ms_dests == NULL) 291603831d35Sstevel goto exit; 291703831d35Sstevel 291803831d35Sstevel for (i = 0; i < MAN_MAX_DESTS; i++) { 291903831d35Sstevel mdp = &msp->ms_dests[i]; 292003831d35Sstevel if (mdp->md_state == MAN_DSTATE_READY) { 292103831d35Sstevel dstate[i] = TRUE; 292203831d35Sstevel no_dests = FALSE; 292303831d35Sstevel } else { 292403831d35Sstevel dstate[i] = FALSE; 292503831d35Sstevel } 292603831d35Sstevel rmp[i] = NULL; 292703831d35Sstevel } 292803831d35Sstevel 292903831d35Sstevel if (no_dests) 293003831d35Sstevel goto exit; 293103831d35Sstevel 293203831d35Sstevel /* 293303831d35Sstevel * Build replay and duplicate list for all possible destinations. 293403831d35Sstevel */ 293503831d35Sstevel for (i = 0; i < MAN_MAX_DESTS; i++) { 293603831d35Sstevel if (dstate[i]) { 293703831d35Sstevel rmp[i] = copymsg(mp); 293803831d35Sstevel if (rmp[i] == NULL) { 293903831d35Sstevel status = ENOMEM; 294003831d35Sstevel break; 294103831d35Sstevel } 294203831d35Sstevel } 294303831d35Sstevel } 294403831d35Sstevel 294503831d35Sstevel if (status == 0) { 294603831d35Sstevel for (i = 0; i < MAN_MAX_DESTS; i++) 294703831d35Sstevel if (dstate[i]) { 294803831d35Sstevel mdp = &msp->ms_dests[i]; 294903831d35Sstevel 295003831d35Sstevel ASSERT(mdp->md_wq != NULL); 295103831d35Sstevel ASSERT(mp->b_next == NULL); 295203831d35Sstevel ASSERT(mp->b_prev == NULL); 295303831d35Sstevel 295403831d35Sstevel man_dlpi_replay(mdp, rmp[i]); 295503831d35Sstevel } 295603831d35Sstevel } else { 295703831d35Sstevel for (; i >= 0; i--) 295803831d35Sstevel if (dstate[i] && rmp[i]) 295903831d35Sstevel freemsg(rmp[i]); 296003831d35Sstevel } 296103831d35Sstevel 296203831d35Sstevel exit: 296303831d35Sstevel return (status); 296403831d35Sstevel } 296503831d35Sstevel 296603831d35Sstevel /* 296703831d35Sstevel * man_dlpi_replay - traverse the list of DLPI requests and reapply them to 296803831d35Sstevel * get the upper and lower streams into the same state. Called holding inner 296903831d35Sstevel * perimeter lock exclusive. Note thet we defer M_IOCTL type dlpi messages 297003831d35Sstevel * until we get an OK_ACK to our ATTACH (see man_lrsrv and 297103831d35Sstevel * man_dlioc_replay). 297203831d35Sstevel * 297303831d35Sstevel * mdp - pointer to lower queue (destination) 297403831d35Sstevel * rmp - list of mblks to send down stream. 297503831d35Sstevel */ 297603831d35Sstevel static void 297703831d35Sstevel man_dlpi_replay(man_dest_t *mdp, mblk_t *rmp) 297803831d35Sstevel { 297903831d35Sstevel mblk_t *mp; 298003831d35Sstevel union DL_primitives *dlp = NULL; 298103831d35Sstevel 298203831d35Sstevel MAN_DBG(MAN_DLPI, ("man_dlpi_replay: mdp(0x%p)", (void *)mdp)); 298303831d35Sstevel 298403831d35Sstevel while (rmp) { 298503831d35Sstevel mp = rmp; 298603831d35Sstevel rmp = rmp->b_next; 298703831d35Sstevel mp->b_prev = mp->b_next = NULL; 298803831d35Sstevel 298903831d35Sstevel dlp = (union DL_primitives *)mp->b_rptr; 299003831d35Sstevel MAN_DBG(MAN_DLPI, 299103831d35Sstevel ("man_dlpi_replay: mdp(0x%p) sending %s\n", 299203831d35Sstevel (void *)mdp, 299303831d35Sstevel (dlp->dl_primitive == DL_IOC_HDR_INFO) ? 299403831d35Sstevel "DL_IOC_HDR_INFO" : (dlp->dl_primitive == DLIOCRAW) ? 299503831d35Sstevel "DLIOCRAW" : dps[(unsigned)(dlp->dl_primitive)])); 299603831d35Sstevel 299703831d35Sstevel if (dlp->dl_primitive == DL_ATTACH_REQ) { 299803831d35Sstevel /* 299903831d35Sstevel * insert the lower devices ppa. 300003831d35Sstevel */ 300103831d35Sstevel dlp->attach_req.dl_ppa = mdp->md_device.mdev_ppa; 300203831d35Sstevel } 300303831d35Sstevel 300403831d35Sstevel (void) putnext(mdp->md_wq, mp); 300503831d35Sstevel } 300603831d35Sstevel 300703831d35Sstevel } 300803831d35Sstevel 300903831d35Sstevel static void 301003831d35Sstevel man_dreq(queue_t *wq, mblk_t *mp) 301103831d35Sstevel { 301203831d35Sstevel manstr_t *msp; /* per stream data */ 301303831d35Sstevel man_work_t *wp; 301403831d35Sstevel 301503831d35Sstevel msp = (manstr_t *)wq->q_ptr; 301603831d35Sstevel 301703831d35Sstevel if (MBLKL(mp) < DL_DETACH_REQ_SIZE) { 301803831d35Sstevel dlerrorack(wq, mp, DL_DETACH_REQ, DL_BADPRIM, 0); 301903831d35Sstevel return; 302003831d35Sstevel } 302103831d35Sstevel 302203831d35Sstevel if (msp->ms_dlpistate != DL_UNBOUND) { 302303831d35Sstevel dlerrorack(wq, mp, DL_DETACH_REQ, DL_OUTSTATE, 0); 302403831d35Sstevel return; 302503831d35Sstevel } 302603831d35Sstevel 302703831d35Sstevel ASSERT(msp->ms_dests != NULL); 302803831d35Sstevel 302903831d35Sstevel wp = man_work_alloc(MAN_WORK_CLOSE_STREAM, KM_NOSLEEP); 303003831d35Sstevel if (wp == NULL) { 303103831d35Sstevel dlerrorack(wq, mp, DL_DETACH_REQ, DL_SYSERR, ENOMEM); 303203831d35Sstevel return; 303303831d35Sstevel } 303403831d35Sstevel man_dodetach(msp, wp); 303503831d35Sstevel (void) qassociate(wq, -1); 303603831d35Sstevel 303703831d35Sstevel SETSTATE(msp, DL_UNATTACHED); 303803831d35Sstevel 303903831d35Sstevel dlokack(wq, mp, DL_DETACH_REQ); 304003831d35Sstevel } 304103831d35Sstevel 304203831d35Sstevel static void 304303831d35Sstevel man_dl_clean(mblk_t **mplist) 304403831d35Sstevel { 304503831d35Sstevel mblk_t *tmp; 304603831d35Sstevel 304703831d35Sstevel /* 304803831d35Sstevel * Toss everything. 304903831d35Sstevel */ 305003831d35Sstevel while ((tmp = *mplist) != NULL) { 305103831d35Sstevel *mplist = (*mplist)->b_next; 305203831d35Sstevel tmp->b_next = tmp->b_prev = NULL; 305303831d35Sstevel freemsg(tmp); 305403831d35Sstevel } 305503831d35Sstevel 305603831d35Sstevel } 305703831d35Sstevel 305803831d35Sstevel /* 305903831d35Sstevel * man_dl_release - Remove the corresponding DLPI request from the 306003831d35Sstevel * catch list. Walk thru the catch list looking for the other half of 306103831d35Sstevel * the pair and delete it. If we are detaching, delete the entire list. 306203831d35Sstevel * 306303831d35Sstevel * msp - pointer of stream struct to process 306403831d35Sstevel * mp - pointer to mblk to first half of pair. We will delete other 306503831d35Sstevel * half of pair based on this. 306603831d35Sstevel */ 306703831d35Sstevel static void 306803831d35Sstevel man_dl_release(mblk_t **mplist, mblk_t *mp) 306903831d35Sstevel { 307003831d35Sstevel uchar_t match_dbtype; 307103831d35Sstevel mblk_t *tmp; 307203831d35Sstevel mblk_t *tmpp; 307303831d35Sstevel int matched = FALSE; 307403831d35Sstevel 307503831d35Sstevel if (*mplist == NULL) 307603831d35Sstevel goto exit; 307703831d35Sstevel 307803831d35Sstevel match_dbtype = DB_TYPE(mp); 307903831d35Sstevel 308003831d35Sstevel /* 308103831d35Sstevel * Currently we only clean DL_ PROTO type messages. There is 308203831d35Sstevel * no way to turn off M_CTL or DL_IOC stuff other than sending 308303831d35Sstevel * down a DL_DETACH, which resets everything. 308403831d35Sstevel */ 308503831d35Sstevel if (match_dbtype != M_PROTO && match_dbtype != M_PCPROTO) { 308603831d35Sstevel goto exit; 308703831d35Sstevel } 308803831d35Sstevel 308903831d35Sstevel /* 309003831d35Sstevel * Selectively find a caught mblk that matches this one and 309103831d35Sstevel * remove it from the list 309203831d35Sstevel */ 309303831d35Sstevel tmp = tmpp = *mplist; 309403831d35Sstevel matched = man_match_proto(mp, tmp); 309503831d35Sstevel if (matched) { 309603831d35Sstevel *mplist = tmp->b_next; 309703831d35Sstevel tmp->b_next = tmp->b_prev = NULL; 309803831d35Sstevel } else { 309903831d35Sstevel for (tmp = tmp->b_next; tmp != NULL; tmp = tmp->b_next) { 310003831d35Sstevel if (matched = man_match_proto(mp, tmp)) 310103831d35Sstevel break; 310203831d35Sstevel tmpp = tmp; 310303831d35Sstevel } 310403831d35Sstevel 310503831d35Sstevel if (matched) { 310603831d35Sstevel tmpp->b_next = tmp->b_next; 310703831d35Sstevel tmp->b_next = tmp->b_prev = NULL; 310803831d35Sstevel } 310903831d35Sstevel } 311003831d35Sstevel 311103831d35Sstevel exit: 311203831d35Sstevel if (matched) { 311303831d35Sstevel 311403831d35Sstevel MAN_DBG(MAN_DLPI, ("man_dl_release: release %s", 311503831d35Sstevel (DL_PRIM(mp) == DL_IOC_HDR_INFO) ? "DL_IOC_HDR_INFO" : 311603831d35Sstevel (DL_PRIM(mp) == DLIOCRAW) ? "DLIOCRAW" : 311703831d35Sstevel dps[(int)DL_PRIM(mp)])); 311803831d35Sstevel 311903831d35Sstevel freemsg(tmp); 312003831d35Sstevel } 312103831d35Sstevel MAN_DBG(MAN_DLPI, ("man_dl_release: returns")); 312203831d35Sstevel 312303831d35Sstevel } 312403831d35Sstevel 312503831d35Sstevel /* 312603831d35Sstevel * Compare two DL_ messages. If they are complimentary (e.g. DL_UNBIND 312703831d35Sstevel * compliments DL_BIND), return true. 312803831d35Sstevel */ 312903831d35Sstevel static int 313003831d35Sstevel man_match_proto(mblk_t *mp1, mblk_t *mp2) 313103831d35Sstevel { 313203831d35Sstevel t_uscalar_t prim1; 313303831d35Sstevel t_uscalar_t prim2; 313403831d35Sstevel int matched = FALSE; 313503831d35Sstevel 313603831d35Sstevel /* 313703831d35Sstevel * Primitive to clean off list. 313803831d35Sstevel */ 313903831d35Sstevel prim1 = DL_PRIM(mp1); 314003831d35Sstevel prim2 = DL_PRIM(mp2); 314103831d35Sstevel 314203831d35Sstevel switch (prim1) { 314303831d35Sstevel case DL_UNBIND_REQ: 314403831d35Sstevel if (prim2 == DL_BIND_REQ) 314503831d35Sstevel matched = TRUE; 314603831d35Sstevel break; 314703831d35Sstevel 314803831d35Sstevel case DL_PROMISCOFF_REQ: 314903831d35Sstevel if (prim2 == DL_PROMISCON_REQ) { 315003831d35Sstevel dl_promiscoff_req_t *poff1; 315103831d35Sstevel dl_promiscoff_req_t *poff2; 315203831d35Sstevel 315303831d35Sstevel poff1 = (dl_promiscoff_req_t *)mp1->b_rptr; 315403831d35Sstevel poff2 = (dl_promiscoff_req_t *)mp2->b_rptr; 315503831d35Sstevel 315603831d35Sstevel if (poff1->dl_level == poff2->dl_level) 315703831d35Sstevel matched = TRUE; 315803831d35Sstevel } 315903831d35Sstevel break; 316003831d35Sstevel 316103831d35Sstevel case DL_DISABMULTI_REQ: 316203831d35Sstevel if (prim2 == DL_ENABMULTI_REQ) { 316303831d35Sstevel union DL_primitives *dlp; 316403831d35Sstevel t_uscalar_t off; 316503831d35Sstevel eaddr_t *addrp1; 316603831d35Sstevel eaddr_t *addrp2; 316703831d35Sstevel 316803831d35Sstevel dlp = (union DL_primitives *)mp1->b_rptr; 316903831d35Sstevel off = dlp->disabmulti_req.dl_addr_offset; 317003831d35Sstevel addrp1 = (eaddr_t *)(mp1->b_rptr + off); 317103831d35Sstevel 317203831d35Sstevel dlp = (union DL_primitives *)mp2->b_rptr; 317303831d35Sstevel off = dlp->disabmulti_req.dl_addr_offset; 317403831d35Sstevel addrp2 = (eaddr_t *)(mp2->b_rptr + off); 317503831d35Sstevel 317603831d35Sstevel if (ether_cmp(addrp1, addrp2) == 0) 317703831d35Sstevel matched = 1; 317803831d35Sstevel } 317903831d35Sstevel break; 318003831d35Sstevel 318103831d35Sstevel default: 318203831d35Sstevel break; 318303831d35Sstevel } 318403831d35Sstevel 318503831d35Sstevel MAN_DBG(MAN_DLPI, ("man_match_proto returns %d", matched)); 318603831d35Sstevel 318703831d35Sstevel return (matched); 318803831d35Sstevel } 318903831d35Sstevel 319003831d35Sstevel /* 319103831d35Sstevel * Bind upper stream to a particular SAP. Called with exclusive innerperim 319203831d35Sstevel * QPAIR, shared outerperim. 319303831d35Sstevel */ 319403831d35Sstevel static void 319503831d35Sstevel man_breq(queue_t *wq, mblk_t *mp) 319603831d35Sstevel { 319703831d35Sstevel man_t *manp; /* per instance data */ 319803831d35Sstevel manstr_t *msp; /* per stream data */ 319903831d35Sstevel union DL_primitives *dlp; 320003831d35Sstevel man_dladdr_t man_addr; 320103831d35Sstevel t_uscalar_t sap; 320203831d35Sstevel t_uscalar_t xidtest; 320303831d35Sstevel 320403831d35Sstevel msp = (manstr_t *)wq->q_ptr; 320503831d35Sstevel 320603831d35Sstevel if (MBLKL(mp) < DL_BIND_REQ_SIZE) { 320703831d35Sstevel dlerrorack(wq, mp, DL_BIND_REQ, DL_BADPRIM, 0); 320803831d35Sstevel return; 320903831d35Sstevel } 321003831d35Sstevel 321103831d35Sstevel if (msp->ms_dlpistate != DL_UNBOUND) { 321203831d35Sstevel dlerrorack(wq, mp, DL_BIND_REQ, DL_OUTSTATE, 0); 321303831d35Sstevel return; 321403831d35Sstevel } 321503831d35Sstevel 321603831d35Sstevel dlp = (union DL_primitives *)mp->b_rptr; 321703831d35Sstevel manp = msp->ms_manp; /* valid after attach */ 321803831d35Sstevel sap = dlp->bind_req.dl_sap; 321903831d35Sstevel xidtest = dlp->bind_req.dl_xidtest_flg; 322003831d35Sstevel 322103831d35Sstevel ASSERT(manp); 322203831d35Sstevel 322303831d35Sstevel if (xidtest) { 322403831d35Sstevel dlerrorack(wq, mp, DL_BIND_REQ, DL_NOAUTO, 0); 322503831d35Sstevel return; 322603831d35Sstevel } 322703831d35Sstevel 322803831d35Sstevel if (sap > ETHERTYPE_MAX) { 322903831d35Sstevel dlerrorack(wq, mp, DL_BIND_REQ, DL_BADSAP, 0); 323003831d35Sstevel return; 323103831d35Sstevel } 323203831d35Sstevel 323303831d35Sstevel if (man_dlpi(msp, mp)) { 323403831d35Sstevel dlerrorack(wq, mp, DL_BIND_REQ, DL_SYSERR, ENOMEM); 323503831d35Sstevel return; 323603831d35Sstevel } 323703831d35Sstevel 323803831d35Sstevel msp->ms_sap = sap; 323903831d35Sstevel 324003831d35Sstevel SETSTATE(msp, DL_IDLE); 324103831d35Sstevel 324203831d35Sstevel man_addr.dl_sap = msp->ms_sap; 324303831d35Sstevel ether_copy(&msp->ms_manp->man_eaddr, &man_addr.dl_phys); 324403831d35Sstevel 324503831d35Sstevel dlbindack(wq, mp, msp->ms_sap, &man_addr, MAN_ADDRL, 0, 0); 324603831d35Sstevel 324703831d35Sstevel } 324803831d35Sstevel 324903831d35Sstevel static void 325003831d35Sstevel man_ubreq(queue_t *wq, mblk_t *mp) 325103831d35Sstevel { 325203831d35Sstevel manstr_t *msp; /* per stream data */ 325303831d35Sstevel 325403831d35Sstevel msp = (manstr_t *)wq->q_ptr; 325503831d35Sstevel 325603831d35Sstevel if (MBLKL(mp) < DL_UNBIND_REQ_SIZE) { 325703831d35Sstevel dlerrorack(wq, mp, DL_UNBIND_REQ, DL_BADPRIM, 0); 325803831d35Sstevel return; 325903831d35Sstevel } 326003831d35Sstevel 326103831d35Sstevel if (msp->ms_dlpistate != DL_IDLE) { 326203831d35Sstevel dlerrorack(wq, mp, DL_UNBIND_REQ, DL_OUTSTATE, 0); 326303831d35Sstevel return; 326403831d35Sstevel } 326503831d35Sstevel 326603831d35Sstevel if (man_dlpi_senddown(msp, mp)) { 326703831d35Sstevel dlerrorack(wq, mp, DL_UNBIND_REQ, DL_SYSERR, ENOMEM); 326803831d35Sstevel return; 326903831d35Sstevel } 327003831d35Sstevel 327103831d35Sstevel man_dl_release(&msp->ms_dl_mp, mp); 327203831d35Sstevel 327303831d35Sstevel SETSTATE(msp, DL_UNBOUND); 327403831d35Sstevel 327503831d35Sstevel dlokack(wq, mp, DL_UNBIND_REQ); 327603831d35Sstevel 327703831d35Sstevel } 327803831d35Sstevel 327903831d35Sstevel static void 328003831d35Sstevel man_ireq(queue_t *wq, mblk_t *mp) 328103831d35Sstevel { 328203831d35Sstevel manstr_t *msp; 328303831d35Sstevel dl_info_ack_t *dlip; 328403831d35Sstevel man_dladdr_t *dlap; 328503831d35Sstevel eaddr_t *ep; 328603831d35Sstevel size_t size; 328703831d35Sstevel 328803831d35Sstevel msp = (manstr_t *)wq->q_ptr; 328903831d35Sstevel 329003831d35Sstevel if (MBLKL(mp) < DL_INFO_REQ_SIZE) { 329103831d35Sstevel dlerrorack(wq, mp, DL_INFO_REQ, DL_BADPRIM, 0); 329203831d35Sstevel return; 329303831d35Sstevel } 329403831d35Sstevel 329503831d35Sstevel /* Exchange current msg for a DL_INFO_ACK. */ 329603831d35Sstevel size = sizeof (dl_info_ack_t) + MAN_ADDRL + ETHERADDRL; 329703831d35Sstevel mp = mexchange(wq, mp, size, M_PCPROTO, DL_INFO_ACK); 329803831d35Sstevel if (mp == NULL) { 329903831d35Sstevel MAN_DBG(MAN_DLPI, ("man_ireq: man_ireq: mp == NULL.")); 330003831d35Sstevel return; 330103831d35Sstevel } 330203831d35Sstevel 330303831d35Sstevel /* Fill in the DL_INFO_ACK fields and reply. */ 330403831d35Sstevel dlip = (dl_info_ack_t *)mp->b_rptr; 330503831d35Sstevel *dlip = man_infoack; 330603831d35Sstevel dlip->dl_current_state = msp->ms_dlpistate; 330703831d35Sstevel dlap = (man_dladdr_t *)(mp->b_rptr + dlip->dl_addr_offset); 330803831d35Sstevel dlap->dl_sap = msp->ms_sap; 330903831d35Sstevel 331003831d35Sstevel /* 331103831d35Sstevel * If attached, return physical address. 331203831d35Sstevel */ 331303831d35Sstevel if (msp->ms_manp != NULL) { 331403831d35Sstevel ether_copy(&msp->ms_manp->man_eaddr, &dlap->dl_phys); 331503831d35Sstevel } else { 331603831d35Sstevel bzero((caddr_t)&dlap->dl_phys, ETHERADDRL); 331703831d35Sstevel } 331803831d35Sstevel 331903831d35Sstevel ep = (struct ether_addr *)(mp->b_rptr + dlip->dl_brdcst_addr_offset); 332003831d35Sstevel ether_copy(ðerbroadcast, ep); 332103831d35Sstevel 332203831d35Sstevel qreply(wq, mp); 332303831d35Sstevel 332403831d35Sstevel } 332503831d35Sstevel 332603831d35Sstevel 332703831d35Sstevel static void 332803831d35Sstevel man_ponreq(queue_t *wq, mblk_t *mp) 332903831d35Sstevel { 333003831d35Sstevel manstr_t *msp; 333103831d35Sstevel int flag; 333203831d35Sstevel 333303831d35Sstevel msp = (manstr_t *)wq->q_ptr; 333403831d35Sstevel 333503831d35Sstevel if (MBLKL(mp) < DL_PROMISCON_REQ_SIZE) { 333603831d35Sstevel dlerrorack(wq, mp, DL_PROMISCON_REQ, DL_BADPRIM, 0); 333703831d35Sstevel return; 333803831d35Sstevel } 333903831d35Sstevel 334003831d35Sstevel switch (((dl_promiscon_req_t *)mp->b_rptr)->dl_level) { 334103831d35Sstevel case DL_PROMISC_PHYS: 334203831d35Sstevel flag = MAN_SFLAG_ALLPHYS; 334303831d35Sstevel break; 334403831d35Sstevel 334503831d35Sstevel case DL_PROMISC_SAP: 334603831d35Sstevel flag = MAN_SFLAG_ALLSAP; 334703831d35Sstevel break; 334803831d35Sstevel 334903831d35Sstevel case DL_PROMISC_MULTI: 335003831d35Sstevel flag = MAN_SFLAG_ALLMULTI; 335103831d35Sstevel break; 335203831d35Sstevel 335303831d35Sstevel default: 335403831d35Sstevel dlerrorack(wq, mp, DL_PROMISCON_REQ, DL_NOTSUPPORTED, 0); 335503831d35Sstevel return; 335603831d35Sstevel } 335703831d35Sstevel 335803831d35Sstevel /* 335903831d35Sstevel * Catch request for replay, and forward down to any lower 336003831d35Sstevel * lower stream. 336103831d35Sstevel */ 336203831d35Sstevel if (man_dlpi(msp, mp)) { 336303831d35Sstevel dlerrorack(wq, mp, DL_PROMISCON_REQ, DL_SYSERR, ENOMEM); 336403831d35Sstevel return; 336503831d35Sstevel } 336603831d35Sstevel 336703831d35Sstevel msp->ms_flags |= flag; 336803831d35Sstevel 336903831d35Sstevel dlokack(wq, mp, DL_PROMISCON_REQ); 337003831d35Sstevel 337103831d35Sstevel } 337203831d35Sstevel 337303831d35Sstevel static void 337403831d35Sstevel man_poffreq(queue_t *wq, mblk_t *mp) 337503831d35Sstevel { 337603831d35Sstevel manstr_t *msp; 337703831d35Sstevel int flag; 337803831d35Sstevel 337903831d35Sstevel msp = (manstr_t *)wq->q_ptr; 338003831d35Sstevel 338103831d35Sstevel if (MBLKL(mp) < DL_PROMISCOFF_REQ_SIZE) { 338203831d35Sstevel dlerrorack(wq, mp, DL_PROMISCOFF_REQ, DL_BADPRIM, 0); 338303831d35Sstevel return; 338403831d35Sstevel } 338503831d35Sstevel 338603831d35Sstevel switch (((dl_promiscoff_req_t *)mp->b_rptr)->dl_level) { 338703831d35Sstevel case DL_PROMISC_PHYS: 338803831d35Sstevel flag = MAN_SFLAG_ALLPHYS; 338903831d35Sstevel break; 339003831d35Sstevel 339103831d35Sstevel case DL_PROMISC_SAP: 339203831d35Sstevel flag = MAN_SFLAG_ALLSAP; 339303831d35Sstevel break; 339403831d35Sstevel 339503831d35Sstevel case DL_PROMISC_MULTI: 339603831d35Sstevel flag = MAN_SFLAG_ALLMULTI; 339703831d35Sstevel break; 339803831d35Sstevel 339903831d35Sstevel default: 340003831d35Sstevel dlerrorack(wq, mp, DL_PROMISCOFF_REQ, DL_NOTSUPPORTED, 0); 340103831d35Sstevel return; 340203831d35Sstevel } 340303831d35Sstevel 340403831d35Sstevel if ((msp->ms_flags & flag) == 0) { 340503831d35Sstevel dlerrorack(wq, mp, DL_PROMISCOFF_REQ, DL_NOTENAB, 0); 340603831d35Sstevel return; 340703831d35Sstevel } 340803831d35Sstevel 340903831d35Sstevel if (man_dlpi_senddown(msp, mp)) { 341003831d35Sstevel dlerrorack(wq, mp, DL_PROMISCOFF_REQ, DL_SYSERR, ENOMEM); 341103831d35Sstevel return; 341203831d35Sstevel } 341303831d35Sstevel 341403831d35Sstevel man_dl_release(&msp->ms_dl_mp, mp); 341503831d35Sstevel 341603831d35Sstevel msp->ms_flags &= ~flag; 341703831d35Sstevel 341803831d35Sstevel dlokack(wq, mp, DL_PROMISCOFF_REQ); 341903831d35Sstevel 342003831d35Sstevel } 342103831d35Sstevel 342203831d35Sstevel /* 342303831d35Sstevel * Enable multicast requests. We might need to track addresses instead of 342403831d35Sstevel * just passing things through (see eri_dmreq) - TBD. 342503831d35Sstevel */ 342603831d35Sstevel static void 342703831d35Sstevel man_emreq(queue_t *wq, mblk_t *mp) 342803831d35Sstevel { 342903831d35Sstevel manstr_t *msp; 343003831d35Sstevel union DL_primitives *dlp; 343103831d35Sstevel eaddr_t *addrp; 343203831d35Sstevel t_uscalar_t off; 343303831d35Sstevel t_uscalar_t len; 343403831d35Sstevel 343503831d35Sstevel msp = (manstr_t *)wq->q_ptr; 343603831d35Sstevel 343703831d35Sstevel if (MBLKL(mp) < DL_ENABMULTI_REQ_SIZE) { 343803831d35Sstevel dlerrorack(wq, mp, DL_ENABMULTI_REQ, DL_BADPRIM, 0); 343903831d35Sstevel return; 344003831d35Sstevel } 344103831d35Sstevel 344203831d35Sstevel if (msp->ms_dlpistate == DL_UNATTACHED) { 344303831d35Sstevel dlerrorack(wq, mp, DL_ENABMULTI_REQ, DL_OUTSTATE, 0); 344403831d35Sstevel return; 344503831d35Sstevel } 344603831d35Sstevel 344703831d35Sstevel dlp = (union DL_primitives *)mp->b_rptr; 344803831d35Sstevel len = dlp->enabmulti_req.dl_addr_length; 344903831d35Sstevel off = dlp->enabmulti_req.dl_addr_offset; 345003831d35Sstevel addrp = (struct ether_addr *)(mp->b_rptr + off); 345103831d35Sstevel 345203831d35Sstevel if ((len != ETHERADDRL) || 345303831d35Sstevel !MBLKIN(mp, off, len) || 345403831d35Sstevel ((addrp->ether_addr_octet[0] & 01) == 0)) { 345503831d35Sstevel dlerrorack(wq, mp, DL_ENABMULTI_REQ, DL_BADADDR, 0); 345603831d35Sstevel return; 345703831d35Sstevel } 345803831d35Sstevel 345903831d35Sstevel /* 346003831d35Sstevel * Catch request for replay, and forward down to any lower 346103831d35Sstevel * lower stream. 346203831d35Sstevel */ 346303831d35Sstevel if (man_dlpi(msp, mp)) { 346403831d35Sstevel dlerrorack(wq, mp, DL_ENABMULTI_REQ, DL_SYSERR, ENOMEM); 346503831d35Sstevel return; 346603831d35Sstevel } 346703831d35Sstevel 346803831d35Sstevel dlokack(wq, mp, DL_ENABMULTI_REQ); 346903831d35Sstevel 347003831d35Sstevel } 347103831d35Sstevel 347203831d35Sstevel static void 347303831d35Sstevel man_dmreq(queue_t *wq, mblk_t *mp) 347403831d35Sstevel { 347503831d35Sstevel manstr_t *msp; 347603831d35Sstevel union DL_primitives *dlp; 347703831d35Sstevel eaddr_t *addrp; 347803831d35Sstevel t_uscalar_t off; 347903831d35Sstevel t_uscalar_t len; 348003831d35Sstevel 348103831d35Sstevel msp = (manstr_t *)wq->q_ptr; 348203831d35Sstevel 348303831d35Sstevel if (MBLKL(mp) < DL_DISABMULTI_REQ_SIZE) { 348403831d35Sstevel dlerrorack(wq, mp, DL_DISABMULTI_REQ, DL_BADPRIM, 0); 348503831d35Sstevel return; 348603831d35Sstevel } 348703831d35Sstevel 348803831d35Sstevel if (msp->ms_dlpistate == DL_UNATTACHED) { 348903831d35Sstevel dlerrorack(wq, mp, DL_ENABMULTI_REQ, DL_OUTSTATE, 0); 349003831d35Sstevel return; 349103831d35Sstevel } 349203831d35Sstevel 349303831d35Sstevel dlp = (union DL_primitives *)mp->b_rptr; 349403831d35Sstevel len = dlp->enabmulti_req.dl_addr_length; 349503831d35Sstevel off = dlp->enabmulti_req.dl_addr_offset; 349603831d35Sstevel addrp = (struct ether_addr *)(mp->b_rptr + off); 349703831d35Sstevel 349803831d35Sstevel if ((len != ETHERADDRL) || 349903831d35Sstevel !MBLKIN(mp, off, len) || 350003831d35Sstevel ((addrp->ether_addr_octet[0] & 01) == 0)) { 350103831d35Sstevel dlerrorack(wq, mp, DL_ENABMULTI_REQ, DL_BADADDR, 0); 350203831d35Sstevel return; 350303831d35Sstevel } 350403831d35Sstevel 350503831d35Sstevel if (man_dlpi_senddown(msp, mp)) { 350603831d35Sstevel dlerrorack(wq, mp, DL_ENABMULTI_REQ, DL_SYSERR, ENOMEM); 350703831d35Sstevel return; 350803831d35Sstevel } 350903831d35Sstevel 351003831d35Sstevel man_dl_release(&msp->ms_dl_mp, mp); 351103831d35Sstevel 351203831d35Sstevel dlokack(wq, mp, DL_DISABMULTI_REQ); 351303831d35Sstevel 351403831d35Sstevel } 351503831d35Sstevel 351603831d35Sstevel static void 351703831d35Sstevel man_pareq(queue_t *wq, mblk_t *mp) 351803831d35Sstevel { 351903831d35Sstevel manstr_t *msp; 352003831d35Sstevel union DL_primitives *dlp; 352103831d35Sstevel uint32_t type; 352203831d35Sstevel struct ether_addr addr; 352303831d35Sstevel 352403831d35Sstevel msp = (manstr_t *)wq->q_ptr; 352503831d35Sstevel 352603831d35Sstevel if (MBLKL(mp) < DL_PHYS_ADDR_REQ_SIZE) { 352703831d35Sstevel dlerrorack(wq, mp, DL_PHYS_ADDR_REQ, DL_BADPRIM, 0); 352803831d35Sstevel return; 352903831d35Sstevel } 353003831d35Sstevel 353103831d35Sstevel dlp = (union DL_primitives *)mp->b_rptr; 353203831d35Sstevel type = dlp->physaddr_req.dl_addr_type; 353303831d35Sstevel if (msp->ms_manp == NULL) { 353403831d35Sstevel dlerrorack(wq, mp, DL_PHYS_ADDR_REQ, DL_OUTSTATE, 0); 353503831d35Sstevel return; 353603831d35Sstevel } 353703831d35Sstevel 353803831d35Sstevel switch (type) { 353903831d35Sstevel case DL_FACT_PHYS_ADDR: 354003831d35Sstevel (void) localetheraddr((struct ether_addr *)NULL, &addr); 354103831d35Sstevel break; 354203831d35Sstevel 354303831d35Sstevel case DL_CURR_PHYS_ADDR: 354403831d35Sstevel ether_bcopy(&msp->ms_manp->man_eaddr, &addr); 354503831d35Sstevel break; 354603831d35Sstevel 354703831d35Sstevel default: 354803831d35Sstevel dlerrorack(wq, mp, DL_PHYS_ADDR_REQ, DL_NOTSUPPORTED, 0); 354903831d35Sstevel return; 355003831d35Sstevel } 355103831d35Sstevel 355203831d35Sstevel dlphysaddrack(wq, mp, &addr, ETHERADDRL); 355303831d35Sstevel } 355403831d35Sstevel 355503831d35Sstevel /* 355603831d35Sstevel * TBD - this routine probably should be protected w/ an ndd 355703831d35Sstevel * tuneable, or a man.conf parameter. 355803831d35Sstevel */ 355903831d35Sstevel static void 356003831d35Sstevel man_spareq(queue_t *wq, mblk_t *mp) 356103831d35Sstevel { 356203831d35Sstevel manstr_t *msp; 356303831d35Sstevel union DL_primitives *dlp; 356403831d35Sstevel t_uscalar_t off; 356503831d35Sstevel t_uscalar_t len; 356603831d35Sstevel eaddr_t *addrp; 356703831d35Sstevel 356803831d35Sstevel msp = (manstr_t *)wq->q_ptr; 356903831d35Sstevel 357003831d35Sstevel if (MBLKL(mp) < DL_SET_PHYS_ADDR_REQ_SIZE) { 357103831d35Sstevel dlerrorack(wq, mp, DL_SET_PHYS_ADDR_REQ, DL_BADPRIM, 0); 357203831d35Sstevel return; 357303831d35Sstevel } 357403831d35Sstevel 357503831d35Sstevel dlp = (union DL_primitives *)mp->b_rptr; 357603831d35Sstevel len = dlp->set_physaddr_req.dl_addr_length; 357703831d35Sstevel off = dlp->set_physaddr_req.dl_addr_offset; 357803831d35Sstevel 357903831d35Sstevel if (!MBLKIN(mp, off, len)) { 358003831d35Sstevel dlerrorack(wq, mp, DL_SET_PHYS_ADDR_REQ, DL_BADPRIM, 0); 358103831d35Sstevel return; 358203831d35Sstevel } 358303831d35Sstevel 358403831d35Sstevel addrp = (struct ether_addr *)(mp->b_rptr + off); 358503831d35Sstevel 358603831d35Sstevel /* 358703831d35Sstevel * Error if length of address isn't right or the address 358803831d35Sstevel * specified is a multicast or broadcast address. 358903831d35Sstevel */ 359003831d35Sstevel if ((len != ETHERADDRL) || 359103831d35Sstevel ((addrp->ether_addr_octet[0] & 01) == 1) || 359203831d35Sstevel (ether_cmp(addrp, ðerbroadcast) == 0)) { 359303831d35Sstevel dlerrorack(wq, mp, DL_SET_PHYS_ADDR_REQ, DL_BADADDR, 0); 359403831d35Sstevel return; 359503831d35Sstevel } 359603831d35Sstevel /* 359703831d35Sstevel * Error if this stream is not attached to a device. 359803831d35Sstevel */ 359903831d35Sstevel if (msp->ms_manp == NULL) { 360003831d35Sstevel dlerrorack(wq, mp, DL_SET_PHYS_ADDR_REQ, DL_OUTSTATE, 0); 360103831d35Sstevel return; 360203831d35Sstevel } 360303831d35Sstevel 360403831d35Sstevel /* 360503831d35Sstevel * We will also resend DL_SET_PHYS_ADDR_REQ for each dest 360603831d35Sstevel * when it is linked under us. 360703831d35Sstevel */ 360803831d35Sstevel if (man_dlpi_senddown(msp, mp)) { 360903831d35Sstevel dlerrorack(wq, mp, DL_SET_PHYS_ADDR_REQ, DL_SYSERR, ENOMEM); 361003831d35Sstevel return; 361103831d35Sstevel } 361203831d35Sstevel 361303831d35Sstevel ether_copy(addrp, msp->ms_manp->man_eaddr.ether_addr_octet); 361403831d35Sstevel 361503831d35Sstevel MAN_DBG(MAN_DLPI, ("man_sareq: snagged %s\n", 361603831d35Sstevel ether_sprintf(&msp->ms_manp->man_eaddr))); 361703831d35Sstevel 361803831d35Sstevel dlokack(wq, mp, DL_SET_PHYS_ADDR_REQ); 361903831d35Sstevel 362003831d35Sstevel } 362103831d35Sstevel 362203831d35Sstevel /* 362303831d35Sstevel * These routines make up the lower part of the MAN streams framework. 362403831d35Sstevel */ 362503831d35Sstevel 362603831d35Sstevel /* 362703831d35Sstevel * man_lwsrv - Deferred mblks for down stream. We end up here when 362803831d35Sstevel * the destination is not DL_IDLE when traffic comes downstream. 362903831d35Sstevel * 363003831d35Sstevel * wq - lower write queue of mxx 363103831d35Sstevel */ 363203831d35Sstevel static int 363303831d35Sstevel man_lwsrv(queue_t *wq) 363403831d35Sstevel { 363503831d35Sstevel mblk_t *mp; 363603831d35Sstevel mblk_t *mlistp; 363703831d35Sstevel man_dest_t *mdp; 363803831d35Sstevel size_t count; 363903831d35Sstevel 364003831d35Sstevel mdp = (man_dest_t *)wq->q_ptr; 364103831d35Sstevel 364203831d35Sstevel MAN_DBG(MAN_LWSRV, ("man_lwsrv: wq(0x%p) mdp(0x%p)" 364303831d35Sstevel " md_rq(0x%p)\n", (void *)wq, (void *)mdp, 364403831d35Sstevel mdp ? (void *)mdp->md_rq : NULL)); 364503831d35Sstevel 364603831d35Sstevel if (mdp == NULL) 364703831d35Sstevel goto exit; 364803831d35Sstevel 364903831d35Sstevel if (mdp->md_state & MAN_DSTATE_CLOSING) { 365003831d35Sstevel flushq(wq, FLUSHDATA); 365103831d35Sstevel flushq(RD(wq), FLUSHDATA); 365203831d35Sstevel goto exit; 365303831d35Sstevel } 365403831d35Sstevel 365503831d35Sstevel /* 365603831d35Sstevel * Arrange to send deferred mp's first, then mblks on the 365703831d35Sstevel * service queue. Since we are exclusive in the inner perimeter, 365803831d35Sstevel * we dont have to worry about md_lock, like the put procedures, 365903831d35Sstevel * which are MTPUTSHARED. 366003831d35Sstevel */ 366103831d35Sstevel mutex_enter(&mdp->md_lock); 366203831d35Sstevel mlistp = mdp->md_dmp_head; 366303831d35Sstevel mdp->md_dmp_head = NULL; 366403831d35Sstevel count = mdp->md_dmp_count; 366503831d35Sstevel mdp->md_dmp_count = 0; 366603831d35Sstevel mutex_exit(&mdp->md_lock); 366703831d35Sstevel 366803831d35Sstevel while (mlistp != NULL) { 366903831d35Sstevel mp = mlistp; 367003831d35Sstevel mlistp = mp->b_next; 367103831d35Sstevel mp->b_next = NULL; 367203831d35Sstevel count -= msgsize(mp); 367303831d35Sstevel if (man_start_lower(mdp, mp, NULL, MAN_LOWER)) { 367403831d35Sstevel 367503831d35Sstevel mutex_enter(&mdp->md_lock); 367603831d35Sstevel mdp->md_dmp_count += count + msgsize(mp); 367703831d35Sstevel mp->b_next = mlistp; 367803831d35Sstevel mdp->md_dmp_head = mp; 367903831d35Sstevel mutex_exit(&mdp->md_lock); 368003831d35Sstevel goto exit; 368103831d35Sstevel } 368203831d35Sstevel } 368303831d35Sstevel mdp->md_dmp_tail = NULL; 368403831d35Sstevel 368503831d35Sstevel while (mp = getq(wq)) { 368603831d35Sstevel if (man_start_lower(mdp, mp, NULL, MAN_LOWER)) { 368703831d35Sstevel /* 368803831d35Sstevel * Put it back on queue, making sure to avoid 368903831d35Sstevel * infinite loop mentioned in putbq(9F) 369003831d35Sstevel */ 369103831d35Sstevel noenable(wq); 3692*07d06da5SSurya Prakki (void) putbq(wq, mp); 369303831d35Sstevel enableok(wq); 369403831d35Sstevel 369503831d35Sstevel break; 369603831d35Sstevel } 369703831d35Sstevel } 369803831d35Sstevel 369903831d35Sstevel exit: 370003831d35Sstevel 370103831d35Sstevel return (0); 370203831d35Sstevel } 370303831d35Sstevel 370403831d35Sstevel /* 370503831d35Sstevel * man_lrput - handle DLPI messages issued from downstream. 370603831d35Sstevel * 370703831d35Sstevel * rq - lower read queue of mxx 370803831d35Sstevel * mp - mblk ptr to DLPI request 370903831d35Sstevel * 371003831d35Sstevel * returns 0 371103831d35Sstevel */ 371203831d35Sstevel static int 371303831d35Sstevel man_lrput(queue_t *rq, mblk_t *mp) 371403831d35Sstevel { 371503831d35Sstevel man_dest_t *mdp; 371603831d35Sstevel manstr_t *msp; 371703831d35Sstevel 371803831d35Sstevel #if defined(DEBUG) 371903831d35Sstevel union DL_primitives *dlp; 372003831d35Sstevel t_uscalar_t prim = MAN_DLPI_MAX_PRIM + 1; 372103831d35Sstevel char *prim_str; 372203831d35Sstevel #endif /* DEBUG */ 372303831d35Sstevel 372403831d35Sstevel mdp = (man_dest_t *)rq->q_ptr; 372503831d35Sstevel 372603831d35Sstevel #if defined(DEBUG) 372703831d35Sstevel if (DB_TYPE(mp) == M_PROTO) { 372803831d35Sstevel dlp = (union DL_primitives *)mp->b_rptr; 372903831d35Sstevel prim = dlp->dl_primitive; 373003831d35Sstevel } 373103831d35Sstevel 373203831d35Sstevel prim_str = (prim > MAN_DLPI_MAX_PRIM) ? "NON DLPI" : 373303831d35Sstevel (prim == DL_IOC_HDR_INFO) ? "DL_IOC_HDR_INFO" : 373403831d35Sstevel (prim == DLIOCRAW) ? "DLIOCRAW" : 373503831d35Sstevel dps[(unsigned int)prim]; 373603831d35Sstevel MAN_DBG(MAN_LRPUT, ("man_lrput: rq(0x%p) mp(0x%p) mdp(0x%p)" 373703831d35Sstevel " db_type(0x%x) dl_prim %s", (void *)rq, 373803831d35Sstevel (void *)mp, (void *)mdp, DB_TYPE(mp), prim_str)); 373903831d35Sstevel MAN_DBGCALL(MAN_LRPUT2, man_print_mdp(mdp)); 374003831d35Sstevel #endif /* DEBUG */ 374103831d35Sstevel 374203831d35Sstevel if (DB_TYPE(mp) == M_FLUSH) { 374303831d35Sstevel /* Turn around */ 374403831d35Sstevel if (*mp->b_rptr & FLUSHW) { 374503831d35Sstevel *mp->b_rptr &= ~FLUSHR; 374603831d35Sstevel qreply(rq, mp); 374703831d35Sstevel } else 374803831d35Sstevel freemsg(mp); 374903831d35Sstevel return (0); 375003831d35Sstevel } 375103831d35Sstevel 375203831d35Sstevel if (mdp == NULL || mdp->md_state != MAN_DSTATE_READY) { 375303831d35Sstevel 375403831d35Sstevel MAN_DBG(MAN_LRPUT, ("man_lrput: not ready mdp(0x%p)," 375503831d35Sstevel " state(%d)", (void *)mdp, mdp ? mdp->md_state : -1)); 375603831d35Sstevel freemsg(mp); 375703831d35Sstevel return (0); 375803831d35Sstevel } 375903831d35Sstevel 376003831d35Sstevel /* 376103831d35Sstevel * If we have a destination in the right state, forward on datagrams. 376203831d35Sstevel */ 376303831d35Sstevel if (MAN_IS_DATA(mp)) { 376403831d35Sstevel if (mdp->md_dlpistate == DL_IDLE && canputnext(mdp->md_rq)) { 376503831d35Sstevel 376603831d35Sstevel msp = mdp->md_msp; 376703831d35Sstevel if (!(msp->ms_flags & MAN_SFLAG_PROMISC)) 376803831d35Sstevel mdp->md_rcvcnt++; /* Count for failover */ 376903831d35Sstevel /* 377003831d35Sstevel * go put mblk_t directly up to next queue. 377103831d35Sstevel */ 377203831d35Sstevel MAN_DBG(MAN_LRPUT, ("man_lrput: putnext to rq(0x%p)", 377303831d35Sstevel (void *)mdp->md_rq)); 377403831d35Sstevel (void) putnext(mdp->md_rq, mp); 377503831d35Sstevel } else { 377603831d35Sstevel freemsg(mp); 377703831d35Sstevel } 377803831d35Sstevel } else { 377903831d35Sstevel /* 378003831d35Sstevel * Handle in man_lrsrv with exclusive inner perimeter lock. 378103831d35Sstevel */ 3782*07d06da5SSurya Prakki (void) putq(rq, mp); 378303831d35Sstevel } 378403831d35Sstevel 378503831d35Sstevel return (0); 378603831d35Sstevel } 378703831d35Sstevel 378803831d35Sstevel /* 378903831d35Sstevel * Either this is a response from our attempt to sync the upper and lower 379003831d35Sstevel * stream states, or its data. If its not data. Do DL_* response processing 379103831d35Sstevel * and transition md_dlpistate accordingly. If its data, toss it. 379203831d35Sstevel */ 379303831d35Sstevel static int 379403831d35Sstevel man_lrsrv(queue_t *rq) 379503831d35Sstevel { 379603831d35Sstevel man_dest_t *mdp; 379703831d35Sstevel mblk_t *mp; 379803831d35Sstevel union DL_primitives *dlp; 379903831d35Sstevel ulong_t prim; 380003831d35Sstevel ulong_t cprim; 380103831d35Sstevel int need_dl_reset = FALSE; 380203831d35Sstevel 380303831d35Sstevel #if defined(DEBUG) 380403831d35Sstevel struct iocblk *iocp; 380503831d35Sstevel char ioc_cmd[256]; 380603831d35Sstevel #endif /* DEBUG */ 380703831d35Sstevel 380803831d35Sstevel MAN_DBG(MAN_LRSRV, ("man_lrsrv: rq(0x%p)", (void *)rq)); 380903831d35Sstevel 381003831d35Sstevel mdp = (man_dest_t *)rq->q_ptr; 381103831d35Sstevel 381203831d35Sstevel if ((mdp == NULL) || (mdp->md_state & MAN_DSTATE_CLOSING)) { 381303831d35Sstevel flushq(rq, FLUSHDATA); 381403831d35Sstevel flushq(WR(rq), FLUSHDATA); 381503831d35Sstevel goto exit; 381603831d35Sstevel } 381703831d35Sstevel 381803831d35Sstevel while (mp = getq(rq)) { 381903831d35Sstevel 382003831d35Sstevel 382103831d35Sstevel /* 382203831d35Sstevel * If we're not connected, or its a datagram, toss it. 382303831d35Sstevel */ 382403831d35Sstevel if (MAN_IS_DATA(mp) || mdp->md_state != MAN_DSTATE_READY) { 382503831d35Sstevel 382603831d35Sstevel MAN_DBG(MAN_LRSRV, ("man_lrsrv: dropping mblk mdp(0x%p)" 382703831d35Sstevel " is_data(%d)", (void *)mdp, MAN_IS_DATA(mp))); 382803831d35Sstevel freemsg(mp); 382903831d35Sstevel continue; 383003831d35Sstevel } 383103831d35Sstevel 383203831d35Sstevel /* 383303831d35Sstevel * Should be response to man_dlpi_replay. Discard unless there 383403831d35Sstevel * is a failure we care about. 383503831d35Sstevel */ 383603831d35Sstevel 383703831d35Sstevel switch (DB_TYPE(mp)) { 383803831d35Sstevel case M_PROTO: 383903831d35Sstevel case M_PCPROTO: 384003831d35Sstevel /* Do proto processing below. */ 384103831d35Sstevel break; 384203831d35Sstevel 384303831d35Sstevel case M_IOCNAK: 384403831d35Sstevel /* 384503831d35Sstevel * DL_IOC* failed for some reason. 384603831d35Sstevel */ 384703831d35Sstevel need_dl_reset = TRUE; 384803831d35Sstevel 384903831d35Sstevel #if defined(DEBUG) 385003831d35Sstevel iocp = (struct iocblk *)mp->b_rptr; 385103831d35Sstevel 3852*07d06da5SSurya Prakki (void) sprintf(ioc_cmd, "0x%x", iocp->ioc_cmd); 385303831d35Sstevel MAN_DBG(MAN_LRSRV, ("man_lrsrv: M_IOCNAK err %d for cmd(%s)\n", 385403831d35Sstevel iocp->ioc_error, 385503831d35Sstevel (iocp->ioc_cmd == DL_IOC_HDR_INFO) ? "DL_IOC_HDR_INFO" : 385603831d35Sstevel (iocp->ioc_cmd == DLIOCRAW) ? "DLIOCRAW" : ioc_cmd)); 385703831d35Sstevel #endif /* DEBUG */ 385803831d35Sstevel 385903831d35Sstevel /* FALLTHRU */ 386003831d35Sstevel 386103831d35Sstevel case M_IOCACK: 386203831d35Sstevel case M_CTL: 386303831d35Sstevel /* 386403831d35Sstevel * OK response from DL_IOC*, ignore. 386503831d35Sstevel */ 386603831d35Sstevel goto dl_reset; 386703831d35Sstevel } 386803831d35Sstevel 386903831d35Sstevel dlp = (union DL_primitives *)mp->b_rptr; 387003831d35Sstevel prim = dlp->dl_primitive; 387103831d35Sstevel 387203831d35Sstevel MAN_DBG(MAN_LRSRV, ("man_lrsrv: prim %s", dps[(int)prim])); 387303831d35Sstevel 387403831d35Sstevel /* 387503831d35Sstevel * DLPI state processing big theory: We do not rigorously check 387603831d35Sstevel * DLPI states (e.g. PENDING stuff). Simple rules: 387703831d35Sstevel * 387803831d35Sstevel * 1) If we see an OK_ACK to an ATTACH_REQ, dlpistate = DL_UNBOUND. 387903831d35Sstevel * 2) If we see an BIND_ACK to a BIND_REQ, dlpistate = DL_IDLE. 388003831d35Sstevel * 3) If we see a OK_ACK response to an UNBIND_REQ 388103831d35Sstevel * dlpistate = DL_UNBOUND. 388203831d35Sstevel * 4) If we see a OK_ACK response to a DETACH_REQ, 388303831d35Sstevel * dlpistate = DL_UNATTACHED. 388403831d35Sstevel * 388503831d35Sstevel * Everything that isn't handle by 1-4 above is handled by 5) 388603831d35Sstevel * 388703831d35Sstevel * 5) A NAK to any DL_* messages we care about causes 388803831d35Sstevel * dlpistate = DL_UNATTACHED and man_reset_dlpi to run 388903831d35Sstevel * 389003831d35Sstevel * TBD - need a reset counter so we can try a switch if it gets 389103831d35Sstevel * too high. 389203831d35Sstevel */ 389303831d35Sstevel 389403831d35Sstevel switch (prim) { 389503831d35Sstevel case DL_OK_ACK: 389603831d35Sstevel cprim = dlp->ok_ack.dl_correct_primitive; 389703831d35Sstevel 389803831d35Sstevel switch (cprim) { 389903831d35Sstevel case DL_ATTACH_REQ: 390003831d35Sstevel if (man_dlioc_replay(mdp)) { 390103831d35Sstevel D_SETSTATE(mdp, DL_UNBOUND); 390203831d35Sstevel } else { 390303831d35Sstevel need_dl_reset = TRUE; 390403831d35Sstevel break; 390503831d35Sstevel } 390603831d35Sstevel break; 390703831d35Sstevel 390803831d35Sstevel case DL_DETACH_REQ: 390903831d35Sstevel D_SETSTATE(mdp, DL_UNATTACHED); 391003831d35Sstevel break; 391103831d35Sstevel 391203831d35Sstevel case DL_UNBIND_REQ: 391303831d35Sstevel /* 391403831d35Sstevel * Cancel timer and set md_dlpistate. 391503831d35Sstevel */ 391603831d35Sstevel D_SETSTATE(mdp, DL_UNBOUND); 391703831d35Sstevel 391803831d35Sstevel ASSERT(mdp->md_bc_id == 0); 391903831d35Sstevel if (mdp->md_lc_timer_id != 0) { 392003831d35Sstevel (void) quntimeout(man_ctl_wq, 392103831d35Sstevel mdp->md_lc_timer_id); 392203831d35Sstevel mdp->md_lc_timer_id = 0; 392303831d35Sstevel } 392403831d35Sstevel } 392503831d35Sstevel MAN_DBG(MAN_DLPI, 392603831d35Sstevel (" cprim %s", dps[(int)cprim])); 392703831d35Sstevel break; 392803831d35Sstevel 392903831d35Sstevel case DL_BIND_ACK: 393003831d35Sstevel /* 393103831d35Sstevel * We're ready for data. Get man_lwsrv to run to 393203831d35Sstevel * process any defered data and start linkcheck timer. 393303831d35Sstevel */ 393403831d35Sstevel D_SETSTATE(mdp, DL_IDLE); 393503831d35Sstevel qenable(mdp->md_wq); 393603831d35Sstevel mdp->md_linkstate = MAN_LINKGOOD; 393703831d35Sstevel if (man_needs_linkcheck(mdp)) { 393803831d35Sstevel mdp->md_lc_timer_id = qtimeout(man_ctl_wq, 393903831d35Sstevel man_linkcheck_timer, (void *)mdp, 394003831d35Sstevel man_gettimer(MAN_TIMER_LINKCHECK, mdp)); 394103831d35Sstevel } 394203831d35Sstevel 394303831d35Sstevel break; 394403831d35Sstevel 394503831d35Sstevel case DL_ERROR_ACK: 394603831d35Sstevel cprim = dlp->error_ack.dl_error_primitive; 394703831d35Sstevel switch (cprim) { 394803831d35Sstevel case DL_ATTACH_REQ: 394903831d35Sstevel case DL_BIND_REQ: 395003831d35Sstevel case DL_DISABMULTI_REQ: 395103831d35Sstevel case DL_ENABMULTI_REQ: 395203831d35Sstevel case DL_PROMISCON_REQ: 395303831d35Sstevel case DL_PROMISCOFF_REQ: 395403831d35Sstevel case DL_SET_PHYS_ADDR_REQ: 395503831d35Sstevel need_dl_reset = TRUE; 395603831d35Sstevel break; 395703831d35Sstevel 395803831d35Sstevel /* 395903831d35Sstevel * ignore error TBD (better comment) 396003831d35Sstevel */ 396103831d35Sstevel case DL_UNBIND_REQ: 396203831d35Sstevel case DL_DETACH_REQ: 396303831d35Sstevel break; 396403831d35Sstevel } 396503831d35Sstevel 396603831d35Sstevel MAN_DBG(MAN_DLPI, 396703831d35Sstevel ("\tdl_errno %d dl_unix_errno %d cprim %s", 396803831d35Sstevel dlp->error_ack.dl_errno, dlp->error_ack.dl_unix_errno, 396903831d35Sstevel dps[(int)cprim])); 397003831d35Sstevel break; 397103831d35Sstevel 397203831d35Sstevel case DL_UDERROR_IND: 397303831d35Sstevel MAN_DBG(MAN_DLPI, 397403831d35Sstevel ("\tdl_errno %d unix_errno %d", 397503831d35Sstevel dlp->uderror_ind.dl_errno, 397603831d35Sstevel dlp->uderror_ind.dl_unix_errno)); 397703831d35Sstevel break; 397803831d35Sstevel 397903831d35Sstevel case DL_INFO_ACK: 398003831d35Sstevel break; 398103831d35Sstevel 398203831d35Sstevel default: 398303831d35Sstevel /* 398403831d35Sstevel * We should not get here. 398503831d35Sstevel */ 398603831d35Sstevel cmn_err(CE_WARN, "man_lrsrv: unexpected DL prim 0x%lx!", 398703831d35Sstevel prim); 398803831d35Sstevel need_dl_reset = TRUE; 398903831d35Sstevel break; 399003831d35Sstevel } 399103831d35Sstevel 399203831d35Sstevel dl_reset: 399303831d35Sstevel freemsg(mp); 399403831d35Sstevel 399503831d35Sstevel if (need_dl_reset) { 399603831d35Sstevel man_pg_t *mpg; 399703831d35Sstevel man_path_t *mp; 399803831d35Sstevel 399903831d35Sstevel if (qsize(rq)) { /* Dump all messages. */ 400003831d35Sstevel flushq(rq, FLUSHDATA); 400103831d35Sstevel flushq(WR(rq), FLUSHDATA); 400203831d35Sstevel } 400303831d35Sstevel 400403831d35Sstevel mdp->md_dlpierrors++; 400503831d35Sstevel D_SETSTATE(mdp, DL_UNATTACHED); 400603831d35Sstevel if (mdp->md_lc_timer_id != 0) { 400703831d35Sstevel (void) quntimeout(man_ctl_wq, mdp->md_lc_timer_id); 400803831d35Sstevel mdp->md_lc_timer_id = 0; 400903831d35Sstevel } 401003831d35Sstevel 401103831d35Sstevel mutex_enter(&man_lock); 401203831d35Sstevel ASSERT(mdp->md_msp != NULL); 401303831d35Sstevel ASSERT(mdp->md_msp->ms_manp != NULL); 401403831d35Sstevel mpg = man_find_pg_by_id(mdp->md_msp->ms_manp->man_pg, 401503831d35Sstevel mdp->md_pg_id); 401603831d35Sstevel ASSERT(mpg != NULL); 401703831d35Sstevel mp = man_find_path_by_ppa(mpg->mpg_pathp, 401803831d35Sstevel mdp->md_device.mdev_ppa); 401903831d35Sstevel ASSERT(mp != NULL); 402003831d35Sstevel mp->mp_device.mdev_state |= MDEV_FAILED; 402103831d35Sstevel if ((mdp->md_dlpierrors >= MAN_MAX_DLPIERRORS) && 402203831d35Sstevel (man_is_on_domain || 402303831d35Sstevel mdp->md_msp->ms_manp->man_meta_ppa == 1)) { 402403831d35Sstevel /* 402503831d35Sstevel * Autoswitching is disabled for instance 0 402603831d35Sstevel * on the SC as we expect the domain to 402703831d35Sstevel * initiate the path switching. 402803831d35Sstevel */ 402903831d35Sstevel (void) man_do_autoswitch((man_dest_t *)mdp); 403003831d35Sstevel MAN_DBG(MAN_WARN, ("man_lrsrv: dlpi failure(%d,%d)," 403103831d35Sstevel " switching path", mdp->md_device.mdev_major, 403203831d35Sstevel mdp->md_device.mdev_ppa)); 403303831d35Sstevel } else { 403403831d35Sstevel mdp->md_lc_timer_id = qtimeout(man_ctl_wq, 403503831d35Sstevel man_reset_dlpi, (void *)mdp, 403603831d35Sstevel man_gettimer(MAN_TIMER_DLPIRESET, mdp)); 403703831d35Sstevel } 403803831d35Sstevel mutex_exit(&man_lock); 403903831d35Sstevel } 404003831d35Sstevel 404103831d35Sstevel 404203831d35Sstevel } /* End while (getq()) */ 404303831d35Sstevel 404403831d35Sstevel exit: 404503831d35Sstevel MAN_DBG(MAN_DLPI, ("man_lrsrv: returns")); 404603831d35Sstevel 404703831d35Sstevel return (0); 404803831d35Sstevel } 404903831d35Sstevel 405003831d35Sstevel static int 405103831d35Sstevel man_needs_linkcheck(man_dest_t *mdp) 405203831d35Sstevel { 405303831d35Sstevel /* 405403831d35Sstevel * Not ready for linkcheck. 405503831d35Sstevel */ 405603831d35Sstevel if (mdp->md_msp == NULL || mdp->md_msp->ms_manp == NULL) 405703831d35Sstevel return (0); 405803831d35Sstevel 405903831d35Sstevel /* 406003831d35Sstevel * Linkchecking needs to be done on IP streams. For domain, all 406103831d35Sstevel * driver instances need checking, for SC only instance 1 needs it. 406203831d35Sstevel */ 406303831d35Sstevel if ((man_is_on_domain || mdp->md_msp->ms_manp->man_meta_ppa == 1) && 406403831d35Sstevel (mdp->md_msp->ms_sap == ETHERTYPE_IP || 406503831d35Sstevel mdp->md_msp->ms_sap == ETHERTYPE_IPV6)) 406603831d35Sstevel 406703831d35Sstevel return (1); 406803831d35Sstevel 406903831d35Sstevel /* 407003831d35Sstevel * Linkcheck not need on this link. 407103831d35Sstevel */ 407203831d35Sstevel return (0); 407303831d35Sstevel } 407403831d35Sstevel 407503831d35Sstevel /* 407603831d35Sstevel * The following routines process work requests posted to man_iwork_q 407703831d35Sstevel * from the non-STREAMS half of the driver (see man_bwork.c). The work 407803831d35Sstevel * requires access to the inner perimeter lock of the driver. This 407903831d35Sstevel * lock is acquired by man_uwsrv, who calls man_iwork to process the 408003831d35Sstevel * man_iwork_q-> 408103831d35Sstevel */ 408203831d35Sstevel 408303831d35Sstevel /* 408403831d35Sstevel * The man_bwork has posted some work for us to do inside the 408503831d35Sstevel * perimeter. This mainly involves updating lower multiplexor data 408603831d35Sstevel * structures (non-blocking type stuff). So, we can hold the man_lock 408703831d35Sstevel * until we are done processing all work items. Note that some of these 408803831d35Sstevel * routines in turn submit work back to the bgthread, which they can do 408903831d35Sstevel * since we hold the man_lock. 409003831d35Sstevel */ 409103831d35Sstevel static void 409203831d35Sstevel man_iwork() 409303831d35Sstevel { 409403831d35Sstevel man_work_t *wp; 409503831d35Sstevel int wp_finished; 409603831d35Sstevel 409703831d35Sstevel MAN_DBG(MAN_SWITCH, ("man_iwork: q_work(0x%p)", 409803831d35Sstevel (void *)man_iwork_q->q_work)); 409903831d35Sstevel 410003831d35Sstevel mutex_enter(&man_lock); 410103831d35Sstevel 410203831d35Sstevel while (man_iwork_q->q_work) { 410303831d35Sstevel 410403831d35Sstevel wp = man_iwork_q->q_work; 410503831d35Sstevel man_iwork_q->q_work = wp->mw_next; 410603831d35Sstevel wp->mw_next = NULL; 410703831d35Sstevel 410803831d35Sstevel mutex_exit(&man_lock); 410903831d35Sstevel 411003831d35Sstevel MAN_DBG(MAN_SWITCH, ("man_iwork: type %s", 411103831d35Sstevel _mw_type[wp->mw_type])); 411203831d35Sstevel 411303831d35Sstevel wp_finished = TRUE; 411403831d35Sstevel 411503831d35Sstevel switch (wp->mw_type) { 411603831d35Sstevel case MAN_WORK_DRATTACH: 411703831d35Sstevel (void) man_do_dr_attach(wp); 411803831d35Sstevel break; 411903831d35Sstevel 412003831d35Sstevel case MAN_WORK_DRSWITCH: 412103831d35Sstevel /* 412203831d35Sstevel * Return status to man_dr_detach immediately. If 412303831d35Sstevel * no error submitting SWITCH request, man_iswitch 412403831d35Sstevel * or man_bclose will cv_signal man_dr_detach on 412503831d35Sstevel * completion of SWITCH work request. 412603831d35Sstevel */ 412703831d35Sstevel if (man_do_dr_switch(wp) == 0) 412803831d35Sstevel wp_finished = FALSE; 412903831d35Sstevel break; 413003831d35Sstevel 413103831d35Sstevel case MAN_WORK_DRDETACH: 413203831d35Sstevel man_do_dr_detach(wp); 413303831d35Sstevel break; 413403831d35Sstevel 413503831d35Sstevel case MAN_WORK_SWITCH: 413603831d35Sstevel if (man_iswitch(wp)) 413703831d35Sstevel wp_finished = FALSE; 413803831d35Sstevel break; 413903831d35Sstevel 414003831d35Sstevel case MAN_WORK_KSTAT_UPDATE: 414103831d35Sstevel man_do_kstats(wp); 414203831d35Sstevel break; 414303831d35Sstevel 414403831d35Sstevel default: 414503831d35Sstevel cmn_err(CE_WARN, "man_iwork: " 414603831d35Sstevel "illegal work type(%d)", wp->mw_type); 414703831d35Sstevel break; 414803831d35Sstevel } 414903831d35Sstevel 415003831d35Sstevel mutex_enter(&man_lock); 415103831d35Sstevel 415203831d35Sstevel /* 415303831d35Sstevel * If we've completed the work request, delete, or 415403831d35Sstevel * cv_signal waiter. 415503831d35Sstevel */ 415603831d35Sstevel if (wp_finished) { 415703831d35Sstevel wp->mw_flags |= MAN_WFLAGS_DONE; 415803831d35Sstevel 415903831d35Sstevel if (wp->mw_flags & MAN_WFLAGS_CVWAITER) 416003831d35Sstevel cv_signal(&wp->mw_cv); 416103831d35Sstevel else 416203831d35Sstevel man_work_free(wp); 416303831d35Sstevel } 416403831d35Sstevel } 416503831d35Sstevel 416603831d35Sstevel mutex_exit(&man_lock); 416703831d35Sstevel } 416803831d35Sstevel 416903831d35Sstevel /* 417003831d35Sstevel * man_dr_detach has submitted a request to DRSWITCH a path. 417103831d35Sstevel * He is in cv_wait_sig(wp->mw_cv). We forward the work request on to 417203831d35Sstevel * man_bwork as a switch request. It should end up back at 417303831d35Sstevel * man_iwork, who will cv_signal(wp->mw_cv) man_dr_detach. 417403831d35Sstevel * 417503831d35Sstevel * Called holding inner perimeter lock. 417603831d35Sstevel * man_lock is held to synchronize access to pathgroup list(man_pg). 417703831d35Sstevel */ 417803831d35Sstevel static int 417903831d35Sstevel man_do_dr_switch(man_work_t *wp) 418003831d35Sstevel { 418103831d35Sstevel man_t *manp; 418203831d35Sstevel man_pg_t *mpg; 418303831d35Sstevel man_path_t *mp; 418403831d35Sstevel man_path_t *ap; 418503831d35Sstevel man_adest_t *adp; 418603831d35Sstevel mi_path_t mpath; 418703831d35Sstevel int status = 0; 418803831d35Sstevel 418903831d35Sstevel adp = &wp->mw_arg; 419003831d35Sstevel 419103831d35Sstevel MAN_DBG(MAN_SWITCH, ("man_do_dr_switch: pg_id %d work:", adp->a_pg_id)); 419203831d35Sstevel MAN_DBGCALL(MAN_SWITCH, man_print_work(wp)); 419303831d35Sstevel 419403831d35Sstevel mutex_enter(&man_lock); 419503831d35Sstevel manp = ddi_get_soft_state(man_softstate, adp->a_man_ppa); 419603831d35Sstevel if (manp == NULL || manp->man_pg == NULL) { 419703831d35Sstevel status = ENODEV; 419803831d35Sstevel goto exit; 419903831d35Sstevel } 420003831d35Sstevel 420103831d35Sstevel mpg = man_find_pg_by_id(manp->man_pg, adp->a_pg_id); 420203831d35Sstevel if (mpg == NULL) { 420303831d35Sstevel status = ENODEV; 420403831d35Sstevel goto exit; 420503831d35Sstevel } 420603831d35Sstevel 420703831d35Sstevel if (mpg->mpg_flags & MAN_PG_SWITCHING) { 420803831d35Sstevel status = EAGAIN; 420903831d35Sstevel goto exit; 421003831d35Sstevel } 421103831d35Sstevel 421203831d35Sstevel /* 421303831d35Sstevel * Check to see if detaching device is active. If so, activate 421403831d35Sstevel * an alternate. 421503831d35Sstevel */ 421603831d35Sstevel mp = man_find_active_path(mpg->mpg_pathp); 421703831d35Sstevel if (mp && mp->mp_device.mdev_ppa == adp->a_sf_dev.mdev_ppa) { 421803831d35Sstevel 421903831d35Sstevel ap = man_find_alternate_path(mpg->mpg_pathp); 422003831d35Sstevel if (ap == NULL) { 422103831d35Sstevel status = EBUSY; 422203831d35Sstevel goto exit; 422303831d35Sstevel } 422403831d35Sstevel 422503831d35Sstevel bzero((char *)&mpath, sizeof (mi_path_t)); 422603831d35Sstevel 422703831d35Sstevel mpath.mip_cmd = MI_PATH_ACTIVATE; 422803831d35Sstevel mpath.mip_man_ppa = 0; 422903831d35Sstevel mpath.mip_pg_id = 0; 423003831d35Sstevel mpath.mip_devs[0] = ap->mp_device; 423103831d35Sstevel mpath.mip_ndevs = 1; 423203831d35Sstevel ether_copy(&manp->man_eaddr, &mpath.mip_eaddr); 423303831d35Sstevel 423403831d35Sstevel /* 423503831d35Sstevel * DR thread is sleeping on wp->mw_cv. We change the work 423603831d35Sstevel * request from DRSWITCH to SWITCH and submit it to 423703831d35Sstevel * for processing by man_bwork (via man_pg_cmd). At 423803831d35Sstevel * completion the SWITCH work request is processed by 423903831d35Sstevel * man_iswitch() or man_bclose and the DR thread will 424003831d35Sstevel * be cv_signal'd. 424103831d35Sstevel */ 424203831d35Sstevel wp->mw_type = MAN_WORK_SWITCH; 424303831d35Sstevel if (status = man_pg_cmd(&mpath, wp)) 424403831d35Sstevel goto exit; 424503831d35Sstevel 424603831d35Sstevel } else { 424703831d35Sstevel /* 424803831d35Sstevel * Tell man_dr_detach that detaching device is not currently 424903831d35Sstevel * in use. 425003831d35Sstevel */ 425103831d35Sstevel status = ENODEV; 425203831d35Sstevel } 425303831d35Sstevel 425403831d35Sstevel exit: 425503831d35Sstevel if (status) { 425603831d35Sstevel /* 425703831d35Sstevel * ENODEV is a noop, not really an error. 425803831d35Sstevel */ 425903831d35Sstevel if (status != ENODEV) 426003831d35Sstevel wp->mw_status = status; 426103831d35Sstevel } 426203831d35Sstevel mutex_exit(&man_lock); 426303831d35Sstevel 426403831d35Sstevel return (status); 426503831d35Sstevel } 426603831d35Sstevel 426703831d35Sstevel /* 426803831d35Sstevel * man_dr_attach has submitted a request to DRATTACH a path, 426903831d35Sstevel * add that path to the path list. 427003831d35Sstevel * 427103831d35Sstevel * Called holding perimeter lock. 427203831d35Sstevel */ 427303831d35Sstevel static int 427403831d35Sstevel man_do_dr_attach(man_work_t *wp) 427503831d35Sstevel { 427603831d35Sstevel man_t *manp; 427703831d35Sstevel man_adest_t *adp; 427803831d35Sstevel mi_path_t mpath; 427903831d35Sstevel manc_t manc; 428003831d35Sstevel int status = 0; 428103831d35Sstevel 428203831d35Sstevel adp = &wp->mw_arg; 428303831d35Sstevel 428403831d35Sstevel MAN_DBG(MAN_SWITCH, ("man_do_dr_attach: pg_id %d work:", adp->a_pg_id)); 428503831d35Sstevel MAN_DBGCALL(MAN_SWITCH, man_print_work(wp)); 428603831d35Sstevel 428703831d35Sstevel mutex_enter(&man_lock); 428803831d35Sstevel manp = ddi_get_soft_state(man_softstate, adp->a_man_ppa); 428903831d35Sstevel if (manp == NULL || manp->man_pg == NULL) { 429003831d35Sstevel status = ENODEV; 429103831d35Sstevel goto exit; 429203831d35Sstevel } 429303831d35Sstevel 429403831d35Sstevel if (status = man_get_iosram(&manc)) { 429503831d35Sstevel goto exit; 429603831d35Sstevel } 429703831d35Sstevel /* 429803831d35Sstevel * Extract SC ethernet address from IOSRAM. 429903831d35Sstevel */ 430003831d35Sstevel ether_copy(&manc.manc_sc_eaddr, &mpath.mip_eaddr); 430103831d35Sstevel 430203831d35Sstevel mpath.mip_pg_id = adp->a_pg_id; 430303831d35Sstevel mpath.mip_man_ppa = adp->a_man_ppa; 430403831d35Sstevel /* 430503831d35Sstevel * man_dr_attach passes the new device info in a_sf_dev. 430603831d35Sstevel */ 430703831d35Sstevel MAN_DBG(MAN_DR, ("man_do_dr_attach: ")); 430803831d35Sstevel MAN_DBGCALL(MAN_DR, man_print_dev(&adp->a_sf_dev)); 430903831d35Sstevel mpath.mip_devs[0] = adp->a_sf_dev; 431003831d35Sstevel mpath.mip_ndevs = 1; 431103831d35Sstevel mpath.mip_cmd = MI_PATH_ADD; 431203831d35Sstevel status = man_pg_cmd(&mpath, NULL); 431303831d35Sstevel 431403831d35Sstevel exit: 431503831d35Sstevel mutex_exit(&man_lock); 431603831d35Sstevel return (status); 431703831d35Sstevel } 431803831d35Sstevel 431903831d35Sstevel /* 432003831d35Sstevel * man_dr_detach has submitted a request to DRDETACH a path. 432103831d35Sstevel * He is in cv_wait_sig(wp->mw_cv). We remove the path and 432203831d35Sstevel * cv_signal(wp->mw_cv) man_dr_detach. 432303831d35Sstevel * 432403831d35Sstevel * Called holding perimeter lock. 432503831d35Sstevel */ 432603831d35Sstevel static void 432703831d35Sstevel man_do_dr_detach(man_work_t *wp) 432803831d35Sstevel { 432903831d35Sstevel man_t *manp; 433003831d35Sstevel man_pg_t *mpg; 433103831d35Sstevel man_path_t *mp; 433203831d35Sstevel man_adest_t *adp; 433303831d35Sstevel manc_t manc; 433403831d35Sstevel mi_path_t mpath; 433503831d35Sstevel int i; 433603831d35Sstevel int found; 433703831d35Sstevel int status = 0; 433803831d35Sstevel 433903831d35Sstevel adp = &wp->mw_arg; 434003831d35Sstevel 434103831d35Sstevel MAN_DBG(MAN_SWITCH, ("man_do_dr_detach: pg_id %d work:", adp->a_pg_id)); 434203831d35Sstevel MAN_DBGCALL(MAN_SWITCH, man_print_work(wp)); 434303831d35Sstevel 434403831d35Sstevel mutex_enter(&man_lock); 434503831d35Sstevel manp = ddi_get_soft_state(man_softstate, adp->a_man_ppa); 434603831d35Sstevel if (manp == NULL || manp->man_pg == NULL) { 434703831d35Sstevel status = ENODEV; 434803831d35Sstevel goto exit; 434903831d35Sstevel } 435003831d35Sstevel 435103831d35Sstevel mpg = man_find_pg_by_id(manp->man_pg, adp->a_pg_id); 435203831d35Sstevel if (mpg == NULL) { 435303831d35Sstevel status = ENODEV; 435403831d35Sstevel goto exit; 435503831d35Sstevel } 435603831d35Sstevel 435703831d35Sstevel if (mpg->mpg_flags & MAN_PG_SWITCHING) { 435803831d35Sstevel status = EAGAIN; 435903831d35Sstevel goto exit; 436003831d35Sstevel } 436103831d35Sstevel 436203831d35Sstevel /* 436303831d35Sstevel * We should have switched detaching path if it was active. 436403831d35Sstevel */ 436503831d35Sstevel mp = man_find_active_path(mpg->mpg_pathp); 436603831d35Sstevel if (mp && mp->mp_device.mdev_ppa == adp->a_sf_dev.mdev_ppa) { 436703831d35Sstevel status = EAGAIN; 436803831d35Sstevel goto exit; 436903831d35Sstevel } 437003831d35Sstevel 437103831d35Sstevel /* 437203831d35Sstevel * Submit an ASSIGN command, minus the detaching device. 437303831d35Sstevel */ 437403831d35Sstevel bzero((char *)&mpath, sizeof (mi_path_t)); 437503831d35Sstevel 437603831d35Sstevel if (status = man_get_iosram(&manc)) { 437703831d35Sstevel goto exit; 437803831d35Sstevel } 437903831d35Sstevel 438003831d35Sstevel mpath.mip_cmd = MI_PATH_ASSIGN; 438103831d35Sstevel mpath.mip_man_ppa = 0; 438203831d35Sstevel mpath.mip_pg_id = 0; 438303831d35Sstevel 438403831d35Sstevel mp = mpg->mpg_pathp; 438503831d35Sstevel i = 0; 438603831d35Sstevel found = FALSE; 438703831d35Sstevel while (mp != NULL) { 438803831d35Sstevel if (mp->mp_device.mdev_ppa != adp->a_sf_dev.mdev_ppa) { 438903831d35Sstevel mpath.mip_devs[i] = mp->mp_device; 439003831d35Sstevel i++; 439103831d35Sstevel } else { 439203831d35Sstevel found = TRUE; 439303831d35Sstevel } 439403831d35Sstevel mp = mp->mp_next; 439503831d35Sstevel } 439603831d35Sstevel 439703831d35Sstevel if (found) { 439803831d35Sstevel /* 439903831d35Sstevel * Need to include SCs ethernet address in command. 440003831d35Sstevel */ 440103831d35Sstevel mpath.mip_ndevs = i; 440203831d35Sstevel ether_copy(&manc.manc_sc_eaddr, &mpath.mip_eaddr); 440303831d35Sstevel 440403831d35Sstevel status = man_pg_cmd(&mpath, NULL); 440503831d35Sstevel } 440603831d35Sstevel 440703831d35Sstevel /* 440803831d35Sstevel * Hand back status to man_dr_detach request. 440903831d35Sstevel */ 441003831d35Sstevel exit: 441103831d35Sstevel if (status != ENODEV) 441203831d35Sstevel wp->mw_status = status; 441303831d35Sstevel 441403831d35Sstevel mutex_exit(&man_lock); 441503831d35Sstevel 441603831d35Sstevel } 441703831d35Sstevel 441803831d35Sstevel 441903831d35Sstevel /* 442003831d35Sstevel * The background thread has configured new lower multiplexor streams for 442103831d35Sstevel * the given destinations. Update the appropriate destination data structures 442203831d35Sstevel * inside the inner perimeter. We must take care to deal with destinations 442303831d35Sstevel * whose upper stream has closed or detached from lower streams. 442403831d35Sstevel * 442503831d35Sstevel * Returns 442603831d35Sstevel * 0 Done with work request. 442703831d35Sstevel * 1 Reused work request. 442803831d35Sstevel */ 442903831d35Sstevel static int 443003831d35Sstevel man_iswitch(man_work_t *wp) 443103831d35Sstevel { 443203831d35Sstevel man_adest_t *adp; 443303831d35Sstevel man_t *manp; 443403831d35Sstevel man_pg_t *mpg; 443503831d35Sstevel man_path_t *mp = NULL; 443603831d35Sstevel man_dest_t *mdp; 443703831d35Sstevel man_dest_t *tdp; 443803831d35Sstevel int i; 443903831d35Sstevel int switch_ok = TRUE; 444003831d35Sstevel 444103831d35Sstevel adp = &wp->mw_arg; 444203831d35Sstevel 444303831d35Sstevel if (wp->mw_status != 0) { 444403831d35Sstevel switch_ok = FALSE; /* Never got things opened */ 444503831d35Sstevel } 444603831d35Sstevel 444703831d35Sstevel /* 444803831d35Sstevel * Update destination structures as appropriate. 444903831d35Sstevel */ 445003831d35Sstevel for (i = 0; i < adp->a_ndests; i++) { 445103831d35Sstevel man_dest_t tmp; 445203831d35Sstevel 445303831d35Sstevel /* 445403831d35Sstevel * Check to see if lower stream we just switch is still 445503831d35Sstevel * around. 445603831d35Sstevel */ 445703831d35Sstevel tdp = &adp->a_mdp[i]; 445803831d35Sstevel mdp = man_switch_match(tdp, adp->a_pg_id, tdp->md_switch_id); 445903831d35Sstevel 446003831d35Sstevel if (mdp == NULL) 446103831d35Sstevel continue; 446203831d35Sstevel 446303831d35Sstevel if (switch_ok == FALSE) { 446403831d35Sstevel /* 446503831d35Sstevel * Switch failed for some reason. Clear 446603831d35Sstevel * PLUMBING flag and retry switch again later. 446703831d35Sstevel */ 446803831d35Sstevel man_ifail_dest(mdp); 446903831d35Sstevel continue; 447003831d35Sstevel } 447103831d35Sstevel 447203831d35Sstevel /* 447303831d35Sstevel * Swap new info, for old. We return the old info to 447403831d35Sstevel * man_bwork to close things up below. 447503831d35Sstevel */ 447603831d35Sstevel bcopy((char *)mdp, (char *)&tmp, sizeof (man_dest_t)); 447703831d35Sstevel 447803831d35Sstevel ASSERT(mdp->md_state & MAN_DSTATE_PLUMBING); 447903831d35Sstevel ASSERT(mdp->md_state == tdp->md_state); 448003831d35Sstevel 448103831d35Sstevel mdp->md_state = tdp->md_state; 448203831d35Sstevel 448303831d35Sstevel /* 448403831d35Sstevel * save the wq from the destination passed(tdp). 448503831d35Sstevel */ 448603831d35Sstevel mdp->md_wq = tdp->md_wq; 448703831d35Sstevel RD(mdp->md_wq)->q_ptr = (void *)(mdp); 448803831d35Sstevel WR(mdp->md_wq)->q_ptr = (void *)(mdp); 448903831d35Sstevel 449003831d35Sstevel mdp->md_state &= ~MAN_DSTATE_INITIALIZING; 449103831d35Sstevel mdp->md_state |= MAN_DSTATE_READY; 449203831d35Sstevel 449303831d35Sstevel ASSERT(mdp->md_device.mdev_major == adp->a_sf_dev.mdev_major); 449403831d35Sstevel 449503831d35Sstevel ASSERT(tdp->md_device.mdev_ppa == adp->a_st_dev.mdev_ppa); 449603831d35Sstevel ASSERT(tdp->md_device.mdev_major == adp->a_st_dev.mdev_major); 449703831d35Sstevel 449803831d35Sstevel mdp->md_device = tdp->md_device; 449903831d35Sstevel mdp->md_muxid = tdp->md_muxid; 450003831d35Sstevel mdp->md_linkstate = MAN_LINKUNKNOWN; 450103831d35Sstevel (void) drv_getparm(TIME, &mdp->md_lastswitch); 450203831d35Sstevel mdp->md_state &= ~MAN_DSTATE_PLUMBING; 450303831d35Sstevel mdp->md_switch_id = 0; 450403831d35Sstevel mdp->md_switches++; 450503831d35Sstevel mdp->md_dlpierrors = 0; 450603831d35Sstevel D_SETSTATE(mdp, DL_UNATTACHED); 450703831d35Sstevel 450803831d35Sstevel /* 450903831d35Sstevel * Resync lower w/ upper dlpi state. This will start link 451003831d35Sstevel * timer if/when lower stream goes to DL_IDLE (see man_lrsrv). 451103831d35Sstevel */ 451203831d35Sstevel man_reset_dlpi((void *)mdp); 451303831d35Sstevel 451403831d35Sstevel bcopy((char *)&tmp, (char *)tdp, sizeof (man_dest_t)); 451503831d35Sstevel } 451603831d35Sstevel 451703831d35Sstevel if (switch_ok) { 451803831d35Sstevel for (i = 0; i < adp->a_ndests; i++) { 451903831d35Sstevel tdp = &adp->a_mdp[i]; 452003831d35Sstevel 452103831d35Sstevel tdp->md_state &= ~MAN_DSTATE_PLUMBING; 452203831d35Sstevel tdp->md_state &= ~MAN_DSTATE_INITIALIZING; 452303831d35Sstevel tdp->md_state |= MAN_DSTATE_READY; 452403831d35Sstevel } 452503831d35Sstevel } else { 452603831d35Sstevel /* 452703831d35Sstevel * Never got switch-to destinations open, free them. 452803831d35Sstevel */ 452903831d35Sstevel man_kfree(adp->a_mdp, 453003831d35Sstevel sizeof (man_dest_t) * adp->a_ndests); 453103831d35Sstevel } 453203831d35Sstevel 453303831d35Sstevel /* 453403831d35Sstevel * Clear pathgroup switching flag and update path flags. 453503831d35Sstevel */ 453603831d35Sstevel mutex_enter(&man_lock); 453703831d35Sstevel manp = ddi_get_soft_state(man_softstate, adp->a_man_ppa); 453803831d35Sstevel 453903831d35Sstevel ASSERT(manp != NULL); 454003831d35Sstevel ASSERT(manp->man_pg != NULL); 454103831d35Sstevel 454203831d35Sstevel mpg = man_find_pg_by_id(manp->man_pg, adp->a_pg_id); 454303831d35Sstevel ASSERT(mpg != NULL); 454403831d35Sstevel ASSERT(mpg->mpg_flags & MAN_PG_SWITCHING); 454503831d35Sstevel mpg->mpg_flags &= ~MAN_PG_SWITCHING; 454603831d35Sstevel 454703831d35Sstevel /* 454803831d35Sstevel * Switch succeeded, mark path we switched from as failed, and 454903831d35Sstevel * device we switch to as active and clear its failed flag (if set). 455003831d35Sstevel * Sync up kstats. 455103831d35Sstevel */ 455203831d35Sstevel if (switch_ok) { 455303831d35Sstevel mp = man_find_active_path(mpg->mpg_pathp); 455403831d35Sstevel if (mp != NULL) { 455503831d35Sstevel 455603831d35Sstevel ASSERT(adp->a_sf_dev.mdev_major != 0); 455703831d35Sstevel 455803831d35Sstevel MAN_DBG(MAN_SWITCH, ("man_iswitch: switch from dev:")); 455903831d35Sstevel MAN_DBGCALL(MAN_SWITCH, man_print_dev(&adp->a_sf_dev)); 456003831d35Sstevel 456103831d35Sstevel mp->mp_device.mdev_state &= ~MDEV_ACTIVE; 456203831d35Sstevel } else 456303831d35Sstevel ASSERT(adp->a_sf_dev.mdev_major == 0); 456403831d35Sstevel 456503831d35Sstevel MAN_DBG(MAN_SWITCH, ("man_iswitch: switch to dev:")); 456603831d35Sstevel MAN_DBGCALL(MAN_SWITCH, man_print_dev(&adp->a_st_dev)); 456703831d35Sstevel 456803831d35Sstevel ASSERT(adp->a_st_dev.mdev_major != 0); 456903831d35Sstevel 457003831d35Sstevel mp = man_find_path_by_ppa(mpg->mpg_pathp, 457103831d35Sstevel adp->a_st_dev.mdev_ppa); 457203831d35Sstevel 457303831d35Sstevel ASSERT(mp != NULL); 457403831d35Sstevel 457503831d35Sstevel mp->mp_device.mdev_state |= MDEV_ACTIVE; 457603831d35Sstevel } 457703831d35Sstevel 457803831d35Sstevel /* 457903831d35Sstevel * Decrement manp reference count and hand back work request if 458003831d35Sstevel * needed. 458103831d35Sstevel */ 458203831d35Sstevel manp->man_refcnt--; 458303831d35Sstevel 458403831d35Sstevel if (switch_ok) { 458503831d35Sstevel wp->mw_type = MAN_WORK_CLOSE; 458603831d35Sstevel man_work_add(man_bwork_q, wp); 458703831d35Sstevel } 458803831d35Sstevel 458903831d35Sstevel mutex_exit(&man_lock); 459003831d35Sstevel 459103831d35Sstevel return (switch_ok); 459203831d35Sstevel } 459303831d35Sstevel 459403831d35Sstevel /* 459503831d35Sstevel * Find the destination in the upper stream that we just switched. 459603831d35Sstevel */ 459703831d35Sstevel man_dest_t * 459803831d35Sstevel man_switch_match(man_dest_t *sdp, int pg_id, void *sid) 459903831d35Sstevel { 460003831d35Sstevel man_dest_t *mdp = NULL; 460103831d35Sstevel manstr_t *msp; 460203831d35Sstevel 460303831d35Sstevel for (msp = man_strup; msp != NULL; msp = msp->ms_next) { 460403831d35Sstevel /* 460503831d35Sstevel * Check if upper stream closed, or detached. 460603831d35Sstevel */ 460703831d35Sstevel if (msp != sdp->md_msp) 460803831d35Sstevel continue; 460903831d35Sstevel 461003831d35Sstevel if (msp->ms_dests == NULL) 461103831d35Sstevel break; 461203831d35Sstevel 461303831d35Sstevel mdp = &msp->ms_dests[pg_id]; 461403831d35Sstevel 461503831d35Sstevel /* 461603831d35Sstevel * Upper stream detached and reattached while we were 461703831d35Sstevel * switching. 461803831d35Sstevel */ 461903831d35Sstevel if (mdp->md_switch_id != sid) { 462003831d35Sstevel mdp = NULL; 462103831d35Sstevel break; 462203831d35Sstevel } 462303831d35Sstevel } 462403831d35Sstevel 462503831d35Sstevel return (mdp); 462603831d35Sstevel } 462703831d35Sstevel 462803831d35Sstevel /* 462903831d35Sstevel * bg_thread cant complete the switch for some reason. (Re)start the 463003831d35Sstevel * linkcheck timer again. 463103831d35Sstevel */ 463203831d35Sstevel static void 463303831d35Sstevel man_ifail_dest(man_dest_t *mdp) 463403831d35Sstevel { 463503831d35Sstevel ASSERT(mdp->md_lc_timer_id == 0); 463603831d35Sstevel ASSERT(mdp->md_bc_id == 0); 463703831d35Sstevel ASSERT(mdp->md_state & MAN_DSTATE_PLUMBING); 463803831d35Sstevel 463903831d35Sstevel MAN_DBG(MAN_SWITCH, ("man_ifail_dest")); 464003831d35Sstevel MAN_DBGCALL(MAN_SWITCH, man_print_mdp(mdp)); 464103831d35Sstevel 464203831d35Sstevel mdp->md_state &= ~MAN_DSTATE_PLUMBING; 464303831d35Sstevel mdp->md_linkstate = MAN_LINKFAIL; 464403831d35Sstevel 464503831d35Sstevel /* 464603831d35Sstevel * If we have not yet initialized link, or the upper stream is 464703831d35Sstevel * DL_IDLE, restart the linktimer. 464803831d35Sstevel */ 464903831d35Sstevel if ((mdp->md_state & MAN_DSTATE_INITIALIZING) || 465003831d35Sstevel ((mdp->md_msp->ms_sap == ETHERTYPE_IPV6 || 465103831d35Sstevel mdp->md_msp->ms_sap == ETHERTYPE_IP) && 465203831d35Sstevel mdp->md_msp->ms_dlpistate == DL_IDLE)) { 465303831d35Sstevel 465403831d35Sstevel mdp->md_lc_timer_id = qtimeout(man_ctl_wq, man_linkcheck_timer, 465503831d35Sstevel (void *)mdp, man_gettimer(MAN_TIMER_LINKCHECK, mdp)); 465603831d35Sstevel } 465703831d35Sstevel 465803831d35Sstevel } 465903831d35Sstevel 466003831d35Sstevel /* 466103831d35Sstevel * Arrange to replay all of ms_dl_mp on the new lower stream to get it 466203831d35Sstevel * in sync with the upper stream. Note that this includes setting the 466303831d35Sstevel * physical address. 466403831d35Sstevel * 466503831d35Sstevel * Called from qtimeout with inner perimeter lock. 466603831d35Sstevel */ 466703831d35Sstevel static void 466803831d35Sstevel man_reset_dlpi(void *argp) 466903831d35Sstevel { 467003831d35Sstevel man_dest_t *mdp = (man_dest_t *)argp; 467103831d35Sstevel manstr_t *msp; 467203831d35Sstevel mblk_t *mp; 467303831d35Sstevel mblk_t *rmp = NULL; 467403831d35Sstevel mblk_t *tmp; 467503831d35Sstevel 467603831d35Sstevel mdp->md_lc_timer_id = 0; 467703831d35Sstevel 467803831d35Sstevel if (mdp->md_state != MAN_DSTATE_READY) { 467903831d35Sstevel MAN_DBG(MAN_DLPI, ("man_reset_dlpi: not ready!")); 468003831d35Sstevel return; 468103831d35Sstevel } 468203831d35Sstevel 468303831d35Sstevel msp = mdp->md_msp; 468403831d35Sstevel 468503831d35Sstevel rmp = man_dup_mplist(msp->ms_dl_mp); 468603831d35Sstevel if (rmp == NULL) 468703831d35Sstevel goto fail; 468803831d35Sstevel 468903831d35Sstevel /* 469003831d35Sstevel * Send down an unbind and detach request, just to clean things 469103831d35Sstevel * out, we ignore ERROR_ACKs for unbind and detach in man_lrsrv. 469203831d35Sstevel */ 469303831d35Sstevel tmp = man_alloc_ubreq_dreq(); 469403831d35Sstevel if (tmp == NULL) { 469503831d35Sstevel goto fail; 469603831d35Sstevel } 469703831d35Sstevel mp = tmp; 469803831d35Sstevel while (mp->b_next != NULL) 469903831d35Sstevel mp = mp->b_next; 470003831d35Sstevel mp->b_next = rmp; 470103831d35Sstevel rmp = tmp; 470203831d35Sstevel 470303831d35Sstevel man_dlpi_replay(mdp, rmp); 470403831d35Sstevel 470503831d35Sstevel return; 470603831d35Sstevel 470703831d35Sstevel fail: 470803831d35Sstevel 470903831d35Sstevel while (rmp) { 471003831d35Sstevel mp = rmp; 471103831d35Sstevel rmp = rmp->b_next; 471203831d35Sstevel mp->b_next = mp->b_prev = NULL; 471303831d35Sstevel freemsg(mp); 471403831d35Sstevel } 471503831d35Sstevel 471603831d35Sstevel ASSERT(mdp->md_lc_timer_id == 0); 471703831d35Sstevel ASSERT(mdp->md_bc_id == 0); 471803831d35Sstevel 471903831d35Sstevel /* 472003831d35Sstevel * If low on memory, try again later. I Could use qbufcall, but that 472103831d35Sstevel * could fail and I would have to try and recover from that w/ 472203831d35Sstevel * qtimeout anyway. 472303831d35Sstevel */ 472403831d35Sstevel mdp->md_lc_timer_id = qtimeout(man_ctl_wq, man_reset_dlpi, 472503831d35Sstevel (void *)mdp, man_gettimer(MAN_TIMER_LINKCHECK, mdp)); 472603831d35Sstevel } 472703831d35Sstevel 472803831d35Sstevel /* 472903831d35Sstevel * Once we receive acknowledgement that DL_ATTACH_REQ was successful, 473003831d35Sstevel * we can send down the DL_* related IOCTLs (e.g. DL_IOC_HDR). If we 473103831d35Sstevel * try and send them downsteam w/o waiting, the ioctl's get processed before 473203831d35Sstevel * the ATTACH_REQ and they are rejected. TBD - could just do the lower 473303831d35Sstevel * dlpi state change in lock step. TBD 473403831d35Sstevel */ 473503831d35Sstevel static int 473603831d35Sstevel man_dlioc_replay(man_dest_t *mdp) 473703831d35Sstevel { 473803831d35Sstevel mblk_t *rmp; 473903831d35Sstevel int status = 1; 474003831d35Sstevel 474103831d35Sstevel if (mdp->md_msp->ms_dlioc_mp == NULL) 474203831d35Sstevel goto exit; 474303831d35Sstevel 474403831d35Sstevel rmp = man_dup_mplist(mdp->md_msp->ms_dlioc_mp); 474503831d35Sstevel if (rmp == NULL) { 474603831d35Sstevel status = 0; 474703831d35Sstevel goto exit; 474803831d35Sstevel } 474903831d35Sstevel 475003831d35Sstevel man_dlpi_replay(mdp, rmp); 475103831d35Sstevel exit: 475203831d35Sstevel return (status); 475303831d35Sstevel } 475403831d35Sstevel 475503831d35Sstevel static mblk_t * 475603831d35Sstevel man_alloc_ubreq_dreq() 475703831d35Sstevel { 475803831d35Sstevel mblk_t *dreq; 475903831d35Sstevel mblk_t *ubreq = NULL; 476003831d35Sstevel union DL_primitives *dlp; 476103831d35Sstevel 476203831d35Sstevel dreq = allocb(DL_DETACH_REQ_SIZE, BPRI_MED); 476303831d35Sstevel if (dreq == NULL) 476403831d35Sstevel goto exit; 476503831d35Sstevel 476603831d35Sstevel dreq->b_datap->db_type = M_PROTO; 476703831d35Sstevel dlp = (union DL_primitives *)dreq->b_rptr; 476803831d35Sstevel dlp->dl_primitive = DL_DETACH_REQ; 476903831d35Sstevel dreq->b_wptr += DL_DETACH_REQ_SIZE; 477003831d35Sstevel 477103831d35Sstevel ubreq = allocb(DL_UNBIND_REQ_SIZE, BPRI_MED); 477203831d35Sstevel if (ubreq == NULL) { 477303831d35Sstevel freemsg(dreq); 477403831d35Sstevel goto exit; 477503831d35Sstevel } 477603831d35Sstevel 477703831d35Sstevel ubreq->b_datap->db_type = M_PROTO; 477803831d35Sstevel dlp = (union DL_primitives *)ubreq->b_rptr; 477903831d35Sstevel dlp->dl_primitive = DL_UNBIND_REQ; 478003831d35Sstevel ubreq->b_wptr += DL_UNBIND_REQ_SIZE; 478103831d35Sstevel 478203831d35Sstevel ubreq->b_next = dreq; 478303831d35Sstevel 478403831d35Sstevel exit: 478503831d35Sstevel 478603831d35Sstevel return (ubreq); 478703831d35Sstevel } 478803831d35Sstevel 478903831d35Sstevel static mblk_t * 479003831d35Sstevel man_dup_mplist(mblk_t *mp) 479103831d35Sstevel { 479203831d35Sstevel mblk_t *listp = NULL; 479303831d35Sstevel mblk_t *tailp = NULL; 479403831d35Sstevel 479503831d35Sstevel for (; mp != NULL; mp = mp->b_next) { 479603831d35Sstevel 479703831d35Sstevel mblk_t *nmp; 479803831d35Sstevel mblk_t *prev; 479903831d35Sstevel mblk_t *next; 480003831d35Sstevel 480103831d35Sstevel prev = mp->b_prev; 480203831d35Sstevel next = mp->b_next; 480303831d35Sstevel mp->b_prev = mp->b_next = NULL; 480403831d35Sstevel 480503831d35Sstevel nmp = copymsg(mp); 480603831d35Sstevel 480703831d35Sstevel mp->b_prev = prev; 480803831d35Sstevel mp->b_next = next; 480903831d35Sstevel 481003831d35Sstevel if (nmp == NULL) 481103831d35Sstevel goto nomem; 481203831d35Sstevel 481303831d35Sstevel if (listp == NULL) { 481403831d35Sstevel listp = tailp = nmp; 481503831d35Sstevel } else { 481603831d35Sstevel tailp->b_next = nmp; 481703831d35Sstevel tailp = nmp; 481803831d35Sstevel } 481903831d35Sstevel } 482003831d35Sstevel 482103831d35Sstevel return (listp); 482203831d35Sstevel nomem: 482303831d35Sstevel 482403831d35Sstevel while (listp) { 482503831d35Sstevel mp = listp; 482603831d35Sstevel listp = mp->b_next; 482703831d35Sstevel mp->b_next = mp->b_prev = NULL; 482803831d35Sstevel freemsg(mp); 482903831d35Sstevel } 483003831d35Sstevel 483103831d35Sstevel return (NULL); 483203831d35Sstevel 483303831d35Sstevel } 483403831d35Sstevel 483503831d35Sstevel static mblk_t * 483603831d35Sstevel man_alloc_physreq_mp(eaddr_t *man_eap) 483703831d35Sstevel { 483803831d35Sstevel 483903831d35Sstevel mblk_t *mp; 484003831d35Sstevel union DL_primitives *dlp; 484103831d35Sstevel t_uscalar_t off; 484203831d35Sstevel eaddr_t *eap; 484303831d35Sstevel 484403831d35Sstevel mp = allocb(DL_SET_PHYS_ADDR_REQ_SIZE + ETHERADDRL, BPRI_MED); 484503831d35Sstevel if (mp == NULL) 484603831d35Sstevel goto exit; 484703831d35Sstevel 484803831d35Sstevel mp->b_datap->db_type = M_PROTO; 484903831d35Sstevel dlp = (union DL_primitives *)mp->b_wptr; 485003831d35Sstevel dlp->set_physaddr_req.dl_primitive = DL_SET_PHYS_ADDR_REQ; 485103831d35Sstevel dlp->set_physaddr_req.dl_addr_length = ETHERADDRL; 485203831d35Sstevel off = DL_SET_PHYS_ADDR_REQ_SIZE; 485303831d35Sstevel dlp->set_physaddr_req.dl_addr_offset = off; 485403831d35Sstevel mp->b_wptr += DL_SET_PHYS_ADDR_REQ_SIZE + ETHERADDRL; 485503831d35Sstevel 485603831d35Sstevel eap = (eaddr_t *)(mp->b_rptr + off); 485703831d35Sstevel ether_copy(man_eap, eap); 485803831d35Sstevel 485903831d35Sstevel exit: 486003831d35Sstevel MAN_DBG(MAN_DLPI, ("man_alloc_physreq: physaddr %s\n", 486103831d35Sstevel ether_sprintf(eap))); 486203831d35Sstevel 486303831d35Sstevel return (mp); 486403831d35Sstevel } 486503831d35Sstevel 486603831d35Sstevel /* 486703831d35Sstevel * A new path in a pathgroup has become active for the first time. Setup 486803831d35Sstevel * the lower destinations in prepartion for man_pg_activate to call 486903831d35Sstevel * man_autoswitch. 487003831d35Sstevel */ 487103831d35Sstevel static void 487203831d35Sstevel man_add_dests(man_pg_t *mpg) 487303831d35Sstevel { 487403831d35Sstevel manstr_t *msp; 487503831d35Sstevel man_dest_t *mdp; 487603831d35Sstevel 487703831d35Sstevel for (msp = man_strup; msp != NULL; msp = msp->ms_next) { 487803831d35Sstevel 487903831d35Sstevel if (!man_str_uses_pg(msp, mpg)) 488003831d35Sstevel continue; 488103831d35Sstevel 488203831d35Sstevel mdp = &msp->ms_dests[mpg->mpg_pg_id]; 488303831d35Sstevel 488403831d35Sstevel /* 488503831d35Sstevel * TBD - Take out 488603831d35Sstevel * ASSERT(mdp->md_device.mdev_state == MDEV_UNASSIGNED); 488703831d35Sstevel * ASSERT(mdp->md_state == MAN_DSTATE_NOTPRESENT); 488803831d35Sstevel */ 488903831d35Sstevel if (mdp->md_device.mdev_state != MDEV_UNASSIGNED) { 489003831d35Sstevel cmn_err(CE_NOTE, "man_add_dests mdev !unassigned"); 489103831d35Sstevel MAN_DBGCALL(MAN_PATH, man_print_mdp(mdp)); 489203831d35Sstevel } 489303831d35Sstevel 489403831d35Sstevel man_start_dest(mdp, msp, mpg); 489503831d35Sstevel } 489603831d35Sstevel 489703831d35Sstevel } 489803831d35Sstevel 489903831d35Sstevel static int 490003831d35Sstevel man_remove_dests(man_pg_t *mpg) 490103831d35Sstevel { 490203831d35Sstevel manstr_t *msp; 490303831d35Sstevel int close_cnt = 0; 490403831d35Sstevel man_dest_t *cdp; 490503831d35Sstevel man_dest_t *mdp; 490603831d35Sstevel man_dest_t *tdp; 490703831d35Sstevel man_work_t *wp; 490803831d35Sstevel mblk_t *mp; 490903831d35Sstevel int status = 0; 491003831d35Sstevel 491103831d35Sstevel wp = man_work_alloc(MAN_WORK_CLOSE, KM_NOSLEEP); 491203831d35Sstevel if (wp == NULL) { 491303831d35Sstevel status = ENOMEM; 491403831d35Sstevel goto exit; 491503831d35Sstevel } 491603831d35Sstevel 491703831d35Sstevel /* 491803831d35Sstevel * Count up number of destinations we need to close. 491903831d35Sstevel */ 492003831d35Sstevel for (msp = man_strup; msp != NULL; msp = msp->ms_next) { 492103831d35Sstevel if (!man_str_uses_pg(msp, mpg)) 492203831d35Sstevel continue; 492303831d35Sstevel 492403831d35Sstevel close_cnt++; 492503831d35Sstevel } 492603831d35Sstevel 492703831d35Sstevel if (close_cnt == 0) 492803831d35Sstevel goto exit; 492903831d35Sstevel 493003831d35Sstevel cdp = man_kzalloc(sizeof (man_dest_t) * close_cnt, KM_NOSLEEP); 493103831d35Sstevel if (cdp == NULL) { 493203831d35Sstevel status = ENOMEM; 493303831d35Sstevel man_work_free(wp); 493403831d35Sstevel goto exit; 493503831d35Sstevel } 493603831d35Sstevel 493703831d35Sstevel tdp = cdp; 493803831d35Sstevel for (msp = man_strup; msp != NULL; msp = msp->ms_next) { 493903831d35Sstevel if (!man_str_uses_pg(msp, mpg)) 494003831d35Sstevel continue; 494103831d35Sstevel 494203831d35Sstevel mdp = &msp->ms_dests[mpg->mpg_pg_id]; 494303831d35Sstevel 494403831d35Sstevel mdp->md_state |= MAN_DSTATE_CLOSING; 494503831d35Sstevel mdp->md_device.mdev_state = MDEV_UNASSIGNED; 494603831d35Sstevel mdp->md_msp = NULL; 494703831d35Sstevel mdp->md_rq = NULL; 494803831d35Sstevel 494903831d35Sstevel /* 495003831d35Sstevel * Clean up optimized destination pointer if we are 495103831d35Sstevel * closing it. 495203831d35Sstevel */ 495303831d35Sstevel man_set_optimized_dest(msp); 495403831d35Sstevel 495503831d35Sstevel if (mdp->md_lc_timer_id != 0) { 495603831d35Sstevel (void) quntimeout(man_ctl_wq, mdp->md_lc_timer_id); 495703831d35Sstevel mdp->md_lc_timer_id = 0; 495803831d35Sstevel } 495903831d35Sstevel if (mdp->md_bc_id != 0) { 496003831d35Sstevel qunbufcall(man_ctl_wq, mdp->md_bc_id); 496103831d35Sstevel mdp->md_bc_id = 0; 496203831d35Sstevel } 496303831d35Sstevel 496403831d35Sstevel mutex_enter(&mdp->md_lock); 496503831d35Sstevel while ((mp = mdp->md_dmp_head) != NULL) { 496603831d35Sstevel mdp->md_dmp_head = mp->b_next; 496703831d35Sstevel mp->b_next = NULL; 496803831d35Sstevel freemsg(mp); 496903831d35Sstevel } 497003831d35Sstevel mdp->md_dmp_count = 0; 497103831d35Sstevel mdp->md_dmp_tail = NULL; 497203831d35Sstevel mutex_exit(&mdp->md_lock); 497303831d35Sstevel 497403831d35Sstevel *tdp++ = *mdp; 497503831d35Sstevel 497603831d35Sstevel mdp->md_state = MAN_DSTATE_NOTPRESENT; 497703831d35Sstevel mdp->md_muxid = -1; 497803831d35Sstevel } 497903831d35Sstevel 498003831d35Sstevel wp->mw_arg.a_mdp = cdp; 498103831d35Sstevel wp->mw_arg.a_ndests = close_cnt; 498203831d35Sstevel man_work_add(man_bwork_q, wp); 498303831d35Sstevel 498403831d35Sstevel exit: 498503831d35Sstevel return (status); 498603831d35Sstevel 498703831d35Sstevel } 498803831d35Sstevel 498903831d35Sstevel /* 499003831d35Sstevel * Returns TRUE if stream uses pathgroup, FALSE otherwise. 499103831d35Sstevel */ 499203831d35Sstevel static int 499303831d35Sstevel man_str_uses_pg(manstr_t *msp, man_pg_t *mpg) 499403831d35Sstevel { 499503831d35Sstevel int status; 499603831d35Sstevel 499703831d35Sstevel status = ((msp->ms_flags & MAN_SFLAG_CONTROL) || 499803831d35Sstevel (msp->ms_dests == NULL) || 499903831d35Sstevel (msp->ms_manp == NULL) || 500003831d35Sstevel (msp->ms_manp->man_meta_ppa != mpg->mpg_man_ppa)); 500103831d35Sstevel 500203831d35Sstevel return (!status); 500303831d35Sstevel } 500403831d35Sstevel 500503831d35Sstevel static int 500603831d35Sstevel man_gettimer(int timer, man_dest_t *mdp) 500703831d35Sstevel { 500803831d35Sstevel 500903831d35Sstevel int attached = TRUE; 501003831d35Sstevel int time = 0; 501103831d35Sstevel 501203831d35Sstevel if (mdp == NULL || mdp->md_msp == NULL || mdp->md_msp->ms_manp == NULL) 501303831d35Sstevel attached = FALSE; 501403831d35Sstevel 501503831d35Sstevel switch (timer) { 501603831d35Sstevel case MAN_TIMER_INIT: 501703831d35Sstevel if (attached) 501803831d35Sstevel time = mdp->md_msp->ms_manp->man_init_time; 501903831d35Sstevel else 502003831d35Sstevel time = MAN_INIT_TIME; 502103831d35Sstevel break; 502203831d35Sstevel 502303831d35Sstevel case MAN_TIMER_LINKCHECK: 502403831d35Sstevel if (attached) { 502503831d35Sstevel if (mdp->md_linkstate == MAN_LINKSTALE) 502603831d35Sstevel time = mdp->md_msp->ms_manp->man_linkstale_time; 502703831d35Sstevel else 502803831d35Sstevel time = mdp->md_msp->ms_manp->man_linkcheck_time; 502903831d35Sstevel } else 503003831d35Sstevel time = MAN_LINKCHECK_TIME; 503103831d35Sstevel break; 503203831d35Sstevel 503303831d35Sstevel case MAN_TIMER_DLPIRESET: 503403831d35Sstevel if (attached) 503503831d35Sstevel time = mdp->md_msp->ms_manp->man_dlpireset_time; 503603831d35Sstevel else 503703831d35Sstevel time = MAN_DLPIRESET_TIME; 503803831d35Sstevel break; 503903831d35Sstevel 504003831d35Sstevel default: 504103831d35Sstevel MAN_DBG(MAN_LINK, ("man_gettimer: unknown timer %d", timer)); 504203831d35Sstevel time = MAN_LINKCHECK_TIME; 504303831d35Sstevel break; 504403831d35Sstevel } 504503831d35Sstevel 504603831d35Sstevel return (drv_usectohz(time)); 504703831d35Sstevel } 504803831d35Sstevel 504903831d35Sstevel /* 505003831d35Sstevel * Check the links for each active destination. Called inside inner 505103831d35Sstevel * perimeter via qtimeout. This timer only runs on the domain side of the 505203831d35Sstevel * driver. It should never run on the SC side. 505303831d35Sstevel * 505403831d35Sstevel * On a MAN_LINKGOOD link, we check/probe the link health every 505503831d35Sstevel * MAN_LINKCHECK_TIME seconds. If the link goes MAN_LINKSTALE, the we probe 505603831d35Sstevel * the link every MAN_LINKSTALE_TIME seconds, and fail the link after probing 505703831d35Sstevel * the link MAN_LINKSTALE_RETRIES times. 505803831d35Sstevel * The man_lock is held to synchronize access pathgroup list(man_pg). 505903831d35Sstevel */ 506003831d35Sstevel void 506103831d35Sstevel man_linkcheck_timer(void *argp) 506203831d35Sstevel { 506303831d35Sstevel man_dest_t *mdp = (man_dest_t *)argp; 506403831d35Sstevel int restart_timer = TRUE; 506503831d35Sstevel int send_ping = TRUE; 506603831d35Sstevel int newstate; 506703831d35Sstevel int oldstate; 506803831d35Sstevel man_pg_t *mpg; 506903831d35Sstevel man_path_t *mp; 507003831d35Sstevel 507103831d35Sstevel MAN_DBG(MAN_LINK, ("man_linkcheck_timer: mdp")); 507203831d35Sstevel MAN_DBGCALL(MAN_LINK, man_print_mdp(mdp)); 507303831d35Sstevel 507403831d35Sstevel /* 507503831d35Sstevel * Clear timeout id and check if someones waiting on us to 507603831d35Sstevel * complete a close. 507703831d35Sstevel */ 507803831d35Sstevel mdp->md_lc_timer_id = 0; 507903831d35Sstevel 508003831d35Sstevel if (mdp->md_state == MAN_DSTATE_NOTPRESENT || 508103831d35Sstevel mdp->md_state & MAN_DSTATE_BUSY) { 508203831d35Sstevel 508303831d35Sstevel MAN_DBG(MAN_LINK, ("man_linkcheck_timer: not ready mdp")); 508403831d35Sstevel MAN_DBGCALL(MAN_LINK, man_print_mdp(mdp)); 508503831d35Sstevel goto exit; 508603831d35Sstevel } 508703831d35Sstevel 508803831d35Sstevel mutex_enter(&man_lock); 508903831d35Sstevel /* 509003831d35Sstevel * If the lower stream needs initializing, just go straight to 509103831d35Sstevel * switch code. As the linkcheck timer is started for all 509203831d35Sstevel * SAPs, do not send ping packets during the initialization. 509303831d35Sstevel */ 509403831d35Sstevel if (mdp->md_state == MAN_DSTATE_INITIALIZING) { 509503831d35Sstevel send_ping = FALSE; 509603831d35Sstevel goto do_switch; 509703831d35Sstevel } 509803831d35Sstevel 509903831d35Sstevel newstate = oldstate = mdp->md_linkstate; 510003831d35Sstevel 510103831d35Sstevel if (!man_needs_linkcheck(mdp)) { 510203831d35Sstevel cmn_err(CE_NOTE, 510303831d35Sstevel "man_linkcheck_timer: unneeded linkcheck on mdp(0x%p)", 510403831d35Sstevel (void *)mdp); 510503831d35Sstevel mutex_exit(&man_lock); 510603831d35Sstevel return; 510703831d35Sstevel } 510803831d35Sstevel 510903831d35Sstevel /* 511003831d35Sstevel * The above call to man_needs_linkcheck() validates 511103831d35Sstevel * mdp->md_msp and mdp->md_msp->ms_manp pointers. 511203831d35Sstevel */ 511303831d35Sstevel mpg = man_find_pg_by_id(mdp->md_msp->ms_manp->man_pg, mdp->md_pg_id); 511403831d35Sstevel ASSERT(mpg != NULL); 511503831d35Sstevel mp = man_find_path_by_ppa(mpg->mpg_pathp, mdp->md_device.mdev_ppa); 511603831d35Sstevel ASSERT(mp != NULL); 511703831d35Sstevel 511803831d35Sstevel /* 511903831d35Sstevel * This is the most common case, when traffic is flowing. 512003831d35Sstevel */ 512103831d35Sstevel if (mdp->md_rcvcnt != mdp->md_lastrcvcnt) { 512203831d35Sstevel 512303831d35Sstevel newstate = MAN_LINKGOOD; 512403831d35Sstevel mdp->md_lastrcvcnt = mdp->md_rcvcnt; 512503831d35Sstevel send_ping = FALSE; 512603831d35Sstevel 512703831d35Sstevel /* 512803831d35Sstevel * Clear the FAILED flag and update lru. 512903831d35Sstevel */ 513003831d35Sstevel mp->mp_device.mdev_state &= ~MDEV_FAILED; 513103831d35Sstevel (void) drv_getparm(TIME, &mp->mp_lru); 513203831d35Sstevel 513303831d35Sstevel if (mdp->md_link_updown_msg == MAN_LINK_DOWN_MSG) { 513403831d35Sstevel man_t *manp = mdp->md_msp->ms_manp; 513503831d35Sstevel 513603831d35Sstevel cmn_err(CE_NOTE, "%s%d Link up", 513703831d35Sstevel ddi_major_to_name(manp->man_meta_major), 513803831d35Sstevel manp->man_meta_ppa); 513903831d35Sstevel 514003831d35Sstevel mdp->md_link_updown_msg = MAN_LINK_UP_MSG; 514103831d35Sstevel } 514203831d35Sstevel 514303831d35Sstevel goto done; 514403831d35Sstevel } 514503831d35Sstevel 514603831d35Sstevel /* 514703831d35Sstevel * If we're here, it means we have not seen any traffic 514803831d35Sstevel */ 514903831d35Sstevel switch (oldstate) { 515003831d35Sstevel case MAN_LINKINIT: 515103831d35Sstevel case MAN_LINKGOOD: 515203831d35Sstevel newstate = MAN_LINKSTALE; 515303831d35Sstevel mdp->md_linkstales++; 515403831d35Sstevel mdp->md_linkstale_retries = 515503831d35Sstevel mdp->md_msp->ms_manp->man_linkstale_retries; 515603831d35Sstevel break; 515703831d35Sstevel 515803831d35Sstevel case MAN_LINKSTALE: 515903831d35Sstevel case MAN_LINKFAIL: 516003831d35Sstevel mdp->md_linkstales++; 516103831d35Sstevel mdp->md_linkstale_retries--; 516203831d35Sstevel if (mdp->md_linkstale_retries < 0) { 516303831d35Sstevel newstate = MAN_LINKFAIL; 516403831d35Sstevel mdp->md_linkfails++; 516503831d35Sstevel mdp->md_linkstale_retries = 516603831d35Sstevel mdp->md_msp->ms_manp->man_linkstale_retries; 516703831d35Sstevel /* 516803831d35Sstevel * Mark the destination as FAILED and 516903831d35Sstevel * update lru. 517003831d35Sstevel */ 517103831d35Sstevel if (oldstate != MAN_LINKFAIL) { 517203831d35Sstevel mp->mp_device.mdev_state |= MDEV_FAILED; 517303831d35Sstevel (void) drv_getparm(TIME, &mp->mp_lru); 517403831d35Sstevel } 517503831d35Sstevel } 517603831d35Sstevel break; 517703831d35Sstevel 517803831d35Sstevel default: 517903831d35Sstevel cmn_err(CE_WARN, "man_linkcheck_timer: illegal link" 518003831d35Sstevel " state %d", oldstate); 518103831d35Sstevel break; 518203831d35Sstevel } 518303831d35Sstevel done: 518403831d35Sstevel 518503831d35Sstevel if (oldstate != newstate) { 518603831d35Sstevel 518703831d35Sstevel MAN_DBG(MAN_LINK, ("man_linkcheck_timer" 518803831d35Sstevel " link state %s -> %s", lss[oldstate], 518903831d35Sstevel lss[newstate])); 519003831d35Sstevel 519103831d35Sstevel mdp->md_linkstate = newstate; 519203831d35Sstevel } 519303831d35Sstevel 519403831d35Sstevel /* 519503831d35Sstevel * Do any work required from state transitions above. 519603831d35Sstevel */ 519703831d35Sstevel if (newstate == MAN_LINKFAIL) { 519803831d35Sstevel do_switch: 519903831d35Sstevel if (!man_do_autoswitch(mdp)) { 520003831d35Sstevel /* 520103831d35Sstevel * Stop linkcheck timer until switch completes. 520203831d35Sstevel */ 520303831d35Sstevel restart_timer = FALSE; 520403831d35Sstevel send_ping = FALSE; 520503831d35Sstevel } 520603831d35Sstevel } 520703831d35Sstevel 520803831d35Sstevel mutex_exit(&man_lock); 520903831d35Sstevel if (send_ping) 521003831d35Sstevel man_do_icmp_bcast(mdp, mdp->md_msp->ms_sap); 521103831d35Sstevel 521203831d35Sstevel if (restart_timer) 521303831d35Sstevel mdp->md_lc_timer_id = qtimeout(man_ctl_wq, man_linkcheck_timer, 521403831d35Sstevel (void *)mdp, man_gettimer(MAN_TIMER_LINKCHECK, mdp)); 521503831d35Sstevel 521603831d35Sstevel exit: 521703831d35Sstevel MAN_DBG(MAN_LINK, ("man_linkcheck_timer: returns")); 521803831d35Sstevel 521903831d35Sstevel } 522003831d35Sstevel 522103831d35Sstevel /* 522203831d35Sstevel * Handle linkcheck initiated autoswitching. 522303831d35Sstevel * Called with man_lock held. 522403831d35Sstevel */ 522503831d35Sstevel static int 522603831d35Sstevel man_do_autoswitch(man_dest_t *mdp) 522703831d35Sstevel { 522803831d35Sstevel man_pg_t *mpg; 522903831d35Sstevel man_path_t *ap; 523003831d35Sstevel int status = 0; 523103831d35Sstevel 523203831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 523303831d35Sstevel /* 523403831d35Sstevel * Set flags and refcnt. Cleared in man_iswitch when SWITCH completes. 523503831d35Sstevel */ 523603831d35Sstevel mdp->md_msp->ms_manp->man_refcnt++; 523703831d35Sstevel 523803831d35Sstevel mpg = man_find_pg_by_id(mdp->md_msp->ms_manp->man_pg, mdp->md_pg_id); 523903831d35Sstevel ASSERT(mpg); 524003831d35Sstevel 524103831d35Sstevel if (mpg->mpg_flags & MAN_PG_SWITCHING) 524203831d35Sstevel return (EBUSY); 524303831d35Sstevel 524403831d35Sstevel mpg->mpg_flags |= MAN_PG_SWITCHING; 524503831d35Sstevel 524603831d35Sstevel if (mdp->md_state == MAN_DSTATE_INITIALIZING) { 524703831d35Sstevel /* 524803831d35Sstevel * We're initializing, ask for a switch to our currently 524903831d35Sstevel * active device. 525003831d35Sstevel */ 525103831d35Sstevel status = man_autoswitch(mpg, &mdp->md_device, NULL); 525203831d35Sstevel } else { 525303831d35Sstevel 525403831d35Sstevel if (mdp->md_msp != NULL && mdp->md_msp->ms_manp != NULL && 525503831d35Sstevel mdp->md_link_updown_msg == MAN_LINK_UP_MSG) { 525603831d35Sstevel 525703831d35Sstevel man_t *manp = mdp->md_msp->ms_manp; 525803831d35Sstevel 525903831d35Sstevel cmn_err(CE_NOTE, "%s%d Link down", 526003831d35Sstevel ddi_major_to_name(manp->man_meta_major), 526103831d35Sstevel manp->man_meta_ppa); 526203831d35Sstevel } 526303831d35Sstevel mdp->md_link_updown_msg = MAN_LINK_DOWN_MSG; 526403831d35Sstevel 526503831d35Sstevel MAN_DBG(MAN_LINK, ("man_linkcheck_timer: link failure on %s%d", 526603831d35Sstevel ddi_major_to_name(mdp->md_device.mdev_major), 526703831d35Sstevel mdp->md_device.mdev_ppa)); 526803831d35Sstevel 526903831d35Sstevel ap = man_find_alternate_path(mpg->mpg_pathp); 527003831d35Sstevel 527103831d35Sstevel if (ap == NULL) { 527203831d35Sstevel status = ENODEV; 527303831d35Sstevel goto exit; 527403831d35Sstevel } 527503831d35Sstevel status = man_autoswitch(mpg, &ap->mp_device, NULL); 527603831d35Sstevel } 527703831d35Sstevel exit: 527803831d35Sstevel if (status != 0) { 527903831d35Sstevel /* 528003831d35Sstevel * man_iswitch not going to run, clean up. 528103831d35Sstevel */ 528203831d35Sstevel mpg->mpg_flags &= ~MAN_PG_SWITCHING; 528303831d35Sstevel mdp->md_msp->ms_manp->man_refcnt--; 528403831d35Sstevel } 528503831d35Sstevel 528603831d35Sstevel return (status); 528703831d35Sstevel } 528803831d35Sstevel 528903831d35Sstevel /* 529003831d35Sstevel * Gather up all lower multiplexor streams that have this link open and 529103831d35Sstevel * try to switch them. Called from inner perimeter and holding man_lock. 529203831d35Sstevel * 529303831d35Sstevel * pg_id - Pathgroup to do switch for. 529403831d35Sstevel * st_devp - New device to switch to. 529503831d35Sstevel * wait_for_switch - whether or not to qwait for completion. 529603831d35Sstevel */ 529703831d35Sstevel static int 529803831d35Sstevel man_autoswitch(man_pg_t *mpg, man_dev_t *st_devp, man_work_t *waiter_wp) 529903831d35Sstevel { 530003831d35Sstevel man_work_t *wp; 530103831d35Sstevel int sdp_cnt = 0; 530203831d35Sstevel man_dest_t *sdp; 530303831d35Sstevel int status = 0; 530403831d35Sstevel 530503831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 530603831d35Sstevel if (waiter_wp == NULL) { 530703831d35Sstevel wp = man_work_alloc(MAN_WORK_SWITCH, KM_NOSLEEP); 530803831d35Sstevel if (wp == NULL) { 530903831d35Sstevel status = ENOMEM; 531003831d35Sstevel goto exit; 531103831d35Sstevel } 531203831d35Sstevel } else { 531303831d35Sstevel ASSERT(waiter_wp->mw_type == MAN_WORK_SWITCH); 531403831d35Sstevel wp = waiter_wp; 531503831d35Sstevel } 531603831d35Sstevel 531703831d35Sstevel /* 531803831d35Sstevel * Set dests as PLUMBING, cancel timers and return array of dests 531903831d35Sstevel * that need a switch. 532003831d35Sstevel */ 532103831d35Sstevel status = man_prep_dests_for_switch(mpg, &sdp, &sdp_cnt); 532203831d35Sstevel if (status) { 532303831d35Sstevel if (waiter_wp == NULL) 532403831d35Sstevel man_work_free(wp); 532503831d35Sstevel goto exit; 532603831d35Sstevel } 532703831d35Sstevel 532803831d35Sstevel /* 532903831d35Sstevel * If no streams are active, there are no streams to switch. 533003831d35Sstevel * Return ENODEV (see man_pg_activate). 533103831d35Sstevel */ 533203831d35Sstevel if (sdp_cnt == 0) { 533303831d35Sstevel if (waiter_wp == NULL) 533403831d35Sstevel man_work_free(wp); 533503831d35Sstevel status = ENODEV; 533603831d35Sstevel goto exit; 533703831d35Sstevel } 533803831d35Sstevel 533903831d35Sstevel /* 534003831d35Sstevel * Ask the bgthread to switch. See man_bwork. 534103831d35Sstevel */ 534203831d35Sstevel wp->mw_arg.a_sf_dev = sdp->md_device; 534303831d35Sstevel wp->mw_arg.a_st_dev = *st_devp; 534403831d35Sstevel wp->mw_arg.a_pg_id = mpg->mpg_pg_id; 534503831d35Sstevel wp->mw_arg.a_man_ppa = mpg->mpg_man_ppa; 534603831d35Sstevel 534703831d35Sstevel wp->mw_arg.a_mdp = sdp; 534803831d35Sstevel wp->mw_arg.a_ndests = sdp_cnt; 534903831d35Sstevel man_work_add(man_bwork_q, wp); 535003831d35Sstevel 535103831d35Sstevel exit: 535203831d35Sstevel 535303831d35Sstevel return (status); 535403831d35Sstevel } 535503831d35Sstevel 535603831d35Sstevel /* 535703831d35Sstevel * If an alternate path exists for pathgroup, arrange for switch to 535803831d35Sstevel * happen. Note that we need to switch each of msp->dests[pg_id], for 535903831d35Sstevel * all on man_strup. We must: 536003831d35Sstevel * 536103831d35Sstevel * Cancel any timers 536203831d35Sstevel * Mark dests as PLUMBING 536303831d35Sstevel * Submit switch request to man_bwork_q-> 536403831d35Sstevel */ 536503831d35Sstevel static int 536603831d35Sstevel man_prep_dests_for_switch(man_pg_t *mpg, man_dest_t **mdpp, int *cntp) 536703831d35Sstevel { 536803831d35Sstevel manstr_t *msp; 536903831d35Sstevel man_dest_t *mdp; 537003831d35Sstevel int sdp_cnt = 0; 537103831d35Sstevel man_dest_t *sdp = NULL; 537203831d35Sstevel man_dest_t *tdp; 537303831d35Sstevel int status = 0; 537403831d35Sstevel 537503831d35Sstevel MAN_DBG(MAN_SWITCH, ("man_prep_dests_for_switch: pg_id %d", 537603831d35Sstevel mpg->mpg_pg_id)); 537703831d35Sstevel 537803831d35Sstevel /* 537903831d35Sstevel * Count up number of streams, there is one destination that needs 538003831d35Sstevel * switching per stream. 538103831d35Sstevel */ 538203831d35Sstevel for (msp = man_strup; msp != NULL; msp = msp->ms_next) { 538303831d35Sstevel if (man_str_uses_pg(msp, mpg)) 538403831d35Sstevel sdp_cnt++; 538503831d35Sstevel } 538603831d35Sstevel 538703831d35Sstevel if (sdp_cnt == 0) 538803831d35Sstevel goto exit; 538903831d35Sstevel 539003831d35Sstevel sdp = man_kzalloc(sizeof (man_dest_t) * sdp_cnt, KM_NOSLEEP); 539103831d35Sstevel if (sdp == NULL) { 539203831d35Sstevel status = ENOMEM; 539303831d35Sstevel goto exit; 539403831d35Sstevel } 539503831d35Sstevel tdp = sdp; 539603831d35Sstevel /* 539703831d35Sstevel * Mark each destination as unusable. 539803831d35Sstevel */ 539903831d35Sstevel for (msp = man_strup; msp != NULL; msp = msp->ms_next) { 540003831d35Sstevel if (man_str_uses_pg(msp, mpg)) { 540103831d35Sstevel 540203831d35Sstevel /* 540303831d35Sstevel * Mark destination as plumbing and store the 540403831d35Sstevel * address of sdp as a way to identify the 540503831d35Sstevel * SWITCH request when it comes back (see man_iswitch). 540603831d35Sstevel */ 540703831d35Sstevel mdp = &msp->ms_dests[mpg->mpg_pg_id]; 540803831d35Sstevel mdp->md_state |= MAN_DSTATE_PLUMBING; 540903831d35Sstevel mdp->md_switch_id = sdp; 541003831d35Sstevel 541103831d35Sstevel /* 541203831d35Sstevel * Copy destination info. 541303831d35Sstevel */ 541403831d35Sstevel bcopy(mdp, tdp, sizeof (man_dest_t)); 541503831d35Sstevel tdp++; 541603831d35Sstevel 541703831d35Sstevel /* 541803831d35Sstevel * Cancel timers. 541903831d35Sstevel */ 542003831d35Sstevel if (mdp->md_lc_timer_id) { 542103831d35Sstevel (void) quntimeout(man_ctl_wq, 542203831d35Sstevel mdp->md_lc_timer_id); 542303831d35Sstevel mdp->md_lc_timer_id = 0; 542403831d35Sstevel } 542503831d35Sstevel if (mdp->md_bc_id) { 542603831d35Sstevel qunbufcall(man_ctl_wq, mdp->md_bc_id); 542703831d35Sstevel mdp->md_bc_id = 0; 542803831d35Sstevel } 542903831d35Sstevel } 543003831d35Sstevel } 543103831d35Sstevel 543203831d35Sstevel *mdpp = sdp; 543303831d35Sstevel *cntp = sdp_cnt; 543403831d35Sstevel status = 0; 543503831d35Sstevel exit: 543603831d35Sstevel 543703831d35Sstevel MAN_DBG(MAN_SWITCH, ("man_prep_dests_for_switch: returns %d" 543803831d35Sstevel " sdp(0x%p) sdp_cnt(%d)", status, (void *)sdp, sdp_cnt)); 543903831d35Sstevel 544003831d35Sstevel return (status); 544103831d35Sstevel 544203831d35Sstevel } 544303831d35Sstevel 544403831d35Sstevel /* 544503831d35Sstevel * The code below generates an ICMP echo packet and sends it to the 544603831d35Sstevel * broadcast address in the hopes that the other end will respond 544703831d35Sstevel * and the man_linkcheck_timer logic will see the traffic. 544803831d35Sstevel * 544903831d35Sstevel * This assumes ethernet-like media. 545003831d35Sstevel */ 545103831d35Sstevel /* 545203831d35Sstevel * Generate an ICMP packet. Called exclusive inner perimeter. 545303831d35Sstevel * 545403831d35Sstevel * mdp - destination to send packet to. 545503831d35Sstevel * sap - either ETHERTYPE_ARP or ETHERTYPE_IPV6 545603831d35Sstevel */ 545703831d35Sstevel static void 545803831d35Sstevel man_do_icmp_bcast(man_dest_t *mdp, t_uscalar_t sap) 545903831d35Sstevel { 546003831d35Sstevel mblk_t *mp = NULL; 546103831d35Sstevel 546203831d35Sstevel /* TBD - merge pinger and this routine. */ 546303831d35Sstevel 546403831d35Sstevel ASSERT(sap == ETHERTYPE_IPV6 || sap == ETHERTYPE_IP); 546503831d35Sstevel 546603831d35Sstevel if (sap == ETHERTYPE_IPV6) { 546703831d35Sstevel mdp->md_icmpv6probes++; 546803831d35Sstevel } else { 546903831d35Sstevel mdp->md_icmpv4probes++; 547003831d35Sstevel } 547103831d35Sstevel /* 547203831d35Sstevel * Send the ICMP message 547303831d35Sstevel */ 547403831d35Sstevel mp = man_pinger(sap); 547503831d35Sstevel 547603831d35Sstevel MAN_DBG(MAN_LINK, ("man_do_icmp_bcast: sap=0x%x mp=0x%p", 547703831d35Sstevel sap, (void *)mp)); 547803831d35Sstevel if (mp == NULL) 547903831d35Sstevel return; 548003831d35Sstevel 548103831d35Sstevel /* 548203831d35Sstevel * Send it out. 548303831d35Sstevel */ 548403831d35Sstevel if (man_start_lower(mdp, mp, NULL, MAN_LOWER)) { 548503831d35Sstevel 548603831d35Sstevel MAN_DBG(MAN_LINK, ("man_do_icmp_broadcast: xmit failed")); 548703831d35Sstevel 548803831d35Sstevel freemsg(mp); 548903831d35Sstevel } 549003831d35Sstevel 549103831d35Sstevel } 549203831d35Sstevel 549303831d35Sstevel static mblk_t * 549403831d35Sstevel man_pinger(t_uscalar_t sap) 549503831d35Sstevel { 549603831d35Sstevel mblk_t *mp = NULL; 549703831d35Sstevel man_dladdr_t dlsap; 549803831d35Sstevel icmph_t *icmph; 549903831d35Sstevel int ipver; 550003831d35Sstevel ipha_t *ipha; 550103831d35Sstevel ip6_t *ip6h; 550203831d35Sstevel int iph_hdr_len; 550303831d35Sstevel int datalen = 64; 550403831d35Sstevel uchar_t *datap; 550503831d35Sstevel uint16_t size; 550603831d35Sstevel uchar_t i; 550703831d35Sstevel 550803831d35Sstevel dlsap.dl_sap = htons(sap); 550903831d35Sstevel bcopy(ðerbroadcast, &dlsap.dl_phys, sizeof (dlsap.dl_phys)); 551003831d35Sstevel 551103831d35Sstevel if (sap == ETHERTYPE_IPV6) { 551203831d35Sstevel ipver = IPV6_VERSION; 551303831d35Sstevel iph_hdr_len = sizeof (ip6_t); 551403831d35Sstevel size = ICMP6_MINLEN; 551503831d35Sstevel } else { 551603831d35Sstevel ipver = IPV4_VERSION; 551703831d35Sstevel iph_hdr_len = sizeof (ipha_t); 551803831d35Sstevel size = ICMPH_SIZE; 551903831d35Sstevel } 552003831d35Sstevel size += (uint16_t)iph_hdr_len; 552103831d35Sstevel size += datalen; 552203831d35Sstevel 552303831d35Sstevel mp = man_alloc_udreq(size, &dlsap); 552403831d35Sstevel if (mp == NULL) 552503831d35Sstevel goto exit; 552603831d35Sstevel 552703831d35Sstevel /* 552803831d35Sstevel * fill out the ICMP echo packet headers 552903831d35Sstevel */ 553003831d35Sstevel mp->b_cont->b_wptr += iph_hdr_len; 553103831d35Sstevel if (ipver == IPV4_VERSION) { 553203831d35Sstevel ipha = (ipha_t *)mp->b_cont->b_rptr; 553303831d35Sstevel ipha->ipha_version_and_hdr_length = (IP_VERSION << 4) 553403831d35Sstevel | IP_SIMPLE_HDR_LENGTH_IN_WORDS; 553503831d35Sstevel ipha->ipha_type_of_service = 0; 553603831d35Sstevel ipha->ipha_length = size; 553703831d35Sstevel ipha->ipha_fragment_offset_and_flags = IPH_DF; 553803831d35Sstevel ipha->ipha_ttl = 1; 553903831d35Sstevel ipha->ipha_protocol = IPPROTO_ICMP; 554003831d35Sstevel if (man_is_on_domain) { 554103831d35Sstevel manc_t manc; 554203831d35Sstevel 554303831d35Sstevel if (man_get_iosram(&manc)) { 554403831d35Sstevel freemsg(mp); 554503831d35Sstevel mp = NULL; 554603831d35Sstevel goto exit; 554703831d35Sstevel } 554803831d35Sstevel 554903831d35Sstevel /* 555003831d35Sstevel * Domain generates ping packets for domain to 555103831d35Sstevel * SC network (dman0 <--> scman0). 555203831d35Sstevel */ 555303831d35Sstevel ipha->ipha_dst = manc.manc_sc_ipaddr; 555403831d35Sstevel ipha->ipha_src = manc.manc_dom_ipaddr; 555503831d35Sstevel } else { 555603831d35Sstevel /* 555703831d35Sstevel * Note that ping packets are only generated 555803831d35Sstevel * by the SC across scman1 (SC to SC network). 555903831d35Sstevel */ 556003831d35Sstevel ipha->ipha_dst = man_sc_ipaddrs.ip_other_sc_ipaddr; 556103831d35Sstevel ipha->ipha_src = man_sc_ipaddrs.ip_my_sc_ipaddr; 556203831d35Sstevel } 556303831d35Sstevel 556403831d35Sstevel ipha->ipha_ident = 0; 556503831d35Sstevel 556603831d35Sstevel ipha->ipha_hdr_checksum = 0; 556703831d35Sstevel ipha->ipha_hdr_checksum = IP_CSUM(mp->b_cont, 0, 0); 556803831d35Sstevel 556903831d35Sstevel } else { 557003831d35Sstevel ip6h = (ip6_t *)mp->b_cont->b_rptr; 557103831d35Sstevel /* 557203831d35Sstevel * IP version = 6, priority = 0, flow = 0 557303831d35Sstevel */ 557403831d35Sstevel ip6h->ip6_flow = (IPV6_VERSION << 28); 557503831d35Sstevel ip6h->ip6_plen = 557603831d35Sstevel htons((short)(size - iph_hdr_len)); 557703831d35Sstevel ip6h->ip6_nxt = IPPROTO_ICMPV6; 557803831d35Sstevel ip6h->ip6_hlim = 1; /* stay on link */ 557903831d35Sstevel 558003831d35Sstevel if (man_is_on_domain) { 558103831d35Sstevel manc_t manc; 558203831d35Sstevel 558303831d35Sstevel if (man_get_iosram(&manc)) { 558403831d35Sstevel freemsg(mp); 558503831d35Sstevel mp = NULL; 558603831d35Sstevel goto exit; 558703831d35Sstevel } 558803831d35Sstevel 558903831d35Sstevel /* 559003831d35Sstevel * Domain generates ping packets for domain to 559103831d35Sstevel * SC network (dman0 <--> scman0). 559203831d35Sstevel */ 559303831d35Sstevel ip6h->ip6_src = manc.manc_dom_ipv6addr; 559403831d35Sstevel ip6h->ip6_dst = manc.manc_sc_ipv6addr; 559503831d35Sstevel } else { 559603831d35Sstevel /* 559703831d35Sstevel * Note that ping packets are only generated 559803831d35Sstevel * by the SC across scman1 (SC to SC network). 559903831d35Sstevel */ 560003831d35Sstevel ip6h->ip6_src = man_sc_ip6addrs.ip6_my_sc_ipaddr; 560103831d35Sstevel ip6h->ip6_dst = man_sc_ip6addrs.ip6_other_sc_ipaddr; 560203831d35Sstevel } 560303831d35Sstevel } 560403831d35Sstevel 560503831d35Sstevel /* 560603831d35Sstevel * IPv6 and IP are the same for ICMP as far as I'm concerned. 560703831d35Sstevel */ 560803831d35Sstevel icmph = (icmph_t *)mp->b_cont->b_wptr; 560903831d35Sstevel if (ipver == IPV4_VERSION) { 561003831d35Sstevel mp->b_cont->b_wptr += ICMPH_SIZE; 561103831d35Sstevel icmph->icmph_type = ICMP_ECHO_REQUEST; 561203831d35Sstevel icmph->icmph_code = 0; 561303831d35Sstevel } else { 561403831d35Sstevel mp->b_cont->b_wptr += ICMP6_MINLEN; 561503831d35Sstevel icmph->icmph_type = ICMP6_ECHO_REQUEST; 561603831d35Sstevel icmph->icmph_code = 0; 561703831d35Sstevel } 561803831d35Sstevel 561903831d35Sstevel datap = mp->b_cont->b_wptr; 562003831d35Sstevel mp->b_cont->b_wptr += datalen; 562103831d35Sstevel 562203831d35Sstevel for (i = 0; i < datalen; i++) 562303831d35Sstevel *datap++ = i; 562403831d35Sstevel 562503831d35Sstevel if (ipver == IPV4_VERSION) { 562603831d35Sstevel icmph->icmph_checksum = IP_CSUM(mp->b_cont, iph_hdr_len, 0); 562703831d35Sstevel } else { 562803831d35Sstevel uint32_t sum; 562903831d35Sstevel 563003831d35Sstevel sum = htons(IPPROTO_ICMPV6) + ip6h->ip6_plen; 563103831d35Sstevel icmph->icmph_checksum = IP_CSUM(mp->b_cont, iph_hdr_len - 32, 563203831d35Sstevel (sum & 0xffff) + (sum >> 16)); 563303831d35Sstevel } 563403831d35Sstevel 563503831d35Sstevel /* 563603831d35Sstevel * TBD 563703831d35Sstevel * icp->icmp_time = ???; 563803831d35Sstevel */ 563903831d35Sstevel 564003831d35Sstevel exit: 564103831d35Sstevel return (mp); 564203831d35Sstevel } 564303831d35Sstevel 564403831d35Sstevel static mblk_t * 564503831d35Sstevel man_alloc_udreq(int size, man_dladdr_t *dlsap) 564603831d35Sstevel { 564703831d35Sstevel dl_unitdata_req_t *udreq; 564803831d35Sstevel mblk_t *bp; 564903831d35Sstevel mblk_t *mp; 565003831d35Sstevel 565103831d35Sstevel mp = allocb(sizeof (dl_unitdata_req_t) + sizeof (*dlsap), BPRI_MED); 565203831d35Sstevel 565303831d35Sstevel if (mp == NULL) { 565403831d35Sstevel cmn_err(CE_NOTE, "man_preparepkt: allocb failed"); 565503831d35Sstevel return (NULL); 565603831d35Sstevel } 565703831d35Sstevel 565803831d35Sstevel if ((bp = allocb(size, BPRI_MED)) == NULL) { 565903831d35Sstevel freemsg(mp); 566003831d35Sstevel cmn_err(CE_NOTE, "man_preparepkts: allocb failed"); 566103831d35Sstevel return (NULL); 566203831d35Sstevel } 566303831d35Sstevel bzero(bp->b_rptr, size); 566403831d35Sstevel 566503831d35Sstevel mp->b_cont = bp; 566603831d35Sstevel mp->b_datap->db_type = M_PROTO; 566703831d35Sstevel udreq = (dl_unitdata_req_t *)mp->b_wptr; 566803831d35Sstevel mp->b_wptr += sizeof (dl_unitdata_req_t); 566903831d35Sstevel 567003831d35Sstevel /* 567103831d35Sstevel * phys addr first - TBD 567203831d35Sstevel */ 567303831d35Sstevel bcopy((char *)dlsap, mp->b_wptr, sizeof (*dlsap)); 567403831d35Sstevel mp->b_wptr += sizeof (*dlsap); 567503831d35Sstevel 567603831d35Sstevel udreq->dl_primitive = DL_UNITDATA_REQ; 567703831d35Sstevel udreq->dl_dest_addr_length = sizeof (*dlsap); 567803831d35Sstevel udreq->dl_dest_addr_offset = sizeof (*udreq); 567903831d35Sstevel udreq->dl_priority.dl_min = 0; 568003831d35Sstevel udreq->dl_priority.dl_max = 0; 568103831d35Sstevel 568203831d35Sstevel return (mp); 568303831d35Sstevel } 568403831d35Sstevel 568503831d35Sstevel 568603831d35Sstevel /* 568703831d35Sstevel * The routines in this file are executed by the MAN background thread, 568803831d35Sstevel * which executes outside of the STREAMS framework (see man_str.c). It is 568903831d35Sstevel * allowed to do the things required to modify the STREAMS driver (things 569003831d35Sstevel * that are normally done from a user process). These routines do things like 569103831d35Sstevel * open and close drivers, PLINK and PUNLINK streams to/from the multiplexor, 569203831d35Sstevel * etc. 569303831d35Sstevel * 569403831d35Sstevel * The mechanism of communication between the STREAMS portion of the driver 569503831d35Sstevel * and the background thread portion are two work queues, man_bwork_q 569603831d35Sstevel * and man_iwork_q (background work q and streams work q). Work 569703831d35Sstevel * requests are placed on those queues when one half of the driver wants 569803831d35Sstevel * the other half to do some work for it. 569903831d35Sstevel * 570003831d35Sstevel * The MAN background thread executes the man_bwork routine. Its sole 570103831d35Sstevel * job is to process work requests placed on this work q. The MAN upper 570203831d35Sstevel * write service routine is responsible for processing work requests posted 570303831d35Sstevel * to the man_iwork_q-> 570403831d35Sstevel * 570503831d35Sstevel * Both work queues are protected by the global mutex man_lock. The 570603831d35Sstevel * man_bwork is signalged via the condvarman_bwork_q->q_cv. The man_uwsrv 570703831d35Sstevel * routine is signaled by calling qenable (forcing man_uwsrv to run). 570803831d35Sstevel */ 570903831d35Sstevel 571003831d35Sstevel /* 571103831d35Sstevel * man_bwork - Work thread for this device. It is responsible for 571203831d35Sstevel * performing operations which can't occur within the STREAMS framework. 571303831d35Sstevel * 571403831d35Sstevel * Locking: 571503831d35Sstevel * - Called holding no locks 571603831d35Sstevel * - Obtains the global mutex man_lock to remove work from 571703831d35Sstevel * man_bwork_q, and post work to man_iwork_q-> 571803831d35Sstevel * - Note that we do not want to hold any locks when making 571903831d35Sstevel * any ldi_ calls. 572003831d35Sstevel */ 572103831d35Sstevel void 572203831d35Sstevel man_bwork() 572303831d35Sstevel { 572403831d35Sstevel man_work_t *wp; 572503831d35Sstevel int done = 0; 572603831d35Sstevel callb_cpr_t cprinfo; 572703831d35Sstevel int wp_finished; 572803831d35Sstevel 572903831d35Sstevel CALLB_CPR_INIT(&cprinfo, &man_lock, callb_generic_cpr, 573003831d35Sstevel "mn_work_thrd"); 573103831d35Sstevel 573203831d35Sstevel MAN_DBG(MAN_CONFIG, ("man_bwork: enter")); 573303831d35Sstevel 573403831d35Sstevel while (done == 0) { 573503831d35Sstevel 573603831d35Sstevel mutex_enter(&man_lock); 573703831d35Sstevel /* 573803831d35Sstevel * While there is nothing to do, sit in cv_wait. If work 573903831d35Sstevel * request is made, requester will signal. 574003831d35Sstevel */ 574103831d35Sstevel while (man_bwork_q->q_work == NULL) { 574203831d35Sstevel 574303831d35Sstevel CALLB_CPR_SAFE_BEGIN(&cprinfo); 574403831d35Sstevel 574503831d35Sstevel cv_wait(&man_bwork_q->q_cv, &man_lock); 574603831d35Sstevel 574703831d35Sstevel CALLB_CPR_SAFE_END(&cprinfo, &man_lock); 574803831d35Sstevel } 574903831d35Sstevel 575003831d35Sstevel wp = man_bwork_q->q_work; 575103831d35Sstevel man_bwork_q->q_work = wp->mw_next; 575203831d35Sstevel wp->mw_next = NULL; 575303831d35Sstevel mutex_exit(&man_lock); 575403831d35Sstevel 575503831d35Sstevel wp_finished = TRUE; 575603831d35Sstevel 575703831d35Sstevel MAN_DBG(MAN_SWITCH, ("man_bwork: type %s", 575803831d35Sstevel _mw_type[wp->mw_type])); 575903831d35Sstevel 576003831d35Sstevel switch (wp->mw_type) { 576103831d35Sstevel case MAN_WORK_OPEN_CTL: 576203831d35Sstevel wp->mw_status = man_open_ctl(); 576303831d35Sstevel break; 576403831d35Sstevel 576503831d35Sstevel case MAN_WORK_CLOSE_CTL: 576603831d35Sstevel man_close_ctl(); 576703831d35Sstevel break; 576803831d35Sstevel 576903831d35Sstevel case MAN_WORK_CLOSE: 577003831d35Sstevel case MAN_WORK_CLOSE_STREAM: 577103831d35Sstevel man_bclose(&wp->mw_arg); 577203831d35Sstevel break; 577303831d35Sstevel 577403831d35Sstevel case MAN_WORK_SWITCH: 577503831d35Sstevel man_bswitch(&wp->mw_arg, wp); 577603831d35Sstevel wp_finished = FALSE; 577703831d35Sstevel break; 577803831d35Sstevel 577903831d35Sstevel case MAN_WORK_STOP: /* man_bwork_stop() */ 578003831d35Sstevel done = 1; 578103831d35Sstevel mutex_enter(&man_lock); 578203831d35Sstevel CALLB_CPR_EXIT(&cprinfo); /* Unlocks man_lock */ 578303831d35Sstevel break; 578403831d35Sstevel 578503831d35Sstevel default: 578603831d35Sstevel cmn_err(CE_WARN, "man_bwork: " 578703831d35Sstevel "illegal work type(%d)", wp->mw_type); 578803831d35Sstevel break; 578903831d35Sstevel } 579003831d35Sstevel 579103831d35Sstevel mutex_enter(&man_lock); 579203831d35Sstevel 579303831d35Sstevel if (wp_finished) { 579403831d35Sstevel wp->mw_flags |= MAN_WFLAGS_DONE; 579503831d35Sstevel if (wp->mw_flags & MAN_WFLAGS_CVWAITER) 579603831d35Sstevel cv_signal(&wp->mw_cv); 579703831d35Sstevel else if (wp->mw_flags & MAN_WFLAGS_QWAITER) 579803831d35Sstevel qenable(wp->mw_q); 579903831d35Sstevel else 580003831d35Sstevel man_work_free(wp); 580103831d35Sstevel } 580203831d35Sstevel 580303831d35Sstevel mutex_exit(&man_lock); 580403831d35Sstevel } 580503831d35Sstevel 580603831d35Sstevel MAN_DBG(MAN_CONFIG, ("man_bwork: thread_exit")); 580703831d35Sstevel 580803831d35Sstevel mutex_enter(&man_lock); 580903831d35Sstevel man_bwork_id = NULL; 581003831d35Sstevel mutex_exit(&man_lock); 581103831d35Sstevel 581203831d35Sstevel thread_exit(); 581303831d35Sstevel } 581403831d35Sstevel 581503831d35Sstevel /* 581603831d35Sstevel * man_open_ctl - Open the control stream. 581703831d35Sstevel * 581803831d35Sstevel * returns - success - 0 581903831d35Sstevel * - failure - errno code 582003831d35Sstevel * 582103831d35Sstevel * Mutex Locking Notes: 582203831d35Sstevel * We need a way to keep the CLONE_OPEN qwaiters in man_open from 582303831d35Sstevel * checking the man_config variables after the ldi_open call below 582403831d35Sstevel * returns from man_open, leaving the inner perimeter. So, we use the 582503831d35Sstevel * man_lock to synchronize the threads in man_open_ctl and man_open. We 582603831d35Sstevel * hold man_lock across this call into man_open, which in general is a 582703831d35Sstevel * no-no. But, the STREAMs portion of the driver (other than open) 582803831d35Sstevel * doesn't use it. So, if ldi_open gets hijacked to run any part of 582903831d35Sstevel * the MAN streams driver, it wont end up recursively trying to acquire 583003831d35Sstevel * man_lock. Note that the non-CLONE_OPEN portion of man_open doesnt 583103831d35Sstevel * acquire it either, so again no recursive mutex. 583203831d35Sstevel */ 583303831d35Sstevel static int 583403831d35Sstevel man_open_ctl() 583503831d35Sstevel { 583603831d35Sstevel int status = 0; 583703831d35Sstevel ldi_handle_t ctl_lh = NULL; 583803831d35Sstevel ldi_ident_t li = NULL; 583903831d35Sstevel 584003831d35Sstevel MAN_DBG(MAN_CONFIG, ("man_open_ctl: plumbing control stream\n")); 584103831d35Sstevel 584203831d35Sstevel /* 584303831d35Sstevel * Get eri driver loaded and kstats initialized. Is there a better 584403831d35Sstevel * way to do this? - TBD. 584503831d35Sstevel */ 584603831d35Sstevel status = ldi_ident_from_mod(&modlinkage, &li); 584703831d35Sstevel if (status) { 584803831d35Sstevel cmn_err(CE_WARN, 584903831d35Sstevel "man_open_ctl: ident alloc failed, error %d", status); 585003831d35Sstevel goto exit; 585103831d35Sstevel } 585203831d35Sstevel 585303831d35Sstevel status = ldi_open_by_name(ERI_PATH, FREAD | FWRITE | FNOCTTY, 585403831d35Sstevel kcred, &ctl_lh, li); 585503831d35Sstevel if (status) { 585603831d35Sstevel cmn_err(CE_WARN, 585703831d35Sstevel "man_open_ctl: eri open failed, error %d", status); 585803831d35Sstevel ctl_lh = NULL; 585903831d35Sstevel goto exit; 586003831d35Sstevel } 586103831d35Sstevel (void) ldi_close(ctl_lh, NULL, kcred); 586203831d35Sstevel ctl_lh = NULL; 586303831d35Sstevel 586403831d35Sstevel mutex_enter(&man_lock); 586503831d35Sstevel 586603831d35Sstevel if (man_ctl_lh != NULL) { 586703831d35Sstevel mutex_exit(&man_lock); 586803831d35Sstevel goto exit; 586903831d35Sstevel } 587003831d35Sstevel 587103831d35Sstevel ASSERT(man_ctl_wq == NULL); 587203831d35Sstevel mutex_exit(&man_lock); 587303831d35Sstevel 587403831d35Sstevel status = ldi_open_by_name(DMAN_INT_PATH, FREAD | FWRITE | FNOCTTY, 587503831d35Sstevel kcred, &ctl_lh, li); 587603831d35Sstevel if (status) { 587703831d35Sstevel cmn_err(CE_WARN, 587803831d35Sstevel "man_open_ctl: man control dev open failed, " 587903831d35Sstevel "error %d", status); 588003831d35Sstevel goto exit; 588103831d35Sstevel } 588203831d35Sstevel 588303831d35Sstevel /* 588403831d35Sstevel * Update global config state. TBD - dont need lock here, since 588503831d35Sstevel * everyone is stuck in open until we finish. Only other modifier 588603831d35Sstevel * is man_deconfigure via _fini, which returns EBUSY if there is 588703831d35Sstevel * any open streams (other than control). Do need to signal qwaiters 588803831d35Sstevel * on error. 588903831d35Sstevel */ 589003831d35Sstevel mutex_enter(&man_lock); 589103831d35Sstevel ASSERT(man_config_state == MAN_CONFIGURING); 589203831d35Sstevel ASSERT(man_ctl_lh == NULL); 589303831d35Sstevel man_ctl_lh = ctl_lh; 589403831d35Sstevel mutex_exit(&man_lock); 589503831d35Sstevel 589603831d35Sstevel exit: 589703831d35Sstevel if (li) 589803831d35Sstevel ldi_ident_release(li); 589903831d35Sstevel 590003831d35Sstevel MAN_DBG(MAN_CONFIG, ("man_open_ctl: man_ctl_lh(0x%p) errno = %d\n", 590103831d35Sstevel (void *)man_ctl_lh, status)); 590203831d35Sstevel 590303831d35Sstevel return (status); 590403831d35Sstevel } 590503831d35Sstevel 590603831d35Sstevel /* 590703831d35Sstevel * man_close_ctl - Close control stream, we are about to unload driver. 590803831d35Sstevel * 590903831d35Sstevel * Locking: 591003831d35Sstevel * - Called holding no locks. 591103831d35Sstevel */ 591203831d35Sstevel static void 591303831d35Sstevel man_close_ctl() 591403831d35Sstevel { 591503831d35Sstevel ldi_handle_t tlh; 591603831d35Sstevel 591703831d35Sstevel MAN_DBG(MAN_CONFIG, ("man_close_ctl: unplumbing control stream\n")); 591803831d35Sstevel 591903831d35Sstevel mutex_enter(&man_lock); 592003831d35Sstevel if ((tlh = man_ctl_lh) != NULL) 592103831d35Sstevel man_ctl_lh = NULL; 592203831d35Sstevel mutex_exit(&man_lock); 592303831d35Sstevel 592403831d35Sstevel if (tlh != NULL) { 592503831d35Sstevel (void) ldi_close(tlh, NULL, kcred); 592603831d35Sstevel } 592703831d35Sstevel 592803831d35Sstevel } 592903831d35Sstevel 593003831d35Sstevel /* 593103831d35Sstevel * Close the lower streams. Get all the timers canceled, close the lower 593203831d35Sstevel * stream and delete the dest array. 593303831d35Sstevel * 593403831d35Sstevel * Returns: 593503831d35Sstevel * 0 Closed all streams. 593603831d35Sstevel * 1 Couldn't close one or more streams, timers still running. 593703831d35Sstevel * 593803831d35Sstevel * Locking: 593903831d35Sstevel * - Called holding no locks. 594003831d35Sstevel */ 594103831d35Sstevel static void 594203831d35Sstevel man_bclose(man_adest_t *adp) 594303831d35Sstevel { 594403831d35Sstevel int i; 594503831d35Sstevel man_dest_t *mdp; 594603831d35Sstevel 594703831d35Sstevel man_cancel_timers(adp); 594803831d35Sstevel 594903831d35Sstevel for (i = 0; i < adp->a_ndests; i++) { 595003831d35Sstevel mdp = &adp->a_mdp[i]; 595103831d35Sstevel 595203831d35Sstevel if (mdp->md_muxid != -1) 595303831d35Sstevel man_unplumb(mdp); 595403831d35Sstevel } 595503831d35Sstevel 595603831d35Sstevel mutex_destroy(&mdp->md_lock); 595703831d35Sstevel man_kfree(adp->a_mdp, sizeof (man_dest_t) * adp->a_ndests); 595803831d35Sstevel adp->a_mdp = NULL; 595903831d35Sstevel } 596003831d35Sstevel 596103831d35Sstevel /* 596203831d35Sstevel * We want to close down all lower streams. Need to wait until all 596303831d35Sstevel * timers and work related to these lower streams is quiesced. 596403831d35Sstevel * 596503831d35Sstevel * Returns 1 if lower streams are quiesced, 0 if we need to wait 596603831d35Sstevel * a bit longer. 596703831d35Sstevel */ 596803831d35Sstevel static void 596903831d35Sstevel man_cancel_timers(man_adest_t *adp) 597003831d35Sstevel { 597103831d35Sstevel man_dest_t *mdp; 597203831d35Sstevel int cnt; 597303831d35Sstevel int i; 597403831d35Sstevel 597503831d35Sstevel mdp = adp->a_mdp; 597603831d35Sstevel cnt = adp->a_ndests; 597703831d35Sstevel 597803831d35Sstevel MAN_DBG(MAN_SWITCH, ("man_cancel_timers: mdp(0x%p) cnt %d", 597903831d35Sstevel (void *)mdp, cnt)); 598003831d35Sstevel 598103831d35Sstevel for (i = 0; i < cnt; i++) { 598203831d35Sstevel 598303831d35Sstevel if (mdp[i].md_lc_timer_id != 0) { 598403831d35Sstevel (void) quntimeout(man_ctl_wq, mdp[i].md_lc_timer_id); 598503831d35Sstevel mdp[i].md_lc_timer_id = 0; 598603831d35Sstevel } 598703831d35Sstevel 598803831d35Sstevel if (mdp[i].md_bc_id != 0) { 598903831d35Sstevel qunbufcall(man_ctl_wq, mdp[i].md_bc_id); 599003831d35Sstevel mdp[i].md_bc_id = 0; 599103831d35Sstevel } 599203831d35Sstevel } 599303831d35Sstevel 599403831d35Sstevel MAN_DBG(MAN_SWITCH, ("man_cancel_timers: returns")); 599503831d35Sstevel } 599603831d35Sstevel 599703831d35Sstevel /* 599803831d35Sstevel * A failover is started at start of day, when the driver detects a 599903831d35Sstevel * link failure (see man_linkcheck_timer), or when DR detaches 600003831d35Sstevel * the IO board containing the current active link between SC and 600103831d35Sstevel * domain (see man_dr_detach, man_iwork, and man_do_dr_detach). A 600203831d35Sstevel * MAN_WORK_SWITCH work request containing all the lower streams that 600303831d35Sstevel * should be switched is posted on the man_bwork_q-> This work request is 600403831d35Sstevel * processed here. Once all lower streams have been switched to an 600503831d35Sstevel * alternate path, the MAN_WORK_SWITCH work request is passed back to 600603831d35Sstevel * man_iwork_q where it is processed within the inner perimeter of the 600703831d35Sstevel * STREAMS framework (see man_iswitch). 600803831d35Sstevel * 600903831d35Sstevel * Note that when the switch fails for whatever reason, we just hand 601003831d35Sstevel * back the lower streams untouched and let another failover happen. 601103831d35Sstevel * Hopefully we will sooner or later succeed at the failover. 601203831d35Sstevel */ 601303831d35Sstevel static void 601403831d35Sstevel man_bswitch(man_adest_t *adp, man_work_t *wp) 601503831d35Sstevel { 601603831d35Sstevel man_dest_t *tdp; 601703831d35Sstevel man_t *manp; 601803831d35Sstevel int i; 601903831d35Sstevel int status = 0; 602003831d35Sstevel 602103831d35Sstevel /* 602203831d35Sstevel * Make a temporary copy of dest array, updating device to the 602303831d35Sstevel * alternate and try to open all lower streams. bgthread can sleep. 602403831d35Sstevel */ 602503831d35Sstevel 602603831d35Sstevel tdp = man_kzalloc(sizeof (man_dest_t) * adp->a_ndests, 602703831d35Sstevel KM_SLEEP); 602803831d35Sstevel bcopy(adp->a_mdp, tdp, sizeof (man_dest_t) * adp->a_ndests); 602903831d35Sstevel 603003831d35Sstevel /* 603103831d35Sstevel * Before we switch to the new path, lets sync the kstats. 603203831d35Sstevel */ 603303831d35Sstevel mutex_enter(&man_lock); 603403831d35Sstevel 603503831d35Sstevel manp = ddi_get_soft_state(man_softstate, adp->a_man_ppa); 603603831d35Sstevel if (manp != NULL) { 603703831d35Sstevel man_update_path_kstats(manp); 603803831d35Sstevel } else 603903831d35Sstevel status = ENODEV; 604003831d35Sstevel 604103831d35Sstevel mutex_exit(&man_lock); 604203831d35Sstevel 604303831d35Sstevel if (status != 0) 604403831d35Sstevel goto exit; 604503831d35Sstevel 604603831d35Sstevel for (i = 0; i < adp->a_ndests; i++) { 604703831d35Sstevel 604803831d35Sstevel tdp[i].md_device = adp->a_st_dev; 604903831d35Sstevel tdp[i].md_muxid = -1; 605003831d35Sstevel 605103831d35Sstevel if (man_plumb(&tdp[i])) 605203831d35Sstevel break; 605303831d35Sstevel } 605403831d35Sstevel 605503831d35Sstevel /* 605603831d35Sstevel * Didn't plumb everyone, unplumb new lower stuff and return. 605703831d35Sstevel */ 605803831d35Sstevel if (i < adp->a_ndests) { 605903831d35Sstevel int j; 606003831d35Sstevel 606103831d35Sstevel for (j = 0; j <= i; j++) 606203831d35Sstevel man_unplumb(&tdp[j]); 606303831d35Sstevel status = EAGAIN; 606403831d35Sstevel goto exit; 606503831d35Sstevel } 606603831d35Sstevel 606703831d35Sstevel if (man_is_on_domain && man_dossc_switch(adp->a_st_dev.mdev_exp_id)) { 606803831d35Sstevel /* 606903831d35Sstevel * If we cant set new path on the SSC, then fail the 607003831d35Sstevel * failover. 607103831d35Sstevel */ 607203831d35Sstevel for (i = 0; i < adp->a_ndests; i++) 607303831d35Sstevel man_unplumb(&tdp[i]); 607403831d35Sstevel status = EAGAIN; 607503831d35Sstevel goto exit; 607603831d35Sstevel } 607703831d35Sstevel 607803831d35Sstevel man_kfree(adp->a_mdp, sizeof (man_dest_t) * adp->a_ndests); 607903831d35Sstevel adp->a_mdp = tdp; 608003831d35Sstevel 608103831d35Sstevel exit: 608203831d35Sstevel if (status) 608303831d35Sstevel man_kfree(tdp, sizeof (man_dest_t) * adp->a_ndests); 608403831d35Sstevel 608503831d35Sstevel 608603831d35Sstevel MAN_DBG(MAN_SWITCH, ("man_bswitch: returns %d", status)); 608703831d35Sstevel 608803831d35Sstevel /* 608903831d35Sstevel * Hand processed switch request back to man_iwork for 609003831d35Sstevel * processing in man_iswitch. 609103831d35Sstevel */ 609203831d35Sstevel wp->mw_status = status; 609303831d35Sstevel 609403831d35Sstevel mutex_enter(&man_lock); 609503831d35Sstevel man_work_add(man_iwork_q, wp); 609603831d35Sstevel mutex_exit(&man_lock); 609703831d35Sstevel 609803831d35Sstevel } 609903831d35Sstevel 610003831d35Sstevel /* 610103831d35Sstevel * man_plumb - Configure a lower stream for this destination. 610203831d35Sstevel * 610303831d35Sstevel * Locking: 610403831d35Sstevel * - Called holding no locks. 610503831d35Sstevel * 610603831d35Sstevel * Returns: 610703831d35Sstevel * - success - 0 610803831d35Sstevel * - failure - error code of failure 610903831d35Sstevel */ 611003831d35Sstevel static int 611103831d35Sstevel man_plumb(man_dest_t *mdp) 611203831d35Sstevel { 611303831d35Sstevel int status; 611403831d35Sstevel int muxid; 611503831d35Sstevel ldi_handle_t lh; 611603831d35Sstevel ldi_ident_t li = NULL; 611703831d35Sstevel 611803831d35Sstevel MAN_DBG(MAN_SWITCH, ("man_plumb: mdp(0x%p) %s%d exp(%d)", 611903831d35Sstevel (void *)mdp, ddi_major_to_name(mdp->md_device.mdev_major), 612003831d35Sstevel mdp->md_device.mdev_ppa, mdp->md_device.mdev_exp_id)); 612103831d35Sstevel 612203831d35Sstevel /* 612303831d35Sstevel * Control stream should already be open. 612403831d35Sstevel */ 612503831d35Sstevel if (man_ctl_lh == NULL) { 612603831d35Sstevel status = EAGAIN; 612703831d35Sstevel goto exit; 612803831d35Sstevel } 612903831d35Sstevel 613003831d35Sstevel mutex_enter(&man_lock); 613103831d35Sstevel ASSERT(man_ctl_wq != NULL); 613203831d35Sstevel status = ldi_ident_from_stream(man_ctl_wq, &li); 613303831d35Sstevel if (status != 0) { 613403831d35Sstevel cmn_err(CE_WARN, 613503831d35Sstevel "man_plumb: ident alloc failed, error %d", status); 613603831d35Sstevel goto exit; 613703831d35Sstevel } 613803831d35Sstevel mutex_exit(&man_lock); 613903831d35Sstevel 614003831d35Sstevel /* 614103831d35Sstevel * previously opens were done by a dev_t of makedev(clone_major, 614203831d35Sstevel * mdev_major) which should always map to /devices/pseudo/clone@0:eri 614303831d35Sstevel */ 614403831d35Sstevel ASSERT(strcmp(ERI_IDNAME, 614503831d35Sstevel ddi_major_to_name(mdp->md_device.mdev_major)) == 0); 614603831d35Sstevel 614703831d35Sstevel status = ldi_open_by_name(ERI_PATH, FREAD | FWRITE | FNOCTTY, 614803831d35Sstevel kcred, &lh, li); 614903831d35Sstevel if (status) { 615003831d35Sstevel cmn_err(CE_WARN, 615103831d35Sstevel "man_plumb: eri open failed, error %d", status); 615203831d35Sstevel goto exit; 615303831d35Sstevel } 615403831d35Sstevel 615503831d35Sstevel /* 615603831d35Sstevel * Link netdev under MAN. 615703831d35Sstevel */ 615803831d35Sstevel ASSERT(mdp->md_muxid == -1); 615903831d35Sstevel 616003831d35Sstevel status = ldi_ioctl(man_ctl_lh, I_PLINK, (intptr_t)lh, 616103831d35Sstevel FREAD+FWRITE+FNOCTTY+FKIOCTL, kcred, &muxid); 616203831d35Sstevel if (status) { 616303831d35Sstevel cmn_err(CE_WARN, 616403831d35Sstevel "man_plumb: ldi_ioctl(I_PLINK) failed, error %d", status); 616503831d35Sstevel (void) ldi_close(lh, NULL, kcred); 616603831d35Sstevel goto exit; 616703831d35Sstevel 616803831d35Sstevel } 616903831d35Sstevel mdp->md_muxid = muxid; 617003831d35Sstevel mdp->md_wq = man_linkrec_find(muxid); 617103831d35Sstevel /* 617203831d35Sstevel * If we can't find the linkrec then return an 617303831d35Sstevel * error. It will be automatically unplumbed on failure. 617403831d35Sstevel */ 617503831d35Sstevel if (mdp->md_wq == NULL) 617603831d35Sstevel status = EAGAIN; 617703831d35Sstevel 617803831d35Sstevel (void) ldi_close(lh, NULL, kcred); 617903831d35Sstevel exit: 618003831d35Sstevel if (li) 618103831d35Sstevel ldi_ident_release(li); 618203831d35Sstevel 618303831d35Sstevel MAN_DBG(MAN_SWITCH, ("man_plumb: exit\n")); 618403831d35Sstevel 618503831d35Sstevel return (status); 618603831d35Sstevel } 618703831d35Sstevel 618803831d35Sstevel /* 618903831d35Sstevel * man_unplumb - tear down the STREAMs framework for the lower multiplexor. 619003831d35Sstevel * 619103831d35Sstevel * mdp - destination struct of interest 619203831d35Sstevel * 619303831d35Sstevel * returns - success - 0 619403831d35Sstevel * - failure - return error from ldi_ioctl 619503831d35Sstevel */ 619603831d35Sstevel static void 619703831d35Sstevel man_unplumb(man_dest_t *mdp) 619803831d35Sstevel { 619903831d35Sstevel int status, rval; 620003831d35Sstevel 620103831d35Sstevel MAN_DBG(MAN_SWITCH, ("man_unplumb: mdp")); 620203831d35Sstevel MAN_DBGCALL(MAN_SWITCH, man_print_mdp(mdp)); 620303831d35Sstevel 620403831d35Sstevel if (mdp->md_muxid == -1) 620503831d35Sstevel return; 620603831d35Sstevel 620703831d35Sstevel ASSERT(man_ctl_lh != NULL); 620803831d35Sstevel 620903831d35Sstevel /* 621003831d35Sstevel * I_PUNLINK causes the multiplexor resources to be freed. 621103831d35Sstevel */ 621203831d35Sstevel status = ldi_ioctl(man_ctl_lh, I_PUNLINK, (intptr_t)mdp->md_muxid, 621303831d35Sstevel FREAD+FWRITE+FNOCTTY+FKIOCTL, kcred, &rval); 621403831d35Sstevel if (status) { 621503831d35Sstevel cmn_err(CE_WARN, "man_unplumb: ldi_ioctl(I_PUNLINK) failed" 621603831d35Sstevel " errno %d\n", status); 621703831d35Sstevel } 621803831d35Sstevel /* 621903831d35Sstevel * Delete linkrec if it exists. 622003831d35Sstevel */ 622103831d35Sstevel (void) man_linkrec_find(mdp->md_muxid); 622203831d35Sstevel mdp->md_muxid = -1; 622303831d35Sstevel 622403831d35Sstevel } 622503831d35Sstevel 622603831d35Sstevel /* 622703831d35Sstevel * The routines below deal with paths and pathgroups. These data structures 622803831d35Sstevel * are used to track the physical devices connecting the domain and SSC. 622903831d35Sstevel * These devices make up the lower streams of the MAN multiplexor. The 623003831d35Sstevel * routines all expect the man_lock to be held. 623103831d35Sstevel * 623203831d35Sstevel * A pathgroup consists of all paths that connect a particular domain and the 623303831d35Sstevel * SSC. The concept of a pathgroup id (pg_id) is used to uniquely identify 623403831d35Sstevel * a pathgroup. For Domains, there is just one pathgroup, that connecting 623503831d35Sstevel * the domain to the SSC (pg_id == 0). On the SSC, there is one pathgroup per 623603831d35Sstevel * domain. The pg_id field corresponds to the domain tags A-R. A pg_id of 623703831d35Sstevel * 0 means domain tag A, a pg_id of 1 means domain B, etc. 623803831d35Sstevel * 623903831d35Sstevel * The path data structure identifies one path between the SSC and a domain. 624003831d35Sstevel * It describes the information for the path: the major and minor number of 624103831d35Sstevel * the physical device; kstat pointers; and ethernet address of the 624203831d35Sstevel * other end of the path. 624303831d35Sstevel * 624403831d35Sstevel * The pathgroups are anchored at man_pg_head and are protected by the 624503831d35Sstevel * by the inner perimeter. The routines are only called by the STREAMs 624603831d35Sstevel * portion of the driver. 624703831d35Sstevel */ 624803831d35Sstevel 624903831d35Sstevel /* 625003831d35Sstevel * Update man instance pathgroup info. Exclusive inner perimeter assures 625103831d35Sstevel * this code is single threaded. man_refcnt assures man_t wont detach 625203831d35Sstevel * while we are playing with man_pg stuff. 625303831d35Sstevel * 625403831d35Sstevel * Returns 0 on success, errno on failure. 625503831d35Sstevel */ 625603831d35Sstevel int 625703831d35Sstevel man_pg_cmd(mi_path_t *mip, man_work_t *waiter_wp) 625803831d35Sstevel { 625903831d35Sstevel int status = 0; 626003831d35Sstevel man_t *manp; 626103831d35Sstevel 626203831d35Sstevel if (mip->mip_ndevs < 0) { 626303831d35Sstevel status = EINVAL; 626403831d35Sstevel cmn_err(CE_WARN, "man_pg_cmd: EINVAL: mip_ndevs %d", 626503831d35Sstevel mip->mip_ndevs); 626603831d35Sstevel goto exit; 626703831d35Sstevel } 626803831d35Sstevel 626903831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 627003831d35Sstevel manp = ddi_get_soft_state(man_softstate, mip->mip_man_ppa); 627103831d35Sstevel if (manp == NULL) { 627203831d35Sstevel status = ENODEV; 627303831d35Sstevel goto exit; 627403831d35Sstevel } 627503831d35Sstevel 627603831d35Sstevel MAN_DBG(MAN_PATH, ("man_pg_cmd: mip")); 627703831d35Sstevel MAN_DBGCALL(MAN_PATH, man_print_mip(mip)); 627803831d35Sstevel 627903831d35Sstevel MAN_DBG(MAN_PATH, ("\tman_t")); 628003831d35Sstevel MAN_DBGCALL(MAN_PATH, man_print_man(manp)); 628103831d35Sstevel 628203831d35Sstevel switch (mip->mip_cmd) { 628303831d35Sstevel case MI_PATH_ASSIGN: 628403831d35Sstevel status = man_pg_assign(&manp->man_pg, mip, FALSE); 628503831d35Sstevel break; 628603831d35Sstevel 628703831d35Sstevel case MI_PATH_ADD: 628803831d35Sstevel status = man_pg_assign(&manp->man_pg, mip, TRUE); 628903831d35Sstevel break; 629003831d35Sstevel 629103831d35Sstevel case MI_PATH_UNASSIGN: 629203831d35Sstevel status = man_pg_unassign(&manp->man_pg, mip); 629303831d35Sstevel break; 629403831d35Sstevel 629503831d35Sstevel case MI_PATH_ACTIVATE: 629603831d35Sstevel status = man_pg_activate(manp, mip, waiter_wp); 629703831d35Sstevel break; 629803831d35Sstevel 629903831d35Sstevel case MI_PATH_READ: 630003831d35Sstevel status = man_pg_read(manp->man_pg, mip); 630103831d35Sstevel break; 630203831d35Sstevel 630303831d35Sstevel default: 630403831d35Sstevel status = EINVAL; 630503831d35Sstevel cmn_err(CE_NOTE, "man_pg_cmd: invalid command"); 630603831d35Sstevel break; 630703831d35Sstevel } 630803831d35Sstevel 630903831d35Sstevel exit: 631003831d35Sstevel MAN_DBG(MAN_PATH, ("man_pg_cmd: returns %d", status)); 631103831d35Sstevel 631203831d35Sstevel return (status); 631303831d35Sstevel } 631403831d35Sstevel 631503831d35Sstevel /* 631603831d35Sstevel * Assign paths to a pathgroup. If pathgroup doesnt exists, create it. 631703831d35Sstevel * If path doesnt exist, create it. If ethernet address of existing 631803831d35Sstevel * pathgroup different, change it. If an existing path is not in the new 631903831d35Sstevel * list, remove it. If anything changed, send PATH_UPDATE request to 632003831d35Sstevel * man_iwork to update all man_dest_t's. 632103831d35Sstevel * 632203831d35Sstevel * mplpp - man pathgroup list point to point. 632303831d35Sstevel * mip - new/updated pathgroup info to assign. 632403831d35Sstevel */ 632503831d35Sstevel static int 632603831d35Sstevel man_pg_assign(man_pg_t **mplpp, mi_path_t *mip, int add_only) 632703831d35Sstevel { 632803831d35Sstevel man_pg_t *mpg; 632903831d35Sstevel man_path_t *mp; 633003831d35Sstevel man_path_t *add_paths = NULL; 633103831d35Sstevel int cnt; 633203831d35Sstevel int i; 633303831d35Sstevel int first_pass = TRUE; 633403831d35Sstevel int status = 0; 633503831d35Sstevel 633603831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 633703831d35Sstevel 633803831d35Sstevel cnt = mip->mip_ndevs; 633903831d35Sstevel if (cnt == 0) { 634003831d35Sstevel status = EINVAL; 634103831d35Sstevel cmn_err(CE_NOTE, "man_pg_assign: mip_ndevs == 0"); 634203831d35Sstevel goto exit; 634303831d35Sstevel } 634403831d35Sstevel 634503831d35Sstevel /* 634603831d35Sstevel * Assure the devices to be assigned are not assigned to some other 634703831d35Sstevel * pathgroup. 634803831d35Sstevel */ 634903831d35Sstevel for (i = 0; i < cnt; i++) { 635003831d35Sstevel mpg = man_find_path_by_dev(*mplpp, &mip->mip_devs[i], NULL); 635103831d35Sstevel 635203831d35Sstevel if (mpg == NULL) 635303831d35Sstevel continue; 635403831d35Sstevel 635503831d35Sstevel if ((mpg->mpg_man_ppa != mip->mip_man_ppa) || 635603831d35Sstevel (mpg->mpg_pg_id != mip->mip_pg_id)) { 635703831d35Sstevel /* 635803831d35Sstevel * Already assigned to some other man instance 635903831d35Sstevel * or pathgroup. 636003831d35Sstevel */ 636103831d35Sstevel status = EEXIST; 636203831d35Sstevel goto exit; 636303831d35Sstevel } 636403831d35Sstevel } 636503831d35Sstevel 636603831d35Sstevel /* 636703831d35Sstevel * Find pathgroup, or allocate new one if it doesnt exist and 636803831d35Sstevel * add it to list at mplpp. Result is that mpg points to 636903831d35Sstevel * pathgroup to modify. 637003831d35Sstevel */ 637103831d35Sstevel mpg = man_find_pg_by_id(*mplpp, mip->mip_pg_id); 637203831d35Sstevel if (mpg == NULL) { 637303831d35Sstevel 637403831d35Sstevel status = man_pg_create(mplpp, &mpg, mip); 637503831d35Sstevel if (status) 637603831d35Sstevel goto exit; 637703831d35Sstevel 637803831d35Sstevel } else if (ether_cmp(&mip->mip_eaddr, &mpg->mpg_dst_eaddr) != 0) { 637903831d35Sstevel 638003831d35Sstevel cmn_err(CE_WARN, "man_pg_assign: ethernet address mismatch"); 638103831d35Sstevel cmn_err(CE_CONT, "existing %s", 638203831d35Sstevel ether_sprintf(&mpg->mpg_dst_eaddr)); 638303831d35Sstevel cmn_err(CE_CONT, "new %s", 638403831d35Sstevel ether_sprintf(&mip->mip_eaddr)); 638503831d35Sstevel 638603831d35Sstevel status = EINVAL; 638703831d35Sstevel goto exit; 638803831d35Sstevel } 638903831d35Sstevel 639003831d35Sstevel /* 639103831d35Sstevel * Create list of new paths to add to pathgroup. 639203831d35Sstevel */ 639303831d35Sstevel for (i = 0; i < cnt; i++) { 639403831d35Sstevel 639503831d35Sstevel if (man_find_path_by_dev(*mplpp, &mip->mip_devs[i], NULL)) 639603831d35Sstevel continue; /* Already exists in this pathgroup */ 639703831d35Sstevel 639803831d35Sstevel mp = man_kzalloc(sizeof (man_path_t), KM_NOSLEEP); 639903831d35Sstevel if (mp == NULL) { 640003831d35Sstevel status = ENOMEM; 640103831d35Sstevel goto exit; 640203831d35Sstevel } 640303831d35Sstevel 640403831d35Sstevel mp->mp_device = mip->mip_devs[i]; 640503831d35Sstevel mp->mp_device.mdev_state = MDEV_ASSIGNED; 640603831d35Sstevel 640703831d35Sstevel MAN_DBG(MAN_PATH, ("man_pg_assign: assigning mdp")); 640803831d35Sstevel MAN_DBGCALL(MAN_PATH, man_print_dev(&mp->mp_device)); 640903831d35Sstevel 641003831d35Sstevel status = man_path_kstat_init(mp); 641103831d35Sstevel if (status) { 641203831d35Sstevel man_kfree(mp, sizeof (man_path_t)); 641303831d35Sstevel goto exit; 641403831d35Sstevel } 641503831d35Sstevel 641603831d35Sstevel man_path_insert(&add_paths, mp); 641703831d35Sstevel } 641803831d35Sstevel 641903831d35Sstevel /* 642003831d35Sstevel * man_dr_attach passes only the path which is being DRd in. 642103831d35Sstevel * So just add the path and don't worry about removing paths. 642203831d35Sstevel */ 642303831d35Sstevel if (add_only == TRUE) 642403831d35Sstevel goto exit; 642503831d35Sstevel 642603831d35Sstevel 642703831d35Sstevel /* 642803831d35Sstevel * Check if any paths we want to remove are ACTIVE. If not, 642903831d35Sstevel * do a second pass and remove them. 643003831d35Sstevel */ 643103831d35Sstevel again: 643203831d35Sstevel mp = mpg->mpg_pathp; 643303831d35Sstevel while (mp != NULL) { 643403831d35Sstevel int in_new_list; 643503831d35Sstevel man_path_t *rp; 643603831d35Sstevel 643703831d35Sstevel rp = NULL; 643803831d35Sstevel in_new_list = FALSE; 643903831d35Sstevel 644003831d35Sstevel for (i = 0; i < cnt; i++) { 644103831d35Sstevel if (mp->mp_device.mdev_ppa == 644203831d35Sstevel mip->mip_devs[i].mdev_ppa) { 644303831d35Sstevel 644403831d35Sstevel in_new_list = TRUE; 644503831d35Sstevel break; 644603831d35Sstevel } 644703831d35Sstevel } 644803831d35Sstevel 644903831d35Sstevel if (!in_new_list) { 645003831d35Sstevel if (first_pass) { 645103831d35Sstevel if (mp->mp_device.mdev_state & MDEV_ACTIVE) { 645203831d35Sstevel status = EBUSY; 645303831d35Sstevel goto exit; 645403831d35Sstevel } 645503831d35Sstevel } else { 645603831d35Sstevel rp = mp; 645703831d35Sstevel } 645803831d35Sstevel } 645903831d35Sstevel mp = mp->mp_next; 646003831d35Sstevel 646103831d35Sstevel if (rp != NULL) 646203831d35Sstevel man_path_remove(&mpg->mpg_pathp, rp); 646303831d35Sstevel } 646403831d35Sstevel 646503831d35Sstevel if (first_pass == TRUE) { 646603831d35Sstevel first_pass = FALSE; 646703831d35Sstevel goto again; 646803831d35Sstevel } 646903831d35Sstevel 647003831d35Sstevel exit: 647103831d35Sstevel if (status == 0) { 647203831d35Sstevel if (add_paths) 647303831d35Sstevel man_path_merge(&mpg->mpg_pathp, add_paths); 647403831d35Sstevel } else { 647503831d35Sstevel while (add_paths != NULL) { 647603831d35Sstevel mp = add_paths; 647703831d35Sstevel add_paths = mp->mp_next; 647803831d35Sstevel mp->mp_next = NULL; 647903831d35Sstevel 648003831d35Sstevel man_path_kstat_uninit(mp); 648103831d35Sstevel man_kfree(mp, sizeof (man_path_t)); 648203831d35Sstevel } 648303831d35Sstevel } 648403831d35Sstevel 648503831d35Sstevel return (status); 648603831d35Sstevel } 648703831d35Sstevel 648803831d35Sstevel /* 648903831d35Sstevel * Remove all paths from a pathgroup (domain shutdown). If there is an 649003831d35Sstevel * active path in the group, shut down all destinations referencing it 649103831d35Sstevel * first. 649203831d35Sstevel */ 649303831d35Sstevel static int 649403831d35Sstevel man_pg_unassign(man_pg_t **plpp, mi_path_t *mip) 649503831d35Sstevel { 649603831d35Sstevel man_pg_t *mpg; 649703831d35Sstevel man_pg_t *tpg; 649803831d35Sstevel man_pg_t *tppg; 649903831d35Sstevel man_path_t *mp = NULL; 650003831d35Sstevel int status = 0; 650103831d35Sstevel 650203831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 650303831d35Sstevel 650403831d35Sstevel /* 650503831d35Sstevel * Check for existence of pathgroup. 650603831d35Sstevel */ 650703831d35Sstevel if ((mpg = man_find_pg_by_id(*plpp, mip->mip_pg_id)) == NULL) 650803831d35Sstevel goto exit; 650903831d35Sstevel 651003831d35Sstevel if (man_find_active_path(mpg->mpg_pathp) != NULL) { 651103831d35Sstevel status = man_remove_dests(mpg); 651203831d35Sstevel if (status) 651303831d35Sstevel goto exit; 651403831d35Sstevel } 651503831d35Sstevel 651603831d35Sstevel /* 651703831d35Sstevel * Free all the paths for this pathgroup. 651803831d35Sstevel */ 651903831d35Sstevel while (mpg->mpg_pathp) { 652003831d35Sstevel mp = mpg->mpg_pathp; 652103831d35Sstevel mpg->mpg_pathp = mp->mp_next; 652203831d35Sstevel mp->mp_next = NULL; 652303831d35Sstevel 652403831d35Sstevel man_path_kstat_uninit(mp); 652503831d35Sstevel man_kfree(mp, sizeof (man_path_t)); 652603831d35Sstevel } 652703831d35Sstevel 652803831d35Sstevel /* 652903831d35Sstevel * Remove this pathgroup from the list, and free it. 653003831d35Sstevel */ 653103831d35Sstevel tpg = tppg = *plpp; 653203831d35Sstevel if (tpg == mpg) { 653303831d35Sstevel *plpp = tpg->mpg_next; 653403831d35Sstevel goto free_pg; 653503831d35Sstevel } 653603831d35Sstevel 653703831d35Sstevel for (tpg = tpg->mpg_next; tpg != NULL; tpg = tpg->mpg_next) { 653803831d35Sstevel if (tpg == mpg) 653903831d35Sstevel break; 654003831d35Sstevel tppg = tpg; 654103831d35Sstevel } 654203831d35Sstevel 654303831d35Sstevel ASSERT(tpg != NULL); 654403831d35Sstevel 654503831d35Sstevel tppg->mpg_next = tpg->mpg_next; 654603831d35Sstevel tpg->mpg_next = NULL; 654703831d35Sstevel 654803831d35Sstevel free_pg: 654903831d35Sstevel man_kfree(tpg, sizeof (man_pg_t)); 655003831d35Sstevel 655103831d35Sstevel exit: 655203831d35Sstevel return (status); 655303831d35Sstevel 655403831d35Sstevel } 655503831d35Sstevel 655603831d35Sstevel /* 655703831d35Sstevel * Set a new active path. This is done via man_ioctl so we are 655803831d35Sstevel * exclusive in the inner perimeter. 655903831d35Sstevel */ 656003831d35Sstevel static int 656103831d35Sstevel man_pg_activate(man_t *manp, mi_path_t *mip, man_work_t *waiter_wp) 656203831d35Sstevel { 656303831d35Sstevel man_pg_t *mpg1; 656403831d35Sstevel man_pg_t *mpg2; 656503831d35Sstevel man_pg_t *plp; 656603831d35Sstevel man_path_t *mp; 656703831d35Sstevel man_path_t *ap; 656803831d35Sstevel int status = 0; 656903831d35Sstevel 657003831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 657103831d35Sstevel MAN_DBG(MAN_PATH, ("man_pg_activate: dev")); 657203831d35Sstevel MAN_DBGCALL(MAN_PATH, man_print_dev(mip->mip_devs)); 657303831d35Sstevel 657403831d35Sstevel if (mip->mip_ndevs != 1) { 657503831d35Sstevel status = EINVAL; 657603831d35Sstevel goto exit; 657703831d35Sstevel } 657803831d35Sstevel 657903831d35Sstevel plp = manp->man_pg; 658003831d35Sstevel mpg1 = man_find_pg_by_id(plp, mip->mip_pg_id); 658103831d35Sstevel if (mpg1 == NULL) { 658203831d35Sstevel status = EINVAL; 658303831d35Sstevel goto exit; 658403831d35Sstevel } 658503831d35Sstevel 658603831d35Sstevel mpg2 = man_find_path_by_dev(plp, mip->mip_devs, &mp); 658703831d35Sstevel if (mpg2 == NULL) { 658803831d35Sstevel status = ENODEV; 658903831d35Sstevel goto exit; 659003831d35Sstevel } 659103831d35Sstevel 659203831d35Sstevel if (mpg1 != mpg2) { 659303831d35Sstevel status = EINVAL; 659403831d35Sstevel goto exit; 659503831d35Sstevel } 659603831d35Sstevel 659703831d35Sstevel ASSERT(mp->mp_device.mdev_ppa == mip->mip_devs->mdev_ppa); 659803831d35Sstevel 659903831d35Sstevel if (mpg1->mpg_flags & MAN_PG_SWITCHING) { 660003831d35Sstevel status = EAGAIN; 660103831d35Sstevel goto exit; 660203831d35Sstevel } 660303831d35Sstevel 660403831d35Sstevel ap = man_find_active_path(mpg1->mpg_pathp); 660503831d35Sstevel if (ap == NULL) { 660603831d35Sstevel /* 660703831d35Sstevel * This is the first time a path has been activated for 660803831d35Sstevel * this pathgroup. Initialize all upper streams dest 660903831d35Sstevel * structure for this pathgroup so autoswitch will find 661003831d35Sstevel * them. 661103831d35Sstevel */ 661203831d35Sstevel mp->mp_device.mdev_state |= MDEV_ACTIVE; 661303831d35Sstevel man_add_dests(mpg1); 661403831d35Sstevel goto exit; 661503831d35Sstevel } 661603831d35Sstevel 661703831d35Sstevel /* 661803831d35Sstevel * Path already active, nothing to do. 661903831d35Sstevel */ 662003831d35Sstevel if (ap == mp) 662103831d35Sstevel goto exit; 662203831d35Sstevel 662303831d35Sstevel /* 662403831d35Sstevel * Try to autoswitch to requested device. Set flags and refcnt. 662503831d35Sstevel * Cleared in man_iswitch when SWITCH completes. 662603831d35Sstevel */ 662703831d35Sstevel manp->man_refcnt++; 662803831d35Sstevel mpg1->mpg_flags |= MAN_PG_SWITCHING; 662903831d35Sstevel 663003831d35Sstevel /* 663103831d35Sstevel * Switch to path specified. 663203831d35Sstevel */ 663303831d35Sstevel status = man_autoswitch(mpg1, mip->mip_devs, waiter_wp); 663403831d35Sstevel 663503831d35Sstevel if (status != 0) { 663603831d35Sstevel /* 663703831d35Sstevel * man_iswitch not going to run, clean up. 663803831d35Sstevel */ 663903831d35Sstevel manp->man_refcnt--; 664003831d35Sstevel mpg1->mpg_flags &= ~MAN_PG_SWITCHING; 664103831d35Sstevel 664203831d35Sstevel if (status == ENODEV) { 664303831d35Sstevel /* 664403831d35Sstevel * Device not plumbed isn't really an error. Change 664503831d35Sstevel * active device setting here, since man_iswitch isn't 664603831d35Sstevel * going to be run to do it. 664703831d35Sstevel */ 664803831d35Sstevel status = 0; 664903831d35Sstevel ap->mp_device.mdev_state &= ~MDEV_ACTIVE; 665003831d35Sstevel mp->mp_device.mdev_state |= MDEV_ACTIVE; 665103831d35Sstevel } 665203831d35Sstevel } 665303831d35Sstevel 665403831d35Sstevel exit: 665503831d35Sstevel MAN_DBG(MAN_PATH, ("man_pg_activate: returns %d", status)); 665603831d35Sstevel 665703831d35Sstevel return (status); 665803831d35Sstevel } 665903831d35Sstevel 666003831d35Sstevel static int 666103831d35Sstevel man_pg_read(man_pg_t *plp, mi_path_t *mip) 666203831d35Sstevel { 666303831d35Sstevel man_pg_t *mpg; 666403831d35Sstevel man_path_t *mp; 666503831d35Sstevel int cnt; 666603831d35Sstevel int status = 0; 666703831d35Sstevel 666803831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 666903831d35Sstevel 667003831d35Sstevel if ((mpg = man_find_pg_by_id(plp, mip->mip_pg_id)) == NULL) { 667103831d35Sstevel status = ENODEV; 667203831d35Sstevel goto exit; 667303831d35Sstevel } 667403831d35Sstevel 667503831d35Sstevel cnt = 0; 667603831d35Sstevel for (mp = mpg->mpg_pathp; mp != NULL; mp = mp->mp_next) { 667703831d35Sstevel bcopy(&mp->mp_device, &mip->mip_devs[cnt], sizeof (man_dev_t)); 667803831d35Sstevel if (cnt == mip->mip_ndevs) 667903831d35Sstevel break; 668003831d35Sstevel cnt++; 668103831d35Sstevel } 668203831d35Sstevel 668303831d35Sstevel MAN_DBG(MAN_PATH, ("man_pg_read: pg(0x%p) id(%d) found %d paths", 668403831d35Sstevel (void *)mpg, mpg->mpg_pg_id, cnt)); 668503831d35Sstevel 668603831d35Sstevel mip->mip_ndevs = cnt; 668703831d35Sstevel 668803831d35Sstevel /* 668903831d35Sstevel * TBD - What should errno be if user buffer too small ? 669003831d35Sstevel */ 669103831d35Sstevel if (mp != NULL) { 669203831d35Sstevel status = ENOMEM; 669303831d35Sstevel } 669403831d35Sstevel 669503831d35Sstevel exit: 669603831d35Sstevel 669703831d35Sstevel return (status); 669803831d35Sstevel } 669903831d35Sstevel 670003831d35Sstevel /* 670103831d35Sstevel * return existing pathgroup, or create it. TBD - Need to update 670203831d35Sstevel * all of destinations if we added a pathgroup. Also, need to update 670303831d35Sstevel * all of man_strup if we add a path. 670403831d35Sstevel * 670503831d35Sstevel * mplpp - man pathgroup list point to pointer. 670603831d35Sstevel * mpgp - returns newly created man pathgroup. 670703831d35Sstevel * mip - info to fill in mpgp. 670803831d35Sstevel */ 670903831d35Sstevel static int 671003831d35Sstevel man_pg_create(man_pg_t **mplpp, man_pg_t **mpgp, mi_path_t *mip) 671103831d35Sstevel { 671203831d35Sstevel man_pg_t *mpg; 671303831d35Sstevel man_pg_t *tpg; 671403831d35Sstevel int status = 0; 671503831d35Sstevel 671603831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 671703831d35Sstevel 671803831d35Sstevel if (ether_cmp(&mip->mip_eaddr, &zero_ether_addr) == 0) { 671903831d35Sstevel cmn_err(CE_NOTE, "man_ioctl: man_pg_create: ether" 672003831d35Sstevel " addresss not set!"); 672103831d35Sstevel status = EINVAL; 672203831d35Sstevel goto exit; 672303831d35Sstevel } 672403831d35Sstevel 672503831d35Sstevel mpg = man_kzalloc(sizeof (man_pg_t), KM_NOSLEEP); 672603831d35Sstevel if (mpg == NULL) { 672703831d35Sstevel status = ENOMEM; 672803831d35Sstevel goto exit; 672903831d35Sstevel } 673003831d35Sstevel 673103831d35Sstevel mpg->mpg_flags = MAN_PG_IDLE; 673203831d35Sstevel mpg->mpg_pg_id = mip->mip_pg_id; 673303831d35Sstevel mpg->mpg_man_ppa = mip->mip_man_ppa; 673403831d35Sstevel ether_copy(&mip->mip_eaddr, &mpg->mpg_dst_eaddr); 673503831d35Sstevel 673603831d35Sstevel MAN_DBG(MAN_PATH, ("man_pg_create: new mpg")); 673703831d35Sstevel MAN_DBGCALL(MAN_PATH, man_print_mpg(mpg)); 673803831d35Sstevel 673903831d35Sstevel tpg = *mplpp; 674003831d35Sstevel if (tpg == NULL) { 674103831d35Sstevel *mplpp = mpg; 674203831d35Sstevel } else { 674303831d35Sstevel while (tpg->mpg_next != NULL) 674403831d35Sstevel tpg = tpg->mpg_next; 674503831d35Sstevel tpg->mpg_next = mpg; 674603831d35Sstevel } 674703831d35Sstevel 674803831d35Sstevel exit: 674903831d35Sstevel *mpgp = mpg; 675003831d35Sstevel 675103831d35Sstevel return (status); 675203831d35Sstevel } 675303831d35Sstevel 675403831d35Sstevel /* 675503831d35Sstevel * Return pointer to pathgroup containing mdevp, null otherwise. Also, 675603831d35Sstevel * if a path pointer is passed in, set it to matching path in pathgroup. 675703831d35Sstevel * 675803831d35Sstevel * Called holding man_lock. 675903831d35Sstevel */ 676003831d35Sstevel static man_pg_t * 676103831d35Sstevel man_find_path_by_dev(man_pg_t *plp, man_dev_t *mdevp, man_path_t **mpp) 676203831d35Sstevel { 676303831d35Sstevel man_pg_t *mpg; 676403831d35Sstevel man_path_t *mp; 676503831d35Sstevel 676603831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 676703831d35Sstevel for (mpg = plp; mpg != NULL; mpg = mpg->mpg_next) { 676803831d35Sstevel for (mp = mpg->mpg_pathp; mp != NULL; mp = mp->mp_next) { 676903831d35Sstevel if (mp->mp_device.mdev_major == mdevp->mdev_major && 677003831d35Sstevel mp->mp_device.mdev_ppa == mdevp->mdev_ppa) { 677103831d35Sstevel 677203831d35Sstevel if (mpp != NULL) 677303831d35Sstevel *mpp = mp; 677403831d35Sstevel return (mpg); 677503831d35Sstevel } 677603831d35Sstevel } 677703831d35Sstevel } 677803831d35Sstevel 677903831d35Sstevel return (NULL); 678003831d35Sstevel } 678103831d35Sstevel 678203831d35Sstevel /* 678303831d35Sstevel * Return pointer to pathgroup assigned to destination, null if not found. 678403831d35Sstevel * 678503831d35Sstevel * Called holding man_lock. 678603831d35Sstevel */ 678703831d35Sstevel static man_pg_t * 678803831d35Sstevel man_find_pg_by_id(man_pg_t *mpg, int pg_id) 678903831d35Sstevel { 679003831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 679103831d35Sstevel for (; mpg != NULL; mpg = mpg->mpg_next) { 679203831d35Sstevel if (mpg->mpg_pg_id == pg_id) 679303831d35Sstevel return (mpg); 679403831d35Sstevel } 679503831d35Sstevel 679603831d35Sstevel return (NULL); 679703831d35Sstevel } 679803831d35Sstevel 679903831d35Sstevel static man_path_t * 680003831d35Sstevel man_find_path_by_ppa(man_path_t *mplist, int ppa) 680103831d35Sstevel { 680203831d35Sstevel man_path_t *mp; 680303831d35Sstevel 680403831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 680503831d35Sstevel for (mp = mplist; mp != NULL; mp = mp->mp_next) { 680603831d35Sstevel if (mp->mp_device.mdev_ppa == ppa) 680703831d35Sstevel return (mp); 680803831d35Sstevel } 680903831d35Sstevel 681003831d35Sstevel return (NULL); 681103831d35Sstevel } 681203831d35Sstevel 681303831d35Sstevel static man_path_t * 681403831d35Sstevel man_find_active_path(man_path_t *mplist) 681503831d35Sstevel { 681603831d35Sstevel man_path_t *mp; 681703831d35Sstevel 681803831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 681903831d35Sstevel for (mp = mplist; mp != NULL; mp = mp->mp_next) 682003831d35Sstevel if (mp->mp_device.mdev_state & MDEV_ACTIVE) 682103831d35Sstevel return (mp); 682203831d35Sstevel 682303831d35Sstevel return (NULL); 682403831d35Sstevel } 682503831d35Sstevel 682603831d35Sstevel /* 682703831d35Sstevel * Try and find an alternate path. 682803831d35Sstevel */ 682903831d35Sstevel static man_path_t * 683003831d35Sstevel man_find_alternate_path(man_path_t *mlp) 683103831d35Sstevel { 683203831d35Sstevel man_path_t *ap; /* Active path */ 683303831d35Sstevel man_path_t *np; /* New alternate path */ 683403831d35Sstevel man_path_t *fp = NULL; /* LRU failed path */ 683503831d35Sstevel 683603831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 683703831d35Sstevel ap = man_find_active_path(mlp); 683803831d35Sstevel 683903831d35Sstevel /* 684003831d35Sstevel * Find a non-failed path, or the lru failed path and switch to it. 684103831d35Sstevel */ 684203831d35Sstevel for (np = mlp; np != NULL; np = np->mp_next) { 684303831d35Sstevel if (np == ap) 684403831d35Sstevel continue; 684503831d35Sstevel 684603831d35Sstevel if (np->mp_device.mdev_state == MDEV_ASSIGNED) 684703831d35Sstevel goto exit; 684803831d35Sstevel 684903831d35Sstevel if (np->mp_device.mdev_state & MDEV_FAILED) { 685003831d35Sstevel if (fp == NULL) 685103831d35Sstevel fp = np; 685203831d35Sstevel else 685303831d35Sstevel if (fp->mp_lru > np->mp_lru) 685403831d35Sstevel fp = np; 685503831d35Sstevel } 685603831d35Sstevel } 685703831d35Sstevel 685803831d35Sstevel /* 685903831d35Sstevel * Nowhere to switch to. 686003831d35Sstevel */ 686103831d35Sstevel if (np == NULL && (np = fp) == NULL) 686203831d35Sstevel goto exit; 686303831d35Sstevel 686403831d35Sstevel exit: 686503831d35Sstevel return (np); 686603831d35Sstevel } 686703831d35Sstevel 686803831d35Sstevel /* 686903831d35Sstevel * Assumes caller has verified existence. 687003831d35Sstevel */ 687103831d35Sstevel static void 687203831d35Sstevel man_path_remove(man_path_t **lpp, man_path_t *mp) 687303831d35Sstevel { 687403831d35Sstevel man_path_t *tp; 687503831d35Sstevel man_path_t *tpp; 687603831d35Sstevel 687703831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 687803831d35Sstevel MAN_DBG(MAN_PATH, ("man_path_remove: removing path")); 687903831d35Sstevel MAN_DBGCALL(MAN_PATH, man_print_path(mp)); 688003831d35Sstevel 688103831d35Sstevel tp = tpp = *lpp; 688203831d35Sstevel if (tp == mp) { 688303831d35Sstevel *lpp = tp->mp_next; 688403831d35Sstevel goto exit; 688503831d35Sstevel } 688603831d35Sstevel 688703831d35Sstevel for (tp = tp->mp_next; tp != NULL; tp = tp->mp_next) { 688803831d35Sstevel if (tp == mp) 688903831d35Sstevel break; 689003831d35Sstevel tpp = tp; 689103831d35Sstevel } 689203831d35Sstevel 689303831d35Sstevel ASSERT(tp != NULL); 689403831d35Sstevel 689503831d35Sstevel tpp->mp_next = tp->mp_next; 689603831d35Sstevel tp->mp_next = NULL; 689703831d35Sstevel 689803831d35Sstevel exit: 689903831d35Sstevel man_path_kstat_uninit(tp); 690003831d35Sstevel man_kfree(tp, sizeof (man_path_t)); 690103831d35Sstevel 690203831d35Sstevel } 690303831d35Sstevel 690403831d35Sstevel /* 690503831d35Sstevel * Insert path into list, ascending order by ppa. 690603831d35Sstevel */ 690703831d35Sstevel static void 690803831d35Sstevel man_path_insert(man_path_t **lpp, man_path_t *mp) 690903831d35Sstevel { 691003831d35Sstevel man_path_t *tp; 691103831d35Sstevel man_path_t *tpp; 691203831d35Sstevel 691303831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 691403831d35Sstevel if (*lpp == NULL) { 691503831d35Sstevel *lpp = mp; 691603831d35Sstevel return; 691703831d35Sstevel } 691803831d35Sstevel 691903831d35Sstevel tp = tpp = *lpp; 692003831d35Sstevel if (tp->mp_device.mdev_ppa > mp->mp_device.mdev_ppa) { 692103831d35Sstevel mp->mp_next = tp; 692203831d35Sstevel *lpp = mp; 692303831d35Sstevel return; 692403831d35Sstevel } 692503831d35Sstevel 692603831d35Sstevel for (tp = tp->mp_next; tp != NULL; tp = tp->mp_next) { 692703831d35Sstevel if (tp->mp_device.mdev_ppa > mp->mp_device.mdev_ppa) 692803831d35Sstevel break; 692903831d35Sstevel tpp = tp; 693003831d35Sstevel } 693103831d35Sstevel 693203831d35Sstevel if (tp == NULL) { 693303831d35Sstevel tpp->mp_next = mp; 693403831d35Sstevel } else { 693503831d35Sstevel tpp->mp_next = mp; 693603831d35Sstevel mp->mp_next = tp; 693703831d35Sstevel } 693803831d35Sstevel } 693903831d35Sstevel 694003831d35Sstevel /* 694103831d35Sstevel * Merge npp into lpp, ascending order by ppa. Assumes no 694203831d35Sstevel * duplicates in either list. 694303831d35Sstevel */ 694403831d35Sstevel static void 694503831d35Sstevel man_path_merge(man_path_t **lpp, man_path_t *np) 694603831d35Sstevel { 694703831d35Sstevel man_path_t *tmp; 694803831d35Sstevel 694903831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 695003831d35Sstevel while (np != NULL) { 695103831d35Sstevel tmp = np; 695203831d35Sstevel np = np->mp_next; 695303831d35Sstevel tmp->mp_next = NULL; 695403831d35Sstevel 695503831d35Sstevel man_path_insert(lpp, tmp); 695603831d35Sstevel } 695703831d35Sstevel 695803831d35Sstevel } 695903831d35Sstevel 696003831d35Sstevel static int 696103831d35Sstevel man_path_kstat_init(man_path_t *mpp) 696203831d35Sstevel { 696303831d35Sstevel 696403831d35Sstevel kstat_named_t *dev_knp; 696503831d35Sstevel int status = 0; 696603831d35Sstevel 696703831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 696803831d35Sstevel MAN_DBG(MAN_PATH, ("man_path_kstat_init: mpp(0x%p)\n", (void *)mpp)); 696903831d35Sstevel 697003831d35Sstevel /* 697103831d35Sstevel * Create named kstats for accounting purposes. 697203831d35Sstevel */ 697303831d35Sstevel dev_knp = man_kzalloc(MAN_NUMSTATS * sizeof (kstat_named_t), 697403831d35Sstevel KM_NOSLEEP); 697503831d35Sstevel if (dev_knp == NULL) { 697603831d35Sstevel status = ENOMEM; 697703831d35Sstevel goto exit; 697803831d35Sstevel } 697903831d35Sstevel man_kstat_named_init(dev_knp, MAN_NUMSTATS); 698003831d35Sstevel mpp->mp_last_knp = dev_knp; 698103831d35Sstevel 698203831d35Sstevel exit: 698303831d35Sstevel 698403831d35Sstevel MAN_DBG(MAN_PATH, ("man_path_kstat_init: returns %d\n", status)); 698503831d35Sstevel 698603831d35Sstevel return (status); 698703831d35Sstevel } 698803831d35Sstevel 698903831d35Sstevel static void 699003831d35Sstevel man_path_kstat_uninit(man_path_t *mp) 699103831d35Sstevel { 699203831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 699303831d35Sstevel man_kfree(mp->mp_last_knp, MAN_NUMSTATS * sizeof (kstat_named_t)); 699403831d35Sstevel } 699503831d35Sstevel 699603831d35Sstevel /* 699703831d35Sstevel * man_work_alloc - allocate and initiate a work request structure 699803831d35Sstevel * 699903831d35Sstevel * type - type of request to allocate 700003831d35Sstevel * returns - success - ptr to an initialized work structure 700103831d35Sstevel * - failure - NULL 700203831d35Sstevel */ 700303831d35Sstevel man_work_t * 700403831d35Sstevel man_work_alloc(int type, int kmflag) 700503831d35Sstevel { 700603831d35Sstevel man_work_t *wp; 700703831d35Sstevel 700803831d35Sstevel wp = man_kzalloc(sizeof (man_work_t), kmflag); 700903831d35Sstevel if (wp == NULL) 701003831d35Sstevel goto exit; 701103831d35Sstevel 701203831d35Sstevel cv_init(&wp->mw_cv, NULL, CV_DRIVER, NULL); \ 701303831d35Sstevel wp->mw_type = type; 701403831d35Sstevel 701503831d35Sstevel exit: 701603831d35Sstevel return (wp); 701703831d35Sstevel } 701803831d35Sstevel 701903831d35Sstevel /* 702003831d35Sstevel * man_work_free - deallocate a work request structure 702103831d35Sstevel * 702203831d35Sstevel * wp - ptr to work structure to be freed 702303831d35Sstevel */ 702403831d35Sstevel void 702503831d35Sstevel man_work_free(man_work_t *wp) 702603831d35Sstevel { 702703831d35Sstevel cv_destroy(&wp->mw_cv); 702803831d35Sstevel man_kfree((void *)wp, sizeof (man_work_t)); 702903831d35Sstevel } 703003831d35Sstevel 703103831d35Sstevel /* 703203831d35Sstevel * Post work to a work queue. The man_bwork sleeps on 703303831d35Sstevel * man_bwork_q->q_cv, and work requesters may sleep on mw_cv. 703403831d35Sstevel * The man_lock is used to protect both cv's. 703503831d35Sstevel */ 703603831d35Sstevel void 703703831d35Sstevel man_work_add(man_workq_t *q, man_work_t *wp) 703803831d35Sstevel { 703903831d35Sstevel man_work_t *lp = q->q_work; 704003831d35Sstevel 704103831d35Sstevel if (lp) { 704203831d35Sstevel while (lp->mw_next != NULL) 704303831d35Sstevel lp = lp->mw_next; 704403831d35Sstevel 704503831d35Sstevel lp->mw_next = wp; 704603831d35Sstevel 704703831d35Sstevel } else { 704803831d35Sstevel q->q_work = wp; 704903831d35Sstevel } 705003831d35Sstevel 705103831d35Sstevel /* 705203831d35Sstevel * cv_signal for man_bwork_q, qenable for man_iwork_q 705303831d35Sstevel */ 705403831d35Sstevel if (q == man_bwork_q) { 705503831d35Sstevel cv_signal(&q->q_cv); 705603831d35Sstevel 705703831d35Sstevel } else { /* q == man_iwork_q */ 705803831d35Sstevel 705903831d35Sstevel if (man_ctl_wq != NULL) 706003831d35Sstevel qenable(man_ctl_wq); 706103831d35Sstevel } 706203831d35Sstevel 706303831d35Sstevel } 706403831d35Sstevel 706503831d35Sstevel /* <<<<<<<<<<<<<<<<<<<<<<< NDD SUPPORT FUNCTIONS >>>>>>>>>>>>>>>>>>> */ 706603831d35Sstevel /* 706703831d35Sstevel * ndd support functions to get/set parameters 706803831d35Sstevel */ 706903831d35Sstevel 707003831d35Sstevel /* 707103831d35Sstevel * Register each element of the parameter array with the 707203831d35Sstevel * named dispatch handler. Each element is loaded using 707303831d35Sstevel * nd_load() 707403831d35Sstevel * 707503831d35Sstevel * cnt - the number of elements present in the parameter array 707603831d35Sstevel */ 707703831d35Sstevel static int 707803831d35Sstevel man_param_register(param_t *manpa, int cnt) 707903831d35Sstevel { 708003831d35Sstevel int i; 708103831d35Sstevel ndgetf_t getp; 708203831d35Sstevel ndsetf_t setp; 708303831d35Sstevel int status = B_TRUE; 708403831d35Sstevel 708503831d35Sstevel MAN_DBG(MAN_CONFIG, ("man_param_register: manpa(0x%p) cnt %d\n", 708603831d35Sstevel (void *)manpa, cnt)); 708703831d35Sstevel 708803831d35Sstevel getp = man_param_get; 708903831d35Sstevel 709003831d35Sstevel for (i = 0; i < cnt; i++, manpa++) { 709103831d35Sstevel switch (man_param_display[i]) { 709203831d35Sstevel case MAN_NDD_GETABLE: 709303831d35Sstevel setp = NULL; 709403831d35Sstevel break; 709503831d35Sstevel 709603831d35Sstevel case MAN_NDD_SETABLE: 709703831d35Sstevel setp = man_param_set; 709803831d35Sstevel break; 709903831d35Sstevel 710003831d35Sstevel default: 710103831d35Sstevel continue; 710203831d35Sstevel } 710303831d35Sstevel 710403831d35Sstevel if (!nd_load(&man_ndlist, manpa->param_name, getp, 710503831d35Sstevel setp, (caddr_t)manpa)) { 710603831d35Sstevel 710703831d35Sstevel (void) man_nd_free(&man_ndlist); 710803831d35Sstevel status = B_FALSE; 710903831d35Sstevel goto exit; 711003831d35Sstevel } 711103831d35Sstevel } 711203831d35Sstevel 711303831d35Sstevel if (!nd_load(&man_ndlist, "man_pathgroups_report", 711403831d35Sstevel man_pathgroups_report, NULL, NULL)) { 711503831d35Sstevel 711603831d35Sstevel (void) man_nd_free(&man_ndlist); 711703831d35Sstevel status = B_FALSE; 711803831d35Sstevel goto exit; 711903831d35Sstevel } 712003831d35Sstevel 712103831d35Sstevel if (!nd_load(&man_ndlist, "man_set_active_path", 712203831d35Sstevel NULL, man_set_active_path, NULL)) { 712303831d35Sstevel 712403831d35Sstevel (void) man_nd_free(&man_ndlist); 712503831d35Sstevel status = B_FALSE; 712603831d35Sstevel goto exit; 712703831d35Sstevel } 712803831d35Sstevel 712903831d35Sstevel if (!nd_load(&man_ndlist, "man_get_hostinfo", 713003831d35Sstevel man_get_hostinfo, NULL, NULL)) { 713103831d35Sstevel 713203831d35Sstevel (void) man_nd_free(&man_ndlist); 713303831d35Sstevel status = B_FALSE; 713403831d35Sstevel goto exit; 713503831d35Sstevel } 713603831d35Sstevel 713703831d35Sstevel exit: 713803831d35Sstevel 713903831d35Sstevel MAN_DBG(MAN_CONFIG, ("man_param_register: returns %d\n", status)); 714003831d35Sstevel 714103831d35Sstevel return (status); 714203831d35Sstevel } 714303831d35Sstevel 714403831d35Sstevel static void 714503831d35Sstevel man_nd_getset(queue_t *wq, mblk_t *mp) 714603831d35Sstevel { 714703831d35Sstevel 714803831d35Sstevel if (!nd_getset(wq, man_ndlist, mp)) 714903831d35Sstevel miocnak(wq, mp, 0, ENOENT); 715003831d35Sstevel else 715103831d35Sstevel qreply(wq, mp); 715203831d35Sstevel } 715303831d35Sstevel 715403831d35Sstevel /*ARGSUSED*/ 715503831d35Sstevel static int 715603831d35Sstevel man_pathgroups_report(queue_t *wq, mblk_t *mp, caddr_t cp, cred_t *cr) 715703831d35Sstevel { 715803831d35Sstevel 715903831d35Sstevel man_t *manp; 716003831d35Sstevel man_pg_t *mpg; 716103831d35Sstevel int i; 716203831d35Sstevel char pad[] = " "; /* 17 spaces */ 716303831d35Sstevel int pad_end; 716403831d35Sstevel 716503831d35Sstevel 716603831d35Sstevel MAN_DBG(MAN_PATH, ("man_pathgroups_report: wq(0x%p) mp(0x%p)" 716703831d35Sstevel " caddr 0x%p", (void *)wq, (void *)mp, (void *)cp)); 716803831d35Sstevel 716903831d35Sstevel (void) mi_mpprintf(mp, "MAN Pathgroup report: (* == failed)"); 717003831d35Sstevel (void) mi_mpprintf(mp, "=====================================" 717103831d35Sstevel "=========================================="); 717203831d35Sstevel 717303831d35Sstevel mutex_enter(&man_lock); 717403831d35Sstevel 717503831d35Sstevel for (i = 0; i < 2; i++) { 717603831d35Sstevel manp = ddi_get_soft_state(man_softstate, i); 717703831d35Sstevel if (manp == NULL) 717803831d35Sstevel continue; 717903831d35Sstevel 718003831d35Sstevel (void) mi_mpprintf(mp, 718103831d35Sstevel "Interface\tDestination\t\tActive Path\tAlternate Paths"); 718203831d35Sstevel (void) mi_mpprintf(mp, "---------------------------------------" 718303831d35Sstevel "----------------------------------------"); 718403831d35Sstevel 718503831d35Sstevel for (mpg = manp->man_pg; mpg != NULL; mpg = mpg->mpg_next) { 718603831d35Sstevel 718703831d35Sstevel (void) mi_mpprintf(mp, "%s%d\t\t", 718803831d35Sstevel ddi_major_to_name(manp->man_meta_major), 718903831d35Sstevel manp->man_meta_ppa); 719003831d35Sstevel 719103831d35Sstevel if (man_is_on_domain) { 719203831d35Sstevel (void) mi_mpprintf_nr(mp, "Master SSC\t"); 719303831d35Sstevel man_preport(mpg->mpg_pathp, mp); 719403831d35Sstevel } else { 719503831d35Sstevel if (i == 0) { 719603831d35Sstevel pad_end = 17 - strlen(ether_sprintf( 719703831d35Sstevel &mpg->mpg_dst_eaddr)); 719803831d35Sstevel if (pad_end < 0 || pad_end > 16) 719903831d35Sstevel pad_end = 0; 720003831d35Sstevel pad[pad_end] = '\0'; 720103831d35Sstevel 720203831d35Sstevel (void) mi_mpprintf_nr(mp, "%c %s%s", 720303831d35Sstevel mpg->mpg_pg_id + 'A', 720403831d35Sstevel ether_sprintf(&mpg->mpg_dst_eaddr), 720503831d35Sstevel pad); 720603831d35Sstevel 720703831d35Sstevel pad[pad_end] = ' '; 720803831d35Sstevel } else { 720919397407SSherry Moore (void) mi_mpprintf_nr(mp, 721019397407SSherry Moore "Other SSC\t"); 721103831d35Sstevel } 721203831d35Sstevel man_preport(mpg->mpg_pathp, mp); 721303831d35Sstevel } 721403831d35Sstevel (void) mi_mpprintf_nr(mp, "\n"); 721503831d35Sstevel } 721603831d35Sstevel } 721703831d35Sstevel 721803831d35Sstevel mutex_exit(&man_lock); 721903831d35Sstevel MAN_DBG(MAN_PATH, ("man_pathgroups_report: returns")); 722003831d35Sstevel 722103831d35Sstevel return (0); 722203831d35Sstevel } 722303831d35Sstevel 722403831d35Sstevel static void 722503831d35Sstevel man_preport(man_path_t *plist, mblk_t *mp) 722603831d35Sstevel { 722703831d35Sstevel man_path_t *ap; 722803831d35Sstevel 722903831d35Sstevel ap = man_find_active_path(plist); 723003831d35Sstevel /* 723103831d35Sstevel * Active path 723203831d35Sstevel */ 723303831d35Sstevel if (ap != NULL) { 723403831d35Sstevel (void) mi_mpprintf_nr(mp, "\t%s%d\t\t", 723503831d35Sstevel ddi_major_to_name(ap->mp_device.mdev_major), 723603831d35Sstevel ap->mp_device.mdev_ppa); 723703831d35Sstevel } else { 723803831d35Sstevel (void) mi_mpprintf_nr(mp, "None \t"); 723903831d35Sstevel } 724003831d35Sstevel 724103831d35Sstevel /* 724203831d35Sstevel * Alternate Paths. 724303831d35Sstevel */ 724403831d35Sstevel while (plist != NULL) { 724503831d35Sstevel (void) mi_mpprintf_nr(mp, "%s%d exp %d", 724603831d35Sstevel ddi_major_to_name(plist->mp_device.mdev_major), 724703831d35Sstevel plist->mp_device.mdev_ppa, 724803831d35Sstevel plist->mp_device.mdev_exp_id); 724903831d35Sstevel if (plist->mp_device.mdev_state & MDEV_FAILED) 725003831d35Sstevel (void) mi_mpprintf_nr(mp, "*"); 725103831d35Sstevel plist = plist->mp_next; 725203831d35Sstevel if (plist) 725303831d35Sstevel (void) mi_mpprintf_nr(mp, ", "); 725403831d35Sstevel } 725503831d35Sstevel } 725603831d35Sstevel 725703831d35Sstevel /* 725803831d35Sstevel * NDD request to set active path. Calling context is man_ioctl, so we are 725903831d35Sstevel * exclusive in the inner perimeter. 726003831d35Sstevel * 726103831d35Sstevel * Syntax is "ndd -set /dev/dman <man ppa> <pg_id> <phys ppa>" 726203831d35Sstevel */ 726303831d35Sstevel /* ARGSUSED3 */ 726403831d35Sstevel static int 726503831d35Sstevel man_set_active_path(queue_t *wq, mblk_t *mp, char *value, caddr_t cp, 726603831d35Sstevel cred_t *cr) 726703831d35Sstevel { 726803831d35Sstevel char *end, *meta_ppap, *phys_ppap, *pg_idp; 726903831d35Sstevel int meta_ppa; 727003831d35Sstevel int phys_ppa; 727103831d35Sstevel int pg_id; 727203831d35Sstevel man_t *manp; 727303831d35Sstevel man_pg_t *mpg; 727403831d35Sstevel man_path_t *np; 727503831d35Sstevel mi_path_t mpath; 727603831d35Sstevel int status = 0; 727703831d35Sstevel 727803831d35Sstevel MAN_DBG(MAN_PATH, ("man_set_active_path: wq(0x%p) mp(0x%p)" 727903831d35Sstevel " args %s", (void *)wq, (void *)mp, value)); 728003831d35Sstevel 728103831d35Sstevel meta_ppap = value; 728203831d35Sstevel 728303831d35Sstevel if ((pg_idp = strchr(value, ' ')) == NULL) { 728403831d35Sstevel status = EINVAL; 728503831d35Sstevel goto exit; 728603831d35Sstevel } 728703831d35Sstevel 728803831d35Sstevel *pg_idp++ = '\0'; 728903831d35Sstevel 729003831d35Sstevel if ((phys_ppap = strchr(pg_idp, ' ')) == NULL) { 729103831d35Sstevel status = EINVAL; 729203831d35Sstevel goto exit; 729303831d35Sstevel } 729403831d35Sstevel 729503831d35Sstevel *phys_ppap++ = '\0'; 729603831d35Sstevel 729703831d35Sstevel meta_ppa = (int)mi_strtol(meta_ppap, &end, 10); 729803831d35Sstevel pg_id = (int)mi_strtol(pg_idp, &end, 10); 729903831d35Sstevel phys_ppa = (int)mi_strtol(phys_ppap, &end, 10); 730003831d35Sstevel 730103831d35Sstevel mutex_enter(&man_lock); 730203831d35Sstevel manp = ddi_get_soft_state(man_softstate, meta_ppa); 730303831d35Sstevel if (manp == NULL || manp->man_pg == NULL) { 730403831d35Sstevel status = EINVAL; 730503831d35Sstevel mutex_exit(&man_lock); 730603831d35Sstevel goto exit; 730703831d35Sstevel } 730803831d35Sstevel 730903831d35Sstevel mpg = man_find_pg_by_id(manp->man_pg, pg_id); 731003831d35Sstevel if (mpg == NULL) { 731103831d35Sstevel status = EINVAL; 731203831d35Sstevel mutex_exit(&man_lock); 731303831d35Sstevel goto exit; 731403831d35Sstevel } 731503831d35Sstevel 731603831d35Sstevel np = man_find_path_by_ppa(mpg->mpg_pathp, phys_ppa); 731703831d35Sstevel 731803831d35Sstevel if (np == NULL) { 731903831d35Sstevel status = EINVAL; 732003831d35Sstevel mutex_exit(&man_lock); 732103831d35Sstevel goto exit; 732203831d35Sstevel } 732303831d35Sstevel 732403831d35Sstevel mpath.mip_cmd = MI_PATH_ACTIVATE; 732503831d35Sstevel mpath.mip_pg_id = pg_id; 732603831d35Sstevel mpath.mip_man_ppa = meta_ppa; 732703831d35Sstevel mpath.mip_devs[0] = np->mp_device; 732803831d35Sstevel mpath.mip_ndevs = 1; 732903831d35Sstevel 733003831d35Sstevel status = man_pg_cmd(&mpath, NULL); 733103831d35Sstevel mutex_exit(&man_lock); 733203831d35Sstevel 733303831d35Sstevel exit: 733403831d35Sstevel 733503831d35Sstevel MAN_DBG(MAN_PATH, ("man_set_active_path: returns %d", status)); 733603831d35Sstevel 733703831d35Sstevel return (status); 733803831d35Sstevel } 733903831d35Sstevel 734003831d35Sstevel /* 734103831d35Sstevel * Dump out the contents of the IOSRAM handoff structure. Note that if 734203831d35Sstevel * anything changes here, you must make sure that the sysinit script 734303831d35Sstevel * stays in sync with this output. 734403831d35Sstevel */ 734503831d35Sstevel /* ARGSUSED */ 734603831d35Sstevel static int 734703831d35Sstevel man_get_hostinfo(queue_t *wq, mblk_t *mp, caddr_t cp, cred_t *cr) 734803831d35Sstevel { 734903831d35Sstevel manc_t manc; 735003831d35Sstevel char *ipaddr; 735103831d35Sstevel char ipv6addr[INET6_ADDRSTRLEN]; 735203831d35Sstevel int i; 735303831d35Sstevel int status; 735403831d35Sstevel 735503831d35Sstevel if (!man_is_on_domain) 735603831d35Sstevel return (0); 735703831d35Sstevel 735803831d35Sstevel if (status = man_get_iosram(&manc)) { 735903831d35Sstevel return (status); 736003831d35Sstevel } 736103831d35Sstevel 7362*07d06da5SSurya Prakki (void) mi_mpprintf(mp, "manc_magic = 0x%x", manc.manc_magic); 7363*07d06da5SSurya Prakki (void) mi_mpprintf(mp, "manc_version = 0%d", manc.manc_version); 7364*07d06da5SSurya Prakki (void) mi_mpprintf(mp, "manc_csum = 0x%x", manc.manc_csum); 736503831d35Sstevel 736603831d35Sstevel if (manc.manc_ip_type == AF_INET) { 736703831d35Sstevel in_addr_t netnum; 736803831d35Sstevel 7369*07d06da5SSurya Prakki (void) mi_mpprintf(mp, "manc_ip_type = AF_INET"); 737003831d35Sstevel 737103831d35Sstevel ipaddr = man_inet_ntoa(manc.manc_dom_ipaddr); 7372*07d06da5SSurya Prakki (void) mi_mpprintf(mp, "manc_dom_ipaddr = %s", ipaddr); 737303831d35Sstevel 737403831d35Sstevel ipaddr = man_inet_ntoa(manc.manc_dom_ip_netmask); 7375*07d06da5SSurya Prakki (void) mi_mpprintf(mp, "manc_dom_ip_netmask = %s", ipaddr); 737603831d35Sstevel 737703831d35Sstevel netnum = manc.manc_dom_ipaddr & manc.manc_dom_ip_netmask; 737803831d35Sstevel ipaddr = man_inet_ntoa(netnum); 7379*07d06da5SSurya Prakki (void) mi_mpprintf(mp, "manc_dom_ip_netnum = %s", ipaddr); 738003831d35Sstevel 738103831d35Sstevel ipaddr = man_inet_ntoa(manc.manc_sc_ipaddr); 7382*07d06da5SSurya Prakki (void) mi_mpprintf(mp, "manc_sc_ipaddr = %s", ipaddr); 738303831d35Sstevel 738403831d35Sstevel } else if (manc.manc_ip_type == AF_INET6) { 738503831d35Sstevel 7386*07d06da5SSurya Prakki (void) mi_mpprintf(mp, "manc_ip_type = AF_INET6"); 738703831d35Sstevel 738803831d35Sstevel (void) inet_ntop(AF_INET6, (void *)&manc.manc_dom_ipv6addr, 738903831d35Sstevel ipv6addr, INET6_ADDRSTRLEN); 7390*07d06da5SSurya Prakki (void) mi_mpprintf(mp, "manc_dom_ipv6addr = %s", ipv6addr); 739103831d35Sstevel 7392*07d06da5SSurya Prakki (void) mi_mpprintf(mp, "manc_dom_ipv6_netmask = %d", 739303831d35Sstevel manc.manc_dom_ipv6_netmask.s6_addr[0]); 739403831d35Sstevel 739503831d35Sstevel (void) inet_ntop(AF_INET6, (void *)&manc.manc_sc_ipv6addr, 739603831d35Sstevel ipv6addr, INET6_ADDRSTRLEN); 7397*07d06da5SSurya Prakki (void) mi_mpprintf(mp, "manc_sc_ipv6addr = %s", ipv6addr); 739803831d35Sstevel 739903831d35Sstevel } else { 740003831d35Sstevel 7401*07d06da5SSurya Prakki (void) mi_mpprintf(mp, "manc_ip_type = NONE"); 740203831d35Sstevel } 740303831d35Sstevel 7404*07d06da5SSurya Prakki (void) mi_mpprintf(mp, "manc_dom_eaddr = %s", 740503831d35Sstevel ether_sprintf(&manc.manc_dom_eaddr)); 7406*07d06da5SSurya Prakki (void) mi_mpprintf(mp, "manc_sc_eaddr = %s", 740703831d35Sstevel ether_sprintf(&manc.manc_sc_eaddr)); 740803831d35Sstevel 7409*07d06da5SSurya Prakki (void) mi_mpprintf(mp, "manc_iob_bitmap = 0x%x\tio boards = ", 741003831d35Sstevel manc.manc_iob_bitmap); 741103831d35Sstevel for (i = 0; i < MAN_MAX_EXPANDERS; i++) { 741203831d35Sstevel if ((manc.manc_iob_bitmap >> i) & 0x1) { 7413*07d06da5SSurya Prakki (void) mi_mpprintf_nr(mp, "%d.1, ", i); 741403831d35Sstevel } 741503831d35Sstevel } 7416*07d06da5SSurya Prakki (void) mi_mpprintf(mp, "manc_golden_iob = %d", manc.manc_golden_iob); 741703831d35Sstevel 741803831d35Sstevel return (0); 741903831d35Sstevel } 742003831d35Sstevel 742103831d35Sstevel static char * 742203831d35Sstevel man_inet_ntoa(in_addr_t in) 742303831d35Sstevel { 742403831d35Sstevel static char b[18]; 742503831d35Sstevel unsigned char *p; 742603831d35Sstevel 742703831d35Sstevel p = (unsigned char *)∈ 742803831d35Sstevel (void) sprintf(b, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); 742903831d35Sstevel return (b); 743003831d35Sstevel } 743103831d35Sstevel 743203831d35Sstevel /* 743303831d35Sstevel * parameter value. cp points to the required parameter. 743403831d35Sstevel */ 743503831d35Sstevel /* ARGSUSED */ 743603831d35Sstevel static int 743703831d35Sstevel man_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *cr) 743803831d35Sstevel { 743903831d35Sstevel param_t *manpa = (param_t *)cp; 744003831d35Sstevel 744103831d35Sstevel (void) mi_mpprintf(mp, "%u", manpa->param_val); 744203831d35Sstevel return (0); 744303831d35Sstevel } 744403831d35Sstevel 744503831d35Sstevel /* 744603831d35Sstevel * Sets the man parameter to the value in the param_register using 744703831d35Sstevel * nd_load(). 744803831d35Sstevel */ 744903831d35Sstevel /* ARGSUSED */ 745003831d35Sstevel static int 745103831d35Sstevel man_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, cred_t *cr) 745203831d35Sstevel { 745303831d35Sstevel char *end; 745403831d35Sstevel size_t new_value; 745503831d35Sstevel param_t *manpa = (param_t *)cp; 745603831d35Sstevel 745703831d35Sstevel new_value = mi_strtol(value, &end, 10); 745803831d35Sstevel 745903831d35Sstevel if (end == value || new_value < manpa->param_min || 746003831d35Sstevel new_value > manpa->param_max) { 746103831d35Sstevel return (EINVAL); 746203831d35Sstevel } 746303831d35Sstevel 746403831d35Sstevel manpa->param_val = new_value; 746503831d35Sstevel 746603831d35Sstevel return (0); 746703831d35Sstevel 746803831d35Sstevel } 746903831d35Sstevel 747003831d35Sstevel /* 747103831d35Sstevel * Free the Named Dispatch Table by calling man_nd_free 747203831d35Sstevel */ 747303831d35Sstevel static void 747403831d35Sstevel man_param_cleanup() 747503831d35Sstevel { 747603831d35Sstevel if (man_ndlist != NULL) 747703831d35Sstevel nd_free(&man_ndlist); 747803831d35Sstevel } 747903831d35Sstevel 748003831d35Sstevel /* 748103831d35Sstevel * Free the table pointed to by 'ndp' 748203831d35Sstevel */ 748303831d35Sstevel static void 748403831d35Sstevel man_nd_free(caddr_t *nd_pparam) 748503831d35Sstevel { 748603831d35Sstevel ND *nd; 748703831d35Sstevel 748803831d35Sstevel if ((nd = (ND *)(*nd_pparam)) != NULL) { 748903831d35Sstevel if (nd->nd_tbl) 749003831d35Sstevel mi_free((char *)nd->nd_tbl); 749103831d35Sstevel mi_free((char *)nd); 749203831d35Sstevel *nd_pparam = NULL; 749303831d35Sstevel } 749403831d35Sstevel } 749503831d35Sstevel 749603831d35Sstevel 749703831d35Sstevel /* 749803831d35Sstevel * man_kstat_update - update the statistics for a meta-interface. 749903831d35Sstevel * 750003831d35Sstevel * ksp - kstats struct 750103831d35Sstevel * rw - flag indicating whether stats are to be read or written. 750203831d35Sstevel * 750303831d35Sstevel * returns 0 750403831d35Sstevel * 750503831d35Sstevel * The destination specific kstat information is protected by the 750603831d35Sstevel * perimeter lock, so we submit a work request to get the stats 750703831d35Sstevel * updated (see man_do_kstats()), and then collect the results 750803831d35Sstevel * when cv_signal'd. Note that we are doing cv_timedwait_sig() 750903831d35Sstevel * as a precautionary measure only. 751003831d35Sstevel */ 751103831d35Sstevel static int 751203831d35Sstevel man_kstat_update(kstat_t *ksp, int rw) 751303831d35Sstevel { 751403831d35Sstevel man_t *manp; /* per instance data */ 751503831d35Sstevel man_work_t *wp; 751603831d35Sstevel int status = 0; 751703831d35Sstevel kstat_named_t *knp; 751803831d35Sstevel kstat_named_t *man_knp; 751903831d35Sstevel int i; 752003831d35Sstevel 752103831d35Sstevel MAN_DBG(MAN_KSTAT, ("man_kstat_update: %s\n", rw ? "KSTAT_WRITE" : 752203831d35Sstevel "KSTAT_READ")); 752303831d35Sstevel 752403831d35Sstevel mutex_enter(&man_lock); 752503831d35Sstevel manp = (man_t *)ksp->ks_private; 752603831d35Sstevel manp->man_refcnt++; 752703831d35Sstevel 752803831d35Sstevel /* 752903831d35Sstevel * If the driver has been configured, get kstats updated by inner 753003831d35Sstevel * perimeter prior to retrieving. 753103831d35Sstevel */ 753203831d35Sstevel if (man_config_state == MAN_CONFIGURED) { 753303831d35Sstevel clock_t wait_status; 753403831d35Sstevel 753503831d35Sstevel man_update_path_kstats(manp); 753603831d35Sstevel wp = man_work_alloc(MAN_WORK_KSTAT_UPDATE, KM_SLEEP); 753703831d35Sstevel wp->mw_arg.a_man_ppa = manp->man_meta_ppa; 753803831d35Sstevel wp->mw_flags = MAN_WFLAGS_CVWAITER; 753903831d35Sstevel man_work_add(man_iwork_q, wp); 754003831d35Sstevel 7541d3d50737SRafael Vanoni wait_status = cv_reltimedwait_sig(&wp->mw_cv, &man_lock, 7542d3d50737SRafael Vanoni drv_usectohz(manp->man_kstat_waittime), TR_CLOCK_TICK); 754303831d35Sstevel 754403831d35Sstevel if (wp->mw_flags & MAN_WFLAGS_DONE) { 754503831d35Sstevel status = wp->mw_status; 754603831d35Sstevel man_work_free(wp); 754703831d35Sstevel } else { 754803831d35Sstevel ASSERT(wait_status <= 0); 754903831d35Sstevel wp->mw_flags &= ~MAN_WFLAGS_CVWAITER; 755003831d35Sstevel if (wait_status == 0) 755103831d35Sstevel status = EINTR; 755203831d35Sstevel else { 755303831d35Sstevel MAN_DBG(MAN_KSTAT, ("man_kstat_update: " 755403831d35Sstevel "timedout, returning stale stats.")); 755503831d35Sstevel status = 0; 755603831d35Sstevel } 755703831d35Sstevel } 755803831d35Sstevel if (status) 755903831d35Sstevel goto exit; 756003831d35Sstevel } 756103831d35Sstevel 756203831d35Sstevel knp = (kstat_named_t *)ksp->ks_data; 756303831d35Sstevel man_knp = (kstat_named_t *)manp->man_ksp->ks_data; 756403831d35Sstevel 756503831d35Sstevel if (rw == KSTAT_READ) { 756603831d35Sstevel for (i = 0; i < MAN_NUMSTATS; i++) { 756703831d35Sstevel knp[i].value.ui64 = man_knp[i].value.ui64; 756803831d35Sstevel } 756903831d35Sstevel } else { 757003831d35Sstevel for (i = 0; i < MAN_NUMSTATS; i++) { 757103831d35Sstevel man_knp[i].value.ui64 = knp[i].value.ui64; 757203831d35Sstevel } 757303831d35Sstevel } 757403831d35Sstevel 757503831d35Sstevel exit: 757603831d35Sstevel manp->man_refcnt--; 757703831d35Sstevel mutex_exit(&man_lock); 757803831d35Sstevel 757903831d35Sstevel MAN_DBG(MAN_KSTAT, ("man_kstat_update: returns %d", status)); 758003831d35Sstevel 758103831d35Sstevel return (status); 758203831d35Sstevel } 758303831d35Sstevel 758403831d35Sstevel /* 758503831d35Sstevel * Sum destination kstats for all active paths for a given instance of the 758603831d35Sstevel * MAN driver. Called with perimeter lock. 758703831d35Sstevel */ 758803831d35Sstevel static void 758903831d35Sstevel man_do_kstats(man_work_t *wp) 759003831d35Sstevel { 759103831d35Sstevel man_t *manp; 759203831d35Sstevel man_pg_t *mpg; 759303831d35Sstevel man_path_t *mp; 759403831d35Sstevel 759503831d35Sstevel MAN_DBG(MAN_KSTAT, ("man_do_kstats:")); 759603831d35Sstevel 759703831d35Sstevel mutex_enter(&man_lock); 759803831d35Sstevel /* 759903831d35Sstevel * Sync mp_last_knp for each path associated with the MAN instance. 760003831d35Sstevel */ 760103831d35Sstevel manp = (man_t *)ddi_get_soft_state(man_softstate, 760203831d35Sstevel wp->mw_arg.a_man_ppa); 760303831d35Sstevel for (mpg = manp->man_pg; mpg != NULL; mpg = mpg->mpg_next) { 760403831d35Sstevel 760503831d35Sstevel ASSERT(mpg->mpg_man_ppa == manp->man_meta_ppa); 760603831d35Sstevel 760703831d35Sstevel if ((mp = man_find_active_path(mpg->mpg_pathp)) != NULL) { 760803831d35Sstevel 760903831d35Sstevel MAN_DBG(MAN_KSTAT, ("\tkstat: path")); 761003831d35Sstevel MAN_DBGCALL(MAN_KSTAT, man_print_path(mp)); 761103831d35Sstevel 761203831d35Sstevel /* 761303831d35Sstevel * We just to update the destination statistics here. 761403831d35Sstevel */ 761503831d35Sstevel man_sum_dests_kstats(mp->mp_last_knp, mpg); 761603831d35Sstevel } 761703831d35Sstevel } 761803831d35Sstevel mutex_exit(&man_lock); 761903831d35Sstevel MAN_DBG(MAN_KSTAT, ("man_do_kstats: returns")); 762003831d35Sstevel } 762103831d35Sstevel 762203831d35Sstevel /* 762303831d35Sstevel * Sum device kstats for all active paths for a given instance of the 762403831d35Sstevel * MAN driver. Called with man_lock. 762503831d35Sstevel */ 762603831d35Sstevel static void 762703831d35Sstevel man_update_path_kstats(man_t *manp) 762803831d35Sstevel { 762903831d35Sstevel kstat_named_t *man_knp; 763003831d35Sstevel man_pg_t *mpg; 763103831d35Sstevel man_path_t *mp; 763203831d35Sstevel 763303831d35Sstevel ASSERT(MUTEX_HELD(&man_lock)); 763403831d35Sstevel MAN_DBG(MAN_KSTAT, ("man_update_path_kstats:")); 763503831d35Sstevel 763603831d35Sstevel man_knp = (kstat_named_t *)manp->man_ksp->ks_data; 763703831d35Sstevel 763803831d35Sstevel for (mpg = manp->man_pg; mpg != NULL; mpg = mpg->mpg_next) { 763903831d35Sstevel 764003831d35Sstevel ASSERT(mpg->mpg_man_ppa == manp->man_meta_ppa); 764103831d35Sstevel 764203831d35Sstevel if ((mp = man_find_active_path(mpg->mpg_pathp)) != NULL) { 764303831d35Sstevel 764403831d35Sstevel man_update_dev_kstats(man_knp, mp); 764503831d35Sstevel 764603831d35Sstevel } 764703831d35Sstevel } 764803831d35Sstevel MAN_DBG(MAN_KSTAT, ("man_update_path_kstats: returns")); 764903831d35Sstevel } 765003831d35Sstevel 765103831d35Sstevel /* 765203831d35Sstevel * Update the device kstats. 765303831d35Sstevel * As man_kstat_update() is called with kstat_chain_lock held, 765403831d35Sstevel * we can safely update the statistics from the underlying driver here. 765503831d35Sstevel */ 765603831d35Sstevel static void 765703831d35Sstevel man_update_dev_kstats(kstat_named_t *man_knp, man_path_t *mp) 765803831d35Sstevel { 765903831d35Sstevel kstat_t *dev_ksp; 766003831d35Sstevel major_t major; 766103831d35Sstevel int instance; 766203831d35Sstevel char buf[KSTAT_STRLEN]; 766303831d35Sstevel 766403831d35Sstevel 766503831d35Sstevel major = mp->mp_device.mdev_major; 766603831d35Sstevel instance = mp->mp_device.mdev_ppa; 766703831d35Sstevel (void) sprintf(buf, "%s%d", ddi_major_to_name(major), instance); 766803831d35Sstevel 766903831d35Sstevel dev_ksp = kstat_hold_byname(ddi_major_to_name(major), instance, buf, 767003831d35Sstevel ALL_ZONES); 767103831d35Sstevel if (dev_ksp != NULL) { 767203831d35Sstevel 767303831d35Sstevel KSTAT_ENTER(dev_ksp); 767403831d35Sstevel KSTAT_UPDATE(dev_ksp, KSTAT_READ); 767503831d35Sstevel man_sum_kstats(man_knp, dev_ksp, mp->mp_last_knp); 767603831d35Sstevel KSTAT_EXIT(dev_ksp); 767703831d35Sstevel kstat_rele(dev_ksp); 767803831d35Sstevel 767903831d35Sstevel } else { 768003831d35Sstevel MAN_DBG(MAN_KSTAT, 768103831d35Sstevel ("man_update_dev_kstats: no kstat data found for %s(%d,%d)", 768203831d35Sstevel buf, major, instance)); 768303831d35Sstevel } 768403831d35Sstevel } 768503831d35Sstevel 768603831d35Sstevel static void 768703831d35Sstevel man_sum_dests_kstats(kstat_named_t *knp, man_pg_t *mpg) 768803831d35Sstevel { 768903831d35Sstevel int i; 769003831d35Sstevel int flags; 769103831d35Sstevel char *statname; 769203831d35Sstevel manstr_t *msp; 769303831d35Sstevel man_dest_t *mdp; 769403831d35Sstevel uint64_t switches = 0; 769503831d35Sstevel uint64_t linkfails = 0; 769603831d35Sstevel uint64_t linkstales = 0; 769703831d35Sstevel uint64_t icmpv4probes = 0; 769803831d35Sstevel uint64_t icmpv6probes = 0; 769903831d35Sstevel 770003831d35Sstevel MAN_DBG(MAN_KSTAT, ("man_sum_dests_kstats: mpg 0x%p", (void *)mpg)); 770103831d35Sstevel 770203831d35Sstevel for (msp = man_strup; msp != NULL; msp = msp->ms_next) { 770303831d35Sstevel 770403831d35Sstevel if (!man_str_uses_pg(msp, mpg)) 770503831d35Sstevel continue; 770603831d35Sstevel 770703831d35Sstevel mdp = &msp->ms_dests[mpg->mpg_pg_id]; 770803831d35Sstevel 770903831d35Sstevel switches += mdp->md_switches; 771003831d35Sstevel linkfails += mdp->md_linkfails; 771103831d35Sstevel linkstales += mdp->md_linkstales; 771203831d35Sstevel icmpv4probes += mdp->md_icmpv4probes; 771303831d35Sstevel icmpv6probes += mdp->md_icmpv6probes; 771403831d35Sstevel } 771503831d35Sstevel 771603831d35Sstevel for (i = 0; i < MAN_NUMSTATS; i++) { 771703831d35Sstevel 771803831d35Sstevel statname = man_kstat_info[i].mk_name; 771903831d35Sstevel flags = man_kstat_info[i].mk_flags; 772003831d35Sstevel 772103831d35Sstevel if (!(flags & MK_NOT_PHYSICAL)) 772203831d35Sstevel continue; 772303831d35Sstevel 772403831d35Sstevel if (strcmp(statname, "man_switches") == 0) { 772503831d35Sstevel knp[i].value.ui64 = switches; 772603831d35Sstevel } else if (strcmp(statname, "man_link_fails") == 0) { 772703831d35Sstevel knp[i].value.ui64 = linkfails; 772803831d35Sstevel } else if (strcmp(statname, "man_link_stales") == 0) { 772903831d35Sstevel knp[i].value.ui64 = linkstales; 773003831d35Sstevel } else if (strcmp(statname, "man_icmpv4_probes") == 0) { 773103831d35Sstevel knp[i].value.ui64 = icmpv4probes; 773203831d35Sstevel } else if (strcmp(statname, "man_icmpv6_probes") == 0) { 773303831d35Sstevel knp[i].value.ui64 = icmpv6probes; 773403831d35Sstevel } 773503831d35Sstevel } 773603831d35Sstevel 773703831d35Sstevel MAN_DBG(MAN_KSTAT, ("man_sum_dests_kstats: returns")); 773803831d35Sstevel } 773903831d35Sstevel 774003831d35Sstevel /* 774103831d35Sstevel * Initialize MAN named kstats in the space provided. 774203831d35Sstevel */ 774303831d35Sstevel static void 774403831d35Sstevel man_kstat_named_init(kstat_named_t *knp, int num_stats) 774503831d35Sstevel { 774603831d35Sstevel int i; 774703831d35Sstevel 774803831d35Sstevel MAN_DBG(MAN_KSTAT, ("man_kstat_named_init: knp(0x%p) num_stats = %d", 774903831d35Sstevel (void *)knp, num_stats)); 775003831d35Sstevel 775103831d35Sstevel for (i = 0; i < num_stats; i++) { 775203831d35Sstevel kstat_named_init(&knp[i], man_kstat_info[i].mk_name, 775303831d35Sstevel man_kstat_info[i].mk_type); 775403831d35Sstevel } 775503831d35Sstevel 775603831d35Sstevel MAN_DBG(MAN_KSTAT, ("man_kstat_named_init: returns")); 775703831d35Sstevel 775803831d35Sstevel } 775903831d35Sstevel 776003831d35Sstevel /* 776103831d35Sstevel * man_kstat_byname - get a kernel stat value from its structure 776203831d35Sstevel * 776303831d35Sstevel * ksp - kstat_t structure to play with 776403831d35Sstevel * s - string to match names with 776503831d35Sstevel * res - in/out result data pointer 776603831d35Sstevel * 776703831d35Sstevel * returns - success - 1 (found) 776803831d35Sstevel * - failure - 0 (not found) 776903831d35Sstevel */ 777003831d35Sstevel static int 777103831d35Sstevel man_kstat_byname(kstat_t *ksp, char *s, kstat_named_t *res) 777203831d35Sstevel { 777303831d35Sstevel int found = 0; 777403831d35Sstevel 777503831d35Sstevel MAN_DBG(MAN_KSTAT2, ("man_kstat_byname: GETTING %s\n", s)); 777603831d35Sstevel 777703831d35Sstevel if (ksp->ks_type == KSTAT_TYPE_NAMED) { 777803831d35Sstevel kstat_named_t *knp; 777903831d35Sstevel 778003831d35Sstevel for (knp = KSTAT_NAMED_PTR(ksp); 778103831d35Sstevel (caddr_t)knp < ((caddr_t)ksp->ks_data+ksp->ks_data_size); 778203831d35Sstevel knp++) { 778303831d35Sstevel 778403831d35Sstevel if (strcmp(s, knp->name) == NULL) { 778503831d35Sstevel 778603831d35Sstevel res->data_type = knp->data_type; 778703831d35Sstevel res->value = knp->value; 778803831d35Sstevel found++; 778903831d35Sstevel 779003831d35Sstevel MAN_DBG(MAN_KSTAT2, ("\t%s: %d\n", knp->name, 779103831d35Sstevel (int)knp->value.ul)); 779203831d35Sstevel } 779303831d35Sstevel } 779403831d35Sstevel } else { 779503831d35Sstevel MAN_DBG(MAN_KSTAT2, ("\tbad kstats type %d\n", ksp->ks_type)); 779603831d35Sstevel } 779703831d35Sstevel 779803831d35Sstevel /* 779903831d35Sstevel * if getting a value but couldn't find the namestring, result = 0. 780003831d35Sstevel */ 780103831d35Sstevel if (!found) { 780203831d35Sstevel /* 780303831d35Sstevel * a reasonable default 780403831d35Sstevel */ 780503831d35Sstevel res->data_type = KSTAT_DATA_ULONG; 780603831d35Sstevel res->value.l = 0; 780703831d35Sstevel MAN_DBG(MAN_KSTAT2, ("\tcouldn't find, using defaults\n")); 780803831d35Sstevel } 780903831d35Sstevel 781003831d35Sstevel MAN_DBG(MAN_KSTAT2, ("man_kstat_byname: returns\n")); 781103831d35Sstevel 781203831d35Sstevel return (found); 781303831d35Sstevel } 781403831d35Sstevel 781503831d35Sstevel 781603831d35Sstevel /* 781703831d35Sstevel * 781803831d35Sstevel * Accumulate MAN driver kstats from the incremental values of the underlying 781903831d35Sstevel * physical interfaces. 782003831d35Sstevel * 782103831d35Sstevel * Parameters: 782203831d35Sstevel * sum_knp - The named kstat area to put cumulative value, 782303831d35Sstevel * NULL if we just want to sync next two params. 782403831d35Sstevel * phys_ksp - Physical interface kstat_t pointer. Contains 782503831d35Sstevel * more current counts. 782603831d35Sstevel * phys_last_knp - counts from the last time we were called for this 782703831d35Sstevel * physical interface. Note that the name kstats 782803831d35Sstevel * pointed to are actually in MAN format, but they 782903831d35Sstevel * hold the mirrored physical devices last read 783003831d35Sstevel * kstats. 783103831d35Sstevel * Basic algorithm is: 783203831d35Sstevel * 783303831d35Sstevel * for each named kstat variable { 783403831d35Sstevel * sum_knp[i] += (phys_ksp->ksp_data[i] - phys_last_knp[i]); 783503831d35Sstevel * phys_last_knp[i] = phys_ksp->ksp_data[i]; 783603831d35Sstevel * } 783703831d35Sstevel * 783803831d35Sstevel */ 783903831d35Sstevel static void 784003831d35Sstevel man_sum_kstats(kstat_named_t *sum_knp, kstat_t *phys_ksp, 784103831d35Sstevel kstat_named_t *phys_last_knp) 784203831d35Sstevel { 784303831d35Sstevel char *physname; 784403831d35Sstevel char *physalias; 784503831d35Sstevel char *statname; 784603831d35Sstevel kstat_named_t phys_kn_entry; 784703831d35Sstevel uint64_t delta64; 784803831d35Sstevel int i; 784903831d35Sstevel 785003831d35Sstevel MAN_DBG(MAN_KSTAT, ("man_sum_kstats: sum_knp(0x%p) phys_ksp(0x%p)" 785103831d35Sstevel " phys_last_knp(0x%p)\n", (void *)sum_knp, (void *)phys_ksp, 785203831d35Sstevel (void *)phys_last_knp)); 785303831d35Sstevel 785403831d35Sstevel /* 785503831d35Sstevel * Now for each entry in man_kstat_info, sum the named kstat. 785603831d35Sstevel * Not that all MAN specific kstats will end up !found. 785703831d35Sstevel */ 785803831d35Sstevel for (i = 0; i < MAN_NUMSTATS; i++) { 785903831d35Sstevel int found = 0; 786003831d35Sstevel int flags = 0; 786103831d35Sstevel 786203831d35Sstevel delta64 = 0; 786303831d35Sstevel 786403831d35Sstevel statname = man_kstat_info[i].mk_name; 786503831d35Sstevel physname = man_kstat_info[i].mk_physname; 786603831d35Sstevel physalias = man_kstat_info[i].mk_physalias; 786703831d35Sstevel flags = man_kstat_info[i].mk_flags; 786803831d35Sstevel 786903831d35Sstevel /* 787003831d35Sstevel * Update MAN private kstats. 787103831d35Sstevel */ 787203831d35Sstevel if (flags & MK_NOT_PHYSICAL) { 787303831d35Sstevel 787403831d35Sstevel kstat_named_t *knp = phys_last_knp; 787503831d35Sstevel 787603831d35Sstevel if (sum_knp == NULL) 787703831d35Sstevel continue; 787803831d35Sstevel 787903831d35Sstevel if (strcmp(statname, "man_switches") == 0) { 788003831d35Sstevel sum_knp[i].value.ui64 = knp[i].value.ui64; 788103831d35Sstevel } else if (strcmp(statname, "man_link_fails") == 0) { 788203831d35Sstevel sum_knp[i].value.ui64 = knp[i].value.ui64; 788303831d35Sstevel } else if (strcmp(statname, "man_link_stales") == 0) { 788403831d35Sstevel sum_knp[i].value.ui64 = knp[i].value.ui64; 788503831d35Sstevel } else if (strcmp(statname, "man_icmpv4_probes") == 0) { 788603831d35Sstevel sum_knp[i].value.ui64 = knp[i].value.ui64; 788703831d35Sstevel } else if (strcmp(statname, "man_icmpv6_probes") == 0) { 788803831d35Sstevel sum_knp[i].value.ui64 = knp[i].value.ui64; 788903831d35Sstevel } 789003831d35Sstevel 789103831d35Sstevel continue; /* phys_ksp doesnt have this stat */ 789203831d35Sstevel } 789303831d35Sstevel 789403831d35Sstevel /* 789503831d35Sstevel * first try it by the "official" name 789603831d35Sstevel */ 789703831d35Sstevel if (phys_ksp) { 789803831d35Sstevel if (man_kstat_byname(phys_ksp, physname, 789903831d35Sstevel &phys_kn_entry)) { 790003831d35Sstevel 790103831d35Sstevel found = 1; 790203831d35Sstevel 790303831d35Sstevel } else if ((physalias) && (man_kstat_byname(phys_ksp, 790403831d35Sstevel physalias, &phys_kn_entry))) { 790503831d35Sstevel 790603831d35Sstevel found = 1; 790703831d35Sstevel } 790803831d35Sstevel } 790903831d35Sstevel 791003831d35Sstevel if (!found) { 791103831d35Sstevel /* 791203831d35Sstevel * clear up the "last" value, no change to the sum 791303831d35Sstevel */ 791403831d35Sstevel phys_last_knp[i].value.ui64 = 0; 791503831d35Sstevel continue; 791603831d35Sstevel } 791703831d35Sstevel 791803831d35Sstevel /* 791903831d35Sstevel * at this point, we should have the good underlying 792003831d35Sstevel * kstat value stored in phys_kn_entry 792103831d35Sstevel */ 792203831d35Sstevel if (flags & MK_NOT_COUNTER) { 792303831d35Sstevel /* 792403831d35Sstevel * it isn't a counter, so store the value and 792503831d35Sstevel * move on (e.g. ifspeed) 792603831d35Sstevel */ 792703831d35Sstevel phys_last_knp[i].value = phys_kn_entry.value; 792803831d35Sstevel continue; 792903831d35Sstevel } 793003831d35Sstevel 793103831d35Sstevel switch (phys_kn_entry.data_type) { 793203831d35Sstevel case KSTAT_DATA_UINT32: 793303831d35Sstevel 793403831d35Sstevel /* 793503831d35Sstevel * this handles 32-bit wrapping 793603831d35Sstevel */ 793703831d35Sstevel if (phys_kn_entry.value.ui32 < 793803831d35Sstevel phys_last_knp[i].value.ui32) { 793903831d35Sstevel 794003831d35Sstevel /* 794103831d35Sstevel * we've wrapped! 794203831d35Sstevel */ 794303831d35Sstevel delta64 += (UINT_MAX - 794403831d35Sstevel phys_last_knp[i].value.ui32); 794503831d35Sstevel phys_last_knp[i].value.ui32 = 0; 794603831d35Sstevel } 794703831d35Sstevel 794803831d35Sstevel delta64 += phys_kn_entry.value.ui32 - 794903831d35Sstevel phys_last_knp[i].value.ui32; 795003831d35Sstevel phys_last_knp[i].value.ui32 = phys_kn_entry.value.ui32; 795103831d35Sstevel break; 795203831d35Sstevel 795303831d35Sstevel default: 795403831d35Sstevel /* 795503831d35Sstevel * must be a 64-bit value, we ignore 64-bit 795603831d35Sstevel * wraps, since they shouldn't ever happen 795703831d35Sstevel * within the life of a machine (if we assume 795803831d35Sstevel * machines don't stay up for more than a few 795903831d35Sstevel * hundred years without a reboot...) 796003831d35Sstevel */ 796103831d35Sstevel delta64 = phys_kn_entry.value.ui64 - 796203831d35Sstevel phys_last_knp[i].value.ui64; 796303831d35Sstevel phys_last_knp[i].value.ui64 = phys_kn_entry.value.ui64; 796403831d35Sstevel } 796503831d35Sstevel 796603831d35Sstevel if (sum_knp != NULL) { 796703831d35Sstevel /* 796803831d35Sstevel * now we need to save the value 796903831d35Sstevel */ 797003831d35Sstevel switch (sum_knp[i].data_type) { 797103831d35Sstevel case KSTAT_DATA_UINT32: 797203831d35Sstevel /* trunk down to 32 bits, possibly lossy */ 797303831d35Sstevel sum_knp[i].value.ui32 += (uint32_t)delta64; 797403831d35Sstevel break; 797503831d35Sstevel 797603831d35Sstevel default: 797703831d35Sstevel sum_knp[i].value.ui64 += delta64; 797803831d35Sstevel break; 797903831d35Sstevel } 798003831d35Sstevel } 798103831d35Sstevel } 798203831d35Sstevel 798303831d35Sstevel MAN_DBG(MAN_KSTAT, ("man_sum_kstats: returns\n")); 798403831d35Sstevel } 798503831d35Sstevel 798603831d35Sstevel 798703831d35Sstevel #if defined(DEBUG) 798803831d35Sstevel 798903831d35Sstevel 799003831d35Sstevel static char *_ms_flags[] = { 799103831d35Sstevel "NONE", 799203831d35Sstevel "FAST", /* 0x1 */ 799303831d35Sstevel "RAW", /* 0x2 */ 799403831d35Sstevel "ALLPHYS", /* 0x4 */ 799503831d35Sstevel "ALLMULTI", /* 0x8 */ 799603831d35Sstevel "ALLSAP", /* 0x10 */ 799703831d35Sstevel "CKSUM", /* 0x20 */ 799803831d35Sstevel "MULTI", /* 0x40 */ 799903831d35Sstevel "SERLPBK", /* 0x80 */ 800003831d35Sstevel "MACLPBK", /* 0x100 */ 800103831d35Sstevel "CLOSING", /* 0x200 */ 800203831d35Sstevel "CLOSE_DONE", /* 0x400 */ 800303831d35Sstevel "CONTROL" /* 0x800 */ 800403831d35Sstevel }; 800503831d35Sstevel 800603831d35Sstevel static void 800703831d35Sstevel man_print_msp(manstr_t *msp) 800803831d35Sstevel { 800903831d35Sstevel char buf[512]; 801003831d35Sstevel char prbuf[512]; 801103831d35Sstevel uint_t flags; 801203831d35Sstevel int i; 801303831d35Sstevel 801403831d35Sstevel cmn_err(CE_CONT, "\tmsp(0x%p)\n", (void *)msp); 801503831d35Sstevel 801603831d35Sstevel if (msp == NULL) 801703831d35Sstevel return; 801803831d35Sstevel 801903831d35Sstevel cmn_err(CE_CONT, "\t%s%d SAP(0x%x):\n", 802003831d35Sstevel ddi_major_to_name(msp->ms_meta_maj), msp->ms_meta_ppa, 802103831d35Sstevel msp->ms_sap); 802203831d35Sstevel 802303831d35Sstevel buf[0] = '\0'; 802403831d35Sstevel prbuf[0] = '\0'; 802503831d35Sstevel flags = msp->ms_flags; 802603831d35Sstevel for (i = 0; i < A_CNT(_ms_flags); i++) { 802703831d35Sstevel if ((flags >> i) & 0x1) { 8028*07d06da5SSurya Prakki (void) sprintf(buf, " %s |", _ms_flags[i+1]); 8029*07d06da5SSurya Prakki (void) strcat(prbuf, buf); 803003831d35Sstevel } 803103831d35Sstevel } 803203831d35Sstevel prbuf[strlen(prbuf) - 1] = '\0'; 803303831d35Sstevel cmn_err(CE_CONT, "\tms_flags: %s\n", prbuf); 803403831d35Sstevel 803503831d35Sstevel cmn_err(CE_CONT, "\tms_dlpistate: %s\n", dss[msp->ms_dlpistate]); 803603831d35Sstevel 803703831d35Sstevel cmn_err(CE_CONT, "\tms_dl_mp: 0x%p\n", (void *)msp->ms_dl_mp); 803803831d35Sstevel 803903831d35Sstevel cmn_err(CE_CONT, "\tms_manp: 0x%p\n", (void *)msp->ms_manp); 804003831d35Sstevel 804103831d35Sstevel cmn_err(CE_CONT, "\tms_dests: 0x%p\n", (void *)msp->ms_dests); 804203831d35Sstevel 804303831d35Sstevel } 804403831d35Sstevel 804503831d35Sstevel static char *_md_state[] = { 804603831d35Sstevel "NOTPRESENT", /* 0x0 */ 804703831d35Sstevel "INITIALIZING", /* 0x1 */ 804803831d35Sstevel "READY", /* 0x2 */ 804903831d35Sstevel "PLUMBING", /* 0x4 */ 805003831d35Sstevel "CLOSING" /* 0x8 */ 805103831d35Sstevel }; 805203831d35Sstevel 805303831d35Sstevel static void 805403831d35Sstevel man_print_mdp(man_dest_t *mdp) 805503831d35Sstevel { 805603831d35Sstevel uint_t state; 805703831d35Sstevel int i; 805803831d35Sstevel char buf[64]; 805903831d35Sstevel char prbuf[512]; 806003831d35Sstevel 806103831d35Sstevel buf[0] = '\0'; 806203831d35Sstevel prbuf[0] = '\0'; 806303831d35Sstevel 806403831d35Sstevel cmn_err(CE_CONT, "\tmdp(0x%p)\n", (void *)mdp); 806503831d35Sstevel 806603831d35Sstevel if (mdp == NULL) 806703831d35Sstevel return; 806803831d35Sstevel 806903831d35Sstevel cmn_err(CE_CONT, "\tmd_pg_id: %d\n", mdp->md_pg_id); 807003831d35Sstevel cmn_err(CE_CONT, "\tmd_dst_eaddr: %s\n", 807103831d35Sstevel ether_sprintf(&mdp->md_dst_eaddr)); 807203831d35Sstevel cmn_err(CE_CONT, "\tmd_src_eaddr: %s\n", 807303831d35Sstevel ether_sprintf(&mdp->md_src_eaddr)); 807403831d35Sstevel cmn_err(CE_CONT, "\tmd_dlpistate: %s", dss[mdp->md_dlpistate]); 807503831d35Sstevel cmn_err(CE_CONT, "\tmd_muxid: 0x%u", mdp->md_muxid); 807603831d35Sstevel cmn_err(CE_CONT, "\tmd_rcvcnt %lu md_lastrcvcnt %lu", mdp->md_rcvcnt, 807703831d35Sstevel mdp->md_lastrcvcnt); 807803831d35Sstevel 807903831d35Sstevel /* 808003831d35Sstevel * Print out state as text. 808103831d35Sstevel */ 808203831d35Sstevel state = mdp->md_state; 808303831d35Sstevel 808403831d35Sstevel if (state == 0) { 8085*07d06da5SSurya Prakki (void) strcat(prbuf, _md_state[0]); 808603831d35Sstevel } else { 808703831d35Sstevel 808803831d35Sstevel for (i = 0; i < A_CNT(_md_state); i++) { 808903831d35Sstevel if ((state >> i) & 0x1) { 8090*07d06da5SSurya Prakki (void) sprintf(buf, " %s |", _md_state[i+1]); 8091*07d06da5SSurya Prakki (void) strcat(prbuf, buf); 809203831d35Sstevel } 809303831d35Sstevel } 809403831d35Sstevel prbuf[strlen(prbuf) -1] = '\0'; 809503831d35Sstevel } 809603831d35Sstevel cmn_err(CE_CONT, "\tmd_state: %s", prbuf); 809703831d35Sstevel 809803831d35Sstevel cmn_err(CE_CONT, "\tmd_device:\n"); 809903831d35Sstevel man_print_dev(&mdp->md_device); 810003831d35Sstevel 810103831d35Sstevel } 810203831d35Sstevel 810303831d35Sstevel static void 810403831d35Sstevel man_print_man(man_t *manp) 810503831d35Sstevel { 810603831d35Sstevel char buf[512]; 810703831d35Sstevel char prbuf[512]; 810803831d35Sstevel 810903831d35Sstevel buf[0] = '\0'; 811003831d35Sstevel prbuf[0] = '\0'; 811103831d35Sstevel 811203831d35Sstevel if (manp == NULL) 811303831d35Sstevel return; 811403831d35Sstevel 811503831d35Sstevel if (ddi_major_to_name(manp->man_meta_major)) { 8116*07d06da5SSurya Prakki (void) sprintf(buf, "\t man_device: %s%d\n", 811703831d35Sstevel ddi_major_to_name(manp->man_meta_major), 811803831d35Sstevel manp->man_meta_ppa); 811903831d35Sstevel } else { 8120*07d06da5SSurya Prakki (void) sprintf(buf, "\t major: %d", manp->man_meta_major); 8121*07d06da5SSurya Prakki (void) sprintf(buf, "\t ppa: %d", manp->man_meta_ppa); 812203831d35Sstevel } 812303831d35Sstevel 812403831d35Sstevel cmn_err(CE_CONT, "%s", buf); 812503831d35Sstevel 812603831d35Sstevel } 812703831d35Sstevel 812803831d35Sstevel static char *_mdev_state[] = { 812903831d35Sstevel "UNASSIGNED ", 813003831d35Sstevel "ASSIGNED", 813103831d35Sstevel "ACTIVE", 813203831d35Sstevel "FAILED" 813303831d35Sstevel }; 813403831d35Sstevel 813503831d35Sstevel static void 813603831d35Sstevel man_print_dev(man_dev_t *mdevp) 813703831d35Sstevel { 813803831d35Sstevel char buf[512]; 813903831d35Sstevel char prbuf[512]; 814003831d35Sstevel int i; 814103831d35Sstevel uint_t state; 814203831d35Sstevel 814303831d35Sstevel buf[0] = '\0'; 814403831d35Sstevel prbuf[0] = '\0'; 814503831d35Sstevel 814603831d35Sstevel if (mdevp == NULL) 814703831d35Sstevel return; 814803831d35Sstevel 814903831d35Sstevel if (mdevp->mdev_major == 0) { 815003831d35Sstevel number: 8151*07d06da5SSurya Prakki (void) sprintf(buf, "\t mdev_major: %d\n", mdevp->mdev_major); 815203831d35Sstevel } else if (ddi_major_to_name(mdevp->mdev_major)) { 8153*07d06da5SSurya Prakki (void) sprintf(buf, "\t mdev_device: %s%d\n", 815403831d35Sstevel ddi_major_to_name(mdevp->mdev_major), 815503831d35Sstevel mdevp->mdev_ppa); 815603831d35Sstevel } else 815703831d35Sstevel goto number; 815803831d35Sstevel 815903831d35Sstevel cmn_err(CE_CONT, "%s", buf); 816003831d35Sstevel 816103831d35Sstevel cmn_err(CE_CONT, "\t mdev_exp_id: %d\n", mdevp->mdev_exp_id); 816203831d35Sstevel 816303831d35Sstevel buf[0] = '\0'; 816403831d35Sstevel prbuf[0] = '\0'; 816503831d35Sstevel state = mdevp->mdev_state; 816603831d35Sstevel 816703831d35Sstevel if (state == 0) { 8168*07d06da5SSurya Prakki (void) strcat(prbuf, _mdev_state[0]); 816903831d35Sstevel } else { 817003831d35Sstevel for (i = 0; i < A_CNT(_mdev_state); i++) { 817103831d35Sstevel if ((state >> i) & 0x1) { 8172*07d06da5SSurya Prakki (void) sprintf(buf, " %s |", _mdev_state[i+1]); 8173*07d06da5SSurya Prakki (void) strcat(prbuf, buf); 817403831d35Sstevel } 817503831d35Sstevel } 817603831d35Sstevel } 817703831d35Sstevel 817803831d35Sstevel prbuf[strlen(prbuf) - 2] = '\0'; 817903831d35Sstevel 818003831d35Sstevel cmn_err(CE_CONT, "\t mdev_state: %s\n", prbuf); 818103831d35Sstevel 818203831d35Sstevel } 818303831d35Sstevel 818403831d35Sstevel static char *_mip_cmd[] = { 818503831d35Sstevel "MI_PATH_READ", 818603831d35Sstevel "MI_PATH_ASSIGN", 818703831d35Sstevel "MI_PATH_ACTIVATE", 818803831d35Sstevel "MI_PATH_DEACTIVATE", 818903831d35Sstevel "MI_PATH_UNASSIGN" 819003831d35Sstevel }; 819103831d35Sstevel 819203831d35Sstevel static void 819303831d35Sstevel man_print_mtp(mi_time_t *mtp) 819403831d35Sstevel { 819503831d35Sstevel cmn_err(CE_CONT, "\tmtp(0x%p)\n", (void *)mtp); 819603831d35Sstevel 819703831d35Sstevel if (mtp == NULL) 819803831d35Sstevel return; 819903831d35Sstevel 820003831d35Sstevel cmn_err(CE_CONT, "\tmtp_instance: %d\n", mtp->mtp_man_ppa); 820103831d35Sstevel 820203831d35Sstevel cmn_err(CE_CONT, "\tmtp_time: %d\n", mtp->mtp_time); 820303831d35Sstevel 820403831d35Sstevel } 820503831d35Sstevel 820603831d35Sstevel static void 820703831d35Sstevel man_print_mip(mi_path_t *mip) 820803831d35Sstevel { 820903831d35Sstevel cmn_err(CE_CONT, "\tmip(0x%p)\n", (void *)mip); 821003831d35Sstevel 821103831d35Sstevel if (mip == NULL) 821203831d35Sstevel return; 821303831d35Sstevel 821403831d35Sstevel cmn_err(CE_CONT, "\tmip_pg_id: %d\n", mip->mip_pg_id); 821503831d35Sstevel 821603831d35Sstevel cmn_err(CE_CONT, "\tmip_cmd: %s\n", _mip_cmd[mip->mip_cmd]); 821703831d35Sstevel 821803831d35Sstevel cmn_err(CE_CONT, "\tmip_eaddr: %s\n", ether_sprintf(&mip->mip_eaddr)); 821903831d35Sstevel 822003831d35Sstevel cmn_err(CE_CONT, "\tmip_devs: 0x%p\n", (void *)mip->mip_devs); 822103831d35Sstevel 822203831d35Sstevel cmn_err(CE_CONT, "\tmip_ndevs: %d\n", mip->mip_ndevs); 822303831d35Sstevel 822403831d35Sstevel } 822503831d35Sstevel 822603831d35Sstevel static void 822703831d35Sstevel man_print_mpg(man_pg_t *mpg) 822803831d35Sstevel { 822903831d35Sstevel cmn_err(CE_CONT, "\tmpg(0x%p)\n", (void *)mpg); 823003831d35Sstevel 823103831d35Sstevel if (mpg == NULL) 823203831d35Sstevel return; 823303831d35Sstevel 823403831d35Sstevel cmn_err(CE_CONT, "\tmpg_next: 0x%p\n", (void *)mpg->mpg_next); 823503831d35Sstevel 823603831d35Sstevel cmn_err(CE_CONT, "\tmpg_pg_id: %d\n", mpg->mpg_pg_id); 823703831d35Sstevel 823803831d35Sstevel cmn_err(CE_CONT, "\tmpg_man_ppa: %d\n", mpg->mpg_man_ppa); 823903831d35Sstevel 824003831d35Sstevel cmn_err(CE_CONT, "\tmpg_dst_eaddr: %s\n", 824103831d35Sstevel ether_sprintf(&mpg->mpg_dst_eaddr)); 824203831d35Sstevel 824303831d35Sstevel cmn_err(CE_CONT, "\tmpg_pathp: 0x%p\n", (void *)mpg->mpg_pathp); 824403831d35Sstevel 824503831d35Sstevel } 824603831d35Sstevel 824703831d35Sstevel static char *_mw_flags[] = { 824803831d35Sstevel "NOWAITER", /* 0x0 */ 824903831d35Sstevel "CVWAITER", /* 0x1 */ 825003831d35Sstevel "QWAITER", /* 0x2 */ 825103831d35Sstevel "DONE" /* 0x3 */ 825203831d35Sstevel }; 825303831d35Sstevel 825403831d35Sstevel static void 825503831d35Sstevel man_print_work(man_work_t *wp) 825603831d35Sstevel { 825703831d35Sstevel int i; 825803831d35Sstevel 825903831d35Sstevel cmn_err(CE_CONT, "\twp(0x%p)\n\n", (void *)wp); 826003831d35Sstevel 826103831d35Sstevel if (wp == NULL) 826203831d35Sstevel return; 826303831d35Sstevel 826403831d35Sstevel cmn_err(CE_CONT, "\tmw_type: %s\n", _mw_type[wp->mw_type]); 826503831d35Sstevel 826603831d35Sstevel cmn_err(CE_CONT, "\tmw_flags: "); 826703831d35Sstevel for (i = 0; i < A_CNT(_mw_flags); i++) { 826803831d35Sstevel if ((wp->mw_flags >> i) & 0x1) 826903831d35Sstevel cmn_err(CE_CONT, "%s", _mw_flags[i]); 827003831d35Sstevel } 827103831d35Sstevel cmn_err(CE_CONT, "\n"); 827203831d35Sstevel 827303831d35Sstevel cmn_err(CE_CONT, "\twp_status: %d\n", wp->mw_status); 827403831d35Sstevel 827503831d35Sstevel cmn_err(CE_CONT, "\twp_arg: 0x%p\n", (void *)&wp->mw_arg); 827603831d35Sstevel 827703831d35Sstevel cmn_err(CE_CONT, "\tmw_next: 0x%p\n", (void *)wp->mw_next); 827803831d35Sstevel 827903831d35Sstevel cmn_err(CE_CONT, "\twp_q: 0x%p", (void *)wp->mw_q); 828003831d35Sstevel 828103831d35Sstevel } 828203831d35Sstevel 828303831d35Sstevel static void 828403831d35Sstevel man_print_path(man_path_t *mp) 828503831d35Sstevel { 828603831d35Sstevel cmn_err(CE_CONT, "\tmp(0x%p)\n\n", (void *)mp); 828703831d35Sstevel 828803831d35Sstevel if (mp == NULL) 828903831d35Sstevel return; 829003831d35Sstevel 829103831d35Sstevel cmn_err(CE_CONT, "\tmp_device:"); 829203831d35Sstevel man_print_dev(&mp->mp_device); 829303831d35Sstevel 829403831d35Sstevel cmn_err(CE_CONT, "\tmp_next: 0x%p\n", (void *)mp->mp_next); 829503831d35Sstevel 829603831d35Sstevel cmn_err(CE_CONT, "\tmp_last_knp: 0x%p\n", (void *)mp->mp_last_knp); 829703831d35Sstevel 829803831d35Sstevel cmn_err(CE_CONT, "\tmp_lru: 0x%lx", mp->mp_lru); 829903831d35Sstevel 830003831d35Sstevel } 830103831d35Sstevel 830203831d35Sstevel void * 830303831d35Sstevel man_dbg_kzalloc(int line, size_t size, int kmflags) 830403831d35Sstevel { 830503831d35Sstevel void *tmp; 830603831d35Sstevel 830703831d35Sstevel tmp = kmem_zalloc(size, kmflags); 830803831d35Sstevel MAN_DBG(MAN_KMEM, ("0x%p %lu\tzalloc'd @ %d\n", (void *)tmp, 830903831d35Sstevel size, line)); 831003831d35Sstevel 831103831d35Sstevel return (tmp); 831203831d35Sstevel 831303831d35Sstevel } 831403831d35Sstevel 831503831d35Sstevel void 831603831d35Sstevel man_dbg_kfree(int line, void *buf, size_t size) 831703831d35Sstevel { 831803831d35Sstevel 831903831d35Sstevel MAN_DBG(MAN_KMEM, ("0x%p %lu\tfree'd @ %d\n", (void *)buf, size, line)); 832003831d35Sstevel 832103831d35Sstevel kmem_free(buf, size); 832203831d35Sstevel 832303831d35Sstevel } 832403831d35Sstevel 832503831d35Sstevel #endif /* DEBUG */ 8326