/******************************************************************************* * 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. Load more
/******************************************************************************* * 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);