diff options
Diffstat (limited to 'drivers/scsi/libfc')
| -rw-r--r-- | drivers/scsi/libfc/fc_disc.c | 41 | ||||
| -rw-r--r-- | drivers/scsi/libfc/fc_elsct.c | 5 | ||||
| -rw-r--r-- | drivers/scsi/libfc/fc_exch.c | 669 | ||||
| -rw-r--r-- | drivers/scsi/libfc/fc_fcp.c | 285 | ||||
| -rw-r--r-- | drivers/scsi/libfc/fc_frame.c | 2 | ||||
| -rw-r--r-- | drivers/scsi/libfc/fc_libfc.c | 129 | ||||
| -rw-r--r-- | drivers/scsi/libfc/fc_libfc.h | 67 | ||||
| -rw-r--r-- | drivers/scsi/libfc/fc_lport.c | 504 | ||||
| -rw-r--r-- | drivers/scsi/libfc/fc_npiv.c | 11 | ||||
| -rw-r--r-- | drivers/scsi/libfc/fc_rport.c | 268 | 
10 files changed, 1451 insertions, 530 deletions
diff --git a/drivers/scsi/libfc/fc_disc.c b/drivers/scsi/libfc/fc_disc.c index 911b2736caf..880a9068ca1 100644 --- a/drivers/scsi/libfc/fc_disc.c +++ b/drivers/scsi/libfc/fc_disc.c @@ -35,6 +35,7 @@  #include <linux/timer.h>  #include <linux/slab.h>  #include <linux/err.h> +#include <linux/export.h>  #include <asm/unaligned.h>  #include <scsi/fc/fc_gs.h> @@ -60,7 +61,7 @@ static void fc_disc_restart(struct fc_disc *);   * Locking Note: This function expects that the lport mutex is locked before   * calling it.   */ -void fc_disc_stop_rports(struct fc_disc *disc) +static void fc_disc_stop_rports(struct fc_disc *disc)  {  	struct fc_lport *lport;  	struct fc_rport_priv *rdata; @@ -205,6 +206,7 @@ static void fc_disc_recv_req(struct fc_lport *lport, struct fc_frame *fp)  	default:  		FC_DISC_DBG(disc, "Received an unsupported request, "  			    "the opcode is (%x)\n", op); +		fc_frame_free(fp);  		break;  	}  } @@ -335,6 +337,13 @@ static void fc_disc_error(struct fc_disc *disc, struct fc_frame *fp)  			schedule_delayed_work(&disc->disc_work, delay);  		} else  			fc_disc_done(disc, DISC_EV_FAILED); +	} else if (PTR_ERR(fp) == -FC_EX_CLOSED) { +		/* +		 * if discovery fails due to lport reset, clear +		 * pending flag so that subsequent discovery can +		 * continue +		 */ +		disc->pending = 0;  	}  } @@ -680,7 +689,7 @@ static int fc_disc_single(struct fc_lport *lport, struct fc_disc_port *dp)   * fc_disc_stop() - Stop discovery for a given lport   * @lport: The local port that discovery should stop on   */ -void fc_disc_stop(struct fc_lport *lport) +static void fc_disc_stop(struct fc_lport *lport)  {  	struct fc_disc *disc = &lport->disc; @@ -696,19 +705,20 @@ void fc_disc_stop(struct fc_lport *lport)   * This function will block until discovery has been   * completely stopped and all rports have been deleted.   */ -void fc_disc_stop_final(struct fc_lport *lport) +static void fc_disc_stop_final(struct fc_lport *lport)  {  	fc_disc_stop(lport);  	lport->tt.rport_flush_queue();  }  /** - * fc_disc_init() - Initialize the discovery layer for a local port - * @lport: The local port that needs the discovery layer to be initialized + * fc_disc_config() - Configure the discovery layer for a local port + * @lport: The local port that needs the discovery layer to be configured + * @priv: Private data structre for users of the discovery layer   */ -int fc_disc_init(struct fc_lport *lport) +void fc_disc_config(struct fc_lport *lport, void *priv)  { -	struct fc_disc *disc; +	struct fc_disc *disc = &lport->disc;  	if (!lport->tt.disc_start)  		lport->tt.disc_start = fc_disc_start; @@ -723,12 +733,21 @@ int fc_disc_init(struct fc_lport *lport)  		lport->tt.disc_recv_req = fc_disc_recv_req;  	disc = &lport->disc; + +	disc->priv = priv; +} +EXPORT_SYMBOL(fc_disc_config); + +/** + * fc_disc_init() - Initialize the discovery layer for a local port + * @lport: The local port that needs the discovery layer to be initialized + */ +void fc_disc_init(struct fc_lport *lport) +{ +	struct fc_disc *disc = &lport->disc; +  	INIT_DELAYED_WORK(&disc->disc_work, fc_disc_timeout);  	mutex_init(&disc->disc_mutex);  	INIT_LIST_HEAD(&disc->rports); - -	disc->priv = lport; - -	return 0;  }  EXPORT_SYMBOL(fc_disc_init); diff --git a/drivers/scsi/libfc/fc_elsct.c b/drivers/scsi/libfc/fc_elsct.c index 9b25969e2ad..c2384d50147 100644 --- a/drivers/scsi/libfc/fc_elsct.c +++ b/drivers/scsi/libfc/fc_elsct.c @@ -21,12 +21,14 @@   * Provide interface to send ELS/CT FC frames   */ +#include <linux/export.h>  #include <asm/unaligned.h>  #include <scsi/fc/fc_gs.h>  #include <scsi/fc/fc_ns.h>  #include <scsi/fc/fc_els.h>  #include <scsi/libfc.h>  #include <scsi/fc_encode.h> +#include "fc_libfc.h"  /**   * fc_elsct_send() - Send an ELS or CT frame @@ -54,8 +56,7 @@ struct fc_seq *fc_elsct_send(struct fc_lport *lport, u32 did,  		rc = fc_els_fill(lport, did, fp, op, &r_ctl, &fh_type);  	else {  		/* CT requests */ -		rc = fc_ct_fill(lport, did, fp, op, &r_ctl, &fh_type); -		did = FC_FID_DIR_SERV; +		rc = fc_ct_fill(lport, did, fp, op, &r_ctl, &fh_type, &did);  	}  	if (rc) { diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index ec2a1aec235..1b3a0947345 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -26,6 +26,8 @@  #include <linux/timer.h>  #include <linux/slab.h>  #include <linux/err.h> +#include <linux/export.h> +#include <linux/log2.h>  #include <scsi/fc/fc_fc2.h> @@ -38,7 +40,7 @@ u16	fc_cpu_mask;		/* cpu mask for possible cpus */  EXPORT_SYMBOL(fc_cpu_mask);  static u16	fc_cpu_order;	/* 2's power to represent total possible cpus */  static struct kmem_cache *fc_em_cachep;	       /* cache for exchanges */ -struct workqueue_struct *fc_exch_workqueue; +static struct workqueue_struct *fc_exch_workqueue;  /*   * Structure and function definitions for managing Fibre Channel Exchanges @@ -65,11 +67,15 @@ struct workqueue_struct *fc_exch_workqueue;   * assigned range of exchanges to per cpu pool.   */  struct fc_exch_pool { -	u16		 next_index; -	u16		 total_exches;  	spinlock_t	 lock;  	struct list_head ex_list; -}; +	u16		 next_index; +	u16		 total_exches; + +	/* two cache of free slot in exch array */ +	u16		 left; +	u16		 right; +} ____cacheline_aligned_in_smp;  /**   * struct fc_exch_mgr - The Exchange Manager (EM). @@ -86,19 +92,14 @@ struct fc_exch_pool {   * It manages the allocation of exchange IDs.   */  struct fc_exch_mgr { +	struct fc_exch_pool __percpu *pool; +	mempool_t	*ep_pool;  	enum fc_class	class;  	struct kref	kref;  	u16		min_xid;  	u16		max_xid; -	mempool_t	*ep_pool;  	u16		pool_max_index; -	struct fc_exch_pool *pool; -	/* -	 * currently exchange mgr stats are updated but not used. -	 * either stats can be expose via sysfs or remove them -	 * all together if not used XXX -	 */  	struct {  		atomic_t no_free_exch;  		atomic_t no_free_exch_xid; @@ -108,7 +109,6 @@ struct fc_exch_mgr {  		atomic_t non_bls_resp;  	} stats;  }; -#define	fc_seq_exch(sp) container_of(sp, struct fc_exch, seq)  /**   * struct fc_exch_mgr_anchor - primary structure for list of EMs @@ -120,7 +120,7 @@ struct fc_exch_mgr {   * for each anchor to determine if that EM should be used. The last   * anchor in the list will always match to handle any exchanges not   * handled by other EMs. The non-default EMs would be added to the - * anchor list by HW that provides FCoE offloads. + * anchor list by HW that provides offloads.   */  struct fc_exch_mgr_anchor {  	struct list_head ema_list; @@ -304,10 +304,7 @@ static void fc_exch_setup_hdr(struct fc_exch *ep, struct fc_frame *fp,  		fr_eof(fp) = FC_EOF_N;  	} -	/* -	 * Initialize remainig fh fields -	 * from fc_fill_fc_hdr -	 */ +	/* Initialize remaining fh fields from fc_fill_fc_hdr */  	fh->fh_ox_id = htons(ep->oxid);  	fh->fh_rx_id = htons(ep->rxid);  	fh->fh_seq_id = ep->seq.id; @@ -335,8 +332,57 @@ static void fc_exch_release(struct fc_exch *ep)  }  /** + * fc_exch_timer_cancel() - cancel exch timer + * @ep:		The exchange whose timer to be canceled + */ +static inline void fc_exch_timer_cancel(struct fc_exch *ep) +{ +	if (cancel_delayed_work(&ep->timeout_work)) { +		FC_EXCH_DBG(ep, "Exchange timer canceled\n"); +		atomic_dec(&ep->ex_refcnt); /* drop hold for timer */ +	} +} + +/** + * fc_exch_timer_set_locked() - Start a timer for an exchange w/ the + *				the exchange lock held + * @ep:		The exchange whose timer will start + * @timer_msec: The timeout period + * + * Used for upper level protocols to time out the exchange. + * The timer is cancelled when it fires or when the exchange completes. + */ +static inline void fc_exch_timer_set_locked(struct fc_exch *ep, +					    unsigned int timer_msec) +{ +	if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE)) +		return; + +	FC_EXCH_DBG(ep, "Exchange timer armed : %d msecs\n", timer_msec); + +	fc_exch_hold(ep);		/* hold for timer */ +	if (!queue_delayed_work(fc_exch_workqueue, &ep->timeout_work, +				msecs_to_jiffies(timer_msec))) +		fc_exch_release(ep); +} + +/** + * fc_exch_timer_set() - Lock the exchange and set the timer + * @ep:		The exchange whose timer will start + * @timer_msec: The timeout period + */ +static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec) +{ +	spin_lock_bh(&ep->ex_lock); +	fc_exch_timer_set_locked(ep, timer_msec); +	spin_unlock_bh(&ep->ex_lock); +} + +/**   * fc_exch_done_locked() - Complete an exchange with the exchange lock held   * @ep: The exchange that is complete + * + * Note: May sleep if invoked from outside a response handler.   */  static int fc_exch_done_locked(struct fc_exch *ep)  { @@ -348,15 +394,13 @@ static int fc_exch_done_locked(struct fc_exch *ep)  	 * ep, and in that case we only clear the resp and set it as  	 * complete, so it can be reused by the timer to send the rrq.  	 */ -	ep->resp = NULL;  	if (ep->state & FC_EX_DONE)  		return rc;  	ep->esb_stat |= ESB_ST_COMPLETE;  	if (!(ep->esb_stat & ESB_ST_REC_QUAL)) {  		ep->state |= FC_EX_DONE; -		if (cancel_delayed_work(&ep->timeout_work)) -			atomic_dec(&ep->ex_refcnt); /* drop hold for timer */ +		fc_exch_timer_cancel(ep);  		rc = 0;  	}  	return rc; @@ -397,68 +441,45 @@ static inline void fc_exch_ptr_set(struct fc_exch_pool *pool, u16 index,  static void fc_exch_delete(struct fc_exch *ep)  {  	struct fc_exch_pool *pool; +	u16 index;  	pool = ep->pool;  	spin_lock_bh(&pool->lock);  	WARN_ON(pool->total_exches <= 0);  	pool->total_exches--; -	fc_exch_ptr_set(pool, (ep->xid - ep->em->min_xid) >> fc_cpu_order, -			NULL); + +	/* update cache of free slot */ +	index = (ep->xid - ep->em->min_xid) >> fc_cpu_order; +	if (pool->left == FC_XID_UNKNOWN) +		pool->left = index; +	else if (pool->right == FC_XID_UNKNOWN) +		pool->right = index; +	else +		pool->next_index = index; + +	fc_exch_ptr_set(pool, index, NULL);  	list_del(&ep->ex_list);  	spin_unlock_bh(&pool->lock);  	fc_exch_release(ep);	/* drop hold for exch in mp */  } -/** - * fc_exch_timer_set_locked() - Start a timer for an exchange w/ the - *				the exchange lock held - * @ep:		The exchange whose timer will start - * @timer_msec: The timeout period - * - * Used for upper level protocols to time out the exchange. - * The timer is cancelled when it fires or when the exchange completes. - */ -static inline void fc_exch_timer_set_locked(struct fc_exch *ep, -					    unsigned int timer_msec) -{ -	if (ep->state & (FC_EX_RST_CLEANUP | FC_EX_DONE)) -		return; - -	FC_EXCH_DBG(ep, "Exchange timer armed\n"); - -	if (queue_delayed_work(fc_exch_workqueue, &ep->timeout_work, -			       msecs_to_jiffies(timer_msec))) -		fc_exch_hold(ep);		/* hold for timer */ -} - -/** - * fc_exch_timer_set() - Lock the exchange and set the timer - * @ep:		The exchange whose timer will start - * @timer_msec: The timeout period - */ -static void fc_exch_timer_set(struct fc_exch *ep, unsigned int timer_msec) -{ -	spin_lock_bh(&ep->ex_lock); -	fc_exch_timer_set_locked(ep, timer_msec); -	spin_unlock_bh(&ep->ex_lock); -} - -/** - * fc_seq_send() - Send a frame using existing sequence/exchange pair - * @lport: The local port that the exchange will be sent on - * @sp:	   The sequence to be sent - * @fp:	   The frame to be sent on the exchange - */ -static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp, -		       struct fc_frame *fp) +static int fc_seq_send_locked(struct fc_lport *lport, struct fc_seq *sp, +			      struct fc_frame *fp)  {  	struct fc_exch *ep;  	struct fc_frame_header *fh = fc_frame_header_get(fp); -	int error; +	int error = -ENXIO;  	u32 f_ctl; +	u8 fh_type = fh->fh_type;  	ep = fc_seq_exch(sp); -	WARN_ON((ep->esb_stat & ESB_ST_SEQ_INIT) != ESB_ST_SEQ_INIT); + +	if (ep->esb_stat & (ESB_ST_COMPLETE | ESB_ST_ABNORMAL)) { +		fc_frame_free(fp); +		goto out; +	} + +	WARN_ON(!(ep->esb_stat & ESB_ST_SEQ_INIT));  	f_ctl = ntoh24(fh->fh_f_ctl);  	fc_exch_setup_hdr(ep, fp, f_ctl); @@ -480,15 +501,38 @@ static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp,  	 */  	error = lport->tt.frame_send(lport, fp); +	if (fh_type == FC_TYPE_BLS) +		goto out; +  	/*  	 * Update the exchange and sequence flags,  	 * assuming all frames for the sequence have been sent.  	 * We can only be called to send once for each sequence.  	 */ -	spin_lock_bh(&ep->ex_lock);  	ep->f_ctl = f_ctl & ~FC_FC_FIRST_SEQ;	/* not first seq */  	if (f_ctl & FC_FC_SEQ_INIT)  		ep->esb_stat &= ~ESB_ST_SEQ_INIT; +out: +	return error; +} + +/** + * fc_seq_send() - Send a frame using existing sequence/exchange pair + * @lport: The local port that the exchange will be sent on + * @sp:	   The sequence to be sent + * @fp:	   The frame to be sent on the exchange + * + * Note: The frame will be freed either by a direct call to fc_frame_free(fp) + * or indirectly by calling libfc_function_template.frame_send(). + */ +static int fc_seq_send(struct fc_lport *lport, struct fc_seq *sp, +		       struct fc_frame *fp) +{ +	struct fc_exch *ep; +	int error; +	ep = fc_seq_exch(sp); +	spin_lock_bh(&ep->ex_lock); +	error = fc_seq_send_locked(lport, sp, fp);  	spin_unlock_bh(&ep->ex_lock);  	return error;  } @@ -544,65 +588,167 @@ static struct fc_seq *fc_seq_start_next(struct fc_seq *sp)  	return sp;  } +/* + * Set the response handler for the exchange associated with a sequence. + * + * Note: May sleep if invoked from outside a response handler. + */ +static void fc_seq_set_resp(struct fc_seq *sp, +			    void (*resp)(struct fc_seq *, struct fc_frame *, +					 void *), +			    void *arg) +{ +	struct fc_exch *ep = fc_seq_exch(sp); +	DEFINE_WAIT(wait); + +	spin_lock_bh(&ep->ex_lock); +	while (ep->resp_active && ep->resp_task != current) { +		prepare_to_wait(&ep->resp_wq, &wait, TASK_UNINTERRUPTIBLE); +		spin_unlock_bh(&ep->ex_lock); + +		schedule(); + +		spin_lock_bh(&ep->ex_lock); +	} +	finish_wait(&ep->resp_wq, &wait); +	ep->resp = resp; +	ep->arg = arg; +	spin_unlock_bh(&ep->ex_lock); +} +  /** - * fc_seq_exch_abort() - Abort an exchange and sequence - * @req_sp:	The sequence to be aborted + * fc_exch_abort_locked() - Abort an exchange + * @ep:	The exchange to be aborted   * @timer_msec: The period of time to wait before aborting   * - * Generally called because of a timeout or an abort from the upper layer. + * Locking notes:  Called with exch lock held + * + * Return value: 0 on success else error code   */ -static int fc_seq_exch_abort(const struct fc_seq *req_sp, -			     unsigned int timer_msec) +static int fc_exch_abort_locked(struct fc_exch *ep, +				unsigned int timer_msec)  {  	struct fc_seq *sp; -	struct fc_exch *ep;  	struct fc_frame *fp;  	int error; -	ep = fc_seq_exch(req_sp); - -	spin_lock_bh(&ep->ex_lock);  	if (ep->esb_stat & (ESB_ST_COMPLETE | ESB_ST_ABNORMAL) || -	    ep->state & (FC_EX_DONE | FC_EX_RST_CLEANUP)) { -		spin_unlock_bh(&ep->ex_lock); +	    ep->state & (FC_EX_DONE | FC_EX_RST_CLEANUP))  		return -ENXIO; -	}  	/*  	 * Send the abort on a new sequence if possible.  	 */  	sp = fc_seq_start_next_locked(&ep->seq); -	if (!sp) { -		spin_unlock_bh(&ep->ex_lock); +	if (!sp)  		return -ENOMEM; -	} -	ep->esb_stat |= ESB_ST_SEQ_INIT | ESB_ST_ABNORMAL;  	if (timer_msec)  		fc_exch_timer_set_locked(ep, timer_msec); -	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; +	if (ep->sid) { +		/* +		 * Send an abort for the sequence that timed out. +		 */ +		fp = fc_frame_alloc(ep->lp, 0); +		if (fp) { +			ep->esb_stat |= ESB_ST_SEQ_INIT; +			fc_fill_fc_hdr(fp, FC_RCTL_BA_ABTS, ep->did, ep->sid, +				       FC_TYPE_BLS, FC_FC_END_SEQ | +				       FC_FC_SEQ_INIT, 0); +			error = fc_seq_send_locked(ep->lp, sp, fp); +		} else { +			error = -ENOBUFS; +		} +	} else { +		/* +		 * If not logged into the fabric, don't send ABTS but leave +		 * sequence active until next timeout. +		 */ +		error = 0; +	} +	ep->esb_stat |= ESB_ST_ABNORMAL; +	return error; +} -	/* -	 * Send an abort for the sequence that timed out. -	 */ -	fp = fc_frame_alloc(ep->lp, 0); -	if (fp) { -		fc_fill_fc_hdr(fp, FC_RCTL_BA_ABTS, ep->did, ep->sid, -			       FC_TYPE_BLS, FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); -		error = fc_seq_send(ep->lp, sp, fp); -	} else -		error = -ENOBUFS; +/** + * fc_seq_exch_abort() - Abort an exchange and sequence + * @req_sp:	The sequence to be aborted + * @timer_msec: The period of time to wait before aborting + * + * Generally called because of a timeout or an abort from the upper layer. + * + * Return value: 0 on success else error code + */ +static int fc_seq_exch_abort(const struct fc_seq *req_sp, +			     unsigned int timer_msec) +{ +	struct fc_exch *ep; +	int error; + +	ep = fc_seq_exch(req_sp); +	spin_lock_bh(&ep->ex_lock); +	error = fc_exch_abort_locked(ep, timer_msec); +	spin_unlock_bh(&ep->ex_lock);  	return error;  }  /** + * fc_invoke_resp() - invoke ep->resp() + * + * Notes: + * It is assumed that after initialization finished (this means the + * first unlock of ex_lock after fc_exch_alloc()) ep->resp and ep->arg are + * modified only via fc_seq_set_resp(). This guarantees that none of these + * two variables changes if ep->resp_active > 0. + * + * If an fc_seq_set_resp() call is busy modifying ep->resp and ep->arg when + * this function is invoked, the first spin_lock_bh() call in this function + * will wait until fc_seq_set_resp() has finished modifying these variables. + * + * Since fc_exch_done() invokes fc_seq_set_resp() it is guaranteed that that + * ep->resp() won't be invoked after fc_exch_done() has returned. + * + * The response handler itself may invoke fc_exch_done(), which will clear the + * ep->resp pointer. + * + * Return value: + * Returns true if and only if ep->resp has been invoked. + */ +static bool fc_invoke_resp(struct fc_exch *ep, struct fc_seq *sp, +			   struct fc_frame *fp) +{ +	void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg); +	void *arg; +	bool res = false; + +	spin_lock_bh(&ep->ex_lock); +	ep->resp_active++; +	if (ep->resp_task != current) +		ep->resp_task = !ep->resp_task ? current : NULL; +	resp = ep->resp; +	arg = ep->arg; +	spin_unlock_bh(&ep->ex_lock); + +	if (resp) { +		resp(sp, fp, arg); +		res = true; +	} else if (!IS_ERR(fp)) { +		fc_frame_free(fp); +	} + +	spin_lock_bh(&ep->ex_lock); +	if (--ep->resp_active == 0) +		ep->resp_task = NULL; +	spin_unlock_bh(&ep->ex_lock); + +	if (ep->resp_active == 0) +		wake_up(&ep->resp_wq); + +	return res; +} + +/**   * fc_exch_timeout() - Handle exchange timer expiration   * @work: The work_struct identifying the exchange that timed out   */ @@ -611,8 +757,6 @@ static void fc_exch_timeout(struct work_struct *work)  	struct fc_exch *ep = container_of(work, struct fc_exch,  					  timeout_work.work);  	struct fc_seq *sp = &ep->seq; -	void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg); -	void *arg;  	u32 e_stat;  	int rc = 1; @@ -630,16 +774,13 @@ static void fc_exch_timeout(struct work_struct *work)  			fc_exch_rrq(ep);  		goto done;  	} else { -		resp = ep->resp; -		arg = ep->arg; -		ep->resp = NULL;  		if (e_stat & ESB_ST_ABNORMAL)  			rc = fc_exch_done_locked(ep);  		spin_unlock_bh(&ep->ex_lock);  		if (!rc)  			fc_exch_delete(ep); -		if (resp) -			resp(sp, ERR_PTR(-FC_EX_TIMEOUT), arg); +		fc_invoke_resp(ep, sp, ERR_PTR(-FC_EX_TIMEOUT)); +		fc_seq_set_resp(sp, NULL, ep->arg);  		fc_seq_exch_abort(sp, 2 * ep->r_a_tov);  		goto done;  	} @@ -679,6 +820,19 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport,  	pool = per_cpu_ptr(mp->pool, cpu);  	spin_lock_bh(&pool->lock);  	put_cpu(); + +	/* peek cache of free slot */ +	if (pool->left != FC_XID_UNKNOWN) { +		index = pool->left; +		pool->left = FC_XID_UNKNOWN; +		goto hit; +	} +	if (pool->right != FC_XID_UNKNOWN) { +		index = pool->right; +		pool->right = FC_XID_UNKNOWN; +		goto hit; +	} +  	index = pool->next_index;  	/* allocate new exch from pool */  	while (fc_exch_ptr_get(pool, index)) { @@ -687,7 +841,7 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport,  			goto err;  	}  	pool->next_index = index == mp->pool_max_index ? 0 : index + 1; - +hit:  	fc_exch_hold(ep);	/* hold for exch in mp */  	spin_lock_init(&ep->ex_lock);  	/* @@ -713,6 +867,8 @@ static struct fc_exch *fc_exch_em_alloc(struct fc_lport *lport,  	ep->f_ctl = FC_FC_FIRST_SEQ;	/* next seq is first seq */  	ep->rxid = FC_XID_UNKNOWN;  	ep->class = mp->class; +	ep->resp_active = 0; +	init_waitqueue_head(&ep->resp_wq);  	INIT_DELAYED_WORK(&ep->timeout_work, fc_exch_timeout);  out:  	return ep; @@ -760,8 +916,8 @@ static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)  		spin_lock_bh(&pool->lock);  		ep = fc_exch_ptr_get(pool, (xid - mp->min_xid) >> fc_cpu_order);  		if (ep) { -			fc_exch_hold(ep);  			WARN_ON(ep->xid != xid); +			fc_exch_hold(ep);  		}  		spin_unlock_bh(&pool->lock);  	} @@ -773,6 +929,8 @@ static struct fc_exch *fc_exch_find(struct fc_exch_mgr *mp, u16 xid)   * fc_exch_done() - Indicate that an exchange/sequence tuple is complete and   *		    the memory allocated for the related objects may be freed.   * @sp: The sequence that has completed + * + * Note: May sleep if invoked from outside a response handler.   */  static void fc_exch_done(struct fc_seq *sp)  { @@ -782,6 +940,8 @@ static void fc_exch_done(struct fc_seq *sp)  	spin_lock_bh(&ep->ex_lock);  	rc = fc_exch_done_locked(ep);  	spin_unlock_bh(&ep->ex_lock); + +	fc_seq_set_resp(sp, NULL, ep->arg);  	if (!rc)  		fc_exch_delete(ep);  } @@ -910,6 +1070,7 @@ static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_lport *lport,  		}  	} +	spin_lock_bh(&ep->ex_lock);  	/*  	 * At this point, we have the exchange held.  	 * Find or create the sequence. @@ -922,14 +1083,37 @@ static enum fc_pf_rjt_reason fc_seq_lookup_recip(struct fc_lport *lport,  		sp = &ep->seq;  		if (sp->id != fh->fh_seq_id) {  			atomic_inc(&mp->stats.seq_not_found); -			reject = FC_RJT_SEQ_ID;	/* sequence/exch should exist */ -			goto rel; +			if (f_ctl & FC_FC_END_SEQ) { +				/* +				 * Update sequence_id based on incoming last +				 * frame of sequence exchange. This is needed +				 * for FC target where DDP has been used +				 * on target where, stack is indicated only +				 * about last frame's (payload _header) header. +				 * Whereas "seq_id" which is part of +				 * frame_header is allocated by initiator +				 * which is totally different from "seq_id" +				 * allocated when XFER_RDY was sent by target. +				 * To avoid false -ve which results into not +				 * sending RSP, hence write request on other +				 * end never finishes. +				 */ +				sp->ssb_stat |= SSB_ST_RESP; +				sp->id = fh->fh_seq_id; +			} else { +				spin_unlock_bh(&ep->ex_lock); + +				/* sequence/exch should exist */ +				reject = FC_RJT_SEQ_ID; +				goto rel; +			}  		}  	}  	WARN_ON(ep != fc_seq_exch(sp));  	if (f_ctl & FC_FC_SEQ_INIT)  		ep->esb_stat |= ESB_ST_SEQ_INIT; +	spin_unlock_bh(&ep->ex_lock);  	fr_seq(fp) = sp;  out: @@ -999,7 +1183,7 @@ static void fc_exch_set_addr(struct fc_exch *ep,  }  /** - * fc_seq_els_rsp_send() - Send an ELS response using infomation from + * fc_seq_els_rsp_send() - Send an ELS response using information from   *			   the existing sequence/exchange.   * @fp:	      The received frame   * @els_cmd:  The ELS command to be sent @@ -1044,7 +1228,7 @@ static void fc_seq_send_last(struct fc_seq *sp, struct fc_frame *fp,  	f_ctl = FC_FC_LAST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT;  	f_ctl |= ep->f_ctl;  	fc_fill_fc_hdr(fp, rctl, ep->did, ep->sid, fh_type, f_ctl, 0); -	fc_seq_send(ep->lp, sp, fp); +	fc_seq_send_locked(ep->lp, sp, fp);  }  /** @@ -1110,7 +1294,7 @@ static void fc_seq_send_ack(struct fc_seq *sp, const struct fc_frame *rx_fp)   * fc_exch_send_ba_rjt() - Send BLS Reject   * @rx_fp:  The frame being rejected   * @reason: The reason the frame is being rejected - * @explan: The explaination for the rejection + * @explan: The explanation for the rejection   *   * This is for rejecting BA_ABTS only.   */ @@ -1192,21 +1376,23 @@ static void fc_exch_recv_abts(struct fc_exch *ep, struct fc_frame *rx_fp)  	if (!ep)  		goto reject; + +	fp = fc_frame_alloc(ep->lp, sizeof(*ap)); +	if (!fp) +		goto free; +  	spin_lock_bh(&ep->ex_lock);  	if (ep->esb_stat & ESB_ST_COMPLETE) {  		spin_unlock_bh(&ep->ex_lock); + +		fc_frame_free(fp);  		goto reject;  	} -	if (!(ep->esb_stat & ESB_ST_REC_QUAL)) +	if (!(ep->esb_stat & ESB_ST_REC_QUAL)) { +		ep->esb_stat |= ESB_ST_REC_QUAL;  		fc_exch_hold(ep);		/* hold for REC_QUAL */ -	ep->esb_stat |= ESB_ST_ABNORMAL | ESB_ST_REC_QUAL; -	fc_exch_timer_set_locked(ep, ep->r_a_tov); - -	fp = fc_frame_alloc(ep->lp, sizeof(*ap)); -	if (!fp) { -		spin_unlock_bh(&ep->ex_lock); -		goto free;  	} +	fc_exch_timer_set_locked(ep, ep->r_a_tov);  	fh = fc_frame_header_get(fp);  	ap = fc_frame_payload_get(fp, sizeof(*ap));  	memset(ap, 0, sizeof(*ap)); @@ -1219,15 +1405,17 @@ static void fc_exch_recv_abts(struct fc_exch *ep, struct fc_frame *rx_fp)  		ap->ba_low_seq_cnt = htons(sp->cnt);  	}  	sp = fc_seq_start_next_locked(sp); -	spin_unlock_bh(&ep->ex_lock);  	fc_seq_send_last(sp, fp, FC_RCTL_BA_ACC, FC_TYPE_BLS); +	ep->esb_stat |= ESB_ST_ABNORMAL; +	spin_unlock_bh(&ep->ex_lock); + +free:  	fc_frame_free(rx_fp);  	return;  reject:  	fc_exch_send_ba_rjt(rx_fp, FC_BA_RJT_UNABLE, FC_BA_RJT_INV_XID); -free: -	fc_frame_free(rx_fp); +	goto free;  }  /** @@ -1236,6 +1424,8 @@ free:   * @fp:    The request frame   *   * On success, the sequence pointer will be returned and also in fr_seq(@fp). + * A reference will be held on the exchange/sequence for the caller, which + * must call fc_seq_release().   */  static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp)  { @@ -1247,12 +1437,21 @@ static struct fc_seq *fc_seq_assign(struct fc_lport *lport, struct fc_frame *fp)  	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) +		    fc_seq_lookup_recip(lport, ema->mp, fp) == FC_RJT_NONE)  			break;  	return fr_seq(fp);  }  /** + * fc_seq_release() - Release the hold + * @sp:    The sequence. + */ +static void fc_seq_release(struct fc_seq *sp) +{ +	fc_exch_release(fc_seq_exch(sp)); +} + +/**   * 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 @@ -1306,9 +1505,7 @@ static void fc_exch_recv_req(struct fc_lport *lport, struct fc_exch_mgr *mp,  		 * If new exch resp handler is valid then call that  		 * first.  		 */ -		if (ep->resp) -			ep->resp(sp, fp, ep->arg); -		else +		if (!fc_invoke_resp(ep, sp, fp))  			lport->tt.lport_recv(lport, fp);  		fc_exch_release(ep);	/* release from lookup */  	} else { @@ -1332,8 +1529,6 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)  	struct fc_exch *ep;  	enum fc_sof sof;  	u32 f_ctl; -	void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg); -	void *ex_resp_arg;  	int rc;  	ep = fc_exch_find(mp, ntohs(fh->fh_ox_id)); @@ -1343,7 +1538,7 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)  	}  	if (ep->esb_stat & ESB_ST_COMPLETE) {  		atomic_inc(&mp->stats.xid_not_found); -		goto out; +		goto rel;  	}  	if (ep->rxid == FC_XID_UNKNOWN)  		ep->rxid = ntohs(fh->fh_rx_id); @@ -1368,13 +1563,14 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)  	f_ctl = ntoh24(fh->fh_f_ctl);  	fr_seq(fp) = sp; + +	spin_lock_bh(&ep->ex_lock);  	if (f_ctl & FC_FC_SEQ_INIT)  		ep->esb_stat |= ESB_ST_SEQ_INIT; +	spin_unlock_bh(&ep->ex_lock);  	if (fc_sof_needs_ack(sof))  		fc_seq_send_ack(sp, fp); -	resp = ep->resp; -	ex_resp_arg = ep->arg;  	if (fh->fh_type != FC_TYPE_FCP && fr_eof(fp) == FC_EOF_T &&  	    (f_ctl & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) == @@ -1400,10 +1596,8 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)  	 * If new exch resp handler is valid then call that  	 * first.  	 */ -	if (resp) -		resp(sp, fp, ex_resp_arg); -	else -		fc_frame_free(fp); +	fc_invoke_resp(ep, sp, fp); +  	fc_exch_release(ep);  	return;  rel: @@ -1442,8 +1636,6 @@ static void fc_exch_recv_resp(struct fc_exch_mgr *mp, struct fc_frame *fp)   */  static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)  { -	void (*resp)(struct fc_seq *, struct fc_frame *fp, void *arg); -	void *ex_resp_arg;  	struct fc_frame_header *fh;  	struct fc_ba_acc *ap;  	struct fc_seq *sp; @@ -1455,8 +1647,10 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)  	FC_EXCH_DBG(ep, "exch: BLS rctl %x - %s\n", fh->fh_r_ctl,  		    fc_exch_rctl_name(fh->fh_r_ctl)); -	if (cancel_delayed_work_sync(&ep->timeout_work)) +	if (cancel_delayed_work_sync(&ep->timeout_work)) { +		FC_EXCH_DBG(ep, "Exchange timer canceled due to ABTS response\n");  		fc_exch_release(ep);	/* release from pending timer hold */ +	}  	spin_lock_bh(&ep->ex_lock);  	switch (fh->fh_r_ctl) { @@ -1486,9 +1680,6 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)  		break;  	} -	resp = ep->resp; -	ex_resp_arg = ep->arg; -  	/* do we need to do some other checks here. Can we reuse more of  	 * fc_exch_recv_seq_resp  	 */ @@ -1500,17 +1691,14 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp)  	    ntoh24(fh->fh_f_ctl) & FC_FC_LAST_SEQ)  		rc = fc_exch_done_locked(ep);  	spin_unlock_bh(&ep->ex_lock); + +	fc_exch_hold(ep);  	if (!rc)  		fc_exch_delete(ep); - -	if (resp) -		resp(sp, fp, ex_resp_arg); -	else -		fc_frame_free(fp); - +	fc_invoke_resp(ep, sp, fp);  	if (has_rec)  		fc_exch_timer_set(ep, ep->r_a_tov); - +	fc_exch_release(ep);  }  /** @@ -1548,9 +1736,10 @@ static void fc_exch_recv_bls(struct fc_exch_mgr *mp, struct fc_frame *fp)  		case FC_RCTL_ACK_0:  			break;  		default: -			FC_EXCH_DBG(ep, "BLS rctl %x - %s received", -				    fh->fh_r_ctl, -				    fc_exch_rctl_name(fh->fh_r_ctl)); +			if (ep) +				FC_EXCH_DBG(ep, "BLS rctl %x - %s received\n", +					    fh->fh_r_ctl, +					    fc_exch_rctl_name(fh->fh_r_ctl));  			break;  		}  		fc_frame_free(fp); @@ -1631,32 +1820,33 @@ static void fc_seq_ls_rjt(struct fc_frame *rx_fp, enum fc_els_rjt_reason reason,  /**   * fc_exch_reset() - Reset an exchange   * @ep: The exchange to be reset + * + * Note: May sleep if invoked from outside a response handler.   */  static void fc_exch_reset(struct fc_exch *ep)  {  	struct fc_seq *sp; -	void (*resp)(struct fc_seq *, struct fc_frame *, void *); -	void *arg;  	int rc = 1;  	spin_lock_bh(&ep->ex_lock); +	fc_exch_abort_locked(ep, 0);  	ep->state |= FC_EX_RST_CLEANUP; -	if (cancel_delayed_work(&ep->timeout_work)) -		atomic_dec(&ep->ex_refcnt);	/* drop hold for timer */ -	resp = ep->resp; -	ep->resp = NULL; +	fc_exch_timer_cancel(ep);  	if (ep->esb_stat & ESB_ST_REC_QUAL)  		atomic_dec(&ep->ex_refcnt);	/* drop hold for rec_qual */  	ep->esb_stat &= ~ESB_ST_REC_QUAL; -	arg = ep->arg;  	sp = &ep->seq;  	rc = fc_exch_done_locked(ep);  	spin_unlock_bh(&ep->ex_lock); + +	fc_exch_hold(ep); +  	if (!rc)  		fc_exch_delete(ep); -	if (resp) -		resp(sp, ERR_PTR(-FC_EX_CLOSED), arg); +	fc_invoke_resp(ep, sp, ERR_PTR(-FC_EX_CLOSED)); +	fc_seq_set_resp(sp, NULL, ep->arg); +	fc_exch_release(ep);  }  /** @@ -1699,6 +1889,9 @@ restart:  			goto restart;  		}  	} +	pool->next_index = 0; +	pool->left = FC_XID_UNKNOWN; +	pool->right = FC_XID_UNKNOWN;  	spin_unlock_bh(&pool->lock);  } @@ -1839,13 +2032,13 @@ static void fc_exch_rrq_resp(struct fc_seq *sp, struct fc_frame *fp, void *arg)  	switch (op) {  	case ELS_LS_RJT: -		FC_EXCH_DBG(aborted_ep, "LS_RJT for RRQ"); +		FC_EXCH_DBG(aborted_ep, "LS_RJT for RRQ\n");  		/* fall through */  	case ELS_LS_ACC:  		goto cleanup;  	default: -		FC_EXCH_DBG(aborted_ep, "unexpected response op %x " -			    "for RRQ", op); +		FC_EXCH_DBG(aborted_ep, "unexpected response op %x for RRQ\n", +			    op);  		return;  	} @@ -1887,6 +2080,7 @@ static struct fc_seq *fc_exch_seq_send(struct fc_lport *lport,  	struct fc_exch *ep;  	struct fc_seq *sp = NULL;  	struct fc_frame_header *fh; +	struct fc_fcp_pkt *fsp = NULL;  	int rc = 1;  	ep = fc_exch_alloc(lport, fp); @@ -1909,8 +2103,10 @@ static struct fc_seq *fc_exch_seq_send(struct fc_lport *lport,  	fc_exch_setup_hdr(ep, fp, ep->f_ctl);  	sp->cnt++; -	if (ep->xid <= lport->lro_xid && fh->fh_r_ctl == FC_RCTL_DD_UNSOL_CMD) +	if (ep->xid <= lport->lro_xid && fh->fh_r_ctl == FC_RCTL_DD_UNSOL_CMD) { +		fsp = fr_fsp(fp);  		fc_fcp_ddp_setup(fr_fsp(fp), ep->xid); +	}  	if (unlikely(lport->tt.frame_send(lport, fp)))  		goto err; @@ -1924,6 +2120,8 @@ static struct fc_seq *fc_exch_seq_send(struct fc_lport *lport,  	spin_unlock_bh(&ep->ex_lock);  	return sp;  err: +	if (fsp) +		fc_fcp_ddp_done(fsp);  	rc = fc_exch_done_locked(ep);  	spin_unlock_bh(&ep->ex_lock);  	if (!rc) @@ -2029,10 +2227,8 @@ static void fc_exch_els_rrq(struct fc_frame *fp)  		ep->esb_stat &= ~ESB_ST_REC_QUAL;  		atomic_dec(&ep->ex_refcnt);	/* drop hold for rec qual */  	} -	if (ep->esb_stat & ESB_ST_COMPLETE) { -		if (cancel_delayed_work(&ep->timeout_work)) -			atomic_dec(&ep->ex_refcnt);	/* drop timer hold */ -	} +	if (ep->esb_stat & ESB_ST_COMPLETE) +		fc_exch_timer_cancel(ep);  	spin_unlock_bh(&ep->ex_lock); @@ -2052,6 +2248,31 @@ out:  }  /** + * fc_exch_update_stats() - update exches stats to lport + * @lport: The local port to update exchange manager stats + */ +void fc_exch_update_stats(struct fc_lport *lport) +{ +	struct fc_host_statistics *st; +	struct fc_exch_mgr_anchor *ema; +	struct fc_exch_mgr *mp; + +	st = &lport->host_stats; + +	list_for_each_entry(ema, &lport->ema_list, ema_list) { +		mp = ema->mp; +		st->fc_no_free_exch += atomic_read(&mp->stats.no_free_exch); +		st->fc_no_free_exch_xid += +				atomic_read(&mp->stats.no_free_exch_xid); +		st->fc_xid_not_found += atomic_read(&mp->stats.xid_not_found); +		st->fc_xid_busy += atomic_read(&mp->stats.xid_busy); +		st->fc_seq_not_found += atomic_read(&mp->stats.seq_not_found); +		st->fc_non_bls_resp += atomic_read(&mp->stats.non_bls_resp); +	} +} +EXPORT_SYMBOL(fc_exch_update_stats); + +/**   * fc_exch_mgr_add() - Add an exchange manager to a local port's list of EMs   * @lport: The local port to add the exchange manager to   * @mp:	   The exchange manager to be added to the local port @@ -2121,6 +2342,7 @@ err:  		fc_exch_mgr_del(ema);  	return -ENOMEM;  } +EXPORT_SYMBOL(fc_exch_mgr_list_clone);  /**   * fc_exch_mgr_alloc() - Allocate an exchange manager @@ -2158,7 +2380,18 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport,  	mp->class = class;  	/* adjust em exch xid range for offload */  	mp->min_xid = min_xid; -	mp->max_xid = max_xid; + +       /* reduce range so per cpu pool fits into PCPU_MIN_UNIT_SIZE pool */ +	pool_exch_range = (PCPU_MIN_UNIT_SIZE - sizeof(*pool)) / +		sizeof(struct fc_exch *); +	if ((max_xid - min_xid + 1) / (fc_cpu_mask + 1) > pool_exch_range) { +		mp->max_xid = pool_exch_range * (fc_cpu_mask + 1) + +			min_xid - 1; +	} else { +		mp->max_xid = max_xid; +		pool_exch_range = (mp->max_xid - mp->min_xid + 1) / +			(fc_cpu_mask + 1); +	}  	mp->ep_pool = mempool_create_slab_pool(2, fc_em_cachep);  	if (!mp->ep_pool) @@ -2169,7 +2402,6 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport,  	 * divided across all cpus. The exch pointers array memory is  	 * allocated for exch range per pool.  	 */ -	pool_exch_range = (mp->max_xid - mp->min_xid + 1) / (fc_cpu_mask + 1);  	mp->pool_max_index = pool_exch_range - 1;  	/* @@ -2181,6 +2413,9 @@ struct fc_exch_mgr *fc_exch_mgr_alloc(struct fc_lport *lport,  		goto free_mempool;  	for_each_possible_cpu(cpu) {  		pool = per_cpu_ptr(mp->pool, cpu); +		pool->next_index = 0; +		pool->left = FC_XID_UNKNOWN; +		pool->right = FC_XID_UNKNOWN;  		spin_lock_init(&pool->lock);  		INIT_LIST_HEAD(&pool->ex_list);  	} @@ -2222,16 +2457,45 @@ void fc_exch_mgr_free(struct fc_lport *lport)  EXPORT_SYMBOL(fc_exch_mgr_free);  /** + * fc_find_ema() - Lookup and return appropriate Exchange Manager Anchor depending + * upon 'xid'. + * @f_ctl: f_ctl + * @lport: The local port the frame was received on + * @fh: The received frame header + */ +static struct fc_exch_mgr_anchor *fc_find_ema(u32 f_ctl, +					      struct fc_lport *lport, +					      struct fc_frame_header *fh) +{ +	struct fc_exch_mgr_anchor *ema; +	u16 xid; + +	if (f_ctl & FC_FC_EX_CTX) +		xid = ntohs(fh->fh_ox_id); +	else { +		xid = ntohs(fh->fh_rx_id); +		if (xid == FC_XID_UNKNOWN) +			return list_entry(lport->ema_list.prev, +					  typeof(*ema), ema_list); +	} + +	list_for_each_entry(ema, &lport->ema_list, ema_list) { +		if ((xid >= ema->mp->min_xid) && +		    (xid <= ema->mp->max_xid)) +			return ema; +	} +	return NULL; +} +/**   * fc_exch_recv() - Handler for received frames   * @lport: The local port the frame was received on - * @fp:	   The received frame + * @fp:	The received frame   */  void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp)  {  	struct fc_frame_header *fh = fc_frame_header_get(fp);  	struct fc_exch_mgr_anchor *ema; -	u32 f_ctl, found = 0; -	u16 oxid; +	u32 f_ctl;  	/* lport lock ? */  	if (!lport || lport->state == LPORT_ST_DISABLED) { @@ -2242,24 +2506,17 @@ void fc_exch_recv(struct fc_lport *lport, struct fc_frame *fp)  	}  	f_ctl = ntoh24(fh->fh_f_ctl); -	oxid = ntohs(fh->fh_ox_id); -	if (f_ctl & FC_FC_EX_CTX) { -		list_for_each_entry(ema, &lport->ema_list, ema_list) { -			if ((oxid >= ema->mp->min_xid) && -			    (oxid <= ema->mp->max_xid)) { -				found = 1; -				break; -			} -		} - -		if (!found) { -			FC_LPORT_DBG(lport, "Received response for out " -				     "of range oxid:%hx\n", oxid); -			fc_frame_free(fp); -			return; -		} -	} else -		ema = list_entry(lport->ema_list.prev, typeof(*ema), ema_list); +	ema = fc_find_ema(f_ctl, lport, fh); +	if (!ema) { +		FC_LPORT_DBG(lport, "Unable to find Exchange Manager Anchor," +				    "fc_ctl <0x%x>, xid <0x%x>\n", +				     f_ctl, +				     (f_ctl & FC_FC_EX_CTX) ? +				     ntohs(fh->fh_ox_id) : +				     ntohs(fh->fh_rx_id)); +		fc_frame_free(fp); +		return; +	}  	/*  	 * If frame is marked invalid, just drop it. @@ -2297,6 +2554,9 @@ int fc_exch_init(struct fc_lport *lport)  	if (!lport->tt.seq_start_next)  		lport->tt.seq_start_next = fc_seq_start_next; +	if (!lport->tt.seq_set_resp) +		lport->tt.seq_set_resp = fc_seq_set_resp; +  	if (!lport->tt.exch_seq_send)  		lport->tt.exch_seq_send = fc_exch_seq_send; @@ -2318,6 +2578,9 @@ int fc_exch_init(struct fc_lport *lport)  	if (!lport->tt.seq_assign)  		lport->tt.seq_assign = fc_seq_assign; +	if (!lport->tt.seq_release) +		lport->tt.seq_release = fc_seq_release; +  	return 0;  }  EXPORT_SYMBOL(fc_exch_init); @@ -2325,7 +2588,7 @@ EXPORT_SYMBOL(fc_exch_init);  /**   * fc_setup_exch_mgr() - Setup an exchange manager   */ -int fc_setup_exch_mgr() +int fc_setup_exch_mgr(void)  {  	fc_em_cachep = kmem_cache_create("libfc_em", sizeof(struct fc_exch),  					 0, SLAB_HWCACHE_ALIGN, NULL); @@ -2346,24 +2609,22 @@ int fc_setup_exch_mgr()  	 * cpu on which exchange originated by simple bitwise  	 * AND operation between fc_cpu_mask and exchange id.  	 */ -	fc_cpu_mask = 1; -	fc_cpu_order = 0; -	while (fc_cpu_mask < nr_cpu_ids) { -		fc_cpu_mask <<= 1; -		fc_cpu_order++; -	} -	fc_cpu_mask--; +	fc_cpu_order = ilog2(roundup_pow_of_two(nr_cpu_ids)); +	fc_cpu_mask = (1 << fc_cpu_order) - 1;  	fc_exch_workqueue = create_singlethread_workqueue("fc_exch_workqueue");  	if (!fc_exch_workqueue) -		return -ENOMEM; +		goto err;  	return 0; +err: +	kmem_cache_destroy(fc_em_cachep); +	return -ENOMEM;  }  /**   * fc_destroy_exch_mgr() - Destroy an exchange manager   */ -void fc_destroy_exch_mgr() +void fc_destroy_exch_mgr(void)  {  	destroy_workqueue(fc_exch_workqueue);  	kmem_cache_destroy(fc_em_cachep); diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c index e340373b509..1d7e76e8b44 100644 --- a/drivers/scsi/libfc/fc_fcp.c +++ b/drivers/scsi/libfc/fc_fcp.c @@ -42,7 +42,7 @@  #include "fc_libfc.h" -struct kmem_cache *scsi_pkt_cachep; +static struct kmem_cache *scsi_pkt_cachep;  /* SRB state definitions */  #define FC_SRB_FREE		0		/* cmd is free */ @@ -96,7 +96,7 @@ static void fc_fcp_resp(struct fc_fcp_pkt *, struct fc_frame *);  static void fc_fcp_complete_locked(struct fc_fcp_pkt *);  static void fc_tm_done(struct fc_seq *, struct fc_frame *, void *);  static void fc_fcp_error(struct fc_fcp_pkt *, struct fc_frame *); -static void fc_fcp_recovery(struct fc_fcp_pkt *); +static void fc_fcp_recovery(struct fc_fcp_pkt *, u8 code);  static void fc_fcp_timeout(unsigned long);  static void fc_fcp_rec(struct fc_fcp_pkt *);  static void fc_fcp_rec_error(struct fc_fcp_pkt *, struct fc_frame *); @@ -120,14 +120,13 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *, struct fc_frame *);  #define FC_DATA_UNDRUN		7  #define FC_ERROR		8  #define FC_HRD_ERROR		9 -#define FC_CMD_RECOVERY		10 +#define FC_CRC_ERROR		10 +#define FC_TIMED_OUT		11  /*   * Error recovery timeout values.   */ -#define FC_SCSI_ER_TIMEOUT	(10 * HZ)  #define FC_SCSI_TM_TOV		(10 * HZ) -#define FC_SCSI_REC_TOV		(2 * HZ)  #define FC_HOST_RESET_TIMEOUT	(30 * HZ)  #define FC_CAN_QUEUE_PERIOD	(60 * HZ) @@ -153,10 +152,15 @@ static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lport, gfp_t gfp)  	if (fsp) {  		memset(fsp, 0, sizeof(*fsp));  		fsp->lp = lport; +		fsp->xfer_ddp = FC_XID_UNKNOWN;  		atomic_set(&fsp->ref_cnt, 1);  		init_timer(&fsp->timer); +		fsp->timer.data = (unsigned long)fsp;  		INIT_LIST_HEAD(&fsp->list);  		spin_lock_init(&fsp->scsi_pkt_lock); +	} else { +		per_cpu_ptr(lport->stats, get_cpu())->FcpPktAllocFails++; +		put_cpu();  	}  	return fsp;  } @@ -245,7 +249,7 @@ static inline void fc_fcp_unlock_pkt(struct fc_fcp_pkt *fsp)  /**   * fc_fcp_timer_set() - Start a timer for a fcp_pkt   * @fsp:   The FCP packet to start a timer for - * @delay: The timeout period for the timer + * @delay: The timeout period in jiffies   */  static void fc_fcp_timer_set(struct fc_fcp_pkt *fsp, unsigned long delay)  { @@ -263,6 +267,9 @@ static int fc_fcp_send_abort(struct fc_fcp_pkt *fsp)  	if (!fsp->seq_ptr)  		return -EINVAL; +	per_cpu_ptr(fsp->lp->stats, get_cpu())->FcpPktAborts++; +	put_cpu(); +  	fsp->state |= FC_SRB_ABORT_PENDING;  	return fsp->lp->tt.seq_exch_abort(fsp->seq_ptr, 0);  } @@ -312,7 +319,7 @@ void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid)   *		       DDP related resources for a fcp_pkt   * @fsp: The FCP packet that DDP had been used on   */ -static void fc_fcp_ddp_done(struct fc_fcp_pkt *fsp) +void fc_fcp_ddp_done(struct fc_fcp_pkt *fsp)  {  	struct fc_lport *lport; @@ -332,22 +339,23 @@ static void fc_fcp_ddp_done(struct fc_fcp_pkt *fsp)  /**   * fc_fcp_can_queue_ramp_up() - increases can_queue   * @lport: lport to ramp up can_queue - * - * Locking notes: Called with Scsi_Host lock held   */  static void fc_fcp_can_queue_ramp_up(struct fc_lport *lport)  {  	struct fc_fcp_internal *si = fc_get_scsi_internal(lport); +	unsigned long flags;  	int can_queue; +	spin_lock_irqsave(lport->host->host_lock, flags); +  	if (si->last_can_queue_ramp_up_time &&  	    (time_before(jiffies, si->last_can_queue_ramp_up_time +  			 FC_CAN_QUEUE_PERIOD))) -		return; +		goto unlock;  	if (time_before(jiffies, si->last_can_queue_ramp_down_time +  			FC_CAN_QUEUE_PERIOD)) -		return; +		goto unlock;  	si->last_can_queue_ramp_up_time = jiffies; @@ -359,6 +367,9 @@ static void fc_fcp_can_queue_ramp_up(struct fc_lport *lport)  	lport->host->can_queue = can_queue;  	shost_printk(KERN_ERR, lport->host, "libfc: increased "  		     "can_queue to %d.\n", can_queue); + +unlock: +	spin_unlock_irqrestore(lport->host->host_lock, flags);  }  /** @@ -370,18 +381,19 @@ static void fc_fcp_can_queue_ramp_up(struct fc_lport *lport)   * commands complete or timeout, then try again with a reduced   * can_queue. Eventually we will hit the point where we run   * on all reserved structs. - * - * Locking notes: Called with Scsi_Host lock held   */  static void fc_fcp_can_queue_ramp_down(struct fc_lport *lport)  {  	struct fc_fcp_internal *si = fc_get_scsi_internal(lport); +	unsigned long flags;  	int can_queue; +	spin_lock_irqsave(lport->host->host_lock, flags); +  	if (si->last_can_queue_ramp_down_time &&  	    (time_before(jiffies, si->last_can_queue_ramp_down_time +  			 FC_CAN_QUEUE_PERIOD))) -		return; +		goto unlock;  	si->last_can_queue_ramp_down_time = jiffies; @@ -392,6 +404,9 @@ static void fc_fcp_can_queue_ramp_down(struct fc_lport *lport)  	lport->host->can_queue = can_queue;  	shost_printk(KERN_ERR, lport->host, "libfc: Could not allocate frame.\n"  		     "Reducing can_queue to %d.\n", can_queue); + +unlock: +	spin_unlock_irqrestore(lport->host->host_lock, flags);  }  /* @@ -406,16 +421,15 @@ static inline struct fc_frame *fc_fcp_frame_alloc(struct fc_lport *lport,  						  size_t len)  {  	struct fc_frame *fp; -	unsigned long flags;  	fp = fc_frame_alloc(lport, len);  	if (likely(fp))  		return fp; +	per_cpu_ptr(lport->stats, get_cpu())->FcpFrameAllocFails++; +	put_cpu();  	/* error case */ -	spin_lock_irqsave(lport->host->host_lock, flags);  	fc_fcp_can_queue_ramp_down(lport); -	spin_unlock_irqrestore(lport->host->host_lock, flags);  	return NULL;  } @@ -428,7 +442,7 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)  {  	struct scsi_cmnd *sc = fsp->cmd;  	struct fc_lport *lport = fsp->lp; -	struct fcoe_dev_stats *stats; +	struct fc_stats *stats;  	struct fc_frame_header *fh;  	size_t start_offset;  	size_t offset; @@ -438,6 +452,7 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)  	void *buf;  	struct scatterlist *sg;  	u32 nents; +	u8 host_bcode = FC_COMPLETE;  	fh = fc_frame_header_get(fp);  	offset = ntohl(fh->fh_parm_offset); @@ -446,13 +461,16 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)  	buf = fc_frame_payload_get(fp, 0);  	/* -	 * if this I/O is ddped then clear it -	 * and initiate recovery since data -	 * frames are expected to be placed -	 * directly in that case. +	 * if this I/O is ddped then clear it and initiate recovery since data +	 * frames are expected to be placed directly in that case. +	 * +	 * Indicate error to scsi-ml because something went wrong with the +	 * ddp handling to get us here.  	 */  	if (fsp->xfer_ddp != FC_XID_UNKNOWN) {  		fc_fcp_ddp_done(fsp); +		FC_FCP_DBG(fsp, "DDP I/O in fc_fcp_recv_data set ERROR\n"); +		host_bcode = FC_ERROR;  		goto err;  	}  	if (offset + len > fsp->data_len) { @@ -462,6 +480,9 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)  			goto crc_err;  		FC_FCP_DBG(fsp, "data received past end. len %zx offset %zx "  			   "data_len %x\n", len, offset, fsp->data_len); + +		/* Data is corrupted indicate scsi-ml should retry */ +		host_bcode = FC_DATA_OVRRUN;  		goto err;  	}  	if (offset != fsp->xfer_len) @@ -472,21 +493,21 @@ static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp)  	if (!(fr_flags(fp) & FCPHF_CRC_UNCHECKED)) {  		copy_len = fc_copy_buffer_to_sglist(buf, len, sg, &nents, -						    &offset, KM_SOFTIRQ0, NULL); +						    &offset, NULL);  	} else {  		crc = crc32(~0, (u8 *) fh, sizeof(*fh));  		copy_len = fc_copy_buffer_to_sglist(buf, len, sg, &nents, -						    &offset, KM_SOFTIRQ0, &crc); +						    &offset, &crc);  		buf = fc_frame_payload_get(fp, 0);  		if (len % 4)  			crc = crc32(crc, buf + len, 4 - (len % 4));  		if (~crc != le32_to_cpu(fr_crc(fp))) {  crc_err: -			stats = per_cpu_ptr(lport->dev_stats, get_cpu()); +			stats = per_cpu_ptr(lport->stats, get_cpu());  			stats->ErrorFrames++;  			/* per cpu count, not total count, but OK for limit */ -			if (stats->InvalidCRCCount++ < 5) +			if (stats->InvalidCRCCount++ < FC_MAX_ERROR_CNT)  				printk(KERN_WARNING "libfc: CRC error on data "  				       "frame for port (%6.6x)\n",  				       lport->port_id); @@ -498,8 +519,10 @@ crc_err:  			 * If so, we need to retry the entire operation.  			 * Otherwise, ignore it.  			 */ -			if (fsp->state & FC_SRB_DISCONTIG) +			if (fsp->state & FC_SRB_DISCONTIG) { +				host_bcode = FC_CRC_ERROR;  				goto err; +			}  			return;  		}  	} @@ -517,7 +540,7 @@ crc_err:  		fc_fcp_complete_locked(fsp);  	return;  err: -	fc_fcp_recovery(fsp); +	fc_fcp_recovery(fsp, host_bcode);  }  /** @@ -635,10 +658,10 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,  			 * The scatterlist item may be bigger than PAGE_SIZE,  			 * but we must not cross pages inside the kmap.  			 */ -			page_addr = kmap_atomic(page, KM_SOFTIRQ0); +			page_addr = kmap_atomic(page);  			memcpy(data, (char *)page_addr + (off & ~PAGE_MASK),  			       sg_bytes); -			kunmap_atomic(page_addr, KM_SOFTIRQ0); +			kunmap_atomic(page_addr);  			data += sg_bytes;  		}  		offset += sg_bytes; @@ -667,8 +690,7 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,  		error = lport->tt.seq_send(lport, seq, fp);  		if (error) {  			WARN_ON(1);		/* send error should be rare */ -			fc_fcp_retry_cmd(fsp); -			return 0; +			return error;  		}  		fp = NULL;  	} @@ -677,7 +699,7 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,  }  /** - * fc_fcp_abts_resp() - Send an ABTS response + * fc_fcp_abts_resp() - Receive an ABTS response   * @fsp: The FCP packet that is being aborted   * @fp:	 The response frame   */ @@ -717,7 +739,7 @@ static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)  }  /** - * fc_fcp_recv() - Reveive an FCP frame + * fc_fcp_recv() - Receive an FCP frame   * @seq: The sequence the frame is on   * @fp:	 The received frame   * @arg: The related FCP packet @@ -746,7 +768,6 @@ static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg)  		goto out;  	if (fc_fcp_lock_pkt(fsp))  		goto out; -	fsp->last_pkt_time = jiffies;  	if (fh->fh_type == FC_TYPE_BLS) {  		fc_fcp_abts_resp(fsp, fp); @@ -830,7 +851,8 @@ static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)  			fc_rp_info = (struct fcp_resp_rsp_info *)(rp_ex + 1);  			if (flags & FCP_RSP_LEN_VAL) {  				respl = ntohl(rp_ex->fr_rsp_len); -				if (respl != sizeof(*fc_rp_info)) +				if ((respl != FCP_RESP_RSP_INFO_LEN4) && +				    (respl != FCP_RESP_RSP_INFO_LEN8))  					goto len_err;  				if (fsp->wait_for_comp) {  					/* Abuse cdb_status for rsp code */ @@ -858,7 +880,7 @@ static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)  				fsp->scsi_resid = ntohl(rp_ex->fr_resid);  				/*  				 * The cmnd->underflow is the minimum number of -				 * bytes that must be transfered for this +				 * bytes that must be transferred for this  				 * command.  Provided a sense condition is not  				 * present, make sure the actual amount  				 * transferred is at least the underflow value @@ -880,7 +902,8 @@ static void fc_fcp_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp)  	/*  	 * Check for missing or extra data frames.  	 */ -	if (unlikely(fsp->xfer_len != expected_len)) { +	if (unlikely(fsp->cdb_status == SAM_STAT_GOOD && +		     fsp->xfer_len != expected_len)) {  		if (fsp->xfer_len < expected_len) {  			/*  			 * Some data may be queued locally, @@ -933,12 +956,11 @@ static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)  		 * Test for transport underrun, independent of response  		 * underrun status.  		 */ -		if (fsp->xfer_len < fsp->data_len && !fsp->io_status && +		if (fsp->cdb_status == SAM_STAT_GOOD && +		    fsp->xfer_len < fsp->data_len && !fsp->io_status &&  		    (!(fsp->scsi_comp_flags & FCP_RESID_UNDER) || -		     fsp->xfer_len < fsp->data_len - fsp->scsi_resid)) { +		     fsp->xfer_len < fsp->data_len - fsp->scsi_resid))  			fsp->status_code = FC_DATA_UNDRUN; -			fsp->io_status = 0; -		}  	}  	seq = fsp->seq_ptr; @@ -962,7 +984,13 @@ static void fc_fcp_complete_locked(struct fc_fcp_pkt *fsp)  		}  		lport->tt.exch_done(seq);  	} -	fc_io_compl(fsp); +	/* +	 * Some resets driven by SCSI are not I/Os and do not have +	 * SCSI commands associated with the requests. We should not +	 * call I/O completion if we do not have a SCSI command. +	 */ +	if (fsp->cmd) +		fc_io_compl(fsp);  }  /** @@ -1055,8 +1083,7 @@ static int fc_fcp_pkt_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp)  	fsp->cdb_cmd.fc_dl = htonl(fsp->data_len);  	fsp->cdb_cmd.fc_flags = fsp->req_flags & ~FCP_CFL_LEN_MASK; -	int_to_scsilun(fsp->cmd->device->lun, -		       (struct scsi_lun *)fsp->cdb_cmd.fc_lun); +	int_to_scsilun(fsp->cmd->device->lun, &fsp->cdb_cmd.fc_lun);  	memcpy(fsp->cdb_cmd.fc_cdb, fsp->cmd->cmnd, fsp->cmd->cmd_len);  	spin_lock_irqsave(&si->scsi_queue_lock, flags); @@ -1065,6 +1092,7 @@ static int fc_fcp_pkt_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp)  	rc = lport->tt.fcp_cmd_send(lport, fsp, fc_fcp_recv);  	if (unlikely(rc)) {  		spin_lock_irqsave(&si->scsi_queue_lock, flags); +		fsp->cmd->SCp.ptr = NULL;  		list_del(&fsp->list);  		spin_unlock_irqrestore(&si->scsi_queue_lock, flags);  	} @@ -1073,6 +1101,19 @@ static int fc_fcp_pkt_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp)  }  /** + * get_fsp_rec_tov() - Helper function to get REC_TOV + * @fsp: the FCP packet + * + * Returns rec tov in jiffies as rpriv->e_d_tov + 1 second + */ +static inline unsigned int get_fsp_rec_tov(struct fc_fcp_pkt *fsp) +{ +	struct fc_rport_libfc_priv *rpriv = fsp->rport->dd_data; + +	return msecs_to_jiffies(rpriv->e_d_tov) + HZ; +} + +/**   * fc_fcp_cmd_send() - Send a FCP command   * @lport: The local port to send the command on   * @fsp:   The FCP packet the command is on @@ -1115,14 +1156,13 @@ static int fc_fcp_cmd_send(struct fc_lport *lport, struct fc_fcp_pkt *fsp,  		rc = -1;  		goto unlock;  	} -	fsp->last_pkt_time = jiffies;  	fsp->seq_ptr = seq;  	fc_fcp_pkt_hold(fsp);	/* hold for fc_fcp_pkt_destroy */  	setup_timer(&fsp->timer, fc_fcp_timeout, (unsigned long)fsp); -	fc_fcp_timer_set(fsp, -			 (fsp->tgt_flags & FC_RP_FLAGS_REC_SUPPORTED) ? -			 FC_SCSI_REC_TOV : FC_SCSI_ER_TIMEOUT); +	if (rpriv->flags & FC_RP_FLAGS_REC_SUPPORTED) +		fc_fcp_timer_set(fsp, get_fsp_rec_tov(fsp)); +  unlock:  	fc_fcp_unlock_pkt(fsp);  	return rc; @@ -1165,6 +1205,7 @@ unlock:  static int fc_fcp_pkt_abort(struct fc_fcp_pkt *fsp)  {  	int rc = FAILED; +	unsigned long ticks_left;  	if (fc_fcp_send_abort(fsp))  		return FAILED; @@ -1173,13 +1214,13 @@ static int fc_fcp_pkt_abort(struct fc_fcp_pkt *fsp)  	fsp->wait_for_comp = 1;  	spin_unlock_bh(&fsp->scsi_pkt_lock); -	rc = wait_for_completion_timeout(&fsp->tm_done, FC_SCSI_TM_TOV); +	ticks_left = wait_for_completion_timeout(&fsp->tm_done, +							FC_SCSI_TM_TOV);  	spin_lock_bh(&fsp->scsi_pkt_lock);  	fsp->wait_for_comp = 0; -	if (!rc) { +	if (!ticks_left) {  		FC_FCP_DBG(fsp, "target abort cmd  failed\n"); -		rc = FAILED;  	} else if (fsp->state & FC_SRB_ABORTED) {  		FC_FCP_DBG(fsp, "target abort cmd  passed\n");  		rc = SUCCESS; @@ -1197,13 +1238,14 @@ static void fc_lun_reset_send(unsigned long data)  {  	struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)data;  	struct fc_lport *lport = fsp->lp; +  	if (lport->tt.fcp_cmd_send(lport, fsp, fc_tm_done)) {  		if (fsp->recov_retry++ >= FC_MAX_RECOV_RETRY)  			return;  		if (fc_fcp_lock_pkt(fsp))  			return;  		setup_timer(&fsp->timer, fc_lun_reset_send, (unsigned long)fsp); -		fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV); +		fc_fcp_timer_set(fsp, get_fsp_rec_tov(fsp));  		fc_fcp_unlock_pkt(fsp);  	}  } @@ -1211,7 +1253,7 @@ static void fc_lun_reset_send(unsigned long data)  /**   * fc_lun_reset() - Send a LUN RESET command to a device   *		    and wait for the reply - * @lport: The local port to sent the comand on + * @lport: The local port to sent the command on   * @fsp:   The FCP packet that identifies the LUN to be reset   * @id:	   The SCSI command ID   * @lun:   The LUN ID to be reset @@ -1223,7 +1265,7 @@ static int fc_lun_reset(struct fc_lport *lport, struct fc_fcp_pkt *fsp,  	fsp->cdb_cmd.fc_dl = htonl(fsp->data_len);  	fsp->cdb_cmd.fc_tm_flags = FCP_TMF_LUN_RESET; -	int_to_scsilun(lun, (struct scsi_lun *)fsp->cdb_cmd.fc_lun); +	int_to_scsilun(lun, &fsp->cdb_cmd.fc_lun);  	fsp->wait_for_comp = 1;  	init_completion(&fsp->tm_done); @@ -1265,7 +1307,7 @@ static int fc_lun_reset(struct fc_lport *lport, struct fc_fcp_pkt *fsp,  }  /** - * fc_tm_done() - Task Managment response handler + * fc_tm_done() - Task Management response handler   * @seq: The sequence that the response is on   * @fp:	 The response frame   * @arg: The FCP packet the response is for @@ -1286,23 +1328,23 @@ static void fc_tm_done(struct fc_seq *seq, struct fc_frame *fp, void *arg)  	}  	if (fc_fcp_lock_pkt(fsp)) -		return; +		goto out;  	/*  	 * raced with eh timeout handler.  	 */ -	if (!fsp->seq_ptr || !fsp->wait_for_comp) { -		spin_unlock_bh(&fsp->scsi_pkt_lock); -		return; -	} +	if (!fsp->seq_ptr || !fsp->wait_for_comp) +		goto out_unlock;  	fh = fc_frame_header_get(fp);  	if (fh->fh_type != FC_TYPE_BLS)  		fc_fcp_resp(fsp, fp);  	fsp->seq_ptr = NULL;  	fsp->lp->tt.exch_done(seq); -	fc_frame_free(fp); +out_unlock:  	fc_fcp_unlock_pkt(fsp); +out: +	fc_frame_free(fp);  }  /** @@ -1341,13 +1383,10 @@ static void fc_fcp_timeout(unsigned long data)  	if (rpriv->flags & FC_RP_FLAGS_REC_SUPPORTED)  		fc_fcp_rec(fsp); -	else if (time_after_eq(fsp->last_pkt_time + (FC_SCSI_ER_TIMEOUT / 2), -			       jiffies)) -		fc_fcp_timer_set(fsp, FC_SCSI_ER_TIMEOUT);  	else if (fsp->state & FC_SRB_RCV_STATUS)  		fc_fcp_complete_locked(fsp);  	else -		fc_fcp_recovery(fsp); +		fc_fcp_recovery(fsp, FC_TIMED_OUT);  	fsp->state &= ~FC_SRB_FCP_PROCESSING_TMO;  unlock:  	fc_fcp_unlock_pkt(fsp); @@ -1373,6 +1412,7 @@ static void fc_fcp_rec(struct fc_fcp_pkt *fsp)  		fc_fcp_complete_locked(fsp);  		return;  	} +  	fp = fc_fcp_frame_alloc(lport, sizeof(struct fc_els_rec));  	if (!fp)  		goto retry; @@ -1383,15 +1423,15 @@ static void fc_fcp_rec(struct fc_fcp_pkt *fsp)  		       FC_FCTL_REQ, 0);  	if (lport->tt.elsct_send(lport, rport->port_id, fp, ELS_REC,  				 fc_fcp_rec_resp, fsp, -				 jiffies_to_msecs(FC_SCSI_REC_TOV))) { +				 2 * lport->r_a_tov)) {  		fc_fcp_pkt_hold(fsp);		/* hold while REC outstanding */  		return;  	}  retry:  	if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY) -		fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV); +		fc_fcp_timer_set(fsp, get_fsp_rec_tov(fsp));  	else -		fc_fcp_recovery(fsp); +		fc_fcp_recovery(fsp, FC_TIMED_OUT);  }  /** @@ -1445,7 +1485,6 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)  			 * making progress.  			 */  			rpriv->flags &= ~FC_RP_FLAGS_REC_SUPPORTED; -			fc_fcp_timer_set(fsp, FC_SCSI_ER_TIMEOUT);  			break;  		case ELS_RJT_LOGIC:  		case ELS_RJT_UNAB: @@ -1460,7 +1499,7 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)  				fc_fcp_retry_cmd(fsp);  				break;  			} -			fc_fcp_recovery(fsp); +			fc_fcp_recovery(fsp, FC_ERROR);  			break;  		}  	} else if (opcode == ELS_LS_ACC) { @@ -1498,12 +1537,11 @@ static void fc_fcp_rec_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)  			}  			fc_fcp_srr(fsp, r_ctl, offset);  		} else if (e_stat & ESB_ST_SEQ_INIT) { -  			/*  			 * The remote port has the initiative, so just  			 * keep waiting for it to complete.  			 */ -			fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV); +			fc_fcp_timer_set(fsp,  get_fsp_rec_tov(fsp));  		} else {  			/* @@ -1575,7 +1613,7 @@ static void fc_fcp_rec_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)  		if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)  			fc_fcp_rec(fsp);  		else -			fc_fcp_recovery(fsp); +			fc_fcp_recovery(fsp, FC_ERROR);  		break;  	}  	fc_fcp_unlock_pkt(fsp); @@ -1587,9 +1625,9 @@ out:   * fc_fcp_recovery() - Handler for fcp_pkt recovery   * @fsp: The FCP pkt that needs to be aborted   */ -static void fc_fcp_recovery(struct fc_fcp_pkt *fsp) +static void fc_fcp_recovery(struct fc_fcp_pkt *fsp, u8 code)  { -	fsp->status_code = FC_CMD_RECOVERY; +	fsp->status_code = code;  	fsp->cdb_status = 0;  	fsp->io_status = 0;  	/* @@ -1615,11 +1653,10 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)  	struct fc_seq *seq;  	struct fcp_srr *srr;  	struct fc_frame *fp; -	u8 cdb_op; +	unsigned int rec_tov;  	rport = fsp->rport;  	rpriv = rport->dd_data; -	cdb_op = fsp->cdb_cmd.fc_cdb[0];  	if (!(rpriv->flags & FC_RP_FLAGS_RETRY) ||  	    rpriv->rp_state != RPORT_ST_READY) @@ -1640,8 +1677,10 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)  		       rpriv->local_port->port_id, FC_TYPE_FCP,  		       FC_FCTL_REQ, 0); -	seq = lport->tt.exch_seq_send(lport, fp, fc_fcp_srr_resp, NULL, -				      fsp, jiffies_to_msecs(FC_SCSI_REC_TOV)); +	rec_tov = get_fsp_rec_tov(fsp); +	seq = lport->tt.exch_seq_send(lport, fp, fc_fcp_srr_resp, +				      fc_fcp_pkt_destroy, +				      fsp, jiffies_to_msecs(rec_tov));  	if (!seq)  		goto retry; @@ -1687,22 +1726,20 @@ static void fc_fcp_srr_resp(struct fc_seq *seq, struct fc_frame *fp, void *arg)  		return;  	} -	fsp->recov_seq = NULL;  	switch (fc_frame_payload_op(fp)) {  	case ELS_LS_ACC:  		fsp->recov_retry = 0; -		fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV); +		fc_fcp_timer_set(fsp, get_fsp_rec_tov(fsp));  		break;  	case ELS_LS_RJT:  	default: -		fc_fcp_recovery(fsp); +		fc_fcp_recovery(fsp, FC_ERROR);  		break;  	}  	fc_fcp_unlock_pkt(fsp); -	fsp->lp->tt.exch_done(seq);  out: +	fsp->lp->tt.exch_done(seq);  	fc_frame_free(fp); -	fc_fcp_pkt_release(fsp);	/* drop hold for outstanding SRR */  }  /** @@ -1714,14 +1751,12 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)  {  	if (fc_fcp_lock_pkt(fsp))  		goto out; -	fsp->lp->tt.exch_done(fsp->recov_seq); -	fsp->recov_seq = NULL;  	switch (PTR_ERR(fp)) {  	case -FC_EX_TIMEOUT:  		if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)  			fc_fcp_rec(fsp);  		else -			fc_fcp_recovery(fsp); +			fc_fcp_recovery(fsp, FC_TIMED_OUT);  		break;  	case -FC_EX_CLOSED:			/* e.g., link failure */  		/* fall through */ @@ -1731,7 +1766,7 @@ static void fc_fcp_srr_error(struct fc_fcp_pkt *fsp, struct fc_frame *fp)  	}  	fc_fcp_unlock_pkt(fsp);  out: -	fc_fcp_pkt_release(fsp);	/* drop hold for outstanding SRR */ +	fsp->lp->tt.exch_done(fsp->recov_seq);  }  /** @@ -1747,31 +1782,27 @@ static inline int fc_fcp_lport_queue_ready(struct fc_lport *lport)  /**   * fc_queuecommand() - The queuecommand function of the SCSI template + * @shost: The Scsi_Host that the command was issued to   * @cmd:   The scsi_cmnd to be executed - * @done:  The callback function to be called when the scsi_cmnd is complete   * - * This is the i/o strategy routine, called by the SCSI layer. This routine - * is called with the host_lock held. + * This is the i/o strategy routine, called by the SCSI layer.   */ -int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *)) +int fc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *sc_cmd)  { -	struct fc_lport *lport; +	struct fc_lport *lport = shost_priv(shost);  	struct fc_rport *rport = starget_to_rport(scsi_target(sc_cmd->device));  	struct fc_fcp_pkt *fsp;  	struct fc_rport_libfc_priv *rpriv;  	int rval;  	int rc = 0; -	struct fcoe_dev_stats *stats; - -	lport = shost_priv(sc_cmd->device->host); +	struct fc_stats *stats;  	rval = fc_remote_port_chkready(rport);  	if (rval) {  		sc_cmd->result = rval; -		done(sc_cmd); +		sc_cmd->scsi_done(sc_cmd);  		return 0;  	} -	spin_unlock_irq(lport->host->host_lock);  	if (!*(struct fc_remote_port **)rport->dd_data) {  		/* @@ -1779,7 +1810,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))  		 * online  		 */  		sc_cmd->result = DID_IMM_RETRY << 16; -		done(sc_cmd); +		sc_cmd->scsi_done(sc_cmd);  		goto out;  	} @@ -1802,10 +1833,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))  	 * build the libfc request pkt  	 */  	fsp->cmd = sc_cmd;	/* save the cmd */ -	fsp->lp = lport;	/* save the softc ptr */  	fsp->rport = rport;	/* set the remote port ptr */ -	fsp->xfer_ddp = FC_XID_UNKNOWN; -	sc_cmd->scsi_done = done;  	/*  	 * set up the transfer length @@ -1816,26 +1844,21 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))  	/*  	 * setup the data direction  	 */ -	stats = per_cpu_ptr(lport->dev_stats, get_cpu()); +	stats = per_cpu_ptr(lport->stats, get_cpu());  	if (sc_cmd->sc_data_direction == DMA_FROM_DEVICE) {  		fsp->req_flags = FC_SRB_READ;  		stats->InputRequests++; -		stats->InputMegabytes = fsp->data_len; +		stats->InputBytes += fsp->data_len;  	} else if (sc_cmd->sc_data_direction == DMA_TO_DEVICE) {  		fsp->req_flags = FC_SRB_WRITE;  		stats->OutputRequests++; -		stats->OutputMegabytes = fsp->data_len; +		stats->OutputBytes += fsp->data_len;  	} else {  		fsp->req_flags = 0;  		stats->ControlRequests++;  	}  	put_cpu(); -	fsp->tgt_flags = rpriv->flags; - -	init_timer(&fsp->timer); -	fsp->timer.data = (unsigned long)fsp; -  	/*  	 * send it to the lower layer  	 * if we get -1 return then put the request in the pending @@ -1848,7 +1871,6 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))  		rc = SCSI_MLQUEUE_HOST_BUSY;  	}  out: -	spin_lock_irq(lport->host->host_lock);  	return rc;  }  EXPORT_SYMBOL(fc_queuecommand); @@ -1944,18 +1966,29 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)  		break;  	case FC_CMD_ABORTED:  		FC_FCP_DBG(fsp, "Returning DID_ERROR to scsi-ml " -			   "due to FC_CMD_ABORTED\n"); +			  "due to FC_CMD_ABORTED\n");  		sc_cmd->result = (DID_ERROR << 16) | fsp->io_status;  		break; -	case FC_CMD_RECOVERY: -		sc_cmd->result = (DID_BUS_BUSY << 16) | fsp->io_status; -		break;  	case FC_CMD_RESET: +		FC_FCP_DBG(fsp, "Returning DID_RESET to scsi-ml " +			   "due to FC_CMD_RESET\n");  		sc_cmd->result = (DID_RESET << 16);  		break;  	case FC_HRD_ERROR: +		FC_FCP_DBG(fsp, "Returning DID_NO_CONNECT to scsi-ml " +			   "due to FC_HRD_ERROR\n");  		sc_cmd->result = (DID_NO_CONNECT << 16);  		break; +	case FC_CRC_ERROR: +		FC_FCP_DBG(fsp, "Returning DID_PARITY to scsi-ml " +			   "due to FC_CRC_ERROR\n"); +		sc_cmd->result = (DID_PARITY << 16); +		break; +	case FC_TIMED_OUT: +		FC_FCP_DBG(fsp, "Returning DID_BUS_BUSY to scsi-ml " +			   "due to FC_TIMED_OUT\n"); +		sc_cmd->result = (DID_BUS_BUSY << 16) | fsp->io_status; +		break;  	default:  		FC_FCP_DBG(fsp, "Returning DID_ERROR to scsi-ml "  			   "due to unknown error\n"); @@ -1990,6 +2023,11 @@ int fc_eh_abort(struct scsi_cmnd *sc_cmd)  	struct fc_fcp_internal *si;  	int rc = FAILED;  	unsigned long flags; +	int rval; + +	rval = fc_block_scsi_eh(sc_cmd); +	if (rval) +		return rval;  	lport = shost_priv(sc_cmd->device->host);  	if (lport->state != LPORT_ST_READY) @@ -2002,10 +2040,10 @@ int fc_eh_abort(struct scsi_cmnd *sc_cmd)  	fsp = CMD_SP(sc_cmd);  	if (!fsp) {  		/* command completed while scsi eh was setting up */ -		spin_unlock_irqrestore(lport->host->host_lock, flags); +		spin_unlock_irqrestore(&si->scsi_queue_lock, flags);  		return SUCCESS;  	} -	/* grab a ref so the fsp and sc_cmd cannot be relased from under us */ +	/* grab a ref so the fsp and sc_cmd cannot be released from under us */  	fc_fcp_pkt_hold(fsp);  	spin_unlock_irqrestore(&si->scsi_queue_lock, flags); @@ -2039,9 +2077,9 @@ int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)  	int rc = FAILED;  	int rval; -	rval = fc_remote_port_chkready(rport); +	rval = fc_block_scsi_eh(sc_cmd);  	if (rval) -		goto out; +		return rval;  	lport = shost_priv(sc_cmd->device->host); @@ -2061,7 +2099,6 @@ int fc_eh_device_reset(struct scsi_cmnd *sc_cmd)  	 * the sc passed in is not setup for execution like when sent  	 * through the queuecommand callout.  	 */ -	fsp->lp = lport;	/* save the softc ptr */  	fsp->rport = rport;	/* set the remote port ptr */  	/* @@ -2088,6 +2125,8 @@ int fc_eh_host_reset(struct scsi_cmnd *sc_cmd)  	FC_SCSI_DBG(lport, "Resetting host\n"); +	fc_block_scsi_eh(sc_cmd); +  	lport->tt.lport_reset(lport);  	wait_tmo = jiffies + FC_HOST_RESET_TIMEOUT;  	while (!fc_fcp_lport_queue_ready(lport) && time_before(jiffies, @@ -2194,7 +2233,7 @@ void fc_fcp_destroy(struct fc_lport *lport)  }  EXPORT_SYMBOL(fc_fcp_destroy); -int fc_setup_fcp() +int fc_setup_fcp(void)  {  	int rc = 0; @@ -2210,7 +2249,7 @@ int fc_setup_fcp()  	return rc;  } -void fc_destroy_fcp() +void fc_destroy_fcp(void)  {  	if (scsi_pkt_cachep)  		kmem_cache_destroy(scsi_pkt_cachep); diff --git a/drivers/scsi/libfc/fc_frame.c b/drivers/scsi/libfc/fc_frame.c index 981329a17c4..0382ac06906 100644 --- a/drivers/scsi/libfc/fc_frame.c +++ b/drivers/scsi/libfc/fc_frame.c @@ -49,7 +49,7 @@ u32 fc_frame_crc_check(struct fc_frame *fp)  EXPORT_SYMBOL(fc_frame_crc_check);  /* - * Allocate a frame intended to be sent via fcoe_xmit. + * Allocate a frame intended to be sent.   * Get an sk_buff for the frame and set the length.   */  struct fc_frame *_fc_frame_alloc(size_t len) diff --git a/drivers/scsi/libfc/fc_libfc.c b/drivers/scsi/libfc/fc_libfc.c index 6a48c28e442..8d65a51a759 100644 --- a/drivers/scsi/libfc/fc_libfc.c +++ b/drivers/scsi/libfc/fc_libfc.c @@ -21,6 +21,7 @@  #include <linux/types.h>  #include <linux/scatterlist.h>  #include <linux/crc32.h> +#include <linux/module.h>  #include <scsi/libfc.h>  #include <scsi/fc_encode.h> @@ -35,6 +36,27 @@ unsigned int fc_debug_logging;  module_param_named(debug_logging, fc_debug_logging, int, S_IRUGO|S_IWUSR);  MODULE_PARM_DESC(debug_logging, "a bit mask of logging levels"); +DEFINE_MUTEX(fc_prov_mutex); +static LIST_HEAD(fc_local_ports); +struct blocking_notifier_head fc_lport_notifier_head = +		BLOCKING_NOTIFIER_INIT(fc_lport_notifier_head); +EXPORT_SYMBOL(fc_lport_notifier_head); + +/* + * Providers which primarily send requests and PRLIs. + */ +struct fc4_prov *fc_active_prov[FC_FC4_PROV_SIZE] = { +	[0] = &fc_rport_t0_prov, +	[FC_TYPE_FCP] = &fc_rport_fcp_init, +}; + +/* + * Providers which receive requests. + */ +struct fc4_prov *fc_passive_prov[FC_FC4_PROV_SIZE] = { +	[FC_TYPE_ELS] = &fc_lport_els_prov, +}; +  /**   * libfc_init() - Initialize libfc.ko   */ @@ -83,14 +105,13 @@ module_exit(libfc_exit);   * @sg: pointer to the pointer of the SG list.   * @nents: pointer to the remaining number of entries in the SG list.   * @offset: pointer to the current offset in the SG list. - * @km_type: dedicated page table slot type for kmap_atomic.   * @crc: pointer to the 32-bit crc value.   *	 If crc is NULL, CRC is not calculated.   */  u32 fc_copy_buffer_to_sglist(void *buf, size_t len,  			     struct scatterlist *sg,  			     u32 *nents, size_t *offset, -			     enum km_type km_type, u32 *crc) +			     u32 *crc)  {  	size_t remaining = len;  	u32 copy_len = 0; @@ -120,12 +141,11 @@ u32 fc_copy_buffer_to_sglist(void *buf, size_t len,  		off = *offset + sg->offset;  		sg_bytes = min(sg_bytes,  			       (size_t)(PAGE_SIZE - (off & ~PAGE_MASK))); -		page_addr = kmap_atomic(sg_page(sg) + (off >> PAGE_SHIFT), -					km_type); +		page_addr = kmap_atomic(sg_page(sg) + (off >> PAGE_SHIFT));  		if (crc)  			*crc = crc32(*crc, buf, sg_bytes);  		memcpy((char *)page_addr + (off & ~PAGE_MASK), buf, sg_bytes); -		kunmap_atomic(page_addr, km_type); +		kunmap_atomic(page_addr);  		buf += sg_bytes;  		*offset += sg_bytes;  		remaining -= sg_bytes; @@ -210,3 +230,102 @@ void fc_fill_reply_hdr(struct fc_frame *fp, const struct fc_frame *in_fp,  	fc_fill_hdr(fp, in_fp, r_ctl, FC_FCTL_RESP, 0, parm_offset);  }  EXPORT_SYMBOL(fc_fill_reply_hdr); + +/** + * fc_fc4_conf_lport_params() - Modify "service_params" of specified lport + * if there is service provider (target provider) registered with libfc + * for specified "fc_ft_type" + * @lport: Local port which service_params needs to be modified + * @type: FC-4 type, such as FC_TYPE_FCP + */ +void fc_fc4_conf_lport_params(struct fc_lport *lport, enum fc_fh_type type) +{ +	struct fc4_prov *prov_entry; +	BUG_ON(type >= FC_FC4_PROV_SIZE); +	BUG_ON(!lport); +	prov_entry = fc_passive_prov[type]; +	if (type == FC_TYPE_FCP) { +		if (prov_entry && prov_entry->recv) +			lport->service_params |= FCP_SPPF_TARG_FCN; +	} +} + +void fc_lport_iterate(void (*notify)(struct fc_lport *, void *), void *arg) +{ +	struct fc_lport *lport; + +	mutex_lock(&fc_prov_mutex); +	list_for_each_entry(lport, &fc_local_ports, lport_list) +		notify(lport, arg); +	mutex_unlock(&fc_prov_mutex); +} +EXPORT_SYMBOL(fc_lport_iterate); + +/** + * fc_fc4_register_provider() - register FC-4 upper-level provider. + * @type: FC-4 type, such as FC_TYPE_FCP + * @prov: structure describing provider including ops vector. + * + * Returns 0 on success, negative error otherwise. + */ +int fc_fc4_register_provider(enum fc_fh_type type, struct fc4_prov *prov) +{ +	struct fc4_prov **prov_entry; +	int ret = 0; + +	if (type >= FC_FC4_PROV_SIZE) +		return -EINVAL; +	mutex_lock(&fc_prov_mutex); +	prov_entry = (prov->recv ? fc_passive_prov : fc_active_prov) + type; +	if (*prov_entry) +		ret = -EBUSY; +	else +		*prov_entry = prov; +	mutex_unlock(&fc_prov_mutex); +	return ret; +} +EXPORT_SYMBOL(fc_fc4_register_provider); + +/** + * fc_fc4_deregister_provider() - deregister FC-4 upper-level provider. + * @type: FC-4 type, such as FC_TYPE_FCP + * @prov: structure describing provider including ops vector. + */ +void fc_fc4_deregister_provider(enum fc_fh_type type, struct fc4_prov *prov) +{ +	BUG_ON(type >= FC_FC4_PROV_SIZE); +	mutex_lock(&fc_prov_mutex); +	if (prov->recv) +		rcu_assign_pointer(fc_passive_prov[type], NULL); +	else +		rcu_assign_pointer(fc_active_prov[type], NULL); +	mutex_unlock(&fc_prov_mutex); +	synchronize_rcu(); +} +EXPORT_SYMBOL(fc_fc4_deregister_provider); + +/** + * fc_fc4_add_lport() - add new local port to list and run notifiers. + * @lport:  The new local port. + */ +void fc_fc4_add_lport(struct fc_lport *lport) +{ +	mutex_lock(&fc_prov_mutex); +	list_add_tail(&lport->lport_list, &fc_local_ports); +	blocking_notifier_call_chain(&fc_lport_notifier_head, +				     FC_LPORT_EV_ADD, lport); +	mutex_unlock(&fc_prov_mutex); +} + +/** + * fc_fc4_del_lport() - remove local port from list and run notifiers. + * @lport:  The new local port. + */ +void fc_fc4_del_lport(struct fc_lport *lport) +{ +	mutex_lock(&fc_prov_mutex); +	list_del(&lport->lport_list); +	blocking_notifier_call_chain(&fc_lport_notifier_head, +				     FC_LPORT_EV_DEL, lport); +	mutex_unlock(&fc_prov_mutex); +} diff --git a/drivers/scsi/libfc/fc_libfc.h b/drivers/scsi/libfc/fc_libfc.h index 16d2162dda1..b74189d8932 100644 --- a/drivers/scsi/libfc/fc_libfc.h +++ b/drivers/scsi/libfc/fc_libfc.h @@ -41,50 +41,74 @@ extern unsigned int fc_debug_logging;  #define FC_LIBFC_DBG(fmt, args...)					\  	FC_CHECK_LOGGING(FC_LIBFC_LOGGING,				\ -			 printk(KERN_INFO "libfc: " fmt, ##args)) +			 pr_info("libfc: " fmt, ##args))  #define FC_LPORT_DBG(lport, fmt, args...)				\  	FC_CHECK_LOGGING(FC_LPORT_LOGGING,				\ -			 printk(KERN_INFO "host%u: lport %6.6x: " fmt,	\ -				(lport)->host->host_no,			\ -				(lport)->port_id, ##args)) +			 pr_info("host%u: lport %6.6x: " fmt,		\ +				 (lport)->host->host_no,		\ +				 (lport)->port_id, ##args)) -#define FC_DISC_DBG(disc, fmt, args...)				\ -	FC_CHECK_LOGGING(FC_DISC_LOGGING,			\ -			 printk(KERN_INFO "host%u: disc: " fmt,	\ -				fc_disc_lport(disc)->host->host_no,	\ -				##args)) +#define FC_DISC_DBG(disc, fmt, args...)					\ +	FC_CHECK_LOGGING(FC_DISC_LOGGING,				\ +			 pr_info("host%u: disc: " fmt,			\ +				 fc_disc_lport(disc)->host->host_no,	\ +				 ##args))  #define FC_RPORT_ID_DBG(lport, port_id, fmt, args...)			\  	FC_CHECK_LOGGING(FC_RPORT_LOGGING,				\ -			 printk(KERN_INFO "host%u: rport %6.6x: " fmt,	\ -				(lport)->host->host_no,			\ -				(port_id), ##args)) +			 pr_info("host%u: rport %6.6x: " fmt,		\ +				 (lport)->host->host_no,		\ +				 (port_id), ##args))  #define FC_RPORT_DBG(rdata, fmt, args...)				\  	FC_RPORT_ID_DBG((rdata)->local_port, (rdata)->ids.port_id, fmt, ##args)  #define FC_FCP_DBG(pkt, fmt, args...)					\  	FC_CHECK_LOGGING(FC_FCP_LOGGING,				\ -			 printk(KERN_INFO "host%u: fcp: %6.6x: " fmt,	\ +	{								\ +		if ((pkt)->seq_ptr) {					\ +			struct fc_exch *_ep = NULL;			\ +			_ep = fc_seq_exch((pkt)->seq_ptr);		\ +			pr_info("host%u: fcp: %6.6x: "			\ +				"xid %04x-%04x: " fmt,			\  				(pkt)->lp->host->host_no,		\ -				pkt->rport->port_id, ##args)) +				(pkt)->rport->port_id,			\ +				(_ep)->oxid, (_ep)->rxid, ##args);	\ +		} else {						\ +			pr_info("host%u: fcp: %6.6x: " fmt,		\ +				(pkt)->lp->host->host_no,		\ +				(pkt)->rport->port_id, ##args);		\ +		}							\ +	})  #define FC_EXCH_DBG(exch, fmt, args...)					\  	FC_CHECK_LOGGING(FC_EXCH_LOGGING,				\ -			 printk(KERN_INFO "host%u: xid %4x: " fmt,	\ -				(exch)->lp->host->host_no,		\ -				exch->xid, ##args)) +			 pr_info("host%u: xid %4x: " fmt,		\ +				 (exch)->lp->host->host_no,		\ +				 exch->xid, ##args))  #define FC_SCSI_DBG(lport, fmt, args...)				\  	FC_CHECK_LOGGING(FC_SCSI_LOGGING,				\ -			 printk(KERN_INFO "host%u: scsi: " fmt,		\ -				(lport)->host->host_no,	##args)) +			 pr_info("host%u: scsi: " fmt,			\ +				 (lport)->host->host_no, ##args)) + +/* + * FC-4 Providers. + */ +extern struct fc4_prov *fc_active_prov[];	/* providers without recv */ +extern struct fc4_prov *fc_passive_prov[];	/* providers with recv */ +extern struct mutex fc_prov_mutex;		/* lock over table changes */ + +extern struct fc4_prov fc_rport_t0_prov;	/* type 0 provider */ +extern struct fc4_prov fc_lport_els_prov;	/* ELS provider */ +extern struct fc4_prov fc_rport_fcp_init;	/* FCP initiator provider */  /*   * Set up direct-data placement for this I/O request   */  void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid); +void fc_fcp_ddp_done(struct fc_fcp_pkt *fsp);  /*   * Module setup functions @@ -100,6 +124,9 @@ void fc_destroy_fcp(void);   * Internal libfc functions   */  const char *fc_els_resp_type(struct fc_frame *); +extern void fc_fc4_add_lport(struct fc_lport *); +extern void fc_fc4_del_lport(struct fc_lport *); +extern void fc_fc4_conf_lport_params(struct fc_lport *, enum fc_fh_type);  /*   * Copies a buffer into an sg list @@ -107,6 +134,6 @@ const char *fc_els_resp_type(struct fc_frame *);  u32 fc_copy_buffer_to_sglist(void *buf, size_t len,  			     struct scatterlist *sg,  			     u32 *nents, size_t *offset, -			     enum km_type km_type, u32 *crc); +			     u32 *crc);  #endif /* _FC_LIBFC_H_ */ diff --git a/drivers/scsi/libfc/fc_lport.c b/drivers/scsi/libfc/fc_lport.c index 9be63edbf8f..e01a29863c3 100644 --- a/drivers/scsi/libfc/fc_lport.c +++ b/drivers/scsi/libfc/fc_lport.c @@ -52,7 +52,7 @@   * while making the callback. To ensure that the rport is not free'd while   * processing the callback the rport callbacks are serialized through a   * single-threaded workqueue. An rport would never be free'd while in a - * callback handler becuase no other rport work in this queue can be executed + * callback handler because no other rport work in this queue can be executed   * at the same time.   *   * When discovery succeeds or fails a callback is made to the lport as @@ -88,6 +88,8 @@   */  #include <linux/timer.h> +#include <linux/delay.h> +#include <linux/module.h>  #include <linux/slab.h>  #include <asm/unaligned.h> @@ -114,6 +116,8 @@ static void fc_lport_enter_ns(struct fc_lport *, enum fc_lport_state);  static void fc_lport_enter_scr(struct fc_lport *);  static void fc_lport_enter_ready(struct fc_lport *);  static void fc_lport_enter_logo(struct fc_lport *); +static void fc_lport_enter_fdmi(struct fc_lport *lport); +static void fc_lport_enter_ms(struct fc_lport *, enum fc_lport_state);  static const char *fc_lport_state_names[] = {  	[LPORT_ST_DISABLED] = "disabled", @@ -124,6 +128,11 @@ static const char *fc_lport_state_names[] = {  	[LPORT_ST_RSPN_ID] =  "RSPN_ID",  	[LPORT_ST_RFT_ID] =   "RFT_ID",  	[LPORT_ST_RFF_ID] =   "RFF_ID", +	[LPORT_ST_FDMI] =     "FDMI", +	[LPORT_ST_RHBA] =     "RHBA", +	[LPORT_ST_RPA] =      "RPA", +	[LPORT_ST_DHBA] =     "DHBA", +	[LPORT_ST_DPRT] =     "DPRT",  	[LPORT_ST_SCR] =      "SCR",  	[LPORT_ST_READY] =    "Ready",  	[LPORT_ST_LOGO] =     "LOGO", @@ -163,7 +172,7 @@ static int fc_frame_drop(struct fc_lport *lport, struct fc_frame *fp)   * fc_lport_rport_callback() - Event handler for rport events   * @lport: The lport which is receiving the event   * @rdata: private remote port data - * @event: The event that occured + * @event: The event that occurred   *   * Locking Note: The rport lock should not be held when calling   *		 this function. @@ -181,11 +190,14 @@ static void fc_lport_rport_callback(struct fc_lport *lport,  		if (lport->state == LPORT_ST_DNS) {  			lport->dns_rdata = rdata;  			fc_lport_enter_ns(lport, LPORT_ST_RNN_ID); +		} else if (lport->state == LPORT_ST_FDMI) { +			lport->ms_rdata = rdata; +			fc_lport_enter_ms(lport, LPORT_ST_DHBA);  		} else {  			FC_LPORT_DBG(lport, "Received an READY event "  				     "on port (%6.6x) for the directory "  				     "server, but the lport is not " -				     "in the DNS state, it's in the " +				     "in the DNS or FDMI state, it's in the "  				     "%d state", rdata->ids.port_id,  				     lport->state);  			lport->tt.rport_logoff(rdata); @@ -194,7 +206,10 @@ static void fc_lport_rport_callback(struct fc_lport *lport,  	case RPORT_EV_LOGO:  	case RPORT_EV_FAILED:  	case RPORT_EV_STOP: -		lport->dns_rdata = NULL; +		if (rdata->ids.port_id == FC_FID_DIR_SERV) +			lport->dns_rdata = NULL; +		else if (rdata->ids.port_id == FC_FID_MGMT_SERV) +			lport->ms_rdata = NULL;  		break;  	case RPORT_EV_NONE:  		break; @@ -284,43 +299,54 @@ EXPORT_SYMBOL(fc_get_host_speed);   */  struct fc_host_statistics *fc_get_host_stats(struct Scsi_Host *shost)  { -	struct fc_host_statistics *fcoe_stats; +	struct fc_host_statistics *fc_stats;  	struct fc_lport *lport = shost_priv(shost);  	struct timespec v0, v1;  	unsigned int cpu; +	u64 fcp_in_bytes = 0; +	u64 fcp_out_bytes = 0; -	fcoe_stats = &lport->host_stats; -	memset(fcoe_stats, 0, sizeof(struct fc_host_statistics)); +	fc_stats = &lport->host_stats; +	memset(fc_stats, 0, sizeof(struct fc_host_statistics));  	jiffies_to_timespec(jiffies, &v0);  	jiffies_to_timespec(lport->boot_time, &v1); -	fcoe_stats->seconds_since_last_reset = (v0.tv_sec - v1.tv_sec); +	fc_stats->seconds_since_last_reset = (v0.tv_sec - v1.tv_sec);  	for_each_possible_cpu(cpu) { -		struct fcoe_dev_stats *stats; - -		stats = per_cpu_ptr(lport->dev_stats, cpu); - -		fcoe_stats->tx_frames += stats->TxFrames; -		fcoe_stats->tx_words += stats->TxWords; -		fcoe_stats->rx_frames += stats->RxFrames; -		fcoe_stats->rx_words += stats->RxWords; -		fcoe_stats->error_frames += stats->ErrorFrames; -		fcoe_stats->invalid_crc_count += stats->InvalidCRCCount; -		fcoe_stats->fcp_input_requests += stats->InputRequests; -		fcoe_stats->fcp_output_requests += stats->OutputRequests; -		fcoe_stats->fcp_control_requests += stats->ControlRequests; -		fcoe_stats->fcp_input_megabytes += stats->InputMegabytes; -		fcoe_stats->fcp_output_megabytes += stats->OutputMegabytes; -		fcoe_stats->link_failure_count += stats->LinkFailureCount; +		struct fc_stats *stats; + +		stats = per_cpu_ptr(lport->stats, cpu); + +		fc_stats->tx_frames += stats->TxFrames; +		fc_stats->tx_words += stats->TxWords; +		fc_stats->rx_frames += stats->RxFrames; +		fc_stats->rx_words += stats->RxWords; +		fc_stats->error_frames += stats->ErrorFrames; +		fc_stats->invalid_crc_count += stats->InvalidCRCCount; +		fc_stats->fcp_input_requests += stats->InputRequests; +		fc_stats->fcp_output_requests += stats->OutputRequests; +		fc_stats->fcp_control_requests += stats->ControlRequests; +		fcp_in_bytes += stats->InputBytes; +		fcp_out_bytes += stats->OutputBytes; +		fc_stats->fcp_packet_alloc_failures += stats->FcpPktAllocFails; +		fc_stats->fcp_packet_aborts += stats->FcpPktAborts; +		fc_stats->fcp_frame_alloc_failures += stats->FcpFrameAllocFails; +		fc_stats->link_failure_count += stats->LinkFailureCount;  	} -	fcoe_stats->lip_count = -1; -	fcoe_stats->nos_count = -1; -	fcoe_stats->loss_of_sync_count = -1; -	fcoe_stats->loss_of_signal_count = -1; -	fcoe_stats->prim_seq_protocol_err_count = -1; -	fcoe_stats->dumped_frames = -1; -	return fcoe_stats; +	fc_stats->fcp_input_megabytes = div_u64(fcp_in_bytes, 1000000); +	fc_stats->fcp_output_megabytes = div_u64(fcp_out_bytes, 1000000); +	fc_stats->lip_count = -1; +	fc_stats->nos_count = -1; +	fc_stats->loss_of_sync_count = -1; +	fc_stats->loss_of_signal_count = -1; +	fc_stats->prim_seq_protocol_err_count = -1; +	fc_stats->dumped_frames = -1; + +	/* update exches stats */ +	fc_exch_update_stats(lport); + +	return fc_stats;  }  EXPORT_SYMBOL(fc_get_host_stats); @@ -375,7 +401,7 @@ static void fc_lport_add_fc4_type(struct fc_lport *lport, enum fc_fh_type type)  /**   * fc_lport_recv_rlir_req() - Handle received Registered Link Incident Report. - * @lport: Fibre Channel local port recieving the RLIR + * @lport: Fibre Channel local port receiving the RLIR   * @fp:	   The RLIR request frame   *   * Locking Note: The lport lock is expected to be held before calling @@ -392,7 +418,7 @@ static void fc_lport_recv_rlir_req(struct fc_lport *lport, struct fc_frame *fp)  /**   * fc_lport_recv_echo_req() - Handle received ECHO request - * @lport: The local port recieving the ECHO + * @lport: The local port receiving the ECHO   * @fp:	   ECHO request frame   *   * Locking Note: The lport lock is expected to be held before calling @@ -428,7 +454,7 @@ static void fc_lport_recv_echo_req(struct fc_lport *lport,  /**   * fc_lport_recv_rnid_req() - Handle received Request Node ID data request - * @lport: The local port recieving the RNID + * @lport: The local port receiving the RNID   * @fp:	   The RNID request frame   *   * Locking Note: The lport lock is expected to be held before calling @@ -487,10 +513,10 @@ static void fc_lport_recv_rnid_req(struct fc_lport *lport,  /**   * fc_lport_recv_logo_req() - Handle received fabric LOGO request - * @lport: The local port recieving the LOGO + * @lport: The local port receiving the LOGO   * @fp:	   The LOGO request frame   * - * Locking Note: The lport lock is exected to be held before calling + * Locking Note: The lport lock is expected to be held before calling   * this function.   */  static void fc_lport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp) @@ -629,6 +655,8 @@ int fc_lport_destroy(struct fc_lport *lport)  	lport->tt.fcp_abort_io(lport);  	lport->tt.disc_stop_final(lport);  	lport->tt.exch_mgr_reset(lport, 0, 0); +	cancel_delayed_work_sync(&lport->retry_work); +	fc_fc4_del_lport(lport);  	return 0;  }  EXPORT_SYMBOL(fc_lport_destroy); @@ -670,7 +698,8 @@ EXPORT_SYMBOL(fc_set_mfs);   * @lport: The local port receiving the event   * @event: The discovery event   */ -void fc_lport_disc_callback(struct fc_lport *lport, enum fc_disc_event event) +static void fc_lport_disc_callback(struct fc_lport *lport, +				   enum fc_disc_event event)  {  	switch (event) {  	case DISC_EV_SUCCESS: @@ -766,7 +795,7 @@ EXPORT_SYMBOL(fc_lport_set_local_id);  /**   * fc_lport_recv_flogi_req() - Receive a FLOGI request - * @lport: The local port that recieved the request + * @lport: The local port that received the request   * @rx_fp: The FLOGI frame   *   * A received FLOGI request indicates a point-to-point connection. @@ -845,7 +874,7 @@ out:  }  /** - * fc_lport_recv_req() - The generic lport request handler + * fc_lport_recv_els_req() - The generic lport ELS request handler   * @lport: The local port that received the request   * @fp:	   The request frame   * @@ -853,11 +882,11 @@ out:   * if an rport should handle the request.   *   * Locking Note: This function should not be called with the lport - *		 lock held becuase it will grab the lock. + *		 lock held because it will grab the lock.   */ -static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp) +static void fc_lport_recv_els_req(struct fc_lport *lport, +				  struct fc_frame *fp)  { -	struct fc_frame_header *fh = fc_frame_header_get(fp);  	void (*recv)(struct fc_lport *, struct fc_frame *);  	mutex_lock(&lport->lp_mutex); @@ -869,8 +898,7 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp)  	 */  	if (!lport->link_up)  		fc_frame_free(fp); -	else if (fh->fh_type == FC_TYPE_ELS && -		 fh->fh_r_ctl == FC_RCTL_ELS_REQ) { +	else {  		/*  		 * Check opcode.  		 */ @@ -899,14 +927,63 @@ static void fc_lport_recv_req(struct fc_lport *lport, struct fc_frame *fp)  		}  		recv(lport, fp); -	} else { -		FC_LPORT_DBG(lport, "dropping invalid frame (eof %x)\n", -			     fr_eof(fp)); -		fc_frame_free(fp);  	}  	mutex_unlock(&lport->lp_mutex);  } +static int fc_lport_els_prli(struct fc_rport_priv *rdata, u32 spp_len, +			     const struct fc_els_spp *spp_in, +			     struct fc_els_spp *spp_out) +{ +	return FC_SPP_RESP_INVL; +} + +struct fc4_prov fc_lport_els_prov = { +	.prli = fc_lport_els_prli, +	.recv = fc_lport_recv_els_req, +}; + +/** + * fc_lport_recv_req() - The generic lport request handler + * @lport: The lport that received the request + * @fp: The frame the request is in + * + * Locking Note: This function should not be called with the lport + *		 lock held because it may grab the lock. + */ +static void fc_lport_recv_req(struct fc_lport *lport, +			      struct fc_frame *fp) +{ +	struct fc_frame_header *fh = fc_frame_header_get(fp); +	struct fc_seq *sp = fr_seq(fp); +	struct fc4_prov *prov; + +	/* +	 * Use RCU read lock and module_lock to be sure module doesn't +	 * deregister and get unloaded while we're calling it. +	 * try_module_get() is inlined and accepts a NULL parameter. +	 * Only ELSes and FCP target ops should come through here. +	 * The locking is unfortunate, and a better scheme is being sought. +	 */ + +	rcu_read_lock(); +	if (fh->fh_type >= FC_FC4_PROV_SIZE) +		goto drop; +	prov = rcu_dereference(fc_passive_prov[fh->fh_type]); +	if (!prov || !try_module_get(prov->module)) +		goto drop; +	rcu_read_unlock(); +	prov->recv(lport, fp); +	module_put(prov->module); +	return; +drop: +	rcu_read_unlock(); +	FC_LPORT_DBG(lport, "dropping unexpected frame type %x\n", fh->fh_type); +	fc_frame_free(fp); +	if (sp) +		lport->tt.exch_done(sp); +} +  /**   * fc_lport_reset() - Reset a local port   * @lport: The local port which should be reset @@ -973,6 +1050,8 @@ static void fc_lport_enter_reset(struct fc_lport *lport)  			fc_vport_set_state(lport->vport, FC_VPORT_LINKDOWN);  	}  	fc_lport_state_enter(lport, LPORT_ST_RESET); +	fc_host_post_event(lport->host, fc_get_event_number(), +			   FCH_EVT_LIPRESET, 0);  	fc_vports_linkchange(lport);  	fc_lport_reset_locked(lport);  	if (lport->link_up) @@ -1009,7 +1088,7 @@ static void fc_lport_error(struct fc_lport *lport, struct fc_frame *fp)  {  	unsigned long delay = 0;  	FC_LPORT_DBG(lport, "Error %ld in state %s, retries %d\n", -		     PTR_ERR(fp), fc_lport_state(lport), +		     IS_ERR(fp) ? -PTR_ERR(fp) : 0, fc_lport_state(lport),  		     lport->retry_count);  	if (PTR_ERR(fp) == -FC_EX_CLOSED) @@ -1091,7 +1170,10 @@ static void fc_lport_ns_resp(struct fc_seq *sp, struct fc_frame *fp,  			fc_lport_enter_ns(lport, LPORT_ST_RFF_ID);  			break;  		case LPORT_ST_RFF_ID: -			fc_lport_enter_scr(lport); +			if (lport->fdmi_enabled) +				fc_lport_enter_fdmi(lport); +			else +				fc_lport_enter_scr(lport);  			break;  		default:  			/* should have already been caught by state checks */ @@ -1106,6 +1188,85 @@ err:  }  /** + * fc_lport_ms_resp() - Handle response to a management server + *			exchange + * @sp:	    current sequence in exchange + * @fp:	    response frame + * @lp_arg: Fibre Channel host port instance + * + * Locking Note: This function will be called without the lport lock + * held, but it will lock, call an _enter_* function or fc_lport_error() + * and then unlock the lport. + */ +static void fc_lport_ms_resp(struct fc_seq *sp, struct fc_frame *fp, +			     void *lp_arg) +{ +	struct fc_lport *lport = lp_arg; +	struct fc_frame_header *fh; +	struct fc_ct_hdr *ct; + +	FC_LPORT_DBG(lport, "Received a ms %s\n", fc_els_resp_type(fp)); + +	if (fp == ERR_PTR(-FC_EX_CLOSED)) +		return; + +	mutex_lock(&lport->lp_mutex); + +	if (lport->state < LPORT_ST_RHBA || lport->state > LPORT_ST_DPRT) { +		FC_LPORT_DBG(lport, "Received a management server response, " +			     "but in state %s\n", fc_lport_state(lport)); +		if (IS_ERR(fp)) +			goto err; +		goto out; +	} + +	if (IS_ERR(fp)) { +		fc_lport_error(lport, fp); +		goto err; +	} + +	fh = fc_frame_header_get(fp); +	ct = fc_frame_payload_get(fp, sizeof(*ct)); + +	if (fh && ct && fh->fh_type == FC_TYPE_CT && +	    ct->ct_fs_type == FC_FST_MGMT && +	    ct->ct_fs_subtype == FC_FDMI_SUBTYPE) { +		FC_LPORT_DBG(lport, "Received a management server response, " +				    "reason=%d explain=%d\n", +				    ct->ct_reason, +				    ct->ct_explan); + +		switch (lport->state) { +		case LPORT_ST_RHBA: +			if (ntohs(ct->ct_cmd) == FC_FS_ACC) +				fc_lport_enter_ms(lport, LPORT_ST_RPA); +			else /* Error Skip RPA */ +				fc_lport_enter_scr(lport); +			break; +		case LPORT_ST_RPA: +			fc_lport_enter_scr(lport); +			break; +		case LPORT_ST_DPRT: +			fc_lport_enter_ms(lport, LPORT_ST_RHBA); +			break; +		case LPORT_ST_DHBA: +			fc_lport_enter_ms(lport, LPORT_ST_DPRT); +			break; +		default: +			/* should have already been caught by state checks */ +			break; +		} +	} else { +		/* Invalid Frame? */ +		fc_lport_error(lport, fp); +	} +out: +	fc_frame_free(fp); +err: +	mutex_unlock(&lport->lp_mutex); +} + +/**   * fc_lport_scr_resp() - Handle response to State Change Register (SCR) request   * @sp:	    current sequence in SCR exchange   * @fp:	    response frame @@ -1282,6 +1443,123 @@ err:  }  /** + * fc_lport_enter_ms() - management server commands + * @lport: Fibre Channel local port to register + * + * Locking Note: The lport lock is expected to be held before calling + * this routine. + */ +static void fc_lport_enter_ms(struct fc_lport *lport, enum fc_lport_state state) +{ +	struct fc_frame *fp; +	enum fc_fdmi_req cmd; +	int size = sizeof(struct fc_ct_hdr); +	size_t len; +	int numattrs; + +	FC_LPORT_DBG(lport, "Entered %s state from %s state\n", +		     fc_lport_state_names[state], +		     fc_lport_state(lport)); + +	fc_lport_state_enter(lport, state); + +	switch (state) { +	case LPORT_ST_RHBA: +		cmd = FC_FDMI_RHBA; +		/* Number of HBA Attributes */ +		numattrs = 10; +		len = sizeof(struct fc_fdmi_rhba); +		len -= sizeof(struct fc_fdmi_attr_entry); +		len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN); +		len += FC_FDMI_HBA_ATTR_NODENAME_LEN; +		len += FC_FDMI_HBA_ATTR_MANUFACTURER_LEN; +		len += FC_FDMI_HBA_ATTR_SERIALNUMBER_LEN; +		len += FC_FDMI_HBA_ATTR_MODEL_LEN; +		len += FC_FDMI_HBA_ATTR_MODELDESCR_LEN; +		len += FC_FDMI_HBA_ATTR_HARDWAREVERSION_LEN; +		len += FC_FDMI_HBA_ATTR_DRIVERVERSION_LEN; +		len += FC_FDMI_HBA_ATTR_OPTIONROMVERSION_LEN; +		len += FC_FDMI_HBA_ATTR_FIRMWAREVERSION_LEN; +		len += FC_FDMI_HBA_ATTR_OSNAMEVERSION_LEN; + +		size += len; +		break; +	case LPORT_ST_RPA: +		cmd = FC_FDMI_RPA; +		/* Number of Port Attributes */ +		numattrs = 6; +		len = sizeof(struct fc_fdmi_rpa); +		len -= sizeof(struct fc_fdmi_attr_entry); +		len += (numattrs * FC_FDMI_ATTR_ENTRY_HEADER_LEN); +		len += FC_FDMI_PORT_ATTR_FC4TYPES_LEN; +		len += FC_FDMI_PORT_ATTR_SUPPORTEDSPEED_LEN; +		len += FC_FDMI_PORT_ATTR_CURRENTPORTSPEED_LEN; +		len += FC_FDMI_PORT_ATTR_MAXFRAMESIZE_LEN; +		len += FC_FDMI_PORT_ATTR_OSDEVICENAME_LEN; +		len += FC_FDMI_PORT_ATTR_HOSTNAME_LEN; + +		size += len; +		break; +	case LPORT_ST_DPRT: +		cmd = FC_FDMI_DPRT; +		len = sizeof(struct fc_fdmi_dprt); +		size += len; +		break; +	case LPORT_ST_DHBA: +		cmd = FC_FDMI_DHBA; +		len = sizeof(struct fc_fdmi_dhba); +		size += len; +		break; +	default: +		fc_lport_error(lport, NULL); +		return; +	} + +	FC_LPORT_DBG(lport, "Cmd=0x%x Len %d size %d\n", +			     cmd, (int)len, size); +	fp = fc_frame_alloc(lport, size); +	if (!fp) { +		fc_lport_error(lport, fp); +		return; +	} + +	if (!lport->tt.elsct_send(lport, FC_FID_MGMT_SERV, fp, cmd, +				  fc_lport_ms_resp, +				  lport, 3 * lport->r_a_tov)) +		fc_lport_error(lport, fp); +} + +/** + * fc_rport_enter_fdmi() - Create a fc_rport for the management server + * @lport: The local port requesting a remote port for the management server + * + * Locking Note: The lport lock is expected to be held before calling + * this routine. + */ +static void fc_lport_enter_fdmi(struct fc_lport *lport) +{ +	struct fc_rport_priv *rdata; + +	FC_LPORT_DBG(lport, "Entered FDMI state from %s state\n", +		     fc_lport_state(lport)); + +	fc_lport_state_enter(lport, LPORT_ST_FDMI); + +	mutex_lock(&lport->disc.disc_mutex); +	rdata = lport->tt.rport_create(lport, FC_FID_MGMT_SERV); +	mutex_unlock(&lport->disc.disc_mutex); +	if (!rdata) +		goto err; + +	rdata->ops = &fc_lport_rport_ops; +	lport->tt.rport_login(rdata); +	return; + +err: +	fc_lport_error(lport, NULL); +} + +/**   * fc_lport_timeout() - Handler for the retry_work timer   * @work: The work struct of the local port   */ @@ -1295,10 +1573,8 @@ static void fc_lport_timeout(struct work_struct *work)  	switch (lport->state) {  	case LPORT_ST_DISABLED: -		WARN_ON(1);  		break;  	case LPORT_ST_READY: -		WARN_ON(1);  		break;  	case LPORT_ST_RESET:  		break; @@ -1315,6 +1591,16 @@ static void fc_lport_timeout(struct work_struct *work)  	case LPORT_ST_RFF_ID:  		fc_lport_enter_ns(lport, lport->state);  		break; +	case LPORT_ST_FDMI: +		fc_lport_enter_fdmi(lport); +		break; +	case LPORT_ST_RHBA: +	case LPORT_ST_RPA: +	case LPORT_ST_DHBA: +	case LPORT_ST_DPRT: +		FC_LPORT_DBG(lport, "Skipping lport state %s to SCR\n", +			     fc_lport_state(lport)); +		/* fall thru */  	case LPORT_ST_SCR:  		fc_lport_enter_scr(lport);  		break; @@ -1419,6 +1705,7 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,  			 void *lp_arg)  {  	struct fc_lport *lport = lp_arg; +	struct fc_frame_header *fh;  	struct fc_els_flogi *flp;  	u32 did;  	u16 csp_flags; @@ -1446,49 +1733,66 @@ void fc_lport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,  		goto err;  	} +	fh = fc_frame_header_get(fp);  	did = fc_frame_did(fp); -	if (fc_frame_payload_op(fp) == ELS_LS_ACC && did) { -		flp = fc_frame_payload_get(fp, sizeof(*flp)); -		if (flp) { -			mfs = ntohs(flp->fl_csp.sp_bb_data) & -				FC_SP_BB_DATA_MASK; -			if (mfs >= FC_SP_MIN_MAX_PAYLOAD && -			    mfs < lport->mfs) -				lport->mfs = mfs; -			csp_flags = ntohs(flp->fl_csp.sp_features); -			r_a_tov = ntohl(flp->fl_csp.sp_r_a_tov); -			e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov); -			if (csp_flags & FC_SP_FT_EDTR) -				e_d_tov /= 1000000; - -			lport->npiv_enabled = !!(csp_flags & FC_SP_FT_NPIV_ACC); - -			if ((csp_flags & FC_SP_FT_FPORT) == 0) { -				if (e_d_tov > lport->e_d_tov) -					lport->e_d_tov = e_d_tov; -				lport->r_a_tov = 2 * e_d_tov; -				fc_lport_set_port_id(lport, did, fp); -				printk(KERN_INFO "host%d: libfc: " -				       "Port (%6.6x) entered " -				       "point-to-point mode\n", -				       lport->host->host_no, did); -				fc_lport_ptp_setup(lport, fc_frame_sid(fp), -						   get_unaligned_be64( -							   &flp->fl_wwpn), -						   get_unaligned_be64( -							   &flp->fl_wwnn)); -			} else { -				lport->e_d_tov = e_d_tov; -				lport->r_a_tov = r_a_tov; -				fc_host_fabric_name(lport->host) = -					get_unaligned_be64(&flp->fl_wwnn); -				fc_lport_set_port_id(lport, did, fp); -				fc_lport_enter_dns(lport); -			} -		} -	} else { -		FC_LPORT_DBG(lport, "FLOGI RJT or bad response\n"); +	if (fh->fh_r_ctl != FC_RCTL_ELS_REP || did == 0 || +	    fc_frame_payload_op(fp) != ELS_LS_ACC) { +		FC_LPORT_DBG(lport, "FLOGI not accepted or bad response\n");  		fc_lport_error(lport, fp); +		goto err; +	} + +	flp = fc_frame_payload_get(fp, sizeof(*flp)); +	if (!flp) { +		FC_LPORT_DBG(lport, "FLOGI bad response\n"); +		fc_lport_error(lport, fp); +		goto err; +	} + +	mfs = ntohs(flp->fl_csp.sp_bb_data) & +		FC_SP_BB_DATA_MASK; + +	if (mfs < FC_SP_MIN_MAX_PAYLOAD || mfs > FC_SP_MAX_MAX_PAYLOAD) { +		FC_LPORT_DBG(lport, "FLOGI bad mfs:%hu response, " +			     "lport->mfs:%hu\n", mfs, lport->mfs); +		fc_lport_error(lport, fp); +		goto err; +	} + +	if (mfs <= lport->mfs) { +		lport->mfs = mfs; +		fc_host_maxframe_size(lport->host) = mfs; +	} + +	csp_flags = ntohs(flp->fl_csp.sp_features); +	r_a_tov = ntohl(flp->fl_csp.sp_r_a_tov); +	e_d_tov = ntohl(flp->fl_csp.sp_e_d_tov); +	if (csp_flags & FC_SP_FT_EDTR) +		e_d_tov /= 1000000; + +	lport->npiv_enabled = !!(csp_flags & FC_SP_FT_NPIV_ACC); + +	if ((csp_flags & FC_SP_FT_FPORT) == 0) { +		if (e_d_tov > lport->e_d_tov) +			lport->e_d_tov = e_d_tov; +		lport->r_a_tov = 2 * e_d_tov; +		fc_lport_set_port_id(lport, did, fp); +		printk(KERN_INFO "host%d: libfc: " +		       "Port (%6.6x) entered " +		       "point-to-point mode\n", +		       lport->host->host_no, did); +		fc_lport_ptp_setup(lport, fc_frame_sid(fp), +				   get_unaligned_be64( +					   &flp->fl_wwpn), +				   get_unaligned_be64( +					   &flp->fl_wwnn)); +	} else { +		lport->e_d_tov = e_d_tov; +		lport->r_a_tov = r_a_tov; +		fc_host_fabric_name(lport->host) = +			get_unaligned_be64(&flp->fl_wwnn); +		fc_lport_set_port_id(lport, did, fp); +		fc_lport_enter_dns(lport);  	}  out: @@ -1505,7 +1809,7 @@ EXPORT_SYMBOL(fc_lport_flogi_resp);   * Locking Note: The lport lock is expected to be held before calling   * this routine.   */ -void fc_lport_enter_flogi(struct fc_lport *lport) +static void fc_lport_enter_flogi(struct fc_lport *lport)  {  	struct fc_frame *fp; @@ -1545,6 +1849,7 @@ int fc_lport_config(struct fc_lport *lport)  	fc_lport_add_fc4_type(lport, FC_TYPE_FCP);  	fc_lport_add_fc4_type(lport, FC_TYPE_CT); +	fc_fc4_conf_lport_params(lport, FC_TYPE_FCP);  	return 0;  } @@ -1582,6 +1887,7 @@ int fc_lport_init(struct fc_lport *lport)  		fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_1GBIT;  	if (lport->link_supported_speeds & FC_PORTSPEED_10GBIT)  		fc_host_supported_speeds(lport->host) |= FC_PORTSPEED_10GBIT; +	fc_fc4_add_lport(lport);  	return 0;  } @@ -1632,7 +1938,7 @@ static void fc_lport_bsg_resp(struct fc_seq *sp, struct fc_frame *fp,  	job->reply->reply_payload_rcv_len +=  		fc_copy_buffer_to_sglist(buf, len, info->sg, &info->nents, -					 &info->offset, KM_BIO_SRC_IRQ, NULL); +					 &info->offset, NULL);  	if (fr_eof(fp) == FC_EOF_T &&  	    (ntoh24(fh->fh_f_ctl) & (FC_FC_LAST_SEQ | FC_FC_END_SEQ)) == @@ -1703,8 +2009,10 @@ static int fc_lport_els_request(struct fc_bsg_job *job,  	info->sg = job->reply_payload.sg_list;  	if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp, -				     NULL, info, tov)) +				     NULL, info, tov)) { +		kfree(info);  		return -ECOMM; +	}  	return 0;  } @@ -1762,8 +2070,10 @@ static int fc_lport_ct_request(struct fc_bsg_job *job,  	info->sg = job->reply_payload.sg_list;  	if (!lport->tt.exch_seq_send(lport, fp, fc_lport_bsg_resp, -				     NULL, info, tov)) +				     NULL, info, tov)) { +		kfree(info);  		return -ECOMM; +	}  	return 0;  } diff --git a/drivers/scsi/libfc/fc_npiv.c b/drivers/scsi/libfc/fc_npiv.c index dd2b43bb1c7..9fbf78ed821 100644 --- a/drivers/scsi/libfc/fc_npiv.c +++ b/drivers/scsi/libfc/fc_npiv.c @@ -22,6 +22,7 @@   */  #include <scsi/libfc.h> +#include <linux/export.h>  /**   * fc_vport_create() - Create a new NPIV vport instance @@ -37,9 +38,7 @@ struct fc_lport *libfc_vport_create(struct fc_vport *vport, int privsize)  	vn_port = libfc_host_alloc(shost->hostt, privsize);  	if (!vn_port) -		goto err_out; -	if (fc_exch_mgr_list_clone(n_port, vn_port)) -		goto err_put; +		return vn_port;  	vn_port->vport = vport;  	vport->dd_data = vn_port; @@ -49,11 +48,6 @@ struct fc_lport *libfc_vport_create(struct fc_vport *vport, int privsize)  	mutex_unlock(&n_port->lp_mutex);  	return vn_port; - -err_put: -	scsi_host_put(vn_port->host); -err_out: -	return NULL;  }  EXPORT_SYMBOL(libfc_vport_create); @@ -86,6 +80,7 @@ struct fc_lport *fc_vport_id_lookup(struct fc_lport *n_port, u32 port_id)  	return lport;  } +EXPORT_SYMBOL(fc_vport_id_lookup);  /*   * When setting the link state of vports during an lport state change, it's diff --git a/drivers/scsi/libfc/fc_rport.c b/drivers/scsi/libfc/fc_rport.c index a84ef13ed74..589ff9aedd3 100644 --- a/drivers/scsi/libfc/fc_rport.c +++ b/drivers/scsi/libfc/fc_rport.c @@ -51,6 +51,7 @@  #include <linux/rcupdate.h>  #include <linux/timer.h>  #include <linux/workqueue.h> +#include <linux/export.h>  #include <asm/unaligned.h>  #include <scsi/libfc.h> @@ -58,7 +59,7 @@  #include "fc_libfc.h" -struct workqueue_struct *rport_event_queue; +static struct workqueue_struct *rport_event_queue;  static void fc_rport_enter_flogi(struct fc_rport_priv *);  static void fc_rport_enter_plogi(struct fc_rport_priv *); @@ -145,24 +146,14 @@ static struct fc_rport_priv *fc_rport_create(struct fc_lport *lport,  	rdata->maxframe_size = FC_MIN_MAX_PAYLOAD;  	INIT_DELAYED_WORK(&rdata->retry_work, fc_rport_timeout);  	INIT_WORK(&rdata->event_work, fc_rport_work); -	if (port_id != FC_FID_DIR_SERV) +	if (port_id != FC_FID_DIR_SERV) { +		rdata->lld_event_callback = lport->tt.rport_event_callback;  		list_add_rcu(&rdata->peers, &lport->disc.rports); +	}  	return rdata;  }  /** - * fc_rport_free_rcu() - Free a remote port - * @rcu: The rcu_head structure inside the remote port - */ -static void fc_rport_free_rcu(struct rcu_head *rcu) -{ -	struct fc_rport_priv *rdata; - -	rdata = container_of(rcu, struct fc_rport_priv, rcu); -	kfree(rdata); -} - -/**   * fc_rport_destroy() - Free a remote port after last reference is released   * @kref: The remote port's kref   */ @@ -171,7 +162,7 @@ static void fc_rport_destroy(struct kref *kref)  	struct fc_rport_priv *rdata;  	rdata = container_of(kref, struct fc_rport_priv, kref); -	call_rcu(&rdata->rcu, fc_rport_free_rcu); +	kfree_rcu(rdata, rcu);  }  /** @@ -257,6 +248,8 @@ static void fc_rport_work(struct work_struct *work)  	struct fc_rport_operations *rport_ops;  	struct fc_rport_identifiers ids;  	struct fc_rport *rport; +	struct fc4_prov *prov; +	u8 type;  	mutex_lock(&rdata->rp_mutex);  	event = rdata->event; @@ -300,12 +293,25 @@ static void fc_rport_work(struct work_struct *work)  			FC_RPORT_DBG(rdata, "callback ev %d\n", event);  			rport_ops->event_callback(lport, rdata, event);  		} +		if (rdata->lld_event_callback) { +			FC_RPORT_DBG(rdata, "lld callback ev %d\n", event); +			rdata->lld_event_callback(lport, rdata, event); +		}  		kref_put(&rdata->kref, lport->tt.rport_destroy);  		break;  	case RPORT_EV_FAILED:  	case RPORT_EV_LOGO:  	case RPORT_EV_STOP: +		if (rdata->prli_count) { +			mutex_lock(&fc_prov_mutex); +			for (type = 1; type < FC_FC4_PROV_SIZE; type++) { +				prov = fc_passive_prov[type]; +				if (prov && prov->prlo) +					prov->prlo(rdata); +			} +			mutex_unlock(&fc_prov_mutex); +		}  		port_id = rdata->ids.port_id;  		mutex_unlock(&rdata->rp_mutex); @@ -313,6 +319,10 @@ static void fc_rport_work(struct work_struct *work)  			FC_RPORT_DBG(rdata, "callback ev %d\n", event);  			rport_ops->event_callback(lport, rdata, event);  		} +		if (rdata->lld_event_callback) { +			FC_RPORT_DBG(rdata, "lld callback ev %d\n", event); +			rdata->lld_event_callback(lport, rdata, event); +		}  		cancel_delayed_work_sync(&rdata->retry_work);  		/* @@ -336,6 +346,7 @@ static void fc_rport_work(struct work_struct *work)  			if (port_id == FC_FID_DIR_SERV) {  				rdata->event = RPORT_EV_NONE;  				mutex_unlock(&rdata->rp_mutex); +				kref_put(&rdata->kref, lport->tt.rport_destroy);  			} else if ((rdata->flags & FC_RP_STARTED) &&  				   rdata->major_retries <  				   lport->max_rport_retry_count) { @@ -380,7 +391,7 @@ static void fc_rport_work(struct work_struct *work)   * If it appears we are already logged in, ADISC is used to verify   * the setup.   */ -int fc_rport_login(struct fc_rport_priv *rdata) +static int fc_rport_login(struct fc_rport_priv *rdata)  {  	mutex_lock(&rdata->rp_mutex); @@ -440,7 +451,7 @@ static void fc_rport_enter_delete(struct fc_rport_priv *rdata,   * function will hold the rport lock, call an _enter_*   * function and then unlock the rport.   */ -int fc_rport_logoff(struct fc_rport_priv *rdata) +static int fc_rport_logoff(struct fc_rport_priv *rdata)  {  	mutex_lock(&rdata->rp_mutex); @@ -571,11 +582,11 @@ static void fc_rport_error(struct fc_rport_priv *rdata, struct fc_frame *fp)  static void fc_rport_error_retry(struct fc_rport_priv *rdata,  				 struct fc_frame *fp)  { -	unsigned long delay = FC_DEF_E_D_TOV; +	unsigned long delay = msecs_to_jiffies(FC_DEF_E_D_TOV);  	/* make sure this isn't an FC_EX_CLOSED error, never retry those */  	if (PTR_ERR(fp) == -FC_EX_CLOSED) -		return fc_rport_error(rdata, fp); +		goto out;  	if (rdata->retries < rdata->local_port->max_rport_retry_count) {  		FC_RPORT_DBG(rdata, "Error %ld in state %s, retrying\n", @@ -588,7 +599,8 @@ static void fc_rport_error_retry(struct fc_rport_priv *rdata,  		return;  	} -	return fc_rport_error(rdata, fp); +out: +	fc_rport_error(rdata, fp);  }  /** @@ -641,8 +653,8 @@ static int fc_rport_login_complete(struct fc_rport_priv *rdata,   * @fp:	    The FLOGI response frame   * @rp_arg: The remote port that received the FLOGI response   */ -void fc_rport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, -			 void *rp_arg) +static void fc_rport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp, +				void *rp_arg)  {  	struct fc_rport_priv *rdata = rp_arg;  	struct fc_lport *lport = rdata->local_port; @@ -652,7 +664,7 @@ void fc_rport_flogi_resp(struct fc_seq *sp, struct fc_frame *fp,  	FC_RPORT_DBG(rdata, "Received a FLOGI %s\n", fc_els_resp_type(fp));  	if (fp == ERR_PTR(-FC_EX_CLOSED)) -		return; +		goto put;  	mutex_lock(&rdata->rp_mutex); @@ -689,6 +701,7 @@ out:  	fc_frame_free(fp);  err:  	mutex_unlock(&rdata->rp_mutex); +put:  	kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);  	return;  bad: @@ -777,6 +790,20 @@ static void fc_rport_recv_flogi_req(struct fc_lport *lport,  	switch (rdata->rp_state) {  	case RPORT_ST_INIT: +		/* +		 * If received the FLOGI request on RPORT which is INIT state +		 * (means not transition to FLOGI either fc_rport timeout +		 * function didn;t trigger or this end hasn;t received +		 * beacon yet from other end. In that case only, allow RPORT +		 * state machine to continue, otherwise fall through which +		 * causes the code to send reject response. +		 * NOTE; Not checking for FIP->state such as VNMP_UP or +		 * VNMP_CLAIM because if FIP state is not one of those, +		 * RPORT wouldn;t have created and 'rport_lookup' would have +		 * failed anyway in that case. +		 */ +		if (lport->point_to_multipoint) +			break;  	case RPORT_ST_DELETE:  		mutex_unlock(&rdata->rp_mutex);  		rjt_data.reason = ELS_RJT_FIP; @@ -877,6 +904,9 @@ static void fc_rport_plogi_resp(struct fc_seq *sp, struct fc_frame *fp,  		rdata->ids.port_name = get_unaligned_be64(&plp->fl_wwpn);  		rdata->ids.node_name = get_unaligned_be64(&plp->fl_wwnn); +		/* save plogi response sp_features for further reference */ +		rdata->sp_features = ntohs(plp->fl_csp.sp_features); +  		if (lport->point_to_multipoint)  			fc_rport_login_complete(rdata, fp);  		csp_seq = ntohs(plp->fl_csp.sp_tot_seq); @@ -896,6 +926,20 @@ err:  	kref_put(&rdata->kref, rdata->local_port->tt.rport_destroy);  } +static bool +fc_rport_compatible_roles(struct fc_lport *lport, struct fc_rport_priv *rdata) +{ +	if (rdata->ids.roles == FC_PORT_ROLE_UNKNOWN) +		return true; +	if ((rdata->ids.roles & FC_PORT_ROLE_FCP_TARGET) && +	    (lport->service_params & FCP_SPPF_INIT_FCN)) +		return true; +	if ((rdata->ids.roles & FC_PORT_ROLE_FCP_INITIATOR) && +	    (lport->service_params & FCP_SPPF_TARG_FCN)) +		return true; +	return false; +} +  /**   * fc_rport_enter_plogi() - Send Port Login (PLOGI) request   * @rdata: The remote port to send a PLOGI to @@ -908,6 +952,12 @@ static void fc_rport_enter_plogi(struct fc_rport_priv *rdata)  	struct fc_lport *lport = rdata->local_port;  	struct fc_frame *fp; +	if (!fc_rport_compatible_roles(lport, rdata)) { +		FC_RPORT_DBG(rdata, "PLOGI suppressed for incompatible role\n"); +		fc_rport_state_enter(rdata, RPORT_ST_PLOGI_WAIT); +		return; +	} +  	FC_RPORT_DBG(rdata, "Port entered PLOGI state from %s state\n",  		     fc_rport_state(rdata)); @@ -948,6 +998,8 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,  		struct fc_els_prli prli;  		struct fc_els_spp spp;  	} *pp; +	struct fc_els_spp temp_spp; +	struct fc4_prov *prov;  	u32 roles = FC_RPORT_ROLE_UNKNOWN;  	u32 fcp_parm = 0;  	u8 op; @@ -982,6 +1034,7 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,  		resp_code = (pp->spp.spp_flags & FC_SPP_RESP_MASK);  		FC_RPORT_DBG(rdata, "PRLI spp_flags = 0x%x\n",  			     pp->spp.spp_flags); +		rdata->spp_type = pp->spp.spp_type;  		if (resp_code != FC_SPP_RESP_ACK) {  			if (resp_code == FC_SPP_RESP_CONF)  				fc_rport_error(rdata, fp); @@ -995,6 +1048,15 @@ static void fc_rport_prli_resp(struct fc_seq *sp, struct fc_frame *fp,  		fcp_parm = ntohl(pp->spp.spp_params);  		if (fcp_parm & FCP_SPPF_RETRY)  			rdata->flags |= FC_RP_FLAGS_RETRY; +		if (fcp_parm & FCP_SPPF_CONF_COMPL) +			rdata->flags |= FC_RP_FLAGS_CONF_REQ; + +		prov = fc_passive_prov[FC_TYPE_FCP]; +		if (prov) { +			memset(&temp_spp, 0, sizeof(temp_spp)); +			prov->prli(rdata, pp->prli.prli_spp_len, +				   &pp->spp, &temp_spp); +		}  		rdata->supported_classes = FC_COS_CLASS3;  		if (fcp_parm & FCP_SPPF_INIT_FCN) @@ -1032,6 +1094,7 @@ static void fc_rport_enter_prli(struct fc_rport_priv *rdata)  		struct fc_els_spp spp;  	} *pp;  	struct fc_frame *fp; +	struct fc4_prov *prov;  	/*  	 * If the rport is one of the well known addresses @@ -1053,9 +1116,20 @@ static void fc_rport_enter_prli(struct fc_rport_priv *rdata)  		return;  	} -	if (!lport->tt.elsct_send(lport, rdata->ids.port_id, fp, ELS_PRLI, -				  fc_rport_prli_resp, rdata, -				  2 * lport->r_a_tov)) +	fc_prli_fill(lport, fp); + +	prov = fc_passive_prov[FC_TYPE_FCP]; +	if (prov) { +		pp = fc_frame_payload_get(fp, sizeof(*pp)); +		prov->prli(rdata, sizeof(pp->spp), NULL, &pp->spp); +	} + +	fc_fill_fc_hdr(fp, FC_RCTL_ELS_REQ, rdata->ids.port_id, +		       fc_host_port_id(lport->host), FC_TYPE_ELS, +		       FC_FC_FIRST_SEQ | FC_FC_END_SEQ | FC_FC_SEQ_INIT, 0); + +	if (!lport->tt.exch_seq_send(lport, fp, fc_rport_prli_resp, +				    NULL, rdata, 2 * lport->r_a_tov))  		fc_rport_error_retry(rdata, NULL);  	else  		kref_get(&rdata->kref); @@ -1466,7 +1540,7 @@ reject:   *   * Locking Note: Called with the lport lock held.   */ -void fc_rport_recv_req(struct fc_lport *lport, struct fc_frame *fp) +static void fc_rport_recv_req(struct fc_lport *lport, struct fc_frame *fp)  {  	struct fc_seq_els_data els_data; @@ -1592,6 +1666,13 @@ static void fc_rport_recv_plogi_req(struct fc_lport *lport,  		rjt_data.explan = ELS_EXPL_NONE;  		goto reject;  	} +	if (!fc_rport_compatible_roles(lport, rdata)) { +		FC_RPORT_DBG(rdata, "Received PLOGI for incompatible role\n"); +		mutex_unlock(&rdata->rp_mutex); +		rjt_data.reason = ELS_RJT_LOGIC; +		rjt_data.explan = ELS_EXPL_NONE; +		goto reject; +	}  	/*  	 * Get session payload size from incoming PLOGI. @@ -1624,7 +1705,7 @@ reject:   * @rdata: The remote port that sent the PRLI request   * @rx_fp: The PRLI request frame   * - * Locking Note: The rport lock is exected to be held before calling + * Locking Note: The rport lock is expected to be held before calling   * this function.   */  static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata, @@ -1641,9 +1722,9 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,  	unsigned int len;  	unsigned int plen;  	enum fc_els_spp_resp resp; +	enum fc_els_spp_resp passive;  	struct fc_seq_els_data rjt_data; -	u32 fcp_parm; -	u32 roles = FC_RPORT_ROLE_UNKNOWN; +	struct fc4_prov *prov;  	FC_RPORT_DBG(rdata, "Received PRLI request while in state %s\n",  		     fc_rport_state(rdata)); @@ -1677,46 +1758,42 @@ static void fc_rport_recv_prli_req(struct fc_rport_priv *rdata,  	pp->prli.prli_len = htons(len);  	len -= sizeof(struct fc_els_prli); -	/* reinitialize remote port roles */ -	rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN; -  	/*  	 * Go through all the service parameter pages and build  	 * response.  If plen indicates longer SPP than standard,  	 * use that.  The entire response has been pre-cleared above.  	 */  	spp = &pp->spp; +	mutex_lock(&fc_prov_mutex);  	while (len >= plen) { +		rdata->spp_type = rspp->spp_type;  		spp->spp_type = rspp->spp_type;  		spp->spp_type_ext = rspp->spp_type_ext; -		spp->spp_flags = rspp->spp_flags & FC_SPP_EST_IMG_PAIR; -		resp = FC_SPP_RESP_ACK; - -		switch (rspp->spp_type) { -		case 0:	/* common to all FC-4 types */ -			break; -		case FC_TYPE_FCP: -			fcp_parm = ntohl(rspp->spp_params); -			if (fcp_parm & FCP_SPPF_RETRY) -				rdata->flags |= FC_RP_FLAGS_RETRY; -			rdata->supported_classes = FC_COS_CLASS3; -			if (fcp_parm & FCP_SPPF_INIT_FCN) -				roles |= FC_RPORT_ROLE_FCP_INITIATOR; -			if (fcp_parm & FCP_SPPF_TARG_FCN) -				roles |= FC_RPORT_ROLE_FCP_TARGET; -			rdata->ids.roles = roles; - -			spp->spp_params = htonl(lport->service_params); -			break; -		default: -			resp = FC_SPP_RESP_INVL; -			break; +		resp = 0; + +		if (rspp->spp_type < FC_FC4_PROV_SIZE) { +			prov = fc_active_prov[rspp->spp_type]; +			if (prov) +				resp = prov->prli(rdata, plen, rspp, spp); +			prov = fc_passive_prov[rspp->spp_type]; +			if (prov) { +				passive = prov->prli(rdata, plen, rspp, spp); +				if (!resp || passive == FC_SPP_RESP_ACK) +					resp = passive; +			} +		} +		if (!resp) { +			if (spp->spp_flags & FC_SPP_EST_IMG_PAIR) +				resp |= FC_SPP_RESP_CONF; +			else +				resp |= FC_SPP_RESP_INVL;  		}  		spp->spp_flags |= resp;  		len -= plen;  		rspp = (struct fc_els_spp *)((char *)rspp + plen);  		spp = (struct fc_els_spp *)((char *)spp + plen);  	} +	mutex_unlock(&fc_prov_mutex);  	/*  	 * Send LS_ACC.	 If this fails, the originator should retry. @@ -1747,7 +1824,7 @@ drop:   * @rdata: The remote port that sent the PRLO request   * @rx_fp: The PRLO request frame   * - * Locking Note: The rport lock is exected to be held before calling + * Locking Note: The rport lock is expected to be held before calling   * this function.   */  static void fc_rport_recv_prlo_req(struct fc_rport_priv *rdata, @@ -1818,7 +1895,7 @@ drop:   * @lport: The local port that received the LOGO request   * @fp:	   The LOGO request frame   * - * Locking Note: The rport lock is exected to be held before calling + * Locking Note: The rport lock is expected to be held before calling   * this function.   */  static void fc_rport_recv_logo_req(struct fc_lport *lport, struct fc_frame *fp) @@ -1886,9 +1963,82 @@ int fc_rport_init(struct fc_lport *lport)  EXPORT_SYMBOL(fc_rport_init);  /** + * fc_rport_fcp_prli() - Handle incoming PRLI for the FCP initiator. + * @rdata: remote port private + * @spp_len: service parameter page length + * @rspp: received service parameter page + * @spp: response service parameter page + * + * Returns the value for the response code to be placed in spp_flags; + * Returns 0 if not an initiator. + */ +static int fc_rport_fcp_prli(struct fc_rport_priv *rdata, u32 spp_len, +			     const struct fc_els_spp *rspp, +			     struct fc_els_spp *spp) +{ +	struct fc_lport *lport = rdata->local_port; +	u32 fcp_parm; + +	fcp_parm = ntohl(rspp->spp_params); +	rdata->ids.roles = FC_RPORT_ROLE_UNKNOWN; +	if (fcp_parm & FCP_SPPF_INIT_FCN) +		rdata->ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR; +	if (fcp_parm & FCP_SPPF_TARG_FCN) +		rdata->ids.roles |= FC_RPORT_ROLE_FCP_TARGET; +	if (fcp_parm & FCP_SPPF_RETRY) +		rdata->flags |= FC_RP_FLAGS_RETRY; +	rdata->supported_classes = FC_COS_CLASS3; + +	if (!(lport->service_params & FCP_SPPF_INIT_FCN)) +		return 0; + +	spp->spp_flags |= rspp->spp_flags & FC_SPP_EST_IMG_PAIR; + +	/* +	 * OR in our service parameters with other providers (target), if any. +	 */ +	fcp_parm = ntohl(spp->spp_params); +	spp->spp_params = htonl(fcp_parm | lport->service_params); +	return FC_SPP_RESP_ACK; +} + +/* + * FC-4 provider ops for FCP initiator. + */ +struct fc4_prov fc_rport_fcp_init = { +	.prli = fc_rport_fcp_prli, +}; + +/** + * fc_rport_t0_prli() - Handle incoming PRLI parameters for type 0 + * @rdata: remote port private + * @spp_len: service parameter page length + * @rspp: received service parameter page + * @spp: response service parameter page + */ +static int fc_rport_t0_prli(struct fc_rport_priv *rdata, u32 spp_len, +			    const struct fc_els_spp *rspp, +			    struct fc_els_spp *spp) +{ +	if (rspp->spp_flags & FC_SPP_EST_IMG_PAIR) +		return FC_SPP_RESP_INVL; +	return FC_SPP_RESP_ACK; +} + +/* + * FC-4 provider ops for type 0 service parameters. + * + * This handles the special case of type 0 which is always successful + * but doesn't do anything otherwise. + */ +struct fc4_prov fc_rport_t0_prov = { +	.prli = fc_rport_t0_prli, +}; + +/**   * fc_setup_rport() - Initialize the rport_event_queue   */ -int fc_setup_rport() +int fc_setup_rport(void)  {  	rport_event_queue = create_singlethread_workqueue("fc_rport_eq");  	if (!rport_event_queue) @@ -1899,7 +2049,7 @@ int fc_setup_rport()  /**   * fc_destroy_rport() - Destroy the rport_event_queue   */ -void fc_destroy_rport() +void fc_destroy_rport(void)  {  	destroy_workqueue(rport_event_queue);  }  | 
