diff options
Diffstat (limited to 'drivers/target/tcm_fc')
| -rw-r--r-- | drivers/target/tcm_fc/tcm_fc.h | 14 | ||||
| -rw-r--r-- | drivers/target/tcm_fc/tfc_cmd.c | 44 | ||||
| -rw-r--r-- | drivers/target/tcm_fc/tfc_conf.c | 100 | ||||
| -rw-r--r-- | drivers/target/tcm_fc/tfc_io.c | 17 | ||||
| -rw-r--r-- | drivers/target/tcm_fc/tfc_sess.c | 11 | 
5 files changed, 116 insertions, 70 deletions
diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h index 0dd54a44abc..a0bcfd3e7e7 100644 --- a/drivers/target/tcm_fc/tcm_fc.h +++ b/drivers/target/tcm_fc/tcm_fc.h @@ -22,6 +22,7 @@  #define FT_NAMELEN 32		/* length of ASCII WWPNs including pad */  #define FT_TPG_NAMELEN 32	/* max length of TPG name */  #define FT_LUN_NAMELEN 32	/* max length of LUN name */ +#define TCM_FC_DEFAULT_TAGS 512	/* tags used for per-session preallocation */  struct ft_transport_id {  	__u8	format; @@ -93,20 +94,19 @@ struct ft_lun {   */  struct ft_tpg {  	u32 index; -	struct ft_lport_acl *lport_acl; +	struct ft_lport_wwn *lport_wwn;  	struct ft_tport *tport;		/* active tport or NULL */ -	struct list_head list;		/* linkage in ft_lport_acl tpg_list */  	struct list_head lun_list;	/* head of LUNs */  	struct se_portal_group se_tpg;  	struct workqueue_struct *workqueue;  }; -struct ft_lport_acl { +struct ft_lport_wwn {  	u64 wwpn;  	char name[FT_NAMELEN]; -	struct list_head list; -	struct list_head tpg_list; -	struct se_wwn fc_lport_wwn; +	struct list_head ft_wwn_node; +	struct ft_tpg *tpg; +	struct se_wwn se_wwn;  };  /* @@ -127,7 +127,6 @@ struct ft_cmd {  	u32 sg_cnt;			/* No. of item in scatterlist */  }; -extern struct list_head ft_lport_list;  extern struct mutex ft_lport_lock;  extern struct fc4_prov ft_prov;  extern struct target_fabric_configfs *ft_configfs; @@ -162,6 +161,7 @@ int ft_write_pending_status(struct se_cmd *);  u32 ft_get_task_tag(struct se_cmd *);  int ft_get_cmd_state(struct se_cmd *);  void ft_queue_tm_resp(struct se_cmd *); +void ft_aborted_task(struct se_cmd *);  /*   * other internal functions. diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c index 0e5a1caed17..be0c0d08c56 100644 --- a/drivers/target/tcm_fc/tfc_cmd.c +++ b/drivers/target/tcm_fc/tfc_cmd.c @@ -28,6 +28,7 @@  #include <linux/configfs.h>  #include <linux/ctype.h>  #include <linux/hash.h> +#include <linux/percpu_ida.h>  #include <asm/unaligned.h>  #include <scsi/scsi.h>  #include <scsi/scsi_host.h> @@ -89,16 +90,18 @@ static void ft_free_cmd(struct ft_cmd *cmd)  {  	struct fc_frame *fp;  	struct fc_lport *lport; +	struct ft_sess *sess;  	if (!cmd)  		return; +	sess = cmd->sess;  	fp = cmd->req_frame;  	lport = fr_dev(fp);  	if (fr_seq(fp))  		lport->tt.seq_release(fr_seq(fp));  	fc_frame_free(fp); -	ft_sess_put(cmd->sess);	/* undo get from lookup at recv */ -	kfree(cmd); +	percpu_ida_free(&sess->se_sess->sess_tag_pool, cmd->se_cmd.map_tag); +	ft_sess_put(sess);	/* undo get from lookup at recv */  }  void ft_release_cmd(struct se_cmd *se_cmd) @@ -125,6 +128,7 @@ int ft_queue_status(struct se_cmd *se_cmd)  	struct fc_lport *lport;  	struct fc_exch *ep;  	size_t len; +	int rc;  	if (cmd->aborted)  		return 0; @@ -134,9 +138,10 @@ int ft_queue_status(struct se_cmd *se_cmd)  	len = sizeof(*fcp) + se_cmd->scsi_sense_length;  	fp = fc_frame_alloc(lport, len);  	if (!fp) { -		/* XXX shouldn't just drop it - requeue and retry? */ -		return 0; +		se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL; +		return -ENOMEM;  	} +  	fcp = fc_frame_payload_get(fp, len);  	memset(fcp, 0, len);  	fcp->resp.fr_status = se_cmd->scsi_status; @@ -167,7 +172,18 @@ int ft_queue_status(struct se_cmd *se_cmd)  	fc_fill_fc_hdr(fp, FC_RCTL_DD_CMD_STATUS, ep->did, ep->sid, FC_TYPE_FCP,  		       FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ, 0); -	lport->tt.seq_send(lport, cmd->seq, fp); +	rc = lport->tt.seq_send(lport, cmd->seq, fp); +	if (rc) { +		pr_info_ratelimited("%s: Failed to send response frame %p, " +				    "xid <0x%x>\n", __func__, fp, ep->xid); +		/* +		 * Generate a TASK_SET_FULL status to notify the initiator +		 * to reduce it's queue_depth after the se_cmd response has +		 * been re-queued by target-core. +		 */ +		se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL; +		return -ENOMEM; +	}  	lport->tt.exch_done(cmd->seq);  	return 0;  } @@ -423,6 +439,11 @@ void ft_queue_tm_resp(struct se_cmd *se_cmd)  	ft_send_resp_code(cmd, code);  } +void ft_aborted_task(struct se_cmd *se_cmd) +{ +	return; +} +  static void ft_send_work(struct work_struct *work);  /* @@ -432,14 +453,21 @@ static void ft_recv_cmd(struct ft_sess *sess, struct fc_frame *fp)  {  	struct ft_cmd *cmd;  	struct fc_lport *lport = sess->tport->lport; +	struct se_session *se_sess = sess->se_sess; +	int tag; -	cmd = kzalloc(sizeof(*cmd), GFP_ATOMIC); -	if (!cmd) +	tag = percpu_ida_alloc(&se_sess->sess_tag_pool, TASK_RUNNING); +	if (tag < 0)  		goto busy; + +	cmd = &((struct ft_cmd *)se_sess->sess_cmd_map)[tag]; +	memset(cmd, 0, sizeof(struct ft_cmd)); + +	cmd->se_cmd.map_tag = tag;  	cmd->sess = sess;  	cmd->seq = lport->tt.seq_assign(lport, fp);  	if (!cmd->seq) { -		kfree(cmd); +		percpu_ida_free(&se_sess->sess_tag_pool, tag);  		goto busy;  	}  	cmd->req_frame = fp;		/* hold frame during cmd */ diff --git a/drivers/target/tcm_fc/tfc_conf.c b/drivers/target/tcm_fc/tfc_conf.c index 4e0050840a7..efdcb9663a1 100644 --- a/drivers/target/tcm_fc/tfc_conf.c +++ b/drivers/target/tcm_fc/tfc_conf.c @@ -50,7 +50,7 @@  struct target_fabric_configfs *ft_configfs; -LIST_HEAD(ft_lport_list); +static LIST_HEAD(ft_wwn_list);  DEFINE_MUTEX(ft_lport_lock);  unsigned int ft_debug_logging; @@ -267,7 +267,7 @@ struct ft_node_acl *ft_acl_get(struct ft_tpg *tpg, struct fc_rport_priv *rdata)  	return found;  } -struct se_node_acl *ft_tpg_alloc_fabric_acl(struct se_portal_group *se_tpg) +static struct se_node_acl *ft_tpg_alloc_fabric_acl(struct se_portal_group *se_tpg)  {  	struct ft_node_acl *acl; @@ -298,7 +298,7 @@ static struct se_portal_group *ft_add_tpg(  	struct config_group *group,  	const char *name)  { -	struct ft_lport_acl *lacl; +	struct ft_lport_wwn *ft_wwn;  	struct ft_tpg *tpg;  	struct workqueue_struct *wq;  	unsigned long index; @@ -318,12 +318,17 @@ static struct se_portal_group *ft_add_tpg(  	if (index > UINT_MAX)  		return NULL; -	lacl = container_of(wwn, struct ft_lport_acl, fc_lport_wwn); +	if ((index != 1)) { +		pr_err("Error, a single TPG=1 is used for HW port mappings\n"); +		return ERR_PTR(-ENOSYS); +	} + +	ft_wwn = container_of(wwn, struct ft_lport_wwn, se_wwn);  	tpg = kzalloc(sizeof(*tpg), GFP_KERNEL);  	if (!tpg)  		return NULL;  	tpg->index = index; -	tpg->lport_acl = lacl; +	tpg->lport_wwn = ft_wwn;  	INIT_LIST_HEAD(&tpg->lun_list);  	wq = alloc_workqueue("tcm_fc", 0, 1); @@ -342,7 +347,7 @@ static struct se_portal_group *ft_add_tpg(  	tpg->workqueue = wq;  	mutex_lock(&ft_lport_lock); -	list_add_tail(&tpg->list, &lacl->tpg_list); +	ft_wwn->tpg = tpg;  	mutex_unlock(&ft_lport_lock);  	return &tpg->se_tpg; @@ -351,6 +356,7 @@ static struct se_portal_group *ft_add_tpg(  static void ft_del_tpg(struct se_portal_group *se_tpg)  {  	struct ft_tpg *tpg = container_of(se_tpg, struct ft_tpg, se_tpg); +	struct ft_lport_wwn *ft_wwn = tpg->lport_wwn;  	pr_debug("del tpg %s\n",  		    config_item_name(&tpg->se_tpg.tpg_group.cg_item)); @@ -361,7 +367,7 @@ static void ft_del_tpg(struct se_portal_group *se_tpg)  	synchronize_rcu();  	mutex_lock(&ft_lport_lock); -	list_del(&tpg->list); +	ft_wwn->tpg = NULL;  	if (tpg->tport) {  		tpg->tport->tpg = NULL;  		tpg->tport = NULL; @@ -380,15 +386,11 @@ static void ft_del_tpg(struct se_portal_group *se_tpg)   */  struct ft_tpg *ft_lport_find_tpg(struct fc_lport *lport)  { -	struct ft_lport_acl *lacl; -	struct ft_tpg *tpg; +	struct ft_lport_wwn *ft_wwn; -	list_for_each_entry(lacl, &ft_lport_list, list) { -		if (lacl->wwpn == lport->wwpn) { -			list_for_each_entry(tpg, &lacl->tpg_list, list) -				return tpg; /* XXX for now return first entry */ -			return NULL; -		} +	list_for_each_entry(ft_wwn, &ft_wwn_list, ft_wwn_node) { +		if (ft_wwn->wwpn == lport->wwpn) +			return ft_wwn->tpg;  	}  	return NULL;  } @@ -401,50 +403,49 @@ struct ft_tpg *ft_lport_find_tpg(struct fc_lport *lport)   * Add lport to allowed config.   * The name is the WWPN in lower-case ASCII, colon-separated bytes.   */ -static struct se_wwn *ft_add_lport( +static struct se_wwn *ft_add_wwn(  	struct target_fabric_configfs *tf,  	struct config_group *group,  	const char *name)  { -	struct ft_lport_acl *lacl; -	struct ft_lport_acl *old_lacl; +	struct ft_lport_wwn *ft_wwn; +	struct ft_lport_wwn *old_ft_wwn;  	u64 wwpn; -	pr_debug("add lport %s\n", name); +	pr_debug("add wwn %s\n", name);  	if (ft_parse_wwn(name, &wwpn, 1) < 0)  		return NULL; -	lacl = kzalloc(sizeof(*lacl), GFP_KERNEL); -	if (!lacl) +	ft_wwn = kzalloc(sizeof(*ft_wwn), GFP_KERNEL); +	if (!ft_wwn)  		return NULL; -	lacl->wwpn = wwpn; -	INIT_LIST_HEAD(&lacl->tpg_list); +	ft_wwn->wwpn = wwpn;  	mutex_lock(&ft_lport_lock); -	list_for_each_entry(old_lacl, &ft_lport_list, list) { -		if (old_lacl->wwpn == wwpn) { +	list_for_each_entry(old_ft_wwn, &ft_wwn_list, ft_wwn_node) { +		if (old_ft_wwn->wwpn == wwpn) {  			mutex_unlock(&ft_lport_lock); -			kfree(lacl); +			kfree(ft_wwn);  			return NULL;  		}  	} -	list_add_tail(&lacl->list, &ft_lport_list); -	ft_format_wwn(lacl->name, sizeof(lacl->name), wwpn); +	list_add_tail(&ft_wwn->ft_wwn_node, &ft_wwn_list); +	ft_format_wwn(ft_wwn->name, sizeof(ft_wwn->name), wwpn);  	mutex_unlock(&ft_lport_lock); -	return &lacl->fc_lport_wwn; +	return &ft_wwn->se_wwn;  } -static void ft_del_lport(struct se_wwn *wwn) +static void ft_del_wwn(struct se_wwn *wwn)  { -	struct ft_lport_acl *lacl = container_of(wwn, -				struct ft_lport_acl, fc_lport_wwn); +	struct ft_lport_wwn *ft_wwn = container_of(wwn, +				struct ft_lport_wwn, se_wwn); -	pr_debug("del lport %s\n", lacl->name); +	pr_debug("del wwn %s\n", ft_wwn->name);  	mutex_lock(&ft_lport_lock); -	list_del(&lacl->list); +	list_del(&ft_wwn->ft_wwn_node);  	mutex_unlock(&ft_lport_lock); -	kfree(lacl); +	kfree(ft_wwn);  }  static ssize_t ft_wwn_show_attr_version( @@ -471,7 +472,7 @@ static char *ft_get_fabric_wwn(struct se_portal_group *se_tpg)  {  	struct ft_tpg *tpg = se_tpg->se_tpg_fabric_ptr; -	return tpg->lport_acl->name; +	return tpg->lport_wwn->name;  }  static u16 ft_get_tag(struct se_portal_group *se_tpg) @@ -536,12 +537,13 @@ static struct target_core_fabric_ops ft_fabric_ops = {  	.queue_data_in =		ft_queue_data_in,  	.queue_status =			ft_queue_status,  	.queue_tm_rsp =			ft_queue_tm_resp, +	.aborted_task =			ft_aborted_task,  	/*  	 * Setup function pointers for generic logic in  	 * target_core_fabric_configfs.c  	 */ -	.fabric_make_wwn =		&ft_add_lport, -	.fabric_drop_wwn =		&ft_del_lport, +	.fabric_make_wwn =		&ft_add_wwn, +	.fabric_drop_wwn =		&ft_del_wwn,  	.fabric_make_tpg =		&ft_add_tpg,  	.fabric_drop_tpg =		&ft_del_tpg,  	.fabric_post_link =		NULL, @@ -552,7 +554,7 @@ static struct target_core_fabric_ops ft_fabric_ops = {  	.fabric_drop_nodeacl =		&ft_del_acl,  }; -int ft_register_configfs(void) +static int ft_register_configfs(void)  {  	struct target_fabric_configfs *fabric;  	int ret; @@ -571,16 +573,16 @@ int ft_register_configfs(void)  	/*  	 * Setup default attribute lists for various fabric->tf_cit_tmpl  	 */ -	TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = ft_wwn_attrs; -	TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = NULL; -	TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL; -	TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL; -	TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL; -	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = +	fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = ft_wwn_attrs; +	fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = NULL; +	fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL; +	fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL; +	fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL; +	fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs =  						    ft_nacl_base_attrs; -	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL; -	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL; -	TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL; +	fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL; +	fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL; +	fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL;  	/*  	 * register the fabric for use within TCM  	 */ @@ -599,7 +601,7 @@ int ft_register_configfs(void)  	return 0;  } -void ft_deregister_configfs(void) +static void ft_deregister_configfs(void)  {  	if (!ft_configfs)  		return; diff --git a/drivers/target/tcm_fc/tfc_io.c b/drivers/target/tcm_fc/tfc_io.c index e415af32115..97b486c3dda 100644 --- a/drivers/target/tcm_fc/tfc_io.c +++ b/drivers/target/tcm_fc/tfc_io.c @@ -82,6 +82,10 @@ int ft_queue_data_in(struct se_cmd *se_cmd)  	if (cmd->aborted)  		return 0; + +	if (se_cmd->scsi_status == SAM_STAT_TASK_SET_FULL) +		goto queue_status; +  	ep = fc_seq_exch(cmd->seq);  	lport = ep->lp;  	cmd->seq = lport->tt.seq_start_next(cmd->seq); @@ -178,14 +182,23 @@ int ft_queue_data_in(struct se_cmd *se_cmd)  			       FC_TYPE_FCP, f_ctl, fh_off);  		error = lport->tt.seq_send(lport, seq, fp);  		if (error) { -			/* XXX For now, initiator will retry */ -			pr_err_ratelimited("%s: Failed to send frame %p, " +			pr_info_ratelimited("%s: Failed to send frame %p, "  						"xid <0x%x>, remaining %zu, "  						"lso_max <0x%x>\n",  						__func__, fp, ep->xid,  						remaining, lport->lso_max); +			/* +			 * Go ahead and set TASK_SET_FULL status ignoring the +			 * rest of the DataIN, and immediately attempt to +			 * send the response via ft_queue_status() in order +			 * to notify the initiator that it should reduce it's +			 * per LUN queue_depth. +			 */ +			se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL; +			break;  		}  	} +queue_status:  	return ft_queue_status(se_cmd);  } diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c index 4859505ae2e..21ce50880c7 100644 --- a/drivers/target/tcm_fc/tfc_sess.c +++ b/drivers/target/tcm_fc/tfc_sess.c @@ -51,7 +51,7 @@ static void ft_sess_delete_all(struct ft_tport *);   * Lookup or allocate target local port.   * Caller holds ft_lport_lock.   */ -static struct ft_tport *ft_tport_create(struct fc_lport *lport) +static struct ft_tport *ft_tport_get(struct fc_lport *lport)  {  	struct ft_tpg *tpg;  	struct ft_tport *tport; @@ -68,6 +68,7 @@ static struct ft_tport *ft_tport_create(struct fc_lport *lport)  	if (tport) {  		tport->tpg = tpg; +		tpg->tport = tport;  		return tport;  	} @@ -114,7 +115,7 @@ static void ft_tport_delete(struct ft_tport *tport)  void ft_lport_add(struct fc_lport *lport, void *arg)  {  	mutex_lock(&ft_lport_lock); -	ft_tport_create(lport); +	ft_tport_get(lport);  	mutex_unlock(&ft_lport_lock);  } @@ -210,7 +211,9 @@ static struct ft_sess *ft_sess_create(struct ft_tport *tport, u32 port_id,  	if (!sess)  		return NULL; -	sess->se_sess = transport_init_session(); +	sess->se_sess = transport_init_session_tags(TCM_FC_DEFAULT_TAGS, +						    sizeof(struct ft_cmd), +						    TARGET_PROT_NORMAL);  	if (IS_ERR(sess->se_sess)) {  		kfree(sess);  		return NULL; @@ -349,7 +352,7 @@ static int ft_prli_locked(struct fc_rport_priv *rdata, u32 spp_len,  	struct ft_node_acl *acl;  	u32 fcp_parm; -	tport = ft_tport_create(rdata->local_port); +	tport = ft_tport_get(rdata->local_port);  	if (!tport)  		goto not_target;	/* not a target for this local port */  | 
