1224 lines · 31.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/******************************************************************************* * Filename:  target_core_device.c (based on iscsi_target_device.c) * * This file contains the TCM Virtual Device and Disk Transport * agnostic related functions. * * (c) Copyright 2003-2013 Datera, Inc. * * Nicholas A. Bellinger <nab@kernel.org> * ******************************************************************************/ #include <linux/net.h>#include <linux/string.h>#include <linux/delay.h>#include <linux/timer.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <linux/kthread.h>#include <linux/in.h>#include <linux/export.h>#include <linux/t10-pi.h>#include <linux/unaligned.h>#include <net/sock.h>#include <net/tcp.h>#include <scsi/scsi_common.h>#include <scsi/scsi_proto.h> #include <target/target_core_base.h>#include <target/target_core_backend.h>#include <target/target_core_fabric.h> #include "target_core_internal.h"#include "target_core_alua.h"#include "target_core_pr.h"#include "target_core_ua.h" static DEFINE_MUTEX(device_mutex);static DEFINE_IDR(devices_idr); static struct se_hba *lun0_hba;/* not static, needed by tpg.c */struct se_device *g_lun0_dev; sense_reason_ttransport_lookup_cmd_lun(struct se_cmd *se_cmd){	struct se_lun *se_lun = NULL;	struct se_session *se_sess = se_cmd->se_sess;	struct se_node_acl *nacl = se_sess->se_node_acl;	struct se_dev_entry *deve;	sense_reason_t ret = TCM_NO_SENSE; 	rcu_read_lock();	deve = target_nacl_find_deve(nacl, se_cmd->orig_fe_lun);	if (deve) {		this_cpu_inc(deve->stats->total_cmds); 		if (se_cmd->data_direction == DMA_TO_DEVICE)			this_cpu_add(deve->stats->write_bytes,				     se_cmd->data_length);		else if (se_cmd->data_direction == DMA_FROM_DEVICE)			this_cpu_add(deve->stats->read_bytes,				     se_cmd->data_length); 		if ((se_cmd->data_direction == DMA_TO_DEVICE) &&		    deve->lun_access_ro) {			pr_err("TARGET_CORE[%s]: Detected WRITE_PROTECTED LUN"				" Access for 0x%08llx\n",				se_cmd->se_tfo->fabric_name,				se_cmd->orig_fe_lun);			rcu_read_unlock();			return TCM_WRITE_PROTECTED;		} 		se_lun = deve->se_lun; 		if (!percpu_ref_tryget_live(&se_lun->lun_ref)) {			se_lun = NULL;			goto out_unlock;		} 		se_cmd->se_lun = se_lun;		se_cmd->pr_res_key = deve->pr_res_key;		se_cmd->se_cmd_flags |= SCF_SE_LUN_CMD;		se_cmd->lun_ref_active = true;	}out_unlock:	rcu_read_unlock(); 	if (!se_lun) {		/*		 * Use the se_portal_group->tpg_virt_lun0 to allow for		 * REPORT_LUNS, et al to be returned when no active		 * MappedLUN=0 exists for this Initiator Port.		 */		if (se_cmd->orig_fe_lun != 0) {			pr_err("TARGET_CORE[%s]: Detected NON_EXISTENT_LUN"				" Access for 0x%08llx from %s\n",				se_cmd->se_tfo->fabric_name,				se_cmd->orig_fe_lun,				nacl->initiatorname);			return TCM_NON_EXISTENT_LUN;		} 		/*		 * Force WRITE PROTECT for virtual LUN 0		 */		if ((se_cmd->data_direction != DMA_FROM_DEVICE) &&		    (se_cmd->data_direction != DMA_NONE))			return TCM_WRITE_PROTECTED; 		se_lun = se_sess->se_tpg->tpg_virt_lun0;		if (!percpu_ref_tryget_live(&se_lun->lun_ref))			return TCM_NON_EXISTENT_LUN; 		se_cmd->se_lun = se_sess->se_tpg->tpg_virt_lun0;		se_cmd->se_cmd_flags |= SCF_SE_LUN_CMD;		se_cmd->lun_ref_active = true;	}	/*	 * RCU reference protected by percpu se_lun->lun_ref taken above that	 * must drop to zero (including initial reference) before this se_lun	 * pointer can be kfree_rcu() by the final se_lun->lun_group put via	 * target_core_fabric_configfs.c:target_fabric_port_release	 */	se_cmd->se_dev = rcu_dereference_raw(se_lun->lun_se_dev);	this_cpu_inc(se_cmd->se_dev->stats->total_cmds); 	if (se_cmd->data_direction == DMA_TO_DEVICE)		this_cpu_add(se_cmd->se_dev->stats->write_bytes,			     se_cmd->data_length);	else if (se_cmd->data_direction == DMA_FROM_DEVICE)		this_cpu_add(se_cmd->se_dev->stats->read_bytes,			     se_cmd->data_length); 	return ret;}EXPORT_SYMBOL(transport_lookup_cmd_lun); int transport_lookup_tmr_lun(struct se_cmd *se_cmd){	struct se_dev_entry *deve;	struct se_lun *se_lun = NULL;	struct se_session *se_sess = se_cmd->se_sess;	struct se_node_acl *nacl = se_sess->se_node_acl;	struct se_tmr_req *se_tmr = se_cmd->se_tmr_req; 	rcu_read_lock();	deve = target_nacl_find_deve(nacl, se_cmd->orig_fe_lun);	if (deve) {		se_lun = deve->se_lun; 		if (!percpu_ref_tryget_live(&se_lun->lun_ref)) {			se_lun = NULL;			goto out_unlock;		} 		se_cmd->se_lun = se_lun;		se_cmd->pr_res_key = deve->pr_res_key;		se_cmd->se_cmd_flags |= SCF_SE_LUN_CMD;		se_cmd->lun_ref_active = true;	}out_unlock:	rcu_read_unlock(); 	if (!se_lun) {		pr_debug("TARGET_CORE[%s]: Detected NON_EXISTENT_LUN"			" Access for 0x%08llx for %s\n",			se_cmd->se_tfo->fabric_name,			se_cmd->orig_fe_lun,			nacl->initiatorname);		return -ENODEV;	}	se_cmd->se_dev = rcu_dereference_raw(se_lun->lun_se_dev);	se_tmr->tmr_dev = rcu_dereference_raw(se_lun->lun_se_dev); 	return 0;}EXPORT_SYMBOL(transport_lookup_tmr_lun); bool target_lun_is_rdonly(struct se_cmd *cmd){	struct se_session *se_sess = cmd->se_sess;	struct se_dev_entry *deve;	bool ret; 	rcu_read_lock();	deve = target_nacl_find_deve(se_sess->se_node_acl, cmd->orig_fe_lun);	ret = deve && deve->lun_access_ro;	rcu_read_unlock(); 	return ret;}EXPORT_SYMBOL(target_lun_is_rdonly); /* * This function is called from core_scsi3_emulate_pro_register_and_move() * and core_scsi3_decode_spec_i_port(), and will increment &deve->pr_kref * when a matching rtpi is found. */struct se_dev_entry *core_get_se_deve_from_rtpi(	struct se_node_acl *nacl,	u16 rtpi){	struct se_dev_entry *deve;	struct se_lun *lun;	struct se_portal_group *tpg = nacl->se_tpg; 	rcu_read_lock();	hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link) {		lun = deve->se_lun;		if (!lun) {			pr_err("%s device entries device pointer is"				" NULL, but Initiator has access.\n",				tpg->se_tpg_tfo->fabric_name);			continue;		}		if (lun->lun_tpg->tpg_rtpi != rtpi)			continue; 		kref_get(&deve->pr_kref);		rcu_read_unlock(); 		return deve;	}	rcu_read_unlock(); 	return NULL;} void core_free_device_list_for_node(	struct se_node_acl *nacl,	struct se_portal_group *tpg){	struct se_dev_entry *deve; 	mutex_lock(&nacl->lun_entry_mutex);	hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link)		core_disable_device_list_for_node(deve->se_lun, deve, nacl, tpg);	mutex_unlock(&nacl->lun_entry_mutex);} void core_update_device_list_access(	u64 mapped_lun,	bool lun_access_ro,	struct se_node_acl *nacl){	struct se_dev_entry *deve; 	mutex_lock(&nacl->lun_entry_mutex);	deve = target_nacl_find_deve(nacl, mapped_lun);	if (deve)		deve->lun_access_ro = lun_access_ro;	mutex_unlock(&nacl->lun_entry_mutex);} /* * Called with rcu_read_lock or nacl->device_list_lock held. */struct se_dev_entry *target_nacl_find_deve(struct se_node_acl *nacl, u64 mapped_lun){	struct se_dev_entry *deve; 	hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link)		if (deve->mapped_lun == mapped_lun)			return deve; 	return NULL;}EXPORT_SYMBOL(target_nacl_find_deve); void target_pr_kref_release(struct kref *kref){	struct se_dev_entry *deve = container_of(kref, struct se_dev_entry,						 pr_kref);	complete(&deve->pr_comp);} /* * Establish UA condition on SCSI device - all LUNs */void target_dev_ua_allocate(struct se_device *dev, u8 asc, u8 ascq){	struct se_dev_entry *se_deve;	struct se_lun *lun; 	spin_lock(&dev->se_port_lock);	list_for_each_entry(lun, &dev->dev_sep_list, lun_dev_link) { 		spin_lock(&lun->lun_deve_lock);		list_for_each_entry(se_deve, &lun->lun_deve_list, lun_link)			core_scsi3_ua_allocate(se_deve, asc, ascq);		spin_unlock(&lun->lun_deve_lock);	}	spin_unlock(&dev->se_port_lock);} static voidtarget_luns_data_has_changed(struct se_node_acl *nacl, struct se_dev_entry *new,			     bool skip_new){	struct se_dev_entry *tmp; 	rcu_read_lock();	hlist_for_each_entry_rcu(tmp, &nacl->lun_entry_hlist, link) {		if (skip_new && tmp == new)			continue;		core_scsi3_ua_allocate(tmp, 0x3F,				       ASCQ_3FH_REPORTED_LUNS_DATA_HAS_CHANGED);	}	rcu_read_unlock();} int core_enable_device_list_for_node(	struct se_lun *lun,	struct se_lun_acl *lun_acl,	u64 mapped_lun,	bool lun_access_ro,	struct se_node_acl *nacl,	struct se_portal_group *tpg){	struct se_dev_entry *orig, *new;	int ret = 0; 	new = kzalloc_obj(*new);	if (!new) {		pr_err("Unable to allocate se_dev_entry memory\n");		return -ENOMEM;	} 	new->stats = alloc_percpu(struct se_dev_entry_io_stats);	if (!new->stats) {		ret = -ENOMEM;		goto free_deve;	} 	spin_lock_init(&new->ua_lock);	INIT_LIST_HEAD(&new->ua_list);	INIT_LIST_HEAD(&new->lun_link); 	new->mapped_lun = mapped_lun;	kref_init(&new->pr_kref);	init_completion(&new->pr_comp); 	new->lun_access_ro = lun_access_ro;	new->creation_time = get_jiffies_64();	new->attach_count++; 	mutex_lock(&nacl->lun_entry_mutex);	orig = target_nacl_find_deve(nacl, mapped_lun);	if (orig && orig->se_lun) {		struct se_lun *orig_lun = orig->se_lun; 		if (orig_lun != lun) {			pr_err("Existing orig->se_lun doesn't match new lun"			       " for dynamic -> explicit NodeACL conversion:"				" %s\n", nacl->initiatorname);			mutex_unlock(&nacl->lun_entry_mutex);			ret = -EINVAL;			goto free_stats;		}		if (orig->se_lun_acl != NULL) {			pr_warn_ratelimited("Detected existing explicit"				" se_lun_acl->se_lun_group reference for %s"				" mapped_lun: %llu, failing\n",				 nacl->initiatorname, mapped_lun);			mutex_unlock(&nacl->lun_entry_mutex);			ret = -EINVAL;			goto free_stats;		} 		new->se_lun = lun;		new->se_lun_acl = lun_acl;		hlist_del_rcu(&orig->link);		hlist_add_head_rcu(&new->link, &nacl->lun_entry_hlist);		mutex_unlock(&nacl->lun_entry_mutex); 		spin_lock(&lun->lun_deve_lock);		list_del(&orig->lun_link);		list_add_tail(&new->lun_link, &lun->lun_deve_list);		spin_unlock(&lun->lun_deve_lock); 		kref_put(&orig->pr_kref, target_pr_kref_release);		wait_for_completion(&orig->pr_comp); 		target_luns_data_has_changed(nacl, new, true);		kfree_rcu(orig, rcu_head);		return 0;	} 	new->se_lun = lun;	new->se_lun_acl = lun_acl;	hlist_add_head_rcu(&new->link, &nacl->lun_entry_hlist);	mutex_unlock(&nacl->lun_entry_mutex); 	spin_lock(&lun->lun_deve_lock);	list_add_tail(&new->lun_link, &lun->lun_deve_list);	spin_unlock(&lun->lun_deve_lock); 	target_luns_data_has_changed(nacl, new, true);	return 0; free_stats:	free_percpu(new->stats);free_deve:	kfree(new);	return ret;} static void target_free_dev_entry(struct rcu_head *head){	struct se_dev_entry *deve = container_of(head, struct se_dev_entry,						 rcu_head);	free_percpu(deve->stats);	kfree(deve);} void core_disable_device_list_for_node(	struct se_lun *lun,	struct se_dev_entry *orig,	struct se_node_acl *nacl,	struct se_portal_group *tpg){	/*	 * rcu_dereference_raw protected by se_lun->lun_group symlink	 * reference to se_device->dev_group.	 */	struct se_device *dev = rcu_dereference_raw(lun->lun_se_dev); 	lockdep_assert_held(&nacl->lun_entry_mutex); 	/*	 * If the MappedLUN entry is being disabled, the entry in	 * lun->lun_deve_list must be removed now before clearing the	 * struct se_dev_entry pointers below as logic in	 * core_alua_do_transition_tg_pt() depends on these being present.	 *	 * deve->se_lun_acl will be NULL for demo-mode created LUNs	 * that have not been explicitly converted to MappedLUNs ->	 * struct se_lun_acl, but we remove deve->lun_link from	 * lun->lun_deve_list. This also means that active UAs and	 * NodeACL context specific PR metadata for demo-mode	 * MappedLUN *deve will be released below..	 */	spin_lock(&lun->lun_deve_lock);	list_del(&orig->lun_link);	spin_unlock(&lun->lun_deve_lock);	/*	 * Disable struct se_dev_entry LUN ACL mapping	 */	core_scsi3_ua_release_all(orig); 	hlist_del_rcu(&orig->link);	clear_bit(DEF_PR_REG_ACTIVE, &orig->deve_flags);	orig->lun_access_ro = false;	orig->creation_time = 0;	orig->attach_count--;	/*	 * Before firing off RCU callback, wait for any in process SPEC_I_PT=1	 * or REGISTER_AND_MOVE PR operation to complete.	 */	kref_put(&orig->pr_kref, target_pr_kref_release);	wait_for_completion(&orig->pr_comp); 	call_rcu(&orig->rcu_head, target_free_dev_entry); 	core_scsi3_free_pr_reg_from_nacl(dev, nacl);	target_luns_data_has_changed(nacl, NULL, false);} /*      core_clear_lun_from_tpg(): * * */void core_clear_lun_from_tpg(struct se_lun *lun, struct se_portal_group *tpg){	struct se_node_acl *nacl;	struct se_dev_entry *deve; 	mutex_lock(&tpg->acl_node_mutex);	list_for_each_entry(nacl, &tpg->acl_node_list, acl_list) { 		mutex_lock(&nacl->lun_entry_mutex);		hlist_for_each_entry_rcu(deve, &nacl->lun_entry_hlist, link) {			if (lun != deve->se_lun)				continue; 			core_disable_device_list_for_node(lun, deve, nacl, tpg);		}		mutex_unlock(&nacl->lun_entry_mutex);	}	mutex_unlock(&tpg->acl_node_mutex);} static void se_release_vpd_for_dev(struct se_device *dev){	struct t10_vpd *vpd, *vpd_tmp; 

Lines 1–500 of 1224.