diff options
Diffstat (limited to 'usr/src/uts/sun4v/io/ldc.c')
-rw-r--r-- | usr/src/uts/sun4v/io/ldc.c | 329 |
1 files changed, 272 insertions, 57 deletions
diff --git a/usr/src/uts/sun4v/io/ldc.c b/usr/src/uts/sun4v/io/ldc.c index 3e526a623c..4b2bd1a092 100644 --- a/usr/src/uts/sun4v/io/ldc.c +++ b/usr/src/uts/sun4v/io/ldc.c @@ -421,6 +421,8 @@ i_ldc_txq_reconf(ldc_chan_t *ldcp) int rv; ASSERT(MUTEX_HELD(&ldcp->lock)); + ASSERT(MUTEX_HELD(&ldcp->tx_lock)); + rv = hv_ldc_tx_qconf(ldcp->id, ldcp->tx_q_ra, ldcp->tx_q_entries); if (rv) { cmn_err(CE_WARN, @@ -513,6 +515,9 @@ i_ldc_reset(ldc_chan_t *ldcp) { D2(ldcp->id, "i_ldc_reset: (0x%llx) channel reset\n", ldcp->id); + ASSERT(MUTEX_HELD(&ldcp->lock)); + ASSERT(MUTEX_HELD(&ldcp->tx_lock)); + (void) i_ldc_txq_reconf(ldcp); (void) i_ldc_rxq_reconf(ldcp); i_ldc_reset_state(ldcp); @@ -558,7 +563,9 @@ i_ldc_set_rx_head(ldc_chan_t *ldcp, uint64_t head) cmn_err(CE_WARN, "ldc_rx_set_qhead: (0x%lx) cannot set qhead 0x%lx", ldcp->id, head); + mutex_enter(&ldcp->tx_lock); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); return (ECONNRESET); } @@ -575,7 +582,7 @@ i_ldc_get_tx_tail(ldc_chan_t *ldcp, uint64_t *tail) int rv; uint64_t current_head, new_tail; - ASSERT(MUTEX_HELD(&ldcp->lock)); + ASSERT(MUTEX_HELD(&ldcp->tx_lock)); /* Read the head and tail ptrs from HV */ rv = hv_ldc_tx_get_state(ldcp->id, &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state); @@ -626,7 +633,7 @@ i_ldc_set_tx_tail(ldc_chan_t *ldcp, uint64_t tail) int rv, retval = EWOULDBLOCK; int retries; - ASSERT(MUTEX_HELD(&ldcp->lock)); + ASSERT(MUTEX_HELD(&ldcp->tx_lock)); for (retries = 0; retries < ldc_max_retries; retries++) { if ((rv = hv_ldc_tx_set_qtail(ldcp->id, tail)) == 0) { @@ -658,7 +665,9 @@ i_ldc_send_pkt(ldc_chan_t *ldcp, uint8_t pkttype, uint8_t subtype, uint64_t tx_tail; uint32_t curr_seqid = ldcp->last_msg_snt; - ASSERT(MUTEX_HELD(&ldcp->lock)); + /* Obtain Tx lock */ + mutex_enter(&ldcp->tx_lock); + /* get the current tail for the message */ rv = i_ldc_get_tx_tail(ldcp, &tx_tail); if (rv) { @@ -666,6 +675,7 @@ i_ldc_send_pkt(ldc_chan_t *ldcp, uint8_t pkttype, uint8_t subtype, "i_ldc_send_pkt: (0x%llx) error sending pkt, " "type=0x%x,subtype=0x%x,ctrl=0x%x\n", ldcp->id, pkttype, subtype, ctrlmsg); + mutex_exit(&ldcp->tx_lock); return (rv); } @@ -698,12 +708,14 @@ i_ldc_send_pkt(ldc_chan_t *ldcp, uint8_t pkttype, uint8_t subtype, "i_ldc_send_pkt:(0x%llx) error sending pkt, " "type=0x%x,stype=0x%x,ctrl=0x%x\n", ldcp->id, pkttype, subtype, ctrlmsg); + mutex_exit(&ldcp->tx_lock); return (EIO); } ldcp->last_msg_snt = curr_seqid; ldcp->tx_tail = tx_tail; + mutex_exit(&ldcp->tx_lock); return (0); } @@ -755,6 +767,9 @@ i_ldc_process_VER(ldc_chan_t *ldcp, ldc_msg_t *msg) D2(ldcp->id, "i_ldc_process_VER: (0x%llx) received VER v%u.%u\n", ldcp->id, rcvd_ver->major, rcvd_ver->minor); + /* Obtain Tx lock */ + mutex_enter(&ldcp->tx_lock); + switch (msg->stype) { case LDC_INFO: @@ -765,6 +780,7 @@ i_ldc_process_VER(ldc_chan_t *ldcp, ldc_msg_t *msg) "i_ldc_process_VER: (0x%llx) err sending " "version ACK/NACK\n", ldcp->id); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); return (ECONNRESET); } @@ -850,6 +866,7 @@ i_ldc_process_VER(ldc_chan_t *ldcp, ldc_msg_t *msg) "i_ldc_process_VER: (0x%llx) error sending " "ACK/NACK\n", ldcp->id); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); return (ECONNRESET); } @@ -871,6 +888,7 @@ i_ldc_process_VER(ldc_chan_t *ldcp, ldc_msg_t *msg) "i_ldc_process_VER: (0x%llx) cannot send RTS\n", ldcp->id); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); return (ECONNRESET); } @@ -898,6 +916,7 @@ i_ldc_process_VER(ldc_chan_t *ldcp, ldc_msg_t *msg) "i_ldc_process_VER: (0x%llx) no listener\n", ldcp->id); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); return (ECONNRESET); } @@ -914,6 +933,7 @@ i_ldc_process_VER(ldc_chan_t *ldcp, ldc_msg_t *msg) "i_ldc_process_VER: (0x%llx) no version match\n", ldcp->id); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); return (ECONNRESET); } @@ -924,6 +944,7 @@ i_ldc_process_VER(ldc_chan_t *ldcp, ldc_msg_t *msg) "i_ldc_process_VER: (0x%lx) err sending " "version ACK/NACK\n", ldcp->id); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); return (ECONNRESET); } @@ -973,6 +994,7 @@ i_ldc_process_VER(ldc_chan_t *ldcp, ldc_msg_t *msg) if (idx == LDC_NUM_VERS) { /* no version match - terminate */ ldcp->next_vidx = 0; + mutex_exit(&ldcp->tx_lock); return (ECONNRESET); } } @@ -992,12 +1014,14 @@ i_ldc_process_VER(ldc_chan_t *ldcp, ldc_msg_t *msg) "i_ldc_process_VER: (0x%lx) error sending version" "INFO\n", ldcp->id); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); return (ECONNRESET); } break; } + mutex_exit(&ldcp->tx_lock); return (rv); } @@ -1022,7 +1046,9 @@ i_ldc_process_RTS(ldc_chan_t *ldcp, ldc_msg_t *msg) ldcp->id); /* Reset the channel -- as we cannot continue */ + mutex_enter(&ldcp->tx_lock); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); rv = ECONNRESET; break; @@ -1040,7 +1066,9 @@ i_ldc_process_RTS(ldc_chan_t *ldcp, ldc_msg_t *msg) rv = i_ldc_send_pkt(ldcp, LDC_CTRL, LDC_NACK, LDC_RTS); if (rv) { /* if cannot send NACK - reset channel */ + mutex_enter(&ldcp->tx_lock); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); rv = ECONNRESET; break; } @@ -1050,7 +1078,9 @@ i_ldc_process_RTS(ldc_chan_t *ldcp, ldc_msg_t *msg) default: DWARN(ldcp->id, "i_ldc_process_RTS: (0x%llx) unexp ACK\n", ldcp->id); + mutex_enter(&ldcp->tx_lock); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); rv = ECONNRESET; break; } @@ -1070,6 +1100,9 @@ i_ldc_process_RTS(ldc_chan_t *ldcp, ldc_msg_t *msg) /* store initial SEQID info */ ldcp->last_msg_snt = msg->seqid; + /* Obtain Tx lock */ + mutex_enter(&ldcp->tx_lock); + /* get the current tail for the response */ rv = i_ldc_get_tx_tail(ldcp, &tx_tail); if (rv != 0) { @@ -1077,6 +1110,7 @@ i_ldc_process_RTS(ldc_chan_t *ldcp, ldc_msg_t *msg) "i_ldc_process_RTS: (0x%lx) err sending RTR\n", ldcp->id); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); return (ECONNRESET); } @@ -1111,9 +1145,11 @@ i_ldc_process_RTS(ldc_chan_t *ldcp, ldc_msg_t *msg) "i_ldc_process_RTS: (0x%lx) error sending RTR\n", ldcp->id); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); return (ECONNRESET); } + mutex_exit(&ldcp->tx_lock); return (0); } @@ -1136,7 +1172,9 @@ i_ldc_process_RTR(ldc_chan_t *ldcp, ldc_msg_t *msg) ldcp->id); /* Reset the channel -- as we cannot continue */ + mutex_enter(&ldcp->tx_lock); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); rv = ECONNRESET; break; @@ -1155,7 +1193,9 @@ i_ldc_process_RTR(ldc_chan_t *ldcp, ldc_msg_t *msg) rv = i_ldc_send_pkt(ldcp, LDC_CTRL, LDC_NACK, LDC_RTR); if (rv) { /* if cannot send NACK - reset channel */ + mutex_enter(&ldcp->tx_lock); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); rv = ECONNRESET; break; } @@ -1168,7 +1208,9 @@ i_ldc_process_RTR(ldc_chan_t *ldcp, ldc_msg_t *msg) ldcp->id); /* Reset the channel -- as we cannot continue */ + mutex_enter(&ldcp->tx_lock); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); rv = ECONNRESET; break; } @@ -1190,7 +1232,9 @@ i_ldc_process_RTR(ldc_chan_t *ldcp, ldc_msg_t *msg) cmn_err(CE_NOTE, "i_ldc_process_RTR: (0x%lx) cannot send RDX\n", ldcp->id); + mutex_enter(&ldcp->tx_lock); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); return (ECONNRESET); } D2(ldcp->id, @@ -1224,7 +1268,9 @@ i_ldc_process_RDX(ldc_chan_t *ldcp, ldc_msg_t *msg) ldcp->id); /* Reset the channel -- as we cannot continue */ + mutex_enter(&ldcp->tx_lock); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); rv = ECONNRESET; break; @@ -1239,7 +1285,9 @@ i_ldc_process_RDX(ldc_chan_t *ldcp, ldc_msg_t *msg) DWARN(DBG_ALL_LDCS, "i_ldc_process_RDX: (0x%llx) unexpected RDX" " - LDC reset\n", ldcp->id); + mutex_enter(&ldcp->tx_lock); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); return (ECONNRESET); } @@ -1255,7 +1303,9 @@ i_ldc_process_RDX(ldc_chan_t *ldcp, ldc_msg_t *msg) ldcp->id); /* Reset the channel -- as we cannot continue */ + mutex_enter(&ldcp->tx_lock); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); rv = ECONNRESET; break; } @@ -1273,8 +1323,11 @@ i_ldc_process_data_ACK(ldc_chan_t *ldcp, ldc_msg_t *msg) uint64_t tx_head; ldc_msg_t *pkt; + /* Obtain Tx lock */ + mutex_enter(&ldcp->tx_lock); + /* - * Read the curret Tx head and tail + * Read the current Tx head and tail */ rv = hv_ldc_tx_get_state(ldcp->id, &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state); @@ -1282,7 +1335,11 @@ i_ldc_process_data_ACK(ldc_chan_t *ldcp, ldc_msg_t *msg) cmn_err(CE_WARN, "i_ldc_process_data_ACK: (0x%lx) cannot read qptrs\n", ldcp->id); - return (0); + + /* Reset the channel -- as we cannot continue */ + i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); + return (ECONNRESET); } /* @@ -1310,10 +1367,15 @@ i_ldc_process_data_ACK(ldc_chan_t *ldcp, ldc_msg_t *msg) DWARN(ldcp->id, "i_ldc_process_data_ACK: (0x%llx) invalid ACKid\n", ldcp->id); - break; + + /* Reset the channel -- as we cannot continue */ + i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); + return (ECONNRESET); } } + mutex_exit(&ldcp->tx_lock); return (0); } @@ -1353,8 +1415,10 @@ i_ldc_ctrlmsg(ldc_chan_t *ldcp, ldc_msg_t *msg) switch (msg->ctrl & LDC_CTRL_MASK) { case LDC_VER: /* peer is redoing version negotiation */ + mutex_enter(&ldcp->tx_lock); (void) i_ldc_txq_reconf(ldcp); i_ldc_reset_state(ldcp); + mutex_exit(&ldcp->tx_lock); rv = EAGAIN; break; case LDC_RTS: @@ -1387,8 +1451,10 @@ i_ldc_ctrlmsg(ldc_chan_t *ldcp, ldc_msg_t *msg) "i_ldc_ctrlmsg: (0x%llx) unexpected VER " "- LDC reset\n", ldcp->id); /* peer is redoing version negotiation */ + mutex_enter(&ldcp->tx_lock); (void) i_ldc_txq_reconf(ldcp); i_ldc_reset_state(ldcp); + mutex_exit(&ldcp->tx_lock); rv = EAGAIN; break; @@ -1472,20 +1538,28 @@ i_ldc_unregister_channel(ldc_chan_t *ldcp) if (ldcp->tstate & TS_CNEX_RDY) { + /* Remove the Rx interrupt */ rv = cinfo->rem_intr(cinfo->dip, ldcp->id, CNEX_RX_INTR); if (rv) { DWARN(ldcp->id, "i_ldc_unregister_channel: err removing Rx intr\n"); + return (rv); } + + /* Remove the Tx interrupt */ rv = cinfo->rem_intr(cinfo->dip, ldcp->id, CNEX_TX_INTR); if (rv) { DWARN(ldcp->id, "i_ldc_unregister_channel: err removing Tx intr\n"); + return (rv); } + + /* Unregister the channel */ rv = cinfo->unreg_chan(ldcssp->cinfo.dip, ldcp->id); if (rv) { DWARN(ldcp->id, "i_ldc_unregister_channel: cannot unreg channel\n"); + return (rv); } ldcp->tstate &= ~TS_CNEX_RDY; @@ -1520,12 +1594,16 @@ i_ldc_tx_hdlr(caddr_t arg1, caddr_t arg2) /* Lock channel */ mutex_enter(&ldcp->lock); + /* Obtain Tx lock */ + mutex_enter(&ldcp->tx_lock); + rv = hv_ldc_tx_get_state(ldcp->id, &ldcp->tx_head, &ldcp->tx_tail, &ldcp->link_state); if (rv) { cmn_err(CE_WARN, "i_ldc_tx_hdlr: (0x%lx) cannot read queue ptrs rv=0x%d\n", ldcp->id, rv); + mutex_exit(&ldcp->tx_lock); mutex_exit(&ldcp->lock); return (DDI_INTR_CLAIMED); } @@ -1565,6 +1643,7 @@ i_ldc_tx_hdlr(caddr_t arg1, caddr_t arg2) ldcp->cb_inprogress = B_TRUE; /* Unlock channel */ + mutex_exit(&ldcp->tx_lock); mutex_exit(&ldcp->lock); if (notify_client) { @@ -1603,6 +1682,7 @@ i_ldc_rx_hdlr(caddr_t arg1, caddr_t arg2) ldc_chan_t *ldcp; boolean_t notify_client = B_FALSE; uint64_t notify_event = 0; + uint64_t first_fragment = 0; /* Get the channel for which interrupt was received */ if (arg1 == NULL) { @@ -1645,7 +1725,9 @@ i_ldc_rx_hdlr(caddr_t arg1, caddr_t arg2) if (ldcp->link_state == LDC_CHANNEL_DOWN) { D1(ldcp->id, "i_ldc_rx_hdlr: channel link down\n", ldcp->id); + mutex_enter(&ldcp->tx_lock); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); notify_client = B_TRUE; notify_event = LDC_EVT_DOWN; break; @@ -1653,7 +1735,9 @@ i_ldc_rx_hdlr(caddr_t arg1, caddr_t arg2) if (ldcp->link_state == LDC_CHANNEL_RESET) { D1(ldcp->id, "i_ldc_rx_hdlr: channel link reset\n", ldcp->id); + mutex_enter(&ldcp->tx_lock); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); notify_client = B_TRUE; notify_event = LDC_EVT_RESET; } @@ -1715,11 +1799,11 @@ i_ldc_rx_hdlr(caddr_t arg1, caddr_t arg2) "q_ptrs=0x%lx,0x%lx", ldcp->id, rx_head, rx_tail); /* Reset last_msg_rcd to start of message */ - if (ldcp->first_fragment != 0) { - ldcp->last_msg_rcd = - ldcp->first_fragment - 1; - ldcp->first_fragment = 0; + if (first_fragment != 0) { + ldcp->last_msg_rcd = first_fragment - 1; + first_fragment = 0; } + /* * Send a NACK due to seqid mismatch */ @@ -1730,6 +1814,13 @@ i_ldc_rx_hdlr(caddr_t arg1, caddr_t arg2) cmn_err(CE_NOTE, "i_ldc_rx_hdlr: (0x%lx) err sending " "CTRL/NACK msg\n", ldcp->id); + + /* if cannot send NACK - reset channel */ + mutex_enter(&ldcp->tx_lock); + i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); + rv = ECONNRESET; + break; } /* purge receive queue */ @@ -1769,7 +1860,11 @@ i_ldc_rx_hdlr(caddr_t arg1, caddr_t arg2) /* process data ACKs */ if ((msg->type & LDC_DATA) && (msg->stype & LDC_ACK)) { - (void) i_ldc_process_data_ACK(ldcp, msg); + if (rv = i_ldc_process_data_ACK(ldcp, msg)) { + notify_client = B_TRUE; + notify_event = LDC_EVT_RESET; + break; + } } /* move the head one position */ @@ -1878,11 +1973,24 @@ ldc_init(uint64_t id, ldc_attr_t *attr, ldc_handle_t *handle) /* Allocate an ldcp structure */ ldcp = kmem_zalloc(sizeof (ldc_chan_t), KM_SLEEP); - /* Initialize the channel lock */ + /* + * Initialize the channel and Tx lock + * + * The channel 'lock' protects the entire channel and + * should be acquired before initializing, resetting, + * destroying or reading from a channel. + * + * The 'tx_lock' should be acquired prior to transmitting + * data over the channel. The lock should also be acquired + * prior to channel reconfiguration (in order to prevent + * concurrent writes). + * + * ORDERING: When both locks are being acquired, to prevent + * deadlocks, the channel lock should be always acquired prior + * to the tx_lock. + */ mutex_init(&ldcp->lock, NULL, MUTEX_DRIVER, NULL); - - /* Channel specific processing */ - mutex_enter(&ldcp->lock); + mutex_init(&ldcp->tx_lock, NULL, MUTEX_DRIVER, NULL); /* Initialize the channel */ ldcp->id = id; @@ -1996,8 +2104,6 @@ ldc_init(uint64_t id, ldc_attr_t *attr, ldc_handle_t *handle) /* mark status as INITialized */ ldcp->status = LDC_INIT; - mutex_exit(&ldcp->lock); - /* Add to channel list */ mutex_enter(&ldcssp->lock); ldcp->next = ldcssp->chan_list; @@ -2025,7 +2131,7 @@ cleanup_on_exit: contig_mem_free((caddr_t)ldcp->rx_q_va, (ldcp->rx_q_entries << LDC_PACKET_SHIFT)); - mutex_exit(&ldcp->lock); + mutex_destroy(&ldcp->tx_lock); mutex_destroy(&ldcp->lock); if (ldcp) @@ -2121,6 +2227,7 @@ ldc_fini(ldc_handle_t handle) mutex_exit(&ldcp->lock); /* Destroy mutex */ + mutex_destroy(&ldcp->tx_lock); mutex_destroy(&ldcp->lock); /* free channel structure */ @@ -2289,7 +2396,7 @@ int ldc_close(ldc_handle_t handle) { ldc_chan_t *ldcp; - int rv = 0; + int rv = 0, retries = 0; boolean_t chk_done = B_FALSE; if (handle == NULL) { @@ -2331,6 +2438,9 @@ ldc_close(ldc_handle_t handle) return (EBUSY); } + /* Obtain Tx lock */ + mutex_enter(&ldcp->tx_lock); + /* * Wait for pending transmits to complete i.e Tx queue to drain * if there are pending pkts - wait 1 ms and retry again @@ -2342,6 +2452,7 @@ ldc_close(ldc_handle_t handle) if (rv) { cmn_err(CE_WARN, "ldc_close: (0x%lx) cannot read qptrs\n", ldcp->id); + mutex_exit(&ldcp->tx_lock); mutex_exit(&ldcp->lock); return (EIO); } @@ -2366,13 +2477,27 @@ ldc_close(ldc_handle_t handle) /* * Unregister the channel with the nexus */ - rv = i_ldc_unregister_channel(ldcp); - if (rv && rv != EAGAIN) { - cmn_err(CE_WARN, - "ldc_close: (0x%lx) channel unregister failed\n", - ldcp->id); + while ((rv = i_ldc_unregister_channel(ldcp)) != 0) { + + mutex_exit(&ldcp->tx_lock); mutex_exit(&ldcp->lock); - return (rv); + + /* if any error other than EAGAIN return back */ + if (rv != EAGAIN || retries >= LDC_MAX_RETRIES) { + cmn_err(CE_WARN, + "ldc_close: (0x%lx) unregister failed, %d\n", + ldcp->id, rv); + return (rv); + } + + /* + * As there could be pending interrupts we need + * to wait and try again + */ + drv_usecwait(LDC_DELAY); + mutex_enter(&ldcp->lock); + mutex_enter(&ldcp->tx_lock); + retries++; } /* @@ -2383,6 +2508,7 @@ ldc_close(ldc_handle_t handle) cmn_err(CE_WARN, "ldc_close: (0x%lx) channel TX queue unconf failed\n", ldcp->id); + mutex_exit(&ldcp->tx_lock); mutex_exit(&ldcp->lock); return (EIO); } @@ -2391,6 +2517,7 @@ ldc_close(ldc_handle_t handle) cmn_err(CE_WARN, "ldc_close: (0x%lx) channel RX queue unconf failed\n", ldcp->id); + mutex_exit(&ldcp->tx_lock); mutex_exit(&ldcp->lock); return (EIO); } @@ -2406,6 +2533,7 @@ ldc_close(ldc_handle_t handle) ldcp->tstate = TS_INIT; ldcp->status = LDC_INIT; + mutex_exit(&ldcp->tx_lock); mutex_exit(&ldcp->lock); /* Decrement number of open channels */ @@ -2557,11 +2685,14 @@ ldc_up(ldc_handle_t handle) return (0); } + mutex_enter(&ldcp->tx_lock); + /* get the current tail for the LDC msg */ rv = i_ldc_get_tx_tail(ldcp, &tx_tail); if (rv) { DWARN(ldcp->id, "ldc_up: (0x%llx) cannot initiate handshake\n", ldcp->id); + mutex_exit(&ldcp->tx_lock); mutex_exit(&ldcp->lock); return (ECONNREFUSED); } @@ -2586,6 +2717,7 @@ ldc_up(ldc_handle_t handle) DWARN(ldcp->id, "ldc_up: (0x%llx) cannot initiate handshake rv=%d\n", ldcp->id, rv); + mutex_exit(&ldcp->tx_lock); mutex_exit(&ldcp->lock); return (rv); } @@ -2594,6 +2726,7 @@ ldc_up(ldc_handle_t handle) ldcp->tx_tail = tx_tail; D1(ldcp->id, "ldc_up: (0x%llx) channel up initiated\n", ldcp->id); + mutex_exit(&ldcp->tx_lock); mutex_exit(&ldcp->lock); return (rv); @@ -2615,7 +2748,9 @@ ldc_reset(ldc_handle_t handle) ldcp = (ldc_chan_t *)handle; mutex_enter(&ldcp->lock); + mutex_enter(&ldcp->tx_lock); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); mutex_exit(&ldcp->lock); return (0); @@ -2736,7 +2871,9 @@ ldc_chkq(ldc_handle_t handle, boolean_t *isempty) /* reset the channel state if the channel went down */ if (ldcp->link_state == LDC_CHANNEL_DOWN || ldcp->link_state == LDC_CHANNEL_RESET) { + mutex_enter(&ldcp->tx_lock); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); mutex_exit(&ldcp->lock); return (ECONNRESET); } @@ -2839,7 +2976,9 @@ i_ldc_read_raw(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep) /* reset the channel state if the channel went down */ if (ldcp->link_state == LDC_CHANNEL_DOWN) { + mutex_enter(&ldcp->tx_lock); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); return (ECONNRESET); } @@ -2886,14 +3025,12 @@ i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep) size_t len = 0, bytes_read = 0; int retries = 0; uint64_t q_size_mask; + uint64_t first_fragment = 0; target = target_bufp; ASSERT(mutex_owned(&ldcp->lock)); - /* reset first frag to 0 */ - ldcp->first_fragment = 0; - /* compute mask for increment */ q_size_mask = (ldcp->rx_q_entries-1)<<LDC_PACKET_SHIFT; @@ -2913,7 +3050,9 @@ i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep) /* reset the channel state if the channel went down */ if (ldcp->link_state == LDC_CHANNEL_DOWN) { + mutex_enter(&ldcp->tx_lock); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); return (ECONNRESET); } @@ -2930,7 +3069,9 @@ i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep) } /* reset the channel state if the channel went down */ if (ldcp->link_state == LDC_CHANNEL_DOWN) { + mutex_enter(&ldcp->tx_lock); i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); return (ECONNRESET); } } @@ -2938,7 +3079,7 @@ i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep) if (curr_head == rx_tail) { /* If in the middle of a fragmented xfer */ - if (ldcp->first_fragment != 0) { + if (first_fragment != 0) { /* wait for ldc_delay usecs */ drv_usecwait(ldc_delay); @@ -2947,7 +3088,7 @@ i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep) continue; *sizep = 0; - ldcp->last_msg_rcd = ldcp->first_fragment - 1; + ldcp->last_msg_rcd = first_fragment - 1; DWARN(DBG_ALL_LDCS, "ldc_read: (0x%llx) read timeout", ldcp->id); @@ -2978,10 +3119,9 @@ i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep) bytes_read = 0; /* Reset last_msg_rcd to start of message */ - if (ldcp->first_fragment != 0) { - ldcp->last_msg_rcd = - ldcp->first_fragment - 1; - ldcp->first_fragment = 0; + if (first_fragment != 0) { + ldcp->last_msg_rcd = first_fragment - 1; + first_fragment = 0; } /* * Send a NACK -- invalid seqid @@ -2993,6 +3133,13 @@ i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep) cmn_err(CE_NOTE, "ldc_read: (0x%lx) err sending " "NACK msg\n", ldcp->id); + + /* if cannot send NACK - reset channel */ + mutex_enter(&ldcp->tx_lock); + i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); + rv = ECONNRESET; + break; } /* purge receive queue */ @@ -3021,7 +3168,11 @@ i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep) /* process data ACKs */ if ((msg->type & LDC_DATA) && (msg->stype & LDC_ACK)) { - (void) i_ldc_process_data_ACK(ldcp, msg); + if (rv = i_ldc_process_data_ACK(ldcp, msg)) { + *sizep = 0; + bytes_read = 0; + break; + } } /* process data messages */ @@ -3047,7 +3198,7 @@ i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep) * currently expensive. */ - if (ldcp->first_fragment == 0) { + if (first_fragment == 0) { /* * first packets should always have the start @@ -3074,7 +3225,7 @@ i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep) continue; } - ldcp->first_fragment = msg->seqid; + first_fragment = msg->seqid; } else { /* check to see if this is a pkt w/ START bit */ if (msg->env & LDC_FRAG_START) { @@ -3089,7 +3240,7 @@ i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep) /* throw data we have read so far */ bytes_read = 0; target = target_bufp; - ldcp->first_fragment = msg->seqid; + first_fragment = msg->seqid; if (rv = i_ldc_set_rx_head(ldcp, curr_head)) @@ -3113,7 +3264,7 @@ i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep) "head=0x%lx, expect=%d, got=%d\n", ldcp->id, curr_head, *sizep, bytes_read+len); - ldcp->first_fragment = 0; + first_fragment = 0; target = target_bufp; bytes_read = 0; @@ -3173,10 +3324,15 @@ i_ldc_read_packet(ldc_chan_t *ldcp, caddr_t target_bufp, size_t *sizep) ldcp->mode == LDC_MODE_STREAM)) { rv = i_ldc_send_pkt(ldcp, LDC_DATA, LDC_ACK, 0); - if (rv != 0) { + if (rv) { cmn_err(CE_NOTE, "ldc_read: (0x%lx) cannot send ACK\n", ldcp->id); - return (0); + + /* if cannot send ACK - reset channel */ + mutex_enter(&ldcp->tx_lock); + i_ldc_reset(ldcp); + mutex_exit(&ldcp->tx_lock); + rv = ECONNRESET; } } @@ -3250,20 +3406,28 @@ ldc_write(ldc_handle_t handle, caddr_t buf, size_t *sizep) } ldcp = (ldc_chan_t *)handle; - mutex_enter(&ldcp->lock); + /* check if writes can occur */ + if (!mutex_tryenter(&ldcp->tx_lock)) { + /* + * Could not get the lock - channel could + * be in the process of being unconfigured + * or reader has encountered an error + */ + return (EAGAIN); + } /* check if non-zero data to write */ if (buf == NULL || sizep == NULL) { DWARN(ldcp->id, "ldc_write: (0x%llx) invalid data write\n", ldcp->id); - mutex_exit(&ldcp->lock); + mutex_exit(&ldcp->tx_lock); return (EINVAL); } if (*sizep == 0) { DWARN(ldcp->id, "ldc_write: (0x%llx) write size of zero\n", ldcp->id); - mutex_exit(&ldcp->lock); + mutex_exit(&ldcp->tx_lock); return (0); } @@ -3278,7 +3442,7 @@ ldc_write(ldc_handle_t handle, caddr_t buf, size_t *sizep) rv = ldcp->write_p(ldcp, buf, sizep); } - mutex_exit(&ldcp->lock); + mutex_exit(&ldcp->tx_lock); return (rv); } @@ -3295,7 +3459,7 @@ i_ldc_write_raw(ldc_chan_t *ldcp, caddr_t buf, size_t *sizep) int rv = 0; size_t size; - ASSERT(mutex_owned(&ldcp->lock)); + ASSERT(MUTEX_HELD(&ldcp->tx_lock)); ASSERT(ldcp->mode == LDC_MODE_RAW); size = *sizep; @@ -3326,8 +3490,22 @@ i_ldc_write_raw(ldc_chan_t *ldcp, caddr_t buf, size_t *sizep) ldcp->link_state == LDC_CHANNEL_RESET) { DWARN(ldcp->id, "ldc_write: (0x%llx) channel down/reset\n", ldcp->id); - i_ldc_reset(ldcp); + *sizep = 0; + if (mutex_tryenter(&ldcp->lock)) { + i_ldc_reset(ldcp); + mutex_exit(&ldcp->lock); + } else { + /* + * Release Tx lock, and then reacquire channel + * and Tx lock in correct order + */ + mutex_exit(&ldcp->tx_lock); + mutex_enter(&ldcp->lock); + mutex_enter(&ldcp->tx_lock); + i_ldc_reset(ldcp); + mutex_exit(&ldcp->lock); + } return (ECONNRESET); } @@ -3349,10 +3527,10 @@ i_ldc_write_raw(ldc_chan_t *ldcp, caddr_t buf, size_t *sizep) /* Send the data now */ ldcmsg = (ldc_msg_t *)(ldcp->tx_q_va + tx_tail); - /* copy the data into pkt */ + /* copy the data into pkt */ bcopy((uint8_t *)buf, ldcmsg, size); - /* increment tail */ + /* increment tail */ tx_tail = new_tail; /* @@ -3368,9 +3546,21 @@ i_ldc_write_raw(ldc_chan_t *ldcp, caddr_t buf, size_t *sizep) return (EWOULDBLOCK); } - /* cannot write data - reset channel */ - i_ldc_reset(ldcp); *sizep = 0; + if (mutex_tryenter(&ldcp->lock)) { + i_ldc_reset(ldcp); + mutex_exit(&ldcp->lock); + } else { + /* + * Release Tx lock, and then reacquire channel + * and Tx lock in correct order + */ + mutex_exit(&ldcp->tx_lock); + mutex_enter(&ldcp->lock); + mutex_enter(&ldcp->tx_lock); + i_ldc_reset(ldcp); + mutex_exit(&ldcp->lock); + } return (ECONNRESET); } @@ -3403,7 +3593,7 @@ i_ldc_write_packet(ldc_chan_t *ldcp, caddr_t buf, size_t *size) int rv; uint32_t curr_seqid; - ASSERT(mutex_owned(&ldcp->lock)); + ASSERT(MUTEX_HELD(&ldcp->tx_lock)); ASSERT(ldcp->mode == LDC_MODE_RELIABLE || ldcp->mode == LDC_MODE_UNRELIABLE || @@ -3427,7 +3617,20 @@ i_ldc_write_packet(ldc_chan_t *ldcp, caddr_t buf, size_t *size) DWARN(ldcp->id, "ldc_write: (0x%llx) channel down/reset\n", ldcp->id); *size = 0; - i_ldc_reset(ldcp); + if (mutex_tryenter(&ldcp->lock)) { + i_ldc_reset(ldcp); + mutex_exit(&ldcp->lock); + } else { + /* + * Release Tx lock, and then reacquire channel + * and Tx lock in correct order + */ + mutex_exit(&ldcp->tx_lock); + mutex_enter(&ldcp->lock); + mutex_enter(&ldcp->tx_lock); + i_ldc_reset(ldcp); + mutex_exit(&ldcp->lock); + } return (ECONNRESET); } @@ -3522,9 +3725,21 @@ i_ldc_write_packet(ldc_chan_t *ldcp, caddr_t buf, size_t *size) int rv2; if (rv != EWOULDBLOCK) { - /* cannot write data - reset channel */ - i_ldc_reset(ldcp); *size = 0; + if (mutex_tryenter(&ldcp->lock)) { + i_ldc_reset(ldcp); + mutex_exit(&ldcp->lock); + } else { + /* + * Release Tx lock, and then reacquire channel + * and Tx lock in correct order + */ + mutex_exit(&ldcp->tx_lock); + mutex_enter(&ldcp->lock); + mutex_enter(&ldcp->tx_lock); + i_ldc_reset(ldcp); + mutex_exit(&ldcp->lock); + } return (ECONNRESET); } @@ -3560,7 +3775,7 @@ i_ldc_write_packet(ldc_chan_t *ldcp, caddr_t buf, size_t *size) static int i_ldc_write_stream(ldc_chan_t *ldcp, caddr_t buf, size_t *sizep) { - ASSERT(mutex_owned(&ldcp->lock)); + ASSERT(MUTEX_HELD(&ldcp->tx_lock)); ASSERT(ldcp->mode == LDC_MODE_STREAM); /* Truncate packet to max of MTU size */ @@ -4692,7 +4907,7 @@ ldc_mem_map(ldc_mem_handle_t mhandle, ldc_mem_cookie_t *cookie, uint32_t ccount, } D1(ldcp->id, "ldc_mem_map: (0x%llx) cookie = 0x%llx,0x%llx\n", - mhandle, cookie->addr, cookie->size); + ldcp->id, cookie->addr, cookie->size); /* FUTURE: get the page size, pgsz code, and shift */ pg_size = MMU_PAGESIZE; |