Print this page
12694 race between write() and shutdown() for unix sockets

@@ -23,11 +23,11 @@
  * Use is subject to license terms.
  */
 /*
  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
  * Copyright (c) 2012 by Delphix. All rights reserved.
- * Copyright (c) 2018, Joyent, Inc.
+ * Copyright 2020 Joyent, Inc.
  */
 
 /*
  * Multithreaded STREAMS Local Transport Provider.
  *

@@ -374,12 +374,10 @@
  */
 
 /*
  * Local declarations
  */
-#define NEXTSTATE(EV, ST)       ti_statetbl[EV][ST]
-
 #define BADSEQNUM       (-1)    /* initial seq number used by T_DISCON_IND */
 #define TL_BUFWAIT      (10000) /* usecs to wait for allocb buffer timeout */
 #define TL_TIDUSZ (64*1024)     /* tidu size when "strmsgz" is unlimited (0) */
 /*
  * Hash tables size.

@@ -415,19 +413,10 @@
  * LOCAL MACROS
  */
 #define T_ALIGN(p)      P2ROUNDUP((p), sizeof (t_scalar_t))
 
 /*
- * EXTERNAL VARIABLE DECLARATIONS
- * -----------------------------
- */
-/*
- * state table defined in the OS space.c
- */
-extern  char    ti_statetbl[TE_NOEVENTS][TS_NOSTATES];
-
-/*
  * STREAMS DRIVER ENTRY POINTS PROTOTYPES
  */
 static int tl_open(queue_t *, dev_t *, int, int, cred_t *);
 static int tl_close(queue_t *, int, cred_t *);
 static int tl_wput(queue_t *, mblk_t *);

@@ -821,10 +810,86 @@
 static int tl_disable_early_connect = 0;
 static int tl_client_closing_when_accepting;
 
 static int tl_serializer_noswitch;
 
+#define nr      127             /* not reachable */
+
+#define TE_NOEVENTS     28
+
+static char nextstate[TE_NOEVENTS][TS_NOSTATES] = {
+                                /* STATES */
+        /* 0  1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16 */
+
+/* Initialization events */
+
+#define TE_BIND_REQ     0       /* bind request                         */
+        { 1, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr},
+#define TE_UNBIND_REQ   1       /* unbind request                       */
+        {nr, nr, nr,  2, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr},
+#define TE_OPTMGMT_REQ  2       /* manage options req                   */
+        {nr, nr, nr,  4, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr},
+#define TE_BIND_ACK     3       /* bind acknowledment                   */
+        {nr,  3, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr},
+#define TE_OPTMGMT_ACK  4       /* manage options ack                   */
+        {nr, nr, nr, nr,  3, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr},
+#define TE_ERROR_ACK    5       /* error acknowledgment                 */
+        {nr,  0,  3, nr,  3,  3, nr, nr,  7, nr, nr, nr,  6,  7,  9, 10, 11},
+#define TE_OK_ACK1      6       /* ok ack  seqcnt == 0                  */
+        {nr, nr,  0, nr, nr,  6, nr, nr, nr, nr, nr, nr,  3, nr,  3,  3,  3},
+#define TE_OK_ACK2      7       /* ok ack  seqcnt == 1, q == resq       */
+        {nr, nr, nr, nr, nr, nr, nr, nr,  9, nr, nr, nr, nr,  3, nr, nr, nr},
+#define TE_OK_ACK3      8       /* ok ack  seqcnt == 1, q != resq       */
+        {nr, nr, nr, nr, nr, nr, nr, nr,  3, nr, nr, nr, nr,  3, nr, nr, nr},
+#define TE_OK_ACK4      9       /* ok ack  seqcnt > 1                   */
+        {nr, nr, nr, nr, nr, nr, nr, nr,  7, nr, nr, nr, nr,  7, nr, nr, nr},
+
+/* Connection oriented events */
+#define TE_CONN_REQ     10      /* connection request                   */
+        {nr, nr, nr,  5, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr},
+#define TE_CONN_RES     11      /* connection response                  */
+        {nr, nr, nr, nr, nr, nr, nr,  8, nr, nr, nr, nr, nr, nr, nr, nr, nr},
+#define TE_DISCON_REQ   12      /* disconnect request                   */
+        {nr, nr, nr, nr, nr, nr, 12, 13, nr, 14, 15, 16, nr, nr, nr, nr, nr},
+#define TE_DATA_REQ     13      /* data request                         */
+        {nr, nr, nr, nr, nr, nr, nr, nr, nr,  9, nr, 11, nr, nr, nr, nr, nr},
+#define TE_EXDATA_REQ   14      /* expedited data request               */
+        {nr, nr, nr, nr, nr, nr, nr, nr, nr,  9, nr, 11, nr, nr, nr, nr, nr},
+#define TE_ORDREL_REQ   15      /* orderly release req                  */
+        {nr, nr, nr, nr, nr, nr, nr, nr, nr, 10, nr,  3, nr, nr, nr, nr, nr},
+#define TE_CONN_IND     16      /* connection indication                */
+        {nr, nr, nr,  7, nr, nr, nr,  7, nr, nr, nr, nr, nr, nr, nr, nr, nr},
+#define TE_CONN_CON     17      /* connection confirmation              */
+        {nr, nr, nr, nr, nr, nr,  9, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr},
+#define TE_DATA_IND     18      /* data indication                      */
+        {nr, nr, nr, nr, nr, nr, nr, nr, nr,  9, 10, nr, nr, nr, nr, nr, nr},
+#define TE_EXDATA_IND   19      /* expedited data indication            */
+        {nr, nr, nr, nr, nr, nr, nr, nr, nr,  9, 10, nr, nr, nr, nr, nr, nr},
+#define TE_ORDREL_IND   20      /* orderly release ind                  */
+        {nr, nr, nr, nr, nr, nr, nr, nr, nr, 11,  3, nr, nr, nr, nr, nr, nr},
+#define TE_DISCON_IND1  21      /* disconnect indication seq == 0       */
+        {nr, nr, nr, nr, nr, nr,  3, nr, nr,  3,  3,  3, nr, nr, nr, nr, nr},
+#define TE_DISCON_IND2  22      /* disconnect indication seq == 1       */
+        {nr, nr, nr, nr, nr, nr, nr,  3, nr, nr, nr, nr, nr, nr, nr, nr, nr},
+#define TE_DISCON_IND3  23      /* disconnect indication seq > 1        */
+        {nr, nr, nr, nr, nr, nr, nr,  7, nr, nr, nr, nr, nr, nr, nr, nr, nr},
+#define TE_PASS_CONN    24      /* pass connection                      */
+        {nr, nr, nr,  9, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr},
+
+
+/* Unit data events */
+
+#define TE_UNITDATA_REQ 25      /* unitdata request                     */
+        {nr, nr, nr,  3, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr},
+#define TE_UNITDATA_IND 26      /* unitdata indication                  */
+        {nr, nr, nr,  3, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr},
+#define TE_UDERROR_IND  27      /* unitdata error indication            */
+        {nr, nr, nr,  3, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr, nr},
+};
+
+
+
 /*
  * LOCAL FUNCTION PROTOTYPES
  * -------------------------
  */
 static boolean_t tl_eqaddr(tl_addr_t *, tl_addr_t *);

@@ -1953,12 +2018,13 @@
             !peer_tep->te_closing &&
             ((tep->te_state == TS_DATA_XFER) ||
             (tep->te_state == TS_WREQ_ORDREL)) &&
             (tep->te_wq != NULL) &&
             (tep->te_wq->q_first == NULL) &&
-            ((peer_tep->te_state == TS_DATA_XFER) ||
-            (peer_tep->te_state == TS_WREQ_ORDREL))     &&
+            (peer_tep->te_state == TS_DATA_XFER ||
+            peer_tep->te_state == TS_WIND_ORDREL ||
+            peer_tep->te_state == TS_WREQ_ORDREL) &&
             ((peer_rq = peer_tep->te_rq) != NULL) &&
             (canputnext(peer_rq) || tep->te_closing)) {
                 putnext(peer_rq, mp);
         } else if (tep->te_closing) {
                 /*

@@ -2380,11 +2446,11 @@
                 tli_err = TSYSERR;
                 unix_err = EINVAL;
                 goto error;
         }
 
-        tep->te_state = NEXTSTATE(TE_BIND_REQ, tep->te_state);
+        tep->te_state = nextstate[TE_BIND_REQ][tep->te_state];
 
         ASSERT((bind->PRIM_type == O_T_BIND_REQ) ||
             (bind->PRIM_type == T_BIND_REQ));
 
         alen = bind->ADDR_length;

@@ -2420,11 +2486,11 @@
                     (aoff < 0) ||
                     (aoff + alen > msz)) {
                         (void) (STRLOG(TL_ID, tep->te_minor,
                             1, SL_TRACE | SL_ERROR,
                             "tl_bind: invalid socket addr"));
-                        tep->te_state = NEXTSTATE(TE_ERROR_ACK, tep->te_state);
+                        tep->te_state = nextstate[TE_ERROR_ACK][tep->te_state];
                         tli_err = TSYSERR;
                         unix_err = EINVAL;
                         goto error;
                 }
                 /* Copy address from message to local buffer. */

@@ -2435,31 +2501,31 @@
                 if ((ux_addr.soua_magic != SOU_MAGIC_EXPLICIT) &&
                     (ux_addr.soua_magic != SOU_MAGIC_IMPLICIT)) {
                         (void) (STRLOG(TL_ID, tep->te_minor,
                             1, SL_TRACE | SL_ERROR,
                             "tl_bind: invalid socket magic"));
-                        tep->te_state = NEXTSTATE(TE_ERROR_ACK, tep->te_state);
+                        tep->te_state = nextstate[TE_ERROR_ACK][tep->te_state];
                         tli_err = TSYSERR;
                         unix_err = EINVAL;
                         goto error;
                 }
                 if ((ux_addr.soua_magic == SOU_MAGIC_IMPLICIT) &&
                     (ux_addr.soua_vp != NULL)) {
                         (void) (STRLOG(TL_ID, tep->te_minor,
                             1, SL_TRACE | SL_ERROR,
                             "tl_bind: implicit addr non-empty"));
-                        tep->te_state = NEXTSTATE(TE_ERROR_ACK, tep->te_state);
+                        tep->te_state = nextstate[TE_ERROR_ACK][tep->te_state];
                         tli_err = TSYSERR;
                         unix_err = EINVAL;
                         goto error;
                 }
                 if ((ux_addr.soua_magic == SOU_MAGIC_EXPLICIT) &&
                     (ux_addr.soua_vp == NULL)) {
                         (void) (STRLOG(TL_ID, tep->te_minor,
                             1, SL_TRACE | SL_ERROR,
                             "tl_bind: explicit addr empty"));
-                        tep->te_state = NEXTSTATE(TE_ERROR_ACK, tep->te_state);
+                        tep->te_state = nextstate[TE_ERROR_ACK][tep->te_state];
                         tli_err = TSYSERR;
                         unix_err = EINVAL;
                         goto error;
                 }
         } else {

@@ -2467,20 +2533,20 @@
                     ((ssize_t)(aoff + alen) > msz) ||
                     ((aoff + alen) < 0))) {
                         (void) (STRLOG(TL_ID, tep->te_minor,
                             1, SL_TRACE | SL_ERROR,
                             "tl_bind: invalid message"));
-                        tep->te_state = NEXTSTATE(TE_ERROR_ACK, tep->te_state);
+                        tep->te_state = nextstate[TE_ERROR_ACK][tep->te_state];
                         tli_err = TSYSERR;
                         unix_err = EINVAL;
                         goto error;
                 }
                 if ((alen < 0) || (alen > (msz - sizeof (struct T_bind_req)))) {
                         (void) (STRLOG(TL_ID, tep->te_minor,
                             1, SL_TRACE | SL_ERROR,
                             "tl_bind: bad addr in  message"));
-                        tep->te_state = NEXTSTATE(TE_ERROR_ACK, tep->te_state);
+                        tep->te_state = nextstate[TE_ERROR_ACK][tep->te_state];
                         tli_err = TBADADDR;
                         goto error;
                 }
 #ifdef DEBUG
                 /*

@@ -2642,11 +2708,11 @@
                 tep->te_qlen = qlen;
                 if (qlen > 0)
                         tep->te_flag |= TL_LISTENER;
         }
 
-        tep->te_state = NEXTSTATE(TE_BIND_ACK, tep->te_state);
+        tep->te_state = nextstate[TE_BIND_ACK][tep->te_state];
         /*
          * send T_BIND_ACK message
          */
         (void) qreply(wq, bamp);
         return;

@@ -2659,11 +2725,11 @@
                  */
                 tep->te_state = save_state;
                 tl_memrecover(wq, mp, sizeof (struct T_error_ack));
                 return;
         }
-        tep->te_state = NEXTSTATE(TE_ERROR_ACK, tep->te_state);
+        tep->te_state = nextstate[TE_ERROR_ACK][tep->te_state];
         tl_error_ack(wq, ackmp, tli_err, unix_err, save_prim_type);
 }
 
 /*
  * Process T_UNBIND_REQ.

@@ -2705,11 +2771,11 @@
                     "tl_wput:T_UNBIND_REQ:out of state, state=%d",
                     tep->te_state));
                 tl_error_ack(wq, ackmp, TOUTSTATE, 0, T_UNBIND_REQ);
                 return;
         }
-        tep->te_state = NEXTSTATE(TE_UNBIND_REQ, tep->te_state);
+        tep->te_state = nextstate[TE_UNBIND_REQ][tep->te_state];
 
         /*
          * TPI says on T_UNBIND_REQ:
          *    send up a M_FLUSH to flush both
          *    read and write queues

@@ -2725,11 +2791,11 @@
                  * We allow rebind with a new qlen value.
                  */
                 tl_addr_unbind(tep);
         }
 
-        tep->te_state = NEXTSTATE(TE_OK_ACK1, tep->te_state);
+        tep->te_state = nextstate[TE_OK_ACK1][tep->te_state];
         /*
          * send  T_OK_ACK
          */
         tl_ok_ack(wq, ackmp, T_UNBIND_REQ);
 }

@@ -2963,11 +3029,11 @@
                 tl_error_ack(wq, ackmp, TOUTSTATE, 0, T_CONN_REQ);
                 freemsg(mp);
                 return;
         }
 
-        tep->te_state = NEXTSTATE(TE_CONN_REQ, tep->te_state);
+        tep->te_state = nextstate[TE_CONN_REQ][tep->te_state];
         /*
          * get endpoint to connect to
          * check that peer with DEST addr is bound to addr
          * and has CONIND_number > 0
          */

@@ -3001,11 +3067,11 @@
          */
         if (err != 0) {
                 if (peer_tep != NULL)
                         tl_refrele(peer_tep);
                 /* We are still expected to send T_OK_ACK */
-                tep->te_state = NEXTSTATE(TE_OK_ACK1, tep->te_state);
+                tep->te_state = nextstate[TE_OK_ACK1][tep->te_state];
                 tl_ok_ack(tep->te_wq, ackmp, T_CONN_REQ);
                 tl_closeok(tep);
                 dimp = tpi_ack_alloc(mp, sizeof (struct T_discon_ind),
                     M_PROTO, T_DISCON_IND);
                 if (dimp == NULL) {

@@ -3222,11 +3288,11 @@
          */
 
         /*
          * ack validity of request and send the peer credential in the ACK.
          */
-        tep->te_state = NEXTSTATE(TE_OK_ACK1, tep->te_state);
+        tep->te_state = nextstate[TE_OK_ACK1][tep->te_state];
 
         if (peer_tep != NULL && peer_tep->te_credp != NULL &&
             confmp != NULL) {
                 mblk_setcred(confmp, peer_tep->te_credp, peer_tep->te_cpid);
         }

@@ -3296,11 +3362,11 @@
         tip->ti_tep = tep;
         tip->ti_seqno = tep->te_seqno;
         list_insert_tail(&peer_tep->te_iconp, tip);
         peer_tep->te_nicon++;
 
-        peer_tep->te_state = NEXTSTATE(TE_CONN_IND, peer_tep->te_state);
+        peer_tep->te_state = nextstate[TE_CONN_IND][peer_tep->te_state];
         /*
          * send the T_CONN_IND message
          */
         putnext(peer_tep->te_rq, cimp);
 

@@ -3307,11 +3373,11 @@
         /*
          * Send a T_CONN_CON message for sockets.
          * Disable the queues until we have reached the correct state!
          */
         if (confmp != NULL) {
-                tep->te_state = NEXTSTATE(TE_CONN_CON, tep->te_state);
+                tep->te_state = nextstate[TE_CONN_CON][tep->te_state];
                 noenable(wq);
                 putnext(tep->te_rq, confmp);
         }
         /*
          * Now we need to increment tep reference because tep is referenced by

@@ -3427,18 +3493,18 @@
                 tl_error_ack(wq, ackmp, TBADOPT, 0, prim);
                 freemsg(mp);
                 return;
         }
 
-        tep->te_state = NEXTSTATE(TE_CONN_RES, tep->te_state);
+        tep->te_state = nextstate[TE_CONN_RES][tep->te_state];
         ASSERT(tep->te_state == TS_WACK_CRES);
 
         if (cres->SEQ_number < TL_MINOR_START &&
             cres->SEQ_number >= BADSEQNUM) {
                 (void) (STRLOG(TL_ID, tep->te_minor, 2, SL_TRACE | SL_ERROR,
                     "tl_conn_res:remote endpoint sequence number bad"));
-                tep->te_state = NEXTSTATE(TE_ERROR_ACK, tep->te_state);
+                tep->te_state = nextstate[TE_ERROR_ACK][tep->te_state];
                 tl_error_ack(wq, ackmp, TBADSEQ, 0, prim);
                 freemsg(mp);
                 return;
         }
 

@@ -3448,11 +3514,11 @@
         if (mod_hash_find_cb(tep->te_transport->tr_ai_hash,
             (mod_hash_key_t)(uintptr_t)cres->ACCEPTOR_id,
             (mod_hash_val_t *)&acc_ep, tl_find_callback) != 0) {
                 (void) (STRLOG(TL_ID, tep->te_minor, 2, SL_TRACE | SL_ERROR,
                     "tl_conn_res:bad accepting endpoint"));
-                tep->te_state = NEXTSTATE(TE_ERROR_ACK, tep->te_state);
+                tep->te_state = nextstate[TE_ERROR_ACK][tep->te_state];
                 tl_error_ack(wq, ackmp, TBADF, 0, prim);
                 freemsg(mp);
                 return;
         }
 

@@ -3460,11 +3526,11 @@
          * Prevent acceptor from closing.
          */
         if (!tl_noclose(acc_ep)) {
                 (void) (STRLOG(TL_ID, tep->te_minor, 2, SL_TRACE | SL_ERROR,
                     "tl_conn_res:bad accepting endpoint"));
-                tep->te_state = NEXTSTATE(TE_ERROR_ACK, tep->te_state);
+                tep->te_state = nextstate[TE_ERROR_ACK][tep->te_state];
                 tl_error_ack(wq, ackmp, TBADF, 0, prim);
                 tl_refrele(acc_ep);
                 freemsg(mp);
                 return;
         }

@@ -3478,11 +3544,11 @@
          */
         if ((tep != acc_ep) && (acc_ep->te_state != TS_IDLE)) {
                 (void) (STRLOG(TL_ID, tep->te_minor, 2, SL_TRACE | SL_ERROR,
                     "tl_conn_res:accepting endpoint has no address bound,"
                     "state=%d", acc_ep->te_state));
-                tep->te_state = NEXTSTATE(TE_ERROR_ACK, tep->te_state);
+                tep->te_state = nextstate[TE_ERROR_ACK][tep->te_state];
                 tl_error_ack(wq, ackmp, TOUTSTATE, 0, prim);
                 freemsg(mp);
                 tl_closeok(acc_ep);
                 tl_refrele(acc_ep);
                 return;

@@ -3494,11 +3560,11 @@
          */
 
         if ((tep == acc_ep) && (tep->te_nicon > 1)) {
                 (void) (STRLOG(TL_ID, tep->te_minor, 3, SL_TRACE | SL_ERROR,
                     "tl_conn_res: > 1 conn_ind on listener-acceptor"));
-                tep->te_state = NEXTSTATE(TE_ERROR_ACK, tep->te_state);
+                tep->te_state = nextstate[TE_ERROR_ACK][tep->te_state];
                 tl_error_ack(wq, ackmp, TBADF, 0, prim);
                 freemsg(mp);
                 tl_closeok(acc_ep);
                 tl_refrele(acc_ep);
                 return;

@@ -3512,11 +3578,11 @@
          */
         tip = tl_icon_find(tep, cres->SEQ_number);
         if (tip == NULL) {
                 (void) (STRLOG(TL_ID, tep->te_minor, 2, SL_TRACE | SL_ERROR,
                     "tl_conn_res:no client in listener list"));
-                tep->te_state = NEXTSTATE(TE_ERROR_ACK, tep->te_state);
+                tep->te_state = nextstate[TE_ERROR_ACK][tep->te_state];
                 tl_error_ack(wq, ackmp, TBADSEQ, 0, prim);
                 freemsg(mp);
                 tl_closeok(acc_ep);
                 tl_refrele(acc_ep);
                 return;

@@ -3630,15 +3696,15 @@
         /*
          * Now ack validity of request
          */
         if (tep->te_nicon == 1) {
                 if (tep == acc_ep)
-                        tep->te_state = NEXTSTATE(TE_OK_ACK2, tep->te_state);
+                        tep->te_state = nextstate[TE_OK_ACK2][tep->te_state];
                 else
-                        tep->te_state = NEXTSTATE(TE_OK_ACK3, tep->te_state);
+                        tep->te_state = nextstate[TE_OK_ACK3][tep->te_state];
         } else {
-                tep->te_state = NEXTSTATE(TE_OK_ACK4, tep->te_state);
+                tep->te_state = nextstate[TE_OK_ACK4][tep->te_state];
         }
 
         /*
          * send T_DISCON_IND now if client state validation failed earlier
          */

@@ -3687,11 +3753,11 @@
 
         /*
          * now start connecting the accepting endpoint
          */
         if (tep != acc_ep)
-                acc_ep->te_state = NEXTSTATE(TE_PASS_CONN, acc_ep->te_state);
+                acc_ep->te_state = nextstate[TE_PASS_CONN][acc_ep->te_state];
 
         if (cl_ep == NULL) {
                 /*
                  * The client has already closed. Send up any queued messages
                  * and change the state accordingly.

@@ -3848,11 +3914,11 @@
                         freemsg(ccmp);
         } else {
                 /*
                  * change client state on TE_CONN_CON event
                  */
-                cl_ep->te_state = NEXTSTATE(TE_CONN_CON, cl_ep->te_state);
+                cl_ep->te_state = nextstate[TE_CONN_CON][cl_ep->te_state];
                 putnext(cl_ep->te_rq, ccmp);
         }
 
         /* Mark the both endpoints as accepted */
         cl_ep->te_flag |= TL_ACCEPTED;

@@ -3934,17 +4000,17 @@
         }
         /*
          * Defer committing the state change until it is determined if
          * the message will be queued with the tl_icon or not.
          */
-        new_state  = NEXTSTATE(TE_DISCON_REQ, tep->te_state);
+        new_state  = nextstate[TE_DISCON_REQ][tep->te_state];
 
         /* validate the message */
         if (msz < sizeof (struct T_discon_req)) {
                 (void) (STRLOG(TL_ID, tep->te_minor, 1, SL_TRACE | SL_ERROR,
                     "tl_discon_req:invalid message"));
-                tep->te_state = NEXTSTATE(TE_ERROR_ACK, new_state);
+                tep->te_state = nextstate[TE_ERROR_ACK][new_state];
                 tl_error_ack(wq, ackmp, TSYSERR, EINVAL, T_DISCON_REQ);
                 freemsg(mp);
                 return;
         }
 

@@ -3960,11 +4026,11 @@
                 tip = tl_icon_find(tep, dr->SEQ_number);
                 if (tip == NULL) {
                         (void) (STRLOG(TL_ID, tep->te_minor, 2,
                             SL_TRACE | SL_ERROR,
                             "tl_discon_req:no disconnect endpoint"));
-                        tep->te_state = NEXTSTATE(TE_ERROR_ACK, new_state);
+                        tep->te_state = nextstate[TE_ERROR_ACK][new_state];
                         tl_error_ack(wq, ackmp, TBADSEQ, 0, T_DISCON_REQ);
                         freemsg(mp);
                         return;
                 }
                 /*

@@ -3990,16 +4056,16 @@
 
         /*
          * prepare message to ack validity of request
          */
         if (tep->te_nicon == 0) {
-                new_state = NEXTSTATE(TE_OK_ACK1, new_state);
+                new_state = nextstate[TE_OK_ACK1][new_state];
         } else {
                 if (tep->te_nicon == 1)
-                        new_state = NEXTSTATE(TE_OK_ACK2, new_state);
+                        new_state = nextstate[TE_OK_ACK2][new_state];
                 else
-                        new_state = NEXTSTATE(TE_OK_ACK4, new_state);
+                        new_state = nextstate[TE_OK_ACK4][new_state];
         }
 
         /*
          * Flushing queues according to TPI. Using the old state.
          */

@@ -4095,16 +4161,16 @@
                         if (tip != NULL) {
                                 ASSERT(tep == tip->ti_tep);
                                 save_state = peer_tep->te_state;
                                 if (peer_tep->te_nicon == 1)
                                         peer_tep->te_state =
-                                            NEXTSTATE(TE_DISCON_IND2,
-                                            peer_tep->te_state);
+                                            nextstate[TE_DISCON_IND2]
+                                            [peer_tep->te_state];
                                 else
                                         peer_tep->te_state =
-                                            NEXTSTATE(TE_DISCON_IND3,
-                                            peer_tep->te_state);
+                                            nextstate[TE_DISCON_IND3]
+                                            [peer_tep->te_state];
                                 tl_freetip(peer_tep, tip);
                         }
                         ASSERT(tep->te_oconp != NULL);
                         TL_UNCONNECT(tep->te_oconp);
                 }

@@ -4563,11 +4629,11 @@
                     "tl_data:cots:out of state"));
                 tl_merror(wq, mp, EPROTO);
                 return;
         }
         /*
-         * tep->te_state = NEXTSTATE(TE_DATA_REQ, tep->te_state);
+         * tep->te_state = nextstate[TE_DATA_REQ][tep->te_state];
          * (State stays same on this event)
          */
 
         /*
          * get connected endpoint

@@ -4613,11 +4679,11 @@
                         prim->type = T_DATA_IND;
                 else
                         prim->type = T_OPTDATA_IND;
         }
         /*
-         * peer_tep->te_state = NEXTSTATE(TE_DATA_IND, peer_tep->te_state);
+         * peer_tep->te_state = nextstate[TE_DATA_IND][peer_tep->te_state];
          * (peer state stays same on this event)
          */
         /*
          * send data to connected peer
          */

@@ -4730,11 +4796,11 @@
                     tep->te_state));
                 tl_merror(wq, mp, EPROTO);
                 return;
         }
         /*
-         * tep->te_state = NEXTSTATE(TE_EXDATA_REQ, tep->te_state);
+         * tep->te_state = nextstate[TE_EXDATA_REQ][tep->te_state];
          * (state stays same on this event)
          */
 
         /*
          * get connected endpoint

@@ -4772,11 +4838,11 @@
                     "tl_exdata:rx side:invalid state"));
                 tl_merror(peer_tep->te_wq, mp, EPROTO);
                 return;
         }
         /*
-         * peer_tep->te_state = NEXTSTATE(TE_DATA_IND, peer_tep->te_state);
+         * peer_tep->te_state = nextstate[TE_DATA_IND][peer_tep->te_state];
          * (peer state stays same on this event)
          */
         /*
          * reuse message block
          */

@@ -4856,11 +4922,11 @@
                 } else {
                         freemsg(mp);
                 }
                 return;
         }
-        tep->te_state = NEXTSTATE(TE_ORDREL_REQ, tep->te_state);
+        tep->te_state = nextstate[TE_ORDREL_REQ][tep->te_state];
 
         /*
          * get connected endpoint
          */
         if (((peer_tep = tep->te_conp) == NULL) || peer_tep->te_closing) {

@@ -4895,11 +4961,11 @@
                 (void) (STRLOG(TL_ID, tep->te_minor, 1, SL_TRACE | SL_ERROR,
                     "tl_ordrel:rx side:invalid state"));
                 tl_merror(peer_tep->te_wq, mp, EPROTO);
                 return;
         }
-        peer_tep->te_state = NEXTSTATE(TE_ORDREL_IND, peer_tep->te_state);
+        peer_tep->te_state = nextstate[TE_ORDREL_IND][peer_tep->te_state];
 
         /*
          * reuse message block
          */
         prim->type = T_ORDREL_IND;

@@ -4980,11 +5046,11 @@
         freemsg(mp);
 
         /*
          * send indication message
          */
-        tep->te_state = NEXTSTATE(TE_UDERROR_IND, tep->te_state);
+        tep->te_state = nextstate[TE_UDERROR_IND][tep->te_state];
 
         qreply(wq, err_mp);
 }
 
 static void

@@ -5038,11 +5104,11 @@
                     "tl_wput:T_CONN_REQ:out of state"));
                 tl_merror(wq, mp, EPROTO);
                 return;
         }
         /*
-         * tep->te_state = NEXTSTATE(TE_UNITDATA_REQ, tep->te_state);
+         * tep->te_state = nextstate[TE_UNITDATA_REQ][tep->te_state];
          * (state does not change on this event)
          */
 
         /*
          * validate the message

@@ -5337,11 +5403,11 @@
                 mp = ui_mp;
         }
         /*
          * send indication message
          */
-        peer_tep->te_state = NEXTSTATE(TE_UNITDATA_IND, peer_tep->te_state);
+        peer_tep->te_state = nextstate[TE_UNITDATA_IND][peer_tep->te_state];
         putnext(peer_tep->te_rq, mp);
 }
 
 
 

@@ -5659,16 +5725,16 @@
                                 /*
                                  * Delete tip from the server list.
                                  */
                                 if (srv_tep->te_nicon == 1) {
                                         srv_tep->te_state =
-                                            NEXTSTATE(TE_DISCON_IND2,
-                                            srv_tep->te_state);
+                                            nextstate[TE_DISCON_IND2]
+                                            [srv_tep->te_state];
                                 } else {
                                         srv_tep->te_state =
-                                            NEXTSTATE(TE_DISCON_IND3,
-                                            srv_tep->te_state);
+                                            nextstate[TE_DISCON_IND3]
+                                            [srv_tep->te_state];
                                 }
                                 ASSERT(*(uint32_t *)(d_mp->b_rptr) ==
                                     T_DISCON_IND);
                                 putnext(srv_tep->te_rq, d_mp);
                                 tl_freetip(srv_tep, tip);

@@ -5692,11 +5758,11 @@
                          * send ordrel ind
                          */
                         (void) (STRLOG(TL_ID, tep->te_minor, 3, SL_TRACE,
                         "tl_co_unconnect:connected: ordrel_ind state %d->%d",
                             peer_tep->te_state,
-                            NEXTSTATE(TE_ORDREL_IND, peer_tep->te_state)));
+                            nextstate[TE_ORDREL_IND][peer_tep->te_state]));
                         d_mp = tl_ordrel_ind_alloc();
                         if (d_mp == NULL) {
                                 (void) (STRLOG(TL_ID, tep->te_minor, 3,
                                     SL_TRACE | SL_ERROR,
                                     "tl_co_unconnect:connected:"

@@ -5707,11 +5773,11 @@
                                  */
                                 TL_QENABLE(peer_tep);
                                 goto discon_peer;
                         }
                         peer_tep->te_state =
-                            NEXTSTATE(TE_ORDREL_IND, peer_tep->te_state);
+                            nextstate[TE_ORDREL_IND][peer_tep->te_state];
 
                         putnext(peer_tep->te_rq, d_mp);
                         /*
                          * Handle flow control case.  This will generate
                          * a t_discon_ind message with reason 0 if there

@@ -5951,12 +6017,12 @@
                         case T_OPTDATA_IND:
                         case T_EXDATA_IND:
                                 putnext(tep->te_rq, mp);
                                 break;
                         case T_ORDREL_IND:
-                                tep->te_state = NEXTSTATE(TE_ORDREL_IND,
-                                    tep->te_state);
+                                tep->te_state = nextstate[TE_ORDREL_IND]
+                                    [tep->te_state];
                                 putnext(tep->te_rq, mp);
                                 break;
                         case T_DISCON_IND:
                                 tep->te_state = TS_IDLE;
                                 putnext(tep->te_rq, mp);