mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	[SCSI] target: Add LIO target core v4.0.0-rc6
LIO target is a full featured in-kernel target framework with the
following feature set:
High-performance, non-blocking, multithreaded architecture with SIMD
support.
Advanced SCSI feature set:
    * Persistent Reservations (PRs)
    * Asymmetric Logical Unit Assignment (ALUA)
    * Protocol and intra-nexus multiplexing, load-balancing and failover (MC/S)
    * Full Error Recovery (ERL=0,1,2)
    * Active/active task migration and session continuation (ERL=2)
    * Thin LUN provisioning (UNMAP and WRITE_SAMExx)
Multiprotocol target plugins
Storage media independence:
    * Virtualization of all storage media; transparent mapping of IO to LUNs
    * No hard limits on number of LUNs per Target; maximum LUN size ~750 TB
    * Backstores: SATA, SAS, SCSI, BluRay, DVD, FLASH, USB, ramdisk, etc.
Standards compliance:
    * Full compliance with IETF (RFC 3720)
    * Full implementation of SPC-4 PRs and ALUA
Significant code cleanups done by Christoph Hellwig.
[jejb: fix up for new block bdev exclusive interface. Minor fixes from
 Randy Dunlap and Dan Carpenter.]
Signed-off-by: Nicholas A. Bellinger <nab@linux-iscsi.org>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
			
			
This commit is contained in:
		
							parent
							
								
									f4013c3879
								
							
						
					
					
						commit
						c66ac9db8d
					
				
					 44 changed files with 30521 additions and 0 deletions
				
			
		
							
								
								
									
										1094
									
								
								Documentation/target/tcm_mod_builder.py
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										1094
									
								
								Documentation/target/tcm_mod_builder.py
									
									
									
									
									
										Executable file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										145
									
								
								Documentation/target/tcm_mod_builder.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								Documentation/target/tcm_mod_builder.txt
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,145 @@
 | 
			
		|||
>>>>>>>>>> The TCM v4 fabric module script generator <<<<<<<<<<
 | 
			
		||||
 | 
			
		||||
Greetings all,
 | 
			
		||||
 | 
			
		||||
This document is intended to be a mini-HOWTO for using the tcm_mod_builder.py
 | 
			
		||||
script to generate a brand new functional TCM v4 fabric .ko module of your very own,
 | 
			
		||||
that once built can be immediately be loaded to start access the new TCM/ConfigFS
 | 
			
		||||
fabric skeleton, by simply using:
 | 
			
		||||
 | 
			
		||||
	modprobe $TCM_NEW_MOD
 | 
			
		||||
	mkdir -p /sys/kernel/config/target/$TCM_NEW_MOD
 | 
			
		||||
 | 
			
		||||
This script will create a new drivers/target/$TCM_NEW_MOD/, and will do the following
 | 
			
		||||
 | 
			
		||||
	*) Generate new API callers for drivers/target/target_core_fabric_configs.c logic
 | 
			
		||||
	   ->make_nodeacl(), ->drop_nodeacl(), ->make_tpg(), ->drop_tpg()
 | 
			
		||||
	   ->make_wwn(), ->drop_wwn().  These are created into $TCM_NEW_MOD/$TCM_NEW_MOD_configfs.c
 | 
			
		||||
	*) Generate basic infrastructure for loading/unloading LKMs and TCM/ConfigFS fabric module
 | 
			
		||||
	   using a skeleton struct target_core_fabric_ops API template.
 | 
			
		||||
	*) Based on user defined T10 Proto_Ident for the new fabric module being built,
 | 
			
		||||
	   the TransportID / Initiator and Target WWPN related handlers for
 | 
			
		||||
	   SPC-3 persistent reservation are automatically generated in $TCM_NEW_MOD/$TCM_NEW_MOD_fabric.c
 | 
			
		||||
	   using drivers/target/target_core_fabric_lib.c logic.
 | 
			
		||||
	*) NOP API calls for all other Data I/O path and fabric dependent attribute logic
 | 
			
		||||
	   in $TCM_NEW_MOD/$TCM_NEW_MOD_fabric.c
 | 
			
		||||
 | 
			
		||||
tcm_mod_builder.py depends upon the mandatory '-p $PROTO_IDENT' and '-m
 | 
			
		||||
$FABRIC_MOD_name' parameters, and actually running the script looks like:
 | 
			
		||||
 | 
			
		||||
target:/mnt/sdb/lio-core-2.6.git/Documentation/target# python tcm_mod_builder.py -p iSCSI -m tcm_nab5000
 | 
			
		||||
tcm_dir: /mnt/sdb/lio-core-2.6.git/Documentation/target/../../
 | 
			
		||||
Set fabric_mod_name: tcm_nab5000
 | 
			
		||||
Set fabric_mod_dir:
 | 
			
		||||
/mnt/sdb/lio-core-2.6.git/Documentation/target/../../drivers/target/tcm_nab5000
 | 
			
		||||
Using proto_ident: iSCSI
 | 
			
		||||
Creating fabric_mod_dir:
 | 
			
		||||
/mnt/sdb/lio-core-2.6.git/Documentation/target/../../drivers/target/tcm_nab5000
 | 
			
		||||
Writing file:
 | 
			
		||||
/mnt/sdb/lio-core-2.6.git/Documentation/target/../../drivers/target/tcm_nab5000/tcm_nab5000_base.h
 | 
			
		||||
Using tcm_mod_scan_fabric_ops:
 | 
			
		||||
/mnt/sdb/lio-core-2.6.git/Documentation/target/../../include/target/target_core_fabric_ops.h
 | 
			
		||||
Writing file:
 | 
			
		||||
/mnt/sdb/lio-core-2.6.git/Documentation/target/../../drivers/target/tcm_nab5000/tcm_nab5000_fabric.c
 | 
			
		||||
Writing file:
 | 
			
		||||
/mnt/sdb/lio-core-2.6.git/Documentation/target/../../drivers/target/tcm_nab5000/tcm_nab5000_fabric.h
 | 
			
		||||
Writing file:
 | 
			
		||||
/mnt/sdb/lio-core-2.6.git/Documentation/target/../../drivers/target/tcm_nab5000/tcm_nab5000_configfs.c
 | 
			
		||||
Writing file:
 | 
			
		||||
/mnt/sdb/lio-core-2.6.git/Documentation/target/../../drivers/target/tcm_nab5000/Kbuild
 | 
			
		||||
Writing file:
 | 
			
		||||
/mnt/sdb/lio-core-2.6.git/Documentation/target/../../drivers/target/tcm_nab5000/Kconfig
 | 
			
		||||
Would you like to add tcm_nab5000to drivers/target/Kbuild..? [yes,no]: yes
 | 
			
		||||
Would you like to add tcm_nab5000to drivers/target/Kconfig..? [yes,no]: yes
 | 
			
		||||
 | 
			
		||||
At the end of tcm_mod_builder.py. the script will ask to add the following
 | 
			
		||||
line to drivers/target/Kbuild:
 | 
			
		||||
 | 
			
		||||
	obj-$(CONFIG_TCM_NAB5000)       += tcm_nab5000/
 | 
			
		||||
 | 
			
		||||
and the same for drivers/target/Kconfig:
 | 
			
		||||
 | 
			
		||||
	source "drivers/target/tcm_nab5000/Kconfig"
 | 
			
		||||
 | 
			
		||||
*) Run 'make menuconfig' and select the new CONFIG_TCM_NAB5000 item:
 | 
			
		||||
 | 
			
		||||
	<M>   TCM_NAB5000 fabric module
 | 
			
		||||
 | 
			
		||||
*) Build using 'make modules', once completed you will have:
 | 
			
		||||
 | 
			
		||||
target:/mnt/sdb/lio-core-2.6.git# ls -la drivers/target/tcm_nab5000/
 | 
			
		||||
total 1348
 | 
			
		||||
drwxr-xr-x 2 root root   4096 2010-10-05 03:23 .
 | 
			
		||||
drwxr-xr-x 9 root root   4096 2010-10-05 03:22 ..
 | 
			
		||||
-rw-r--r-- 1 root root    282 2010-10-05 03:22 Kbuild
 | 
			
		||||
-rw-r--r-- 1 root root    171 2010-10-05 03:22 Kconfig
 | 
			
		||||
-rw-r--r-- 1 root root     49 2010-10-05 03:23 modules.order
 | 
			
		||||
-rw-r--r-- 1 root root    738 2010-10-05 03:22 tcm_nab5000_base.h
 | 
			
		||||
-rw-r--r-- 1 root root   9096 2010-10-05 03:22 tcm_nab5000_configfs.c
 | 
			
		||||
-rw-r--r-- 1 root root 191200 2010-10-05 03:23 tcm_nab5000_configfs.o
 | 
			
		||||
-rw-r--r-- 1 root root  40504 2010-10-05 03:23 .tcm_nab5000_configfs.o.cmd
 | 
			
		||||
-rw-r--r-- 1 root root   5414 2010-10-05 03:22 tcm_nab5000_fabric.c
 | 
			
		||||
-rw-r--r-- 1 root root   2016 2010-10-05 03:22 tcm_nab5000_fabric.h
 | 
			
		||||
-rw-r--r-- 1 root root 190932 2010-10-05 03:23 tcm_nab5000_fabric.o
 | 
			
		||||
-rw-r--r-- 1 root root  40713 2010-10-05 03:23 .tcm_nab5000_fabric.o.cmd
 | 
			
		||||
-rw-r--r-- 1 root root 401861 2010-10-05 03:23 tcm_nab5000.ko
 | 
			
		||||
-rw-r--r-- 1 root root    265 2010-10-05 03:23 .tcm_nab5000.ko.cmd
 | 
			
		||||
-rw-r--r-- 1 root root    459 2010-10-05 03:23 tcm_nab5000.mod.c
 | 
			
		||||
-rw-r--r-- 1 root root  23896 2010-10-05 03:23 tcm_nab5000.mod.o
 | 
			
		||||
-rw-r--r-- 1 root root  22655 2010-10-05 03:23 .tcm_nab5000.mod.o.cmd
 | 
			
		||||
-rw-r--r-- 1 root root 379022 2010-10-05 03:23 tcm_nab5000.o
 | 
			
		||||
-rw-r--r-- 1 root root    211 2010-10-05 03:23 .tcm_nab5000.o.cmd
 | 
			
		||||
 | 
			
		||||
*) Load the new module, create a lun_0 configfs group, and add new TCM Core
 | 
			
		||||
   IBLOCK backstore symlink to port:
 | 
			
		||||
 | 
			
		||||
target:/mnt/sdb/lio-core-2.6.git# insmod drivers/target/tcm_nab5000.ko
 | 
			
		||||
target:/mnt/sdb/lio-core-2.6.git# mkdir -p /sys/kernel/config/target/nab5000/iqn.foo/tpgt_1/lun/lun_0
 | 
			
		||||
target:/mnt/sdb/lio-core-2.6.git# cd /sys/kernel/config/target/nab5000/iqn.foo/tpgt_1/lun/lun_0/
 | 
			
		||||
target:/sys/kernel/config/target/nab5000/iqn.foo/tpgt_1/lun/lun_0# ln -s /sys/kernel/config/target/core/iblock_0/lvm_test0 nab5000_port
 | 
			
		||||
 | 
			
		||||
target:/sys/kernel/config/target/nab5000/iqn.foo/tpgt_1/lun/lun_0# cd -
 | 
			
		||||
target:/mnt/sdb/lio-core-2.6.git# tree /sys/kernel/config/target/nab5000/
 | 
			
		||||
/sys/kernel/config/target/nab5000/
 | 
			
		||||
|-- discovery_auth
 | 
			
		||||
|-- iqn.foo
 | 
			
		||||
|   `-- tpgt_1
 | 
			
		||||
|       |-- acls
 | 
			
		||||
|       |-- attrib
 | 
			
		||||
|       |-- lun
 | 
			
		||||
|       |   `-- lun_0
 | 
			
		||||
|       |       |-- alua_tg_pt_gp
 | 
			
		||||
|       |       |-- alua_tg_pt_offline
 | 
			
		||||
|       |       |-- alua_tg_pt_status
 | 
			
		||||
|       |       |-- alua_tg_pt_write_md
 | 
			
		||||
|	|	`-- nab5000_port -> ../../../../../../target/core/iblock_0/lvm_test0
 | 
			
		||||
|       |-- np
 | 
			
		||||
|       `-- param
 | 
			
		||||
`-- version
 | 
			
		||||
 | 
			
		||||
target:/mnt/sdb/lio-core-2.6.git# lsmod
 | 
			
		||||
Module                  Size  Used by
 | 
			
		||||
tcm_nab5000             3935  4
 | 
			
		||||
iscsi_target_mod      193211  0
 | 
			
		||||
target_core_stgt        8090  0
 | 
			
		||||
target_core_pscsi      11122  1
 | 
			
		||||
target_core_file        9172  2
 | 
			
		||||
target_core_iblock      9280  1
 | 
			
		||||
target_core_mod       228575  31
 | 
			
		||||
tcm_nab5000,iscsi_target_mod,target_core_stgt,target_core_pscsi,target_core_file,target_core_iblock
 | 
			
		||||
libfc                  73681  0
 | 
			
		||||
scsi_debug             56265  0
 | 
			
		||||
scsi_tgt                8666  1 target_core_stgt
 | 
			
		||||
configfs               20644  2 target_core_mod
 | 
			
		||||
 | 
			
		||||
----------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
Future TODO items:
 | 
			
		||||
 | 
			
		||||
	*) Add more T10 proto_idents
 | 
			
		||||
	*) Make tcm_mod_dump_fabric_ops() smarter and generate function pointer
 | 
			
		||||
	   defs directly from include/target/target_core_fabric_ops.h:struct target_core_fabric_ops
 | 
			
		||||
	   structure members.
 | 
			
		||||
 | 
			
		||||
October 5th, 2010
 | 
			
		||||
Nicholas A. Bellinger <nab@linux-iscsi.org>
 | 
			
		||||
| 
						 | 
				
			
			@ -26,6 +26,8 @@ source "drivers/ata/Kconfig"
 | 
			
		|||
 | 
			
		||||
source "drivers/md/Kconfig"
 | 
			
		||||
 | 
			
		||||
source "drivers/target/Kconfig"
 | 
			
		||||
 | 
			
		||||
source "drivers/message/fusion/Kconfig"
 | 
			
		||||
 | 
			
		||||
source "drivers/firewire/Kconfig"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -46,6 +46,7 @@ obj-y				+= macintosh/
 | 
			
		|||
obj-$(CONFIG_IDE)		+= ide/
 | 
			
		||||
obj-$(CONFIG_SCSI)		+= scsi/
 | 
			
		||||
obj-$(CONFIG_ATA)		+= ata/
 | 
			
		||||
obj-$(CONFIG_TARGET_CORE)	+= target/
 | 
			
		||||
obj-$(CONFIG_MTD)		+= mtd/
 | 
			
		||||
obj-$(CONFIG_SPI)		+= spi/
 | 
			
		||||
obj-y				+= net/
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										32
									
								
								drivers/target/Kconfig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								drivers/target/Kconfig
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,32 @@
 | 
			
		|||
 | 
			
		||||
menuconfig TARGET_CORE
 | 
			
		||||
	tristate "Generic Target Core Mod (TCM) and ConfigFS Infrastructure"
 | 
			
		||||
	depends on SCSI && BLOCK
 | 
			
		||||
	select CONFIGFS_FS
 | 
			
		||||
	default n
 | 
			
		||||
	help
 | 
			
		||||
	Say Y or M here to enable the TCM Storage Engine and ConfigFS enabled
 | 
			
		||||
	control path for target_core_mod.  This includes built-in TCM RAMDISK
 | 
			
		||||
	subsystem logic for virtual LUN 0 access
 | 
			
		||||
 | 
			
		||||
if TARGET_CORE
 | 
			
		||||
 | 
			
		||||
config TCM_IBLOCK
 | 
			
		||||
	tristate "TCM/IBLOCK Subsystem Plugin for Linux/BLOCK"
 | 
			
		||||
	help
 | 
			
		||||
	Say Y here to enable the TCM/IBLOCK subsystem plugin for non-buffered
 | 
			
		||||
	access to Linux/Block devices using BIO
 | 
			
		||||
 | 
			
		||||
config TCM_FILEIO
 | 
			
		||||
	tristate "TCM/FILEIO Subsystem Plugin for Linux/VFS"
 | 
			
		||||
	help
 | 
			
		||||
	Say Y here to enable the TCM/FILEIO subsystem plugin for buffered
 | 
			
		||||
	access to Linux/VFS struct file or struct block_device
 | 
			
		||||
 | 
			
		||||
config TCM_PSCSI
 | 
			
		||||
	tristate "TCM/pSCSI Subsystem Plugin for Linux/SCSI"
 | 
			
		||||
	help
 | 
			
		||||
	Say Y here to enable the TCM/pSCSI subsystem plugin for non-buffered
 | 
			
		||||
	passthrough access to Linux/SCSI device
 | 
			
		||||
 | 
			
		||||
endif
 | 
			
		||||
							
								
								
									
										24
									
								
								drivers/target/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								drivers/target/Makefile
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
EXTRA_CFLAGS += -I$(srctree)/drivers/target/ -I$(srctree)/drivers/scsi/
 | 
			
		||||
 | 
			
		||||
target_core_mod-y		:= target_core_configfs.o \
 | 
			
		||||
				   target_core_device.o \
 | 
			
		||||
				   target_core_fabric_configfs.o \
 | 
			
		||||
				   target_core_fabric_lib.o \
 | 
			
		||||
				   target_core_hba.o \
 | 
			
		||||
				   target_core_pr.o \
 | 
			
		||||
				   target_core_alua.o \
 | 
			
		||||
				   target_core_scdb.o \
 | 
			
		||||
				   target_core_tmr.o \
 | 
			
		||||
				   target_core_tpg.o \
 | 
			
		||||
				   target_core_transport.o \
 | 
			
		||||
				   target_core_cdb.o \
 | 
			
		||||
				   target_core_ua.o \
 | 
			
		||||
				   target_core_rd.o \
 | 
			
		||||
				   target_core_mib.o
 | 
			
		||||
 | 
			
		||||
obj-$(CONFIG_TARGET_CORE)	+= target_core_mod.o
 | 
			
		||||
 | 
			
		||||
# Subsystem modules
 | 
			
		||||
obj-$(CONFIG_TCM_IBLOCK)	+= target_core_iblock.o
 | 
			
		||||
obj-$(CONFIG_TCM_FILEIO)	+= target_core_file.o
 | 
			
		||||
obj-$(CONFIG_TCM_PSCSI)		+= target_core_pscsi.o
 | 
			
		||||
							
								
								
									
										1991
									
								
								drivers/target/target_core_alua.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1991
									
								
								drivers/target/target_core_alua.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										126
									
								
								drivers/target/target_core_alua.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								drivers/target/target_core_alua.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,126 @@
 | 
			
		|||
#ifndef TARGET_CORE_ALUA_H
 | 
			
		||||
#define TARGET_CORE_ALUA_H
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * INQUIRY response data, TPGS Field
 | 
			
		||||
 *
 | 
			
		||||
 * from spc4r17 section 6.4.2 Table 135
 | 
			
		||||
 */
 | 
			
		||||
#define TPGS_NO_ALUA				0x00
 | 
			
		||||
#define TPGS_IMPLICT_ALUA			0x10
 | 
			
		||||
#define TPGS_EXPLICT_ALUA			0x20
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ASYMMETRIC ACCESS STATE field
 | 
			
		||||
 *
 | 
			
		||||
 * from spc4r17 section 6.27 Table 245
 | 
			
		||||
 */
 | 
			
		||||
#define ALUA_ACCESS_STATE_ACTIVE_OPTMIZED	0x0
 | 
			
		||||
#define ALUA_ACCESS_STATE_ACTIVE_NON_OPTIMIZED	0x1
 | 
			
		||||
#define ALUA_ACCESS_STATE_STANDBY		0x2
 | 
			
		||||
#define ALUA_ACCESS_STATE_UNAVAILABLE		0x3
 | 
			
		||||
#define ALUA_ACCESS_STATE_OFFLINE		0xe
 | 
			
		||||
#define ALUA_ACCESS_STATE_TRANSITION		0xf
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * REPORT_TARGET_PORT_GROUP STATUS CODE
 | 
			
		||||
 *
 | 
			
		||||
 * from spc4r17 section 6.27 Table 246
 | 
			
		||||
 */
 | 
			
		||||
#define ALUA_STATUS_NONE				0x00
 | 
			
		||||
#define ALUA_STATUS_ALTERED_BY_EXPLICT_STPG		0x01
 | 
			
		||||
#define ALUA_STATUS_ALTERED_BY_IMPLICT_ALUA		0x02
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * From spc4r17, Table D.1: ASC and ASCQ Assignement
 | 
			
		||||
 */
 | 
			
		||||
#define ASCQ_04H_ALUA_STATE_TRANSITION			0x0a
 | 
			
		||||
#define ASCQ_04H_ALUA_TG_PT_STANDBY			0x0b
 | 
			
		||||
#define ASCQ_04H_ALUA_TG_PT_UNAVAILABLE			0x0c
 | 
			
		||||
#define ASCQ_04H_ALUA_OFFLINE				0x12
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Used as the default for Active/NonOptimized delay (in milliseconds)
 | 
			
		||||
 * This can also be changed via configfs on a per target port group basis..
 | 
			
		||||
 */
 | 
			
		||||
#define ALUA_DEFAULT_NONOP_DELAY_MSECS			100
 | 
			
		||||
#define ALUA_MAX_NONOP_DELAY_MSECS			10000 /* 10 seconds */
 | 
			
		||||
/*
 | 
			
		||||
 * Used for implict and explict ALUA transitional delay, that is disabled
 | 
			
		||||
 * by default, and is intended to be used for debugging client side ALUA code.
 | 
			
		||||
 */
 | 
			
		||||
#define ALUA_DEFAULT_TRANS_DELAY_MSECS			0
 | 
			
		||||
#define ALUA_MAX_TRANS_DELAY_MSECS			30000 /* 30 seconds */
 | 
			
		||||
/*
 | 
			
		||||
 * Used by core_alua_update_tpg_primary_metadata() and
 | 
			
		||||
 * core_alua_update_tpg_secondary_metadata()
 | 
			
		||||
 */
 | 
			
		||||
#define ALUA_METADATA_PATH_LEN				512
 | 
			
		||||
/*
 | 
			
		||||
 * Used by core_alua_update_tpg_secondary_metadata()
 | 
			
		||||
 */
 | 
			
		||||
#define ALUA_SECONDARY_METADATA_WWN_LEN			256
 | 
			
		||||
 | 
			
		||||
extern struct kmem_cache *t10_alua_lu_gp_cache;
 | 
			
		||||
extern struct kmem_cache *t10_alua_lu_gp_mem_cache;
 | 
			
		||||
extern struct kmem_cache *t10_alua_tg_pt_gp_cache;
 | 
			
		||||
extern struct kmem_cache *t10_alua_tg_pt_gp_mem_cache;
 | 
			
		||||
 | 
			
		||||
extern int core_emulate_report_target_port_groups(struct se_cmd *);
 | 
			
		||||
extern int core_emulate_set_target_port_groups(struct se_cmd *);
 | 
			
		||||
extern int core_alua_check_nonop_delay(struct se_cmd *);
 | 
			
		||||
extern int core_alua_do_port_transition(struct t10_alua_tg_pt_gp *,
 | 
			
		||||
				struct se_device *, struct se_port *,
 | 
			
		||||
				struct se_node_acl *, int, int);
 | 
			
		||||
extern char *core_alua_dump_status(int);
 | 
			
		||||
extern struct t10_alua_lu_gp *core_alua_allocate_lu_gp(const char *, int);
 | 
			
		||||
extern int core_alua_set_lu_gp_id(struct t10_alua_lu_gp *, u16);
 | 
			
		||||
extern void core_alua_free_lu_gp(struct t10_alua_lu_gp *);
 | 
			
		||||
extern void core_alua_free_lu_gp_mem(struct se_device *);
 | 
			
		||||
extern struct t10_alua_lu_gp *core_alua_get_lu_gp_by_name(const char *);
 | 
			
		||||
extern void core_alua_put_lu_gp_from_name(struct t10_alua_lu_gp *);
 | 
			
		||||
extern void __core_alua_attach_lu_gp_mem(struct t10_alua_lu_gp_member *,
 | 
			
		||||
					struct t10_alua_lu_gp *);
 | 
			
		||||
extern void __core_alua_drop_lu_gp_mem(struct t10_alua_lu_gp_member *,
 | 
			
		||||
					struct t10_alua_lu_gp *);
 | 
			
		||||
extern void core_alua_drop_lu_gp_dev(struct se_device *);
 | 
			
		||||
extern struct t10_alua_tg_pt_gp *core_alua_allocate_tg_pt_gp(
 | 
			
		||||
			struct se_subsystem_dev *, const char *, int);
 | 
			
		||||
extern int core_alua_set_tg_pt_gp_id(struct t10_alua_tg_pt_gp *, u16);
 | 
			
		||||
extern struct t10_alua_tg_pt_gp_member *core_alua_allocate_tg_pt_gp_mem(
 | 
			
		||||
					struct se_port *);
 | 
			
		||||
extern void core_alua_free_tg_pt_gp(struct t10_alua_tg_pt_gp *);
 | 
			
		||||
extern void core_alua_free_tg_pt_gp_mem(struct se_port *);
 | 
			
		||||
extern void __core_alua_attach_tg_pt_gp_mem(struct t10_alua_tg_pt_gp_member *,
 | 
			
		||||
					struct t10_alua_tg_pt_gp *);
 | 
			
		||||
extern ssize_t core_alua_show_tg_pt_gp_info(struct se_port *, char *);
 | 
			
		||||
extern ssize_t core_alua_store_tg_pt_gp_info(struct se_port *, const char *,
 | 
			
		||||
						size_t);
 | 
			
		||||
extern ssize_t core_alua_show_access_type(struct t10_alua_tg_pt_gp *, char *);
 | 
			
		||||
extern ssize_t core_alua_store_access_type(struct t10_alua_tg_pt_gp *,
 | 
			
		||||
					const char *, size_t);
 | 
			
		||||
extern ssize_t core_alua_show_nonop_delay_msecs(struct t10_alua_tg_pt_gp *,
 | 
			
		||||
						char *);
 | 
			
		||||
extern ssize_t core_alua_store_nonop_delay_msecs(struct t10_alua_tg_pt_gp *,
 | 
			
		||||
					const char *, size_t);
 | 
			
		||||
extern ssize_t core_alua_show_trans_delay_msecs(struct t10_alua_tg_pt_gp *,
 | 
			
		||||
					char *);
 | 
			
		||||
extern ssize_t core_alua_store_trans_delay_msecs(struct t10_alua_tg_pt_gp *,
 | 
			
		||||
					const char *, size_t);
 | 
			
		||||
extern ssize_t core_alua_show_preferred_bit(struct t10_alua_tg_pt_gp *,
 | 
			
		||||
					char *);
 | 
			
		||||
extern ssize_t core_alua_store_preferred_bit(struct t10_alua_tg_pt_gp *,
 | 
			
		||||
					const char *, size_t);
 | 
			
		||||
extern ssize_t core_alua_show_offline_bit(struct se_lun *, char *);
 | 
			
		||||
extern ssize_t core_alua_store_offline_bit(struct se_lun *, const char *,
 | 
			
		||||
					size_t);
 | 
			
		||||
extern ssize_t core_alua_show_secondary_status(struct se_lun *, char *);
 | 
			
		||||
extern ssize_t core_alua_store_secondary_status(struct se_lun *,
 | 
			
		||||
					const char *, size_t);
 | 
			
		||||
extern ssize_t core_alua_show_secondary_write_metadata(struct se_lun *,
 | 
			
		||||
					char *);
 | 
			
		||||
extern ssize_t core_alua_store_secondary_write_metadata(struct se_lun *,
 | 
			
		||||
					const char *, size_t);
 | 
			
		||||
extern int core_setup_alua(struct se_device *, int);
 | 
			
		||||
 | 
			
		||||
#endif /* TARGET_CORE_ALUA_H */
 | 
			
		||||
							
								
								
									
										1131
									
								
								drivers/target/target_core_cdb.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1131
									
								
								drivers/target/target_core_cdb.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										3225
									
								
								drivers/target/target_core_configfs.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3225
									
								
								drivers/target/target_core_configfs.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										1694
									
								
								drivers/target/target_core_device.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1694
									
								
								drivers/target/target_core_device.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										996
									
								
								drivers/target/target_core_fabric_configfs.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										996
									
								
								drivers/target/target_core_fabric_configfs.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,996 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
* Filename: target_core_fabric_configfs.c
 | 
			
		||||
 *
 | 
			
		||||
 * This file contains generic fabric module configfs infrastructure for
 | 
			
		||||
 * TCM v4.x code
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2010 Rising Tide Systems
 | 
			
		||||
 * Copyright (c) 2010 Linux-iSCSI.org
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2010 Nicholas A. Bellinger <nab@linux-iscsi.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.
 | 
			
		||||
 ****************************************************************************/
 | 
			
		||||
 | 
			
		||||
#include <linux/module.h>
 | 
			
		||||
#include <linux/moduleparam.h>
 | 
			
		||||
#include <linux/version.h>
 | 
			
		||||
#include <generated/utsrelease.h>
 | 
			
		||||
#include <linux/utsname.h>
 | 
			
		||||
#include <linux/init.h>
 | 
			
		||||
#include <linux/fs.h>
 | 
			
		||||
#include <linux/namei.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/types.h>
 | 
			
		||||
#include <linux/delay.h>
 | 
			
		||||
#include <linux/unistd.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/syscalls.h>
 | 
			
		||||
#include <linux/configfs.h>
 | 
			
		||||
 | 
			
		||||
#include <target/target_core_base.h>
 | 
			
		||||
#include <target/target_core_device.h>
 | 
			
		||||
#include <target/target_core_tpg.h>
 | 
			
		||||
#include <target/target_core_transport.h>
 | 
			
		||||
#include <target/target_core_fabric_ops.h>
 | 
			
		||||
#include <target/target_core_fabric_configfs.h>
 | 
			
		||||
#include <target/target_core_configfs.h>
 | 
			
		||||
#include <target/configfs_macros.h>
 | 
			
		||||
 | 
			
		||||
#include "target_core_alua.h"
 | 
			
		||||
#include "target_core_hba.h"
 | 
			
		||||
#include "target_core_pr.h"
 | 
			
		||||
 | 
			
		||||
#define TF_CIT_SETUP(_name, _item_ops, _group_ops, _attrs)		\
 | 
			
		||||
static void target_fabric_setup_##_name##_cit(struct target_fabric_configfs *tf) \
 | 
			
		||||
{									\
 | 
			
		||||
	struct target_fabric_configfs_template *tfc = &tf->tf_cit_tmpl;	\
 | 
			
		||||
	struct config_item_type *cit = &tfc->tfc_##_name##_cit;		\
 | 
			
		||||
									\
 | 
			
		||||
	cit->ct_item_ops = _item_ops;					\
 | 
			
		||||
	cit->ct_group_ops = _group_ops;					\
 | 
			
		||||
	cit->ct_attrs = _attrs;						\
 | 
			
		||||
	cit->ct_owner = tf->tf_module;					\
 | 
			
		||||
	printk("Setup generic %s\n", __stringify(_name));		\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Start of tfc_tpg_mappedlun_cit */
 | 
			
		||||
 | 
			
		||||
static int target_fabric_mappedlun_link(
 | 
			
		||||
	struct config_item *lun_acl_ci,
 | 
			
		||||
	struct config_item *lun_ci)
 | 
			
		||||
{
 | 
			
		||||
	struct se_dev_entry *deve;
 | 
			
		||||
	struct se_lun *lun = container_of(to_config_group(lun_ci),
 | 
			
		||||
			struct se_lun, lun_group);
 | 
			
		||||
	struct se_lun_acl *lacl = container_of(to_config_group(lun_acl_ci),
 | 
			
		||||
			struct se_lun_acl, se_lun_group);
 | 
			
		||||
	struct se_portal_group *se_tpg;
 | 
			
		||||
	struct config_item *nacl_ci, *tpg_ci, *tpg_ci_s, *wwn_ci, *wwn_ci_s;
 | 
			
		||||
	int ret = 0, lun_access;
 | 
			
		||||
	/*
 | 
			
		||||
	 * Ensure that the source port exists
 | 
			
		||||
	 */
 | 
			
		||||
	if (!(lun->lun_sep) || !(lun->lun_sep->sep_tpg)) {
 | 
			
		||||
		printk(KERN_ERR "Source se_lun->lun_sep or lun->lun_sep->sep"
 | 
			
		||||
				"_tpg does not exist\n");
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	se_tpg = lun->lun_sep->sep_tpg;
 | 
			
		||||
 | 
			
		||||
	nacl_ci = &lun_acl_ci->ci_parent->ci_group->cg_item;
 | 
			
		||||
	tpg_ci = &nacl_ci->ci_group->cg_item;
 | 
			
		||||
	wwn_ci = &tpg_ci->ci_group->cg_item;
 | 
			
		||||
	tpg_ci_s = &lun_ci->ci_parent->ci_group->cg_item;
 | 
			
		||||
	wwn_ci_s = &tpg_ci_s->ci_group->cg_item;
 | 
			
		||||
	/*
 | 
			
		||||
	 * Make sure the SymLink is going to the same $FABRIC/$WWN/tpgt_$TPGT
 | 
			
		||||
	 */
 | 
			
		||||
	if (strcmp(config_item_name(wwn_ci), config_item_name(wwn_ci_s))) {
 | 
			
		||||
		printk(KERN_ERR "Illegal Initiator ACL SymLink outside of %s\n",
 | 
			
		||||
			config_item_name(wwn_ci));
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	if (strcmp(config_item_name(tpg_ci), config_item_name(tpg_ci_s))) {
 | 
			
		||||
		printk(KERN_ERR "Illegal Initiator ACL Symlink outside of %s"
 | 
			
		||||
			" TPGT: %s\n", config_item_name(wwn_ci),
 | 
			
		||||
			config_item_name(tpg_ci));
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	/*
 | 
			
		||||
	 * If this struct se_node_acl was dynamically generated with
 | 
			
		||||
	 * tpg_1/attrib/generate_node_acls=1, use the existing deve->lun_flags,
 | 
			
		||||
	 * which be will write protected (READ-ONLY) when
 | 
			
		||||
	 * tpg_1/attrib/demo_mode_write_protect=1
 | 
			
		||||
	 */
 | 
			
		||||
	spin_lock_irq(&lacl->se_lun_nacl->device_list_lock);
 | 
			
		||||
	deve = &lacl->se_lun_nacl->device_list[lacl->mapped_lun];
 | 
			
		||||
	if (deve->lun_flags & TRANSPORT_LUNFLAGS_INITIATOR_ACCESS)
 | 
			
		||||
		lun_access = deve->lun_flags;
 | 
			
		||||
	else
 | 
			
		||||
		lun_access =
 | 
			
		||||
			(TPG_TFO(se_tpg)->tpg_check_prod_mode_write_protect(
 | 
			
		||||
				se_tpg)) ? TRANSPORT_LUNFLAGS_READ_ONLY :
 | 
			
		||||
					   TRANSPORT_LUNFLAGS_READ_WRITE;
 | 
			
		||||
	spin_unlock_irq(&lacl->se_lun_nacl->device_list_lock);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Determine the actual mapped LUN value user wants..
 | 
			
		||||
	 *
 | 
			
		||||
	 * This value is what the SCSI Initiator actually sees the
 | 
			
		||||
	 * iscsi/$IQN/$TPGT/lun/lun_* as on their SCSI Initiator Ports.
 | 
			
		||||
	 */
 | 
			
		||||
	ret = core_dev_add_initiator_node_lun_acl(se_tpg, lacl,
 | 
			
		||||
			lun->unpacked_lun, lun_access);
 | 
			
		||||
 | 
			
		||||
	return (ret < 0) ? -EINVAL : 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int target_fabric_mappedlun_unlink(
 | 
			
		||||
	struct config_item *lun_acl_ci,
 | 
			
		||||
	struct config_item *lun_ci)
 | 
			
		||||
{
 | 
			
		||||
	struct se_lun *lun;
 | 
			
		||||
	struct se_lun_acl *lacl = container_of(to_config_group(lun_acl_ci),
 | 
			
		||||
			struct se_lun_acl, se_lun_group);
 | 
			
		||||
	struct se_node_acl *nacl = lacl->se_lun_nacl;
 | 
			
		||||
	struct se_dev_entry *deve = &nacl->device_list[lacl->mapped_lun];
 | 
			
		||||
	struct se_portal_group *se_tpg;
 | 
			
		||||
	/*
 | 
			
		||||
	 * Determine if the underlying MappedLUN has already been released..
 | 
			
		||||
	 */
 | 
			
		||||
	if (!(deve->se_lun))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	lun = container_of(to_config_group(lun_ci), struct se_lun, lun_group);
 | 
			
		||||
	se_tpg = lun->lun_sep->sep_tpg;
 | 
			
		||||
 | 
			
		||||
	core_dev_del_initiator_node_lun_acl(se_tpg, lun, lacl);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_STRUCT(target_fabric_mappedlun, se_lun_acl);
 | 
			
		||||
#define TCM_MAPPEDLUN_ATTR(_name, _mode)				\
 | 
			
		||||
static struct target_fabric_mappedlun_attribute target_fabric_mappedlun_##_name = \
 | 
			
		||||
	__CONFIGFS_EATTR(_name, _mode,					\
 | 
			
		||||
	target_fabric_mappedlun_show_##_name,				\
 | 
			
		||||
	target_fabric_mappedlun_store_##_name);
 | 
			
		||||
 | 
			
		||||
static ssize_t target_fabric_mappedlun_show_write_protect(
 | 
			
		||||
	struct se_lun_acl *lacl,
 | 
			
		||||
	char *page)
 | 
			
		||||
{
 | 
			
		||||
	struct se_node_acl *se_nacl = lacl->se_lun_nacl;
 | 
			
		||||
	struct se_dev_entry *deve;
 | 
			
		||||
	ssize_t len;
 | 
			
		||||
 | 
			
		||||
	spin_lock_irq(&se_nacl->device_list_lock);
 | 
			
		||||
	deve = &se_nacl->device_list[lacl->mapped_lun];
 | 
			
		||||
	len = sprintf(page, "%d\n",
 | 
			
		||||
			(deve->lun_flags & TRANSPORT_LUNFLAGS_READ_ONLY) ?
 | 
			
		||||
			1 : 0);
 | 
			
		||||
	spin_unlock_irq(&se_nacl->device_list_lock);
 | 
			
		||||
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t target_fabric_mappedlun_store_write_protect(
 | 
			
		||||
	struct se_lun_acl *lacl,
 | 
			
		||||
	const char *page,
 | 
			
		||||
	size_t count)
 | 
			
		||||
{
 | 
			
		||||
	struct se_node_acl *se_nacl = lacl->se_lun_nacl;
 | 
			
		||||
	struct se_portal_group *se_tpg = se_nacl->se_tpg;
 | 
			
		||||
	unsigned long op;
 | 
			
		||||
 | 
			
		||||
	if (strict_strtoul(page, 0, &op))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	if ((op != 1) && (op != 0))
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
 | 
			
		||||
	core_update_device_list_access(lacl->mapped_lun, (op) ?
 | 
			
		||||
			TRANSPORT_LUNFLAGS_READ_ONLY :
 | 
			
		||||
			TRANSPORT_LUNFLAGS_READ_WRITE,
 | 
			
		||||
			lacl->se_lun_nacl);
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "%s_ConfigFS: Changed Initiator ACL: %s"
 | 
			
		||||
		" Mapped LUN: %u Write Protect bit to %s\n",
 | 
			
		||||
		TPG_TFO(se_tpg)->get_fabric_name(),
 | 
			
		||||
		lacl->initiatorname, lacl->mapped_lun, (op) ? "ON" : "OFF");
 | 
			
		||||
 | 
			
		||||
	return count;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TCM_MAPPEDLUN_ATTR(write_protect, S_IRUGO | S_IWUSR);
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_OPS(target_fabric_mappedlun, se_lun_acl, se_lun_group);
 | 
			
		||||
 | 
			
		||||
static struct configfs_attribute *target_fabric_mappedlun_attrs[] = {
 | 
			
		||||
	&target_fabric_mappedlun_write_protect.attr,
 | 
			
		||||
	NULL,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct configfs_item_operations target_fabric_mappedlun_item_ops = {
 | 
			
		||||
	.show_attribute		= target_fabric_mappedlun_attr_show,
 | 
			
		||||
	.store_attribute	= target_fabric_mappedlun_attr_store,
 | 
			
		||||
	.allow_link		= target_fabric_mappedlun_link,
 | 
			
		||||
	.drop_link		= target_fabric_mappedlun_unlink,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TF_CIT_SETUP(tpg_mappedlun, &target_fabric_mappedlun_item_ops, NULL,
 | 
			
		||||
		target_fabric_mappedlun_attrs);
 | 
			
		||||
 | 
			
		||||
/* End of tfc_tpg_mappedlun_cit */
 | 
			
		||||
 | 
			
		||||
/* Start of tfc_tpg_nacl_attrib_cit */
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_OPS(target_fabric_nacl_attrib, se_node_acl, acl_attrib_group);
 | 
			
		||||
 | 
			
		||||
static struct configfs_item_operations target_fabric_nacl_attrib_item_ops = {
 | 
			
		||||
	.show_attribute		= target_fabric_nacl_attrib_attr_show,
 | 
			
		||||
	.store_attribute	= target_fabric_nacl_attrib_attr_store,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TF_CIT_SETUP(tpg_nacl_attrib, &target_fabric_nacl_attrib_item_ops, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
/* End of tfc_tpg_nacl_attrib_cit */
 | 
			
		||||
 | 
			
		||||
/* Start of tfc_tpg_nacl_auth_cit */
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_OPS(target_fabric_nacl_auth, se_node_acl, acl_auth_group);
 | 
			
		||||
 | 
			
		||||
static struct configfs_item_operations target_fabric_nacl_auth_item_ops = {
 | 
			
		||||
	.show_attribute		= target_fabric_nacl_auth_attr_show,
 | 
			
		||||
	.store_attribute	= target_fabric_nacl_auth_attr_store,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TF_CIT_SETUP(tpg_nacl_auth, &target_fabric_nacl_auth_item_ops, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
/* End of tfc_tpg_nacl_auth_cit */
 | 
			
		||||
 | 
			
		||||
/* Start of tfc_tpg_nacl_param_cit */
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_OPS(target_fabric_nacl_param, se_node_acl, acl_param_group);
 | 
			
		||||
 | 
			
		||||
static struct configfs_item_operations target_fabric_nacl_param_item_ops = {
 | 
			
		||||
	.show_attribute		= target_fabric_nacl_param_attr_show,
 | 
			
		||||
	.store_attribute	= target_fabric_nacl_param_attr_store,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TF_CIT_SETUP(tpg_nacl_param, &target_fabric_nacl_param_item_ops, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
/* End of tfc_tpg_nacl_param_cit */
 | 
			
		||||
 | 
			
		||||
/* Start of tfc_tpg_nacl_base_cit */
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_OPS(target_fabric_nacl_base, se_node_acl, acl_group);
 | 
			
		||||
 | 
			
		||||
static struct config_group *target_fabric_make_mappedlun(
 | 
			
		||||
	struct config_group *group,
 | 
			
		||||
	const char *name)
 | 
			
		||||
{
 | 
			
		||||
	struct se_node_acl *se_nacl = container_of(group,
 | 
			
		||||
			struct se_node_acl, acl_group);
 | 
			
		||||
	struct se_portal_group *se_tpg = se_nacl->se_tpg;
 | 
			
		||||
	struct target_fabric_configfs *tf = se_tpg->se_tpg_wwn->wwn_tf;
 | 
			
		||||
	struct se_lun_acl *lacl;
 | 
			
		||||
	struct config_item *acl_ci;
 | 
			
		||||
	char *buf;
 | 
			
		||||
	unsigned long mapped_lun;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	acl_ci = &group->cg_item;
 | 
			
		||||
	if (!(acl_ci)) {
 | 
			
		||||
		printk(KERN_ERR "Unable to locatel acl_ci\n");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf = kzalloc(strlen(name) + 1, GFP_KERNEL);
 | 
			
		||||
	if (!(buf)) {
 | 
			
		||||
		printk(KERN_ERR "Unable to allocate memory for name buf\n");
 | 
			
		||||
		return ERR_PTR(-ENOMEM);
 | 
			
		||||
	}
 | 
			
		||||
	snprintf(buf, strlen(name) + 1, "%s", name);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Make sure user is creating iscsi/$IQN/$TPGT/acls/$INITIATOR/lun_$ID.
 | 
			
		||||
	 */
 | 
			
		||||
	if (strstr(buf, "lun_") != buf) {
 | 
			
		||||
		printk(KERN_ERR "Unable to locate \"lun_\" from buf: %s"
 | 
			
		||||
			" name: %s\n", buf, name);
 | 
			
		||||
		ret = -EINVAL;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
	/*
 | 
			
		||||
	 * Determine the Mapped LUN value.  This is what the SCSI Initiator
 | 
			
		||||
	 * Port will actually see.
 | 
			
		||||
	 */
 | 
			
		||||
	if (strict_strtoul(buf + 4, 0, &mapped_lun) || mapped_lun > UINT_MAX) {
 | 
			
		||||
		ret = -EINVAL;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lacl = core_dev_init_initiator_node_lun_acl(se_tpg, mapped_lun,
 | 
			
		||||
			config_item_name(acl_ci), &ret);
 | 
			
		||||
	if (!(lacl))
 | 
			
		||||
		goto out;
 | 
			
		||||
 | 
			
		||||
	config_group_init_type_name(&lacl->se_lun_group, name,
 | 
			
		||||
			&TF_CIT_TMPL(tf)->tfc_tpg_mappedlun_cit);
 | 
			
		||||
 | 
			
		||||
	kfree(buf);
 | 
			
		||||
	return &lacl->se_lun_group;
 | 
			
		||||
out:
 | 
			
		||||
	kfree(buf);
 | 
			
		||||
	return ERR_PTR(ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void target_fabric_drop_mappedlun(
 | 
			
		||||
	struct config_group *group,
 | 
			
		||||
	struct config_item *item)
 | 
			
		||||
{
 | 
			
		||||
	struct se_lun_acl *lacl = container_of(to_config_group(item),
 | 
			
		||||
			struct se_lun_acl, se_lun_group);
 | 
			
		||||
	struct se_portal_group *se_tpg = lacl->se_lun_nacl->se_tpg;
 | 
			
		||||
 | 
			
		||||
	config_item_put(item);
 | 
			
		||||
	core_dev_free_initiator_node_lun_acl(se_tpg, lacl);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct configfs_item_operations target_fabric_nacl_base_item_ops = {
 | 
			
		||||
	.show_attribute		= target_fabric_nacl_base_attr_show,
 | 
			
		||||
	.store_attribute	= target_fabric_nacl_base_attr_store,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct configfs_group_operations target_fabric_nacl_base_group_ops = {
 | 
			
		||||
	.make_group		= target_fabric_make_mappedlun,
 | 
			
		||||
	.drop_item		= target_fabric_drop_mappedlun,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TF_CIT_SETUP(tpg_nacl_base, &target_fabric_nacl_base_item_ops,
 | 
			
		||||
		&target_fabric_nacl_base_group_ops, NULL);
 | 
			
		||||
 | 
			
		||||
/* End of tfc_tpg_nacl_base_cit */
 | 
			
		||||
 | 
			
		||||
/* Start of tfc_tpg_nacl_cit */
 | 
			
		||||
 | 
			
		||||
static struct config_group *target_fabric_make_nodeacl(
 | 
			
		||||
	struct config_group *group,
 | 
			
		||||
	const char *name)
 | 
			
		||||
{
 | 
			
		||||
	struct se_portal_group *se_tpg = container_of(group,
 | 
			
		||||
			struct se_portal_group, tpg_acl_group);
 | 
			
		||||
	struct target_fabric_configfs *tf = se_tpg->se_tpg_wwn->wwn_tf;
 | 
			
		||||
	struct se_node_acl *se_nacl;
 | 
			
		||||
	struct config_group *nacl_cg;
 | 
			
		||||
 | 
			
		||||
	if (!(tf->tf_ops.fabric_make_nodeacl)) {
 | 
			
		||||
		printk(KERN_ERR "tf->tf_ops.fabric_make_nodeacl is NULL\n");
 | 
			
		||||
		return ERR_PTR(-ENOSYS);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	se_nacl = tf->tf_ops.fabric_make_nodeacl(se_tpg, group, name);
 | 
			
		||||
	if (IS_ERR(se_nacl))
 | 
			
		||||
		return ERR_PTR(PTR_ERR(se_nacl));
 | 
			
		||||
 | 
			
		||||
	nacl_cg = &se_nacl->acl_group;
 | 
			
		||||
	nacl_cg->default_groups = se_nacl->acl_default_groups;
 | 
			
		||||
	nacl_cg->default_groups[0] = &se_nacl->acl_attrib_group;
 | 
			
		||||
	nacl_cg->default_groups[1] = &se_nacl->acl_auth_group;
 | 
			
		||||
	nacl_cg->default_groups[2] = &se_nacl->acl_param_group;
 | 
			
		||||
	nacl_cg->default_groups[3] = NULL;
 | 
			
		||||
 | 
			
		||||
	config_group_init_type_name(&se_nacl->acl_group, name,
 | 
			
		||||
			&TF_CIT_TMPL(tf)->tfc_tpg_nacl_base_cit);
 | 
			
		||||
	config_group_init_type_name(&se_nacl->acl_attrib_group, "attrib",
 | 
			
		||||
			&TF_CIT_TMPL(tf)->tfc_tpg_nacl_attrib_cit);
 | 
			
		||||
	config_group_init_type_name(&se_nacl->acl_auth_group, "auth",
 | 
			
		||||
			&TF_CIT_TMPL(tf)->tfc_tpg_nacl_auth_cit);
 | 
			
		||||
	config_group_init_type_name(&se_nacl->acl_param_group, "param",
 | 
			
		||||
			&TF_CIT_TMPL(tf)->tfc_tpg_nacl_param_cit);
 | 
			
		||||
 | 
			
		||||
	return &se_nacl->acl_group;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void target_fabric_drop_nodeacl(
 | 
			
		||||
	struct config_group *group,
 | 
			
		||||
	struct config_item *item)
 | 
			
		||||
{
 | 
			
		||||
	struct se_portal_group *se_tpg = container_of(group,
 | 
			
		||||
			struct se_portal_group, tpg_acl_group);
 | 
			
		||||
	struct target_fabric_configfs *tf = se_tpg->se_tpg_wwn->wwn_tf;
 | 
			
		||||
	struct se_node_acl *se_nacl = container_of(to_config_group(item),
 | 
			
		||||
			struct se_node_acl, acl_group);
 | 
			
		||||
	struct config_item *df_item;
 | 
			
		||||
	struct config_group *nacl_cg;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	nacl_cg = &se_nacl->acl_group;
 | 
			
		||||
	for (i = 0; nacl_cg->default_groups[i]; i++) {
 | 
			
		||||
		df_item = &nacl_cg->default_groups[i]->cg_item;
 | 
			
		||||
		nacl_cg->default_groups[i] = NULL;
 | 
			
		||||
		config_item_put(df_item);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	config_item_put(item);
 | 
			
		||||
	tf->tf_ops.fabric_drop_nodeacl(se_nacl);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct configfs_group_operations target_fabric_nacl_group_ops = {
 | 
			
		||||
	.make_group	= target_fabric_make_nodeacl,
 | 
			
		||||
	.drop_item	= target_fabric_drop_nodeacl,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TF_CIT_SETUP(tpg_nacl, NULL, &target_fabric_nacl_group_ops, NULL);
 | 
			
		||||
 | 
			
		||||
/* End of tfc_tpg_nacl_cit */
 | 
			
		||||
 | 
			
		||||
/* Start of tfc_tpg_np_base_cit */
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_OPS(target_fabric_np_base, se_tpg_np, tpg_np_group);
 | 
			
		||||
 | 
			
		||||
static struct configfs_item_operations target_fabric_np_base_item_ops = {
 | 
			
		||||
	.show_attribute		= target_fabric_np_base_attr_show,
 | 
			
		||||
	.store_attribute	= target_fabric_np_base_attr_store,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TF_CIT_SETUP(tpg_np_base, &target_fabric_np_base_item_ops, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
/* End of tfc_tpg_np_base_cit */
 | 
			
		||||
 | 
			
		||||
/* Start of tfc_tpg_np_cit */
 | 
			
		||||
 | 
			
		||||
static struct config_group *target_fabric_make_np(
 | 
			
		||||
	struct config_group *group,
 | 
			
		||||
	const char *name)
 | 
			
		||||
{
 | 
			
		||||
	struct se_portal_group *se_tpg = container_of(group,
 | 
			
		||||
				struct se_portal_group, tpg_np_group);
 | 
			
		||||
	struct target_fabric_configfs *tf = se_tpg->se_tpg_wwn->wwn_tf;
 | 
			
		||||
	struct se_tpg_np *se_tpg_np;
 | 
			
		||||
 | 
			
		||||
	if (!(tf->tf_ops.fabric_make_np)) {
 | 
			
		||||
		printk(KERN_ERR "tf->tf_ops.fabric_make_np is NULL\n");
 | 
			
		||||
		return ERR_PTR(-ENOSYS);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	se_tpg_np = tf->tf_ops.fabric_make_np(se_tpg, group, name);
 | 
			
		||||
	if (!(se_tpg_np) || IS_ERR(se_tpg_np))
 | 
			
		||||
		return ERR_PTR(-EINVAL);
 | 
			
		||||
 | 
			
		||||
	config_group_init_type_name(&se_tpg_np->tpg_np_group, name,
 | 
			
		||||
			&TF_CIT_TMPL(tf)->tfc_tpg_np_base_cit);
 | 
			
		||||
 | 
			
		||||
	return &se_tpg_np->tpg_np_group;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void target_fabric_drop_np(
 | 
			
		||||
	struct config_group *group,
 | 
			
		||||
	struct config_item *item)
 | 
			
		||||
{
 | 
			
		||||
	struct se_portal_group *se_tpg = container_of(group,
 | 
			
		||||
				struct se_portal_group, tpg_np_group);
 | 
			
		||||
	struct target_fabric_configfs *tf = se_tpg->se_tpg_wwn->wwn_tf;
 | 
			
		||||
	struct se_tpg_np *se_tpg_np = container_of(to_config_group(item),
 | 
			
		||||
				struct se_tpg_np, tpg_np_group);
 | 
			
		||||
 | 
			
		||||
	config_item_put(item);
 | 
			
		||||
	tf->tf_ops.fabric_drop_np(se_tpg_np);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct configfs_group_operations target_fabric_np_group_ops = {
 | 
			
		||||
	.make_group	= &target_fabric_make_np,
 | 
			
		||||
	.drop_item	= &target_fabric_drop_np,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TF_CIT_SETUP(tpg_np, NULL, &target_fabric_np_group_ops, NULL);
 | 
			
		||||
 | 
			
		||||
/* End of tfc_tpg_np_cit */
 | 
			
		||||
 | 
			
		||||
/* Start of tfc_tpg_port_cit */
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_STRUCT(target_fabric_port, se_lun);
 | 
			
		||||
#define TCM_PORT_ATTR(_name, _mode)					\
 | 
			
		||||
static struct target_fabric_port_attribute target_fabric_port_##_name =	\
 | 
			
		||||
	__CONFIGFS_EATTR(_name, _mode,					\
 | 
			
		||||
	target_fabric_port_show_attr_##_name,				\
 | 
			
		||||
	target_fabric_port_store_attr_##_name);
 | 
			
		||||
 | 
			
		||||
#define TCM_PORT_ATTOR_RO(_name)					\
 | 
			
		||||
	__CONFIGFS_EATTR_RO(_name,					\
 | 
			
		||||
	target_fabric_port_show_attr_##_name);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * alua_tg_pt_gp
 | 
			
		||||
 */
 | 
			
		||||
static ssize_t target_fabric_port_show_attr_alua_tg_pt_gp(
 | 
			
		||||
	struct se_lun *lun,
 | 
			
		||||
	char *page)
 | 
			
		||||
{
 | 
			
		||||
	if (!(lun))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	if (!(lun->lun_sep))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	return core_alua_show_tg_pt_gp_info(lun->lun_sep, page);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t target_fabric_port_store_attr_alua_tg_pt_gp(
 | 
			
		||||
	struct se_lun *lun,
 | 
			
		||||
	const char *page,
 | 
			
		||||
	size_t count)
 | 
			
		||||
{
 | 
			
		||||
	if (!(lun))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	if (!(lun->lun_sep))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	return core_alua_store_tg_pt_gp_info(lun->lun_sep, page, count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TCM_PORT_ATTR(alua_tg_pt_gp, S_IRUGO | S_IWUSR);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * alua_tg_pt_offline
 | 
			
		||||
 */
 | 
			
		||||
static ssize_t target_fabric_port_show_attr_alua_tg_pt_offline(
 | 
			
		||||
	struct se_lun *lun,
 | 
			
		||||
	char *page)
 | 
			
		||||
{
 | 
			
		||||
	if (!(lun))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	if (!(lun->lun_sep))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	return core_alua_show_offline_bit(lun, page);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t target_fabric_port_store_attr_alua_tg_pt_offline(
 | 
			
		||||
	struct se_lun *lun,
 | 
			
		||||
	const char *page,
 | 
			
		||||
	size_t count)
 | 
			
		||||
{
 | 
			
		||||
	if (!(lun))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	if (!(lun->lun_sep))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	return core_alua_store_offline_bit(lun, page, count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TCM_PORT_ATTR(alua_tg_pt_offline, S_IRUGO | S_IWUSR);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * alua_tg_pt_status
 | 
			
		||||
 */
 | 
			
		||||
static ssize_t target_fabric_port_show_attr_alua_tg_pt_status(
 | 
			
		||||
	struct se_lun *lun,
 | 
			
		||||
	char *page)
 | 
			
		||||
{
 | 
			
		||||
	if (!(lun))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	if (!(lun->lun_sep))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	return core_alua_show_secondary_status(lun, page);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t target_fabric_port_store_attr_alua_tg_pt_status(
 | 
			
		||||
	struct se_lun *lun,
 | 
			
		||||
	const char *page,
 | 
			
		||||
	size_t count)
 | 
			
		||||
{
 | 
			
		||||
	if (!(lun))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	if (!(lun->lun_sep))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	return core_alua_store_secondary_status(lun, page, count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TCM_PORT_ATTR(alua_tg_pt_status, S_IRUGO | S_IWUSR);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * alua_tg_pt_write_md
 | 
			
		||||
 */
 | 
			
		||||
static ssize_t target_fabric_port_show_attr_alua_tg_pt_write_md(
 | 
			
		||||
	struct se_lun *lun,
 | 
			
		||||
	char *page)
 | 
			
		||||
{
 | 
			
		||||
	if (!(lun))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	if (!(lun->lun_sep))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	return core_alua_show_secondary_write_metadata(lun, page);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t target_fabric_port_store_attr_alua_tg_pt_write_md(
 | 
			
		||||
	struct se_lun *lun,
 | 
			
		||||
	const char *page,
 | 
			
		||||
	size_t count)
 | 
			
		||||
{
 | 
			
		||||
	if (!(lun))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	if (!(lun->lun_sep))
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
 | 
			
		||||
	return core_alua_store_secondary_write_metadata(lun, page, count);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TCM_PORT_ATTR(alua_tg_pt_write_md, S_IRUGO | S_IWUSR);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static struct configfs_attribute *target_fabric_port_attrs[] = {
 | 
			
		||||
	&target_fabric_port_alua_tg_pt_gp.attr,
 | 
			
		||||
	&target_fabric_port_alua_tg_pt_offline.attr,
 | 
			
		||||
	&target_fabric_port_alua_tg_pt_status.attr,
 | 
			
		||||
	&target_fabric_port_alua_tg_pt_write_md.attr,
 | 
			
		||||
	NULL,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_OPS(target_fabric_port, se_lun, lun_group);
 | 
			
		||||
 | 
			
		||||
static int target_fabric_port_link(
 | 
			
		||||
	struct config_item *lun_ci,
 | 
			
		||||
	struct config_item *se_dev_ci)
 | 
			
		||||
{
 | 
			
		||||
	struct config_item *tpg_ci;
 | 
			
		||||
	struct se_device *dev;
 | 
			
		||||
	struct se_lun *lun = container_of(to_config_group(lun_ci),
 | 
			
		||||
				struct se_lun, lun_group);
 | 
			
		||||
	struct se_lun *lun_p;
 | 
			
		||||
	struct se_portal_group *se_tpg;
 | 
			
		||||
	struct se_subsystem_dev *se_dev = container_of(
 | 
			
		||||
				to_config_group(se_dev_ci), struct se_subsystem_dev,
 | 
			
		||||
				se_dev_group);
 | 
			
		||||
	struct target_fabric_configfs *tf;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	tpg_ci = &lun_ci->ci_parent->ci_group->cg_item;
 | 
			
		||||
	se_tpg = container_of(to_config_group(tpg_ci),
 | 
			
		||||
				struct se_portal_group, tpg_group);
 | 
			
		||||
	tf = se_tpg->se_tpg_wwn->wwn_tf;
 | 
			
		||||
 | 
			
		||||
	if (lun->lun_se_dev !=  NULL) {
 | 
			
		||||
		printk(KERN_ERR "Port Symlink already exists\n");
 | 
			
		||||
		return -EEXIST;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dev = se_dev->se_dev_ptr;
 | 
			
		||||
	if (!(dev)) {
 | 
			
		||||
		printk(KERN_ERR "Unable to locate struct se_device pointer from"
 | 
			
		||||
			" %s\n", config_item_name(se_dev_ci));
 | 
			
		||||
		ret = -ENODEV;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	lun_p = core_dev_add_lun(se_tpg, dev->se_hba, dev,
 | 
			
		||||
				lun->unpacked_lun);
 | 
			
		||||
	if ((IS_ERR(lun_p)) || !(lun_p)) {
 | 
			
		||||
		printk(KERN_ERR "core_dev_add_lun() failed\n");
 | 
			
		||||
		ret = -EINVAL;
 | 
			
		||||
		goto out;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (tf->tf_ops.fabric_post_link) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Call the optional fabric_post_link() to allow a
 | 
			
		||||
		 * fabric module to setup any additional state once
 | 
			
		||||
		 * core_dev_add_lun() has been called..
 | 
			
		||||
		 */
 | 
			
		||||
		tf->tf_ops.fabric_post_link(se_tpg, lun);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
out:
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int target_fabric_port_unlink(
 | 
			
		||||
	struct config_item *lun_ci,
 | 
			
		||||
	struct config_item *se_dev_ci)
 | 
			
		||||
{
 | 
			
		||||
	struct se_lun *lun = container_of(to_config_group(lun_ci),
 | 
			
		||||
				struct se_lun, lun_group);
 | 
			
		||||
	struct se_portal_group *se_tpg = lun->lun_sep->sep_tpg;
 | 
			
		||||
	struct target_fabric_configfs *tf = se_tpg->se_tpg_wwn->wwn_tf;
 | 
			
		||||
 | 
			
		||||
	if (tf->tf_ops.fabric_pre_unlink) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Call the optional fabric_pre_unlink() to allow a
 | 
			
		||||
		 * fabric module to release any additional stat before
 | 
			
		||||
		 * core_dev_del_lun() is called.
 | 
			
		||||
		*/
 | 
			
		||||
		tf->tf_ops.fabric_pre_unlink(se_tpg, lun);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	core_dev_del_lun(se_tpg, lun->unpacked_lun);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct configfs_item_operations target_fabric_port_item_ops = {
 | 
			
		||||
	.show_attribute		= target_fabric_port_attr_show,
 | 
			
		||||
	.store_attribute	= target_fabric_port_attr_store,
 | 
			
		||||
	.allow_link		= target_fabric_port_link,
 | 
			
		||||
	.drop_link		= target_fabric_port_unlink,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TF_CIT_SETUP(tpg_port, &target_fabric_port_item_ops, NULL, target_fabric_port_attrs);
 | 
			
		||||
 | 
			
		||||
/* End of tfc_tpg_port_cit */
 | 
			
		||||
 | 
			
		||||
/* Start of tfc_tpg_lun_cit */
 | 
			
		||||
 | 
			
		||||
static struct config_group *target_fabric_make_lun(
 | 
			
		||||
	struct config_group *group,
 | 
			
		||||
	const char *name)
 | 
			
		||||
{
 | 
			
		||||
	struct se_lun *lun;
 | 
			
		||||
	struct se_portal_group *se_tpg = container_of(group,
 | 
			
		||||
			struct se_portal_group, tpg_lun_group);
 | 
			
		||||
	struct target_fabric_configfs *tf = se_tpg->se_tpg_wwn->wwn_tf;
 | 
			
		||||
	unsigned long unpacked_lun;
 | 
			
		||||
 | 
			
		||||
	if (strstr(name, "lun_") != name) {
 | 
			
		||||
		printk(KERN_ERR "Unable to locate \'_\" in"
 | 
			
		||||
				" \"lun_$LUN_NUMBER\"\n");
 | 
			
		||||
		return ERR_PTR(-EINVAL);
 | 
			
		||||
	}
 | 
			
		||||
	if (strict_strtoul(name + 4, 0, &unpacked_lun) || unpacked_lun > UINT_MAX)
 | 
			
		||||
		return ERR_PTR(-EINVAL);
 | 
			
		||||
 | 
			
		||||
	lun = core_get_lun_from_tpg(se_tpg, unpacked_lun);
 | 
			
		||||
	if (!(lun))
 | 
			
		||||
		return ERR_PTR(-EINVAL);
 | 
			
		||||
 | 
			
		||||
	config_group_init_type_name(&lun->lun_group, name,
 | 
			
		||||
			&TF_CIT_TMPL(tf)->tfc_tpg_port_cit);
 | 
			
		||||
 | 
			
		||||
	return &lun->lun_group;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void target_fabric_drop_lun(
 | 
			
		||||
	struct config_group *group,
 | 
			
		||||
	struct config_item *item)
 | 
			
		||||
{
 | 
			
		||||
	config_item_put(item);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct configfs_group_operations target_fabric_lun_group_ops = {
 | 
			
		||||
	.make_group	= &target_fabric_make_lun,
 | 
			
		||||
	.drop_item	= &target_fabric_drop_lun,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TF_CIT_SETUP(tpg_lun, NULL, &target_fabric_lun_group_ops, NULL);
 | 
			
		||||
 | 
			
		||||
/* End of tfc_tpg_lun_cit */
 | 
			
		||||
 | 
			
		||||
/* Start of tfc_tpg_attrib_cit */
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_OPS(target_fabric_tpg_attrib, se_portal_group, tpg_attrib_group);
 | 
			
		||||
 | 
			
		||||
static struct configfs_item_operations target_fabric_tpg_attrib_item_ops = {
 | 
			
		||||
	.show_attribute		= target_fabric_tpg_attrib_attr_show,
 | 
			
		||||
	.store_attribute	= target_fabric_tpg_attrib_attr_store,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TF_CIT_SETUP(tpg_attrib, &target_fabric_tpg_attrib_item_ops, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
/* End of tfc_tpg_attrib_cit */
 | 
			
		||||
 | 
			
		||||
/* Start of tfc_tpg_param_cit */
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_OPS(target_fabric_tpg_param, se_portal_group, tpg_param_group);
 | 
			
		||||
 | 
			
		||||
static struct configfs_item_operations target_fabric_tpg_param_item_ops = {
 | 
			
		||||
	.show_attribute		= target_fabric_tpg_param_attr_show,
 | 
			
		||||
	.store_attribute	= target_fabric_tpg_param_attr_store,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TF_CIT_SETUP(tpg_param, &target_fabric_tpg_param_item_ops, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
/* End of tfc_tpg_param_cit */
 | 
			
		||||
 | 
			
		||||
/* Start of tfc_tpg_base_cit */
 | 
			
		||||
/*
 | 
			
		||||
 * For use with TF_TPG_ATTR() and TF_TPG_ATTR_RO()
 | 
			
		||||
 */
 | 
			
		||||
CONFIGFS_EATTR_OPS(target_fabric_tpg, se_portal_group, tpg_group);
 | 
			
		||||
 | 
			
		||||
static struct configfs_item_operations target_fabric_tpg_base_item_ops = {
 | 
			
		||||
	.show_attribute		= target_fabric_tpg_attr_show,
 | 
			
		||||
	.store_attribute	= target_fabric_tpg_attr_store,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TF_CIT_SETUP(tpg_base, &target_fabric_tpg_base_item_ops, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
/* End of tfc_tpg_base_cit */
 | 
			
		||||
 | 
			
		||||
/* Start of tfc_tpg_cit */
 | 
			
		||||
 | 
			
		||||
static struct config_group *target_fabric_make_tpg(
 | 
			
		||||
	struct config_group *group,
 | 
			
		||||
	const char *name)
 | 
			
		||||
{
 | 
			
		||||
	struct se_wwn *wwn = container_of(group, struct se_wwn, wwn_group);
 | 
			
		||||
	struct target_fabric_configfs *tf = wwn->wwn_tf;
 | 
			
		||||
	struct se_portal_group *se_tpg;
 | 
			
		||||
 | 
			
		||||
	if (!(tf->tf_ops.fabric_make_tpg)) {
 | 
			
		||||
		printk(KERN_ERR "tf->tf_ops.fabric_make_tpg is NULL\n");
 | 
			
		||||
		return ERR_PTR(-ENOSYS);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	se_tpg = tf->tf_ops.fabric_make_tpg(wwn, group, name);
 | 
			
		||||
	if (!(se_tpg) || IS_ERR(se_tpg))
 | 
			
		||||
		return ERR_PTR(-EINVAL);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Setup default groups from pre-allocated se_tpg->tpg_default_groups
 | 
			
		||||
	 */
 | 
			
		||||
	se_tpg->tpg_group.default_groups = se_tpg->tpg_default_groups;
 | 
			
		||||
	se_tpg->tpg_group.default_groups[0] = &se_tpg->tpg_lun_group;
 | 
			
		||||
	se_tpg->tpg_group.default_groups[1] = &se_tpg->tpg_np_group;
 | 
			
		||||
	se_tpg->tpg_group.default_groups[2] = &se_tpg->tpg_acl_group;
 | 
			
		||||
	se_tpg->tpg_group.default_groups[3] = &se_tpg->tpg_attrib_group;
 | 
			
		||||
	se_tpg->tpg_group.default_groups[4] = &se_tpg->tpg_param_group;
 | 
			
		||||
	se_tpg->tpg_group.default_groups[5] = NULL;
 | 
			
		||||
 | 
			
		||||
	config_group_init_type_name(&se_tpg->tpg_group, name,
 | 
			
		||||
			&TF_CIT_TMPL(tf)->tfc_tpg_base_cit);
 | 
			
		||||
	config_group_init_type_name(&se_tpg->tpg_lun_group, "lun",
 | 
			
		||||
			&TF_CIT_TMPL(tf)->tfc_tpg_lun_cit);
 | 
			
		||||
	config_group_init_type_name(&se_tpg->tpg_np_group, "np",
 | 
			
		||||
			&TF_CIT_TMPL(tf)->tfc_tpg_np_cit);
 | 
			
		||||
	config_group_init_type_name(&se_tpg->tpg_acl_group, "acls",
 | 
			
		||||
			&TF_CIT_TMPL(tf)->tfc_tpg_nacl_cit);
 | 
			
		||||
	config_group_init_type_name(&se_tpg->tpg_attrib_group, "attrib",
 | 
			
		||||
			&TF_CIT_TMPL(tf)->tfc_tpg_attrib_cit);
 | 
			
		||||
	config_group_init_type_name(&se_tpg->tpg_param_group, "param",
 | 
			
		||||
			&TF_CIT_TMPL(tf)->tfc_tpg_param_cit);
 | 
			
		||||
 | 
			
		||||
	return &se_tpg->tpg_group;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void target_fabric_drop_tpg(
 | 
			
		||||
	struct config_group *group,
 | 
			
		||||
	struct config_item *item)
 | 
			
		||||
{
 | 
			
		||||
	struct se_wwn *wwn = container_of(group, struct se_wwn, wwn_group);
 | 
			
		||||
	struct target_fabric_configfs *tf = wwn->wwn_tf;
 | 
			
		||||
	struct se_portal_group *se_tpg = container_of(to_config_group(item),
 | 
			
		||||
				struct se_portal_group, tpg_group);
 | 
			
		||||
	struct config_group *tpg_cg = &se_tpg->tpg_group;
 | 
			
		||||
	struct config_item *df_item;
 | 
			
		||||
	int i;
 | 
			
		||||
	/*
 | 
			
		||||
	 * Release default groups, but do not release tpg_cg->default_groups
 | 
			
		||||
	 * memory as it is statically allocated at se_tpg->tpg_default_groups.
 | 
			
		||||
	 */
 | 
			
		||||
	for (i = 0; tpg_cg->default_groups[i]; i++) {
 | 
			
		||||
		df_item = &tpg_cg->default_groups[i]->cg_item;
 | 
			
		||||
		tpg_cg->default_groups[i] = NULL;
 | 
			
		||||
		config_item_put(df_item);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	config_item_put(item);
 | 
			
		||||
	tf->tf_ops.fabric_drop_tpg(se_tpg);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct configfs_group_operations target_fabric_tpg_group_ops = {
 | 
			
		||||
	.make_group	= target_fabric_make_tpg,
 | 
			
		||||
	.drop_item	= target_fabric_drop_tpg,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TF_CIT_SETUP(tpg, NULL, &target_fabric_tpg_group_ops, NULL);
 | 
			
		||||
 | 
			
		||||
/* End of tfc_tpg_cit */
 | 
			
		||||
 | 
			
		||||
/* Start of tfc_wwn_cit */
 | 
			
		||||
 | 
			
		||||
static struct config_group *target_fabric_make_wwn(
 | 
			
		||||
	struct config_group *group,
 | 
			
		||||
	const char *name)
 | 
			
		||||
{
 | 
			
		||||
	struct target_fabric_configfs *tf = container_of(group,
 | 
			
		||||
				struct target_fabric_configfs, tf_group);
 | 
			
		||||
	struct se_wwn *wwn;
 | 
			
		||||
 | 
			
		||||
	if (!(tf->tf_ops.fabric_make_wwn)) {
 | 
			
		||||
		printk(KERN_ERR "tf->tf_ops.fabric_make_wwn is NULL\n");
 | 
			
		||||
		return ERR_PTR(-ENOSYS);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wwn = tf->tf_ops.fabric_make_wwn(tf, group, name);
 | 
			
		||||
	if (!(wwn) || IS_ERR(wwn))
 | 
			
		||||
		return ERR_PTR(-EINVAL);
 | 
			
		||||
 | 
			
		||||
	wwn->wwn_tf = tf;
 | 
			
		||||
	config_group_init_type_name(&wwn->wwn_group, name,
 | 
			
		||||
			&TF_CIT_TMPL(tf)->tfc_tpg_cit);
 | 
			
		||||
 | 
			
		||||
	return &wwn->wwn_group;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void target_fabric_drop_wwn(
 | 
			
		||||
	struct config_group *group,
 | 
			
		||||
	struct config_item *item)
 | 
			
		||||
{
 | 
			
		||||
	struct target_fabric_configfs *tf = container_of(group,
 | 
			
		||||
				struct target_fabric_configfs, tf_group);
 | 
			
		||||
	struct se_wwn *wwn = container_of(to_config_group(item),
 | 
			
		||||
				struct se_wwn, wwn_group);
 | 
			
		||||
 | 
			
		||||
	config_item_put(item);
 | 
			
		||||
	tf->tf_ops.fabric_drop_wwn(wwn);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct configfs_group_operations target_fabric_wwn_group_ops = {
 | 
			
		||||
	.make_group	= target_fabric_make_wwn,
 | 
			
		||||
	.drop_item	= target_fabric_drop_wwn,
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
 * For use with TF_WWN_ATTR() and TF_WWN_ATTR_RO()
 | 
			
		||||
 */
 | 
			
		||||
CONFIGFS_EATTR_OPS(target_fabric_wwn, target_fabric_configfs, tf_group);
 | 
			
		||||
 | 
			
		||||
static struct configfs_item_operations target_fabric_wwn_item_ops = {
 | 
			
		||||
	.show_attribute		= target_fabric_wwn_attr_show,
 | 
			
		||||
	.store_attribute	= target_fabric_wwn_attr_store,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TF_CIT_SETUP(wwn, &target_fabric_wwn_item_ops, &target_fabric_wwn_group_ops, NULL);
 | 
			
		||||
 | 
			
		||||
/* End of tfc_wwn_cit */
 | 
			
		||||
 | 
			
		||||
/* Start of tfc_discovery_cit */
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_OPS(target_fabric_discovery, target_fabric_configfs,
 | 
			
		||||
		tf_disc_group);
 | 
			
		||||
 | 
			
		||||
static struct configfs_item_operations target_fabric_discovery_item_ops = {
 | 
			
		||||
	.show_attribute		= target_fabric_discovery_attr_show,
 | 
			
		||||
	.store_attribute	= target_fabric_discovery_attr_store,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
TF_CIT_SETUP(discovery, &target_fabric_discovery_item_ops, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
/* End of tfc_discovery_cit */
 | 
			
		||||
 | 
			
		||||
int target_fabric_setup_cits(struct target_fabric_configfs *tf)
 | 
			
		||||
{
 | 
			
		||||
	target_fabric_setup_discovery_cit(tf);
 | 
			
		||||
	target_fabric_setup_wwn_cit(tf);
 | 
			
		||||
	target_fabric_setup_tpg_cit(tf);
 | 
			
		||||
	target_fabric_setup_tpg_base_cit(tf);
 | 
			
		||||
	target_fabric_setup_tpg_port_cit(tf);
 | 
			
		||||
	target_fabric_setup_tpg_lun_cit(tf);
 | 
			
		||||
	target_fabric_setup_tpg_np_cit(tf);
 | 
			
		||||
	target_fabric_setup_tpg_np_base_cit(tf);
 | 
			
		||||
	target_fabric_setup_tpg_attrib_cit(tf);
 | 
			
		||||
	target_fabric_setup_tpg_param_cit(tf);
 | 
			
		||||
	target_fabric_setup_tpg_nacl_cit(tf);
 | 
			
		||||
	target_fabric_setup_tpg_nacl_base_cit(tf);
 | 
			
		||||
	target_fabric_setup_tpg_nacl_attrib_cit(tf);
 | 
			
		||||
	target_fabric_setup_tpg_nacl_auth_cit(tf);
 | 
			
		||||
	target_fabric_setup_tpg_nacl_param_cit(tf);
 | 
			
		||||
	target_fabric_setup_tpg_mappedlun_cit(tf);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										451
									
								
								drivers/target/target_core_fabric_lib.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										451
									
								
								drivers/target/target_core_fabric_lib.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,451 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Filename:  target_core_fabric_lib.c
 | 
			
		||||
 *
 | 
			
		||||
 * This file contains generic high level protocol identifier and PR
 | 
			
		||||
 * handlers for TCM fabric modules
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2010 Rising Tide Systems, Inc.
 | 
			
		||||
 * Copyright (c) 2010 Linux-iSCSI.org
 | 
			
		||||
 *
 | 
			
		||||
 * Nicholas A. Bellinger <nab@linux-iscsi.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/string.h>
 | 
			
		||||
#include <linux/ctype.h>
 | 
			
		||||
#include <linux/spinlock.h>
 | 
			
		||||
#include <linux/smp_lock.h>
 | 
			
		||||
#include <scsi/scsi.h>
 | 
			
		||||
#include <scsi/scsi_cmnd.h>
 | 
			
		||||
 | 
			
		||||
#include <target/target_core_base.h>
 | 
			
		||||
#include <target/target_core_device.h>
 | 
			
		||||
#include <target/target_core_transport.h>
 | 
			
		||||
#include <target/target_core_fabric_ops.h>
 | 
			
		||||
#include <target/target_core_configfs.h>
 | 
			
		||||
 | 
			
		||||
#include "target_core_hba.h"
 | 
			
		||||
#include "target_core_pr.h"
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Handlers for Serial Attached SCSI (SAS)
 | 
			
		||||
 */
 | 
			
		||||
u8 sas_get_fabric_proto_ident(struct se_portal_group *se_tpg)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * Return a SAS Serial SCSI Protocol identifier for loopback operations
 | 
			
		||||
	 * This is defined in  section 7.5.1 Table 362 in spc4r17
 | 
			
		||||
	 */
 | 
			
		||||
	return 0x6;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(sas_get_fabric_proto_ident);
 | 
			
		||||
 | 
			
		||||
u32 sas_get_pr_transport_id(
 | 
			
		||||
	struct se_portal_group *se_tpg,
 | 
			
		||||
	struct se_node_acl *se_nacl,
 | 
			
		||||
	struct t10_pr_registration *pr_reg,
 | 
			
		||||
	int *format_code,
 | 
			
		||||
	unsigned char *buf)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char binary, *ptr;
 | 
			
		||||
	int i;
 | 
			
		||||
	u32 off = 4;
 | 
			
		||||
	/*
 | 
			
		||||
	 * Set PROTOCOL IDENTIFIER to 6h for SAS
 | 
			
		||||
	 */
 | 
			
		||||
	buf[0] = 0x06;
 | 
			
		||||
	/*
 | 
			
		||||
	 * From spc4r17, 7.5.4.7 TransportID for initiator ports using SCSI
 | 
			
		||||
	 * over SAS Serial SCSI Protocol
 | 
			
		||||
	 */
 | 
			
		||||
	ptr = &se_nacl->initiatorname[4]; /* Skip over 'naa. prefix */
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < 16; i += 2) {
 | 
			
		||||
		binary = transport_asciihex_to_binaryhex(&ptr[i]);
 | 
			
		||||
		buf[off++] = binary;
 | 
			
		||||
	}
 | 
			
		||||
	/*
 | 
			
		||||
	 * The SAS Transport ID is a hardcoded 24-byte length
 | 
			
		||||
	 */
 | 
			
		||||
	return 24;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(sas_get_pr_transport_id);
 | 
			
		||||
 | 
			
		||||
u32 sas_get_pr_transport_id_len(
 | 
			
		||||
	struct se_portal_group *se_tpg,
 | 
			
		||||
	struct se_node_acl *se_nacl,
 | 
			
		||||
	struct t10_pr_registration *pr_reg,
 | 
			
		||||
	int *format_code)
 | 
			
		||||
{
 | 
			
		||||
	*format_code = 0;
 | 
			
		||||
	/*
 | 
			
		||||
	 * From spc4r17, 7.5.4.7 TransportID for initiator ports using SCSI
 | 
			
		||||
	 * over SAS Serial SCSI Protocol
 | 
			
		||||
	 *
 | 
			
		||||
	 * The SAS Transport ID is a hardcoded 24-byte length
 | 
			
		||||
	 */
 | 
			
		||||
	return 24;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(sas_get_pr_transport_id_len);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Used for handling SCSI fabric dependent TransportIDs in SPC-3 and above
 | 
			
		||||
 * Persistent Reservation SPEC_I_PT=1 and PROUT REGISTER_AND_MOVE operations.
 | 
			
		||||
 */
 | 
			
		||||
char *sas_parse_pr_out_transport_id(
 | 
			
		||||
	struct se_portal_group *se_tpg,
 | 
			
		||||
	const char *buf,
 | 
			
		||||
	u32 *out_tid_len,
 | 
			
		||||
	char **port_nexus_ptr)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * Assume the FORMAT CODE 00b from spc4r17, 7.5.4.7 TransportID
 | 
			
		||||
	 * for initiator ports using SCSI over SAS Serial SCSI Protocol
 | 
			
		||||
	 *
 | 
			
		||||
	 * The TransportID for a SAS Initiator Port is of fixed size of
 | 
			
		||||
	 * 24 bytes, and SAS does not contain a I_T nexus identifier,
 | 
			
		||||
	 * so we return the **port_nexus_ptr set to NULL.
 | 
			
		||||
	 */
 | 
			
		||||
	*port_nexus_ptr = NULL;
 | 
			
		||||
	*out_tid_len = 24;
 | 
			
		||||
 | 
			
		||||
	return (char *)&buf[4];
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(sas_parse_pr_out_transport_id);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Handlers for Fibre Channel Protocol (FCP)
 | 
			
		||||
 */
 | 
			
		||||
u8 fc_get_fabric_proto_ident(struct se_portal_group *se_tpg)
 | 
			
		||||
{
 | 
			
		||||
	return 0x0;	/* 0 = fcp-2 per SPC4 section 7.5.1 */
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(fc_get_fabric_proto_ident);
 | 
			
		||||
 | 
			
		||||
u32 fc_get_pr_transport_id_len(
 | 
			
		||||
	struct se_portal_group *se_tpg,
 | 
			
		||||
	struct se_node_acl *se_nacl,
 | 
			
		||||
	struct t10_pr_registration *pr_reg,
 | 
			
		||||
	int *format_code)
 | 
			
		||||
{
 | 
			
		||||
	*format_code = 0;
 | 
			
		||||
	/*
 | 
			
		||||
	 * The FC Transport ID is a hardcoded 24-byte length
 | 
			
		||||
	 */
 | 
			
		||||
	return 24;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(fc_get_pr_transport_id_len);
 | 
			
		||||
 | 
			
		||||
u32 fc_get_pr_transport_id(
 | 
			
		||||
	struct se_portal_group *se_tpg,
 | 
			
		||||
	struct se_node_acl *se_nacl,
 | 
			
		||||
	struct t10_pr_registration *pr_reg,
 | 
			
		||||
	int *format_code,
 | 
			
		||||
	unsigned char *buf)
 | 
			
		||||
{
 | 
			
		||||
	unsigned char binary, *ptr;
 | 
			
		||||
	int i;
 | 
			
		||||
	u32 off = 8;
 | 
			
		||||
	/*
 | 
			
		||||
	 * PROTOCOL IDENTIFIER is 0h for FCP-2
 | 
			
		||||
	 *
 | 
			
		||||
	 * From spc4r17, 7.5.4.2 TransportID for initiator ports using
 | 
			
		||||
	 * SCSI over Fibre Channel
 | 
			
		||||
	 *
 | 
			
		||||
	 * We convert the ASCII formatted N Port name into a binary
 | 
			
		||||
	 * encoded TransportID.
 | 
			
		||||
	 */
 | 
			
		||||
	ptr = &se_nacl->initiatorname[0];
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < 24; ) {
 | 
			
		||||
		if (!(strncmp(&ptr[i], ":", 1))) {
 | 
			
		||||
			i++;
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		binary = transport_asciihex_to_binaryhex(&ptr[i]);
 | 
			
		||||
		buf[off++] = binary;
 | 
			
		||||
		i += 2;
 | 
			
		||||
	}
 | 
			
		||||
	/*
 | 
			
		||||
	 * The FC Transport ID is a hardcoded 24-byte length
 | 
			
		||||
	 */
 | 
			
		||||
	return 24;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(fc_get_pr_transport_id);
 | 
			
		||||
 | 
			
		||||
char *fc_parse_pr_out_transport_id(
 | 
			
		||||
	struct se_portal_group *se_tpg,
 | 
			
		||||
	const char *buf,
 | 
			
		||||
	u32 *out_tid_len,
 | 
			
		||||
	char **port_nexus_ptr)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * The TransportID for a FC N Port is of fixed size of
 | 
			
		||||
	 * 24 bytes, and FC does not contain a I_T nexus identifier,
 | 
			
		||||
	 * so we return the **port_nexus_ptr set to NULL.
 | 
			
		||||
	 */
 | 
			
		||||
	*port_nexus_ptr = NULL;
 | 
			
		||||
	*out_tid_len = 24;
 | 
			
		||||
 | 
			
		||||
	 return (char *)&buf[8];
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(fc_parse_pr_out_transport_id);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Handlers for Internet Small Computer Systems Interface (iSCSI)
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
u8 iscsi_get_fabric_proto_ident(struct se_portal_group *se_tpg)
 | 
			
		||||
{
 | 
			
		||||
	/*
 | 
			
		||||
	 * This value is defined for "Internet SCSI (iSCSI)"
 | 
			
		||||
	 * in spc4r17 section 7.5.1 Table 362
 | 
			
		||||
	 */
 | 
			
		||||
	return 0x5;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(iscsi_get_fabric_proto_ident);
 | 
			
		||||
 | 
			
		||||
u32 iscsi_get_pr_transport_id(
 | 
			
		||||
	struct se_portal_group *se_tpg,
 | 
			
		||||
	struct se_node_acl *se_nacl,
 | 
			
		||||
	struct t10_pr_registration *pr_reg,
 | 
			
		||||
	int *format_code,
 | 
			
		||||
	unsigned char *buf)
 | 
			
		||||
{
 | 
			
		||||
	u32 off = 4, padding = 0;
 | 
			
		||||
	u16 len = 0;
 | 
			
		||||
 | 
			
		||||
	spin_lock_irq(&se_nacl->nacl_sess_lock);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Set PROTOCOL IDENTIFIER to 5h for iSCSI
 | 
			
		||||
	*/
 | 
			
		||||
	buf[0] = 0x05;
 | 
			
		||||
	/*
 | 
			
		||||
	 * From spc4r17 Section 7.5.4.6: TransportID for initiator
 | 
			
		||||
	 * ports using SCSI over iSCSI.
 | 
			
		||||
	 *
 | 
			
		||||
	 * The null-terminated, null-padded (see 4.4.2) ISCSI NAME field
 | 
			
		||||
	 * shall contain the iSCSI name of an iSCSI initiator node (see
 | 
			
		||||
	 * RFC 3720). The first ISCSI NAME field byte containing an ASCII
 | 
			
		||||
	 * null character terminates the ISCSI NAME field without regard for
 | 
			
		||||
	 * the specified length of the iSCSI TransportID or the contents of
 | 
			
		||||
	 * the ADDITIONAL LENGTH field.
 | 
			
		||||
	 */
 | 
			
		||||
	len = sprintf(&buf[off], "%s", se_nacl->initiatorname);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Add Extra byte for NULL terminator
 | 
			
		||||
	 */
 | 
			
		||||
	len++;
 | 
			
		||||
	/*
 | 
			
		||||
	 * If there is ISID present with the registration and *format code == 1
 | 
			
		||||
	 * 1, use iSCSI Initiator port TransportID format.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Otherwise use iSCSI Initiator device TransportID format that
 | 
			
		||||
	 * does not contain the ASCII encoded iSCSI Initiator iSID value
 | 
			
		||||
	 * provied by the iSCSi Initiator during the iSCSI login process.
 | 
			
		||||
	 */
 | 
			
		||||
	if ((*format_code == 1) && (pr_reg->isid_present_at_reg)) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Set FORMAT CODE 01b for iSCSI Initiator port TransportID
 | 
			
		||||
		 * format.
 | 
			
		||||
		 */
 | 
			
		||||
		buf[0] |= 0x40;
 | 
			
		||||
		/*
 | 
			
		||||
		 * From spc4r17 Section 7.5.4.6: TransportID for initiator
 | 
			
		||||
		 * ports using SCSI over iSCSI.  Table 390
 | 
			
		||||
		 *
 | 
			
		||||
		 * The SEPARATOR field shall contain the five ASCII
 | 
			
		||||
		 * characters ",i,0x".
 | 
			
		||||
		 *
 | 
			
		||||
		 * The null-terminated, null-padded ISCSI INITIATOR SESSION ID
 | 
			
		||||
		 * field shall contain the iSCSI initiator session identifier
 | 
			
		||||
		 * (see RFC 3720) in the form of ASCII characters that are the
 | 
			
		||||
		 * hexadecimal digits converted from the binary iSCSI initiator
 | 
			
		||||
		 * session identifier value. The first ISCSI INITIATOR SESSION
 | 
			
		||||
		 * ID field byte containing an ASCII null character
 | 
			
		||||
		 */
 | 
			
		||||
		buf[off+len] = 0x2c; off++; /* ASCII Character: "," */
 | 
			
		||||
		buf[off+len] = 0x69; off++; /* ASCII Character: "i" */
 | 
			
		||||
		buf[off+len] = 0x2c; off++; /* ASCII Character: "," */
 | 
			
		||||
		buf[off+len] = 0x30; off++; /* ASCII Character: "0" */
 | 
			
		||||
		buf[off+len] = 0x78; off++; /* ASCII Character: "x" */
 | 
			
		||||
		len += 5;
 | 
			
		||||
		buf[off+len] = pr_reg->pr_reg_isid[0]; off++;
 | 
			
		||||
		buf[off+len] = pr_reg->pr_reg_isid[1]; off++;
 | 
			
		||||
		buf[off+len] = pr_reg->pr_reg_isid[2]; off++;
 | 
			
		||||
		buf[off+len] = pr_reg->pr_reg_isid[3]; off++;
 | 
			
		||||
		buf[off+len] = pr_reg->pr_reg_isid[4]; off++;
 | 
			
		||||
		buf[off+len] = pr_reg->pr_reg_isid[5]; off++;
 | 
			
		||||
		buf[off+len] = '\0'; off++;
 | 
			
		||||
		len += 7;
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock_irq(&se_nacl->nacl_sess_lock);
 | 
			
		||||
	/*
 | 
			
		||||
	 * The ADDITIONAL LENGTH field specifies the number of bytes that follow
 | 
			
		||||
	 * in the TransportID. The additional length shall be at least 20 and
 | 
			
		||||
	 * shall be a multiple of four.
 | 
			
		||||
	*/
 | 
			
		||||
	padding = ((-len) & 3);
 | 
			
		||||
	if (padding != 0)
 | 
			
		||||
		len += padding;
 | 
			
		||||
 | 
			
		||||
	buf[2] = ((len >> 8) & 0xff);
 | 
			
		||||
	buf[3] = (len & 0xff);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Increment value for total payload + header length for
 | 
			
		||||
	 * full status descriptor
 | 
			
		||||
	 */
 | 
			
		||||
	len += 4;
 | 
			
		||||
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(iscsi_get_pr_transport_id);
 | 
			
		||||
 | 
			
		||||
u32 iscsi_get_pr_transport_id_len(
 | 
			
		||||
	struct se_portal_group *se_tpg,
 | 
			
		||||
	struct se_node_acl *se_nacl,
 | 
			
		||||
	struct t10_pr_registration *pr_reg,
 | 
			
		||||
	int *format_code)
 | 
			
		||||
{
 | 
			
		||||
	u32 len = 0, padding = 0;
 | 
			
		||||
 | 
			
		||||
	spin_lock_irq(&se_nacl->nacl_sess_lock);
 | 
			
		||||
	len = strlen(se_nacl->initiatorname);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Add extra byte for NULL terminator
 | 
			
		||||
	 */
 | 
			
		||||
	len++;
 | 
			
		||||
	/*
 | 
			
		||||
	 * If there is ISID present with the registration, use format code:
 | 
			
		||||
	 * 01b: iSCSI Initiator port TransportID format
 | 
			
		||||
	 *
 | 
			
		||||
	 * If there is not an active iSCSI session, use format code:
 | 
			
		||||
	 * 00b: iSCSI Initiator device TransportID format
 | 
			
		||||
	 */
 | 
			
		||||
	if (pr_reg->isid_present_at_reg) {
 | 
			
		||||
		len += 5; /* For ",i,0x" ASCII seperator */
 | 
			
		||||
		len += 7; /* For iSCSI Initiator Session ID + Null terminator */
 | 
			
		||||
		*format_code = 1;
 | 
			
		||||
	} else
 | 
			
		||||
		*format_code = 0;
 | 
			
		||||
	spin_unlock_irq(&se_nacl->nacl_sess_lock);
 | 
			
		||||
	/*
 | 
			
		||||
	 * The ADDITIONAL LENGTH field specifies the number of bytes that follow
 | 
			
		||||
	 * in the TransportID. The additional length shall be at least 20 and
 | 
			
		||||
	 * shall be a multiple of four.
 | 
			
		||||
	 */
 | 
			
		||||
	padding = ((-len) & 3);
 | 
			
		||||
	if (padding != 0)
 | 
			
		||||
		len += padding;
 | 
			
		||||
	/*
 | 
			
		||||
	 * Increment value for total payload + header length for
 | 
			
		||||
	 * full status descriptor
 | 
			
		||||
	 */
 | 
			
		||||
	len += 4;
 | 
			
		||||
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(iscsi_get_pr_transport_id_len);
 | 
			
		||||
 | 
			
		||||
char *iscsi_parse_pr_out_transport_id(
 | 
			
		||||
	struct se_portal_group *se_tpg,
 | 
			
		||||
	const char *buf,
 | 
			
		||||
	u32 *out_tid_len,
 | 
			
		||||
	char **port_nexus_ptr)
 | 
			
		||||
{
 | 
			
		||||
	char *p;
 | 
			
		||||
	u32 tid_len, padding;
 | 
			
		||||
	int i;
 | 
			
		||||
	u16 add_len;
 | 
			
		||||
	u8 format_code = (buf[0] & 0xc0);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Check for FORMAT CODE 00b or 01b from spc4r17, section 7.5.4.6:
 | 
			
		||||
	 *
 | 
			
		||||
	 *       TransportID for initiator ports using SCSI over iSCSI,
 | 
			
		||||
	 *       from Table 388 -- iSCSI TransportID formats.
 | 
			
		||||
	 *
 | 
			
		||||
	 *    00b     Initiator port is identified using the world wide unique
 | 
			
		||||
	 *            SCSI device name of the iSCSI initiator
 | 
			
		||||
	 *            device containing the initiator port (see table 389).
 | 
			
		||||
	 *    01b     Initiator port is identified using the world wide unique
 | 
			
		||||
	 *            initiator port identifier (see table 390).10b to 11b
 | 
			
		||||
	 *            Reserved
 | 
			
		||||
	 */
 | 
			
		||||
	if ((format_code != 0x00) && (format_code != 0x40)) {
 | 
			
		||||
		printk(KERN_ERR "Illegal format code: 0x%02x for iSCSI"
 | 
			
		||||
			" Initiator Transport ID\n", format_code);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	/*
 | 
			
		||||
	 * If the caller wants the TransportID Length, we set that value for the
 | 
			
		||||
	 * entire iSCSI Tarnsport ID now.
 | 
			
		||||
	 */
 | 
			
		||||
	 if (out_tid_len != NULL) {
 | 
			
		||||
		add_len = ((buf[2] >> 8) & 0xff);
 | 
			
		||||
		add_len |= (buf[3] & 0xff);
 | 
			
		||||
 | 
			
		||||
		tid_len = strlen((char *)&buf[4]);
 | 
			
		||||
		tid_len += 4; /* Add four bytes for iSCSI Transport ID header */
 | 
			
		||||
		tid_len += 1; /* Add one byte for NULL terminator */
 | 
			
		||||
		padding = ((-tid_len) & 3);
 | 
			
		||||
		if (padding != 0)
 | 
			
		||||
			tid_len += padding;
 | 
			
		||||
 | 
			
		||||
		if ((add_len + 4) != tid_len) {
 | 
			
		||||
			printk(KERN_INFO "LIO-Target Extracted add_len: %hu "
 | 
			
		||||
				"does not match calculated tid_len: %u,"
 | 
			
		||||
				" using tid_len instead\n", add_len+4, tid_len);
 | 
			
		||||
			*out_tid_len = tid_len;
 | 
			
		||||
		} else
 | 
			
		||||
			*out_tid_len = (add_len + 4);
 | 
			
		||||
	}
 | 
			
		||||
	/*
 | 
			
		||||
	 * Check for ',i,0x' seperator between iSCSI Name and iSCSI Initiator
 | 
			
		||||
	 * Session ID as defined in Table 390 - iSCSI initiator port TransportID
 | 
			
		||||
	 * format.
 | 
			
		||||
	 */
 | 
			
		||||
	if (format_code == 0x40) {
 | 
			
		||||
		p = strstr((char *)&buf[4], ",i,0x");
 | 
			
		||||
		if (!(p)) {
 | 
			
		||||
			printk(KERN_ERR "Unable to locate \",i,0x\" seperator"
 | 
			
		||||
				" for Initiator port identifier: %s\n",
 | 
			
		||||
				(char *)&buf[4]);
 | 
			
		||||
			return NULL;
 | 
			
		||||
		}
 | 
			
		||||
		*p = '\0'; /* Terminate iSCSI Name */
 | 
			
		||||
		p += 5; /* Skip over ",i,0x" seperator */
 | 
			
		||||
 | 
			
		||||
		*port_nexus_ptr = p;
 | 
			
		||||
		/*
 | 
			
		||||
		 * Go ahead and do the lower case conversion of the received
 | 
			
		||||
		 * 12 ASCII characters representing the ISID in the TransportID
 | 
			
		||||
		 * for comparision against the running iSCSI session's ISID from
 | 
			
		||||
		 * iscsi_target.c:lio_sess_get_initiator_sid()
 | 
			
		||||
		 */
 | 
			
		||||
		for (i = 0; i < 12; i++) {
 | 
			
		||||
			if (isdigit(*p)) {
 | 
			
		||||
				p++;
 | 
			
		||||
				continue;
 | 
			
		||||
			}
 | 
			
		||||
			*p = tolower(*p);
 | 
			
		||||
			p++;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return (char *)&buf[4];
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(iscsi_parse_pr_out_transport_id);
 | 
			
		||||
							
								
								
									
										688
									
								
								drivers/target/target_core_file.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										688
									
								
								drivers/target/target_core_file.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,688 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Filename:  target_core_file.c
 | 
			
		||||
 *
 | 
			
		||||
 * This file contains the Storage Engine <-> FILEIO transport specific functions
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 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/version.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/parser.h>
 | 
			
		||||
#include <linux/timer.h>
 | 
			
		||||
#include <linux/blkdev.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/spinlock.h>
 | 
			
		||||
#include <linux/smp_lock.h>
 | 
			
		||||
#include <scsi/scsi.h>
 | 
			
		||||
#include <scsi/scsi_host.h>
 | 
			
		||||
 | 
			
		||||
#include <target/target_core_base.h>
 | 
			
		||||
#include <target/target_core_device.h>
 | 
			
		||||
#include <target/target_core_transport.h>
 | 
			
		||||
 | 
			
		||||
#include "target_core_file.h"
 | 
			
		||||
 | 
			
		||||
#if 1
 | 
			
		||||
#define DEBUG_FD_CACHE(x...) printk(x)
 | 
			
		||||
#else
 | 
			
		||||
#define DEBUG_FD_CACHE(x...)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if 1
 | 
			
		||||
#define DEBUG_FD_FUA(x...) printk(x)
 | 
			
		||||
#else
 | 
			
		||||
#define DEBUG_FD_FUA(x...)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static struct se_subsystem_api fileio_template;
 | 
			
		||||
 | 
			
		||||
/*	fd_attach_hba(): (Part of se_subsystem_api_t template)
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
static int fd_attach_hba(struct se_hba *hba, u32 host_id)
 | 
			
		||||
{
 | 
			
		||||
	struct fd_host *fd_host;
 | 
			
		||||
 | 
			
		||||
	fd_host = kzalloc(sizeof(struct fd_host), GFP_KERNEL);
 | 
			
		||||
	if (!(fd_host)) {
 | 
			
		||||
		printk(KERN_ERR "Unable to allocate memory for struct fd_host\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fd_host->fd_host_id = host_id;
 | 
			
		||||
 | 
			
		||||
	atomic_set(&hba->left_queue_depth, FD_HBA_QUEUE_DEPTH);
 | 
			
		||||
	atomic_set(&hba->max_queue_depth, FD_HBA_QUEUE_DEPTH);
 | 
			
		||||
	hba->hba_ptr = (void *) fd_host;
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "CORE_HBA[%d] - TCM FILEIO HBA Driver %s on Generic"
 | 
			
		||||
		" Target Core Stack %s\n", hba->hba_id, FD_VERSION,
 | 
			
		||||
		TARGET_CORE_MOD_VERSION);
 | 
			
		||||
	printk(KERN_INFO "CORE_HBA[%d] - Attached FILEIO HBA: %u to Generic"
 | 
			
		||||
		" Target Core with TCQ Depth: %d MaxSectors: %u\n",
 | 
			
		||||
		hba->hba_id, fd_host->fd_host_id,
 | 
			
		||||
		atomic_read(&hba->max_queue_depth), FD_MAX_SECTORS);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void fd_detach_hba(struct se_hba *hba)
 | 
			
		||||
{
 | 
			
		||||
	struct fd_host *fd_host = hba->hba_ptr;
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "CORE_HBA[%d] - Detached FILEIO HBA: %u from Generic"
 | 
			
		||||
		" Target Core\n", hba->hba_id, fd_host->fd_host_id);
 | 
			
		||||
 | 
			
		||||
	kfree(fd_host);
 | 
			
		||||
	hba->hba_ptr = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *fd_allocate_virtdevice(struct se_hba *hba, const char *name)
 | 
			
		||||
{
 | 
			
		||||
	struct fd_dev *fd_dev;
 | 
			
		||||
	struct fd_host *fd_host = (struct fd_host *) hba->hba_ptr;
 | 
			
		||||
 | 
			
		||||
	fd_dev = kzalloc(sizeof(struct fd_dev), GFP_KERNEL);
 | 
			
		||||
	if (!(fd_dev)) {
 | 
			
		||||
		printk(KERN_ERR "Unable to allocate memory for struct fd_dev\n");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fd_dev->fd_host = fd_host;
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "FILEIO: Allocated fd_dev for %p\n", name);
 | 
			
		||||
 | 
			
		||||
	return fd_dev;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*	fd_create_virtdevice(): (Part of se_subsystem_api_t template)
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
static struct se_device *fd_create_virtdevice(
 | 
			
		||||
	struct se_hba *hba,
 | 
			
		||||
	struct se_subsystem_dev *se_dev,
 | 
			
		||||
	void *p)
 | 
			
		||||
{
 | 
			
		||||
	char *dev_p = NULL;
 | 
			
		||||
	struct se_device *dev;
 | 
			
		||||
	struct se_dev_limits dev_limits;
 | 
			
		||||
	struct queue_limits *limits;
 | 
			
		||||
	struct fd_dev *fd_dev = (struct fd_dev *) p;
 | 
			
		||||
	struct fd_host *fd_host = (struct fd_host *) hba->hba_ptr;
 | 
			
		||||
	mm_segment_t old_fs;
 | 
			
		||||
	struct file *file;
 | 
			
		||||
	struct inode *inode = NULL;
 | 
			
		||||
	int dev_flags = 0, flags;
 | 
			
		||||
 | 
			
		||||
	memset(&dev_limits, 0, sizeof(struct se_dev_limits));
 | 
			
		||||
 | 
			
		||||
	old_fs = get_fs();
 | 
			
		||||
	set_fs(get_ds());
 | 
			
		||||
	dev_p = getname(fd_dev->fd_dev_name);
 | 
			
		||||
	set_fs(old_fs);
 | 
			
		||||
 | 
			
		||||
	if (IS_ERR(dev_p)) {
 | 
			
		||||
		printk(KERN_ERR "getname(%s) failed: %lu\n",
 | 
			
		||||
			fd_dev->fd_dev_name, IS_ERR(dev_p));
 | 
			
		||||
		goto fail;
 | 
			
		||||
	}
 | 
			
		||||
#if 0
 | 
			
		||||
	if (di->no_create_file)
 | 
			
		||||
		flags = O_RDWR | O_LARGEFILE;
 | 
			
		||||
	else
 | 
			
		||||
		flags = O_RDWR | O_CREAT | O_LARGEFILE;
 | 
			
		||||
#else
 | 
			
		||||
	flags = O_RDWR | O_CREAT | O_LARGEFILE;
 | 
			
		||||
#endif
 | 
			
		||||
/*	flags |= O_DIRECT; */
 | 
			
		||||
	/*
 | 
			
		||||
	 * If fd_buffered_io=1 has not been set explictly (the default),
 | 
			
		||||
	 * use O_SYNC to force FILEIO writes to disk.
 | 
			
		||||
	 */
 | 
			
		||||
	if (!(fd_dev->fbd_flags & FDBD_USE_BUFFERED_IO))
 | 
			
		||||
		flags |= O_SYNC;
 | 
			
		||||
 | 
			
		||||
	file = filp_open(dev_p, flags, 0600);
 | 
			
		||||
 | 
			
		||||
	if (IS_ERR(file) || !file || !file->f_dentry) {
 | 
			
		||||
		printk(KERN_ERR "filp_open(%s) failed\n", dev_p);
 | 
			
		||||
		goto fail;
 | 
			
		||||
	}
 | 
			
		||||
	fd_dev->fd_file = file;
 | 
			
		||||
	/*
 | 
			
		||||
	 * If using a block backend with this struct file, we extract
 | 
			
		||||
	 * fd_dev->fd_[block,dev]_size from struct block_device.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Otherwise, we use the passed fd_size= from configfs
 | 
			
		||||
	 */
 | 
			
		||||
	inode = file->f_mapping->host;
 | 
			
		||||
	if (S_ISBLK(inode->i_mode)) {
 | 
			
		||||
		struct request_queue *q;
 | 
			
		||||
		/*
 | 
			
		||||
		 * Setup the local scope queue_limits from struct request_queue->limits
 | 
			
		||||
		 * to pass into transport_add_device_to_core_hba() as struct se_dev_limits.
 | 
			
		||||
		 */
 | 
			
		||||
		q = bdev_get_queue(inode->i_bdev);
 | 
			
		||||
		limits = &dev_limits.limits;
 | 
			
		||||
		limits->logical_block_size = bdev_logical_block_size(inode->i_bdev);
 | 
			
		||||
		limits->max_hw_sectors = queue_max_hw_sectors(q);
 | 
			
		||||
		limits->max_sectors = queue_max_sectors(q);
 | 
			
		||||
		/*
 | 
			
		||||
		 * Determine the number of bytes from i_size_read() minus
 | 
			
		||||
		 * one (1) logical sector from underlying struct block_device
 | 
			
		||||
		 */
 | 
			
		||||
		fd_dev->fd_block_size = bdev_logical_block_size(inode->i_bdev);
 | 
			
		||||
		fd_dev->fd_dev_size = (i_size_read(file->f_mapping->host) -
 | 
			
		||||
				       fd_dev->fd_block_size);
 | 
			
		||||
 | 
			
		||||
		printk(KERN_INFO "FILEIO: Using size: %llu bytes from struct"
 | 
			
		||||
			" block_device blocks: %llu logical_block_size: %d\n",
 | 
			
		||||
			fd_dev->fd_dev_size,
 | 
			
		||||
			div_u64(fd_dev->fd_dev_size, fd_dev->fd_block_size),
 | 
			
		||||
			fd_dev->fd_block_size);
 | 
			
		||||
	} else {
 | 
			
		||||
		if (!(fd_dev->fbd_flags & FBDF_HAS_SIZE)) {
 | 
			
		||||
			printk(KERN_ERR "FILEIO: Missing fd_dev_size="
 | 
			
		||||
				" parameter, and no backing struct"
 | 
			
		||||
				" block_device\n");
 | 
			
		||||
			goto fail;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		limits = &dev_limits.limits;
 | 
			
		||||
		limits->logical_block_size = FD_BLOCKSIZE;
 | 
			
		||||
		limits->max_hw_sectors = FD_MAX_SECTORS;
 | 
			
		||||
		limits->max_sectors = FD_MAX_SECTORS;
 | 
			
		||||
		fd_dev->fd_block_size = FD_BLOCKSIZE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dev_limits.hw_queue_depth = FD_MAX_DEVICE_QUEUE_DEPTH;
 | 
			
		||||
	dev_limits.queue_depth = FD_DEVICE_QUEUE_DEPTH;
 | 
			
		||||
 | 
			
		||||
	dev = transport_add_device_to_core_hba(hba, &fileio_template,
 | 
			
		||||
				se_dev, dev_flags, (void *)fd_dev,
 | 
			
		||||
				&dev_limits, "FILEIO", FD_VERSION);
 | 
			
		||||
	if (!(dev))
 | 
			
		||||
		goto fail;
 | 
			
		||||
 | 
			
		||||
	fd_dev->fd_dev_id = fd_host->fd_host_dev_id_count++;
 | 
			
		||||
	fd_dev->fd_queue_depth = dev->queue_depth;
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "CORE_FILE[%u] - Added TCM FILEIO Device ID: %u at %s,"
 | 
			
		||||
		" %llu total bytes\n", fd_host->fd_host_id, fd_dev->fd_dev_id,
 | 
			
		||||
			fd_dev->fd_dev_name, fd_dev->fd_dev_size);
 | 
			
		||||
 | 
			
		||||
	putname(dev_p);
 | 
			
		||||
	return dev;
 | 
			
		||||
fail:
 | 
			
		||||
	if (fd_dev->fd_file) {
 | 
			
		||||
		filp_close(fd_dev->fd_file, NULL);
 | 
			
		||||
		fd_dev->fd_file = NULL;
 | 
			
		||||
	}
 | 
			
		||||
	putname(dev_p);
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*	fd_free_device(): (Part of se_subsystem_api_t template)
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
static void fd_free_device(void *p)
 | 
			
		||||
{
 | 
			
		||||
	struct fd_dev *fd_dev = (struct fd_dev *) p;
 | 
			
		||||
 | 
			
		||||
	if (fd_dev->fd_file) {
 | 
			
		||||
		filp_close(fd_dev->fd_file, NULL);
 | 
			
		||||
		fd_dev->fd_file = NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kfree(fd_dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline struct fd_request *FILE_REQ(struct se_task *task)
 | 
			
		||||
{
 | 
			
		||||
	return container_of(task, struct fd_request, fd_task);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static struct se_task *
 | 
			
		||||
fd_alloc_task(struct se_cmd *cmd)
 | 
			
		||||
{
 | 
			
		||||
	struct fd_request *fd_req;
 | 
			
		||||
 | 
			
		||||
	fd_req = kzalloc(sizeof(struct fd_request), GFP_KERNEL);
 | 
			
		||||
	if (!(fd_req)) {
 | 
			
		||||
		printk(KERN_ERR "Unable to allocate struct fd_request\n");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fd_req->fd_dev = SE_DEV(cmd)->dev_ptr;
 | 
			
		||||
 | 
			
		||||
	return &fd_req->fd_task;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int fd_do_readv(struct se_task *task)
 | 
			
		||||
{
 | 
			
		||||
	struct fd_request *req = FILE_REQ(task);
 | 
			
		||||
	struct file *fd = req->fd_dev->fd_file;
 | 
			
		||||
	struct scatterlist *sg = task->task_sg;
 | 
			
		||||
	struct iovec *iov;
 | 
			
		||||
	mm_segment_t old_fs;
 | 
			
		||||
	loff_t pos = (task->task_lba * DEV_ATTRIB(task->se_dev)->block_size);
 | 
			
		||||
	int ret = 0, i;
 | 
			
		||||
 | 
			
		||||
	iov = kzalloc(sizeof(struct iovec) * task->task_sg_num, GFP_KERNEL);
 | 
			
		||||
	if (!(iov)) {
 | 
			
		||||
		printk(KERN_ERR "Unable to allocate fd_do_readv iov[]\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < task->task_sg_num; i++) {
 | 
			
		||||
		iov[i].iov_len = sg[i].length;
 | 
			
		||||
		iov[i].iov_base = sg_virt(&sg[i]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	old_fs = get_fs();
 | 
			
		||||
	set_fs(get_ds());
 | 
			
		||||
	ret = vfs_readv(fd, &iov[0], task->task_sg_num, &pos);
 | 
			
		||||
	set_fs(old_fs);
 | 
			
		||||
 | 
			
		||||
	kfree(iov);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Return zeros and GOOD status even if the READ did not return
 | 
			
		||||
	 * the expected virt_size for struct file w/o a backing struct
 | 
			
		||||
	 * block_device.
 | 
			
		||||
	 */
 | 
			
		||||
	if (S_ISBLK(fd->f_dentry->d_inode->i_mode)) {
 | 
			
		||||
		if (ret < 0 || ret != task->task_size) {
 | 
			
		||||
			printk(KERN_ERR "vfs_readv() returned %d,"
 | 
			
		||||
				" expecting %d for S_ISBLK\n", ret,
 | 
			
		||||
				(int)task->task_size);
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		if (ret < 0) {
 | 
			
		||||
			printk(KERN_ERR "vfs_readv() returned %d for non"
 | 
			
		||||
				" S_ISBLK\n", ret);
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int fd_do_writev(struct se_task *task)
 | 
			
		||||
{
 | 
			
		||||
	struct fd_request *req = FILE_REQ(task);
 | 
			
		||||
	struct file *fd = req->fd_dev->fd_file;
 | 
			
		||||
	struct scatterlist *sg = task->task_sg;
 | 
			
		||||
	struct iovec *iov;
 | 
			
		||||
	mm_segment_t old_fs;
 | 
			
		||||
	loff_t pos = (task->task_lba * DEV_ATTRIB(task->se_dev)->block_size);
 | 
			
		||||
	int ret, i = 0;
 | 
			
		||||
 | 
			
		||||
	iov = kzalloc(sizeof(struct iovec) * task->task_sg_num, GFP_KERNEL);
 | 
			
		||||
	if (!(iov)) {
 | 
			
		||||
		printk(KERN_ERR "Unable to allocate fd_do_writev iov[]\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < task->task_sg_num; i++) {
 | 
			
		||||
		iov[i].iov_len = sg[i].length;
 | 
			
		||||
		iov[i].iov_base = sg_virt(&sg[i]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	old_fs = get_fs();
 | 
			
		||||
	set_fs(get_ds());
 | 
			
		||||
	ret = vfs_writev(fd, &iov[0], task->task_sg_num, &pos);
 | 
			
		||||
	set_fs(old_fs);
 | 
			
		||||
 | 
			
		||||
	kfree(iov);
 | 
			
		||||
 | 
			
		||||
	if (ret < 0 || ret != task->task_size) {
 | 
			
		||||
		printk(KERN_ERR "vfs_writev() returned %d\n", ret);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void fd_emulate_sync_cache(struct se_task *task)
 | 
			
		||||
{
 | 
			
		||||
	struct se_cmd *cmd = TASK_CMD(task);
 | 
			
		||||
	struct se_device *dev = cmd->se_dev;
 | 
			
		||||
	struct fd_dev *fd_dev = dev->dev_ptr;
 | 
			
		||||
	int immed = (cmd->t_task->t_task_cdb[1] & 0x2);
 | 
			
		||||
	loff_t start, end;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If the Immediate bit is set, queue up the GOOD response
 | 
			
		||||
	 * for this SYNCHRONIZE_CACHE op
 | 
			
		||||
	 */
 | 
			
		||||
	if (immed)
 | 
			
		||||
		transport_complete_sync_cache(cmd, 1);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Determine if we will be flushing the entire device.
 | 
			
		||||
	 */
 | 
			
		||||
	if (cmd->t_task->t_task_lba == 0 && cmd->data_length == 0) {
 | 
			
		||||
		start = 0;
 | 
			
		||||
		end = LLONG_MAX;
 | 
			
		||||
	} else {
 | 
			
		||||
		start = cmd->t_task->t_task_lba * DEV_ATTRIB(dev)->block_size;
 | 
			
		||||
		if (cmd->data_length)
 | 
			
		||||
			end = start + cmd->data_length;
 | 
			
		||||
		else
 | 
			
		||||
			end = LLONG_MAX;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = vfs_fsync_range(fd_dev->fd_file, start, end, 1);
 | 
			
		||||
	if (ret != 0)
 | 
			
		||||
		printk(KERN_ERR "FILEIO: vfs_fsync_range() failed: %d\n", ret);
 | 
			
		||||
 | 
			
		||||
	if (!immed)
 | 
			
		||||
		transport_complete_sync_cache(cmd, ret == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Tell TCM Core that we are capable of WriteCache emulation for
 | 
			
		||||
 * an underlying struct se_device.
 | 
			
		||||
 */
 | 
			
		||||
static int fd_emulated_write_cache(struct se_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int fd_emulated_dpo(struct se_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
/*
 | 
			
		||||
 * Tell TCM Core that we will be emulating Forced Unit Access (FUA) for WRITEs
 | 
			
		||||
 * for TYPE_DISK.
 | 
			
		||||
 */
 | 
			
		||||
static int fd_emulated_fua_write(struct se_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int fd_emulated_fua_read(struct se_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * WRITE Force Unit Access (FUA) emulation on a per struct se_task
 | 
			
		||||
 * LBA range basis..
 | 
			
		||||
 */
 | 
			
		||||
static void fd_emulate_write_fua(struct se_cmd *cmd, struct se_task *task)
 | 
			
		||||
{
 | 
			
		||||
	struct se_device *dev = cmd->se_dev;
 | 
			
		||||
	struct fd_dev *fd_dev = dev->dev_ptr;
 | 
			
		||||
	loff_t start = task->task_lba * DEV_ATTRIB(dev)->block_size;
 | 
			
		||||
	loff_t end = start + task->task_size;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	DEBUG_FD_CACHE("FILEIO: FUA WRITE LBA: %llu, bytes: %u\n",
 | 
			
		||||
			task->task_lba, task->task_size);
 | 
			
		||||
 | 
			
		||||
	ret = vfs_fsync_range(fd_dev->fd_file, start, end, 1);
 | 
			
		||||
	if (ret != 0)
 | 
			
		||||
		printk(KERN_ERR "FILEIO: vfs_fsync_range() failed: %d\n", ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int fd_do_task(struct se_task *task)
 | 
			
		||||
{
 | 
			
		||||
	struct se_cmd *cmd = task->task_se_cmd;
 | 
			
		||||
	struct se_device *dev = cmd->se_dev;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Call vectorized fileio functions to map struct scatterlist
 | 
			
		||||
	 * physical memory addresses to struct iovec virtual memory.
 | 
			
		||||
	 */
 | 
			
		||||
	if (task->task_data_direction == DMA_FROM_DEVICE) {
 | 
			
		||||
		ret = fd_do_readv(task);
 | 
			
		||||
	} else {
 | 
			
		||||
		ret = fd_do_writev(task);
 | 
			
		||||
 | 
			
		||||
		if (ret > 0 &&
 | 
			
		||||
		    DEV_ATTRIB(dev)->emulate_write_cache > 0 &&
 | 
			
		||||
		    DEV_ATTRIB(dev)->emulate_fua_write > 0 &&
 | 
			
		||||
		    T_TASK(cmd)->t_tasks_fua) {
 | 
			
		||||
			/*
 | 
			
		||||
			 * We might need to be a bit smarter here
 | 
			
		||||
			 * and return some sense data to let the initiator
 | 
			
		||||
			 * know the FUA WRITE cache sync failed..?
 | 
			
		||||
			 */
 | 
			
		||||
			fd_emulate_write_fua(cmd, task);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return ret;
 | 
			
		||||
	if (ret) {
 | 
			
		||||
		task->task_scsi_status = GOOD;
 | 
			
		||||
		transport_complete_task(task, 1);
 | 
			
		||||
	}
 | 
			
		||||
	return PYX_TRANSPORT_SENT_TO_TRANSPORT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*	fd_free_task(): (Part of se_subsystem_api_t template)
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
static void fd_free_task(struct se_task *task)
 | 
			
		||||
{
 | 
			
		||||
	struct fd_request *req = FILE_REQ(task);
 | 
			
		||||
 | 
			
		||||
	kfree(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	Opt_fd_dev_name, Opt_fd_dev_size, Opt_fd_buffered_io, Opt_err
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static match_table_t tokens = {
 | 
			
		||||
	{Opt_fd_dev_name, "fd_dev_name=%s"},
 | 
			
		||||
	{Opt_fd_dev_size, "fd_dev_size=%s"},
 | 
			
		||||
	{Opt_fd_buffered_io, "fd_buffered_id=%d"},
 | 
			
		||||
	{Opt_err, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static ssize_t fd_set_configfs_dev_params(
 | 
			
		||||
	struct se_hba *hba,
 | 
			
		||||
	struct se_subsystem_dev *se_dev,
 | 
			
		||||
	const char *page, ssize_t count)
 | 
			
		||||
{
 | 
			
		||||
	struct fd_dev *fd_dev = se_dev->se_dev_su_ptr;
 | 
			
		||||
	char *orig, *ptr, *arg_p, *opts;
 | 
			
		||||
	substring_t args[MAX_OPT_ARGS];
 | 
			
		||||
	int ret = 0, arg, token;
 | 
			
		||||
 | 
			
		||||
	opts = kstrdup(page, GFP_KERNEL);
 | 
			
		||||
	if (!opts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	orig = opts;
 | 
			
		||||
 | 
			
		||||
	while ((ptr = strsep(&opts, ",")) != NULL) {
 | 
			
		||||
		if (!*ptr)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		token = match_token(ptr, tokens, args);
 | 
			
		||||
		switch (token) {
 | 
			
		||||
		case Opt_fd_dev_name:
 | 
			
		||||
			snprintf(fd_dev->fd_dev_name, FD_MAX_DEV_NAME,
 | 
			
		||||
					"%s", match_strdup(&args[0]));
 | 
			
		||||
			printk(KERN_INFO "FILEIO: Referencing Path: %s\n",
 | 
			
		||||
					fd_dev->fd_dev_name);
 | 
			
		||||
			fd_dev->fbd_flags |= FBDF_HAS_PATH;
 | 
			
		||||
			break;
 | 
			
		||||
		case Opt_fd_dev_size:
 | 
			
		||||
			arg_p = match_strdup(&args[0]);
 | 
			
		||||
			ret = strict_strtoull(arg_p, 0, &fd_dev->fd_dev_size);
 | 
			
		||||
			if (ret < 0) {
 | 
			
		||||
				printk(KERN_ERR "strict_strtoull() failed for"
 | 
			
		||||
						" fd_dev_size=\n");
 | 
			
		||||
				goto out;
 | 
			
		||||
			}
 | 
			
		||||
			printk(KERN_INFO "FILEIO: Referencing Size: %llu"
 | 
			
		||||
					" bytes\n", fd_dev->fd_dev_size);
 | 
			
		||||
			fd_dev->fbd_flags |= FBDF_HAS_SIZE;
 | 
			
		||||
			break;
 | 
			
		||||
		case Opt_fd_buffered_io:
 | 
			
		||||
			match_int(args, &arg);
 | 
			
		||||
			if (arg != 1) {
 | 
			
		||||
				printk(KERN_ERR "bogus fd_buffered_io=%d value\n", arg);
 | 
			
		||||
				ret = -EINVAL;
 | 
			
		||||
				goto out;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			printk(KERN_INFO "FILEIO: Using buffered I/O"
 | 
			
		||||
				" operations for struct fd_dev\n");
 | 
			
		||||
 | 
			
		||||
			fd_dev->fbd_flags |= FDBD_USE_BUFFERED_IO;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	kfree(orig);
 | 
			
		||||
	return (!ret) ? count : ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t fd_check_configfs_dev_params(struct se_hba *hba, struct se_subsystem_dev *se_dev)
 | 
			
		||||
{
 | 
			
		||||
	struct fd_dev *fd_dev = (struct fd_dev *) se_dev->se_dev_su_ptr;
 | 
			
		||||
 | 
			
		||||
	if (!(fd_dev->fbd_flags & FBDF_HAS_PATH)) {
 | 
			
		||||
		printk(KERN_ERR "Missing fd_dev_name=\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t fd_show_configfs_dev_params(
 | 
			
		||||
	struct se_hba *hba,
 | 
			
		||||
	struct se_subsystem_dev *se_dev,
 | 
			
		||||
	char *b)
 | 
			
		||||
{
 | 
			
		||||
	struct fd_dev *fd_dev = se_dev->se_dev_su_ptr;
 | 
			
		||||
	ssize_t bl = 0;
 | 
			
		||||
 | 
			
		||||
	bl = sprintf(b + bl, "TCM FILEIO ID: %u", fd_dev->fd_dev_id);
 | 
			
		||||
	bl += sprintf(b + bl, "        File: %s  Size: %llu  Mode: %s\n",
 | 
			
		||||
		fd_dev->fd_dev_name, fd_dev->fd_dev_size,
 | 
			
		||||
		(fd_dev->fbd_flags & FDBD_USE_BUFFERED_IO) ?
 | 
			
		||||
		"Buffered" : "Synchronous");
 | 
			
		||||
	return bl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*	fd_get_cdb(): (Part of se_subsystem_api_t template)
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
static unsigned char *fd_get_cdb(struct se_task *task)
 | 
			
		||||
{
 | 
			
		||||
	struct fd_request *req = FILE_REQ(task);
 | 
			
		||||
 | 
			
		||||
	return req->fd_scsi_cdb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*	fd_get_device_rev(): (Part of se_subsystem_api_t template)
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
static u32 fd_get_device_rev(struct se_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	return SCSI_SPC_2; /* Returns SPC-3 in Initiator Data */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*	fd_get_device_type(): (Part of se_subsystem_api_t template)
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
static u32 fd_get_device_type(struct se_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	return TYPE_DISK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static sector_t fd_get_blocks(struct se_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	struct fd_dev *fd_dev = dev->dev_ptr;
 | 
			
		||||
	unsigned long long blocks_long = div_u64(fd_dev->fd_dev_size,
 | 
			
		||||
			DEV_ATTRIB(dev)->block_size);
 | 
			
		||||
 | 
			
		||||
	return blocks_long;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct se_subsystem_api fileio_template = {
 | 
			
		||||
	.name			= "fileio",
 | 
			
		||||
	.owner			= THIS_MODULE,
 | 
			
		||||
	.transport_type		= TRANSPORT_PLUGIN_VHBA_PDEV,
 | 
			
		||||
	.attach_hba		= fd_attach_hba,
 | 
			
		||||
	.detach_hba		= fd_detach_hba,
 | 
			
		||||
	.allocate_virtdevice	= fd_allocate_virtdevice,
 | 
			
		||||
	.create_virtdevice	= fd_create_virtdevice,
 | 
			
		||||
	.free_device		= fd_free_device,
 | 
			
		||||
	.dpo_emulated		= fd_emulated_dpo,
 | 
			
		||||
	.fua_write_emulated	= fd_emulated_fua_write,
 | 
			
		||||
	.fua_read_emulated	= fd_emulated_fua_read,
 | 
			
		||||
	.write_cache_emulated	= fd_emulated_write_cache,
 | 
			
		||||
	.alloc_task		= fd_alloc_task,
 | 
			
		||||
	.do_task		= fd_do_task,
 | 
			
		||||
	.do_sync_cache		= fd_emulate_sync_cache,
 | 
			
		||||
	.free_task		= fd_free_task,
 | 
			
		||||
	.check_configfs_dev_params = fd_check_configfs_dev_params,
 | 
			
		||||
	.set_configfs_dev_params = fd_set_configfs_dev_params,
 | 
			
		||||
	.show_configfs_dev_params = fd_show_configfs_dev_params,
 | 
			
		||||
	.get_cdb		= fd_get_cdb,
 | 
			
		||||
	.get_device_rev		= fd_get_device_rev,
 | 
			
		||||
	.get_device_type	= fd_get_device_type,
 | 
			
		||||
	.get_blocks		= fd_get_blocks,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init fileio_module_init(void)
 | 
			
		||||
{
 | 
			
		||||
	return transport_subsystem_register(&fileio_template);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void fileio_module_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	transport_subsystem_release(&fileio_template);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MODULE_DESCRIPTION("TCM FILEIO subsystem plugin");
 | 
			
		||||
MODULE_AUTHOR("nab@Linux-iSCSI.org");
 | 
			
		||||
MODULE_LICENSE("GPL");
 | 
			
		||||
 | 
			
		||||
module_init(fileio_module_init);
 | 
			
		||||
module_exit(fileio_module_exit);
 | 
			
		||||
							
								
								
									
										50
									
								
								drivers/target/target_core_file.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								drivers/target/target_core_file.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
#ifndef TARGET_CORE_FILE_H
 | 
			
		||||
#define TARGET_CORE_FILE_H
 | 
			
		||||
 | 
			
		||||
#define FD_VERSION		"4.0"
 | 
			
		||||
 | 
			
		||||
#define FD_MAX_DEV_NAME		256
 | 
			
		||||
/* Maximum queuedepth for the FILEIO HBA */
 | 
			
		||||
#define FD_HBA_QUEUE_DEPTH	256
 | 
			
		||||
#define FD_DEVICE_QUEUE_DEPTH	32
 | 
			
		||||
#define FD_MAX_DEVICE_QUEUE_DEPTH 128
 | 
			
		||||
#define FD_BLOCKSIZE		512
 | 
			
		||||
#define FD_MAX_SECTORS		1024
 | 
			
		||||
 | 
			
		||||
#define RRF_EMULATE_CDB		0x01
 | 
			
		||||
#define RRF_GOT_LBA		0x02
 | 
			
		||||
 | 
			
		||||
struct fd_request {
 | 
			
		||||
	struct se_task	fd_task;
 | 
			
		||||
	/* SCSI CDB from iSCSI Command PDU */
 | 
			
		||||
	unsigned char	fd_scsi_cdb[TCM_MAX_COMMAND_SIZE];
 | 
			
		||||
	/* FILEIO device */
 | 
			
		||||
	struct fd_dev	*fd_dev;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
#define FBDF_HAS_PATH		0x01
 | 
			
		||||
#define FBDF_HAS_SIZE		0x02
 | 
			
		||||
#define FDBD_USE_BUFFERED_IO	0x04
 | 
			
		||||
 | 
			
		||||
struct fd_dev {
 | 
			
		||||
	u32		fbd_flags;
 | 
			
		||||
	unsigned char	fd_dev_name[FD_MAX_DEV_NAME];
 | 
			
		||||
	/* Unique Ramdisk Device ID in Ramdisk HBA */
 | 
			
		||||
	u32		fd_dev_id;
 | 
			
		||||
	/* Number of SG tables in sg_table_array */
 | 
			
		||||
	u32		fd_table_count;
 | 
			
		||||
	u32		fd_queue_depth;
 | 
			
		||||
	u32		fd_block_size;
 | 
			
		||||
	unsigned long long fd_dev_size;
 | 
			
		||||
	struct file	*fd_file;
 | 
			
		||||
	/* FILEIO HBA device is connected to */
 | 
			
		||||
	struct fd_host *fd_host;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct fd_host {
 | 
			
		||||
	u32		fd_host_dev_id_count;
 | 
			
		||||
	/* Unique FILEIO Host ID */
 | 
			
		||||
	u32		fd_host_id;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
#endif /* TARGET_CORE_FILE_H */
 | 
			
		||||
							
								
								
									
										185
									
								
								drivers/target/target_core_hba.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										185
									
								
								drivers/target/target_core_hba.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,185 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Filename:  target_core_hba.c
 | 
			
		||||
 *
 | 
			
		||||
 * This file copntains the iSCSI HBA Transport related functions.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2003, 2004, 2005 PyX Technologies, Inc.
 | 
			
		||||
 * Copyright (c) 2005, 2006, 2007 SBE, Inc.
 | 
			
		||||
 * 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/timer.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/spinlock.h>
 | 
			
		||||
#include <linux/smp_lock.h>
 | 
			
		||||
#include <linux/in.h>
 | 
			
		||||
#include <net/sock.h>
 | 
			
		||||
#include <net/tcp.h>
 | 
			
		||||
 | 
			
		||||
#include <target/target_core_base.h>
 | 
			
		||||
#include <target/target_core_device.h>
 | 
			
		||||
#include <target/target_core_device.h>
 | 
			
		||||
#include <target/target_core_tpg.h>
 | 
			
		||||
#include <target/target_core_transport.h>
 | 
			
		||||
 | 
			
		||||
#include "target_core_hba.h"
 | 
			
		||||
 | 
			
		||||
static LIST_HEAD(subsystem_list);
 | 
			
		||||
static DEFINE_MUTEX(subsystem_mutex);
 | 
			
		||||
 | 
			
		||||
int transport_subsystem_register(struct se_subsystem_api *sub_api)
 | 
			
		||||
{
 | 
			
		||||
	struct se_subsystem_api *s;
 | 
			
		||||
 | 
			
		||||
	INIT_LIST_HEAD(&sub_api->sub_api_list);
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&subsystem_mutex);
 | 
			
		||||
	list_for_each_entry(s, &subsystem_list, sub_api_list) {
 | 
			
		||||
		if (!(strcmp(s->name, sub_api->name))) {
 | 
			
		||||
			printk(KERN_ERR "%p is already registered with"
 | 
			
		||||
				" duplicate name %s, unable to process"
 | 
			
		||||
				" request\n", s, s->name);
 | 
			
		||||
			mutex_unlock(&subsystem_mutex);
 | 
			
		||||
			return -EEXIST;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	list_add_tail(&sub_api->sub_api_list, &subsystem_list);
 | 
			
		||||
	mutex_unlock(&subsystem_mutex);
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "TCM: Registered subsystem plugin: %s struct module:"
 | 
			
		||||
			" %p\n", sub_api->name, sub_api->owner);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(transport_subsystem_register);
 | 
			
		||||
 | 
			
		||||
void transport_subsystem_release(struct se_subsystem_api *sub_api)
 | 
			
		||||
{
 | 
			
		||||
	mutex_lock(&subsystem_mutex);
 | 
			
		||||
	list_del(&sub_api->sub_api_list);
 | 
			
		||||
	mutex_unlock(&subsystem_mutex);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(transport_subsystem_release);
 | 
			
		||||
 | 
			
		||||
static struct se_subsystem_api *core_get_backend(const char *sub_name)
 | 
			
		||||
{
 | 
			
		||||
	struct se_subsystem_api *s;
 | 
			
		||||
 | 
			
		||||
	mutex_lock(&subsystem_mutex);
 | 
			
		||||
	list_for_each_entry(s, &subsystem_list, sub_api_list) {
 | 
			
		||||
		if (!strcmp(s->name, sub_name))
 | 
			
		||||
			goto found;
 | 
			
		||||
	}
 | 
			
		||||
	mutex_unlock(&subsystem_mutex);
 | 
			
		||||
	return NULL;
 | 
			
		||||
found:
 | 
			
		||||
	if (s->owner && !try_module_get(s->owner))
 | 
			
		||||
		s = NULL;
 | 
			
		||||
	mutex_unlock(&subsystem_mutex);
 | 
			
		||||
	return s;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct se_hba *
 | 
			
		||||
core_alloc_hba(const char *plugin_name, u32 plugin_dep_id, u32 hba_flags)
 | 
			
		||||
{
 | 
			
		||||
	struct se_hba *hba;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
 | 
			
		||||
	hba = kzalloc(sizeof(*hba), GFP_KERNEL);
 | 
			
		||||
	if (!hba) {
 | 
			
		||||
		printk(KERN_ERR "Unable to allocate struct se_hba\n");
 | 
			
		||||
		return ERR_PTR(-ENOMEM);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	INIT_LIST_HEAD(&hba->hba_dev_list);
 | 
			
		||||
	spin_lock_init(&hba->device_lock);
 | 
			
		||||
	spin_lock_init(&hba->hba_queue_lock);
 | 
			
		||||
	mutex_init(&hba->hba_access_mutex);
 | 
			
		||||
 | 
			
		||||
	hba->hba_index = scsi_get_new_index(SCSI_INST_INDEX);
 | 
			
		||||
	hba->hba_flags |= hba_flags;
 | 
			
		||||
 | 
			
		||||
	atomic_set(&hba->max_queue_depth, 0);
 | 
			
		||||
	atomic_set(&hba->left_queue_depth, 0);
 | 
			
		||||
 | 
			
		||||
	hba->transport = core_get_backend(plugin_name);
 | 
			
		||||
	if (!hba->transport) {
 | 
			
		||||
		ret = -EINVAL;
 | 
			
		||||
		goto out_free_hba;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ret = hba->transport->attach_hba(hba, plugin_dep_id);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		goto out_module_put;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&se_global->hba_lock);
 | 
			
		||||
	hba->hba_id = se_global->g_hba_id_counter++;
 | 
			
		||||
	list_add_tail(&hba->hba_list, &se_global->g_hba_list);
 | 
			
		||||
	spin_unlock(&se_global->hba_lock);
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "CORE_HBA[%d] - Attached HBA to Generic Target"
 | 
			
		||||
			" Core\n", hba->hba_id);
 | 
			
		||||
 | 
			
		||||
	return hba;
 | 
			
		||||
 | 
			
		||||
out_module_put:
 | 
			
		||||
	if (hba->transport->owner)
 | 
			
		||||
		module_put(hba->transport->owner);
 | 
			
		||||
	hba->transport = NULL;
 | 
			
		||||
out_free_hba:
 | 
			
		||||
	kfree(hba);
 | 
			
		||||
	return ERR_PTR(ret);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int
 | 
			
		||||
core_delete_hba(struct se_hba *hba)
 | 
			
		||||
{
 | 
			
		||||
	struct se_device *dev, *dev_tmp;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&hba->device_lock);
 | 
			
		||||
	list_for_each_entry_safe(dev, dev_tmp, &hba->hba_dev_list, dev_list) {
 | 
			
		||||
 | 
			
		||||
		se_clear_dev_ports(dev);
 | 
			
		||||
		spin_unlock(&hba->device_lock);
 | 
			
		||||
 | 
			
		||||
		se_release_device_for_hba(dev);
 | 
			
		||||
 | 
			
		||||
		spin_lock(&hba->device_lock);
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock(&hba->device_lock);
 | 
			
		||||
 | 
			
		||||
	hba->transport->detach_hba(hba);
 | 
			
		||||
 | 
			
		||||
	spin_lock(&se_global->hba_lock);
 | 
			
		||||
	list_del(&hba->hba_list);
 | 
			
		||||
	spin_unlock(&se_global->hba_lock);
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "CORE_HBA[%d] - Detached HBA from Generic Target"
 | 
			
		||||
			" Core\n", hba->hba_id);
 | 
			
		||||
 | 
			
		||||
	if (hba->transport->owner)
 | 
			
		||||
		module_put(hba->transport->owner);
 | 
			
		||||
 | 
			
		||||
	hba->transport = NULL;
 | 
			
		||||
	kfree(hba);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										7
									
								
								drivers/target/target_core_hba.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								drivers/target/target_core_hba.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
#ifndef TARGET_CORE_HBA_H
 | 
			
		||||
#define TARGET_CORE_HBA_H
 | 
			
		||||
 | 
			
		||||
extern struct se_hba *core_alloc_hba(const char *, u32, u32);
 | 
			
		||||
extern int core_delete_hba(struct se_hba *);
 | 
			
		||||
 | 
			
		||||
#endif /* TARGET_CORE_HBA_H */
 | 
			
		||||
							
								
								
									
										808
									
								
								drivers/target/target_core_iblock.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										808
									
								
								drivers/target/target_core_iblock.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,808 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Filename:  target_core_iblock.c
 | 
			
		||||
 *
 | 
			
		||||
 * This file contains the Storage Engine  <-> Linux BlockIO transport
 | 
			
		||||
 * specific functions.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2003, 2004, 2005 PyX Technologies, Inc.
 | 
			
		||||
 * Copyright (c) 2005, 2006, 2007 SBE, Inc.
 | 
			
		||||
 * 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/version.h>
 | 
			
		||||
#include <linux/string.h>
 | 
			
		||||
#include <linux/parser.h>
 | 
			
		||||
#include <linux/timer.h>
 | 
			
		||||
#include <linux/fs.h>
 | 
			
		||||
#include <linux/blkdev.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/spinlock.h>
 | 
			
		||||
#include <linux/smp_lock.h>
 | 
			
		||||
#include <linux/bio.h>
 | 
			
		||||
#include <linux/genhd.h>
 | 
			
		||||
#include <linux/file.h>
 | 
			
		||||
#include <scsi/scsi.h>
 | 
			
		||||
#include <scsi/scsi_host.h>
 | 
			
		||||
 | 
			
		||||
#include <target/target_core_base.h>
 | 
			
		||||
#include <target/target_core_device.h>
 | 
			
		||||
#include <target/target_core_transport.h>
 | 
			
		||||
 | 
			
		||||
#include "target_core_iblock.h"
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
#define DEBUG_IBLOCK(x...) printk(x)
 | 
			
		||||
#else
 | 
			
		||||
#define DEBUG_IBLOCK(x...)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static struct se_subsystem_api iblock_template;
 | 
			
		||||
 | 
			
		||||
static void iblock_bio_done(struct bio *, int);
 | 
			
		||||
 | 
			
		||||
/*	iblock_attach_hba(): (Part of se_subsystem_api_t template)
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
static int iblock_attach_hba(struct se_hba *hba, u32 host_id)
 | 
			
		||||
{
 | 
			
		||||
	struct iblock_hba *ib_host;
 | 
			
		||||
 | 
			
		||||
	ib_host = kzalloc(sizeof(struct iblock_hba), GFP_KERNEL);
 | 
			
		||||
	if (!(ib_host)) {
 | 
			
		||||
		printk(KERN_ERR "Unable to allocate memory for"
 | 
			
		||||
				" struct iblock_hba\n");
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ib_host->iblock_host_id = host_id;
 | 
			
		||||
 | 
			
		||||
	atomic_set(&hba->left_queue_depth, IBLOCK_HBA_QUEUE_DEPTH);
 | 
			
		||||
	atomic_set(&hba->max_queue_depth, IBLOCK_HBA_QUEUE_DEPTH);
 | 
			
		||||
	hba->hba_ptr = (void *) ib_host;
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "CORE_HBA[%d] - TCM iBlock HBA Driver %s on"
 | 
			
		||||
		" Generic Target Core Stack %s\n", hba->hba_id,
 | 
			
		||||
		IBLOCK_VERSION, TARGET_CORE_MOD_VERSION);
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "CORE_HBA[%d] - Attached iBlock HBA: %u to Generic"
 | 
			
		||||
		" Target Core TCQ Depth: %d\n", hba->hba_id,
 | 
			
		||||
		ib_host->iblock_host_id, atomic_read(&hba->max_queue_depth));
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void iblock_detach_hba(struct se_hba *hba)
 | 
			
		||||
{
 | 
			
		||||
	struct iblock_hba *ib_host = hba->hba_ptr;
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "CORE_HBA[%d] - Detached iBlock HBA: %u from Generic"
 | 
			
		||||
		" Target Core\n", hba->hba_id, ib_host->iblock_host_id);
 | 
			
		||||
 | 
			
		||||
	kfree(ib_host);
 | 
			
		||||
	hba->hba_ptr = NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void *iblock_allocate_virtdevice(struct se_hba *hba, const char *name)
 | 
			
		||||
{
 | 
			
		||||
	struct iblock_dev *ib_dev = NULL;
 | 
			
		||||
	struct iblock_hba *ib_host = hba->hba_ptr;
 | 
			
		||||
 | 
			
		||||
	ib_dev = kzalloc(sizeof(struct iblock_dev), GFP_KERNEL);
 | 
			
		||||
	if (!(ib_dev)) {
 | 
			
		||||
		printk(KERN_ERR "Unable to allocate struct iblock_dev\n");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	ib_dev->ibd_host = ib_host;
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO  "IBLOCK: Allocated ib_dev for %s\n", name);
 | 
			
		||||
 | 
			
		||||
	return ib_dev;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct se_device *iblock_create_virtdevice(
 | 
			
		||||
	struct se_hba *hba,
 | 
			
		||||
	struct se_subsystem_dev *se_dev,
 | 
			
		||||
	void *p)
 | 
			
		||||
{
 | 
			
		||||
	struct iblock_dev *ib_dev = p;
 | 
			
		||||
	struct se_device *dev;
 | 
			
		||||
	struct se_dev_limits dev_limits;
 | 
			
		||||
	struct block_device *bd = NULL;
 | 
			
		||||
	struct request_queue *q;
 | 
			
		||||
	struct queue_limits *limits;
 | 
			
		||||
	u32 dev_flags = 0;
 | 
			
		||||
 | 
			
		||||
	if (!(ib_dev)) {
 | 
			
		||||
		printk(KERN_ERR "Unable to locate struct iblock_dev parameter\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	memset(&dev_limits, 0, sizeof(struct se_dev_limits));
 | 
			
		||||
	/*
 | 
			
		||||
	 * These settings need to be made tunable..
 | 
			
		||||
	 */
 | 
			
		||||
	ib_dev->ibd_bio_set = bioset_create(32, 64);
 | 
			
		||||
	if (!(ib_dev->ibd_bio_set)) {
 | 
			
		||||
		printk(KERN_ERR "IBLOCK: Unable to create bioset()\n");
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	printk(KERN_INFO "IBLOCK: Created bio_set()\n");
 | 
			
		||||
	/*
 | 
			
		||||
	 * iblock_check_configfs_dev_params() ensures that ib_dev->ibd_udev_path
 | 
			
		||||
	 * must already have been set in order for echo 1 > $HBA/$DEV/enable to run.
 | 
			
		||||
	 */
 | 
			
		||||
	printk(KERN_INFO  "IBLOCK: Claiming struct block_device: %s\n",
 | 
			
		||||
			ib_dev->ibd_udev_path);
 | 
			
		||||
 | 
			
		||||
	bd = blkdev_get_by_path(ib_dev->ibd_udev_path,
 | 
			
		||||
				FMODE_WRITE|FMODE_READ|FMODE_EXCL, ib_dev);
 | 
			
		||||
	if (!(bd))
 | 
			
		||||
		goto failed;
 | 
			
		||||
	/*
 | 
			
		||||
	 * Setup the local scope queue_limits from struct request_queue->limits
 | 
			
		||||
	 * to pass into transport_add_device_to_core_hba() as struct se_dev_limits.
 | 
			
		||||
	 */
 | 
			
		||||
	q = bdev_get_queue(bd);
 | 
			
		||||
	limits = &dev_limits.limits;
 | 
			
		||||
	limits->logical_block_size = bdev_logical_block_size(bd);
 | 
			
		||||
	limits->max_hw_sectors = queue_max_hw_sectors(q);
 | 
			
		||||
	limits->max_sectors = queue_max_sectors(q);
 | 
			
		||||
	dev_limits.hw_queue_depth = IBLOCK_MAX_DEVICE_QUEUE_DEPTH;
 | 
			
		||||
	dev_limits.queue_depth = IBLOCK_DEVICE_QUEUE_DEPTH;
 | 
			
		||||
 | 
			
		||||
	ib_dev->ibd_major = MAJOR(bd->bd_dev);
 | 
			
		||||
	ib_dev->ibd_minor = MINOR(bd->bd_dev);
 | 
			
		||||
	ib_dev->ibd_bd = bd;
 | 
			
		||||
 | 
			
		||||
	dev = transport_add_device_to_core_hba(hba,
 | 
			
		||||
			&iblock_template, se_dev, dev_flags, (void *)ib_dev,
 | 
			
		||||
			&dev_limits, "IBLOCK", IBLOCK_VERSION);
 | 
			
		||||
	if (!(dev))
 | 
			
		||||
		goto failed;
 | 
			
		||||
 | 
			
		||||
	ib_dev->ibd_depth = dev->queue_depth;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Check if the underlying struct block_device request_queue supports
 | 
			
		||||
	 * the QUEUE_FLAG_DISCARD bit for UNMAP/WRITE_SAME in SCSI + TRIM
 | 
			
		||||
	 * in ATA and we need to set TPE=1
 | 
			
		||||
	 */
 | 
			
		||||
	if (blk_queue_discard(bdev_get_queue(bd))) {
 | 
			
		||||
		struct request_queue *q = bdev_get_queue(bd);
 | 
			
		||||
 | 
			
		||||
		DEV_ATTRIB(dev)->max_unmap_lba_count =
 | 
			
		||||
				q->limits.max_discard_sectors;
 | 
			
		||||
		/*
 | 
			
		||||
		 * Currently hardcoded to 1 in Linux/SCSI code..
 | 
			
		||||
		 */
 | 
			
		||||
		DEV_ATTRIB(dev)->max_unmap_block_desc_count = 1;
 | 
			
		||||
		DEV_ATTRIB(dev)->unmap_granularity =
 | 
			
		||||
				q->limits.discard_granularity;
 | 
			
		||||
		DEV_ATTRIB(dev)->unmap_granularity_alignment =
 | 
			
		||||
				q->limits.discard_alignment;
 | 
			
		||||
 | 
			
		||||
		printk(KERN_INFO "IBLOCK: BLOCK Discard support available,"
 | 
			
		||||
				" disabled by default\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return dev;
 | 
			
		||||
 | 
			
		||||
failed:
 | 
			
		||||
	if (ib_dev->ibd_bio_set) {
 | 
			
		||||
		bioset_free(ib_dev->ibd_bio_set);
 | 
			
		||||
		ib_dev->ibd_bio_set = NULL;
 | 
			
		||||
	}
 | 
			
		||||
	ib_dev->ibd_bd = NULL;
 | 
			
		||||
	ib_dev->ibd_major = 0;
 | 
			
		||||
	ib_dev->ibd_minor = 0;
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void iblock_free_device(void *p)
 | 
			
		||||
{
 | 
			
		||||
	struct iblock_dev *ib_dev = p;
 | 
			
		||||
 | 
			
		||||
	blkdev_put(ib_dev->ibd_bd, FMODE_WRITE|FMODE_READ|FMODE_EXCL);
 | 
			
		||||
	bioset_free(ib_dev->ibd_bio_set);
 | 
			
		||||
	kfree(ib_dev);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline struct iblock_req *IBLOCK_REQ(struct se_task *task)
 | 
			
		||||
{
 | 
			
		||||
	return container_of(task, struct iblock_req, ib_task);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct se_task *
 | 
			
		||||
iblock_alloc_task(struct se_cmd *cmd)
 | 
			
		||||
{
 | 
			
		||||
	struct iblock_req *ib_req;
 | 
			
		||||
 | 
			
		||||
	ib_req = kzalloc(sizeof(struct iblock_req), GFP_KERNEL);
 | 
			
		||||
	if (!(ib_req)) {
 | 
			
		||||
		printk(KERN_ERR "Unable to allocate memory for struct iblock_req\n");
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ib_req->ib_dev = SE_DEV(cmd)->dev_ptr;
 | 
			
		||||
	atomic_set(&ib_req->ib_bio_cnt, 0);
 | 
			
		||||
	return &ib_req->ib_task;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static unsigned long long iblock_emulate_read_cap_with_block_size(
 | 
			
		||||
	struct se_device *dev,
 | 
			
		||||
	struct block_device *bd,
 | 
			
		||||
	struct request_queue *q)
 | 
			
		||||
{
 | 
			
		||||
	unsigned long long blocks_long = (div_u64(i_size_read(bd->bd_inode),
 | 
			
		||||
					bdev_logical_block_size(bd)) - 1);
 | 
			
		||||
	u32 block_size = bdev_logical_block_size(bd);
 | 
			
		||||
 | 
			
		||||
	if (block_size == DEV_ATTRIB(dev)->block_size)
 | 
			
		||||
		return blocks_long;
 | 
			
		||||
 | 
			
		||||
	switch (block_size) {
 | 
			
		||||
	case 4096:
 | 
			
		||||
		switch (DEV_ATTRIB(dev)->block_size) {
 | 
			
		||||
		case 2048:
 | 
			
		||||
			blocks_long <<= 1;
 | 
			
		||||
			break;
 | 
			
		||||
		case 1024:
 | 
			
		||||
			blocks_long <<= 2;
 | 
			
		||||
			break;
 | 
			
		||||
		case 512:
 | 
			
		||||
			blocks_long <<= 3;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case 2048:
 | 
			
		||||
		switch (DEV_ATTRIB(dev)->block_size) {
 | 
			
		||||
		case 4096:
 | 
			
		||||
			blocks_long >>= 1;
 | 
			
		||||
			break;
 | 
			
		||||
		case 1024:
 | 
			
		||||
			blocks_long <<= 1;
 | 
			
		||||
			break;
 | 
			
		||||
		case 512:
 | 
			
		||||
			blocks_long <<= 2;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case 1024:
 | 
			
		||||
		switch (DEV_ATTRIB(dev)->block_size) {
 | 
			
		||||
		case 4096:
 | 
			
		||||
			blocks_long >>= 2;
 | 
			
		||||
			break;
 | 
			
		||||
		case 2048:
 | 
			
		||||
			blocks_long >>= 1;
 | 
			
		||||
			break;
 | 
			
		||||
		case 512:
 | 
			
		||||
			blocks_long <<= 1;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case 512:
 | 
			
		||||
		switch (DEV_ATTRIB(dev)->block_size) {
 | 
			
		||||
		case 4096:
 | 
			
		||||
			blocks_long >>= 3;
 | 
			
		||||
			break;
 | 
			
		||||
		case 2048:
 | 
			
		||||
			blocks_long >>= 2;
 | 
			
		||||
			break;
 | 
			
		||||
		case 1024:
 | 
			
		||||
			blocks_long >>= 1;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return blocks_long;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Emulate SYCHRONIZE_CACHE_*
 | 
			
		||||
 */
 | 
			
		||||
static void iblock_emulate_sync_cache(struct se_task *task)
 | 
			
		||||
{
 | 
			
		||||
	struct se_cmd *cmd = TASK_CMD(task);
 | 
			
		||||
	struct iblock_dev *ib_dev = cmd->se_dev->dev_ptr;
 | 
			
		||||
	int immed = (T_TASK(cmd)->t_task_cdb[1] & 0x2);
 | 
			
		||||
	sector_t error_sector;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * If the Immediate bit is set, queue up the GOOD response
 | 
			
		||||
	 * for this SYNCHRONIZE_CACHE op
 | 
			
		||||
	 */
 | 
			
		||||
	if (immed)
 | 
			
		||||
		transport_complete_sync_cache(cmd, 1);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * blkdev_issue_flush() does not support a specifying a range, so
 | 
			
		||||
	 * we have to flush the entire cache.
 | 
			
		||||
	 */
 | 
			
		||||
	ret = blkdev_issue_flush(ib_dev->ibd_bd, GFP_KERNEL, &error_sector);
 | 
			
		||||
	if (ret != 0) {
 | 
			
		||||
		printk(KERN_ERR "IBLOCK: block_issue_flush() failed: %d "
 | 
			
		||||
			" error_sector: %llu\n", ret,
 | 
			
		||||
			(unsigned long long)error_sector);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!immed)
 | 
			
		||||
		transport_complete_sync_cache(cmd, ret == 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Tell TCM Core that we are capable of WriteCache emulation for
 | 
			
		||||
 * an underlying struct se_device.
 | 
			
		||||
 */
 | 
			
		||||
static int iblock_emulated_write_cache(struct se_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int iblock_emulated_dpo(struct se_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Tell TCM Core that we will be emulating Forced Unit Access (FUA) for WRITEs
 | 
			
		||||
 * for TYPE_DISK.
 | 
			
		||||
 */
 | 
			
		||||
static int iblock_emulated_fua_write(struct se_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int iblock_emulated_fua_read(struct se_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int iblock_do_task(struct se_task *task)
 | 
			
		||||
{
 | 
			
		||||
	struct se_device *dev = task->task_se_cmd->se_dev;
 | 
			
		||||
	struct iblock_req *req = IBLOCK_REQ(task);
 | 
			
		||||
	struct iblock_dev *ibd = (struct iblock_dev *)req->ib_dev;
 | 
			
		||||
	struct request_queue *q = bdev_get_queue(ibd->ibd_bd);
 | 
			
		||||
	struct bio *bio = req->ib_bio, *nbio = NULL;
 | 
			
		||||
	int rw;
 | 
			
		||||
 | 
			
		||||
	if (task->task_data_direction == DMA_TO_DEVICE) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Force data to disk if we pretend to not have a volatile
 | 
			
		||||
		 * write cache, or the initiator set the Force Unit Access bit.
 | 
			
		||||
		 */
 | 
			
		||||
		if (DEV_ATTRIB(dev)->emulate_write_cache == 0 ||
 | 
			
		||||
		    (DEV_ATTRIB(dev)->emulate_fua_write > 0 &&
 | 
			
		||||
		     T_TASK(task->task_se_cmd)->t_tasks_fua))
 | 
			
		||||
			rw = WRITE_FUA;
 | 
			
		||||
		else
 | 
			
		||||
			rw = WRITE;
 | 
			
		||||
	} else {
 | 
			
		||||
		rw = READ;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while (bio) {
 | 
			
		||||
		nbio = bio->bi_next;
 | 
			
		||||
		bio->bi_next = NULL;
 | 
			
		||||
		DEBUG_IBLOCK("Calling submit_bio() task: %p bio: %p"
 | 
			
		||||
			" bio->bi_sector: %llu\n", task, bio, bio->bi_sector);
 | 
			
		||||
 | 
			
		||||
		submit_bio(rw, bio);
 | 
			
		||||
		bio = nbio;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (q->unplug_fn)
 | 
			
		||||
		q->unplug_fn(q);
 | 
			
		||||
	return PYX_TRANSPORT_SENT_TO_TRANSPORT;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int iblock_do_discard(struct se_device *dev, sector_t lba, u32 range)
 | 
			
		||||
{
 | 
			
		||||
	struct iblock_dev *ibd = dev->dev_ptr;
 | 
			
		||||
	struct block_device *bd = ibd->ibd_bd;
 | 
			
		||||
	int barrier = 0;
 | 
			
		||||
 | 
			
		||||
	return blkdev_issue_discard(bd, lba, range, GFP_KERNEL, barrier);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void iblock_free_task(struct se_task *task)
 | 
			
		||||
{
 | 
			
		||||
	struct iblock_req *req = IBLOCK_REQ(task);
 | 
			
		||||
	struct bio *bio, *hbio = req->ib_bio;
 | 
			
		||||
	/*
 | 
			
		||||
	 * We only release the bio(s) here if iblock_bio_done() has not called
 | 
			
		||||
	 * bio_put() -> iblock_bio_destructor().
 | 
			
		||||
	 */
 | 
			
		||||
	while (hbio != NULL) {
 | 
			
		||||
		bio = hbio;
 | 
			
		||||
		hbio = hbio->bi_next;
 | 
			
		||||
		bio->bi_next = NULL;
 | 
			
		||||
		bio_put(bio);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	kfree(req);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum {
 | 
			
		||||
	Opt_udev_path, Opt_force, Opt_err
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static match_table_t tokens = {
 | 
			
		||||
	{Opt_udev_path, "udev_path=%s"},
 | 
			
		||||
	{Opt_force, "force=%d"},
 | 
			
		||||
	{Opt_err, NULL}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static ssize_t iblock_set_configfs_dev_params(struct se_hba *hba,
 | 
			
		||||
					       struct se_subsystem_dev *se_dev,
 | 
			
		||||
					       const char *page, ssize_t count)
 | 
			
		||||
{
 | 
			
		||||
	struct iblock_dev *ib_dev = se_dev->se_dev_su_ptr;
 | 
			
		||||
	char *orig, *ptr, *opts;
 | 
			
		||||
	substring_t args[MAX_OPT_ARGS];
 | 
			
		||||
	int ret = 0, arg, token;
 | 
			
		||||
 | 
			
		||||
	opts = kstrdup(page, GFP_KERNEL);
 | 
			
		||||
	if (!opts)
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
 | 
			
		||||
	orig = opts;
 | 
			
		||||
 | 
			
		||||
	while ((ptr = strsep(&opts, ",")) != NULL) {
 | 
			
		||||
		if (!*ptr)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		token = match_token(ptr, tokens, args);
 | 
			
		||||
		switch (token) {
 | 
			
		||||
		case Opt_udev_path:
 | 
			
		||||
			if (ib_dev->ibd_bd) {
 | 
			
		||||
				printk(KERN_ERR "Unable to set udev_path= while"
 | 
			
		||||
					" ib_dev->ibd_bd exists\n");
 | 
			
		||||
				ret = -EEXIST;
 | 
			
		||||
				goto out;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			ret = snprintf(ib_dev->ibd_udev_path, SE_UDEV_PATH_LEN,
 | 
			
		||||
				"%s", match_strdup(&args[0]));
 | 
			
		||||
			printk(KERN_INFO "IBLOCK: Referencing UDEV path: %s\n",
 | 
			
		||||
					ib_dev->ibd_udev_path);
 | 
			
		||||
			ib_dev->ibd_flags |= IBDF_HAS_UDEV_PATH;
 | 
			
		||||
			break;
 | 
			
		||||
		case Opt_force:
 | 
			
		||||
			match_int(args, &arg);
 | 
			
		||||
			ib_dev->ibd_force = arg;
 | 
			
		||||
			printk(KERN_INFO "IBLOCK: Set force=%d\n",
 | 
			
		||||
				ib_dev->ibd_force);
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
out:
 | 
			
		||||
	kfree(orig);
 | 
			
		||||
	return (!ret) ? count : ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t iblock_check_configfs_dev_params(
 | 
			
		||||
	struct se_hba *hba,
 | 
			
		||||
	struct se_subsystem_dev *se_dev)
 | 
			
		||||
{
 | 
			
		||||
	struct iblock_dev *ibd = se_dev->se_dev_su_ptr;
 | 
			
		||||
 | 
			
		||||
	if (!(ibd->ibd_flags & IBDF_HAS_UDEV_PATH)) {
 | 
			
		||||
		printk(KERN_ERR "Missing udev_path= parameters for IBLOCK\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static ssize_t iblock_show_configfs_dev_params(
 | 
			
		||||
	struct se_hba *hba,
 | 
			
		||||
	struct se_subsystem_dev *se_dev,
 | 
			
		||||
	char *b)
 | 
			
		||||
{
 | 
			
		||||
	struct iblock_dev *ibd = se_dev->se_dev_su_ptr;
 | 
			
		||||
	struct block_device *bd = ibd->ibd_bd;
 | 
			
		||||
	char buf[BDEVNAME_SIZE];
 | 
			
		||||
	ssize_t bl = 0;
 | 
			
		||||
 | 
			
		||||
	if (bd)
 | 
			
		||||
		bl += sprintf(b + bl, "iBlock device: %s",
 | 
			
		||||
				bdevname(bd, buf));
 | 
			
		||||
	if (ibd->ibd_flags & IBDF_HAS_UDEV_PATH) {
 | 
			
		||||
		bl += sprintf(b + bl, "  UDEV PATH: %s\n",
 | 
			
		||||
				ibd->ibd_udev_path);
 | 
			
		||||
	} else
 | 
			
		||||
		bl += sprintf(b + bl, "\n");
 | 
			
		||||
 | 
			
		||||
	bl += sprintf(b + bl, "        ");
 | 
			
		||||
	if (bd) {
 | 
			
		||||
		bl += sprintf(b + bl, "Major: %d Minor: %d  %s\n",
 | 
			
		||||
			ibd->ibd_major, ibd->ibd_minor, (!bd->bd_contains) ?
 | 
			
		||||
			"" : (bd->bd_holder == (struct iblock_dev *)ibd) ?
 | 
			
		||||
			"CLAIMED: IBLOCK" : "CLAIMED: OS");
 | 
			
		||||
	} else {
 | 
			
		||||
		bl += sprintf(b + bl, "Major: %d Minor: %d\n",
 | 
			
		||||
			ibd->ibd_major, ibd->ibd_minor);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return bl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void iblock_bio_destructor(struct bio *bio)
 | 
			
		||||
{
 | 
			
		||||
	struct se_task *task = bio->bi_private;
 | 
			
		||||
	struct iblock_dev *ib_dev = task->se_dev->dev_ptr;
 | 
			
		||||
 | 
			
		||||
	bio_free(bio, ib_dev->ibd_bio_set);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct bio *iblock_get_bio(
 | 
			
		||||
	struct se_task *task,
 | 
			
		||||
	struct iblock_req *ib_req,
 | 
			
		||||
	struct iblock_dev *ib_dev,
 | 
			
		||||
	int *ret,
 | 
			
		||||
	sector_t lba,
 | 
			
		||||
	u32 sg_num)
 | 
			
		||||
{
 | 
			
		||||
	struct bio *bio;
 | 
			
		||||
 | 
			
		||||
	bio = bio_alloc_bioset(GFP_NOIO, sg_num, ib_dev->ibd_bio_set);
 | 
			
		||||
	if (!(bio)) {
 | 
			
		||||
		printk(KERN_ERR "Unable to allocate memory for bio\n");
 | 
			
		||||
		*ret = PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES;
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	DEBUG_IBLOCK("Allocated bio: %p task_sg_num: %u using ibd_bio_set:"
 | 
			
		||||
		" %p\n", bio, task->task_sg_num, ib_dev->ibd_bio_set);
 | 
			
		||||
	DEBUG_IBLOCK("Allocated bio: %p task_size: %u\n", bio, task->task_size);
 | 
			
		||||
 | 
			
		||||
	bio->bi_bdev = ib_dev->ibd_bd;
 | 
			
		||||
	bio->bi_private = (void *) task;
 | 
			
		||||
	bio->bi_destructor = iblock_bio_destructor;
 | 
			
		||||
	bio->bi_end_io = &iblock_bio_done;
 | 
			
		||||
	bio->bi_sector = lba;
 | 
			
		||||
	atomic_inc(&ib_req->ib_bio_cnt);
 | 
			
		||||
 | 
			
		||||
	DEBUG_IBLOCK("Set bio->bi_sector: %llu\n", bio->bi_sector);
 | 
			
		||||
	DEBUG_IBLOCK("Set ib_req->ib_bio_cnt: %d\n",
 | 
			
		||||
			atomic_read(&ib_req->ib_bio_cnt));
 | 
			
		||||
	return bio;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int iblock_map_task_SG(struct se_task *task)
 | 
			
		||||
{
 | 
			
		||||
	struct se_cmd *cmd = task->task_se_cmd;
 | 
			
		||||
	struct se_device *dev = SE_DEV(cmd);
 | 
			
		||||
	struct iblock_dev *ib_dev = task->se_dev->dev_ptr;
 | 
			
		||||
	struct iblock_req *ib_req = IBLOCK_REQ(task);
 | 
			
		||||
	struct bio *bio = NULL, *hbio = NULL, *tbio = NULL;
 | 
			
		||||
	struct scatterlist *sg;
 | 
			
		||||
	int ret = 0;
 | 
			
		||||
	u32 i, sg_num = task->task_sg_num;
 | 
			
		||||
	sector_t block_lba;
 | 
			
		||||
	/*
 | 
			
		||||
	 * Do starting conversion up from non 512-byte blocksize with
 | 
			
		||||
	 * struct se_task SCSI blocksize into Linux/Block 512 units for BIO.
 | 
			
		||||
	 */
 | 
			
		||||
	if (DEV_ATTRIB(dev)->block_size == 4096)
 | 
			
		||||
		block_lba = (task->task_lba << 3);
 | 
			
		||||
	else if (DEV_ATTRIB(dev)->block_size == 2048)
 | 
			
		||||
		block_lba = (task->task_lba << 2);
 | 
			
		||||
	else if (DEV_ATTRIB(dev)->block_size == 1024)
 | 
			
		||||
		block_lba = (task->task_lba << 1);
 | 
			
		||||
	else if (DEV_ATTRIB(dev)->block_size == 512)
 | 
			
		||||
		block_lba = task->task_lba;
 | 
			
		||||
	else {
 | 
			
		||||
		printk(KERN_ERR "Unsupported SCSI -> BLOCK LBA conversion:"
 | 
			
		||||
				" %u\n", DEV_ATTRIB(dev)->block_size);
 | 
			
		||||
		return PYX_TRANSPORT_LU_COMM_FAILURE;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bio = iblock_get_bio(task, ib_req, ib_dev, &ret, block_lba, sg_num);
 | 
			
		||||
	if (!(bio))
 | 
			
		||||
		return ret;
 | 
			
		||||
 | 
			
		||||
	ib_req->ib_bio = bio;
 | 
			
		||||
	hbio = tbio = bio;
 | 
			
		||||
	/*
 | 
			
		||||
	 * Use fs/bio.c:bio_add_pages() to setup the bio_vec maplist
 | 
			
		||||
	 * from TCM struct se_mem -> task->task_sg -> struct scatterlist memory.
 | 
			
		||||
	 */
 | 
			
		||||
	for_each_sg(task->task_sg, sg, task->task_sg_num, i) {
 | 
			
		||||
		DEBUG_IBLOCK("task: %p bio: %p Calling bio_add_page(): page:"
 | 
			
		||||
			" %p len: %u offset: %u\n", task, bio, sg_page(sg),
 | 
			
		||||
				sg->length, sg->offset);
 | 
			
		||||
again:
 | 
			
		||||
		ret = bio_add_page(bio, sg_page(sg), sg->length, sg->offset);
 | 
			
		||||
		if (ret != sg->length) {
 | 
			
		||||
 | 
			
		||||
			DEBUG_IBLOCK("*** Set bio->bi_sector: %llu\n",
 | 
			
		||||
					bio->bi_sector);
 | 
			
		||||
			DEBUG_IBLOCK("** task->task_size: %u\n",
 | 
			
		||||
					task->task_size);
 | 
			
		||||
			DEBUG_IBLOCK("*** bio->bi_max_vecs: %u\n",
 | 
			
		||||
					bio->bi_max_vecs);
 | 
			
		||||
			DEBUG_IBLOCK("*** bio->bi_vcnt: %u\n",
 | 
			
		||||
					bio->bi_vcnt);
 | 
			
		||||
 | 
			
		||||
			bio = iblock_get_bio(task, ib_req, ib_dev, &ret,
 | 
			
		||||
						block_lba, sg_num);
 | 
			
		||||
			if (!(bio))
 | 
			
		||||
				goto fail;
 | 
			
		||||
 | 
			
		||||
			tbio = tbio->bi_next = bio;
 | 
			
		||||
			DEBUG_IBLOCK("-----------------> Added +1 bio: %p to"
 | 
			
		||||
				" list, Going to again\n", bio);
 | 
			
		||||
			goto again;
 | 
			
		||||
		}
 | 
			
		||||
		/* Always in 512 byte units for Linux/Block */
 | 
			
		||||
		block_lba += sg->length >> IBLOCK_LBA_SHIFT;
 | 
			
		||||
		sg_num--;
 | 
			
		||||
		DEBUG_IBLOCK("task: %p bio-add_page() passed!, decremented"
 | 
			
		||||
			" sg_num to %u\n", task, sg_num);
 | 
			
		||||
		DEBUG_IBLOCK("task: %p bio_add_page() passed!, increased lba"
 | 
			
		||||
				" to %llu\n", task, block_lba);
 | 
			
		||||
		DEBUG_IBLOCK("task: %p bio_add_page() passed!, bio->bi_vcnt:"
 | 
			
		||||
				" %u\n", task, bio->bi_vcnt);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
fail:
 | 
			
		||||
	while (hbio) {
 | 
			
		||||
		bio = hbio;
 | 
			
		||||
		hbio = hbio->bi_next;
 | 
			
		||||
		bio->bi_next = NULL;
 | 
			
		||||
		bio_put(bio);
 | 
			
		||||
	}
 | 
			
		||||
	return ret;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static unsigned char *iblock_get_cdb(struct se_task *task)
 | 
			
		||||
{
 | 
			
		||||
	return IBLOCK_REQ(task)->ib_scsi_cdb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static u32 iblock_get_device_rev(struct se_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	return SCSI_SPC_2; /* Returns SPC-3 in Initiator Data */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static u32 iblock_get_device_type(struct se_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	return TYPE_DISK;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static sector_t iblock_get_blocks(struct se_device *dev)
 | 
			
		||||
{
 | 
			
		||||
	struct iblock_dev *ibd = dev->dev_ptr;
 | 
			
		||||
	struct block_device *bd = ibd->ibd_bd;
 | 
			
		||||
	struct request_queue *q = bdev_get_queue(bd);
 | 
			
		||||
 | 
			
		||||
	return iblock_emulate_read_cap_with_block_size(dev, bd, q);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void iblock_bio_done(struct bio *bio, int err)
 | 
			
		||||
{
 | 
			
		||||
	struct se_task *task = bio->bi_private;
 | 
			
		||||
	struct iblock_req *ibr = IBLOCK_REQ(task);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Set -EIO if !BIO_UPTODATE and the passed is still err=0
 | 
			
		||||
	 */
 | 
			
		||||
	if (!(test_bit(BIO_UPTODATE, &bio->bi_flags)) && !(err))
 | 
			
		||||
		err = -EIO;
 | 
			
		||||
 | 
			
		||||
	if (err != 0) {
 | 
			
		||||
		printk(KERN_ERR "test_bit(BIO_UPTODATE) failed for bio: %p,"
 | 
			
		||||
			" err: %d\n", bio, err);
 | 
			
		||||
		/*
 | 
			
		||||
		 * Bump the ib_bio_err_cnt and release bio.
 | 
			
		||||
		 */
 | 
			
		||||
		atomic_inc(&ibr->ib_bio_err_cnt);
 | 
			
		||||
		smp_mb__after_atomic_inc();
 | 
			
		||||
		bio_put(bio);
 | 
			
		||||
		/*
 | 
			
		||||
		 * Wait to complete the task until the last bio as completed.
 | 
			
		||||
		 */
 | 
			
		||||
		if (!(atomic_dec_and_test(&ibr->ib_bio_cnt)))
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		ibr->ib_bio = NULL;
 | 
			
		||||
		transport_complete_task(task, 0);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	DEBUG_IBLOCK("done[%p] bio: %p task_lba: %llu bio_lba: %llu err=%d\n",
 | 
			
		||||
		task, bio, task->task_lba, bio->bi_sector, err);
 | 
			
		||||
	/*
 | 
			
		||||
	 * bio_put() will call iblock_bio_destructor() to release the bio back
 | 
			
		||||
	 * to ibr->ib_bio_set.
 | 
			
		||||
	 */
 | 
			
		||||
	bio_put(bio);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Wait to complete the task until the last bio as completed.
 | 
			
		||||
	 */
 | 
			
		||||
	if (!(atomic_dec_and_test(&ibr->ib_bio_cnt)))
 | 
			
		||||
		return;
 | 
			
		||||
	/*
 | 
			
		||||
	 * Return GOOD status for task if zero ib_bio_err_cnt exists.
 | 
			
		||||
	 */
 | 
			
		||||
	ibr->ib_bio = NULL;
 | 
			
		||||
	transport_complete_task(task, (!atomic_read(&ibr->ib_bio_err_cnt)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct se_subsystem_api iblock_template = {
 | 
			
		||||
	.name			= "iblock",
 | 
			
		||||
	.owner			= THIS_MODULE,
 | 
			
		||||
	.transport_type		= TRANSPORT_PLUGIN_VHBA_PDEV,
 | 
			
		||||
	.map_task_SG		= iblock_map_task_SG,
 | 
			
		||||
	.attach_hba		= iblock_attach_hba,
 | 
			
		||||
	.detach_hba		= iblock_detach_hba,
 | 
			
		||||
	.allocate_virtdevice	= iblock_allocate_virtdevice,
 | 
			
		||||
	.create_virtdevice	= iblock_create_virtdevice,
 | 
			
		||||
	.free_device		= iblock_free_device,
 | 
			
		||||
	.dpo_emulated		= iblock_emulated_dpo,
 | 
			
		||||
	.fua_write_emulated	= iblock_emulated_fua_write,
 | 
			
		||||
	.fua_read_emulated	= iblock_emulated_fua_read,
 | 
			
		||||
	.write_cache_emulated	= iblock_emulated_write_cache,
 | 
			
		||||
	.alloc_task		= iblock_alloc_task,
 | 
			
		||||
	.do_task		= iblock_do_task,
 | 
			
		||||
	.do_discard		= iblock_do_discard,
 | 
			
		||||
	.do_sync_cache		= iblock_emulate_sync_cache,
 | 
			
		||||
	.free_task		= iblock_free_task,
 | 
			
		||||
	.check_configfs_dev_params = iblock_check_configfs_dev_params,
 | 
			
		||||
	.set_configfs_dev_params = iblock_set_configfs_dev_params,
 | 
			
		||||
	.show_configfs_dev_params = iblock_show_configfs_dev_params,
 | 
			
		||||
	.get_cdb		= iblock_get_cdb,
 | 
			
		||||
	.get_device_rev		= iblock_get_device_rev,
 | 
			
		||||
	.get_device_type	= iblock_get_device_type,
 | 
			
		||||
	.get_blocks		= iblock_get_blocks,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static int __init iblock_module_init(void)
 | 
			
		||||
{
 | 
			
		||||
	return transport_subsystem_register(&iblock_template);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void iblock_module_exit(void)
 | 
			
		||||
{
 | 
			
		||||
	transport_subsystem_release(&iblock_template);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MODULE_DESCRIPTION("TCM IBLOCK subsystem plugin");
 | 
			
		||||
MODULE_AUTHOR("nab@Linux-iSCSI.org");
 | 
			
		||||
MODULE_LICENSE("GPL");
 | 
			
		||||
 | 
			
		||||
module_init(iblock_module_init);
 | 
			
		||||
module_exit(iblock_module_exit);
 | 
			
		||||
							
								
								
									
										40
									
								
								drivers/target/target_core_iblock.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								drivers/target/target_core_iblock.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,40 @@
 | 
			
		|||
#ifndef TARGET_CORE_IBLOCK_H
 | 
			
		||||
#define TARGET_CORE_IBLOCK_H
 | 
			
		||||
 | 
			
		||||
#define IBLOCK_VERSION		"4.0"
 | 
			
		||||
 | 
			
		||||
#define IBLOCK_HBA_QUEUE_DEPTH	512
 | 
			
		||||
#define IBLOCK_DEVICE_QUEUE_DEPTH	32
 | 
			
		||||
#define IBLOCK_MAX_DEVICE_QUEUE_DEPTH	128
 | 
			
		||||
#define IBLOCK_MAX_CDBS		16
 | 
			
		||||
#define IBLOCK_LBA_SHIFT	9
 | 
			
		||||
 | 
			
		||||
struct iblock_req {
 | 
			
		||||
	struct se_task ib_task;
 | 
			
		||||
	unsigned char ib_scsi_cdb[TCM_MAX_COMMAND_SIZE];
 | 
			
		||||
	atomic_t ib_bio_cnt;
 | 
			
		||||
	atomic_t ib_bio_err_cnt;
 | 
			
		||||
	struct bio *ib_bio;
 | 
			
		||||
	struct iblock_dev *ib_dev;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
#define IBDF_HAS_UDEV_PATH		0x01
 | 
			
		||||
#define IBDF_HAS_FORCE			0x02
 | 
			
		||||
 | 
			
		||||
struct iblock_dev {
 | 
			
		||||
	unsigned char ibd_udev_path[SE_UDEV_PATH_LEN];
 | 
			
		||||
	int	ibd_force;
 | 
			
		||||
	int	ibd_major;
 | 
			
		||||
	int	ibd_minor;
 | 
			
		||||
	u32	ibd_depth;
 | 
			
		||||
	u32	ibd_flags;
 | 
			
		||||
	struct bio_set	*ibd_bio_set;
 | 
			
		||||
	struct block_device *ibd_bd;
 | 
			
		||||
	struct iblock_hba *ibd_host;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct iblock_hba {
 | 
			
		||||
	int		iblock_host_id;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
#endif /* TARGET_CORE_IBLOCK_H */
 | 
			
		||||
							
								
								
									
										1078
									
								
								drivers/target/target_core_mib.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1078
									
								
								drivers/target/target_core_mib.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										28
									
								
								drivers/target/target_core_mib.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								drivers/target/target_core_mib.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
#ifndef TARGET_CORE_MIB_H
 | 
			
		||||
#define TARGET_CORE_MIB_H
 | 
			
		||||
 | 
			
		||||
typedef enum {
 | 
			
		||||
	SCSI_INST_INDEX,
 | 
			
		||||
	SCSI_DEVICE_INDEX,
 | 
			
		||||
	SCSI_AUTH_INTR_INDEX,
 | 
			
		||||
	SCSI_INDEX_TYPE_MAX
 | 
			
		||||
} scsi_index_t;
 | 
			
		||||
 | 
			
		||||
struct scsi_index_table {
 | 
			
		||||
	spinlock_t	lock;
 | 
			
		||||
	u32 		scsi_mib_index[SCSI_INDEX_TYPE_MAX];
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
/* SCSI Port stats */
 | 
			
		||||
struct scsi_port_stats {
 | 
			
		||||
	u64	cmd_pdus;
 | 
			
		||||
	u64	tx_data_octets;
 | 
			
		||||
	u64	rx_data_octets;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
extern int init_scsi_target_mib(void);
 | 
			
		||||
extern void remove_scsi_target_mib(void);
 | 
			
		||||
extern void init_scsi_index_table(void);
 | 
			
		||||
extern u32 scsi_get_new_index(scsi_index_t);
 | 
			
		||||
 | 
			
		||||
#endif   /*** TARGET_CORE_MIB_H ***/
 | 
			
		||||
							
								
								
									
										4252
									
								
								drivers/target/target_core_pr.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4252
									
								
								drivers/target/target_core_pr.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										67
									
								
								drivers/target/target_core_pr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								drivers/target/target_core_pr.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,67 @@
 | 
			
		|||
#ifndef TARGET_CORE_PR_H
 | 
			
		||||
#define TARGET_CORE_PR_H
 | 
			
		||||
/*
 | 
			
		||||
 * PERSISTENT_RESERVE_OUT service action codes
 | 
			
		||||
 *
 | 
			
		||||
 * spc4r17 section 6.14.2 Table 171
 | 
			
		||||
 */
 | 
			
		||||
#define PRO_REGISTER				0x00
 | 
			
		||||
#define PRO_RESERVE				0x01
 | 
			
		||||
#define PRO_RELEASE				0x02
 | 
			
		||||
#define PRO_CLEAR				0x03
 | 
			
		||||
#define PRO_PREEMPT				0x04
 | 
			
		||||
#define PRO_PREEMPT_AND_ABORT			0x05
 | 
			
		||||
#define PRO_REGISTER_AND_IGNORE_EXISTING_KEY	0x06
 | 
			
		||||
#define PRO_REGISTER_AND_MOVE			0x07
 | 
			
		||||
/*
 | 
			
		||||
 * PERSISTENT_RESERVE_IN service action codes
 | 
			
		||||
 *
 | 
			
		||||
 * spc4r17 section 6.13.1 Table 159
 | 
			
		||||
 */
 | 
			
		||||
#define PRI_READ_KEYS				0x00
 | 
			
		||||
#define PRI_READ_RESERVATION			0x01
 | 
			
		||||
#define PRI_REPORT_CAPABILITIES			0x02
 | 
			
		||||
#define PRI_READ_FULL_STATUS			0x03
 | 
			
		||||
/*
 | 
			
		||||
 * PERSISTENT_RESERVE_ SCOPE field
 | 
			
		||||
 *
 | 
			
		||||
 * spc4r17 section 6.13.3.3 Table 163
 | 
			
		||||
 */
 | 
			
		||||
#define PR_SCOPE_LU_SCOPE			0x00
 | 
			
		||||
/*
 | 
			
		||||
 * PERSISTENT_RESERVE_* TYPE field
 | 
			
		||||
 *
 | 
			
		||||
 * spc4r17 section 6.13.3.4 Table 164
 | 
			
		||||
 */
 | 
			
		||||
#define PR_TYPE_WRITE_EXCLUSIVE			0x01
 | 
			
		||||
#define PR_TYPE_EXCLUSIVE_ACCESS		0x03
 | 
			
		||||
#define PR_TYPE_WRITE_EXCLUSIVE_REGONLY		0x05
 | 
			
		||||
#define PR_TYPE_EXCLUSIVE_ACCESS_REGONLY	0x06
 | 
			
		||||
#define PR_TYPE_WRITE_EXCLUSIVE_ALLREG		0x07
 | 
			
		||||
#define PR_TYPE_EXCLUSIVE_ACCESS_ALLREG		0x08
 | 
			
		||||
 | 
			
		||||
#define PR_APTPL_MAX_IPORT_LEN			256
 | 
			
		||||
#define PR_APTPL_MAX_TPORT_LEN			256
 | 
			
		||||
 | 
			
		||||
extern struct kmem_cache *t10_pr_reg_cache;
 | 
			
		||||
 | 
			
		||||
extern int core_pr_dump_initiator_port(struct t10_pr_registration *,
 | 
			
		||||
			char *, u32);
 | 
			
		||||
extern int core_scsi2_emulate_crh(struct se_cmd *);
 | 
			
		||||
extern int core_scsi3_alloc_aptpl_registration(
 | 
			
		||||
			struct t10_reservation_template *, u64,
 | 
			
		||||
			unsigned char *, unsigned char *, u32,
 | 
			
		||||
			unsigned char *, u16, u32, int, int, u8);
 | 
			
		||||
extern int core_scsi3_check_aptpl_registration(struct se_device *,
 | 
			
		||||
			struct se_portal_group *, struct se_lun *,
 | 
			
		||||
			struct se_lun_acl *);
 | 
			
		||||
extern void core_scsi3_free_pr_reg_from_nacl(struct se_device *,
 | 
			
		||||
					     struct se_node_acl *);
 | 
			
		||||
extern void core_scsi3_free_all_registrations(struct se_device *);
 | 
			
		||||
extern unsigned char *core_scsi3_pr_dump_type(int);
 | 
			
		||||
extern int core_scsi3_check_cdb_abort_and_preempt(struct list_head *,
 | 
			
		||||
						  struct se_cmd *);
 | 
			
		||||
extern int core_scsi3_emulate_pr(struct se_cmd *);
 | 
			
		||||
extern int core_setup_reservations(struct se_device *, int);
 | 
			
		||||
 | 
			
		||||
#endif /* TARGET_CORE_PR_H */
 | 
			
		||||
							
								
								
									
										1470
									
								
								drivers/target/target_core_pscsi.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1470
									
								
								drivers/target/target_core_pscsi.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										65
									
								
								drivers/target/target_core_pscsi.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								drivers/target/target_core_pscsi.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,65 @@
 | 
			
		|||
#ifndef TARGET_CORE_PSCSI_H
 | 
			
		||||
#define TARGET_CORE_PSCSI_H
 | 
			
		||||
 | 
			
		||||
#define PSCSI_VERSION		"v4.0"
 | 
			
		||||
#define PSCSI_VIRTUAL_HBA_DEPTH	2048
 | 
			
		||||
 | 
			
		||||
/* used in pscsi_find_alloc_len() */
 | 
			
		||||
#ifndef INQUIRY_DATA_SIZE
 | 
			
		||||
#define INQUIRY_DATA_SIZE	0x24
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* used in pscsi_add_device_to_list() */
 | 
			
		||||
#define PSCSI_DEFAULT_QUEUEDEPTH	1
 | 
			
		||||
 | 
			
		||||
#define PS_RETRY		5
 | 
			
		||||
#define PS_TIMEOUT_DISK		(15*HZ)
 | 
			
		||||
#define PS_TIMEOUT_OTHER	(500*HZ)
 | 
			
		||||
 | 
			
		||||
#include <linux/device.h>
 | 
			
		||||
#include <scsi/scsi_driver.h>
 | 
			
		||||
#include <scsi/scsi_device.h>
 | 
			
		||||
#include <linux/kref.h>
 | 
			
		||||
#include <linux/kobject.h>
 | 
			
		||||
 | 
			
		||||
struct pscsi_plugin_task {
 | 
			
		||||
	struct se_task pscsi_task;
 | 
			
		||||
	unsigned char *pscsi_cdb;
 | 
			
		||||
	unsigned char __pscsi_cdb[TCM_MAX_COMMAND_SIZE];
 | 
			
		||||
	unsigned char pscsi_sense[SCSI_SENSE_BUFFERSIZE];
 | 
			
		||||
	int	pscsi_direction;
 | 
			
		||||
	int	pscsi_result;
 | 
			
		||||
	u32	pscsi_resid;
 | 
			
		||||
	struct request *pscsi_req;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
#define PDF_HAS_CHANNEL_ID	0x01
 | 
			
		||||
#define PDF_HAS_TARGET_ID	0x02
 | 
			
		||||
#define PDF_HAS_LUN_ID		0x04
 | 
			
		||||
#define PDF_HAS_VPD_UNIT_SERIAL 0x08
 | 
			
		||||
#define PDF_HAS_VPD_DEV_IDENT	0x10
 | 
			
		||||
#define PDF_HAS_VIRT_HOST_ID	0x20
 | 
			
		||||
 | 
			
		||||
struct pscsi_dev_virt {
 | 
			
		||||
	int	pdv_flags;
 | 
			
		||||
	int	pdv_host_id;
 | 
			
		||||
	int	pdv_channel_id;
 | 
			
		||||
	int	pdv_target_id;
 | 
			
		||||
	int	pdv_lun_id;
 | 
			
		||||
	struct block_device *pdv_bd;
 | 
			
		||||
	struct scsi_device *pdv_sd;
 | 
			
		||||
	struct se_hba *pdv_se_hba;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
typedef enum phv_modes {
 | 
			
		||||
	PHV_VIRUTAL_HOST_ID,
 | 
			
		||||
	PHV_LLD_SCSI_HOST_NO
 | 
			
		||||
} phv_modes_t;
 | 
			
		||||
 | 
			
		||||
struct pscsi_hba_virt {
 | 
			
		||||
	int			phv_host_id;
 | 
			
		||||
	phv_modes_t		phv_mode;
 | 
			
		||||
	struct Scsi_Host	*phv_lld_host;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
#endif   /*** TARGET_CORE_PSCSI_H ***/
 | 
			
		||||
							
								
								
									
										1091
									
								
								drivers/target/target_core_rd.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1091
									
								
								drivers/target/target_core_rd.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										73
									
								
								drivers/target/target_core_rd.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										73
									
								
								drivers/target/target_core_rd.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,73 @@
 | 
			
		|||
#ifndef TARGET_CORE_RD_H
 | 
			
		||||
#define TARGET_CORE_RD_H
 | 
			
		||||
 | 
			
		||||
#define RD_HBA_VERSION		"v4.0"
 | 
			
		||||
#define RD_DR_VERSION		"4.0"
 | 
			
		||||
#define RD_MCP_VERSION		"4.0"
 | 
			
		||||
 | 
			
		||||
/* Largest piece of memory kmalloc can allocate */
 | 
			
		||||
#define RD_MAX_ALLOCATION_SIZE	65536
 | 
			
		||||
/* Maximum queuedepth for the Ramdisk HBA */
 | 
			
		||||
#define RD_HBA_QUEUE_DEPTH	256
 | 
			
		||||
#define RD_DEVICE_QUEUE_DEPTH	32
 | 
			
		||||
#define RD_MAX_DEVICE_QUEUE_DEPTH 128
 | 
			
		||||
#define RD_BLOCKSIZE		512
 | 
			
		||||
#define RD_MAX_SECTORS		1024
 | 
			
		||||
 | 
			
		||||
extern struct kmem_cache *se_mem_cache;
 | 
			
		||||
 | 
			
		||||
/* Used in target_core_init_configfs() for virtual LUN 0 access */
 | 
			
		||||
int __init rd_module_init(void);
 | 
			
		||||
void rd_module_exit(void);
 | 
			
		||||
 | 
			
		||||
#define RRF_EMULATE_CDB		0x01
 | 
			
		||||
#define RRF_GOT_LBA		0x02
 | 
			
		||||
 | 
			
		||||
struct rd_request {
 | 
			
		||||
	struct se_task	rd_task;
 | 
			
		||||
 | 
			
		||||
	/* SCSI CDB from iSCSI Command PDU */
 | 
			
		||||
	unsigned char	rd_scsi_cdb[TCM_MAX_COMMAND_SIZE];
 | 
			
		||||
	/* Offset from start of page */
 | 
			
		||||
	u32		rd_offset;
 | 
			
		||||
	/* Starting page in Ramdisk for request */
 | 
			
		||||
	u32		rd_page;
 | 
			
		||||
	/* Total number of pages needed for request */
 | 
			
		||||
	u32		rd_page_count;
 | 
			
		||||
	/* Scatterlist count */
 | 
			
		||||
	u32		rd_size;
 | 
			
		||||
	/* Ramdisk device */
 | 
			
		||||
	struct rd_dev	*rd_dev;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct rd_dev_sg_table {
 | 
			
		||||
	u32		page_start_offset;
 | 
			
		||||
	u32		page_end_offset;
 | 
			
		||||
	u32		rd_sg_count;
 | 
			
		||||
	struct scatterlist *sg_table;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
#define RDF_HAS_PAGE_COUNT	0x01
 | 
			
		||||
 | 
			
		||||
struct rd_dev {
 | 
			
		||||
	int		rd_direct;
 | 
			
		||||
	u32		rd_flags;
 | 
			
		||||
	/* Unique Ramdisk Device ID in Ramdisk HBA */
 | 
			
		||||
	u32		rd_dev_id;
 | 
			
		||||
	/* Total page count for ramdisk device */
 | 
			
		||||
	u32		rd_page_count;
 | 
			
		||||
	/* Number of SG tables in sg_table_array */
 | 
			
		||||
	u32		sg_table_count;
 | 
			
		||||
	u32		rd_queue_depth;
 | 
			
		||||
	/* Array of rd_dev_sg_table_t containing scatterlists */
 | 
			
		||||
	struct rd_dev_sg_table *sg_table_array;
 | 
			
		||||
	/* Ramdisk HBA device is connected to */
 | 
			
		||||
	struct rd_host *rd_host;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct rd_host {
 | 
			
		||||
	u32		rd_host_dev_id_count;
 | 
			
		||||
	u32		rd_host_id;		/* Unique Ramdisk Host ID */
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
#endif /* TARGET_CORE_RD_H */
 | 
			
		||||
							
								
								
									
										105
									
								
								drivers/target/target_core_scdb.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								drivers/target/target_core_scdb.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,105 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Filename:  target_core_scdb.c
 | 
			
		||||
 *
 | 
			
		||||
 * This file contains the generic target engine Split CDB related functions.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2004-2005 PyX Technologies, Inc.
 | 
			
		||||
 * Copyright (c) 2005, 2006, 2007 SBE, Inc.
 | 
			
		||||
 * 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 <scsi/scsi.h>
 | 
			
		||||
#include <asm/unaligned.h>
 | 
			
		||||
 | 
			
		||||
#include <target/target_core_base.h>
 | 
			
		||||
#include <target/target_core_transport.h>
 | 
			
		||||
 | 
			
		||||
#include "target_core_scdb.h"
 | 
			
		||||
 | 
			
		||||
/*	split_cdb_XX_6():
 | 
			
		||||
 *
 | 
			
		||||
 *      21-bit LBA w/ 8-bit SECTORS
 | 
			
		||||
 */
 | 
			
		||||
void split_cdb_XX_6(
 | 
			
		||||
	unsigned long long lba,
 | 
			
		||||
	u32 *sectors,
 | 
			
		||||
	unsigned char *cdb)
 | 
			
		||||
{
 | 
			
		||||
	cdb[1] = (lba >> 16) & 0x1f;
 | 
			
		||||
	cdb[2] = (lba >> 8) & 0xff;
 | 
			
		||||
	cdb[3] = lba & 0xff;
 | 
			
		||||
	cdb[4] = *sectors & 0xff;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*	split_cdb_XX_10():
 | 
			
		||||
 *
 | 
			
		||||
 *	32-bit LBA w/ 16-bit SECTORS
 | 
			
		||||
 */
 | 
			
		||||
void split_cdb_XX_10(
 | 
			
		||||
	unsigned long long lba,
 | 
			
		||||
	u32 *sectors,
 | 
			
		||||
	unsigned char *cdb)
 | 
			
		||||
{
 | 
			
		||||
	put_unaligned_be32(lba, &cdb[2]);
 | 
			
		||||
	put_unaligned_be16(*sectors, &cdb[7]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*	split_cdb_XX_12():
 | 
			
		||||
 *
 | 
			
		||||
 *	32-bit LBA w/ 32-bit SECTORS
 | 
			
		||||
 */
 | 
			
		||||
void split_cdb_XX_12(
 | 
			
		||||
	unsigned long long lba,
 | 
			
		||||
	u32 *sectors,
 | 
			
		||||
	unsigned char *cdb)
 | 
			
		||||
{
 | 
			
		||||
	put_unaligned_be32(lba, &cdb[2]);
 | 
			
		||||
	put_unaligned_be32(*sectors, &cdb[6]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*	split_cdb_XX_16():
 | 
			
		||||
 *
 | 
			
		||||
 *	64-bit LBA w/ 32-bit SECTORS
 | 
			
		||||
 */
 | 
			
		||||
void split_cdb_XX_16(
 | 
			
		||||
	unsigned long long lba,
 | 
			
		||||
	u32 *sectors,
 | 
			
		||||
	unsigned char *cdb)
 | 
			
		||||
{
 | 
			
		||||
	put_unaligned_be64(lba, &cdb[2]);
 | 
			
		||||
	put_unaligned_be32(*sectors, &cdb[10]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 *	split_cdb_XX_32():
 | 
			
		||||
 *
 | 
			
		||||
 * 	64-bit LBA w/ 32-bit SECTORS such as READ_32, WRITE_32 and emulated XDWRITEREAD_32
 | 
			
		||||
 */
 | 
			
		||||
void split_cdb_XX_32(
 | 
			
		||||
	unsigned long long lba,
 | 
			
		||||
	u32 *sectors,
 | 
			
		||||
	unsigned char *cdb)
 | 
			
		||||
{
 | 
			
		||||
	put_unaligned_be64(lba, &cdb[12]);
 | 
			
		||||
	put_unaligned_be32(*sectors, &cdb[28]);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										10
									
								
								drivers/target/target_core_scdb.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								drivers/target/target_core_scdb.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,10 @@
 | 
			
		|||
#ifndef TARGET_CORE_SCDB_H
 | 
			
		||||
#define TARGET_CORE_SCDB_H
 | 
			
		||||
 | 
			
		||||
extern void split_cdb_XX_6(unsigned long long, u32 *, unsigned char *);
 | 
			
		||||
extern void split_cdb_XX_10(unsigned long long, u32 *, unsigned char *);
 | 
			
		||||
extern void split_cdb_XX_12(unsigned long long, u32 *, unsigned char *);
 | 
			
		||||
extern void split_cdb_XX_16(unsigned long long, u32 *, unsigned char *);
 | 
			
		||||
extern void split_cdb_XX_32(unsigned long long, u32 *, unsigned char *);
 | 
			
		||||
 | 
			
		||||
#endif /* TARGET_CORE_SCDB_H */
 | 
			
		||||
							
								
								
									
										404
									
								
								drivers/target/target_core_tmr.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										404
									
								
								drivers/target/target_core_tmr.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,404 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Filename:  target_core_tmr.c
 | 
			
		||||
 *
 | 
			
		||||
 * This file contains SPC-3 task management infrastructure
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2009,2010 Rising Tide Systems
 | 
			
		||||
 * Copyright (c) 2009,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/version.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/spinlock.h>
 | 
			
		||||
#include <linux/list.h>
 | 
			
		||||
#include <scsi/scsi.h>
 | 
			
		||||
#include <scsi/scsi_cmnd.h>
 | 
			
		||||
 | 
			
		||||
#include <target/target_core_base.h>
 | 
			
		||||
#include <target/target_core_device.h>
 | 
			
		||||
#include <target/target_core_tmr.h>
 | 
			
		||||
#include <target/target_core_transport.h>
 | 
			
		||||
#include <target/target_core_fabric_ops.h>
 | 
			
		||||
#include <target/target_core_configfs.h>
 | 
			
		||||
 | 
			
		||||
#include "target_core_alua.h"
 | 
			
		||||
#include "target_core_pr.h"
 | 
			
		||||
 | 
			
		||||
#define DEBUG_LUN_RESET
 | 
			
		||||
#ifdef DEBUG_LUN_RESET
 | 
			
		||||
#define DEBUG_LR(x...) printk(KERN_INFO x)
 | 
			
		||||
#else
 | 
			
		||||
#define DEBUG_LR(x...)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
struct se_tmr_req *core_tmr_alloc_req(
 | 
			
		||||
	struct se_cmd *se_cmd,
 | 
			
		||||
	void *fabric_tmr_ptr,
 | 
			
		||||
	u8 function)
 | 
			
		||||
{
 | 
			
		||||
	struct se_tmr_req *tmr;
 | 
			
		||||
 | 
			
		||||
	tmr = kmem_cache_zalloc(se_tmr_req_cache, GFP_KERNEL);
 | 
			
		||||
	if (!(tmr)) {
 | 
			
		||||
		printk(KERN_ERR "Unable to allocate struct se_tmr_req\n");
 | 
			
		||||
		return ERR_PTR(-ENOMEM);
 | 
			
		||||
	}
 | 
			
		||||
	tmr->task_cmd = se_cmd;
 | 
			
		||||
	tmr->fabric_tmr_ptr = fabric_tmr_ptr;
 | 
			
		||||
	tmr->function = function;
 | 
			
		||||
	INIT_LIST_HEAD(&tmr->tmr_list);
 | 
			
		||||
 | 
			
		||||
	return tmr;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(core_tmr_alloc_req);
 | 
			
		||||
 | 
			
		||||
void core_tmr_release_req(
 | 
			
		||||
	struct se_tmr_req *tmr)
 | 
			
		||||
{
 | 
			
		||||
	struct se_device *dev = tmr->tmr_dev;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&dev->se_tmr_lock);
 | 
			
		||||
	list_del(&tmr->tmr_list);
 | 
			
		||||
	kmem_cache_free(se_tmr_req_cache, tmr);
 | 
			
		||||
	spin_unlock(&dev->se_tmr_lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void core_tmr_handle_tas_abort(
 | 
			
		||||
	struct se_node_acl *tmr_nacl,
 | 
			
		||||
	struct se_cmd *cmd,
 | 
			
		||||
	int tas,
 | 
			
		||||
	int fe_count)
 | 
			
		||||
{
 | 
			
		||||
	if (!(fe_count)) {
 | 
			
		||||
		transport_cmd_finish_abort(cmd, 1);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	/*
 | 
			
		||||
	 * TASK ABORTED status (TAS) bit support
 | 
			
		||||
	*/
 | 
			
		||||
	if (((tmr_nacl != NULL) &&
 | 
			
		||||
	     (tmr_nacl == cmd->se_sess->se_node_acl)) || tas)
 | 
			
		||||
		transport_send_task_abort(cmd);
 | 
			
		||||
 | 
			
		||||
	transport_cmd_finish_abort(cmd, 0);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int core_tmr_lun_reset(
 | 
			
		||||
	struct se_device *dev,
 | 
			
		||||
	struct se_tmr_req *tmr,
 | 
			
		||||
	struct list_head *preempt_and_abort_list,
 | 
			
		||||
	struct se_cmd *prout_cmd)
 | 
			
		||||
{
 | 
			
		||||
	struct se_cmd *cmd;
 | 
			
		||||
	struct se_queue_req *qr, *qr_tmp;
 | 
			
		||||
	struct se_node_acl *tmr_nacl = NULL;
 | 
			
		||||
	struct se_portal_group *tmr_tpg = NULL;
 | 
			
		||||
	struct se_queue_obj *qobj = dev->dev_queue_obj;
 | 
			
		||||
	struct se_tmr_req *tmr_p, *tmr_pp;
 | 
			
		||||
	struct se_task *task, *task_tmp;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	int fe_count, state, tas;
 | 
			
		||||
	/*
 | 
			
		||||
	 * TASK_ABORTED status bit, this is configurable via ConfigFS
 | 
			
		||||
	 * struct se_device attributes.  spc4r17 section 7.4.6 Control mode page
 | 
			
		||||
	 *
 | 
			
		||||
	 * A task aborted status (TAS) bit set to zero specifies that aborted
 | 
			
		||||
	 * tasks shall be terminated by the device server without any response
 | 
			
		||||
	 * to the application client. A TAS bit set to one specifies that tasks
 | 
			
		||||
	 * aborted by the actions of an I_T nexus other than the I_T nexus on
 | 
			
		||||
	 * which the command was received shall be completed with TASK ABORTED
 | 
			
		||||
	 * status (see SAM-4).
 | 
			
		||||
	 */
 | 
			
		||||
	tas = DEV_ATTRIB(dev)->emulate_tas;
 | 
			
		||||
	/*
 | 
			
		||||
	 * Determine if this se_tmr is coming from a $FABRIC_MOD
 | 
			
		||||
	 * or struct se_device passthrough..
 | 
			
		||||
	 */
 | 
			
		||||
	if (tmr && tmr->task_cmd && tmr->task_cmd->se_sess) {
 | 
			
		||||
		tmr_nacl = tmr->task_cmd->se_sess->se_node_acl;
 | 
			
		||||
		tmr_tpg = tmr->task_cmd->se_sess->se_tpg;
 | 
			
		||||
		if (tmr_nacl && tmr_tpg) {
 | 
			
		||||
			DEBUG_LR("LUN_RESET: TMR caller fabric: %s"
 | 
			
		||||
				" initiator port %s\n",
 | 
			
		||||
				TPG_TFO(tmr_tpg)->get_fabric_name(),
 | 
			
		||||
				tmr_nacl->initiatorname);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	DEBUG_LR("LUN_RESET: %s starting for [%s], tas: %d\n",
 | 
			
		||||
		(preempt_and_abort_list) ? "Preempt" : "TMR",
 | 
			
		||||
		TRANSPORT(dev)->name, tas);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Release all pending and outgoing TMRs aside from the received
 | 
			
		||||
	 * LUN_RESET tmr..
 | 
			
		||||
	 */
 | 
			
		||||
	spin_lock(&dev->se_tmr_lock);
 | 
			
		||||
	list_for_each_entry_safe(tmr_p, tmr_pp, &dev->dev_tmr_list, tmr_list) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Allow the received TMR to return with FUNCTION_COMPLETE.
 | 
			
		||||
		 */
 | 
			
		||||
		if (tmr && (tmr_p == tmr))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		cmd = tmr_p->task_cmd;
 | 
			
		||||
		if (!(cmd)) {
 | 
			
		||||
			printk(KERN_ERR "Unable to locate struct se_cmd for TMR\n");
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		/*
 | 
			
		||||
		 * If this function was called with a valid pr_res_key
 | 
			
		||||
		 * parameter (eg: for PROUT PREEMPT_AND_ABORT service action
 | 
			
		||||
		 * skip non regisration key matching TMRs.
 | 
			
		||||
		 */
 | 
			
		||||
		if ((preempt_and_abort_list != NULL) &&
 | 
			
		||||
		    (core_scsi3_check_cdb_abort_and_preempt(
 | 
			
		||||
					preempt_and_abort_list, cmd) != 0))
 | 
			
		||||
			continue;
 | 
			
		||||
		spin_unlock(&dev->se_tmr_lock);
 | 
			
		||||
 | 
			
		||||
		spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
 | 
			
		||||
		if (!(atomic_read(&T_TASK(cmd)->t_transport_active))) {
 | 
			
		||||
			spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
 | 
			
		||||
			spin_lock(&dev->se_tmr_lock);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if (cmd->t_state == TRANSPORT_ISTATE_PROCESSING) {
 | 
			
		||||
			spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
 | 
			
		||||
			spin_lock(&dev->se_tmr_lock);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		DEBUG_LR("LUN_RESET: %s releasing TMR %p Function: 0x%02x,"
 | 
			
		||||
			" Response: 0x%02x, t_state: %d\n",
 | 
			
		||||
			(preempt_and_abort_list) ? "Preempt" : "", tmr_p,
 | 
			
		||||
			tmr_p->function, tmr_p->response, cmd->t_state);
 | 
			
		||||
		spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
 | 
			
		||||
 | 
			
		||||
		transport_cmd_finish_abort_tmr(cmd);
 | 
			
		||||
		spin_lock(&dev->se_tmr_lock);
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock(&dev->se_tmr_lock);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Complete outstanding struct se_task CDBs with TASK_ABORTED SAM status.
 | 
			
		||||
	 * This is following sam4r17, section 5.6 Aborting commands, Table 38
 | 
			
		||||
	 * for TMR LUN_RESET:
 | 
			
		||||
	 *
 | 
			
		||||
	 * a) "Yes" indicates that each command that is aborted on an I_T nexus
 | 
			
		||||
	 * other than the one that caused the SCSI device condition is
 | 
			
		||||
	 * completed with TASK ABORTED status, if the TAS bit is set to one in
 | 
			
		||||
	 * the Control mode page (see SPC-4). "No" indicates that no status is
 | 
			
		||||
	 * returned for aborted commands.
 | 
			
		||||
	 *
 | 
			
		||||
	 * d) If the logical unit reset is caused by a particular I_T nexus
 | 
			
		||||
	 * (e.g., by a LOGICAL UNIT RESET task management function), then "yes"
 | 
			
		||||
	 * (TASK_ABORTED status) applies.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Otherwise (e.g., if triggered by a hard reset), "no"
 | 
			
		||||
	 * (no TASK_ABORTED SAM status) applies.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Note that this seems to be independent of TAS (Task Aborted Status)
 | 
			
		||||
	 * in the Control Mode Page.
 | 
			
		||||
	 */
 | 
			
		||||
	spin_lock_irqsave(&dev->execute_task_lock, flags);
 | 
			
		||||
	list_for_each_entry_safe(task, task_tmp, &dev->state_task_list,
 | 
			
		||||
				t_state_list) {
 | 
			
		||||
		if (!(TASK_CMD(task))) {
 | 
			
		||||
			printk(KERN_ERR "TASK_CMD(task) is NULL!\n");
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		cmd = TASK_CMD(task);
 | 
			
		||||
 | 
			
		||||
		if (!T_TASK(cmd)) {
 | 
			
		||||
			printk(KERN_ERR "T_TASK(cmd) is NULL for task: %p cmd:"
 | 
			
		||||
				" %p ITT: 0x%08x\n", task, cmd,
 | 
			
		||||
				CMD_TFO(cmd)->get_task_tag(cmd));
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		/*
 | 
			
		||||
		 * For PREEMPT_AND_ABORT usage, only process commands
 | 
			
		||||
		 * with a matching reservation key.
 | 
			
		||||
		 */
 | 
			
		||||
		if ((preempt_and_abort_list != NULL) &&
 | 
			
		||||
		    (core_scsi3_check_cdb_abort_and_preempt(
 | 
			
		||||
					preempt_and_abort_list, cmd) != 0))
 | 
			
		||||
			continue;
 | 
			
		||||
		/*
 | 
			
		||||
		 * Not aborting PROUT PREEMPT_AND_ABORT CDB..
 | 
			
		||||
		 */
 | 
			
		||||
		if (prout_cmd == cmd)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		list_del(&task->t_state_list);
 | 
			
		||||
		atomic_set(&task->task_state_active, 0);
 | 
			
		||||
		spin_unlock_irqrestore(&dev->execute_task_lock, flags);
 | 
			
		||||
 | 
			
		||||
		spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
 | 
			
		||||
		DEBUG_LR("LUN_RESET: %s cmd: %p task: %p"
 | 
			
		||||
			" ITT/CmdSN: 0x%08x/0x%08x, i_state: %d, t_state/"
 | 
			
		||||
			"def_t_state: %d/%d cdb: 0x%02x\n",
 | 
			
		||||
			(preempt_and_abort_list) ? "Preempt" : "", cmd, task,
 | 
			
		||||
			CMD_TFO(cmd)->get_task_tag(cmd), 0,
 | 
			
		||||
			CMD_TFO(cmd)->get_cmd_state(cmd), cmd->t_state,
 | 
			
		||||
			cmd->deferred_t_state, T_TASK(cmd)->t_task_cdb[0]);
 | 
			
		||||
		DEBUG_LR("LUN_RESET: ITT[0x%08x] - pr_res_key: 0x%016Lx"
 | 
			
		||||
			" t_task_cdbs: %d t_task_cdbs_left: %d"
 | 
			
		||||
			" t_task_cdbs_sent: %d -- t_transport_active: %d"
 | 
			
		||||
			" t_transport_stop: %d t_transport_sent: %d\n",
 | 
			
		||||
			CMD_TFO(cmd)->get_task_tag(cmd), cmd->pr_res_key,
 | 
			
		||||
			T_TASK(cmd)->t_task_cdbs,
 | 
			
		||||
			atomic_read(&T_TASK(cmd)->t_task_cdbs_left),
 | 
			
		||||
			atomic_read(&T_TASK(cmd)->t_task_cdbs_sent),
 | 
			
		||||
			atomic_read(&T_TASK(cmd)->t_transport_active),
 | 
			
		||||
			atomic_read(&T_TASK(cmd)->t_transport_stop),
 | 
			
		||||
			atomic_read(&T_TASK(cmd)->t_transport_sent));
 | 
			
		||||
 | 
			
		||||
		if (atomic_read(&task->task_active)) {
 | 
			
		||||
			atomic_set(&task->task_stop, 1);
 | 
			
		||||
			spin_unlock_irqrestore(
 | 
			
		||||
				&T_TASK(cmd)->t_state_lock, flags);
 | 
			
		||||
 | 
			
		||||
			DEBUG_LR("LUN_RESET: Waiting for task: %p to shutdown"
 | 
			
		||||
				" for dev: %p\n", task, dev);
 | 
			
		||||
			wait_for_completion(&task->task_stop_comp);
 | 
			
		||||
			DEBUG_LR("LUN_RESET Completed task: %p shutdown for"
 | 
			
		||||
				" dev: %p\n", task, dev);
 | 
			
		||||
			spin_lock_irqsave(&T_TASK(cmd)->t_state_lock, flags);
 | 
			
		||||
			atomic_dec(&T_TASK(cmd)->t_task_cdbs_left);
 | 
			
		||||
 | 
			
		||||
			atomic_set(&task->task_active, 0);
 | 
			
		||||
			atomic_set(&task->task_stop, 0);
 | 
			
		||||
		}
 | 
			
		||||
		__transport_stop_task_timer(task, &flags);
 | 
			
		||||
 | 
			
		||||
		if (!(atomic_dec_and_test(&T_TASK(cmd)->t_task_cdbs_ex_left))) {
 | 
			
		||||
			spin_unlock_irqrestore(
 | 
			
		||||
					&T_TASK(cmd)->t_state_lock, flags);
 | 
			
		||||
			DEBUG_LR("LUN_RESET: Skipping task: %p, dev: %p for"
 | 
			
		||||
				" t_task_cdbs_ex_left: %d\n", task, dev,
 | 
			
		||||
				atomic_read(&T_TASK(cmd)->t_task_cdbs_ex_left));
 | 
			
		||||
 | 
			
		||||
			spin_lock_irqsave(&dev->execute_task_lock, flags);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		fe_count = atomic_read(&T_TASK(cmd)->t_fe_count);
 | 
			
		||||
 | 
			
		||||
		if (atomic_read(&T_TASK(cmd)->t_transport_active)) {
 | 
			
		||||
			DEBUG_LR("LUN_RESET: got t_transport_active = 1 for"
 | 
			
		||||
				" task: %p, t_fe_count: %d dev: %p\n", task,
 | 
			
		||||
				fe_count, dev);
 | 
			
		||||
			spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock,
 | 
			
		||||
						flags);
 | 
			
		||||
			core_tmr_handle_tas_abort(tmr_nacl, cmd, tas, fe_count);
 | 
			
		||||
 | 
			
		||||
			spin_lock_irqsave(&dev->execute_task_lock, flags);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		DEBUG_LR("LUN_RESET: Got t_transport_active = 0 for task: %p,"
 | 
			
		||||
			" t_fe_count: %d dev: %p\n", task, fe_count, dev);
 | 
			
		||||
		spin_unlock_irqrestore(&T_TASK(cmd)->t_state_lock, flags);
 | 
			
		||||
		core_tmr_handle_tas_abort(tmr_nacl, cmd, tas, fe_count);
 | 
			
		||||
 | 
			
		||||
		spin_lock_irqsave(&dev->execute_task_lock, flags);
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock_irqrestore(&dev->execute_task_lock, flags);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Release all commands remaining in the struct se_device cmd queue.
 | 
			
		||||
	 *
 | 
			
		||||
	 * This follows the same logic as above for the struct se_device
 | 
			
		||||
	 * struct se_task state list, where commands are returned with
 | 
			
		||||
	 * TASK_ABORTED status, if there is an outstanding $FABRIC_MOD
 | 
			
		||||
	 * reference, otherwise the struct se_cmd is released.
 | 
			
		||||
	 */
 | 
			
		||||
	spin_lock_irqsave(&qobj->cmd_queue_lock, flags);
 | 
			
		||||
	list_for_each_entry_safe(qr, qr_tmp, &qobj->qobj_list, qr_list) {
 | 
			
		||||
		cmd = (struct se_cmd *)qr->cmd;
 | 
			
		||||
		if (!(cmd)) {
 | 
			
		||||
			/*
 | 
			
		||||
			 * Skip these for non PREEMPT_AND_ABORT usage..
 | 
			
		||||
			 */
 | 
			
		||||
			if (preempt_and_abort_list != NULL)
 | 
			
		||||
				continue;
 | 
			
		||||
 | 
			
		||||
			atomic_dec(&qobj->queue_cnt);
 | 
			
		||||
			list_del(&qr->qr_list);
 | 
			
		||||
			kfree(qr);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		/*
 | 
			
		||||
		 * For PREEMPT_AND_ABORT usage, only process commands
 | 
			
		||||
		 * with a matching reservation key.
 | 
			
		||||
		 */
 | 
			
		||||
		if ((preempt_and_abort_list != NULL) &&
 | 
			
		||||
		    (core_scsi3_check_cdb_abort_and_preempt(
 | 
			
		||||
					preempt_and_abort_list, cmd) != 0))
 | 
			
		||||
			continue;
 | 
			
		||||
		/*
 | 
			
		||||
		 * Not aborting PROUT PREEMPT_AND_ABORT CDB..
 | 
			
		||||
		 */
 | 
			
		||||
		if (prout_cmd == cmd)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		atomic_dec(&T_TASK(cmd)->t_transport_queue_active);
 | 
			
		||||
		atomic_dec(&qobj->queue_cnt);
 | 
			
		||||
		list_del(&qr->qr_list);
 | 
			
		||||
		spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
 | 
			
		||||
 | 
			
		||||
		state = qr->state;
 | 
			
		||||
		kfree(qr);
 | 
			
		||||
 | 
			
		||||
		DEBUG_LR("LUN_RESET: %s from Device Queue: cmd: %p t_state:"
 | 
			
		||||
			" %d t_fe_count: %d\n", (preempt_and_abort_list) ?
 | 
			
		||||
			"Preempt" : "", cmd, state,
 | 
			
		||||
			atomic_read(&T_TASK(cmd)->t_fe_count));
 | 
			
		||||
		/*
 | 
			
		||||
		 * Signal that the command has failed via cmd->se_cmd_flags,
 | 
			
		||||
		 * and call TFO->new_cmd_failure() to wakeup any fabric
 | 
			
		||||
		 * dependent code used to wait for unsolicited data out
 | 
			
		||||
		 * allocation to complete.  The fabric module is expected
 | 
			
		||||
		 * to dump any remaining unsolicited data out for the aborted
 | 
			
		||||
		 * command at this point.
 | 
			
		||||
		 */
 | 
			
		||||
		transport_new_cmd_failure(cmd);
 | 
			
		||||
 | 
			
		||||
		core_tmr_handle_tas_abort(tmr_nacl, cmd, tas,
 | 
			
		||||
				atomic_read(&T_TASK(cmd)->t_fe_count));
 | 
			
		||||
		spin_lock_irqsave(&qobj->cmd_queue_lock, flags);
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock_irqrestore(&qobj->cmd_queue_lock, flags);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Clear any legacy SPC-2 reservation when called during
 | 
			
		||||
	 * LOGICAL UNIT RESET
 | 
			
		||||
	 */
 | 
			
		||||
	if (!(preempt_and_abort_list) &&
 | 
			
		||||
	     (dev->dev_flags & DF_SPC2_RESERVATIONS)) {
 | 
			
		||||
		spin_lock(&dev->dev_reservation_lock);
 | 
			
		||||
		dev->dev_reserved_node_acl = NULL;
 | 
			
		||||
		dev->dev_flags &= ~DF_SPC2_RESERVATIONS;
 | 
			
		||||
		spin_unlock(&dev->dev_reservation_lock);
 | 
			
		||||
		printk(KERN_INFO "LUN_RESET: SCSI-2 Released reservation\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	spin_lock(&dev->stats_lock);
 | 
			
		||||
	dev->num_resets++;
 | 
			
		||||
	spin_unlock(&dev->stats_lock);
 | 
			
		||||
 | 
			
		||||
	DEBUG_LR("LUN_RESET: %s for [%s] Complete\n",
 | 
			
		||||
			(preempt_and_abort_list) ? "Preempt" : "TMR",
 | 
			
		||||
			TRANSPORT(dev)->name);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										826
									
								
								drivers/target/target_core_tpg.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										826
									
								
								drivers/target/target_core_tpg.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,826 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Filename:  target_core_tpg.c
 | 
			
		||||
 *
 | 
			
		||||
 * This file contains generic Target Portal Group related functions.
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2002, 2003, 2004, 2005 PyX Technologies, Inc.
 | 
			
		||||
 * Copyright (c) 2005, 2006, 2007 SBE, Inc.
 | 
			
		||||
 * 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/timer.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/spinlock.h>
 | 
			
		||||
#include <linux/smp_lock.h>
 | 
			
		||||
#include <linux/in.h>
 | 
			
		||||
#include <net/sock.h>
 | 
			
		||||
#include <net/tcp.h>
 | 
			
		||||
#include <scsi/scsi.h>
 | 
			
		||||
#include <scsi/scsi_cmnd.h>
 | 
			
		||||
 | 
			
		||||
#include <target/target_core_base.h>
 | 
			
		||||
#include <target/target_core_device.h>
 | 
			
		||||
#include <target/target_core_tpg.h>
 | 
			
		||||
#include <target/target_core_transport.h>
 | 
			
		||||
#include <target/target_core_fabric_ops.h>
 | 
			
		||||
 | 
			
		||||
#include "target_core_hba.h"
 | 
			
		||||
 | 
			
		||||
/*	core_clear_initiator_node_from_tpg():
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
static void core_clear_initiator_node_from_tpg(
 | 
			
		||||
	struct se_node_acl *nacl,
 | 
			
		||||
	struct se_portal_group *tpg)
 | 
			
		||||
{
 | 
			
		||||
	int i;
 | 
			
		||||
	struct se_dev_entry *deve;
 | 
			
		||||
	struct se_lun *lun;
 | 
			
		||||
	struct se_lun_acl *acl, *acl_tmp;
 | 
			
		||||
 | 
			
		||||
	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) {
 | 
			
		||||
			printk(KERN_ERR "%s device entries device pointer is"
 | 
			
		||||
				" NULL, but Initiator has access.\n",
 | 
			
		||||
				TPG_TFO(tpg)->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(&lun->lun_acl_lock);
 | 
			
		||||
		list_for_each_entry_safe(acl, acl_tmp,
 | 
			
		||||
					&lun->lun_acl_list, lacl_list) {
 | 
			
		||||
			if (!(strcmp(acl->initiatorname,
 | 
			
		||||
					nacl->initiatorname)) &&
 | 
			
		||||
			     (acl->mapped_lun == deve->mapped_lun))
 | 
			
		||||
				break;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!acl) {
 | 
			
		||||
			printk(KERN_ERR "Unable to locate struct se_lun_acl for %s,"
 | 
			
		||||
				" mapped_lun: %u\n", nacl->initiatorname,
 | 
			
		||||
				deve->mapped_lun);
 | 
			
		||||
			spin_unlock(&lun->lun_acl_lock);
 | 
			
		||||
			spin_lock_irq(&nacl->device_list_lock);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		list_del(&acl->lacl_list);
 | 
			
		||||
		spin_unlock(&lun->lun_acl_lock);
 | 
			
		||||
 | 
			
		||||
		spin_lock_irq(&nacl->device_list_lock);
 | 
			
		||||
		kfree(acl);
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock_irq(&nacl->device_list_lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*	__core_tpg_get_initiator_node_acl():
 | 
			
		||||
 *
 | 
			
		||||
 *	spin_lock_bh(&tpg->acl_node_lock); must be held when calling
 | 
			
		||||
 */
 | 
			
		||||
struct se_node_acl *__core_tpg_get_initiator_node_acl(
 | 
			
		||||
	struct se_portal_group *tpg,
 | 
			
		||||
	const char *initiatorname)
 | 
			
		||||
{
 | 
			
		||||
	struct se_node_acl *acl;
 | 
			
		||||
 | 
			
		||||
	list_for_each_entry(acl, &tpg->acl_node_list, acl_list) {
 | 
			
		||||
		if (!(strcmp(acl->initiatorname, initiatorname)))
 | 
			
		||||
			return acl;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*	core_tpg_get_initiator_node_acl():
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
struct se_node_acl *core_tpg_get_initiator_node_acl(
 | 
			
		||||
	struct se_portal_group *tpg,
 | 
			
		||||
	unsigned char *initiatorname)
 | 
			
		||||
{
 | 
			
		||||
	struct se_node_acl *acl;
 | 
			
		||||
 | 
			
		||||
	spin_lock_bh(&tpg->acl_node_lock);
 | 
			
		||||
	list_for_each_entry(acl, &tpg->acl_node_list, acl_list) {
 | 
			
		||||
		if (!(strcmp(acl->initiatorname, initiatorname)) &&
 | 
			
		||||
		   (!(acl->dynamic_node_acl))) {
 | 
			
		||||
			spin_unlock_bh(&tpg->acl_node_lock);
 | 
			
		||||
			return acl;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock_bh(&tpg->acl_node_lock);
 | 
			
		||||
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*	core_tpg_add_node_to_devs():
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
void core_tpg_add_node_to_devs(
 | 
			
		||||
	struct se_node_acl *acl,
 | 
			
		||||
	struct se_portal_group *tpg)
 | 
			
		||||
{
 | 
			
		||||
	int i = 0;
 | 
			
		||||
	u32 lun_access = 0;
 | 
			
		||||
	struct se_lun *lun;
 | 
			
		||||
	struct se_device *dev;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&tpg->tpg_lun_lock);
 | 
			
		||||
	for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
 | 
			
		||||
		lun = &tpg->tpg_lun_list[i];
 | 
			
		||||
		if (lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		spin_unlock(&tpg->tpg_lun_lock);
 | 
			
		||||
 | 
			
		||||
		dev = lun->lun_se_dev;
 | 
			
		||||
		/*
 | 
			
		||||
		 * By default in LIO-Target $FABRIC_MOD,
 | 
			
		||||
		 * demo_mode_write_protect is ON, or READ_ONLY;
 | 
			
		||||
		 */
 | 
			
		||||
		if (!(TPG_TFO(tpg)->tpg_check_demo_mode_write_protect(tpg))) {
 | 
			
		||||
			if (dev->dev_flags & DF_READ_ONLY)
 | 
			
		||||
				lun_access = TRANSPORT_LUNFLAGS_READ_ONLY;
 | 
			
		||||
			else
 | 
			
		||||
				lun_access = TRANSPORT_LUNFLAGS_READ_WRITE;
 | 
			
		||||
		} else {
 | 
			
		||||
			/*
 | 
			
		||||
			 * Allow only optical drives to issue R/W in default RO
 | 
			
		||||
			 * demo mode.
 | 
			
		||||
			 */
 | 
			
		||||
			if (TRANSPORT(dev)->get_device_type(dev) == TYPE_DISK)
 | 
			
		||||
				lun_access = TRANSPORT_LUNFLAGS_READ_ONLY;
 | 
			
		||||
			else
 | 
			
		||||
				lun_access = TRANSPORT_LUNFLAGS_READ_WRITE;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		printk(KERN_INFO "TARGET_CORE[%s]->TPG[%u]_LUN[%u] - Adding %s"
 | 
			
		||||
			" access for LUN in Demo Mode\n",
 | 
			
		||||
			TPG_TFO(tpg)->get_fabric_name(),
 | 
			
		||||
			TPG_TFO(tpg)->tpg_get_tag(tpg), lun->unpacked_lun,
 | 
			
		||||
			(lun_access == TRANSPORT_LUNFLAGS_READ_WRITE) ?
 | 
			
		||||
			"READ-WRITE" : "READ-ONLY");
 | 
			
		||||
 | 
			
		||||
		core_update_device_list_for_node(lun, NULL, lun->unpacked_lun,
 | 
			
		||||
				lun_access, acl, tpg, 1);
 | 
			
		||||
		spin_lock(&tpg->tpg_lun_lock);
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock(&tpg->tpg_lun_lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*      core_set_queue_depth_for_node():
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
static int core_set_queue_depth_for_node(
 | 
			
		||||
	struct se_portal_group *tpg,
 | 
			
		||||
	struct se_node_acl *acl)
 | 
			
		||||
{
 | 
			
		||||
	if (!acl->queue_depth) {
 | 
			
		||||
		printk(KERN_ERR "Queue depth for %s Initiator Node: %s is 0,"
 | 
			
		||||
			"defaulting to 1.\n", TPG_TFO(tpg)->get_fabric_name(),
 | 
			
		||||
			acl->initiatorname);
 | 
			
		||||
		acl->queue_depth = 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*      core_create_device_list_for_node():
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
static int core_create_device_list_for_node(struct se_node_acl *nacl)
 | 
			
		||||
{
 | 
			
		||||
	struct se_dev_entry *deve;
 | 
			
		||||
	int i;
 | 
			
		||||
 | 
			
		||||
	nacl->device_list = kzalloc(sizeof(struct se_dev_entry) *
 | 
			
		||||
				TRANSPORT_MAX_LUNS_PER_TPG, GFP_KERNEL);
 | 
			
		||||
	if (!(nacl->device_list)) {
 | 
			
		||||
		printk(KERN_ERR "Unable to allocate memory for"
 | 
			
		||||
			" struct se_node_acl->device_list\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
 | 
			
		||||
		deve = &nacl->device_list[i];
 | 
			
		||||
 | 
			
		||||
		atomic_set(&deve->ua_count, 0);
 | 
			
		||||
		atomic_set(&deve->pr_ref_count, 0);
 | 
			
		||||
		spin_lock_init(&deve->ua_lock);
 | 
			
		||||
		INIT_LIST_HEAD(&deve->alua_port_list);
 | 
			
		||||
		INIT_LIST_HEAD(&deve->ua_list);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*	core_tpg_check_initiator_node_acl()
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
struct se_node_acl *core_tpg_check_initiator_node_acl(
 | 
			
		||||
	struct se_portal_group *tpg,
 | 
			
		||||
	unsigned char *initiatorname)
 | 
			
		||||
{
 | 
			
		||||
	struct se_node_acl *acl;
 | 
			
		||||
 | 
			
		||||
	acl = core_tpg_get_initiator_node_acl(tpg, initiatorname);
 | 
			
		||||
	if ((acl))
 | 
			
		||||
		return acl;
 | 
			
		||||
 | 
			
		||||
	if (!(TPG_TFO(tpg)->tpg_check_demo_mode(tpg)))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	acl =  TPG_TFO(tpg)->tpg_alloc_fabric_acl(tpg);
 | 
			
		||||
	if (!(acl))
 | 
			
		||||
		return NULL;
 | 
			
		||||
 | 
			
		||||
	INIT_LIST_HEAD(&acl->acl_list);
 | 
			
		||||
	INIT_LIST_HEAD(&acl->acl_sess_list);
 | 
			
		||||
	spin_lock_init(&acl->device_list_lock);
 | 
			
		||||
	spin_lock_init(&acl->nacl_sess_lock);
 | 
			
		||||
	atomic_set(&acl->acl_pr_ref_count, 0);
 | 
			
		||||
	atomic_set(&acl->mib_ref_count, 0);
 | 
			
		||||
	acl->queue_depth = TPG_TFO(tpg)->tpg_get_default_depth(tpg);
 | 
			
		||||
	snprintf(acl->initiatorname, TRANSPORT_IQN_LEN, "%s", initiatorname);
 | 
			
		||||
	acl->se_tpg = tpg;
 | 
			
		||||
	acl->acl_index = scsi_get_new_index(SCSI_AUTH_INTR_INDEX);
 | 
			
		||||
	spin_lock_init(&acl->stats_lock);
 | 
			
		||||
	acl->dynamic_node_acl = 1;
 | 
			
		||||
 | 
			
		||||
	TPG_TFO(tpg)->set_default_node_attributes(acl);
 | 
			
		||||
 | 
			
		||||
	if (core_create_device_list_for_node(acl) < 0) {
 | 
			
		||||
		TPG_TFO(tpg)->tpg_release_fabric_acl(tpg, acl);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (core_set_queue_depth_for_node(tpg, acl) < 0) {
 | 
			
		||||
		core_free_device_list_for_node(acl, tpg);
 | 
			
		||||
		TPG_TFO(tpg)->tpg_release_fabric_acl(tpg, acl);
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	core_tpg_add_node_to_devs(acl, tpg);
 | 
			
		||||
 | 
			
		||||
	spin_lock_bh(&tpg->acl_node_lock);
 | 
			
		||||
	list_add_tail(&acl->acl_list, &tpg->acl_node_list);
 | 
			
		||||
	tpg->num_node_acls++;
 | 
			
		||||
	spin_unlock_bh(&tpg->acl_node_lock);
 | 
			
		||||
 | 
			
		||||
	printk("%s_TPG[%u] - Added DYNAMIC ACL with TCQ Depth: %d for %s"
 | 
			
		||||
		" Initiator Node: %s\n", TPG_TFO(tpg)->get_fabric_name(),
 | 
			
		||||
		TPG_TFO(tpg)->tpg_get_tag(tpg), acl->queue_depth,
 | 
			
		||||
		TPG_TFO(tpg)->get_fabric_name(), initiatorname);
 | 
			
		||||
 | 
			
		||||
	return acl;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(core_tpg_check_initiator_node_acl);
 | 
			
		||||
 | 
			
		||||
void core_tpg_wait_for_nacl_pr_ref(struct se_node_acl *nacl)
 | 
			
		||||
{
 | 
			
		||||
	while (atomic_read(&nacl->acl_pr_ref_count) != 0)
 | 
			
		||||
		cpu_relax();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void core_tpg_wait_for_mib_ref(struct se_node_acl *nacl)
 | 
			
		||||
{
 | 
			
		||||
	while (atomic_read(&nacl->mib_ref_count) != 0)
 | 
			
		||||
		cpu_relax();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void core_tpg_clear_object_luns(struct se_portal_group *tpg)
 | 
			
		||||
{
 | 
			
		||||
	int i, ret;
 | 
			
		||||
	struct se_lun *lun;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&tpg->tpg_lun_lock);
 | 
			
		||||
	for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
 | 
			
		||||
		lun = &tpg->tpg_lun_list[i];
 | 
			
		||||
 | 
			
		||||
		if ((lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE) ||
 | 
			
		||||
		    (lun->lun_se_dev == NULL))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		spin_unlock(&tpg->tpg_lun_lock);
 | 
			
		||||
		ret = core_dev_del_lun(tpg, lun->unpacked_lun);
 | 
			
		||||
		spin_lock(&tpg->tpg_lun_lock);
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock(&tpg->tpg_lun_lock);
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(core_tpg_clear_object_luns);
 | 
			
		||||
 | 
			
		||||
/*	core_tpg_add_initiator_node_acl():
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
struct se_node_acl *core_tpg_add_initiator_node_acl(
 | 
			
		||||
	struct se_portal_group *tpg,
 | 
			
		||||
	struct se_node_acl *se_nacl,
 | 
			
		||||
	const char *initiatorname,
 | 
			
		||||
	u32 queue_depth)
 | 
			
		||||
{
 | 
			
		||||
	struct se_node_acl *acl = NULL;
 | 
			
		||||
 | 
			
		||||
	spin_lock_bh(&tpg->acl_node_lock);
 | 
			
		||||
	acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname);
 | 
			
		||||
	if ((acl)) {
 | 
			
		||||
		if (acl->dynamic_node_acl) {
 | 
			
		||||
			acl->dynamic_node_acl = 0;
 | 
			
		||||
			printk(KERN_INFO "%s_TPG[%u] - Replacing dynamic ACL"
 | 
			
		||||
				" for %s\n", TPG_TFO(tpg)->get_fabric_name(),
 | 
			
		||||
				TPG_TFO(tpg)->tpg_get_tag(tpg), initiatorname);
 | 
			
		||||
			spin_unlock_bh(&tpg->acl_node_lock);
 | 
			
		||||
			/*
 | 
			
		||||
			 * Release the locally allocated struct se_node_acl
 | 
			
		||||
			 * because * core_tpg_add_initiator_node_acl() returned
 | 
			
		||||
			 * a pointer to an existing demo mode node ACL.
 | 
			
		||||
			 */
 | 
			
		||||
			if (se_nacl)
 | 
			
		||||
				TPG_TFO(tpg)->tpg_release_fabric_acl(tpg,
 | 
			
		||||
							se_nacl);
 | 
			
		||||
			goto done;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		printk(KERN_ERR "ACL entry for %s Initiator"
 | 
			
		||||
			" Node %s already exists for TPG %u, ignoring"
 | 
			
		||||
			" request.\n",  TPG_TFO(tpg)->get_fabric_name(),
 | 
			
		||||
			initiatorname, TPG_TFO(tpg)->tpg_get_tag(tpg));
 | 
			
		||||
		spin_unlock_bh(&tpg->acl_node_lock);
 | 
			
		||||
		return ERR_PTR(-EEXIST);
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock_bh(&tpg->acl_node_lock);
 | 
			
		||||
 | 
			
		||||
	if (!(se_nacl)) {
 | 
			
		||||
		printk("struct se_node_acl pointer is NULL\n");
 | 
			
		||||
		return ERR_PTR(-EINVAL);
 | 
			
		||||
	}
 | 
			
		||||
	/*
 | 
			
		||||
	 * For v4.x logic the se_node_acl_s is hanging off a fabric
 | 
			
		||||
	 * dependent structure allocated via
 | 
			
		||||
	 * struct target_core_fabric_ops->fabric_make_nodeacl()
 | 
			
		||||
	 */
 | 
			
		||||
	acl = se_nacl;
 | 
			
		||||
 | 
			
		||||
	INIT_LIST_HEAD(&acl->acl_list);
 | 
			
		||||
	INIT_LIST_HEAD(&acl->acl_sess_list);
 | 
			
		||||
	spin_lock_init(&acl->device_list_lock);
 | 
			
		||||
	spin_lock_init(&acl->nacl_sess_lock);
 | 
			
		||||
	atomic_set(&acl->acl_pr_ref_count, 0);
 | 
			
		||||
	acl->queue_depth = queue_depth;
 | 
			
		||||
	snprintf(acl->initiatorname, TRANSPORT_IQN_LEN, "%s", initiatorname);
 | 
			
		||||
	acl->se_tpg = tpg;
 | 
			
		||||
	acl->acl_index = scsi_get_new_index(SCSI_AUTH_INTR_INDEX);
 | 
			
		||||
	spin_lock_init(&acl->stats_lock);
 | 
			
		||||
 | 
			
		||||
	TPG_TFO(tpg)->set_default_node_attributes(acl);
 | 
			
		||||
 | 
			
		||||
	if (core_create_device_list_for_node(acl) < 0) {
 | 
			
		||||
		TPG_TFO(tpg)->tpg_release_fabric_acl(tpg, acl);
 | 
			
		||||
		return ERR_PTR(-ENOMEM);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (core_set_queue_depth_for_node(tpg, acl) < 0) {
 | 
			
		||||
		core_free_device_list_for_node(acl, tpg);
 | 
			
		||||
		TPG_TFO(tpg)->tpg_release_fabric_acl(tpg, acl);
 | 
			
		||||
		return ERR_PTR(-EINVAL);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	spin_lock_bh(&tpg->acl_node_lock);
 | 
			
		||||
	list_add_tail(&acl->acl_list, &tpg->acl_node_list);
 | 
			
		||||
	tpg->num_node_acls++;
 | 
			
		||||
	spin_unlock_bh(&tpg->acl_node_lock);
 | 
			
		||||
 | 
			
		||||
done:
 | 
			
		||||
	printk(KERN_INFO "%s_TPG[%hu] - Added ACL with TCQ Depth: %d for %s"
 | 
			
		||||
		" Initiator Node: %s\n", TPG_TFO(tpg)->get_fabric_name(),
 | 
			
		||||
		TPG_TFO(tpg)->tpg_get_tag(tpg), acl->queue_depth,
 | 
			
		||||
		TPG_TFO(tpg)->get_fabric_name(), initiatorname);
 | 
			
		||||
 | 
			
		||||
	return acl;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(core_tpg_add_initiator_node_acl);
 | 
			
		||||
 | 
			
		||||
/*	core_tpg_del_initiator_node_acl():
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
int core_tpg_del_initiator_node_acl(
 | 
			
		||||
	struct se_portal_group *tpg,
 | 
			
		||||
	struct se_node_acl *acl,
 | 
			
		||||
	int force)
 | 
			
		||||
{
 | 
			
		||||
	struct se_session *sess, *sess_tmp;
 | 
			
		||||
	int dynamic_acl = 0;
 | 
			
		||||
 | 
			
		||||
	spin_lock_bh(&tpg->acl_node_lock);
 | 
			
		||||
	if (acl->dynamic_node_acl) {
 | 
			
		||||
		acl->dynamic_node_acl = 0;
 | 
			
		||||
		dynamic_acl = 1;
 | 
			
		||||
	}
 | 
			
		||||
	list_del(&acl->acl_list);
 | 
			
		||||
	tpg->num_node_acls--;
 | 
			
		||||
	spin_unlock_bh(&tpg->acl_node_lock);
 | 
			
		||||
 | 
			
		||||
	spin_lock_bh(&tpg->session_lock);
 | 
			
		||||
	list_for_each_entry_safe(sess, sess_tmp,
 | 
			
		||||
				&tpg->tpg_sess_list, sess_list) {
 | 
			
		||||
		if (sess->se_node_acl != acl)
 | 
			
		||||
			continue;
 | 
			
		||||
		/*
 | 
			
		||||
		 * Determine if the session needs to be closed by our context.
 | 
			
		||||
		 */
 | 
			
		||||
		if (!(TPG_TFO(tpg)->shutdown_session(sess)))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		spin_unlock_bh(&tpg->session_lock);
 | 
			
		||||
		/*
 | 
			
		||||
		 * If the $FABRIC_MOD session for the Initiator Node ACL exists,
 | 
			
		||||
		 * forcefully shutdown the $FABRIC_MOD session/nexus.
 | 
			
		||||
		 */
 | 
			
		||||
		TPG_TFO(tpg)->close_session(sess);
 | 
			
		||||
 | 
			
		||||
		spin_lock_bh(&tpg->session_lock);
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock_bh(&tpg->session_lock);
 | 
			
		||||
 | 
			
		||||
	core_tpg_wait_for_nacl_pr_ref(acl);
 | 
			
		||||
	core_tpg_wait_for_mib_ref(acl);
 | 
			
		||||
	core_clear_initiator_node_from_tpg(acl, tpg);
 | 
			
		||||
	core_free_device_list_for_node(acl, tpg);
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "%s_TPG[%hu] - Deleted ACL with TCQ Depth: %d for %s"
 | 
			
		||||
		" Initiator Node: %s\n", TPG_TFO(tpg)->get_fabric_name(),
 | 
			
		||||
		TPG_TFO(tpg)->tpg_get_tag(tpg), acl->queue_depth,
 | 
			
		||||
		TPG_TFO(tpg)->get_fabric_name(), acl->initiatorname);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(core_tpg_del_initiator_node_acl);
 | 
			
		||||
 | 
			
		||||
/*	core_tpg_set_initiator_node_queue_depth():
 | 
			
		||||
 *
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
int core_tpg_set_initiator_node_queue_depth(
 | 
			
		||||
	struct se_portal_group *tpg,
 | 
			
		||||
	unsigned char *initiatorname,
 | 
			
		||||
	u32 queue_depth,
 | 
			
		||||
	int force)
 | 
			
		||||
{
 | 
			
		||||
	struct se_session *sess, *init_sess = NULL;
 | 
			
		||||
	struct se_node_acl *acl;
 | 
			
		||||
	int dynamic_acl = 0;
 | 
			
		||||
 | 
			
		||||
	spin_lock_bh(&tpg->acl_node_lock);
 | 
			
		||||
	acl = __core_tpg_get_initiator_node_acl(tpg, initiatorname);
 | 
			
		||||
	if (!(acl)) {
 | 
			
		||||
		printk(KERN_ERR "Access Control List entry for %s Initiator"
 | 
			
		||||
			" Node %s does not exists for TPG %hu, ignoring"
 | 
			
		||||
			" request.\n", TPG_TFO(tpg)->get_fabric_name(),
 | 
			
		||||
			initiatorname, TPG_TFO(tpg)->tpg_get_tag(tpg));
 | 
			
		||||
		spin_unlock_bh(&tpg->acl_node_lock);
 | 
			
		||||
		return -ENODEV;
 | 
			
		||||
	}
 | 
			
		||||
	if (acl->dynamic_node_acl) {
 | 
			
		||||
		acl->dynamic_node_acl = 0;
 | 
			
		||||
		dynamic_acl = 1;
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock_bh(&tpg->acl_node_lock);
 | 
			
		||||
 | 
			
		||||
	spin_lock_bh(&tpg->session_lock);
 | 
			
		||||
	list_for_each_entry(sess, &tpg->tpg_sess_list, sess_list) {
 | 
			
		||||
		if (sess->se_node_acl != acl)
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		if (!force) {
 | 
			
		||||
			printk(KERN_ERR "Unable to change queue depth for %s"
 | 
			
		||||
				" Initiator Node: %s while session is"
 | 
			
		||||
				" operational.  To forcefully change the queue"
 | 
			
		||||
				" depth and force session reinstatement"
 | 
			
		||||
				" use the \"force=1\" parameter.\n",
 | 
			
		||||
				TPG_TFO(tpg)->get_fabric_name(), initiatorname);
 | 
			
		||||
			spin_unlock_bh(&tpg->session_lock);
 | 
			
		||||
 | 
			
		||||
			spin_lock_bh(&tpg->acl_node_lock);
 | 
			
		||||
			if (dynamic_acl)
 | 
			
		||||
				acl->dynamic_node_acl = 1;
 | 
			
		||||
			spin_unlock_bh(&tpg->acl_node_lock);
 | 
			
		||||
			return -EEXIST;
 | 
			
		||||
		}
 | 
			
		||||
		/*
 | 
			
		||||
		 * Determine if the session needs to be closed by our context.
 | 
			
		||||
		 */
 | 
			
		||||
		if (!(TPG_TFO(tpg)->shutdown_session(sess)))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		init_sess = sess;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * User has requested to change the queue depth for a Initiator Node.
 | 
			
		||||
	 * Change the value in the Node's struct se_node_acl, and call
 | 
			
		||||
	 * core_set_queue_depth_for_node() to add the requested queue depth.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Finally call  TPG_TFO(tpg)->close_session() to force session
 | 
			
		||||
	 * reinstatement to occur if there is an active session for the
 | 
			
		||||
	 * $FABRIC_MOD Initiator Node in question.
 | 
			
		||||
	 */
 | 
			
		||||
	acl->queue_depth = queue_depth;
 | 
			
		||||
 | 
			
		||||
	if (core_set_queue_depth_for_node(tpg, acl) < 0) {
 | 
			
		||||
		spin_unlock_bh(&tpg->session_lock);
 | 
			
		||||
		/*
 | 
			
		||||
		 * Force session reinstatement if
 | 
			
		||||
		 * core_set_queue_depth_for_node() failed, because we assume
 | 
			
		||||
		 * the $FABRIC_MOD has already the set session reinstatement
 | 
			
		||||
		 * bit from TPG_TFO(tpg)->shutdown_session() called above.
 | 
			
		||||
		 */
 | 
			
		||||
		if (init_sess)
 | 
			
		||||
			TPG_TFO(tpg)->close_session(init_sess);
 | 
			
		||||
 | 
			
		||||
		spin_lock_bh(&tpg->acl_node_lock);
 | 
			
		||||
		if (dynamic_acl)
 | 
			
		||||
			acl->dynamic_node_acl = 1;
 | 
			
		||||
		spin_unlock_bh(&tpg->acl_node_lock);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock_bh(&tpg->session_lock);
 | 
			
		||||
	/*
 | 
			
		||||
	 * If the $FABRIC_MOD session for the Initiator Node ACL exists,
 | 
			
		||||
	 * forcefully shutdown the $FABRIC_MOD session/nexus.
 | 
			
		||||
	 */
 | 
			
		||||
	if (init_sess)
 | 
			
		||||
		TPG_TFO(tpg)->close_session(init_sess);
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "Successfuly changed queue depth to: %d for Initiator"
 | 
			
		||||
		" Node: %s on %s Target Portal Group: %u\n", queue_depth,
 | 
			
		||||
		initiatorname, TPG_TFO(tpg)->get_fabric_name(),
 | 
			
		||||
		TPG_TFO(tpg)->tpg_get_tag(tpg));
 | 
			
		||||
 | 
			
		||||
	spin_lock_bh(&tpg->acl_node_lock);
 | 
			
		||||
	if (dynamic_acl)
 | 
			
		||||
		acl->dynamic_node_acl = 1;
 | 
			
		||||
	spin_unlock_bh(&tpg->acl_node_lock);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(core_tpg_set_initiator_node_queue_depth);
 | 
			
		||||
 | 
			
		||||
static int core_tpg_setup_virtual_lun0(struct se_portal_group *se_tpg)
 | 
			
		||||
{
 | 
			
		||||
	/* Set in core_dev_setup_virtual_lun0() */
 | 
			
		||||
	struct se_device *dev = se_global->g_lun0_dev;
 | 
			
		||||
	struct se_lun *lun = &se_tpg->tpg_virt_lun0;
 | 
			
		||||
	u32 lun_access = TRANSPORT_LUNFLAGS_READ_ONLY;
 | 
			
		||||
	int ret;
 | 
			
		||||
 | 
			
		||||
	lun->unpacked_lun = 0;
 | 
			
		||||
	lun->lun_status = TRANSPORT_LUN_STATUS_FREE;
 | 
			
		||||
	atomic_set(&lun->lun_acl_count, 0);
 | 
			
		||||
	init_completion(&lun->lun_shutdown_comp);
 | 
			
		||||
	INIT_LIST_HEAD(&lun->lun_acl_list);
 | 
			
		||||
	INIT_LIST_HEAD(&lun->lun_cmd_list);
 | 
			
		||||
	spin_lock_init(&lun->lun_acl_lock);
 | 
			
		||||
	spin_lock_init(&lun->lun_cmd_lock);
 | 
			
		||||
	spin_lock_init(&lun->lun_sep_lock);
 | 
			
		||||
 | 
			
		||||
	ret = core_tpg_post_addlun(se_tpg, lun, lun_access, dev);
 | 
			
		||||
	if (ret < 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void core_tpg_release_virtual_lun0(struct se_portal_group *se_tpg)
 | 
			
		||||
{
 | 
			
		||||
	struct se_lun *lun = &se_tpg->tpg_virt_lun0;
 | 
			
		||||
 | 
			
		||||
	core_tpg_post_dellun(se_tpg, lun);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int core_tpg_register(
 | 
			
		||||
	struct target_core_fabric_ops *tfo,
 | 
			
		||||
	struct se_wwn *se_wwn,
 | 
			
		||||
	struct se_portal_group *se_tpg,
 | 
			
		||||
	void *tpg_fabric_ptr,
 | 
			
		||||
	int se_tpg_type)
 | 
			
		||||
{
 | 
			
		||||
	struct se_lun *lun;
 | 
			
		||||
	u32 i;
 | 
			
		||||
 | 
			
		||||
	se_tpg->tpg_lun_list = kzalloc((sizeof(struct se_lun) *
 | 
			
		||||
				TRANSPORT_MAX_LUNS_PER_TPG), GFP_KERNEL);
 | 
			
		||||
	if (!(se_tpg->tpg_lun_list)) {
 | 
			
		||||
		printk(KERN_ERR "Unable to allocate struct se_portal_group->"
 | 
			
		||||
				"tpg_lun_list\n");
 | 
			
		||||
		return -ENOMEM;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < TRANSPORT_MAX_LUNS_PER_TPG; i++) {
 | 
			
		||||
		lun = &se_tpg->tpg_lun_list[i];
 | 
			
		||||
		lun->unpacked_lun = i;
 | 
			
		||||
		lun->lun_status = TRANSPORT_LUN_STATUS_FREE;
 | 
			
		||||
		atomic_set(&lun->lun_acl_count, 0);
 | 
			
		||||
		init_completion(&lun->lun_shutdown_comp);
 | 
			
		||||
		INIT_LIST_HEAD(&lun->lun_acl_list);
 | 
			
		||||
		INIT_LIST_HEAD(&lun->lun_cmd_list);
 | 
			
		||||
		spin_lock_init(&lun->lun_acl_lock);
 | 
			
		||||
		spin_lock_init(&lun->lun_cmd_lock);
 | 
			
		||||
		spin_lock_init(&lun->lun_sep_lock);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	se_tpg->se_tpg_type = se_tpg_type;
 | 
			
		||||
	se_tpg->se_tpg_fabric_ptr = tpg_fabric_ptr;
 | 
			
		||||
	se_tpg->se_tpg_tfo = tfo;
 | 
			
		||||
	se_tpg->se_tpg_wwn = se_wwn;
 | 
			
		||||
	atomic_set(&se_tpg->tpg_pr_ref_count, 0);
 | 
			
		||||
	INIT_LIST_HEAD(&se_tpg->acl_node_list);
 | 
			
		||||
	INIT_LIST_HEAD(&se_tpg->se_tpg_list);
 | 
			
		||||
	INIT_LIST_HEAD(&se_tpg->tpg_sess_list);
 | 
			
		||||
	spin_lock_init(&se_tpg->acl_node_lock);
 | 
			
		||||
	spin_lock_init(&se_tpg->session_lock);
 | 
			
		||||
	spin_lock_init(&se_tpg->tpg_lun_lock);
 | 
			
		||||
 | 
			
		||||
	if (se_tpg->se_tpg_type == TRANSPORT_TPG_TYPE_NORMAL) {
 | 
			
		||||
		if (core_tpg_setup_virtual_lun0(se_tpg) < 0) {
 | 
			
		||||
			kfree(se_tpg);
 | 
			
		||||
			return -ENOMEM;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	spin_lock_bh(&se_global->se_tpg_lock);
 | 
			
		||||
	list_add_tail(&se_tpg->se_tpg_list, &se_global->g_se_tpg_list);
 | 
			
		||||
	spin_unlock_bh(&se_global->se_tpg_lock);
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "TARGET_CORE[%s]: Allocated %s struct se_portal_group for"
 | 
			
		||||
		" endpoint: %s, Portal Tag: %u\n", tfo->get_fabric_name(),
 | 
			
		||||
		(se_tpg->se_tpg_type == TRANSPORT_TPG_TYPE_NORMAL) ?
 | 
			
		||||
		"Normal" : "Discovery", (tfo->tpg_get_wwn(se_tpg) == NULL) ?
 | 
			
		||||
		"None" : tfo->tpg_get_wwn(se_tpg), tfo->tpg_get_tag(se_tpg));
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(core_tpg_register);
 | 
			
		||||
 | 
			
		||||
int core_tpg_deregister(struct se_portal_group *se_tpg)
 | 
			
		||||
{
 | 
			
		||||
	printk(KERN_INFO "TARGET_CORE[%s]: Deallocating %s struct se_portal_group"
 | 
			
		||||
		" for endpoint: %s Portal Tag %u\n",
 | 
			
		||||
		(se_tpg->se_tpg_type == TRANSPORT_TPG_TYPE_NORMAL) ?
 | 
			
		||||
		"Normal" : "Discovery", TPG_TFO(se_tpg)->get_fabric_name(),
 | 
			
		||||
		TPG_TFO(se_tpg)->tpg_get_wwn(se_tpg),
 | 
			
		||||
		TPG_TFO(se_tpg)->tpg_get_tag(se_tpg));
 | 
			
		||||
 | 
			
		||||
	spin_lock_bh(&se_global->se_tpg_lock);
 | 
			
		||||
	list_del(&se_tpg->se_tpg_list);
 | 
			
		||||
	spin_unlock_bh(&se_global->se_tpg_lock);
 | 
			
		||||
 | 
			
		||||
	while (atomic_read(&se_tpg->tpg_pr_ref_count) != 0)
 | 
			
		||||
		cpu_relax();
 | 
			
		||||
 | 
			
		||||
	if (se_tpg->se_tpg_type == TRANSPORT_TPG_TYPE_NORMAL)
 | 
			
		||||
		core_tpg_release_virtual_lun0(se_tpg);
 | 
			
		||||
 | 
			
		||||
	se_tpg->se_tpg_fabric_ptr = NULL;
 | 
			
		||||
	kfree(se_tpg->tpg_lun_list);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
EXPORT_SYMBOL(core_tpg_deregister);
 | 
			
		||||
 | 
			
		||||
struct se_lun *core_tpg_pre_addlun(
 | 
			
		||||
	struct se_portal_group *tpg,
 | 
			
		||||
	u32 unpacked_lun)
 | 
			
		||||
{
 | 
			
		||||
	struct se_lun *lun;
 | 
			
		||||
 | 
			
		||||
	if (unpacked_lun > (TRANSPORT_MAX_LUNS_PER_TPG-1)) {
 | 
			
		||||
		printk(KERN_ERR "%s LUN: %u exceeds TRANSPORT_MAX_LUNS_PER_TPG"
 | 
			
		||||
			"-1: %u for Target Portal Group: %u\n",
 | 
			
		||||
			TPG_TFO(tpg)->get_fabric_name(),
 | 
			
		||||
			unpacked_lun, TRANSPORT_MAX_LUNS_PER_TPG-1,
 | 
			
		||||
			TPG_TFO(tpg)->tpg_get_tag(tpg));
 | 
			
		||||
		return ERR_PTR(-EOVERFLOW);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	spin_lock(&tpg->tpg_lun_lock);
 | 
			
		||||
	lun = &tpg->tpg_lun_list[unpacked_lun];
 | 
			
		||||
	if (lun->lun_status == TRANSPORT_LUN_STATUS_ACTIVE) {
 | 
			
		||||
		printk(KERN_ERR "TPG Logical Unit Number: %u is already active"
 | 
			
		||||
			" on %s Target Portal Group: %u, ignoring request.\n",
 | 
			
		||||
			unpacked_lun, TPG_TFO(tpg)->get_fabric_name(),
 | 
			
		||||
			TPG_TFO(tpg)->tpg_get_tag(tpg));
 | 
			
		||||
		spin_unlock(&tpg->tpg_lun_lock);
 | 
			
		||||
		return ERR_PTR(-EINVAL);
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock(&tpg->tpg_lun_lock);
 | 
			
		||||
 | 
			
		||||
	return lun;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int core_tpg_post_addlun(
 | 
			
		||||
	struct se_portal_group *tpg,
 | 
			
		||||
	struct se_lun *lun,
 | 
			
		||||
	u32 lun_access,
 | 
			
		||||
	void *lun_ptr)
 | 
			
		||||
{
 | 
			
		||||
	if (core_dev_export(lun_ptr, tpg, lun) < 0)
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&tpg->tpg_lun_lock);
 | 
			
		||||
	lun->lun_access = lun_access;
 | 
			
		||||
	lun->lun_status = TRANSPORT_LUN_STATUS_ACTIVE;
 | 
			
		||||
	spin_unlock(&tpg->tpg_lun_lock);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void core_tpg_shutdown_lun(
 | 
			
		||||
	struct se_portal_group *tpg,
 | 
			
		||||
	struct se_lun *lun)
 | 
			
		||||
{
 | 
			
		||||
	core_clear_lun_from_tpg(lun, tpg);
 | 
			
		||||
	transport_clear_lun_from_sessions(lun);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct se_lun *core_tpg_pre_dellun(
 | 
			
		||||
	struct se_portal_group *tpg,
 | 
			
		||||
	u32 unpacked_lun,
 | 
			
		||||
	int *ret)
 | 
			
		||||
{
 | 
			
		||||
	struct se_lun *lun;
 | 
			
		||||
 | 
			
		||||
	if (unpacked_lun > (TRANSPORT_MAX_LUNS_PER_TPG-1)) {
 | 
			
		||||
		printk(KERN_ERR "%s LUN: %u exceeds TRANSPORT_MAX_LUNS_PER_TPG"
 | 
			
		||||
			"-1: %u for Target Portal Group: %u\n",
 | 
			
		||||
			TPG_TFO(tpg)->get_fabric_name(), unpacked_lun,
 | 
			
		||||
			TRANSPORT_MAX_LUNS_PER_TPG-1,
 | 
			
		||||
			TPG_TFO(tpg)->tpg_get_tag(tpg));
 | 
			
		||||
		return ERR_PTR(-EOVERFLOW);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	spin_lock(&tpg->tpg_lun_lock);
 | 
			
		||||
	lun = &tpg->tpg_lun_list[unpacked_lun];
 | 
			
		||||
	if (lun->lun_status != TRANSPORT_LUN_STATUS_ACTIVE) {
 | 
			
		||||
		printk(KERN_ERR "%s Logical Unit Number: %u is not active on"
 | 
			
		||||
			" Target Portal Group: %u, ignoring request.\n",
 | 
			
		||||
			TPG_TFO(tpg)->get_fabric_name(), unpacked_lun,
 | 
			
		||||
			TPG_TFO(tpg)->tpg_get_tag(tpg));
 | 
			
		||||
		spin_unlock(&tpg->tpg_lun_lock);
 | 
			
		||||
		return ERR_PTR(-ENODEV);
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock(&tpg->tpg_lun_lock);
 | 
			
		||||
 | 
			
		||||
	return lun;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int core_tpg_post_dellun(
 | 
			
		||||
	struct se_portal_group *tpg,
 | 
			
		||||
	struct se_lun *lun)
 | 
			
		||||
{
 | 
			
		||||
	core_tpg_shutdown_lun(tpg, lun);
 | 
			
		||||
 | 
			
		||||
	core_dev_unexport(lun->lun_se_dev, tpg, lun);
 | 
			
		||||
 | 
			
		||||
	spin_lock(&tpg->tpg_lun_lock);
 | 
			
		||||
	lun->lun_status = TRANSPORT_LUN_STATUS_FREE;
 | 
			
		||||
	spin_unlock(&tpg->tpg_lun_lock);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										6134
									
								
								drivers/target/target_core_transport.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6134
									
								
								drivers/target/target_core_transport.c
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										332
									
								
								drivers/target/target_core_ua.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										332
									
								
								drivers/target/target_core_ua.c
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,332 @@
 | 
			
		|||
/*******************************************************************************
 | 
			
		||||
 * Filename: target_core_ua.c
 | 
			
		||||
 *
 | 
			
		||||
 * This file contains logic for SPC-3 Unit Attention emulation
 | 
			
		||||
 *
 | 
			
		||||
 * Copyright (c) 2009,2010 Rising Tide Systems
 | 
			
		||||
 * Copyright (c) 2009,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/version.h>
 | 
			
		||||
#include <linux/slab.h>
 | 
			
		||||
#include <linux/spinlock.h>
 | 
			
		||||
#include <scsi/scsi.h>
 | 
			
		||||
#include <scsi/scsi_cmnd.h>
 | 
			
		||||
 | 
			
		||||
#include <target/target_core_base.h>
 | 
			
		||||
#include <target/target_core_device.h>
 | 
			
		||||
#include <target/target_core_transport.h>
 | 
			
		||||
#include <target/target_core_fabric_ops.h>
 | 
			
		||||
#include <target/target_core_configfs.h>
 | 
			
		||||
 | 
			
		||||
#include "target_core_alua.h"
 | 
			
		||||
#include "target_core_hba.h"
 | 
			
		||||
#include "target_core_pr.h"
 | 
			
		||||
#include "target_core_ua.h"
 | 
			
		||||
 | 
			
		||||
int core_scsi3_ua_check(
 | 
			
		||||
	struct se_cmd *cmd,
 | 
			
		||||
	unsigned char *cdb)
 | 
			
		||||
{
 | 
			
		||||
	struct se_dev_entry *deve;
 | 
			
		||||
	struct se_session *sess = cmd->se_sess;
 | 
			
		||||
	struct se_node_acl *nacl;
 | 
			
		||||
 | 
			
		||||
	if (!(sess))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	nacl = sess->se_node_acl;
 | 
			
		||||
	if (!(nacl))
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	deve = &nacl->device_list[cmd->orig_fe_lun];
 | 
			
		||||
	if (!(atomic_read(&deve->ua_count)))
 | 
			
		||||
		return 0;
 | 
			
		||||
	/*
 | 
			
		||||
	 * From sam4r14, section 5.14 Unit attention condition:
 | 
			
		||||
	 *
 | 
			
		||||
	 * a) if an INQUIRY command enters the enabled command state, the
 | 
			
		||||
	 *    device server shall process the INQUIRY command and shall neither
 | 
			
		||||
	 *    report nor clear any unit attention condition;
 | 
			
		||||
	 * b) if a REPORT LUNS command enters the enabled command state, the
 | 
			
		||||
	 *    device server shall process the REPORT LUNS command and shall not
 | 
			
		||||
	 *    report any unit attention condition;
 | 
			
		||||
	 * e) if a REQUEST SENSE command enters the enabled command state while
 | 
			
		||||
	 *    a unit attention condition exists for the SCSI initiator port
 | 
			
		||||
	 *    associated with the I_T nexus on which the REQUEST SENSE command
 | 
			
		||||
	 *    was received, then the device server shall process the command
 | 
			
		||||
	 *    and either:
 | 
			
		||||
	 */
 | 
			
		||||
	switch (cdb[0]) {
 | 
			
		||||
	case INQUIRY:
 | 
			
		||||
	case REPORT_LUNS:
 | 
			
		||||
	case REQUEST_SENSE:
 | 
			
		||||
		return 0;
 | 
			
		||||
	default:
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int core_scsi3_ua_allocate(
 | 
			
		||||
	struct se_node_acl *nacl,
 | 
			
		||||
	u32 unpacked_lun,
 | 
			
		||||
	u8 asc,
 | 
			
		||||
	u8 ascq)
 | 
			
		||||
{
 | 
			
		||||
	struct se_dev_entry *deve;
 | 
			
		||||
	struct se_ua *ua, *ua_p, *ua_tmp;
 | 
			
		||||
	/*
 | 
			
		||||
	 * PASSTHROUGH OPS
 | 
			
		||||
	 */
 | 
			
		||||
	if (!(nacl))
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	ua = kmem_cache_zalloc(se_ua_cache, GFP_ATOMIC);
 | 
			
		||||
	if (!(ua)) {
 | 
			
		||||
		printk(KERN_ERR "Unable to allocate struct se_ua\n");
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	INIT_LIST_HEAD(&ua->ua_dev_list);
 | 
			
		||||
	INIT_LIST_HEAD(&ua->ua_nacl_list);
 | 
			
		||||
 | 
			
		||||
	ua->ua_nacl = nacl;
 | 
			
		||||
	ua->ua_asc = asc;
 | 
			
		||||
	ua->ua_ascq = ascq;
 | 
			
		||||
 | 
			
		||||
	spin_lock_irq(&nacl->device_list_lock);
 | 
			
		||||
	deve = &nacl->device_list[unpacked_lun];
 | 
			
		||||
 | 
			
		||||
	spin_lock(&deve->ua_lock);
 | 
			
		||||
	list_for_each_entry_safe(ua_p, ua_tmp, &deve->ua_list, ua_nacl_list) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * Do not report the same UNIT ATTENTION twice..
 | 
			
		||||
		 */
 | 
			
		||||
		if ((ua_p->ua_asc == asc) && (ua_p->ua_ascq == ascq)) {
 | 
			
		||||
			spin_unlock(&deve->ua_lock);
 | 
			
		||||
			spin_unlock_irq(&nacl->device_list_lock);
 | 
			
		||||
			kmem_cache_free(se_ua_cache, ua);
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
		/*
 | 
			
		||||
		 * Attach the highest priority Unit Attention to
 | 
			
		||||
		 * the head of the list following sam4r14,
 | 
			
		||||
		 * Section 5.14 Unit Attention Condition:
 | 
			
		||||
		 *
 | 
			
		||||
		 * POWER ON, RESET, OR BUS DEVICE RESET OCCURRED highest
 | 
			
		||||
		 * POWER ON OCCURRED or
 | 
			
		||||
		 * DEVICE INTERNAL RESET
 | 
			
		||||
		 * SCSI BUS RESET OCCURRED or
 | 
			
		||||
		 * MICROCODE HAS BEEN CHANGED or
 | 
			
		||||
		 * protocol specific
 | 
			
		||||
		 * BUS DEVICE RESET FUNCTION OCCURRED
 | 
			
		||||
		 * I_T NEXUS LOSS OCCURRED
 | 
			
		||||
		 * COMMANDS CLEARED BY POWER LOSS NOTIFICATION
 | 
			
		||||
		 * all others                                    Lowest
 | 
			
		||||
		 *
 | 
			
		||||
		 * Each of the ASCQ codes listed above are defined in
 | 
			
		||||
		 * the 29h ASC family, see spc4r17 Table D.1
 | 
			
		||||
		 */
 | 
			
		||||
		if (ua_p->ua_asc == 0x29) {
 | 
			
		||||
			if ((asc == 0x29) && (ascq > ua_p->ua_ascq))
 | 
			
		||||
				list_add(&ua->ua_nacl_list,
 | 
			
		||||
						&deve->ua_list);
 | 
			
		||||
			else
 | 
			
		||||
				list_add_tail(&ua->ua_nacl_list,
 | 
			
		||||
						&deve->ua_list);
 | 
			
		||||
		} else if (ua_p->ua_asc == 0x2a) {
 | 
			
		||||
			/*
 | 
			
		||||
			 * Incoming Family 29h ASCQ codes will override
 | 
			
		||||
			 * Family 2AHh ASCQ codes for Unit Attention condition.
 | 
			
		||||
			 */
 | 
			
		||||
			if ((asc == 0x29) || (ascq > ua_p->ua_asc))
 | 
			
		||||
				list_add(&ua->ua_nacl_list,
 | 
			
		||||
					&deve->ua_list);
 | 
			
		||||
			else
 | 
			
		||||
				list_add_tail(&ua->ua_nacl_list,
 | 
			
		||||
						&deve->ua_list);
 | 
			
		||||
		} else
 | 
			
		||||
			list_add_tail(&ua->ua_nacl_list,
 | 
			
		||||
				&deve->ua_list);
 | 
			
		||||
		spin_unlock(&deve->ua_lock);
 | 
			
		||||
		spin_unlock_irq(&nacl->device_list_lock);
 | 
			
		||||
 | 
			
		||||
		atomic_inc(&deve->ua_count);
 | 
			
		||||
		smp_mb__after_atomic_inc();
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
	list_add_tail(&ua->ua_nacl_list, &deve->ua_list);
 | 
			
		||||
	spin_unlock(&deve->ua_lock);
 | 
			
		||||
	spin_unlock_irq(&nacl->device_list_lock);
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "[%s]: Allocated UNIT ATTENTION, mapped LUN: %u, ASC:"
 | 
			
		||||
		" 0x%02x, ASCQ: 0x%02x\n",
 | 
			
		||||
		TPG_TFO(nacl->se_tpg)->get_fabric_name(), unpacked_lun,
 | 
			
		||||
		asc, ascq);
 | 
			
		||||
 | 
			
		||||
	atomic_inc(&deve->ua_count);
 | 
			
		||||
	smp_mb__after_atomic_inc();
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void core_scsi3_ua_release_all(
 | 
			
		||||
	struct se_dev_entry *deve)
 | 
			
		||||
{
 | 
			
		||||
	struct se_ua *ua, *ua_p;
 | 
			
		||||
 | 
			
		||||
	spin_lock(&deve->ua_lock);
 | 
			
		||||
	list_for_each_entry_safe(ua, ua_p, &deve->ua_list, ua_nacl_list) {
 | 
			
		||||
		list_del(&ua->ua_nacl_list);
 | 
			
		||||
		kmem_cache_free(se_ua_cache, ua);
 | 
			
		||||
 | 
			
		||||
		atomic_dec(&deve->ua_count);
 | 
			
		||||
		smp_mb__after_atomic_dec();
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock(&deve->ua_lock);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void core_scsi3_ua_for_check_condition(
 | 
			
		||||
	struct se_cmd *cmd,
 | 
			
		||||
	u8 *asc,
 | 
			
		||||
	u8 *ascq)
 | 
			
		||||
{
 | 
			
		||||
	struct se_device *dev = SE_DEV(cmd);
 | 
			
		||||
	struct se_dev_entry *deve;
 | 
			
		||||
	struct se_session *sess = cmd->se_sess;
 | 
			
		||||
	struct se_node_acl *nacl;
 | 
			
		||||
	struct se_ua *ua = NULL, *ua_p;
 | 
			
		||||
	int head = 1;
 | 
			
		||||
 | 
			
		||||
	if (!(sess))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	nacl = sess->se_node_acl;
 | 
			
		||||
	if (!(nacl))
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	spin_lock_irq(&nacl->device_list_lock);
 | 
			
		||||
	deve = &nacl->device_list[cmd->orig_fe_lun];
 | 
			
		||||
	if (!(atomic_read(&deve->ua_count))) {
 | 
			
		||||
		spin_unlock_irq(&nacl->device_list_lock);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
	/*
 | 
			
		||||
	 * The highest priority Unit Attentions are placed at the head of the
 | 
			
		||||
	 * struct se_dev_entry->ua_list, and will be returned in CHECK_CONDITION +
 | 
			
		||||
	 * sense data for the received CDB.
 | 
			
		||||
	 */
 | 
			
		||||
	spin_lock(&deve->ua_lock);
 | 
			
		||||
	list_for_each_entry_safe(ua, ua_p, &deve->ua_list, ua_nacl_list) {
 | 
			
		||||
		/*
 | 
			
		||||
		 * For ua_intlck_ctrl code not equal to 00b, only report the
 | 
			
		||||
		 * highest priority UNIT_ATTENTION and ASC/ASCQ without
 | 
			
		||||
		 * clearing it.
 | 
			
		||||
		 */
 | 
			
		||||
		if (DEV_ATTRIB(dev)->emulate_ua_intlck_ctrl != 0) {
 | 
			
		||||
			*asc = ua->ua_asc;
 | 
			
		||||
			*ascq = ua->ua_ascq;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		/*
 | 
			
		||||
		 * Otherwise for the default 00b, release the UNIT ATTENTION
 | 
			
		||||
		 * condition.  Return the ASC/ASCQ of the higest priority UA
 | 
			
		||||
		 * (head of the list) in the outgoing CHECK_CONDITION + sense.
 | 
			
		||||
		 */
 | 
			
		||||
		if (head) {
 | 
			
		||||
			*asc = ua->ua_asc;
 | 
			
		||||
			*ascq = ua->ua_ascq;
 | 
			
		||||
			head = 0;
 | 
			
		||||
		}
 | 
			
		||||
		list_del(&ua->ua_nacl_list);
 | 
			
		||||
		kmem_cache_free(se_ua_cache, ua);
 | 
			
		||||
 | 
			
		||||
		atomic_dec(&deve->ua_count);
 | 
			
		||||
		smp_mb__after_atomic_dec();
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock(&deve->ua_lock);
 | 
			
		||||
	spin_unlock_irq(&nacl->device_list_lock);
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "[%s]: %s UNIT ATTENTION condition with"
 | 
			
		||||
		" INTLCK_CTRL: %d, mapped LUN: %u, got CDB: 0x%02x"
 | 
			
		||||
		" reported ASC: 0x%02x, ASCQ: 0x%02x\n",
 | 
			
		||||
		TPG_TFO(nacl->se_tpg)->get_fabric_name(),
 | 
			
		||||
		(DEV_ATTRIB(dev)->emulate_ua_intlck_ctrl != 0) ? "Reporting" :
 | 
			
		||||
		"Releasing", DEV_ATTRIB(dev)->emulate_ua_intlck_ctrl,
 | 
			
		||||
		cmd->orig_fe_lun, T_TASK(cmd)->t_task_cdb[0], *asc, *ascq);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int core_scsi3_ua_clear_for_request_sense(
 | 
			
		||||
	struct se_cmd *cmd,
 | 
			
		||||
	u8 *asc,
 | 
			
		||||
	u8 *ascq)
 | 
			
		||||
{
 | 
			
		||||
	struct se_dev_entry *deve;
 | 
			
		||||
	struct se_session *sess = cmd->se_sess;
 | 
			
		||||
	struct se_node_acl *nacl;
 | 
			
		||||
	struct se_ua *ua = NULL, *ua_p;
 | 
			
		||||
	int head = 1;
 | 
			
		||||
 | 
			
		||||
	if (!(sess))
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	nacl = sess->se_node_acl;
 | 
			
		||||
	if (!(nacl))
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	spin_lock_irq(&nacl->device_list_lock);
 | 
			
		||||
	deve = &nacl->device_list[cmd->orig_fe_lun];
 | 
			
		||||
	if (!(atomic_read(&deve->ua_count))) {
 | 
			
		||||
		spin_unlock_irq(&nacl->device_list_lock);
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	/*
 | 
			
		||||
	 * The highest priority Unit Attentions are placed at the head of the
 | 
			
		||||
	 * struct se_dev_entry->ua_list.  The First (and hence highest priority)
 | 
			
		||||
	 * ASC/ASCQ will be returned in REQUEST_SENSE payload data for the
 | 
			
		||||
	 * matching struct se_lun.
 | 
			
		||||
	 *
 | 
			
		||||
	 * Once the returning ASC/ASCQ values are set, we go ahead and
 | 
			
		||||
	 * release all of the Unit Attention conditions for the assoicated
 | 
			
		||||
	 * struct se_lun.
 | 
			
		||||
	 */
 | 
			
		||||
	spin_lock(&deve->ua_lock);
 | 
			
		||||
	list_for_each_entry_safe(ua, ua_p, &deve->ua_list, ua_nacl_list) {
 | 
			
		||||
		if (head) {
 | 
			
		||||
			*asc = ua->ua_asc;
 | 
			
		||||
			*ascq = ua->ua_ascq;
 | 
			
		||||
			head = 0;
 | 
			
		||||
		}
 | 
			
		||||
		list_del(&ua->ua_nacl_list);
 | 
			
		||||
		kmem_cache_free(se_ua_cache, ua);
 | 
			
		||||
 | 
			
		||||
		atomic_dec(&deve->ua_count);
 | 
			
		||||
		smp_mb__after_atomic_dec();
 | 
			
		||||
	}
 | 
			
		||||
	spin_unlock(&deve->ua_lock);
 | 
			
		||||
	spin_unlock_irq(&nacl->device_list_lock);
 | 
			
		||||
 | 
			
		||||
	printk(KERN_INFO "[%s]: Released UNIT ATTENTION condition, mapped"
 | 
			
		||||
		" LUN: %u, got REQUEST_SENSE reported ASC: 0x%02x,"
 | 
			
		||||
		" ASCQ: 0x%02x\n", TPG_TFO(nacl->se_tpg)->get_fabric_name(),
 | 
			
		||||
		cmd->orig_fe_lun, *asc, *ascq);
 | 
			
		||||
 | 
			
		||||
	return (head) ? -1 : 0;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										36
									
								
								drivers/target/target_core_ua.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								drivers/target/target_core_ua.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,36 @@
 | 
			
		|||
#ifndef TARGET_CORE_UA_H
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * From spc4r17, Table D.1: ASC and ASCQ Assignement
 | 
			
		||||
 */
 | 
			
		||||
#define ASCQ_29H_POWER_ON_RESET_OR_BUS_DEVICE_RESET_OCCURED	0x00
 | 
			
		||||
#define ASCQ_29H_POWER_ON_OCCURRED				0x01
 | 
			
		||||
#define ASCQ_29H_SCSI_BUS_RESET_OCCURED				0x02
 | 
			
		||||
#define ASCQ_29H_BUS_DEVICE_RESET_FUNCTION_OCCURRED		0x03
 | 
			
		||||
#define ASCQ_29H_DEVICE_INTERNAL_RESET				0x04
 | 
			
		||||
#define ASCQ_29H_TRANSCEIVER_MODE_CHANGED_TO_SINGLE_ENDED	0x05
 | 
			
		||||
#define ASCQ_29H_TRANSCEIVER_MODE_CHANGED_TO_LVD		0x06
 | 
			
		||||
#define ASCQ_29H_NEXUS_LOSS_OCCURRED				0x07
 | 
			
		||||
 | 
			
		||||
#define ASCQ_2AH_PARAMETERS_CHANGED				0x00
 | 
			
		||||
#define ASCQ_2AH_MODE_PARAMETERS_CHANGED			0x01
 | 
			
		||||
#define ASCQ_2AH_LOG_PARAMETERS_CHANGED				0x02
 | 
			
		||||
#define ASCQ_2AH_RESERVATIONS_PREEMPTED				0x03
 | 
			
		||||
#define ASCQ_2AH_RESERVATIONS_RELEASED				0x04
 | 
			
		||||
#define ASCQ_2AH_REGISTRATIONS_PREEMPTED			0x05
 | 
			
		||||
#define ASCQ_2AH_ASYMMETRIC_ACCESS_STATE_CHANGED		0x06
 | 
			
		||||
#define ASCQ_2AH_IMPLICT_ASYMMETRIC_ACCESS_STATE_TRANSITION_FAILED 0x07
 | 
			
		||||
#define ASCQ_2AH_PRIORITY_CHANGED				0x08
 | 
			
		||||
 | 
			
		||||
#define ASCQ_2CH_PREVIOUS_RESERVATION_CONFLICT_STATUS		0x09
 | 
			
		||||
 | 
			
		||||
extern struct kmem_cache *se_ua_cache;
 | 
			
		||||
 | 
			
		||||
extern int core_scsi3_ua_check(struct se_cmd *, unsigned char *);
 | 
			
		||||
extern int core_scsi3_ua_allocate(struct se_node_acl *, u32, u8, u8);
 | 
			
		||||
extern void core_scsi3_ua_release_all(struct se_dev_entry *);
 | 
			
		||||
extern void core_scsi3_ua_for_check_condition(struct se_cmd *, u8 *, u8 *);
 | 
			
		||||
extern int core_scsi3_ua_clear_for_request_sense(struct se_cmd *,
 | 
			
		||||
						u8 *, u8 *);
 | 
			
		||||
 | 
			
		||||
#endif /* TARGET_CORE_UA_H */
 | 
			
		||||
							
								
								
									
										147
									
								
								include/target/configfs_macros.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								include/target/configfs_macros.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,147 @@
 | 
			
		|||
/* -*- mode: c; c-basic-offset: 8; -*-
 | 
			
		||||
 * vim: noexpandtab sw=8 ts=8 sts=0:
 | 
			
		||||
 *
 | 
			
		||||
 * configfs_macros.h - extends macros for configfs
 | 
			
		||||
 *
 | 
			
		||||
 * 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 021110-1307, USA.
 | 
			
		||||
 *
 | 
			
		||||
 * Based on sysfs:
 | 
			
		||||
 * 	sysfs is Copyright (C) 2001, 2002, 2003 Patrick Mochel
 | 
			
		||||
 *
 | 
			
		||||
 * Based on kobject.h:
 | 
			
		||||
 *      Copyright (c) 2002-2003	Patrick Mochel
 | 
			
		||||
 *      Copyright (c) 2002-2003	Open Source Development Labs
 | 
			
		||||
 *
 | 
			
		||||
 * configfs Copyright (C) 2005 Oracle.  All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * Added CONFIGFS_EATTR() macros from original configfs.h macros
 | 
			
		||||
 * Copright (C) 2008-2009 Nicholas A. Bellinger <nab@linux-iscsi.org>
 | 
			
		||||
 *
 | 
			
		||||
 * Please read Documentation/filesystems/configfs.txt before using the
 | 
			
		||||
 * configfs interface, ESPECIALLY the parts about reference counts and
 | 
			
		||||
 * item destructors.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#ifndef _CONFIGFS_MACROS_H_
 | 
			
		||||
#define _CONFIGFS_MACROS_H_
 | 
			
		||||
 | 
			
		||||
#include <linux/configfs.h>
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Users often need to create attribute structures for their configurable
 | 
			
		||||
 * attributes, containing a configfs_attribute member and function pointers
 | 
			
		||||
 * for the show() and store() operations on that attribute. If they don't
 | 
			
		||||
 * need anything else on the extended attribute structure, they can use
 | 
			
		||||
 * this macro to define it.  The argument _name isends up as
 | 
			
		||||
 * 'struct _name_attribute, as well as names of to CONFIGFS_ATTR_OPS() below.
 | 
			
		||||
 * The argument _item is the name of the structure containing the
 | 
			
		||||
 * struct config_item or struct config_group structure members
 | 
			
		||||
 */
 | 
			
		||||
#define CONFIGFS_EATTR_STRUCT(_name, _item)				\
 | 
			
		||||
struct _name##_attribute {						\
 | 
			
		||||
	struct configfs_attribute attr;					\
 | 
			
		||||
	ssize_t (*show)(struct _item *, char *);			\
 | 
			
		||||
	ssize_t (*store)(struct _item *, const char *, size_t);		\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * With the extended attribute structure, users can use this macro
 | 
			
		||||
 * (similar to sysfs' __ATTR) to make defining attributes easier.
 | 
			
		||||
 * An example:
 | 
			
		||||
 * #define MYITEM_EATTR(_name, _mode, _show, _store)	\
 | 
			
		||||
 * struct myitem_attribute childless_attr_##_name =	\
 | 
			
		||||
 *         __CONFIGFS_EATTR(_name, _mode, _show, _store)
 | 
			
		||||
 */
 | 
			
		||||
#define __CONFIGFS_EATTR(_name, _mode, _show, _store)			\
 | 
			
		||||
{									\
 | 
			
		||||
	.attr	= {							\
 | 
			
		||||
			.ca_name = __stringify(_name),			\
 | 
			
		||||
			.ca_mode = _mode,				\
 | 
			
		||||
			.ca_owner = THIS_MODULE,			\
 | 
			
		||||
	},								\
 | 
			
		||||
	.show	= _show,						\
 | 
			
		||||
	.store	= _store,						\
 | 
			
		||||
}
 | 
			
		||||
/* Here is a readonly version, only requiring a show() operation */
 | 
			
		||||
#define __CONFIGFS_EATTR_RO(_name, _show)				\
 | 
			
		||||
{									\
 | 
			
		||||
	.attr	= {							\
 | 
			
		||||
			.ca_name = __stringify(_name),			\
 | 
			
		||||
			.ca_mode = 0444,				\
 | 
			
		||||
			.ca_owner = THIS_MODULE,			\
 | 
			
		||||
	},								\
 | 
			
		||||
	.show	= _show,						\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * With these extended attributes, the simple show_attribute() and
 | 
			
		||||
 * store_attribute() operations need to call the show() and store() of the
 | 
			
		||||
 * attributes.  This is a common pattern, so we provide a macro to define
 | 
			
		||||
 * them.  The argument _name is the name of the attribute defined by
 | 
			
		||||
 * CONFIGFS_ATTR_STRUCT(). The argument _item is the name of the structure
 | 
			
		||||
 * containing the struct config_item or struct config_group structure member.
 | 
			
		||||
 * The argument _item_member is the actual name of the struct config_* struct
 | 
			
		||||
 * in your _item structure.  Meaning  my_structure->some_config_group.
 | 
			
		||||
 *		                      ^^_item^^^^^  ^^_item_member^^^
 | 
			
		||||
 * This macro expects the attributes to be named "struct <name>_attribute".
 | 
			
		||||
 */
 | 
			
		||||
#define CONFIGFS_EATTR_OPS_TO_FUNC(_name, _item, _item_member)		\
 | 
			
		||||
static struct _item *to_##_name(struct config_item *ci)			\
 | 
			
		||||
{									\
 | 
			
		||||
	return (ci) ? container_of(to_config_group(ci), struct _item,	\
 | 
			
		||||
		_item_member) : NULL;					\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define CONFIGFS_EATTR_OPS_SHOW(_name, _item)				\
 | 
			
		||||
static ssize_t _name##_attr_show(struct config_item *item,		\
 | 
			
		||||
				 struct configfs_attribute *attr,	\
 | 
			
		||||
				 char *page)				\
 | 
			
		||||
{									\
 | 
			
		||||
	struct _item *_item = to_##_name(item);				\
 | 
			
		||||
	struct _name##_attribute * _name##_attr =			\
 | 
			
		||||
		container_of(attr, struct _name##_attribute, attr);	\
 | 
			
		||||
	ssize_t ret = 0;						\
 | 
			
		||||
									\
 | 
			
		||||
	if (_name##_attr->show)						\
 | 
			
		||||
		ret = _name##_attr->show(_item, page);			\
 | 
			
		||||
	return ret;							\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define CONFIGFS_EATTR_OPS_STORE(_name, _item)				\
 | 
			
		||||
static ssize_t _name##_attr_store(struct config_item *item,		\
 | 
			
		||||
				  struct configfs_attribute *attr,	\
 | 
			
		||||
				  const char *page, size_t count)	\
 | 
			
		||||
{									\
 | 
			
		||||
	struct _item *_item = to_##_name(item);				\
 | 
			
		||||
	struct _name##_attribute * _name##_attr =			\
 | 
			
		||||
		container_of(attr, struct _name##_attribute, attr);	\
 | 
			
		||||
	ssize_t ret = -EINVAL;						\
 | 
			
		||||
									\
 | 
			
		||||
	if (_name##_attr->store)					\
 | 
			
		||||
		ret = _name##_attr->store(_item, page, count);		\
 | 
			
		||||
	return ret;							\
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define CONFIGFS_EATTR_OPS(_name, _item, _item_member)			\
 | 
			
		||||
	CONFIGFS_EATTR_OPS_TO_FUNC(_name, _item, _item_member);		\
 | 
			
		||||
	CONFIGFS_EATTR_OPS_SHOW(_name, _item);				\
 | 
			
		||||
	CONFIGFS_EATTR_OPS_STORE(_name, _item);
 | 
			
		||||
 | 
			
		||||
#define CONFIGFS_EATTR_OPS_RO(_name, _item, _item_member)		\
 | 
			
		||||
	CONFIGFS_EATTR_OPS_TO_FUNC(_name, _item, _item_member);		\
 | 
			
		||||
	CONFIGFS_EATTR_OPS_SHOW(_name, _item);
 | 
			
		||||
 | 
			
		||||
#endif /* _CONFIGFS_MACROS_H_ */
 | 
			
		||||
							
								
								
									
										937
									
								
								include/target/target_core_base.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										937
									
								
								include/target/target_core_base.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,937 @@
 | 
			
		|||
#ifndef TARGET_CORE_BASE_H
 | 
			
		||||
#define TARGET_CORE_BASE_H
 | 
			
		||||
 | 
			
		||||
#include <linux/in.h>
 | 
			
		||||
#include <linux/configfs.h>
 | 
			
		||||
#include <linux/dma-mapping.h>
 | 
			
		||||
#include <linux/blkdev.h>
 | 
			
		||||
#include <scsi/scsi_cmnd.h>
 | 
			
		||||
#include <net/sock.h>
 | 
			
		||||
#include <net/tcp.h>
 | 
			
		||||
#include "target_core_mib.h"
 | 
			
		||||
 | 
			
		||||
#define TARGET_CORE_MOD_VERSION		"v4.0.0-rc6"
 | 
			
		||||
#define SHUTDOWN_SIGS	(sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGABRT))
 | 
			
		||||
 | 
			
		||||
/* Used by transport_generic_allocate_iovecs() */
 | 
			
		||||
#define TRANSPORT_IOV_DATA_BUFFER		5
 | 
			
		||||
/* Maximum Number of LUNs per Target Portal Group */
 | 
			
		||||
#define TRANSPORT_MAX_LUNS_PER_TPG		256
 | 
			
		||||
/*
 | 
			
		||||
 * By default we use 32-byte CDBs in TCM Core and subsystem plugin code.
 | 
			
		||||
 *
 | 
			
		||||
 * Note that both include/scsi/scsi_cmnd.h:MAX_COMMAND_SIZE and
 | 
			
		||||
 * include/linux/blkdev.h:BLOCK_MAX_CDB as of v2.6.36-rc4 still use
 | 
			
		||||
 * 16-byte CDBs by default and require an extra allocation for
 | 
			
		||||
 * 32-byte CDBs to becasue of legacy issues.
 | 
			
		||||
 *
 | 
			
		||||
 * Within TCM Core there are no such legacy limitiations, so we go ahead
 | 
			
		||||
 * use 32-byte CDBs by default and use include/scsi/scsi.h:scsi_command_size()
 | 
			
		||||
 * within all TCM Core and subsystem plugin code.
 | 
			
		||||
 */
 | 
			
		||||
#define TCM_MAX_COMMAND_SIZE			32
 | 
			
		||||
/*
 | 
			
		||||
 * From include/scsi/scsi_cmnd.h:SCSI_SENSE_BUFFERSIZE, currently
 | 
			
		||||
 * defined 96, but the real limit is 252 (or 260 including the header)
 | 
			
		||||
 */
 | 
			
		||||
#define TRANSPORT_SENSE_BUFFER			SCSI_SENSE_BUFFERSIZE
 | 
			
		||||
/* Used by transport_send_check_condition_and_sense() */
 | 
			
		||||
#define SPC_SENSE_KEY_OFFSET			2
 | 
			
		||||
#define SPC_ASC_KEY_OFFSET			12
 | 
			
		||||
#define SPC_ASCQ_KEY_OFFSET			13
 | 
			
		||||
#define TRANSPORT_IQN_LEN			224
 | 
			
		||||
/* Used by target_core_store_alua_lu_gp() and target_core_alua_lu_gp_show_attr_members() */
 | 
			
		||||
#define LU_GROUP_NAME_BUF			256
 | 
			
		||||
/* Used by core_alua_store_tg_pt_gp_info() and target_core_alua_tg_pt_gp_show_attr_members() */
 | 
			
		||||
#define TG_PT_GROUP_NAME_BUF			256
 | 
			
		||||
/* Used to parse VPD into struct t10_vpd */
 | 
			
		||||
#define VPD_TMP_BUF_SIZE			128
 | 
			
		||||
/* Used by transport_generic_cmd_sequencer() */
 | 
			
		||||
#define READ_BLOCK_LEN          		6
 | 
			
		||||
#define READ_CAP_LEN            		8
 | 
			
		||||
#define READ_POSITION_LEN       		20
 | 
			
		||||
#define INQUIRY_LEN				36
 | 
			
		||||
/* Used by transport_get_inquiry_vpd_serial() */
 | 
			
		||||
#define INQUIRY_VPD_SERIAL_LEN			254
 | 
			
		||||
/* Used by transport_get_inquiry_vpd_device_ident() */
 | 
			
		||||
#define INQUIRY_VPD_DEVICE_IDENTIFIER_LEN	254
 | 
			
		||||
 | 
			
		||||
/* struct se_hba->hba_flags */
 | 
			
		||||
enum hba_flags_table {
 | 
			
		||||
	HBA_FLAGS_INTERNAL_USE	= 0x01,
 | 
			
		||||
	HBA_FLAGS_PSCSI_MODE	= 0x02,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* struct se_lun->lun_status */
 | 
			
		||||
enum transport_lun_status_table {
 | 
			
		||||
	TRANSPORT_LUN_STATUS_FREE = 0,
 | 
			
		||||
	TRANSPORT_LUN_STATUS_ACTIVE = 1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* struct se_portal_group->se_tpg_type */
 | 
			
		||||
enum transport_tpg_type_table {
 | 
			
		||||
	TRANSPORT_TPG_TYPE_NORMAL = 0,
 | 
			
		||||
	TRANSPORT_TPG_TYPE_DISCOVERY = 1,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Used for generate timer flags */
 | 
			
		||||
enum timer_flags_table {
 | 
			
		||||
	TF_RUNNING	= 0x01,
 | 
			
		||||
	TF_STOP		= 0x02,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Special transport agnostic struct se_cmd->t_states */
 | 
			
		||||
enum transport_state_table {
 | 
			
		||||
	TRANSPORT_NO_STATE	= 0,
 | 
			
		||||
	TRANSPORT_NEW_CMD	= 1,
 | 
			
		||||
	TRANSPORT_DEFERRED_CMD	= 2,
 | 
			
		||||
	TRANSPORT_WRITE_PENDING	= 3,
 | 
			
		||||
	TRANSPORT_PROCESS_WRITE	= 4,
 | 
			
		||||
	TRANSPORT_PROCESSING	= 5,
 | 
			
		||||
	TRANSPORT_COMPLETE_OK	= 6,
 | 
			
		||||
	TRANSPORT_COMPLETE_FAILURE = 7,
 | 
			
		||||
	TRANSPORT_COMPLETE_TIMEOUT = 8,
 | 
			
		||||
	TRANSPORT_PROCESS_TMR	= 9,
 | 
			
		||||
	TRANSPORT_TMR_COMPLETE	= 10,
 | 
			
		||||
	TRANSPORT_ISTATE_PROCESSING = 11,
 | 
			
		||||
	TRANSPORT_ISTATE_PROCESSED = 12,
 | 
			
		||||
	TRANSPORT_KILL		= 13,
 | 
			
		||||
	TRANSPORT_REMOVE	= 14,
 | 
			
		||||
	TRANSPORT_FREE		= 15,
 | 
			
		||||
	TRANSPORT_NEW_CMD_MAP	= 16,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Used for struct se_cmd->se_cmd_flags */
 | 
			
		||||
enum se_cmd_flags_table {
 | 
			
		||||
	SCF_SUPPORTED_SAM_OPCODE	= 0x00000001,
 | 
			
		||||
	SCF_TRANSPORT_TASK_SENSE	= 0x00000002,
 | 
			
		||||
	SCF_EMULATED_TASK_SENSE		= 0x00000004,
 | 
			
		||||
	SCF_SCSI_DATA_SG_IO_CDB		= 0x00000008,
 | 
			
		||||
	SCF_SCSI_CONTROL_SG_IO_CDB	= 0x00000010,
 | 
			
		||||
	SCF_SCSI_CONTROL_NONSG_IO_CDB	= 0x00000020,
 | 
			
		||||
	SCF_SCSI_NON_DATA_CDB		= 0x00000040,
 | 
			
		||||
	SCF_SCSI_CDB_EXCEPTION		= 0x00000080,
 | 
			
		||||
	SCF_SCSI_RESERVATION_CONFLICT	= 0x00000100,
 | 
			
		||||
	SCF_CMD_PASSTHROUGH_NOALLOC	= 0x00000200,
 | 
			
		||||
	SCF_SE_CMD_FAILED		= 0x00000400,
 | 
			
		||||
	SCF_SE_LUN_CMD			= 0x00000800,
 | 
			
		||||
	SCF_SE_ALLOW_EOO		= 0x00001000,
 | 
			
		||||
	SCF_SE_DISABLE_ONLINE_CHECK	= 0x00002000,
 | 
			
		||||
	SCF_SENT_CHECK_CONDITION	= 0x00004000,
 | 
			
		||||
	SCF_OVERFLOW_BIT		= 0x00008000,
 | 
			
		||||
	SCF_UNDERFLOW_BIT		= 0x00010000,
 | 
			
		||||
	SCF_SENT_DELAYED_TAS		= 0x00020000,
 | 
			
		||||
	SCF_ALUA_NON_OPTIMIZED		= 0x00040000,
 | 
			
		||||
	SCF_DELAYED_CMD_FROM_SAM_ATTR	= 0x00080000,
 | 
			
		||||
	SCF_PASSTHROUGH_SG_TO_MEM	= 0x00100000,
 | 
			
		||||
	SCF_PASSTHROUGH_CONTIG_TO_SG	= 0x00200000,
 | 
			
		||||
	SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC = 0x00400000,
 | 
			
		||||
	SCF_EMULATE_SYNC_CACHE		= 0x00800000,
 | 
			
		||||
	SCF_EMULATE_CDB_ASYNC		= 0x01000000,
 | 
			
		||||
	SCF_EMULATE_SYNC_UNMAP		= 0x02000000
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* struct se_dev_entry->lun_flags and struct se_lun->lun_access */
 | 
			
		||||
enum transport_lunflags_table {
 | 
			
		||||
	TRANSPORT_LUNFLAGS_NO_ACCESS		= 0x00,
 | 
			
		||||
	TRANSPORT_LUNFLAGS_INITIATOR_ACCESS	= 0x01,
 | 
			
		||||
	TRANSPORT_LUNFLAGS_READ_ONLY		= 0x02,
 | 
			
		||||
	TRANSPORT_LUNFLAGS_READ_WRITE		= 0x04,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* struct se_device->dev_status */
 | 
			
		||||
enum transport_device_status_table {
 | 
			
		||||
	TRANSPORT_DEVICE_ACTIVATED		= 0x01,
 | 
			
		||||
	TRANSPORT_DEVICE_DEACTIVATED		= 0x02,
 | 
			
		||||
	TRANSPORT_DEVICE_QUEUE_FULL		= 0x04,
 | 
			
		||||
	TRANSPORT_DEVICE_SHUTDOWN		= 0x08,
 | 
			
		||||
	TRANSPORT_DEVICE_OFFLINE_ACTIVATED	= 0x10,
 | 
			
		||||
	TRANSPORT_DEVICE_OFFLINE_DEACTIVATED	= 0x20,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Used by transport_send_check_condition_and_sense() and se_cmd->scsi_sense_reason
 | 
			
		||||
 * to signal which ASC/ASCQ sense payload should be built.
 | 
			
		||||
 */
 | 
			
		||||
enum tcm_sense_reason_table {
 | 
			
		||||
	TCM_NON_EXISTENT_LUN			= 0x01,
 | 
			
		||||
	TCM_UNSUPPORTED_SCSI_OPCODE		= 0x02,
 | 
			
		||||
	TCM_INCORRECT_AMOUNT_OF_DATA		= 0x03,
 | 
			
		||||
	TCM_UNEXPECTED_UNSOLICITED_DATA		= 0x04,
 | 
			
		||||
	TCM_SERVICE_CRC_ERROR			= 0x05,
 | 
			
		||||
	TCM_SNACK_REJECTED			= 0x06,
 | 
			
		||||
	TCM_SECTOR_COUNT_TOO_MANY		= 0x07,
 | 
			
		||||
	TCM_INVALID_CDB_FIELD			= 0x08,
 | 
			
		||||
	TCM_INVALID_PARAMETER_LIST		= 0x09,
 | 
			
		||||
	TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE	= 0x0a,
 | 
			
		||||
	TCM_UNKNOWN_MODE_PAGE			= 0x0b,
 | 
			
		||||
	TCM_WRITE_PROTECTED			= 0x0c,
 | 
			
		||||
	TCM_CHECK_CONDITION_ABORT_CMD		= 0x0d,
 | 
			
		||||
	TCM_CHECK_CONDITION_UNIT_ATTENTION	= 0x0e,
 | 
			
		||||
	TCM_CHECK_CONDITION_NOT_READY		= 0x0f,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct se_obj {
 | 
			
		||||
	atomic_t obj_access_count;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Used by TCM Core internally to signal if ALUA emulation is enabled or
 | 
			
		||||
 * disabled, or running in with TCM/pSCSI passthrough mode
 | 
			
		||||
 */
 | 
			
		||||
typedef enum {
 | 
			
		||||
	SPC_ALUA_PASSTHROUGH,
 | 
			
		||||
	SPC2_ALUA_DISABLED,
 | 
			
		||||
	SPC3_ALUA_EMULATED
 | 
			
		||||
} t10_alua_index_t;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Used by TCM Core internally to signal if SAM Task Attribute emulation
 | 
			
		||||
 * is enabled or disabled, or running in with TCM/pSCSI passthrough mode
 | 
			
		||||
 */
 | 
			
		||||
typedef enum {
 | 
			
		||||
	SAM_TASK_ATTR_PASSTHROUGH,
 | 
			
		||||
	SAM_TASK_ATTR_UNTAGGED,
 | 
			
		||||
	SAM_TASK_ATTR_EMULATED
 | 
			
		||||
} t10_task_attr_index_t;
 | 
			
		||||
 | 
			
		||||
struct se_cmd;
 | 
			
		||||
 | 
			
		||||
struct t10_alua {
 | 
			
		||||
	t10_alua_index_t alua_type;
 | 
			
		||||
	/* ALUA Target Port Group ID */
 | 
			
		||||
	u16	alua_tg_pt_gps_counter;
 | 
			
		||||
	u32	alua_tg_pt_gps_count;
 | 
			
		||||
	spinlock_t tg_pt_gps_lock;
 | 
			
		||||
	struct se_subsystem_dev *t10_sub_dev;
 | 
			
		||||
	/* Used for default ALUA Target Port Group */
 | 
			
		||||
	struct t10_alua_tg_pt_gp *default_tg_pt_gp;
 | 
			
		||||
	/* Used for default ALUA Target Port Group ConfigFS group */
 | 
			
		||||
	struct config_group alua_tg_pt_gps_group;
 | 
			
		||||
	int (*alua_state_check)(struct se_cmd *, unsigned char *, u8 *);
 | 
			
		||||
	struct list_head tg_pt_gps_list;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct t10_alua_lu_gp {
 | 
			
		||||
	u16	lu_gp_id;
 | 
			
		||||
	int	lu_gp_valid_id;
 | 
			
		||||
	u32	lu_gp_members;
 | 
			
		||||
	atomic_t lu_gp_shutdown;
 | 
			
		||||
	atomic_t lu_gp_ref_cnt;
 | 
			
		||||
	spinlock_t lu_gp_lock;
 | 
			
		||||
	struct config_group lu_gp_group;
 | 
			
		||||
	struct list_head lu_gp_list;
 | 
			
		||||
	struct list_head lu_gp_mem_list;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct t10_alua_lu_gp_member {
 | 
			
		||||
	int lu_gp_assoc:1;
 | 
			
		||||
	atomic_t lu_gp_mem_ref_cnt;
 | 
			
		||||
	spinlock_t lu_gp_mem_lock;
 | 
			
		||||
	struct t10_alua_lu_gp *lu_gp;
 | 
			
		||||
	struct se_device *lu_gp_mem_dev;
 | 
			
		||||
	struct list_head lu_gp_mem_list;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct t10_alua_tg_pt_gp {
 | 
			
		||||
	u16	tg_pt_gp_id;
 | 
			
		||||
	int	tg_pt_gp_valid_id;
 | 
			
		||||
	int	tg_pt_gp_alua_access_status;
 | 
			
		||||
	int	tg_pt_gp_alua_access_type;
 | 
			
		||||
	int	tg_pt_gp_nonop_delay_msecs;
 | 
			
		||||
	int	tg_pt_gp_trans_delay_msecs;
 | 
			
		||||
	int	tg_pt_gp_pref;
 | 
			
		||||
	int	tg_pt_gp_write_metadata;
 | 
			
		||||
	/* Used by struct t10_alua_tg_pt_gp->tg_pt_gp_md_buf_len */
 | 
			
		||||
#define ALUA_MD_BUF_LEN				1024
 | 
			
		||||
	u32	tg_pt_gp_md_buf_len;
 | 
			
		||||
	u32	tg_pt_gp_members;
 | 
			
		||||
	atomic_t tg_pt_gp_alua_access_state;
 | 
			
		||||
	atomic_t tg_pt_gp_ref_cnt;
 | 
			
		||||
	spinlock_t tg_pt_gp_lock;
 | 
			
		||||
	struct mutex tg_pt_gp_md_mutex;
 | 
			
		||||
	struct se_subsystem_dev *tg_pt_gp_su_dev;
 | 
			
		||||
	struct config_group tg_pt_gp_group;
 | 
			
		||||
	struct list_head tg_pt_gp_list;
 | 
			
		||||
	struct list_head tg_pt_gp_mem_list;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct t10_alua_tg_pt_gp_member {
 | 
			
		||||
	int tg_pt_gp_assoc:1;
 | 
			
		||||
	atomic_t tg_pt_gp_mem_ref_cnt;
 | 
			
		||||
	spinlock_t tg_pt_gp_mem_lock;
 | 
			
		||||
	struct t10_alua_tg_pt_gp *tg_pt_gp;
 | 
			
		||||
	struct se_port *tg_pt;
 | 
			
		||||
	struct list_head tg_pt_gp_mem_list;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct t10_vpd {
 | 
			
		||||
	unsigned char device_identifier[INQUIRY_VPD_DEVICE_IDENTIFIER_LEN];
 | 
			
		||||
	int protocol_identifier_set;
 | 
			
		||||
	u32 protocol_identifier;
 | 
			
		||||
	u32 device_identifier_code_set;
 | 
			
		||||
	u32 association;
 | 
			
		||||
	u32 device_identifier_type;
 | 
			
		||||
	struct list_head vpd_list;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct t10_wwn {
 | 
			
		||||
	unsigned char vendor[8];
 | 
			
		||||
	unsigned char model[16];
 | 
			
		||||
	unsigned char revision[4];
 | 
			
		||||
	unsigned char unit_serial[INQUIRY_VPD_SERIAL_LEN];
 | 
			
		||||
	spinlock_t t10_vpd_lock;
 | 
			
		||||
	struct se_subsystem_dev *t10_sub_dev;
 | 
			
		||||
	struct config_group t10_wwn_group;
 | 
			
		||||
	struct list_head t10_vpd_list;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Used by TCM Core internally to signal if >= SPC-3 peristent reservations
 | 
			
		||||
 * emulation is enabled or disabled, or running in with TCM/pSCSI passthrough
 | 
			
		||||
 * mode
 | 
			
		||||
 */
 | 
			
		||||
typedef enum {
 | 
			
		||||
	SPC_PASSTHROUGH,
 | 
			
		||||
	SPC2_RESERVATIONS,
 | 
			
		||||
	SPC3_PERSISTENT_RESERVATIONS
 | 
			
		||||
} t10_reservations_index_t;
 | 
			
		||||
 | 
			
		||||
struct t10_pr_registration {
 | 
			
		||||
	/* Used for fabrics that contain WWN+ISID */
 | 
			
		||||
#define PR_REG_ISID_LEN				16
 | 
			
		||||
	/* PR_REG_ISID_LEN + ',i,0x' */
 | 
			
		||||
#define PR_REG_ISID_ID_LEN			(PR_REG_ISID_LEN + 5)
 | 
			
		||||
	char pr_reg_isid[PR_REG_ISID_LEN];
 | 
			
		||||
	/* Used during APTPL metadata reading */
 | 
			
		||||
#define PR_APTPL_MAX_IPORT_LEN			256
 | 
			
		||||
	unsigned char pr_iport[PR_APTPL_MAX_IPORT_LEN];
 | 
			
		||||
	/* Used during APTPL metadata reading */
 | 
			
		||||
#define PR_APTPL_MAX_TPORT_LEN			256
 | 
			
		||||
	unsigned char pr_tport[PR_APTPL_MAX_TPORT_LEN];
 | 
			
		||||
	/* For writing out live meta data */
 | 
			
		||||
	unsigned char *pr_aptpl_buf;
 | 
			
		||||
	u16 pr_aptpl_rpti;
 | 
			
		||||
	u16 pr_reg_tpgt;
 | 
			
		||||
	/* Reservation effects all target ports */
 | 
			
		||||
	int pr_reg_all_tg_pt;
 | 
			
		||||
	/* Activate Persistence across Target Power Loss */
 | 
			
		||||
	int pr_reg_aptpl;
 | 
			
		||||
	int pr_res_holder;
 | 
			
		||||
	int pr_res_type;
 | 
			
		||||
	int pr_res_scope;
 | 
			
		||||
	/* Used for fabric initiator WWPNs using a ISID */
 | 
			
		||||
	int isid_present_at_reg:1;
 | 
			
		||||
	u32 pr_res_mapped_lun;
 | 
			
		||||
	u32 pr_aptpl_target_lun;
 | 
			
		||||
	u32 pr_res_generation;
 | 
			
		||||
	u64 pr_reg_bin_isid;
 | 
			
		||||
	u64 pr_res_key;
 | 
			
		||||
	atomic_t pr_res_holders;
 | 
			
		||||
	struct se_node_acl *pr_reg_nacl;
 | 
			
		||||
	struct se_dev_entry *pr_reg_deve;
 | 
			
		||||
	struct se_lun *pr_reg_tg_pt_lun;
 | 
			
		||||
	struct list_head pr_reg_list;
 | 
			
		||||
	struct list_head pr_reg_abort_list;
 | 
			
		||||
	struct list_head pr_reg_aptpl_list;
 | 
			
		||||
	struct list_head pr_reg_atp_list;
 | 
			
		||||
	struct list_head pr_reg_atp_mem_list;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * This set of function pointer ops is set based upon SPC3_PERSISTENT_RESERVATIONS,
 | 
			
		||||
 * SPC2_RESERVATIONS or SPC_PASSTHROUGH in drivers/target/target_core_pr.c:
 | 
			
		||||
 * core_setup_reservations()
 | 
			
		||||
 */
 | 
			
		||||
struct t10_reservation_ops {
 | 
			
		||||
	int (*t10_reservation_check)(struct se_cmd *, u32 *);
 | 
			
		||||
	int (*t10_seq_non_holder)(struct se_cmd *, unsigned char *, u32);
 | 
			
		||||
	int (*t10_pr_register)(struct se_cmd *);
 | 
			
		||||
	int (*t10_pr_clear)(struct se_cmd *);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct t10_reservation_template {
 | 
			
		||||
	/* Reservation effects all target ports */
 | 
			
		||||
	int pr_all_tg_pt;
 | 
			
		||||
	/* Activate Persistence across Target Power Loss enabled
 | 
			
		||||
	 * for SCSI device */
 | 
			
		||||
	int pr_aptpl_active;
 | 
			
		||||
	/* Used by struct t10_reservation_template->pr_aptpl_buf_len */
 | 
			
		||||
#define PR_APTPL_BUF_LEN			8192
 | 
			
		||||
	u32 pr_aptpl_buf_len;
 | 
			
		||||
	u32 pr_generation;
 | 
			
		||||
	t10_reservations_index_t res_type;
 | 
			
		||||
	spinlock_t registration_lock;
 | 
			
		||||
	spinlock_t aptpl_reg_lock;
 | 
			
		||||
	/*
 | 
			
		||||
	 * This will always be set by one individual I_T Nexus.
 | 
			
		||||
	 * However with all_tg_pt=1, other I_T Nexus from the
 | 
			
		||||
	 * same initiator can access PR reg/res info on a different
 | 
			
		||||
	 * target port.
 | 
			
		||||
	 *
 | 
			
		||||
	 * There is also the 'All Registrants' case, where there is
 | 
			
		||||
	 * a single *pr_res_holder of the reservation, but all
 | 
			
		||||
	 * registrations are considered reservation holders.
 | 
			
		||||
	 */
 | 
			
		||||
	struct se_node_acl *pr_res_holder;
 | 
			
		||||
	struct list_head registration_list;
 | 
			
		||||
	struct list_head aptpl_reg_list;
 | 
			
		||||
	struct t10_reservation_ops pr_ops;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct se_queue_req {
 | 
			
		||||
	int			state;
 | 
			
		||||
	void			*cmd;
 | 
			
		||||
	struct list_head	qr_list;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct se_queue_obj {
 | 
			
		||||
	atomic_t		queue_cnt;
 | 
			
		||||
	spinlock_t		cmd_queue_lock;
 | 
			
		||||
	struct list_head	qobj_list;
 | 
			
		||||
	wait_queue_head_t	thread_wq;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Used one per struct se_cmd to hold all extra struct se_task
 | 
			
		||||
 * metadata.  This structure is setup and allocated in
 | 
			
		||||
 * drivers/target/target_core_transport.c:__transport_alloc_se_cmd()
 | 
			
		||||
 */
 | 
			
		||||
struct se_transport_task {
 | 
			
		||||
	unsigned char		*t_task_cdb;
 | 
			
		||||
	unsigned char		__t_task_cdb[TCM_MAX_COMMAND_SIZE];
 | 
			
		||||
	unsigned long long	t_task_lba;
 | 
			
		||||
	int			t_tasks_failed;
 | 
			
		||||
	int			t_tasks_fua;
 | 
			
		||||
	int			t_tasks_bidi:1;
 | 
			
		||||
	u32			t_task_cdbs;
 | 
			
		||||
	u32			t_tasks_check;
 | 
			
		||||
	u32			t_tasks_no;
 | 
			
		||||
	u32			t_tasks_sectors;
 | 
			
		||||
	u32			t_tasks_se_num;
 | 
			
		||||
	u32			t_tasks_se_bidi_num;
 | 
			
		||||
	u32			t_tasks_sg_chained_no;
 | 
			
		||||
	atomic_t		t_fe_count;
 | 
			
		||||
	atomic_t		t_se_count;
 | 
			
		||||
	atomic_t		t_task_cdbs_left;
 | 
			
		||||
	atomic_t		t_task_cdbs_ex_left;
 | 
			
		||||
	atomic_t		t_task_cdbs_timeout_left;
 | 
			
		||||
	atomic_t		t_task_cdbs_sent;
 | 
			
		||||
	atomic_t		t_transport_aborted;
 | 
			
		||||
	atomic_t		t_transport_active;
 | 
			
		||||
	atomic_t		t_transport_complete;
 | 
			
		||||
	atomic_t		t_transport_queue_active;
 | 
			
		||||
	atomic_t		t_transport_sent;
 | 
			
		||||
	atomic_t		t_transport_stop;
 | 
			
		||||
	atomic_t		t_transport_timeout;
 | 
			
		||||
	atomic_t		transport_dev_active;
 | 
			
		||||
	atomic_t		transport_lun_active;
 | 
			
		||||
	atomic_t		transport_lun_fe_stop;
 | 
			
		||||
	atomic_t		transport_lun_stop;
 | 
			
		||||
	spinlock_t		t_state_lock;
 | 
			
		||||
	struct completion	t_transport_stop_comp;
 | 
			
		||||
	struct completion	transport_lun_fe_stop_comp;
 | 
			
		||||
	struct completion	transport_lun_stop_comp;
 | 
			
		||||
	struct scatterlist	*t_tasks_sg_chained;
 | 
			
		||||
	struct scatterlist	t_tasks_sg_bounce;
 | 
			
		||||
	void			*t_task_buf;
 | 
			
		||||
	/*
 | 
			
		||||
	 * Used for pre-registered fabric SGL passthrough WRITE and READ
 | 
			
		||||
	 * with the special SCF_PASSTHROUGH_CONTIG_TO_SG case for TCM_Loop
 | 
			
		||||
	 * and other HW target mode fabric modules.
 | 
			
		||||
	 */
 | 
			
		||||
	struct scatterlist	*t_task_pt_sgl;
 | 
			
		||||
	struct list_head	*t_mem_list;
 | 
			
		||||
	/* Used for BIDI READ */
 | 
			
		||||
	struct list_head	*t_mem_bidi_list;
 | 
			
		||||
	struct list_head	t_task_list;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct se_task {
 | 
			
		||||
	unsigned char	task_sense;
 | 
			
		||||
	struct scatterlist *task_sg;
 | 
			
		||||
	struct scatterlist *task_sg_bidi;
 | 
			
		||||
	u8		task_scsi_status;
 | 
			
		||||
	u8		task_flags;
 | 
			
		||||
	int		task_error_status;
 | 
			
		||||
	int		task_state_flags;
 | 
			
		||||
	int		task_padded_sg:1;
 | 
			
		||||
	unsigned long long	task_lba;
 | 
			
		||||
	u32		task_no;
 | 
			
		||||
	u32		task_sectors;
 | 
			
		||||
	u32		task_size;
 | 
			
		||||
	u32		task_sg_num;
 | 
			
		||||
	u32		task_sg_offset;
 | 
			
		||||
	enum dma_data_direction	task_data_direction;
 | 
			
		||||
	struct se_cmd *task_se_cmd;
 | 
			
		||||
	struct se_device	*se_dev;
 | 
			
		||||
	struct completion	task_stop_comp;
 | 
			
		||||
	atomic_t	task_active;
 | 
			
		||||
	atomic_t	task_execute_queue;
 | 
			
		||||
	atomic_t	task_timeout;
 | 
			
		||||
	atomic_t	task_sent;
 | 
			
		||||
	atomic_t	task_stop;
 | 
			
		||||
	atomic_t	task_state_active;
 | 
			
		||||
	struct timer_list	task_timer;
 | 
			
		||||
	struct se_device *se_obj_ptr;
 | 
			
		||||
	struct list_head t_list;
 | 
			
		||||
	struct list_head t_execute_list;
 | 
			
		||||
	struct list_head t_state_list;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
#define TASK_CMD(task)	((struct se_cmd *)task->task_se_cmd)
 | 
			
		||||
#define TASK_DEV(task)	((struct se_device *)task->se_dev)
 | 
			
		||||
 | 
			
		||||
struct se_cmd {
 | 
			
		||||
	/* SAM response code being sent to initiator */
 | 
			
		||||
	u8			scsi_status;
 | 
			
		||||
	u8			scsi_asc;
 | 
			
		||||
	u8			scsi_ascq;
 | 
			
		||||
	u8			scsi_sense_reason;
 | 
			
		||||
	u16			scsi_sense_length;
 | 
			
		||||
	/* Delay for ALUA Active/NonOptimized state access in milliseconds */
 | 
			
		||||
	int			alua_nonop_delay;
 | 
			
		||||
	/* See include/linux/dma-mapping.h */
 | 
			
		||||
	enum dma_data_direction	data_direction;
 | 
			
		||||
	/* For SAM Task Attribute */
 | 
			
		||||
	int			sam_task_attr;
 | 
			
		||||
	/* Transport protocol dependent state, see transport_state_table */
 | 
			
		||||
	enum transport_state_table t_state;
 | 
			
		||||
	/* Transport protocol dependent state for out of order CmdSNs */
 | 
			
		||||
	int			deferred_t_state;
 | 
			
		||||
	/* Transport specific error status */
 | 
			
		||||
	int			transport_error_status;
 | 
			
		||||
	/* See se_cmd_flags_table */
 | 
			
		||||
	u32			se_cmd_flags;
 | 
			
		||||
	u32			se_ordered_id;
 | 
			
		||||
	/* Total size in bytes associated with command */
 | 
			
		||||
	u32			data_length;
 | 
			
		||||
	/* SCSI Presented Data Transfer Length */
 | 
			
		||||
	u32			cmd_spdtl;
 | 
			
		||||
	u32			residual_count;
 | 
			
		||||
	u32			orig_fe_lun;
 | 
			
		||||
	/* Persistent Reservation key */
 | 
			
		||||
	u64			pr_res_key;
 | 
			
		||||
	atomic_t                transport_sent;
 | 
			
		||||
	/* Used for sense data */
 | 
			
		||||
	void			*sense_buffer;
 | 
			
		||||
	struct list_head	se_delayed_list;
 | 
			
		||||
	struct list_head	se_ordered_list;
 | 
			
		||||
	struct list_head	se_lun_list;
 | 
			
		||||
	struct se_device      *se_dev;
 | 
			
		||||
	struct se_dev_entry   *se_deve;
 | 
			
		||||
	struct se_device	*se_obj_ptr;
 | 
			
		||||
	struct se_device	*se_orig_obj_ptr;
 | 
			
		||||
	struct se_lun		*se_lun;
 | 
			
		||||
	/* Only used for internal passthrough and legacy TCM fabric modules */
 | 
			
		||||
	struct se_session	*se_sess;
 | 
			
		||||
	struct se_tmr_req	*se_tmr_req;
 | 
			
		||||
	/* t_task is setup to t_task_backstore in transport_init_se_cmd() */
 | 
			
		||||
	struct se_transport_task *t_task;
 | 
			
		||||
	struct se_transport_task t_task_backstore;
 | 
			
		||||
	struct target_core_fabric_ops *se_tfo;
 | 
			
		||||
	int (*transport_emulate_cdb)(struct se_cmd *);
 | 
			
		||||
	void (*transport_split_cdb)(unsigned long long, u32 *, unsigned char *);
 | 
			
		||||
	void (*transport_wait_for_tasks)(struct se_cmd *, int, int);
 | 
			
		||||
	void (*transport_complete_callback)(struct se_cmd *);
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
#define T_TASK(cmd)     ((struct se_transport_task *)(cmd->t_task))
 | 
			
		||||
#define CMD_TFO(cmd) ((struct target_core_fabric_ops *)cmd->se_tfo)
 | 
			
		||||
 | 
			
		||||
struct se_tmr_req {
 | 
			
		||||
	/* Task Management function to be preformed */
 | 
			
		||||
	u8			function;
 | 
			
		||||
	/* Task Management response to send */
 | 
			
		||||
	u8			response;
 | 
			
		||||
	int			call_transport;
 | 
			
		||||
	/* Reference to ITT that Task Mgmt should be preformed */
 | 
			
		||||
	u32			ref_task_tag;
 | 
			
		||||
	/* 64-bit encoded SAM LUN from $FABRIC_MOD TMR header */
 | 
			
		||||
	u64			ref_task_lun;
 | 
			
		||||
	void 			*fabric_tmr_ptr;
 | 
			
		||||
	struct se_cmd		*task_cmd;
 | 
			
		||||
	struct se_cmd		*ref_cmd;
 | 
			
		||||
	struct se_device	*tmr_dev;
 | 
			
		||||
	struct se_lun		*tmr_lun;
 | 
			
		||||
	struct list_head	tmr_list;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct se_ua {
 | 
			
		||||
	u8			ua_asc;
 | 
			
		||||
	u8			ua_ascq;
 | 
			
		||||
	struct se_node_acl	*ua_nacl;
 | 
			
		||||
	struct list_head	ua_dev_list;
 | 
			
		||||
	struct list_head	ua_nacl_list;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct se_node_acl {
 | 
			
		||||
	char			initiatorname[TRANSPORT_IQN_LEN];
 | 
			
		||||
	/* Used to signal demo mode created ACL, disabled by default */
 | 
			
		||||
	int			dynamic_node_acl:1;
 | 
			
		||||
	u32			queue_depth;
 | 
			
		||||
	u32			acl_index;
 | 
			
		||||
	u64			num_cmds;
 | 
			
		||||
	u64			read_bytes;
 | 
			
		||||
	u64			write_bytes;
 | 
			
		||||
	spinlock_t		stats_lock;
 | 
			
		||||
	/* Used for PR SPEC_I_PT=1 and REGISTER_AND_MOVE */
 | 
			
		||||
	atomic_t		acl_pr_ref_count;
 | 
			
		||||
	/* Used for MIB access */
 | 
			
		||||
	atomic_t		mib_ref_count;
 | 
			
		||||
	struct se_dev_entry	*device_list;
 | 
			
		||||
	struct se_session	*nacl_sess;
 | 
			
		||||
	struct se_portal_group *se_tpg;
 | 
			
		||||
	spinlock_t		device_list_lock;
 | 
			
		||||
	spinlock_t		nacl_sess_lock;
 | 
			
		||||
	struct config_group	acl_group;
 | 
			
		||||
	struct config_group	acl_attrib_group;
 | 
			
		||||
	struct config_group	acl_auth_group;
 | 
			
		||||
	struct config_group	acl_param_group;
 | 
			
		||||
	struct config_group	*acl_default_groups[4];
 | 
			
		||||
	struct list_head	acl_list;
 | 
			
		||||
	struct list_head	acl_sess_list;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct se_session {
 | 
			
		||||
	/* Used for MIB access */
 | 
			
		||||
	atomic_t		mib_ref_count;
 | 
			
		||||
	u64			sess_bin_isid;
 | 
			
		||||
	struct se_node_acl	*se_node_acl;
 | 
			
		||||
	struct se_portal_group *se_tpg;
 | 
			
		||||
	void			*fabric_sess_ptr;
 | 
			
		||||
	struct list_head	sess_list;
 | 
			
		||||
	struct list_head	sess_acl_list;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
#define SE_SESS(cmd)		((struct se_session *)(cmd)->se_sess)
 | 
			
		||||
#define SE_NODE_ACL(sess)	((struct se_node_acl *)(sess)->se_node_acl)
 | 
			
		||||
 | 
			
		||||
struct se_device;
 | 
			
		||||
struct se_transform_info;
 | 
			
		||||
struct scatterlist;
 | 
			
		||||
 | 
			
		||||
struct se_lun_acl {
 | 
			
		||||
	char			initiatorname[TRANSPORT_IQN_LEN];
 | 
			
		||||
	u32			mapped_lun;
 | 
			
		||||
	struct se_node_acl	*se_lun_nacl;
 | 
			
		||||
	struct se_lun		*se_lun;
 | 
			
		||||
	struct list_head	lacl_list;
 | 
			
		||||
	struct config_group	se_lun_group;
 | 
			
		||||
}  ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct se_dev_entry {
 | 
			
		||||
	int			def_pr_registered:1;
 | 
			
		||||
	/* See transport_lunflags_table */
 | 
			
		||||
	u32			lun_flags;
 | 
			
		||||
	u32			deve_cmds;
 | 
			
		||||
	u32			mapped_lun;
 | 
			
		||||
	u32			average_bytes;
 | 
			
		||||
	u32			last_byte_count;
 | 
			
		||||
	u32			total_cmds;
 | 
			
		||||
	u32			total_bytes;
 | 
			
		||||
	u64			pr_res_key;
 | 
			
		||||
	u64			creation_time;
 | 
			
		||||
	u32			attach_count;
 | 
			
		||||
	u64			read_bytes;
 | 
			
		||||
	u64			write_bytes;
 | 
			
		||||
	atomic_t		ua_count;
 | 
			
		||||
	/* Used for PR SPEC_I_PT=1 and REGISTER_AND_MOVE */
 | 
			
		||||
	atomic_t		pr_ref_count;
 | 
			
		||||
	struct se_lun_acl	*se_lun_acl;
 | 
			
		||||
	spinlock_t		ua_lock;
 | 
			
		||||
	struct se_lun		*se_lun;
 | 
			
		||||
	struct list_head	alua_port_list;
 | 
			
		||||
	struct list_head	ua_list;
 | 
			
		||||
}  ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct se_dev_limits {
 | 
			
		||||
	/* Max supported HW queue depth */
 | 
			
		||||
	u32		hw_queue_depth;
 | 
			
		||||
	/* Max supported virtual queue depth */
 | 
			
		||||
	u32		queue_depth;
 | 
			
		||||
	/* From include/linux/blkdev.h for the other HW/SW limits. */
 | 
			
		||||
	struct queue_limits limits;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct se_dev_attrib {
 | 
			
		||||
	int		emulate_dpo;
 | 
			
		||||
	int		emulate_fua_write;
 | 
			
		||||
	int		emulate_fua_read;
 | 
			
		||||
	int		emulate_write_cache;
 | 
			
		||||
	int		emulate_ua_intlck_ctrl;
 | 
			
		||||
	int		emulate_tas;
 | 
			
		||||
	int		emulate_tpu;
 | 
			
		||||
	int		emulate_tpws;
 | 
			
		||||
	int		emulate_reservations;
 | 
			
		||||
	int		emulate_alua;
 | 
			
		||||
	int		enforce_pr_isids;
 | 
			
		||||
	u32		hw_block_size;
 | 
			
		||||
	u32		block_size;
 | 
			
		||||
	u32		hw_max_sectors;
 | 
			
		||||
	u32		max_sectors;
 | 
			
		||||
	u32		optimal_sectors;
 | 
			
		||||
	u32		hw_queue_depth;
 | 
			
		||||
	u32		queue_depth;
 | 
			
		||||
	u32		task_timeout;
 | 
			
		||||
	u32		max_unmap_lba_count;
 | 
			
		||||
	u32		max_unmap_block_desc_count;
 | 
			
		||||
	u32		unmap_granularity;
 | 
			
		||||
	u32		unmap_granularity_alignment;
 | 
			
		||||
	struct se_subsystem_dev *da_sub_dev;
 | 
			
		||||
	struct config_group da_group;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct se_subsystem_dev {
 | 
			
		||||
/* Used for struct se_subsystem_dev-->se_dev_alias, must be less than PAGE_SIZE */
 | 
			
		||||
#define SE_DEV_ALIAS_LEN		512
 | 
			
		||||
	unsigned char	se_dev_alias[SE_DEV_ALIAS_LEN];
 | 
			
		||||
/* Used for struct se_subsystem_dev->se_dev_udev_path[], must be less than PAGE_SIZE */
 | 
			
		||||
#define SE_UDEV_PATH_LEN		512
 | 
			
		||||
	unsigned char	se_dev_udev_path[SE_UDEV_PATH_LEN];
 | 
			
		||||
	u32		su_dev_flags;
 | 
			
		||||
	struct se_hba *se_dev_hba;
 | 
			
		||||
	struct se_device *se_dev_ptr;
 | 
			
		||||
	struct se_dev_attrib se_dev_attrib;
 | 
			
		||||
	/* T10 Asymmetric Logical Unit Assignment for Target Ports */
 | 
			
		||||
	struct t10_alua	t10_alua;
 | 
			
		||||
	/* T10 Inquiry and VPD WWN Information */
 | 
			
		||||
	struct t10_wwn	t10_wwn;
 | 
			
		||||
	/* T10 SPC-2 + SPC-3 Reservations */
 | 
			
		||||
	struct t10_reservation_template t10_reservation;
 | 
			
		||||
	spinlock_t      se_dev_lock;
 | 
			
		||||
	void            *se_dev_su_ptr;
 | 
			
		||||
	struct list_head g_se_dev_list;
 | 
			
		||||
	struct config_group se_dev_group;
 | 
			
		||||
	/* For T10 Reservations */
 | 
			
		||||
	struct config_group se_dev_pr_group;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
#define T10_ALUA(su_dev)	(&(su_dev)->t10_alua)
 | 
			
		||||
#define T10_RES(su_dev)		(&(su_dev)->t10_reservation)
 | 
			
		||||
#define T10_PR_OPS(su_dev)	(&(su_dev)->t10_reservation.pr_ops)
 | 
			
		||||
 | 
			
		||||
struct se_device {
 | 
			
		||||
	/* Set to 1 if thread is NOT sleeping on thread_sem */
 | 
			
		||||
	u8			thread_active;
 | 
			
		||||
	u8			dev_status_timer_flags;
 | 
			
		||||
	/* RELATIVE TARGET PORT IDENTIFER Counter */
 | 
			
		||||
	u16			dev_rpti_counter;
 | 
			
		||||
	/* Used for SAM Task Attribute ordering */
 | 
			
		||||
	u32			dev_cur_ordered_id;
 | 
			
		||||
	u32			dev_flags;
 | 
			
		||||
	u32			dev_port_count;
 | 
			
		||||
	/* See transport_device_status_table */
 | 
			
		||||
	u32			dev_status;
 | 
			
		||||
	u32			dev_tcq_window_closed;
 | 
			
		||||
	/* Physical device queue depth */
 | 
			
		||||
	u32			queue_depth;
 | 
			
		||||
	/* Used for SPC-2 reservations enforce of ISIDs */
 | 
			
		||||
	u64			dev_res_bin_isid;
 | 
			
		||||
	t10_task_attr_index_t	dev_task_attr_type;
 | 
			
		||||
	/* Pointer to transport specific device structure */
 | 
			
		||||
	void 			*dev_ptr;
 | 
			
		||||
	u32			dev_index;
 | 
			
		||||
	u64			creation_time;
 | 
			
		||||
	u32			num_resets;
 | 
			
		||||
	u64			num_cmds;
 | 
			
		||||
	u64			read_bytes;
 | 
			
		||||
	u64			write_bytes;
 | 
			
		||||
	spinlock_t		stats_lock;
 | 
			
		||||
	/* Active commands on this virtual SE device */
 | 
			
		||||
	atomic_t		active_cmds;
 | 
			
		||||
	atomic_t		simple_cmds;
 | 
			
		||||
	atomic_t		depth_left;
 | 
			
		||||
	atomic_t		dev_ordered_id;
 | 
			
		||||
	atomic_t		dev_tur_active;
 | 
			
		||||
	atomic_t		execute_tasks;
 | 
			
		||||
	atomic_t		dev_status_thr_count;
 | 
			
		||||
	atomic_t		dev_hoq_count;
 | 
			
		||||
	atomic_t		dev_ordered_sync;
 | 
			
		||||
	struct se_obj		dev_obj;
 | 
			
		||||
	struct se_obj		dev_access_obj;
 | 
			
		||||
	struct se_obj		dev_export_obj;
 | 
			
		||||
	struct se_queue_obj	*dev_queue_obj;
 | 
			
		||||
	struct se_queue_obj	*dev_status_queue_obj;
 | 
			
		||||
	spinlock_t		delayed_cmd_lock;
 | 
			
		||||
	spinlock_t		ordered_cmd_lock;
 | 
			
		||||
	spinlock_t		execute_task_lock;
 | 
			
		||||
	spinlock_t		state_task_lock;
 | 
			
		||||
	spinlock_t		dev_alua_lock;
 | 
			
		||||
	spinlock_t		dev_reservation_lock;
 | 
			
		||||
	spinlock_t		dev_state_lock;
 | 
			
		||||
	spinlock_t		dev_status_lock;
 | 
			
		||||
	spinlock_t		dev_status_thr_lock;
 | 
			
		||||
	spinlock_t		se_port_lock;
 | 
			
		||||
	spinlock_t		se_tmr_lock;
 | 
			
		||||
	/* Used for legacy SPC-2 reservationsa */
 | 
			
		||||
	struct se_node_acl	*dev_reserved_node_acl;
 | 
			
		||||
	/* Used for ALUA Logical Unit Group membership */
 | 
			
		||||
	struct t10_alua_lu_gp_member *dev_alua_lu_gp_mem;
 | 
			
		||||
	/* Used for SPC-3 Persistent Reservations */
 | 
			
		||||
	struct t10_pr_registration *dev_pr_res_holder;
 | 
			
		||||
	struct list_head	dev_sep_list;
 | 
			
		||||
	struct list_head	dev_tmr_list;
 | 
			
		||||
	struct timer_list	dev_status_timer;
 | 
			
		||||
	/* Pointer to descriptor for processing thread */
 | 
			
		||||
	struct task_struct	*process_thread;
 | 
			
		||||
	pid_t			process_thread_pid;
 | 
			
		||||
	struct task_struct		*dev_mgmt_thread;
 | 
			
		||||
	struct list_head	delayed_cmd_list;
 | 
			
		||||
	struct list_head	ordered_cmd_list;
 | 
			
		||||
	struct list_head	execute_task_list;
 | 
			
		||||
	struct list_head	state_task_list;
 | 
			
		||||
	/* Pointer to associated SE HBA */
 | 
			
		||||
	struct se_hba		*se_hba;
 | 
			
		||||
	struct se_subsystem_dev *se_sub_dev;
 | 
			
		||||
	/* Pointer to template of function pointers for transport */
 | 
			
		||||
	struct se_subsystem_api *transport;
 | 
			
		||||
	/* Linked list for struct se_hba struct se_device list */
 | 
			
		||||
	struct list_head	dev_list;
 | 
			
		||||
	/* Linked list for struct se_global->g_se_dev_list */
 | 
			
		||||
	struct list_head	g_se_dev_list;
 | 
			
		||||
}  ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
#define SE_DEV(cmd)		((struct se_device *)(cmd)->se_lun->lun_se_dev)
 | 
			
		||||
#define SU_DEV(dev)		((struct se_subsystem_dev *)(dev)->se_sub_dev)
 | 
			
		||||
#define DEV_ATTRIB(dev)		(&(dev)->se_sub_dev->se_dev_attrib)
 | 
			
		||||
#define DEV_T10_WWN(dev)	(&(dev)->se_sub_dev->t10_wwn)
 | 
			
		||||
 | 
			
		||||
struct se_hba {
 | 
			
		||||
	u16			hba_tpgt;
 | 
			
		||||
	u32			hba_id;
 | 
			
		||||
	/* See hba_flags_table */
 | 
			
		||||
	u32			hba_flags;
 | 
			
		||||
	/* Virtual iSCSI devices attached. */
 | 
			
		||||
	u32			dev_count;
 | 
			
		||||
	u32			hba_index;
 | 
			
		||||
	atomic_t		dev_mib_access_count;
 | 
			
		||||
	atomic_t		load_balance_queue;
 | 
			
		||||
	atomic_t		left_queue_depth;
 | 
			
		||||
	/* Maximum queue depth the HBA can handle. */
 | 
			
		||||
	atomic_t		max_queue_depth;
 | 
			
		||||
	/* Pointer to transport specific host structure. */
 | 
			
		||||
	void			*hba_ptr;
 | 
			
		||||
	/* Linked list for struct se_device */
 | 
			
		||||
	struct list_head	hba_dev_list;
 | 
			
		||||
	struct list_head	hba_list;
 | 
			
		||||
	spinlock_t		device_lock;
 | 
			
		||||
	spinlock_t		hba_queue_lock;
 | 
			
		||||
	struct config_group	hba_group;
 | 
			
		||||
	struct mutex		hba_access_mutex;
 | 
			
		||||
	struct se_subsystem_api *transport;
 | 
			
		||||
}  ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
#define SE_HBA(d)		((struct se_hba *)(d)->se_hba)
 | 
			
		||||
 | 
			
		||||
struct se_lun {
 | 
			
		||||
	/* See transport_lun_status_table */
 | 
			
		||||
	enum transport_lun_status_table lun_status;
 | 
			
		||||
	u32			lun_access;
 | 
			
		||||
	u32			lun_flags;
 | 
			
		||||
	u32			unpacked_lun;
 | 
			
		||||
	atomic_t		lun_acl_count;
 | 
			
		||||
	spinlock_t		lun_acl_lock;
 | 
			
		||||
	spinlock_t		lun_cmd_lock;
 | 
			
		||||
	spinlock_t		lun_sep_lock;
 | 
			
		||||
	struct completion	lun_shutdown_comp;
 | 
			
		||||
	struct list_head	lun_cmd_list;
 | 
			
		||||
	struct list_head	lun_acl_list;
 | 
			
		||||
	struct se_device	*lun_se_dev;
 | 
			
		||||
	struct config_group	lun_group;
 | 
			
		||||
	struct se_port	*lun_sep;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
#define SE_LUN(c)		((struct se_lun *)(c)->se_lun)
 | 
			
		||||
 | 
			
		||||
struct se_port {
 | 
			
		||||
	/* RELATIVE TARGET PORT IDENTIFER */
 | 
			
		||||
	u16		sep_rtpi;
 | 
			
		||||
	int		sep_tg_pt_secondary_stat;
 | 
			
		||||
	int		sep_tg_pt_secondary_write_md;
 | 
			
		||||
	u32		sep_index;
 | 
			
		||||
	struct scsi_port_stats sep_stats;
 | 
			
		||||
	/* Used for ALUA Target Port Groups membership */
 | 
			
		||||
	atomic_t	sep_tg_pt_gp_active;
 | 
			
		||||
	atomic_t	sep_tg_pt_secondary_offline;
 | 
			
		||||
	/* Used for PR ALL_TG_PT=1 */
 | 
			
		||||
	atomic_t	sep_tg_pt_ref_cnt;
 | 
			
		||||
	spinlock_t	sep_alua_lock;
 | 
			
		||||
	struct mutex	sep_tg_pt_md_mutex;
 | 
			
		||||
	struct t10_alua_tg_pt_gp_member *sep_alua_tg_pt_gp_mem;
 | 
			
		||||
	struct se_lun *sep_lun;
 | 
			
		||||
	struct se_portal_group *sep_tpg;
 | 
			
		||||
	struct list_head sep_alua_list;
 | 
			
		||||
	struct list_head sep_list;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct se_tpg_np {
 | 
			
		||||
	struct config_group	tpg_np_group;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct se_portal_group {
 | 
			
		||||
	/* Type of target portal group, see transport_tpg_type_table */
 | 
			
		||||
	enum transport_tpg_type_table se_tpg_type;
 | 
			
		||||
	/* Number of ACLed Initiator Nodes for this TPG */
 | 
			
		||||
	u32			num_node_acls;
 | 
			
		||||
	/* Used for PR SPEC_I_PT=1 and REGISTER_AND_MOVE */
 | 
			
		||||
	atomic_t		tpg_pr_ref_count;
 | 
			
		||||
	/* Spinlock for adding/removing ACLed Nodes */
 | 
			
		||||
	spinlock_t		acl_node_lock;
 | 
			
		||||
	/* Spinlock for adding/removing sessions */
 | 
			
		||||
	spinlock_t		session_lock;
 | 
			
		||||
	spinlock_t		tpg_lun_lock;
 | 
			
		||||
	/* Pointer to $FABRIC_MOD portal group */
 | 
			
		||||
	void			*se_tpg_fabric_ptr;
 | 
			
		||||
	struct list_head	se_tpg_list;
 | 
			
		||||
	/* linked list for initiator ACL list */
 | 
			
		||||
	struct list_head	acl_node_list;
 | 
			
		||||
	struct se_lun		*tpg_lun_list;
 | 
			
		||||
	struct se_lun		tpg_virt_lun0;
 | 
			
		||||
	/* List of TCM sessions assoicated wth this TPG */
 | 
			
		||||
	struct list_head	tpg_sess_list;
 | 
			
		||||
	/* Pointer to $FABRIC_MOD dependent code */
 | 
			
		||||
	struct target_core_fabric_ops *se_tpg_tfo;
 | 
			
		||||
	struct se_wwn		*se_tpg_wwn;
 | 
			
		||||
	struct config_group	tpg_group;
 | 
			
		||||
	struct config_group	*tpg_default_groups[6];
 | 
			
		||||
	struct config_group	tpg_lun_group;
 | 
			
		||||
	struct config_group	tpg_np_group;
 | 
			
		||||
	struct config_group	tpg_acl_group;
 | 
			
		||||
	struct config_group	tpg_attrib_group;
 | 
			
		||||
	struct config_group	tpg_param_group;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
#define TPG_TFO(se_tpg)	((struct target_core_fabric_ops *)(se_tpg)->se_tpg_tfo)
 | 
			
		||||
 | 
			
		||||
struct se_wwn {
 | 
			
		||||
	struct target_fabric_configfs *wwn_tf;
 | 
			
		||||
	struct config_group	wwn_group;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
struct se_global {
 | 
			
		||||
	u16			alua_lu_gps_counter;
 | 
			
		||||
	int			g_sub_api_initialized;
 | 
			
		||||
	u32			in_shutdown;
 | 
			
		||||
	u32			alua_lu_gps_count;
 | 
			
		||||
	u32			g_hba_id_counter;
 | 
			
		||||
	struct config_group	target_core_hbagroup;
 | 
			
		||||
	struct config_group	alua_group;
 | 
			
		||||
	struct config_group	alua_lu_gps_group;
 | 
			
		||||
	struct list_head	g_lu_gps_list;
 | 
			
		||||
	struct list_head	g_se_tpg_list;
 | 
			
		||||
	struct list_head	g_hba_list;
 | 
			
		||||
	struct list_head	g_se_dev_list;
 | 
			
		||||
	struct se_hba		*g_lun0_hba;
 | 
			
		||||
	struct se_subsystem_dev *g_lun0_su_dev;
 | 
			
		||||
	struct se_device	*g_lun0_dev;
 | 
			
		||||
	struct t10_alua_lu_gp	*default_lu_gp;
 | 
			
		||||
	spinlock_t		g_device_lock;
 | 
			
		||||
	spinlock_t		hba_lock;
 | 
			
		||||
	spinlock_t		se_tpg_lock;
 | 
			
		||||
	spinlock_t		lu_gps_lock;
 | 
			
		||||
	spinlock_t		plugin_class_lock;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
#endif /* TARGET_CORE_BASE_H */
 | 
			
		||||
							
								
								
									
										52
									
								
								include/target/target_core_configfs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								include/target/target_core_configfs.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,52 @@
 | 
			
		|||
#define TARGET_CORE_CONFIGFS_VERSION TARGET_CORE_MOD_VERSION
 | 
			
		||||
 | 
			
		||||
#define TARGET_CORE_CONFIG_ROOT	"/sys/kernel/config"
 | 
			
		||||
 | 
			
		||||
#define TARGET_CORE_NAME_MAX_LEN	64
 | 
			
		||||
#define TARGET_FABRIC_NAME_SIZE		32
 | 
			
		||||
 | 
			
		||||
extern struct target_fabric_configfs *target_fabric_configfs_init(
 | 
			
		||||
				struct module *, const char *);
 | 
			
		||||
extern void target_fabric_configfs_free(struct target_fabric_configfs *);
 | 
			
		||||
extern int target_fabric_configfs_register(struct target_fabric_configfs *);
 | 
			
		||||
extern void target_fabric_configfs_deregister(struct target_fabric_configfs *);
 | 
			
		||||
 | 
			
		||||
struct target_fabric_configfs_template {
 | 
			
		||||
	struct config_item_type tfc_discovery_cit;
 | 
			
		||||
	struct config_item_type	tfc_wwn_cit;
 | 
			
		||||
	struct config_item_type tfc_tpg_cit;
 | 
			
		||||
	struct config_item_type tfc_tpg_base_cit;
 | 
			
		||||
	struct config_item_type tfc_tpg_lun_cit;
 | 
			
		||||
	struct config_item_type tfc_tpg_port_cit;
 | 
			
		||||
	struct config_item_type tfc_tpg_np_cit;
 | 
			
		||||
	struct config_item_type tfc_tpg_np_base_cit;
 | 
			
		||||
	struct config_item_type tfc_tpg_attrib_cit;
 | 
			
		||||
	struct config_item_type tfc_tpg_param_cit;
 | 
			
		||||
	struct config_item_type tfc_tpg_nacl_cit;
 | 
			
		||||
	struct config_item_type tfc_tpg_nacl_base_cit;
 | 
			
		||||
	struct config_item_type tfc_tpg_nacl_attrib_cit;
 | 
			
		||||
	struct config_item_type tfc_tpg_nacl_auth_cit;
 | 
			
		||||
	struct config_item_type tfc_tpg_nacl_param_cit;
 | 
			
		||||
	struct config_item_type tfc_tpg_mappedlun_cit;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct target_fabric_configfs {
 | 
			
		||||
	char			tf_name[TARGET_FABRIC_NAME_SIZE];
 | 
			
		||||
	atomic_t		tf_access_cnt;
 | 
			
		||||
	struct list_head	tf_list;
 | 
			
		||||
	struct config_group	tf_group;
 | 
			
		||||
	struct config_group	tf_disc_group;
 | 
			
		||||
	struct config_group	*tf_default_groups[2];
 | 
			
		||||
	/* Pointer to fabric's config_item */
 | 
			
		||||
	struct config_item	*tf_fabric;
 | 
			
		||||
	/* Passed from fabric modules */
 | 
			
		||||
	struct config_item_type	*tf_fabric_cit;
 | 
			
		||||
	/* Pointer to target core subsystem */
 | 
			
		||||
	struct configfs_subsystem *tf_subsys;
 | 
			
		||||
	/* Pointer to fabric's struct module */
 | 
			
		||||
	struct module *tf_module;
 | 
			
		||||
	struct target_core_fabric_ops tf_ops;
 | 
			
		||||
	struct target_fabric_configfs_template tf_cit_tmpl;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#define TF_CIT_TMPL(tf) (&(tf)->tf_cit_tmpl)
 | 
			
		||||
							
								
								
									
										61
									
								
								include/target/target_core_device.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								include/target/target_core_device.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,61 @@
 | 
			
		|||
#ifndef TARGET_CORE_DEVICE_H
 | 
			
		||||
#define TARGET_CORE_DEVICE_H
 | 
			
		||||
 | 
			
		||||
extern int transport_get_lun_for_cmd(struct se_cmd *, unsigned char *, u32);
 | 
			
		||||
extern int transport_get_lun_for_tmr(struct se_cmd *, u32);
 | 
			
		||||
extern struct se_dev_entry *core_get_se_deve_from_rtpi(
 | 
			
		||||
					struct se_node_acl *, u16);
 | 
			
		||||
extern int core_free_device_list_for_node(struct se_node_acl *,
 | 
			
		||||
					struct se_portal_group *);
 | 
			
		||||
extern void core_dec_lacl_count(struct se_node_acl *, struct se_cmd *);
 | 
			
		||||
extern void core_update_device_list_access(u32, u32, struct se_node_acl *);
 | 
			
		||||
extern int core_update_device_list_for_node(struct se_lun *, struct se_lun_acl *, u32,
 | 
			
		||||
					u32, struct se_node_acl *,
 | 
			
		||||
					struct se_portal_group *, int);
 | 
			
		||||
extern void core_clear_lun_from_tpg(struct se_lun *, struct se_portal_group *);
 | 
			
		||||
extern int core_dev_export(struct se_device *, struct se_portal_group *,
 | 
			
		||||
					struct se_lun *);
 | 
			
		||||
extern void core_dev_unexport(struct se_device *, struct se_portal_group *,
 | 
			
		||||
					struct se_lun *);
 | 
			
		||||
extern int transport_core_report_lun_response(struct se_cmd *);
 | 
			
		||||
extern void se_release_device_for_hba(struct se_device *);
 | 
			
		||||
extern void se_release_vpd_for_dev(struct se_device *);
 | 
			
		||||
extern void se_clear_dev_ports(struct se_device *);
 | 
			
		||||
extern int se_free_virtual_device(struct se_device *, struct se_hba *);
 | 
			
		||||
extern int se_dev_check_online(struct se_device *);
 | 
			
		||||
extern int se_dev_check_shutdown(struct se_device *);
 | 
			
		||||
extern void se_dev_set_default_attribs(struct se_device *, struct se_dev_limits *);
 | 
			
		||||
extern int se_dev_set_task_timeout(struct se_device *, u32);
 | 
			
		||||
extern int se_dev_set_max_unmap_lba_count(struct se_device *, u32);
 | 
			
		||||
extern int se_dev_set_max_unmap_block_desc_count(struct se_device *, u32);
 | 
			
		||||
extern int se_dev_set_unmap_granularity(struct se_device *, u32);
 | 
			
		||||
extern int se_dev_set_unmap_granularity_alignment(struct se_device *, u32);
 | 
			
		||||
extern int se_dev_set_emulate_dpo(struct se_device *, int);
 | 
			
		||||
extern int se_dev_set_emulate_fua_write(struct se_device *, int);
 | 
			
		||||
extern int se_dev_set_emulate_fua_read(struct se_device *, int);
 | 
			
		||||
extern int se_dev_set_emulate_write_cache(struct se_device *, int);
 | 
			
		||||
extern int se_dev_set_emulate_ua_intlck_ctrl(struct se_device *, int);
 | 
			
		||||
extern int se_dev_set_emulate_tas(struct se_device *, int);
 | 
			
		||||
extern int se_dev_set_emulate_tpu(struct se_device *, int);
 | 
			
		||||
extern int se_dev_set_emulate_tpws(struct se_device *, int);
 | 
			
		||||
extern int se_dev_set_enforce_pr_isids(struct se_device *, int);
 | 
			
		||||
extern int se_dev_set_queue_depth(struct se_device *, u32);
 | 
			
		||||
extern int se_dev_set_max_sectors(struct se_device *, u32);
 | 
			
		||||
extern int se_dev_set_optimal_sectors(struct se_device *, u32);
 | 
			
		||||
extern int se_dev_set_block_size(struct se_device *, u32);
 | 
			
		||||
extern struct se_lun *core_dev_add_lun(struct se_portal_group *, struct se_hba *,
 | 
			
		||||
					struct se_device *, u32);
 | 
			
		||||
extern int core_dev_del_lun(struct se_portal_group *, u32);
 | 
			
		||||
extern struct se_lun *core_get_lun_from_tpg(struct se_portal_group *, u32);
 | 
			
		||||
extern struct se_lun_acl *core_dev_init_initiator_node_lun_acl(struct se_portal_group *,
 | 
			
		||||
							u32, char *, int *);
 | 
			
		||||
extern int core_dev_add_initiator_node_lun_acl(struct se_portal_group *,
 | 
			
		||||
						struct se_lun_acl *, u32, u32);
 | 
			
		||||
extern int core_dev_del_initiator_node_lun_acl(struct se_portal_group *,
 | 
			
		||||
						struct se_lun *, struct se_lun_acl *);
 | 
			
		||||
extern void core_dev_free_initiator_node_lun_acl(struct se_portal_group *,
 | 
			
		||||
						struct se_lun_acl *lacl);
 | 
			
		||||
extern int core_dev_setup_virtual_lun0(void);
 | 
			
		||||
extern void core_dev_release_virtual_lun0(void);
 | 
			
		||||
 | 
			
		||||
#endif /* TARGET_CORE_DEVICE_H */
 | 
			
		||||
							
								
								
									
										106
									
								
								include/target/target_core_fabric_configfs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								include/target/target_core_fabric_configfs.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,106 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Used for tfc_wwn_cit attributes
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <target/configfs_macros.h>
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_STRUCT(target_fabric_nacl_attrib, se_node_acl);
 | 
			
		||||
#define TF_NACL_ATTRIB_ATTR(_fabric, _name, _mode)			\
 | 
			
		||||
static struct target_fabric_nacl_attrib_attribute _fabric##_nacl_attrib_##_name = \
 | 
			
		||||
	__CONFIGFS_EATTR(_name, _mode,					\
 | 
			
		||||
	_fabric##_nacl_attrib_show_##_name,				\
 | 
			
		||||
	_fabric##_nacl_attrib_store_##_name);
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_STRUCT(target_fabric_nacl_auth, se_node_acl);
 | 
			
		||||
#define TF_NACL_AUTH_ATTR(_fabric, _name, _mode)			\
 | 
			
		||||
static struct target_fabric_nacl_auth_attribute _fabric##_nacl_auth_##_name = \
 | 
			
		||||
	__CONFIGFS_EATTR(_name, _mode,					\
 | 
			
		||||
	_fabric##_nacl_auth_show_##_name,				\
 | 
			
		||||
	_fabric##_nacl_auth_store_##_name);
 | 
			
		||||
 | 
			
		||||
#define TF_NACL_AUTH_ATTR_RO(_fabric, _name)				\
 | 
			
		||||
static struct target_fabric_nacl_auth_attribute _fabric##_nacl_auth_##_name = \
 | 
			
		||||
	__CONFIGFS_EATTR_RO(_name,					\
 | 
			
		||||
	_fabric##_nacl_auth_show_##_name);
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_STRUCT(target_fabric_nacl_param, se_node_acl);
 | 
			
		||||
#define TF_NACL_PARAM_ATTR(_fabric, _name, _mode)			\
 | 
			
		||||
static struct target_fabric_nacl_param_attribute _fabric##_nacl_param_##_name = \
 | 
			
		||||
	__CONFIGFS_EATTR(_name, _mode,					\
 | 
			
		||||
	_fabric##_nacl_param_show_##_name,				\
 | 
			
		||||
	_fabric##_nacl_param_store_##_name);
 | 
			
		||||
 | 
			
		||||
#define TF_NACL_PARAM_ATTR_RO(_fabric, _name)				\
 | 
			
		||||
static struct target_fabric_nacl_param_attribute _fabric##_nacl_param_##_name = \
 | 
			
		||||
	__CONFIGFS_EATTR_RO(_name,					\
 | 
			
		||||
	_fabric##_nacl_param_show_##_name);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_STRUCT(target_fabric_nacl_base, se_node_acl);
 | 
			
		||||
#define TF_NACL_BASE_ATTR(_fabric, _name, _mode)			\
 | 
			
		||||
static struct target_fabric_nacl_base_attribute _fabric##_nacl_##_name = \
 | 
			
		||||
	__CONFIGFS_EATTR(_name, _mode,					\
 | 
			
		||||
	_fabric##_nacl_show_##_name,					\
 | 
			
		||||
	_fabric##_nacl_store_##_name);
 | 
			
		||||
 | 
			
		||||
#define TF_NACL_BASE_ATTR_RO(_fabric, _name)				\
 | 
			
		||||
static struct target_fabric_nacl_base_attribute _fabric##_nacl_##_name = \
 | 
			
		||||
	__CONFIGFS_EATTR_RO(_name,					\
 | 
			
		||||
	_fabric##_nacl_show_##_name);
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_STRUCT(target_fabric_np_base, se_tpg_np);
 | 
			
		||||
#define TF_NP_BASE_ATTR(_fabric, _name, _mode)				\
 | 
			
		||||
static struct target_fabric_np_base_attribute _fabric##_np_##_name =	\
 | 
			
		||||
	__CONFIGFS_EATTR(_name, _mode,					\
 | 
			
		||||
	_fabric##_np_show_##_name,					\
 | 
			
		||||
	_fabric##_np_store_##_name);
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_STRUCT(target_fabric_tpg_attrib, se_portal_group);
 | 
			
		||||
#define TF_TPG_ATTRIB_ATTR(_fabric, _name, _mode)			\
 | 
			
		||||
static struct target_fabric_tpg_attrib_attribute _fabric##_tpg_attrib_##_name = \
 | 
			
		||||
	__CONFIGFS_EATTR(_name, _mode,					\
 | 
			
		||||
	_fabric##_tpg_attrib_show_##_name,				\
 | 
			
		||||
	_fabric##_tpg_attrib_store_##_name);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_STRUCT(target_fabric_tpg_param, se_portal_group);
 | 
			
		||||
#define TF_TPG_PARAM_ATTR(_fabric, _name, _mode)			\
 | 
			
		||||
static struct target_fabric_tpg_param_attribute _fabric##_tpg_param_##_name = \
 | 
			
		||||
	__CONFIGFS_EATTR(_name, _mode,					\
 | 
			
		||||
	_fabric##_tpg_param_show_##_name,				\
 | 
			
		||||
	_fabric##_tpg_param_store_##_name);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_STRUCT(target_fabric_tpg, se_portal_group);
 | 
			
		||||
#define TF_TPG_BASE_ATTR(_fabric, _name, _mode)				\
 | 
			
		||||
static struct target_fabric_tpg_attribute _fabric##_tpg_##_name =	\
 | 
			
		||||
	__CONFIGFS_EATTR(_name, _mode,					\
 | 
			
		||||
	_fabric##_tpg_show_##_name,					\
 | 
			
		||||
	_fabric##_tpg_store_##_name);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_STRUCT(target_fabric_wwn, target_fabric_configfs);
 | 
			
		||||
#define TF_WWN_ATTR(_fabric, _name, _mode)				\
 | 
			
		||||
static struct target_fabric_wwn_attribute _fabric##_wwn_##_name =	\
 | 
			
		||||
	__CONFIGFS_EATTR(_name, _mode,					\
 | 
			
		||||
	_fabric##_wwn_show_attr_##_name,				\
 | 
			
		||||
	_fabric##_wwn_store_attr_##_name);
 | 
			
		||||
 | 
			
		||||
#define TF_WWN_ATTR_RO(_fabric, _name)					\
 | 
			
		||||
static struct target_fabric_wwn_attribute _fabric##_wwn_##_name =	\
 | 
			
		||||
	__CONFIGFS_EATTR_RO(_name,					\
 | 
			
		||||
	_fabric##_wwn_show_attr_##_name);
 | 
			
		||||
 | 
			
		||||
CONFIGFS_EATTR_STRUCT(target_fabric_discovery, target_fabric_configfs);
 | 
			
		||||
#define TF_DISC_ATTR(_fabric, _name, _mode)				\
 | 
			
		||||
static struct target_fabric_discovery_attribute _fabric##_disc_##_name = \
 | 
			
		||||
	__CONFIGFS_EATTR(_name, _mode,					\
 | 
			
		||||
	_fabric##_disc_show_##_name,					\
 | 
			
		||||
	_fabric##_disc_store_##_name);
 | 
			
		||||
 | 
			
		||||
#define TF_DISC_ATTR_RO(_fabric, _name)					\
 | 
			
		||||
static struct target_fabric_discovery_attribute _fabric##_disc_##_name = \
 | 
			
		||||
	__CONFIGFS_EATTR_RO(_name,					\
 | 
			
		||||
	_fabric##_disc_show_##_name);
 | 
			
		||||
 | 
			
		||||
extern int target_fabric_setup_cits(struct target_fabric_configfs *);
 | 
			
		||||
							
								
								
									
										28
									
								
								include/target/target_core_fabric_lib.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								include/target/target_core_fabric_lib.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
#ifndef TARGET_CORE_FABRIC_LIB_H
 | 
			
		||||
#define TARGET_CORE_FABRIC_LIB_H
 | 
			
		||||
 | 
			
		||||
extern u8 sas_get_fabric_proto_ident(struct se_portal_group *);
 | 
			
		||||
extern u32 sas_get_pr_transport_id(struct se_portal_group *, struct se_node_acl *,
 | 
			
		||||
			struct t10_pr_registration *, int *, unsigned char *);
 | 
			
		||||
extern u32 sas_get_pr_transport_id_len(struct se_portal_group *, struct se_node_acl *,
 | 
			
		||||
			struct t10_pr_registration *, int *);
 | 
			
		||||
extern char *sas_parse_pr_out_transport_id(struct se_portal_group *,
 | 
			
		||||
			const char *, u32 *, char **);
 | 
			
		||||
 | 
			
		||||
extern u8 fc_get_fabric_proto_ident(struct se_portal_group *);
 | 
			
		||||
extern u32 fc_get_pr_transport_id(struct se_portal_group *, struct se_node_acl *,
 | 
			
		||||
			struct t10_pr_registration *, int *, unsigned char *);
 | 
			
		||||
extern u32 fc_get_pr_transport_id_len(struct se_portal_group *, struct se_node_acl *,
 | 
			
		||||
			struct t10_pr_registration *, int *);
 | 
			
		||||
extern char *fc_parse_pr_out_transport_id(struct se_portal_group *,
 | 
			
		||||
			const char *, u32 *, char **);
 | 
			
		||||
 | 
			
		||||
extern u8 iscsi_get_fabric_proto_ident(struct se_portal_group *);
 | 
			
		||||
extern u32 iscsi_get_pr_transport_id(struct se_portal_group *, struct se_node_acl *,
 | 
			
		||||
			struct t10_pr_registration *, int *, unsigned char *);
 | 
			
		||||
extern u32 iscsi_get_pr_transport_id_len(struct se_portal_group *, struct se_node_acl *,
 | 
			
		||||
			struct t10_pr_registration *, int *);
 | 
			
		||||
extern char *iscsi_parse_pr_out_transport_id(struct se_portal_group *,
 | 
			
		||||
			const char *, u32 *, char **);
 | 
			
		||||
 | 
			
		||||
#endif /* TARGET_CORE_FABRIC_LIB_H */
 | 
			
		||||
							
								
								
									
										100
									
								
								include/target/target_core_fabric_ops.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								include/target/target_core_fabric_ops.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,100 @@
 | 
			
		|||
/* Defined in target_core_configfs.h */
 | 
			
		||||
struct target_fabric_configfs;
 | 
			
		||||
 | 
			
		||||
struct target_core_fabric_ops {
 | 
			
		||||
	struct configfs_subsystem *tf_subsys;
 | 
			
		||||
	/*
 | 
			
		||||
	 * Optional to signal struct se_task->task_sg[] padding entries
 | 
			
		||||
	 * for scatterlist chaining using transport_do_task_sg_link(),
 | 
			
		||||
	 * disabled by default
 | 
			
		||||
	 */
 | 
			
		||||
	int task_sg_chaining:1;
 | 
			
		||||
	char *(*get_fabric_name)(void);
 | 
			
		||||
	u8 (*get_fabric_proto_ident)(struct se_portal_group *);
 | 
			
		||||
	char *(*tpg_get_wwn)(struct se_portal_group *);
 | 
			
		||||
	u16 (*tpg_get_tag)(struct se_portal_group *);
 | 
			
		||||
	u32 (*tpg_get_default_depth)(struct se_portal_group *);
 | 
			
		||||
	u32 (*tpg_get_pr_transport_id)(struct se_portal_group *,
 | 
			
		||||
				struct se_node_acl *,
 | 
			
		||||
				struct t10_pr_registration *, int *,
 | 
			
		||||
				unsigned char *);
 | 
			
		||||
	u32 (*tpg_get_pr_transport_id_len)(struct se_portal_group *,
 | 
			
		||||
				struct se_node_acl *,
 | 
			
		||||
				struct t10_pr_registration *, int *);
 | 
			
		||||
	char *(*tpg_parse_pr_out_transport_id)(struct se_portal_group *,
 | 
			
		||||
				const char *, u32 *, char **);
 | 
			
		||||
	int (*tpg_check_demo_mode)(struct se_portal_group *);
 | 
			
		||||
	int (*tpg_check_demo_mode_cache)(struct se_portal_group *);
 | 
			
		||||
	int (*tpg_check_demo_mode_write_protect)(struct se_portal_group *);
 | 
			
		||||
	int (*tpg_check_prod_mode_write_protect)(struct se_portal_group *);
 | 
			
		||||
	struct se_node_acl *(*tpg_alloc_fabric_acl)(
 | 
			
		||||
					struct se_portal_group *);
 | 
			
		||||
	void (*tpg_release_fabric_acl)(struct se_portal_group *,
 | 
			
		||||
					struct se_node_acl *);
 | 
			
		||||
	u32 (*tpg_get_inst_index)(struct se_portal_group *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Optional function pointer for TCM to perform command map
 | 
			
		||||
	 * from TCM processing thread context, for those struct se_cmd
 | 
			
		||||
	 * initally allocated in interrupt context.
 | 
			
		||||
	 */
 | 
			
		||||
	int (*new_cmd_map)(struct se_cmd *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Optional function pointer for TCM fabric modules that use
 | 
			
		||||
	 * Linux/NET sockets to allocate struct iovec array to struct se_cmd
 | 
			
		||||
	 */
 | 
			
		||||
	int (*alloc_cmd_iovecs)(struct se_cmd *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Optional to release struct se_cmd and fabric dependent allocated
 | 
			
		||||
	 * I/O descriptor in transport_cmd_check_stop()
 | 
			
		||||
	 */
 | 
			
		||||
	void (*check_stop_free)(struct se_cmd *);
 | 
			
		||||
	void (*release_cmd_to_pool)(struct se_cmd *);
 | 
			
		||||
	void (*release_cmd_direct)(struct se_cmd *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Called with spin_lock_bh(struct se_portal_group->session_lock held.
 | 
			
		||||
	 */
 | 
			
		||||
	int (*shutdown_session)(struct se_session *);
 | 
			
		||||
	void (*close_session)(struct se_session *);
 | 
			
		||||
	void (*stop_session)(struct se_session *, int, int);
 | 
			
		||||
	void (*fall_back_to_erl0)(struct se_session *);
 | 
			
		||||
	int (*sess_logged_in)(struct se_session *);
 | 
			
		||||
	u32 (*sess_get_index)(struct se_session *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Used only for SCSI fabrics that contain multi-value TransportIDs
 | 
			
		||||
	 * (like iSCSI).  All other SCSI fabrics should set this to NULL.
 | 
			
		||||
	 */
 | 
			
		||||
	u32 (*sess_get_initiator_sid)(struct se_session *,
 | 
			
		||||
				      unsigned char *, u32);
 | 
			
		||||
	int (*write_pending)(struct se_cmd *);
 | 
			
		||||
	int (*write_pending_status)(struct se_cmd *);
 | 
			
		||||
	void (*set_default_node_attributes)(struct se_node_acl *);
 | 
			
		||||
	u32 (*get_task_tag)(struct se_cmd *);
 | 
			
		||||
	int (*get_cmd_state)(struct se_cmd *);
 | 
			
		||||
	void (*new_cmd_failure)(struct se_cmd *);
 | 
			
		||||
	int (*queue_data_in)(struct se_cmd *);
 | 
			
		||||
	int (*queue_status)(struct se_cmd *);
 | 
			
		||||
	int (*queue_tm_rsp)(struct se_cmd *);
 | 
			
		||||
	u16 (*set_fabric_sense_len)(struct se_cmd *, u32);
 | 
			
		||||
	u16 (*get_fabric_sense_len)(void);
 | 
			
		||||
	int (*is_state_remove)(struct se_cmd *);
 | 
			
		||||
	u64 (*pack_lun)(unsigned int);
 | 
			
		||||
	/*
 | 
			
		||||
	 * fabric module calls for target_core_fabric_configfs.c
 | 
			
		||||
	 */
 | 
			
		||||
	struct se_wwn *(*fabric_make_wwn)(struct target_fabric_configfs *,
 | 
			
		||||
				struct config_group *, const char *);
 | 
			
		||||
	void (*fabric_drop_wwn)(struct se_wwn *);
 | 
			
		||||
	struct se_portal_group *(*fabric_make_tpg)(struct se_wwn *,
 | 
			
		||||
				struct config_group *, const char *);
 | 
			
		||||
	void (*fabric_drop_tpg)(struct se_portal_group *);
 | 
			
		||||
	int (*fabric_post_link)(struct se_portal_group *,
 | 
			
		||||
				struct se_lun *);
 | 
			
		||||
	void (*fabric_pre_unlink)(struct se_portal_group *,
 | 
			
		||||
				struct se_lun *);
 | 
			
		||||
	struct se_tpg_np *(*fabric_make_np)(struct se_portal_group *,
 | 
			
		||||
				struct config_group *, const char *);
 | 
			
		||||
	void (*fabric_drop_np)(struct se_tpg_np *);
 | 
			
		||||
	struct se_node_acl *(*fabric_make_nodeacl)(struct se_portal_group *,
 | 
			
		||||
				struct config_group *, const char *);
 | 
			
		||||
	void (*fabric_drop_nodeacl)(struct se_node_acl *);
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										43
									
								
								include/target/target_core_tmr.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								include/target/target_core_tmr.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,43 @@
 | 
			
		|||
#ifndef TARGET_CORE_TMR_H
 | 
			
		||||
#define TARGET_CORE_TMR_H
 | 
			
		||||
 | 
			
		||||
/* task management function values */
 | 
			
		||||
#ifdef ABORT_TASK
 | 
			
		||||
#undef ABORT_TASK
 | 
			
		||||
#endif /* ABORT_TASK */
 | 
			
		||||
#define ABORT_TASK				1
 | 
			
		||||
#ifdef ABORT_TASK_SET
 | 
			
		||||
#undef ABORT_TASK_SET
 | 
			
		||||
#endif /* ABORT_TASK_SET */
 | 
			
		||||
#define ABORT_TASK_SET				2
 | 
			
		||||
#ifdef CLEAR_ACA
 | 
			
		||||
#undef CLEAR_ACA
 | 
			
		||||
#endif /* CLEAR_ACA */
 | 
			
		||||
#define CLEAR_ACA				3
 | 
			
		||||
#ifdef CLEAR_TASK_SET
 | 
			
		||||
#undef CLEAR_TASK_SET
 | 
			
		||||
#endif /* CLEAR_TASK_SET */
 | 
			
		||||
#define CLEAR_TASK_SET				4
 | 
			
		||||
#define LUN_RESET				5
 | 
			
		||||
#define TARGET_WARM_RESET			6
 | 
			
		||||
#define TARGET_COLD_RESET			7
 | 
			
		||||
#define TASK_REASSIGN				8
 | 
			
		||||
 | 
			
		||||
/* task management response values */
 | 
			
		||||
#define TMR_FUNCTION_COMPLETE			0
 | 
			
		||||
#define TMR_TASK_DOES_NOT_EXIST			1
 | 
			
		||||
#define TMR_LUN_DOES_NOT_EXIST			2
 | 
			
		||||
#define TMR_TASK_STILL_ALLEGIANT		3
 | 
			
		||||
#define TMR_TASK_FAILOVER_NOT_SUPPORTED		4
 | 
			
		||||
#define TMR_TASK_MGMT_FUNCTION_NOT_SUPPORTED	5
 | 
			
		||||
#define TMR_FUNCTION_AUTHORIZATION_FAILED	6
 | 
			
		||||
#define TMR_FUNCTION_REJECTED			255
 | 
			
		||||
 | 
			
		||||
extern struct kmem_cache *se_tmr_req_cache;
 | 
			
		||||
 | 
			
		||||
extern struct se_tmr_req *core_tmr_alloc_req(struct se_cmd *, void *, u8);
 | 
			
		||||
extern void core_tmr_release_req(struct se_tmr_req *);
 | 
			
		||||
extern int core_tmr_lun_reset(struct se_device *, struct se_tmr_req *,
 | 
			
		||||
				struct list_head *, struct se_cmd *);
 | 
			
		||||
 | 
			
		||||
#endif /* TARGET_CORE_TMR_H */
 | 
			
		||||
							
								
								
									
										35
									
								
								include/target/target_core_tpg.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								include/target/target_core_tpg.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,35 @@
 | 
			
		|||
#ifndef TARGET_CORE_TPG_H
 | 
			
		||||
#define TARGET_CORE_TPG_H
 | 
			
		||||
 | 
			
		||||
extern struct se_node_acl *__core_tpg_get_initiator_node_acl(struct se_portal_group *tpg,
 | 
			
		||||
						const char *);
 | 
			
		||||
extern struct se_node_acl *core_tpg_get_initiator_node_acl(struct se_portal_group *tpg,
 | 
			
		||||
						unsigned char *);
 | 
			
		||||
extern void core_tpg_add_node_to_devs(struct se_node_acl *,
 | 
			
		||||
						struct se_portal_group *);
 | 
			
		||||
extern struct se_node_acl *core_tpg_check_initiator_node_acl(
 | 
			
		||||
						struct se_portal_group *,
 | 
			
		||||
						unsigned char *);
 | 
			
		||||
extern void core_tpg_wait_for_nacl_pr_ref(struct se_node_acl *);
 | 
			
		||||
extern void core_tpg_wait_for_mib_ref(struct se_node_acl *);
 | 
			
		||||
extern void core_tpg_clear_object_luns(struct se_portal_group *);
 | 
			
		||||
extern struct se_node_acl *core_tpg_add_initiator_node_acl(
 | 
			
		||||
					struct se_portal_group *,
 | 
			
		||||
					struct se_node_acl *,
 | 
			
		||||
					const char *, u32);
 | 
			
		||||
extern int core_tpg_del_initiator_node_acl(struct se_portal_group *,
 | 
			
		||||
						struct se_node_acl *, int);
 | 
			
		||||
extern int core_tpg_set_initiator_node_queue_depth(struct se_portal_group *,
 | 
			
		||||
						unsigned char *, u32, int);
 | 
			
		||||
extern int core_tpg_register(struct target_core_fabric_ops *,
 | 
			
		||||
					struct se_wwn *,
 | 
			
		||||
					struct se_portal_group *, void *,
 | 
			
		||||
					int);
 | 
			
		||||
extern int core_tpg_deregister(struct se_portal_group *);
 | 
			
		||||
extern struct se_lun *core_tpg_pre_addlun(struct se_portal_group *, u32);
 | 
			
		||||
extern int core_tpg_post_addlun(struct se_portal_group *, struct se_lun *, u32,
 | 
			
		||||
				void *);
 | 
			
		||||
extern struct se_lun *core_tpg_pre_dellun(struct se_portal_group *, u32, int *);
 | 
			
		||||
extern int core_tpg_post_dellun(struct se_portal_group *, struct se_lun *);
 | 
			
		||||
 | 
			
		||||
#endif /* TARGET_CORE_TPG_H */
 | 
			
		||||
							
								
								
									
										351
									
								
								include/target/target_core_transport.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										351
									
								
								include/target/target_core_transport.h
									
									
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,351 @@
 | 
			
		|||
#ifndef TARGET_CORE_TRANSPORT_H
 | 
			
		||||
#define TARGET_CORE_TRANSPORT_H
 | 
			
		||||
 | 
			
		||||
#define TARGET_CORE_VERSION			TARGET_CORE_MOD_VERSION
 | 
			
		||||
 | 
			
		||||
/* Attempts before moving from SHORT to LONG */
 | 
			
		||||
#define PYX_TRANSPORT_WINDOW_CLOSED_THRESHOLD	3
 | 
			
		||||
#define PYX_TRANSPORT_WINDOW_CLOSED_WAIT_SHORT	3  /* In milliseconds */
 | 
			
		||||
#define PYX_TRANSPORT_WINDOW_CLOSED_WAIT_LONG	10 /* In milliseconds */
 | 
			
		||||
 | 
			
		||||
#define PYX_TRANSPORT_STATUS_INTERVAL		5 /* In seconds */
 | 
			
		||||
 | 
			
		||||
#define PYX_TRANSPORT_SENT_TO_TRANSPORT		0
 | 
			
		||||
#define PYX_TRANSPORT_WRITE_PENDING		1
 | 
			
		||||
 | 
			
		||||
#define PYX_TRANSPORT_UNKNOWN_SAM_OPCODE	-1
 | 
			
		||||
#define PYX_TRANSPORT_HBA_QUEUE_FULL		-2
 | 
			
		||||
#define PYX_TRANSPORT_REQ_TOO_MANY_SECTORS	-3
 | 
			
		||||
#define PYX_TRANSPORT_OUT_OF_MEMORY_RESOURCES	-4
 | 
			
		||||
#define PYX_TRANSPORT_INVALID_CDB_FIELD		-5
 | 
			
		||||
#define PYX_TRANSPORT_INVALID_PARAMETER_LIST	-6
 | 
			
		||||
#define PYX_TRANSPORT_LU_COMM_FAILURE		-7
 | 
			
		||||
#define PYX_TRANSPORT_UNKNOWN_MODE_PAGE		-8
 | 
			
		||||
#define PYX_TRANSPORT_WRITE_PROTECTED		-9
 | 
			
		||||
#define PYX_TRANSPORT_TASK_TIMEOUT		-10
 | 
			
		||||
#define PYX_TRANSPORT_RESERVATION_CONFLICT	-11
 | 
			
		||||
#define PYX_TRANSPORT_ILLEGAL_REQUEST		-12
 | 
			
		||||
#define PYX_TRANSPORT_USE_SENSE_REASON		-13
 | 
			
		||||
 | 
			
		||||
#ifndef SAM_STAT_RESERVATION_CONFLICT
 | 
			
		||||
#define SAM_STAT_RESERVATION_CONFLICT		0x18
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define TRANSPORT_PLUGIN_FREE			0
 | 
			
		||||
#define TRANSPORT_PLUGIN_REGISTERED		1
 | 
			
		||||
 | 
			
		||||
#define TRANSPORT_PLUGIN_PHBA_PDEV		1
 | 
			
		||||
#define TRANSPORT_PLUGIN_VHBA_PDEV		2
 | 
			
		||||
#define TRANSPORT_PLUGIN_VHBA_VDEV		3
 | 
			
		||||
 | 
			
		||||
/* For SE OBJ Plugins, in seconds */
 | 
			
		||||
#define TRANSPORT_TIMEOUT_TUR			10
 | 
			
		||||
#define TRANSPORT_TIMEOUT_TYPE_DISK		60
 | 
			
		||||
#define TRANSPORT_TIMEOUT_TYPE_ROM		120
 | 
			
		||||
#define TRANSPORT_TIMEOUT_TYPE_TAPE		600
 | 
			
		||||
#define TRANSPORT_TIMEOUT_TYPE_OTHER		300
 | 
			
		||||
 | 
			
		||||
/* For se_task->task_state_flags */
 | 
			
		||||
#define TSF_EXCEPTION_CLEARED			0x01
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * struct se_subsystem_dev->su_dev_flags
 | 
			
		||||
*/
 | 
			
		||||
#define SDF_FIRMWARE_VPD_UNIT_SERIAL		0x00000001
 | 
			
		||||
#define SDF_EMULATED_VPD_UNIT_SERIAL		0x00000002
 | 
			
		||||
#define SDF_USING_UDEV_PATH			0x00000004
 | 
			
		||||
#define SDF_USING_ALIAS				0x00000008
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * struct se_device->dev_flags
 | 
			
		||||
 */
 | 
			
		||||
#define DF_READ_ONLY				0x00000001
 | 
			
		||||
#define DF_SPC2_RESERVATIONS			0x00000002
 | 
			
		||||
#define DF_SPC2_RESERVATIONS_WITH_ISID		0x00000004
 | 
			
		||||
 | 
			
		||||
/* struct se_dev_attrib sanity values */
 | 
			
		||||
/* 10 Minutes */
 | 
			
		||||
#define DA_TASK_TIMEOUT_MAX			600
 | 
			
		||||
/* Default max_unmap_lba_count */
 | 
			
		||||
#define DA_MAX_UNMAP_LBA_COUNT			0
 | 
			
		||||
/* Default max_unmap_block_desc_count */
 | 
			
		||||
#define DA_MAX_UNMAP_BLOCK_DESC_COUNT		0
 | 
			
		||||
/* Default unmap_granularity */
 | 
			
		||||
#define DA_UNMAP_GRANULARITY_DEFAULT		0
 | 
			
		||||
/* Default unmap_granularity_alignment */
 | 
			
		||||
#define DA_UNMAP_GRANULARITY_ALIGNMENT_DEFAULT	0
 | 
			
		||||
/* Emulation for Direct Page Out */
 | 
			
		||||
#define DA_EMULATE_DPO				0
 | 
			
		||||
/* Emulation for Forced Unit Access WRITEs */
 | 
			
		||||
#define DA_EMULATE_FUA_WRITE			1
 | 
			
		||||
/* Emulation for Forced Unit Access READs */
 | 
			
		||||
#define DA_EMULATE_FUA_READ			0
 | 
			
		||||
/* Emulation for WriteCache and SYNCHRONIZE_CACHE */
 | 
			
		||||
#define DA_EMULATE_WRITE_CACHE			0
 | 
			
		||||
/* Emulation for UNIT ATTENTION Interlock Control */
 | 
			
		||||
#define DA_EMULATE_UA_INTLLCK_CTRL		0
 | 
			
		||||
/* Emulation for TASK_ABORTED status (TAS) by default */
 | 
			
		||||
#define DA_EMULATE_TAS				1
 | 
			
		||||
/* Emulation for Thin Provisioning UNMAP using block/blk-lib.c:blkdev_issue_discard() */
 | 
			
		||||
#define DA_EMULATE_TPU				0
 | 
			
		||||
/*
 | 
			
		||||
 * Emulation for Thin Provisioning WRITE_SAME w/ UNMAP=1 bit using
 | 
			
		||||
 * block/blk-lib.c:blkdev_issue_discard()
 | 
			
		||||
 */
 | 
			
		||||
#define DA_EMULATE_TPWS				0
 | 
			
		||||
/* No Emulation for PSCSI by default */
 | 
			
		||||
#define DA_EMULATE_RESERVATIONS			0
 | 
			
		||||
/* No Emulation for PSCSI by default */
 | 
			
		||||
#define DA_EMULATE_ALUA				0
 | 
			
		||||
/* Enforce SCSI Initiator Port TransportID with 'ISID' for PR */
 | 
			
		||||
#define DA_ENFORCE_PR_ISIDS			1
 | 
			
		||||
#define DA_STATUS_MAX_SECTORS_MIN		16
 | 
			
		||||
#define DA_STATUS_MAX_SECTORS_MAX		8192
 | 
			
		||||
 | 
			
		||||
#define SE_MODE_PAGE_BUF			512
 | 
			
		||||
 | 
			
		||||
#define MOD_MAX_SECTORS(ms, bs)			(ms % (PAGE_SIZE / bs))
 | 
			
		||||
 | 
			
		||||
struct se_mem;
 | 
			
		||||
struct se_subsystem_api;
 | 
			
		||||
 | 
			
		||||
extern int init_se_global(void);
 | 
			
		||||
extern void release_se_global(void);
 | 
			
		||||
extern void transport_init_queue_obj(struct se_queue_obj *);
 | 
			
		||||
extern int transport_subsystem_check_init(void);
 | 
			
		||||
extern int transport_subsystem_register(struct se_subsystem_api *);
 | 
			
		||||
extern void transport_subsystem_release(struct se_subsystem_api *);
 | 
			
		||||
extern void transport_load_plugins(void);
 | 
			
		||||
extern struct se_session *transport_init_session(void);
 | 
			
		||||
extern void __transport_register_session(struct se_portal_group *,
 | 
			
		||||
					struct se_node_acl *,
 | 
			
		||||
					struct se_session *, void *);
 | 
			
		||||
extern void transport_register_session(struct se_portal_group *,
 | 
			
		||||
					struct se_node_acl *,
 | 
			
		||||
					struct se_session *, void *);
 | 
			
		||||
extern void transport_free_session(struct se_session *);
 | 
			
		||||
extern void transport_deregister_session_configfs(struct se_session *);
 | 
			
		||||
extern void transport_deregister_session(struct se_session *);
 | 
			
		||||
extern void transport_cmd_finish_abort(struct se_cmd *, int);
 | 
			
		||||
extern void transport_cmd_finish_abort_tmr(struct se_cmd *);
 | 
			
		||||
extern void transport_complete_sync_cache(struct se_cmd *, int);
 | 
			
		||||
extern void transport_complete_task(struct se_task *, int);
 | 
			
		||||
extern void transport_add_task_to_execute_queue(struct se_task *,
 | 
			
		||||
						struct se_task *,
 | 
			
		||||
						struct se_device *);
 | 
			
		||||
unsigned char *transport_dump_cmd_direction(struct se_cmd *);
 | 
			
		||||
extern void transport_dump_dev_state(struct se_device *, char *, int *);
 | 
			
		||||
extern void transport_dump_dev_info(struct se_device *, struct se_lun *,
 | 
			
		||||
					unsigned long long, char *, int *);
 | 
			
		||||
extern void transport_dump_vpd_proto_id(struct t10_vpd *,
 | 
			
		||||
					unsigned char *, int);
 | 
			
		||||
extern void transport_set_vpd_proto_id(struct t10_vpd *, unsigned char *);
 | 
			
		||||
extern int transport_dump_vpd_assoc(struct t10_vpd *,
 | 
			
		||||
					unsigned char *, int);
 | 
			
		||||
extern int transport_set_vpd_assoc(struct t10_vpd *, unsigned char *);
 | 
			
		||||
extern int transport_dump_vpd_ident_type(struct t10_vpd *,
 | 
			
		||||
					unsigned char *, int);
 | 
			
		||||
extern int transport_set_vpd_ident_type(struct t10_vpd *, unsigned char *);
 | 
			
		||||
extern int transport_dump_vpd_ident(struct t10_vpd *,
 | 
			
		||||
					unsigned char *, int);
 | 
			
		||||
extern int transport_set_vpd_ident(struct t10_vpd *, unsigned char *);
 | 
			
		||||
extern struct se_device *transport_add_device_to_core_hba(struct se_hba *,
 | 
			
		||||
					struct se_subsystem_api *,
 | 
			
		||||
					struct se_subsystem_dev *, u32,
 | 
			
		||||
					void *, struct se_dev_limits *,
 | 
			
		||||
					const char *, const char *);
 | 
			
		||||
extern void transport_device_setup_cmd(struct se_cmd *);
 | 
			
		||||
extern void transport_init_se_cmd(struct se_cmd *,
 | 
			
		||||
					struct target_core_fabric_ops *,
 | 
			
		||||
					struct se_session *, u32, int, int,
 | 
			
		||||
					unsigned char *);
 | 
			
		||||
extern void transport_free_se_cmd(struct se_cmd *);
 | 
			
		||||
extern int transport_generic_allocate_tasks(struct se_cmd *, unsigned char *);
 | 
			
		||||
extern int transport_generic_handle_cdb(struct se_cmd *);
 | 
			
		||||
extern int transport_generic_handle_cdb_map(struct se_cmd *);
 | 
			
		||||
extern int transport_generic_handle_data(struct se_cmd *);
 | 
			
		||||
extern void transport_new_cmd_failure(struct se_cmd *);
 | 
			
		||||
extern int transport_generic_handle_tmr(struct se_cmd *);
 | 
			
		||||
extern void __transport_stop_task_timer(struct se_task *, unsigned long *);
 | 
			
		||||
extern unsigned char transport_asciihex_to_binaryhex(unsigned char val[2]);
 | 
			
		||||
extern int transport_generic_map_mem_to_cmd(struct se_cmd *cmd, struct scatterlist *, u32,
 | 
			
		||||
				struct scatterlist *, u32);
 | 
			
		||||
extern int transport_clear_lun_from_sessions(struct se_lun *);
 | 
			
		||||
extern int transport_check_aborted_status(struct se_cmd *, int);
 | 
			
		||||
extern int transport_send_check_condition_and_sense(struct se_cmd *, u8, int);
 | 
			
		||||
extern void transport_send_task_abort(struct se_cmd *);
 | 
			
		||||
extern void transport_release_cmd_to_pool(struct se_cmd *);
 | 
			
		||||
extern void transport_generic_free_cmd(struct se_cmd *, int, int, int);
 | 
			
		||||
extern void transport_generic_wait_for_cmds(struct se_cmd *, int);
 | 
			
		||||
extern u32 transport_calc_sg_num(struct se_task *, struct se_mem *, u32);
 | 
			
		||||
extern int transport_map_mem_to_sg(struct se_task *, struct list_head *,
 | 
			
		||||
					void *, struct se_mem *,
 | 
			
		||||
					struct se_mem **, u32 *, u32 *);
 | 
			
		||||
extern void transport_do_task_sg_chain(struct se_cmd *);
 | 
			
		||||
extern void transport_generic_process_write(struct se_cmd *);
 | 
			
		||||
extern int transport_generic_do_tmr(struct se_cmd *);
 | 
			
		||||
/* From target_core_alua.c */
 | 
			
		||||
extern int core_alua_check_nonop_delay(struct se_cmd *);
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Each se_transport_task_t can have N number of possible struct se_task's
 | 
			
		||||
 * for the storage transport(s) to possibly execute.
 | 
			
		||||
 * Used primarily for splitting up CDBs that exceed the physical storage
 | 
			
		||||
 * HBA's maximum sector count per task.
 | 
			
		||||
 */
 | 
			
		||||
struct se_mem {
 | 
			
		||||
	struct page	*se_page;
 | 
			
		||||
	u32		se_len;
 | 
			
		||||
	u32		se_off;
 | 
			
		||||
	struct list_head se_list;
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * 	Each type of disk transport supported MUST have a template defined
 | 
			
		||||
 *	within its .h file.
 | 
			
		||||
 */
 | 
			
		||||
struct se_subsystem_api {
 | 
			
		||||
	/*
 | 
			
		||||
	 * The Name. :-)
 | 
			
		||||
	 */
 | 
			
		||||
	char name[16];
 | 
			
		||||
	/*
 | 
			
		||||
	 * Transport Type.
 | 
			
		||||
	 */
 | 
			
		||||
	u8 transport_type;
 | 
			
		||||
	/*
 | 
			
		||||
	 * struct module for struct se_hba references
 | 
			
		||||
	 */
 | 
			
		||||
	struct module *owner;
 | 
			
		||||
	/*
 | 
			
		||||
	 * Used for global se_subsystem_api list_head
 | 
			
		||||
	 */
 | 
			
		||||
	struct list_head sub_api_list;
 | 
			
		||||
	/*
 | 
			
		||||
	 * For SCF_SCSI_NON_DATA_CDB
 | 
			
		||||
	 */
 | 
			
		||||
	int (*cdb_none)(struct se_task *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * For SCF_SCSI_CONTROL_NONSG_IO_CDB
 | 
			
		||||
	 */
 | 
			
		||||
	int (*map_task_non_SG)(struct se_task *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * For SCF_SCSI_DATA_SG_IO_CDB and SCF_SCSI_CONTROL_SG_IO_CDB
 | 
			
		||||
	 */
 | 
			
		||||
	int (*map_task_SG)(struct se_task *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * attach_hba():
 | 
			
		||||
	 */
 | 
			
		||||
	int (*attach_hba)(struct se_hba *, u32);
 | 
			
		||||
	/*
 | 
			
		||||
	 * detach_hba():
 | 
			
		||||
	 */
 | 
			
		||||
	void (*detach_hba)(struct se_hba *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * pmode_hba(): Used for TCM/pSCSI subsystem plugin HBA ->
 | 
			
		||||
	 *		Linux/SCSI struct Scsi_Host passthrough
 | 
			
		||||
	*/
 | 
			
		||||
	int (*pmode_enable_hba)(struct se_hba *, unsigned long);
 | 
			
		||||
	/*
 | 
			
		||||
	 * allocate_virtdevice():
 | 
			
		||||
	 */
 | 
			
		||||
	void *(*allocate_virtdevice)(struct se_hba *, const char *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * create_virtdevice(): Only for Virtual HBAs
 | 
			
		||||
	 */
 | 
			
		||||
	struct se_device *(*create_virtdevice)(struct se_hba *,
 | 
			
		||||
				struct se_subsystem_dev *, void *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * free_device():
 | 
			
		||||
	 */
 | 
			
		||||
	void (*free_device)(void *);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * dpo_emulated():
 | 
			
		||||
	 */
 | 
			
		||||
	int (*dpo_emulated)(struct se_device *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * fua_write_emulated():
 | 
			
		||||
	 */
 | 
			
		||||
	int (*fua_write_emulated)(struct se_device *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * fua_read_emulated():
 | 
			
		||||
	 */
 | 
			
		||||
	int (*fua_read_emulated)(struct se_device *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * write_cache_emulated():
 | 
			
		||||
	 */
 | 
			
		||||
	int (*write_cache_emulated)(struct se_device *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * transport_complete():
 | 
			
		||||
	 *
 | 
			
		||||
	 * Use transport_generic_complete() for majority of DAS transport
 | 
			
		||||
	 * drivers.  Provided out of convenience.
 | 
			
		||||
	 */
 | 
			
		||||
	int (*transport_complete)(struct se_task *task);
 | 
			
		||||
	struct se_task *(*alloc_task)(struct se_cmd *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * do_task():
 | 
			
		||||
	 */
 | 
			
		||||
	int (*do_task)(struct se_task *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Used by virtual subsystem plugins IBLOCK and FILEIO to emulate
 | 
			
		||||
	 * UNMAP and WRITE_SAME_* w/ UNMAP=1 <-> Linux/Block Discard
 | 
			
		||||
	 */
 | 
			
		||||
	int (*do_discard)(struct se_device *, sector_t, u32);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Used  by virtual subsystem plugins IBLOCK and FILEIO to emulate
 | 
			
		||||
	 * SYNCHRONIZE_CACHE_* <-> Linux/Block blkdev_issue_flush()
 | 
			
		||||
	 */
 | 
			
		||||
	void (*do_sync_cache)(struct se_task *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * free_task():
 | 
			
		||||
	 */
 | 
			
		||||
	void (*free_task)(struct se_task *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * check_configfs_dev_params():
 | 
			
		||||
	 */
 | 
			
		||||
	ssize_t (*check_configfs_dev_params)(struct se_hba *, struct se_subsystem_dev *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * set_configfs_dev_params():
 | 
			
		||||
	 */
 | 
			
		||||
	ssize_t (*set_configfs_dev_params)(struct se_hba *, struct se_subsystem_dev *,
 | 
			
		||||
						const char *, ssize_t);
 | 
			
		||||
	/*
 | 
			
		||||
	 * show_configfs_dev_params():
 | 
			
		||||
	 */
 | 
			
		||||
	ssize_t (*show_configfs_dev_params)(struct se_hba *, struct se_subsystem_dev *,
 | 
			
		||||
						char *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * get_cdb():
 | 
			
		||||
	 */
 | 
			
		||||
	unsigned char *(*get_cdb)(struct se_task *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * get_device_rev():
 | 
			
		||||
	 */
 | 
			
		||||
	u32 (*get_device_rev)(struct se_device *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * get_device_type():
 | 
			
		||||
	 */
 | 
			
		||||
	u32 (*get_device_type)(struct se_device *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * Get the sector_t from a subsystem backstore..
 | 
			
		||||
	 */
 | 
			
		||||
	sector_t (*get_blocks)(struct se_device *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * do_se_mem_map():
 | 
			
		||||
	 */
 | 
			
		||||
	int (*do_se_mem_map)(struct se_task *, struct list_head *, void *,
 | 
			
		||||
				struct se_mem *, struct se_mem **, u32 *, u32 *);
 | 
			
		||||
	/*
 | 
			
		||||
	 * get_sense_buffer():
 | 
			
		||||
	 */
 | 
			
		||||
	unsigned char *(*get_sense_buffer)(struct se_task *);
 | 
			
		||||
} ____cacheline_aligned;
 | 
			
		||||
 | 
			
		||||
#define TRANSPORT(dev)		((dev)->transport)
 | 
			
		||||
#define HBA_TRANSPORT(hba)	((hba)->transport)
 | 
			
		||||
 | 
			
		||||
extern struct se_global *se_global;
 | 
			
		||||
 | 
			
		||||
#endif /* TARGET_CORE_TRANSPORT_H */
 | 
			
		||||
		Loading…
	
		Reference in a new issue