From c1065a6c9c4008735c669d9528f98169f9c1cc38 Mon Sep 17 00:00:00 2001 From: Mike Christie Date: Tue, 12 Aug 2008 21:55:37 -0500 Subject: [PATCH 06/23] libfc: don't WARN_ON if fc_seq_start_next and fc_seq_exch_abort race. If fc_exch_recv is completing the ep while fc_fcp.c is calling fc_seq_exch_abort then we could hit the WARN_ON for the completion check. Instead of warning we just want to fail the fc_seq_exch_abort call. This also adds a check in fc_seq_exch_abort for if we are sending an abort. There is no point in sending another one. Signed-off-by: Mike Christie --- drivers/scsi/libfc/fc_exch.c | 75 +++++++++++++++++++++++++----------------- 1 files changed, 45 insertions(+), 30 deletions(-) diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index 377d165..66b1c0d 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -138,6 +138,7 @@ static void fc_seq_ls_rjt(struct fc_seq *, enum fc_els_rjt_reason, enum fc_els_rjt_explan); static void fc_exch_els_rec(struct fc_seq *, struct fc_frame *); static void fc_exch_els_rrq(struct fc_seq *, struct fc_frame *); +static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp); /* * Internal implementation notes. @@ -352,35 +353,43 @@ int fc_seq_exch_abort(const struct fc_seq *req_sp) ep = fc_seq_exch(req_sp); + spin_lock_bh(&ep->ex_lock); + if (ep->esb_stat & (ESB_ST_COMPLETE | ESB_ST_ABNORMAL)) { + spin_unlock_bh(&ep->ex_lock); + return -ENXIO; + } + /* * Send the abort on a new sequence if possible. */ - error = -ENOMEM; - sp = fc_seq_start_next(&ep->seq); - if (sp) { - spin_lock_bh(&ep->ex_lock); - sp->f_ctl |= FC_FC_SEQ_INIT; - ep->esb_stat |= ESB_ST_SEQ_INIT | ESB_ST_ABNORMAL; - fc_exch_timer_set_locked(ep, ep->r_a_tov); + sp = fc_seq_start_next_locked(&ep->seq); + if (!sp) { spin_unlock_bh(&ep->ex_lock); + return -ENOMEM; + } - /* - * If not logged into the fabric, don't send ABTS but leave - * sequence active until next timeout. - */ - if (!ep->sid) - return 0; + sp->f_ctl |= FC_FC_SEQ_INIT; + ep->esb_stat |= ESB_ST_SEQ_INIT | ESB_ST_ABNORMAL; + fc_exch_timer_set_locked(ep, ep->r_a_tov); + spin_unlock_bh(&ep->ex_lock); + + /* + * If not logged into the fabric, don't send ABTS but leave + * sequence active until next timeout. + */ + if (!ep->sid) + return 0; + + /* + * Send an abort for the sequence that timed out. + */ + fp = fc_frame_alloc(ep->lp, 0); + if (fp) { + fc_frame_setup(fp, FC_RCTL_BA_ABTS, FC_TYPE_BLS); + error = fc_seq_send(ep->lp, sp, fp, FC_FC_END_SEQ); + } else + error = -ENOBUFS; - /* - * Send an abort for the sequence that timed out. - */ - fp = fc_frame_alloc(ep->lp, 0); - if (fp) { - fc_frame_setup(fp, FC_RCTL_BA_ABTS, FC_TYPE_BLS); - error = fc_seq_send(ep->lp, sp, fp, FC_FC_END_SEQ); - } else - error = -ENOBUFS; - } return error; } EXPORT_SYMBOL(fc_seq_exch_abort); @@ -717,11 +726,11 @@ fc_seq_lookup_recip(struct fc_exch_mgr *mp, struct fc_frame *fp) */ if (fc_sof_is_init(fr_sof(fp))) { sp = fc_seq_start_next(&ep->seq); - sp->id = fh->fh_seq_id; if (!sp) { reject = FC_RJT_SEQ_XS; /* exchange shortage */ goto out; } + sp->id = fh->fh_seq_id; sp->ssb_stat |= SSB_ST_RESP; } else { sp = &ep->seq; @@ -793,6 +802,16 @@ static void fc_exch_set_addr(struct fc_exch *ep, } } +static struct fc_seq *fc_seq_start_next_locked(struct fc_seq *sp) +{ + struct fc_exch *ep = fc_seq_exch(sp); + + sp = fc_seq_alloc(ep, ep->seq_id++); + if (fc_exch_debug) + FC_DBG("exch %4x f_ctl %6x seq %2x f_ctl %6x\n", + ep->xid, ep->f_ctl, sp->id, sp->f_ctl); + return sp; +} /* * Allocate a new sequence on the same exchange as the supplied sequence. * This will never return NULL. @@ -803,13 +822,9 @@ struct fc_seq *fc_seq_start_next(struct fc_seq *sp) spin_lock_bh(&ep->ex_lock); WARN_ON((ep->esb_stat & ESB_ST_COMPLETE) != 0); - - sp = fc_seq_alloc(ep, ep->seq_id++); - - if (fc_exch_debug) - FC_DBG("exch %4x f_ctl %6x seq %2x f_ctl %6x\n", - ep->xid, ep->f_ctl, sp->id, sp->f_ctl); + sp = fc_seq_start_next_locked(sp); spin_unlock_bh(&ep->ex_lock); + return sp; } EXPORT_SYMBOL(fc_seq_start_next); -- 1.5.5.1