diff options
Diffstat (limited to 'drivers/scsi/libfc/fc_exch.c')
| -rw-r--r-- | drivers/scsi/libfc/fc_exch.c | 215 | 
1 files changed, 127 insertions, 88 deletions
diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index ca52bfa4a1e..ec2a1aec235 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -129,11 +129,11 @@ struct fc_exch_mgr_anchor {  };  static void fc_exch_rrq(struct fc_exch *); -static void fc_seq_ls_acc(struct fc_seq *); -static void fc_seq_ls_rjt(struct fc_seq *, enum fc_els_rjt_reason, +static void fc_seq_ls_acc(struct fc_frame *); +static void fc_seq_ls_rjt(struct fc_frame *, 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 void fc_exch_els_rec(struct fc_frame *); +static void fc_exch_els_rrq(struct fc_frame *);  /*   * Internal implementation notes. @@ -462,6 +462,7 @@ static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp,  	f_ctl = ntoh24(fh->fh_f_ctl);  	fc_exch_setup_hdr(ep, fp, f_ctl); +	fr_encaps(fp) = ep->encaps;  	/*  	 * update sequence count if this frame is carrying @@ -1000,28 +1001,30 @@ static void fc_exch_set_addr(struct fc_exch *ep,  /**   * fc_seq_els_rsp_send() - Send an ELS response using infomation from   *			   the existing sequence/exchange. - * @sp:	      The sequence/exchange to get information from + * @fp:	      The received frame   * @els_cmd:  The ELS command to be sent   * @els_data: The ELS data to be sent + * + * The received frame is not freed.   */ -static void fc_seq_els_rsp_send(struct fc_seq *sp, enum fc_els_cmd els_cmd, +static void fc_seq_els_rsp_send(struct fc_frame *fp, enum fc_els_cmd els_cmd,  				struct fc_seq_els_data *els_data)  {  	switch (els_cmd) {  	case ELS_LS_RJT: -		fc_seq_ls_rjt(sp, els_data->reason, els_data->explan); +		fc_seq_ls_rjt(fp, els_data->reason, els_data->explan);  		break;  	case ELS_LS_ACC: -		fc_seq_ls_acc(sp); +		fc_seq_ls_acc(fp);  		break;  	case ELS_RRQ: -		fc_exch_els_rrq(sp, els_data->fp); +		fc_exch_els_rrq(fp);  		break;  	case ELS_REC: -		fc_exch_els_rec(sp, els_data->fp); +		fc_exch_els_rec(fp);  		break;  	default: -		FC_EXCH_DBG(fc_seq_exch(sp), "Invalid ELS CMD:%x\n", els_cmd); +		FC_LPORT_DBG(fr_dev(fp), "Invalid ELS CMD:%x\n", els_cmd);  	}  } @@ -1228,11 +1231,35 @@ free:  }  /** - * fc_exch_recv_req() - Handler for an incoming request where is other - *			end is originating the sequence + * fc_seq_assign() - Assign exchange and sequence for incoming request + * @lport: The local port that received the request + * @fp:    The request frame + * + * On success, the sequence pointer will be returned and also in fr_seq(@fp). + */ +static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp) +{ +	struct fc_exch_mgr_anchor *ema; + +	WARN_ON(lport != fr_dev(fp)); +	WARN_ON(fr_seq(fp)); +	fr_seq(fp) = NULL; + +	list_for_each_entry(ema, &lport->ema_list, ema_list) +		if ((!ema->match || ema->match(fp)) && +		    fc_seq_lookup_recip(lport, ema->mp, fp) != FC_RJT_NONE) +			break; +	return fr_seq(fp); +} + +/** + * fc_exch_recv_req() - Handler for an incoming request   * @lport: The local port that received the request   * @mp:	   The EM that the exchange is on   * @fp:	   The request frame + * + * This is used when the other end is originating the exchange + * and the sequence.   */  static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp,  			     struct fc_frame *fp) @@ -1250,13 +1277,23 @@ static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp,  		fc_frame_free(fp);  		return;  	} +	fr_dev(fp) = lport; + +	BUG_ON(fr_seq(fp));		/* XXX remove later */ + +	/* +	 * If the RX_ID is 0xffff, don't allocate an exchange. +	 * The upper-level protocol may request one later, if needed. +	 */ +	if (fh->fh_rx_id == htons(FC_XID_UNKNOWN)) +		return lport->tt.lport_recv(lport, fp); -	fr_seq(fp) = NULL;  	reject = fc_seq_lookup_recip(lport, mp, fp);  	if (reject == FC_RJT_NONE) {  		sp = fr_seq(fp);	/* sequence will be held */  		ep = fc_seq_exch(sp);  		fc_seq_send_ack(sp, fp); +		ep->encaps = fr_encaps(fp);  		/*  		 * Call the receive function. @@ -1272,7 +1309,7 @@ static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp,  		if (ep->resp)  			ep->resp(sp, fp, ep->arg);  		else -			lport->tt.lport_recv(lport, sp, fp); +			lport->tt.lport_recv(lport, fp);  		fc_exch_release(ep);	/* release from lookup */  	} else {  		FC_LPORT_DBG(lport, "exch/seq lookup failed: reject %x\n", @@ -1540,53 +1577,55 @@ static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp)  /**   * fc_seq_ls_acc() - Accept sequence with LS_ACC - * @req_sp: The request sequence + * @rx_fp: The received frame, not freed here.   *   * If this fails due to allocation or transmit congestion, assume the   * originator will repeat the sequence.   */ -static void fc_seq_ls_acc(struct fc_seq *req_sp) +static void fc_seq_ls_acc(struct fc_frame *rx_fp)  { -	struct fc_seq *sp; +	struct fc_lport *lport;  	struct fc_els_ls_acc *acc;  	struct fc_frame *fp; -	sp = fc_seq_start_next(req_sp); -	fp = fc_frame_alloc(fc_seq_exch(sp)->lp, sizeof(*acc)); -	if (fp) { -		acc = fc_frame_payload_get(fp, sizeof(*acc)); -		memset(acc, 0, sizeof(*acc)); -		acc->la_cmd = ELS_LS_ACC; -		fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS); -	} +	lport = fr_dev(rx_fp); +	fp = fc_frame_alloc(lport, sizeof(*acc)); +	if (!fp) +		return; +	acc = fc_frame_payload_get(fp, sizeof(*acc)); +	memset(acc, 0, sizeof(*acc)); +	acc->la_cmd = ELS_LS_ACC; +	fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0); +	lport->tt.frame_send(lport, fp);  }  /**   * fc_seq_ls_rjt() - Reject a sequence with ELS LS_RJT - * @req_sp: The request sequence + * @rx_fp: The received frame, not freed here.   * @reason: The reason the sequence is being rejected - * @explan: The explaination for the rejection + * @explan: The explanation for the rejection   *   * If this fails due to allocation or transmit congestion, assume the   * originator will repeat the sequence.   */ -static void fc_seq_ls_rjt(struct fc_seq *req_sp, enum fc_els_rjt_reason reason, +static void fc_seq_ls_rjt(struct fc_frame *rx_fp, enum fc_els_rjt_reason reason,  			  enum fc_els_rjt_explan explan)  { -	struct fc_seq *sp; +	struct fc_lport *lport;  	struct fc_els_ls_rjt *rjt;  	struct fc_frame *fp; -	sp = fc_seq_start_next(req_sp); -	fp = fc_frame_alloc(fc_seq_exch(sp)->lp, sizeof(*rjt)); -	if (fp) { -		rjt = fc_frame_payload_get(fp, sizeof(*rjt)); -		memset(rjt, 0, sizeof(*rjt)); -		rjt->er_cmd = ELS_LS_RJT; -		rjt->er_reason = reason; -		rjt->er_explan = explan; -		fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS); -	} +	lport = fr_dev(rx_fp); +	fp = fc_frame_alloc(lport, sizeof(*rjt)); +	if (!fp) +		return; +	rjt = fc_frame_payload_get(fp, sizeof(*rjt)); +	memset(rjt, 0, sizeof(*rjt)); +	rjt->er_cmd = ELS_LS_RJT; +	rjt->er_reason = reason; +	rjt->er_explan = explan; +	fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_ELS_REP, 0); +	lport->tt.frame_send(lport, fp);  }  /** @@ -1689,17 +1728,33 @@ void fc_exch_mgr_reset(struct fc_lport *lport, u32 sid, u32 did)  EXPORT_SYMBOL(fc_exch_mgr_reset);  /** + * fc_exch_lookup() - find an exchange + * @lport: The local port + * @xid: The exchange ID + * + * Returns exchange pointer with hold for caller, or NULL if not found. + */ +static struct fc_exch *fc_exch_lookup(struct fc_lport *lport, u32 xid) +{ +	struct fc_exch_mgr_anchor *ema; + +	list_for_each_entry(ema, &lport->ema_list, ema_list) +		if (ema->mp->min_xid <= xid && xid <= ema->mp->max_xid) +			return fc_exch_find(ema->mp, xid); +	return NULL; +} + +/**   * fc_exch_els_rec() - Handler for ELS REC (Read Exchange Concise) requests - * @sp:	 The sequence the REC is on - * @rfp: The REC frame + * @rfp: The REC frame, not freed here.   *   * Note that the requesting port may be different than the S_ID in the request.   */ -static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp) +static void fc_exch_els_rec(struct fc_frame *rfp)  { +	struct fc_lport *lport;  	struct fc_frame *fp;  	struct fc_exch *ep; -	struct fc_exch_mgr *em;  	struct fc_els_rec *rp;  	struct fc_els_rec_acc *acc;  	enum fc_els_rjt_reason reason = ELS_RJT_LOGIC; @@ -1708,6 +1763,7 @@ static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp)  	u16 rxid;  	u16 oxid; +	lport = fr_dev(rfp);  	rp = fc_frame_payload_get(rfp, sizeof(*rp));  	explan = ELS_EXPL_INV_LEN;  	if (!rp) @@ -1716,35 +1772,19 @@ static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp)  	rxid = ntohs(rp->rec_rx_id);  	oxid = ntohs(rp->rec_ox_id); -	/* -	 * Currently it's hard to find the local S_ID from the exchange -	 * manager.  This will eventually be fixed, but for now it's easier -	 * to lookup the subject exchange twice, once as if we were -	 * the initiator, and then again if we weren't. -	 */ -	em = fc_seq_exch(sp)->em; -	ep = fc_exch_find(em, oxid); +	ep = fc_exch_lookup(lport, +			    sid == fc_host_port_id(lport->host) ? oxid : rxid);  	explan = ELS_EXPL_OXID_RXID; -	if (ep && ep->oid == sid) { -		if (ep->rxid != FC_XID_UNKNOWN && -		    rxid != FC_XID_UNKNOWN && -		    ep->rxid != rxid) -			goto rel; -	} else { -		if (ep) -			fc_exch_release(ep); -		ep = NULL; -		if (rxid != FC_XID_UNKNOWN) -			ep = fc_exch_find(em, rxid); -		if (!ep) -			goto reject; -	} - -	fp = fc_frame_alloc(fc_seq_exch(sp)->lp, sizeof(*acc)); -	if (!fp) { -		fc_exch_done(sp); +	if (!ep) +		goto reject; +	if (ep->oid != sid || oxid != ep->oxid) +		goto rel; +	if (rxid != FC_XID_UNKNOWN && rxid != ep->rxid) +		goto rel; +	fp = fc_frame_alloc(lport, sizeof(*acc)); +	if (!fp)  		goto out; -	} +  	acc = fc_frame_payload_get(fp, sizeof(*acc));  	memset(acc, 0, sizeof(*acc));  	acc->reca_cmd = ELS_LS_ACC; @@ -1759,18 +1799,16 @@ static void fc_exch_els_rec(struct fc_seq *sp, struct fc_frame *rfp)  	acc->reca_e_stat = htonl(ep->esb_stat & (ESB_ST_RESP |  						 ESB_ST_SEQ_INIT |  						 ESB_ST_COMPLETE)); -	sp = fc_seq_start_next(sp); -	fc_seq_send_last(sp, fp, FC_RCTL_ELS_REP, FC_TYPE_ELS); +	fc_fill_reply_hdr(fp, rfp, FC_RCTL_ELS_REP, 0); +	lport->tt.frame_send(lport, fp);  out:  	fc_exch_release(ep); -	fc_frame_free(rfp);  	return;  rel:  	fc_exch_release(ep);  reject: -	fc_seq_ls_rjt(sp, reason, explan); -	fc_frame_free(rfp); +	fc_seq_ls_rjt(rfp, reason, explan);  }  /** @@ -1945,20 +1983,20 @@ retry:  	spin_unlock_bh(&ep->ex_lock);  } -  /**   * fc_exch_els_rrq() - Handler for ELS RRQ (Reset Recovery Qualifier) requests - * @sp: The sequence that the RRQ is on - * @fp: The RRQ frame + * @fp: The RRQ frame, not freed here.   */ -static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp) +static void fc_exch_els_rrq(struct fc_frame *fp)  { +	struct fc_lport *lport;  	struct fc_exch *ep = NULL;	/* request or subject exchange */  	struct fc_els_rrq *rp;  	u32 sid;  	u16 xid;  	enum fc_els_rjt_explan explan; +	lport = fr_dev(fp);  	rp = fc_frame_payload_get(fp, sizeof(*rp));  	explan = ELS_EXPL_INV_LEN;  	if (!rp) @@ -1967,11 +2005,10 @@ static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp)  	/*  	 * lookup subject exchange.  	 */ -	ep = fc_seq_exch(sp);  	sid = ntoh24(rp->rrq_s_id);		/* subject source */ -	xid = ep->did == sid ? ntohs(rp->rrq_ox_id) : ntohs(rp->rrq_rx_id); -	ep = fc_exch_find(ep->em, xid); - +	xid = fc_host_port_id(lport->host) == sid ? +			ntohs(rp->rrq_ox_id) : ntohs(rp->rrq_rx_id); +	ep = fc_exch_lookup(lport, xid);  	explan = ELS_EXPL_OXID_RXID;  	if (!ep)  		goto reject; @@ -2002,15 +2039,14 @@ static void fc_exch_els_rrq(struct fc_seq *sp, struct fc_frame *fp)  	/*  	 * Send LS_ACC.  	 */ -	fc_seq_ls_acc(sp); +	fc_seq_ls_acc(fp);  	goto out;  unlock_reject:  	spin_unlock_bh(&ep->ex_lock);  reject: -	fc_seq_ls_rjt(sp, ELS_RJT_LOGIC, explan); +	fc_seq_ls_rjt(fp, ELS_RJT_LOGIC, explan);  out: -	fc_frame_free(fp);  	if (ep)  		fc_exch_release(ep);	/* drop hold from fc_exch_find */  } @@ -2241,7 +2277,7 @@ void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp)  			fc_exch_recv_seq_resp(ema->mp, fp);  		else if (f_ctl & FC_FC_SEQ_CTX)  			fc_exch_recv_resp(ema->mp, fp); -		else +		else	/* no EX_CTX and no SEQ_CTX */  			fc_exch_recv_req(lport, ema->mp, fp);  		break;  	default: @@ -2279,6 +2315,9 @@ int fc_exch_init(struct fc_lport *lport)  	if (!lport->tt.seq_exch_abort)  		lport->tt.seq_exch_abort = fc_seq_exch_abort; +	if (!lport->tt.seq_assign) +		lport->tt.seq_assign = fc_seq_assign; +  	return 0;  }  EXPORT_SYMBOL(fc_exch_init);  |