diff options
Diffstat (limited to 'drivers/target')
| -rw-r--r-- | drivers/target/iscsi/iscsi_target_login.c | 11 | ||||
| -rw-r--r-- | drivers/target/target_core_alua.c | 7 | ||||
| -rw-r--r-- | drivers/target/target_core_device.c | 7 | ||||
| -rw-r--r-- | drivers/target/target_core_iblock.c | 17 | ||||
| -rw-r--r-- | drivers/target/target_core_pr.c | 8 | ||||
| -rw-r--r-- | drivers/target/target_core_pscsi.c | 36 | ||||
| -rw-r--r-- | drivers/target/target_core_spc.c | 35 | ||||
| -rw-r--r-- | drivers/target/target_core_transport.c | 161 | ||||
| -rw-r--r-- | drivers/target/tcm_fc/tcm_fc.h | 1 | ||||
| -rw-r--r-- | drivers/target/tcm_fc/tfc_cmd.c | 8 | ||||
| -rw-r--r-- | drivers/target/tcm_fc/tfc_sess.c | 4 | 
11 files changed, 162 insertions, 133 deletions
diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index 0694d9b1bce..6aba4395e8d 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -221,6 +221,7 @@ static int iscsi_login_zero_tsih_s1(  {  	struct iscsi_session *sess = NULL;  	struct iscsi_login_req *pdu = (struct iscsi_login_req *)buf; +	int ret;  	sess = kzalloc(sizeof(struct iscsi_session), GFP_KERNEL);  	if (!sess) { @@ -257,9 +258,17 @@ static int iscsi_login_zero_tsih_s1(  		return -ENOMEM;  	}  	spin_lock(&sess_idr_lock); -	idr_get_new(&sess_idr, NULL, &sess->session_index); +	ret = idr_get_new(&sess_idr, NULL, &sess->session_index);  	spin_unlock(&sess_idr_lock); +	if (ret < 0) { +		pr_err("idr_get_new() for sess_idr failed\n"); +		iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_TARGET_ERR, +				ISCSI_LOGIN_STATUS_NO_RESOURCES); +		kfree(sess); +		return -ENOMEM; +	} +  	sess->creation_time = get_jiffies_64();  	spin_lock_init(&sess->session_stats_lock);  	/* diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index 91799973081..41641ba5482 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -218,6 +218,13 @@ int target_emulate_set_target_port_groups(struct se_cmd *cmd)  		cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;  		return -EINVAL;  	} +	if (cmd->data_length < 4) { +		pr_warn("SET TARGET PORT GROUPS parameter list length %u too" +			" small\n", cmd->data_length); +		cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST; +		return -EINVAL; +	} +  	buf = transport_kmap_data_sg(cmd);  	/* diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index cf2c66f3c11..9fc9a6006ca 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -669,6 +669,13 @@ int target_report_luns(struct se_cmd *se_cmd)  	unsigned char *buf;  	u32 lun_count = 0, offset = 8, i; +	if (se_cmd->data_length < 16) { +		pr_warn("REPORT LUNS allocation length %u too small\n", +			se_cmd->data_length); +		se_cmd->scsi_sense_reason = TCM_INVALID_CDB_FIELD; +		return -EINVAL; +	} +  	buf = transport_kmap_data_sg(se_cmd);  	if (!buf)  		return -ENOMEM; diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 76db75e836e..9ba495477fd 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -325,17 +325,30 @@ static int iblock_execute_unmap(struct se_cmd *cmd)  	struct iblock_dev *ibd = dev->dev_ptr;  	unsigned char *buf, *ptr = NULL;  	sector_t lba; -	int size = cmd->data_length; +	int size;  	u32 range;  	int ret = 0;  	int dl, bd_dl; +	if (cmd->data_length < 8) { +		pr_warn("UNMAP parameter list length %u too small\n", +			cmd->data_length); +		cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST; +		return -EINVAL; +	} +  	buf = transport_kmap_data_sg(cmd);  	dl = get_unaligned_be16(&buf[0]);  	bd_dl = get_unaligned_be16(&buf[2]); -	size = min(size - 8, bd_dl); +	size = cmd->data_length - 8; +	if (bd_dl > size) +		pr_warn("UNMAP parameter list length %u too small, ignoring bd_dl %u\n", +			cmd->data_length, bd_dl); +	else +		size = bd_dl; +  	if (size / 16 > dev->se_sub_dev->se_dev_attrib.max_unmap_block_desc_count) {  		cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST;  		ret = -EINVAL; diff --git a/drivers/target/target_core_pr.c b/drivers/target/target_core_pr.c index 1e946502c37..956c84c6b66 100644 --- a/drivers/target/target_core_pr.c +++ b/drivers/target/target_core_pr.c @@ -1540,6 +1540,14 @@ static int core_scsi3_decode_spec_i_port(  	tidh_new->dest_local_nexus = 1;  	list_add_tail(&tidh_new->dest_list, &tid_dest_list); +	if (cmd->data_length < 28) { +		pr_warn("SPC-PR: Received PR OUT parameter list" +			" length too small: %u\n", cmd->data_length); +		cmd->scsi_sense_reason = TCM_INVALID_PARAMETER_LIST; +		ret = -EINVAL; +		goto out; +	} +  	buf = transport_kmap_data_sg(cmd);  	/*  	 * For a PERSISTENT RESERVE OUT specify initiator ports payload, diff --git a/drivers/target/target_core_pscsi.c b/drivers/target/target_core_pscsi.c index 6e32ff6f2fa..9d7ce3daa26 100644 --- a/drivers/target/target_core_pscsi.c +++ b/drivers/target/target_core_pscsi.c @@ -667,24 +667,32 @@ static void pscsi_free_device(void *p)  	kfree(pdv);  } -static int pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg) +static void pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg, +				     unsigned char *sense_buffer)  {  	struct pscsi_dev_virt *pdv = cmd->se_dev->dev_ptr;  	struct scsi_device *sd = pdv->pdv_sd;  	int result;  	struct pscsi_plugin_task *pt = cmd->priv; -	unsigned char *cdb = &pt->pscsi_cdb[0]; +	unsigned char *cdb; +	/* +	 * Special case for REPORT_LUNs handling where pscsi_plugin_task has +	 * not been allocated because TCM is handling the emulation directly. +	 */ +	if (!pt) +		return; +	cdb = &pt->pscsi_cdb[0];  	result = pt->pscsi_result;  	/*  	 * Hack to make sure that Write-Protect modepage is set if R/O mode is  	 * forced.  	 */ +	if (!cmd->se_deve || !cmd->data_length) +		goto after_mode_sense; +  	if (((cdb[0] == MODE_SENSE) || (cdb[0] == MODE_SENSE_10)) &&  	     (status_byte(result) << 1) == SAM_STAT_GOOD) { -		if (!cmd->se_deve) -			goto after_mode_sense; -  		if (cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY) {  			unsigned char *buf = transport_kmap_data_sg(cmd); @@ -701,7 +709,7 @@ static int pscsi_transport_complete(struct se_cmd *cmd, struct scatterlist *sg)  	}  after_mode_sense: -	if (sd->type != TYPE_TAPE) +	if (sd->type != TYPE_TAPE || !cmd->data_length)  		goto after_mode_select;  	/* @@ -743,10 +751,10 @@ after_mode_sense:  	}  after_mode_select: -	if (status_byte(result) & CHECK_CONDITION) -		return 1; - -	return 0; +	if (sense_buffer && (status_byte(result) & CHECK_CONDITION)) { +		memcpy(sense_buffer, pt->pscsi_sense, TRANSPORT_SENSE_BUFFER); +		cmd->se_cmd_flags |= SCF_TRANSPORT_TASK_SENSE; +	}  }  enum { @@ -1177,13 +1185,6 @@ fail:  	return -ENOMEM;  } -static unsigned char *pscsi_get_sense_buffer(struct se_cmd *cmd) -{ -	struct pscsi_plugin_task *pt = cmd->priv; - -	return pt->pscsi_sense; -} -  /*	pscsi_get_device_rev():   *   * @@ -1266,7 +1267,6 @@ static struct se_subsystem_api pscsi_template = {  	.check_configfs_dev_params = pscsi_check_configfs_dev_params,  	.set_configfs_dev_params = pscsi_set_configfs_dev_params,  	.show_configfs_dev_params = pscsi_show_configfs_dev_params, -	.get_sense_buffer	= pscsi_get_sense_buffer,  	.get_device_rev		= pscsi_get_device_rev,  	.get_device_type	= pscsi_get_device_type,  	.get_blocks		= pscsi_get_blocks, diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index 4c861de538c..388a922c8f6 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -877,9 +877,11 @@ static int spc_emulate_modesense(struct se_cmd *cmd)  static int spc_emulate_request_sense(struct se_cmd *cmd)  {  	unsigned char *cdb = cmd->t_task_cdb; -	unsigned char *buf; +	unsigned char *rbuf;  	u8 ua_asc = 0, ua_ascq = 0; -	int err = 0; +	unsigned char buf[SE_SENSE_BUF]; + +	memset(buf, 0, SE_SENSE_BUF);  	if (cdb[1] & 0x01) {  		pr_err("REQUEST_SENSE description emulation not" @@ -888,20 +890,21 @@ static int spc_emulate_request_sense(struct se_cmd *cmd)  		return -ENOSYS;  	} -	buf = transport_kmap_data_sg(cmd); - -	if (!core_scsi3_ua_clear_for_request_sense(cmd, &ua_asc, &ua_ascq)) { +	rbuf = transport_kmap_data_sg(cmd); +	if (cmd->scsi_sense_reason != 0) { +		/* +		 * Out of memory.  We will fail with CHECK CONDITION, so +		 * we must not clear the unit attention condition. +		 */ +		target_complete_cmd(cmd, CHECK_CONDITION); +		return 0; +	} else if (!core_scsi3_ua_clear_for_request_sense(cmd, &ua_asc, &ua_ascq)) {  		/*  		 * CURRENT ERROR, UNIT ATTENTION  		 */  		buf[0] = 0x70;  		buf[SPC_SENSE_KEY_OFFSET] = UNIT_ATTENTION; -		if (cmd->data_length < 18) { -			buf[7] = 0x00; -			err = -EINVAL; -			goto end; -		}  		/*  		 * The Additional Sense Code (ASC) from the UNIT ATTENTION  		 */ @@ -915,11 +918,6 @@ static int spc_emulate_request_sense(struct se_cmd *cmd)  		buf[0] = 0x70;  		buf[SPC_SENSE_KEY_OFFSET] = NO_SENSE; -		if (cmd->data_length < 18) { -			buf[7] = 0x00; -			err = -EINVAL; -			goto end; -		}  		/*  		 * NO ADDITIONAL SENSE INFORMATION  		 */ @@ -927,8 +925,11 @@ static int spc_emulate_request_sense(struct se_cmd *cmd)  		buf[7] = 0x0A;  	} -end: -	transport_kunmap_data_sg(cmd); +	if (rbuf) { +		memcpy(rbuf, buf, min_t(u32, sizeof(buf), cmd->data_length)); +		transport_kunmap_data_sg(cmd); +	} +  	target_complete_cmd(cmd, GOOD);  	return 0;  } diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 0eaae23d12b..269f5448839 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -567,6 +567,34 @@ static void target_complete_failure_work(struct work_struct *work)  	transport_generic_request_failure(cmd);  } +/* + * Used when asking transport to copy Sense Data from the underlying + * Linux/SCSI struct scsi_cmnd + */ +static unsigned char *transport_get_sense_buffer(struct se_cmd *cmd) +{ +	unsigned char *buffer = cmd->sense_buffer; +	struct se_device *dev = cmd->se_dev; +	u32 offset = 0; + +	WARN_ON(!cmd->se_lun); + +	if (!dev) +		return NULL; + +	if (cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION) +		return NULL; + +	offset = cmd->se_tfo->set_fabric_sense_len(cmd, TRANSPORT_SENSE_BUFFER); + +	/* Automatically padded */ +	cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER + offset; + +	pr_debug("HBA_[%u]_PLUG[%s]: Requesting sense for SAM STATUS: 0x%02x\n", +		dev->se_hba->hba_id, dev->transport->name, cmd->scsi_status); +	return &buffer[offset]; +} +  void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)  {  	struct se_device *dev = cmd->se_dev; @@ -580,11 +608,11 @@ void target_complete_cmd(struct se_cmd *cmd, u8 scsi_status)  	cmd->transport_state &= ~CMD_T_BUSY;  	if (dev && dev->transport->transport_complete) { -		if (dev->transport->transport_complete(cmd, -				cmd->t_data_sg) != 0) { -			cmd->se_cmd_flags |= SCF_TRANSPORT_TASK_SENSE; +		dev->transport->transport_complete(cmd, +				cmd->t_data_sg, +				transport_get_sense_buffer(cmd)); +		if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE)  			success = 1; -		}  	}  	/* @@ -1165,8 +1193,6 @@ int target_cmd_size_check(struct se_cmd *cmd, unsigned int size)  			" 0x%02x\n", cmd->se_tfo->get_fabric_name(),  				cmd->data_length, size, cmd->t_task_cdb[0]); -		cmd->cmd_spdtl = size; -  		if (cmd->data_direction == DMA_TO_DEVICE) {  			pr_err("Rejecting underflow/overflow"  					" WRITE data\n"); @@ -1183,15 +1209,20 @@ int target_cmd_size_check(struct se_cmd *cmd, unsigned int size)  			/* Returns CHECK_CONDITION + INVALID_CDB_FIELD */  			goto out_invalid_cdb_field;  		} - +		/* +		 * For the overflow case keep the existing fabric provided +		 * ->data_length.  Otherwise for the underflow case, reset +		 * ->data_length to the smaller SCSI expected data transfer +		 * length. +		 */  		if (size > cmd->data_length) {  			cmd->se_cmd_flags |= SCF_OVERFLOW_BIT;  			cmd->residual_count = (size - cmd->data_length);  		} else {  			cmd->se_cmd_flags |= SCF_UNDERFLOW_BIT;  			cmd->residual_count = (cmd->data_length - size); +			cmd->data_length = size;  		} -		cmd->data_length = size;  	}  	return 0; @@ -1818,61 +1849,6 @@ execute:  EXPORT_SYMBOL(target_execute_cmd);  /* - * Used to obtain Sense Data from underlying Linux/SCSI struct scsi_cmnd - */ -static int transport_get_sense_data(struct se_cmd *cmd) -{ -	unsigned char *buffer = cmd->sense_buffer, *sense_buffer = NULL; -	struct se_device *dev = cmd->se_dev; -	unsigned long flags; -	u32 offset = 0; - -	WARN_ON(!cmd->se_lun); - -	if (!dev) -		return 0; - -	spin_lock_irqsave(&cmd->t_state_lock, flags); -	if (cmd->se_cmd_flags & SCF_SENT_CHECK_CONDITION) { -		spin_unlock_irqrestore(&cmd->t_state_lock, flags); -		return 0; -	} - -	if (!(cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE)) -		goto out; - -	if (!dev->transport->get_sense_buffer) { -		pr_err("dev->transport->get_sense_buffer is NULL\n"); -		goto out; -	} - -	sense_buffer = dev->transport->get_sense_buffer(cmd); -	if (!sense_buffer) { -		pr_err("ITT 0x%08x cmd %p: Unable to locate" -			" sense buffer for task with sense\n", -			cmd->se_tfo->get_task_tag(cmd), cmd); -		goto out; -	} - -	spin_unlock_irqrestore(&cmd->t_state_lock, flags); - -	offset = cmd->se_tfo->set_fabric_sense_len(cmd, TRANSPORT_SENSE_BUFFER); - -	memcpy(&buffer[offset], sense_buffer, TRANSPORT_SENSE_BUFFER); - -	/* Automatically padded */ -	cmd->scsi_sense_length = TRANSPORT_SENSE_BUFFER + offset; - -	pr_debug("HBA_[%u]_PLUG[%s]: Set SAM STATUS: 0x%02x and sense\n", -		dev->se_hba->hba_id, dev->transport->name, cmd->scsi_status); -	return 0; - -out: -	spin_unlock_irqrestore(&cmd->t_state_lock, flags); -	return -1; -} - -/*   * Process all commands up to the last received ORDERED task attribute which   * requires another blocking boundary   */ @@ -1987,7 +1963,7 @@ static void transport_handle_queue_full(  static void target_complete_ok_work(struct work_struct *work)  {  	struct se_cmd *cmd = container_of(work, struct se_cmd, work); -	int reason = 0, ret; +	int ret;  	/*  	 * Check if we need to move delayed/dormant tasks from cmds on the @@ -2004,23 +1980,19 @@ static void target_complete_ok_work(struct work_struct *work)  		schedule_work(&cmd->se_dev->qf_work_queue);  	/* -	 * Check if we need to retrieve a sense buffer from +	 * Check if we need to send a sense buffer from  	 * the struct se_cmd in question.  	 */  	if (cmd->se_cmd_flags & SCF_TRANSPORT_TASK_SENSE) { -		if (transport_get_sense_data(cmd) < 0) -			reason = TCM_NON_EXISTENT_LUN; - -		if (cmd->scsi_status) { -			ret = transport_send_check_condition_and_sense( -					cmd, reason, 1); -			if (ret == -EAGAIN || ret == -ENOMEM) -				goto queue_full; +		WARN_ON(!cmd->scsi_status); +		ret = transport_send_check_condition_and_sense( +					cmd, 0, 1); +		if (ret == -EAGAIN || ret == -ENOMEM) +			goto queue_full; -			transport_lun_remove_cmd(cmd); -			transport_cmd_check_stop_to_fabric(cmd); -			return; -		} +		transport_lun_remove_cmd(cmd); +		transport_cmd_check_stop_to_fabric(cmd); +		return;  	}  	/*  	 * Check for a callback, used by amongst other things @@ -2218,7 +2190,6 @@ void *transport_kmap_data_sg(struct se_cmd *cmd)  	struct page **pages;  	int i; -	BUG_ON(!sg);  	/*  	 * We need to take into account a possible offset here for fabrics like  	 * tcm_loop who may be using a contig buffer from the SCSI midlayer for @@ -2226,13 +2197,17 @@ void *transport_kmap_data_sg(struct se_cmd *cmd)  	 */  	if (!cmd->t_data_nents)  		return NULL; -	else if (cmd->t_data_nents == 1) + +	BUG_ON(!sg); +	if (cmd->t_data_nents == 1)  		return kmap(sg_page(sg)) + sg->offset;  	/* >1 page. use vmap */  	pages = kmalloc(sizeof(*pages) * cmd->t_data_nents, GFP_KERNEL); -	if (!pages) +	if (!pages) { +		cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;  		return NULL; +	}  	/* convert sg[] to pages[] */  	for_each_sg(cmd->t_data_sg, sg, cmd->t_data_nents, i) { @@ -2241,8 +2216,10 @@ void *transport_kmap_data_sg(struct se_cmd *cmd)  	cmd->t_data_vmap = vmap(pages, cmd->t_data_nents,  VM_MAP, PAGE_KERNEL);  	kfree(pages); -	if (!cmd->t_data_vmap) +	if (!cmd->t_data_vmap) { +		cmd->scsi_sense_reason = TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE;  		return NULL; +	}  	return cmd->t_data_vmap + cmd->t_data_sg[0].offset;  } @@ -2294,9 +2271,9 @@ transport_generic_get_mem(struct se_cmd *cmd)  	return 0;  out: -	while (i >= 0) { -		__free_page(sg_page(&cmd->t_data_sg[i])); +	while (i > 0) {  		i--; +		__free_page(sg_page(&cmd->t_data_sg[i]));  	}  	kfree(cmd->t_data_sg);  	cmd->t_data_sg = NULL; @@ -2323,21 +2300,19 @@ int transport_generic_new_cmd(struct se_cmd *cmd)  		if (ret < 0)  			goto out_fail;  	} - -	/* Workaround for handling zero-length control CDBs */ -	if (!(cmd->se_cmd_flags & SCF_SCSI_DATA_CDB) && !cmd->data_length) { +	/* +	 * If this command doesn't have any payload and we don't have to call +	 * into the fabric for data transfers, go ahead and complete it right +	 * away. +	 */ +	if (!cmd->data_length && +	    cmd->t_task_cdb[0] != REQUEST_SENSE && +	    cmd->se_dev->transport->transport_type != TRANSPORT_PLUGIN_PHBA_PDEV) {  		spin_lock_irq(&cmd->t_state_lock);  		cmd->t_state = TRANSPORT_COMPLETE;  		cmd->transport_state |= CMD_T_ACTIVE;  		spin_unlock_irq(&cmd->t_state_lock); -		if (cmd->t_task_cdb[0] == REQUEST_SENSE) { -			u8 ua_asc = 0, ua_ascq = 0; - -			core_scsi3_ua_clear_for_request_sense(cmd, -					&ua_asc, &ua_ascq); -		} -  		INIT_WORK(&cmd->work, target_complete_ok_work);  		queue_work(target_completion_wq, &cmd->work);  		return 0; diff --git a/drivers/target/tcm_fc/tcm_fc.h b/drivers/target/tcm_fc/tcm_fc.h index c5eb3c33c3d..eea69358ced 100644 --- a/drivers/target/tcm_fc/tcm_fc.h +++ b/drivers/target/tcm_fc/tcm_fc.h @@ -131,6 +131,7 @@ 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; +extern unsigned int ft_debug_logging;  /*   * Fabric methods. diff --git a/drivers/target/tcm_fc/tfc_cmd.c b/drivers/target/tcm_fc/tfc_cmd.c index b9cb5006177..823e6922249 100644 --- a/drivers/target/tcm_fc/tfc_cmd.c +++ b/drivers/target/tcm_fc/tfc_cmd.c @@ -48,7 +48,7 @@  /*   * Dump cmd state for debugging.   */ -void ft_dump_cmd(struct ft_cmd *cmd, const char *caller) +static void _ft_dump_cmd(struct ft_cmd *cmd, const char *caller)  {  	struct fc_exch *ep;  	struct fc_seq *sp; @@ -80,6 +80,12 @@ void ft_dump_cmd(struct ft_cmd *cmd, const char *caller)  	}  } +void ft_dump_cmd(struct ft_cmd *cmd, const char *caller) +{ +	if (unlikely(ft_debug_logging)) +		_ft_dump_cmd(cmd, caller); +} +  static void ft_free_cmd(struct ft_cmd *cmd)  {  	struct fc_frame *fp; diff --git a/drivers/target/tcm_fc/tfc_sess.c b/drivers/target/tcm_fc/tfc_sess.c index 87901fa74dd..3c9e5b57caa 100644 --- a/drivers/target/tcm_fc/tfc_sess.c +++ b/drivers/target/tcm_fc/tfc_sess.c @@ -456,7 +456,9 @@ static void ft_prlo(struct fc_rport_priv *rdata)  	struct ft_tport *tport;  	mutex_lock(&ft_lport_lock); -	tport = rcu_dereference(rdata->local_port->prov[FC_TYPE_FCP]); +	tport = rcu_dereference_protected(rdata->local_port->prov[FC_TYPE_FCP], +					  lockdep_is_held(&ft_lport_lock)); +  	if (!tport) {  		mutex_unlock(&ft_lport_lock);  		return;  |