|
@@ -512,9 +512,345 @@ __lpfc_get_active_sglq(struct lpfc_hba *phba, uint16_t xritag)
|
|
|
return sglq;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * __lpfc_set_rrq_active - set RRQ active bit in the ndlp's xri_bitmap.
|
|
|
+ * @phba: Pointer to HBA context object.
|
|
|
+ * @ndlp: nodelist pointer for this target.
|
|
|
+ * @xritag: xri used in this exchange.
|
|
|
+ * @rxid: Remote Exchange ID.
|
|
|
+ * @send_rrq: Flag used to determine if we should send rrq els cmd.
|
|
|
+ *
|
|
|
+ * This function is called with hbalock held.
|
|
|
+ * The active bit is set in the ndlp's active rrq xri_bitmap. Allocates an
|
|
|
+ * rrq struct and adds it to the active_rrq_list.
|
|
|
+ *
|
|
|
+ * returns 0 for rrq slot for this xri
|
|
|
+ * < 0 Were not able to get rrq mem or invalid parameter.
|
|
|
+ **/
|
|
|
+static int
|
|
|
+__lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
|
|
|
+ uint16_t xritag, uint16_t rxid, uint16_t send_rrq)
|
|
|
+{
|
|
|
+ uint16_t adj_xri;
|
|
|
+ struct lpfc_node_rrq *rrq;
|
|
|
+ int empty;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * set the active bit even if there is no mem available.
|
|
|
+ */
|
|
|
+ adj_xri = xritag - phba->sli4_hba.max_cfg_param.xri_base;
|
|
|
+ if (!ndlp)
|
|
|
+ return -EINVAL;
|
|
|
+ if (test_and_set_bit(adj_xri, ndlp->active_rrqs.xri_bitmap))
|
|
|
+ return -EINVAL;
|
|
|
+ rrq = mempool_alloc(phba->rrq_pool, GFP_KERNEL);
|
|
|
+ if (rrq) {
|
|
|
+ rrq->send_rrq = send_rrq;
|
|
|
+ rrq->xritag = xritag;
|
|
|
+ rrq->rrq_stop_time = jiffies + HZ * (phba->fc_ratov + 1);
|
|
|
+ rrq->ndlp = ndlp;
|
|
|
+ rrq->nlp_DID = ndlp->nlp_DID;
|
|
|
+ rrq->vport = ndlp->vport;
|
|
|
+ rrq->rxid = rxid;
|
|
|
+ empty = list_empty(&phba->active_rrq_list);
|
|
|
+ if (phba->cfg_enable_rrq && send_rrq)
|
|
|
+ /*
|
|
|
+ * We need the xri before we can add this to the
|
|
|
+ * phba active rrq list.
|
|
|
+ */
|
|
|
+ rrq->send_rrq = send_rrq;
|
|
|
+ else
|
|
|
+ rrq->send_rrq = 0;
|
|
|
+ list_add_tail(&rrq->list, &phba->active_rrq_list);
|
|
|
+ if (!(phba->hba_flag & HBA_RRQ_ACTIVE)) {
|
|
|
+ phba->hba_flag |= HBA_RRQ_ACTIVE;
|
|
|
+ if (empty)
|
|
|
+ lpfc_worker_wake_up(phba);
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+ return -ENOMEM;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * __lpfc_clr_rrq_active - Clears RRQ active bit in xri_bitmap.
|
|
|
+ * @phba: Pointer to HBA context object.
|
|
|
+ * @xritag: xri used in this exchange.
|
|
|
+ * @rrq: The RRQ to be cleared.
|
|
|
+ *
|
|
|
+ * This function is called with hbalock held. This function
|
|
|
+ **/
|
|
|
+static void
|
|
|
+__lpfc_clr_rrq_active(struct lpfc_hba *phba,
|
|
|
+ uint16_t xritag,
|
|
|
+ struct lpfc_node_rrq *rrq)
|
|
|
+{
|
|
|
+ uint16_t adj_xri;
|
|
|
+ struct lpfc_nodelist *ndlp;
|
|
|
+
|
|
|
+ ndlp = lpfc_findnode_did(rrq->vport, rrq->nlp_DID);
|
|
|
+
|
|
|
+ /* The target DID could have been swapped (cable swap)
|
|
|
+ * we should use the ndlp from the findnode if it is
|
|
|
+ * available.
|
|
|
+ */
|
|
|
+ if (!ndlp)
|
|
|
+ ndlp = rrq->ndlp;
|
|
|
+
|
|
|
+ adj_xri = xritag - phba->sli4_hba.max_cfg_param.xri_base;
|
|
|
+ if (test_and_clear_bit(adj_xri, ndlp->active_rrqs.xri_bitmap)) {
|
|
|
+ rrq->send_rrq = 0;
|
|
|
+ rrq->xritag = 0;
|
|
|
+ rrq->rrq_stop_time = 0;
|
|
|
+ }
|
|
|
+ mempool_free(rrq, phba->rrq_pool);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_handle_rrq_active - Checks if RRQ has waithed RATOV.
|
|
|
+ * @phba: Pointer to HBA context object.
|
|
|
+ *
|
|
|
+ * This function is called with hbalock held. This function
|
|
|
+ * Checks if stop_time (ratov from setting rrq active) has
|
|
|
+ * been reached, if it has and the send_rrq flag is set then
|
|
|
+ * it will call lpfc_send_rrq. If the send_rrq flag is not set
|
|
|
+ * then it will just call the routine to clear the rrq and
|
|
|
+ * free the rrq resource.
|
|
|
+ * The timer is set to the next rrq that is going to expire before
|
|
|
+ * leaving the routine.
|
|
|
+ *
|
|
|
+ **/
|
|
|
+void
|
|
|
+lpfc_handle_rrq_active(struct lpfc_hba *phba)
|
|
|
+{
|
|
|
+ struct lpfc_node_rrq *rrq;
|
|
|
+ struct lpfc_node_rrq *nextrrq;
|
|
|
+ unsigned long next_time;
|
|
|
+ unsigned long iflags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&phba->hbalock, iflags);
|
|
|
+ phba->hba_flag &= ~HBA_RRQ_ACTIVE;
|
|
|
+ next_time = jiffies + HZ * (phba->fc_ratov + 1);
|
|
|
+ list_for_each_entry_safe(rrq, nextrrq,
|
|
|
+ &phba->active_rrq_list, list) {
|
|
|
+ if (time_after(jiffies, rrq->rrq_stop_time)) {
|
|
|
+ list_del(&rrq->list);
|
|
|
+ if (!rrq->send_rrq)
|
|
|
+ /* this call will free the rrq */
|
|
|
+ __lpfc_clr_rrq_active(phba, rrq->xritag, rrq);
|
|
|
+ else {
|
|
|
+ /* if we send the rrq then the completion handler
|
|
|
+ * will clear the bit in the xribitmap.
|
|
|
+ */
|
|
|
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
|
|
|
+ if (lpfc_send_rrq(phba, rrq)) {
|
|
|
+ lpfc_clr_rrq_active(phba, rrq->xritag,
|
|
|
+ rrq);
|
|
|
+ }
|
|
|
+ spin_lock_irqsave(&phba->hbalock, iflags);
|
|
|
+ }
|
|
|
+ } else if (time_before(rrq->rrq_stop_time, next_time))
|
|
|
+ next_time = rrq->rrq_stop_time;
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
|
|
|
+ if (!list_empty(&phba->active_rrq_list))
|
|
|
+ mod_timer(&phba->rrq_tmr, next_time);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_get_active_rrq - Get the active RRQ for this exchange.
|
|
|
+ * @vport: Pointer to vport context object.
|
|
|
+ * @xri: The xri used in the exchange.
|
|
|
+ * @did: The targets DID for this exchange.
|
|
|
+ *
|
|
|
+ * returns NULL = rrq not found in the phba->active_rrq_list.
|
|
|
+ * rrq = rrq for this xri and target.
|
|
|
+ **/
|
|
|
+struct lpfc_node_rrq *
|
|
|
+lpfc_get_active_rrq(struct lpfc_vport *vport, uint16_t xri, uint32_t did)
|
|
|
+{
|
|
|
+ struct lpfc_hba *phba = vport->phba;
|
|
|
+ struct lpfc_node_rrq *rrq;
|
|
|
+ struct lpfc_node_rrq *nextrrq;
|
|
|
+ unsigned long iflags;
|
|
|
+
|
|
|
+ if (phba->sli_rev != LPFC_SLI_REV4)
|
|
|
+ return NULL;
|
|
|
+ spin_lock_irqsave(&phba->hbalock, iflags);
|
|
|
+ list_for_each_entry_safe(rrq, nextrrq, &phba->active_rrq_list, list) {
|
|
|
+ if (rrq->vport == vport && rrq->xritag == xri &&
|
|
|
+ rrq->nlp_DID == did){
|
|
|
+ list_del(&rrq->list);
|
|
|
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
|
|
|
+ return rrq;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
|
|
|
+ return NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_cleanup_vports_rrqs - Remove and clear the active RRQ for this vport.
|
|
|
+ * @vport: Pointer to vport context object.
|
|
|
+ *
|
|
|
+ * Remove all active RRQs for this vport from the phba->active_rrq_list and
|
|
|
+ * clear the rrq.
|
|
|
+ **/
|
|
|
+void
|
|
|
+lpfc_cleanup_vports_rrqs(struct lpfc_vport *vport)
|
|
|
+
|
|
|
+{
|
|
|
+ struct lpfc_hba *phba = vport->phba;
|
|
|
+ struct lpfc_node_rrq *rrq;
|
|
|
+ struct lpfc_node_rrq *nextrrq;
|
|
|
+ unsigned long iflags;
|
|
|
+
|
|
|
+ if (phba->sli_rev != LPFC_SLI_REV4)
|
|
|
+ return;
|
|
|
+ spin_lock_irqsave(&phba->hbalock, iflags);
|
|
|
+ list_for_each_entry_safe(rrq, nextrrq, &phba->active_rrq_list, list) {
|
|
|
+ if (rrq->vport == vport) {
|
|
|
+ list_del(&rrq->list);
|
|
|
+ __lpfc_clr_rrq_active(phba, rrq->xritag, rrq);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_cleanup_wt_rrqs - Remove all rrq's from the active list.
|
|
|
+ * @phba: Pointer to HBA context object.
|
|
|
+ *
|
|
|
+ * Remove all rrqs from the phba->active_rrq_list and free them by
|
|
|
+ * calling __lpfc_clr_active_rrq
|
|
|
+ *
|
|
|
+ **/
|
|
|
+void
|
|
|
+lpfc_cleanup_wt_rrqs(struct lpfc_hba *phba)
|
|
|
+{
|
|
|
+ struct lpfc_node_rrq *rrq;
|
|
|
+ struct lpfc_node_rrq *nextrrq;
|
|
|
+ unsigned long next_time;
|
|
|
+ unsigned long iflags;
|
|
|
+
|
|
|
+ if (phba->sli_rev != LPFC_SLI_REV4)
|
|
|
+ return;
|
|
|
+ spin_lock_irqsave(&phba->hbalock, iflags);
|
|
|
+ phba->hba_flag &= ~HBA_RRQ_ACTIVE;
|
|
|
+ next_time = jiffies + HZ * (phba->fc_ratov * 2);
|
|
|
+ list_for_each_entry_safe(rrq, nextrrq, &phba->active_rrq_list, list) {
|
|
|
+ list_del(&rrq->list);
|
|
|
+ __lpfc_clr_rrq_active(phba, rrq->xritag, rrq);
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
|
|
|
+ if (!list_empty(&phba->active_rrq_list))
|
|
|
+ mod_timer(&phba->rrq_tmr, next_time);
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * __lpfc_test_rrq_active - Test RRQ bit in xri_bitmap.
|
|
|
+ * @phba: Pointer to HBA context object.
|
|
|
+ * @ndlp: Targets nodelist pointer for this exchange.
|
|
|
+ * @xritag the xri in the bitmap to test.
|
|
|
+ *
|
|
|
+ * This function is called with hbalock held. This function
|
|
|
+ * returns 0 = rrq not active for this xri
|
|
|
+ * 1 = rrq is valid for this xri.
|
|
|
+ **/
|
|
|
+static int
|
|
|
+__lpfc_test_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
|
|
|
+ uint16_t xritag)
|
|
|
+{
|
|
|
+ uint16_t adj_xri;
|
|
|
+
|
|
|
+ adj_xri = xritag - phba->sli4_hba.max_cfg_param.xri_base;
|
|
|
+ if (!ndlp)
|
|
|
+ return 0;
|
|
|
+ if (test_bit(adj_xri, ndlp->active_rrqs.xri_bitmap))
|
|
|
+ return 1;
|
|
|
+ else
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_set_rrq_active - set RRQ active bit in xri_bitmap.
|
|
|
+ * @phba: Pointer to HBA context object.
|
|
|
+ * @ndlp: nodelist pointer for this target.
|
|
|
+ * @xritag: xri used in this exchange.
|
|
|
+ * @rxid: Remote Exchange ID.
|
|
|
+ * @send_rrq: Flag used to determine if we should send rrq els cmd.
|
|
|
+ *
|
|
|
+ * This function takes the hbalock.
|
|
|
+ * The active bit is always set in the active rrq xri_bitmap even
|
|
|
+ * if there is no slot avaiable for the other rrq information.
|
|
|
+ *
|
|
|
+ * returns 0 rrq actived for this xri
|
|
|
+ * < 0 No memory or invalid ndlp.
|
|
|
+ **/
|
|
|
+int
|
|
|
+lpfc_set_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
|
|
|
+ uint16_t xritag, uint16_t rxid, uint16_t send_rrq)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ unsigned long iflags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&phba->hbalock, iflags);
|
|
|
+ ret = __lpfc_set_rrq_active(phba, ndlp, xritag, rxid, send_rrq);
|
|
|
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_clr_rrq_active - Clears RRQ active bit in xri_bitmap.
|
|
|
+ * @phba: Pointer to HBA context object.
|
|
|
+ * @xritag: xri used in this exchange.
|
|
|
+ * @rrq: The RRQ to be cleared.
|
|
|
+ *
|
|
|
+ * This function is takes the hbalock.
|
|
|
+ **/
|
|
|
+void
|
|
|
+lpfc_clr_rrq_active(struct lpfc_hba *phba,
|
|
|
+ uint16_t xritag,
|
|
|
+ struct lpfc_node_rrq *rrq)
|
|
|
+{
|
|
|
+ unsigned long iflags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&phba->hbalock, iflags);
|
|
|
+ __lpfc_clr_rrq_active(phba, xritag, rrq);
|
|
|
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
|
|
|
+ return;
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+/**
|
|
|
+ * lpfc_test_rrq_active - Test RRQ bit in xri_bitmap.
|
|
|
+ * @phba: Pointer to HBA context object.
|
|
|
+ * @ndlp: Targets nodelist pointer for this exchange.
|
|
|
+ * @xritag the xri in the bitmap to test.
|
|
|
+ *
|
|
|
+ * This function takes the hbalock.
|
|
|
+ * returns 0 = rrq not active for this xri
|
|
|
+ * 1 = rrq is valid for this xri.
|
|
|
+ **/
|
|
|
+int
|
|
|
+lpfc_test_rrq_active(struct lpfc_hba *phba, struct lpfc_nodelist *ndlp,
|
|
|
+ uint16_t xritag)
|
|
|
+{
|
|
|
+ int ret;
|
|
|
+ unsigned long iflags;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&phba->hbalock, iflags);
|
|
|
+ ret = __lpfc_test_rrq_active(phba, ndlp, xritag);
|
|
|
+ spin_unlock_irqrestore(&phba->hbalock, iflags);
|
|
|
+ return ret;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* __lpfc_sli_get_sglq - Allocates an iocb object from sgl pool
|
|
|
* @phba: Pointer to HBA context object.
|
|
|
+ * @piocb: Pointer to the iocbq.
|
|
|
*
|
|
|
* This function is called with hbalock held. This function
|
|
|
* Gets a new driver sglq object from the sglq list. If the
|
|
@@ -522,17 +858,53 @@ __lpfc_get_active_sglq(struct lpfc_hba *phba, uint16_t xritag)
|
|
|
* allocated sglq object else it returns NULL.
|
|
|
**/
|
|
|
static struct lpfc_sglq *
|
|
|
-__lpfc_sli_get_sglq(struct lpfc_hba *phba)
|
|
|
+__lpfc_sli_get_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq)
|
|
|
{
|
|
|
struct list_head *lpfc_sgl_list = &phba->sli4_hba.lpfc_sgl_list;
|
|
|
struct lpfc_sglq *sglq = NULL;
|
|
|
+ struct lpfc_sglq *start_sglq = NULL;
|
|
|
uint16_t adj_xri;
|
|
|
+ struct lpfc_scsi_buf *lpfc_cmd;
|
|
|
+ struct lpfc_nodelist *ndlp;
|
|
|
+ int found = 0;
|
|
|
+
|
|
|
+ if (piocbq->iocb_flag & LPFC_IO_FCP) {
|
|
|
+ lpfc_cmd = (struct lpfc_scsi_buf *) piocbq->context1;
|
|
|
+ ndlp = lpfc_cmd->rdata->pnode;
|
|
|
+ } else if (piocbq->iocb.ulpCommand == CMD_GEN_REQUEST64_CR)
|
|
|
+ ndlp = piocbq->context_un.ndlp;
|
|
|
+ else if (piocbq->iocb.ulpCommand == CMD_XMIT_BLS_RSP64_CX)
|
|
|
+ ndlp = lpfc_findnode_did(piocbq->vport,
|
|
|
+ piocbq->iocb.ulpContext);
|
|
|
+ else
|
|
|
+ ndlp = piocbq->context1;
|
|
|
+
|
|
|
list_remove_head(lpfc_sgl_list, sglq, struct lpfc_sglq, list);
|
|
|
- if (!sglq)
|
|
|
- return NULL;
|
|
|
- adj_xri = sglq->sli4_xritag - phba->sli4_hba.max_cfg_param.xri_base;
|
|
|
- phba->sli4_hba.lpfc_sglq_active_list[adj_xri] = sglq;
|
|
|
- sglq->state = SGL_ALLOCATED;
|
|
|
+ start_sglq = sglq;
|
|
|
+ while (!found) {
|
|
|
+ if (!sglq)
|
|
|
+ return NULL;
|
|
|
+ adj_xri = sglq->sli4_xritag -
|
|
|
+ phba->sli4_hba.max_cfg_param.xri_base;
|
|
|
+ if (__lpfc_test_rrq_active(phba, ndlp, sglq->sli4_xritag)) {
|
|
|
+ /* This xri has an rrq outstanding for this DID.
|
|
|
+ * put it back in the list and get another xri.
|
|
|
+ */
|
|
|
+ list_add_tail(&sglq->list, lpfc_sgl_list);
|
|
|
+ sglq = NULL;
|
|
|
+ list_remove_head(lpfc_sgl_list, sglq,
|
|
|
+ struct lpfc_sglq, list);
|
|
|
+ if (sglq == start_sglq) {
|
|
|
+ sglq = NULL;
|
|
|
+ break;
|
|
|
+ } else
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ sglq->ndlp = ndlp;
|
|
|
+ found = 1;
|
|
|
+ phba->sli4_hba.lpfc_sglq_active_list[adj_xri] = sglq;
|
|
|
+ sglq->state = SGL_ALLOCATED;
|
|
|
+ }
|
|
|
return sglq;
|
|
|
}
|
|
|
|
|
@@ -598,6 +970,7 @@ __lpfc_sli_release_iocbq_s4(struct lpfc_hba *phba, struct lpfc_iocbq *iocbq)
|
|
|
&phba->sli4_hba.abts_sgl_list_lock, iflag);
|
|
|
} else {
|
|
|
sglq->state = SGL_FREED;
|
|
|
+ sglq->ndlp = NULL;
|
|
|
list_add(&sglq->list, &phba->sli4_hba.lpfc_sgl_list);
|
|
|
|
|
|
/* Check if TXQ queue needs to be serviced */
|
|
@@ -6352,7 +6725,7 @@ __lpfc_sli_issue_iocb_s4(struct lpfc_hba *phba, uint32_t ring_number,
|
|
|
return IOCB_BUSY;
|
|
|
}
|
|
|
} else {
|
|
|
- sglq = __lpfc_sli_get_sglq(phba);
|
|
|
+ sglq = __lpfc_sli_get_sglq(phba, piocb);
|
|
|
if (!sglq) {
|
|
|
if (!(flag & SLI_IOCB_RET_IOCB)) {
|
|
|
__lpfc_sli_ringtx_put(phba,
|
|
@@ -11570,6 +11943,10 @@ lpfc_sli4_seq_abort_acc(struct lpfc_hba *phba,
|
|
|
"SID:x%x\n", oxid, sid);
|
|
|
return;
|
|
|
}
|
|
|
+ if (rxid >= phba->sli4_hba.max_cfg_param.xri_base
|
|
|
+ && rxid <= (phba->sli4_hba.max_cfg_param.max_xri
|
|
|
+ + phba->sli4_hba.max_cfg_param.xri_base))
|
|
|
+ lpfc_set_rrq_active(phba, ndlp, rxid, oxid, 0);
|
|
|
|
|
|
/* Allocate buffer for acc iocb */
|
|
|
ctiocb = lpfc_sli_get_iocbq(phba);
|
|
@@ -13008,12 +13385,13 @@ lpfc_drain_txq(struct lpfc_hba *phba)
|
|
|
while (pring->txq_cnt) {
|
|
|
spin_lock_irqsave(&phba->hbalock, iflags);
|
|
|
|
|
|
- sglq = __lpfc_sli_get_sglq(phba);
|
|
|
+ piocbq = lpfc_sli_ringtx_get(phba, pring);
|
|
|
+ sglq = __lpfc_sli_get_sglq(phba, piocbq);
|
|
|
if (!sglq) {
|
|
|
+ __lpfc_sli_ringtx_put(phba, pring, piocbq);
|
|
|
spin_unlock_irqrestore(&phba->hbalock, iflags);
|
|
|
break;
|
|
|
} else {
|
|
|
- piocbq = lpfc_sli_ringtx_get(phba, pring);
|
|
|
if (!piocbq) {
|
|
|
/* The txq_cnt out of sync. This should
|
|
|
* never happen
|