1628 lines · 45.5 KB · C
RawDownload
/******************************************************************************* * Filename:  target_core_device.c (based on iscsi_target_device.c) * * This file contains the TCM Virtual Device and Disk Transport * agnostic related functions. * * Copyright (c) 2003, 2004, 2005 PyX Technologies, Inc. * Copyright (c) 2005-2006 SBE, Inc.  All Rights Reserved. * Copyright (c) 2007-2010 Rising Tide Systems * Copyright (c) 2008-2010 Linux-iSCSI.org * * Nicholas A. Bellinger <nab@kernel.org> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ******************************************************************************/ #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 <net/sock.h>#include <net/tcp.h>#include <scsi/scsi.h>#include <scsi/scsi_device.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 void se_dev_start(struct se_device *dev);static void se_dev_stop(struct se_device *dev); static struct se_hba *lun0_hba;static struct se_subsystem_dev *lun0_su_dev;/* not static, needed by tpg.c */struct se_device *g_lun0_dev; int transport_lookup_cmd_lun(struct se_cmd *se_cmd, u32 unpacked_lun){	struct se_lun *se_lun = NULL;	struct se_session *se_sess = se_cmd->se_sess;	struct se_device *dev;	unsigned long flags; 	if (unpacked_lun >= TRANSPORT_MAX_LUNS_PER_TPG) {		se_cmd->scsi_sense_reason = TCM_NON_EXISTENT_LUN;		se_cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;		return -ENODEV;	} 	spin_lock_irqsave(&se_sess->se_node_acl->device_list_lock, flags);	se_cmd->se_deve = se_sess->se_node_acl->device_list[unpacked_lun];	if (se_cmd->se_deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS) {		struct se_dev_entry *deve = se_cmd->se_deve; 		deve->total_cmds++;		deve->total_bytes += se_cmd->data_length; 		if ((se_cmd->data_direction == DMA_TO_DEVICE) &&		    (deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY)) {			se_cmd->scsi_sense_reason = TCM_WRITE_PROTECTED;			se_cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;			pr_err("TARGET_CORE[%s]: Detected WRITE_PROTECTED LUN"				" Access for 0x%08x\n",				se_cmd->se_tfo->get_fabric_name(),				unpacked_lun);			spin_unlock_irqrestore(&se_sess->se_node_acl->device_list_lock, flags);			return -EACCES;		} 		if (se_cmd->data_direction == DMA_TO_DEVICE)			deve->write_bytes += se_cmd->data_length;		else if (se_cmd->data_direction == DMA_FROM_DEVICE)			deve->read_bytes += se_cmd->data_length; 		deve->deve_cmds++; 		se_lun = deve->se_lun;		se_cmd->se_lun = deve->se_lun;		se_cmd->pr_res_key = deve->pr_res_key;		se_cmd->orig_fe_lun = unpacked_lun;		se_cmd->se_cmd_flags |= SCF_SE_LUN_CMD;	}	spin_unlock_irqrestore(&se_sess->se_node_acl->device_list_lock, flags); 	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 (unpacked_lun != 0) {			se_cmd->scsi_sense_reason = TCM_NON_EXISTENT_LUN;			se_cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;			pr_err("TARGET_CORE[%s]: Detected NON_EXISTENT_LUN"				" Access for 0x%08x\n",				se_cmd->se_tfo->get_fabric_name(),				unpacked_lun);			return -ENODEV;		}		/*		 * Force WRITE PROTECT for virtual LUN 0		 */		if ((se_cmd->data_direction != DMA_FROM_DEVICE) &&		    (se_cmd->data_direction != DMA_NONE)) {			se_cmd->scsi_sense_reason = TCM_WRITE_PROTECTED;			se_cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;			return -EACCES;		} 		se_lun = &se_sess->se_tpg->tpg_virt_lun0;		se_cmd->se_lun = &se_sess->se_tpg->tpg_virt_lun0;		se_cmd->orig_fe_lun = 0;		se_cmd->se_cmd_flags |= SCF_SE_LUN_CMD;	}	/*	 * Determine if the struct se_lun is online.	 * FIXME: Check for LUN_RESET + UNIT Attention	 */	if (se_dev_check_online(se_lun->lun_se_dev) != 0) {		se_cmd->scsi_sense_reason = TCM_NON_EXISTENT_LUN;		se_cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;		return -ENODEV;	} 	/* Directly associate cmd with se_dev */	se_cmd->se_dev = se_lun->lun_se_dev; 	/* TODO: get rid of this and use atomics for stats */	dev = se_lun->lun_se_dev;	spin_lock_irqsave(&dev->stats_lock, flags);	dev->num_cmds++;	if (se_cmd->data_direction == DMA_TO_DEVICE)		dev->write_bytes += se_cmd->data_length;	else if (se_cmd->data_direction == DMA_FROM_DEVICE)		dev->read_bytes += se_cmd->data_length;	spin_unlock_irqrestore(&dev->stats_lock, flags); 	spin_lock_irqsave(&se_lun->lun_cmd_lock, flags);	list_add_tail(&se_cmd->se_lun_node, &se_lun->lun_cmd_list);	spin_unlock_irqrestore(&se_lun->lun_cmd_lock, flags); 	return 0;}EXPORT_SYMBOL(transport_lookup_cmd_lun); int transport_lookup_tmr_lun(struct se_cmd *se_cmd, u32 unpacked_lun){	struct se_dev_entry *deve;	struct se_lun *se_lun = NULL;	struct se_session *se_sess = se_cmd->se_sess;	struct se_tmr_req *se_tmr = se_cmd->se_tmr_req;	unsigned long flags; 	if (unpacked_lun >= TRANSPORT_MAX_LUNS_PER_TPG) {		se_cmd->scsi_sense_reason = TCM_NON_EXISTENT_LUN;		se_cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;		return -ENODEV;	} 	spin_lock_irqsave(&se_sess->se_node_acl->device_list_lock, flags);	se_cmd->se_deve = se_sess->se_node_acl->device_list[unpacked_lun];	deve = se_cmd->se_deve; 	if (deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS) {		se_tmr->tmr_lun = deve->se_lun;		se_cmd->se_lun = deve->se_lun;		se_lun = deve->se_lun;		se_cmd->pr_res_key = deve->pr_res_key;		se_cmd->orig_fe_lun = unpacked_lun;	}	spin_unlock_irqrestore(&se_sess->se_node_acl->device_list_lock, flags); 	if (!se_lun) {		pr_debug("TARGET_CORE[%s]: Detected NON_EXISTENT_LUN"			" Access for 0x%08x\n",			se_cmd->se_tfo->get_fabric_name(),			unpacked_lun);		se_cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;		return -ENODEV;	}	/*	 * Determine if the struct se_lun is online.	 * FIXME: Check for LUN_RESET + UNIT Attention	 */	if (se_dev_check_online(se_lun->lun_se_dev) != 0) {		se_cmd->se_cmd_flags |= SCF_SCSI_CDB_EXCEPTION;		return -ENODEV;	} 	/* Directly associate cmd with se_dev */	se_cmd->se_dev = se_lun->lun_se_dev;	se_tmr->tmr_dev = se_lun->lun_se_dev; 	spin_lock_irqsave(&se_tmr->tmr_dev->se_tmr_lock, flags);	list_add_tail(&se_tmr->tmr_list, &se_tmr->tmr_dev->dev_tmr_list);	spin_unlock_irqrestore(&se_tmr->tmr_dev->se_tmr_lock, flags); 	return 0;}EXPORT_SYMBOL(transport_lookup_tmr_lun); /* * This function is called from core_scsi3_emulate_pro_register_and_move() * and core_scsi3_decode_spec_i_port(), and will increment &deve->pr_ref_count * 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_port *port;	struct se_portal_group *tpg = nacl->se_tpg;	u32 i; 	spin_lock_irq(&nacl->device_list_lock);	for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {		deve = nacl->device_list[i]; 		if (!(deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS))			continue; 		lun = deve->se_lun;		if (!lun) {			pr_err("%s device entries device pointer is"				" NULL, but Initiator has access.\n",				tpg->se_tpg_tfo->get_fabric_name());			continue;		}		port = lun->lun_sep;		if (!port) {			pr_err("%s device entries device pointer is"				" NULL, but Initiator has access.\n",				tpg->se_tpg_tfo->get_fabric_name());			continue;		}		if (port->sep_rtpi != rtpi)			continue; 		atomic_inc(&deve->pr_ref_count);		smp_mb__after_atomic_inc();		spin_unlock_irq(&nacl->device_list_lock); 		return deve;	}	spin_unlock_irq(&nacl->device_list_lock); 	return NULL;} int core_free_device_list_for_node(	struct se_node_acl *nacl,	struct se_portal_group *tpg){	struct se_dev_entry *deve;	struct se_lun *lun;	u32 i; 	if (!nacl->device_list)		return 0; 	spin_lock_irq(&nacl->device_list_lock);	for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {		deve = nacl->device_list[i]; 		if (!(deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS))			continue; 		if (!deve->se_lun) {			pr_err("%s device entries device pointer is"				" NULL, but Initiator has access.\n",				tpg->se_tpg_tfo->get_fabric_name());			continue;		}		lun = deve->se_lun; 		spin_unlock_irq(&nacl->device_list_lock);		core_update_device_list_for_node(lun, NULL, deve->mapped_lun,			TRANSPORT_LUNFLAGS_NO_ACCESS, nacl, tpg, 0);		spin_lock_irq(&nacl->device_list_lock);	}	spin_unlock_irq(&nacl->device_list_lock); 	array_free(nacl->device_list, TRANSPORT_MAX_LUNS_PER_TPG);	nacl->device_list = NULL; 	return 0;} void core_dec_lacl_count(struct se_node_acl *se_nacl, struct se_cmd *se_cmd){	struct se_dev_entry *deve;	unsigned long flags; 	spin_lock_irqsave(&se_nacl->device_list_lock, flags);	deve = se_nacl->device_list[se_cmd->orig_fe_lun];	deve->deve_cmds--;	spin_unlock_irqrestore(&se_nacl->device_list_lock, flags);} void core_update_device_list_access(	u32 mapped_lun,	u32 lun_access,	struct se_node_acl *nacl){	struct se_dev_entry *deve; 	spin_lock_irq(&nacl->device_list_lock);	deve = nacl->device_list[mapped_lun];	if (lun_access & TRANSPORT_LUNFLAGS_READ_WRITE) {		deve->lun_flags &= ~TRANSPORT_LUNFLAGS_READ_ONLY;		deve->lun_flags |= TRANSPORT_LUNFLAGS_READ_WRITE;	} else {		deve->lun_flags &= ~TRANSPORT_LUNFLAGS_READ_WRITE;		deve->lun_flags |= TRANSPORT_LUNFLAGS_READ_ONLY;	}	spin_unlock_irq(&nacl->device_list_lock);} /*      core_update_device_list_for_node(): * * */int core_update_device_list_for_node(	struct se_lun *lun,	struct se_lun_acl *lun_acl,	u32 mapped_lun,	u32 lun_access,	struct se_node_acl *nacl,	struct se_portal_group *tpg,	int enable){	struct se_port *port = lun->lun_sep;	struct se_dev_entry *deve = nacl->device_list[mapped_lun];	int trans = 0;	/*	 * If the MappedLUN entry is being disabled, the entry in	 * port->sep_alua_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.	 */	if (!enable) {		/*		 * deve->se_lun_acl will be NULL for demo-mode created LUNs		 * that have not been explicitly concerted to MappedLUNs ->		 * struct se_lun_acl, but we remove deve->alua_port_list from		 * port->sep_alua_list. This also means that active UAs and		 * NodeACL context specific PR metadata for demo-mode		 * MappedLUN *deve will be released below..		 */		spin_lock_bh(&port->sep_alua_lock);		list_del(&deve->alua_port_list);		spin_unlock_bh(&port->sep_alua_lock);	} 	spin_lock_irq(&nacl->device_list_lock);	if (enable) {		/*		 * Check if the call is handling demo mode -> explict LUN ACL		 * transition.  This transition must be for the same struct se_lun		 * + mapped_lun that was setup in demo mode..		 */		if (deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS) {			if (deve->se_lun_acl != NULL) {				pr_err("struct se_dev_entry->se_lun_acl"					" already set for demo mode -> explict"					" LUN ACL transition\n");				spin_unlock_irq(&nacl->device_list_lock);				return -EINVAL;			}			if (deve->se_lun != lun) {				pr_err("struct se_dev_entry->se_lun does"					" match passed struct se_lun for demo mode"					" -> explict LUN ACL transition\n");				spin_unlock_irq(&nacl->device_list_lock);				return -EINVAL;			}			deve->se_lun_acl = lun_acl;			trans = 1;		} else {			deve->se_lun = lun;			deve->se_lun_acl = lun_acl;			deve->mapped_lun = mapped_lun;			deve->lun_flags |= TRANSPORT_LUNFLAGS_INITIATOR_ACCESS;		} 		if (lun_access & TRANSPORT_LUNFLAGS_READ_WRITE) {			deve->lun_flags &= ~TRANSPORT_LUNFLAGS_READ_ONLY;			deve->lun_flags |= TRANSPORT_LUNFLAGS_READ_WRITE;		} else {			deve->lun_flags &= ~TRANSPORT_LUNFLAGS_READ_WRITE;			deve->lun_flags |= TRANSPORT_LUNFLAGS_READ_ONLY;		} 		if (trans) {			spin_unlock_irq(&nacl->device_list_lock);			return 0;		}		deve->creation_time = get_jiffies_64();		deve->attach_count++;		spin_unlock_irq(&nacl->device_list_lock); 		spin_lock_bh(&port->sep_alua_lock);		list_add_tail(&deve->alua_port_list, &port->sep_alua_list);		spin_unlock_bh(&port->sep_alua_lock); 		return 0;	}	/*	 * Wait for any in process SPEC_I_PT=1 or REGISTER_AND_MOVE	 * PR operation to complete.	 */	spin_unlock_irq(&nacl->device_list_lock);	while (atomic_read(&deve->pr_ref_count) != 0)		cpu_relax();	spin_lock_irq(&nacl->device_list_lock);	/*	 * Disable struct se_dev_entry LUN ACL mapping	 */	core_scsi3_ua_release_all(deve);	deve->se_lun = NULL;	deve->se_lun_acl = NULL;	deve->lun_flags = 0;	deve->creation_time = 0;	deve->attach_count--;	spin_unlock_irq(&nacl->device_list_lock); 	core_scsi3_free_pr_reg_from_nacl(lun->lun_se_dev, nacl);	return 0;} /*      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;	u32 i; 	spin_lock_irq(&tpg->acl_node_lock);	list_for_each_entry(nacl, &tpg->acl_node_list, acl_list) {		spin_unlock_irq(&tpg->acl_node_lock); 		spin_lock_irq(&nacl->device_list_lock);		for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {			deve = nacl->device_list[i];			if (lun != deve->se_lun)				continue;			spin_unlock_irq(&nacl->device_list_lock); 			core_update_device_list_for_node(lun, NULL,				deve->mapped_lun, TRANSPORT_LUNFLAGS_NO_ACCESS,				nacl, tpg, 0); 			spin_lock_irq(&nacl->device_list_lock);		}		spin_unlock_irq(&nacl->device_list_lock); 		spin_lock_irq(&tpg->acl_node_lock);	}	spin_unlock_irq(&tpg->acl_node_lock);} static struct se_port *core_alloc_port(struct se_device *dev){	struct se_port *port, *port_tmp; 	port = kzalloc(sizeof(struct se_port), GFP_KERNEL);	if (!port) {		pr_err("Unable to allocate struct se_port\n");		return ERR_PTR(-ENOMEM);	}	INIT_LIST_HEAD(&port->sep_alua_list);

Lines 1–500 of 1628.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t