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
_init(void)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
_info(struct modinfo * modinfop)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
_fini(void)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
man_deconfigure()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
man_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
man_get_our_etheraddr(eaddr_t * eap)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
man_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
man_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)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
man_open(queue_t * rq,dev_t * devp,int flag,int sflag,cred_t * credp)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
man_configure(queue_t * rq)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
man_close(queue_t * rq)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
man_dodetach(manstr_t * msp,man_work_t * wp)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
man_uwput(register queue_t * wq,register mblk_t * mp)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
man_start(register queue_t * wq,register mblk_t * mp,eaddr_t * eap)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
man_start_lower(man_dest_t * mdp,mblk_t * mp,queue_t * flow_wq,int caller)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
man_ioctl(register queue_t * wq,register mblk_t * mp)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
man_plink(queue_t * wq,mblk_t * mp)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
man_unplink(queue_t * wq,mblk_t * mp)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
man_linkrec_insert(man_linkrec_t * lrp)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 *
man_linkrec_find(int muxid)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
man_set_linkcheck_time(queue_t * wq,mblk_t * mp)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
man_setpath(queue_t * wq,mblk_t * mp)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
man_geteaddr(queue_t * wq,mblk_t * mp)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
man_set_sc_ipaddrs(queue_t * wq,mblk_t * mp)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
man_set_sc_ip6addrs(queue_t * wq,mblk_t * mp)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
man_dl_ioc_hdr_info(queue_t * wq,mblk_t * mp)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
man_uwsrv(queue_t * wq)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
man_proto(queue_t * wq,mblk_t * mp)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
man_udreq(queue_t * wq,mblk_t * mp)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
man_areq(queue_t * wq,mblk_t * mp)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
man_init_dests(man_t * manp,manstr_t * msp)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
man_start_dest(man_dest_t * mdp,manstr_t * msp,man_pg_t * mpg)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
man_set_optimized_dest(manstr_t * msp)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
man_dlpi(manstr_t * msp,mblk_t * mp)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
man_dlioc(manstr_t * msp,mblk_t * mp)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
man_dl_catch(mblk_t ** mplist,mblk_t * mp)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
man_dlpi_senddown(manstr_t * msp,mblk_t * mp)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
man_dlpi_replay(man_dest_t * mdp,mblk_t * rmp)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
man_dreq(queue_t * wq,mblk_t * mp)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
man_dl_clean(mblk_t ** mplist)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
man_dl_release(mblk_t ** mplist,mblk_t * mp)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
man_match_proto(mblk_t * mp1,mblk_t * mp2)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
man_breq(queue_t * wq,mblk_t * mp)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
man_ubreq(queue_t * wq,mblk_t * mp)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
man_ireq(queue_t * wq,mblk_t * mp)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
man_ponreq(queue_t * wq,mblk_t * mp)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
man_poffreq(queue_t * wq,mblk_t * mp)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
man_emreq(queue_t * wq,mblk_t * mp)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
man_dmreq(queue_t * wq,mblk_t * mp)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
man_pareq(queue_t * wq,mblk_t * mp)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
man_spareq(queue_t * wq,mblk_t * mp)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
man_lwsrv(queue_t * wq)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
man_lrput(queue_t * rq,mblk_t * mp)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
man_lrsrv(queue_t * rq)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
man_needs_linkcheck(man_dest_t * mdp)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
man_iwork()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
man_do_dr_switch(man_work_t * wp)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
man_do_dr_attach(man_work_t * wp)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
man_do_dr_detach(man_work_t * wp)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
man_iswitch(man_work_t * wp)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 *
man_switch_match(man_dest_t * sdp,int pg_id,void * sid)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
man_ifail_dest(man_dest_t * mdp)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
man_reset_dlpi(void * argp)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
man_dlioc_replay(man_dest_t * mdp)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 *
man_alloc_ubreq_dreq()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 *
man_dup_mplist(mblk_t * mp)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 *
man_alloc_physreq_mp(eaddr_t * man_eap)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
man_add_dests(man_pg_t * mpg)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
man_remove_dests(man_pg_t * mpg)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
man_str_uses_pg(manstr_t * msp,man_pg_t * mpg)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
man_gettimer(int timer,man_dest_t * mdp)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
man_linkcheck_timer(void * argp)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
man_do_autoswitch(man_dest_t * mdp)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
man_autoswitch(man_pg_t * mpg,man_dev_t * st_devp,man_work_t * waiter_wp)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
man_prep_dests_for_switch(man_pg_t * mpg,man_dest_t ** mdpp,int * cntp)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
man_do_icmp_bcast(man_dest_t * mdp,t_uscalar_t sap)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 *
man_pinger(t_uscalar_t sap)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 *
man_alloc_udreq(int size,man_dladdr_t * dlsap)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
man_bwork()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
man_open_ctl()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
man_close_ctl()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
man_bclose(man_adest_t * adp)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
man_cancel_timers(man_adest_t * adp)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
man_bswitch(man_adest_t * adp,man_work_t * wp)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
man_plumb(man_dest_t * mdp)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
man_unplumb(man_dest_t * mdp)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
man_pg_cmd(mi_path_t * mip,man_work_t * waiter_wp)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
man_pg_assign(man_pg_t ** mplpp,mi_path_t * mip,int add_only)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
man_pg_unassign(man_pg_t ** plpp,mi_path_t * mip)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
man_pg_activate(man_t * manp,mi_path_t * mip,man_work_t * waiter_wp)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
man_pg_read(man_pg_t * plp,mi_path_t * mip)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
man_pg_create(man_pg_t ** mplpp,man_pg_t ** mpgp,mi_path_t * mip)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 *
man_find_path_by_dev(man_pg_t * plp,man_dev_t * mdevp,man_path_t ** mpp)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 *
man_find_pg_by_id(man_pg_t * mpg,int pg_id)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 *
man_find_path_by_ppa(man_path_t * mplist,int ppa)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 *
man_find_active_path(man_path_t * mplist)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 *
man_find_alternate_path(man_path_t * mlp)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
man_path_remove(man_path_t ** lpp,man_path_t * mp)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
man_path_insert(man_path_t ** lpp,man_path_t * mp)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
man_path_merge(man_path_t ** lpp,man_path_t * np)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
man_path_kstat_init(man_path_t * mpp)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
man_path_kstat_uninit(man_path_t * mp)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 *
man_work_alloc(int type,int kmflag)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
man_work_free(man_work_t * wp)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
man_work_add(man_workq_t * q,man_work_t * wp)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
man_param_register(param_t * manpa,int cnt)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
man_nd_getset(queue_t * wq,mblk_t * mp)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
man_pathgroups_report(queue_t * wq,mblk_t * mp,caddr_t cp,cred_t * cr)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
man_preport(man_path_t * plist,mblk_t * mp)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
man_set_active_path(queue_t * wq,mblk_t * mp,char * value,caddr_t cp,cred_t * cr)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
man_get_hostinfo(queue_t * wq,mblk_t * mp,caddr_t cp,cred_t * cr)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 *
man_inet_ntoa(in_addr_t in)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
man_param_get(queue_t * q,mblk_t * mp,caddr_t cp,cred_t * cr)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
man_param_set(queue_t * q,mblk_t * mp,char * value,caddr_t cp,cred_t * cr)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
man_param_cleanup()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
man_nd_free(caddr_t * nd_pparam)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
man_kstat_update(kstat_t * ksp,int rw)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
man_do_kstats(man_work_t * wp)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
man_update_path_kstats(man_t * manp)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
man_update_dev_kstats(kstat_named_t * man_knp,man_path_t * mp)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
man_sum_dests_kstats(kstat_named_t * knp,man_pg_t * mpg)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
man_kstat_named_init(kstat_named_t * knp,int num_stats)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
man_kstat_byname(kstat_t * ksp,char * s,kstat_named_t * res)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
man_sum_kstats(kstat_named_t * sum_knp,kstat_t * phys_ksp,kstat_named_t * phys_last_knp)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
man_print_msp(manstr_t * msp)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
man_print_mdp(man_dest_t * mdp)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
man_print_man(man_t * manp)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
man_print_dev(man_dev_t * mdevp)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
man_print_mtp(mi_time_t * mtp)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
man_print_mip(mi_path_t * mip)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
man_print_mpg(man_pg_t * mpg)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
man_print_work(man_work_t * wp)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
man_print_path(man_path_t * mp)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 *
man_dbg_kzalloc(int line,size_t size,int kmflags)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
man_dbg_kfree(int line,void * buf,size_t size)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