diff options
Diffstat (limited to 'usr/src/uts/sun4u/starfire/io/idn_proto.c')
| -rw-r--r-- | usr/src/uts/sun4u/starfire/io/idn_proto.c | 12971 |
1 files changed, 0 insertions, 12971 deletions
diff --git a/usr/src/uts/sun4u/starfire/io/idn_proto.c b/usr/src/uts/sun4u/starfire/io/idn_proto.c deleted file mode 100644 index cebcdb89f7..0000000000 --- a/usr/src/uts/sun4u/starfire/io/idn_proto.c +++ /dev/null @@ -1,12971 +0,0 @@ -/* - * CDDL HEADER START - * - * The contents of this file are subject to the terms of the - * Common Development and Distribution License (the "License"). - * You may not use this file except in compliance with the License. - * - * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE - * or http://www.opensolaris.org/os/licensing. - * See the License for the specific language governing permissions - * and limitations under the License. - * - * When distributing Covered Code, include this CDDL HEADER in each - * file and include the License file at usr/src/OPENSOLARIS.LICENSE. - * If applicable, add the following below this CDDL HEADER, with the - * fields enclosed by brackets "[]" replaced with your own identifying - * information: Portions Copyright [yyyy] [name of copyright owner] - * - * CDDL HEADER END - */ - -/* - * Copyright 2009 Sun Microsystems, Inc. All rights reserved. - * Use is subject to license terms. - * Copyright (c) 2016 by Delphix. All rights reserved. - */ - -/* - * Inter-Domain Network - * - * IDN Protocol functions to support domain link/unlink/reconfig. - */ - -#include <sys/types.h> -#include <sys/param.h> -#include <sys/machparam.h> -#include <sys/debug.h> -#include <sys/cpuvar.h> -#include <sys/kmem.h> -#include <sys/mutex.h> -#include <sys/rwlock.h> -#include <sys/systm.h> -#include <sys/stream.h> -#include <sys/strsun.h> -#include <sys/stropts.h> -#include <sys/sema_impl.h> -#include <sys/membar.h> -#include <sys/utsname.h> -#include <inet/common.h> -#include <inet/mi.h> -#include <netinet/ip6.h> -#include <inet/ip.h> -#include <netinet/in.h> -#include <sys/vm_machparam.h> -#include <sys/x_call.h> -#include <sys/ddi.h> -#include <sys/sunddi.h> -#include <sys/atomic.h> -#include <vm/as.h> /* kas decl */ - -#include <sys/idn.h> -#include <sys/idn_xf.h> - -#define IDNBUG_CPUPERBOARD - -extern pri_t maxclsyspri; -extern u_longlong_t gettick(); - -clock_t idn_xmit_monitor_freq = 50; - -static int idn_connect(int domid); -static int idn_disconnect(int domid, idn_fin_t fintype, - idn_finarg_t finarg, idn_finsync_t finsync); -static void idn_deconfig(int domid); -static void idn_unlink_domainset(domainset_t domset, idn_fin_t fintype, - idn_finarg_t finarg, idn_finopt_t finopt, - boardset_t idnset); -static void idn_retry_execute(void *arg); -static void idn_retry_submit(void (*func)(uint_t token, void *arg), - void *arg, uint_t token, clock_t ticks); -static void idn_shutdown_datapath(domainset_t domset, int force); -static mblk_t *idn_fill_buffer(caddr_t bufp, int size, mblk_t *mp, - uchar_t **data_rptrp); -static ushort_t idn_cksum(register ushort_t *hdrp, register int count); -static int idn_mark_awol(int domid, clock_t *atime); - -static void idn_recv_proto(idn_protomsg_t *hp); -static void idn_send_config(int domid, int phase); -static void idn_recv_config(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static int idn_send_master_config(int domid, int phase); -static int idn_send_slave_config(int domid, int phase); -static uint_t idn_check_master_config(int domid, uint_t *exp, uint_t *act); -static uint_t idn_check_slave_config(int domid, uint_t *exp, uint_t *act); -static int idn_recv_config_done(int domid); -static void idn_nego_cleanup_check(int domid, int new_masterid, - int new_cpuid); -static void idn_recv_cmd(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static int idn_recv_data(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static int idn_send_data_loopback(idn_netaddr_t dst_netaddr, - queue_t *wq, mblk_t *mp); -static void idn_send_dataresp(int domid, idn_nack_t nacktype); -static int idn_send_mboxdata(int domid, struct idn *sip, int channel, - caddr_t bufp); -static int idn_recv_mboxdata(int channel, caddr_t bufp); -static int idn_program_hardware(int domid); -static int idn_deprogram_hardware(int domid); - -static void idn_send_cmd_nackresp(int domid, idn_msgtype_t *mtp, - idn_cmd_t cmdtype, idn_nack_t nacktype); -static void idn_local_cmd(idn_cmd_t cmdtype, uint_t arg1, - uint_t arg2, uint_t arg3); -static void idn_terminate_cmd(int domid, int serrno); -static void idn_mboxarea_init(idn_mboxtbl_t *mtp, register int ntbls); -static void idn_mainmbox_activate(int domid); -static void idn_mainmbox_deactivate(ushort_t domset); -static void idn_mainmbox_chan_register(int domid, - idn_mainmbox_t *send_mmp, - idn_mainmbox_t *recv_mmp, int channel); -static int idn_mainmbox_chan_unregister(ushort_t domset, int channel); -static int idn_mainmbox_flush(int domid, idn_mainmbox_t *mmp); -static void idn_mainmbox_reset(int domid, idn_mainmbox_t *cmp); -static int idn_activate_channel(idn_chanset_t chanset, - idn_chanop_t chanop); -static void idn_deactivate_channel(idn_chanset_t chanset, - idn_chanop_t chanop); -static int idn_deactivate_channel_services(int channel, - idn_chanop_t chanop); -static int idn_activate_channel_services(int channel); -static void idn_chan_server(idn_chansvr_t **cspp); -#if 0 -static void idn_chan_flush(idn_chansvr_t *csp); -#endif /* 0 */ -static void idn_chan_action(int channel, idn_chanaction_t chanaction, - int wait); -static void idn_chan_addmbox(int channel, ushort_t domset); -static void idn_chan_delmbox(int channel, ushort_t domset); -static void idn_submit_chanactivate_job(int channel); -static void idn_exec_chanactivate(void *chn); - -static void idn_link_established(void *arg); -static void idn_prealloc_slab(int nslabs); -static void idn_recv_slaballoc_req(int domid, idn_msgtype_t *mtp, - uint_t slab_size); -static void idn_send_slaballoc_resp(int domid, idn_msgtype_t *mtp, - uint_t slab_offset, uint_t slab_size, - int serrno); -static void idn_recv_slaballoc_resp(int domid, smr_offset_t slab_offset, - uint_t slab_size, int serrno); -static void idn_recv_slabreap_req(int domid, idn_msgtype_t *mtp, - int nslabs); -static void idn_recv_slabreap_resp(int domid, int nslabs, int serrno); -static void idn_send_slabreap_resp(int domid, idn_msgtype_t *mtp, - int nslabs, int serrno); -static void idn_recv_slabfree_req(int domid, idn_msgtype_t *mtp, - smr_offset_t slab_offset, uint_t slab_size); -static void idn_recv_slabfree_resp(int domid, uint_t slab_offset, - uint_t slab_size, int serrno); -static void idn_send_slabfree_resp(int domid, idn_msgtype_t *mtp, - uint_t slab_offset, uint_t slab_size, - int serrno); -static void idn_retry_nodename_req(void *arg); -static void idn_send_nodename_req(int domid); -static void idn_send_nodename_resp(int domid, idn_msgtype_t *mtp, - uint_t bufoffset, int serrno); -static void idn_recv_nodename_req(int domid, idn_msgtype_t *mtp, - uint_t bufoffset); -static void idn_recv_nodename_resp(int domid, uint_t bufoffset, - int serrno); - -static void idn_protocol_server(int *id); -static void idn_protocol_server_killall(); -static void idn_protojob_free(idn_protojob_t *jp); - -static int idn_xstate_transfunc(int domid, void *transarg); -static int idn_xphase_transition(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static void idn_sync_enter(int domid, idn_synccmd_t cmd, - domainset_t xset, domainset_t rset, - int (*transfunc)(), void *transarg); -static domainset_t - idn_sync_register(int domid, idn_synccmd_t cmd, - domainset_t ready_set, idn_syncreg_t regtype); -static void idn_sync_register_awol(int domid); -static int idn_verify_config_mbox(int domid); -static int idn_select_master(int domid, int rmasterid, int rcpuid); - -static int valid_mtu(uint_t mtu); -static int valid_bufsize(uint_t bufsize); -static int valid_slabsize(int slabsize); -static int valid_nwrsize(int nwrsize); - -static int idn_master_init(); -static void idn_master_deinit(); - -static void idn_send_acknack(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); - -static int idn_send_nego(int domid, idn_msgtype_t *mtp, - domainset_t conset); -static void idn_retry_nego(uint_t token, void *arg); -static int idn_check_nego(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static void idn_action_nego_pend(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static void idn_error_nego(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static void idn_action_nego_sent(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static void idn_action_nego_rcvd(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static void idn_final_nego(int domid); -static void idn_exit_nego(int domid, uint_t msgtype); - -static int idn_send_con(int domid, idn_msgtype_t *mtp, - idn_con_t contype, domainset_t conset); -static void idn_retry_con(uint_t token, void *arg); -static int idn_check_con(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static void idn_action_con_pend(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static void idn_error_con(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static void idn_action_con_sent(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static void idn_action_con_rcvd(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static void idn_final_con(int domid); -static void idn_exit_con(int domid, uint_t msgtype); - -static int idn_send_fin(int domid, idn_msgtype_t *mtp, idn_fin_t fintype, - idn_finarg_t finarg, idn_finopt_t finopt, - domainset_t finset, uint_t finmaster); -static void idn_retry_fin(uint_t token, void *arg); -static int idn_check_fin_pend(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static void idn_action_fin_pend(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static void idn_error_fin_pend(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static int idn_check_fin_sent(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static void idn_action_fin_sent(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static void idn_error_fin_sent(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static void idn_action_fin_rcvd(int domid, idn_msgtype_t *mtp, - idn_xdcargs_t xargs); -static void idn_final_fin(int domid); -static void idn_exit_fin(int domid, uint_t msgtype); - -/* - * We keep a small cache of protojob structures just - * in case allocation within idn_handler comes back - * with nothing from the land of kmem. - */ -idn_protojob_t idn_protojob_cache[IDN_DMV_PENDING_MAX]; -idn_protojob_t *idn_protojob_cache_list; -kmutex_t idn_protojob_cache_lock; - -/* - * - receive message. - * - call check-function for current state. - * - if (check-function == ok) then - * call action-function for current state. - * else - * call error-function for current state. - * - transition state based on check results. - * - if (next state == final state) then - * call final-function. - */ -static idn_xphase_t xphase_nego = { - IDNP_NEGO, - { - { IDNDS_NEGO_PEND, - idn_check_nego, - idn_action_nego_pend, - idn_error_nego}, - { IDNDS_NEGO_SENT, - idn_check_nego, - idn_action_nego_sent, - idn_error_nego}, - { IDNDS_NEGO_RCVD, - NULL, - idn_action_nego_rcvd, - NULL }, - { IDNDS_CONFIG, NULL, NULL, NULL }, - }, - idn_final_nego, - idn_exit_nego -}; - -static idn_xphase_t xphase_con = { - IDNP_CON, - { - { IDNDS_CON_PEND, - idn_check_con, - idn_action_con_pend, - idn_error_con}, - { IDNDS_CON_SENT, - idn_check_con, - idn_action_con_sent, - idn_error_con}, - { IDNDS_CON_RCVD, - NULL, - idn_action_con_rcvd, - NULL }, - { IDNDS_CON_READY, NULL, NULL, NULL }, - }, - idn_final_con, - idn_exit_con -}; - -static idn_xphase_t xphase_fin = { - IDNP_FIN, - { - { IDNDS_FIN_PEND, - idn_check_fin_pend, - idn_action_fin_pend, - idn_error_fin_pend }, - { IDNDS_FIN_SENT, - idn_check_fin_sent, - idn_action_fin_sent, - idn_error_fin_sent }, - { IDNDS_FIN_RCVD, - NULL, - idn_action_fin_rcvd, - NULL }, - { IDNDS_DMAP, NULL, NULL, NULL }, - }, - idn_final_fin, - idn_exit_fin -}; - -static int idnxs_state_table[4][5][2] = { - { /* IDNXS_PEND */ - { IDNXS_SENT, IDNXS_PEND }, /* 0 */ - { IDNXS_RCVD, IDNXS_PEND }, /* msg */ - { IDNXS_NIL, IDNXS_PEND }, /* msg+ack */ - { IDNXS_PEND, IDNXS_NIL }, /* ack */ - { IDNXS_PEND, IDNXS_NIL }, /* nack */ - }, - { /* IDNXS_SENT */ - { IDNXS_NIL, IDNXS_NIL }, /* 0 */ - { IDNXS_RCVD, IDNXS_PEND }, /* msg */ - { IDNXS_FINAL, IDNXS_PEND }, /* msg+ack */ - { IDNXS_NIL, IDNXS_NIL }, /* ack */ - { IDNXS_PEND, IDNXS_NIL }, /* nack */ - }, - { /* IDNXS_RCVD */ - { IDNXS_NIL, IDNXS_NIL }, /* 0 */ - { IDNXS_NIL, IDNXS_NIL }, /* msg */ - { IDNXS_FINAL, IDNXS_NIL }, /* msg+ack */ - { IDNXS_FINAL, IDNXS_NIL }, /* ack */ - { IDNXS_PEND, IDNXS_NIL }, /* nack */ - }, - { /* IDNXS_FINAL */ - { IDNXS_NIL, IDNXS_NIL }, /* 0 */ - { IDNXS_NIL, IDNXS_NIL }, /* msg */ - { IDNXS_NIL, IDNXS_NIL }, /* msg+ack */ - { IDNXS_NIL, IDNXS_NIL }, /* ack */ - { IDNXS_NIL, IDNXS_NIL }, /* nack */ - } -}; - -/* - * NONE Respective domain does not have a master. - * OTHER Respective domain has a master different - * than either local or remote. - * LOCAL Respective domain has chosen local as master. - * REMOTE Respective domain has chosen remote as master. - * - * Actions: - * VOTE Compare votes and select one. - * VOTE_RCFG Compare votes and Reconfigure - * if necessary, i.e. remote won. - * CONNECT Connect to remote's OTHER if different - * than our local master. - * LOCAL Local domain is winner. - * REMOTE Remote domain is winner. - * WAIT Wait for remote to connect to our - * master if theirs is different. - * ERROR An impossible condition. - * - * Index: - * 0 = Local - * 1 = Remote - */ -static idn_master_select_t master_select_table[4][4] = { - { /* local remote */ - MASTER_SELECT_VOTE, /* NONE NONE */ - MASTER_SELECT_CONNECT, /* NONE OTHER */ - MASTER_SELECT_LOCAL, /* NONE LOCAL */ - MASTER_SELECT_REMOTE /* NONE REMOTE */ - }, - { - MASTER_SELECT_WAIT, /* OTHER NONE */ - MASTER_SELECT_CONNECT, /* OTHER OTHER */ - MASTER_SELECT_WAIT, /* OTHER LOCAL */ - MASTER_SELECT_WAIT /* OTHER REMOTE */ - }, - { - MASTER_SELECT_LOCAL, /* LOCAL NONE */ - MASTER_SELECT_CONNECT, /* LOCAL OTHER */ - MASTER_SELECT_LOCAL, /* LOCAL LOCAL */ - MASTER_SELECT_VOTE_RCFG /* LOCAL REMOTE */ - }, - { - MASTER_SELECT_REMOTE, /* REMOTE NONE */ - MASTER_SELECT_CONNECT, /* REMOTE OTHER */ - MASTER_SELECT_ERROR, /* REMOTE LOCAL */ - MASTER_SELECT_REMOTE /* REMOTE REMOTE */ - } -}; - -void -idn_assign_cookie(int domid) -{ - static ushort_t num = 0; - ushort_t cookie; - procname_t proc = "idn_assign_cookie"; - - if ((cookie = idn_domain[domid].dcookie_recv) != 0) - return; - - cookie = (ushort_t)(((uint64_t)&idn_domain[domid] >> 8) & 0xff); - while ((cookie ^= num++ & 0xff) == 0) - ; - - PR_PROTO("%s:%d: assigned RECV cookie 0x%x\n", proc, domid, cookie); - - idn_domain[domid].dcookie_recv = cookie; -} - -void -idn_update_priority(int domid, int pri) -{ - idn_domain_t *dp; - procname_t proc = "idn_update_priority"; - - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - dp = &idn_domain[domid]; - - if (pri >= IDNVOTE_MINPRI) { - dp->dvote.v.priority = pri & IDNVOTE_PRI_MASK; - - PR_PROTO("%s:%d: SETTING PRIORITY to req(%d) " - "(localpri = 0x%x)\n", - proc, domid, pri, IDNVOTE_PRIVALUE(dp->dvote)); - } else { - PR_PROTO("%s:%d: PRIORITIES UNCHANGED (pri = 0x%x)\n", - proc, domid, IDNVOTE_PRIVALUE(dp->dvote)); - } -} - -/* - * Initiate a link between the local domain and the remote domain - * containing the given cpuid. - */ -int -idn_link(int domid, int cpuid, int pri, int waittime, idnsb_error_t *sep) -{ - int rv; - idn_domain_t *dp; - void *opcookie; - procname_t proc = "idn_link"; - - if ((cpuid < 0) || (cpuid >= NCPU)) { - cmn_err(CE_WARN, - "IDN: 201: (LINK) invalid CPU ID (%d)", cpuid); - return (EINVAL); - } - if (waittime < 0) { - cmn_err(CE_WARN, - "IDN: 202: (LINK) invalid time-out value (%d)", - waittime); - return (EINVAL); - } - if (!VALID_DOMAINID(domid)) { - cmn_err(CE_WARN, - "IDN: 203: (LINK) invalid domain ID (%d)", - domid); - return (EINVAL); - } - if (domid == idn.localid) - return (0); - - IDN_SYNC_LOCK(); - IDN_DLOCK_EXCL(domid); - - dp = &idn_domain[domid]; - - switch (dp->dstate) { - case IDNDS_CLOSED: - break; - - case IDNDS_CONNECTED: -#ifdef DEBUG - cmn_err(CE_NOTE, - "!IDN: domain %d (CPU ID %d) already connected", - domid, cpuid); -#endif /* DEBUG */ - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - return (0); - - default: - cmn_err(CE_WARN, - "IDN: 204: domain %d state (%s) inappropriate", - domid, idnds_str[dp->dstate]); - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - return (EINVAL); - } - - rv = idn_open_domain(domid, cpuid, 0); - if (rv != 0) { - cmn_err(CE_WARN, - "IDN: 205: (%s) failed to open-domain(%d,%d)", - proc, domid, cpuid); - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - return (EIO); - } - - - IDN_DLOCK_EXCL(idn.localid); - idn_update_priority(idn.localid, pri); - IDN_DUNLOCK(idn.localid); - - if (waittime > 0) - opcookie = idn_init_op(IDNOP_CONNECTED, DOMAINSET(domid), sep); - - (void) idn_connect(domid); - - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - - PR_PROTO("%s:%d: ALLOCATED idn_link(%d)\n", proc, domid, cpuid); - - if (waittime > 0) { - boardset_t domset = 0; - /* - * Well we've successfully allocated a domain id, - * but the link may not be fully established yet. - * Need to wait since it happens asynchronously. - */ - PR_PROTO("%s:%d: WAITING for op(%s) for (domset 0%x)...\n", - proc, domid, idnop_str[IDNOP_CONNECTED], - DOMAINSET(domid)); - - rv = idn_wait_op(opcookie, &domset, waittime); - } - -#ifdef DEBUG - if (rv == 0) { - if (waittime > 0) { - PR_PROTO("%s:%d: connect SUCCEEDED (cpu %d)\n", - proc, domid, cpuid); - } else { - PR_PROTO("%s:%d: connect KICKED OFF (cpu %d)\n", - proc, domid, cpuid); - } - } else { - PR_PROTO("%s:%d: connect FAILED (cpu %d)\n", - proc, domid, cpuid); - } -#endif /* DEBUG */ - - return (rv); -} - -/* - * Unlink the given domain from any domain cluster of - * which it might be a member. Force indicates that domain - * should not go AWOL and if it's currently AWOL to close - * and remove it. - * IMPORTANT: If the (hard) force flag is set, the caller is - * assumed to GUARANTEE that the given domain will - * not attempt to communicate with the local domain - * in any manner. - */ -int -idn_unlink(int domid, boardset_t idnset, idn_fin_t fintype, - idn_finopt_t finopt, int waittime, idnsb_error_t *sep) -{ - int rv = 0; - domainset_t domset; - void *opcookie; - procname_t proc = "idn_unlink"; - - - if (waittime < 0) { - cmn_err(CE_WARN, - "IDN: 202: (UNLINK) invalid time-out value (%d)", - waittime); - SET_IDNKERR_IDNERR(sep, IDNKERR_INVALID_WTIME); - SET_IDNKERR_PARAM0(sep, waittime); - return (EINVAL); - } - if (!VALID_DOMAINID(domid)) { - cmn_err(CE_WARN, - "IDN: 203: (UNLINK) invalid domain ID (%d)", - domid); - SET_IDNKERR_IDNERR(sep, IDNKERR_INVALID_DOMAIN); - SET_IDNKERR_PARAM0(sep, domid); - SET_IDNKERR_PARAM1(sep, -1); - return (EINVAL); - } - if (idn.localid == IDN_NIL_DOMID) { -#ifdef DEBUG - cmn_err(CE_NOTE, - "!IDN: %s: local domain not connected to an IDNnet", - proc); -#endif /* DEBUG */ - return (0); - } - - /* - * Lock ordering protocols requires that we grab the - * global lock _before_ the local domain's lock. - * However, non-local domains must have their lock - * grabbed _before_ the global lock. - */ - IDN_SYNC_LOCK(); - IDN_GLOCK_EXCL(); - domset = idn.domset.ds_trans_on | idn.domset.ds_trans_off; - if ((idn.state == IDNGS_OFFLINE) && !domset) { -#ifdef DEBUG - cmn_err(CE_WARN, - "!IDN: %s: local domain not connected to an IDNnet", - proc); -#endif /* DEBUG */ - IDN_GUNLOCK(); - IDN_SYNC_UNLOCK(); - return (0); - } - - if ((domid == IDN_NIL_DOMID) || (domid == idn.localid)) { - domid = idn.localid; - IDN_GSTATE_TRANSITION(IDNGS_DISCONNECT); - IDN_SET_NEW_MASTERID(IDN_NIL_DOMID); - domset = DOMAINSET_ALL; - DOMAINSET_DEL(domset, idn.localid); - } else { - domset = DOMAINSET(domid); - } - IDN_GUNLOCK(); - - if (waittime > 0) - opcookie = idn_init_op(IDNOP_DISCONNECTED, domset, sep); - - idn_unlink_domainset(domset, fintype, IDNFIN_ARG_NONE, finopt, idnset); - - IDN_SYNC_UNLOCK(); - - if (waittime > 0) { - /* - * Well the unlink has successfully kicked off. - * Since process is asynchronous we need to wait - * for it to complete. - */ - PR_PROTO("%s:%d: WAITING for op(%s) for (domset 0%x)...\n", - proc, domid, idnop_str[IDNOP_DISCONNECTED], - domset); - - rv = idn_wait_op(opcookie, &domset, waittime); - } - - if (rv == 0) { - if (waittime > 0) { - PR_PROTO("%s:%d: disconnect SUCCEEDED\n", - proc, domid); - } else { - PR_PROTO("%s:%d: disconnect KICKED OFF\n", - proc, domid); - } - } else { - PR_PROTO("%s:%d: disconnect FAILED\n", proc, domid); - } - - return (rv); -} - -static void -idn_unlink_domainset(domainset_t domset, idn_fin_t fintype, - idn_finarg_t finarg, idn_finopt_t finopt, - boardset_t idnset) -{ - int d; - domainset_t offset; - procname_t proc = "idn_unlink_domainset"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - - /* - * Determine subset for which we have - * no active connections. - */ - offset = domset & ~(idn.domset.ds_trans_on | - idn.domset.ds_connected | - idn.domset.ds_trans_off | - idn.domset.ds_relink); - /* - * Determine subset that are really candidates. - * Note that we include those already down the path - * since it's possible a request came in to upgrade - * their fintype (e.g. NORMAL->FORCE_SOFT). - */ - domset &= ~offset; - - if (offset) - idn_update_op(IDNOP_DISCONNECTED, offset, NULL); - - IDN_GLOCK_EXCL(); - if ((finopt == IDNFIN_OPT_RELINK) && (idn.state != IDNGS_DISCONNECT)) { - /* - * Don't add domains already transitioning off. - * If they caught on an earlier Reconfig wave then - * they'll already be in ds_relink anyway. Otherwise, - * once a domain is transition off we can't upgrade - * him to a RELINK. - */ -#ifdef DEBUG - if (idn.domset.ds_hitlist & domset) { - PR_HITLIST("%s: domset=%x, hitlist=%x, trans_off=%x " - "-> relink = %x -> %x\n", - proc, domset, idn.domset.ds_hitlist, - idn.domset.ds_relink, idn.domset.ds_trans_off, - idn.domset.ds_relink | - (domset & ~idn.domset.ds_trans_off)); - } -#endif /* DEBUG */ - - domset &= ~idn.domset.ds_trans_off; - idn.domset.ds_relink |= domset; - } else { - idn.domset.ds_relink &= ~domset; - } - /* - * Update the ds_trans_on/off so we don't waste - * time talking to these folks. - */ - idn.domset.ds_trans_on &= ~domset; - idn.domset.ds_trans_off |= domset; - - if (domset == 0) { - if ((idn.domset.ds_trans_on | - idn.domset.ds_connected | - idn.domset.ds_trans_off | - idn.domset.ds_relink) == 0) { - PR_HITLIST("%s:%x: HITLIST %x -> 0\n", - proc, domset, idn.domset.ds_hitlist); - idn.domset.ds_hitlist = 0; - IDN_GSTATE_TRANSITION(IDNGS_OFFLINE); - } - IDN_GUNLOCK(); - return; - } - IDN_GUNLOCK(); - - for (d = 0; d < MAX_DOMAINS; d++) { - idn_domain_t *dp; - idn_fin_t ftype; - - if (!DOMAIN_IN_SET(domset, d)) - continue; - - dp = &idn_domain[d]; - IDN_DLOCK_EXCL(d); - IDN_HISTORY_LOG(IDNH_RELINK, d, dp->dstate, - idn.domset.ds_relink); - ftype = fintype; - if ((dp->dcpu != IDN_NIL_DCPU) && dp->dhw.dh_boardset) { - /* - * If domain is not in the IDNSET passed - * down then we need to upgrade this to - * hard-force in order to prevent possible - * system failures (arbstop). This is simply - * extra protection beyond that checked by - * the SSP. IDNSET contains the set of boards - * that have a "link" to the local domain, - * including the SMD regs. - */ - if ((idnset & dp->dhw.dh_boardset) == 0) { - PR_PROTO("%s:%d: boardset 0x%x " - "NOT in IDNSET 0x%x\n", - proc, d, dp->dhw.dh_boardset, - idnset); - if (ftype != IDNFIN_FORCE_HARD) - cmn_err(CE_NOTE, - "!IDN: 222: no IDN linkage " - "found (b=0x%x, i=0x%x) " - "upgrading unlink %s to %s", - dp->dhw.dh_boardset, - idnset, idnfin_str[ftype], - idnfin_str[IDNFIN_FORCE_HARD]); - - ftype = IDNFIN_FORCE_HARD; - } else { - PR_PROTO("%s:%d: boardset 0x%x " - "FOUND in IDNSET 0x%x\n", - proc, d, dp->dhw.dh_boardset, - idnset); - } - } - (void) idn_disconnect(d, ftype, finarg, IDNDS_SYNC_TYPE(dp)); - IDN_DUNLOCK(d); - } -} - -/* - * Return w/locks held. - */ -static int -idn_connect(int domid) -{ - idn_xdcargs_t xargs; - idn_domain_t *dp = &idn_domain[domid]; - procname_t proc = "idn_connect"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - ASSERT(dp->dcpu != IDN_NIL_DCPU); - - if (dp->dstate != IDNDS_CLOSED) { - if (DOMAIN_IN_SET(idn.domset.ds_trans_on | - idn.domset.ds_connected, domid)) { - PR_PROTO("%s:%d: already connected or " - "in-progress\n", proc, domid); - } else { - PR_PROTO("%s:%d: current state (%s) != " - "CLOSED\n", proc, domid, - idnds_str[dp->dstate]); - } - return (-1); - } - - ASSERT(!DOMAIN_IN_SET(idn.domset.ds_connected, domid)); - ASSERT(!DOMAIN_IN_SET(idn.domset.ds_trans_off, domid)); - - dp->dxp = &xphase_nego; - IDN_XSTATE_TRANSITION(dp, IDNXS_PEND); - - (void) idn_xphase_transition(domid, NULL, xargs); - - return (0); -} - -/* - * Return w/locks held. - */ -static int -idn_disconnect(int domid, idn_fin_t fintype, idn_finarg_t finarg, - idn_finsync_t finsync) -{ - int new_masterid, new_cpuid = IDN_NIL_DCPU; - uint_t token; - uint_t finmaster; - idn_xdcargs_t xargs; - idn_finopt_t finopt; - idn_domain_t *dp = &idn_domain[domid]; - procname_t proc = "idn_disconnect"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - if (dp->dstate == IDNDS_CLOSED) { - PR_PROTO("%s:%d: already CLOSED\n", proc, domid); - idn_update_op(IDNOP_DISCONNECTED, DOMAINSET(domid), NULL); - return (-1); - } - - /* - * Terminate any outstanding commands that were - * targeted towards this domain. - */ - idn_terminate_cmd(domid, ECANCELED); - - /* - * Terminate any and all retries that may have - * outstanding for this domain. - */ - token = IDN_RETRY_TOKEN(domid, IDN_RETRY_TYPEALL); - (void) idn_retry_terminate(token); - - /* - * Stop all outstanding message timers for - * this guy. - */ - IDN_MSGTIMER_STOP(domid, 0, 0); - - dp->dxp = &xphase_fin; - IDN_XSTATE_TRANSITION(dp, IDNXS_PEND); - if ((int)dp->dfin < (int)fintype) { - /* - * You can only upgrade a fin type. - * We don't allow it to be downgraded - * as it's too dangerous since some - * state may have been blown away while - * we were fin'ing at a higher level. - */ - IDN_FSTATE_TRANSITION(dp, fintype); - } - - dp->dfin_sync = finsync; - PR_PROTO("%s:%d: disconnect synchronously = %s\n", - proc, domid, (finsync == IDNFIN_SYNC_OFF) ? "OFF" : - (finsync == IDNFIN_SYNC_NO) ? "NO" : "YES"); - - IDN_GLOCK_SHARED(); - if (DOMAIN_IN_SET(idn.domset.ds_relink, domid) && - (idn.state != IDNGS_DISCONNECT)) { - finopt = IDNFIN_OPT_RELINK; - } else { - finopt = IDNFIN_OPT_UNLINK; - PR_HITLIST("%s:%d: HITLIST %x -> %x\n", - proc, domid, idn.domset.ds_hitlist, - idn.domset.ds_hitlist | DOMAINSET(domid)); - DOMAINSET_ADD(idn.domset.ds_hitlist, domid); - } - - CLR_XARGS(xargs); - SET_XARGS_FIN_TYPE(xargs, dp->dfin); - SET_XARGS_FIN_ARG(xargs, finarg); - SET_XARGS_FIN_OPT(xargs, finopt); - SET_XARGS_FIN_DOMSET(xargs, 0); /* unused when msg = 0 */ - new_masterid = IDN_GET_NEW_MASTERID(); - IDN_GUNLOCK(); - if (new_masterid != IDN_NIL_DOMID) - new_cpuid = idn_domain[new_masterid].dcpu; - finmaster = MAKE_FIN_MASTER(new_masterid, new_cpuid); - SET_XARGS_FIN_MASTER(xargs, finmaster); - - (void) idn_xphase_transition(domid, NULL, xargs); - - return (0); -} - -static int -idn_next_xstate(idn_xstate_t o_xstate, int err, uint_t msg) -{ - int index; - procname_t proc = "idn_next_xstate"; - - ASSERT(((int)o_xstate >= 0) && ((int)o_xstate <= 4)); - - if (!msg) - index = 0; - else if ((msg & IDNP_MSGTYPE_MASK) == 0) - index = (msg & IDNP_ACK) ? 3 : (msg & IDNP_NACK) ? 4 : -1; - else - index = (msg & IDNP_ACK) ? 2 : - !(msg & IDNP_ACKNACK_MASK) ? 1 : -1; - - if (index == -1) { - STRING(str); - - INUM2STR(msg, str); - PR_PROTO("%s: (msg = 0x%x(%s))\n", proc, msg, str); - return (IDNXS_NIL); - } - - if (err == -1) { - int n_xstate; - /* - * Caller is just interested in querying is this - * is a valid message to receive in the current - * xstate. A return value of IDNXS_NIL indicates - * that it's not. A return value of non-IDNXS_NIL - * indicates it's cool. An invalid message is - * determined by both err & !err states being IDNXS_NIL. - */ - n_xstate = idnxs_state_table[(int)o_xstate][index][0]; - if (n_xstate != IDNXS_NIL) - return (n_xstate); - else - return (idnxs_state_table[(int)o_xstate][index][1]); - } else { - return (idnxs_state_table[(int)o_xstate][index][err ? 1 : 0]); - } -} - -static int -idn_select_candidate(domainset_t master_set) -{ - int d, best_id = IDN_NIL_DOMID; - uint_t best_vote = 0; - idn_domain_t *dp; - procname_t proc = "idn_select_candidate"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - - if (master_set == 0) { - PR_PROTO("%s: %x -> %d\n", proc, master_set, IDN_NIL_DOMID); - return (IDN_NIL_DOMID); - } - - for (d = 0; d < MAX_DOMAINS; d++) { - uint_t vote; - idn_vote_t v; - - if (!DOMAIN_IN_SET(master_set, d)) - continue; - - dp = &idn_domain[d]; - - if ((dp->domid == IDN_NIL_DOMID) || - (dp->dcpu == IDN_NIL_DCPU) || - ((v.ticket = dp->dvote.ticket) == 0)) - continue; - - vote = IDNVOTE_ELECT(v); - - if (vote > best_vote) { - best_vote = vote; - best_id = d; - } - } - - PR_PROTO("%s: %x -> %d\n", proc, master_set, best_id); - - return (best_id); -} - -/* - * If a non-zero value is returned then GLOCK will have been dropped. - * Otherwise, routine returns with all incoming locks still held. - */ -static int -idn_select_master(int domid, int rmasterid, int rcpuid) -{ - char *sel; - int lmasterid, masterid; - int do_reconfig = 0; - int lindex, rindex; - idn_domain_t *ldp, *rdp; - uint_t rvote, lvote; - idn_master_select_t select; - procname_t proc = "idn_select_master"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_GLOCK_IS_EXCL()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - PR_PROTO("%s:%d: lmasterid = %d, rmasterid = %d, rcpuid = %d\n", - proc, domid, IDN_GET_MASTERID(), rmasterid, rcpuid); - - IDN_DLOCK_EXCL(idn.localid); - - ldp = &idn_domain[idn.localid]; - rdp = &idn_domain[domid]; - - /* - * Clear master bits since mastership is derived from - * other information (local/remote idn.masterid/idn.new_masterid) - * and we don't want the vote master bit to confuse matters. - */ - lvote = IDNVOTE_ELECT(ldp->dvote); - rvote = IDNVOTE_ELECT(rdp->dvote); - - lmasterid = IDN_GET_MASTERID(); - - lindex = (lmasterid == IDN_NIL_DOMID) ? MASTER_IS_NONE : - (lmasterid == idn.localid) ? MASTER_IS_LOCAL : - (lmasterid == domid) ? MASTER_IS_REMOTE : - MASTER_IS_OTHER; - - rindex = (rmasterid == IDN_NIL_DOMID) ? MASTER_IS_NONE : - (rmasterid == domid) ? MASTER_IS_REMOTE : - (rmasterid == idn.localid) ? MASTER_IS_LOCAL : - MASTER_IS_OTHER; - - select = master_select_table[lindex][rindex]; - - masterid = IDN_NIL_DOMID; - - /* - * Each case is responsible for dropping DLOCK(localid) - * and GLOCK if it doesn't select a master, unless a - * reconfig is necessary. - */ - switch (select) { - case MASTER_SELECT_VOTE_RCFG: - sel = "VOTE_RECONFIG"; - if (lvote > rvote) { - /* - * If the local domain is the winner then remote - * domain will have to Reconfig. We'll continue - * through the connection process anyway. The - * remote domains will tell us to back-off while - * Reconfigs, but that's okay as we'll keep retrying. - */ - masterid = idn.localid; - } else if (lvote < rvote) { - do_reconfig = 1; - /* - * GLOCK will get dropped once reconfig - * is kicked off. - */ - } else { - cmn_err(CE_WARN, - "IDN: 206: cannot link domains " - "with equal votes (L(%d),R(%d),0x%x)", - idn.localid, domid, rvote); - IDN_GUNLOCK(); - } - IDN_DUNLOCK(idn.localid); - break; - - case MASTER_SELECT_VOTE: - sel = "VOTE"; - if (lvote > rvote) { - masterid = idn.localid; - ldp->dvote.v.master = 1; - rdp->dvote.v.master = 0; - } else if (lvote < rvote) { - masterid = domid; - ldp->dvote.v.master = 0; - rdp->dvote.v.master = 1; - } else { - cmn_err(CE_WARN, - "IDN: 206: cannot link domains " - "with equal votes (L(%d),R(%d),0x%x)", - idn.localid, domid, rvote); - } - ASSERT(IDN_GET_MASTERID() == IDN_NIL_DOMID); - if (masterid != IDN_NIL_DOMID) { - IDN_SET_MASTERID(masterid); - IDN_SET_NEW_MASTERID(IDN_NIL_DOMID); - } else { - IDN_GUNLOCK(); - } - IDN_DUNLOCK(idn.localid); - break; - - case MASTER_SELECT_REMOTE: - sel = "REMOTE"; - masterid = domid; - if (IDN_GET_MASTERID() == IDN_NIL_DOMID) { - IDN_SET_MASTERID(masterid); - IDN_SET_NEW_MASTERID(IDN_NIL_DOMID); - ldp->dvote.v.master = 0; - rdp->dvote.v.master = 1; - } - ASSERT(IDN_GET_MASTERID() == domid); - IDN_DUNLOCK(idn.localid); - break; - - case MASTER_SELECT_LOCAL: - sel = "LOCAL"; - masterid = idn.localid; - if (IDN_GET_MASTERID() == IDN_NIL_DOMID) { - IDN_SET_MASTERID(masterid); - IDN_SET_NEW_MASTERID(IDN_NIL_DOMID); - ldp->dvote.v.master = 1; - rdp->dvote.v.master = 0; - } - ASSERT(IDN_GET_MASTERID() == idn.localid); - IDN_DUNLOCK(idn.localid); - break; - - case MASTER_SELECT_CONNECT: - sel = "CONNECT"; - if (rmasterid == lmasterid) { - /* - * Local and remote have same master, - * let him come onboard. - */ - masterid = lmasterid; - IDN_DUNLOCK(idn.localid); - - } else { - int rv; - - IDN_DUNLOCK(idn.localid); - IDN_GUNLOCK(); - IDN_DLOCK_EXCL(rmasterid); - PR_PROTO("%s:%d: attempting connect w/remote " - "master %d\n", - proc, domid, rmasterid); - rv = idn_open_domain(rmasterid, rcpuid, 0); - if (rv == 0) { - (void) idn_connect(rmasterid); - } else if (rv < 0) { - cmn_err(CE_WARN, - "IDN: 205: (%s) failed to " - "open-domain(%d,%d)", - proc, rmasterid, rcpuid); - } else { - /* - * Must already have a connection going. - */ - PR_PROTO("%s:%d: failed " - "idn_open_domain(%d,%d,0) " - "(rv = %d)\n", - proc, domid, rmasterid, - rcpuid, rv); - } - IDN_DUNLOCK(rmasterid); - } - break; - - case MASTER_SELECT_WAIT: - sel = "WAIT"; - /* - * If the remote domain has the same master as the local - * domain then there's no need to wait. - */ - if (rmasterid == lmasterid) { - masterid = lmasterid; - } else { - IDN_GUNLOCK(); - } - IDN_DUNLOCK(idn.localid); - break; - - case MASTER_SELECT_ERROR: - sel = "ERROR"; - /* - * Hit impossible condition. - */ - cmn_err(CE_WARN, - "IDN: 207: local/remote master-id conflict " - "(%d.lmasterid = %d, %d.rmasterid = %d)", - idn.localid, lmasterid, domid, rmasterid); - IDN_GUNLOCK(); - IDN_DUNLOCK(idn.localid); - break; - - default: - cmn_err(CE_WARN, - "IDN: 208: %s: unknown case (%d)", - proc, (int)select); - IDN_GUNLOCK(); - IDN_DUNLOCK(idn.localid); - ASSERT(0); - break; - } - - if (masterid == IDN_NIL_DOMID) { - PR_PROTO("%s:%d: NO MASTER SELECTED (rmstr=%d) sel=%s\n", - proc, domid, rmasterid, sel); - } else { - PR_PROTO("%s:%d: MASTER SELECTED = %d (%s)\n", - proc, domid, masterid, - (masterid == idn.localid) ? "LOCAL" : - (masterid == domid) ? "REMOTE" : "OTHER"); - } - - if (do_reconfig) { - domainset_t dis_set; - - /* - * Local domain already has a master. - * Need to dismantle all connections - * and reestablish one with new master. - */ - IDN_GKSTAT_GLOBAL_EVENT(gk_reconfigs, gk_reconfig_last); - - PR_PROTO("%s:%d: RECONFIG new masterid = %d\n", - proc, domid, domid); - - IDN_GSTATE_TRANSITION(IDNGS_RECONFIG); - IDN_SET_NEW_MASTERID(domid); - IDN_GUNLOCK(); - - dis_set = idn.domset.ds_trans_on | idn.domset.ds_connected; - DOMAINSET_DEL(dis_set, domid); - - idn_unlink_domainset(dis_set, IDNFIN_NORMAL, IDNFIN_ARG_NONE, - IDNFIN_OPT_RELINK, BOARDSET_ALL); - } - - return ((masterid == IDN_NIL_DOMID) ? -1 : 0); -} - -/*ARGSUSED1*/ -static void -idn_retry_query(uint_t token, void *arg) -{ - idn_retry_t rtype = IDN_RETRY_TOKEN2TYPE(token); - int d, domid = IDN_RETRY_TOKEN2DOMID(token); - idn_domain_t *dp = &idn_domain[domid]; - idn_synccmd_t sync_cmd; - domainset_t query_set, my_ready_set; - procname_t proc = "idn_retry_query"; - - IDN_SYNC_LOCK(); - IDN_DLOCK_EXCL(domid); - - switch (rtype) { - case IDNRETRY_CONQ: - sync_cmd = IDNSYNC_CONNECT; - my_ready_set = idn.domset.ds_ready_on | idn.domset.ds_connected; - my_ready_set &= ~idn.domset.ds_trans_off; - DOMAINSET_ADD(my_ready_set, idn.localid); - break; - - case IDNRETRY_FINQ: - sync_cmd = IDNSYNC_DISCONNECT; - my_ready_set = idn.domset.ds_ready_off | - ~idn.domset.ds_connected; - break; - - default: - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - return; - } - - if (dp->dsync.s_cmd == sync_cmd) - my_ready_set |= dp->dsync.s_set_rdy; - - query_set = idn_sync_register(domid, sync_cmd, 0, IDNSYNC_REG_QUERY); - - PR_PROTO("%s:%d: query_set = 0x%x\n", proc, domid, query_set); - - if (query_set == 0) { - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - return; - } - - for (d = 0; d < MAX_DOMAINS; d++) { - if (!DOMAIN_IN_SET(query_set, d)) - continue; - - dp = &idn_domain[d]; - if (d != domid) - IDN_DLOCK_EXCL(d); - - if ((dp->dsync.s_cmd == sync_cmd) || - (!dp->dcookie_send && - (rtype == IDNRETRY_CONQ))) { - if (d != domid) - IDN_DUNLOCK(d); - continue; - } - - IDN_SYNC_QUERY_UPDATE(domid, d); - - if (rtype == IDNRETRY_CONQ) - (void) idn_send_con(d, NULL, IDNCON_QUERY, - my_ready_set); - else - (void) idn_send_fin(d, NULL, IDNFIN_QUERY, - IDNFIN_ARG_NONE, IDNFIN_OPT_NONE, my_ready_set, - NIL_FIN_MASTER); - if (d != domid) - IDN_DUNLOCK(d); - } - - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); -} - -static int -idn_send_nego(int domid, idn_msgtype_t *mtp, domainset_t conset) -{ - idn_domain_t *ldp, *dp; - int d, masterid; - uint_t dmask; - uint_t acknack; - uint_t ticket; - idnneg_dset_t dset; - idn_msgtype_t mt; - procname_t proc = "idn_send_nego"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - if (mtp) { - acknack = mtp->mt_mtype & IDNP_ACKNACK_MASK; - mt.mt_mtype = mtp->mt_mtype; - mt.mt_atype = mtp->mt_atype; - mt.mt_cookie = mtp->mt_cookie; - } else { - acknack = 0; - mt.mt_mtype = IDNP_NEGO; - mt.mt_atype = 0; - mt.mt_cookie = IDN_TIMER_PUBLIC_COOKIE; - } - - IDN_GLOCK_SHARED(); - - dp = &idn_domain[domid]; - ldp = &idn_domain[idn.localid]; - - if ((idn.state == IDNGS_RECONFIG) || - ((masterid = IDN_GET_MASTERID()) == IDN_NIL_DOMID)) { - masterid = IDN_GET_NEW_MASTERID(); - if ((masterid == idn.localid) || (masterid == domid)) { - /* - * We only send the new-master "hint" to - * "other" domains. If the new-master is - * ourself or we're talking to the new-master - * then we need to be accurate about our - * real master so that the correct master - * is selected. - */ - masterid = IDN_NIL_DOMID; - } - } - - DOMAINSET_DEL(conset, idn.localid); - DOMAINSET_DEL(conset, domid); - /* - * Exclude domains from conset that are on - * remote domain's hitlist. He's not interested - * in hearing about them. SSP is probably requesting - * such domains be unlinked - will eventually get to - * local domain. - */ - conset &= ~idn.domset.ds_hitlist; - if ((masterid != IDN_NIL_DOMID) && - DOMAIN_IN_SET(idn.domset.ds_hitlist, masterid)) { - PR_PROTO("%s:%d: masterid(%d) on hitlist(0x%x) -> -1\n", - proc, domid, masterid, idn.domset.ds_hitlist); - /* - * Yikes, our chosen master is on the hitlist! - */ - masterid = IDN_NIL_DOMID; - } - - dmask = IDNNEG_DSET_MYMASK(); - IDNNEG_DSET_INIT(dset, dmask); - for (d = 0; d < MAX_DOMAINS; d++) { - int cpuid; - - if (!DOMAIN_IN_SET(conset, d)) - continue; - - if ((cpuid = idn_domain[d].dcpu) == IDN_NIL_DCPU) { - ASSERT(d != masterid); - continue; - } - - IDNNEG_DSET_SET(dset, d, cpuid, dmask); - } - IDNNEG_DSET_SET_MASTER(dset, domid, masterid); - ASSERT((masterid != IDN_NIL_DOMID) ? - (idn_domain[masterid].dcpu != IDN_NIL_DCPU) : 1); - IDN_GUNLOCK(); - - IDN_DLOCK_SHARED(idn.localid); - ticket = IDNVOTE_BASICS(ldp->dvote); - /* - * We just want to send basic vote components without an - * indication of mastership (master bit) since that's primarily - * for local domain's usage. There is more correct master - * indications in the DSET. Recall that if we were in a - * Reconfig we would have transmitted the "new_masterid" - * which might conflict with the local domain's vote.v.master - * bit if he was originally the master prior to the Reconfig. - */ - - PR_PROTO("%s:%d: sending nego%sto (cpu %d) " - "[v=0x%x, cs=0x%x, mstr=%d]\n", - proc, domid, - (acknack & IDNP_ACK) ? "+ack " : - (acknack & IDNP_NACK) ? "+nack " : " ", - dp->dcpu, ticket, conset, masterid); - - IDN_MSGTIMER_START(domid, IDNP_NEGO, 0, - idn_msg_waittime[IDNP_NEGO], &mt.mt_cookie); - - IDNXDC(domid, &mt, ticket, dset[0], dset[1], dset[2]); - - IDN_DUNLOCK(idn.localid); - - return (0); -} - -static int -idn_recv_nego(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs, - ushort_t dcookie) -{ - uint_t msg = mtp->mt_mtype; - idn_msgtype_t mt; - idn_domain_t *dp = &idn_domain[domid]; - idn_xdcargs_t nargs; - procname_t proc = "idn_recv_nego"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - mt.mt_cookie = mtp->mt_cookie; - -#ifdef DEBUG - if (DOMAIN_IN_SET(idn.domset.ds_hitlist, domid)) { - PR_HITLIST("%s:%d: dcpu=%d, dstate=%s, msg=%x, " - "hitlist=%x\n", - proc, domid, dp->dcpu, idnds_str[dp->dstate], - msg, idn.domset.ds_hitlist); - } -#endif /* DEBUG */ - - if (dp->dcpu == IDN_NIL_DCPU) { - int cpuid; - uint_t ticket; - /* - * Brandnew link. Need to open a new domain entry. - */ - ticket = GET_XARGS_NEGO_TICKET(xargs); - cpuid = dp->dcpu_last; - ASSERT(VALID_CPUID(cpuid)); - - if (idn_open_domain(domid, cpuid, ticket) != 0) { - PR_PROTO("%s:%d: FAILED to open doamin " - "(ticket = 0x%x)\n", - proc, domid, ticket); - return (-1); - } - } - - if ((msg & IDNP_MSGTYPE_MASK) == IDNP_NEGO) { - PR_PROTO("%s:%d: assigned SEND cookie 0x%x\n", - proc, domid, dcookie); - dp->dcookie_send = dcookie; - } - - if ((dp->dxp == NULL) && IDNDS_IS_CLOSED(dp)) { - dp->dxp = &xphase_nego; - IDN_XSTATE_TRANSITION(dp, IDNXS_PEND); - } else if (dp->dxp != &xphase_nego) { - if (msg & IDNP_MSGTYPE_MASK) { - /* - * If we already have a connection to somebody - * trying to initiate a connection to us, then - * possibly we've awaken from a coma or he did. - * In any case, dismantle current connection - * and attempt to establish a new one. - */ - if (dp->dstate == IDNDS_CONNECTED) { - DOMAINSET_ADD(idn.domset.ds_relink, domid); - IDN_HISTORY_LOG(IDNH_RELINK, domid, - dp->dstate, idn.domset.ds_relink); - (void) idn_disconnect(domid, IDNFIN_NORMAL, - IDNFIN_ARG_NONE, IDNFIN_SYNC_YES); - } else { - mt.mt_mtype = IDNP_NACK; - mt.mt_atype = msg; - - CLR_XARGS(nargs); - - if (DOMAIN_IN_SET(idn.domset.ds_hitlist, - domid)) { - SET_XARGS_NACK_TYPE(nargs, - IDNNACK_EXIT); - } else { - int new_masterid; - int new_cpuid = IDN_NIL_DCPU; - - SET_XARGS_NACK_TYPE(nargs, - IDNNACK_RETRY); - IDN_GLOCK_SHARED(); - new_masterid = IDN_GET_NEW_MASTERID(); - if (new_masterid == IDN_NIL_DOMID) - new_masterid = - IDN_GET_MASTERID(); - if (new_masterid != IDN_NIL_DOMID) { - idn_domain_t *mdp; - - mdp = &idn_domain[new_masterid]; - new_cpuid = mdp->dcpu; - } - SET_XARGS_NACK_ARG1(nargs, - new_masterid); - SET_XARGS_NACK_ARG2(nargs, new_cpuid); - IDN_GUNLOCK(); - } - idn_send_acknack(domid, &mt, nargs); - } - } - return (0); - } - - (void) idn_xphase_transition(domid, mtp, xargs); - - return (0); -} - -/*ARGSUSED1*/ -static void -idn_retry_nego(uint_t token, void *arg) -{ - int domid = IDN_RETRY_TOKEN2DOMID(token); - int new_masterid; - idn_domain_t *dp = &idn_domain[domid]; - idn_xdcargs_t xargs; - procname_t proc = "idn_retry_nego"; - - ASSERT(IDN_RETRY_TOKEN2TYPE(token) == IDNRETRY_NEGO); - - IDN_SYNC_LOCK(); - IDN_DLOCK_EXCL(domid); - - if (dp->dxp != &xphase_nego) { - STRING(str); - -#ifdef DEBUG - if (dp->dxp) { - INUM2STR(dp->dxp->xt_msgtype, str); - } -#endif /* DEBUG */ - - PR_PROTO("%s:%d: dxp(%s) != NEGO...bailing...\n", - proc, domid, dp->dxp ? str : "NULL"); - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - return; - } - - if (dp->dxstate != IDNXS_PEND) { - PR_PROTO("%s:%d: xstate(%s) != %s...bailing\n", - proc, domid, idnxs_str[dp->dxstate], - idnxs_str[IDNXS_PEND]); - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - return; - } - - IDN_GLOCK_SHARED(); - if (idn.state == IDNGS_RECONFIG) { - /* - * Have to try again later after - * reconfig has completed. - */ - PR_PROTO("%s:%d: reconfig in-progress...try later\n", - proc, domid); - idn_retry_submit(idn_retry_nego, NULL, token, - idn_msg_retrytime[IDNP_NEGO]); - IDN_GUNLOCK(); - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - return; - } - new_masterid = IDN_GET_NEW_MASTERID(); - if ((idn.state == IDNGS_CONNECT) && - (new_masterid != IDN_NIL_DOMID) && - (domid != new_masterid) && - (idn.localid != new_masterid)) { - /* - * We have a new master pending and this - * guy isn't it. Wait until the local domain - * has a chance to connect with the new - * master before going forward with this - * guy. - */ - PR_PROTO("%s:%d: waiting for connect to new master %d\n", - proc, domid, IDN_GET_NEW_MASTERID()); - idn_retry_submit(idn_retry_nego, NULL, token, - idn_msg_retrytime[IDNP_NEGO]); - IDN_GUNLOCK(); - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - return; - } - IDN_GUNLOCK(); - - (void) idn_xphase_transition(domid, NULL, xargs); - - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); -} - -static int -idn_check_nego(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - int d, new_masterid, masterid; - int cpuid, m_cpuid = -1; - uint_t dmask; - uint_t msg = mtp ? mtp->mt_mtype : 0; - idn_domain_t *dp, *ldp; - domainset_t con_set, pending_set; - idnneg_dset_t dset; - procname_t proc = "idn_check_nego"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - dp = &idn_domain[domid]; - ldp = &idn_domain[idn.localid]; - - if (msg & IDNP_NACK) { - if (GET_XARGS_NACK_TYPE(xargs) == IDNNACK_EXIT) { - PR_HITLIST("%s:%d(%s): (msg=%x) EXIT received, " - "adding to hitlist %x -> %x\n", - proc, domid, idnds_str[dp->dstate], msg, - idn.domset.ds_hitlist, - idn.domset.ds_hitlist | DOMAINSET(domid)); - - DOMAINSET_ADD(idn.domset.ds_hitlist, domid); - return (-1); - } else { - return (0); - } - } - - if (DOMAIN_IN_SET(idn.domset.ds_hitlist, domid)) { - PR_HITLIST("%s:%d(%s): (msg=%x) domain in hitlist (%x) - " - "exiting phase\n", - proc, domid, idnds_str[dp->dstate], msg, - idn.domset.ds_hitlist); - return (-1); - } - - if ((dp->dstate == IDNDS_NEGO_PEND) && (msg & IDNP_MSGTYPE_MASK) && - (msg & IDNP_ACK)) /* nego+ack */ - return (1); - - dmask = (uint_t)-1; - - IDN_GLOCK_EXCL(); - if (idn.state == IDNGS_DISCONNECT) { - PR_PROTO("%s:%d: DISCONNECT in-progress >>> EXIT\n", - proc, domid); - IDN_GUNLOCK(); - return (-1); - } else if (idn.state == IDNGS_OFFLINE) { - IDN_GSTATE_TRANSITION(IDNGS_CONNECT); - IDN_PREP_HWINIT(); - IDN_DLOCK_EXCL(idn.localid); - ldp->dvote.v.connected = 0; - IDN_DUNLOCK(idn.localid); - } - - if (!DOMAIN_IN_SET(idn.domset.ds_trans_on, domid)) { - DOMAINSET_ADD(idn.domset.ds_trans_on, domid); - IDN_HISTORY_LOG(IDNH_NEGO, domid, - idn.domset.ds_trans_on, - idn.domset.ds_connected); - } - - switch (idn.state) { - case IDNGS_RECONFIG: - PR_PROTO("%s:%d: RECONFIG in-progress >>> RETRY\n", - proc, domid); - IDN_GUNLOCK(); - return (1); - - case IDNGS_CONNECT: - new_masterid = IDN_GET_NEW_MASTERID(); - if ((new_masterid != IDN_NIL_DOMID) && - (domid != new_masterid) && - (idn.localid != new_masterid)) { - PR_PROTO("%s:%d: waiting for connect to " - "new master %d\n", - proc, domid, IDN_GET_NEW_MASTERID()); - IDN_GUNLOCK(); - return (1); - } - break; - - default: - break; - } - - ASSERT((idn.state == IDNGS_CONNECT) || (idn.state == IDNGS_ONLINE)); - - con_set = 0; - - if (msg) { - idn_domain_t *mdp; - idn_vote_t vote; - - vote.ticket = GET_XARGS_NEGO_TICKET(xargs); - /* - * Sender should note have set master bit, - * but just in case clear it so local domain - * doesn't get confused. - */ - vote.v.master = 0; - dp->dvote.ticket = vote.ticket; - GET_XARGS_NEGO_DSET(xargs, dset); - /*LINTED*/ - IDNNEG_DSET_GET_MASK(dset, domid, dmask); - IDNNEG_DSET_GET_MASTER(dset, new_masterid); - if (new_masterid == IDNNEG_NO_MASTER) { - new_masterid = IDN_NIL_DOMID; - } else { - /* - * Remote domain has a master. Find - * his cpuid in the dset. We may need - * it to initiate a connection. - */ - if (new_masterid == domid) { - m_cpuid = dp->dcpu; - } else { - IDNNEG_DSET_GET(dset, new_masterid, m_cpuid, - dmask); - if (m_cpuid == -1) { - /* - * Something is bogus if remote domain - * is reporting a valid masterid, but - * doesn't have the cpuid for it. - */ - cmn_err(CE_WARN, - "IDN: 209: remote domain (ID " - "%d, CPU %d) reporting master " - "(ID %d) without CPU ID", - domid, dp->dcpu, new_masterid); - DOMAINSET_ADD(idn.domset.ds_hitlist, - domid); - IDN_GUNLOCK(); - return (-1); - } - } - } - - for (d = 0; d < MAX_DOMAINS; d++) { - if ((d == idn.localid) || (d == domid)) - continue; - IDNNEG_DSET_GET(dset, d, cpuid, dmask); - if (cpuid != -1) { - DOMAINSET_ADD(con_set, d); - } - } - -#ifdef DEBUG - if (idn.domset.ds_hitlist) { - PR_HITLIST("%s:%d: con_set %x -> %x (hitlist = %x)\n", - proc, domid, con_set, - con_set & ~idn.domset.ds_hitlist, - idn.domset.ds_hitlist); - } -#endif /* DEBUG */ - - con_set &= ~idn.domset.ds_hitlist; - - ASSERT(!DOMAIN_IN_SET(con_set, idn.localid)); - ASSERT(!DOMAIN_IN_SET(con_set, domid)); - - if ((new_masterid != IDN_NIL_DOMID) && - DOMAIN_IN_SET(idn.domset.ds_hitlist, new_masterid)) { - PR_HITLIST("%s:%d: new_mstr %d -> -1 (hitlist = %x)\n", - proc, domid, new_masterid, - idn.domset.ds_hitlist); - IDN_GUNLOCK(); - return (1); - } - - if (idn_select_master(domid, new_masterid, m_cpuid) < 0) { - /* - * Returns w/GLOCK dropped if error. - */ - return (1); - } - - masterid = IDN_GET_MASTERID(); - ASSERT(masterid != IDN_NIL_DOMID); - - if (idn.state == IDNGS_CONNECT) { - /* - * This is the initial connection for - * the local domain. - */ - IDN_DLOCK_EXCL(idn.localid); - - if (masterid == idn.localid) { - if (idn_master_init() < 0) { - cmn_err(CE_WARN, - "IDN: 210: failed to init " - "MASTER context"); - ldp->dvote.v.master = 0; - IDN_DUNLOCK(idn.localid); - IDN_GSTATE_TRANSITION(IDNGS_DISCONNECT); - IDN_SET_MASTERID(IDN_NIL_DOMID); - IDN_GUNLOCK(); - return (-1); - } - DSLAB_LOCK_EXCL(idn.localid); - ldp->dslab_state = DSLAB_STATE_LOCAL; - DSLAB_UNLOCK(idn.localid); - ldp->dvote.v.connected = 1; - } else { - /* - * Either the remote domain is the - * master or its a new slave trying - * to connect to us. We can't allow - * further progress until we've - * sync'd up with the master. - */ - if (masterid != domid) { - IDN_DUNLOCK(idn.localid); - IDN_GUNLOCK(); - return (1); - } - DSLAB_LOCK_EXCL(idn.localid); - ldp->dslab_state = DSLAB_STATE_REMOTE; - DSLAB_UNLOCK(idn.localid); - } - IDN_DUNLOCK(idn.localid); - /* - * We've sync'd up with the new master. - */ - IDN_GSTATE_TRANSITION(IDNGS_ONLINE); - } - - mdp = &idn_domain[masterid]; - - if ((masterid != domid) && !IDNDS_CONFIG_DONE(mdp)) { - /* - * We can't progress any further with - * other domains until we've exchanged all - * the necessary CFG info with the master, - * i.e. until we have a mailbox area from - * which we can allocate mailboxes to - * other domains. - */ - PR_PROTO("%s:%d: still exchanging CFG " - "w/master(%d)\n", proc, domid, masterid); - IDN_GUNLOCK(); - return (1); - } - - DSLAB_LOCK_EXCL(domid); - dp->dslab_state = ldp->dslab_state; - DSLAB_UNLOCK(domid); - if (idn.state != IDNGS_ONLINE) { - IDN_GSTATE_TRANSITION(IDNGS_ONLINE); - } - } - - IDN_GUNLOCK(); - - pending_set = con_set; - pending_set &= ~(idn.domset.ds_trans_on | idn.domset.ds_connected); - idn.domset.ds_trans_on |= pending_set; - - con_set |= idn.domset.ds_trans_on | idn.domset.ds_connected; - con_set &= ~idn.domset.ds_trans_off; - DOMAINSET_ADD(con_set, idn.localid); - - if (dp->dsync.s_cmd != IDNSYNC_CONNECT) { - idn_sync_exit(domid, IDNSYNC_DISCONNECT); - idn_sync_enter(domid, IDNSYNC_CONNECT, - con_set, DOMAINSET(idn.localid), idn_xstate_transfunc, - (void *)IDNP_CON); - } - - /* - * Get this domain registered as an expected domain on - * the remaining domains in the CONNECT synchronization. - */ - (void) idn_sync_register(domid, IDNSYNC_CONNECT, 0, IDNSYNC_REG_NEW); - - /* - * Note that if (msg == 0), i.e. then there will be - * no dset and also pending_set will be 0. - * So, the following loop will never attempt to - * look at the dset unless (msg != 0), implying - * that we've been through the initial code above - * and have initialized dmask. - */ - ASSERT(pending_set ? (dmask != (uint_t)-1) : 1); - - for (d = 0; d < MAX_DOMAINS; d++) { - int rv; - - if (!DOMAIN_IN_SET(pending_set, d)) - continue; - - ASSERT((d != idn.localid) && (d != domid)); - - dp = &idn_domain[d]; - - IDNNEG_DSET_GET(dset, d, cpuid, dmask); - if (cpuid == -1) { - PR_PROTO("%s:%d: failed to get cpuid from dset " - "for domain %d (pset = 0x%x)\n", - proc, domid, d, pending_set); - DOMAINSET_DEL(idn.domset.ds_trans_on, d); - continue; - } - - IDN_DLOCK_EXCL(d); - if ((rv = idn_open_domain(d, cpuid, 0)) != 0) { - PR_PROTO("%s:%d: failed " - "idn_open_domain(%d,%d,0) (rv = %d)\n", - proc, domid, d, cpuid, rv); - if (rv < 0) { - cmn_err(CE_WARN, - "IDN: 205: (%s) failed to " - "open-domain(%d,%d)", - proc, d, cpuid); - DOMAINSET_DEL(idn.domset.ds_trans_on, d); - } else if (DOMAIN_IN_SET(idn.domset.ds_trans_off, d)) { - /* - * We've requested to connect to a domain - * from which we're disconnecting. We - * better mark this guy for relinking. - */ - DOMAINSET_ADD(idn.domset.ds_relink, d); - IDN_HISTORY_LOG(IDNH_RELINK, d, dp->dstate, - idn.domset.ds_relink); - } - IDN_DUNLOCK(d); - continue; - } - - (void) idn_connect(d); - - IDN_DUNLOCK(d); - } - - return (0); -} - -/*ARGSUSED*/ -static void -idn_action_nego_pend(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - uint_t msg = mtp ? mtp->mt_mtype : 0; - idn_msgtype_t mt; - domainset_t con_set; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - con_set = idn.domset.ds_trans_on | idn.domset.ds_connected; - con_set &= ~idn.domset.ds_trans_off; - - if (!msg) { - (void) idn_send_nego(domid, NULL, con_set); - } else { - mt.mt_mtype = IDNP_NEGO | IDNP_ACK; - mt.mt_atype = 0; - mt.mt_cookie = mtp->mt_cookie; - (void) idn_send_nego(domid, &mt, con_set); - } -} - -/*ARGSUSED*/ -static void -idn_error_nego(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - int new_masterid, new_cpuid; - int retry = 1; - uint_t msg = mtp ? mtp->mt_mtype : 0; - uint_t token; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - if (msg & IDNP_NACK) { - idn_nack_t nack; - - nack = GET_XARGS_NACK_TYPE(xargs); - switch (nack) { - case IDNNACK_RETRY: - new_masterid = (int)GET_XARGS_NACK_ARG1(xargs); - new_cpuid = (int)GET_XARGS_NACK_ARG2(xargs); - break; - - case IDNNACK_EXIT: - retry = 0; - /*FALLTHROUGH*/ - - default: - new_masterid = IDN_NIL_DOMID; - new_cpuid = IDN_NIL_DCPU; - break; - } - idn_nego_cleanup_check(domid, new_masterid, new_cpuid); - } - - if (msg & IDNP_MSGTYPE_MASK) { - idn_msgtype_t mt; - idn_xdcargs_t nargs; - - mt.mt_mtype = IDNP_NACK; - mt.mt_atype = msg; - mt.mt_cookie = mtp->mt_cookie; - CLR_XARGS(nargs); - SET_XARGS_NACK_TYPE(nargs, IDNNACK_RETRY); - IDN_GLOCK_SHARED(); - new_masterid = IDN_GET_NEW_MASTERID(); - if (new_masterid == IDN_NIL_DOMID) - new_masterid = IDN_GET_MASTERID(); - if (new_masterid != IDN_NIL_DOMID) - new_cpuid = idn_domain[new_masterid].dcpu; - else - new_cpuid = IDN_NIL_DCPU; - SET_XARGS_NACK_ARG1(nargs, new_masterid); - SET_XARGS_NACK_ARG2(nargs, new_cpuid); - IDN_GUNLOCK(); - idn_send_acknack(domid, &mt, nargs); - } - - if (retry) { - token = IDN_RETRY_TOKEN(domid, IDNRETRY_NEGO); - idn_retry_submit(idn_retry_nego, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_NEGO]); - } else { - DOMAINSET_DEL(idn.domset.ds_relink, domid); - IDN_RESET_COOKIES(domid); - (void) idn_disconnect(domid, IDNFIN_NORMAL, IDNFIN_ARG_NONE, - IDNDS_SYNC_TYPE(&idn_domain[domid])); - } -} - -/*ARGSUSED*/ -static void -idn_action_nego_sent(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - uint_t msg = mtp ? mtp->mt_mtype : 0; - domainset_t conset; - idn_msgtype_t mt; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - mt.mt_cookie = mtp ? mtp->mt_cookie : 0; - - conset = idn.domset.ds_trans_on | idn.domset.ds_connected; - conset &= ~idn.domset.ds_trans_off; - - if ((msg & IDNP_ACKNACK_MASK) == 0) { - /* - * nego - */ - mt.mt_mtype = IDNP_NEGO | IDNP_ACK; - mt.mt_atype = 0; - (void) idn_send_nego(domid, &mt, conset); - } else if (msg & IDNP_MSGTYPE_MASK) { - int d; - idn_xdcargs_t nargs; - idnneg_dset_t dset; - uint_t dmask; - idn_vote_t vote; - - mt.mt_mtype = IDNP_ACK; - mt.mt_atype = msg; - DOMAINSET_DEL(conset, idn.localid); - DOMAINSET_DEL(conset, domid); - - dmask = IDNNEG_DSET_MYMASK(); - IDNNEG_DSET_INIT(dset, dmask); - for (d = 0; d < MAX_DOMAINS; d++) { - int cpuid; - - if (!DOMAIN_IN_SET(conset, d)) - continue; - - if ((cpuid = idn_domain[d].dcpu) == IDN_NIL_DCPU) - continue; - - IDNNEG_DSET_SET(dset, d, cpuid, dmask); - } - IDNNEG_DSET_SET_MASTER(dset, domid, IDN_GET_MASTERID()); - ASSERT((IDN_GET_MASTERID() != IDN_NIL_DOMID) ? - (idn_domain[IDN_GET_MASTERID()].dcpu != IDN_NIL_DCPU) : 1); - vote.ticket = idn_domain[idn.localid].dvote.ticket; - vote.v.master = 0; - CLR_XARGS(nargs); - SET_XARGS_NEGO_TICKET(nargs, vote.ticket); - SET_XARGS_NEGO_DSET(nargs, dset); - /* - * nego+ack - */ - idn_send_acknack(domid, &mt, nargs); - } else { - uint_t token; - int new_masterid, new_cpuid; - int retry = 1; - idn_nack_t nack; - /* - * nack - retry - * - * It's possible if we've made it this far that - * we may have already chosen a master and this - * dude might be it! If it is we need to clean up. - */ - nack = GET_XARGS_NACK_TYPE(xargs); - switch (nack) { - case IDNNACK_RETRY: - new_masterid = (int)GET_XARGS_NACK_ARG1(xargs); - new_cpuid = (int)GET_XARGS_NACK_ARG2(xargs); - break; - - case IDNNACK_EXIT: - retry = 0; - /*FALLTHROUGH*/ - - default: - new_masterid = IDN_NIL_DOMID; - new_cpuid = IDN_NIL_DCPU; - break; - } - - idn_nego_cleanup_check(domid, new_masterid, new_cpuid); - - if (retry) { - token = IDN_RETRY_TOKEN(domid, IDNRETRY_NEGO); - idn_retry_submit(idn_retry_nego, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_NEGO]); - } else { - DOMAINSET_DEL(idn.domset.ds_relink, domid); - IDN_RESET_COOKIES(domid); - (void) idn_disconnect(domid, IDNFIN_NORMAL, - IDNFIN_ARG_NONE, - IDNDS_SYNC_TYPE(&idn_domain[domid])); - } - } -} - -/*ARGSUSED*/ -static void -idn_action_nego_rcvd(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - uint_t msg = mtp ? mtp->mt_mtype : 0; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - if (msg & IDNP_NACK) { - uint_t token; - int new_masterid, new_cpuid; - int retry = 1; - idn_nack_t nack; - /* - * nack - retry. - * - * At this stage of receiving a nack we need to - * check whether we need to start over again with - * selecting a new master. - */ - nack = GET_XARGS_NACK_TYPE(xargs); - switch (nack) { - case IDNNACK_RETRY: - new_masterid = (int)GET_XARGS_NACK_ARG1(xargs); - new_cpuid = (int)GET_XARGS_NACK_ARG2(xargs); - break; - - case IDNNACK_EXIT: - retry = 0; - /*FALLTHROUGH*/ - - default: - new_masterid = IDN_NIL_DOMID; - new_cpuid = IDN_NIL_DCPU; - break; - } - - idn_nego_cleanup_check(domid, new_masterid, new_cpuid); - - if (retry) { - token = IDN_RETRY_TOKEN(domid, IDNRETRY_NEGO); - idn_retry_submit(idn_retry_nego, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_NEGO]); - } else { - DOMAINSET_DEL(idn.domset.ds_relink, domid); - IDN_RESET_COOKIES(domid); - (void) idn_disconnect(domid, IDNFIN_NORMAL, - IDNFIN_ARG_NONE, - IDNDS_SYNC_TYPE(&idn_domain[domid])); - } - } -} - -static void -idn_final_nego(int domid) -{ - idn_domain_t *dp = &idn_domain[domid]; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - (void) idn_retry_terminate(IDN_RETRY_TOKEN(domid, IDNRETRY_NEGO)); - - ASSERT(dp->dstate == IDNDS_CONFIG); - - dp->dxp = NULL; - IDN_XSTATE_TRANSITION(dp, IDNXS_NIL); - - idn_send_config(domid, 1); -} - -/* - */ -/*ARGSUSED1*/ -static void -idn_exit_nego(int domid, uint_t msgtype) -{ - idn_domain_t *dp; - idn_fin_t fintype; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - dp = &idn_domain[domid]; - - fintype = msgtype ? IDNFIN_NORMAL : IDNFIN_FORCE_HARD; - - (void) idn_retry_terminate(IDN_RETRY_TOKEN(domid, IDNRETRY_NEGO)); - - ASSERT(!DOMAIN_IN_SET(idn.domset.ds_connected, domid)); - ASSERT(!DOMAIN_IN_SET(idn.domset.ds_ready_on, domid)); - ASSERT(dp->dxp == &xphase_nego); - - idn_nego_cleanup_check(domid, IDN_NIL_DOMID, IDN_NIL_DCPU); - - IDN_GLOCK_SHARED(); - if ((idn.state != IDNGS_DISCONNECT) && - !DOMAIN_IN_SET(idn.domset.ds_hitlist, domid)) { - DOMAINSET_ADD(idn.domset.ds_relink, domid); - IDN_HISTORY_LOG(IDNH_RELINK, domid, dp->dstate, - idn.domset.ds_relink); - } else { - idn_update_op(IDNOP_ERROR, DOMAINSET(domid), NULL); - DOMAINSET_DEL(idn.domset.ds_relink, domid); - } - IDN_GUNLOCK(); - /* - * Reset send cookie to 0 so that receiver does not validate - * cookie. This is necessary since at this early stage it's - * possible we may not have exchanged appropriate cookies. - */ - IDN_RESET_COOKIES(domid); - (void) idn_disconnect(domid, fintype, IDNFIN_ARG_NONE, - IDNDS_SYNC_TYPE(dp)); -} - -static void -idn_nego_cleanup_check(int domid, int new_masterid, int new_cpuid) -{ - idn_domain_t *ldp, *dp; - procname_t proc = "idn_nego_cleanup_check"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - dp = &idn_domain[domid]; - ldp = &idn_domain[idn.localid]; - - IDN_GLOCK_EXCL(); - - if (((idn.state == IDNGS_ONLINE) && !idn.domset.ds_connected) || - (idn.state == IDNGS_CONNECT)) { - domainset_t trans_on; - int masterid; - int retry_domid = IDN_NIL_DOMID; - int rv; - - IDN_DLOCK_EXCL(idn.localid); - masterid = (idn.state == IDNGS_ONLINE) ? - IDN_GET_MASTERID() : IDN_GET_NEW_MASTERID(); - trans_on = idn.domset.ds_trans_on; - DOMAINSET_DEL(trans_on, domid); - if (trans_on == 0) { - int d; - domainset_t relink = idn.domset.ds_relink; - /* - * This was the only guy we were trying - * to connect with. - */ - ASSERT((idn.state == IDNGS_ONLINE) ? - ((idn.localid == masterid) || - (domid == masterid)) : 1); - if (idn.localid == masterid) - idn_master_deinit(); - ldp->dvote.v.connected = 0; - ldp->dvote.v.master = 0; - dp->dvote.v.master = 0; - IDN_SET_MASTERID(IDN_NIL_DOMID); - IDN_SET_NEW_MASTERID(new_masterid); - IDN_GSTATE_TRANSITION(IDNGS_CONNECT); - IDN_PREP_HWINIT(); - IDN_DUNLOCK(idn.localid); - IDN_GUNLOCK(); - /* - * If there's a new master available then - * just try and relink with him unless - * it's ourself. - */ - if ((new_masterid != IDN_NIL_DOMID) && - (new_masterid != idn.localid) && - (new_masterid != domid)) { - IDN_DLOCK_EXCL(new_masterid); - rv = idn_open_domain(new_masterid, - new_cpuid, 0); - if (rv < 0) { - cmn_err(CE_WARN, - "IDN: 205: (%s) failed to " - "open-domain(%d,%d)", - proc, new_masterid, new_cpuid); - IDN_GLOCK_EXCL(); - IDN_SET_NEW_MASTERID(IDN_NIL_DOMID); - IDN_GUNLOCK(); - } else { - relink = DOMAINSET(new_masterid); - } - IDN_DUNLOCK(new_masterid); - } - DOMAINSET_DEL(relink, domid); - if (relink) - for (d = 0; d < MAX_DOMAINS; d++) { - if (!DOMAIN_IN_SET(relink, d)) - continue; - retry_domid = d; - break; - } - } else if (domid == masterid) { - /* - * There are other domains we were trying - * to connect to. As long as the chosen - * master was somebody other then this - * domain that nack'd us, life is cool, but - * if it was this remote domain we'll need - * to start over. - */ - IDN_DUNLOCK(idn.localid); - dp->dvote.v.master = 0; - IDN_SET_MASTERID(IDN_NIL_DOMID); - IDN_SET_NEW_MASTERID(new_masterid); - - if (idn.state == IDNGS_ONLINE) { - IDN_GKSTAT_GLOBAL_EVENT(gk_reconfigs, - gk_reconfig_last); - IDN_GSTATE_TRANSITION(IDNGS_RECONFIG); - IDN_GUNLOCK(); - idn_unlink_domainset(trans_on, IDNFIN_NORMAL, - IDNFIN_ARG_NONE, - IDNFIN_OPT_RELINK, - BOARDSET_ALL); - } else if ((new_masterid != IDN_NIL_DOMID) && - (new_masterid != idn.localid) && - (new_masterid != domid) && - !DOMAIN_IN_SET(trans_on, new_masterid)) { - IDN_GUNLOCK(); - IDN_DLOCK_EXCL(new_masterid); - rv = idn_open_domain(new_masterid, - new_cpuid, 0); - IDN_GLOCK_EXCL(); - IDN_DUNLOCK(new_masterid); - if (rv < 0) { - cmn_err(CE_WARN, - "IDN: 205: (%s) failed to " - "open-domain(%d,%d)", - proc, new_masterid, - new_cpuid); - IDN_SET_NEW_MASTERID(IDN_NIL_DOMID); - new_masterid = IDN_NIL_DOMID; - } else { - retry_domid = new_masterid; - } - IDN_GUNLOCK(); - } else { - IDN_GUNLOCK(); - } - } else { - IDN_DUNLOCK(idn.localid); - IDN_GUNLOCK(); - } - if (retry_domid != IDN_NIL_DOMID) { - uint_t token; - idn_domain_t *rdp = &idn_domain[retry_domid]; - - IDN_DLOCK_EXCL(retry_domid); - rdp->dxp = &xphase_nego; - IDN_XSTATE_TRANSITION(rdp, IDNXS_PEND); - IDN_DUNLOCK(retry_domid); - token = IDN_RETRY_TOKEN(retry_domid, IDNRETRY_NEGO); - idn_retry_submit(idn_retry_nego, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_NEGO]); - } - } else { - IDN_GUNLOCK(); - } -} - -static int -idn_send_con(int domid, idn_msgtype_t *mtp, idn_con_t contype, domainset_t - conset) -{ - idn_msgtype_t mt; - uint_t acknack; - procname_t proc = "idn_send_con"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - if (mtp) { - acknack = mtp->mt_mtype & IDNP_ACKNACK_MASK; - mt.mt_mtype = mtp->mt_mtype; - mt.mt_atype = mtp->mt_atype; - mt.mt_cookie = mtp->mt_cookie; - } else { - acknack = 0; - mt.mt_mtype = IDNP_CON; - mt.mt_atype = 0; - /* - * For simple CON queries we want a unique - * timer assigned. For others, they - * effectively share one. - */ - if (contype == IDNCON_QUERY) - mt.mt_cookie = 0; - else - mt.mt_cookie = IDN_TIMER_PUBLIC_COOKIE; - } - - ASSERT((contype == IDNCON_QUERY) ? idn_domain[domid].dcookie_send : 1); - - PR_PROTO("%s:%d: sending con%sto (cpu %d) [ct=%s, cs=0x%x]\n", - proc, domid, - (acknack & IDNP_ACK) ? "+ack " : - (acknack & IDNP_NACK) ? "+nack " : " ", - idn_domain[domid].dcpu, - idncon_str[contype], conset); - - IDN_MSGTIMER_START(domid, IDNP_CON, (ushort_t)contype, - idn_msg_waittime[IDNP_CON], &mt.mt_cookie); - - IDNXDC(domid, &mt, (uint_t)contype, (uint_t)conset, 0, 0); - - return (0); -} - -/* - * Must leave w/DLOCK dropped and SYNC_LOCK held. - */ -static int -idn_recv_con(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - uint_t msg = mtp ? mtp->mt_mtype : 0; - uint_t msgarg = mtp ? mtp->mt_atype : 0; - idn_con_t contype; - domainset_t my_ready_set, ready_set; - idn_msgtype_t mt; - idn_domain_t *dp = &idn_domain[domid]; - idn_xdcargs_t aargs; - procname_t proc = "idn_recv_con"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - mt.mt_cookie = mtp ? mtp->mt_cookie : 0; - - contype = GET_XARGS_CON_TYPE(xargs); - ready_set = GET_XARGS_CON_DOMSET(xargs); - - CLR_XARGS(aargs); - - if (!(msg & IDNP_NACK) && (contype == IDNCON_QUERY)) { - domainset_t query_set; - - query_set = idn_sync_register(domid, IDNSYNC_CONNECT, - ready_set, IDNSYNC_REG_REG); - - my_ready_set = idn.domset.ds_connected | idn.domset.ds_ready_on; - my_ready_set &= ~idn.domset.ds_trans_off; - DOMAINSET_ADD(my_ready_set, idn.localid); - - if (msg & IDNP_MSGTYPE_MASK) { - mt.mt_mtype = IDNP_ACK; - mt.mt_atype = IDNP_CON; - SET_XARGS_CON_TYPE(aargs, contype); - SET_XARGS_CON_DOMSET(aargs, my_ready_set); - idn_send_acknack(domid, &mt, aargs); - } - - if (query_set) { - uint_t token; - - token = IDN_RETRY_TOKEN(domid, IDNRETRY_CONQ); - idn_retry_submit(idn_retry_query, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_CONQ]); - } - - return (0); - } - - if (dp->dxp == NULL) { - STRING(mstr); - STRING(lstr); - /* - * Must have received an inappropriate error - * message as we should already be registered - * by the time we reach here. - */ - INUM2STR(msg, mstr); - INUM2STR(msgarg, lstr); - - PR_PROTO("%s:%d: ERROR: NOT YET REGISTERED (%s/%s)\n", - proc, domid, mstr, lstr); - - if (msg & IDNP_MSGTYPE_MASK) { - mt.mt_mtype = IDNP_NACK; - mt.mt_atype = msg; - SET_XARGS_NACK_TYPE(aargs, IDNNACK_RETRY); - idn_send_acknack(domid, &mt, aargs); - } - - return (-1); - } - - (void) idn_xphase_transition(domid, mtp, xargs); - - return (0); -} - -/*ARGSUSED1*/ -static void -idn_retry_con(uint_t token, void *arg) -{ - int domid = IDN_RETRY_TOKEN2DOMID(token); - idn_domain_t *dp = &idn_domain[domid]; - idn_xdcargs_t xargs; - procname_t proc = "idn_retry_con"; - - ASSERT(IDN_RETRY_TOKEN2TYPE(token) == IDNRETRY_CON); - - IDN_SYNC_LOCK(); - IDN_DLOCK_EXCL(domid); - - if (dp->dxp != &xphase_con) { - STRING(str); - -#ifdef DEBUG - if (dp->dxp) { - INUM2STR(dp->dxp->xt_msgtype, str); - } -#endif /* DEBUG */ - - PR_PROTO("%s:%d: dxp(%s) != CON...bailing...\n", - proc, domid, dp->dxp ? str : "NULL"); - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - return; - } - - if ((dp->dsync.s_cmd != IDNSYNC_CONNECT) || - (dp->dxstate != IDNXS_PEND)) { - PR_PROTO("%s:%d: cmd (%s) and/or xstate (%s) not " - "expected (%s/%s)\n", - proc, domid, idnsync_str[dp->dsync.s_cmd], - idnxs_str[dp->dxstate], idnsync_str[IDNSYNC_CONNECT], - idnxs_str[IDNXS_PEND]); - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - return; - } - - (void) idn_xphase_transition(domid, NULL, xargs); - - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); -} - -static int -idn_check_con(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - int ready; - uint_t msg = mtp ? mtp->mt_mtype : 0; - idn_domain_t *dp = &idn_domain[domid]; - domainset_t ready_set, my_ready_set, query_set; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - if (msg & IDNP_NACK) - return (0); - - if ((dp->dstate == IDNDS_CON_PEND) && - (msg & IDNP_MSGTYPE_MASK) && (msg & IDNP_ACK)) /* con+ack */ - return (1); - - if (msg == 0) { - ready_set = idn.domset.ds_connected & - ~idn.domset.ds_trans_off; - } else { - ready_set = GET_XARGS_CON_DOMSET(xargs); - DOMAINSET_ADD(idn.domset.ds_ready_on, domid); - } - - DOMAINSET_ADD(ready_set, idn.localid); - - query_set = idn_sync_register(domid, IDNSYNC_CONNECT, - ready_set, IDNSYNC_REG_REG); - /* - * No need to query this domain as he's already - * in the CON sequence. - */ - DOMAINSET_DEL(query_set, domid); - - ready = (dp->dsync.s_set_exp == dp->dsync.s_set_rdy) ? 1 : 0; - if (ready) { - DOMAINSET_DEL(idn.domset.ds_ready_on, domid); - DOMAINSET_ADD(idn.domset.ds_connected, domid); - } - - if (query_set) { - int d; - - my_ready_set = idn.domset.ds_ready_on | - idn.domset.ds_connected; - my_ready_set &= ~idn.domset.ds_trans_off; - DOMAINSET_ADD(my_ready_set, idn.localid); - - for (d = 0; d < MAX_DOMAINS; d++) { - if (!DOMAIN_IN_SET(query_set, d)) - continue; - - dp = &idn_domain[d]; - - IDN_DLOCK_EXCL(d); - if ((dp->dsync.s_cmd == IDNSYNC_CONNECT) || - !dp->dcookie_send) { - IDN_DUNLOCK(d); - continue; - } - - IDN_SYNC_QUERY_UPDATE(domid, d); - - (void) idn_send_con(d, NULL, IDNCON_QUERY, - my_ready_set); - IDN_DUNLOCK(d); - } - } - - return (!msg ? 0 : (ready ? 0 : 1)); -} - -/*ARGSUSED2*/ -static void -idn_error_con(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - uint_t token; - uint_t msg = mtp ? mtp->mt_mtype : 0; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - if (msg & IDNP_MSGTYPE_MASK) { - idn_msgtype_t mt; - idn_xdcargs_t nargs; - - mt.mt_mtype = IDNP_NACK; - mt.mt_atype = msg; - mt.mt_cookie = mtp->mt_cookie; - CLR_XARGS(nargs); - SET_XARGS_NACK_TYPE(nargs, IDNNACK_RETRY); - idn_send_acknack(domid, &mt, nargs); - } - - token = IDN_RETRY_TOKEN(domid, IDNRETRY_CON); - idn_retry_submit(idn_retry_con, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_CON]); -} - -/*ARGSUSED*/ -static void -idn_action_con_pend(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - uint_t msg = mtp ? mtp->mt_mtype : 0; - idn_domain_t *dp = &idn_domain[domid]; - idn_msgtype_t mt; - domainset_t my_ready_set; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - my_ready_set = dp->dsync.s_set_rdy | idn.domset.ds_ready_on | - idn.domset.ds_connected; - my_ready_set &= ~idn.domset.ds_trans_off; - DOMAINSET_ADD(my_ready_set, idn.localid); - - if (!msg) { - (void) idn_send_con(domid, NULL, IDNCON_NORMAL, my_ready_set); - } else { - mt.mt_mtype = IDNP_CON | IDNP_ACK; - mt.mt_atype = 0; - mt.mt_cookie = mtp->mt_cookie; - (void) idn_send_con(domid, &mt, IDNCON_NORMAL, my_ready_set); - } -} - -static void -idn_action_con_sent(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - uint_t msg = mtp ? mtp->mt_mtype : 0; - idn_domain_t *dp = &idn_domain[domid]; - idn_con_t contype; - domainset_t my_ready_set; - idn_msgtype_t mt; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - mt.mt_cookie = mtp ? mtp->mt_cookie : 0; - - my_ready_set = dp->dsync.s_set_rdy | idn.domset.ds_ready_on | - idn.domset.ds_connected; - my_ready_set &= ~idn.domset.ds_trans_off; - DOMAINSET_ADD(my_ready_set, idn.localid); - - contype = GET_XARGS_CON_TYPE(xargs); - - if ((msg & IDNP_ACKNACK_MASK) == 0) { - /* - * con - */ - mt.mt_mtype = IDNP_CON | IDNP_ACK; - mt.mt_atype = 0; - (void) idn_send_con(domid, &mt, contype, my_ready_set); - } else if (msg & IDNP_MSGTYPE_MASK) { - idn_xdcargs_t cargs; - - mt.mt_mtype = IDNP_ACK; - mt.mt_atype = msg; - CLR_XARGS(cargs); - SET_XARGS_CON_TYPE(cargs, contype); - SET_XARGS_CON_DOMSET(cargs, my_ready_set); - /* - * con+ack - */ - idn_send_acknack(domid, &mt, cargs); - } else { - uint_t token; - /* - * nack - retry - */ - token = IDN_RETRY_TOKEN(domid, IDNRETRY_CON); - idn_retry_submit(idn_retry_con, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_CON]); - } -} - -/*ARGSUSED*/ -static void -idn_action_con_rcvd(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - uint_t msg = mtp ? mtp->mt_mtype : 0; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - if (msg & IDNP_NACK) { - uint_t token; - /* - * nack - retry - */ - token = IDN_RETRY_TOKEN(domid, IDNRETRY_CON); - idn_retry_submit(idn_retry_con, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_CON]); - } -} - -static void -idn_final_con(int domid) -{ - uint_t targ; - uint_t token = IDN_RETRY_TOKEN(domid, IDNRETRY_CON); - idn_domain_t *dp = &idn_domain[domid]; - procname_t proc = "idn_final_con"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - (void) idn_retry_terminate(token); - - dp->dxp = NULL; - IDN_XSTATE_TRANSITION(dp, IDNXS_NIL); - - idn_sync_exit(domid, IDNSYNC_CONNECT); - - CHECKPOINT_OPENED(IDNSB_CHKPT_LINK, dp->dhw.dh_boardset, 1); - - DOMAINSET_DEL(idn.domset.ds_trans_on, domid); - DOMAINSET_DEL(idn.domset.ds_relink, domid); - IDN_FSTATE_TRANSITION(dp, IDNFIN_OFF); - - PR_PROTO("%s:%d: CONNECTED\n", proc, domid); - - if (idn.domset.ds_trans_on == 0) { - if ((idn.domset.ds_trans_off | idn.domset.ds_relink) == 0) { - PR_HITLIST("%s:%d: HITLIST %x -> 0\n", - proc, domid, idn.domset.ds_hitlist); - idn.domset.ds_hitlist = 0; - } - PR_PROTO("%s:%d: ALL CONNECTED ************ " - "(0x%x + 0x%x) = 0x%x\n", proc, domid, - DOMAINSET(idn.localid), idn.domset.ds_connected, - DOMAINSET(idn.localid) | idn.domset.ds_connected); - } else { - PR_PROTO("%s:%d: >>> ds_trans_on = 0x%x, ds_ready_on = 0x%x\n", - proc, domid, - idn.domset.ds_trans_on, idn.domset.ds_ready_on); - } - - if (idn_verify_config_mbox(domid)) { - idnsb_error_t idnerr; - /* - * Mailbox is not cool. Need to disconnect. - */ - INIT_IDNKERR(&idnerr); - SET_IDNKERR_ERRNO(&idnerr, EPROTO); - SET_IDNKERR_IDNERR(&idnerr, IDNKERR_SMR_CORRUPTED); - SET_IDNKERR_PARAM0(&idnerr, domid); - idn_update_op(IDNOP_ERROR, DOMAINSET(domid), &idnerr); - /* - * We cannot disconnect from an individual domain - * unless all domains are attempting to disconnect - * from him also, especially now since we touched - * the SMR and now we have a potential cache conflicts - * with the other domains with respect to this - * domain. Disconnect attempt will effectively - * shutdown connection with respective domain - * which is the effect we really want anyway. - */ - (void) idn_disconnect(domid, IDNFIN_NORMAL, IDNFIN_ARG_SMRBAD, - IDNFIN_SYNC_YES); - - return; - } - - if (lock_try(&idn.first_swlink)) { - /* - * This is our first connection. Need to - * kick some stuff into gear. - */ - idndl_dlpi_init(); - (void) idn_activate_channel(CHANSET_ALL, IDNCHAN_ONLINE); - - targ = 0xf0; - } else { - targ = 0; - } - - idn_mainmbox_activate(domid); - - idn_update_op(IDNOP_CONNECTED, DOMAINSET(domid), NULL); - - IDN_GKSTAT_GLOBAL_EVENT(gk_links, gk_link_last); - - membar_stst_ldst(); - - IDN_DSTATE_TRANSITION(dp, IDNDS_CONNECTED); - /* - * Need to kick off initial commands in background. - * We do not want to do them within the context of - * a protocol server because they may sleep and thus - * cause the protocol server to incur a soft-deadlock, - * i.e. he's sleeping waiting in the slab-waiting area - * for a response that will arrive on his protojob - * queue, but which he obviously can't process since - * he's not waiting on his protojob queue. - */ - targ |= domid & 0x0f; - (void) timeout(idn_link_established, (void *)(uintptr_t)targ, 50); - - cmn_err(CE_NOTE, - "!IDN: 200: link (domain %d, CPU %d) connected", - dp->domid, dp->dcpu); -} - -static void -idn_exit_con(int domid, uint_t msgtype) -{ - idn_domain_t *dp = &idn_domain[domid]; - idn_fin_t fintype; - procname_t proc = "idn_exit_con"; - STRING(str); - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - INUM2STR(msgtype, str); - PR_PROTO("%s:%d: msgtype = 0x%x(%s)\n", proc, domid, msgtype, str); - - fintype = msgtype ? IDNFIN_NORMAL : IDNFIN_FORCE_HARD; - - IDN_GLOCK_SHARED(); - if (idn.state != IDNGS_DISCONNECT) { - DOMAINSET_ADD(idn.domset.ds_relink, domid); - IDN_HISTORY_LOG(IDNH_RELINK, domid, dp->dstate, - idn.domset.ds_relink); - } else { - DOMAINSET_DEL(idn.domset.ds_relink, domid); - } - IDN_GUNLOCK(); - - (void) idn_disconnect(domid, fintype, IDNFIN_ARG_NONE, - IDNDS_SYNC_TYPE(dp)); -} - -static int -idn_send_fin(int domid, idn_msgtype_t *mtp, idn_fin_t fintype, idn_finarg_t - finarg, idn_finopt_t finopt, domainset_t finset, uint_t finmaster) -{ - int need_timer = 1; - uint_t acknack; - uint_t fintypearg = 0; - idn_msgtype_t mt; - idn_domain_t *dp = &idn_domain[domid]; - procname_t proc = "idn_send_fin"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_HELD(domid)); - - ASSERT((fintype != IDNFIN_QUERY) ? (finopt != IDNFIN_OPT_NONE) : 1); - - if (mtp) { - acknack = mtp->mt_mtype & IDNP_ACKNACK_MASK; - mt.mt_mtype = mtp->mt_mtype; - mt.mt_atype = mtp->mt_atype; - mt.mt_cookie = mtp->mt_cookie; - } else { - acknack = 0; - mt.mt_mtype = IDNP_FIN; - mt.mt_atype = 0; - /* - * For simple FIN queries we want a unique - * timer assigned. For others, they - * effectively share one. - */ - if (fintype == IDNFIN_QUERY) - mt.mt_cookie = 0; - else - mt.mt_cookie = IDN_TIMER_PUBLIC_COOKIE; - } - - PR_PROTO("%s:%d: sending fin%sto (cpu %d) " - "[ft=%s, fa=%s, fs=0x%x, fo=%s, fm=(%d,%d)]\n", - proc, domid, - (acknack & IDNP_ACK) ? "+ack " : - (acknack & IDNP_NACK) ? "+nack " : " ", - dp->dcpu, idnfin_str[fintype], idnfinarg_str[finarg], - (int)finset, idnfinopt_str[finopt], - FIN_MASTER_DOMID(finmaster), FIN_MASTER_CPUID(finmaster)); - - if (need_timer) { - IDN_MSGTIMER_START(domid, IDNP_FIN, (ushort_t)fintype, - idn_msg_waittime[IDNP_FIN], &mt.mt_cookie); - } - - SET_FIN_TYPE(fintypearg, fintype); - SET_FIN_ARG(fintypearg, finarg); - - IDNXDC(domid, &mt, fintypearg, (uint_t)finset, (uint_t)finopt, - finmaster); - - return (0); -} - -/* - * Must leave w/DLOCK dropped and SYNC_LOCK held. - */ -static int -idn_recv_fin(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - uint_t msg = mtp ? mtp->mt_mtype : 0; - idn_fin_t fintype; - idn_finarg_t finarg; - idn_finopt_t finopt; - domainset_t my_ready_set, ready_set; - idn_msgtype_t mt; - idn_domain_t *dp = &idn_domain[domid]; - idn_xdcargs_t aargs; - procname_t proc = "idn_recv_fin"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - mt.mt_cookie = mtp ? mtp->mt_cookie : 0; - - fintype = GET_XARGS_FIN_TYPE(xargs); - finarg = GET_XARGS_FIN_ARG(xargs); - ready_set = GET_XARGS_FIN_DOMSET(xargs); - finopt = GET_XARGS_FIN_OPT(xargs); - - CLR_XARGS(aargs); - - if (msg & IDNP_NACK) { - PR_PROTO("%s:%d: received NACK (type = %s)\n", - proc, domid, idnnack_str[xargs[0]]); - } else { - PR_PROTO("%s:%d: fintype = %s, finopt = %s, " - "finarg = %s, ready_set = 0x%x\n", - proc, domid, idnfin_str[fintype], - idnfinopt_str[finopt], - idnfinarg_str[finarg], ready_set); - } - - if (!(msg & IDNP_NACK) && (fintype == IDNFIN_QUERY)) { - domainset_t query_set; - - query_set = idn_sync_register(domid, IDNSYNC_DISCONNECT, - ready_set, IDNSYNC_REG_REG); - - my_ready_set = ~idn.domset.ds_connected | - idn.domset.ds_ready_off; - - if (msg & IDNP_MSGTYPE_MASK) { - mt.mt_mtype = IDNP_ACK; - mt.mt_atype = IDNP_FIN; - SET_XARGS_FIN_TYPE(aargs, fintype); - SET_XARGS_FIN_ARG(aargs, finarg); - SET_XARGS_FIN_DOMSET(aargs, my_ready_set); - SET_XARGS_FIN_OPT(aargs, IDNFIN_OPT_NONE); - SET_XARGS_FIN_MASTER(aargs, NIL_FIN_MASTER); - idn_send_acknack(domid, &mt, aargs); - } - - if (query_set) { - uint_t token; - - token = IDN_RETRY_TOKEN(domid, IDNRETRY_FINQ); - idn_retry_submit(idn_retry_query, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_FINQ]); - } - - return (0); - } - - if (dp->dxp != &xphase_fin) { - uint_t token; - - if (IDNDS_IS_CLOSED(dp)) { - PR_PROTO("%s:%d: domain already closed (%s)\n", - proc, domid, idnds_str[dp->dstate]); - if (msg & IDNP_MSGTYPE_MASK) { - /* - * fin or fin+ack. - */ - mt.mt_mtype = IDNP_NACK; - mt.mt_atype = msg; - SET_XARGS_NACK_TYPE(aargs, IDNNACK_NOCONN); - idn_send_acknack(domid, &mt, aargs); - } - return (0); - } - dp->dfin_sync = IDNDS_SYNC_TYPE(dp); - - /* - * Need to do some clean-up ala idn_disconnect(). - * - * Terminate any outstanding commands that were - * targeted towards this domain. - */ - idn_terminate_cmd(domid, ECANCELED); - - /* - * Terminate any and all retries that may have - * outstanding for this domain. - */ - token = IDN_RETRY_TOKEN(domid, IDN_RETRY_TYPEALL); - (void) idn_retry_terminate(token); - - /* - * Stop all outstanding message timers for - * this guy. - */ - IDN_MSGTIMER_STOP(domid, 0, 0); - - dp->dxp = &xphase_fin; - IDN_XSTATE_TRANSITION(dp, IDNXS_PEND); - } - - if (msg & IDNP_NACK) { - idn_nack_t nack; - - nack = GET_XARGS_NACK_TYPE(xargs); - if (nack == IDNNACK_NOCONN) { - /* - * We're trying to FIN with somebody we're - * already disconnected from. Need to - * speed this guy through. - */ - DOMAINSET_ADD(idn.domset.ds_ready_off, domid); - (void) idn_sync_register(domid, IDNSYNC_DISCONNECT, - DOMAINSET_ALL, IDNSYNC_REG_REG); - ready_set = (uint_t)DOMAINSET_ALL; - /* - * Need to transform message to allow us to - * pass this guy right through and not waste time - * talking to him. - */ - IDN_FSTATE_TRANSITION(dp, IDNFIN_FORCE_HARD); - - switch (dp->dstate) { - case IDNDS_FIN_PEND: - mtp->mt_mtype = 0; - mtp->mt_atype = 0; - break; - - case IDNDS_FIN_SENT: - mtp->mt_mtype = IDNP_FIN | IDNP_ACK; - mtp->mt_atype = 0; - break; - - case IDNDS_FIN_RCVD: - mtp->mt_mtype = IDNP_ACK; - mtp->mt_atype = IDNP_FIN | IDNP_ACK; - break; - - default: -#ifdef DEBUG - cmn_err(CE_PANIC, - "%s:%d: UNEXPECTED state = %s", - proc, domid, - idnds_str[dp->dstate]); -#endif /* DEBUG */ - break; - } - } - fintype = (uint_t)dp->dfin; - finopt = DOMAIN_IN_SET(idn.domset.ds_relink, domid) ? - IDNFIN_OPT_RELINK : IDNFIN_OPT_UNLINK; - - CLR_XARGS(xargs); - SET_XARGS_FIN_TYPE(xargs, fintype); - SET_XARGS_FIN_ARG(xargs, finarg); - SET_XARGS_FIN_DOMSET(xargs, ready_set); - SET_XARGS_FIN_OPT(xargs, finopt); - SET_XARGS_FIN_MASTER(xargs, NIL_FIN_MASTER); - } - - (void) idn_xphase_transition(domid, mtp, xargs); - - return (0); -} - -/*ARGSUSED1*/ -static void -idn_retry_fin(uint_t token, void *arg) -{ - int domid = IDN_RETRY_TOKEN2DOMID(token); - int new_masterid, new_cpuid = IDN_NIL_DCPU; - uint_t finmaster; - idn_domain_t *dp = &idn_domain[domid]; - idn_xdcargs_t xargs; - idn_finopt_t finopt; - procname_t proc = "idn_retry_fin"; - - ASSERT(IDN_RETRY_TOKEN2TYPE(token) == IDNRETRY_FIN); - - IDN_SYNC_LOCK(); - IDN_DLOCK_EXCL(domid); - - if (dp->dxp != &xphase_fin) { - PR_PROTO("%s:%d: dxp(0x%p) != xstate_fin(0x%p)...bailing\n", - proc, domid, (void *)dp->dxp, (void *)&xphase_fin); - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - return; - } - - if (dp->dxstate != IDNXS_PEND) { - PR_PROTO("%s:%d: xstate(%s) != %s...bailing\n", - proc, domid, idnxs_str[dp->dxstate], - idnxs_str[IDNXS_PEND]); - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - return; - } - - finopt = DOMAIN_IN_SET(idn.domset.ds_relink, domid) ? - IDNFIN_OPT_RELINK : IDNFIN_OPT_UNLINK; - - CLR_XARGS(xargs); - SET_XARGS_FIN_TYPE(xargs, dp->dfin); - /*LINTED*/ - SET_XARGS_FIN_ARG(xargs, IDNFIN_ARG_NONE); - SET_XARGS_FIN_OPT(xargs, finopt); - SET_XARGS_FIN_DOMSET(xargs, 0); /* unused when msg == 0 */ - IDN_GLOCK_SHARED(); - new_masterid = IDN_GET_NEW_MASTERID(); - IDN_GUNLOCK(); - if (new_masterid != IDN_NIL_DOMID) - new_cpuid = idn_domain[new_masterid].dcpu; - finmaster = MAKE_FIN_MASTER(new_masterid, new_cpuid); - SET_XARGS_FIN_MASTER(xargs, finmaster); - - (void) idn_xphase_transition(domid, NULL, xargs); - - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); -} - -static int -idn_check_fin_pend(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - idn_domain_t *dp = &idn_domain[domid]; - idn_fin_t fintype; - idn_finopt_t finopt; - idn_finarg_t finarg; - int ready; - int finmasterid; - int fincpuid; - uint_t finmaster; - uint_t msg = mtp ? mtp->mt_mtype : 0; - domainset_t query_set, ready_set, conn_set; - domainset_t my_ready_set, shutdown_set; - procname_t proc = "idn_check_fin_pend"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - if (msg & IDNP_NACK) - return (0); - - if ((dp->dstate == IDNDS_FIN_PEND) && (msg & IDNP_MSGTYPE_MASK) && - (msg & IDNP_ACK)) /* fin+ack */ - return (1); - - query_set = 0; - - if (!DOMAIN_IN_SET(idn.domset.ds_trans_off, domid)) { - /* - * Can't remove domain from ds_connected yet, - * since he's still officially connected until - * we get an ACK from him. - */ - DOMAINSET_DEL(idn.domset.ds_trans_on, domid); - DOMAINSET_ADD(idn.domset.ds_trans_off, domid); - } - - IDN_GLOCK_SHARED(); - conn_set = (idn.domset.ds_connected | idn.domset.ds_trans_on) & - ~idn.domset.ds_trans_off; - if ((idn.state == IDNGS_DISCONNECT) || - (idn.state == IDNGS_RECONFIG) || - (domid == IDN_GET_MASTERID()) || !conn_set) { - /* - * If we're disconnecting, reconfiguring, - * unlinking from the master, or unlinking - * the last of our connections, then we need - * to shutdown all the channels. - */ - shutdown_set = DOMAINSET_ALL; - } else { - shutdown_set = DOMAINSET(domid); - } - IDN_GUNLOCK(); - - idn_shutdown_datapath(shutdown_set, (dp->dfin == IDNFIN_FORCE_HARD)); - - IDN_GLOCK_EXCL(); - /* - * Remap the SMR back to our local space if the remote - * domain going down is the master. We do this now before - * flushing caches. This will help guarantee that any - * accidental accesses to the SMR after the cache flush - * will only go to local memory. - */ - if ((domid == IDN_GET_MASTERID()) && (idn.smr.rempfn != PFN_INVALID)) { - PR_PROTO("%s:%d: deconfiging CURRENT MASTER - SMR remap\n", - proc, domid); - IDN_DLOCK_EXCL(idn.localid); - /* - * We're going to remap the SMR, - * so gotta blow away our local - * pointer to the mbox table. - */ - idn_domain[idn.localid].dmbox.m_tbl = NULL; - IDN_DUNLOCK(idn.localid); - - idn.smr.rempfn = PFN_INVALID; - idn.smr.rempfnlim = PFN_INVALID; - - smr_remap(&kas, idn.smr.vaddr, idn.smr.locpfn, IDN_SMR_SIZE); - } - IDN_GUNLOCK(); - - if (DOMAIN_IN_SET(idn.domset.ds_flush, domid)) { - idnxf_flushall_ecache(); - CHECKPOINT_CLOSED(IDNSB_CHKPT_CACHE, dp->dhw.dh_boardset, 2); - DOMAINSET_DEL(idn.domset.ds_flush, domid); - } - - fintype = GET_XARGS_FIN_TYPE(xargs); - finarg = GET_XARGS_FIN_ARG(xargs); - ready_set = GET_XARGS_FIN_DOMSET(xargs); - finopt = GET_XARGS_FIN_OPT(xargs); - - ASSERT(fintype != IDNFIN_QUERY); - if (!VALID_FIN(fintype)) { - /* - * If for some reason remote domain - * sent us an invalid FIN type, - * override it to a NORMAL fin. - */ - PR_PROTO("%s:%d: WARNING invalid fintype (%d) -> %s(%d)\n", - proc, domid, (int)fintype, - idnfin_str[IDNFIN_NORMAL], (int)IDNFIN_NORMAL); - fintype = IDNFIN_NORMAL; - } - - if (!VALID_FINOPT(finopt)) { - PR_PROTO("%s:%d: WARNING invalid finopt (%d) -> %s(%d)\n", - proc, domid, (int)finopt, - idnfinopt_str[IDNFIN_OPT_UNLINK], - (int)IDNFIN_OPT_UNLINK); - finopt = IDNFIN_OPT_UNLINK; - } - - finmaster = GET_XARGS_FIN_MASTER(xargs); - finmasterid = FIN_MASTER_DOMID(finmaster); - fincpuid = FIN_MASTER_CPUID(finmaster); - - if ((finarg != IDNFIN_ARG_NONE) && - !DOMAIN_IN_SET(idn.domset.ds_hitlist, domid)) { - idnsb_error_t idnerr; - - INIT_IDNKERR(&idnerr); - SET_IDNKERR_ERRNO(&idnerr, EPROTO); - SET_IDNKERR_IDNERR(&idnerr, FINARG2IDNKERR(finarg)); - SET_IDNKERR_PARAM0(&idnerr, domid); - - if (IDNFIN_ARG_IS_FATAL(finarg)) { - finopt = IDNFIN_OPT_UNLINK; - DOMAINSET_DEL(idn.domset.ds_relink, domid); - DOMAINSET_ADD(idn.domset.ds_hitlist, domid); - - if (idn.domset.ds_connected == 0) { - domainset_t domset; - - IDN_GLOCK_EXCL(); - domset = ~idn.domset.ds_relink; - if (idn.domset.ds_relink == 0) { - IDN_GSTATE_TRANSITION(IDNGS_DISCONNECT); - } - domset &= ~idn.domset.ds_hitlist; - /* - * The primary domain we were trying to - * connect to fin'd us with a fatal argument. - * Something isn't cool in our IDN environment, - * e.g. corrupted SMR or non-compatible CONFIG - * parameters. In any case we need to dismantle - * ourselves completely. - */ - IDN_SET_NEW_MASTERID(IDN_NIL_DOMID); - IDN_GUNLOCK(); - IDN_DUNLOCK(domid); - - DOMAINSET_DEL(domset, idn.localid); - DOMAINSET_DEL(domset, domid); - - idn_update_op(IDNOP_ERROR, DOMAINSET_ALL, - &idnerr); - - PR_HITLIST("%s:%d: unlink_domainset(%x) " - "due to CFG error (relink=%x, " - "hitlist=%x)\n", proc, domid, domset, - idn.domset.ds_relink, - idn.domset.ds_hitlist); - - idn_unlink_domainset(domset, IDNFIN_NORMAL, - finarg, IDNFIN_OPT_UNLINK, BOARDSET_ALL); - IDN_DLOCK_EXCL(domid); - } - PR_HITLIST("%s:%d: CFG error, (conn=%x, relink=%x, " - "hitlist=%x)\n", - proc, domid, idn.domset.ds_connected, - idn.domset.ds_relink, idn.domset.ds_hitlist); - } - idn_update_op(IDNOP_ERROR, DOMAINSET(domid), &idnerr); - } - - if ((finmasterid != IDN_NIL_DOMID) && (!VALID_DOMAINID(finmasterid) || - DOMAIN_IN_SET(idn.domset.ds_hitlist, domid))) { - PR_HITLIST("%s:%d: finmasterid = %d -> -1, relink=%x, " - "hitlist=%x\n", - proc, domid, finmasterid, idn.domset.ds_relink, - idn.domset.ds_hitlist); - PR_PROTO("%s:%d: WARNING invalid finmasterid (%d) -> -1\n", - proc, domid, finmasterid); - finmasterid = IDN_NIL_DOMID; - } - - IDN_GLOCK_EXCL(); - - if ((finopt == IDNFIN_OPT_RELINK) && (idn.state != IDNGS_DISCONNECT)) { - DOMAINSET_ADD(idn.domset.ds_relink, domid); - IDN_HISTORY_LOG(IDNH_RELINK, domid, dp->dstate, - idn.domset.ds_relink); - } else { - DOMAINSET_DEL(idn.domset.ds_relink, domid); - DOMAINSET_ADD(idn.domset.ds_hitlist, domid); - } - - if ((domid == IDN_GET_NEW_MASTERID()) && - !DOMAIN_IN_SET(idn.domset.ds_relink, domid)) { - IDN_SET_NEW_MASTERID(IDN_NIL_DOMID); - } - - if ((idn.state != IDNGS_DISCONNECT) && (idn.state != IDNGS_RECONFIG) && - (domid == IDN_GET_MASTERID())) { - domainset_t dis_set, master_candidates; - - IDN_GKSTAT_GLOBAL_EVENT(gk_reconfigs, gk_reconfig_last); - - IDN_GSTATE_TRANSITION(IDNGS_RECONFIG); - IDN_GUNLOCK(); - - if ((finmasterid != IDN_NIL_DOMID) && - (finmasterid != idn.localid)) { - if (finmasterid != domid) - IDN_DLOCK_EXCL(finmasterid); - if (idn_open_domain(finmasterid, fincpuid, 0) < 0) { - cmn_err(CE_WARN, - "IDN: 205: (%s) failed to " - "open-domain(%d,%d)", - proc, finmasterid, fincpuid); - if (finmasterid != domid) - IDN_DUNLOCK(finmasterid); - finmasterid = IDN_NIL_DOMID; - } - if (finmasterid != domid) - IDN_DUNLOCK(finmasterid); - } - - IDN_GLOCK_EXCL(); - if (finmasterid == IDN_NIL_DOMID) { - int m; - - master_candidates = idn.domset.ds_trans_on | - idn.domset.ds_connected | - idn.domset.ds_relink; - master_candidates &= ~(idn.domset.ds_trans_off & - ~idn.domset.ds_relink); - DOMAINSET_DEL(master_candidates, domid); - /* - * Local domain gets to participate also. - */ - DOMAINSET_ADD(master_candidates, idn.localid); - - m = idn_select_candidate(master_candidates); - IDN_SET_NEW_MASTERID(m); - } else { - IDN_SET_NEW_MASTERID(finmasterid); - } - IDN_GUNLOCK(); - - dis_set = idn.domset.ds_trans_on | idn.domset.ds_connected; - DOMAINSET_DEL(dis_set, domid); - - idn_unlink_domainset(dis_set, IDNFIN_NORMAL, IDNFIN_ARG_NONE, - IDNFIN_OPT_RELINK, BOARDSET_ALL); - } else { - IDN_GUNLOCK(); - } - - /* - * My local ready-set are those domains from which I - * have confirmed no datapaths exist. - */ - my_ready_set = ~idn.domset.ds_connected; - - switch (dp->dfin) { - case IDNFIN_NORMAL: - case IDNFIN_FORCE_SOFT: - case IDNFIN_FORCE_HARD: - if (fintype < dp->dfin) { - /* - * Remote domain has requested a - * FIN of lower priority than what - * we're currently running. Just - * leave the priority where it is. - */ - break; - } - /*FALLTHROUGH*/ - - default: - IDN_FSTATE_TRANSITION(dp, fintype); - break; - } - - ASSERT(dp->dfin_sync != IDNFIN_SYNC_OFF); - - if (msg == 0) { - /* - * Local domain is initiating a FIN sequence - * to remote domid. Note that remote domain - * remains in ds_connected even though he's - * in thet ready-set from the local domain's - * perspective. We can't remove him from - * ds_connected until we get a confirmed message - * from him indicating he has ceased communication. - */ - ready_set = my_ready_set; - } else { - /* - * Remote domain initiated a FIN sequence - * to local domain. This implies that he - * has shutdown his datapath to us. Since - * we shutdown our datapath to him, we're - * effectively now in his ready-set. - */ - DOMAINSET_ADD(ready_set, idn.localid); - /* - * Since we know both sides of the connection - * have ceased, this remote domain is effectively - * considered disconnected. - */ - DOMAINSET_ADD(idn.domset.ds_ready_off, domid); - } - - if (dp->dfin == IDNFIN_FORCE_HARD) { - /* - * If we're doing a hard disconnect - * of this domain then we want to - * blow straight through and not - * waste time trying to talk to the - * remote domain nor to domains we - * believe are AWOL. Although we will - * try and do it cleanly with - * everybody else. - */ - DOMAINSET_ADD(my_ready_set, domid); - my_ready_set |= idn.domset.ds_awol; - ready_set = DOMAINSET_ALL; - - } else if (dp->dfin_sync == IDNFIN_SYNC_NO) { - /* - * If we're not fin'ing this domain - * synchronously then the only - * expected domain set is himself. - */ - ready_set |= ~DOMAINSET(domid); - my_ready_set |= ~DOMAINSET(domid); - } - - if (dp->dsync.s_cmd != IDNSYNC_DISCONNECT) { - idn_sync_exit(domid, IDNSYNC_CONNECT); - idn_sync_enter(domid, IDNSYNC_DISCONNECT, DOMAINSET_ALL, - my_ready_set, idn_xstate_transfunc, (void *)IDNP_FIN); - } - - query_set = idn_sync_register(domid, IDNSYNC_DISCONNECT, ready_set, - IDNSYNC_REG_REG); - - /* - * No need to query this domain as he's already - * in the FIN sequence. - */ - DOMAINSET_DEL(query_set, domid); - - ready = (dp->dsync.s_set_exp == dp->dsync.s_set_rdy) ? 1 : 0; - if (ready) { - DOMAINSET_DEL(idn.domset.ds_ready_off, domid); - DOMAINSET_DEL(idn.domset.ds_connected, domid); - } - - if (query_set) { - int d; - - my_ready_set = idn.domset.ds_ready_off | - ~idn.domset.ds_connected; - - for (d = 0; d < MAX_DOMAINS; d++) { - if (!DOMAIN_IN_SET(query_set, d)) - continue; - - dp = &idn_domain[d]; - - IDN_DLOCK_EXCL(d); - - if (dp->dsync.s_cmd == IDNSYNC_DISCONNECT) { - IDN_DUNLOCK(d); - continue; - } - - IDN_SYNC_QUERY_UPDATE(domid, d); - - (void) idn_send_fin(d, NULL, IDNFIN_QUERY, - IDNFIN_ARG_NONE, IDNFIN_OPT_NONE, my_ready_set, - NIL_FIN_MASTER); - IDN_DUNLOCK(d); - } - } - - return (!msg ? 0 : (ready ? 0 : 1)); -} - -/*ARGSUSED*/ -static void -idn_error_fin_pend(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - uint_t msg = mtp ? mtp->mt_mtype : 0; - uint_t token; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_HELD(domid)); - - /* - * Don't communicate with domains that - * we're forcing a hard disconnect. - */ - if ((idn_domain[domid].dfin != IDNFIN_FORCE_HARD) && - (msg & IDNP_MSGTYPE_MASK)) { - idn_msgtype_t mt; - idn_xdcargs_t nargs; - - mt.mt_mtype = IDNP_NACK; - mt.mt_atype = msg; - mt.mt_cookie = mtp->mt_cookie; - CLR_XARGS(nargs); - SET_XARGS_NACK_TYPE(nargs, IDNNACK_RETRY); - idn_send_acknack(domid, &mt, nargs); - } - - token = IDN_RETRY_TOKEN(domid, IDNRETRY_FIN); - idn_retry_submit(idn_retry_fin, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_FIN]); -} - -static void -idn_action_fin_pend(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - idn_domain_t *dp = &idn_domain[domid]; - domainset_t my_ready_set; - idn_finopt_t finopt; - idn_finarg_t finarg; - uint_t finmaster; - int new_masterid, new_cpuid = IDN_NIL_DCPU; - uint_t msg = mtp ? mtp->mt_mtype : 0; - idn_msgtype_t mt; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_HELD(domid)); - - my_ready_set = dp->dsync.s_set_rdy | idn.domset.ds_ready_off | - ~idn.domset.ds_connected; - - ASSERT(xargs[0] != (uint_t)IDNFIN_QUERY); - - finarg = GET_XARGS_FIN_ARG(xargs); - finopt = DOMAIN_IN_SET(idn.domset.ds_relink, domid) ? - IDNFIN_OPT_RELINK : IDNFIN_OPT_UNLINK; - - mt.mt_cookie = mtp ? mtp->mt_cookie : 0; - - IDN_GLOCK_SHARED(); - new_masterid = IDN_GET_NEW_MASTERID(); - IDN_GUNLOCK(); - if (new_masterid != IDN_NIL_DOMID) - new_cpuid = idn_domain[new_masterid].dcpu; - finmaster = MAKE_FIN_MASTER(new_masterid, new_cpuid); - - if (dp->dfin == IDNFIN_FORCE_HARD) { - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - if (!msg) { - mt.mt_mtype = IDNP_FIN | IDNP_ACK; - mt.mt_atype = 0; - } else { - mt.mt_mtype = IDNP_ACK; - mt.mt_atype = IDNP_FIN | IDNP_ACK; - } - (void) idn_xphase_transition(domid, &mt, xargs); - } else if (!msg) { - (void) idn_send_fin(domid, NULL, dp->dfin, finarg, - finopt, my_ready_set, finmaster); - } else if ((msg & IDNP_ACKNACK_MASK) == 0) { - /* - * fin - */ - mt.mt_mtype = IDNP_FIN | IDNP_ACK; - mt.mt_atype = 0; - (void) idn_send_fin(domid, &mt, dp->dfin, finarg, - finopt, my_ready_set, finmaster); - } else { - uint_t token; - /* - * nack - retry - */ - token = IDN_RETRY_TOKEN(domid, IDNRETRY_FIN); - idn_retry_submit(idn_retry_fin, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_FIN]); - } -} - -static int -idn_check_fin_sent(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - int ready; - uint_t msg = mtp ? mtp->mt_mtype : 0; - idn_fin_t fintype; - idn_finopt_t finopt; - idn_domain_t *dp = &idn_domain[domid]; - domainset_t query_set, ready_set; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - if (msg & IDNP_NACK) - return (0); - - fintype = GET_XARGS_FIN_TYPE(xargs); - ready_set = GET_XARGS_FIN_DOMSET(xargs); - finopt = GET_XARGS_FIN_OPT(xargs); - - ASSERT(fintype != IDNFIN_QUERY); - if (!VALID_FIN(fintype)) { - /* - * If for some reason remote domain - * sent us an invalid FIN type, - * override it to a NORMAL fin. - */ - fintype = IDNFIN_NORMAL; - } - - if (!VALID_FINOPT(finopt)) { - finopt = IDNFIN_OPT_UNLINK; - } - IDN_GLOCK_SHARED(); - if ((finopt == IDNFIN_OPT_RELINK) && (idn.state != IDNGS_DISCONNECT)) { - DOMAINSET_ADD(idn.domset.ds_relink, domid); - IDN_HISTORY_LOG(IDNH_RELINK, domid, dp->dstate, - idn.domset.ds_relink); - } else { - DOMAINSET_DEL(idn.domset.ds_relink, domid); - } - IDN_GUNLOCK(); - - switch (dp->dfin) { - case IDNFIN_NORMAL: - case IDNFIN_FORCE_SOFT: - case IDNFIN_FORCE_HARD: - if (fintype < dp->dfin) { - /* - * Remote domain has requested a - * FIN of lower priority than what - * we're current running. Just - * leave the priority where it is. - */ - break; - } - /*FALLTHROUGH*/ - - default: - IDN_FSTATE_TRANSITION(dp, fintype); - break; - } - - if (dp->dfin == IDNFIN_FORCE_HARD) { - /* - * If we're doing a hard disconnect - * of this domain then we want to - * blow straight through and not - * waste time trying to talk to the - * remote domain. By registering him - * as ready with respect to all - * possible domains he'll transition - * immediately. Note that we'll still - * try and do it coherently with - * other domains to which we're connected. - */ - ready_set = DOMAINSET_ALL; - } else { - DOMAINSET_ADD(ready_set, idn.localid); - } - - DOMAINSET_ADD(idn.domset.ds_ready_off, domid); - - query_set = idn_sync_register(domid, IDNSYNC_DISCONNECT, - ready_set, IDNSYNC_REG_REG); - /* - * No need to query this domain as he's already - * in the FIN sequence. - */ - DOMAINSET_DEL(query_set, domid); - - ready = (dp->dsync.s_set_exp == dp->dsync.s_set_rdy) ? 1 : 0; - if (ready) { - DOMAINSET_DEL(idn.domset.ds_ready_off, domid); - DOMAINSET_DEL(idn.domset.ds_connected, domid); - } - - if (query_set) { - int d; - domainset_t my_ready_set; - - my_ready_set = idn.domset.ds_ready_off | - ~idn.domset.ds_connected; - - for (d = 0; d < MAX_DOMAINS; d++) { - if (!DOMAIN_IN_SET(query_set, d)) - continue; - - dp = &idn_domain[d]; - - IDN_DLOCK_EXCL(d); - - if (dp->dsync.s_cmd == IDNSYNC_DISCONNECT) { - IDN_DUNLOCK(d); - continue; - } - - IDN_SYNC_QUERY_UPDATE(domid, d); - - (void) idn_send_fin(d, NULL, IDNFIN_QUERY, - IDNFIN_ARG_NONE, IDNFIN_OPT_NONE, my_ready_set, - NIL_FIN_MASTER); - IDN_DUNLOCK(d); - } - } - - return ((ready > 0) ? 0 : 1); -} - -/*ARGSUSED*/ -static void -idn_error_fin_sent(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - uint_t msg = mtp ? mtp->mt_mtype : 0; - uint_t token; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - /* - * Don't communicate with domains that - * we're forcing a hard disconnect. - */ - if ((idn_domain[domid].dfin != IDNFIN_FORCE_HARD) && - (msg & IDNP_MSGTYPE_MASK)) { - idn_msgtype_t mt; - idn_xdcargs_t nargs; - - mt.mt_mtype = IDNP_NACK; - mt.mt_atype = msg; - mt.mt_cookie = mtp->mt_cookie; - CLR_XARGS(nargs); - SET_XARGS_NACK_TYPE(nargs, IDNNACK_RETRY); - idn_send_acknack(domid, &mt, nargs); - } - - token = IDN_RETRY_TOKEN(domid, IDNRETRY_FIN); - idn_retry_submit(idn_retry_fin, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_FIN]); -} - -static void -idn_action_fin_sent(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - uint_t msg = mtp ? mtp->mt_mtype : 0; - int new_masterid, new_cpuid = IDN_NIL_DCPU; - uint_t finmaster; - idn_msgtype_t mt; - idn_finopt_t finopt; - idn_finarg_t finarg; - domainset_t my_ready_set; - idn_domain_t *dp = &idn_domain[domid]; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - mt.mt_cookie = mtp ? mtp->mt_cookie : 0; - - finopt = DOMAIN_IN_SET(idn.domset.ds_relink, domid) ? - IDNFIN_OPT_RELINK : IDNFIN_OPT_UNLINK; - - finarg = GET_XARGS_FIN_ARG(xargs); - - my_ready_set = dp->dsync.s_set_rdy | idn.domset.ds_ready_off | - ~idn.domset.ds_connected; - - IDN_GLOCK_SHARED(); - new_masterid = IDN_GET_NEW_MASTERID(); - IDN_GUNLOCK(); - if (new_masterid != IDN_NIL_DOMID) - new_cpuid = idn_domain[new_masterid].dcpu; - finmaster = MAKE_FIN_MASTER(new_masterid, new_cpuid); - - if ((msg & IDNP_ACKNACK_MASK) == 0) { - /* - * fin - */ - if (dp->dfin == IDNFIN_FORCE_HARD) { - mt.mt_mtype = IDNP_ACK; - mt.mt_atype = IDNP_FIN | IDNP_ACK; - (void) idn_xphase_transition(domid, &mt, xargs); - } else { - mt.mt_mtype = IDNP_FIN | IDNP_ACK; - mt.mt_atype = 0; - (void) idn_send_fin(domid, &mt, dp->dfin, finarg, - finopt, my_ready_set, finmaster); - } - } else if (msg & IDNP_MSGTYPE_MASK) { - /* - * fin+ack - */ - if (dp->dfin != IDNFIN_FORCE_HARD) { - idn_xdcargs_t fargs; - - mt.mt_mtype = IDNP_ACK; - mt.mt_atype = msg; - CLR_XARGS(fargs); - SET_XARGS_FIN_TYPE(fargs, dp->dfin); - SET_XARGS_FIN_ARG(fargs, finarg); - SET_XARGS_FIN_DOMSET(fargs, my_ready_set); - SET_XARGS_FIN_OPT(fargs, finopt); - SET_XARGS_FIN_MASTER(fargs, finmaster); - idn_send_acknack(domid, &mt, fargs); - } - } else { - uint_t token; - /* - * nack - retry - */ - token = IDN_RETRY_TOKEN(domid, IDNRETRY_FIN); - idn_retry_submit(idn_retry_fin, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_FIN]); - } -} - -/*ARGSUSED*/ -static void -idn_action_fin_rcvd(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - uint_t msg = mtp ? mtp->mt_mtype : 0; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - if (msg & IDNP_NACK) { - uint_t token; - /* - * nack - retry. - */ - token = IDN_RETRY_TOKEN(domid, IDNRETRY_FIN); - idn_retry_submit(idn_retry_fin, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_FIN]); - } -} - -static void -idn_final_fin(int domid) -{ - int do_relink; - int rv, d, new_masterid = IDN_NIL_DOMID; - idn_gstate_t next_gstate; - domainset_t relinkset; - uint_t token = IDN_RETRY_TOKEN(domid, IDNRETRY_FIN); - idn_domain_t *ldp, *dp = &idn_domain[domid]; - procname_t proc = "idn_final_fin"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - ASSERT(dp->dstate == IDNDS_DMAP); - - (void) idn_retry_terminate(token); - - dp->dxp = NULL; - IDN_XSTATE_TRANSITION(dp, IDNXS_NIL); - - idn_sync_exit(domid, IDNSYNC_DISCONNECT); - - DOMAINSET_DEL(idn.domset.ds_trans_off, domid); - - do_relink = DOMAIN_IN_SET(idn.domset.ds_relink, domid) ? 1 : 0; - - /* - * idn_deconfig will idn_close_domain. - */ - idn_deconfig(domid); - - PR_PROTO("%s:%d: DISCONNECTED\n", proc, domid); - - IDN_GLOCK_EXCL(); - /* - * It's important that this update-op occur within - * the context of holding the glock(EXCL). There is - * still some additional state stuff to cleanup which - * will be completed once the glock is dropped in - * this flow. Which means anybody that's doing a - * SSI_INFO and waiting on glock will not actually - * run until the clean-up is completed, which is what - * we want. Recall that a separate thread processes - * the SSI_LINK/UNLINK calls and when they complete - * (i.e. are awakened) they will immediately SSI_INFO - * and we don't want them to prematurely pick up stale - * information. - */ - idn_update_op(IDNOP_DISCONNECTED, DOMAINSET(domid), NULL); - - ASSERT(idn.state != IDNGS_OFFLINE); - ASSERT(!DOMAIN_IN_SET(idn.domset.ds_trans_on, domid)); - - if (domid == IDN_GET_MASTERID()) { - IDN_SET_MASTERID(IDN_NIL_DOMID); - dp->dvote.v.master = 0; - } - - if ((domid == IDN_GET_NEW_MASTERID()) && !do_relink) { - IDN_SET_NEW_MASTERID(IDN_NIL_DOMID); - } - - if (idn.state == IDNGS_RECONFIG) - new_masterid = IDN_GET_NEW_MASTERID(); - - if ((idn.domset.ds_trans_on | idn.domset.ds_trans_off | - idn.domset.ds_relink) == 0) { - PR_HITLIST("%s:%d: HITLIST %x -> 0\n", - proc, domid, idn.domset.ds_hitlist); - idn.domset.ds_hitlist = 0; - } - - if (idn.domset.ds_connected || idn.domset.ds_trans_off) { - PR_PROTO("%s:%d: ds_connected = 0x%x, ds_trans_off = 0x%x\n", - proc, domid, idn.domset.ds_connected, - idn.domset.ds_trans_off); - IDN_GUNLOCK(); - goto fin_done; - } - - IDN_DLOCK_EXCL(idn.localid); - ldp = &idn_domain[idn.localid]; - - if (idn.domset.ds_trans_on != 0) { - ASSERT((idn.state != IDNGS_DISCONNECT) && - (idn.state != IDNGS_OFFLINE)); - - switch (idn.state) { - case IDNGS_CONNECT: - if (idn.localid == IDN_GET_MASTERID()) { - idn_master_deinit(); - IDN_SET_MASTERID(IDN_NIL_DOMID); - ldp->dvote.v.master = 0; - } - /*FALLTHROUGH*/ - case IDNGS_ONLINE: - next_gstate = idn.state; - break; - - case IDNGS_RECONFIG: - if (idn.localid == IDN_GET_MASTERID()) { - idn_master_deinit(); - IDN_SET_MASTERID(IDN_NIL_DOMID); - ldp->dvote.v.master = 0; - } - ASSERT(IDN_GET_MASTERID() == IDN_NIL_DOMID); - next_gstate = IDNGS_CONNECT; - ldp->dvote.v.connected = 0; - /* - * Need to do HWINIT since we won't - * be transitioning through OFFLINE - * which would normally be caught in - * idn_check_nego() when we - * initially go to CONNECT. - */ - IDN_PREP_HWINIT(); - break; - - case IDNGS_DISCONNECT: - case IDNGS_OFFLINE: - cmn_err(CE_WARN, - "IDN: 211: disconnect domain %d, " - "unexpected Gstate (%s)", - domid, idngs_str[idn.state]); - IDN_DUNLOCK(idn.localid); - IDN_GUNLOCK(); - goto fin_done; - - default: - /* - * XXX - * Go into FATAL state? - */ - cmn_err(CE_PANIC, - "IDN: 212: disconnect domain %d, " - "bad Gstate (%d)", - domid, idn.state); - /* not reached */ - break; - } - } else { - if (idn.localid == IDN_GET_MASTERID()) { - idn_master_deinit(); - IDN_SET_MASTERID(IDN_NIL_DOMID); - ldp->dvote.v.master = 0; - } - next_gstate = IDNGS_OFFLINE; - if (idn.domset.ds_relink == 0) { - IDN_SET_NEW_MASTERID(IDN_NIL_DOMID); - } - } - IDN_DUNLOCK(idn.localid); - - /* - * If we reach here we've effectively disconnected all - * existing links, however new ones may be pending. - */ - PR_PROTO("%s:%d: ALL DISCONNECTED *****************\n", proc, domid); - - IDN_GSTATE_TRANSITION(next_gstate); - - ASSERT((idn.state == IDNGS_OFFLINE) ? - (IDN_GET_MASTERID() == IDN_NIL_DOMID) : 1); - - IDN_GUNLOCK(); - - /* - * If we have no new masterid and yet there are relinkers - * out there, then force us to attempt to link with one - * of them. - */ - if ((new_masterid == IDN_NIL_DOMID) && idn.domset.ds_relink) - new_masterid = idn.localid; - - if (new_masterid != IDN_NIL_DOMID) { - /* - * If the local domain is the selected - * master then we'll want to initiate - * a link with one of the other candidates. - * If not, then we want to initiate a link - * with the master only. - */ - relinkset = (new_masterid == idn.localid) ? - idn.domset.ds_relink : DOMAINSET(new_masterid); - - DOMAINSET_DEL(relinkset, idn.localid); - - for (d = 0; d < MAX_DOMAINS; d++) { - int lock_held; - - if (!DOMAIN_IN_SET(relinkset, d)) - continue; - - if (d == domid) { - do_relink = 0; - lock_held = 0; - } else { - IDN_DLOCK_EXCL(d); - lock_held = 1; - } - - rv = idn_open_domain(d, -1, 0); - if (rv == 0) { - rv = idn_connect(d); - if (lock_held) - IDN_DUNLOCK(d); - /* - * If we're able to kick off at - * least one connect then that's - * good enough for now. The others - * will fall into place normally. - */ - if (rv == 0) - break; - } else if (rv < 0) { - if (lock_held) - IDN_DUNLOCK(d); - cmn_err(CE_WARN, - "IDN: 205: (%s.1) failed to " - "open-domain(%d,%d)", - proc, domid, -1); - DOMAINSET_DEL(idn.domset.ds_relink, d); - } else { - if (lock_held) - IDN_DUNLOCK(d); - PR_PROTO("%s:%d: failed to " - "re-open domain %d " - "(cpu %d) [rv = %d]\n", - proc, domid, d, idn_domain[d].dcpu, - rv); - } - } - } - -fin_done: - if (do_relink) { - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - rv = idn_open_domain(domid, -1, 0); - if (rv == 0) { - (void) idn_connect(domid); - } else if (rv < 0) { - cmn_err(CE_WARN, - "IDN: 205: (%s.2) failed to " - "open-domain(%d,%d)", - proc, domid, -1); - DOMAINSET_DEL(idn.domset.ds_relink, domid); - } - } -} - -static void -idn_exit_fin(int domid, uint_t msgtype) -{ - idn_domain_t *dp = &idn_domain[domid]; - uint_t token; - procname_t proc = "idn_exit_fin"; - STRING(str); - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - INUM2STR(msgtype, str); - PR_PROTO("%s:%d: msgtype = 0x%x(%s)\n", proc, domid, msgtype, str); - - token = IDN_RETRY_TOKEN(domid, IDNRETRY_FIN); - (void) idn_retry_terminate(token); - - DOMAINSET_DEL(idn.domset.ds_ready_off, domid); - - dp->dxp = &xphase_fin; - IDN_XSTATE_TRANSITION(dp, IDNXS_PEND); - - idn_retry_submit(idn_retry_fin, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_FIN]); -} - -/* - * Must return w/locks held. - */ -static int -idn_xphase_transition(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - uint_t msg = mtp ? mtp->mt_mtype : 0; - uint_t msgarg = mtp ? mtp->mt_atype : 0; - idn_xphase_t *xp; - idn_domain_t *dp; - int (*cfunc)(int, idn_msgtype_t *, idn_xdcargs_t); - void (*ffunc)(int); - void (*afunc)(int, idn_msgtype_t *, idn_xdcargs_t); - void (*efunc)(int, idn_msgtype_t *, idn_xdcargs_t); - void (*xfunc)(int, uint_t); - int err = 0; - uint_t msgtype; - idn_xstate_t o_xstate, n_xstate; - procname_t proc = "idn_xphase_transition"; - STRING(mstr); - STRING(astr); - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - INUM2STR(msg, mstr); - INUM2STR(msgarg, astr); - - dp = &idn_domain[domid]; - if ((xp = dp->dxp) == NULL) { - PR_PROTO("%s:%d: WARNING: domain xsp is NULL (msg = %s, " - "msgarg = %s) <<<<<<<<<<<<\n", - proc, domid, mstr, astr); - return (-1); - } - o_xstate = dp->dxstate; - - xfunc = xp->xt_exit; - - if ((msgtype = (msg & IDNP_MSGTYPE_MASK)) == 0) - msgtype = msgarg & IDNP_MSGTYPE_MASK; - - if ((o_xstate == IDNXS_PEND) && msg && - ((msg & IDNP_ACKNACK_MASK) == msg)) { - PR_PROTO("%s:%d: unwanted acknack received (o_xstate = %s, " - "msg = %s/%s - dropping message\n", - proc, domid, idnxs_str[(int)o_xstate], mstr, astr); - return (0); - } - - /* - * Validate that message received is following - * the expected protocol for the current state. - */ - if (idn_next_xstate(o_xstate, -1, msg) == IDNXS_NIL) { - PR_PROTO("%s:%d: WARNING: o_xstate = %s, msg = %s -> NIL " - "<<<<<<<<<\n", - proc, domid, idnxs_str[(int)o_xstate], mstr); - if (xfunc) - (*xfunc)(domid, msgtype); - return (-1); - } - - if (msg || msgarg) { - /* - * Verify that message type is correct for - * the given xstate. - */ - if (msgtype != xp->xt_msgtype) { - STRING(xstr); - STRING(tstr); - - INUM2STR(xp->xt_msgtype, xstr); - INUM2STR(msgtype, tstr); - PR_PROTO("%s:%d: WARNING: msg expected %s(0x%x), " - "actual %s(0x%x) [msg=%s(0x%x), " - "msgarg=%s(0x%x)]\n", - proc, domid, xstr, xp->xt_msgtype, - tstr, msgtype, mstr, msg, astr, msgarg); - if (xfunc) - (*xfunc)(domid, msgtype); - return (-1); - } - } - - cfunc = xp->xt_trans[(int)o_xstate].t_check; - - if (cfunc && ((err = (*cfunc)(domid, mtp, xargs)) < 0)) { - if (o_xstate != IDNXS_PEND) { - IDN_XSTATE_TRANSITION(dp, IDNXS_PEND); - } - if (xfunc) - (*xfunc)(domid, msgtype); - return (-1); - } - - n_xstate = idn_next_xstate(o_xstate, err, msg); - - if (n_xstate == IDNXS_NIL) { - PR_PROTO("%s:%d: WARNING: n_xstate = %s, msg = %s -> NIL " - "<<<<<<<<<\n", - proc, domid, idnxs_str[(int)n_xstate], mstr); - if (xfunc) - (*xfunc)(domid, msgtype); - return (-1); - } - - if (n_xstate != o_xstate) { - IDN_XSTATE_TRANSITION(dp, n_xstate); - } - - if (err) { - if ((efunc = xp->xt_trans[(int)o_xstate].t_error) != NULL) - (*efunc)(domid, mtp, xargs); - } else if ((afunc = xp->xt_trans[(int)o_xstate].t_action) != NULL) { - (*afunc)(domid, mtp, xargs); - } - - if ((n_xstate == IDNXS_FINAL) && ((ffunc = xp->xt_final) != NULL)) - (*ffunc)(domid); - - return (0); -} - -/* - * Entered and returns w/DLOCK & SYNC_LOCK held. - */ -static int -idn_xstate_transfunc(int domid, void *transarg) -{ - uint_t msg = (uint_t)(uintptr_t)transarg; - uint_t token; - procname_t proc = "idn_xstate_transfunc"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - - switch (msg) { - case IDNP_CON: - DOMAINSET_ADD(idn.domset.ds_connected, domid); - break; - - case IDNP_FIN: - DOMAINSET_DEL(idn.domset.ds_connected, domid); - break; - - default: - PR_PROTO("%s:%d: ERROR: unknown msg (0x%x) <<<<<<<<\n", - proc, domid, msg); - return (0); - } - - token = IDN_RETRY_TOKEN(domid, (msg == IDNP_CON) ? - IDNRETRY_CON : IDNRETRY_FIN); - if (msg == IDNP_CON) - idn_retry_submit(idn_retry_con, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_CON]); - else - idn_retry_submit(idn_retry_fin, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_FIN]); - - return (1); -} - -/* - * Entered and returns w/DLOCK & SYNC_LOCK held. - */ -static void -idn_sync_enter(int domid, idn_synccmd_t cmd, domainset_t xset, - domainset_t rset, int (*transfunc)(), void *transarg) -{ - int z; - idn_syncop_t *sp; - idn_synczone_t *zp; - procname_t proc = "idn_sync_enter"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - z = IDN_SYNC_GETZONE(cmd); - ASSERT(z >= 0); - zp = &idn.sync.sz_zone[z]; - - PR_SYNC("%s:%d: cmd=%s(%d), z=%d, xs=0x%x, rx=0x%x, cnt=%d\n", - proc, domid, idnsync_str[cmd], cmd, z, xset, rset, zp->sc_cnt); - - sp = &idn_domain[domid].dsync; - - sp->s_domid = domid; - sp->s_cmd = cmd; - sp->s_msg = 0; - sp->s_set_exp = xset; - sp->s_set_rdy = rset; - sp->s_transfunc = transfunc; - sp->s_transarg = transarg; - IDN_SYNC_QUERY_INIT(domid); - - sp->s_next = zp->sc_op; - zp->sc_op = sp; - zp->sc_cnt++; -} - -/* - * Entered and returns w/DLOCK & SYNC_LOCK held. - */ -void -idn_sync_exit(int domid, idn_synccmd_t cmd) -{ - int d, z, zone, tot_queries, tot_domains; - idn_syncop_t *sp; - idn_synczone_t *zp = NULL; - procname_t proc = "idn_sync_exit"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - sp = &idn_domain[domid].dsync; - - z = IDN_SYNC_GETZONE(sp->s_cmd); - - zone = IDN_SYNC_GETZONE(cmd); - - PR_SYNC("%s:%d: cmd=%s(%d) (z=%d, zone=%d)\n", - proc, domid, idnsync_str[cmd], cmd, z, zone); - -#ifdef DEBUG - if (z != -1) { - tot_queries = tot_domains = 0; - - for (d = 0; d < MAX_DOMAINS; d++) { - int qv; - - if ((qv = sp->s_query[d]) > 0) { - tot_queries += qv; - tot_domains++; - PR_SYNC("%s:%d: query_count = %d\n", - proc, domid, qv); - } - } - PR_SYNC("%s:%d: tot_queries = %d, tot_domaines = %d\n", - proc, domid, tot_queries, tot_domains); - } -#endif /* DEBUG */ - - zp = (z != -1) ? &idn.sync.sz_zone[z] : NULL; - - if (zp) { - idn_syncop_t **spp; - - for (spp = &zp->sc_op; *spp; spp = &((*spp)->s_next)) { - if (*spp == sp) { - *spp = sp->s_next; - sp->s_next = NULL; - zp->sc_cnt--; - break; - } - } - } - - sp->s_cmd = IDNSYNC_NIL; - - for (z = 0; z < IDN_SYNC_NUMZONE; z++) { - idn_syncop_t **spp, **nspp; - - if ((zone != -1) && (z != zone)) - continue; - - zp = &idn.sync.sz_zone[z]; - - for (spp = &zp->sc_op; *spp; spp = nspp) { - sp = *spp; - nspp = &sp->s_next; - - if (!DOMAIN_IN_SET(sp->s_set_exp, domid)) - continue; - - DOMAINSET_DEL(sp->s_set_exp, domid); - DOMAINSET_DEL(sp->s_set_rdy, domid); - - if ((sp->s_set_exp == sp->s_set_rdy) && - sp->s_transfunc) { - int delok; - - ASSERT(sp->s_domid != domid); - - PR_SYNC("%s:%d invoking transfunc " - "for domain %d\n", - proc, domid, sp->s_domid); - delok = (*sp->s_transfunc)(sp->s_domid, - sp->s_transarg); - if (delok) { - *spp = sp->s_next; - sp->s_next = NULL; - zp->sc_cnt--; - nspp = spp; - } - } - } - } -} - -/* - * Entered and returns w/DLOCK & SYNC_LOCK held. - */ -static domainset_t -idn_sync_register(int domid, idn_synccmd_t cmd, domainset_t ready_set, - idn_syncreg_t regtype) -{ - int z; - idn_synczone_t *zp; - idn_syncop_t *sp, **spp, **nspp; - domainset_t query_set = 0, trans_set; - procname_t proc = "idn_sync_register"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - if ((z = IDN_SYNC_GETZONE(cmd)) == -1) { - PR_SYNC("%s:%d: ERROR: unexpected sync cmd(%d)\n", - proc, domid, cmd); - return (0); - } - - /* - * Find out what domains are in transition with respect - * to given command. There will be no need to query - * these folks. - */ - trans_set = IDN_SYNC_GETTRANS(cmd); - - zp = &idn.sync.sz_zone[z]; - - PR_SYNC("%s:%d: cmd=%s(%d), z=%d, rset=0x%x, " - "regtype=%s(%d), sc_op=%s\n", - proc, domid, idnsync_str[cmd], cmd, z, ready_set, - idnreg_str[regtype], regtype, - zp->sc_op ? idnsync_str[zp->sc_op->s_cmd] : "NULL"); - - for (spp = &zp->sc_op; *spp; spp = nspp) { - sp = *spp; - nspp = &sp->s_next; - - if (regtype == IDNSYNC_REG_NEW) { - DOMAINSET_ADD(sp->s_set_exp, domid); - PR_SYNC("%s:%d: adding new to %d (exp=0x%x)\n", - proc, domid, sp->s_domid, sp->s_set_exp); - } else if (regtype == IDNSYNC_REG_QUERY) { - query_set |= ~sp->s_set_rdy & sp->s_set_exp; - continue; - } - - if (!DOMAIN_IN_SET(sp->s_set_exp, domid)) - continue; - - if (!DOMAIN_IN_SET(ready_set, sp->s_domid)) { - /* - * Given domid doesn't have a desired - * domain in his ready-set. We'll need - * to query him again. - */ - DOMAINSET_ADD(query_set, domid); - continue; - } - - /* - * If we reach here, then an expected domain - * has marked its respective datapath to - * sp->s_domid as down (i.e. in his ready_set). - */ - DOMAINSET_ADD(sp->s_set_rdy, domid); - - PR_SYNC("%s:%d: mark READY for domain %d " - "(r=0x%x, x=0x%x)\n", - proc, domid, sp->s_domid, - sp->s_set_rdy, sp->s_set_exp); - - query_set |= ~sp->s_set_rdy & sp->s_set_exp; - - if (sp->s_set_exp == sp->s_set_rdy) { -#ifdef DEBUG - if (sp->s_msg == 0) { - sp->s_msg = 1; - PR_SYNC("%s:%d: >>>>>>>>>>> DOMAIN %d " - "ALL CHECKED IN (0x%x)\n", - proc, domid, sp->s_domid, - sp->s_set_exp); - } -#endif /* DEBUG */ - - if ((sp->s_domid != domid) && sp->s_transfunc) { - int delok; - - PR_SYNC("%s:%d invoking transfunc " - "for domain %d\n", - proc, domid, sp->s_domid); - delok = (*sp->s_transfunc)(sp->s_domid, - sp->s_transarg); - if (delok) { - *spp = sp->s_next; - sp->s_next = NULL; - zp->sc_cnt--; - nspp = spp; - } - } - } - } - - PR_SYNC("%s:%d: trans_set = 0x%x, query_set = 0x%x -> 0x%x\n", - proc, domid, trans_set, query_set, query_set & ~trans_set); - - query_set &= ~trans_set; - - return (query_set); -} - -static void -idn_sync_register_awol(int domid) -{ - int z; - idn_synccmd_t cmd = IDNSYNC_DISCONNECT; - idn_synczone_t *zp; - idn_syncop_t *sp; - procname_t proc = "idn_sync_register_awol"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - - if ((z = IDN_SYNC_GETZONE(cmd)) == -1) { - PR_SYNC("%s:%d: ERROR: unexpected sync cmd(%d)\n", - proc, domid, cmd); - return; - } - - zp = &idn.sync.sz_zone[z]; - - PR_SYNC("%s:%d: cmd=%s(%d), z=%d (domain %d = AWOL)\n", - proc, domid, idnsync_str[cmd], cmd, z, domid); - - for (sp = zp->sc_op; sp; sp = sp->s_next) { - idn_domain_t *dp; - - dp = &idn_domain[sp->s_domid]; - if (dp->dfin == IDNFIN_FORCE_HARD) { - DOMAINSET_ADD(sp->s_set_rdy, domid); - PR_SYNC("%s:%d: adding new to %d (rdy=0x%x)\n", - proc, domid, sp->s_domid, sp->s_set_rdy); - } - } -} - -static void -idn_link_established(void *arg) -{ - int first_link; - int domid, masterid; - uint_t info = (uint_t)(uintptr_t)arg; - - first_link = (int)(info & 0xf0); - domid = (int)(info & 0x0f); - - IDN_GLOCK_SHARED(); - masterid = IDN_GET_MASTERID(); - if ((masterid == IDN_NIL_DOMID) || - (idn_domain[masterid].dstate != IDNDS_CONNECTED)) { - /* - * No point in doing this unless we're connected - * to the master. - */ - if ((masterid != IDN_NIL_DOMID) && - (idn.state == IDNGS_ONLINE)) { - /* - * As long as we're still online keep - * trying. - */ - (void) timeout(idn_link_established, arg, 50); - } - IDN_GUNLOCK(); - return; - } - IDN_GUNLOCK(); - - if (first_link && IDN_SLAB_PREALLOC) - idn_prealloc_slab(IDN_SLAB_PREALLOC); - - /* - * No guarantee, but it might save a little - * time. - */ - if (idn_domain[domid].dstate == IDNDS_CONNECTED) { - /* - * Get the remote domain's dname. - */ - idn_send_nodename_req(domid); - } - - /* - * May have had some streams backed up waiting for - * this connection. Prod them. - */ - rw_enter(&idn.struprwlock, RW_READER); - mutex_enter(&idn.sipwenlock); - idndl_wenable(NULL); - mutex_exit(&idn.sipwenlock); - rw_exit(&idn.struprwlock); -} - -/* - * Send the following chunk of data received from above onto - * the IDN wire. This is raw data as far as the IDN driver - * is concerned. - * Returns: - * IDNXMIT_LOOP - Msg handled in loopback and thus - * still active (i.e. don't free). - * IDNXMIT_OKAY - Data handled (freemsg). - * IDNXMIT_DROP - Packet should be dropped. - * IDNXMIT_RETRY - Packet should be requeued and retried. - * IDNXMIT_REQUEUE - Packet should be requeued, but not - * immediatetly retried. - */ -int -idn_send_data(int dst_domid, idn_netaddr_t dst_netaddr, queue_t *wq, mblk_t *mp) -{ - int pktcnt = 0; - int msglen; - int rv = IDNXMIT_OKAY; - int xfersize = 0; - caddr_t iobufp, iodatap; - uchar_t *data_rptr; - int cpuindex; - int serrno; - int channel; - int retry_reclaim; - idn_chansvr_t *csp = NULL; - uint_t netports = 0; - struct idnstr *stp; - struct idn *sip; - idn_domain_t *dp; - struct ether_header *ehp; - smr_pkthdr_t *hdrp; - idn_msgtype_t mt; - procname_t proc = "idn_send_data"; -#ifdef DEBUG - size_t orig_msglen = msgsize(mp); -#endif /* DEBUG */ - - ASSERT(DB_TYPE(mp) == M_DATA); - - mt.mt_mtype = IDNP_DATA; - mt.mt_atype = 0; - mt.mt_cookie = 0; - - channel = (int)dst_netaddr.net.chan; - - msglen = msgdsize(mp); - PR_DATA("%s:%d: (netaddr 0x%x) msgsize=%ld, msgdsize=%d\n", - proc, dst_domid, dst_netaddr.netaddr, msgsize(mp), msglen); - - ASSERT(wq->q_ptr); - - stp = (struct idnstr *)wq->q_ptr; - sip = stp->ss_sip; - ASSERT(sip); - - if (msglen < 0) { - /* - * No data to send. That was easy! - */ - PR_DATA("%s:%d: BAD msg length (%d) (netaddr 0x%x)\n", - proc, dst_domid, msglen, dst_netaddr.netaddr); - return (IDNXMIT_DROP); - } - - ASSERT(RW_READ_HELD(&stp->ss_rwlock)); - - if (dst_domid == IDN_NIL_DOMID) { - cmn_err(CE_WARN, - "IDN: 213: no destination specified " - "(d=%d, c=%d, n=0x%x)", - dst_domid, dst_netaddr.net.chan, - dst_netaddr.net.netid); - IDN_KSTAT_INC(sip, si_nolink); - IDN_KSTAT_INC(sip, si_macxmt_errors); - rv = IDNXMIT_DROP; - goto nocando; - } - - ehp = (struct ether_header *)mp->b_rptr; - PR_DATA("%s:%d: destination channel = %d\n", proc, dst_domid, channel); - -#ifdef DEBUG - { - uchar_t echn; - - echn = (uchar_t) - ehp->ether_shost.ether_addr_octet[IDNETHER_CHANNEL]; - ASSERT((uchar_t)channel == echn); - } -#endif /* DEBUG */ - ASSERT(msglen <= IDN_DATA_SIZE); - - dp = &idn_domain[dst_domid]; - /* - * Get reader lock. We hold for the duration - * of the transfer so that our state doesn't - * change during this activity. Note that since - * we grab the reader lock, we can still permit - * simultaneous tranfers from different threads - * to the same domain. - * Before we waste a bunch of time gathering locks, etc. - * do a an unprotected check to make sure things are - * semi-copesetic. If these values are in flux, - * that's okay. - */ - if ((dp->dstate != IDNDS_CONNECTED) || (idn.state != IDNGS_ONLINE)) { - IDN_KSTAT_INC(sip, si_linkdown); - if (idn.state != IDNGS_ONLINE) { - rv = IDNXMIT_REQUEUE; - } else { - IDN_KSTAT_INC(sip, si_macxmt_errors); - rv = IDNXMIT_DROP; - } - goto nocando; - } - - if (idn.chan_servers[channel].ch_send.c_checkin) { - /* - * Gotta bail, somethin' s'up. - */ - rv = IDNXMIT_REQUEUE; - goto nocando; - } - - csp = &idn.chan_servers[channel]; - IDN_CHAN_LOCK_SEND(csp); - - if (dst_netaddr.net.netid == IDN_BROADCAST_ALLNETID) { - /* - * We're doing a broadcast. Need to set - * up IDN netaddr's one at a time. - * We set the ethernet destination to the same - * instance as the sending address. The instance - * numbers effectively represent subnets. - */ - dst_netaddr.net.netid = dp->dnetid; - - (void) idndl_domain_etheraddr(dst_domid, channel, - &ehp->ether_dhost); - - if (dst_domid == idn.localid) { - mblk_t *nmp; - /* - * If this is a broadcast and going to - * the local domain, then we need to make - * a private copy of the message since - * the current one will be reused when - * transmitting to other domains. - */ - PR_DATA("%s:%d: dup broadcast msg for local domain\n", - proc, dst_domid); - if ((nmp = copymsg(mp)) == NULL) { - /* - * Couldn't get a duplicate copy. - */ - IDN_CHAN_UNLOCK_SEND(csp); - csp = NULL; - IDN_KSTAT_INC(sip, si_allocbfail); - IDN_KSTAT_INC(sip, si_noxmtbuf); - rv = IDNXMIT_DROP; - goto nocando; - } - mp = nmp; - } - } - - if (dp->dnetid != dst_netaddr.net.netid) { - PR_DATA("%s:%d: dest netid (0x%x) != expected (0x%x)\n", - proc, dst_domid, (uint_t)dst_netaddr.net.netid, - (uint_t)dp->dnetid); - IDN_CHAN_UNLOCK_SEND(csp); - csp = NULL; - IDN_KSTAT_INC(sip, si_nolink); - IDN_KSTAT_INC(sip, si_macxmt_errors); - rv = IDNXMIT_DROP; - goto nocando; - } - - if (dst_domid == idn.localid) { - int lbrv; - /* - * Sending to our local domain! Loopback. - * Note that idn_send_data_loop returning 0 - * does not mean the message can now be freed. - * We need to return (-1) so that caller doesn't - * try to free mblk. - */ - IDN_CHAN_UNLOCK_SEND(csp); - rw_exit(&stp->ss_rwlock); - lbrv = idn_send_data_loopback(dst_netaddr, wq, mp); - rw_enter(&stp->ss_rwlock, RW_READER); - if (lbrv == 0) { - return (IDNXMIT_LOOP); - } else { - IDN_KSTAT_INC(sip, si_macxmt_errors); - return (IDNXMIT_DROP); - } - } - - if (dp->dstate != IDNDS_CONNECTED) { - /* - * Can't send data unless a link has already been - * established with the target domain. Normally, - * a user cannot set the remote netaddr unless a - * link has already been established, however it - * is possible the connection may have become - * disconnected since that time. - */ - IDN_CHAN_UNLOCK_SEND(csp); - csp = NULL; - IDN_KSTAT_INC(sip, si_linkdown); - IDN_KSTAT_INC(sip, si_macxmt_errors); - rv = IDNXMIT_DROP; - goto nocando; - } - - /* - * Need to make sure the channel is active and that the - * domain to which we're sending is allowed to receive stuff. - */ - if (!IDN_CHANNEL_IS_SEND_ACTIVE(csp)) { - int not_active; - /* - * See if we can activate channel. - */ - IDN_CHAN_UNLOCK_SEND(csp); - not_active = idn_activate_channel(CHANSET(channel), - IDNCHAN_OPEN); - if (!not_active) { - /* - * Only grab the lock for a recheck if we were - * able to activate the channel. - */ - IDN_CHAN_LOCK_SEND(csp); - } - /* - * Verify channel still active now that we have the lock. - */ - if (not_active || !IDN_CHANNEL_IS_SEND_ACTIVE(csp)) { - if (!not_active) { - /* - * Only need to drop the lock if it was - * acquired while we thought we had - * activated the channel. - */ - IDN_CHAN_UNLOCK_SEND(csp); - } - ASSERT(!IDN_CHAN_SEND_IS_LOCKED(csp)); - /* - * Damn! Must have went inactive during the window - * before we regrabbed the send lock. Oh well, can't - * spend all day doing this, bail out. Set csp to - * NULL to prevent inprogress update at bottom. - */ - csp = NULL; - /* - * Channel is not active, should not be used. - */ - PR_DATA("%s:%d: dest channel %d NOT ACTIVE\n", - proc, dst_domid, channel); - IDN_KSTAT_INC(sip, si_linkdown); - rv = IDNXMIT_REQUEUE; - goto nocando; - } - ASSERT(IDN_CHAN_SEND_IS_LOCKED(csp)); - } - /* - * If we made it here then the channel is active - * Make sure the target domain is registered to receive stuff, - * i.e. we're still linked. - */ - if (!IDN_CHAN_DOMAIN_IS_REGISTERED(csp, dst_domid)) { - /* - * If domain is not even registered with this channel - * then we have no business being here. Doesn't matter - * whether it's active or not. - */ - PR_DATA("%s:%d: domain not registered with channel %d\n", - proc, dst_domid, channel); - /* - * Set csp to NULL to prevent in-progress update below. - */ - IDN_CHAN_UNLOCK_SEND(csp); - csp = NULL; - IDN_KSTAT_INC(sip, si_linkdown); - IDN_KSTAT_INC(sip, si_macxmt_errors); - rv = IDNXMIT_DROP; - goto nocando; - } - - IDN_CHAN_SEND_INPROGRESS(csp); - IDN_CHAN_UNLOCK_SEND(csp); - - /* - * Find a target cpu to send interrupt to if - * it becomes necessary (i.e. remote channel - * server is idle). - */ - cpuindex = dp->dcpuindex; - - /* - * dcpuindex is atomically incremented, but other than - * that is not well protected and that's okay. The - * intention is to simply spread around the interrupts - * at the destination domain, however we don't have to - * anal about it. If we hit the same cpu multiple times - * in a row that's okay, it will only be for a very short - * period anyway before the cpuindex is incremented - * to the next cpu. - */ - if (cpuindex < NCPU) { - ATOMIC_INC(dp->dcpuindex); - } - if (dp->dcpuindex >= NCPU) - dp->dcpuindex = 0; - - IDN_ASSIGN_DCPU(dp, cpuindex); - -#ifdef XXX_DLPI_UNFRIENDLY - { - ushort_t dstport = (ushort_t)dp->dcpu; - - /* - * XXX - * This is not DLPI friendly, but we need some way - * of distributing our XDC interrupts to the cpus - * on the remote domain in a relatively random fashion - * while trying to remain constant for an individual - * network connection. Don't want the target network - * appl pinging around cpus thrashing the caches. - * So, we'll pick target cpus based on the destination - * TCP/IP port (socket). The (simple) alternative to - * this is to simply send all messages destined for - * particular domain to the same cpu (dcpu), but - * will lower our bandwidth and introduce a lot of - * contention on that target cpu. - */ - if (ehp->ether_type == ETHERTYPE_IP) { - ipha_t *ipha; - uchar_t *dstporta; - int hdr_length; - mblk_t *nmp = mp; - uchar_t *rptr = mp->b_rptr + - sizeof (struct ether_header); - if (nmp->b_wptr <= rptr) { - /* - * Only the ethernet header was contained - * in the first block. Check for the - * next packet. - */ - if ((nmp = mp->b_cont) != NULL) - rptr = nmp->b_rptr; - } - /* - * If we still haven't found the IP header packet - * then don't bother. Can't search forever. - */ - if (nmp && - ((nmp->b_wptr - rptr) >= IP_SIMPLE_HDR_LENGTH)) { - ipha = (ipha_t *)ALIGN32(rptr); - - ASSERT(DB_TYPE(mp) == M_DATA); - hdr_length = IPH_HDR_LENGTH(ipha); - - switch (ipha->ipha_protocol) { - case IPPROTO_UDP: - case IPPROTO_TCP: - /* - * TCP/UDP Protocol Header (1st word) - * 0 15,16 31 - * ----------------------- - * | src port | dst port | - * ----------------------- - */ - dstporta = (uchar_t *)ipha + hdr_length; - netports = *(uint_t *)dstporta; - dstporta += 2; - dstport = *(ushort_t *)dstporta; - break; - default: - break; - } - } - - } - IDN_ASSIGN_DCPU(dp, dstport); - - PR_DATA("%s:%d: (dstport %d) assigned %d\n", - proc, dst_domid, (int)dstport, dp->dcpu); - } -#endif /* XXX_DLPI_UNFRIENDLY */ - - data_rptr = mp->b_rptr; - - ASSERT(dp->dcpu != IDN_NIL_DCPU); - - ASSERT(idn_domain[dst_domid].dmbox.m_send); - - retry_reclaim = 1; -retry: - if ((dp->dio >= IDN_RECLAIM_MIN) || dp->diowanted) { - int reclaim_req; - /* - * Reclaim however many outstanding buffers - * there are up to IDN_RECLAIM_MAX if it's set. - */ - reclaim_req = dp->diowanted ? -1 : IDN_RECLAIM_MAX ? - MIN(dp->dio, IDN_RECLAIM_MAX) : dp->dio; - (void) idn_reclaim_mboxdata(dst_domid, channel, - reclaim_req); - } - - if (dp->dio >= IDN_WINDOW_EMAX) { - - if (lock_try(&dp->diocheck)) { - IDN_MSGTIMER_START(dst_domid, IDNP_DATA, 0, - idn_msg_waittime[IDNP_DATA], - &mt.mt_cookie); - /* - * We have exceeded the minimum window for - * outstanding I/O buffers to this domain. - * Need to start the MSG timer to check for - * possible response from remote domain. - * The remote domain may be hung. Send a - * wakeup! Specify all channels for given - * domain since we don't know precisely which - * is backed up (dio is global). - */ - IDNXDC(dst_domid, &mt, - (uint_t)dst_netaddr.net.chan, 0, 0, 0); - } - - /* - * Yikes! We have exceeded the maximum window - * which means no more packets going to remote - * domain until he frees some up. - */ - IDN_KSTAT_INC(sip, si_txmax); - IDN_KSTAT_INC(sip, si_macxmt_errors); - rv = IDNXMIT_DROP; - goto nocando; - } - - /* - * Allocate a SMR I/O buffer and send it. - */ - if (msglen == 0) { - /* - * A zero length messages is effectively a signal - * to just send an interrupt to the remote domain. - */ - IDN_MSGTIMER_START(dst_domid, IDNP_DATA, 0, - idn_msg_waittime[IDNP_DATA], - &mt.mt_cookie); - IDNXDC(dst_domid, &mt, - (uint_t)dst_netaddr.net.chan, 0, 0, 0); - } - for (; (msglen > 0) && mp; msglen -= xfersize) { - int xrv; - smr_offset_t bufoffset; -#ifdef DEBUG - int n_xfersize; -#endif /* DEBUG */ - - ASSERT(msglen <= IDN_DATA_SIZE); - xfersize = msglen; - - serrno = smr_buf_alloc(dst_domid, xfersize, &iobufp); - if (serrno) { - PR_DATA("%s:%d: failed to alloc SMR I/O buffer " - "(serrno = %d)\n", - proc, dst_domid, serrno); - /* - * Failure is either due to a timeout waiting - * for the master to give us a slab, OR the - * local domain exhausted its slab quota! - * In either case we'll have to bail from - * here and let higher layers decide what - * to do. - * We also could have had locking problems. - * A negative serrno indicates we lost the lock - * on dst_domid, so no need in dropping lock. - */ - - if (lock_try(&dp->diowanted) && retry_reclaim) { - /* - * We were the first to acquire the - * lock indicating that it wasn't - * set on entry to idn_send_data. - * So, let's go back and see if we - * can't reclaim some buffers and - * try again. - * It's very likely diowanted will be - * enough to prevent us from looping - * on retrying here, however to protect - * against the small window where a - * race condition might exist, we use - * the retry_reclaim flag so that we - * don't retry more than once. - */ - retry_reclaim = 0; - goto retry; - } - - rv = (serrno > 0) ? serrno : -serrno; - IDN_KSTAT_INC(sip, si_notbufs); - IDN_KSTAT_INC(sip, si_noxmtbuf); /* MIB II */ - switch (rv) { - case ENOMEM: - case EBUSY: - case ENOLCK: - case ETIMEDOUT: - case EDQUOT: - /* - * These are all transient conditions - * which should be recoverable over - * time. - */ - rv = IDNXMIT_REQUEUE; - break; - - default: - rv = IDNXMIT_DROP; - break; - } - goto nocando; - } - - lock_clear(&dp->diowanted); - - hdrp = IDN_BUF2HDR(iobufp); - bufoffset = (smr_offset_t)IDN_ALIGNPTR(sizeof (smr_pkthdr_t), - data_rptr); - /* - * If the alignment of bufoffset took us pass the - * length of a smr_pkthdr_t then we need to possibly - * lower xfersize since it was calulated based on - * a perfect alignment. However, if we're in DLPI - * mode then shouldn't be necessary since the length - * of the incoming packet (mblk) should have already - * taken into consideration this possible adjustment. - */ -#ifdef DEBUG - if (bufoffset != sizeof (smr_pkthdr_t)) - PR_DATA("%s:%d: offset ALIGNMENT (%lu -> %u) " - "(data_rptr = %p)\n", - proc, dst_domid, sizeof (smr_pkthdr_t), - bufoffset, (void *)data_rptr); - - n_xfersize = MIN(xfersize, (IDN_SMR_BUFSIZE - bufoffset)); - if (xfersize != n_xfersize) { - PR_DATA("%s:%d: xfersize ADJUST (%d -> %d)\n", - proc, dst_domid, xfersize, n_xfersize); - cmn_err(CE_WARN, "%s: ERROR (xfersize = %d, > " - "bufsize(%d)-bufoffset(%d) = %d)", - proc, xfersize, IDN_SMR_BUFSIZE, - bufoffset, - IDN_SMR_BUFSIZE - bufoffset); - } -#endif /* DEBUG */ - xfersize = MIN(xfersize, (int)(IDN_SMR_BUFSIZE - bufoffset)); - - iodatap = IDN_BUF2DATA(iobufp, bufoffset); - mp = idn_fill_buffer(iodatap, xfersize, mp, &data_rptr); - - hdrp->b_netaddr = dst_netaddr.netaddr; - hdrp->b_netports = netports; - hdrp->b_offset = bufoffset; - hdrp->b_length = xfersize; - hdrp->b_next = IDN_NIL_SMROFFSET; - hdrp->b_rawio = 0; - hdrp->b_cksum = IDN_CKSUM_PKT(hdrp); - - xrv = idn_send_mboxdata(dst_domid, sip, channel, iobufp); - if (xrv) { - /* - * Reclaim packet. - * Return error on this packet so it can be retried - * (putbq). Note that it should be safe to assume - * that this for-loop is only executed once when in - * DLPI mode and so no need to worry about fractured - * mblk packet. - */ - PR_DATA("%s:%d: DATA XFER to chan %d FAILED " - "(ret=%d)\n", - proc, dst_domid, channel, xrv); - (void) smr_buf_free(dst_domid, iobufp, xfersize); - - PR_DATA("%s:%d: (line %d) dec(dio) -> %d\n", - proc, dst_domid, __LINE__, dp->dio); - - rv = IDNXMIT_DROP; - IDN_KSTAT_INC(sip, si_macxmt_errors); - goto nocando; - } else { - pktcnt++; - /* - * Packet will get freed on a subsequent send - * when we reclaim buffers that the receivers - * has finished consuming. - */ - } - } - -#ifdef DEBUG - if (pktcnt > 1) - cmn_err(CE_WARN, - "%s: ERROR: sent multi-pkts (%d), len = %ld", - proc, pktcnt, orig_msglen); -#endif /* DEBUG */ - - PR_DATA("%s:%d: SENT %d packets (%d @ 0x%x)\n", - proc, dst_domid, pktcnt, dst_netaddr.net.chan, - dst_netaddr.net.netid); - - IDN_CHAN_LOCK_SEND(csp); - IDN_CHAN_SEND_DONE(csp); - IDN_CHAN_UNLOCK_SEND(csp); - - return (IDNXMIT_OKAY); - -nocando: - - if (csp) { - IDN_CHAN_LOCK_SEND(csp); - IDN_CHAN_SEND_DONE(csp); - IDN_CHAN_UNLOCK_SEND(csp); - } - - if (rv == IDNXMIT_REQUEUE) { - /* - * Better kick off monitor to check when - * it's ready to reenable the queues for - * this channel. - */ - idn_xmit_monitor_kickoff(channel); - } - - return (rv); -} - -/* - * Function to support local loopback testing of IDN driver. - * Primarily geared towards measuring stream-head and IDN driver - * overhead with respect to data messages. Setting idn_strhead_only - * allows routine to focus on stream-head overhead by simply putting - * the message straight to the 'next' queue of the destination - * read-queue. Current implementation puts the message directly to - * the read-queue thus sending the message right back to the IDN driver - * as though the data came in off the wire. No need to worry about - * any IDN layers attempting to ack data as that's normally handled - * by idnh_recv_data. - * - * dst_netaddr = destination port-n-addr on local domain. - * wq = write queue from whence message came. - * mp = the (data-only) message. - * - * Returns 0 Indicates data handled. - * errno EAGAIN indicates data can be retried. - * Other errno's indicate failure to handle. - */ -static int -idn_send_data_loopback(idn_netaddr_t dst_netaddr, queue_t *wq, mblk_t *mp) -{ - register struct idnstr *stp; - struct idn *sip; - int rv = 0; - procname_t proc = "idn_send_data_loopback"; - - if (dst_netaddr.net.netid != idn_domain[idn.localid].dnetid) { - PR_DATA("%s: dst_netaddr.net.netid 0x%x != local 0x%x\n", - proc, dst_netaddr.net.netid, - idn_domain[idn.localid].dnetid); - rv = EADDRNOTAVAIL; - goto done; - } - stp = (struct idnstr *)wq->q_ptr; - if (!stp || !stp->ss_rq) { - rv = EDESTADDRREQ; - goto done; - } - sip = stp->ss_sip; - - idndl_read(sip, mp); - rv = 0; - -done: - return (rv); -} - -/* - * Fill bufp with as much data as possible from the message pointed - * to by mp up to size bytes. - * Save our current read pointer in the variable parameter (data_rptrp) - * so we know where to start on the next go around. Don't want to - * bump the actual b_rptr in the mblk because the mblk may need to - * be reused, e.g. broadcast. - * Return the mblk pointer to the position we had to stop. - */ -static mblk_t * -idn_fill_buffer(caddr_t bufp, int size, mblk_t *mp, uchar_t **data_rptrp) -{ - int copysize; - - ASSERT(bufp && size); - - if (mp == NULL) - return (NULL); - - while ((size > 0) && mp) { - - copysize = MIN(mp->b_wptr - (*data_rptrp), size); - - if (copysize > 0) { - /* - * If there's data to copy, do it. - */ - bcopy((*data_rptrp), bufp, copysize); - (*data_rptrp) += copysize; - bufp += copysize; - size -= copysize; - } - if (mp->b_wptr <= (*data_rptrp)) { - /* - * If we emptied the mblk, then - * move on to the next one. - */ - for (mp = mp->b_cont; - mp && (mp->b_datap->db_type != M_DATA); - mp = mp->b_cont) - ; - if (mp) - *data_rptrp = mp->b_rptr; - } - } - return (mp); -} - -/* - * Messages received here do NOT arrive on a stream, but are - * instead handled via the idn_protocol_servers. This routine - * is effectively the job processor for the protocol servers. - */ -static void -idn_recv_proto(idn_protomsg_t *hp) -{ - int domid, cpuid; - int sync_lock = 0; - idn_domain_t *dp; - register uint_t mtype; - register uint_t msgtype, acktype; - idn_msgtype_t mt; - ushort_t dcookie, tcookie; - procname_t proc = "idn_recv_proto"; - - - if (idn.state == IDNGS_IGNORE) { - /* - * Fault injection to simulate non-responsive domain. - */ - return; - } - - domid = hp->m_domid; - cpuid = hp->m_cpuid; - msgtype = hp->m_msgtype; - acktype = hp->m_acktype; - dcookie = IDN_DCOOKIE(hp->m_cookie); - tcookie = IDN_TCOOKIE(hp->m_cookie); - /* - * msgtype = Is the type of message we received, - * e.g. nego, ack, nego+ack, etc. - * - * acktype = If we received a pure ack or nack - * then this variable is set to the - * type of message that was ack/nack'd. - */ - if ((mtype = msgtype & IDNP_MSGTYPE_MASK) == 0) { - /* - * Received a pure ack/nack. - */ - mtype = acktype & IDNP_MSGTYPE_MASK; - } - - if (!VALID_MSGTYPE(mtype)) { - PR_PROTO("%s:%d: ERROR: invalid message type (0x%x)\n", - proc, domid, mtype); - return; - } - if (!VALID_CPUID(cpuid)) { - PR_PROTO("%s:%d: ERROR: invalid cpuid (%d)\n", - proc, domid, cpuid); - return; - } - - /* - * No pure data packets should reach this level. - * Data+ack messages will reach here, but only - * for the purpose of stopping the timer which - * happens by default when this routine is called. - */ - ASSERT(msgtype != IDNP_DATA); - - /* - * We should never receive a request from ourself, - * except for commands in the case of broadcasts! - */ - if ((domid == idn.localid) && (mtype != IDNP_CMD)) { - char str[15]; - - inum2str(hp->m_msgtype, str); - - cmn_err(CE_WARN, - "IDN: 214: received message (%s[0x%x]) from self " - "(domid %d)", - str, hp->m_msgtype, domid); - return; - } - - IDN_SYNC_LOCK(); - /* - * Set a flag indicating whether we really need - * SYNC-LOCK. We'll drop it in a little bit if - * we really don't need it. - */ - switch (mtype) { - case IDNP_CON: - case IDNP_FIN: - case IDNP_NEGO: - sync_lock = 1; - break; - - default: - break; - } - - dp = &idn_domain[domid]; - IDN_DLOCK_EXCL(domid); - - /* - * The only messages we do _not_ check the cookie are: - * nego - * nego+ack - * fin - if received cookie is 0. - * fin+ack - if received cookie is 0. - * ack/fin - if received cookie is 0. - * nack/fin - if received cookie is 0. - */ - if (((msgtype & IDNP_MSGTYPE_MASK) != IDNP_NEGO) && - ((mtype != IDNP_FIN) || (dcookie && dp->dcookie_recv))) { - if (dp->dcookie_recv != dcookie) { - dp->dcookie_errcnt++; - if (dp->dcookie_err == 0) { - /* - * Set cookie error to prevent a - * possible flood of bogus cookies - * and thus error messages. - */ - dp->dcookie_err = 1; - cmn_err(CE_WARN, - "IDN: 215: invalid cookie (0x%x) " - "for message (0x%x) from domain %d", - dcookie, hp->m_msgtype, domid); - - PR_PROTO("%s:%d: received cookie (0x%x), " - "expected (0x%x) [errcnt = %d]\n", - proc, domid, dcookie, - dp->dcookie_recv, dp->dcookie_errcnt); - } - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - return; - } - } - dp->dcookie_err = 0; - IDN_GLOCK_EXCL(); - - idn_clear_awol(domid); - - IDN_GUNLOCK(); - if (!sync_lock) /* really don't need SYNC-LOCK past here */ - IDN_SYNC_UNLOCK(); - - /* - * Stop any timers that may have been outstanding for - * this domain, for this particular message type. - * Note that CFG timers are directly managed by - * config recv/send code. - */ - if ((mtype != IDNP_CFG) && (msgtype & IDNP_ACKNACK_MASK) && tcookie) { - IDN_MSGTIMER_STOP(domid, mtype, tcookie); - } - - /* - * Keep track of the last cpu to send us a message. - * If the domain has not yet been assigned, we'll need - * this cpuid in order to send back a respond. - */ - dp->dcpu_last = cpuid; - - mt.mt_mtype = (ushort_t)msgtype; - mt.mt_atype = (ushort_t)acktype; - mt.mt_cookie = tcookie; - - switch (mtype) { - case IDNP_NEGO: - (void) idn_recv_nego(domid, &mt, hp->m_xargs, dcookie); - break; - - case IDNP_CFG: - idn_recv_config(domid, &mt, hp->m_xargs); - break; - - case IDNP_CON: - (void) idn_recv_con(domid, &mt, hp->m_xargs); - break; - - case IDNP_FIN: - (void) idn_recv_fin(domid, &mt, hp->m_xargs); - break; - - case IDNP_CMD: - idn_recv_cmd(domid, &mt, hp->m_xargs); - break; - - case IDNP_DATA: - ASSERT(msgtype & IDNP_ACKNACK_MASK); - /* - * When doing the fast track we simply process - * possible nack error conditions. The actual - * processing of the SMR data buffer is taken - * care of in idnh_recv_dataack. When NOT doing - * the fast track, we do all the processing here - * in the protocol server. - */ - (void) idn_recv_data(domid, &mt, hp->m_xargs); - break; - - default: - /* - * Should be receiving 0 inum and 0 acknack. - */ -#ifdef DEBUG - cmn_err(CE_PANIC, -#else /* DEBUG */ - cmn_err(CE_WARN, -#endif /* DEBUG */ - /* CSTYLED */ - "IDN: 216: (0x%x)msgtype/(0x%x)acktype rcvd from " - /* CSTYLED */ - "domain %d", msgtype, acktype, domid); - break; - } - - IDN_DUNLOCK(domid); - /* - * All receiving routines are responsible for dropping drwlock. - */ - - if (sync_lock) - IDN_SYNC_UNLOCK(); -} - -/* - * Once the CONFIG state is hit we immediately blast out all - * of our config info. This guarantees that the CONFIG state - * effectively signifies that the sender has sent _all_ of - * their config info. - */ -static void -idn_send_config(int domid, int phase) -{ - idn_domain_t *dp; - int rv; - clock_t cfg_waittime = idn_msg_waittime[IDNP_CFG]; - procname_t proc = "idn_send_config"; - - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - dp = &idn_domain[domid]; - - ASSERT(dp->dstate == IDNDS_CONFIG); - - if (phase == 1) { - /* - * Reset stuff in dtmp to 0: - * dcfgphase - * dcksum - * dncfgitems - * dmaxnets - * dmboxpernet - */ - dp->dtmp = 0; - } - - if (dp->dcfgsnddone) { - if (!dp->dcfgrcvdone) { - IDN_MSGTIMER_START(domid, IDNP_CFG, 0, - cfg_waittime, NULL); - } - return; - } - - IDN_DLOCK_SHARED(idn.localid); - - PR_PROTO("%s:%d: sending %s config (phase %d)\n", - proc, domid, - idn_domain[idn.localid].dvote.v.master ? "MASTER" : "SLAVE", - phase); - - if (idn_domain[idn.localid].dvote.v.master) - rv = idn_send_master_config(domid, phase); - else - rv = idn_send_slave_config(domid, phase); - - IDN_DUNLOCK(idn.localid); - - if (rv >= 0) { - - if (rv == 1) { - dp->dcfgsnddone = 1; - PR_PROTO("%s:%d: SEND config DONE\n", proc, domid); - if (!dp->dcfgrcvdone) { - IDN_MSGTIMER_START(domid, IDNP_CFG, 0, - cfg_waittime, NULL); - } - } else { - IDN_MSGTIMER_START(domid, IDNP_CFG, 0, - cfg_waittime, NULL); - } - } -} - -/* - * Clear out the mailbox table. - * NOTE: This routine touches the SMR. - */ -static void -idn_reset_mboxtbl(idn_mboxtbl_t *mtp) -{ - int qi; - idn_mboxmsg_t *mp = &mtp->mt_queue[0]; - - qi = 0; - do { - mp[qi].ms_bframe = 0; - mp[qi].ms_owner = 0; - mp[qi].ms_flag = 0; - IDN_MMBOXINDEX_INC(qi); - } while (qi); -} - -static int -idn_get_mbox_config(int domid, int *mindex, smr_offset_t *mtable, - smr_offset_t *mdomain) -{ - idn_domain_t *dp, *ldp; - - dp = &idn_domain[domid]; - ldp = &idn_domain[idn.localid]; - - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - ASSERT(IDN_DLOCK_IS_SHARED(idn.localid)); - ASSERT(IDN_GET_MASTERID() != IDN_NIL_DOMID); - - /* - * Get SMR offset of receive mailbox assigned - * to respective domain. If I'm a slave then - * my dmbox.m_tbl will not have been assigned yet. - * Instead of sending the actual offset I send - * the master his assigned index. Since the - * master knows what offset it will assign to - * me he can determine his assigned (recv) mailbox - * based on the offset and given index. The local - * domain can also use this information once the - * dmbox.m_tbl is received to properly assign the - * correct mbox offset to the master. - */ - if (ldp->dmbox.m_tbl == NULL) { - /* - * Local domain has not yet been assigned a - * (recv) mailbox table. This must be the - * initial connection of this domain. - */ - ASSERT(dp->dvote.v.master && !ldp->dvote.v.master); - ASSERT(mindex); - *mindex = domid; - } else { - idn_mboxtbl_t *mtp; - - mtp = IDN_MBOXTBL_PTR(ldp->dmbox.m_tbl, domid); - - ASSERT(mdomain); - *mdomain = IDN_ADDR2OFFSET(mtp); - - if (ldp->dvote.v.master) { - /* - * Need to calculate mailbox table to - * assign to the given domain. Since - * I'm the master his mailbox is in - * the (all-domains) mailbox table. - */ - mtp = IDN_MBOXAREA_BASE(idn.mboxarea, domid); - ASSERT(mtable); - *mtable = IDN_ADDR2OFFSET(mtp); - - dp->dmbox.m_tbl = mtp; - } - } - - return (0); -} - -/* - * RETURNS: - * 1 Unexpected/unnecessary phase. - * 0 Successfully handled, timer needed. - */ -static int -idn_send_master_config(int domid, int phase) -{ - idn_cfgsubtype_t cfg_subtype; - int rv = 0; - idn_domain_t *dp, *ldp; - idn_msgtype_t mt; - int nmcadr; - uint_t barpfn, larpfn; - uint_t cpus_u32, cpus_l32; - uint_t mcadr[3]; - smr_offset_t mbox_table, mbox_domain; - register int b, p, m; - procname_t proc = "idn_send_master_config"; - - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - ASSERT(IDN_DLOCK_IS_SHARED(idn.localid)); - - dp = &idn_domain[domid]; - ldp = &idn_domain[idn.localid]; - - ASSERT(dp->dstate == IDNDS_CONFIG); - ASSERT(dp->dvote.v.master == 0); - ASSERT(ldp->dvote.v.master == 1); - - mt.mt_mtype = IDNP_CFG; - mt.mt_atype = 0; - mt.mt_cookie = 0; - m = 0; - mcadr[0] = mcadr[1] = mcadr[2] = 0; - cfg_subtype.val = 0; - - switch (phase) { - - case 1: - mbox_table = mbox_domain = IDN_NIL_SMROFFSET; - (void) idn_get_mbox_config(domid, NULL, &mbox_table, - &mbox_domain); - /* - * ---------------------------------------------------- - * Send: SLABSIZE, DATAMBOX.DOMAIN, DATAMBOX.TABLE - * ---------------------------------------------------- - */ - cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_SIZE, - IDNCFGARG_SIZE_SLAB); - cfg_subtype.param.p[1] = IDN_CFGPARAM(IDNCFG_DATAMBOX, - IDNCFGARG_DATAMBOX_DOMAIN); - cfg_subtype.param.p[2] = IDN_CFGPARAM(IDNCFG_DATAMBOX, - IDNCFGARG_DATAMBOX_TABLE); - cfg_subtype.info.num = 3; - cfg_subtype.info.phase = phase; - dp->dcfgphase = phase; - - ASSERT(mbox_domain != IDN_NIL_SMROFFSET); - ASSERT(mbox_table != IDN_NIL_SMROFFSET); - - PR_PROTO("%s:%d:%d: sending SLABSIZE (%d), " - "DATAMBOX.DOMAIN (0x%x), DATAMBOX.TABLE (0x%x)\n", - proc, domid, phase, IDN_SLAB_BUFCOUNT, mbox_domain, - mbox_table); - - IDNXDC(domid, &mt, cfg_subtype.val, IDN_SLAB_BUFCOUNT, - mbox_domain, mbox_table); - break; - - case 2: - barpfn = idn.smr.locpfn; - larpfn = barpfn + (uint_t)btop(MB2B(IDN_SMR_SIZE)); - /* - * ---------------------------------------------------- - * Send: NETID, BARLAR - * ---------------------------------------------------- - */ - cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_NETID, 0); - cfg_subtype.param.p[1] = IDN_CFGPARAM(IDNCFG_BARLAR, - IDNCFGARG_BARLAR_BAR); - cfg_subtype.param.p[2] = IDN_CFGPARAM(IDNCFG_BARLAR, - IDNCFGARG_BARLAR_LAR); - cfg_subtype.info.num = 3; - cfg_subtype.info.phase = phase; - dp->dcfgphase = phase; - - PR_PROTO("%s:%d:%d: sending NETID (%d), " - "BARPFN/LARPFN (0x%x/0x%x)\n", - proc, domid, phase, ldp->dnetid, barpfn, larpfn); - - IDNXDC(domid, &mt, cfg_subtype.val, - (uint_t)ldp->dnetid, barpfn, larpfn); - break; - - case 3: - nmcadr = ldp->dhw.dh_nmcadr; - cpus_u32 = UPPER32_CPUMASK(ldp->dcpuset); - cpus_l32 = LOWER32_CPUMASK(ldp->dcpuset); - /* - * ---------------------------------------------------- - * Send: CPUSET, NMCADR - * ---------------------------------------------------- - */ - cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_CPUSET, - IDNCFGARG_CPUSET_UPPER); - cfg_subtype.param.p[1] = IDN_CFGPARAM(IDNCFG_CPUSET, - IDNCFGARG_CPUSET_LOWER); - cfg_subtype.param.p[2] = IDN_CFGPARAM(IDNCFG_NMCADR, 0); - cfg_subtype.info.num = 3; - cfg_subtype.info.phase = phase; - dp->dcfgphase = phase; - - PR_PROTO("%s:%d:%d: sending CPUSET (0x%x.%x), NMCADR (%d)\n", - proc, domid, phase, cpus_u32, cpus_l32, nmcadr); - - IDNXDC(domid, &mt, cfg_subtype.val, - cpus_u32, cpus_l32, nmcadr); - break; - - case 4: - /* - * ---------------------------------------------------- - * Send: BOARDSET, MTU, BUFSIZE - * ---------------------------------------------------- - */ - cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_BOARDSET, 0); - cfg_subtype.param.p[1] = IDN_CFGPARAM(IDNCFG_SIZE, - IDNCFGARG_SIZE_MTU); - cfg_subtype.param.p[2] = IDN_CFGPARAM(IDNCFG_SIZE, - IDNCFGARG_SIZE_BUF); - cfg_subtype.info.num = 3; - cfg_subtype.info.phase = phase; - dp->dcfgphase = phase; - - PR_PROTO("%s:%d:%d: sending BOARDSET (0x%x), MTU (0x%lx), " - "BUFSIZE (0x%x)\n", proc, domid, phase, - ldp->dhw.dh_boardset, IDN_MTU, IDN_SMR_BUFSIZE); - - IDNXDC(domid, &mt, cfg_subtype.val, - ldp->dhw.dh_boardset, IDN_MTU, IDN_SMR_BUFSIZE); - break; - - case 5: - /* - * ---------------------------------------------------- - * Send: MAXNETS, MBOXPERNET, CKSUM - * ---------------------------------------------------- - */ - cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_DATASVR, - IDNCFGARG_DATASVR_MAXNETS); - cfg_subtype.param.p[1] = IDN_CFGPARAM(IDNCFG_DATASVR, - IDNCFGARG_DATASVR_MBXPERNET); - cfg_subtype.param.p[2] = IDN_CFGPARAM(IDNCFG_OPTIONS, - IDNCFGARG_CHECKSUM); - cfg_subtype.info.num = 3; - cfg_subtype.info.phase = phase; - dp->dcfgphase = phase; - - PR_PROTO("%s:%d:%d: sending MAXNETS (%d), " - "MBOXPERNET (%d), CKSUM (%d)\n", - proc, domid, phase, - IDN_MAX_NETS, IDN_MBOX_PER_NET, - IDN_CHECKSUM); - - IDNXDC(domid, &mt, cfg_subtype.val, - IDN_MAX_NETS, IDN_MBOX_PER_NET, IDN_CHECKSUM); - break; - - case 6: - /* - * ---------------------------------------------------- - * Send: NWRSIZE (piggyback on MCADRs) - * ---------------------------------------------------- - */ - cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_SIZE, - IDNCFGARG_SIZE_NWR); - mcadr[0] = IDN_NWR_SIZE; - m = 1; - - /*FALLTHROUGH*/ - - default: /* case 7 and above */ - /* - * ---------------------------------------------------- - * Send: MCADR's - * ---------------------------------------------------- - * First need to figure how many we've already sent - * based on what phase of CONFIG we're in. - * ---------------------------------------------------- - */ - if (phase > 6) { - p = ((phase - 7) * 3) + 2; - for (b = 0; (b < MAX_BOARDS) && (p > 0); b++) - if (ldp->dhw.dh_mcadr[b]) - p--; - } else { - b = 0; - } - - for (; (b < MAX_BOARDS) && (m < 3); b++) { - if (ldp->dhw.dh_mcadr[b] == 0) - continue; - mcadr[m] = ldp->dhw.dh_mcadr[b]; - cfg_subtype.param.p[m] = IDN_CFGPARAM(IDNCFG_MCADR, b); - m++; - } - if (m > 0) { - if (phase == 6) { - PR_PROTO("%s:%d:%d: sending NWRSIZE (%d), " - "MCADRs (0x%x, 0x%x)\n", - proc, domid, phase, - mcadr[0], mcadr[1], mcadr[2]); - } else { - PR_PROTO("%s:%d:%d: sending MCADRs " - "(0x%x, 0x%x, 0x%x)\n", - proc, domid, phase, - mcadr[0], mcadr[1], mcadr[2]); - } - cfg_subtype.info.num = m; - cfg_subtype.info.phase = phase; - dp->dcfgphase = phase; - - IDNXDC(domid, &mt, cfg_subtype.val, - mcadr[0], mcadr[1], mcadr[2]); - } else { - rv = 1; - } - break; - } - - return (rv); -} - -/* - * RETURNS: - * 1 Unexpected/unnecessary phase. - * 0 Successfully handled. - */ -static int -idn_send_slave_config(int domid, int phase) -{ - idn_cfgsubtype_t cfg_subtype; - int rv = 0; - idn_domain_t *dp, *ldp; - smr_offset_t mbox_domain; - idn_msgtype_t mt; - int mbox_index; - uint_t cpus_u32, cpus_l32; - procname_t proc = "idn_send_slave_config"; - - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - ASSERT(IDN_DLOCK_IS_SHARED(idn.localid)); - - mt.mt_mtype = IDNP_CFG; - mt.mt_atype = 0; - dp = &idn_domain[domid]; - ldp = &idn_domain[idn.localid]; - - ASSERT(dp->dstate == IDNDS_CONFIG); - ASSERT(ldp->dvote.v.master == 0); - - switch (phase) { - - case 1: - mbox_index = IDN_NIL_DOMID; - mbox_domain = IDN_NIL_SMROFFSET; - (void) idn_get_mbox_config(domid, &mbox_index, NULL, - &mbox_domain); - /* - * ---------------------------------------------------- - * Send: DATAMBOX.DOMAIN or DATAMBOX.INDEX, - * DATASVR.MAXNETS, DATASVR.MBXPERNET - * ---------------------------------------------------- - */ - cfg_subtype.val = 0; - if (mbox_index == IDN_NIL_DOMID) { - ASSERT(mbox_domain != IDN_NIL_SMROFFSET); - cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_DATAMBOX, - IDNCFGARG_DATAMBOX_DOMAIN); - } else { - /* - * Should only be sending Index to - * the master and not another slave. - */ - ASSERT(dp->dvote.v.master); - ASSERT(mbox_domain == IDN_NIL_SMROFFSET); - cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_DATAMBOX, - IDNCFGARG_DATAMBOX_INDEX); - } - cfg_subtype.param.p[1] = IDN_CFGPARAM(IDNCFG_DATASVR, - IDNCFGARG_DATASVR_MAXNETS); - cfg_subtype.param.p[2] = IDN_CFGPARAM(IDNCFG_DATASVR, - IDNCFGARG_DATASVR_MBXPERNET); - cfg_subtype.info.num = 3; - cfg_subtype.info.phase = phase; - dp->dcfgphase = phase; - - PR_PROTO("%s:%d:%d: sending DATAMBOX.%s (0x%x), " - "MAXNETS (%d), MBXPERNET (%d)\n", - proc, domid, phase, - (IDN_CFGPARAM_ARG(cfg_subtype.param.p[0]) - == IDNCFGARG_DATAMBOX_INDEX) ? "INDEX" : "DOMAIN", - (mbox_index == IDN_NIL_DOMID) ? mbox_domain : mbox_index, - IDN_MAX_NETS, IDN_MBOX_PER_NET); - - IDNXDC(domid, &mt, cfg_subtype.val, - ((mbox_index == IDN_NIL_DOMID) ? mbox_domain : mbox_index), - IDN_MAX_NETS, IDN_MBOX_PER_NET); - break; - - case 2: - cpus_u32 = UPPER32_CPUMASK(ldp->dcpuset); - cpus_l32 = LOWER32_CPUMASK(ldp->dcpuset); - /* - * ---------------------------------------------------- - * Send: NETID, CPUSET - * ---------------------------------------------------- - */ - cfg_subtype.val = 0; - cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_NETID, 0); - cfg_subtype.param.p[1] = IDN_CFGPARAM(IDNCFG_CPUSET, - IDNCFGARG_CPUSET_UPPER); - cfg_subtype.param.p[2] = IDN_CFGPARAM(IDNCFG_CPUSET, - IDNCFGARG_CPUSET_LOWER); - cfg_subtype.info.num = 3; - cfg_subtype.info.phase = phase; - dp->dcfgphase = phase; - - PR_PROTO("%s:%d:%d: sending NETID (%d), " - "CPUSET (0x%x.%x)\n", proc, domid, phase, - ldp->dnetid, cpus_u32, cpus_l32); - - IDNXDC(domid, &mt, cfg_subtype.val, - (uint_t)ldp->dnetid, cpus_u32, cpus_l32); - break; - - case 3: - /* - * ---------------------------------------------------- - * Send: BOARDSET, MTU, BUFSIZE - * ---------------------------------------------------- - */ - cfg_subtype.val = 0; - cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_BOARDSET, 0); - cfg_subtype.param.p[1] = IDN_CFGPARAM(IDNCFG_SIZE, - IDNCFGARG_SIZE_MTU); - cfg_subtype.param.p[2] = IDN_CFGPARAM(IDNCFG_SIZE, - IDNCFGARG_SIZE_BUF); - cfg_subtype.info.num = 3; - cfg_subtype.info.phase = phase; - dp->dcfgphase = phase; - - PR_PROTO("%s:%d:%d: sending BOARDSET (0x%x), MTU (0x%lx), " - "BUFSIZE (0x%x)\n", - proc, domid, phase, ldp->dhw.dh_boardset, IDN_MTU, - IDN_SMR_BUFSIZE); - - IDNXDC(domid, &mt, cfg_subtype.val, - ldp->dhw.dh_boardset, IDN_MTU, IDN_SMR_BUFSIZE); - break; - - case 4: - /* - * ---------------------------------------------------- - * Send: SLABSIZE, OPTIONS.CHECKSUM, NWR_SIZE - * ---------------------------------------------------- - */ - cfg_subtype.val = 0; - cfg_subtype.param.p[0] = IDN_CFGPARAM(IDNCFG_SIZE, - IDNCFGARG_SIZE_SLAB); - cfg_subtype.param.p[1] = IDN_CFGPARAM(IDNCFG_OPTIONS, - IDNCFGARG_CHECKSUM); - cfg_subtype.param.p[2] = IDN_CFGPARAM(IDNCFG_SIZE, - IDNCFGARG_SIZE_NWR); - cfg_subtype.info.num = 3; - cfg_subtype.info.phase = phase; - dp->dcfgphase = phase; - - PR_PROTO("%s:%d:%d: sending SLABSIZE (%d), CKSUM (%d), " - "NWRSIZE (%d)\n", - proc, domid, phase, IDN_SLAB_BUFCOUNT, - IDN_CHECKSUM, IDN_NWR_SIZE); - - IDNXDC(domid, &mt, cfg_subtype.val, - IDN_SLAB_BUFCOUNT, IDN_CHECKSUM, IDN_NWR_SIZE); - break; - - default: - rv = 1; - break; - } - - return (rv); -} - -#define CFG_FATAL ((uint_t)-1) /* reset link */ -#define CFG_CONTINUE 0x0000 /* looking for more */ -#define CFG_DONE 0x0001 /* got everything expected */ -#define CFG_ERR_MTU 0x0002 -#define CFG_ERR_BUF 0x0004 -#define CFG_ERR_SLAB 0x0008 -#define CFG_ERR_NWR 0x0010 -#define CFG_ERR_NETS 0x0020 -#define CFG_ERR_MBOX 0x0040 -#define CFG_ERR_NMCADR 0x0080 -#define CFG_ERR_MCADR 0x0100 -#define CFG_ERR_CKSUM 0x0200 -#define CFG_ERR_SMR 0x0400 -#define CFG_MAX_ERRORS 16 - -#define CFGERR2IDNKERR(ce) \ - (((ce) & CFG_ERR_MTU) ? IDNKERR_CONFIG_MTU : \ - ((ce) & CFG_ERR_BUF) ? IDNKERR_CONFIG_BUF : \ - ((ce) & CFG_ERR_SLAB) ? IDNKERR_CONFIG_SLAB : \ - ((ce) & CFG_ERR_NWR) ? IDNKERR_CONFIG_NWR : \ - ((ce) & CFG_ERR_NETS) ? IDNKERR_CONFIG_NETS : \ - ((ce) & CFG_ERR_MBOX) ? IDNKERR_CONFIG_MBOX : \ - ((ce) & CFG_ERR_NMCADR) ? IDNKERR_CONFIG_NMCADR : \ - ((ce) & CFG_ERR_MCADR) ? IDNKERR_CONFIG_MCADR : \ - ((ce) & CFG_ERR_CKSUM) ? IDNKERR_CONFIG_CKSUM : \ - ((ce) & CFG_ERR_SMR) ? IDNKERR_CONFIG_SMR : 0) - -#define CFGERR2FINARG(ce) \ - (((ce) & CFG_ERR_MTU) ? IDNFIN_ARG_CFGERR_MTU : \ - ((ce) & CFG_ERR_BUF) ? IDNFIN_ARG_CFGERR_BUF : \ - ((ce) & CFG_ERR_SLAB) ? IDNFIN_ARG_CFGERR_SLAB : \ - ((ce) & CFG_ERR_NWR) ? IDNFIN_ARG_CFGERR_NWR : \ - ((ce) & CFG_ERR_NETS) ? IDNFIN_ARG_CFGERR_NETS : \ - ((ce) & CFG_ERR_MBOX) ? IDNFIN_ARG_CFGERR_MBOX : \ - ((ce) & CFG_ERR_NMCADR) ? IDNFIN_ARG_CFGERR_NMCADR : \ - ((ce) & CFG_ERR_MCADR) ? IDNFIN_ARG_CFGERR_MCADR : \ - ((ce) & CFG_ERR_CKSUM) ? IDNFIN_ARG_CFGERR_CKSUM : \ - ((ce) & CFG_ERR_SMR) ? IDNFIN_ARG_CFGERR_SMR : IDNFIN_ARG_NONE) - -/* - * Called when some CFG messages arrive. We use dncfgitems to count the - * total number of items received so far since we'll receive multiple CFG - * messages during the CONFIG phase. Note that dncfgitems is initialized - * in idn_send_config. - */ -static void -idn_recv_config(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - uint_t msg = mtp->mt_mtype; - uint_t rv, rv_expected, rv_actual; - int pnum; - int phase; - register int p; - register int c; - idn_mainmbox_t *mmp; - register uint_t subtype, subtype_arg; - idn_domain_t *dp; - int index; - idn_domain_t *ldp = &idn_domain[idn.localid]; - idn_mboxtbl_t *mbtp; - idn_cfgsubtype_t cfg_subtype; - idn_xdcargs_t cfg_arg; - idn_msgtype_t mt; - idnsb_error_t idnerr; - procname_t proc = "idn_recv_config"; - - ASSERT(domid != idn.localid); - - GET_XARGS(xargs, &cfg_subtype.val, &cfg_arg[0], &cfg_arg[1], - &cfg_arg[2]); - cfg_arg[3] = 0; - - dp = &idn_domain[domid]; - - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - if (dp->dstate != IDNDS_CONFIG) { - /* - * Not ready to receive config info. - * Drop whatever he sent us. Let the - * timer continue and timeout if needed. - */ - PR_PROTO("%s:%d: WARNING state(%s) != CONFIG\n", - proc, domid, idnds_str[dp->dstate]); - return; - } - - if ((msg & IDNP_ACKNACK_MASK) || dp->dcfgsnddone) { - IDN_MSGTIMER_STOP(domid, IDNP_CFG, 0); - } - - if (msg & IDNP_ACKNACK_MASK) { - /* - * ack/cfg - */ - phase = GET_XARGS_CFG_PHASE(xargs); - - PR_PROTO("%s:%d: received ACK for CFG phase %d\n", - proc, domid, phase); - if (phase != (int)dp->dcfgphase) { - /* - * Phase is not what we were - * expecting. Something got lost - * in the shuffle. Restart the - * timer and let it timeout if necessary - * and reestablish the connection. - */ - IDN_MSGTIMER_START(domid, IDNP_CFG, dp->dcfgphase, - idn_msg_waittime[IDNP_CFG], NULL); - } else { - idn_send_config(domid, phase + 1); - - if (dp->dcfgsnddone && dp->dcfgrcvdone) { - IDN_DUNLOCK(domid); - IDN_SYNC_LOCK(); - IDN_DLOCK_EXCL(domid); - if (dp->dstate == IDNDS_CONFIG) { - dp->dxp = &xphase_con; - IDN_XSTATE_TRANSITION(dp, IDNXS_PEND); - bzero(xargs, sizeof (xargs)); - - (void) idn_xphase_transition(domid, - NULL, xargs); - } - IDN_SYNC_UNLOCK(); - } - } - return; - } - - pnum = (int)cfg_subtype.info.num; - phase = (int)cfg_subtype.info.phase; - - for (p = 0; p < pnum; p++) { - int board; -#ifdef DEBUG - uint_t val; - char *str; - - val = 0; - str = NULL; -#define RCVCFG(s, v) { str = (s); val = (v); } -#else -#define RCVCFG(s, v) {} -#endif /* DEBUG */ - - subtype = IDN_CFGPARAM_TYPE(cfg_subtype.param.p[p]); - subtype_arg = IDN_CFGPARAM_ARG(cfg_subtype.param.p[p]); - - switch (subtype) { - - case IDNCFG_BARLAR: - IDN_GLOCK_EXCL(); - switch (subtype_arg) { - - case IDNCFGARG_BARLAR_BAR: - if (idn.smr.rempfn == PFN_INVALID) { - idn.smr.rempfn = (pfn_t)cfg_arg[p]; - dp->dncfgitems++; - RCVCFG("BARLAR_BAR", cfg_arg[p]); - } - break; - - case IDNCFGARG_BARLAR_LAR: - if (idn.smr.rempfnlim == PFN_INVALID) { - idn.smr.rempfnlim = (pfn_t)cfg_arg[p]; - dp->dncfgitems++; - RCVCFG("BARLAR_LAR", cfg_arg[p]); - } - break; - - default: - cmn_err(CE_WARN, - "IDN 217: unknown CFGARG type (%d) " - "from domain %d", - subtype_arg, domid); - break; - } - IDN_GUNLOCK(); - break; - - case IDNCFG_MCADR: - board = subtype_arg; - if ((board >= 0) && (board < MAX_BOARDS) && - (dp->dhw.dh_mcadr[board] == 0)) { - dp->dhw.dh_mcadr[board] = cfg_arg[p]; - dp->dncfgitems++; - RCVCFG("MCADR", cfg_arg[p]); - } - break; - - case IDNCFG_NMCADR: - if (dp->dhw.dh_nmcadr == 0) { - dp->dhw.dh_nmcadr = cfg_arg[p]; - dp->dncfgitems++; - RCVCFG("NMCADR", cfg_arg[p]); - } - break; - - case IDNCFG_CPUSET: - switch (subtype_arg) { - - case IDNCFGARG_CPUSET_UPPER: - { - cpuset_t tmpset; - - MAKE64_CPUMASK(tmpset, cfg_arg[p], 0); - CPUSET_OR(dp->dcpuset, tmpset); - dp->dncfgitems++; - RCVCFG("CPUSET_UPPER", cfg_arg[p]); - break; - } - case IDNCFGARG_CPUSET_LOWER: - { - cpuset_t tmpset; - - MAKE64_CPUMASK(tmpset, 0, cfg_arg[p]); - CPUSET_OR(dp->dcpuset, tmpset); - dp->dncfgitems++; - RCVCFG("CPUSET_LOWER", cfg_arg[p]); - break; - } - default: - ASSERT(0); - break; - } - break; - - case IDNCFG_NETID: - if (dp->dnetid == (ushort_t)-1) { - dp->dnetid = (ushort_t)cfg_arg[p]; - dp->dncfgitems++; - RCVCFG("NETID", cfg_arg[p]); - } - break; - - case IDNCFG_BOARDSET: - if ((dp->dhw.dh_boardset & cfg_arg[p]) - == dp->dhw.dh_boardset) { - /* - * Boardset better include what we - * already know about. - */ - dp->dhw.dh_boardset = cfg_arg[p]; - dp->dncfgitems++; - RCVCFG("BOARDSET", cfg_arg[p]); - } - break; - - case IDNCFG_SIZE: - switch (subtype_arg) { - - case IDNCFGARG_SIZE_MTU: - if (dp->dmtu == 0) { - dp->dmtu = cfg_arg[p]; - dp->dncfgitems++; - RCVCFG("MTU", cfg_arg[p]); - } - break; - - case IDNCFGARG_SIZE_BUF: - if (dp->dbufsize == 0) { - dp->dbufsize = cfg_arg[p]; - dp->dncfgitems++; - RCVCFG("BUFSIZE", cfg_arg[p]); - } - break; - - case IDNCFGARG_SIZE_SLAB: - if (dp->dslabsize == 0) { - dp->dslabsize = (short)cfg_arg[p]; - dp->dncfgitems++; - RCVCFG("SLABSIZE", cfg_arg[p]); - } - break; - - case IDNCFGARG_SIZE_NWR: - if (dp->dnwrsize == 0) { - dp->dnwrsize = (short)cfg_arg[p]; - dp->dncfgitems++; - RCVCFG("NWRSIZE", cfg_arg[p]); - } - break; - - default: - ASSERT(0); - break; - } - break; - - case IDNCFG_DATAMBOX: - switch (subtype_arg) { - - case IDNCFGARG_DATAMBOX_TABLE: - if (ldp->dmbox.m_tbl || - !dp->dvote.v.master || - !VALID_NWROFFSET(cfg_arg[p], 4)) { - /* - * Only a master should be - * sending us a datambox table. - */ - break; - } - IDN_DLOCK_EXCL(idn.localid); - ldp->dmbox.m_tbl = (idn_mboxtbl_t *) - IDN_OFFSET2ADDR(cfg_arg[p]); - IDN_DUNLOCK(idn.localid); - dp->dncfgitems++; - RCVCFG("DATAMBOX.TABLE", cfg_arg[p]); - break; - - case IDNCFGARG_DATAMBOX_DOMAIN: - if (dp->dmbox.m_send->mm_smr_mboxp || - !VALID_NWROFFSET(cfg_arg[p], 4)) - break; - mbtp = (idn_mboxtbl_t *) - IDN_OFFSET2ADDR(cfg_arg[p]); - mmp = dp->dmbox.m_send; - for (c = 0; c < IDN_MAX_NETS; c++) { - - mutex_enter(&mmp[c].mm_mutex); - mmp[c].mm_smr_mboxp = mbtp; - mutex_exit(&mmp[c].mm_mutex); - - IDN_MBOXTBL_PTR_INC(mbtp); - } - if (c <= 0) - break; - dp->dncfgitems++; - RCVCFG("DATAMBOX.DOMAIN", cfg_arg[p]); - break; - - case IDNCFGARG_DATAMBOX_INDEX: - if (!ldp->dvote.v.master || - dp->dmbox.m_send->mm_smr_mboxp) { - /* - * If I'm not the master then - * I can't handle processing a - * mailbox index. - * OR, if I already have the send - * mailbox, I'm done with this - * config item. - */ - break; - } - ASSERT(dp->dmbox.m_tbl); - index = (int)cfg_arg[p]; - /* - * The given index is the local domain's - * index into the remote domain's mailbox - * table that contains the mailbox that - * remote domain wants the local domain to - * use as the send mailbox for messages - * destined for the remote domain. - * I.e. from the remote domain's - * perspective, this is his receive - * mailbox. - */ - mbtp = IDN_MBOXTBL_PTR(dp->dmbox.m_tbl, index); - mmp = dp->dmbox.m_send; - for (c = 0; c < IDN_MAX_NETS; c++) { - - mutex_enter(&mmp[c].mm_mutex); - mmp[c].mm_smr_mboxp = mbtp; - mutex_exit(&mmp[c].mm_mutex); - - IDN_MBOXTBL_PTR_INC(mbtp); - } - if (c <= 0) - break; - dp->dncfgitems++; - RCVCFG("DATAMBOX.INDEX", cfg_arg[p]); - break; - - default: - ASSERT(0); - break; - } - break; - - case IDNCFG_DATASVR: - switch (subtype_arg) { - - case IDNCFGARG_DATASVR_MAXNETS: - if (dp->dmaxnets) - break; - dp->dmaxnets = (uint_t)(cfg_arg[p] & 0x3f); - dp->dncfgitems++; - RCVCFG("DATASVR.MAXNETS", cfg_arg[p]); - break; - - case IDNCFGARG_DATASVR_MBXPERNET: - if (dp->dmboxpernet) - break; - dp->dmboxpernet = (uint_t)(cfg_arg[p] & 0x1ff); - dp->dncfgitems++; - RCVCFG("DATASVR.MBXPERNET", cfg_arg[p]); - break; - - default: - ASSERT(0); - break; - } - break; - - case IDNCFG_OPTIONS: - switch (subtype_arg) { - - case IDNCFGARG_CHECKSUM: - if (dp->dcksum) - break; - if ((cfg_arg[p] & 0xff) == 0) - dp->dcksum = 1; /* off */ - else - dp->dcksum = 2; /* on */ - dp->dncfgitems++; - RCVCFG("OPTIONS.CHECKSUM", cfg_arg[p]); - break; - - default: - ASSERT(0); - break; - } - - default: - break; - } -#ifdef DEBUG - PR_PROTO("%s:%d: received %s (0x%x)\n", - proc, domid, str ? str : "<empty>", val); -#endif /* DEBUG */ - } - - mt.mt_mtype = IDNP_ACK; - mt.mt_atype = IDNP_CFG; - mt.mt_cookie = mtp->mt_cookie; - CLR_XARGS(cfg_arg); - SET_XARGS_CFG_PHASE(cfg_arg, phase); - idn_send_acknack(domid, &mt, cfg_arg); - - rv_expected = rv_actual = 0; - - if (dp->dvote.v.master == 0) { - /* - * Remote domain is a slave, check if we've received - * all that we were expecting, and if so transition to - * the next state. - */ - rv = idn_check_slave_config(domid, &rv_expected, &rv_actual); - } else { - /* - * Remote domain is a master, check if this slave has - * received all that it was expecting, and if so - * transition to the next state. - */ - rv = idn_check_master_config(domid, &rv_expected, &rv_actual); - } - - switch (rv) { - case CFG_DONE: - /* - * All config info received that was expected, wrap up. - */ - if (!idn_recv_config_done(domid) && dp->dvote.v.master) { - IDN_DLOCK_EXCL(idn.localid); - ldp->dvote.v.connected = 1; - IDN_DUNLOCK(idn.localid); - } - break; - - case CFG_CONTINUE: - /* - * If we're not done sending our own config, then - * there's no need to set a timer since one will - * automatically be set when we send a config - * message waiting for an acknowledgement. - */ - if (dp->dcfgsnddone) { - /* - * We haven't yet received all the config - * information we were expecting. Need to - * restart CFG timer if we've sent everything.. - */ - IDN_MSGTIMER_START(domid, IDNP_CFG, 0, - idn_msg_waittime[IDNP_CFG], NULL); - } - break; - - case CFG_FATAL: - /* - * Fatal error occurred during config exchange. - * We need to shutdown connection in this - * case, so initiate a (non-relink) FIN. - * so let's get the show on the road. - */ - IDN_DUNLOCK(domid); - IDN_SYNC_LOCK(); - IDN_DLOCK_EXCL(domid); - /* - * If the state has changed from CONFIG - * then somebody else has taken over - * control of this domain so we can just - * bail out. - */ - if (dp->dstate == IDNDS_CONFIG) { - INIT_IDNKERR(&idnerr); - SET_IDNKERR_ERRNO(&idnerr, EPROTO); - SET_IDNKERR_IDNERR(&idnerr, IDNKERR_CONFIG_FATAL); - SET_IDNKERR_PARAM0(&idnerr, domid); - idn_update_op(IDNOP_ERROR, DOMAINSET(domid), &idnerr); - /* - * Keep this guy around so we can try again. - */ - DOMAINSET_ADD(idn.domset.ds_relink, domid); - IDN_HISTORY_LOG(IDNH_RELINK, domid, dp->dstate, - idn.domset.ds_relink); - (void) idn_disconnect(domid, IDNFIN_NORMAL, - IDNFIN_ARG_CFGERR_FATAL, - IDNFIN_SYNC_NO); - } - IDN_SYNC_UNLOCK(); - break; - - default: /* parameter conflict */ - IDN_DUNLOCK(domid); - IDN_SYNC_LOCK(); - IDN_DLOCK_EXCL(domid); - if (dp->dstate != IDNDS_CONFIG) { - /* - * Hmmm...changed in the short period - * we had dropped the lock, oh well. - */ - IDN_SYNC_UNLOCK(); - break; - } - c = 0; - for (p = 0; p < CFG_MAX_ERRORS; p++) - if (rv & (1 << p)) - c++; - INIT_IDNKERR(&idnerr); - SET_IDNKERR_ERRNO(&idnerr, EINVAL); - SET_IDNKERR_PARAM0(&idnerr, domid); - if (c > 1) { - SET_IDNKERR_IDNERR(&idnerr, IDNKERR_CONFIG_MULTIPLE); - SET_IDNKERR_PARAM1(&idnerr, c); - } else { - SET_IDNKERR_IDNERR(&idnerr, CFGERR2IDNKERR(rv)); - SET_IDNKERR_PARAM1(&idnerr, rv_expected); - SET_IDNKERR_PARAM2(&idnerr, rv_actual); - } - /* - * Any parameter conflicts are grounds for dismissal. - */ - if (idn.domset.ds_connected == 0) { - domainset_t domset; - /* - * We have no other connections yet. - * We must blow out of here completely - * unless we have relinkers left from - * a RECONFIG. - */ - IDN_GLOCK_EXCL(); - domset = ~idn.domset.ds_relink; - if (idn.domset.ds_relink == 0) { - IDN_GSTATE_TRANSITION(IDNGS_DISCONNECT); - } - domset &= ~idn.domset.ds_hitlist; - IDN_SET_NEW_MASTERID(IDN_NIL_DOMID); - IDN_GUNLOCK(); - IDN_DUNLOCK(domid); - - DOMAINSET_DEL(domset, idn.localid); - - idn_update_op(IDNOP_ERROR, DOMAINSET_ALL, &idnerr); - - PR_HITLIST("%s:%d: unlink_domainset(%x) due to " - "CFG error (relink=%x, hitlist=%x)\n", - proc, domid, domset, idn.domset.ds_relink, - idn.domset.ds_hitlist); - - idn_unlink_domainset(domset, IDNFIN_NORMAL, - CFGERR2FINARG(rv), - IDNFIN_OPT_UNLINK, - BOARDSET_ALL); - IDN_SYNC_UNLOCK(); - IDN_DLOCK_EXCL(domid); - } else { - PR_HITLIST("%s:%d: idn_disconnect(%d) due to CFG " - "error (conn=%x, relink=%x, hitlist=%x)\n", - proc, domid, domid, idn.domset.ds_connected, - idn.domset.ds_relink, idn.domset.ds_hitlist); - /* - * If we have other connections then - * we're only going to blow away this - * single connection. - */ - idn_update_op(IDNOP_ERROR, DOMAINSET(domid), &idnerr); - - DOMAINSET_DEL(idn.domset.ds_relink, domid); - (void) idn_disconnect(domid, IDNFIN_NORMAL, - CFGERR2FINARG(rv), IDNFIN_SYNC_NO); - IDN_SYNC_UNLOCK(); - } - break; - } -} - -/* - * Called by master or slave which expects exactly the following - * with respect to config info received from a SLAVE: - * IDNCFG_CPUSET - * IDNCFG_NETID - * IDNCFG_BOARDSET - * IDNCFG_SIZE (MTU, BUF, SLAB, NWR) - * IDNCFG_DATAMBOX (DOMAIN or INDEX if caller is master) - * IDNCFG_DATASVR (MAXNETS, MBXPERNET) - * IDNCFG_OPTIONS (CHECKSUM) - */ -static uint_t -idn_check_slave_config(int domid, uint_t *exp, uint_t *act) -{ - uint_t rv = 0; - idn_domain_t *ldp, *dp; - procname_t proc = "idn_check_slave_config"; - - dp = &idn_domain[domid]; - ldp = &idn_domain[idn.localid]; - - ASSERT(domid != idn.localid); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - ASSERT(dp->dstate == IDNDS_CONFIG); - - PR_PROTO("%s:%d: number received %d, number expected %d\n", - proc, domid, (int)dp->dncfgitems, IDN_SLAVE_NCFGITEMS); - - if ((int)dp->dncfgitems < IDN_SLAVE_NCFGITEMS) - return (CFG_CONTINUE); - - if ((dp->dnetid == (ushort_t)-1) || - CPUSET_ISNULL(dp->dcpuset) || - (dp->dhw.dh_boardset == 0) || - (dp->dmbox.m_send->mm_smr_mboxp == NULL) || - (dp->dmaxnets == 0) || - (dp->dmboxpernet == 0) || - (dp->dcksum == 0) || - (dp->dmtu == 0) || - (dp->dbufsize == 0) || - (dp->dslabsize == 0) || - (dp->dnwrsize == 0)) { - /* - * We received our IDN_SLAVE_NCFGITEMS config items, - * but not all what we were expecting! Gotta nack and - * close connection. - */ - cmn_err(CE_WARN, - "IDN: 218: missing some required config items from " - "domain %d", domid); - - rv = CFG_FATAL; - goto done; - } - - if (!valid_mtu(dp->dmtu)) { - cmn_err(CE_WARN, - "IDN: 219: remote domain %d MTU (%d) invalid " - "(local.mtu = %d)", dp->domid, dp->dmtu, ldp->dmtu); - - *exp = (uint_t)ldp->dmtu; - *act = (uint_t)dp->dmtu; - rv |= CFG_ERR_MTU; - } - if (!valid_bufsize(dp->dbufsize)) { - cmn_err(CE_WARN, - "IDN: 220: remote domain %d BUFSIZE (%d) invalid " - "(local.bufsize = %d)", dp->domid, dp->dbufsize, - ldp->dbufsize); - - *exp = (uint_t)ldp->dbufsize; - *act = (uint_t)dp->dbufsize; - rv |= CFG_ERR_BUF; - } - if (!valid_slabsize((int)dp->dslabsize)) { - cmn_err(CE_WARN, - "IDN: 221: remote domain %d SLABSIZE (%d) invalid " - "(local.slabsize = %d)", - dp->domid, dp->dslabsize, ldp->dslabsize); - - *exp = (uint_t)ldp->dslabsize; - *act = (uint_t)dp->dslabsize; - rv |= CFG_ERR_SLAB; - } - if (!valid_nwrsize((int)dp->dnwrsize)) { - cmn_err(CE_WARN, - "IDN: 223: remote domain %d NWRSIZE (%d) invalid " - "(local.nwrsize = %d)", - dp->domid, dp->dnwrsize, ldp->dnwrsize); - - *exp = (uint_t)ldp->dnwrsize; - *act = (uint_t)dp->dnwrsize; - rv |= CFG_ERR_NWR; - } - if ((int)dp->dmaxnets != IDN_MAX_NETS) { - cmn_err(CE_WARN, - "IDN: 224: remote domain %d MAX_NETS (%d) invalid " - "(local.maxnets = %d)", - dp->domid, (int)dp->dmaxnets, IDN_MAX_NETS); - - *exp = (uint_t)IDN_MAX_NETS; - *act = (uint_t)dp->dmaxnets; - rv |= CFG_ERR_NETS; - } - if ((int)dp->dmboxpernet != IDN_MBOX_PER_NET) { - cmn_err(CE_WARN, - "IDN: 225: remote domain %d MBOX_PER_NET (%d) " - "invalid (local.mboxpernet = %d)", - dp->domid, (int)dp->dmboxpernet, IDN_MBOX_PER_NET); - - *exp = (uint_t)IDN_MBOX_PER_NET; - *act = (uint_t)dp->dmboxpernet; - rv |= CFG_ERR_MBOX; - } - if ((dp->dcksum - 1) != (uchar_t)IDN_CHECKSUM) { - cmn_err(CE_WARN, - "IDN: 226: remote domain %d CHECKSUM flag (%d) " - "mismatches local domain's (%d)", - dp->domid, (int)dp->dcksum - 1, IDN_CHECKSUM); - - *exp = (uint_t)IDN_CHECKSUM; - *act = (uint_t)(dp->dcksum - 1); - rv |= CFG_ERR_CKSUM; - } - -done: - - return (rv ? rv : CFG_DONE); -} - -/* - * Called by slave ONLY which expects exactly the following - * config info from the MASTER: - * IDNCFG_BARLAR - * IDNCFG_MCADR - * IDNCFG_NMCADR - * IDNCFG_CPUSET - * IDNCFG_NETID - * IDNCFG_BOARDSET - * IDNCFG_SIZE (MTU, BUF, SLAB, NWR) - * IDNCFG_DATAMBOX (TABLE, DOMAIN) - * IDNCFG_DATASVR (MAXNETS, MBXPERNET) - * IDNCFG_OPTIONS (CHECKSUM) - */ -static uint_t -idn_check_master_config(int domid, uint_t *exp, uint_t *act) -{ - uint_t rv = 0; - int nmcadr; - int total_expitems; - int p, m, err; - idn_domain_t *dp; - idn_domain_t *ldp = &idn_domain[idn.localid]; - procname_t proc = "idn_check_master_config"; - - dp = &idn_domain[domid]; - - ASSERT(IDN_GET_MASTERID() != idn.localid); - ASSERT(domid != idn.localid); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - ASSERT(dp->dstate == IDNDS_CONFIG); - - PR_PROTO("%s:%d: number received %d, minimum number expected %d\n", - proc, domid, (int)dp->dncfgitems, IDN_MASTER_NCFGITEMS); - - if ((int)dp->dncfgitems < IDN_MASTER_NCFGITEMS) - return (CFG_CONTINUE); - - /* - * We have at least IDN_MASTER_NCFGITEMS items which - * means we have at least one MCADR. Need to make sure - * we have all that we're expecting, NMCADR. - */ - total_expitems = IDN_MASTER_NCFGITEMS + dp->dhw.dh_nmcadr - 1; - if ((dp->dhw.dh_nmcadr == 0) || - ((int)dp->dncfgitems < total_expitems)) { - /* - * We have not yet received all the MCADRs - * we're expecting. - */ - PR_PROTO("%s:%d: haven't received all MCADRs yet.\n", - proc, domid); - return (CFG_CONTINUE); - } - - nmcadr = 0; - for (p = 0; p < MAX_BOARDS; p++) - if (dp->dhw.dh_mcadr[p] != 0) - nmcadr++; - - IDN_GLOCK_SHARED(); - if ((idn.smr.rempfn == PFN_INVALID) || - (idn.smr.rempfnlim == PFN_INVALID) || - (dp->dnetid == (ushort_t)-1) || - CPUSET_ISNULL(dp->dcpuset) || - (dp->dhw.dh_boardset == 0) || - (nmcadr != dp->dhw.dh_nmcadr) || - (dp->dmbox.m_send->mm_smr_mboxp == NULL) || - (ldp->dmbox.m_tbl == NULL) || - (dp->dmaxnets == 0) || - (dp->dmboxpernet == 0) || - (dp->dcksum == 0) || - (dp->dmtu == 0) || - (dp->dbufsize == 0) || - (dp->dnwrsize == 0)) { - - IDN_GUNLOCK(); - /* - * We received all of our config items, but not - * all what we were expecting! Gotta reset and - * close connection. - */ - cmn_err(CE_WARN, - "IDN: 227: missing some required config items from " - "domain %d", domid); - - rv = CFG_FATAL; - goto done; - } - if ((idn.smr.rempfnlim - idn.smr.rempfn) > btop(MB2B(IDN_SMR_SIZE))) { - /* - * The master's SMR region is larger than - * mine! This means that this domain may - * receive I/O buffers which are out of the - * range of this local domain's SMR virtual - * address space. The master SMR has to be - * no larger than the local SMR in order to - * guarantee enough local virtual addresses - * to see all of the SMR space. - * XXX - Possibly add negotiating SMR size. - * Try to create a new virtual mapping. - * Could let domains negotiate SMR size. - * Winning size would have to be smallest - * in DC. If so, how to handle incoming - * domains with even smaller SMRs? - * - Could either disallow connection - * - Could reconfigure to use smaller SMR. - */ - cmn_err(CE_WARN, - "IDN: 228: master's SMR (%ld) larger than " - "local's SMR (%ld)", - idn.smr.rempfnlim - idn.smr.rempfn, - btop(MB2B(IDN_SMR_SIZE))); - - *exp = (uint_t)IDN_SMR_SIZE; - *act = (uint_t)B2MB(ptob(idn.smr.rempfnlim - idn.smr.rempfn)); - rv |= CFG_ERR_SMR; - } - IDN_GUNLOCK(); - - if (!valid_mtu(dp->dmtu)) { - cmn_err(CE_WARN, - "IDN: 219: remote domain %d MTU (%d) invalid " - "(local.mtu = %d)", dp->domid, dp->dmtu, ldp->dmtu); - - *exp = (uint_t)ldp->dmtu; - *act = (uint_t)dp->dmtu; - rv |= CFG_ERR_MTU; - } - if (!valid_bufsize(dp->dbufsize)) { - cmn_err(CE_WARN, - "IDN: 220: remote domain %d BUFSIZE (%d) invalid " - "(local.bufsize = %d)", dp->domid, dp->dbufsize, - ldp->dbufsize); - - *exp = (uint_t)ldp->dbufsize; - *act = (uint_t)dp->dbufsize; - rv |= CFG_ERR_BUF; - } - if (!valid_nwrsize((int)dp->dnwrsize)) { - cmn_err(CE_WARN, - "IDN: 223: remote domain %d NWRSIZE (%d) invalid " - "(local.nwrsize = %d)", - dp->domid, dp->dnwrsize, ldp->dnwrsize); - - *exp = (uint_t)ldp->dnwrsize; - *act = (uint_t)dp->dnwrsize; - rv |= CFG_ERR_NWR; - } - if ((int)dp->dmaxnets != IDN_MAX_NETS) { - cmn_err(CE_WARN, - "IDN: 224: remote domain %d MAX_NETS (%d) invalid " - "(local.maxnets = %d)", - dp->domid, (int)dp->dmaxnets, IDN_MAX_NETS); - - *exp = (uint_t)IDN_MAX_NETS; - *act = (uint_t)dp->dmaxnets; - rv |= CFG_ERR_NETS; - } - if ((int)dp->dmboxpernet != IDN_MBOX_PER_NET) { - cmn_err(CE_WARN, - "IDN: 225: remote domain %d MBOX_PER_NET (%d) " - "invalid (local.mboxpernet = %d)", - dp->domid, (int)dp->dmboxpernet, IDN_MBOX_PER_NET); - - *exp = (uint_t)IDN_MBOX_PER_NET; - *act = (uint_t)dp->dmboxpernet; - rv |= CFG_ERR_MBOX; - } - if ((dp->dcksum - 1) != (uchar_t)IDN_CHECKSUM) { - cmn_err(CE_WARN, - "IDN: 226: remote domain %d CHECKSUM flag (%d) " - "mismatches local domain's (%d)", - dp->domid, (int)dp->dcksum - 1, IDN_CHECKSUM); - - *exp = (uint_t)IDN_CHECKSUM; - *act = (uint_t)(dp->dcksum - 1); - rv |= CFG_ERR_CKSUM; - } - nmcadr = 0; - err = 0; - for (m = 0; m < MAX_BOARDS; m++) { - if (!BOARD_IN_SET(dp->dhw.dh_boardset, m) && - dp->dhw.dh_mcadr[m]) { - cmn_err(CE_WARN, - "IDN: 229: remote domain %d boardset (0x%x) " - "conflicts with MCADR(board %d) [0x%x]", - dp->domid, (uint_t)dp->dhw.dh_boardset, m, - dp->dhw.dh_mcadr[m]); - err++; - } - if (dp->dhw.dh_mcadr[m]) - nmcadr++; - } - if (err) { - *exp = 0; - *act = err; - rv |= CFG_ERR_MCADR; - } else if (nmcadr != dp->dhw.dh_nmcadr) { - cmn_err(CE_WARN, - "IDN: 230: remote domain %d reported number of " - "MCADRs (%d) mismatches received (%d)", - dp->domid, dp->dhw.dh_nmcadr, nmcadr); - *exp = (uint_t)dp->dhw.dh_nmcadr; - *act = (uint_t)nmcadr; - rv |= CFG_ERR_NMCADR; - } - -done: - - return (rv ? rv : CFG_DONE); -} - -static int -idn_recv_config_done(int domid) -{ - boardset_t b_conflicts; - cpuset_t p_conflicts; - register int p, i; - register idn_domain_t *dp; - idnsb_error_t idnerr; - procname_t proc = "idn_recv_config_done"; - - ASSERT(domid != IDN_NIL_DOMID); - dp = &idn_domain[domid]; - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - /* - * Well, we received all that we were expecting - * so stop any CFG timers we had going. - */ - IDN_MSGTIMER_STOP(domid, IDNP_CFG, 0); - - dp->dncpus = 0; - for (p = 0; p < NCPU; p++) - if (CPU_IN_SET(dp->dcpuset, p)) - dp->dncpus++; - dp->dhw.dh_nboards = 0; - for (p = 0; p < MAX_BOARDS; p++) - if (BOARD_IN_SET(dp->dhw.dh_boardset, p)) - dp->dhw.dh_nboards++; - - IDN_GLOCK_EXCL(); - /* - * Verify dcpuset and dhw.dh_boardset don't - * conflict with any existing DC member. - */ - b_conflicts = idn.dc_boardset & dp->dhw.dh_boardset; - CPUSET_ZERO(p_conflicts); - CPUSET_OR(p_conflicts, idn.dc_cpuset); - CPUSET_AND(p_conflicts, dp->dcpuset); - - if (b_conflicts || !CPUSET_ISNULL(p_conflicts)) { - if (b_conflicts) { - cmn_err(CE_WARN, - "IDN: 231: domain %d boardset " - "(0x%x) conflicts with existing " - "IDN boardset (0x%x)", - domid, dp->dhw.dh_boardset, - b_conflicts); - } - if (!CPUSET_ISNULL(p_conflicts)) { - cmn_err(CE_WARN, - "IDN: 232: domain %d cpuset " - "(0x%x.%0x) conflicts with existing " - "IDN cpuset (0x%x.%0x)", domid, - UPPER32_CPUMASK(dp->dcpuset), - LOWER32_CPUMASK(dp->dcpuset), - UPPER32_CPUMASK(p_conflicts), - LOWER32_CPUMASK(p_conflicts)); - } - IDN_GUNLOCK(); - /* - * Need to disconnect and not retry with this guy. - */ - IDN_DUNLOCK(domid); - IDN_SYNC_LOCK(); - DOMAINSET_DEL(idn.domset.ds_relink, domid); - IDN_DLOCK_EXCL(domid); - - INIT_IDNKERR(&idnerr); - SET_IDNKERR_ERRNO(&idnerr, EPROTO); - SET_IDNKERR_IDNERR(&idnerr, IDNKERR_CONFIG_FATAL); - SET_IDNKERR_PARAM0(&idnerr, domid); - idn_update_op(IDNOP_ERROR, DOMAINSET(domid), &idnerr); - - (void) idn_disconnect(domid, IDNFIN_FORCE_HARD, - IDNFIN_ARG_CFGERR_FATAL, IDNFIN_SYNC_NO); - IDN_SYNC_UNLOCK(); - - return (-1); - } - - idn_mainmbox_reset(domid, dp->dmbox.m_send); - idn_mainmbox_reset(domid, dp->dmbox.m_recv); - -#ifdef IDNBUG_CPUPERBOARD - /* - * We only allow connections to domains whose (mem) boards - * all have at least one cpu. This is necessary so that - * we can program the CICs of that respective board. This - * is primarily only a requirement if the remote domain - * is the master _and_ has the SMR in that particular board. - * To simplify the checking we simply restrict connections to - * domains that have at least one cpu on all boards that - * contain memory. - */ - if (!idn_cpu_per_board((void *)NULL, dp->dcpuset, &dp->dhw)) { - cmn_err(CE_WARN, - "IDN: 233: domain %d missing CPU per " - "memory boardset (0x%x), CPU boardset (0x%x)", - domid, dp->dhw.dh_boardset, - cpuset2boardset(dp->dcpuset)); - - IDN_GUNLOCK(); - /* - * Need to disconnect and not retry with this guy. - */ - IDN_DUNLOCK(domid); - IDN_SYNC_LOCK(); - DOMAINSET_DEL(idn.domset.ds_relink, domid); - IDN_DLOCK_EXCL(domid); - - INIT_IDNKERR(&idnerr); - SET_IDNKERR_ERRNO(&idnerr, EINVAL); - SET_IDNKERR_IDNERR(&idnerr, IDNKERR_CPU_CONFIG); - SET_IDNKERR_PARAM0(&idnerr, domid); - idn_update_op(IDNOP_ERROR, DOMAINSET(domid), &idnerr); - - (void) idn_disconnect(domid, IDNFIN_FORCE_HARD, - IDNFIN_ARG_CPUCFG, IDNFIN_SYNC_NO); - IDN_SYNC_UNLOCK(); - - return (-1); - } -#endif /* IDNBUG_CPUPERBOARD */ - - CPUSET_OR(idn.dc_cpuset, dp->dcpuset); - idn.dc_boardset |= dp->dhw.dh_boardset; - - IDN_GUNLOCK(); - - /* - * Set up the portmap for this domain. - */ - i = -1; - for (p = 0; p < NCPU; p++) { - BUMP_INDEX(dp->dcpuset, i); - dp->dcpumap[p] = (uchar_t)i; - } - - /* - * Got everything we need from the remote - * domain, now we can program hardware as needed. - */ - if (idn_program_hardware(domid) != 0) { - domainset_t domset; - /* - * Yikes! Failed to program hardware. - * Gotta bail. - */ - cmn_err(CE_WARN, - "IDN: 234: failed to program hardware for domain %d " - "(boardset = 0x%x)", - domid, dp->dhw.dh_boardset); - - IDN_DUNLOCK(domid); - /* - * If we're having problems programming our - * hardware we better unlink completely from - * the IDN before things get really bad. - */ - IDN_SYNC_LOCK(); - IDN_GLOCK_EXCL(); - IDN_GSTATE_TRANSITION(IDNGS_DISCONNECT); - domset = DOMAINSET_ALL; - DOMAINSET_DEL(domset, idn.localid); - IDN_SET_NEW_MASTERID(IDN_NIL_DOMID); - IDN_GUNLOCK(); - - INIT_IDNKERR(&idnerr); - SET_IDNKERR_ERRNO(&idnerr, EINVAL); - SET_IDNKERR_IDNERR(&idnerr, IDNKERR_HW_ERROR); - SET_IDNKERR_PARAM0(&idnerr, domid); - idn_update_op(IDNOP_ERROR, DOMAINSET_ALL, &idnerr); - - idn_unlink_domainset(domset, IDNFIN_NORMAL, IDNFIN_ARG_HWERR, - IDNFIN_OPT_UNLINK, BOARDSET_ALL); - - IDN_SYNC_UNLOCK(); - IDN_DLOCK_EXCL(domid); - - return (-1); - } - - /* - * Now that hardware has been programmed we can - * remap the SMR into our local space, if necessary. - */ - IDN_GLOCK_EXCL(); - if (domid == IDN_GET_MASTERID()) { - /* - * No need to worry about disabling the data - * server since at this stage there is only - * one and he doesn't go active until his - * mailbox (dmbox.m_recv->mm_smr_mboxp) is set up. - */ - smr_remap(&kas, idn.smr.vaddr, idn.smr.rempfn, IDN_SMR_SIZE); - } - IDN_GUNLOCK(); - - /* - * There is no need to ACK the CFG messages since remote - * domain would not progress to the next state (CON_SENT) - * unless he has received everything. - */ - - dp->dcfgrcvdone = 1; - PR_PROTO("%s:%d: RECV config DONE\n", proc, domid); - - if (dp->dcfgsnddone) { - idn_xdcargs_t xargs; - /* - * Well, we've received all that we were expecting, - * but we don't know if the remote domain has - * received all that it was expecting from us, - * although we know we transferred everything - * so let's get the show on the road. - */ - IDN_DUNLOCK(domid); - IDN_SYNC_LOCK(); - IDN_DLOCK_EXCL(domid); - /* - * If the state has changed from CONFIG - * then somebody else has taken over - * control of this domain so we can just - * bail out. - */ - if (dp->dstate == IDNDS_CONFIG) { - dp->dxp = &xphase_con; - IDN_XSTATE_TRANSITION(dp, IDNXS_PEND); - bzero(xargs, sizeof (xargs)); - - (void) idn_xphase_transition(domid, NULL, xargs); - } - IDN_SYNC_UNLOCK(); - } - - return (0); -} - -static int -idn_verify_config_mbox(int domid) -{ - idn_domain_t *ldp, *dp; - idn_mainmbox_t *mmp; - idn_mboxtbl_t *mtp; - int c, rv = 0; - uint_t activeptr, readyptr; - ushort_t mbox_csum; - - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - dp = &idn_domain[domid]; - ldp = &idn_domain[idn.localid]; - - /* - * The master will have assigned us the dmbox.m_tbl - * from which we assign our receive mailboxes. - * The first (0) entry contains the cookie used - * for verification. - */ - IDN_DLOCK_SHARED(idn.localid); - /* - * Now that we have an assigned mboxtbl from the - * master, we can determine which receive mailbox - * we indirectly assigned to him at the time we - * sent him his MBOX_INDEX. Prep it, however note - * that the master will have not been able to - * validate it because of the chicken 'n egg - * problem between a master and slave. Thus we - * need to reset the cookie after the prep. - */ - mmp = dp->dmbox.m_recv; - mtp = IDN_MBOXTBL_PTR(ldp->dmbox.m_tbl, domid); - for (c = 0; c < IDN_MAX_NETS; c++) { - mutex_enter(&mmp[c].mm_mutex); - ASSERT(!mmp[c].mm_smr_mboxp); - - mmp[c].mm_smr_mboxp = mtp; - mbox_csum = IDN_CKSUM_MBOX(&mtp->mt_header); - if (!VALID_MBOXHDR(&mtp->mt_header, c, mbox_csum)) { - cmn_err(CE_WARN, - "IDN: 235: [recv] mailbox (domain %d, " - "channel %d) SMR CORRUPTED - RELINK", - domid, c); - cmn_err(CE_CONT, - "IDN: 235: [recv] expected (cookie 0x%x, " - "cksum 0x%x) actual (cookie 0x%x, " - "cksum 0x%x)\n", - IDN_GET_MBOXHDR_COOKIE(&mtp->mt_header), - (int)mtp->mt_header.mh_cksum, - IDN_MAKE_MBOXHDR_COOKIE(0, 0, c), - (int)mbox_csum); - mutex_exit(&mmp[c].mm_mutex); - rv = -1; - break; - } - activeptr = mtp->mt_header.mh_svr_active_ptr; - readyptr = mtp->mt_header.mh_svr_ready_ptr; - /* - * Verify pointers are valid. - */ - if (!activeptr || !VALID_NWROFFSET(activeptr, 2) || - !readyptr || !VALID_NWROFFSET(readyptr, 2)) { - cmn_err(CE_WARN, - "IDN: 235: [recv] mailbox (domain %d, " - "channel %d) SMR CORRUPTED - RELINK", - domid, c); - cmn_err(CE_CONT, - "IDN: 235: [recv] activeptr (0x%x), " - "readyptr (0x%x)\n", - activeptr, readyptr); - mutex_exit(&mmp[c].mm_mutex); - rv = -1; - break; - } - mmp[c].mm_smr_activep = (ushort_t *)IDN_OFFSET2ADDR(activeptr); - mmp[c].mm_smr_readyp = (ushort_t *)IDN_OFFSET2ADDR(readyptr); - mutex_exit(&mmp[c].mm_mutex); - IDN_MBOXTBL_PTR_INC(mtp); - } - - IDN_DUNLOCK(idn.localid); - - if (rv) - return (rv); - - /* - * Now we need to translate SMR offsets for send mailboxes - * to actual virtual addresses. - */ - mmp = dp->dmbox.m_send; - for (c = 0; c < IDN_MAX_NETS; mmp++, c++) { - mutex_enter(&mmp->mm_mutex); - if ((mtp = mmp->mm_smr_mboxp) == NULL) { - mutex_exit(&mmp->mm_mutex); - rv = -1; - break; - } - - mbox_csum = IDN_CKSUM_MBOX(&mtp->mt_header); - - if (!VALID_MBOXHDR(&mtp->mt_header, c, mbox_csum)) { - cmn_err(CE_WARN, - "IDN: 235: [send] mailbox (domain %d, " - "channel %d) SMR CORRUPTED - RELINK", - domid, c); - cmn_err(CE_CONT, - "IDN: 235: [send] expected (cookie 0x%x, " - "cksum 0x%x) actual (cookie 0x%x, " - "cksum 0x%x)\n", - IDN_GET_MBOXHDR_COOKIE(&mtp->mt_header), - (int)mtp->mt_header.mh_cksum, - IDN_MAKE_MBOXHDR_COOKIE(0, 0, c), - (int)mbox_csum); - mutex_exit(&mmp->mm_mutex); - rv = -1; - break; - } - activeptr = mtp->mt_header.mh_svr_active_ptr; - readyptr = mtp->mt_header.mh_svr_ready_ptr; - /* - * Paranoid check. - */ - if (!activeptr || !VALID_NWROFFSET(activeptr, 2) || - !readyptr || !VALID_NWROFFSET(readyptr, 2)) { - cmn_err(CE_WARN, - "IDN: 235: [send] mailbox (domain %d, " - "channel %d) SMR CORRUPTED - RELINK", - domid, c); - cmn_err(CE_CONT, - "IDN: 235: [send] activeptr (0x%x), " - "readyptr (0x%x)\n", - activeptr, readyptr); - mutex_exit(&mmp->mm_mutex); - rv = -1; - break; - } - mmp->mm_smr_activep = (ushort_t *)IDN_OFFSET2ADDR(activeptr); - mmp->mm_smr_readyp = (ushort_t *)IDN_OFFSET2ADDR(readyptr); - idn_reset_mboxtbl(mtp); - mutex_exit(&mmp->mm_mutex); - IDN_MBOXTBL_PTR_INC(mtp); - } - - return (rv); -} - -/* - * The BUFSIZEs between domains have to be equal so that slave buffers - * and the master's slabpool are consistent. - * The MTUs between domains have to be equal so they can transfer - * packets consistently without possible data truncation. - * - * ZZZ - Perhaps these could be negotiated? - */ -static int -valid_mtu(uint_t mtu) -{ - return ((mtu == idn_domain[idn.localid].dmtu) && mtu); -} - -static int -valid_bufsize(uint_t bufsize) -{ - return ((bufsize == idn_domain[idn.localid].dbufsize) && bufsize); -} - -static int -valid_slabsize(int slabsize) -{ - return ((slabsize == idn_domain[idn.localid].dslabsize) && slabsize); -} - -static int -valid_nwrsize(int nwrsize) -{ - return ((nwrsize == idn_domain[idn.localid].dnwrsize) && nwrsize); -} - -static int -idn_program_hardware(int domid) -{ - int rv, is_master; - idn_domain_t *dp; - uint_t *mcadrp; - pfn_t rem_pfn, rem_pfnlimit; - procname_t proc = "idn_program_hardware"; - - PR_PROTO("%s:%d: program hw in domain %d w.r.t remote domain %d\n", - proc, domid, idn.localid, domid); - - dp = &idn_domain[domid]; - - ASSERT(domid != idn.localid); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - ASSERT(dp->dstate == IDNDS_CONFIG); - - IDN_GLOCK_EXCL(); - - if (DOMAIN_IN_SET(idn.domset.ds_hwlinked, domid)) { - IDN_GUNLOCK(); - return (0); - } - - DOMAINSET_ADD(idn.domset.ds_flush, domid); - CHECKPOINT_OPENED(IDNSB_CHKPT_CACHE, dp->dhw.dh_boardset, 1); - - if (domid != IDN_GET_MASTERID()) { - /* - * If the remote domain is a slave, then - * all we have to program is the CIC sm_mask. - */ - is_master = 0; - if ((idn.localid == IDN_GET_MASTERID()) && - lock_try(&idn.first_hwlink)) { - /* - * This is our first HW link and I'm the - * master, which means we need to program - * our local bar/lar. - */ - ASSERT(idn.first_hwmasterid == (short)IDN_NIL_DOMID); - idn.first_hwmasterid = (short)idn.localid; - rem_pfn = idn.smr.locpfn; - rem_pfnlimit = idn.smr.locpfn + - btop(MB2B(IDN_SMR_SIZE)); - } else { - /* - * Otherwise, just a slave linking to - * another slave. No bar/lar updating - * necessary. - */ - rem_pfn = rem_pfnlimit = PFN_INVALID; - } - mcadrp = NULL; - } else { - /* - * If the remote domain is a master, then - * we need to program the CIC sm_mask/sm_bar/sm_lar, - * and PC's. - */ - is_master = 1; - rem_pfn = idn.smr.rempfn; - rem_pfnlimit = idn.smr.rempfnlim; - mcadrp = dp->dhw.dh_mcadr; - ASSERT(idn.first_hwmasterid == (short)IDN_NIL_DOMID); - idn.first_hwmasterid = (short)domid; - } - - PR_PROTO("%s:%d: ADD bset (0x%x)\n", proc, domid, dp->dhw.dh_boardset); - - rv = idnxf_shmem_add(is_master, dp->dhw.dh_boardset, - rem_pfn, rem_pfnlimit, mcadrp); - - if (rv == 0) { - DOMAINSET_ADD(idn.domset.ds_hwlinked, domid); - } else { - if (rem_pfn == idn.smr.locpfn) - lock_clear(&idn.first_hwlink); - - if (idn.first_hwmasterid == (short)domid) - idn.first_hwmasterid = (short)IDN_NIL_DOMID; - - (void) idnxf_shmem_sub(is_master, dp->dhw.dh_boardset); - } - - IDN_GUNLOCK(); - - return (rv); -} - -static int -idn_deprogram_hardware(int domid) -{ - int rv, is_master; - idn_domain_t *dp; - procname_t proc = "idn_deprogram_hardware"; - - - dp = &idn_domain[domid]; - - ASSERT(domid != idn.localid); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - /* - * Need to take into consideration what boards remote - * domain was connected to. If we don't have a connection to - * them ourself, then we better remove them now , otherwise - * they'll never be removed (unless we link to them at some point). - */ -#if 0 - DEBUG_USECDELAY(500000); -#endif /* 0 */ - - IDN_GLOCK_EXCL(); - - if (!DOMAIN_IN_SET(idn.domset.ds_hwlinked, domid)) { - IDN_GUNLOCK(); - return (0); - } - - PR_PROTO("%s:%d: DEprogram hw in domain %d w.r.t remote domain %d\n", - proc, domid, idn.localid, domid); - - /* - * It's possible to come through this flow for domains that - * have not been programmed, i.e. not in idn.hwlinked_domset, - * so don't bother asserting that they might be in there. - * This can occur if we lose a domain during the config/syn - * sequence. If this occurs we won't know whether the remote - * domain has programmed its hardware or not. If it has then - * it will have to go through the DMAP sequence and thus we - * have to go through it also. So, if we reach at least the - * CONFIG state, we need to go through the DMAP handshake. - */ - - PR_PROTO("%s:%d: SUB bset (0x%x)\n", proc, domid, dp->dhw.dh_boardset); - - if (idn.first_hwmasterid == (short)domid) { - is_master = 1; - idn.first_hwmasterid = (short)IDN_NIL_DOMID; - } else { - is_master = 0; - } - rv = idnxf_shmem_sub(is_master, dp->dhw.dh_boardset); - - if (rv == 0) - DOMAINSET_DEL(idn.domset.ds_hwlinked, domid); - - IDN_GUNLOCK(); - - return (rv); -} - -/* - * Remember can't send slabs back to master at this point. - * Entered with write-drwlock held. - * Returns with drwlock dropped. - */ -static void -idn_deconfig(int domid) -{ - idn_domain_t *dp, *ldp; - smr_slab_t *sp; - int c, masterid; - procname_t proc = "idn_deconfig"; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - ASSERT(domid != idn.localid); - - ldp = &idn_domain[idn.localid]; - dp = &idn_domain[domid]; - - ASSERT(dp->dstate == IDNDS_DMAP); - - PR_PROTO("%s:%d: (dio=%d, dioerr=%d, dnslabs=%d)\n", - proc, domid, dp->dio, dp->dioerr, dp->dnslabs); - - IDN_GLOCK_EXCL(); - masterid = IDN_GET_MASTERID(); - - idn.dc_boardset &= ~dp->dhw.dh_boardset; - for (c = 0; c < NCPU; c++) { - if (CPU_IN_SET(dp->dcpuset, c)) { - CPUSET_DEL(idn.dc_cpuset, c); - } - } - - IDN_GUNLOCK(); - - (void) smr_buf_free_all(domid); - - if (idn.localid == masterid) { - /* - * Since I'm the master there may - * have been slabs in this domain's - * idn_domain[] entry. - */ - DSLAB_LOCK_EXCL(domid); - if ((sp = dp->dslab) != NULL) { - PR_PROTO("%s:%d: freeing up %d dead slabs\n", - proc, domid, dp->dnslabs); - smr_slab_free(domid, sp); - dp->dslab = NULL; - dp->dnslabs = 0; - dp->dslab_state = DSLAB_STATE_UNKNOWN; - } - DSLAB_UNLOCK(domid); - } else if (domid == masterid) { - /* - * We're shutting down the master! - * We need to blow away our local slab - * data structures. - * Since I'm not the master, there should - * be no slab structures in the given - * domain's idn_domain[] entry. They should - * only exist in the local domain's entry. - */ - DSLAB_LOCK_EXCL(idn.localid); - ASSERT(dp->dslab == NULL); -#ifdef DEBUG - { - int nbusy = 0; - uint_t dommask = 0; - for (sp = ldp->dslab; sp; sp = sp->sl_next) { - smr_slabbuf_t *bp; - - if (!smr_slab_busy(sp)) - continue; - nbusy++; - for (bp = sp->sl_inuse; bp; bp = bp->sb_next) - if (bp->sb_domid != IDN_NIL_DOMID) - DOMAINSET_ADD(dommask, - bp->sb_domid); - } - if (nbusy) - PR_PROTO("%s:%d: found %d busy slabs " - "(dommask = 0x%x)\n", - proc, domid, nbusy, dommask); - } -#endif /* DEBUG */ - if ((sp = ldp->dslab) != NULL) { - PR_PROTO("%s:%d: freeing up %d local slab " - "structs\n", proc, domid, ldp->dnslabs); - smr_slab_garbage_collection(sp); - ldp->dslab = NULL; - ldp->dnslabs = 0; - ldp->dslab_state = DSLAB_STATE_UNKNOWN; - } - DSLAB_UNLOCK(idn.localid); - } - if (dp->dio) { - PR_PROTO("%s:%d: reset dio (%d) to 0\n", proc, domid, dp->dio); - dp->dio = 0; - } - dp->dioerr = 0; - - PR_PROTO("%s:%d: reset diocheck (%x) to 0\n", - proc, domid, dp->diocheck); - lock_clear(&dp->diocheck); - - CHECKPOINT_CLOSED(IDNSB_CHKPT_LINK, dp->dhw.dh_boardset, 2); - - /* - * Should have already flush our memory before - * reaching this stage. The issue is that by the - * time we reach here the remote domains may have - * already reprogrammed their hardware and so flushing - * out caches now could result in a arbstop/hang - * if we have data that needs to go back to one - * of the remote domains that has already reprogrammed - * its hardware. - */ - ASSERT(!DOMAIN_IN_SET(idn.domset.ds_flush, domid)); - - (void) idn_deprogram_hardware(domid); - /* - * XXX - what to do if we - * fail to program hardware - * probably should panic since - * demise of system may be near? - * Sufficient to just shutdown network? - */ - - IDN_DSTATE_TRANSITION(dp, IDNDS_CLOSED); - - idn_close_domain(domid); -} - -/* - * If we're sending a Reset we better make sure we don't have any - * references or traffic headed in the direction of this guy, since - * when he receives the reset, he'll start shutting down which means - * we effectively have to shutdown _before_ sending the reset. - * DO NOT HOLD ANY DOMAIN RWLOCKS ON ENTRY. Could result in deadlock - * due to channel server looping back through STREAMs and attempting - * to acquire domain lock, i.e. channel server will never "stop". - */ -static void -idn_shutdown_datapath(domainset_t domset, int force) -{ - int do_allchan; - idn_domain_t *dp; - register int d; - procname_t proc = "idn_shutdown_datapath"; - - - PR_CHAN("%s: domset = 0x%x\n", proc, (uint_t)domset); - - do_allchan = (domset == DOMAINSET_ALL) ? 1 : 0; - - DOMAINSET_DEL(domset, idn.localid); - - if (do_allchan) { - /* - * Need to stop all outgoing and - * incoming SMR references. - */ - idn_deactivate_channel(CHANSET_ALL, IDNCHAN_OFFLINE); - } - - /* - * If force is set then we don't want to reference - * the SMR at all, so deactivate the domains from - * channels first. This will result in the mainmbox-flush - * routines to just clean up without referencing the - * SMR space. - */ - if (force) - idn_mainmbox_deactivate(domset); - - /* - * Flush out mailboxes (clear smr reference). - */ - for (d = 0; d < MAX_DOMAINS; d++) { - if (!DOMAIN_IN_SET(domset, d)) - continue; - - dp = &idn_domain[d]; - if ((dp->dmbox.m_send == NULL) && (dp->dmbox.m_recv == NULL)) - continue; - - IDN_MBOX_LOCK(d); - if (dp->dmbox.m_send) - (void) idn_mainmbox_flush(d, dp->dmbox.m_send); - if (dp->dmbox.m_recv) - (void) idn_mainmbox_flush(d, dp->dmbox.m_recv); - IDN_MBOX_UNLOCK(d); - } - /* - * Deactivate all domain references also. - * Only necessary if it wasn't already done above. - */ - if (!force) - idn_mainmbox_deactivate(domset); -} - -void -idn_send_cmd(int domid, idn_cmd_t cmdtype, uint_t arg1, uint_t arg2, uint_t - arg3) -{ - idn_msgtype_t mt; - procname_t proc = "idn_send_cmd"; - - mt.mt_mtype = IDNP_CMD; - mt.mt_atype = 0; - mt.mt_cookie = 0; - - ASSERT(IDN_DLOCK_IS_HELD(domid)); - - PR_PROTO("%s:%d: sending command %s\n", proc, domid, - VALID_IDNCMD(cmdtype) ? idncmd_str[cmdtype] : "unknown"); - - IDN_MSGTIMER_START(domid, IDNP_CMD, (ushort_t)cmdtype, - idn_msg_waittime[IDNP_CMD], &mt.mt_cookie); - - IDNXDC(domid, &mt, (uint_t)cmdtype, arg1, arg2, arg3); -} - -void -idn_send_cmdresp(int domid, idn_msgtype_t *mtp, idn_cmd_t cmdtype, uint_t arg1, - uint_t arg2, uint_t cerrno) -{ - idn_msgtype_t mt; - - ASSERT(IDN_DLOCK_IS_HELD(domid)); - - if (domid == idn.localid) { - /* - * It's possible local domain received a command - * from itself. However, we cannot send a normal - * "ack" response (XDC) to ourself. - */ - return; - } - - mt.mt_mtype = IDNP_CMD | IDNP_ACK; - mt.mt_atype = 0; - mt.mt_cookie = mtp->mt_cookie; - - IDNXDC(domid, &mt, (uint_t)cmdtype, arg1, arg2, cerrno); -} - -static void -idn_send_cmd_nackresp(int domid, idn_msgtype_t *mtp, idn_cmd_t cmdtype, - idn_nack_t nacktype) -{ - idn_msgtype_t mt; - - if (domid == idn.localid) - return; - - mt.mt_mtype = IDNP_CMD | IDNP_NACK; - mt.mt_atype = 0; - mt.mt_cookie = mtp->mt_cookie; - - (void) IDNXDC(domid, &mt, (uint_t)cmdtype, (uint_t)nacktype, 0, 0); -} - -void -idn_broadcast_cmd(idn_cmd_t cmdtype, uint_t arg1, uint_t arg2, uint_t arg3) -{ - idn_msgtype_t mt; - domainset_t domset; - procname_t proc = "idn_broadcast_cmd"; - - IDN_GLOCK_SHARED(); - - domset = idn.domset.ds_connected; - DOMAINSET_DEL(domset, idn.localid); - - PR_PROTO("%s: broadcasting command (%s) to domainset 0x%x\n", - proc, VALID_IDNCMD(cmdtype) ? idncmd_str[cmdtype] : "unknown", - domset); - - mt.mt_mtype = IDNP_CMD; - mt.mt_atype = 0; - mt.mt_cookie = 0; - - IDNXDC_BROADCAST(domset, &mt, (uint_t)cmdtype, arg1, arg2, arg3); - - IDN_GUNLOCK(); - /* - * This is a broadcast which means local domain needs - * to process it also. Since we can't XDC to ourselves - * we simply call a local function. - */ - idn_local_cmd(cmdtype, arg1, arg2, arg3); -} - -/* - * Since xargs[0] contains the cmdtype, only xargs[1], xargs[2], xargs[3] - * are valid possible response arguments. - */ -static void -idn_recv_cmd(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - uint_t msg = mtp->mt_mtype; - register idn_domain_t *dp; - idn_cmd_t cmdtype; - uint_t acknack; - uint_t cmdarg1, cmdarg2, cmdarg3; - int islocal; - int unsup_cmd_sent, unsup_cmd_recvd; - procname_t proc = "idn_recv_cmd"; - - acknack = msg & IDNP_ACKNACK_MASK; - GET_XARGS(xargs, &cmdtype, &cmdarg1, &cmdarg2, &cmdarg3); - - dp = &idn_domain[domid]; - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - IDN_GLOCK_SHARED(); - - islocal = (domid == idn.localid); - - ASSERT(!acknack || (acknack & IDNP_ACKNACK_MASK)); - - PR_PROTO("%s:%d: (local=%d) acknack=0x%x, cmdtype=%s(%d), " - "a1=0x%x, a2=0x%x, a3=0x%x\n", - proc, domid, islocal, acknack, - VALID_IDNCMD(cmdtype) ? idncmd_str[cmdtype] : "unknown", - cmdtype, cmdarg1, cmdarg2, cmdarg3); - - unsup_cmd_sent = unsup_cmd_recvd = 0; - - if ((IDN_GET_MASTERID() == IDN_NIL_DOMID) || - (dp->dstate != IDNDS_CONNECTED)) { - /* - * Commands cannot be handled without a valid - * master. If this is a request then nack him. - */ - PR_PROTO("%s:%d: cannot process CMD w/o master (%d, %s)\n", - proc, domid, IDN_GET_MASTERID(), - idnds_str[dp->dstate]); - - if (!islocal && !(acknack & IDNP_ACKNACK_MASK)) - idn_send_cmd_nackresp(domid, mtp, cmdtype, - IDNNACK_NOCONN); - IDN_GUNLOCK(); - return; - } - IDN_GUNLOCK(); - - if (acknack & IDNP_ACKNACK_MASK) { - idn_nack_t nack; - /* - * Receiving a cmd+ack or cmd+nack in response to some - * earlier command we must have issued. - * If the response is a nack, there are two possibilites: - * - * 1. Remote domain failed to allocate due - * to limited resources. - * - * 2. Remote domain does not support this - * particular command. - * - * In the case of #2, the argument immediately after - * the cmdtype (xargs[1]) will be (-1). - */ - nack = (idn_nack_t)cmdarg1; - if ((acknack & IDNP_NACK) && (nack == IDNNACK_BADCMD)) - unsup_cmd_sent++; - - if (islocal) { - /* - * Shouldn't be receiving local commands w/acks. - */ - cmdtype = (idn_cmd_t)0; - } - - switch (cmdtype) { - case IDNCMD_SLABALLOC: - idn_recv_slaballoc_resp(domid, cmdarg1, cmdarg2, - cmdarg3); - break; - - case IDNCMD_SLABFREE: - idn_recv_slabfree_resp(domid, cmdarg1, cmdarg2, - cmdarg3); - break; - - case IDNCMD_SLABREAP: - /* - * We only care if successful. - */ - if (acknack & IDNP_ACK) - idn_recv_slabreap_resp(domid, cmdarg1, cmdarg3); - break; - - case IDNCMD_NODENAME: - if ((acknack & IDNP_NACK) == 0) { - idn_recv_nodename_resp(domid, cmdarg1, cmdarg3); - break; - } - switch (nack) { - case IDNNACK_NOCONN: - case IDNNACK_RETRY: - /* - * Remote domain was not quite - * ready, try again. - */ - PR_PROTO("%s:%d: remote not ready " - "for %s - retrying " - "[dstate=%s]\n", - proc, domid, - idncmd_str[IDNCMD_NODENAME], - idnds_str[dp->dstate]); - - if (dp->dstate == IDNDS_CONNECTED) - (void) timeout(idn_retry_nodename_req, - (void *)(uintptr_t)domid, hz); - default: - break; - } - break; - - default: - /* - * Unsupported command. - */ - unsup_cmd_recvd++; - break; - } - if (unsup_cmd_sent) { - PR_PROTO("%s:%d: unsupported command " - "requested (0x%x)\n", - proc, domid, cmdtype); - } - if (unsup_cmd_recvd) { - PR_PROTO("%s:%d: unsupported command " - "response (0x%x)\n", - proc, domid, cmdtype); - } - } else { - /* - * Receiving a regular cmd from a remote domain. - */ - switch (cmdtype) { - case IDNCMD_SLABALLOC: - idn_recv_slaballoc_req(domid, mtp, cmdarg1); - break; - - case IDNCMD_SLABFREE: - idn_recv_slabfree_req(domid, mtp, cmdarg1, cmdarg2); - break; - - case IDNCMD_SLABREAP: - idn_recv_slabreap_req(domid, mtp, cmdarg1); - break; - - case IDNCMD_NODENAME: - idn_recv_nodename_req(domid, mtp, cmdarg1); - break; - - default: - /* - * Unsupported command. - */ - unsup_cmd_recvd++; - break; - } - if (!islocal && unsup_cmd_recvd) { - /* - * Received an unsupported IDN command. - */ - idn_send_cmd_nackresp(domid, mtp, cmdtype, - IDNNACK_BADCMD); - } - } -} - -/* - * This is a supporting routine for idn_broadcast_cmd() to - * handle processing of the requested command for the local - * domain. Currently the only support broadcast command - * supported is reaping. - */ -/*ARGSUSED2*/ -static void -idn_local_cmd(idn_cmd_t cmdtype, uint_t arg1, uint_t arg2, uint_t arg3) -{ - idn_protojob_t *jp; - idn_domain_t *ldp = &idn_domain[idn.localid]; - procname_t proc = "idn_local_cmd"; - - PR_PROTO("%s: submitting local command %s on domain %d\n", - proc, VALID_IDNCMD(cmdtype) ? idncmd_str[cmdtype] : "unknown", - idn.localid); - - - jp = idn_protojob_alloc(KM_SLEEP); - - jp->j_msg.m_domid = ldp->domid; - jp->j_msg.m_msgtype = IDNP_CMD; - jp->j_msg.m_cookie = ldp->dcookie_recv; - SET_XARGS(jp->j_msg.m_xargs, cmdtype, arg1, arg2, arg3); - - idn_protojob_submit(ldp->domid, jp); -} - -/* - * Terminate any outstanding commands that may have - * been targeted for the given domain. A command is - * designated as outstanding if it has an active timer. - * - * serrno = ECANCELED. - */ -static void -idn_terminate_cmd(int domid, int serrno) -{ - idn_domain_t *dp; - idn_timer_t *tplist = NULL, *tp; - procname_t proc = "idn_terminate_cmd"; - - dp = &idn_domain[domid]; - - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - IDN_MSGTIMER_GET(dp, IDNP_CMD, tplist, 0); - /* - * At this point the timers are effectively terminated - * since when they're t_onq indication is set false. - */ - if (tplist == NULL) { - PR_PROTO("%s:%d: no outstanding cmds found\n", - proc, domid); - /* - * There is a window where we may have caught a - * request just prior to issuing the actual - * command (SLABALLOC). We're guaranteed if there - * was, then he will have at least registered. - * So, if we abort the command now, he'll catch - * it before going to sleep. - * Drop through. - */ - } - ASSERT(tplist ? (tplist->t_back->t_forw == NULL) : 1); - - for (tp = tplist; tp; tp = tp->t_forw) { - ASSERT(tp->t_type == IDNP_CMD); - - PR_PROTO("%s:%d: found outstanding cmd: %s\n", - proc, domid, idncmd_str[tp->t_subtype]); - - switch (tp->t_subtype) { - case IDNCMD_SLABALLOC: - /* - * Outstanding slaballoc request may have - * slab waiters hanging around. Need to - * tell them to bail out. The given domain - * must be the master if we have an outstanding - * command to him. This also presumes that - * if there are any waiters they're only in - * the local domain's waiting area (i.e. we're - * a slave). - */ -#ifdef DEBUG - IDN_GLOCK_SHARED(); - ASSERT(domid == IDN_GET_MASTERID()); - ASSERT(idn.localid != IDN_GET_MASTERID()); - IDN_GUNLOCK(); -#endif /* DEBUG */ - (void) smr_slabwaiter_abort(idn.localid, serrno); - break; - - case IDNCMD_SLABFREE: - case IDNCMD_SLABREAP: - case IDNCMD_NODENAME: - /* - * Nothing really waiting for these operations - * so no biggy if we just drop. - * Note that NODENAME may have an outstanding - * buffer, however that will be reclaimed - * when we actually unlink from domain. - */ - break; - - default: - ASSERT(0); - break; - } - } - /* - * As mentioned before the timers are effectively no-op'd - * once they're dequeued, however let's cleanup house and - * get rid of the useless entries in the timeout queue. - */ - if (tplist) { - IDN_TIMER_STOPALL(tplist); - } - - if (idn_domain[idn.localid].dvote.v.master) { - /* - * I'm the master so it's possible I had - * outstanding commands (SLABALLOC) waiting - * to be satisfied for the given domain. - * Since we're forcing an error it's okay - * to continue holding onto the drwlock. - */ - PR_PROTO("%s:%d: abort slaballoc waiters\n", proc, domid); - (void) smr_slabwaiter_abort(domid, serrno); - - } else if (dp->dvote.v.master) { - PR_PROTO("%s:%d: abort (local domain) slaballoc waiters\n", - proc, domid); - (void) smr_slabwaiter_abort(idn.localid, serrno); - } -} - -static void -idn_send_acknack(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ - idn_domain_t *dp = &idn_domain[domid]; - procname_t proc = "idn_send_acknack"; - - ASSERT(mtp ? (mtp->mt_mtype & IDNP_ACKNACK_MASK) : 1); - ASSERT(domid != IDN_NIL_DOMID); - -#ifdef DEBUG - { - STRING(mstr); - STRING(astr); - - INUM2STR(mtp->mt_mtype, mstr); - INUM2STR(mtp->mt_atype, astr); - - if (mtp->mt_mtype & IDNP_ACK) { - PR_PROTO("%s:%d: dstate=%s, msg=(%s/%s), " - "a1=0x%x, a2=0x%x, a3=0x%x, a4 = 0x%x\n", - proc, domid, idnds_str[dp->dstate], - astr, mstr, xargs[0], xargs[1], - xargs[2], xargs[3]); - } else { - idn_nack_t nack; - - nack = GET_XARGS_NACK_TYPE(xargs); - PR_PROTO("%s:%d: dstate=%s, msg=(%s/%s), " - "nack=%s(0x%x)\n", - proc, domid, idnds_str[dp->dstate], - astr, mstr, idnnack_str[nack], - (uint_t)nack); - } - } -#endif /* DEBUG */ - - (void) IDNXDC(domid, mtp, xargs[0], xargs[1], xargs[2], xargs[3]); -} - -/*ARGSUSED0*/ -static void -idn_prealloc_slab(int nslabs) -{ - register int s, serrno; - smr_slab_t *sp; - idn_domain_t *ldp = &idn_domain[idn.localid]; - procname_t proc = "idn_prealloc_slab"; - - IDN_GLOCK_SHARED(); - DSLAB_LOCK_SHARED(idn.localid); - if ((idn.state != IDNGS_ONLINE) || (ldp->dnslabs > 0)) { - /* - * Not in the proper state or slab already allocated. - */ - DSLAB_UNLOCK(idn.localid); - IDN_GUNLOCK(); - return; - } - IDN_GUNLOCK(); - ASSERT(!ldp->dslab); - - serrno = 0; - for (s = 0; (s < nslabs) && ((int)ldp->dnslabs < nslabs); s++) { - /* - * Returns with ldp->drwlock dropped. - */ - serrno = smr_slab_alloc(idn.localid, &sp); - if (serrno != 0) { - PR_PROTO("%s: FAILED to pre-alloc'd " - "slab (serrno = %d)\n", proc, serrno); - break; - } - /* - * State may have changed since smr_slab_alloc - * temporarily drops drwlock. Make sure we're - * still connected. - */ - PR_PROTO("%s: SUCCESSFULLY pre-alloc'd slab\n", proc); - - if (idn.state != IDNGS_ONLINE) { - PR_PROTO("%s: Lost connection..leaving\n", proc); - break; - } - } - - DSLAB_UNLOCK(idn.localid); -} - -/* - * Received a request from a remote domain to - * allocate a slab from the master SMR for him. - * Allocate slab and return the response. - */ -static void -idn_recv_slaballoc_req(int domid, idn_msgtype_t *mtp, uint_t slab_size) -{ - register idn_domain_t *dp; - procname_t proc = "idn_recv_slaballoc_req"; - - PR_PROTO("%s: slaballoc req from domain %d (size=0x%x)\n", - proc, domid, slab_size); - - dp = &idn_domain[domid]; - - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - IDN_GLOCK_SHARED(); - - if (idn.localid != IDN_GET_MASTERID()) { - IDN_GUNLOCK(); - /* - * It's a fatal error if the remote domain thinks - * we're the master. - */ - idn_send_slaballoc_resp(domid, mtp, 0, 0, EACCES); - - } else if (dp->dstate != IDNDS_CONNECTED) { - - IDN_GUNLOCK(); - /* - * It's a fatal error if we don't yet have a - * connection established with the requestor. - */ - idn_send_slaballoc_resp(domid, mtp, 0, 0, ENOLINK); - } else { - int serrno; - smr_slab_t *sp; - smr_offset_t slab_offset; - - IDN_GUNLOCK(); - DSLAB_LOCK_SHARED(domid); - IDN_DUNLOCK(domid); - /* - * We're connected and we're the master. - * smr_slab_alloc() returns with dp->drwlock dropped. - */ - if ((serrno = smr_slab_alloc(domid, &sp)) == 0) { - /* - * Successfully allocated slab for remote slave. - */ - slab_offset = IDN_ADDR2OFFSET(sp->sl_start); - slab_size = sp->sl_end - sp->sl_start; - ASSERT((slab_offset != 0) && (slab_size != 0)); - } else { - slab_offset = slab_size = 0; - } - DSLAB_UNLOCK(domid); - /* - * The drwlock is dropped during smr_slab_alloc. - * During that time our connection with the given - * domain may have changed. Better check again. - */ - IDN_DLOCK_SHARED(domid); - if ((dp->dstate != IDNDS_CONNECTED) && !serrno) { - /* - * Connection broke. Keep the slab here. - */ - DSLAB_LOCK_EXCL(domid); - IDN_DUNLOCK(domid); - smr_slab_free(domid, sp); - DSLAB_UNLOCK(domid); - slab_offset = slab_size = 0; - serrno = ECANCELED; - IDN_DLOCK_SHARED(domid); - } - /* - * Send response. - * Note that smr_slab_alloc automatically installs - * slab into domains respective idn_domain entry - * to be associated with that domain. - */ - idn_send_slaballoc_resp(domid, mtp, slab_offset, slab_size, - serrno); - } -} - -static void -idn_send_slaballoc_resp(int domid, idn_msgtype_t *mtp, smr_offset_t slab_offset, - uint_t slab_size, int serrno) -{ - procname_t proc = "idn_send_slaballoc_resp"; - - PR_PROTO("%s: slaballoc resp to domain %d (off=0x%x, size=0x%x) " - "[serrno = %d]\n", - proc, domid, slab_offset, slab_size, serrno); - - idn_send_cmdresp(domid, mtp, IDNCMD_SLABALLOC, slab_offset, slab_size, - serrno); -} - -/* - * Received the ack or nack to a previous allocation request - * made by the local domain to the master for a slab. Need - * to "put" the response into the waiting area for any - * waiters. - */ -static void -idn_recv_slaballoc_resp(int domid, smr_offset_t slab_offset, uint_t slab_size, - int serrno) -{ - smr_slab_t *sp = NULL; - int rv; - procname_t proc = "idn_recv_slaballoc_resp"; - - - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - PR_PROTO("%s: slaballoc resp from domain %d (off=0x%x, size=0x%x) " - "[serrno = %d]\n", - proc, domid, slab_offset, slab_size, serrno); - - if (!serrno) { - IDN_GLOCK_SHARED(); - if (domid != IDN_GET_MASTERID()) { - /* - * We should only be receiving responses from - * our master. This is either a bogus message - * or an old response. In either case dump it. - */ - PR_PROTO("%s: BOGUS slaballoc resp from domid %d " - "(master = %d)\n", - proc, domid, IDN_GET_MASTERID()); - serrno = EPROTO; - } - IDN_GUNLOCK(); - - if (!serrno && - !VALID_NWROFFSET(slab_offset, IDN_SMR_BUFSIZE)) { - PR_PROTO("%s: slab offset (0x%x) out of range " - "(0-0x%lx)\n", - proc, slab_offset, MB2B(IDN_NWR_SIZE)); - serrno = EPROTO; - } else if (!serrno) { - sp = GETSTRUCT(smr_slab_t, 1); - sp->sl_start = IDN_OFFSET2ADDR(slab_offset); - sp->sl_end = sp->sl_start + slab_size; - smr_alloc_buflist(sp); - } - } - - /* - * Always "put" slabs back to yourself since you're a slave. - * Note that we set the forceflag so that even if there are - * no waiters we still install the slab for the domain. - */ - if (!serrno) { - DSLAB_LOCK_EXCL(idn.localid); - } - rv = smr_slaballoc_put(idn.localid, sp, 1, serrno); - if (!serrno) { - DSLAB_UNLOCK(idn.localid); - } - - if (rv < 0) { - /* - * Some kind of error trying to install response. - * If there was a valid slab sent to us, we'll - * just have to send it back. - */ - PR_PROTO("%s: failed to install response in waiting area\n", - proc); - if (slab_size != 0) { - PR_PROTO("%s: sending slab back to domain %d " - "(master = %d)\n", - proc, domid, IDN_GET_MASTERID()); - idn_send_cmd(domid, IDNCMD_SLABFREE, slab_offset, - slab_size, 0); - } - if (sp) { - smr_free_buflist(sp); - FREESTRUCT(sp, smr_slab_t, 1); - } - } -} - -/* - * Note that slab reaping is effectively performed asynchronously - * since the request will be received a protocol server. - */ -static void -idn_recv_slabreap_req(int domid, idn_msgtype_t *mtp, int nslabs) -{ - procname_t proc = "idn_recv_slabreap_req"; - - PR_PROTO("%s: slab reap request (nslabs = %d)\n", proc, nslabs); - - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - IDN_GLOCK_SHARED(); - if (domid != IDN_GET_MASTERID()) { - /* - * Only the master can request that slabs be reaped. - */ - IDN_GUNLOCK(); - PR_PROTO("%s: only master can request slab reaping\n", proc); - - idn_send_cmdresp(domid, mtp, IDNCMD_SLABREAP, 0, 0, EACCES); - - return; - } - IDN_GUNLOCK(); - - if (nslabs != 0) { - IDN_DUNLOCK(domid); - smr_slab_reap(idn.localid, &nslabs); - IDN_DLOCK_SHARED(domid); - } - - PR_PROTO("%s: slab reap result (nslabs = %d)\n", proc, nslabs); - - /* - * Go ahead and send the reap response back before we start - * free'ing off the individual slabs. - */ - idn_send_slabreap_resp(domid, mtp, nslabs, 0); -} - -static void -idn_recv_slabreap_resp(int domid, int nslabs, int serrno) -{ - procname_t proc = "idn_recv_slabreap_resp"; - - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - if ((idn.localid != IDN_GET_MASTERID()) || (idn.localid == domid)) { - PR_PROTO("%s: unexpected slabreap resp received " - "(domid = %d)\n", proc, domid); - ASSERT(0); - return; - } - PR_PROTO("%s: recvd reap response from domain %d for %d slabs " - "[serrno = %d]\n", proc, domid, nslabs, serrno); -} - -/* - * Not really necessary to send slabreap response. - * XXX - perhaps useful to master for accounting or - * throttling of further reaping? - */ -static void -idn_send_slabreap_resp(int domid, idn_msgtype_t *mtp, int nslabs, int serrno) -{ - idn_send_cmdresp(domid, mtp, IDNCMD_SLABREAP, nslabs, 0, serrno); -} - -/* - * Slave -> Master ONLY - * Master never sends slabfree request to itself. - */ -static void -idn_recv_slabfree_req(int domid, idn_msgtype_t *mtp, smr_offset_t slab_offset, - uint_t slab_size) -{ - smr_slab_t *sp; - int serrno; - caddr_t s_start, s_end; - procname_t proc = "idn_recv_slabfree_req"; - - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - if (domid == IDN_GET_MASTERID()) { - PR_PROTO("%s: unexpected slabfree req received (domid = %d)\n", - proc, domid); - idn_send_slabfree_resp(domid, mtp, slab_offset, slab_size, - EACCES); - return; - } - if (slab_size > IDN_SLAB_SIZE) { - PR_PROTO("%s: unexpected slab size. exp %d, recvd %d\n", - proc, IDN_SLAB_SIZE, slab_size); - idn_send_slabfree_resp(domid, mtp, slab_offset, slab_size, - EINVAL); - return; - } - s_start = IDN_OFFSET2ADDR(slab_offset); - s_end = s_start + slab_size; - /* - * Master has received a SLABFREE request (effectively a response - * to some earlier SLABREAP request. - * Find the slab associated with this slab and free it up. - */ - DSLAB_LOCK_EXCL(domid); - if ((sp = smr_slaballoc_get(domid, s_start, s_end)) != NULL) { - smr_slab_free(domid, sp); - serrno = 0; - } else { - serrno = EINVAL; - } - DSLAB_UNLOCK(domid); - - idn_send_slabfree_resp(domid, mtp, slab_offset, slab_size, serrno); -} - -/* - * Master -> Slave ONLY - */ -static void -idn_recv_slabfree_resp(int domid, uint_t slab_offset, uint_t slab_size, int - serrno) -{ - procname_t proc = "idn_recv_slabfree_resp"; - - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - if (domid != IDN_GET_MASTERID()) { - PR_PROTO("%s: unexpected slabfree resp received (domid = %d)\n", - proc, domid); - ASSERT(0); - return; - } - if (slab_size > IDN_SLAB_SIZE) { - PR_PROTO("%s: unexpected slab size. exp %d, recvd %d\n", - proc, IDN_SLAB_SIZE, slab_size); - ASSERT(0); - return; - } - PR_PROTO("%s: recvd free resp from dom %d " - "- slab (off/size) 0x%x/0x%x [serrno = %d]\n", - proc, domid, slab_offset, slab_size, serrno); -} - -static void -idn_send_slabfree_resp(int domid, idn_msgtype_t *mtp, uint_t slab_offset, - uint_t slab_size, int serrno) -{ - idn_send_cmdresp(domid, mtp, IDNCMD_SLABFREE, slab_offset, slab_size, - serrno); -} - -static void -idn_retry_nodename_req(void *arg) -{ - int domid = (int)(uintptr_t)arg; - - idn_send_nodename_req(domid); -} - -static void -idn_send_nodename_req(int domid) -{ - caddr_t b_bufp; - smr_offset_t bufoffset; - int serrno; - idn_domain_t *dp = &idn_domain[domid]; - procname_t proc = "idn_send_nodename_req"; - - /* - * Need to drop domain lock across - * SMR allocation. - */ - serrno = smr_buf_alloc(domid, MAXDNAME+1, &b_bufp); - - IDN_DLOCK_SHARED(domid); - if (dp->dstate != IDNDS_CONNECTED) { - /* - * Lost connection. - */ - PR_PROTO("%s:%d: connection lost [dstate = %s]\n", - proc, domid, idnds_str[dp->dstate]); - IDN_DUNLOCK(domid); - if (!serrno) - (void) smr_buf_free(domid, b_bufp, MAXDNAME+1); - return; - } - if (serrno) { - /* - * Failed to allocate buffer, but still have - * connection so keep trying. We may have queried - * the master a little too earlier. - */ - PR_PROTO("%s:%d: buffer alloc failed [dstate = %s]\n", - proc, domid, idnds_str[dp->dstate]); - (void) timeout(idn_retry_nodename_req, (void *)(uintptr_t)domid, - hz); - IDN_DUNLOCK(domid); - return; - } - - *b_bufp = (char)MAXDNAME; - bufoffset = IDN_ADDR2OFFSET(b_bufp); - - idn_send_cmd(domid, IDNCMD_NODENAME, bufoffset, 0, 0); - IDN_DUNLOCK(domid); -} - -static void -idn_send_nodename_resp(int domid, idn_msgtype_t *mtp, smr_offset_t bufoffset, - int serrno) -{ - idn_send_cmdresp(domid, mtp, IDNCMD_NODENAME, (uint_t)bufoffset, 0, - serrno); -} - -static void -idn_recv_nodename_req(int domid, idn_msgtype_t *mtp, smr_offset_t bufoffset) -{ - caddr_t b_bufp; - int length; - idn_domain_t *ldp = &idn_domain[idn.localid]; - procname_t proc = "idn_recv_nodename_req"; - - IDN_DLOCK_EXCL(idn.localid); - if (!strlen(ldp->dname)) { - if (!strlen(utsname.nodename)) { - /* - * Local domain's nodename hasn't been - * set yet. - */ - IDN_DUNLOCK(idn.localid); - idn_send_cmd_nackresp(domid, mtp, IDNCMD_NODENAME, - IDNNACK_RETRY); - return; - } - (void) strncpy(ldp->dname, utsname.nodename, MAXDNAME - 1); - } - IDN_DLOCK_DOWNGRADE(idn.localid); - - if (!VALID_NWROFFSET(bufoffset, IDN_SMR_BUFSIZE)) { - PR_PROTO("%s:%d: invalid SMR offset received (0x%x)\n", - proc, domid, bufoffset); - IDN_DUNLOCK(idn.localid); - idn_send_nodename_resp(domid, mtp, bufoffset, EINVAL); - return; - } - - b_bufp = IDN_OFFSET2ADDR(bufoffset); - length = (int)(*b_bufp++ & 0xff); - - if (length < strlen(ldp->dname)) { - PR_PROTO("%s:%d: buffer not big enough (req %lu, got %d)\n", - proc, domid, strlen(ldp->dname), length); - IDN_DUNLOCK(idn.localid); - idn_send_nodename_resp(domid, mtp, bufoffset, EINVAL); - return; - } - - (void) strncpy(b_bufp, ldp->dname, MAXDNAME); - b_bufp[MAXDNAME-1] = 0; - IDN_DUNLOCK(idn.localid); - - idn_send_nodename_resp(domid, mtp, bufoffset, 0); -} - -static void -idn_recv_nodename_resp(int domid, smr_offset_t bufoffset, int serrno) -{ - caddr_t b_bufp; - idn_domain_t *dp = &idn_domain[domid]; - procname_t proc = "idn_recv_nodename_resp"; - - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - if (!VALID_NWROFFSET(bufoffset, IDN_SMR_BUFSIZE)) { - PR_PROTO("%s:%d: invalid SMR offset received (0x%x)\n", - proc, domid, bufoffset); - return; - } - - if (serrno == 0) { - b_bufp = IDN_OFFSET2ADDR(bufoffset) + 1; - b_bufp[MAXDNAME-1] = 0; - - if (strlen(b_bufp) > 0) { - (void) strncpy(dp->dname, b_bufp, MAXDNAME); - PR_PROTO("%s:%d: received nodename(%s)\n", - proc, domid, dp->dname); - } - } - - (void) smr_buf_free(domid, b_bufp - 1, MAXDNAME + 1); -} - -/* - * The master allocations the SMR management structures. - */ -static int -idn_master_init() -{ - idn_domain_t *ldp = &idn_domain[idn.localid]; - size_t reserved_size = 0; - caddr_t reserved_area = NULL; - procname_t proc = "idn_master_init"; - - ASSERT(IDN_GLOCK_IS_EXCL()); - ASSERT(IDN_DLOCK_IS_EXCL(idn.localid)); - - if (idn.mboxarea != NULL) { - PR_PROTO("%s: master data already initialized\n", proc); - return (0); - } - - PR_PROTO("%s: initializing master data (domid = %d)\n", - proc, idn.localid); - - /* - * Reserve an area of the SMR for mailbox usage. - * This area is allocated to other domains via - * the master. Round it up to IDN_SMR_BUFSIZE multiple. - */ - reserved_size = IDNROUNDUP(IDN_MBOXAREA_SIZE, IDN_SMR_BUFSIZE); - - PR_PROTO("%s: reserving %lu bytes for mailbox area\n", - proc, reserved_size); - -#ifdef DEBUG - if (reserved_size > (size_t)IDN_SLAB_SIZE) { - PR_PROTO("%s: WARNING mbox area (%ld) > slab size (%d)\n", - proc, reserved_size, IDN_SLAB_SIZE); - } -#endif /* DEBUG */ - /* - * Initialize the pool of slabs and SMR I/O buffers. - */ - if (smr_slabpool_init(reserved_size, &reserved_area) != 0) { - idn_master_deinit(); - return (-1); - } - - ASSERT(idn.mboxarea == NULL); - ASSERT(reserved_area); - - bzero(reserved_area, reserved_size); - - idn.mboxarea = (idn_mboxtbl_t *)reserved_area; - ldp->dmbox.m_tbl = IDN_MBOXAREA_BASE(idn.mboxarea, idn.localid); - /* - * Initialize the SMR pointers in the entire - * mailbox table. - */ - idn_mboxarea_init(idn.mboxarea, IDN_MBOXAREA_SIZE / IDN_MBOXTBL_SIZE); - - return (0); -} - -static void -idn_master_deinit() -{ - idn_domain_t *ldp; - smr_slab_t *sp; - procname_t proc = "idn_master_deinit"; - - ASSERT(IDN_GLOCK_IS_EXCL()); - ASSERT(IDN_DLOCK_IS_EXCL(idn.localid)); - - if (idn.mboxarea == NULL) { - PR_PROTO("%s: master data already deinitialized\n", proc); - return; - } - - ldp = &idn_domain[idn.localid]; - - PR_PROTO("%s: deinitializing master data (domid = %d)\n", - proc, idn.localid); - - ldp->dmbox.m_tbl = NULL; - idn.mboxarea = NULL; - /* - * Master may still be holding onto slabs of his own. - */ - DSLAB_LOCK_EXCL(idn.localid); - sp = ldp->dslab; - ldp->dslab = NULL; - ldp->dnslabs = 0; - if (sp) - smr_slab_free(idn.localid, sp); - ldp->dslab_state = DSLAB_STATE_UNKNOWN; - DSLAB_UNLOCK(idn.localid); - - smr_slabpool_deinit(); -} - -static int -idn_mark_awol(int domid, clock_t *atime) -{ - clock_t awol; - idn_domain_t *dp = &idn_domain[domid]; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_GLOCK_IS_EXCL()); - - if (!DOMAIN_IN_SET(idn.domset.ds_awol, domid)) { - DOMAINSET_ADD(idn.domset.ds_awol, domid); - idn.nawols++; - } - awol = ddi_get_lbolt(); - if (dp->dawol.a_count++ == 0) - dp->dawol.a_time = awol; - dp->dawol.a_last = awol; - if ((awol - dp->dawol.a_msg) >= (clock_t)(idn_awolmsg_interval * hz)) - dp->dawol.a_msg = awol; - else - awol = 0; - - *atime = awol; - - idn_awol_event_set(dp->dhw.dh_boardset); - - return (dp->dawol.a_count); -} - -void -idn_clear_awol(int domid) -{ - idn_domain_t *dp = &idn_domain[domid]; - - ASSERT(IDN_SYNC_IS_LOCKED()); - ASSERT(IDN_GLOCK_IS_EXCL()); - if (DOMAIN_IN_SET(idn.domset.ds_awol, domid)) { - DOMAINSET_DEL(idn.domset.ds_awol, domid); - idn.nawols--; - } - if (dp->dawol.a_count > 0) { - dp->dawol.a_count = 0; - dp->dawol.a_last = dp->dawol.a_time; - dp->dawol.a_time = 0; - dp->dawol.a_msg = 0; - - idn_awol_event_clear(dp->dhw.dh_boardset); - } -} - -/* - * A timer expired. - */ -void -idn_timer_expired(void *arg) -{ - idn_domain_t *dp; - char *op = "UNKNOWN"; - clock_t awol = 0; - int awolcount, dcpu, domid; - idn_timer_t *tp = (idn_timer_t *)arg; - idn_timerq_t *tq = NULL; - uint_t token; - char dname[MAXDNAME]; - procname_t proc = "idn_timer_expired"; - STRING(str); - - tq = tp->t_q; - - ASSERT(tp->t_domid != IDN_NIL_DOMID); - - IDN_TIMERQ_LOCK(tq); - - INUM2STR(tp->t_type, str); - - if (tp->t_onq == 0) { - PR_TIMER("%s: timer CAUGHT TERMINATION (type = %s)\n", - proc, str); - /* - * Timer was dequeued. Somebody is trying - * to shut it down. - */ - IDN_TIMERQ_UNLOCK(tq); - return; - } - - IDN_TIMER_DEQUEUE(tq, tp); - - IDN_TIMERQ_UNLOCK(tq); - - IDN_SYNC_LOCK(); - IDN_DLOCK_EXCL(tp->t_domid); - - domid = tp->t_domid; - - dp = &idn_domain[domid]; - (void) strcpy(dname, dp->dname); - dcpu = dp->dcpu; - - IDN_TIMER_EXEC(tp); - -#ifdef DEBUG - PR_TIMER("%s:%d: [%s] timer EXPIRED (C=0x%x, P=0x%llx, X=0x%llx)\n", - proc, tp->t_domid, str, tp->t_cookie, - tp->t_posttime, tp->t_exectime); -#endif /* DEBUG */ - - /* - * IMPORTANT: - * Each case is responsible for dropping SYNC_LOCK & DLOCK. - */ - switch (tp->t_type) { - case IDNP_DATA: - IDN_SYNC_UNLOCK(); - /* - * Timed out waiting for a data packet response. - * We can't close domain since he may just be - * temporarily AWOL. - * Note that dio and diocheck do not get cleared. - * This is taken care of when the domain restarts - * or is fatally closed. - * We only need a reader lock for this. - */ - IDN_DLOCK_DOWNGRADE(domid); - if (dp->diocheck && dp->dmbox.m_send) { - (void) idn_reclaim_mboxdata(domid, 0, -1); - if (dp->dio >= IDN_WINDOW_EMAX) { - idn_msgtype_t mt; - /* - * Restart timer for another - * go around. - */ - IDN_MSGTIMER_START(domid, IDNP_DATA, 0, - idn_msg_waittime[IDNP_DATA], - &mt.mt_cookie); - } else { - lock_clear(&dp->diocheck); - } - } - IDN_DUNLOCK(domid); - break; - - case IDNP_NEGO: - /* - * If we're not in a NEGO transition, then - * just ignore this timeout. - */ - if (dp->dxp == &xphase_nego) { - uint_t token; - - IDN_GLOCK_EXCL(); - op = "CONNECT"; - awolcount = idn_mark_awol(domid, &awol); - IDN_GUNLOCK(); - - idn_nego_cleanup_check(domid, IDN_NIL_DOMID, - IDN_NIL_DCPU); - - IDN_XSTATE_TRANSITION(dp, IDNXS_PEND); - token = IDN_RETRY_TOKEN(domid, IDNRETRY_NEGO); - idn_retry_submit(idn_retry_nego, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_NEGO]); - } - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - break; - - case IDNP_CMD: - /* - * Timeouts on commands typically mean that the - * the master is not responding. Furthermore, we - * can't FORCE a FIN disconnect since at this stage - * we are CONNECTED and thus other domains may - * have cache entries that we're sharing with them. - * Only choice is to completely disconnect from - * IDN and try to reestablish connection. - * - * However, timeouts attempting to get nodename - * are not fatal. Although we don't want to retry - * either since each timeout is a lost buffer to - * the remote domain. - */ - if (tp->t_subtype == (ushort_t)IDNCMD_NODENAME) { - PR_PROTO("%s:%d: timedout waiting for nodename\n", - proc, domid); - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - break; - } - - IDN_GLOCK_EXCL(); - if (idn.state == IDNGS_ONLINE) { - domainset_t domset; - int masterid = IDN_GET_MASTERID(); - - IDN_GKSTAT_GLOBAL_EVENT(gk_reconfigs, - gk_reconfig_last); - - PR_PROTO("%s:%d: RECONFIG trying old masterid = %d\n", - proc, domid, masterid); - - IDN_GSTATE_TRANSITION(IDNGS_RECONFIG); - IDN_SET_NEW_MASTERID(masterid); - IDN_GUNLOCK(); - IDN_DUNLOCK(domid); - - domset = idn.domset.ds_trans_on | - idn.domset.ds_connected; - - idn_unlink_domainset(domset, IDNFIN_NORMAL, - IDNFIN_ARG_NONE, IDNFIN_OPT_RELINK, BOARDSET_ALL); - } else { - IDN_GUNLOCK(); - IDN_DUNLOCK(domid); - } - IDN_SYNC_UNLOCK(); - break; - - case IDNP_CON: - if (tp->t_subtype == (ushort_t)IDNCON_QUERY) { - /* - * Timed out sending a CON-query. This is - * non-fatal. We simply need to retry. - */ - IDN_GLOCK_EXCL(); - op = "CONNECT"; - awolcount = idn_mark_awol(domid, &awol); - IDN_GUNLOCK(); - token = IDN_RETRY_TOKEN(domid, IDNRETRY_CONQ); - idn_retry_submit(idn_retry_query, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_CONQ]); - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - break; - } - /*FALLTHROUGH*/ - case IDNP_CFG: - /* - * Any timeouts here we simply try to disconnect - * and reestablish the link. Since we haven't - * reached the connected state w.r.t. this domain - * we put his fin state to FORCE-HARD in order - * to shoot right through without involving other - * domains. Recall that other domains may have - * established connections with the given domain - * which means any FIN queries to them will always - * return connected to the given domain. Since - * neither the given domain nor the local domain - * plan on disconnecting from the IDN the connection - * to the other domains will remain thereby preventing - * the local FIN from ever completing. Recall that - * a FIN depends on all member domains FIN'ing also. - */ - IDN_GLOCK_EXCL(); - op = "CONNECT"; - awolcount = idn_mark_awol(domid, &awol); - IDN_GUNLOCK(); - DOMAINSET_ADD(idn.domset.ds_relink, domid); - IDN_HISTORY_LOG(IDNH_RELINK, domid, dp->dstate, - idn.domset.ds_relink); - (void) idn_disconnect(domid, IDNFIN_FORCE_SOFT, - IDNFIN_ARG_NONE, IDNFIN_SYNC_NO); - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - break; - - case IDNP_FIN: - /* - * Timeouts here simply try to retry. - */ - IDN_GLOCK_EXCL(); - op = "DISCONNECT"; - awolcount = idn_mark_awol(domid, &awol); - IDN_GUNLOCK(); - if (tp->t_subtype == (ushort_t)IDNFIN_QUERY) { - int d; - domainset_t rdyset; - /* - * Timed out sending a FIN-query. This is - * non-fatal. We simply need to retry. - * If we were doing a forced unlink of any - * domains, we don't want this awol guy - * to hold us up. Looks for any forced - * unlinks and make them "ready" with - * respect to this awol domain. - */ - rdyset = 0; - for (d = 0; d < MAX_DOMAINS; d++) { - if (FIN_IS_FORCE(idn_domain[d].dfin)) { - DOMAINSET_ADD(rdyset, d); - } - } - if (rdyset) - (void) idn_sync_register(domid, - IDNSYNC_DISCONNECT, - rdyset, IDNSYNC_REG_REG); - - token = IDN_RETRY_TOKEN(domid, IDNRETRY_FINQ); - idn_retry_submit(idn_retry_query, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_FINQ]); - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - break; - } - - if (dp->dfin == IDNFIN_FORCE_SOFT) { - IDN_FSTATE_TRANSITION(dp, IDNFIN_FORCE_HARD); - } - /* - * Anybody that was waiting on this domain and - * had a hard-force in action gets this guy for - * free in their base ready-set. - */ - idn_sync_register_awol(domid); - - dp->dxp = &xphase_fin; - IDN_XSTATE_TRANSITION(dp, IDNXS_PEND); - token = IDN_RETRY_TOKEN(domid, IDNRETRY_FIN); - idn_retry_submit(idn_retry_fin, NULL, token, - idn_msg_retrytime[(int)IDNRETRY_FIN]); - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - break; - - default: - - ASSERT(0); - IDN_DUNLOCK(domid); - IDN_SYNC_UNLOCK(); - break; - } - - IDN_TIMER_FREE(tp); - - if (awol) { - if (strlen(dname) > 0) { - cmn_err(CE_WARN, - "IDN: 236: domain (%s) [ID %d] not " - "responding to %s [#%d]", - dname, domid, op, awolcount); - } else { - cmn_err(CE_WARN, - "IDN: 236: domain [ID %d, CPU %d] not " - "responding to %s [#%d]", - domid, dcpu, op, awolcount); - } - } -} - -#if 0 -static int -idn_retry_check(uint_t token) -{ - int i, count = 0; - int domid = IDN_RETRY_TOKEN2DOMID(token); - int key = IDN_RETRY_TOKEN2TYPE(token); - idn_retry_job_t *rp; - idn_retry_queue_t *qp; - - qp = &idn.retryqueue; - - mutex_enter(&qp->rq_mutex); - - for (i = 0, rp = qp->rq_jobs; i < qp->rq_count; i++, rp = rp->rj_next) - if ((domid == IDN_RETRY_TOKEN2DOMID(rp->rj_token)) && - ((key == IDN_RETRY_TYPEALL) || (rp->rj_token == token))) - count++; - - mutex_exit(&qp->rq_mutex); - - return (count); -} -#endif /* 0 */ - -static void -idn_retry_execute(void *arg) -{ - idn_retry_job_t *rp = (idn_retry_job_t *)arg; - idn_retry_queue_t *qp; - - qp = &idn.retryqueue; - - mutex_enter(&qp->rq_mutex); - if (rp->rj_onq == 0) { - /* - * Job has already been claimed by - * retry termination routine. - * Bail out. - */ - mutex_exit(&qp->rq_mutex); - return; - } - rp->rj_next->rj_prev = rp->rj_prev; - rp->rj_prev->rj_next = rp->rj_next; - if (--(qp->rq_count) == 0) - qp->rq_jobs = NULL; - else if (qp->rq_jobs == rp) - qp->rq_jobs = rp->rj_next; - mutex_exit(&qp->rq_mutex); - - (*rp->rj_func)(rp->rj_token, rp->rj_arg); - - IDNRETRY_FREEJOB(rp); -} - -/* - * - */ -static void -idn_retry_submit(void (*func)(uint_t token, void *arg), void *arg, uint_t token, - clock_t ticks) -{ - idn_retry_job_t *rp, *cp; - idn_retry_queue_t *qp; - int c; - procname_t proc = "idn_retry_submit"; - - if (ticks < 0) { - PR_PROTO("%s: (token = 0x%x) WARNING ticks = %ld\n", - proc, token, ticks); - return; - } - if (ticks == 0) /* At least one tick to get into background */ - ticks++; - - PR_PROTO("%s: token = 0x%x\n", proc, token); - - qp = &idn.retryqueue; - - mutex_enter(&qp->rq_mutex); - for (c = 0, cp = qp->rq_jobs; c < qp->rq_count; cp = cp->rj_next, c++) { - if (cp->rj_token == token) { - PR_PROTO("%s: token = (%d,0x%x) already present\n", - proc, IDN_RETRY_TOKEN2DOMID(token), - IDN_RETRY_TOKEN2TYPE(token)); - break; - } - } - - if (c < qp->rq_count) { - mutex_exit(&qp->rq_mutex); - return; - } - - rp = IDNRETRY_ALLOCJOB(); - rp->rj_func = func; - rp->rj_arg = arg; - rp->rj_token = token; - rp->rj_prev = rp->rj_next = rp; - - if (qp->rq_jobs == NULL) { - qp->rq_jobs = rp; - } else { - rp->rj_next = qp->rq_jobs; - rp->rj_prev = qp->rq_jobs->rj_prev; - rp->rj_next->rj_prev = rp; - rp->rj_prev->rj_next = rp; - } - rp->rj_onq = 1; - qp->rq_count++; - rp->rj_id = timeout(idn_retry_execute, (caddr_t)rp, ticks); - mutex_exit(&qp->rq_mutex); -} - -int -idn_retry_terminate(uint_t token) -{ - int i, domid; - uint_t key, count; - idn_retry_job_t *rp, *nrp, *fp; - idn_retry_queue_t *qp; - procname_t proc = "idn_retry_terminate"; - - key = IDN_RETRY_TOKEN2TYPE(token); - domid = IDN_RETRY_TOKEN2DOMID(token); - fp = NULL; - qp = &idn.retryqueue; - - mutex_enter(&qp->rq_mutex); - for (i = count = 0, rp = qp->rq_jobs; i < qp->rq_count; i++) { - nrp = rp->rj_next; - if ((domid == IDN_RETRY_TOKEN2DOMID(rp->rj_token)) && - ((key == IDN_RETRY_TYPEALL) || - (rp->rj_token == token))) { - /* - * Turn off onq field as a signal to - * the execution routine that this - * retry has been terminated. This - * is necessary since we can't untimeout - * while holding the rq_mutex otherwise - * we'll deadlock with the execution - * routine. We'll untimeout these guys - * _after_ we drop rq_mutex. - */ - rp->rj_onq = 0; - rp->rj_next->rj_prev = rp->rj_prev; - rp->rj_prev->rj_next = rp->rj_next; - if (qp->rq_jobs == rp) - qp->rq_jobs = rp->rj_next; - rp->rj_next = fp; - fp = rp; - count++; - } - rp = nrp; - } - - if ((qp->rq_count -= count) == 0) - qp->rq_jobs = NULL; - - mutex_exit(&qp->rq_mutex); - - PR_PROTO("%s: token = (%d,0x%x), dequeued = %d\n", - proc, domid, key, count); - - for (; fp; fp = nrp) { - (void) untimeout(fp->rj_id); - - nrp = fp->rj_next; - IDNRETRY_FREEJOB(fp); - } - - return (count); -} - -/* - * ----------------------------------------------------------------------- - * The sole purpose of the idn_protocol_server is to manage the IDN - * protocols between the various domains. These messages do _not_ go - * through the regular streams queues since they are not dependent on - * any user process or module necessarily having the IDN driver open. - * There may be multiple instances of these servers to enhance performance - * of domain management. Each server is assigned a idn_protoqueue_t - * from which to obtain the work they need to do. - * ----------------------------------------------------------------------- - */ -int -idn_protocol_init(int nservers) -{ - int i; - idn_protojob_t *jp; - register idn_protoqueue_t *protoq; - - if (nservers <= 0) { - cmn_err(CE_WARN, - "IDN: 237: invalid number (%d) of protocol servers", - nservers); - return (-1); - } - - idn.protocol.p_jobpool = kmem_cache_create("idn_protocol_jobcache", - sizeof (idn_protojob_t), 0, NULL, NULL, NULL, NULL, NULL, 0); - if (idn.protocol.p_jobpool == NULL) { - cmn_err(CE_WARN, - "IDN: 238: kmem_cache_create(jobcache) failed"); - return (-1); - } - - /* - * Initialize static cache for protojob. - */ - mutex_init(&idn_protojob_cache_lock, NULL, MUTEX_DRIVER, NULL); - jp = &idn_protojob_cache[0]; - for (i = 1; i < IDN_DMV_PENDING_MAX; jp = jp->j_next, i++) { - jp->j_cache = 1; - jp->j_next = &idn_protojob_cache[i]; - } - jp->j_cache = 1; - jp->j_next = NULL; - idn_protojob_cache_list = &idn_protojob_cache[0]; - - /* - * Init morgue semaphore. - */ - sema_init(&idn.protocol.p_morgue, 0, NULL, SEMA_DEFAULT, NULL); - /* - * Alloc server queues. - */ - idn.protocol.p_serverq = GETSTRUCT(idn_protoqueue_t, nservers); - - /* - * Init server queues. - */ - protoq = idn.protocol.p_serverq; - for (i = 0; i < nservers; protoq++, i++) { - mutex_init(&protoq->q_mutex, NULL, MUTEX_DRIVER, NULL); - cv_init(&protoq->q_cv, NULL, CV_DEFAULT, NULL); - protoq->q_id = i; - protoq->q_joblist = NULL; - protoq->q_joblist_tail = NULL; - protoq->q_die = 0; - protoq->q_morgue = &idn.protocol.p_morgue; - /* - * Create protocol server thread. - */ - protoq->q_threadp = thread_create(NULL, 0, - idn_protocol_server, (caddr_t)&i, sizeof (i), &p0, - TS_RUN, maxclsyspri); - } - /* - * The servers are kept in the p_server[] array, however - * we'll build a linked list of them to facilitate debugging. - */ - protoq = idn.protocol.p_serverq; - for (i = 0; i < (nservers - 1); protoq++, i++) - protoq->q_next = (protoq + 1); - protoq->q_next = NULL; - - idn.nservers = nservers; - - return (idn.nservers); -} - -void -idn_protocol_deinit() -{ - register int i; - int nservers; - register idn_protoqueue_t *protoq; - - nservers = idn.nservers; - - if (nservers <= 0) - return; - - /* - * Make sure the servers are dead. - */ - idn_protocol_server_killall(); - ASSERT(idn.nservers == 0); - /* - * Destroy the mutexes. - */ - protoq = idn.protocol.p_serverq; - for (i = 0; i < nservers; protoq++, i++) { - mutex_destroy(&protoq->q_mutex); - cv_destroy(&protoq->q_cv); - } - /* - * Free up the protoqueue memory. - */ - FREESTRUCT(idn.protocol.p_serverq, idn_protoqueue_t, nservers); - idn.protocol.p_serverq = NULL; - /* - * Destroy the morgue semaphore. - */ - sema_destroy(&idn.protocol.p_morgue); - - if (idn.protocol.p_jobpool) { - kmem_cache_destroy(idn.protocol.p_jobpool); - idn.protocol.p_jobpool = NULL; - } -} - -static void -idn_protocol_server(int *id) -{ - idn_protoqueue_t *pq; - idn_protojob_t *jl; - register idn_protojob_t *jp; - procname_t proc = "idn_protocol_server"; - - if (id == NULL) { - PR_PROTO("%s: id == NULL, thread exiting\n", proc); - return; - } - ASSERT((*id >= 0) && (*id < idn_protocol_nservers)); - - pq = &idn.protocol.p_serverq[*id]; - - ASSERT(pq->q_id == *id); - - PR_PROTO("%s: id %d starting up (pq = 0x%p)\n", - proc, pq->q_id, (void *)pq); - - /*CONSTCOND*/ - while (1) { - mutex_enter(&pq->q_mutex); - - while (((jl = pq->q_joblist) == NULL) && !pq->q_die) - cv_wait(&pq->q_cv, &pq->q_mutex); - - pq->q_joblist = pq->q_joblist_tail = NULL; - - if (pq->q_die) { - /* - * We've been killed. Need to check-in - * at the morgue. - */ - pq->q_threadp = NULL; - mutex_exit(&pq->q_mutex); - PR_PROTO("%s: thread (%d) killed...bye bye\n", - proc, pq->q_id); - for (jp = jl; jp; jp = jl) { - jl = jp->j_next; - idn_protojob_free(jp); - } - sema_v(pq->q_morgue); - thread_exit(); - /*NOTREACHED*/ - } - mutex_exit(&pq->q_mutex); - - /* - * We can process the jobs asynchronously while more are - * put on. - */ - for (jp = jl; jp; jp = jl) { - jl = jp->j_next; - idn_recv_proto(&(jp->j_msg)); - idn_protojob_free(jp); - } - } -} - -/* - * Kill off all the protocol servers. - */ -static void -idn_protocol_server_killall() -{ - register idn_protoqueue_t *pq; - int i; - procname_t proc = "idn_protocol_server_killall"; - - PR_PROTO("%s: killing off %d protocol servers\n", - proc, idn.nservers); - - pq = idn.protocol.p_serverq; - for (i = 0; i < idn.nservers; pq++, i++) { - mutex_enter(&pq->q_mutex); - pq->q_die = 1; - cv_signal(&pq->q_cv); - mutex_exit(&pq->q_mutex); - } - - while (idn.nservers > 0) { - sema_p(&idn.protocol.p_morgue); - idn.nservers--; - } -} - -idn_protojob_t * -idn_protojob_alloc(int kmflag) -{ - idn_protojob_t *jp; - - jp = kmem_cache_alloc(idn.protocol.p_jobpool, kmflag); - if (jp == NULL) { - mutex_enter(&idn_protojob_cache_lock); - if ((jp = idn_protojob_cache_list) != NULL) - idn_protojob_cache_list = jp->j_next; - mutex_exit(&idn_protojob_cache_lock); - } else { - jp->j_cache = 0; - } - - return (jp); -} - -static void -idn_protojob_free(idn_protojob_t *jp) -{ - ASSERT(jp); - - if (jp->j_cache) { - mutex_enter(&idn_protojob_cache_lock); - jp->j_next = idn_protojob_cache_list; - idn_protojob_cache_list = jp; - mutex_exit(&idn_protojob_cache_lock); - } else { - kmem_cache_free(idn.protocol.p_jobpool, (void *)jp); - } -} - -void -idn_protojob_submit(int cookie, idn_protojob_t *jp) -{ - idn_protoqueue_t *pq; - int serverid; - procname_t proc = "idn_protojob_submit"; - STRING(str); - - if (jp == NULL) - return; - - serverid = IDN_PROTOCOL_SERVER_HASH(cookie); - - pq = &idn.protocol.p_serverq[serverid]; - - INUM2STR(jp->j_msg.m_msgtype, str); - PR_PROTO("%s: job (d=%d, m=0x%x, %s) submitted to " - "protocol server %d\n", proc, jp->j_msg.m_domid, - jp->j_msg.m_msgtype, str, serverid); - - mutex_enter(&pq->q_mutex); - /* - * Can't submit jobs to dying servers. - */ - if (!pq->q_die) { - if (pq->q_joblist_tail) { - pq->q_joblist_tail->j_next = jp; - pq->q_joblist_tail = jp; - } else { - pq->q_joblist = pq->q_joblist_tail = jp; - } - jp->j_next = NULL; - cv_signal(&pq->q_cv); - } else { - PR_PROTO("%s: protocol server dead. freeing protojob\n", - proc); - idn_protojob_free(jp); - } - mutex_exit(&pq->q_mutex); -} - -static void -idn_mboxarea_init(idn_mboxtbl_t *mtp, register int ntbls) -{ - register int d; - caddr_t state_ptr = NULL, mtbasep = (caddr_t)mtp; - idn_mboxtbl_t *amtp; - procname_t proc = "idn_mboxarea_init"; - - ASSERT(mtp && (ntbls > 0)); - - PR_PROTO("%s: init mboxtbl (0x%p) ntbls = %d\n", - proc, (void *)mtp, ntbls); - - for (d = 0; d < ntbls; d++) { - register int pd, sd; - register int ch; - - mtp->mt_header.mh_svr_active = 0; - mtp->mt_header.mh_svr_ready = 0; - /* - * Initialize the header of each mbox table - * with a cookie for identity. - */ - /* - * Format: 0xc0c0DSCC - * D = primary domain - * S = sub-domain of primary - * CC = channel of sub-domain. - */ - pd = (d / MAX_DOMAINS) / IDN_MAX_NETS; - sd = (d / IDN_MAX_NETS) % MAX_DOMAINS; - ch = d % IDN_MAX_NETS; - - /* - * We point all sub-domains in the same channel - * to the same active sync flag since a single server - * services all domains in the same channel. - */ - amtp = IDN_MBOXTBL_ABS_PTR(mtbasep, pd, 0, ch); - - state_ptr = (caddr_t)&amtp->mt_header.mh_svr_active; - mtp->mt_header.mh_svr_active_ptr = IDN_ADDR2OFFSET(state_ptr); - - state_ptr = (caddr_t)&amtp->mt_header.mh_svr_ready; - mtp->mt_header.mh_svr_ready_ptr = IDN_ADDR2OFFSET(state_ptr); - - mtp->mt_header.mh_cookie = IDN_MAKE_MBOXHDR_COOKIE(pd, sd, ch); - - mtp->mt_header.mh_cksum = IDN_CKSUM_MBOX(&mtp->mt_header); - - IDN_MBOXTBL_PTR_INC(mtp); - } - /* - * Now that the master has initialized the entire mailbox - * region the referenced memory may not necessarily be up-to-date - * with respect to the actual SMR memory due to caching. - * In order to make sure future connecting domains get a - * consistent picture of the mailbox region, it's necessary - * for the master to flush its caches. - */ - PR_PROTO("%s: flushing ecache's of local (master) domain\n", proc); - - idnxf_flushall_ecache(); -} - -idn_mainmbox_t * -idn_mainmbox_init(int domid, int mbx) -{ - idn_mainmbox_t *mmp; - int c; - idn_mainmbox_t *cmp; - procname_t proc = "idn_mainmbox_init"; - - ASSERT(idn_domain[domid].dcpu != IDN_NIL_DCPU); - ASSERT(IDN_DLOCK_IS_HELD(domid)); - - PR_PROTO("%s: initializing main %s mailbox for domain %d\n", - proc, IDNMBOX_IS_RECV(mbx) ? "RECV" : "SEND", domid); - - cmp = GETSTRUCT(idn_mainmbox_t, IDN_MAX_NETS); - for (c = 0; c < IDN_MAX_NETS; c++) { - mmp = &cmp[c]; - mmp->mm_channel = (short)c; - mutex_init(&mmp->mm_mutex, NULL, MUTEX_DRIVER, NULL); - mmp->mm_domid = (short)domid; - mmp->mm_type = mbx; - } - mmp = cmp; - /* - * The actual SMR mailbox (mmp->mm_smr_mboxp) gets setup - * when the SMR is setup. - */ - - return (mmp); -} - -static void -idn_mainmbox_reset(int domid, idn_mainmbox_t *cmp) -{ - idn_mainmbox_t *mmp; - int c; - procname_t proc = "idn_mainmbox_reset"; - - ASSERT(IDN_DLOCK_IS_EXCL(domid)); - - PR_PROTO("%s: reseting main %s mailbox for domain %d\n", - proc, IDNMBOX_IS_RECV(cmp->mm_type) ? "RECV" : "SEND", domid); - - for (c = 0; c < IDN_MAX_NETS; c++) { - mmp = &cmp[c]; - - mmp->mm_channel = (short)c; - mmp->mm_domid = (short)domid; - mmp->mm_count = 0; - mmp->mm_flags = 0; - mmp->mm_qiget = mmp->mm_qiput = 0; - mmp->mm_csp = NULL; - ASSERT(mmp->mm_type == cmp->mm_type); - } -} - -void -idn_mainmbox_deinit(int domid, idn_mainmbox_t *mmp) -{ - procname_t proc = "idn_mainmbox_deinit"; - - ASSERT(IDN_DLOCK_IS_HELD(domid)); - - PR_PROTO("%s: deinitializing main %s mailbox for domain %d\n", - proc, IDNMBOX_IS_RECV(mmp->mm_type) ? "RECV" : "SEND", domid); - - ASSERT(idn_domain_is_registered(domid, -1, NULL) == 0); - - FREESTRUCT(mmp, idn_mainmbox_t, IDN_MAX_NETS); -} - -static void -idn_mainmbox_activate(int domid) -{ - register int c; - idn_domain_t *dp = &idn_domain[domid]; - procname_t proc = "idn_mainmbox_activate"; - - ASSERT(IDN_DLOCK_IS_HELD(domid)); - - PR_PROTO("%s:%d: activating main mailbox\n", proc, domid); - - for (c = 0; c < IDN_MAX_NETS; c++) - idn_mainmbox_chan_register(domid, &dp->dmbox.m_send[c], - &dp->dmbox.m_recv[c], c); -} - -/* - * Called upon disabling the SMR to deactivate all the mailboxes - * so that they no longer reference the SMR that's going away. - * - * stopall - Indicates to stop all channel services, across the board. - */ -static void -idn_mainmbox_deactivate(ushort_t domset) -{ - int svr_count; - procname_t proc = "idn_mainmbox_deactivate"; - - - if (domset == 0) - return; - - PR_PROTO("%s: %s deactivating main mailboxes for domset 0x%x\n", - proc, (domset == (ushort_t)-1) ? "STOP-ALL" : "NORMAL", domset); - - svr_count = idn_mainmbox_chan_unregister(domset, -1); - - PR_PROTO("%s: deactivated %d chansvrs (domset 0x%x)\n", - proc, svr_count, domset); -} - -static void -idn_mainmbox_chan_register(int domid, idn_mainmbox_t *send_mmp, - idn_mainmbox_t *recv_mmp, int channel) -{ - ASSERT(IDN_DLOCK_IS_HELD(domid)); - - /* - * Obtain receive mailbox lock first. - */ - mutex_enter(&recv_mmp->mm_mutex); - mutex_enter(&send_mmp->mm_mutex); - - ASSERT(recv_mmp->mm_channel == (short)channel); - ASSERT(send_mmp->mm_channel == (short)channel); - - recv_mmp->mm_csp = &idn.chan_servers[channel]; - recv_mmp->mm_count = 0; - recv_mmp->mm_dropped = 0; - recv_mmp->mm_flags = 0; - - send_mmp->mm_csp = &idn.chan_servers[channel]; - send_mmp->mm_count = 0; - send_mmp->mm_dropped = 0; - send_mmp->mm_flags = 0; - - mutex_exit(&send_mmp->mm_mutex); - mutex_exit(&recv_mmp->mm_mutex); - - /* - * We have to add ourselves to the respective - * channel server's service table. - * Note that the channel may not necessarily be - * active at this time. - */ - ASSERT(idn.chan_servers); - /* - * Have to get the channel server under - * control so we can add ourselves. - * Returns w/c_mutex. - */ - IDN_CHAN_LOCK_GLOBAL(&idn.chan_servers[channel]); - /* - * Add the following domain (mailbox) for monitoring - * by the respective channel server. - */ - idn_chan_addmbox(channel, DOMAINSET(domid)); - - IDN_CHAN_UNLOCK_GLOBAL(&idn.chan_servers[channel]); -} - -/* - * Unregister the given domain from the specified channel(s) for monitoring. - */ -static int -idn_mainmbox_chan_unregister(ushort_t domset, int channel) -{ - int c, dd_count; - int min_chan, max_chan; - procname_t proc = "idn_mainmbox_chan_unregister"; - - PR_CHAN("%s: deactivating main mailboxes (channel %d) " - "for domset 0x%x\n", proc, channel, domset); - - if (channel == -1) { - min_chan = 0; - max_chan = IDN_MAX_NETS - 1; - } else { - min_chan = max_chan = channel; - } - /* - * Point all the data dispatchers to the same morgue - * so we can kill them all at once. - */ - dd_count = 0; - for (c = min_chan; c <= max_chan; c++) { - - /* - * Have to get the channel server under - * control so we can remove ourselves. - * Returns w/c_mutex held. - */ - IDN_CHAN_LOCK_GLOBAL(&idn.chan_servers[c]); - /* - * Delete the following domain (mailbox) from - * monitoring by the respective channel server. - */ - idn_chan_delmbox(c, (ushort_t)domset); - - IDN_CHAN_UNLOCK_GLOBAL(&idn.chan_servers[c]); - dd_count++; - } - PR_CHAN("%s: deactivated %d channel mboxes for domset 0x%x, chan %d\n", - proc, dd_count, domset, channel); - return (dd_count); -} - -/* - * Check if the given domain is registered with the given channel(s). - */ -int -idn_domain_is_registered(int domid, int channel, idn_chanset_t *chansetp) -{ - int regcount; - int c, min_chan, max_chan; - idn_chanset_t chanset; - procname_t proc = "idn_domain_is_registered"; - - CHANSET_ZERO(chanset); - - if (idn.chan_servers == NULL) { - PR_CHAN("%s: idn.chan_servers == NULL!!\n", proc); - return (0); - } - - if (channel == -1) { - min_chan = 0; - max_chan = IDN_MAX_NETS - 1; - } else { - min_chan = max_chan = channel; - } - - regcount = 0; - - for (c = min_chan; c <= max_chan; c++) { - idn_chansvr_t *csp; - - csp = &idn.chan_servers[c]; - IDN_CHAN_LOCK_SEND(csp); - /* - * Don't really need recv side lock since registeration - * can't change while we're holding send side. - * No need to wait for send side to actually suspend - * since all we want to do is prevent the registered - * information from changing. - */ - if (IDN_CHAN_DOMAIN_IS_REGISTERED(csp, domid)) { - regcount++; - CHANSET_ADD(chanset, c); - } - - IDN_CHAN_UNLOCK_SEND(csp); - } - - PR_CHAN("%s: domid %d mbox reg'd with %d channels [0x%x] (req=%d)\n", - proc, domid, regcount, chanset, channel); - - if (chansetp) - *chansetp = chanset; - - return (regcount); -} - -static int -idn_mainmbox_flush(int domid, idn_mainmbox_t *mmp) -{ - register int qi; - register idn_mboxmsg_t *mqp; - int total_count = 0; - int c, count; - int mbox_type; - char *mbox_str; - int lost_io, total_lost_io = 0; - idn_chanset_t chanset; - procname_t proc = "idn_mainmbox_flush"; - - - if (mmp == NULL) - return (0); - - CHANSET_ZERO(chanset); - - mbox_type = mmp->mm_type; - ASSERT((mbox_type == IDNMMBOX_TYPE_SEND) || - (mbox_type == IDNMMBOX_TYPE_RECV)); - - mbox_str = (mbox_type == IDNMMBOX_TYPE_SEND) ? "SEND" : "RECV"; - - /* - * Determine which channels this domain is registered - * with. If he's not registered with any, then we - * can't touch the SMR. - */ - (void) idn_domain_is_registered(domid, -1, &chanset); - - for (c = 0; c < IDN_MAX_NETS; c++) { - ushort_t mbox_csum; - - if (mmp[c].mm_smr_mboxp == NULL) - continue; - mutex_enter(&mmp[c].mm_mutex); - ASSERT(mmp[c].mm_type == mbox_type); - if (CHAN_IN_SET(chanset, c) == 0) { - /* - * Domain is no longer registered. - * DON'T TOUCH THE SMR - IT'S POISON! - */ - if (mmp[c].mm_smr_mboxp) { - PR_CHAN("%s:%d:%s: domain unregistered " - "w/chan %d - DUMPING SMR reference\n", - proc, domid, mbox_str, c); - lost_io = IDN_MMBOXINDEX_DIFF(mmp[c].mm_qiput, - mmp[c].mm_qiget); -#ifdef DEBUG - if (mbox_type == IDNMMBOX_TYPE_RECV) { - PR_CHAN("%s:%d:%s: blowing away %d " - "incoming pkts\n", - proc, domid, mbox_str, lost_io); - } else { - PR_CHAN("%s:%d:%s: blowing away %d/%d " - "outstanding pkts\n", - proc, domid, mbox_str, lost_io, - idn_domain[domid].dio); - } -#endif /* DEBUG */ - } - mmp[c].mm_qiput = mmp[c].mm_qiget = 0; - mmp[c].mm_smr_mboxp = NULL; - total_lost_io += lost_io; - } - if (mmp[c].mm_smr_mboxp) { - mbox_csum = - IDN_CKSUM_MBOX(&mmp[c].mm_smr_mboxp->mt_header); - if (!VALID_NWRADDR(mmp[c].mm_smr_mboxp, 4) || - !VALID_MBOXHDR(&mmp[c].mm_smr_mboxp->mt_header, - c, mbox_csum)) { - lost_io = IDN_MMBOXINDEX_DIFF(mmp[c].mm_qiput, - mmp[c].mm_qiget); -#ifdef DEBUG - if (mbox_type == IDNMMBOX_TYPE_RECV) { - PR_CHAN("%s:%d:%s: bad mbox. blowing " - "away %d incoming pkts\n", - proc, domid, mbox_str, lost_io); - } else { - PR_CHAN("%s:%d:%s: bad mbox. blowing " - "away %d/%d outstanding pkts\n", - proc, domid, mbox_str, lost_io, - idn_domain[domid].dio); - } -#endif /* DEBUG */ - mmp[c].mm_smr_mboxp = NULL; - mmp[c].mm_qiput = mmp[c].mm_qiget = 0; - total_lost_io += lost_io; - } - } - if (mmp[c].mm_smr_mboxp == NULL) { - mutex_exit(&mmp[c].mm_mutex); - continue; - } - mqp = &mmp[c].mm_smr_mboxp->mt_queue[0]; - qi = 0; - count = 0; - /* - * It's quite possible the remote domain may be accessing - * these mailbox entries at the exact same time we're - * clearing the owner bit. That's okay. All we're trying - * to do at this point is to minimize the number of packets - * the remote domain might try to process unnecessarily. - */ - do { - if (mqp[qi].ms_owner) - count++; - mqp[qi].ms_owner = 0; - IDN_MMBOXINDEX_INC(qi); - } while (qi); - - lost_io = IDN_MMBOXINDEX_DIFF(mmp[c].mm_qiput, mmp[c].mm_qiget); - total_lost_io += lost_io; - - mmp[c].mm_qiput = mmp[c].mm_qiget = 0; - mmp[c].mm_smr_mboxp = NULL; - mutex_exit(&mmp[c].mm_mutex); - - total_count += count; - - PR_CHAN("%s:%d:%s: flushed out %d mbox entries for chan %d\n", - proc, domid, mbox_str, count, c); - } - - if (total_lost_io && (mbox_type == IDNMMBOX_TYPE_SEND)) { - int lost_bufs; - /* - * If we lost all our outstanding I/O. We could - * possible could have slabs now with mistakenly - * outstanding I/O buffers. Need to clean them up. - * Clean up of leftovers our self. - */ - lost_bufs = smr_buf_free_all(domid); - - PR_CHAN("%s:%d:%s: flushed %d/%d buffers from slabs\n", - proc, domid, mbox_str, lost_bufs, total_lost_io); - } - - PR_CHAN("%s:%d:%s: flushed total of %d mailbox entries (lost %d)\n", - proc, domid, mbox_str, total_count, total_lost_io); - - return (total_count); -} - -void -idn_chanserver_bind(int net, int cpuid) -{ - int ocpuid; - cpu_t *cp; - idn_chansvr_t *csp; - kthread_id_t tp; - procname_t proc = "idn_chanserver_bind"; - - csp = &idn.chan_servers[net]; - IDN_CHAN_LOCK_GLOBAL(csp); - - mutex_enter(&cpu_lock); /* protect checking cpu_ready_set */ - ocpuid = csp->ch_bound_cpuid; - cp = cpu_get(cpuid); - if ((cpuid != -1) && ((cp == NULL) || !cpu_is_online(cp))) { - mutex_exit(&cpu_lock); - cmn_err(CE_WARN, - "IDN: 239: invalid CPU ID (%d) specified for " - "IDN net %d", - cpuid, net); - IDN_CHAN_UNLOCK_GLOBAL(csp); - return; - } - if ((tp = csp->ch_recv_threadp) == NULL) { - /* - * Thread is not yet active. Set ch_bound_cpuid - * so when thread activates it will automatically - * bind itself. - */ - csp->ch_bound_cpuid = -1; - csp->ch_bound_cpuid_pending = cpuid; - } else { - if (ocpuid != -1) { - thread_affinity_clear(tp); - csp->ch_bound_cpuid = -1; - } - if (cpuid >= 0) { - thread_affinity_set(tp, cpuid); - csp->ch_bound_cpuid = cpuid; - } - csp->ch_bound_cpuid_pending = -1; - } - mutex_exit(&cpu_lock); - - PR_CHAN("%s: bound net/channel (%d) from cpuid %d to%scpuid %d\n", - proc, net, ocpuid, tp ? " " : " (pending) ", cpuid); - - IDN_CHAN_UNLOCK_GLOBAL(csp); -} - -#ifdef DEBUG -static idn_mboxhdr_t *prev_mhp[IDN_MAXMAX_NETS]; -#endif /* DEBUG */ -/* - * Get access to the respective channel server's synchronization - * header which resides in SMR space. - */ -static idn_mboxhdr_t * -idn_chan_server_syncheader(int channel) -{ - idn_domain_t *ldp = &idn_domain[idn.localid]; - idn_mboxtbl_t *mtp; - idn_mboxhdr_t *mhp; - ushort_t mbox_csum; - procname_t proc = "idn_chan_server_syncheader"; - - ASSERT(IDN_CHAN_RECV_IS_LOCKED(&idn.chan_servers[channel])); - - IDN_DLOCK_SHARED(idn.localid); - - if (ldp->dmbox.m_tbl == NULL) { - PR_CHAN("%s: local dmbox.m_tbl == NULL\n", proc); - IDN_DUNLOCK(idn.localid); - return (NULL); - } - - mtp = IDN_MBOXTBL_PTR_CHAN(ldp->dmbox.m_tbl, channel); - mhp = &mtp->mt_header; - mbox_csum = IDN_CKSUM_MBOX(&mtp->mt_header); - -#ifdef DEBUG - if (mhp != prev_mhp[channel]) { - prev_mhp[channel] = mhp; - PR_CHAN("%s: chan_server (%d) cookie = 0x%x (exp 0x%x)\n", - proc, channel, IDN_GET_MBOXHDR_COOKIE(mhp), - IDN_MAKE_MBOXHDR_COOKIE(0, 0, channel)); - PR_CHAN("%s: chan_server (%d) actv_ptr = 0x%x (exp 0x%x)\n", - proc, channel, mhp->mh_svr_active_ptr, - IDN_ADDR2OFFSET(&mhp->mh_svr_active)); - PR_CHAN("%s: chan_server (%d) ready_ptr = 0x%x (exp 0x%x)\n", - proc, channel, mhp->mh_svr_ready_ptr, - IDN_ADDR2OFFSET(&mhp->mh_svr_ready)); - PR_CHAN("%s: chan_server (%d) mbox_cksum = 0x%x (exp 0x%x)\n", - proc, channel, (int)mhp->mh_cksum, (int)mbox_csum); - } -#endif /* DEBUG */ - - if ((IDN_ADDR2OFFSET(&mhp->mh_svr_active) != - mhp->mh_svr_active_ptr) || - (IDN_ADDR2OFFSET(&mhp->mh_svr_ready) != mhp->mh_svr_ready_ptr) || - !VALID_MBOXHDR(mhp, channel, mbox_csum)) { - idn_chansvr_t *csp; - - csp = &idn.chan_servers[channel]; - if (IDN_CHANNEL_IS_RECV_CORRUPTED(csp) == 0) { - IDN_CHANSVC_MARK_RECV_CORRUPTED(csp); - - cmn_err(CE_WARN, - "IDN: 240: (channel %d) SMR CORRUPTED " - "- RELINK", channel); - cmn_err(CE_CONT, - "IDN: 240: (channel %d) cookie " - "(expected 0x%x, actual 0x%x)\n", - channel, - IDN_MAKE_MBOXHDR_COOKIE(0, 0, channel), - mhp->mh_cookie); - cmn_err(CE_CONT, - "IDN: 240: (channel %d) actv_flg " - "(expected 0x%x, actual 0x%x)\n", - channel, mhp->mh_svr_active_ptr, - IDN_ADDR2OFFSET(&mhp->mh_svr_active)); - cmn_err(CE_CONT, - "IDN: 240: (channel %d) ready_flg " - "(expected 0x%x, actual 0x%x)\n", - channel, mhp->mh_svr_ready_ptr, - IDN_ADDR2OFFSET(&mhp->mh_svr_ready)); - } - - mhp = NULL; - } - IDN_DUNLOCK(idn.localid); - - PR_CHAN("%s: channel(%d) mainhp = 0x%p\n", proc, channel, (void *)mhp); - - return (mhp); -} - -#define CHANSVR_SYNC_CACHE(csp, mmp, chan) \ -{ \ - ASSERT(IDN_CHAN_RECV_IS_LOCKED(csp)); \ - if ((csp)->ch_recv_changed) { \ - register int _d; \ - (csp)->ch_recv_scanset = (csp)->ch_recv_scanset_pending; \ - (csp)->ch_recv_domset = (csp)->ch_recv_domset_pending; \ - for (_d = 0; _d < MAX_DOMAINS; _d++) { \ - if (DOMAIN_IN_SET((csp)->ch_recv_domset, _d)) { \ - (mmp)[_d] = \ - &idn_domain[_d].dmbox.m_recv[chan]; \ - } else { \ - (mmp)[_d] = NULL; \ - } \ - } \ - (csp)->ch_recv_changed = 0; \ - } \ -} -#define CHANSVR_NEXT_DOMID(csp, i, d) \ -{ \ - (i) = ((i) + 1) & (MAX_DOMAINS - 1); \ - (d) = (int)(((csp)->ch_recv_scanset >> ((i) << 2)) & 0xf); \ -} -#define CHANSVR_RESET_INDEX(i) ((i) = -1) - -#ifdef DEBUG -static idn_mainmbox_t *Mmp[IDN_MAXMAX_NETS][MAX_DOMAINS]; -#endif /* DEBUG */ - -static void -idn_chan_server(idn_chansvr_t **cspp) -{ - idn_mboxhdr_t *mainhp; - register idn_chansvr_t *csp; - register idn_mboxmsg_t *mqp; -#ifdef DEBUG - idn_mainmbox_t **mmp; -#else - idn_mainmbox_t *mmp[MAX_DOMAINS]; -#endif /* DEBUG */ - register int qi; - struct idn *sip; - int channel; - int cpuid; - int empty; - int tot_pktcount, tot_dropcount; - register int index; - register int domid; - register int idleloops; - procname_t proc = "idn_chan_server"; - - -#ifdef DEBUG - mmp = &Mmp[(*cspp)->ch_id][0]; - bzero(mmp, MAX_DOMAINS * sizeof (idn_mainmbox_t *)); -#else /* DEBUG */ - bzero(mmp, sizeof (mmp)); -#endif /* DEBUG */ - - tot_pktcount = tot_dropcount = 0; - - ASSERT(cspp && *cspp); - - csp = *cspp; - channel = csp->ch_id; - sip = IDN_INST2SIP(channel); - ASSERT(sip); - - PR_CHAN("%s: CHANNEL SERVER (channel %d) GOING ACTIVE...\n", - proc, channel); - - IDN_CHAN_LOCK_RECV(csp); - IDN_CHAN_RECV_INPROGRESS(csp); - ASSERT(csp->ch_recv_threadp == curthread); - mutex_enter(&cpu_lock); - if ((cpuid = csp->ch_bound_cpuid_pending) != -1) { - cpu_t *cp = cpu_get(cpuid); - /* - * We've been requested to bind to - * a particular cpu. - */ - if ((cp == NULL) || !cpu_is_online(cp)) { - /* - * Cpu seems to have gone away or gone offline - * since originally requested. - */ - mutex_exit(&cpu_lock); - cmn_err(CE_WARN, - "IDN: 239: invalid CPU ID (%d) specified for " - "IDN net %d", - cpuid, channel); - } else { - csp->ch_bound_cpuid = cpuid; - affinity_set(csp->ch_bound_cpuid); - mutex_exit(&cpu_lock); - } - csp->ch_bound_cpuid_pending = -1; - } else { - mutex_exit(&cpu_lock); - } - if (csp->ch_bound_cpuid != -1) { - PR_CHAN("%s: thread bound to cpuid %d\n", - proc, csp->ch_bound_cpuid); - } - /* - * Only the first (main) mbox header is used for - * synchronization with data delivery since there is - * only data server for all mailboxes for this - * given channel. - */ - CHANSVR_SYNC_CACHE(csp, mmp, channel); - - mainhp = ((csp->ch_recv_domcount > 0) && - IDN_CHANNEL_IS_RECV_ACTIVE(csp)) - ? idn_chan_server_syncheader(channel) : NULL; - - if (mainhp && IDN_CHANNEL_IS_RECV_ACTIVE(csp)) - mainhp->mh_svr_active = 1; - - ASSERT(csp->ch_recv_domcount ? - (csp->ch_recv_scanset && csp->ch_recv_domset) : 1); - - IDN_CHAN_UNLOCK_RECV(csp); - - empty = 0; - idleloops = 0; - CHANSVR_RESET_INDEX(index); - - /* - * --------------------------------------------- - */ - /*CONSTCOND*/ - while (1) { - register int pktcount; - register int dropcount; - ushort_t mbox_csum; - idn_mboxtbl_t *smr_mboxp; /* points to SMR space */ - register smr_offset_t bufoffset; -#ifdef DEBUG - register smr_pkthdr_t *hdrp; - idn_netaddr_t netaddr; -#endif /* DEBUG */ - - /* - * Speed through and find the next available domid. - */ - CHANSVR_NEXT_DOMID(csp, index, domid); - - if (!index) { - /* - * We only check state changes when - * we wrap around. Done for performance. - */ - if (!IDN_CHANNEL_IS_RECV_ACTIVE(csp) || - csp->ch_recv.c_checkin || - (idn.state != IDNGS_ONLINE)) { - - PR_DATA("%s: (channel %d) %s\n", - proc, channel, - IDN_CHANNEL_IS_DETACHED(csp) - ? "DEAD" : - IDN_CHANNEL_IS_PENDING(csp) - ? "IDLED" : - IDN_CHANNEL_IS_ACTIVE(csp) - ? "ACTIVE" : "DISABLED"); - goto cc_sleep; - } - } - if (csp->ch_recv.c_checkin) - goto cc_sleep; - - if (empty == csp->ch_recv_domcount) { - empty = 0; - goto cc_slowdown; - } - - ASSERT(mmp[domid] != NULL); - - mutex_enter(&mmp[domid]->mm_mutex); - if ((smr_mboxp = mmp[domid]->mm_smr_mboxp) == NULL) { - /* - * Somebody is trying to shut things down. - */ - empty++; - mutex_exit(&mmp[domid]->mm_mutex); - continue; - } - ASSERT(mmp[domid]->mm_channel == (short)channel); - /* - * We don't care if the mm_smr_mboxp is nullified - * after this point. The thread attempting to shut - * us down has to formally pause this channel before - * anything is official anyway. So, we can continue - * with our local SMR reference until the thread - * shutting us down really stops us. - * - * Need to get the qiget index _before_ we drop the - * lock since it might get flushed (idn_mainmbox_flush) - * once we drop the mm_mutex. - * - * We prefer not to hold the mm_mutex across the - * idn_recv_mboxdata() call since that may be time- - * consuming. - */ - qi = mmp[domid]->mm_qiget; - - /* - * Check the mailbox header if checksum is turned on. - */ - mbox_csum = IDN_CKSUM_MBOX(&smr_mboxp->mt_header); - if (!VALID_MBOXHDR(&smr_mboxp->mt_header, channel, mbox_csum)) { - IDN_KSTAT_INC(sip, si_mboxcrc); - IDN_KSTAT_INC(sip, si_ierrors); - if (!(mmp[domid]->mm_flags & IDNMMBOX_FLAG_CORRUPTED)) { - cmn_err(CE_WARN, - "IDN: 241: [recv] (domain %d, " - "channel %d) SMR CORRUPTED - RELINK", - domid, channel); - mmp[domid]->mm_flags |= IDNMMBOX_FLAG_CORRUPTED; - } - empty = 0; - mutex_exit(&mmp[domid]->mm_mutex); - goto cc_sleep; - } - mutex_exit(&mmp[domid]->mm_mutex); - mqp = &smr_mboxp->mt_queue[0]; - - pktcount = dropcount = 0; - - if (mqp[qi].ms_owner == 0) - goto cc_next; - - bufoffset = IDN_BFRAME2OFFSET(mqp[qi].ms_bframe); - - if (!VALID_NWROFFSET(bufoffset, IDN_SMR_BUFSIZE)) { - /* ASSERT(0); */ - mqp[qi].ms_flag |= IDN_MBOXMSG_FLAG_ERR_BADOFFSET; - mqp[qi].ms_owner = 0; - IDN_MMBOXINDEX_INC(qi); - dropcount++; - - IDN_KSTAT_INC(sip, si_smraddr); - IDN_KSTAT_INC(sip, si_ierrors); - - } else { - PR_DATA("%s: (channel %d) pkt (off 0x%x, " - "qiget %d) from domain %d\n", - proc, channel, bufoffset, qi, domid); -#ifdef DEBUG - - hdrp = IDN_BUF2HDR(IDN_OFFSET2ADDR(bufoffset)); - netaddr.netaddr = hdrp->b_netaddr; - ASSERT(netaddr.net.chan == (ushort_t)channel); -#endif /* DEBUG */ - - if (idn_recv_mboxdata(channel, - IDN_OFFSET2ADDR(bufoffset)) < 0) { - mutex_enter(&mmp[domid]->mm_mutex); - if (!(mmp[domid]->mm_flags & - IDNMMBOX_FLAG_CORRUPTED)) { - cmn_err(CE_WARN, - "IDN: 241: [recv] (domain " - "%d, channel %d) SMR " - "CORRUPTED - RELINK", - domid, channel); - mmp[domid]->mm_flags |= - IDNMMBOX_FLAG_CORRUPTED; - } - mutex_exit(&mmp[domid]->mm_mutex); - } - - mqp[qi].ms_owner = 0; - IDN_MMBOXINDEX_INC(qi); - pktcount++; - } - -cc_next: - - mutex_enter(&mmp[domid]->mm_mutex); - if (mmp[domid]->mm_smr_mboxp) { - if (dropcount) - mmp[domid]->mm_dropped += dropcount; - mmp[domid]->mm_qiget = qi; - mmp[domid]->mm_count += pktcount; - } - mutex_exit(&mmp[domid]->mm_mutex); - - if (pktcount == 0) { - empty++; - } else { - csp->ch_recv_waittime = IDN_NETSVR_WAIT_MIN; - empty = 0; - idleloops = 0; - - PR_DATA("%s: (channel %d) dom=%d, pktcnt=%d\n", - proc, channel, domid, pktcount); - } - - continue; - -cc_slowdown: - -#ifdef DEBUG - if (idleloops == 0) { - PR_DATA("%s: (channel %d) going SOFT IDLE...\n", - proc, channel); - } -#endif /* DEBUG */ - if (idleloops++ < IDN_NETSVR_SPIN_COUNT) { - /* - * At this level we only busy-wait. - * Get back into action. - */ - continue; - } - idleloops = 0; - -cc_sleep: - - if (mainhp) - mainhp->mh_svr_active = 0; - - IDN_CHAN_LOCK_RECV(csp); - -cc_die: - - ASSERT(IDN_CHAN_RECV_IS_LOCKED(csp)); - - if (!IDN_CHANNEL_IS_RECV_ACTIVE(csp) && - IDN_CHANNEL_IS_DETACHED(csp)) { - /* - * Time to die... - */ - PR_CHAN("%s: (channel %d) serviced %d " - "packets, drop = %d\n", proc, channel, - tot_pktcount, tot_dropcount); - PR_CHAN("%s: (channel %d) TERMINATING\n", - proc, channel); - PR_CHAN("%s: (channel %d) ch_morguep = %p\n", - proc, channel, (void *)csp->ch_recv_morguep); - - csp->ch_recv_threadp = NULL; -#ifdef DEBUG - for (index = 0; index < csp->ch_recv_domcount; - index++) { - if ((int)((csp->ch_recv_scanset >> - (index*4)) & 0xf) == domid) { - PR_DATA("%s: WARNING (channel %d) " - "DROPPING domid %d...\n", - proc, channel, domid); - } - } -#endif /* DEBUG */ - IDN_CHAN_RECV_DONE(csp); - - sema_v(csp->ch_recv_morguep); - - IDN_CHAN_UNLOCK_RECV(csp); - - thread_exit(); - /* not reached */ - } - - do { - if (IDN_CHANNEL_IS_DETACHED(csp)) { - PR_CHAN("%s: (channel %d) going to DIE...\n", - proc, channel); - goto cc_die; - } -#ifdef DEBUG - if (IDN_CHANNEL_IS_RECV_ACTIVE(csp) && - (csp->ch_recv_waittime <= IDN_NETSVR_WAIT_MAX)) { - PR_CHAN("%s: (channel %d) going SOFT IDLE " - "(waittime = %d ticks)...\n", - proc, channel, - csp->ch_recv_waittime); - } else { - PR_CHAN("%s: (channel %d) going " - "HARD IDLE...\n", proc, channel); - } -#endif /* DEBUG */ - IDN_CHAN_RECV_DONE(csp); - - /* - * If we're being asked to check-in then - * go into a hard sleep. Want to give the - * thread requesting us to checkin a chance. - */ - while (csp->ch_recv.c_checkin) - cv_wait(&csp->ch_recv_cv, - &csp->ch_recv.c_mutex); - - if (csp->ch_recv_waittime > IDN_NETSVR_WAIT_MAX) - cv_wait(&csp->ch_recv_cv, - &csp->ch_recv.c_mutex); - else - (void) cv_reltimedwait(&csp->ch_recv_cv, - &csp->ch_recv.c_mutex, - csp->ch_recv_waittime, TR_CLOCK_TICK); - - IDN_CHAN_RECV_INPROGRESS(csp); - - IDN_KSTAT_INC(sip, si_sigsvr); - - if (csp->ch_recv_waittime <= IDN_NETSVR_WAIT_MAX) - csp->ch_recv_waittime <<= - IDN_NETSVR_WAIT_SHIFT; - - } while (!IDN_CHANNEL_IS_RECV_ACTIVE(csp)); - - /* - * Before we see the world (and touch SMR space), - * see if we've been told to die. - */ - mainhp = NULL; - /* - * The world may have changed since we were - * asleep. Need to resync cache and check for a - * new syncheader. - * - * Reset chansvr cache against any changes in - * mbox fields we need (mm_qiget). - */ - CHANSVR_SYNC_CACHE(csp, mmp, channel); - if (csp->ch_recv_domcount <= 0) { - /* - * Everybody disappeared on us. - * Go back to sleep. - */ - goto cc_die; - } - ASSERT(csp->ch_recv_scanset && csp->ch_recv_domset); - - mainhp = idn_chan_server_syncheader(channel); - if (mainhp == NULL) { - /* - * Bummer...we're idling... - */ - goto cc_die; - } - - mainhp->mh_svr_active = 1; - - IDN_CHAN_UNLOCK_RECV(csp); - /* - * Reset the domid index after sleeping. - */ - CHANSVR_RESET_INDEX(index); - - empty = 0; - idleloops = 0; - } -} - -#if 0 -/* - * We maintain a separate function for flushing the STREAMs - * queue of a channel because it must be done outside the - * context of the idn_chan_action routine. The streams flush - * cannot occur inline with the idn_chan_action because - * the act of flushing may cause IDN send functions to be called - * directly and thus locks to be obtained which could result - * in deadlocks. - */ -static void -idn_chan_flush(idn_chansvr_t *csp) -{ - queue_t *rq; - struct idn *sip; - int flush_type = 0; - idn_chaninfo_t *csend, *crecv; - procname_t proc = "idn_chan_flush"; - - csend = &csp->ch_send; - crecv = &csp->ch_recv; - - mutex_enter(&crecv->c_mutex); - mutex_enter(&csend->c_mutex); - - if (crecv->c_state & IDN_CHANSVC_STATE_FLUSH) - flush_type |= FLUSHR; - - if (csend->c_state & IDN_CHANSVC_STATE_FLUSH) - flush_type |= FLUSHW; - - if (flush_type) { - rq = NULL; - rw_enter(&idn.struprwlock, RW_READER); - if ((sip = IDN_INST2SIP(csp->ch_id)) != NULL) - rq = sip->si_ipq; - rw_exit(&idn.struprwlock); - if (rq) { - /* - * Flush the STREAM if possible - * to get the channel server coherent - * enough to respond to us. - */ - PR_CHAN("%s: sending FLUSH (%x) to channel %d\n", - proc, flush_type, csp->ch_id); - - (void) putnextctl1(rq, M_FLUSH, flush_type); - } - crecv->c_state &= ~IDN_CHANSVC_STATE_FLUSH; - csend->c_state &= ~IDN_CHANSVC_STATE_FLUSH; - - if (crecv->c_waiters) - cv_broadcast(&crecv->c_cv); - } - - mutex_exit(&csend->c_mutex); - mutex_exit(&crecv->c_mutex); -} -#endif /* 0 */ - -/* - * Locks are with respect to SEND/RECV locks (c_mutex). - * - * STOP/SUSPEND/DETACH - * - Entered with locks dropped, leave with locks held. - * DETACH - lock dropped manually. - * RESTART/RESUME - * - Entered with locks held, leave with locks dropped. - * ATTACH - * - both enter and leave with locks dropped. - */ -static void -idn_chan_action(int channel, idn_chanaction_t chanaction, int wait) -{ - uchar_t clr_state, set_state; - uint_t is_running; - domainset_t closed_slabwaiters = 0; - struct idn *sip; - idn_chansvr_t *csp; - idn_chaninfo_t *csend, *crecv; - procname_t proc = "idn_chan_action"; - - ASSERT((channel >= 0) && (channel < IDN_MAX_NETS)); - ASSERT(idn.chan_servers); - - csp = &idn.chan_servers[channel]; - - PR_CHAN("%s: requesting %s for channel %d\n", - proc, chanaction_str[(int)chanaction], channel); - - csend = &csp->ch_send; - crecv = &csp->ch_recv; - - ASSERT(IDN_CHAN_GLOBAL_IS_LOCKED(csp)); - - clr_state = set_state = 0; - - switch (chanaction) { - case IDNCHAN_ACTION_DETACH: - clr_state = IDN_CHANSVC_STATE_MASK; - /*FALLTHROUGH*/ - - case IDNCHAN_ACTION_STOP: - clr_state |= IDN_CHANSVC_STATE_ENABLED; - /*FALLTHROUGH*/ - - case IDNCHAN_ACTION_SUSPEND: - clr_state |= IDN_CHANSVC_STATE_ACTIVE; - - /* - * Must maintain this locking order. - * Set asynchronous check-in flags. - */ - crecv->c_checkin = 1; - csend->c_checkin = 1; - - is_running = 0; - if ((csend->c_inprogress || crecv->c_inprogress) && - wait && (csp->ch_recv_threadp != curthread)) { - - rw_enter(&idn.struprwlock, RW_READER); - if ((sip = IDN_INST2SIP(channel)) != NULL) { - /* - * Temporarily turn off the STREAM - * to give a chance to breath. - */ - is_running = sip->si_flags & IDNRUNNING; - if (is_running) - sip->si_flags &= ~IDNRUNNING; - } - rw_exit(&idn.struprwlock); - } - - mutex_enter(&crecv->c_mutex); - crecv->c_state &= ~clr_state; - - mutex_enter(&csend->c_mutex); - csend->c_state &= ~clr_state; - - /* - * It's possible the channel server could come - * through this flow itself due to putting data upstream - * that ultimately turned around and came back down for - * sending. If this is the case we certainly don't - * want to cv_wait, otherwise we'll obviously deadlock - * waiting for ourself. So, only block if somebody - * other than the channel server we're attempting to - * suspend/stop. - */ - if (wait && (csp->ch_recv_threadp != curthread)) { - int do_flush = 0; - - if (csend->c_inprogress || crecv->c_inprogress) - do_flush++; - - if (do_flush) { - rw_enter(&idn.struprwlock, RW_READER); - if ((sip = IDN_INST2SIP(channel)) != NULL) { - /* - * Temporarily turn off the STREAM - * to give a chance to breath. - */ - if (sip->si_flags & IDNRUNNING) { - is_running = 1; - sip->si_flags &= ~IDNRUNNING; - } - } - rw_exit(&idn.struprwlock); - } - - /* - * If we have any senders in-progress - * it's possible they're stuck waiting - * down in smr_buf_alloc which may never - * arrive if we're in an unlink process. - * Rather than wait for it to timeout - * let's be proactive so we can disconnect - * asap. - */ - closed_slabwaiters = csp->ch_reg_domset; - DOMAINSET_ADD(closed_slabwaiters, idn.localid); - if (closed_slabwaiters) - smr_slabwaiter_close(closed_slabwaiters); - - do { - /* - * It's possible due to a STREAMs - * loopback from read queue to write queue - * that receiver and sender may be same - * thread, i.e. receiver's inprogress - * flag will never clear until sender's - * inprogress flag clears. So, we wait - * for sender's inprogress first. - */ - while (csend->c_inprogress) { - mutex_exit(&crecv->c_mutex); - while (csend->c_inprogress) { - csend->c_waiters++; - cv_wait(&csend->c_cv, - &csend->c_mutex); - csend->c_waiters--; - } - /* - * Maintain lock ordering. - * Eventually we will catch - * him due to the flag settings. - */ - mutex_exit(&csend->c_mutex); - mutex_enter(&crecv->c_mutex); - mutex_enter(&csend->c_mutex); - } - if (crecv->c_inprogress) { - mutex_exit(&csend->c_mutex); - while (crecv->c_inprogress) { - crecv->c_waiters++; - cv_wait(&crecv->c_cv, - &crecv->c_mutex); - crecv->c_waiters--; - } - mutex_enter(&csend->c_mutex); - } - } while (csend->c_inprogress); - } - - if (is_running) { - /* - * Restore the IDNRUNNING bit in - * the flags to let them know the - * channel is still alive. - */ - rw_enter(&idn.struprwlock, RW_READER); - if ((sip = IDN_INST2SIP(channel)) != NULL) - sip->si_flags |= IDNRUNNING; - rw_exit(&idn.struprwlock); - } - - if (closed_slabwaiters) { - /* - * We can reopen now since at this point no new - * slabwaiters will attempt to come in and wait. - */ - smr_slabwaiter_open(csp->ch_reg_domset); - } - - crecv->c_checkin = 0; - csend->c_checkin = 0; - - /* - * ALL leave with locks held. - */ - PR_CHAN("%s: action (%s) for channel %d - COMPLETED\n", - proc, chanaction_str[(int)chanaction], channel); - break; - - case IDNCHAN_ACTION_ATTACH: - mutex_enter(&crecv->c_mutex); - mutex_enter(&csend->c_mutex); - set_state |= csp->ch_state & IDN_CHANSVC_STATE_ATTACHED; - /*FALLTHROUGH*/ - - case IDNCHAN_ACTION_RESTART: - set_state |= csp->ch_state & IDN_CHANSVC_STATE_ENABLED; - /*FALLTHROUGH*/ - - case IDNCHAN_ACTION_RESUME: - ASSERT(IDN_CHAN_LOCAL_IS_LOCKED(csp)); - set_state |= csp->ch_state & IDN_CHANSVC_STATE_ACTIVE; - - crecv->c_state |= set_state; - csend->c_state |= set_state; - - /* - * The channel server itself could come through this - * flow, so obviously no point in attempting to wake - * ourself up!. - */ - if (csp->ch_recv_threadp && (csp->ch_recv_threadp != curthread)) - cv_signal(&csp->ch_recv_cv); - - PR_CHAN("%s: action (%s) for channel %d - COMPLETED\n", - proc, chanaction_str[(int)chanaction], channel); - - /* - * Leaves with lock released. - */ - mutex_exit(&csend->c_mutex); - mutex_exit(&crecv->c_mutex); - break; - - default: - ASSERT(0); - break; - } -} - -static void -idn_chan_addmbox(int channel, ushort_t domset) -{ - idn_chansvr_t *csp; - register int d; - procname_t proc = "idn_chan_addmbox"; - - PR_CHAN("%s: adding domset 0x%x main mailboxes to channel %d\n", - proc, domset, channel); - - ASSERT(idn.chan_servers); - - csp = &idn.chan_servers[channel]; - - /* - * Adding domains to a channel can be - * asynchonous, so we don't bother waiting. - */ - IDN_CHANNEL_SUSPEND(channel, 0); - - /* - * Now we have the sending and receiving sides blocked - * for this channel. - */ - for (d = 0; d < MAX_DOMAINS; d++) { - if (!DOMAIN_IN_SET(domset, d)) - continue; - if (IDN_CHAN_DOMAIN_IS_REGISTERED(csp, d)) { - DOMAINSET_DEL(domset, d); - continue; - } - IDN_CHANSVR_SCANSET_ADD_PENDING(csp, d); - DOMAINSET_ADD(csp->ch_recv_domset_pending, d); - IDN_CHAN_DOMAIN_REGISTER(csp, d); - - PR_CHAN("%s: domain %d (channel %d) RECV (pending) " - "scanset = 0x%lx\n", proc, d, channel, - csp->ch_recv_scanset_pending); - PR_CHAN("%s: domain %d (channel %d) domset = 0x%x\n", - proc, d, channel, (uint_t)csp->ch_reg_domset); - - CHECKPOINT_OPENED(IDNSB_CHKPT_CHAN, - idn_domain[d].dhw.dh_boardset, 1); - } - if (domset) - csp->ch_recv_changed = 1; - - IDN_CHANNEL_RESUME(channel); -} - -static void -idn_chan_delmbox(int channel, ushort_t domset) -{ - idn_chansvr_t *csp; - register int d; - procname_t proc = "idn_chan_delmbox"; - - PR_CHAN("%s: deleting domset 0x%x main mailboxes from channel %d\n", - proc, domset, channel); - - ASSERT(idn.chan_servers); - - csp = &idn.chan_servers[channel]; - - /* - * Here we have to wait for the channel server - * as it's vital that we don't return without guaranteeing - * that the given domset is no longer registered. - */ - IDN_CHANNEL_SUSPEND(channel, 1); - - /* - * Now we have the sending and receiving sides blocked - * for this channel. - */ - for (d = 0; d < MAX_DOMAINS; d++) { - if (!DOMAIN_IN_SET(domset, d)) - continue; - if (!IDN_CHAN_DOMAIN_IS_REGISTERED(csp, d)) { - DOMAINSET_DEL(domset, d); - continue; - } - /* - * This domain has a mailbox hanging on this channel. - * Get him out. - * - * First remove him from the receive side. - */ - ASSERT(csp->ch_recv_domcount > 0); - IDN_CHANSVR_SCANSET_DEL_PENDING(csp, d); - DOMAINSET_DEL(csp->ch_recv_domset_pending, d); - IDN_CHAN_DOMAIN_UNREGISTER(csp, d); - - PR_CHAN("%s: domain %d (channel %d) RECV (pending) " - "scanset = 0x%lx\n", proc, d, channel, - csp->ch_recv_scanset_pending); - PR_CHAN("%s: domain %d (channel %d) domset = 0x%x\n", - proc, d, channel, (uint_t)csp->ch_reg_domset); - - CHECKPOINT_CLOSED(IDNSB_CHKPT_CHAN, - idn_domain[d].dhw.dh_boardset, 2); - - } - if (domset) - csp->ch_recv_changed = 1; - - IDN_CHANNEL_RESUME(channel); -} - -static int -idn_valid_etherheader(struct ether_header *ehp) -{ - uchar_t *eap; - - eap = &ehp->ether_dhost.ether_addr_octet[0]; - - if ((eap[IDNETHER_ZERO] != 0) && (eap[IDNETHER_ZERO] != 0xff)) - return (0); - - if ((eap[IDNETHER_COOKIE1] != IDNETHER_COOKIE1_VAL) && - (eap[IDNETHER_COOKIE1] != 0xff)) - return (0); - - if ((eap[IDNETHER_COOKIE2] != IDNETHER_COOKIE2_VAL) && - (eap[IDNETHER_COOKIE2] != 0xff)) - return (0); - - if ((eap[IDNETHER_RESERVED] != IDNETHER_RESERVED_VAL) && - (eap[IDNETHER_RESERVED] != 0xff)) - return (0); - - if (!VALID_UCHANNEL(eap[IDNETHER_CHANNEL]) && - (eap[IDNETHER_CHANNEL] != 0xff)) - return (0); - - if (!VALID_UDOMAINID(IDN_NETID2DOMID(eap[IDNETHER_NETID])) && - (eap[IDNETHER_NETID] != 0xff)) - return (0); - - return (1); -} - -/* - * Packet header has already been filled in. - * RETURNS: 0 - * ENOLINK - * EPROTO - * ENOSPC - */ -/*ARGSUSED*/ -static int -idn_send_mboxdata(int domid, struct idn *sip, int channel, caddr_t bufp) -{ - idn_mainmbox_t *mmp; - idn_mboxmsg_t *mqp; - smr_pkthdr_t *hdrp; - smr_offset_t bufoffset; - idn_netaddr_t dst; - ushort_t mbox_csum; - int rv = 0; - int pktlen, qi; - procname_t proc = "idn_send_mboxdata"; - - mmp = idn_domain[domid].dmbox.m_send; - if (mmp == NULL) { - PR_DATA("%s: dmbox.m_send == NULL\n", proc); - IDN_KSTAT_INC(sip, si_linkdown); - return (ENOLINK); - } - - mmp += channel; - mutex_enter(&mmp->mm_mutex); - - if (mmp->mm_smr_mboxp == NULL) { - PR_DATA("%s: (d %d, chn %d) mm_smr_mboxp == NULL\n", - proc, domid, channel); - IDN_KSTAT_INC(sip, si_linkdown); - rv = ENOLINK; - goto send_err; - } - mbox_csum = IDN_CKSUM_MBOX(&mmp->mm_smr_mboxp->mt_header); - if (mbox_csum != mmp->mm_smr_mboxp->mt_header.mh_cksum) { - PR_DATA("%s: (d %d, chn %d) mbox hdr cksum (%d) " - "!= actual (%d)\n", - proc, domid, channel, mbox_csum, - mmp->mm_smr_mboxp->mt_header.mh_cksum); - if ((mmp->mm_flags & IDNMMBOX_FLAG_CORRUPTED) == 0) { - cmn_err(CE_WARN, - "IDN: 241: [send] (domain %d, " - "channel %d) SMR CORRUPTED - RELINK", - domid, channel); - mmp->mm_flags |= IDNMMBOX_FLAG_CORRUPTED; - } - IDN_KSTAT_INC(sip, si_mboxcrc); - IDN_KSTAT_INC(sip, si_oerrors); - rv = EPROTO; - goto send_err; - } - - bufoffset = IDN_ADDR2OFFSET(bufp); - hdrp = IDN_BUF2HDR(bufp); - pktlen = hdrp->b_length; - dst.netaddr = hdrp->b_netaddr; - ASSERT(dst.net.chan == (ushort_t)channel); - - mqp = &mmp->mm_smr_mboxp->mt_queue[0]; - qi = mmp->mm_qiput; - - if (mqp[qi].ms_owner) { - PR_DATA("%s: mailbox FULL (qiput=%d, qiget=%d)\n", - proc, mmp->mm_qiput, mmp->mm_qiget); - IDN_KSTAT_INC(sip, si_txfull); - rv = ENOSPC; - goto send_err; - } - if (mqp[qi].ms_flag & IDN_MBOXMSG_FLAG_RECLAIM) { - smr_offset_t recl_bufoffset; - /* - * Remote domain finished with mailbox entry, - * however it has not been reclaimed yet. A reclaim - * was done before coming into this routine, however - * timing may have been such that the entry became - * free just after the reclamation, but before - * entry into here. Go ahead and reclaim this entry. - */ - recl_bufoffset = IDN_BFRAME2OFFSET(mqp[qi].ms_bframe); - - PR_DATA("%s: attempting reclaim (domain %d) " - "(qiput=%d, b_off=0x%x)\n", - proc, domid, qi, recl_bufoffset); - - if (VALID_NWROFFSET(recl_bufoffset, IDN_SMR_BUFSIZE)) { - int recl; - caddr_t b_bufp; - smr_pkthdr_t *b_hdrp; - - b_bufp = IDN_OFFSET2ADDR(recl_bufoffset); - b_hdrp = IDN_BUF2HDR(b_bufp); - - if (IDN_CKSUM_PKT(b_hdrp) != b_hdrp->b_cksum) { - IDN_KSTAT_INC(sip, si_crc); - IDN_KSTAT_INC(sip, si_fcs_errors); - IDN_KSTAT_INC(sip, si_reclaim); - IDN_KSTAT_INC(sip, si_oerrors); - } - - recl = smr_buf_free(domid, b_bufp, b_hdrp->b_length); -#ifdef DEBUG - if (recl == 0) { - PR_DATA("%s: SUCCESSFULLY reclaimed buf " - "(domain %d)\n", proc, domid); - } else { - PR_DATA("%s: WARNING: reclaim failed (FREE) " - "(domain %d)\n", proc, domid); - } -#endif /* DEBUG */ - } else { - IDN_KSTAT_INC(sip, si_smraddr); - IDN_KSTAT_INC(sip, si_reclaim); - PR_DATA("%s: WARNING: reclaim failed (BAD OFFSET) " - "(domain %d)\n", proc, domid); - } - } - - if (*mmp->mm_smr_readyp == 0) { - mmp->mm_qiput = qi; - IDN_KSTAT_INC(sip, si_linkdown); - rv = ENOLINK; - goto send_err; - } - - mqp[qi].ms_flag = IDN_MBOXMSG_FLAG_RECLAIM; - mqp[qi].ms_bframe = IDN_OFFSET2BFRAME(bufoffset); - /* membar_stst(); */ - mqp[qi].ms_owner = 1; - - IDN_MMBOXINDEX_INC(qi); - - mmp->mm_qiput = qi; - - mmp->mm_count++; - - if ((*mmp->mm_smr_readyp) && !(*mmp->mm_smr_activep)) { - idn_msgtype_t mt; - - mt.mt_mtype = IDNP_DATA; - mt.mt_atype = 0; - IDN_KSTAT_INC(sip, si_xdcall); - (void) IDNXDC(domid, &mt, (uint_t)dst.net.chan, 0, 0, 0); - } - mutex_exit(&mmp->mm_mutex); - IDN_KSTAT_INC(sip, si_opackets); - IDN_KSTAT_INC(sip, si_opackets64); - IDN_KSTAT_ADD(sip, si_xmtbytes, pktlen); - IDN_KSTAT_ADD(sip, si_obytes64, (uint64_t)pktlen); - - return (0); - -send_err: - mmp->mm_dropped++; - - mutex_exit(&mmp->mm_mutex); - - return (rv); -} - -static int -idn_recv_mboxdata(int channel, caddr_t bufp) -{ - smr_pkthdr_t *hdrp; - struct idn *sip; - mblk_t *mp = nilp(mblk_t); - int pktlen; - int apktlen; - int rv = 0; - smr_offset_t bufoffset; - ushort_t csum; - idn_netaddr_t dst, daddr; - procname_t proc = "idn_recv_mboxdata"; - - hdrp = IDN_BUF2HDR(bufp); - - csum = IDN_CKSUM_PKT(hdrp); - - sip = IDN_INST2SIP(channel); - if (sip == NULL) { - /*LINTED*/ - sip = IDN_INST2SIP(0); - } - ASSERT(sip); - - if (csum != hdrp->b_cksum) { - PR_DATA("%s: bad checksum(%x) != expected(%x)\n", - proc, (uint_t)csum, (uint_t)hdrp->b_cksum); - IDN_KSTAT_INC(sip, si_crc); - IDN_KSTAT_INC(sip, si_fcs_errors); - rv = -1; - goto recv_err; - } - - daddr.net.chan = (ushort_t)channel; - daddr.net.netid = (ushort_t)idn.localid; - - dst.netaddr = hdrp->b_netaddr; - bufoffset = hdrp->b_offset; - - if (dst.netaddr != daddr.netaddr) { - PR_DATA("%s: wrong dest netaddr (0x%x), expected (0x%x)\n", - proc, dst.netaddr, daddr.netaddr); - IDN_KSTAT_INC(sip, si_nolink); - IDN_KSTAT_INC(sip, si_macrcv_errors); - goto recv_err; - } - pktlen = hdrp->b_length; - apktlen = pktlen; - - if ((pktlen <= 0) || (pktlen > IDN_DATA_SIZE)) { - PR_DATA("%s: invalid packet length (%d) <= 0 || > %lu\n", - proc, pktlen, IDN_DATA_SIZE); - IDN_KSTAT_INC(sip, si_buff); - IDN_KSTAT_INC(sip, si_toolong_errors); - goto recv_err; - } - - mp = allocb(apktlen + IDN_ALIGNSIZE, BPRI_LO); - if (mp == nilp(mblk_t)) { - PR_DATA("%s: allocb(pkt) failed\n", proc); - IDN_KSTAT_INC(sip, si_allocbfail); - IDN_KSTAT_INC(sip, si_norcvbuf); /* MIB II */ - goto recv_err; - } - ASSERT(DB_TYPE(mp) == M_DATA); - /* - * Copy data packet into its streams buffer. - * Align pointers for maximum bcopy performance. - */ - mp->b_rptr = (uchar_t *)IDN_ALIGNPTR(mp->b_rptr, bufoffset); - bcopy(IDN_BUF2DATA(bufp, bufoffset), mp->b_rptr, apktlen); - mp->b_wptr = mp->b_rptr + pktlen; - - if (IDN_CHECKSUM && - !idn_valid_etherheader((struct ether_header *)mp->b_rptr)) { - freeb(mp); - mp = nilp(mblk_t); - PR_DATA("%s: etherheader CORRUPTED\n", proc); - IDN_KSTAT_INC(sip, si_crc); - IDN_KSTAT_INC(sip, si_fcs_errors); - rv = -1; - goto recv_err; - } - - idndl_read(NULL, mp); - -recv_err: - - if (mp == nilp(mblk_t)) { - IDN_KSTAT_INC(sip, si_ierrors); - } - - return (rv); -} - -/* - * When on shutdown path (idn_active_resources) must call - * idn_mainmbox_flush() _BEFORE_ calling idn_reclaim_mboxdata() - * for any final data. This is necessary incase the mailboxes - * have been unregistered. If they have then idn_mainmbox_flush() - * will set mm_smr_mboxp to NULL which prevents us from touching - * poison SMR space. - */ -int -idn_reclaim_mboxdata(int domid, int channel, int nbufs) -{ - idn_mainmbox_t *mmp; - idn_mboxmsg_t *mqp; - smr_pkthdr_t *hdrp; - idn_domain_t *dp; - int qi; - int mi; - int reclaim_cnt = 0; - int free_cnt; - ushort_t csum; - struct idn *sip; - smr_offset_t reclaim_list, curr, prev; - procname_t proc = "idn_reclaim_mboxdata"; - - - sip = IDN_INST2SIP(channel); - if (sip == NULL) { - /*LINTED*/ - sip = IDN_INST2SIP(0); - } - ASSERT(sip); - - dp = &idn_domain[domid]; - - PR_DATA("%s: requested %d buffers from domain %d\n", - proc, nbufs, domid); - - if (lock_try(&dp->dreclaim_inprogress) == 0) { - /* - * Reclaim is already in progress, don't - * bother. - */ - PR_DATA("%s: reclaim already in progress\n", proc); - return (0); - } - - if (dp->dmbox.m_send == NULL) - return (0); - - reclaim_list = curr = prev = IDN_NIL_SMROFFSET; - - mi = (int)dp->dreclaim_index; - do { - ushort_t mbox_csum; - - mmp = &dp->dmbox.m_send[mi]; - /* do-while continues down */ - ASSERT(mmp); - if (mutex_tryenter(&mmp->mm_mutex) == 0) { - /* - * This channel is busy, move on. - */ - IDN_MBOXCHAN_INC(mi); - continue; - } - - if (mmp->mm_smr_mboxp == NULL) { - PR_DATA("%s: no smr pointer for domid %d, chan %d\n", - proc, domid, (int)mmp->mm_channel); - ASSERT(mmp->mm_qiget == mmp->mm_qiput); - mutex_exit(&mmp->mm_mutex); - IDN_MBOXCHAN_INC(mi); - continue; - } - mbox_csum = IDN_CKSUM_MBOX(&mmp->mm_smr_mboxp->mt_header); - if (mbox_csum != mmp->mm_smr_mboxp->mt_header.mh_cksum) { - PR_DATA("%s: (d %d, chn %d) mbox hdr " - "cksum (%d) != actual (%d)\n", - proc, domid, (int)mmp->mm_channel, mbox_csum, - mmp->mm_smr_mboxp->mt_header.mh_cksum); - IDN_KSTAT_INC(sip, si_mboxcrc); - IDN_KSTAT_INC(sip, si_oerrors); - mutex_exit(&mmp->mm_mutex); - IDN_MBOXCHAN_INC(mi); - continue; - } - mqp = &mmp->mm_smr_mboxp->mt_queue[0]; - qi = mmp->mm_qiget; - - while (!mqp[qi].ms_owner && - (mqp[qi].ms_flag & IDN_MBOXMSG_FLAG_RECLAIM) && - nbufs) { - idn_mboxmsg_t *msp; - int badbuf; - - badbuf = 0; - msp = &mqp[qi]; - - if (msp->ms_flag & IDN_MBOXMSG_FLAG_ERRMASK) { - PR_DATA("%s: msg.flag ERROR(0x%x) (off=0x%x, " - "domid=%d, qiget=%d)\n", proc, - (uint_t)(msp->ms_flag & - IDN_MBOXMSG_FLAG_ERRMASK), - IDN_BFRAME2OFFSET(msp->ms_bframe), - domid, qi); - } - prev = curr; - curr = IDN_BFRAME2OFFSET(mqp[qi].ms_bframe); - - if (!VALID_NWROFFSET(curr, IDN_SMR_BUFSIZE)) { - badbuf = 1; - IDN_KSTAT_INC(sip, si_reclaim); - } else { - /* - * Put the buffers onto a list that will be - * formally reclaimed down below. This allows - * us to free up mboxq entries as fast as - * possible. - */ - hdrp = IDN_BUF2HDR(IDN_OFFSET2ADDR(curr)); - csum = IDN_CKSUM_PKT(hdrp); - - if (csum != hdrp->b_cksum) { - badbuf = 1; - IDN_KSTAT_INC(sip, si_crc); - IDN_KSTAT_INC(sip, si_fcs_errors); - IDN_KSTAT_INC(sip, si_reclaim); - if (!(mmp->mm_flags & - IDNMMBOX_FLAG_CORRUPTED)) { - cmn_err(CE_WARN, - "IDN: 241: [send] " - "(domain %d, channel " - "%d) SMR CORRUPTED - " - "RELINK", - domid, channel); - mmp->mm_flags |= - IDNMMBOX_FLAG_CORRUPTED; - } - - } else if (reclaim_list == IDN_NIL_SMROFFSET) { - reclaim_list = curr; - } else { - caddr_t bufp; - - bufp = IDN_OFFSET2ADDR(prev); - hdrp = IDN_BUF2HDR(bufp); - hdrp->b_next = curr; - } - } - - mqp[qi].ms_flag = 0; - - IDN_MMBOXINDEX_INC(qi); - - if (!badbuf) { - nbufs--; - reclaim_cnt++; - } - - if (qi == mmp->mm_qiget) - break; - } - mmp->mm_qiget = qi; - - mutex_exit(&mmp->mm_mutex); - - IDN_MBOXCHAN_INC(mi); - - } while ((mi != (int)dp->dreclaim_index) && nbufs); - - dp->dreclaim_index = (uchar_t)mi; - - if (reclaim_list != IDN_NIL_SMROFFSET) { - hdrp = IDN_BUF2HDR(IDN_OFFSET2ADDR(curr)); - hdrp->b_next = IDN_NIL_SMROFFSET; - } - - PR_DATA("%s: reclaimed %d buffers from domain %d\n", - proc, reclaim_cnt, domid); - - if (reclaim_cnt == 0) { - lock_clear(&dp->dreclaim_inprogress); - return (0); - } - - /* - * Now actually go and reclaim (free) the buffers. - */ - free_cnt = 0; - - for (curr = reclaim_list; curr != IDN_NIL_SMROFFSET; ) { - caddr_t bufp; - - bufp = IDN_OFFSET2ADDR(curr); - hdrp = IDN_BUF2HDR(bufp); - csum = IDN_CKSUM_PKT(hdrp); - if (csum != hdrp->b_cksum) { - /* - * Once corruption is detected we - * can't trust our list any further. - * These buffers are effectively lost. - */ - cmn_err(CE_WARN, - "IDN: 241: [send] (domain %d, channel %d) SMR " - "CORRUPTED - RELINK", domid, channel); - break; - } - - curr = hdrp->b_next; - - if (!smr_buf_free(domid, bufp, hdrp->b_length)) - free_cnt++; - } - - if ((dp->dio < IDN_WINDOW_EMAX) && dp->diocheck) { - lock_clear(&dp->diocheck); - IDN_MSGTIMER_STOP(domid, IDNP_DATA, 0); - } - -#ifdef DEBUG - if (free_cnt != reclaim_cnt) { - PR_DATA("%s: *** WARNING *** freecnt(%d) != reclaim_cnt (%d)\n", - proc, free_cnt, reclaim_cnt); - } -#endif /* DEBUG */ - - lock_clear(&dp->dreclaim_inprogress); - - return (reclaim_cnt); -} - -void -idn_signal_data_server(int domid, ushort_t channel) -{ - idn_nack_t nacktype = 0; - idn_domain_t *dp; - idn_chansvr_t *csp; - int c, min_chan, max_chan; - idn_mainmbox_t *mmp; - procname_t proc = "idn_signal_data_server"; - - - if (domid == IDN_NIL_DOMID) - return; - - dp = &idn_domain[domid]; - - if (dp->dawol.a_count > 0) { - /* - * Domain was previously AWOL, but no longer. - */ - IDN_SYNC_LOCK(); - IDN_GLOCK_EXCL(); - idn_clear_awol(domid); - IDN_GUNLOCK(); - IDN_SYNC_UNLOCK(); - } - /* - * Do a precheck before wasting time trying to acquire the lock. - */ - if ((dp->dstate != IDNDS_CONNECTED) || !IDN_DLOCK_TRY_SHARED(domid)) { - /* - * Either we're not connected or somebody is busy working - * on the domain. Bail on the signal for now, we'll catch - * it on the next go around. - */ - return; - } - /* - * We didn't have the drwlock on the first check of dstate, - * but now that we do, make sure the world hasn't changed! - */ - if (dp->dstate != IDNDS_CONNECTED) { - /* - * If we reach here, then no connection. - * Send no response if this is the case. - */ - nacktype = IDNNACK_NOCONN; - goto send_dresp; - } - - /* - * No need to worry about locking mainmbox - * because we're already holding reader - * lock on domain, plus we're just reading - * fields in the mainmbox which only change - * (or go away) when the writer lock is - * held on the domain. - */ - if ((mmp = dp->dmbox.m_recv) == NULL) { - /* - * No local mailbox. - */ - nacktype = IDNNACK_BADCFG; - goto send_dresp; - } - if ((channel != IDN_BROADCAST_ALLCHAN) && (channel >= IDN_MAX_NETS)) { - nacktype = IDNNACK_BADCHAN; - goto send_dresp; - } - if (channel == IDN_BROADCAST_ALLCHAN) { - PR_DATA("%s: requested signal to ALL channels on domain %d\n", - proc, domid); - min_chan = 0; - max_chan = IDN_MAX_NETS - 1; - } else { - PR_DATA("%s: requested signal to channel %d on domain %d\n", - proc, channel, domid); - min_chan = max_chan = (int)channel; - } - mmp += min_chan; - for (c = min_chan; c <= max_chan; mmp++, c++) { - - /* - * We do a quick check for a pending channel. - * If pending it will need activation and we rather - * do that through a separate (proto) thread. - */ - csp = &idn.chan_servers[c]; - - if (csp->ch_recv.c_checkin) { - PR_DATA("%s: chansvr (%d) for domid %d CHECK-IN\n", - proc, c, domid); - continue; - } - - if (IDN_CHAN_TRYLOCK_RECV(csp) == 0) { - /* - * Failed to grab lock, server must be active. - */ - PR_DATA("%s: chansvr (%d) for domid %d already actv\n", - proc, c, domid); - continue; - } - - if (IDN_CHANNEL_IS_PENDING(csp)) { - /* - * Lock is pending. Submit asynchronous - * job to activate and move-on. - */ - IDN_CHAN_UNLOCK_RECV(csp); - idn_submit_chanactivate_job(c); - continue; - } - - /* - * If he ain't active, we ain't talkin'. - */ - if (IDN_CHANNEL_IS_RECV_ACTIVE(csp) == 0) { - IDN_CHAN_UNLOCK_RECV(csp); - PR_DATA("%s: chansvr (%d) for domid %d inactive\n", - proc, c, domid); - continue; - } - - if (mutex_tryenter(&mmp->mm_mutex) == 0) { - IDN_CHAN_UNLOCK_RECV(csp); - continue; - } - - if (mmp->mm_csp != csp) { - /* - * Not registered. - */ - mutex_exit(&mmp->mm_mutex); - IDN_CHAN_UNLOCK_RECV(csp); - continue; - - } - if (mmp->mm_smr_mboxp == NULL) { - /* - * No SMR mailbox. - */ - mutex_exit(&mmp->mm_mutex); - IDN_CHAN_UNLOCK_RECV(csp); - continue; - } - mutex_exit(&mmp->mm_mutex); - - if (csp->ch_recv.c_inprogress) { - /* - * Data server is already active. - */ - IDN_CHAN_UNLOCK_RECV(csp); - PR_DATA("%s: chansvr (%d) for domid %d already actv\n", - proc, c, domid); - continue; - } - ASSERT(csp == &idn.chan_servers[c]); - - - PR_DATA("%s: signaling data dispatcher for chan %d dom %d\n", - proc, c, domid); - ASSERT(csp); - cv_signal(&csp->ch_recv_cv); - IDN_CHAN_UNLOCK_RECV(csp); - } - - if (!nacktype || (channel == IDN_BROADCAST_ALLCHAN)) { - /* - * If there were no real errors or we were - * handling multiple channels, then just - * return. - */ - IDN_DUNLOCK(domid); - return; - } - -send_dresp: - - PR_DATA("%s: sending NACK (%s) back to domain %d (cpu %d)\n", - proc, idnnack_str[nacktype], domid, idn_domain[domid].dcpu); - - idn_send_dataresp(domid, nacktype); - - IDN_DUNLOCK(domid); -} - -/*ARGSUSED*/ -static int -idn_recv_data(int domid, idn_msgtype_t *mtp, idn_xdcargs_t xargs) -{ -#ifdef DEBUG - uint_t msg = mtp ? mtp->mt_mtype : 0; - uint_t msgarg = mtp ? mtp->mt_atype : 0; - procname_t proc = "idn_recv_data"; - - PR_PROTO("%s:%d: DATA message received (msg = 0x%x, msgarg = 0x%x)\n", - proc, domid, msg, msgarg); - PR_PROTO("%s:%d: xargs = (0x%x, 0x%x, 0x%x, 0x%x)\n", - proc, domid, xargs[0], xargs[1], xargs[2], xargs[3]); -#endif /* DEBUG */ - - return (0); -} - -/* - * Only used when sending a negative response. - */ -static void -idn_send_dataresp(int domid, idn_nack_t nacktype) -{ - idn_msgtype_t mt; - - ASSERT(IDN_DLOCK_IS_HELD(domid)); - - if (idn_domain[domid].dcpu == IDN_NIL_DCPU) - return; - - mt.mt_mtype = IDNP_NACK; - mt.mt_atype = IDNP_DATA; - - (void) IDNXDC(domid, &mt, (uint_t)nacktype, 0, 0, 0); -} - -/* - * Checksum routine used in checksum smr_pkthdr_t and idn_mboxhdr_t. - */ -static ushort_t -idn_cksum(register ushort_t *hdrp, register int count) -{ - register int i; - register ushort_t sum = 0; - - for (i = 0; i < count; i++) - sum += hdrp[i]; - - sum = (sum >> 16) + (sum & 0xffff); - sum += (sum >> 16); - - return (~sum); -} - -/* - * ------------------------------------------------ - */ - -int -idn_open_channel(int channel) -{ - int masterid; - idn_chansvr_t *csp; - struct idn *sip; - procname_t proc = "idn_open_channel"; - - if (channel >= IDN_MAX_NETS) { - cmn_err(CE_WARN, - "IDN: 242: maximum channels (%d) already open", - IDN_MAX_NETS); - return (-1); - } - IDN_GLOCK_EXCL(); - - ASSERT(idn.chan_servers != NULL); - - csp = &idn.chan_servers[channel]; - - IDN_CHAN_LOCK_GLOBAL(csp); - - if (IDN_CHANNEL_IS_ATTACHED(csp)) { - PR_CHAN("%s: channel %d already open\n", proc, channel); - IDN_CHAN_UNLOCK_GLOBAL(csp); - IDN_GUNLOCK(); - return (0); - } - - /* - * Need to zero out the kstats now that we're activating - * this channel. - */ - for (sip = idn.sip; sip; sip = sip->si_nextp) { - if (sip->si_dip && (ddi_get_instance(sip->si_dip) == channel)) { - bzero(&sip->si_kstat, sizeof (sip->si_kstat)); - break; - } - } - - IDN_CHANSVC_MARK_ATTACHED(csp); - idn.nchannels++; - CHANSET_ADD(idn.chanset, channel); - IDN_CHANNEL_ATTACH(channel); - - IDN_CHAN_UNLOCK_GLOBAL(csp); - - /* - * We increase our window threshold each time a channel - * is opened. - */ - ASSERT(idn.nchannels > 0); - IDN_WINDOW_EMAX = IDN_WINDOW_MAX + - ((idn.nchannels - 1) * IDN_WINDOW_INCR); - - PR_CHAN("%s: channel %d is OPEN (nchannels = %d)\n", - proc, channel, idn.nchannels); - - masterid = IDN_GET_MASTERID(); - IDN_GUNLOCK(); - - /* - * Check if there is an active master to which - * we're connected. If so, then activate channel. - */ - if (masterid != IDN_NIL_DOMID) { - idn_domain_t *dp; - - dp = &idn_domain[masterid]; - IDN_DLOCK_SHARED(masterid); - if (dp->dvote.v.master && (dp->dstate == IDNDS_CONNECTED)) - (void) idn_activate_channel(CHANSET(channel), - IDNCHAN_ONLINE); - IDN_DUNLOCK(masterid); - } - - return (0); -} - -void -idn_close_channel(int channel, idn_chanop_t chanop) -{ - idn_chansvr_t *csp; - procname_t proc = "idn_close_channel"; - - - ASSERT(idn.chan_servers != NULL); - - csp = &idn.chan_servers[channel]; - - IDN_GLOCK_EXCL(); - - IDN_CHAN_LOCK_GLOBAL(csp); - if (IDN_CHANNEL_IS_DETACHED(csp)) { - PR_CHAN("%s: channel %d already closed\n", proc, channel); - IDN_CHAN_UNLOCK_GLOBAL(csp); - IDN_GUNLOCK(); - return; - } - IDN_CHAN_UNLOCK_GLOBAL(csp); - - idn_deactivate_channel(CHANSET(channel), chanop); - - IDN_CHAN_LOCK_GLOBAL(csp); - - if (chanop == IDNCHAN_HARD_CLOSE) { - idn.nchannels--; - CHANSET_DEL(idn.chanset, channel); - /* - * We increase our window threshold each time a channel - * is opened. - */ - if (idn.nchannels <= 0) - IDN_WINDOW_EMAX = 0; - else - IDN_WINDOW_EMAX = IDN_WINDOW_MAX + - ((idn.nchannels - 1) * IDN_WINDOW_INCR); - } - - PR_CHAN("%s: channel %d is (%s) CLOSED (nchannels = %d)\n", - proc, channel, - (chanop == IDNCHAN_SOFT_CLOSE) ? "SOFT" - : (chanop == IDNCHAN_HARD_CLOSE) ? "HARD" : "OFFLINE", - idn.nchannels); - - IDN_CHAN_UNLOCK_GLOBAL(csp); - IDN_GUNLOCK(); -} - -static int -idn_activate_channel(idn_chanset_t chanset, idn_chanop_t chanop) -{ - int c, rv = 0; - procname_t proc = "idn_activate_channel"; - - PR_CHAN("%s: chanset = 0x%x, chanop = %s\n", - proc, chanset, chanop_str[chanop]); - - if (idn.state != IDNGS_ONLINE) { - /* - * Can't activate any channels unless local - * domain is connected and thus has a master. - */ - PR_CHAN("%s: local domain not connected. no data servers\n", - proc); - return (-1); - } - - for (c = 0; c < IDN_MAX_NETS; c++) { - idn_chansvr_t *csp; - idn_mboxhdr_t *mainhp; - struct idn *sip; - - if (!CHAN_IN_SET(chanset, c)) - continue; - csp = &idn.chan_servers[c]; - - if (chanop == IDNCHAN_ONLINE) { - IDN_CHAN_LOCK_GLOBAL(csp); - } else { - /* - * We don't wait to grab the global lock - * if IDNCHAN_OPEN since these occur along - * critical data paths and will be retried - * anyway if needed. - */ - if (IDN_CHAN_TRYLOCK_GLOBAL(csp) == 0) { - PR_CHAN("%s: failed to acquire global " - "lock for channel %d\n", - proc, c); - continue; - } - } - - if (!IDN_CHANNEL_IS_ATTACHED(csp)) { - PR_CHAN("%s: channel %d NOT open\n", proc, c); - IDN_CHAN_UNLOCK_GLOBAL(csp); - continue; - - } - - if (IDN_CHANNEL_IS_ACTIVE(csp)) { - - PR_CHAN("%s: channel %d already active\n", proc, c); - rv++; - IDN_CHAN_UNLOCK_GLOBAL(csp); - continue; - - } - /* - * Channel activation can happen asynchronously. - */ - IDN_CHANNEL_SUSPEND(c, 0); - - if (IDN_CHANNEL_IS_PENDING(csp) && (chanop == IDNCHAN_OPEN)) { - - PR_CHAN("%s: ACTIVATING channel %d\n", proc, c); - - if (idn_activate_channel_services(c) >= 0) { - PR_CHAN("%s: Setting channel %d ACTIVE\n", - proc, c); - IDN_CHANSVC_MARK_ACTIVE(csp); - rv++; - } - } else if (!IDN_CHANNEL_IS_PENDING(csp) && - (chanop == IDNCHAN_ONLINE)) { - PR_CHAN("%s: Setting channel %d PENDING\n", proc, c); - - IDN_CHANSVC_MARK_PENDING(csp); - } - /* - * Don't syncheader (i.e. touch SMR) unless - * channel is at least ENABLED. For a DISABLED - * channel, the SMR may be invalid so do NOT - * touch it. - */ - if (IDN_CHANNEL_IS_ENABLED(csp) && - ((mainhp = idn_chan_server_syncheader(c)) != NULL)) { - PR_CHAN("%s: marking chansvr (mhp=0x%p) %d READY\n", - proc, (void *)mainhp, c); - mainhp->mh_svr_ready = 1; - } - - IDN_CHANNEL_RESUME(c); - sip = IDN_INST2SIP(c); - ASSERT(sip); - if (sip->si_wantw) { - mutex_enter(&idn.sipwenlock); - idndl_wenable(sip); - mutex_exit(&idn.sipwenlock); - } - IDN_CHAN_UNLOCK_GLOBAL(csp); - - } - /* - * Returns "not active", i.e. value of 0 indicates - * no channels are activated. - */ - return (rv == 0); -} - -static void -idn_deactivate_channel(idn_chanset_t chanset, idn_chanop_t chanop) -{ - int c; - procname_t proc = "idn_deactivate_channel"; - - PR_CHAN("%s: chanset = 0x%x, chanop = %s\n", - proc, chanset, chanop_str[chanop]); - - for (c = 0; c < IDN_MAX_NETS; c++) { - idn_chansvr_t *csp; - idn_mboxhdr_t *mainhp; - - if (!CHAN_IN_SET(chanset, c)) - continue; - - csp = &idn.chan_servers[c]; - - IDN_CHAN_LOCK_GLOBAL(csp); - - if (((chanop == IDNCHAN_SOFT_CLOSE) && - !IDN_CHANNEL_IS_ACTIVE(csp)) || - ((chanop == IDNCHAN_HARD_CLOSE) && - IDN_CHANNEL_IS_DETACHED(csp)) || - ((chanop == IDNCHAN_OFFLINE) && - !IDN_CHANNEL_IS_ENABLED(csp))) { - - ASSERT(!IDN_CHANNEL_IS_RECV_ACTIVE(csp)); - ASSERT(!IDN_CHANNEL_IS_SEND_ACTIVE(csp)); - - PR_CHAN("%s: channel %d already deactivated\n", - proc, c); - IDN_CHAN_UNLOCK_GLOBAL(csp); - continue; - } - - switch (chanop) { - case IDNCHAN_OFFLINE: - IDN_CHANSVC_MARK_IDLE(csp); - IDN_CHANSVC_MARK_DISABLED(csp); - IDN_CHANNEL_STOP(c, 1); - mainhp = idn_chan_server_syncheader(c); - if (mainhp != NULL) - mainhp->mh_svr_ready = 0; - break; - - case IDNCHAN_HARD_CLOSE: - IDN_CHANSVC_MARK_DETACHED(csp); - IDN_CHANNEL_DETACH(c, 1); - mainhp = idn_chan_server_syncheader(c); - if (mainhp != NULL) - mainhp->mh_svr_ready = 0; - break; - - default: - IDN_CHANSVC_MARK_IDLE(csp); - IDN_CHANNEL_SUSPEND(c, 1); - ASSERT(IDN_CHANNEL_IS_ATTACHED(csp)); - break; - } - - lock_clear(&csp->ch_actvlck); - lock_clear(&csp->ch_initlck); - - PR_CHAN("%s: DEACTIVATING channel %d (%s)\n", proc, c, - chanop_str[chanop]); - PR_CHAN("%s: removing chanset 0x%x data svrs for " - "each domain link\n", proc, chanset); - - (void) idn_deactivate_channel_services(c, chanop); - } - /* - * Returns with channels unlocked. - */ -} - -/* - * The priority of the channel server must be less than that - * of the protocol server since the protocol server tasks - * are (can be) of more importance. - * - * Possible range: 60-99. - */ -static pri_t idn_chansvr_pri = (7 * MAXCLSYSPRI) / 8; - -static int -idn_activate_channel_services(int channel) -{ - idn_chansvr_t *csp; - procname_t proc = "idn_activate_channel_services"; - - - ASSERT((channel >= 0) && (channel < IDN_MAX_NETS)); - - csp = &idn.chan_servers[channel]; - - ASSERT(IDN_CHAN_GLOBAL_IS_LOCKED(csp)); - ASSERT(IDN_CHAN_LOCAL_IS_LOCKED(csp)); - - if (csp->ch_recv_threadp) { - /* - * There's an existing dispatcher! - * Must have been idle'd during an earlier - * stint. - */ - ASSERT(csp->ch_id == (uchar_t)channel); - PR_CHAN("%s: existing chansvr FOUND for (c=%d)\n", - proc, channel); - - if (IDN_CHANNEL_IS_PENDING(csp) == 0) - return (-1); - - PR_CHAN("%s: chansvr (c=%d) Rstate = 0x%x, Sstate = 0x%x\n", - proc, channel, csp->ch_recv.c_state, - csp->ch_send.c_state); - - cv_signal(&csp->ch_recv_cv); - - return (0); - } - - if (IDN_CHANNEL_IS_PENDING(csp) == 0) - return (-1); - - csp->ch_id = (uchar_t)channel; - - PR_CHAN("%s: init channel %d server\n", proc, channel); - - csp->ch_recv_morguep = GETSTRUCT(ksema_t, 1); - sema_init(csp->ch_recv_morguep, 0, NULL, SEMA_DRIVER, NULL); - - csp->ch_recv.c_inprogress = 0; - csp->ch_recv.c_waiters = 0; - csp->ch_recv.c_checkin = 0; - csp->ch_recv_changed = 1; - - csp->ch_recv_domset = csp->ch_reg_domset; - - csp->ch_recv_waittime = IDN_NETSVR_WAIT_MIN; - - csp->ch_recv_threadp = thread_create(NULL, 0, - idn_chan_server, &csp, sizeof (csp), &p0, TS_RUN, idn_chansvr_pri); - - csp->ch_send.c_inprogress = 0; - csp->ch_send.c_waiters = 0; - csp->ch_send.c_checkin = 0; - - return (0); -} - -/* - * This routine can handle terminating a set of channel - * servers all at once, however currently only used - * for serial killing, i.e. one-at-a-time. - * - * Entered with RECV locks held on chanset. - * Acquires SEND locks if needed. - * Leaves with all RECV and SEND locks dropped. - */ -static int -idn_deactivate_channel_services(int channel, idn_chanop_t chanop) -{ - idn_chansvr_t *csp; - int cs_count; - int c; - idn_chanset_t chanset; - ksema_t *central_morguep = NULL; - procname_t proc = "idn_deactivate_channel_services"; - - - ASSERT(idn.chan_servers); - - PR_CHAN("%s: deactivating channel %d services\n", proc, channel); - - /* - * XXX - * Old code allowed us to deactivate multiple channel - * servers at once. Keep for now just in case. - */ - chanset = CHANSET(channel); - - /* - * Point all the data dispatchers to the same morgue - * so we can kill them all at once. - */ - cs_count = 0; - for (c = 0; c < IDN_MAX_NETS; c++) { - if (!CHAN_IN_SET(chanset, c)) - continue; - - csp = &idn.chan_servers[c]; - ASSERT(IDN_CHAN_GLOBAL_IS_LOCKED(csp)); - ASSERT(IDN_CHAN_LOCAL_IS_LOCKED(csp)); - - if (csp->ch_recv_threadp == NULL) { - /* - * No channel server home. - * But we're still holding the c_mutex. - * At mark him idle incase we start him up. - */ - PR_CHAN("%s: no channel server found for chan %d\n", - proc, c); - IDN_CHAN_UNLOCK_LOCAL(csp); - IDN_CHAN_UNLOCK_GLOBAL(csp); - continue; - } - ASSERT(csp->ch_id == (uchar_t)c); - - /* - * Okay, now we've blocked the send and receive sides. - */ - - if ((chanop == IDNCHAN_SOFT_CLOSE) || - (chanop == IDNCHAN_OFFLINE)) { - /* - * We set turned off the ACTIVE flag, but there's - * no guarantee he stopped because of it. He may - * have already been sleeping. We need to be - * sure he recognizes the IDLE, so we need to - * signal him and give him a chance to see it. - */ - cv_signal(&csp->ch_recv_cv); - IDN_CHAN_UNLOCK_LOCAL(csp); - IDN_CHAN_UNLOCK_GLOBAL(csp); - cs_count++; - continue; - } - - PR_CHAN("%s: pointing chansvr %d to morgue (0x%p)\n", - proc, c, central_morguep ? (void *)central_morguep - : (void *)(csp->ch_recv_morguep)); - - if (central_morguep == NULL) { - central_morguep = csp->ch_recv_morguep; - } else { - sema_destroy(csp->ch_recv_morguep); - FREESTRUCT(csp->ch_recv_morguep, ksema_t, 1); - - csp->ch_recv_morguep = central_morguep; - } - cv_signal(&csp->ch_recv_cv); - if (csp->ch_recv.c_waiters > 0) - cv_broadcast(&csp->ch_recv.c_cv); - /* - * Save any existing binding for next reincarnation. - * Note that we're holding the local and global - * locks so we're protected against others touchers - * of the ch_bound_cpuid fields. - */ - csp->ch_bound_cpuid_pending = csp->ch_bound_cpuid; - csp->ch_bound_cpuid = -1; - IDN_CHAN_UNLOCK_LOCAL(csp); - IDN_CHAN_UNLOCK_GLOBAL(csp); - cs_count++; - } - PR_CHAN("%s: signaled %d chansvrs for chanset 0x%x\n", - proc, cs_count, chanset); - - if ((chanop == IDNCHAN_SOFT_CLOSE) || (chanop == IDNCHAN_OFFLINE)) - return (cs_count); - - PR_CHAN("%s: waiting for %d (chnset=0x%x) chan svrs to term\n", - proc, cs_count, chanset); - PR_CHAN("%s: morguep = 0x%p\n", proc, (void *)central_morguep); - - ASSERT((cs_count > 0) ? (central_morguep != NULL) : 1); - while (cs_count-- > 0) - sema_p(central_morguep); - - if (central_morguep) { - sema_destroy(central_morguep); - FREESTRUCT(central_morguep, ksema_t, 1); - } - - return (cs_count); -} - -int -idn_chanservers_init() -{ - int c; - idn_chansvr_t *csp; - - - if (idn.chan_servers) - return (0); - - idn.chan_servers = GETSTRUCT(idn_chansvr_t, IDN_MAXMAX_NETS); - - for (c = 0; c < IDN_MAXMAX_NETS; c++) { - csp = &idn.chan_servers[c]; - mutex_init(&csp->ch_send.c_mutex, NULL, MUTEX_DEFAULT, NULL); - mutex_init(&csp->ch_recv.c_mutex, NULL, MUTEX_DEFAULT, NULL); - cv_init(&csp->ch_send.c_cv, NULL, CV_DRIVER, NULL); - cv_init(&csp->ch_recv.c_cv, NULL, CV_DRIVER, NULL); - cv_init(&csp->ch_recv_cv, NULL, CV_DRIVER, NULL); - csp->ch_bound_cpuid = -1; - csp->ch_bound_cpuid_pending = -1; - } - - return (c); -} - -void -idn_chanservers_deinit() -{ - int c; - idn_chansvr_t *csp; - - - if (idn.chan_servers == NULL) - return; - - for (c = 0; c < IDN_MAXMAX_NETS; c++) { - csp = &idn.chan_servers[c]; - - mutex_destroy(&csp->ch_send.c_mutex); - mutex_destroy(&csp->ch_recv.c_mutex); - cv_destroy(&csp->ch_send.c_cv); - cv_destroy(&csp->ch_recv.c_cv); - cv_destroy(&csp->ch_recv_cv); - } - - FREESTRUCT(idn.chan_servers, idn_chansvr_t, IDN_MAXMAX_NETS); - idn.chan_servers = NULL; -} - -static void -idn_exec_chanactivate(void *chn) -{ - int not_active, channel; - idn_chansvr_t *csp; - - channel = (int)(uintptr_t)chn; - - IDN_GLOCK_SHARED(); - if (idn.chan_servers == NULL) { - IDN_GUNLOCK(); - return; - } - csp = &idn.chan_servers[channel]; - - if (IDN_CHAN_TRYLOCK_GLOBAL(csp) == 0) { - /* - * If we can't grab the global lock, then - * something is up, skip out. - */ - IDN_GUNLOCK(); - return; - } - IDN_GUNLOCK(); - - if (IDN_CHANNEL_IS_PENDING(csp) && lock_try(&csp->ch_actvlck)) { - IDN_CHAN_UNLOCK_GLOBAL(csp); - not_active = idn_activate_channel(CHANSET(channel), - IDNCHAN_OPEN); - if (not_active) - lock_clear(&csp->ch_actvlck); - } else { - IDN_CHAN_UNLOCK_GLOBAL(csp); - } -} - -/* - * Delayed activation of channel. We don't want to do this within - * idn_signal_data_server() since that's called within the context - * of an XDC handler so we submit it as a timeout() call to be short - * as soon as possible. - * The ch_initlck & ch_actvlck are used to synchronize activation - * of the channel so that we don't have multiple idn_activate_channel's - * attempting to activate the same channel. - */ -static void -idn_submit_chanactivate_job(int channel) -{ - idn_chansvr_t *csp; - - if (idn.chan_servers == NULL) - return; - csp = &idn.chan_servers[channel]; - - if (lock_try(&csp->ch_initlck) == 0) - return; - - (void) timeout(idn_exec_chanactivate, (caddr_t)(uintptr_t)channel, 1); -} - -/*ARGSUSED0*/ -static void -idn_xmit_monitor(void *unused) -{ - int c, d; - idn_chansvr_t *csp; - idn_chanset_t wake_set; - domainset_t conset; - smr_slab_t *sp; - procname_t proc = "idn_xmit_monitor"; - - CHANSET_ZERO(wake_set); - - mutex_enter(&idn.xmit_lock); - if ((idn.xmit_tid == NULL) || !idn.xmit_chanset_wanted) { - idn.xmit_tid = NULL; - mutex_exit(&idn.xmit_lock); - PR_XMON("%s: bailing out\n", proc); - return; - } - - /* - * No point in transmitting unless state - * is ONLINE. - */ - if (idn.state != IDNGS_ONLINE) - goto retry; - - conset = idn.domset.ds_connected; - - /* - * Try and reclaim some buffers if possible. - */ - for (d = 0; d < MAX_DOMAINS; d++) { - if (!DOMAIN_IN_SET(conset, d)) - continue; - - if (!IDN_DLOCK_TRY_SHARED(d)) - continue; - - if (idn_domain[d].dcpu != IDN_NIL_DCPU) - (void) idn_reclaim_mboxdata(d, 0, -1); - - IDN_DUNLOCK(d); - } - - /* - * Now check if we were successful in getting - * any buffers. - */ - DSLAB_LOCK_SHARED(idn.localid); - sp = idn_domain[idn.localid].dslab; - for (; sp; sp = sp->sl_next) - if (sp->sl_free) - break; - DSLAB_UNLOCK(idn.localid); - - /* - * If there are no buffers available, - * no point in reenabling the queues. - */ - if (sp == NULL) - goto retry; - - CHANSET_ZERO(wake_set); - for (c = 0; c < IDN_MAX_NETS; c++) { - int pending_bits; - struct idn *sip; - - if (!CHAN_IN_SET(idn.xmit_chanset_wanted, c)) - continue; - - csp = &idn.chan_servers[c]; - if (!IDN_CHAN_TRYLOCK_GLOBAL(csp)) - continue; - - pending_bits = csp->ch_state & IDN_CHANSVC_PENDING_BITS; - - sip = IDN_INST2SIP(c); - - if (!csp->ch_send.c_checkin && - (pending_bits == IDN_CHANSVC_PENDING_BITS) && - sip && (sip->si_flags & IDNRUNNING)) { - - IDN_CHAN_UNLOCK_GLOBAL(csp); - CHANSET_ADD(wake_set, c); - - PR_XMON("%s: QENABLE for channel %d\n", proc, c); - - rw_enter(&idn.struprwlock, RW_READER); - mutex_enter(&idn.sipwenlock); - idndl_wenable(sip); - mutex_exit(&idn.sipwenlock); - rw_exit(&idn.struprwlock); - } else { - IDN_CHAN_UNLOCK_GLOBAL(csp); - } - } - - /* - * Clear the channels we enabled. - */ - idn.xmit_chanset_wanted &= ~wake_set; - -retry: - - if (idn.xmit_chanset_wanted == 0) - idn.xmit_tid = NULL; - else - idn.xmit_tid = timeout(idn_xmit_monitor, NULL, - idn_xmit_monitor_freq); - - mutex_exit(&idn.xmit_lock); -} - -void -idn_xmit_monitor_kickoff(int chan_wanted) -{ - procname_t proc = "idn_xmit_monitor_kickoff"; - - mutex_enter(&idn.xmit_lock); - - if (chan_wanted < 0) { - /* - * Wants all channels. - */ - idn.xmit_chanset_wanted = CHANSET_ALL; - } else { - CHANSET_ADD(idn.xmit_chanset_wanted, chan_wanted); - } - - if (idn.xmit_tid != (timeout_id_t)NULL) { - /* - * A monitor is already running, so - * he will catch the new "wants" when - * he comes around. - */ - mutex_exit(&idn.xmit_lock); - return; - } - - PR_XMON("%s: xmit_mon kicked OFF (chanset = 0x%x)\n", - proc, idn.xmit_chanset_wanted); - - idn.xmit_tid = timeout(idn_xmit_monitor, NULL, idn_xmit_monitor_freq); - - mutex_exit(&idn.xmit_lock); -} |
