mirror of
https://github.com/SCST-project/scst.git
synced 2026-08-20 22:26:23 +00:00
Merge iser branch
Merged revisions: 5228-5237,5243-5245,5257-5259,5261-5263,5278-5280,5297-5299,5313-5314,5317-5319,5327-5328,5341-5343,5453,5486,5519,5528-5531,5559,5576,5655-5656,5660 git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/3.0.x-iser@5663 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
+85
-2
@@ -24,6 +24,7 @@ RCDIR := /etc/rc.d
|
||||
MANDIR ?= $(PREFIX)/man
|
||||
KMOD := $(shell pwd)/kernel
|
||||
INCDIR := $(shell pwd)/include
|
||||
ISERTMOD := $(KMOD)/isert-scst
|
||||
|
||||
ifeq ($(KVER),)
|
||||
ifeq ($(KDIR),)
|
||||
@@ -50,8 +51,57 @@ INSTALL_DIR := $(INSTALL_MOD_PATH)/lib/modules/$(KVER)/extra
|
||||
|
||||
all: include/iscsi_scst_itf_ver.h progs mods
|
||||
|
||||
ISER_SYMVERS:=$(KMOD)/Module.symvers
|
||||
OFED_CFLAGS:=
|
||||
|
||||
MLNX_OFED:=$(shell if ofed_info -s | grep MLNX >/dev/null 2>/dev/null; then echo true; else echo false; fi)
|
||||
|
||||
ifeq ($(MLNX_OFED),true)
|
||||
# Whether MLNX_OFED for ubuntu has been installed
|
||||
MLNX_OFED_IB_UBUNTU_INSTALLED:=$(shell if dpkg -s mlnx-ofed-kernel-dkms >/dev/null 2>/dev/null; then echo true; else echo false; fi)
|
||||
|
||||
# Whether MLNX_OFED for RedHat has been installed
|
||||
MLNX_OFED_IB_RH_INSTALLED:=$(shell if rpm -q mlnx-ofa_kernel-devel >&/dev/null; then echo true; else echo false; fi)
|
||||
|
||||
# Check if we have custom compiled kernel modules
|
||||
ifeq ($(MLNX_OFED_IB_RH_INSTALLED),false)
|
||||
MLNX_OFED_IB_RH_INSTALLED:=$(shell if rpm -q kernel-ib-devel >&/dev/null; then echo true; else echo false; fi)
|
||||
endif
|
||||
|
||||
ifeq ($(MLNX_OFED_IB_UBUNTU_INSTALLED),true)
|
||||
OFED_VERS=$(shell dpkg -s mlnx-ofed-kernel-dkms | awk -F\- '/Version/ {print $$1}' | awk '{print $$2}')
|
||||
OFED_CFLAGS:=-I/var/lib/dkms/mlnx-ofed-kernel/$(OFED_VERS)/build/include -include /var/lib/dkms/mlnx-ofed-kernel/$(OFED_VERS)/build/include/linux/compat-2.6.h
|
||||
ISER_SYMVERS:="$(ISER_SYMVERS) /var/lib/dkms/mlnx-ofed-kernel/$(OFED_VERS)/build/Module.symvers"
|
||||
endif
|
||||
|
||||
ifeq ($(MLNX_OFED_IB_RH_INSTALLED),true)
|
||||
OFED_CFLAGS:=-I/usr/src/ofa_kernel/default/include -include /usr/src/ofa_kernel/default/include/linux/compat-2.6.h
|
||||
ISER_SYMVERS:="$(ISER_SYMVERS) /usr/src/ofa_kernel/default/Module.symvers"
|
||||
endif
|
||||
else
|
||||
# Whether or not the OFED kernel-ib-devel RPM has been installed.
|
||||
OFED_KERNEL_IB_DEVEL_RPM_INSTALLED:=$(shell if rpm -q kernel-ib-devel 2>/dev/null | grep -q $$(uname -r | sed 's/-/_/g'); then echo true; else echo false; fi)
|
||||
|
||||
# Whether or not the OFED compat-rdma-devel RPM has been installed.
|
||||
OFED_COMPAT_RDMA_DEVEL_RPM_INSTALLED:=$(shell if rpm -q compat-rdma-devel 2>/dev/null | grep -q $$(uname -r | sed 's/-/_/g'); then echo true; else echo false; fi)
|
||||
|
||||
ifeq ($(OFED_KERNEL_IB_DEVEL_RPM_INSTALLED),true)
|
||||
# Read OFED's config.mk, which contains the definition of the variable
|
||||
# BACKPORT_INCLUDES.
|
||||
include /usr/src/ofa_kernel/config.mk
|
||||
OFED_CFLAGS:=$(shell echo $(BACKPORT_INCLUDES) -I/usr/src/ofa_kernel/include)
|
||||
ISER_SYMVERS:="$(ISER_SYMVERS) /usr/src/ofa_kernel/Module.symvers"
|
||||
endif
|
||||
|
||||
ifeq ($(OFED_COMPAT_RDMA_DEVEL_RPM_INSTALLED),true)
|
||||
OFED_CFLAGS:=-I/usr/src/compat-rdma/include -include /usr/src/compat-rdma/include/linux/compat-2.6.h
|
||||
ISER_SYMVERS:="$(ISER_SYMVERS) /usr/src/compat-rdma/Module.symvers"
|
||||
endif
|
||||
endif
|
||||
|
||||
mods: Modules.symvers Module.symvers
|
||||
$(MAKE) -C $(KDIR) SCST_INC_DIR=$(SCST_INC_DIR) SUBDIRS=$(KMOD) modules
|
||||
$(MAKE) -C $(KDIR) SCST_INC_DIR=$(SCST_INC_DIR) SUBDIRS=$(ISERTMOD) PRE_CFLAGS="$(OFED_CFLAGS)" KBUILD_EXTRA_SYMBOLS=$(ISER_SYMVERS) modules
|
||||
|
||||
progs:
|
||||
$(MAKE) -C usr SCST_INC_DIR=$(SCST_INC_DIR)
|
||||
@@ -71,6 +121,8 @@ install: all
|
||||
@install -vD -m 644 doc/manpages/iscsi-scst-adm.8 $(DESTDIR)$(MANDIR)/man8/iscsi-scst-adm.8
|
||||
$(MAKE) -C $(KDIR) SCST_INC_DIR=$(SCST_INC_DIR) SUBDIRS=$(KMOD) \
|
||||
modules_install
|
||||
$(MAKE) -C $(KDIR) SCST_INC_DIR=$(SCST_INC_DIR) SUBDIRS=$(ISERTMOD) \
|
||||
modules_install
|
||||
|
||||
uninstall:
|
||||
rm -f $(DESTDIR)$(SBINDIR)/iscsi-scstd \
|
||||
@@ -78,7 +130,8 @@ uninstall:
|
||||
$(DESTDIR)$(MANDIR)/man8/iscsi-scstd.8 \
|
||||
$(DESTDIR)$(SBINDIR)/iscsi-scst-adm \
|
||||
$(DESTDIR)$(MANDIR)/man8/iscsi-scst-adm.8 \
|
||||
$(INSTALL_DIR)/iscsi-scst.ko
|
||||
$(INSTALL_DIR)/iscsi-scst.ko \
|
||||
$(INSTALL_DIR)/isert-scst.ko
|
||||
-/sbin/depmod -b $(INSTALL_MOD_PATH)/ -a $(KVER)
|
||||
|
||||
SCST_MOD_VERS := $(shell ls $(SCST_DIR)/Modules.symvers 2>/dev/null)
|
||||
@@ -86,6 +139,7 @@ ifneq ($(SCST_MOD_VERS),)
|
||||
Modules.symvers: $(SCST_DIR)/Modules.symvers
|
||||
echo $(SCST_MOD_VERS)
|
||||
cp $(SCST_DIR)/Modules.symvers kernel/
|
||||
cp $(SCST_DIR)/Modules.symvers kernel/isert-scst
|
||||
else
|
||||
.PHONY: Modules.symvers
|
||||
endif
|
||||
@@ -95,6 +149,7 @@ SCST_MOD_VERS := $(shell ls $(SCST_DIR)/Module.symvers 2>/dev/null)
|
||||
ifneq ($(SCST_MOD_VERS),)
|
||||
Module.symvers: $(SCST_DIR)/Module.symvers
|
||||
cp $(SCST_DIR)/Module.symvers kernel/
|
||||
cp $(SCST_DIR)/Module.symvers kernel/isert-scst
|
||||
else
|
||||
.PHONY: Module.symvers
|
||||
endif
|
||||
@@ -102,17 +157,24 @@ endif
|
||||
clean:
|
||||
$(MAKE) -C usr $@
|
||||
$(MAKE) -C $(KDIR) SUBDIRS=$(KMOD) $@
|
||||
$(MAKE) -C $(KDIR) SUBDIRS=$(ISERTMOD) $@
|
||||
rm -f kernel/Modules.symvers kernel/Module.symvers \
|
||||
kernel/Module.markers kernel/modules.order \
|
||||
kernel/isert-scst/Modules.symvers kernel/isert-scst/Module.symvers \
|
||||
kernel/isert-scst/Module.markers kernel/isert-scst/modules.order \
|
||||
include/iscsi_scst_itf_ver.h
|
||||
|
||||
extraclean:
|
||||
$(MAKE) -C usr $@
|
||||
$(MAKE) -C $(KDIR) SUBDIRS=$(KMOD) clean
|
||||
$(MAKE) -C $(KDIR) SUBDIRS=$(ISERTMOD) clean
|
||||
rm -f kernel/Modules.symvers kernel/Module.symvers \
|
||||
kernel/Module.markers kernel/modules.order \
|
||||
kernel/isert-scst/Modules.symvers kernel/isert-scst/Module.symvers \
|
||||
kernel/isert-scst/Module.markers kernel/isert-scst/modules.order \
|
||||
include/iscsi_scst_itf_ver.h \
|
||||
kernel/*.orig kernel/*.rej
|
||||
kernel/*.orig kernel/*.rej \
|
||||
kernel/isert-scst/*.orig kernel/isert-scst/*.rej
|
||||
|
||||
2release:
|
||||
sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ $(KMOD)/Makefile
|
||||
@@ -122,6 +184,13 @@ extraclean:
|
||||
sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/ $(KMOD)/Makefile
|
||||
grep "^#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions" $(KMOD)/Makefile >/dev/null
|
||||
rm $(KMOD)/Makefile.aa
|
||||
sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ $(ISERTMOD)/Makefile
|
||||
grep "^#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS" $(ISERTMOD)/Makefile >/dev/null
|
||||
sed -i.aa s/"^#\?EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/"EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/ $(ISERTMOD)/Makefile
|
||||
grep "^EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING" $(ISERTMOD)/Makefile >/dev/null
|
||||
sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/ $(ISERTMOD)/Makefile
|
||||
grep "^#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions" $(ISERTMOD)/Makefile >/dev/null
|
||||
rm $(ISERTMOD)/Makefile.aa
|
||||
|
||||
2debug:
|
||||
sed -i.aa s/"^#\?EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ $(KMOD)/Makefile
|
||||
@@ -131,6 +200,13 @@ extraclean:
|
||||
sed -i.aa s/"^#\?EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/"EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/ $(KMOD)/Makefile
|
||||
grep "^EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions" $(KMOD)/Makefile >/dev/null
|
||||
rm $(KMOD)/Makefile.aa
|
||||
sed -i.aa s/"^#\?EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ $(ISERTMOD)/Makefile
|
||||
grep "^EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS" $(ISERTMOD)/Makefile >/dev/null
|
||||
sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/ $(ISERTMOD)/Makefile
|
||||
grep "^#EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING" $(ISERTMOD)/Makefile >/dev/null
|
||||
sed -i.aa s/"^#\?EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/"EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/ $(ISERTMOD)/Makefile
|
||||
grep "^EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions" $(ISERTMOD)/Makefile >/dev/null
|
||||
rm $(ISERTMOD)/Makefile.aa
|
||||
|
||||
2perf:
|
||||
sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ $(KMOD)/Makefile
|
||||
@@ -140,6 +216,13 @@ extraclean:
|
||||
sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/ $(KMOD)/Makefile
|
||||
grep "^#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions" $(KMOD)/Makefile >/dev/null
|
||||
rm $(KMOD)/Makefile.aa
|
||||
sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS"/ $(ISERTMOD)/Makefile
|
||||
grep "^#EXTRA_CFLAGS += \-DCONFIG_SCST_EXTRACHECKS" $(ISERTMOD)/Makefile >/dev/null
|
||||
sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING"/ $(ISERTMOD)/Makefile
|
||||
grep "^#EXTRA_CFLAGS += \-DCONFIG_SCST_TRACING" $(ISERTMOD)/Makefile >/dev/null
|
||||
sed -i.aa s/"^E\?XTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/"#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions"/ $(ISERTMOD)/Makefile
|
||||
grep "^#EXTRA_CFLAGS += \-DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions" $(ISERTMOD)/Makefile >/dev/null
|
||||
rm $(ISERTMOD)/Makefile.aa
|
||||
|
||||
disable_proc:
|
||||
sed -i.aa s/"^#\?define CONFIG_SCST_PROC"/"\/* #define CONFIG_SCST_PROC *\/"/ $(INCDIR)/iscsi_scst_ver.h
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
iSCSI extensions for RDMA driver:
|
||||
==================================
|
||||
|
||||
Installation & Configuration:
|
||||
---------------------------
|
||||
For installation and configuration, see iscsi README.
|
||||
There are no specific configuration options for iSER.
|
||||
See below for performance optimizations as well as troubleshooting.
|
||||
|
||||
|
||||
Performance considerations:
|
||||
---------------------------
|
||||
|
||||
In order to achieve better performance, it is recommended to specify
|
||||
"QueuedCommands 128" parameter per iSER target, since the transport
|
||||
is very fast and you usually want to connect it to fast backstorage.
|
||||
|
||||
|
||||
Troubleshooting:
|
||||
-----------------
|
||||
* Initiator fails to connect to target. The following message is seen in dmesg:
|
||||
Failed to accept conn request, err: -22
|
||||
The cause of this is often compilation issues if you have OFED or MLNX_OFED installed:
|
||||
If you are compiling for OFED/MLNX_OFED, make sure OFED is installed for
|
||||
the kernel you are running. Also, make sure you followed ALL steps described
|
||||
in README.iser_ofed including patching the kernel.
|
||||
If you are compiling for non-OFED kernel, make sure you don't have
|
||||
OFED/MLNX_OFED installed.
|
||||
|
||||
|
||||
* Discovery of iSER targets takes a long time or login to all discovered targets fails.
|
||||
iSCSI discovery does not have a way to determine between iSCSI and iSER
|
||||
enabled portals. Thus, initiator tries to connect to all interfaces it
|
||||
discovered (by default discovery is done over iSCSI TCP).
|
||||
In order to prevent this behaviour, you should specify
|
||||
"allowed_portal <target interface IP>" parameter for each target you want
|
||||
to export through specific RDMA capable adapters.
|
||||
|
||||
|
||||
* Initiator keeps connecting and disconnecting from target in a loop
|
||||
with constant interval after target reboot.
|
||||
The problem may be that connection requests from initiator are received
|
||||
on wrong port/HCA. This can be one due to one (or both) of the following issues:
|
||||
1) net.ipv4.conf.all.arp_ignore sysclt is not set to 2
|
||||
rdma-cm relies on ARP responses being received on the same interface
|
||||
that sent the request. Linux default does not do that.
|
||||
In order to make Linux behave good for rdma-cm, you _MUST_ add
|
||||
"net.ipv4.conf.all.arp_ignore = 2" to /etc/sysctl.conf
|
||||
2) You have more than 1 HCA and PCI mappings to netdev devices is not
|
||||
persistent between reboots. Possible solution is to have udev rules
|
||||
for mapping the ibX devices in persistent way.
|
||||
See below for udev scripts example:
|
||||
|
||||
/lib/udev/net.sh
|
||||
-------------------
|
||||
#!/bin/sh
|
||||
|
||||
. /etc/sysconfig/net.conf
|
||||
|
||||
type_fd="/sys/${DEVPATH}/type"
|
||||
if [ ! -f $type_fd ]; then
|
||||
exit
|
||||
fi
|
||||
type=`cat /sys/${DEVPATH}/type`
|
||||
|
||||
if [ "$type" = "32" ]; then # IPoIB interface
|
||||
i=0
|
||||
CONFDEV="DEV${i}"
|
||||
CONFPCI=${!CONFDEV}
|
||||
PCI=`basename $PHYSDEVPATH`
|
||||
while [ -n "$CONFPCI" ]; do
|
||||
if [ "$CONFPCI" = "$PCI" ]; then
|
||||
devid=$(printf "%d\n" `cat /sys/$DEVPATH/dev_id`)
|
||||
let id=$i*2+$devid
|
||||
DEV="ib$id"
|
||||
echo "$DEV"
|
||||
exit
|
||||
fi
|
||||
let i=i+1
|
||||
CONFDEV="DEV$i"
|
||||
CONFPCI=${!CONFDEV}
|
||||
done
|
||||
fi
|
||||
|
||||
/etc/sysconfig/net.conf
|
||||
-----------------------
|
||||
DEV0="0000:01:00.0"
|
||||
DEV1="0000:02:00.0"
|
||||
|
||||
/etc/udev/rules.d/90-network.rules
|
||||
-------------------------------------
|
||||
ACTION=="add", SUBSYSTEM=="net", PROGRAM="/lib/udev/net.sh", RESULT=="?*", NAME="$result"
|
||||
|
||||
|
||||
* Login to all targets from initiator sometimes times out.
|
||||
It may be a network problem (try running tools like ibdiagnet
|
||||
and rping between target and initiator hosts). The description of those tools
|
||||
is beyond the scope of this readme.
|
||||
Another issue may be that you failed to set net.ipv4.conf.all.arp_ignore sysctl
|
||||
to the value of 2 (see above problem for more detailed explanation).
|
||||
|
||||
|
||||
* When running IO, latency is getting higher and higher all the time.
|
||||
If you have enabled intel_iommu either in kernel command line or in
|
||||
kernel config (it may be enabled by default), you should specify
|
||||
iommu=pt on kernel command line to avoid the latency issue.
|
||||
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
iSCSI Extensins for RDMA (iSER) Target driver for Linux
|
||||
=================================================
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The iSER target driver has been designed to work on top of the Linux
|
||||
InfiniBand kernel drivers. While all recent Linux distributions
|
||||
include recent versions of the InfiniBand drivers, the only way to
|
||||
obtain the latest available InfiniBand drivers is by installing the
|
||||
OFED or MLNX_OFED (for Mellanox drivers) software stack.
|
||||
|
||||
The OFED stack is distributed by the OpenFabrics Alliance (OFA). The
|
||||
mission of the OpenFabrics Alliance is to is to develop, distribute
|
||||
and promote a unified, transport-independent, open-source software
|
||||
stack for RDMA-capable fabrics and networks, including InfiniBand and
|
||||
Ethernet.
|
||||
|
||||
The MLNX_OFED is distributed by Mellanox and can be obtained from
|
||||
http://www.mellanox.com/page/products_dyn?product_family=26
|
||||
|
||||
Note: because during OFED installation the distro-provided InfiniBand
|
||||
kernel drivers are replaced, doing so voids the support contract
|
||||
offered by your Linux distributor.
|
||||
|
||||
Please follow the instructions below carefully. Skipping a step may
|
||||
result in kernel modules that fail to load, a kernel oops or even a
|
||||
system that does no longer boot.
|
||||
|
||||
|
||||
Verifying the kernel version
|
||||
----------------------------
|
||||
|
||||
Before installing the OFED distribution, it is very important to check
|
||||
the OFED release notes. Each OFED distribution has been tested
|
||||
carefully, but only against the kernel versions specified in
|
||||
docs/OFED_release_notes.txt (you can find this document in the OFED
|
||||
distribution). Make sure that you are using a supported kernel / OFED
|
||||
combination. As an example, if you want to use OFED 1.5.1 on an Ubuntu
|
||||
system, you will have to start with replacing the Ubuntu kernel by a
|
||||
kernel from kernel.org since OFED 1.5.1 has not been tested on any
|
||||
Ubuntu kernel.
|
||||
|
||||
|
||||
Compiling iSER against OFED
|
||||
--------------------------
|
||||
|
||||
Make sure that all necessary packages needed for kernel compilation
|
||||
have been installed (kernel headers, gcc, binutils, ...).
|
||||
|
||||
Unload any loaded InfiniBand drivers:
|
||||
|
||||
/etc/init.d/opensmd stop
|
||||
/etc/init.d/openibd stop
|
||||
|
||||
Remove any distro-provided InfiniBand drivers:
|
||||
|
||||
rm -rf /lib/modules/$(uname -r)/kernel/drivers/infiniband
|
||||
rm -rf /lib/modules/$(uname -r)/kernel/drivers/net/mlx4
|
||||
|
||||
Now locate the file Makefile.lib and patch it such that it supports
|
||||
the variable PRE_CFLAGS:
|
||||
|
||||
if [ -e /lib/modules/$(uname -r)/build/scripts/Makefile.lib ]; then
|
||||
cd /lib/modules/$(uname -r)/build
|
||||
else
|
||||
cd /usr/src/linux-$(uname -r)
|
||||
fi
|
||||
patch -p1 < ${SCST_DIR}/srpt/patches/kernel-${KV}-pre-cflags.patch
|
||||
|
||||
Next, download and install an OFED pacakge.
|
||||
|
||||
For MLNX_OFED, just run the mlnxofedinstall script inside the MLNX_OFED directory.
|
||||
|
||||
NOTE TO ADVANCED USERS:
|
||||
------------------------
|
||||
If you are installing MLNX_OFED by manually selecting which RPMs/DEBs to install,
|
||||
make sure ofed_scripts package is one of them, since it is required for correct OFED
|
||||
version detection by iscsi-scst makefile.
|
||||
|
||||
|
||||
For the OFED package.Make sure to enable
|
||||
at least the kernel-ib and kernel-ib-devel packages (compat-rdma and compat-rdma-devel for OFED 3.5 and above).
|
||||
An example:
|
||||
|
||||
wget http://www.openfabrics.org/downloads/OFED/ofed-1.5.1/OFED-1.5.1.tgz
|
||||
tar xzf OFED-1.5.1.tgz
|
||||
cd OFED-1.5.1
|
||||
cat <<EOF >ofed.conf
|
||||
libibverbs=y
|
||||
libibverbs-utils=y
|
||||
libmthca=y
|
||||
libmlx4=y
|
||||
libcxgb3=y
|
||||
libnes=y
|
||||
libipathverbs=y
|
||||
librdmacm=y
|
||||
librdmacm-utils=y
|
||||
mstflint=y
|
||||
ofed-docs=y
|
||||
ofed-scripts=y
|
||||
kernel-ib=y
|
||||
kernel-ib-devel=y
|
||||
ibvexdmtools=y
|
||||
qlgc_vnic_daemon=y
|
||||
core=y
|
||||
mthca=y
|
||||
mlx4=y
|
||||
mlx4_en=y
|
||||
cxgb3=y
|
||||
nes=y
|
||||
ipath=y
|
||||
ipoib=y
|
||||
opensm=y
|
||||
opensm-libs=y
|
||||
srpt=n
|
||||
srptools=y
|
||||
perftest=y
|
||||
EOF
|
||||
./install.pl -c ofed.conf
|
||||
|
||||
Now continue with the installation instructions you can find in the
|
||||
ISCSI-SCST README file. The Makefile included with ISCSI-SCST detects
|
||||
whether OFED has been installed, and if so, compiles ISCSIS-SCST with
|
||||
the OFED kernel headers instead of with the regular kernel headers.
|
||||
@@ -61,6 +61,13 @@ enum {
|
||||
key_ifmarker,
|
||||
key_ofmarkint,
|
||||
key_ifmarkint,
|
||||
key_rdma_extensions,
|
||||
key_target_recv_data_length,
|
||||
key_initiator_recv_data_length,
|
||||
key_max_ahs_length,
|
||||
key_tagged_buffer_for_solicited_data_only,
|
||||
key_iser_hello_required,
|
||||
key_max_outstanding_unexpected_pdus,
|
||||
session_key_last,
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
|
||||
#ifndef __ISCSI_TRANSPORT_H__
|
||||
#define __ISCSI_TRANSPORT_H__
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#ifdef INSIDE_KERNEL_TREE
|
||||
#include <scst/scst.h>
|
||||
#else
|
||||
#include <scst.h>
|
||||
#endif
|
||||
|
||||
/* Forward declarations */
|
||||
struct iscsi_session;
|
||||
struct iscsi_kern_conn_info;
|
||||
struct iscsi_conn;
|
||||
|
||||
enum iscsit_transport_type {
|
||||
ISCSI_TCP,
|
||||
ISCSI_RDMA,
|
||||
};
|
||||
|
||||
struct iscsit_transport {
|
||||
struct iscsi_cmnd* (*iscsit_alloc_cmd)(struct iscsi_conn *conn,
|
||||
struct iscsi_cmnd *parent);
|
||||
void (*iscsit_preprocessing_done)(struct iscsi_cmnd *cmnd);
|
||||
void (*iscsit_send_data_rsp)(struct iscsi_cmnd *req, u8 *sense,
|
||||
int sense_len, u8 status,
|
||||
int send_status);
|
||||
int (*iscsit_send_locally)(struct iscsi_cmnd *cmnd,
|
||||
unsigned int cmd_count);
|
||||
void (*iscsit_set_sense_data)(struct iscsi_cmnd *rsp,
|
||||
const u8 *sense_buf, int sense_len);
|
||||
int (*iscsit_receive_cmnd_data)(struct iscsi_cmnd *cmnd);
|
||||
void (*iscsit_make_conn_wr_active)(struct iscsi_conn *conn);
|
||||
void (*iscsit_free_cmd)(struct iscsi_cmnd *cmnd);
|
||||
|
||||
void (*iscsit_set_req_data)(struct iscsi_cmnd *req,
|
||||
struct iscsi_cmnd *rsp);
|
||||
|
||||
int (*iscsit_conn_alloc)(struct iscsi_session *session,
|
||||
struct iscsi_kern_conn_info *info,
|
||||
struct iscsi_conn **new_conn,
|
||||
struct iscsit_transport *transport);
|
||||
int (*iscsit_conn_activate)(struct iscsi_conn *conn);
|
||||
void (*iscsit_conn_free)(struct iscsi_conn *conn);
|
||||
void (*iscsit_conn_close)(struct iscsi_conn *conn, int flags);
|
||||
void (*iscsit_mark_conn_closed)(struct iscsi_conn *conn, int flags);
|
||||
|
||||
ssize_t (*iscsit_get_initiator_ip)(struct iscsi_conn *conn, char *buf,
|
||||
int size);
|
||||
|
||||
void (*iscsit_close_all_portals)(void);
|
||||
|
||||
#if !defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
|
||||
unsigned int need_alloc_write_buf:1;
|
||||
#endif
|
||||
|
||||
struct module *owner;
|
||||
const char name[SCST_MAX_NAME];
|
||||
enum iscsit_transport_type transport_type;
|
||||
struct list_head transport_list_entry;
|
||||
} ____cacheline_aligned;
|
||||
|
||||
extern int iscsit_register_transport(struct iscsit_transport *t);
|
||||
extern void iscsit_unregister_transport(struct iscsit_transport *t);
|
||||
extern struct iscsit_transport *iscsit_get_transport(enum iscsit_transport_type type);
|
||||
|
||||
#endif /* __ISCSI_TRANSPORT_H__ */
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef _ISERT_SCST_U_H
|
||||
#define _ISERT_SCST_U_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
#include <linux/types.h>
|
||||
#include <linux/socket.h>
|
||||
#else
|
||||
#include <sys/uio.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
struct isert_addr_info {
|
||||
struct sockaddr_storage addr;
|
||||
size_t addr_len;
|
||||
};
|
||||
|
||||
#define ISERT_MAX_PORTALS 32
|
||||
|
||||
#define SET_LISTEN_ADDR _IOW('y', 0, struct isert_addr_info)
|
||||
#define RDMA_CORK _IOW('y', 1, int)
|
||||
#define GET_PORTAL_ADDR _IOW('y', 2, struct isert_addr_info)
|
||||
#define DISCOVERY_SESSION _IOW('y', 3, int)
|
||||
|
||||
#endif
|
||||
@@ -37,5 +37,6 @@ EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions
|
||||
|
||||
obj-m += iscsi-scst.o
|
||||
iscsi-scst-objs := iscsi.o nthread.o config.o digest.o \
|
||||
conn.o session.o target.o event.o param.o
|
||||
conn.o session.o target.o event.o param.o \
|
||||
iscsit_transport.o
|
||||
|
||||
|
||||
+81
-78
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "iscsi.h"
|
||||
#include "digest.h"
|
||||
#include "iscsit_transport.h"
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
|
||||
#if defined(CONFIG_LOCKDEP) && !defined(CONFIG_SCST_PROC)
|
||||
@@ -157,42 +158,7 @@ struct kobj_type iscsi_conn_ktype = {
|
||||
static ssize_t iscsi_get_initiator_ip(struct iscsi_conn *conn,
|
||||
char *buf, int size)
|
||||
{
|
||||
int pos;
|
||||
struct sock *sk;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
sk = conn->sock->sk;
|
||||
switch (sk->sk_family) {
|
||||
case AF_INET:
|
||||
pos = scnprintf(buf, size,
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,33)
|
||||
"%u.%u.%u.%u", NIPQUAD(inet_sk(sk)->daddr));
|
||||
#else
|
||||
"%pI4", &inet_sk(sk)->inet_daddr);
|
||||
#endif
|
||||
break;
|
||||
case AF_INET6:
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,29)
|
||||
pos = scnprintf(buf, size,
|
||||
"[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]",
|
||||
NIP6(inet6_sk(sk)->daddr));
|
||||
#else
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
|
||||
pos = scnprintf(buf, size, "[%p6]", &inet6_sk(sk)->daddr);
|
||||
#else
|
||||
pos = scnprintf(buf, size, "[%p6]", &sk->sk_v6_daddr);
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
pos = scnprintf(buf, size, "Unknown family %d",
|
||||
sk->sk_family);
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE_EXIT_RES(pos);
|
||||
return pos;
|
||||
return conn->transport->iscsit_get_initiator_ip(conn, buf, size);
|
||||
}
|
||||
|
||||
static ssize_t iscsi_conn_ip_show(struct kobject *kobj,
|
||||
@@ -270,7 +236,7 @@ static void conn_sysfs_del(struct iscsi_conn *conn)
|
||||
return;
|
||||
}
|
||||
|
||||
static int conn_sysfs_add(struct iscsi_conn *conn)
|
||||
int conn_sysfs_add(struct iscsi_conn *conn)
|
||||
{
|
||||
int res;
|
||||
struct iscsi_session *session = conn->session;
|
||||
@@ -344,6 +310,7 @@ out_err:
|
||||
conn_sysfs_del(conn);
|
||||
goto out;
|
||||
}
|
||||
EXPORT_SYMBOL(conn_sysfs_add);
|
||||
|
||||
#endif /* CONFIG_SCST_PROC */
|
||||
|
||||
@@ -430,7 +397,7 @@ void iscsi_make_conn_wr_active(struct iscsi_conn *conn)
|
||||
return;
|
||||
}
|
||||
|
||||
void __mark_conn_closed(struct iscsi_conn *conn, int flags)
|
||||
void iscsi_tcp_mark_conn_closed(struct iscsi_conn *conn, int flags)
|
||||
{
|
||||
spin_lock_bh(&conn->conn_thr_pool->rd_lock);
|
||||
conn->closing = 1;
|
||||
@@ -443,10 +410,16 @@ void __mark_conn_closed(struct iscsi_conn *conn, int flags)
|
||||
iscsi_make_conn_rd_active(conn);
|
||||
}
|
||||
|
||||
void __mark_conn_closed(struct iscsi_conn *conn, int flags)
|
||||
{
|
||||
conn->transport->iscsit_mark_conn_closed(conn, flags);
|
||||
}
|
||||
|
||||
void mark_conn_closed(struct iscsi_conn *conn)
|
||||
{
|
||||
__mark_conn_closed(conn, ISCSI_CONN_ACTIVE_CLOSE);
|
||||
}
|
||||
EXPORT_SYMBOL(mark_conn_closed);
|
||||
|
||||
static void __iscsi_state_change(struct sock *sk)
|
||||
{
|
||||
@@ -745,7 +718,7 @@ void conn_reinst_finished(struct iscsi_conn *conn)
|
||||
return;
|
||||
}
|
||||
|
||||
static void conn_activate(struct iscsi_conn *conn)
|
||||
int conn_activate(struct iscsi_conn *conn)
|
||||
{
|
||||
TRACE_MGMT_DBG("Enabling conn %p", conn);
|
||||
|
||||
@@ -771,7 +744,7 @@ static void conn_activate(struct iscsi_conn *conn)
|
||||
*/
|
||||
__iscsi_state_change(conn->sock->sk);
|
||||
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -813,8 +786,19 @@ out:
|
||||
return res;
|
||||
}
|
||||
|
||||
void iscsi_tcp_conn_free(struct iscsi_conn *conn)
|
||||
{
|
||||
fput(conn->file);
|
||||
conn->file = NULL;
|
||||
conn->sock = NULL;
|
||||
|
||||
free_page((unsigned long)conn->read_iov);
|
||||
|
||||
kmem_cache_free(iscsi_conn_cache, conn);
|
||||
}
|
||||
|
||||
/* target_mutex supposed to be locked */
|
||||
int conn_free(struct iscsi_conn *conn)
|
||||
void conn_free(struct iscsi_conn *conn)
|
||||
{
|
||||
struct iscsi_session *session = conn->session;
|
||||
|
||||
@@ -855,48 +839,19 @@ int conn_free(struct iscsi_conn *conn)
|
||||
|
||||
list_del(&conn->conn_list_entry);
|
||||
|
||||
fput(conn->file);
|
||||
conn->file = NULL;
|
||||
conn->sock = NULL;
|
||||
|
||||
free_page((unsigned long)conn->read_iov);
|
||||
|
||||
kmem_cache_free(iscsi_conn_cache, conn);
|
||||
conn->transport->iscsit_conn_free(conn);
|
||||
|
||||
if (list_empty(&session->conn_list)) {
|
||||
sBUG_ON(session->sess_reinst_successor != NULL);
|
||||
session_free(session, true);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* target_mutex supposed to be locked */
|
||||
static int iscsi_conn_alloc(struct iscsi_session *session,
|
||||
struct iscsi_kern_conn_info *info, struct iscsi_conn **new_conn)
|
||||
int iscsi_init_conn(struct iscsi_session *session,
|
||||
struct iscsi_kern_conn_info *info,
|
||||
struct iscsi_conn *conn)
|
||||
{
|
||||
struct iscsi_conn *conn;
|
||||
int res = 0;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
|
||||
lockdep_assert_held(&session->target->target_mutex);
|
||||
#endif
|
||||
|
||||
conn = kmem_cache_zalloc(iscsi_conn_cache, GFP_KERNEL);
|
||||
if (!conn) {
|
||||
res = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
TRACE_MGMT_DBG("Creating connection %p for sid %#Lx, cid %u", conn,
|
||||
(long long unsigned int)session->sid, info->cid);
|
||||
|
||||
/* Changing it, change ISCSI_CONN_IOV_MAX as well !! */
|
||||
conn->read_iov = (struct iovec *)get_zeroed_page(GFP_KERNEL);
|
||||
if (conn->read_iov == NULL) {
|
||||
res = -ENOMEM;
|
||||
goto out_err_free_conn;
|
||||
}
|
||||
int res;
|
||||
|
||||
atomic_set(&conn->conn_ref_cnt, 0);
|
||||
conn->session = session;
|
||||
@@ -912,7 +867,7 @@ static int iscsi_conn_alloc(struct iscsi_session *session,
|
||||
conn->ddigest_type = session->sess_params.data_digest;
|
||||
res = digest_init(conn);
|
||||
if (res != 0)
|
||||
goto out_free_iov;
|
||||
return res;
|
||||
|
||||
conn->target = session->target;
|
||||
spin_lock_init(&conn->cmd_list_lock);
|
||||
@@ -947,6 +902,44 @@ static int iscsi_conn_alloc(struct iscsi_session *session,
|
||||
conn->nop_in_interval + ISCSI_ADD_SCHED_TIME);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(iscsi_init_conn);
|
||||
|
||||
/* target_mutex supposed to be locked */
|
||||
int iscsi_conn_alloc(struct iscsi_session *session,
|
||||
struct iscsi_kern_conn_info *info, struct iscsi_conn **new_conn,
|
||||
struct iscsit_transport *t)
|
||||
{
|
||||
struct iscsi_conn *conn;
|
||||
int res = 0;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
|
||||
lockdep_assert_held(&session->target->target_mutex);
|
||||
#endif
|
||||
|
||||
conn = kmem_cache_zalloc(iscsi_conn_cache, GFP_KERNEL);
|
||||
if (!conn) {
|
||||
res = -ENOMEM;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
TRACE_MGMT_DBG("Creating connection %p for sid %#Lx, cid %u", conn,
|
||||
(long long unsigned int)session->sid, info->cid);
|
||||
|
||||
conn->transport = t;
|
||||
|
||||
/* Changing it, change ISCSI_CONN_IOV_MAX as well !! */
|
||||
conn->read_iov = (struct iovec *)get_zeroed_page(GFP_KERNEL);
|
||||
if (conn->read_iov == NULL) {
|
||||
res = -ENOMEM;
|
||||
goto out_err_free_conn;
|
||||
}
|
||||
|
||||
res = iscsi_init_conn(session, info, conn);
|
||||
if (res != 0)
|
||||
goto out_free_iov;
|
||||
|
||||
conn->file = fget(info->fd);
|
||||
|
||||
res = conn_setup_sock(conn);
|
||||
@@ -985,6 +978,7 @@ int __add_conn(struct iscsi_session *session, struct iscsi_kern_conn_info *info)
|
||||
struct iscsi_conn *conn, *new_conn = NULL;
|
||||
int err;
|
||||
bool reinstatement = false;
|
||||
struct iscsit_transport *t;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
|
||||
lockdep_assert_held(&session->target->target_mutex);
|
||||
@@ -1000,7 +994,16 @@ int __add_conn(struct iscsi_session *session, struct iscsi_kern_conn_info *info)
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = iscsi_conn_alloc(session, info, &new_conn);
|
||||
if (session->sess_params.rdma_extensions)
|
||||
t = iscsit_get_transport(ISCSI_RDMA);
|
||||
else
|
||||
t = iscsit_get_transport(ISCSI_TCP);
|
||||
if (!t) {
|
||||
err = -ENOENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = t->iscsit_conn_alloc(session, info, &new_conn, t);
|
||||
if (err != 0)
|
||||
goto out;
|
||||
|
||||
@@ -1012,7 +1015,7 @@ int __add_conn(struct iscsi_session *session, struct iscsi_kern_conn_info *info)
|
||||
__mark_conn_closed(conn, 0);
|
||||
}
|
||||
|
||||
conn_activate(new_conn);
|
||||
err = t->iscsit_conn_activate(new_conn);
|
||||
|
||||
out:
|
||||
return err;
|
||||
|
||||
+304
-158
@@ -26,6 +26,7 @@
|
||||
|
||||
#include "iscsi.h"
|
||||
#include "digest.h"
|
||||
#include "iscsit_transport.h"
|
||||
|
||||
#ifndef GENERATING_UPSTREAM_PATCH
|
||||
#if !defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
|
||||
@@ -64,11 +65,9 @@ static void cmnd_remove_data_wait_hash(struct iscsi_cmnd *cmnd);
|
||||
static void iscsi_send_task_mgmt_resp(struct iscsi_cmnd *req, int status,
|
||||
bool dropped);
|
||||
static void iscsi_check_send_delayed_tm_resp(struct iscsi_session *sess);
|
||||
static void req_cmnd_release(struct iscsi_cmnd *req);
|
||||
static int cmnd_insert_data_wait_hash(struct iscsi_cmnd *cmnd);
|
||||
static void iscsi_cmnd_init_write(struct iscsi_cmnd *rsp, int flags);
|
||||
static void iscsi_set_resid_no_scst_cmd(struct iscsi_cmnd *rsp);
|
||||
static void iscsi_set_resid(struct iscsi_cmnd *rsp);
|
||||
|
||||
static void iscsi_set_not_received_data_len(struct iscsi_cmnd *req,
|
||||
unsigned int not_received)
|
||||
@@ -242,7 +241,7 @@ static struct iscsi_cmnd *iscsi_create_tm_clone(struct iscsi_cmnd *cmnd)
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
tm_clone = cmnd_alloc(cmnd->conn, NULL);
|
||||
tm_clone = cmnd->conn->transport->iscsit_alloc_cmd(cmnd->conn, NULL);
|
||||
if (tm_clone != NULL) {
|
||||
set_bit(ISCSI_CMD_ABORTED, &tm_clone->prelim_compl_flags);
|
||||
tm_clone->pdu = cmnd->pdu;
|
||||
@@ -309,14 +308,9 @@ void iscsi_fail_data_waiting_cmnd(struct iscsi_cmnd *cmnd)
|
||||
return;
|
||||
}
|
||||
|
||||
struct iscsi_cmnd *cmnd_alloc(struct iscsi_conn *conn,
|
||||
struct iscsi_cmnd *parent)
|
||||
void iscsi_cmnd_init(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd,
|
||||
struct iscsi_cmnd *parent)
|
||||
{
|
||||
struct iscsi_cmnd *cmnd;
|
||||
|
||||
/* ToDo: __GFP_NOFAIL?? */
|
||||
cmnd = kmem_cache_zalloc(iscsi_cmnd_cache, GFP_KERNEL|__GFP_NOFAIL);
|
||||
|
||||
atomic_set(&cmnd->ref_cnt, 1);
|
||||
cmnd->scst_state = ISCSI_CMD_STATE_NEW;
|
||||
cmnd->conn = conn;
|
||||
@@ -325,9 +319,6 @@ struct iscsi_cmnd *cmnd_alloc(struct iscsi_conn *conn,
|
||||
if (parent == NULL) {
|
||||
conn_get(conn);
|
||||
|
||||
#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
|
||||
atomic_set(&cmnd->net_ref_cnt, 0);
|
||||
#endif
|
||||
INIT_LIST_HEAD(&cmnd->rsp_cmd_list);
|
||||
INIT_LIST_HEAD(&cmnd->rx_ddigest_cmd_list);
|
||||
cmnd->target_task_tag = ISCSI_RESERVED_TAG_CPU32;
|
||||
@@ -336,6 +327,24 @@ struct iscsi_cmnd *cmnd_alloc(struct iscsi_conn *conn,
|
||||
list_add_tail(&cmnd->cmd_list_entry, &conn->cmd_list);
|
||||
spin_unlock_bh(&conn->cmd_list_lock);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(iscsi_cmnd_init);
|
||||
|
||||
struct iscsi_cmnd *cmnd_alloc(struct iscsi_conn *conn,
|
||||
struct iscsi_cmnd *parent)
|
||||
{
|
||||
struct iscsi_cmnd *cmnd;
|
||||
|
||||
/* ToDo: __GFP_NOFAIL?? */
|
||||
cmnd = kmem_cache_zalloc(iscsi_cmnd_cache, GFP_KERNEL|__GFP_NOFAIL);
|
||||
|
||||
iscsi_cmnd_init(conn, cmnd, parent);
|
||||
|
||||
if (parent == NULL) {
|
||||
#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
|
||||
atomic_set(&cmnd->net_ref_cnt, 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
TRACE_DBG("conn %p, parent %p, cmnd %p", conn, parent, cmnd);
|
||||
return cmnd;
|
||||
@@ -496,10 +505,10 @@ void cmnd_done(struct iscsi_cmnd *cmnd)
|
||||
|
||||
list_for_each_entry_safe(rsp, t, &cmnd->rsp_cmd_list,
|
||||
rsp_cmd_list_entry) {
|
||||
cmnd_free(rsp);
|
||||
cmnd->conn->transport->iscsit_free_cmd(rsp);
|
||||
}
|
||||
|
||||
cmnd_free(cmnd);
|
||||
cmnd->conn->transport->iscsit_free_cmd(cmnd);
|
||||
} else {
|
||||
struct iscsi_cmnd *parent = cmnd->parent_req;
|
||||
|
||||
@@ -532,6 +541,7 @@ void cmnd_done(struct iscsi_cmnd *cmnd)
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(cmnd_done);
|
||||
|
||||
/*
|
||||
* Corresponding conn may also get destroyed after this function, except only
|
||||
@@ -634,7 +644,7 @@ out:
|
||||
return;
|
||||
}
|
||||
|
||||
static void req_cmnd_pre_release(struct iscsi_cmnd *req)
|
||||
void req_cmnd_pre_release(struct iscsi_cmnd *req)
|
||||
{
|
||||
struct iscsi_cmnd *c, *t;
|
||||
|
||||
@@ -694,12 +704,13 @@ static void req_cmnd_pre_release(struct iscsi_cmnd *req)
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(req_cmnd_pre_release);
|
||||
|
||||
/*
|
||||
* Corresponding conn may also get destroyed after this function, except only
|
||||
* if it's called from the read thread!
|
||||
*/
|
||||
static void req_cmnd_release(struct iscsi_cmnd *req)
|
||||
void req_cmnd_release(struct iscsi_cmnd *req)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
@@ -709,6 +720,7 @@ static void req_cmnd_release(struct iscsi_cmnd *req)
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(req_cmnd_release);
|
||||
|
||||
/*
|
||||
* Corresponding conn may also get destroyed after this function, except only
|
||||
@@ -728,6 +740,7 @@ void rsp_cmnd_release(struct iscsi_cmnd *cmnd)
|
||||
cmnd_put(cmnd);
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(rsp_cmnd_release);
|
||||
|
||||
static struct iscsi_cmnd *iscsi_alloc_rsp(struct iscsi_cmnd *parent)
|
||||
{
|
||||
@@ -735,7 +748,7 @@ static struct iscsi_cmnd *iscsi_alloc_rsp(struct iscsi_cmnd *parent)
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
rsp = cmnd_alloc(parent->conn, parent);
|
||||
rsp = parent->conn->transport->iscsit_alloc_cmd(parent->conn, parent);
|
||||
|
||||
TRACE_DBG("Adding rsp %p to parent %p", rsp, parent);
|
||||
list_add_tail(&rsp->rsp_cmd_list_entry, &parent->rsp_cmd_list);
|
||||
@@ -797,7 +810,7 @@ static void iscsi_cmnds_init_write(struct list_head *send, int flags)
|
||||
spin_unlock_bh(&conn->write_list_lock);
|
||||
|
||||
if (flags & ISCSI_INIT_WRITE_WAKE)
|
||||
iscsi_make_conn_wr_active(conn);
|
||||
conn->transport->iscsit_make_conn_wr_active(conn);
|
||||
|
||||
return;
|
||||
}
|
||||
@@ -876,7 +889,7 @@ static void iscsi_set_resid_no_scst_cmd(struct iscsi_cmnd *rsp)
|
||||
return;
|
||||
}
|
||||
|
||||
static void iscsi_set_resid(struct iscsi_cmnd *rsp)
|
||||
void iscsi_set_resid(struct iscsi_cmnd *rsp)
|
||||
{
|
||||
struct iscsi_cmnd *req = rsp->parent_req;
|
||||
struct scst_cmd *scst_cmd = req->scst_cmd;
|
||||
@@ -930,6 +943,7 @@ out:
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(iscsi_set_resid);
|
||||
|
||||
static void send_data_rsp(struct iscsi_cmnd *req, u8 status, int send_status)
|
||||
{
|
||||
@@ -992,12 +1006,25 @@ static void send_data_rsp(struct iscsi_cmnd *req, u8 status, int send_status)
|
||||
return;
|
||||
}
|
||||
|
||||
static void iscsi_tcp_set_sense_data(struct iscsi_cmnd *rsp,
|
||||
const u8 *sense_buf, int sense_len)
|
||||
{
|
||||
struct scatterlist *sg;
|
||||
|
||||
sg = rsp->sg = rsp->rsp_sg;
|
||||
rsp->sg_cnt = 2;
|
||||
rsp->own_sg = 1;
|
||||
|
||||
sg_init_table(sg, 2);
|
||||
sg_set_buf(&sg[0], &rsp->sense_hdr, sizeof(rsp->sense_hdr));
|
||||
sg_set_buf(&sg[1], sense_buf, sense_len);
|
||||
}
|
||||
|
||||
static void iscsi_init_status_rsp(struct iscsi_cmnd *rsp,
|
||||
int status, const u8 *sense_buf, int sense_len)
|
||||
{
|
||||
struct iscsi_cmnd *req = rsp->parent_req;
|
||||
struct iscsi_scsi_rsp_hdr *rsp_hdr;
|
||||
struct scatterlist *sg;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
@@ -1011,16 +1038,11 @@ static void iscsi_init_status_rsp(struct iscsi_cmnd *rsp,
|
||||
if (scst_sense_valid(sense_buf)) {
|
||||
TRACE_DBG("%s", "SENSE VALID");
|
||||
|
||||
sg = rsp->sg = rsp->rsp_sg;
|
||||
rsp->sg_cnt = 2;
|
||||
rsp->own_sg = 1;
|
||||
|
||||
sg_init_table(sg, 2);
|
||||
sg_set_buf(&sg[0], &rsp->sense_hdr, sizeof(rsp->sense_hdr));
|
||||
sg_set_buf(&sg[1], sense_buf, sense_len);
|
||||
|
||||
rsp->sense_hdr.length = cpu_to_be16(sense_len);
|
||||
|
||||
rsp->conn->transport->iscsit_set_sense_data(rsp, sense_buf,
|
||||
sense_len);
|
||||
|
||||
rsp->pdu.datasize = sizeof(rsp->sense_hdr) + sense_len;
|
||||
rsp->bufflen = rsp->pdu.datasize;
|
||||
} else {
|
||||
@@ -1032,7 +1054,7 @@ static void iscsi_init_status_rsp(struct iscsi_cmnd *rsp,
|
||||
return;
|
||||
}
|
||||
|
||||
static inline struct iscsi_cmnd *create_status_rsp(struct iscsi_cmnd *req,
|
||||
struct iscsi_cmnd *create_status_rsp(struct iscsi_cmnd *req,
|
||||
int status, const u8 *sense_buf, int sense_len)
|
||||
{
|
||||
struct iscsi_cmnd *rsp;
|
||||
@@ -1048,6 +1070,26 @@ static inline struct iscsi_cmnd *create_status_rsp(struct iscsi_cmnd *req,
|
||||
TRACE_EXIT_HRES((unsigned long)rsp);
|
||||
return rsp;
|
||||
}
|
||||
EXPORT_SYMBOL(create_status_rsp);
|
||||
|
||||
static void iscsi_tcp_send_data_rsp(struct iscsi_cmnd *req, u8 *sense,
|
||||
int sense_len, u8 status,
|
||||
int is_send_status)
|
||||
{
|
||||
if ((status != SAM_STAT_CHECK_CONDITION) &&
|
||||
((cmnd_hdr(req)->flags & (ISCSI_CMD_WRITE|ISCSI_CMD_READ)) !=
|
||||
(ISCSI_CMD_WRITE|ISCSI_CMD_READ))) {
|
||||
send_data_rsp(req, status, is_send_status);
|
||||
} else {
|
||||
struct iscsi_cmnd *rsp;
|
||||
send_data_rsp(req, 0, 0);
|
||||
if (is_send_status) {
|
||||
rsp = create_status_rsp(req, status, sense,
|
||||
sense_len);
|
||||
iscsi_cmnd_init_write(rsp, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Initializes data receive fields. Can be called only when they have not been
|
||||
@@ -1224,7 +1266,7 @@ static inline int iscsi_get_allowed_cmds(struct iscsi_session *sess)
|
||||
return res;
|
||||
}
|
||||
|
||||
static __be32 cmnd_set_sn(struct iscsi_cmnd *cmnd, int set_stat_sn)
|
||||
__be32 cmnd_set_sn(struct iscsi_cmnd *cmnd, int set_stat_sn)
|
||||
{
|
||||
struct iscsi_conn *conn = cmnd->conn;
|
||||
struct iscsi_session *sess = conn->session;
|
||||
@@ -1243,6 +1285,7 @@ static __be32 cmnd_set_sn(struct iscsi_cmnd *cmnd, int set_stat_sn)
|
||||
spin_unlock(&sess->sn_lock);
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(cmnd_set_sn);
|
||||
|
||||
/* Called under sn_lock */
|
||||
static void update_stat_sn(struct iscsi_cmnd *cmnd)
|
||||
@@ -1740,7 +1783,7 @@ static int nop_out_start(struct iscsi_cmnd *cmnd)
|
||||
|
||||
size = cmnd->pdu.datasize;
|
||||
|
||||
if (size) {
|
||||
if (size && !conn->session->sess_params.rdma_extensions) {
|
||||
conn->read_msg.msg_iov = conn->read_iov;
|
||||
if (cmnd->pdu.bhs.itt != ISCSI_RESERVED_TAG) {
|
||||
struct scatterlist *sg;
|
||||
@@ -1797,16 +1840,87 @@ out:
|
||||
return err;
|
||||
}
|
||||
|
||||
int cmnd_rx_continue(struct iscsi_cmnd *req)
|
||||
int iscsi_cmnd_set_write_buf(struct iscsi_cmnd *req)
|
||||
{
|
||||
struct iscsi_conn *conn = req->conn;
|
||||
struct iscsi_session *session = conn->session;
|
||||
struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req);
|
||||
struct scst_cmd *scst_cmd = req->scst_cmd;
|
||||
scst_data_direction dir;
|
||||
bool unsolicited_data_expected = false;
|
||||
int res = 0;
|
||||
|
||||
req->bufflen = scst_cmd_get_write_fields(scst_cmd, &req->sg,
|
||||
&req->sg_cnt);
|
||||
unsolicited_data_expected = !(req_hdr->flags & ISCSI_CMD_FINAL);
|
||||
|
||||
if (unlikely(session->sess_params.initial_r2t &&
|
||||
unsolicited_data_expected)) {
|
||||
PRINT_ERROR("Initiator %s violated negotiated "
|
||||
"parameters: initial R2T is required (ITT %x, "
|
||||
"op %x)", session->initiator_name,
|
||||
req->pdu.bhs.itt, req_hdr->scb[0]);
|
||||
res = -EINVAL;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
if (unlikely(!session->sess_params.immediate_data &&
|
||||
req->pdu.datasize)) {
|
||||
PRINT_ERROR("Initiator %s violated negotiated "
|
||||
"parameters: forbidden immediate data sent "
|
||||
"(ITT %x, op %x)", session->initiator_name,
|
||||
req->pdu.bhs.itt, req_hdr->scb[0]);
|
||||
res = -EINVAL;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
if (unlikely(session->sess_params.first_burst_length < req->pdu.datasize)) {
|
||||
PRINT_ERROR("Initiator %s violated negotiated "
|
||||
"parameters: immediate data len (%d) > "
|
||||
"first_burst_length (%d) (ITT %x, op %x)",
|
||||
session->initiator_name,
|
||||
req->pdu.datasize,
|
||||
session->sess_params.first_burst_length,
|
||||
req->pdu.bhs.itt, req_hdr->scb[0]);
|
||||
res = -EINVAL;
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
req->r2t_len_to_receive = be32_to_cpu(req_hdr->data_length) -
|
||||
req->pdu.datasize;
|
||||
|
||||
/*
|
||||
* In case of residual overflow req->r2t_len_to_receive and
|
||||
* req->pdu.datasize might be > req->bufflen
|
||||
*/
|
||||
|
||||
res = cmnd_insert_data_wait_hash(req);
|
||||
|
||||
if (unsolicited_data_expected) {
|
||||
req->outstanding_r2t = 1;
|
||||
req->r2t_len_to_send = req->r2t_len_to_receive -
|
||||
min_t(unsigned int,
|
||||
session->sess_params.first_burst_length -
|
||||
req->pdu.datasize,
|
||||
req->r2t_len_to_receive);
|
||||
} else
|
||||
req->r2t_len_to_send = req->r2t_len_to_receive;
|
||||
|
||||
if (likely(res == 0))
|
||||
req_add_to_write_timeout_list(req);
|
||||
|
||||
out_close:
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(iscsi_cmnd_set_write_buf);
|
||||
|
||||
int cmnd_rx_continue(struct iscsi_cmnd *req)
|
||||
{
|
||||
struct iscsi_conn *conn = req->conn;
|
||||
struct iscsi_scsi_cmd_hdr *req_hdr = cmnd_hdr(req);
|
||||
struct scst_cmd *scst_cmd = req->scst_cmd;
|
||||
scst_data_direction dir;
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
TRACE_DBG("scsi command: %x", req_hdr->scb[0]);
|
||||
@@ -1832,48 +1946,7 @@ int cmnd_rx_continue(struct iscsi_cmnd *req)
|
||||
/* For prelim completed commands sg & K can be already set! */
|
||||
|
||||
if (dir & SCST_DATA_WRITE) {
|
||||
req->bufflen = scst_cmd_get_write_fields(scst_cmd, &req->sg,
|
||||
&req->sg_cnt);
|
||||
unsolicited_data_expected = !(req_hdr->flags & ISCSI_CMD_FINAL);
|
||||
|
||||
if (unlikely(session->sess_params.initial_r2t &&
|
||||
unsolicited_data_expected)) {
|
||||
PRINT_ERROR("Initiator %s violated negotiated "
|
||||
"parameters: initial R2T is required (ITT %x, "
|
||||
"op %x)", session->initiator_name,
|
||||
req->pdu.bhs.itt, req_hdr->scb[0]);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
if (unlikely(!session->sess_params.immediate_data &&
|
||||
req->pdu.datasize)) {
|
||||
PRINT_ERROR("Initiator %s violated negotiated "
|
||||
"parameters: forbidden immediate data sent "
|
||||
"(ITT %x, op %x)", session->initiator_name,
|
||||
req->pdu.bhs.itt, req_hdr->scb[0]);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
if (unlikely(session->sess_params.first_burst_length < req->pdu.datasize)) {
|
||||
PRINT_ERROR("Initiator %s violated negotiated "
|
||||
"parameters: immediate data len (%d) > "
|
||||
"first_burst_length (%d) (ITT %x, op %x)",
|
||||
session->initiator_name,
|
||||
req->pdu.datasize,
|
||||
session->sess_params.first_burst_length,
|
||||
req->pdu.bhs.itt, req_hdr->scb[0]);
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
req->r2t_len_to_receive = be32_to_cpu(req_hdr->data_length) -
|
||||
req->pdu.datasize;
|
||||
|
||||
/*
|
||||
* In case of residual overflow req->r2t_len_to_receive and
|
||||
* req->pdu.datasize might be > req->bufflen
|
||||
*/
|
||||
|
||||
res = cmnd_insert_data_wait_hash(req);
|
||||
res = iscsi_cmnd_set_write_buf(req);
|
||||
if (unlikely(res != 0)) {
|
||||
/*
|
||||
* We have to close connection, because otherwise a data
|
||||
@@ -1884,18 +1957,6 @@ int cmnd_rx_continue(struct iscsi_cmnd *req)
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
if (unsolicited_data_expected) {
|
||||
req->outstanding_r2t = 1;
|
||||
req->r2t_len_to_send = req->r2t_len_to_receive -
|
||||
min_t(unsigned int,
|
||||
session->sess_params.first_burst_length -
|
||||
req->pdu.datasize,
|
||||
req->r2t_len_to_receive);
|
||||
} else
|
||||
req->r2t_len_to_send = req->r2t_len_to_receive;
|
||||
|
||||
req_add_to_write_timeout_list(req);
|
||||
|
||||
if (req->pdu.datasize) {
|
||||
res = cmnd_prepare_recv_pdu(conn, req, 0, req->pdu.datasize);
|
||||
/* For performance better to send R2Ts ASAP */
|
||||
@@ -1917,11 +1978,9 @@ int cmnd_rx_continue(struct iscsi_cmnd *req)
|
||||
}
|
||||
|
||||
trace:
|
||||
TRACE_DBG("req=%p, dir=%d, unsolicited_data_expected=%d, "
|
||||
"r2t_len_to_receive=%d, r2t_len_to_send=%d, bufflen=%d, "
|
||||
"own_sg %d", req, dir, unsolicited_data_expected,
|
||||
req->r2t_len_to_receive, req->r2t_len_to_send, req->bufflen,
|
||||
req->own_sg);
|
||||
TRACE_DBG("req=%p, dir=%d, r2t_len_to_receive=%d, r2t_len_to_send=%d, "
|
||||
"bufflen=%d, own_sg %d", req, dir, req->r2t_len_to_receive,
|
||||
req->r2t_len_to_send, req->bufflen, req->own_sg);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
@@ -1981,7 +2040,8 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req)
|
||||
scst_cmd_set_expected_out_transfer_len(scst_cmd,
|
||||
be32_to_cpu(req_hdr->data_length));
|
||||
#if !defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
|
||||
scst_cmd_set_tgt_need_alloc_data_buf(scst_cmd);
|
||||
if (conn->transport->need_alloc_write_buf)
|
||||
scst_cmd_set_tgt_need_alloc_data_buf(scst_cmd);
|
||||
#endif
|
||||
}
|
||||
} else if (req_hdr->flags & ISCSI_CMD_READ) {
|
||||
@@ -1989,7 +2049,8 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req)
|
||||
scst_cmd_set_expected(scst_cmd, dir,
|
||||
be32_to_cpu(req_hdr->data_length));
|
||||
#if !defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
|
||||
scst_cmd_set_tgt_need_alloc_data_buf(scst_cmd);
|
||||
if (conn->transport->need_alloc_write_buf)
|
||||
scst_cmd_set_tgt_need_alloc_data_buf(scst_cmd);
|
||||
#endif
|
||||
} else if (req_hdr->flags & ISCSI_CMD_WRITE) {
|
||||
dir = SCST_DATA_WRITE;
|
||||
@@ -2056,7 +2117,7 @@ static int scsi_cmnd_start(struct iscsi_cmnd *req)
|
||||
scst_cmd_init_stage1_done(scst_cmd, SCST_CONTEXT_DIRECT, 0);
|
||||
|
||||
if (req->scst_state != ISCSI_CMD_STATE_RX_CMD)
|
||||
res = cmnd_rx_continue(req);
|
||||
res = req->conn->transport->iscsit_receive_cmnd_data(req);
|
||||
else {
|
||||
TRACE_DBG("Delaying req %p post processing (scst_state %d)",
|
||||
req, req->scst_state);
|
||||
@@ -2667,6 +2728,14 @@ reject:
|
||||
return;
|
||||
}
|
||||
|
||||
static void iscsi_tcp_set_req_data(struct iscsi_cmnd *req,
|
||||
struct iscsi_cmnd *rsp)
|
||||
{
|
||||
rsp->sg = req->sg;
|
||||
rsp->sg_cnt = req->sg_cnt;
|
||||
rsp->bufflen = req->bufflen;
|
||||
}
|
||||
|
||||
static void nop_out_exec(struct iscsi_cmnd *req)
|
||||
{
|
||||
struct iscsi_cmnd *rsp;
|
||||
@@ -2690,11 +2759,8 @@ static void nop_out_exec(struct iscsi_cmnd *req)
|
||||
else
|
||||
sBUG_ON(req->sg != NULL);
|
||||
|
||||
if (req->sg) {
|
||||
rsp->sg = req->sg;
|
||||
rsp->sg_cnt = req->sg_cnt;
|
||||
rsp->bufflen = req->bufflen;
|
||||
}
|
||||
if (req->bufflen)
|
||||
req->conn->transport->iscsit_set_req_data(req, rsp);
|
||||
|
||||
/* We already checked it in check_segment_length() */
|
||||
sBUG_ON(get_pgcnt(req->pdu.datasize, 0) > ISCSI_CONN_IOV_MAX);
|
||||
@@ -3147,6 +3213,7 @@ out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(cmnd_rx_start);
|
||||
|
||||
void cmnd_rx_end(struct iscsi_cmnd *cmnd)
|
||||
{
|
||||
@@ -3178,6 +3245,48 @@ out:
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(cmnd_rx_end);
|
||||
|
||||
static ssize_t iscsi_tcp_get_initiator_ip(struct iscsi_conn *conn,
|
||||
char *buf, int size)
|
||||
{
|
||||
int pos;
|
||||
struct sock *sk;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
sk = conn->sock->sk;
|
||||
switch (sk->sk_family) {
|
||||
case AF_INET:
|
||||
pos = scnprintf(buf, size,
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)
|
||||
"%u.%u.%u.%u", NIPQUAD(inet_sk(sk)->daddr));
|
||||
#else
|
||||
"%pI4", &inet_sk(sk)->inet_daddr);
|
||||
#endif
|
||||
break;
|
||||
case AF_INET6:
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
|
||||
pos = scnprintf(buf, size,
|
||||
"[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]",
|
||||
NIP6(inet6_sk(sk)->daddr));
|
||||
#else
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
|
||||
pos = scnprintf(buf, size, "[%p6]", &inet6_sk(sk)->daddr);
|
||||
#else
|
||||
pos = scnprintf(buf, size, "[%p6]", &sk->sk_v6_daddr);
|
||||
#endif
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
pos = scnprintf(buf, size, "Unknown family %d",
|
||||
sk->sk_family);
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE_EXIT_RES(pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
#if !defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
|
||||
static int iscsi_alloc_data_buf(struct scst_cmd *cmd)
|
||||
@@ -3195,11 +3304,8 @@ static int iscsi_alloc_data_buf(struct scst_cmd *cmd)
|
||||
}
|
||||
#endif
|
||||
|
||||
static void iscsi_preprocessing_done(struct scst_cmd *scst_cmd)
|
||||
static void iscsi_tcp_preprocessing_done(struct iscsi_cmnd *req)
|
||||
{
|
||||
struct iscsi_cmnd *req = (struct iscsi_cmnd *)
|
||||
scst_cmd_get_tgt_priv(scst_cmd);
|
||||
|
||||
TRACE_DBG("req %p", req);
|
||||
|
||||
if (req->conn->rx_task == current)
|
||||
@@ -3226,8 +3332,14 @@ static void iscsi_preprocessing_done(struct scst_cmd *scst_cmd)
|
||||
}
|
||||
cmnd_put(req);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
static void iscsi_preprocessing_done(struct scst_cmd *scst_cmd)
|
||||
{
|
||||
struct iscsi_cmnd *req = (struct iscsi_cmnd *)
|
||||
scst_cmd_get_tgt_priv(scst_cmd);
|
||||
|
||||
req->conn->transport->iscsit_preprocessing_done(req);
|
||||
}
|
||||
|
||||
/* No locks */
|
||||
@@ -3288,6 +3400,52 @@ static void iscsi_try_local_processing(struct iscsi_cmnd *req)
|
||||
return;
|
||||
}
|
||||
|
||||
static int iscsi_tcp_send_locally(struct iscsi_cmnd *req,
|
||||
unsigned int cmd_count)
|
||||
{
|
||||
struct iscsi_conn *conn = req->conn;
|
||||
struct iscsi_cmnd *wr_rsp, *our_rsp;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* There's no need for protection, since we are not going to
|
||||
* dereference them.
|
||||
*/
|
||||
wr_rsp = list_first_entry(&conn->write_list, struct iscsi_cmnd,
|
||||
write_list_entry);
|
||||
our_rsp = list_first_entry(&req->rsp_cmd_list, struct iscsi_cmnd,
|
||||
rsp_cmd_list_entry);
|
||||
if (wr_rsp == our_rsp) {
|
||||
/*
|
||||
* This is our rsp, so let's try to process it locally to
|
||||
* decrease latency. We need to call pre_release before
|
||||
* processing to handle some error recovery cases.
|
||||
*/
|
||||
if (cmd_count <= 2) {
|
||||
req_cmnd_pre_release(req);
|
||||
iscsi_try_local_processing(req);
|
||||
cmnd_put(req);
|
||||
} else {
|
||||
/*
|
||||
* There's too much backend activity, so it could be
|
||||
* better to push it to the write thread.
|
||||
*/
|
||||
ret = 1;
|
||||
}
|
||||
} else
|
||||
ret = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void iscsi_tcp_conn_close(struct iscsi_conn *conn, int flags)
|
||||
{
|
||||
if (!flags)
|
||||
conn->sock->sk->sk_prot->disconnect(conn->sock->sk, 0);
|
||||
else
|
||||
conn->sock->ops->shutdown(conn->sock, flags);
|
||||
}
|
||||
|
||||
static int iscsi_xmit_response(struct scst_cmd *scst_cmd)
|
||||
{
|
||||
int is_send_status = scst_cmd_get_is_send_status(scst_cmd);
|
||||
@@ -3297,7 +3455,6 @@ static int iscsi_xmit_response(struct scst_cmd *scst_cmd)
|
||||
int status = scst_cmd_get_status(scst_cmd);
|
||||
u8 *sense = scst_cmd_get_sense_buffer(scst_cmd);
|
||||
int sense_len = scst_cmd_get_sense_buffer_len(scst_cmd);
|
||||
struct iscsi_cmnd *wr_rsp, *our_rsp;
|
||||
|
||||
EXTRACHECKS_BUG_ON(scst_cmd_atomic(scst_cmd));
|
||||
|
||||
@@ -3372,19 +3529,9 @@ static int iscsi_xmit_response(struct scst_cmd *scst_cmd)
|
||||
* so status is valid here, but in future that could change.
|
||||
* ToDo
|
||||
*/
|
||||
if ((status != SAM_STAT_CHECK_CONDITION) &&
|
||||
((cmnd_hdr(req)->flags & (ISCSI_CMD_WRITE|ISCSI_CMD_READ)) !=
|
||||
(ISCSI_CMD_WRITE|ISCSI_CMD_READ))) {
|
||||
send_data_rsp(req, status, is_send_status);
|
||||
} else {
|
||||
struct iscsi_cmnd *rsp;
|
||||
send_data_rsp(req, 0, 0);
|
||||
if (is_send_status) {
|
||||
rsp = create_status_rsp(req, status, sense,
|
||||
sense_len);
|
||||
iscsi_cmnd_init_write(rsp, 0);
|
||||
}
|
||||
}
|
||||
req->conn->transport->iscsit_send_data_rsp(req, sense,
|
||||
sense_len, status,
|
||||
is_send_status);
|
||||
} else if (is_send_status) {
|
||||
struct iscsi_cmnd *rsp;
|
||||
rsp = create_status_rsp(req, status, sense, sense_len);
|
||||
@@ -3395,32 +3542,7 @@ static int iscsi_xmit_response(struct scst_cmd *scst_cmd)
|
||||
sBUG();
|
||||
#endif
|
||||
|
||||
/*
|
||||
* There's no need for protection, since we are not going to
|
||||
* dereference them.
|
||||
*/
|
||||
wr_rsp = list_first_entry(&conn->write_list, struct iscsi_cmnd,
|
||||
write_list_entry);
|
||||
our_rsp = list_first_entry(&req->rsp_cmd_list, struct iscsi_cmnd,
|
||||
rsp_cmd_list_entry);
|
||||
if (wr_rsp == our_rsp) {
|
||||
/*
|
||||
* This is our rsp, so let's try to process it locally to
|
||||
* decrease latency. We need to call pre_release before
|
||||
* processing to handle some error recovery cases.
|
||||
*/
|
||||
if (scst_get_active_cmd_count(scst_cmd) <= 2) {
|
||||
req_cmnd_pre_release(req);
|
||||
iscsi_try_local_processing(req);
|
||||
cmnd_put(req);
|
||||
} else {
|
||||
/*
|
||||
* There's too much backend activity, so it could be
|
||||
* better to push it to the write thread.
|
||||
*/
|
||||
goto out_push_to_wr_thread;
|
||||
}
|
||||
} else
|
||||
if (conn->transport->iscsit_send_locally(req, scst_get_active_cmd_count(scst_cmd)))
|
||||
goto out_push_to_wr_thread;
|
||||
|
||||
out:
|
||||
@@ -3429,7 +3551,7 @@ out:
|
||||
out_push_to_wr_thread:
|
||||
TRACE_DBG("Waking up write thread (conn %p)", conn);
|
||||
req_cmnd_release(req);
|
||||
iscsi_make_conn_wr_active(conn);
|
||||
conn->transport->iscsit_make_conn_wr_active(conn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -3632,7 +3754,6 @@ static int iscsi_scsi_aen(struct scst_aen *aen)
|
||||
bool found;
|
||||
struct iscsi_cmnd *fake_req, *rsp;
|
||||
struct iscsi_async_msg_hdr *rsp_hdr;
|
||||
struct scatterlist *sg;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
@@ -3655,7 +3776,7 @@ static int iscsi_scsi_aen(struct scst_aen *aen)
|
||||
}
|
||||
|
||||
/* Create a fake request */
|
||||
fake_req = cmnd_alloc(conn, NULL);
|
||||
fake_req = conn->transport->iscsit_alloc_cmd(conn, NULL);
|
||||
if (fake_req == NULL) {
|
||||
PRINT_ERROR("%s", "Unable to alloc fake AEN request");
|
||||
goto out_err_unlock;
|
||||
@@ -3680,15 +3801,10 @@ static int iscsi_scsi_aen(struct scst_aen *aen)
|
||||
rsp_hdr->ffffffff = cpu_to_be32(0xffffffff);
|
||||
rsp_hdr->async_event = ISCSI_ASYNC_SCSI;
|
||||
|
||||
sg = rsp->sg = rsp->rsp_sg;
|
||||
rsp->sg_cnt = 2;
|
||||
rsp->own_sg = 1;
|
||||
|
||||
sg_init_table(sg, 2);
|
||||
sg_set_buf(&sg[0], &rsp->sense_hdr, sizeof(rsp->sense_hdr));
|
||||
sg_set_buf(&sg[1], sense, sense_len);
|
||||
|
||||
rsp->sense_hdr.length = cpu_to_be16(sense_len);
|
||||
|
||||
rsp->conn->transport->iscsit_set_sense_data(rsp, sense, sense_len);
|
||||
|
||||
rsp->pdu.datasize = sizeof(rsp->sense_hdr) + sense_len;
|
||||
rsp->bufflen = rsp->pdu.datasize;
|
||||
|
||||
@@ -3810,7 +3926,7 @@ void iscsi_send_nop_in(struct iscsi_conn *conn)
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
req = cmnd_alloc(conn, NULL);
|
||||
req = conn->transport->iscsit_alloc_cmd(conn, NULL);
|
||||
if (req == NULL) {
|
||||
PRINT_ERROR("%s", "Unable to alloc fake Nop-In request");
|
||||
goto out_err;
|
||||
@@ -3952,6 +4068,30 @@ struct scst_tgt_template iscsi_template = {
|
||||
.get_scsi_transport_version = iscsi_get_scsi_transport_version,
|
||||
};
|
||||
|
||||
static struct iscsit_transport iscsi_tcp_transport = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "iSCSI-TCP",
|
||||
.transport_type = ISCSI_TCP,
|
||||
#if !defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
|
||||
.need_alloc_write_buf = 1,
|
||||
#endif
|
||||
.iscsit_conn_alloc = iscsi_conn_alloc,
|
||||
.iscsit_conn_activate = conn_activate,
|
||||
.iscsit_conn_free = iscsi_tcp_conn_free,
|
||||
.iscsit_alloc_cmd = cmnd_alloc,
|
||||
.iscsit_free_cmd = cmnd_free,
|
||||
.iscsit_preprocessing_done = iscsi_tcp_preprocessing_done,
|
||||
.iscsit_send_data_rsp = iscsi_tcp_send_data_rsp,
|
||||
.iscsit_make_conn_wr_active = iscsi_make_conn_wr_active,
|
||||
.iscsit_mark_conn_closed = iscsi_tcp_mark_conn_closed,
|
||||
.iscsit_conn_close = iscsi_tcp_conn_close,
|
||||
.iscsit_get_initiator_ip = iscsi_tcp_get_initiator_ip,
|
||||
.iscsit_send_locally = iscsi_tcp_send_locally,
|
||||
.iscsit_set_sense_data = iscsi_tcp_set_sense_data,
|
||||
.iscsit_set_req_data = iscsi_tcp_set_req_data,
|
||||
.iscsit_receive_cmnd_data = cmnd_rx_continue,
|
||||
};
|
||||
|
||||
static void __iscsi_threads_pool_put(struct iscsi_thread_pool *p)
|
||||
{
|
||||
struct iscsi_thread *t, *tt;
|
||||
@@ -4111,6 +4251,10 @@ static int __init iscsi_init(void)
|
||||
|
||||
PRINT_INFO("iSCSI SCST Target - version %s", ISCSI_VERSION_STRING);
|
||||
|
||||
err = iscsit_register_transport(&iscsi_tcp_transport);
|
||||
if (err)
|
||||
goto out;
|
||||
|
||||
dummy_page = alloc_pages(GFP_KERNEL, 0);
|
||||
if (dummy_page == NULL) {
|
||||
PRINT_ERROR("%s", "Dummy page allocation failed");
|
||||
@@ -4261,6 +4405,8 @@ static void __exit iscsi_exit(void)
|
||||
|
||||
scst_unregister_target_template(&iscsi_template);
|
||||
|
||||
iscsit_unregister_transport(&iscsi_tcp_transport);
|
||||
|
||||
#if defined(CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION)
|
||||
net_set_get_put_page_callbacks(NULL, NULL);
|
||||
#endif
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#endif
|
||||
#include "iscsi_hdr.h"
|
||||
#include "iscsi_dbg.h"
|
||||
#include "iscsit_transport.h"
|
||||
|
||||
#define iscsi_sense_crc_error ABORTED_COMMAND, 0x47, 0x05
|
||||
#define iscsi_sense_unexpected_unsolicited_data ABORTED_COMMAND, 0x0C, 0x0C
|
||||
@@ -57,6 +58,9 @@ struct iscsi_sess_params {
|
||||
int ifmarker;
|
||||
int ofmarkint;
|
||||
int ifmarkint;
|
||||
int rdma_extensions;
|
||||
int target_recv_data_length;
|
||||
int initiator_recv_data_length;
|
||||
};
|
||||
|
||||
struct iscsi_tgt_params {
|
||||
@@ -190,6 +194,8 @@ struct iscsi_session {
|
||||
#define ISCSI_CONN_WR_STATE_PROCESSING 3
|
||||
|
||||
struct iscsi_conn {
|
||||
struct iscsit_transport *transport;
|
||||
|
||||
struct iscsi_session *session; /* owning session */
|
||||
|
||||
/* Both protected by session->sn_lock */
|
||||
@@ -304,6 +310,7 @@ struct iscsi_conn {
|
||||
#else
|
||||
struct work_struct nop_in_delayed_work;
|
||||
#endif
|
||||
struct work_struct close_work;
|
||||
unsigned int nop_in_interval; /* in jiffies */
|
||||
unsigned int nop_in_timeout; /* in jiffies */
|
||||
struct list_head nop_req_list;
|
||||
@@ -551,7 +558,7 @@ extern struct iscsi_conn *conn_lookup(struct iscsi_session *, u16);
|
||||
extern void conn_reinst_finished(struct iscsi_conn *);
|
||||
extern int __add_conn(struct iscsi_session *, struct iscsi_kern_conn_info *);
|
||||
extern int __del_conn(struct iscsi_session *, struct iscsi_kern_conn_info *);
|
||||
extern int conn_free(struct iscsi_conn *);
|
||||
extern void conn_free(struct iscsi_conn *);
|
||||
extern void iscsi_make_conn_rd_active(struct iscsi_conn *conn);
|
||||
#define ISCSI_CONN_ACTIVE_CLOSE 1
|
||||
#define ISCSI_CONN_DELETING 2
|
||||
@@ -603,6 +610,7 @@ extern void target_del_all(void);
|
||||
extern int iscsi_procfs_init(void);
|
||||
extern void iscsi_procfs_exit(void);
|
||||
#else
|
||||
extern int conn_sysfs_add(struct iscsi_conn *conn);
|
||||
extern const struct attribute *iscsi_attrs[];
|
||||
extern int iscsi_add_attr(struct iscsi_target *target,
|
||||
const struct iscsi_kern_attr *user_info);
|
||||
@@ -825,4 +833,24 @@ static inline void iscsi_extracheck_is_rd_thread(struct iscsi_conn *conn) {}
|
||||
static inline void iscsi_extracheck_is_wr_thread(struct iscsi_conn *conn) {}
|
||||
#endif
|
||||
|
||||
extern int iscsi_conn_alloc(struct iscsi_session *session,
|
||||
struct iscsi_kern_conn_info *info, struct iscsi_conn **new_conn,
|
||||
struct iscsit_transport *t);
|
||||
|
||||
extern int conn_activate(struct iscsi_conn *conn);
|
||||
extern void iscsi_tcp_mark_conn_closed(struct iscsi_conn *conn, int flags);
|
||||
extern void iscsi_tcp_conn_free(struct iscsi_conn *conn);
|
||||
extern void iscsi_cmnd_init(struct iscsi_conn *conn, struct iscsi_cmnd *cmnd,
|
||||
struct iscsi_cmnd *parent);
|
||||
extern struct iscsi_cmnd *iscsi_get_send_cmnd(struct iscsi_conn *conn);
|
||||
extern void start_close_conn(struct iscsi_conn *conn);
|
||||
extern __be32 cmnd_set_sn(struct iscsi_cmnd *cmnd, int set_stat_sn);
|
||||
extern void iscsi_set_resid(struct iscsi_cmnd *rsp);
|
||||
extern int iscsi_init_conn(struct iscsi_session *session,
|
||||
struct iscsi_kern_conn_info *info, struct iscsi_conn *conn);
|
||||
extern void req_cmnd_pre_release(struct iscsi_cmnd *req);
|
||||
extern void req_cmnd_release(struct iscsi_cmnd *req);
|
||||
extern struct iscsi_cmnd *create_status_rsp(struct iscsi_cmnd *req,
|
||||
int status, const u8 *sense_buf, int sense_len);
|
||||
extern int iscsi_cmnd_set_write_buf(struct iscsi_cmnd *req);
|
||||
#endif /* __ISCSI_H__ */
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
#ifndef ISCSI_DBG_H
|
||||
#define ISCSI_DBG_H
|
||||
|
||||
#ifdef LOG_PREFIX
|
||||
#undef LOG_PREFIX
|
||||
#endif
|
||||
|
||||
#define LOG_PREFIX "iscsi-scst"
|
||||
|
||||
#ifdef INSIDE_KERNEL_TREE
|
||||
@@ -53,8 +57,10 @@ extern unsigned long iscsi_get_flow_ctrl_or_mgmt_dbg_log_flag(
|
||||
|
||||
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
|
||||
extern unsigned long iscsi_trace_flag;
|
||||
#ifndef trace_flag
|
||||
#define trace_flag iscsi_trace_flag
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define TRACE_CONN_CLOSE(args...) TRACE_DBG_FLAG(TRACE_DEBUG|TRACE_CONN_OC, args)
|
||||
#define TRACE_CONN_CLOSE_DBG(args...) TRACE(TRACE_CONN_OC_DBG, args)
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include "iscsit_transport.h"
|
||||
#include "iscsi.h"
|
||||
|
||||
static LIST_HEAD(transport_list);
|
||||
static DEFINE_MUTEX(transport_mutex);
|
||||
|
||||
static struct iscsit_transport *__iscsit_get_transport(enum iscsit_transport_type type)
|
||||
{
|
||||
struct iscsit_transport *t;
|
||||
|
||||
list_for_each_entry(t, &transport_list, transport_list_entry) {
|
||||
if (t->transport_type == type)
|
||||
return t;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
struct iscsit_transport *iscsit_get_transport(enum iscsit_transport_type type)
|
||||
{
|
||||
struct iscsit_transport *t;
|
||||
|
||||
mutex_lock(&transport_mutex);
|
||||
t = __iscsit_get_transport(type);
|
||||
mutex_unlock(&transport_mutex);
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
int iscsit_register_transport(struct iscsit_transport *t)
|
||||
{
|
||||
struct iscsit_transport *tmp;
|
||||
int ret = 0;
|
||||
|
||||
INIT_LIST_HEAD(&t->transport_list_entry);
|
||||
|
||||
mutex_lock(&transport_mutex);
|
||||
tmp = __iscsit_get_transport(t->transport_type);
|
||||
if (tmp) {
|
||||
PRINT_ERROR("Unable to register transport type %d - Already registered\n",
|
||||
t->transport_type);
|
||||
ret = -EEXIST;
|
||||
} else {
|
||||
list_add_tail(&t->transport_list_entry, &transport_list);
|
||||
PRINT_INFO("Registered iSCSI transport: %s\n", t->name);
|
||||
}
|
||||
mutex_unlock(&transport_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL(iscsit_register_transport);
|
||||
|
||||
void iscsit_unregister_transport(struct iscsit_transport *t)
|
||||
{
|
||||
mutex_lock(&transport_mutex);
|
||||
list_del(&t->transport_list_entry);
|
||||
mutex_unlock(&transport_mutex);
|
||||
|
||||
PRINT_INFO("Unregistered iSCSI transport: %s\n", t->name);
|
||||
}
|
||||
EXPORT_SYMBOL(iscsit_unregister_transport);
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
config SCST_ISER
|
||||
tristate "ISCSI Target"
|
||||
depends on SCST && SCST_ISCSI
|
||||
default SCST
|
||||
help
|
||||
ISER target driver for SCST framework. The iSCSI iSER extension
|
||||
has been defined in RFC 5046.
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
#
|
||||
# Makefile for the kernel part of iSER-SCST.
|
||||
#
|
||||
# Copyright (C) 2007 - 2014 Vladislav Bolkhovitin
|
||||
# Copyright (C) 2007 - 2014 Fusion-io, Inc.
|
||||
#
|
||||
# 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, version 2
|
||||
# of the License.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# Note! Dependencies are done automatically by 'make dep', which also
|
||||
# removes any old dependencies. DON'T put your own dependencies here
|
||||
# unless it's something special (not a .c file).
|
||||
#
|
||||
# Note 2! The CFLAGS definitions are now in the main makefile.
|
||||
|
||||
cc-option = $(shell if $(CC) $(CFLAGS) $(1) -S -o /dev/null -xc /dev/null \
|
||||
> /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
|
||||
enable-Wextra = $(shell uname_r="$$(uname -r)"; if [ "$${uname_r%.el5}" = "$${uname_r}" ]; then echo "$(1)"; fi)
|
||||
|
||||
EXTRA_CFLAGS += -I$(src)/../../include -I$(src)/../ -I$(SCST_INC_DIR)
|
||||
EXTRA_CFLAGS += $(call enable-Wextra,-Wextra \
|
||||
$(call cc-option,-Wno-old-style-declaration) \
|
||||
-Wno-unused-parameter -Wno-missing-field-initializers)
|
||||
|
||||
EXTRA_CFLAGS += -DCONFIG_SCST_EXTRACHECKS
|
||||
#EXTRA_CFLAGS += -DCONFIG_SCST_TRACING
|
||||
EXTRA_CFLAGS += -DCONFIG_SCST_DEBUG -g -fno-inline -fno-inline-functions
|
||||
|
||||
obj-m += isert-scst.o
|
||||
isert-scst-objs := isert.o isert_login.o \
|
||||
iser_datamover.o iser_rdma.o iser_buf.o iser_pdu.o iser_global.o
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
isert-scst-y := isert.o isert_login.o \
|
||||
iser_datamover.o iser_rdma.o iser_buf.o iser_pdu.o iser_global.o
|
||||
|
||||
obj-$(CONFIG_SCST_ISER) += isert-scst.o
|
||||
@@ -0,0 +1,9 @@
|
||||
* Add suppport for immediate data in iSER
|
||||
* Add suppport for data-out in iSER
|
||||
* Look into allocating wr and sg entries dynamically from kmem_cache instead of embedding them into iser_cmnd
|
||||
* Look into seperating between RX pdu and TX pdu
|
||||
* Do not signal every "response sent" notification
|
||||
* Make the code NUMA aware
|
||||
* Add support for AHS
|
||||
* Add support for bidi commands
|
||||
|
||||
@@ -0,0 +1,313 @@
|
||||
#ifndef __ISER_H__
|
||||
#define __ISER_H__
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <rdma/ib_verbs.h>
|
||||
#include <rdma/rdma_cm.h>
|
||||
|
||||
#include "iser_hdr.h"
|
||||
|
||||
struct isert_portal {
|
||||
struct rdma_cm_id *cm_id;
|
||||
struct sockaddr_storage addr;
|
||||
struct list_head list_node; /* in portals list */
|
||||
/* protected by dev_list_mutex */
|
||||
struct list_head conn_list; /* head of conns list */
|
||||
};
|
||||
|
||||
struct isert_buf {
|
||||
int sg_cnt ____cacheline_aligned;
|
||||
struct scatterlist *sg;
|
||||
u8 *addr;
|
||||
size_t size;
|
||||
enum dma_data_direction dma_dir;
|
||||
unsigned int is_alloced:1;
|
||||
unsigned int is_pgalloced:1;
|
||||
unsigned int is_malloced:1;
|
||||
};
|
||||
|
||||
enum isert_wr_op {
|
||||
ISER_WR_RECV,
|
||||
ISER_WR_SEND,
|
||||
ISER_WR_RDMA_WRITE,
|
||||
ISER_WR_RDMA_READ,
|
||||
};
|
||||
|
||||
struct isert_device;
|
||||
struct isert_connection;
|
||||
|
||||
struct isert_wr {
|
||||
enum isert_wr_op wr_op;
|
||||
struct isert_buf *buf;
|
||||
|
||||
struct isert_connection *conn;
|
||||
struct isert_cmnd *pdu;
|
||||
|
||||
struct isert_device *isert_dev;
|
||||
|
||||
struct ib_sge *sge_list;
|
||||
union {
|
||||
struct ib_recv_wr recv_wr;
|
||||
struct ib_send_wr send_wr;
|
||||
};
|
||||
} ____cacheline_aligned;
|
||||
|
||||
#define ISER_MAX_SGE 128
|
||||
#define ISER_MAX_RDMAS 5
|
||||
|
||||
#define ISER_SQ_SIZE 128
|
||||
#define ISER_MAX_WCE 2048
|
||||
|
||||
struct isert_cmnd {
|
||||
struct iscsi_cmnd iscsi ____cacheline_aligned;
|
||||
|
||||
struct isert_buf buf;
|
||||
struct isert_buf rdma_buf;
|
||||
struct isert_wr wr[ISER_MAX_RDMAS];
|
||||
struct ib_sge sg_pool[ISER_MAX_SGE];
|
||||
|
||||
struct isert_hdr *isert_hdr ____cacheline_aligned;
|
||||
struct iscsi_hdr *bhs;
|
||||
void *ahs;
|
||||
void *data;
|
||||
|
||||
u8 isert_opcode;
|
||||
u8 iscsi_opcode;
|
||||
u8 is_rstag_valid;
|
||||
u8 is_wstag_valid;
|
||||
|
||||
u32 rem_write_stag; /* write rkey */
|
||||
u64 rem_write_va;
|
||||
u32 rem_read_stag; /* read rkey */
|
||||
u64 rem_read_va;
|
||||
|
||||
int is_fake_rx;
|
||||
struct list_head pool_node; /* pool list */
|
||||
};
|
||||
|
||||
enum isert_conn_state {
|
||||
ISER_CONN_INIT = 0,
|
||||
ISER_CONN_HANDSHAKE,
|
||||
ISER_CONN_ACTIVE,
|
||||
ISER_CONN_CLOSING,
|
||||
};
|
||||
|
||||
struct isert_cq {
|
||||
struct ib_cq *cq ____cacheline_aligned;
|
||||
struct ib_wc wc[ISER_SQ_SIZE];
|
||||
struct isert_device *dev;
|
||||
struct workqueue_struct *cq_workqueue;
|
||||
struct work_struct cq_comp_work;
|
||||
int idx;
|
||||
};
|
||||
|
||||
#define ISERT_CONNECTION_ABORTED 0
|
||||
|
||||
struct isert_connection {
|
||||
struct iscsi_conn iscsi ____cacheline_aligned;
|
||||
|
||||
int repost_threshold ____cacheline_aligned;
|
||||
/* access to the following 3 fields is guarded by post_recv_lock */
|
||||
int to_post_recv;
|
||||
struct isert_wr *post_recv_first;
|
||||
struct isert_wr *post_recv_curr;
|
||||
|
||||
spinlock_t post_recv_lock;
|
||||
|
||||
|
||||
spinlock_t tx_lock ____cacheline_aligned;
|
||||
|
||||
/* Following two protected by tx_lock */
|
||||
struct list_head tx_free_list;
|
||||
struct list_head tx_busy_list;
|
||||
|
||||
struct rdma_cm_id *cm_id;
|
||||
struct isert_device *isert_dev;
|
||||
struct ib_qp *qp;
|
||||
struct isert_cq *cq_desc;
|
||||
|
||||
enum isert_conn_state state;
|
||||
|
||||
u32 responder_resources;
|
||||
u32 initiator_depth;
|
||||
u32 max_sge;
|
||||
|
||||
/*
|
||||
* Unprotected. Accessed only before login response is sent and when
|
||||
* freeing connection
|
||||
*/
|
||||
struct list_head rx_buf_list;
|
||||
|
||||
struct isert_cmnd *login_req_pdu;
|
||||
struct isert_cmnd *login_rsp_pdu;
|
||||
struct isert_wr *saved_wr;
|
||||
|
||||
int queue_depth;
|
||||
int immediate_data;
|
||||
unsigned int target_recv_data_length;
|
||||
int initiator_recv_data_length;
|
||||
int initial_r2t;
|
||||
unsigned int first_burst_length;
|
||||
struct sockaddr_storage peer_addr;
|
||||
size_t peer_addrsz;
|
||||
struct sockaddr_storage self_addr;
|
||||
|
||||
struct list_head dev_node;
|
||||
struct list_head portal_node;
|
||||
|
||||
unsigned long flags;
|
||||
struct work_struct close_work;
|
||||
struct kref kref;
|
||||
|
||||
void *priv_data; /* for connection tracking */
|
||||
};
|
||||
|
||||
struct isert_device {
|
||||
struct ib_device *ib_dev;
|
||||
struct ib_pd *pd;
|
||||
struct ib_mr *mr;
|
||||
|
||||
struct list_head devs_node;
|
||||
/* conn_list and refcnt protected by dev_list_mutex */
|
||||
struct list_head conn_list;
|
||||
int refcnt;
|
||||
struct ib_device_attr device_attr;
|
||||
|
||||
int num_cqs;
|
||||
int *cq_qps;
|
||||
struct isert_cq *cq_desc;
|
||||
};
|
||||
|
||||
struct isert_global {
|
||||
spinlock_t portal_lock;
|
||||
/* protected by portal_lock */
|
||||
struct list_head portal_list;
|
||||
/* protected by dev_list_mutex */
|
||||
struct list_head dev_list;
|
||||
struct workqueue_struct *conn_wq;
|
||||
};
|
||||
|
||||
#define _ptr_to_u64(p) (u64)(unsigned long)(p)
|
||||
#define _u64_to_ptr(v) (void *)(unsigned long)(v)
|
||||
|
||||
/* global iser scope */
|
||||
int isert_global_init(void);
|
||||
int isert_datamover_cleanup(void);
|
||||
|
||||
void isert_portal_list_add(struct isert_portal *portal);
|
||||
void isert_portal_list_remove(struct isert_portal *portal);
|
||||
|
||||
void isert_dev_list_add(struct isert_device *isert_dev);
|
||||
void isert_dev_list_remove(struct isert_device *isert_dev);
|
||||
struct isert_device *isert_device_find(struct ib_device *ib_dev);
|
||||
|
||||
void isert_conn_queue_work(struct work_struct *w);
|
||||
|
||||
extern struct kmem_cache *isert_cmnd_cache;
|
||||
extern struct kmem_cache *isert_conn_cache;
|
||||
|
||||
/* iser portal */
|
||||
struct isert_portal *isert_portal_create(void);
|
||||
int isert_portal_listen(struct isert_portal *portal,
|
||||
struct sockaddr *sa,
|
||||
size_t addr_len);
|
||||
void isert_portal_release(struct isert_portal *portal);
|
||||
void isert_portal_list_release_all(void);
|
||||
struct isert_portal *isert_portal_start(struct sockaddr *sa, size_t addr_len);
|
||||
|
||||
/* iser connection */
|
||||
int isert_post_recv(struct isert_connection *isert_conn,
|
||||
struct isert_wr *first_wr, int num_wr);
|
||||
int isert_post_send(struct isert_connection *isert_conn,
|
||||
struct isert_wr *first_wr, int num_wr);
|
||||
|
||||
int isert_alloc_conn_resources(struct isert_connection *isert_conn);
|
||||
void isert_free_conn_resources(struct isert_connection *isert_conn);
|
||||
void isert_conn_free(struct isert_connection *isert_conn);
|
||||
void isert_conn_disconnect(struct isert_connection *isert_conn);
|
||||
|
||||
static inline struct isert_connection *isert_conn_alloc(void)
|
||||
{
|
||||
return kmem_cache_zalloc(isert_conn_cache, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static inline void isert_conn_kfree(struct isert_connection *isert_conn)
|
||||
{
|
||||
kmem_cache_free(isert_conn_cache, isert_conn);
|
||||
}
|
||||
|
||||
/* iser buf */
|
||||
int isert_buf_alloc_data_buf(struct ib_device *ib_dev,
|
||||
struct isert_buf *isert_buf, size_t size,
|
||||
enum dma_data_direction dma_dir);
|
||||
void isert_wr_set_fields(struct isert_wr *wr,
|
||||
struct isert_connection *isert_conn,
|
||||
struct isert_cmnd *pdu);
|
||||
int isert_wr_init(struct isert_wr *wr,
|
||||
enum isert_wr_op wr_op,
|
||||
struct isert_buf *isert_buf,
|
||||
struct isert_connection *isert_conn,
|
||||
struct isert_cmnd *pdu,
|
||||
struct ib_sge *sge,
|
||||
int sg_offset,
|
||||
int sg_cnt,
|
||||
int buff_offset);
|
||||
void isert_wr_release(struct isert_wr *wr);
|
||||
|
||||
void isert_buf_release(struct isert_buf *isert_buf);
|
||||
|
||||
static inline void isert_buf_init_sg(struct isert_buf *isert_buf,
|
||||
struct scatterlist *sg,
|
||||
int sg_cnt, size_t size)
|
||||
{
|
||||
isert_buf->sg_cnt = sg_cnt;
|
||||
isert_buf->sg = sg;
|
||||
isert_buf->size = size;
|
||||
}
|
||||
|
||||
/* iser pdu */
|
||||
static inline struct isert_cmnd *isert_pdu_alloc(void)
|
||||
{
|
||||
return kmem_cache_zalloc(isert_cmnd_cache, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static inline void isert_pdu_kfree(struct isert_cmnd *cmnd)
|
||||
{
|
||||
kmem_cache_free(isert_cmnd_cache, cmnd);
|
||||
}
|
||||
|
||||
struct isert_cmnd *isert_rx_pdu_alloc(struct isert_connection *isert_conn,
|
||||
size_t size);
|
||||
struct isert_cmnd *isert_tx_pdu_alloc(struct isert_connection *isert_conn,
|
||||
size_t size);
|
||||
void isert_tx_pdu_init(struct isert_cmnd *isert_pdu,
|
||||
struct isert_connection *isert_conn);
|
||||
int isert_pdu_send(struct isert_connection *isert_conn,
|
||||
struct isert_cmnd *tx_pdu);
|
||||
|
||||
int isert_prepare_rdma(struct isert_cmnd *isert_pdu,
|
||||
struct isert_connection *isert_conn,
|
||||
enum isert_wr_op op);
|
||||
int isert_pdu_post_rdma_write(struct isert_connection *isert_conn,
|
||||
struct isert_cmnd *isert_cmd,
|
||||
struct isert_cmnd *isert_rsp,
|
||||
int wr_cnt);
|
||||
int isert_pdu_post_rdma_read(struct isert_connection *isert_conn,
|
||||
struct isert_cmnd *isert_cmd,
|
||||
int wr_cnt);
|
||||
|
||||
void isert_pdu_free(struct isert_cmnd *pdu);
|
||||
int isert_rx_pdu_done(struct isert_cmnd *pdu);
|
||||
|
||||
void isert_tx_pdu_convert_from_iscsi(struct isert_cmnd *isert_cmnd,
|
||||
struct iscsi_cmnd *iscsi_cmnd);
|
||||
|
||||
void isert_tx_pdu_init_iscsi(struct isert_cmnd *isert_pdu);
|
||||
|
||||
/* global */
|
||||
void isert_global_cleanup(void);
|
||||
int isert_get_addr_size(struct sockaddr *sa, size_t *size);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* isert_buf.c
|
||||
*
|
||||
* This file is part of iser target kernel module.
|
||||
*
|
||||
* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved.
|
||||
* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com)
|
||||
*
|
||||
* This software is available to you under a choice of one of two
|
||||
* licenses. You may choose to be licensed under the terms of the GNU
|
||||
* General Public License (GPL) Version 2, available from the file
|
||||
* COPYING in the main directory of this source tree, or the
|
||||
* OpenIB.org BSD license below:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "iser.h"
|
||||
|
||||
static int isert_buf_alloc_pg(struct ib_device *ib_dev,
|
||||
struct isert_buf *isert_buf, size_t size,
|
||||
enum dma_data_direction dma_dir)
|
||||
{
|
||||
int res = 0;
|
||||
int i;
|
||||
struct page *page;
|
||||
|
||||
isert_buf->sg_cnt = DIV_ROUND_UP(size, PAGE_SIZE);
|
||||
isert_buf->sg = kmalloc(sizeof(*isert_buf->sg) * isert_buf->sg_cnt,
|
||||
GFP_KERNEL);
|
||||
if (unlikely(!isert_buf->sg)) {
|
||||
pr_err("Failed to allocate buffer SG\n");
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sg_init_table(isert_buf->sg, isert_buf->sg_cnt);
|
||||
for (i = 0; i < isert_buf->sg_cnt; ++i) {
|
||||
size_t page_len = min_t(size_t, size, PAGE_SIZE);
|
||||
|
||||
page = alloc_page(GFP_KERNEL);
|
||||
if (!page) {
|
||||
pr_err("Failed to allocate page\n");
|
||||
res = -ENOMEM;
|
||||
goto out_map_failed;
|
||||
}
|
||||
sg_set_page(&isert_buf->sg[i], page, page_len, 0);
|
||||
size -= page_len;
|
||||
}
|
||||
|
||||
res = ib_dma_map_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt, dma_dir);
|
||||
if (unlikely(!res)) {
|
||||
pr_err("Failed to DMA map iser sg:%p len:%d\n",
|
||||
isert_buf->sg, isert_buf->sg_cnt);
|
||||
res = -ENOMEM;
|
||||
goto out_map_failed;
|
||||
}
|
||||
|
||||
isert_buf->addr = sg_virt(&isert_buf->sg[0]);
|
||||
|
||||
res = 0;
|
||||
goto out;
|
||||
|
||||
out_map_failed:
|
||||
for (; i > 0; --i)
|
||||
__free_page(sg_page(&isert_buf->sg[i]));
|
||||
kfree(isert_buf->sg);
|
||||
isert_buf->sg = NULL;
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
static void isert_buf_release_pg(struct isert_buf *isert_buf)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < isert_buf->sg_cnt; ++i)
|
||||
__free_page(sg_page(&isert_buf->sg[i]));
|
||||
}
|
||||
|
||||
static int isert_buf_malloc(struct ib_device *ib_dev,
|
||||
struct isert_buf *isert_buf, size_t size,
|
||||
enum dma_data_direction dma_dir)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
isert_buf->sg_cnt = 1;
|
||||
isert_buf->sg = kmalloc(sizeof(isert_buf->sg[0]), GFP_KERNEL);
|
||||
if (unlikely(!isert_buf->sg)) {
|
||||
pr_err("Failed to allocate buffer SG\n");
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
isert_buf->addr = kmalloc(size, GFP_KERNEL);
|
||||
if (!isert_buf->addr) {
|
||||
pr_err("Failed to allocate data buffer\n");
|
||||
res = -ENOMEM;
|
||||
goto data_malloc_failed;
|
||||
}
|
||||
|
||||
sg_init_one(&isert_buf->sg[0], isert_buf->addr, size);
|
||||
|
||||
res = ib_dma_map_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt, dma_dir);
|
||||
if (unlikely(!res)) {
|
||||
pr_err("Failed to DMA map iser sg:%p len:%d\n",
|
||||
isert_buf->sg, isert_buf->sg_cnt);
|
||||
res = -ENOMEM;
|
||||
goto out_map_failed;
|
||||
}
|
||||
|
||||
res = 0;
|
||||
goto out;
|
||||
|
||||
out_map_failed:
|
||||
kfree(isert_buf->addr);
|
||||
isert_buf->addr = NULL;
|
||||
data_malloc_failed:
|
||||
kfree(isert_buf->addr);
|
||||
isert_buf->addr = NULL;
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
static void isert_buf_release_kmalloc(struct isert_buf *isert_buf)
|
||||
{
|
||||
kfree(isert_buf->addr);
|
||||
isert_buf->addr = NULL;
|
||||
}
|
||||
|
||||
int isert_buf_alloc_data_buf(struct ib_device *ib_dev,
|
||||
struct isert_buf *isert_buf, size_t size,
|
||||
enum dma_data_direction dma_dir)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
isert_buf->is_alloced = 0;
|
||||
if (size >= PAGE_SIZE) {
|
||||
res = isert_buf_alloc_pg(ib_dev, isert_buf, size, dma_dir);
|
||||
if (unlikely(res))
|
||||
goto out;
|
||||
isert_buf->is_pgalloced = 1;
|
||||
isert_buf->is_malloced = 0;
|
||||
isert_buf->is_alloced = 1;
|
||||
} else if (size) {
|
||||
res = isert_buf_malloc(ib_dev, isert_buf, size, dma_dir);
|
||||
if (unlikely(res))
|
||||
goto out;
|
||||
isert_buf->is_pgalloced = 0;
|
||||
isert_buf->is_malloced = 1;
|
||||
isert_buf->is_alloced = 1;
|
||||
}
|
||||
|
||||
isert_buf->size = size;
|
||||
isert_buf->dma_dir = dma_dir;
|
||||
out:
|
||||
return res;
|
||||
}
|
||||
|
||||
void isert_buf_release(struct isert_buf *isert_buf)
|
||||
{
|
||||
if (isert_buf->is_alloced) {
|
||||
if (isert_buf->is_pgalloced)
|
||||
isert_buf_release_pg(isert_buf);
|
||||
|
||||
if (isert_buf->is_malloced)
|
||||
isert_buf_release_kmalloc(isert_buf);
|
||||
|
||||
isert_buf->is_alloced = 0;
|
||||
kfree(isert_buf->sg);
|
||||
isert_buf->sg = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void isert_wr_set_fields(struct isert_wr *wr,
|
||||
struct isert_connection *isert_conn,
|
||||
struct isert_cmnd *pdu)
|
||||
{
|
||||
struct isert_device *isert_dev = isert_conn->isert_dev;
|
||||
|
||||
wr->conn = isert_conn;
|
||||
wr->pdu = pdu;
|
||||
wr->isert_dev = isert_dev;
|
||||
}
|
||||
|
||||
int isert_wr_init(struct isert_wr *wr,
|
||||
enum isert_wr_op wr_op,
|
||||
struct isert_buf *isert_buf,
|
||||
struct isert_connection *isert_conn,
|
||||
struct isert_cmnd *pdu,
|
||||
struct ib_sge *sge,
|
||||
int sg_offset,
|
||||
int sg_cnt,
|
||||
int buff_offset)
|
||||
{
|
||||
enum ib_wr_opcode send_wr_op = IB_WR_SEND;
|
||||
struct scatterlist *sg_tmp;
|
||||
int i;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
switch (wr_op) {
|
||||
case ISER_WR_RECV:
|
||||
case ISER_WR_SEND:
|
||||
break;
|
||||
case ISER_WR_RDMA_READ:
|
||||
send_wr_op = IB_WR_RDMA_READ;
|
||||
if (unlikely(!pdu->is_wstag_valid)) {
|
||||
pr_err("No write tag/va specified for RDMA op\n");
|
||||
isert_buf_release(isert_buf);
|
||||
buff_offset = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
wr->send_wr.wr.rdma.remote_addr = pdu->rem_write_va +
|
||||
buff_offset;
|
||||
wr->send_wr.wr.rdma.rkey = pdu->rem_write_stag;
|
||||
break;
|
||||
case ISER_WR_RDMA_WRITE:
|
||||
send_wr_op = IB_WR_RDMA_WRITE;
|
||||
if (unlikely(!pdu->is_rstag_valid)) {
|
||||
pr_err("No read tag/va specified for RDMA op\n");
|
||||
isert_buf_release(isert_buf);
|
||||
buff_offset = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
wr->send_wr.wr.rdma.remote_addr = pdu->rem_read_va +
|
||||
buff_offset;
|
||||
wr->send_wr.wr.rdma.rkey = pdu->rem_read_stag;
|
||||
break;
|
||||
default:
|
||||
BUG();
|
||||
}
|
||||
|
||||
EXTRACHECKS_BUG_ON(isert_buf->sg_cnt == 0);
|
||||
|
||||
wr->wr_op = wr_op;
|
||||
wr->buf = isert_buf;
|
||||
|
||||
wr->sge_list = sge + sg_offset;
|
||||
|
||||
sg_tmp = &isert_buf->sg[sg_offset];
|
||||
for (i = 0; i < sg_cnt; i++, sg_tmp++) {
|
||||
wr->sge_list[i].addr = sg_dma_address(sg_tmp);
|
||||
wr->sge_list[i].length = sg_dma_len(sg_tmp);
|
||||
buff_offset += wr->sge_list[i].length;
|
||||
}
|
||||
|
||||
if (wr_op == ISER_WR_RECV) {
|
||||
wr->recv_wr.next = NULL;
|
||||
wr->recv_wr.wr_id = _ptr_to_u64(wr);
|
||||
wr->recv_wr.sg_list = wr->sge_list;
|
||||
wr->recv_wr.num_sge = sg_cnt;
|
||||
} else {
|
||||
wr->send_wr.next = NULL;
|
||||
wr->send_wr.wr_id = _ptr_to_u64(wr);
|
||||
wr->send_wr.sg_list = wr->sge_list;
|
||||
wr->send_wr.num_sge = sg_cnt;
|
||||
wr->send_wr.opcode = send_wr_op;
|
||||
wr->send_wr.send_flags = IB_SEND_SIGNALED;
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(buff_offset);
|
||||
return buff_offset;
|
||||
}
|
||||
|
||||
void isert_wr_release(struct isert_wr *wr)
|
||||
{
|
||||
struct isert_buf *isert_buf = wr->buf;
|
||||
if (isert_buf && isert_buf->is_alloced) {
|
||||
struct isert_device *isert_dev = wr->isert_dev;
|
||||
struct ib_device *ib_dev;
|
||||
|
||||
ib_dev = isert_dev->ib_dev;
|
||||
ib_dma_unmap_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt,
|
||||
isert_buf->dma_dir);
|
||||
isert_buf_release(isert_buf);
|
||||
}
|
||||
memset(wr, 0, sizeof(*wr));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,286 @@
|
||||
/*
|
||||
* isert_datamover.c
|
||||
*
|
||||
* This file is part of iser target kernel module.
|
||||
*
|
||||
* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved.
|
||||
* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com)
|
||||
*
|
||||
* This software is available to you under a choice of one of two
|
||||
* licenses. You may choose to be licensed under the terms of the GNU
|
||||
* General Public License (GPL) Version 2, available from the file
|
||||
* COPYING in the main directory of this source tree, or the
|
||||
* OpenIB.org BSD license below:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "iser.h"
|
||||
#include "iser_datamover.h"
|
||||
|
||||
int isert_datamover_init(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = isert_global_init();
|
||||
if (err) {
|
||||
pr_err("iser datamover init failed, err:%d\n", err);
|
||||
return err;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int isert_datamover_cleanup(void)
|
||||
{
|
||||
isert_global_cleanup();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int isert_get_peer_addr(struct iscsi_conn *iscsi_conn, struct sockaddr *sa,
|
||||
size_t *addr_len)
|
||||
{
|
||||
int ret;
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
struct sockaddr *peer_sa = (struct sockaddr *)&isert_conn->peer_addr;
|
||||
|
||||
ret = isert_get_addr_size(peer_sa, addr_len);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
memcpy(sa, peer_sa, *addr_len);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int isert_get_target_addr(struct iscsi_conn *iscsi_conn, struct sockaddr *sa,
|
||||
size_t *addr_len)
|
||||
{
|
||||
int ret;
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
struct sockaddr *self_sa = (struct sockaddr *)&isert_conn->self_addr;
|
||||
|
||||
ret = isert_get_addr_size(self_sa, addr_len);
|
||||
if (unlikely(ret))
|
||||
goto out;
|
||||
|
||||
memcpy(sa, self_sa, *addr_len);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *isert_portal_add(struct sockaddr *saddr, size_t addr_len)
|
||||
{
|
||||
struct isert_portal *portal = isert_portal_start(saddr, addr_len);
|
||||
|
||||
if (IS_ERR(portal))
|
||||
portal = NULL;
|
||||
|
||||
return portal;
|
||||
}
|
||||
|
||||
int isert_portal_remove(void *portal_h)
|
||||
{
|
||||
struct isert_portal *portal = portal_h;
|
||||
|
||||
isert_portal_release(portal);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void isert_free_connection(struct iscsi_conn *iscsi_conn)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
isert_conn_free(isert_conn);
|
||||
}
|
||||
|
||||
struct iscsi_cmnd *isert_alloc_login_rsp_pdu(struct iscsi_conn *iscsi_conn)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
struct isert_cmnd *isert_pdu = isert_conn->login_rsp_pdu;
|
||||
|
||||
isert_tx_pdu_init(isert_pdu, isert_conn);
|
||||
return &isert_pdu->iscsi;
|
||||
}
|
||||
|
||||
static struct iscsi_cmnd *isert_alloc_scsi_pdu(struct iscsi_conn *iscsi_conn,
|
||||
int fake)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
struct isert_cmnd *isert_pdu;
|
||||
|
||||
spin_lock(&isert_conn->tx_lock);
|
||||
isert_pdu = list_first_entry(&isert_conn->tx_free_list,
|
||||
struct isert_cmnd, pool_node);
|
||||
list_move(&isert_pdu->pool_node, &isert_conn->tx_busy_list);
|
||||
spin_unlock(&isert_conn->tx_lock);
|
||||
|
||||
isert_pdu->is_fake_rx = fake;
|
||||
return &isert_pdu->iscsi;
|
||||
}
|
||||
|
||||
struct iscsi_cmnd *isert_alloc_scsi_rsp_pdu(struct iscsi_conn *iscsi_conn)
|
||||
{
|
||||
return isert_alloc_scsi_pdu(iscsi_conn, 0);
|
||||
}
|
||||
|
||||
struct iscsi_cmnd *isert_alloc_scsi_fake_pdu(struct iscsi_conn *iscsi_conn)
|
||||
{
|
||||
return isert_alloc_scsi_pdu(iscsi_conn, 1);
|
||||
}
|
||||
|
||||
void isert_release_tx_pdu(struct iscsi_cmnd *iscsi_pdu)
|
||||
{
|
||||
struct isert_cmnd *isert_pdu = (struct isert_cmnd *)iscsi_pdu;
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_pdu->conn;
|
||||
|
||||
isert_tx_pdu_init_iscsi(isert_pdu);
|
||||
|
||||
spin_lock(&isert_conn->tx_lock);
|
||||
list_move(&isert_pdu->pool_node, &isert_conn->tx_free_list);
|
||||
spin_unlock(&isert_conn->tx_lock);
|
||||
}
|
||||
|
||||
void isert_release_rx_pdu(struct iscsi_cmnd *iscsi_pdu)
|
||||
{
|
||||
struct isert_cmnd *isert_pdu = (struct isert_cmnd *)iscsi_pdu;
|
||||
|
||||
if (likely(!isert_pdu->is_fake_rx))
|
||||
isert_rx_pdu_done(isert_pdu);
|
||||
}
|
||||
|
||||
/* if last transition into FF (Fully Featured) state */
|
||||
int isert_login_rsp_tx(struct iscsi_cmnd *login_rsp, int last, int discovery)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)login_rsp->conn;
|
||||
|
||||
if (last && !discovery) {
|
||||
int err = isert_alloc_conn_resources(isert_conn);
|
||||
if (err) {
|
||||
pr_err("Failed to init conn resources\n");
|
||||
return err;
|
||||
}
|
||||
isert_pdu_free(isert_conn->login_req_pdu);
|
||||
isert_conn->login_req_pdu = NULL;
|
||||
} else {
|
||||
int err = isert_post_recv(isert_conn,
|
||||
&isert_conn->login_req_pdu->wr[0],
|
||||
1);
|
||||
if (unlikely(err)) {
|
||||
pr_err("Failed to post recv login req rx buf, err:%d\n", err);
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
return isert_pdu_tx(login_rsp);
|
||||
}
|
||||
|
||||
int isert_set_session_params(struct iscsi_conn *iscsi_conn,
|
||||
struct iscsi_sess_params *sess_params,
|
||||
struct iscsi_tgt_params *tgt_params)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
|
||||
isert_conn->queue_depth = tgt_params->queued_cmnds;
|
||||
|
||||
isert_conn->immediate_data = sess_params->immediate_data;
|
||||
isert_conn->target_recv_data_length = sess_params->target_recv_data_length;
|
||||
isert_conn->initial_r2t = sess_params->initial_r2t;
|
||||
isert_conn->first_burst_length = sess_params->first_burst_length;
|
||||
isert_conn->initiator_recv_data_length = sess_params->initiator_recv_data_length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int isert_pdu_tx(struct iscsi_cmnd *iscsi_cmnd)
|
||||
{
|
||||
struct isert_cmnd *isert_cmnd = (struct isert_cmnd *)iscsi_cmnd;
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_cmnd->conn;
|
||||
int err;
|
||||
|
||||
isert_tx_pdu_convert_from_iscsi(isert_cmnd, iscsi_cmnd);
|
||||
err = isert_pdu_send(isert_conn, isert_cmnd);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
int isert_request_data_out(struct iscsi_cmnd *iscsi_cmnd)
|
||||
{
|
||||
struct isert_cmnd *isert_cmnd = (struct isert_cmnd *)iscsi_cmnd;
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_cmnd->conn;
|
||||
int ret;
|
||||
|
||||
ret = isert_prepare_rdma(isert_cmnd, isert_conn, ISER_WR_RDMA_READ);
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
|
||||
ret = isert_pdu_post_rdma_read(isert_conn, isert_cmnd, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int isert_send_data_in(struct iscsi_cmnd *iscsi_cmnd,
|
||||
struct iscsi_cmnd *iscsi_rsp)
|
||||
{
|
||||
struct isert_cmnd *isert_cmnd = (struct isert_cmnd *)iscsi_cmnd;
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_cmnd->conn;
|
||||
struct isert_cmnd *isert_rsp = (struct isert_cmnd *)iscsi_rsp;
|
||||
int ret;
|
||||
|
||||
ret = isert_prepare_rdma(isert_cmnd, isert_conn, ISER_WR_RDMA_WRITE);
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
|
||||
isert_tx_pdu_convert_from_iscsi(isert_rsp, iscsi_rsp);
|
||||
ret = isert_pdu_post_rdma_write(isert_conn, isert_cmnd, isert_rsp, ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int isert_close_connection(struct iscsi_conn *iscsi_conn)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
|
||||
isert_conn_disconnect(isert_conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int isert_task_abort(struct iscsi_cmnd *cmnd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *isert_get_priv(struct iscsi_conn *iscsi_conn)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
|
||||
return isert_conn->priv_data;
|
||||
}
|
||||
|
||||
void isert_set_priv(struct iscsi_conn *iscsi_conn, void *priv)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)iscsi_conn;
|
||||
|
||||
isert_conn->priv_data = priv;
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
#ifndef __ISER_DATAMOVER_H__
|
||||
#define __ISER_DATAMOVER_H__
|
||||
|
||||
#include "iscsi.h"
|
||||
|
||||
/* iscsi layer calling iser */
|
||||
int isert_datamover_init(void);
|
||||
int isert_datamover_cleanup(void);
|
||||
|
||||
void *isert_portal_add(struct sockaddr *sa, size_t addr_len);
|
||||
int isert_portal_remove(void *portal_h);
|
||||
|
||||
struct iscsi_cmnd *isert_alloc_login_rsp_pdu(struct iscsi_conn *iscsi_conn);
|
||||
|
||||
int isert_get_peer_addr(struct iscsi_conn *iscsi_conn, struct sockaddr *sa,
|
||||
size_t *addr_len);
|
||||
|
||||
int isert_get_target_addr(struct iscsi_conn *iscsi_conn, struct sockaddr *sa,
|
||||
size_t *addr_len);
|
||||
|
||||
/* last: if last transition into FF (Fully Featured) state */
|
||||
int isert_login_rsp_tx(struct iscsi_cmnd *login_rsp,
|
||||
int last, int discovery);
|
||||
int isert_set_session_params(struct iscsi_conn *iscsi_conn,
|
||||
struct iscsi_sess_params *sess_params,
|
||||
struct iscsi_tgt_params *tgt_params);
|
||||
|
||||
struct iscsi_cmnd *isert_alloc_scsi_rsp_pdu(struct iscsi_conn *iscsi_conn);
|
||||
struct iscsi_cmnd *isert_alloc_scsi_fake_pdu(struct iscsi_conn *iscsi_conn);
|
||||
|
||||
int isert_pdu_tx(struct iscsi_cmnd *pdu);
|
||||
|
||||
int isert_request_data_out(struct iscsi_cmnd *cmd);
|
||||
int isert_send_data_in(struct iscsi_cmnd *cmd, struct iscsi_cmnd *rsp);
|
||||
int isert_send_status(struct iscsi_cmnd *rsp);
|
||||
|
||||
int isert_close_connection(struct iscsi_conn *iscsi_conn);
|
||||
int isert_task_abort(struct iscsi_cmnd *cmnd);
|
||||
void isert_free_connection(struct iscsi_conn *iscsi_conn);
|
||||
|
||||
void isert_release_tx_pdu(struct iscsi_cmnd *iscsi_pdu);
|
||||
void isert_release_rx_pdu(struct iscsi_cmnd *cmnd);
|
||||
|
||||
/* iser calling iscsi layer */
|
||||
int isert_conn_established(struct iscsi_conn *iscsi_conn,
|
||||
struct sockaddr *from_addr, int addr_len);
|
||||
int isert_login_req_rx(struct iscsi_cmnd *login_req);
|
||||
int isert_pdu_rx(struct iscsi_cmnd *pdu);
|
||||
int isert_data_out_ready(struct iscsi_cmnd *cmd);
|
||||
int isert_data_in_sent(struct iscsi_cmnd *cmd);
|
||||
int isert_pdu_sent(struct iscsi_cmnd *pdu);
|
||||
void isert_pdu_err(struct iscsi_cmnd *pdu);
|
||||
|
||||
int isert_connection_closed(struct iscsi_conn *iscsi_conn);
|
||||
|
||||
void *isert_get_priv(struct iscsi_conn *iscsi_conn);
|
||||
void isert_set_priv(struct iscsi_conn *iscsi_conn, void *priv);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* isert_global.c
|
||||
*
|
||||
* This file is part of iser target kernel module.
|
||||
*
|
||||
* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved.
|
||||
* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com)
|
||||
*
|
||||
* This software is available to you under a choice of one of two
|
||||
* licenses. You may choose to be licensed under the terms of the GNU
|
||||
* General Public License (GPL) Version 2, available from the file
|
||||
* COPYING in the main directory of this source tree, or the
|
||||
* OpenIB.org BSD license below:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "iser.h"
|
||||
|
||||
static struct isert_global isert_glob;
|
||||
|
||||
struct kmem_cache *isert_cmnd_cache;
|
||||
struct kmem_cache *isert_conn_cache;
|
||||
|
||||
void isert_portal_list_add(struct isert_portal *portal)
|
||||
{
|
||||
spin_lock(&isert_glob.portal_lock);
|
||||
list_add_tail(&portal->list_node, &isert_glob.portal_list);
|
||||
spin_unlock(&isert_glob.portal_lock);
|
||||
}
|
||||
|
||||
void isert_portal_list_remove(struct isert_portal *portal)
|
||||
{
|
||||
spin_lock(&isert_glob.portal_lock);
|
||||
list_del_init(&portal->list_node);
|
||||
spin_unlock(&isert_glob.portal_lock);
|
||||
}
|
||||
|
||||
void isert_dev_list_add(struct isert_device *isert_dev)
|
||||
{
|
||||
list_add_tail(&isert_dev->devs_node, &isert_glob.dev_list);
|
||||
}
|
||||
|
||||
void isert_dev_list_remove(struct isert_device *isert_dev)
|
||||
{
|
||||
list_del_init(&isert_dev->devs_node);
|
||||
}
|
||||
|
||||
struct isert_device *isert_device_find(struct ib_device *ib_dev)
|
||||
{
|
||||
struct isert_device *isert_dev;
|
||||
struct isert_device *res = NULL;
|
||||
|
||||
list_for_each_entry(isert_dev, &isert_glob.dev_list, devs_node) {
|
||||
if (isert_dev->ib_dev == ib_dev) {
|
||||
res = isert_dev;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void isert_portal_list_release_all(void)
|
||||
{
|
||||
struct isert_portal *portal, *n;
|
||||
|
||||
list_for_each_entry_safe(portal, n, &isert_glob.portal_list, list_node)
|
||||
isert_portal_release(portal);
|
||||
}
|
||||
|
||||
void isert_conn_queue_work(struct work_struct *w)
|
||||
{
|
||||
queue_work(isert_glob.conn_wq, w);
|
||||
}
|
||||
|
||||
int isert_global_init(void)
|
||||
{
|
||||
INIT_LIST_HEAD(&isert_glob.portal_list);
|
||||
INIT_LIST_HEAD(&isert_glob.dev_list);
|
||||
|
||||
spin_lock_init(&isert_glob.portal_lock);
|
||||
|
||||
isert_glob.conn_wq = create_workqueue("isert_conn_wq");
|
||||
if (!isert_glob.conn_wq) {
|
||||
pr_err("Failed to alloc iser conn work queue\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
isert_cmnd_cache = KMEM_CACHE(isert_cmnd,
|
||||
SCST_SLAB_FLAGS|SLAB_HWCACHE_ALIGN);
|
||||
if (!isert_cmnd_cache) {
|
||||
destroy_workqueue(isert_glob.conn_wq);
|
||||
pr_err("Failed to alloc iser command cache\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
isert_conn_cache = KMEM_CACHE(isert_connection,
|
||||
SCST_SLAB_FLAGS|SLAB_HWCACHE_ALIGN);
|
||||
if (!isert_conn_cache) {
|
||||
destroy_workqueue(isert_glob.conn_wq);
|
||||
kmem_cache_destroy(isert_cmnd_cache);
|
||||
pr_err("Failed to alloc iser connection cache\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void isert_global_cleanup(void)
|
||||
{
|
||||
isert_portal_list_release_all();
|
||||
if (isert_glob.conn_wq)
|
||||
destroy_workqueue(isert_glob.conn_wq);
|
||||
if (isert_cmnd_cache)
|
||||
kmem_cache_destroy(isert_cmnd_cache);
|
||||
if (isert_conn_cache)
|
||||
kmem_cache_destroy(isert_conn_cache);
|
||||
}
|
||||
|
||||
int isert_get_addr_size(struct sockaddr *sa, size_t *addr_len)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (sa->sa_family) {
|
||||
case AF_INET:
|
||||
*addr_len = sizeof(struct sockaddr_in);
|
||||
break;
|
||||
case AF_INET6:
|
||||
*addr_len = sizeof(struct sockaddr_in6);
|
||||
break;
|
||||
default:
|
||||
pr_err("Unknown address family\n");
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef __ISER_HDR_H__
|
||||
#define __ISER_HDR_H__
|
||||
|
||||
#include "iscsi.h"
|
||||
|
||||
#define ISCSI_LOGIN_MAX_RDSL (8 * 1024)
|
||||
|
||||
struct isert_hdr {
|
||||
u8 flags;
|
||||
u8 rsvd[3];
|
||||
__be32 write_stag; /* write rkey */
|
||||
__be64 write_va;
|
||||
__be32 read_stag; /* read rkey */
|
||||
__be64 read_va;
|
||||
} __packed;
|
||||
|
||||
#define ISER_WSV 0x08
|
||||
#define ISER_RSV 0x04
|
||||
|
||||
#define ISER_ISCSI_CTRL 0x10
|
||||
#define ISER_HELLO 0x20
|
||||
#define ISER_HELLORPLY 0x30
|
||||
|
||||
#define ISER_HDRS_SZ (sizeof(struct isert_hdr) + sizeof(struct iscsi_hdr))
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,575 @@
|
||||
/*
|
||||
* isert_pdu.c
|
||||
*
|
||||
* This file is part of iser target kernel module.
|
||||
*
|
||||
* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved.
|
||||
* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com)
|
||||
*
|
||||
* This software is available to you under a choice of one of two
|
||||
* licenses. You may choose to be licensed under the terms of the GNU
|
||||
* General Public License (GPL) Version 2, available from the file
|
||||
* COPYING in the main directory of this source tree, or the
|
||||
* OpenIB.org BSD license below:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include "iser.h"
|
||||
#include "iscsi.h"
|
||||
#include "iser_datamover.h"
|
||||
|
||||
static inline int isert_pdu_rx_buf_init(struct isert_cmnd *isert_pdu,
|
||||
struct isert_connection *isert_conn)
|
||||
{
|
||||
struct isert_buf *isert_buf = &isert_pdu->buf;
|
||||
|
||||
return isert_wr_init(&isert_pdu->wr[0], ISER_WR_RECV, isert_buf,
|
||||
isert_conn, isert_pdu, isert_pdu->sg_pool,
|
||||
0, isert_buf->sg_cnt, 0);
|
||||
}
|
||||
|
||||
static inline int isert_pdu_tx_buf_init(struct isert_cmnd *isert_pdu,
|
||||
struct isert_connection *isert_conn)
|
||||
{
|
||||
struct isert_buf *isert_buf = &isert_pdu->buf;
|
||||
|
||||
return isert_wr_init(&isert_pdu->wr[0], ISER_WR_SEND, isert_buf,
|
||||
isert_conn, isert_pdu, isert_pdu->sg_pool,
|
||||
0, isert_buf->sg_cnt, 0);
|
||||
}
|
||||
|
||||
static inline void isert_pdu_set_hdr_plain(struct isert_cmnd *isert_pdu)
|
||||
{
|
||||
struct isert_hdr *isert_hdr = isert_pdu->isert_hdr;
|
||||
|
||||
isert_hdr->flags = ISER_ISCSI_CTRL;
|
||||
isert_hdr->write_stag = 0;
|
||||
isert_hdr->write_va = 0;
|
||||
isert_hdr->read_stag = 0;
|
||||
isert_hdr->read_va = 0;
|
||||
}
|
||||
|
||||
/* rx pdu should be initialized to get the posted buffer and
|
||||
* the associated pointers right; after a pdu is received
|
||||
* it should be parsed to setup isert_cmnd + iscsi_cmnd in full
|
||||
*/
|
||||
static int isert_rx_pdu_init(struct isert_cmnd *isert_pdu,
|
||||
struct isert_connection *isert_conn)
|
||||
{
|
||||
struct iscsi_cmnd *iscsi_cmnd = &isert_pdu->iscsi;
|
||||
int err = isert_pdu_rx_buf_init(isert_pdu, isert_conn);
|
||||
if (unlikely(err < 0))
|
||||
return err;
|
||||
iscsi_cmnd->conn = &isert_conn->iscsi;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void isert_tx_pdu_init_iscsi(struct isert_cmnd *isert_pdu)
|
||||
{
|
||||
struct iscsi_cmnd *iscsi_cmnd = &isert_pdu->iscsi;
|
||||
struct isert_buf *isert_buf = &isert_pdu->buf;
|
||||
|
||||
memset(iscsi_cmnd, 0, sizeof(*iscsi_cmnd));
|
||||
|
||||
iscsi_cmnd->sg_cnt = isert_buf->sg_cnt;
|
||||
iscsi_cmnd->sg = isert_buf->sg;
|
||||
iscsi_cmnd->bufflen = isert_buf->size;
|
||||
}
|
||||
|
||||
/* tx pdu should set most of the pointers to enable filling out
|
||||
* of the iscsi pdu struct
|
||||
*/
|
||||
void isert_tx_pdu_init(struct isert_cmnd *isert_pdu,
|
||||
struct isert_connection *isert_conn)
|
||||
{
|
||||
struct iscsi_cmnd *iscsi_cmnd = &isert_pdu->iscsi;
|
||||
struct isert_buf *isert_buf = &isert_pdu->buf;
|
||||
void *addr = isert_buf->addr;
|
||||
struct iscsi_hdr *bhs = (struct iscsi_hdr *)(addr + sizeof(struct isert_hdr));
|
||||
|
||||
isert_pdu->isert_hdr = (struct isert_hdr *)addr;
|
||||
isert_pdu->bhs = bhs;
|
||||
isert_pdu->ahs = NULL;
|
||||
|
||||
isert_tx_pdu_init_iscsi(isert_pdu);
|
||||
iscsi_cmnd->conn = &isert_conn->iscsi;
|
||||
}
|
||||
|
||||
void isert_tx_pdu_convert_from_iscsi(struct isert_cmnd *isert_cmnd,
|
||||
struct iscsi_cmnd *iscsi_cmnd)
|
||||
{
|
||||
struct iscsi_pdu *iscsi_pdu = &iscsi_cmnd->pdu;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
memcpy(isert_cmnd->bhs, &iscsi_pdu->bhs, sizeof(*isert_cmnd->bhs));
|
||||
if (unlikely(iscsi_pdu->ahssize)) {
|
||||
isert_cmnd->ahs = isert_cmnd->bhs + 1;
|
||||
memcpy(isert_cmnd->ahs, iscsi_pdu->ahs, iscsi_pdu->ahssize);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
if (iscsi_cmnd->bufflen)
|
||||
EXTRACHECKS_BUG_ON(!iscsi_cmnd->sg);
|
||||
#endif
|
||||
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int isert_pdu_prepare_send(struct isert_connection *isert_conn,
|
||||
struct isert_cmnd *tx_pdu)
|
||||
{
|
||||
struct isert_device *isert_dev = isert_conn->isert_dev;
|
||||
struct ib_sge *sge = tx_pdu->wr[0].sge_list;
|
||||
size_t to_sync, size;
|
||||
int sg_cnt = 0;
|
||||
|
||||
size = ISER_HDRS_SZ + tx_pdu->iscsi.pdu.ahssize +
|
||||
tx_pdu->iscsi.pdu.datasize;
|
||||
while (size) {
|
||||
to_sync = size > PAGE_SIZE ? PAGE_SIZE : size;
|
||||
ib_dma_sync_single_for_device(isert_dev->ib_dev, sge->addr,
|
||||
to_sync,
|
||||
DMA_TO_DEVICE);
|
||||
|
||||
sge->length = to_sync;
|
||||
size -= to_sync;
|
||||
++sge;
|
||||
++sg_cnt;
|
||||
}
|
||||
|
||||
return sg_cnt;
|
||||
}
|
||||
|
||||
static inline void isert_link_send_wrs(struct isert_wr *from_wr,
|
||||
struct isert_wr *to_wr)
|
||||
{
|
||||
from_wr->send_wr.next = &to_wr->send_wr;
|
||||
from_wr->send_wr.send_flags = 0; /* not signaled */
|
||||
|
||||
to_wr->send_wr.next = NULL;
|
||||
to_wr->send_wr.send_flags = IB_SEND_SIGNALED;
|
||||
}
|
||||
|
||||
static inline void isert_link_send_pdu_wrs(struct isert_cmnd *from_pdu,
|
||||
struct isert_cmnd *to_pdu,
|
||||
int wr_cnt)
|
||||
{
|
||||
isert_link_send_wrs(&from_pdu->wr[wr_cnt - 1], &to_pdu->wr[0]);
|
||||
}
|
||||
|
||||
int isert_prepare_rdma(struct isert_cmnd *isert_pdu,
|
||||
struct isert_connection *isert_conn,
|
||||
enum isert_wr_op op)
|
||||
{
|
||||
struct isert_buf *isert_buf = &isert_pdu->rdma_buf;
|
||||
struct isert_device *isert_dev = isert_conn->isert_dev;
|
||||
struct ib_device *ib_dev = isert_dev->ib_dev;
|
||||
int err;
|
||||
int buff_offset;
|
||||
int sg_offset, sg_cnt;
|
||||
int wr_cnt, i;
|
||||
|
||||
isert_buf_init_sg(isert_buf, isert_pdu->iscsi.sg,
|
||||
isert_pdu->iscsi.sg_cnt,
|
||||
isert_pdu->iscsi.bufflen);
|
||||
|
||||
if (op == ISER_WR_RDMA_WRITE)
|
||||
isert_buf->dma_dir = DMA_TO_DEVICE;
|
||||
else
|
||||
isert_buf->dma_dir = DMA_FROM_DEVICE;
|
||||
|
||||
if (unlikely(isert_buf->sg_cnt > ISER_MAX_SGE)) {
|
||||
pr_err("Scatterlist too large: %d\n", isert_buf->sg_cnt);
|
||||
wr_cnt = -EOPNOTSUPP;
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = ib_dma_map_sg(ib_dev, isert_buf->sg, isert_buf->sg_cnt,
|
||||
isert_buf->dma_dir);
|
||||
if (unlikely(!err)) {
|
||||
pr_err("Failed to DMA map iser sg:%p len:%d\n",
|
||||
isert_buf->sg, isert_buf->sg_cnt);
|
||||
wr_cnt = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
buff_offset = 0;
|
||||
sg_cnt = 0;
|
||||
for (wr_cnt = 0, sg_offset = 0; sg_offset < isert_buf->sg_cnt; ++wr_cnt) {
|
||||
sg_cnt = min((int)isert_conn->max_sge,
|
||||
isert_buf->sg_cnt - sg_offset);
|
||||
err = isert_wr_init(&isert_pdu->wr[wr_cnt], op, isert_buf,
|
||||
isert_conn, isert_pdu, isert_pdu->sg_pool,
|
||||
sg_offset, sg_cnt, buff_offset);
|
||||
if (unlikely(err < 0)) {
|
||||
wr_cnt = err;
|
||||
goto out;
|
||||
}
|
||||
buff_offset = err;
|
||||
sg_offset += sg_cnt;
|
||||
}
|
||||
|
||||
for (i = 1; i < wr_cnt; ++i)
|
||||
isert_link_send_wrs(&isert_pdu->wr[i - 1], &isert_pdu->wr[i]);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(wr_cnt);
|
||||
return wr_cnt;
|
||||
}
|
||||
|
||||
void isert_pdu_free(struct isert_cmnd *pdu)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
list_del(&pdu->pool_node);
|
||||
for (i = 0; i < ARRAY_SIZE(pdu->wr); ++i)
|
||||
isert_wr_release(&pdu->wr[i]);
|
||||
|
||||
isert_pdu_kfree(pdu);
|
||||
}
|
||||
|
||||
struct isert_cmnd *isert_rx_pdu_alloc(struct isert_connection *isert_conn,
|
||||
size_t size)
|
||||
{
|
||||
struct isert_cmnd *pdu = NULL;
|
||||
int err;
|
||||
unsigned int i;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
pdu = isert_pdu_alloc();
|
||||
if (unlikely(!pdu)) {
|
||||
pr_err("Failed to alloc pdu\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = isert_buf_alloc_data_buf(isert_conn->isert_dev->ib_dev,
|
||||
&pdu->buf, size, DMA_FROM_DEVICE);
|
||||
if (unlikely(err)) {
|
||||
pr_err("Failed to alloc rx pdu buf sz:%zd\n", size);
|
||||
goto buf_alloc_failed;
|
||||
}
|
||||
|
||||
err = isert_rx_pdu_init(pdu, isert_conn);
|
||||
if (unlikely(err)) {
|
||||
pr_err("Failed to init rx pdu wr:%p size:%zd err:%d\n",
|
||||
&pdu->wr, size, err);
|
||||
goto pdu_init_failed;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pdu->wr); ++i)
|
||||
isert_wr_set_fields(&pdu->wr[i], isert_conn, pdu);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pdu->sg_pool); ++i)
|
||||
pdu->sg_pool[i].lkey = isert_conn->isert_dev->mr->lkey;
|
||||
|
||||
list_add_tail(&pdu->pool_node, &isert_conn->rx_buf_list);
|
||||
|
||||
goto out;
|
||||
|
||||
pdu_init_failed:
|
||||
isert_buf_release(&pdu->buf);
|
||||
buf_alloc_failed:
|
||||
isert_pdu_kfree(pdu);
|
||||
pdu = NULL;
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return pdu;
|
||||
}
|
||||
|
||||
struct isert_cmnd *isert_tx_pdu_alloc(struct isert_connection *isert_conn,
|
||||
size_t size)
|
||||
{
|
||||
struct isert_cmnd *pdu = NULL;
|
||||
int err;
|
||||
unsigned int i;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
pdu = isert_pdu_alloc();
|
||||
if (unlikely(!pdu)) {
|
||||
pr_err("Failed to alloc pdu\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
err = isert_buf_alloc_data_buf(isert_conn->isert_dev->ib_dev,
|
||||
&pdu->buf, size, DMA_TO_DEVICE);
|
||||
if (unlikely(err)) {
|
||||
pr_err("Failed to alloc tx pdu buf sz:%zd\n", size);
|
||||
goto buf_alloc_failed;
|
||||
}
|
||||
|
||||
err = isert_pdu_tx_buf_init(pdu, isert_conn);
|
||||
if (unlikely(err < 0)) {
|
||||
pr_err("Failed to init tx pdu wr:%p size:%zd err:%d\n",
|
||||
&pdu->wr, size, err);
|
||||
goto buf_init_failed;
|
||||
}
|
||||
isert_tx_pdu_init(pdu, isert_conn);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pdu->wr); ++i)
|
||||
isert_wr_set_fields(&pdu->wr[i], isert_conn, pdu);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pdu->sg_pool); ++i)
|
||||
pdu->sg_pool[i].lkey = isert_conn->isert_dev->mr->lkey;
|
||||
|
||||
isert_pdu_set_hdr_plain(pdu);
|
||||
|
||||
list_add_tail(&pdu->pool_node, &isert_conn->tx_free_list);
|
||||
|
||||
goto out;
|
||||
|
||||
buf_init_failed:
|
||||
isert_buf_release(&pdu->buf);
|
||||
buf_alloc_failed:
|
||||
isert_pdu_kfree(pdu);
|
||||
pdu = NULL;
|
||||
out:
|
||||
TRACE_EXIT();
|
||||
return pdu;
|
||||
}
|
||||
|
||||
static inline void isert_link_recv_wrs(struct isert_wr *from_wr,
|
||||
struct isert_wr *to_wr)
|
||||
{
|
||||
from_wr->recv_wr.next = &to_wr->recv_wr;
|
||||
|
||||
to_wr->recv_wr.next = NULL;
|
||||
}
|
||||
|
||||
static inline void isert_link_recv_pdu_wrs(struct isert_cmnd *from_pdu,
|
||||
struct isert_cmnd *to_pdu)
|
||||
{
|
||||
isert_link_recv_wrs(&from_pdu->wr[0], &to_pdu->wr[0]);
|
||||
}
|
||||
|
||||
int isert_alloc_conn_resources(struct isert_connection *isert_conn)
|
||||
{
|
||||
struct isert_cmnd *pdu, *prev_pdu = NULL, *first_pdu = NULL;
|
||||
int t_datasz = 512; /* RFC states that minimum receive data size is 512 */
|
||||
int i_datasz = ISER_HDRS_SZ + SCST_SENSE_BUFFERSIZE;
|
||||
int i, err = 0;
|
||||
int to_alloc;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
isert_conn->repost_threshold = 32;
|
||||
to_alloc = isert_conn->queue_depth * 2 + isert_conn->repost_threshold;
|
||||
|
||||
if (unlikely(to_alloc > ISER_MAX_WCE)) {
|
||||
pr_err("QueuedCommands larger than %d not supported\n",
|
||||
(ISER_MAX_WCE - isert_conn->repost_threshold) / 2);
|
||||
err = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < to_alloc; i++) {
|
||||
pdu = isert_rx_pdu_alloc(isert_conn, t_datasz);
|
||||
if (unlikely(!pdu)) {
|
||||
err = -ENOMEM;
|
||||
goto clean_pdus;
|
||||
}
|
||||
|
||||
if (unlikely(first_pdu == NULL))
|
||||
first_pdu = pdu;
|
||||
else
|
||||
isert_link_recv_pdu_wrs(prev_pdu, pdu);
|
||||
|
||||
prev_pdu = pdu;
|
||||
|
||||
pdu = isert_tx_pdu_alloc(isert_conn, i_datasz);
|
||||
if (unlikely(!pdu)) {
|
||||
err = -ENOMEM;
|
||||
goto clean_pdus;
|
||||
}
|
||||
}
|
||||
|
||||
err = isert_post_recv(isert_conn, &first_pdu->wr[0], to_alloc);
|
||||
if (unlikely(err)) {
|
||||
pr_err("Failed to post recv err:%d\n", err);
|
||||
goto clean_pdus;
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(err);
|
||||
return err;
|
||||
|
||||
clean_pdus:
|
||||
isert_free_conn_resources(isert_conn);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int isert_reinit_rx_pdu(struct isert_cmnd *pdu)
|
||||
{
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)pdu->iscsi.conn;
|
||||
|
||||
pdu->is_rstag_valid = 0;
|
||||
pdu->is_wstag_valid = 0;
|
||||
|
||||
memset(&pdu->iscsi, 0, sizeof(pdu->iscsi));
|
||||
|
||||
return isert_rx_pdu_init(pdu, isert_conn);
|
||||
}
|
||||
|
||||
int isert_rx_pdu_done(struct isert_cmnd *pdu)
|
||||
{
|
||||
int err;
|
||||
struct isert_connection *isert_conn = (struct isert_connection *)pdu->iscsi.conn;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
err = isert_reinit_rx_pdu(pdu);
|
||||
if (unlikely(err))
|
||||
goto out;
|
||||
|
||||
spin_lock(&isert_conn->post_recv_lock);
|
||||
if (unlikely(isert_conn->to_post_recv == 0))
|
||||
isert_conn->post_recv_first = &pdu->wr[0];
|
||||
else
|
||||
isert_link_recv_wrs(isert_conn->post_recv_curr, &pdu->wr[0]);
|
||||
|
||||
isert_conn->post_recv_curr = &pdu->wr[0];
|
||||
|
||||
if (++isert_conn->to_post_recv > isert_conn->repost_threshold) {
|
||||
err = isert_post_recv(isert_conn, isert_conn->post_recv_first,
|
||||
isert_conn->to_post_recv);
|
||||
if (unlikely(err))
|
||||
pr_err("Failed to post recv err:%d\n", err);
|
||||
|
||||
isert_conn->to_post_recv = 0;
|
||||
}
|
||||
spin_unlock(&isert_conn->post_recv_lock);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(err);
|
||||
return err;
|
||||
}
|
||||
|
||||
void isert_free_conn_resources(struct isert_connection *isert_conn)
|
||||
{
|
||||
struct isert_cmnd *pdu;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (isert_conn->login_rsp_pdu) {
|
||||
isert_pdu_free(isert_conn->login_rsp_pdu);
|
||||
isert_conn->login_rsp_pdu = NULL;
|
||||
}
|
||||
if (isert_conn->login_req_pdu) {
|
||||
isert_pdu_free(isert_conn->login_req_pdu);
|
||||
isert_conn->login_req_pdu = NULL;
|
||||
}
|
||||
|
||||
while (!list_empty(&isert_conn->rx_buf_list)) {
|
||||
pdu = list_first_entry(&isert_conn->rx_buf_list,
|
||||
struct isert_cmnd, pool_node);
|
||||
isert_pdu_free(pdu); /* releases buffer as well */
|
||||
}
|
||||
|
||||
spin_lock(&isert_conn->tx_lock);
|
||||
while (!list_empty(&isert_conn->tx_free_list)) {
|
||||
pdu = list_first_entry(&isert_conn->tx_free_list,
|
||||
struct isert_cmnd, pool_node);
|
||||
isert_pdu_free(pdu); /* releases buffer as well */
|
||||
}
|
||||
|
||||
while (!list_empty(&isert_conn->tx_busy_list)) {
|
||||
pdu = list_first_entry(&isert_conn->tx_busy_list,
|
||||
struct isert_cmnd, pool_node);
|
||||
isert_pdu_free(pdu); /* releases buffer as well */
|
||||
}
|
||||
spin_unlock(&isert_conn->tx_lock);
|
||||
|
||||
TRACE_EXIT();
|
||||
}
|
||||
|
||||
int isert_pdu_send(struct isert_connection *isert_conn,
|
||||
struct isert_cmnd *tx_pdu)
|
||||
{
|
||||
int err;
|
||||
struct isert_wr *wr;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
EXTRACHECKS_BUG_ON(!isert_conn);
|
||||
EXTRACHECKS_BUG_ON(!tx_pdu);
|
||||
#endif
|
||||
|
||||
wr = &tx_pdu->wr[0];
|
||||
wr->send_wr.num_sge = isert_pdu_prepare_send(isert_conn, tx_pdu);
|
||||
|
||||
err = isert_post_send(isert_conn, wr, 1);
|
||||
if (unlikely(err)) {
|
||||
pr_err("Failed to send pdu conn:%p pdu:%p err:%d\n",
|
||||
isert_conn, tx_pdu, err);
|
||||
}
|
||||
|
||||
TRACE_EXIT_RES(err);
|
||||
return err;
|
||||
}
|
||||
|
||||
int isert_pdu_post_rdma_write(struct isert_connection *isert_conn,
|
||||
struct isert_cmnd *isert_cmd,
|
||||
struct isert_cmnd *isert_rsp,
|
||||
int wr_cnt)
|
||||
{
|
||||
int err;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
isert_rsp->wr[0].send_wr.num_sge = isert_pdu_prepare_send(isert_conn,
|
||||
isert_rsp);
|
||||
isert_link_send_pdu_wrs(isert_cmd, isert_rsp, wr_cnt);
|
||||
err = isert_post_send(isert_conn, &isert_cmd->wr[0], wr_cnt + 1);
|
||||
if (unlikely(err)) {
|
||||
pr_err("Failed to send pdu conn:%p pdu:%p err:%d\n",
|
||||
isert_conn, isert_cmd, err);
|
||||
}
|
||||
|
||||
TRACE_EXIT_RES(err);
|
||||
return err;
|
||||
}
|
||||
|
||||
int isert_pdu_post_rdma_read(struct isert_connection *isert_conn,
|
||||
struct isert_cmnd *isert_cmd, int wr_cnt)
|
||||
{
|
||||
int err;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
err = isert_post_send(isert_conn, &isert_cmd->wr[0], wr_cnt);
|
||||
if (unlikely(err)) {
|
||||
pr_err("Failed to send pdu conn:%p pdu:%p err:%d\n",
|
||||
isert_conn, isert_cmd, err);
|
||||
}
|
||||
|
||||
TRACE_EXIT_RES(err);
|
||||
return err;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,495 @@
|
||||
/*
|
||||
* This file is part of iser target kernel module.
|
||||
*
|
||||
* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved.
|
||||
* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com)
|
||||
*
|
||||
* This software is available to you under a choice of one of two
|
||||
* licenses. You may choose to be licensed under the terms of the GNU
|
||||
* General Public License (GPL) Version 2, available from the file
|
||||
* COPYING in the main directory of this source tree, or the
|
||||
* OpenIB.org BSD license below:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include "isert.h"
|
||||
#include "isert_dbg.h"
|
||||
#include "iscsit_transport.h"
|
||||
#include "iser_datamover.h"
|
||||
|
||||
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
|
||||
unsigned long isert_trace_flag = ISERT_DEFAULT_LOG_FLAGS;
|
||||
unsigned long iscsi_trace_flag = ISERT_DEFAULT_LOG_FLAGS;
|
||||
#endif
|
||||
|
||||
static unsigned int isert_nr_devs = ISERT_NR_DEVS;
|
||||
module_param(isert_nr_devs, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(isert_nr_devs,
|
||||
"Maximum concurrent number of connection requests to handle.");
|
||||
|
||||
static void isert_mark_conn_closed(struct iscsi_conn *conn, int flags)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
if (flags & ISCSI_CONN_ACTIVE_CLOSE)
|
||||
conn->active_close = 1;
|
||||
if (flags & ISCSI_CONN_DELETING)
|
||||
conn->deleting = 1;
|
||||
|
||||
conn->read_state = 0;
|
||||
|
||||
if (!conn->closing) {
|
||||
conn->closing = 1;
|
||||
schedule_work(&conn->close_work);
|
||||
}
|
||||
|
||||
TRACE_EXIT();
|
||||
}
|
||||
|
||||
static void isert_close_conn(struct iscsi_conn *conn, int flags)
|
||||
{
|
||||
}
|
||||
|
||||
static int isert_receive_cmnd_data(struct iscsi_cmnd *cmnd)
|
||||
{
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
if (cmnd->scst_state == ISCSI_CMD_STATE_RX_CMD)
|
||||
TRACE_DBG("cmnd %p is still in RX_CMD state",
|
||||
cmnd);
|
||||
#endif
|
||||
EXTRACHECKS_BUG_ON(cmnd->scst_state != ISCSI_CMD_STATE_AFTER_PREPROC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void isert_update_len_sn(struct iscsi_cmnd *cmnd)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
iscsi_cmnd_set_length(&cmnd->pdu);
|
||||
switch (cmnd_opcode(cmnd)) {
|
||||
case ISCSI_OP_NOP_IN:
|
||||
if (cmnd->pdu.bhs.itt == ISCSI_RESERVED_TAG)
|
||||
cmnd->pdu.bhs.sn = (__force u32)cmnd_set_sn(cmnd, 0);
|
||||
else
|
||||
cmnd_set_sn(cmnd, 1);
|
||||
break;
|
||||
case ISCSI_OP_SCSI_RSP:
|
||||
cmnd_set_sn(cmnd, 1);
|
||||
break;
|
||||
case ISCSI_OP_SCSI_TASK_MGT_RSP:
|
||||
cmnd_set_sn(cmnd, 1);
|
||||
break;
|
||||
case ISCSI_OP_TEXT_RSP:
|
||||
cmnd_set_sn(cmnd, 1);
|
||||
break;
|
||||
case ISCSI_OP_SCSI_DATA_IN:
|
||||
{
|
||||
struct iscsi_data_in_hdr *rsp =
|
||||
(struct iscsi_data_in_hdr *)&cmnd->pdu.bhs;
|
||||
|
||||
cmnd_set_sn(cmnd, (rsp->flags & ISCSI_FLG_FINAL) ? 1 : 0);
|
||||
break;
|
||||
}
|
||||
case ISCSI_OP_LOGOUT_RSP:
|
||||
cmnd_set_sn(cmnd, 1);
|
||||
break;
|
||||
case ISCSI_OP_R2T:
|
||||
cmnd->pdu.bhs.sn = (__force u32)cmnd_set_sn(cmnd, 0);
|
||||
break;
|
||||
case ISCSI_OP_ASYNC_MSG:
|
||||
cmnd_set_sn(cmnd, 1);
|
||||
break;
|
||||
case ISCSI_OP_REJECT:
|
||||
cmnd_set_sn(cmnd, 1);
|
||||
break;
|
||||
default:
|
||||
PRINT_ERROR("Unexpected cmnd op %x", cmnd_opcode(cmnd));
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE_EXIT();
|
||||
}
|
||||
|
||||
static int isert_process_all_writes(struct iscsi_conn *conn)
|
||||
{
|
||||
struct iscsi_cmnd *cmnd;
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
while ((cmnd = iscsi_get_send_cmnd(conn)) != NULL) {
|
||||
isert_update_len_sn(cmnd);
|
||||
conn_get(conn);
|
||||
isert_pdu_tx(cmnd);
|
||||
}
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int isert_send_locally(struct iscsi_cmnd *req, unsigned int cmd_count)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
req_cmnd_pre_release(req);
|
||||
res = isert_process_all_writes(req->conn);
|
||||
cmnd_put(req);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static struct iscsi_cmnd *isert_cmnd_alloc(struct iscsi_conn *conn,
|
||||
struct iscsi_cmnd *parent)
|
||||
{
|
||||
struct iscsi_cmnd *cmnd;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (likely(parent))
|
||||
cmnd = isert_alloc_scsi_rsp_pdu(conn);
|
||||
else
|
||||
cmnd = isert_alloc_scsi_fake_pdu(conn);
|
||||
|
||||
iscsi_cmnd_init(conn, cmnd, parent);
|
||||
|
||||
TRACE_EXIT();
|
||||
return cmnd;
|
||||
}
|
||||
|
||||
static void isert_cmnd_free(struct iscsi_cmnd *cmnd)
|
||||
{
|
||||
TRACE_ENTRY();
|
||||
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
if (unlikely(cmnd->on_write_list || cmnd->on_write_timeout_list)) {
|
||||
struct iscsi_scsi_cmd_hdr *req = cmnd_hdr(cmnd);
|
||||
|
||||
PRINT_CRIT_ERROR("cmnd %p still on some list?, %x, %x, %x, "
|
||||
"%x, %x, %x, %x", cmnd, req->opcode, req->scb[0],
|
||||
req->flags, req->itt, be32_to_cpu(req->data_length),
|
||||
req->cmd_sn,
|
||||
be32_to_cpu((__force __be32)(cmnd->pdu.datasize)));
|
||||
|
||||
if (unlikely(cmnd->parent_req)) {
|
||||
struct iscsi_scsi_cmd_hdr *preq =
|
||||
cmnd_hdr(cmnd->parent_req);
|
||||
PRINT_CRIT_ERROR("%p %x %u", preq, preq->opcode,
|
||||
preq->scb[0]);
|
||||
}
|
||||
sBUG();
|
||||
}
|
||||
#endif
|
||||
if (cmnd->parent_req)
|
||||
isert_release_tx_pdu(cmnd);
|
||||
else
|
||||
isert_release_rx_pdu(cmnd);
|
||||
|
||||
TRACE_EXIT();
|
||||
}
|
||||
|
||||
static void isert_preprocessing_done(struct iscsi_cmnd *req)
|
||||
{
|
||||
req->scst_state = ISCSI_CMD_STATE_AFTER_PREPROC;
|
||||
}
|
||||
|
||||
static void isert_set_sense_data(struct iscsi_cmnd *rsp,
|
||||
const u8 *sense_buf, int sense_len)
|
||||
{
|
||||
u8 *buf;
|
||||
|
||||
buf = sg_virt(rsp->sg) + ISER_HDRS_SZ;
|
||||
|
||||
memcpy(buf, &rsp->sense_hdr, sizeof(rsp->sense_hdr));
|
||||
memcpy(&buf[sizeof(rsp->sense_hdr)], sense_buf, sense_len);
|
||||
}
|
||||
|
||||
static void isert_set_req_data(struct iscsi_cmnd *req, struct iscsi_cmnd *rsp)
|
||||
{
|
||||
memcpy(sg_virt(rsp->sg) + ISER_HDRS_SZ,
|
||||
sg_virt(req->sg) + ISER_HDRS_SZ, req->bufflen);
|
||||
rsp->bufflen = req->bufflen;
|
||||
}
|
||||
|
||||
static void isert_send_data_rsp(struct iscsi_cmnd *req, u8 *sense,
|
||||
int sense_len, u8 status, int is_send_status)
|
||||
{
|
||||
struct iscsi_cmnd *rsp;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
sBUG_ON(!is_send_status);
|
||||
|
||||
rsp = create_status_rsp(req, status, sense, sense_len);
|
||||
|
||||
isert_update_len_sn(rsp);
|
||||
|
||||
conn_get(rsp->conn);
|
||||
if (status != SAM_STAT_CHECK_CONDITION)
|
||||
isert_send_data_in(req, rsp);
|
||||
else
|
||||
isert_pdu_tx(rsp);
|
||||
|
||||
TRACE_EXIT();
|
||||
}
|
||||
|
||||
static void isert_make_conn_wr_active(struct iscsi_conn *conn)
|
||||
{
|
||||
isert_process_all_writes(conn);
|
||||
}
|
||||
|
||||
static int isert_conn_activate(struct iscsi_conn *conn)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void isert_conn_free(struct iscsi_conn *conn)
|
||||
{
|
||||
isert_free_connection(conn);
|
||||
}
|
||||
|
||||
int isert_handle_close_connection(struct iscsi_conn *conn)
|
||||
{
|
||||
isert_mark_conn_closed(conn, 0);
|
||||
/* Take care of case where our connection is being closed
|
||||
* without being connected to a session - if connection allocation
|
||||
* failed for some reason */
|
||||
if (unlikely(!conn->session))
|
||||
isert_free_connection(conn);
|
||||
else
|
||||
start_close_conn(conn);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int isert_pdu_rx(struct iscsi_cmnd *cmnd)
|
||||
{
|
||||
int res = 0;
|
||||
scst_data_direction dir;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
cmnd->conn->rd_task = current;
|
||||
#endif
|
||||
iscsi_cmnd_init(cmnd->conn, cmnd, NULL);
|
||||
cmnd_rx_start(cmnd);
|
||||
|
||||
if (unlikely(!cmnd->scst_cmd)) {
|
||||
cmnd_rx_end(cmnd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (unlikely(scst_cmd_prelim_completed(cmnd->scst_cmd) ||
|
||||
unlikely(cmnd->prelim_compl_flags != 0))) {
|
||||
set_bit(ISCSI_CMD_PRELIM_COMPLETED, &cmnd->prelim_compl_flags);
|
||||
cmnd_rx_end(cmnd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
dir = scst_cmd_get_data_direction(cmnd->scst_cmd);
|
||||
|
||||
if (dir & SCST_DATA_WRITE) {
|
||||
res = iscsi_cmnd_set_write_buf(cmnd);
|
||||
if (unlikely(res))
|
||||
goto out;
|
||||
res = isert_request_data_out(cmnd);
|
||||
cmnd->r2t_len_to_receive = 0;
|
||||
cmnd->r2t_len_to_send = 0;
|
||||
cmnd->outstanding_r2t = 0;
|
||||
} else {
|
||||
cmnd_rx_end(cmnd);
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
int isert_data_out_ready(struct iscsi_cmnd *cmnd)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
#ifdef CONFIG_SCST_EXTRACHECKS
|
||||
cmnd->conn->rd_task = current;
|
||||
#endif
|
||||
cmnd_rx_end(cmnd);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
int isert_data_in_sent(struct iscsi_cmnd *din)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void isert_pdu_err(struct iscsi_cmnd *pdu)
|
||||
{
|
||||
struct iscsi_conn *conn = pdu->conn;
|
||||
|
||||
if (!conn->session) /* we are still in login phase */
|
||||
return;
|
||||
|
||||
if (pdu->parent_req) {
|
||||
rsp_cmnd_release(pdu);
|
||||
conn_put(conn);
|
||||
} else {
|
||||
/*
|
||||
* we will get multiple pdu errors
|
||||
* for same PDU with multiple RDMAs case
|
||||
*/
|
||||
if (pdu->on_write_timeout_list)
|
||||
req_cmnd_release(pdu);
|
||||
}
|
||||
}
|
||||
|
||||
int isert_pdu_sent(struct iscsi_cmnd *pdu)
|
||||
{
|
||||
struct iscsi_conn *conn = pdu->conn;
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (unlikely(pdu->should_close_conn)) {
|
||||
if (pdu->should_close_all_conn) {
|
||||
struct iscsi_target *target = pdu->conn->session->target;
|
||||
|
||||
PRINT_INFO("Closing all connections for target %x at "
|
||||
"initiator's %s request", target->tid,
|
||||
conn->session->initiator_name);
|
||||
mutex_lock(&target->target_mutex);
|
||||
target_del_all_sess(target, 0);
|
||||
mutex_unlock(&target->target_mutex);
|
||||
} else {
|
||||
PRINT_INFO("Closing connection at initiator's %s "
|
||||
"request", conn->session->initiator_name);
|
||||
mark_conn_closed(conn);
|
||||
}
|
||||
}
|
||||
|
||||
/* we may get NULL parent req for login response */
|
||||
if (likely(pdu->parent_req)) {
|
||||
rsp_cmnd_release(pdu);
|
||||
conn_put(conn);
|
||||
}
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static ssize_t isert_get_initiator_ip(struct iscsi_conn *conn,
|
||||
char *buf, int size)
|
||||
{
|
||||
int pos;
|
||||
struct sockaddr_storage ss;
|
||||
size_t addr_len;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
isert_get_peer_addr(conn, (struct sockaddr *)&ss, &addr_len);
|
||||
|
||||
switch (ss.ss_family) {
|
||||
case AF_INET:
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)
|
||||
pos = scnprintf(buf, size,
|
||||
"%u.%u.%u.%u",
|
||||
NIPQUAD(((struct sockaddr_in *)&ss)->sin_addr.s_addr));
|
||||
#else
|
||||
pos = scnprintf(buf, size,
|
||||
"%pI4", &((struct sockaddr_in *)&ss)->sin_addr.s_addr);
|
||||
#endif
|
||||
break;
|
||||
case AF_INET6:
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
|
||||
pos = scnprintf(buf, size,
|
||||
"[%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x]",
|
||||
NIP6(((struct sockaddr_in6 *)&ss)->sin6_addr.s_addr));
|
||||
#else
|
||||
pos = scnprintf(buf, size, "[%p6]",
|
||||
&((struct sockaddr_in6 *)&ss)->sin6_addr);
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
pos = scnprintf(buf, size, "Unknown family %d",
|
||||
ss.ss_family);
|
||||
break;
|
||||
}
|
||||
|
||||
TRACE_EXIT_RES(pos);
|
||||
return pos;
|
||||
}
|
||||
|
||||
static struct iscsit_transport isert_transport = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "iSER",
|
||||
.transport_type = ISCSI_RDMA,
|
||||
.iscsit_conn_alloc = isert_conn_alloc,
|
||||
.iscsit_conn_activate = isert_conn_activate,
|
||||
.iscsit_conn_free = isert_conn_free,
|
||||
.iscsit_alloc_cmd = isert_cmnd_alloc,
|
||||
.iscsit_free_cmd = isert_cmnd_free,
|
||||
.iscsit_preprocessing_done = isert_preprocessing_done,
|
||||
.iscsit_send_data_rsp = isert_send_data_rsp,
|
||||
.iscsit_make_conn_wr_active = isert_make_conn_wr_active,
|
||||
.iscsit_get_initiator_ip = isert_get_initiator_ip,
|
||||
.iscsit_send_locally = isert_send_locally,
|
||||
.iscsit_mark_conn_closed = isert_mark_conn_closed,
|
||||
.iscsit_conn_close = isert_close_conn,
|
||||
.iscsit_set_sense_data = isert_set_sense_data,
|
||||
.iscsit_set_req_data = isert_set_req_data,
|
||||
.iscsit_receive_cmnd_data = isert_receive_cmnd_data,
|
||||
.iscsit_close_all_portals = isert_close_all_portals,
|
||||
};
|
||||
|
||||
static void isert_cleanup_module(void)
|
||||
{
|
||||
iscsit_unregister_transport(&isert_transport);
|
||||
isert_cleanup_login_devs();
|
||||
}
|
||||
|
||||
static int __init isert_init_module(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = iscsit_register_transport(&isert_transport);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = isert_init_login_devs(isert_nr_devs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Yan Burman");
|
||||
MODULE_LICENSE("Dual BSD/GPL");
|
||||
MODULE_DESCRIPTION("iSER target transport driver");
|
||||
|
||||
module_init(isert_init_module);
|
||||
module_exit(isert_cleanup_module);
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* This file is part of iser target kernel module.
|
||||
*
|
||||
* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved.
|
||||
* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com)
|
||||
*
|
||||
* This software is available to you under a choice of one of two
|
||||
* licenses. You may choose to be licensed under the terms of the GNU
|
||||
* General Public License (GPL) Version 2, available from the file
|
||||
* COPYING in the main directory of this source tree, or the
|
||||
* OpenIB.org BSD license below:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef __ISERT_H__
|
||||
#define __ISERT_H__
|
||||
|
||||
#include <linux/list.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/types.h> /* size_t, dev_t */
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
|
||||
#include <asm/atomic.h>
|
||||
#else
|
||||
#include <linux/atomic.h>
|
||||
#endif
|
||||
|
||||
#ifdef INSIDE_KERNEL_TREE
|
||||
#include <scst/isert_scst.h>
|
||||
#include <scst/iscsi_scst.h>
|
||||
#include <scst/iscsi.h>
|
||||
#else
|
||||
#include "isert_scst.h"
|
||||
#include "iscsi_scst.h"
|
||||
#include "iscsi.h"
|
||||
#endif
|
||||
|
||||
#include "iser_hdr.h"
|
||||
|
||||
struct iscsi_conn;
|
||||
|
||||
#define ISERT_NR_DEVS 64
|
||||
|
||||
struct isert_listener_dev {
|
||||
struct device *dev;
|
||||
struct cdev cdev;
|
||||
dev_t devno;
|
||||
wait_queue_head_t waitqueue;
|
||||
spinlock_t conn_lock;
|
||||
struct list_head new_conn_list;
|
||||
struct list_head curr_conn_list;
|
||||
struct isert_addr_info info;
|
||||
atomic_t available;
|
||||
void *portal_h[ISERT_MAX_PORTALS];
|
||||
int free_portal_idx;
|
||||
};
|
||||
|
||||
enum isert_conn_dev_state {
|
||||
CS_INIT,
|
||||
CS_REQ_BHS,
|
||||
CS_REQ_DATA,
|
||||
CS_REQ_FINISHED,
|
||||
CS_RSP_BHS,
|
||||
CS_RSP_DATA,
|
||||
CS_RSP_FINISHED,
|
||||
CS_DISCONNECTED,
|
||||
};
|
||||
|
||||
struct isert_conn_dev {
|
||||
struct device *dev;
|
||||
struct cdev cdev;
|
||||
dev_t devno;
|
||||
wait_queue_head_t waitqueue;
|
||||
struct list_head conn_list_entry;
|
||||
struct iscsi_conn *conn;
|
||||
unsigned int idx;
|
||||
int occupied;
|
||||
spinlock_t pdu_lock;
|
||||
struct iscsi_cmnd *login_req;
|
||||
struct iscsi_cmnd *login_rsp;
|
||||
atomic_t available;
|
||||
size_t read_len;
|
||||
char *read_buf;
|
||||
size_t write_len;
|
||||
char *write_buf;
|
||||
void *sg_virt;
|
||||
struct page *pages[DIV_ROUND_UP(ISCSI_LOGIN_MAX_RDSL, PAGE_SIZE)];
|
||||
enum isert_conn_dev_state state;
|
||||
int is_discovery;
|
||||
struct timer_list tmo_timer;
|
||||
int timer_active;
|
||||
struct kref kref;
|
||||
};
|
||||
|
||||
#define ISER_CONN_DEV_PREFIX "isert/conn"
|
||||
|
||||
/* isert_login.c */
|
||||
int __init isert_init_login_devs(unsigned int ndevs);
|
||||
void isert_cleanup_login_devs(void);
|
||||
int isert_conn_alloc(struct iscsi_session *session,
|
||||
struct iscsi_kern_conn_info *info,
|
||||
struct iscsi_conn **new_conn,
|
||||
struct iscsit_transport *t);
|
||||
int isert_handle_close_connection(struct iscsi_conn *conn);
|
||||
void isert_close_all_portals(void);
|
||||
|
||||
#endif /* __ISERT_H__ */
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright (C) 2007 - 2014 Vladislav Bolkhovitin
|
||||
* Copyright (C) 2007 - 2014 Fusion-io, Inc.
|
||||
*
|
||||
* 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, version 2
|
||||
* of the License.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ISERT_DBG_H
|
||||
#define ISERT_DBG_H
|
||||
|
||||
#include <linux/version.h>
|
||||
|
||||
#ifdef LOG_PREFIX
|
||||
#undef LOG_PREFIX
|
||||
#endif
|
||||
|
||||
#define LOG_PREFIX "isert" /* Prefix for SCST tracing macros. */
|
||||
|
||||
#ifdef INSIDE_KERNEL_TREE
|
||||
#include <scst/scst_debug.h>
|
||||
#else
|
||||
#include <scst_debug.h>
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SCST_DEBUG
|
||||
#define ISERT_DEFAULT_LOG_FLAGS (TRACE_FUNCTION | TRACE_LINE | TRACE_PID | \
|
||||
TRACE_OUT_OF_MEM | TRACE_MGMT | TRACE_MGMT_DEBUG | \
|
||||
TRACE_MINOR | TRACE_SPECIAL)
|
||||
#else
|
||||
#define ISERT_DEFAULT_LOG_FLAGS (TRACE_OUT_OF_MEM | TRACE_MGMT | \
|
||||
TRACE_SPECIAL)
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
|
||||
extern unsigned long isert_trace_flag;
|
||||
#ifdef trace_flag
|
||||
#undef trace_flag
|
||||
#endif
|
||||
#define trace_flag isert_trace_flag
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,977 @@
|
||||
/*
|
||||
* This file is part of iser target kernel module.
|
||||
*
|
||||
* Copyright (c) 2013 - 2014 Mellanox Technologies. All rights reserved.
|
||||
* Copyright (c) 2013 - 2014 Yan Burman (yanb@mellanox.com)
|
||||
*
|
||||
* This software is available to you under a choice of one of two
|
||||
* licenses. You may choose to be licensed under the terms of the GNU
|
||||
* General Public License (GPL) Version 2, available from the file
|
||||
* COPYING in the main directory of this source tree, or the
|
||||
* OpenIB.org BSD license below:
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or
|
||||
* without modification, are permitted provided that the following
|
||||
* conditions are met:
|
||||
*
|
||||
* - Redistributions of source code must retain the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer.
|
||||
*
|
||||
* - Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h> /* everything... */
|
||||
#include <linux/errno.h> /* error codes */
|
||||
#include <linux/poll.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/file.h>
|
||||
|
||||
#ifdef INSIDE_KERNEL_TREE
|
||||
#include <scst/iscsi.h>
|
||||
#else
|
||||
#include "iscsi.h"
|
||||
#endif
|
||||
|
||||
#include "isert.h"
|
||||
#include "isert_dbg.h"
|
||||
#include "iser_datamover.h"
|
||||
|
||||
static unsigned int n_devs;
|
||||
|
||||
static int isert_major;
|
||||
|
||||
static struct isert_conn_dev *isert_conn_devices;
|
||||
|
||||
static struct isert_listener_dev isert_listen_dev;
|
||||
|
||||
static struct class *isert_class;
|
||||
|
||||
static struct isert_conn_dev *get_available_dev(struct isert_listener_dev *dev,
|
||||
struct iscsi_conn *conn)
|
||||
{
|
||||
unsigned int i;
|
||||
struct isert_conn_dev *res = NULL;
|
||||
|
||||
spin_lock(&dev->conn_lock);
|
||||
for (i = 0; i < n_devs; ++i) {
|
||||
if (!isert_conn_devices[i].occupied) {
|
||||
res = &isert_conn_devices[i];
|
||||
res->occupied = 1;
|
||||
res->conn = conn;
|
||||
isert_set_priv(conn, res);
|
||||
list_add(&res->conn_list_entry, &dev->new_conn_list);
|
||||
break;
|
||||
}
|
||||
}
|
||||
spin_unlock(&dev->conn_lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void isert_del_timer(struct isert_conn_dev *dev)
|
||||
{
|
||||
if (dev->timer_active) {
|
||||
del_timer_sync(&dev->tmo_timer);
|
||||
dev->timer_active = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void release_dev(struct isert_conn_dev *dev)
|
||||
{
|
||||
kref_init(&dev->kref);
|
||||
|
||||
spin_lock(&isert_listen_dev.conn_lock);
|
||||
dev->occupied = 0;
|
||||
list_del_init(&dev->conn_list_entry);
|
||||
dev->state = CS_INIT;
|
||||
atomic_set(&dev->available, 1);
|
||||
spin_unlock(&isert_listen_dev.conn_lock);
|
||||
}
|
||||
|
||||
static void isert_kref_release_dev(struct kref *kref)
|
||||
{
|
||||
struct isert_conn_dev *dev = container_of(kref,
|
||||
struct isert_conn_dev,
|
||||
kref);
|
||||
release_dev(dev);
|
||||
}
|
||||
|
||||
static void isert_dev_release(struct isert_conn_dev *dev)
|
||||
{
|
||||
kref_put(&dev->kref, isert_kref_release_dev);
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
|
||||
static void isert_close_conn_fn(void *ctx)
|
||||
#else
|
||||
static void isert_close_conn_fn(struct work_struct *work)
|
||||
#endif
|
||||
{
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
|
||||
struct iscsi_conn *conn = ctx;
|
||||
#else
|
||||
struct iscsi_conn *conn = container_of(work,
|
||||
struct iscsi_conn, close_work);
|
||||
#endif
|
||||
|
||||
isert_close_connection(conn);
|
||||
}
|
||||
|
||||
static void isert_conn_timer_fn(unsigned long arg)
|
||||
{
|
||||
struct isert_conn_dev *conn_dev = (struct isert_conn_dev *)arg;
|
||||
struct iscsi_conn *conn = conn_dev->conn;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
conn_dev->timer_active = 0;
|
||||
|
||||
PRINT_ERROR("Timeout on connection %p\n", conn_dev->conn);
|
||||
|
||||
schedule_work(&conn->close_work);
|
||||
|
||||
TRACE_EXIT();
|
||||
}
|
||||
|
||||
static int add_new_connection(struct isert_listener_dev *dev,
|
||||
struct iscsi_conn *conn)
|
||||
{
|
||||
struct isert_conn_dev *conn_dev = get_available_dev(dev, conn);
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (!conn_dev) {
|
||||
PRINT_WARNING("%s", "Unable to allocate new connection");
|
||||
res = -ENOSPC;
|
||||
goto out;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
|
||||
INIT_WORK(&conn->close_work, isert_close_conn_fn, conn);
|
||||
#else
|
||||
INIT_WORK(&conn->close_work, isert_close_conn_fn);
|
||||
#endif
|
||||
|
||||
init_timer(&conn_dev->tmo_timer);
|
||||
conn_dev->tmo_timer.function = isert_conn_timer_fn;
|
||||
conn_dev->tmo_timer.expires = jiffies + 120 * HZ;
|
||||
conn_dev->tmo_timer.data = (unsigned long)conn_dev;
|
||||
add_timer(&conn_dev->tmo_timer);
|
||||
conn_dev->timer_active = 1;
|
||||
wake_up(&dev->waitqueue);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool have_new_connection(struct isert_listener_dev *dev)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
spin_lock(&dev->conn_lock);
|
||||
ret = !list_empty(&dev->new_conn_list);
|
||||
spin_unlock(&dev->conn_lock);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int isert_conn_alloc(struct iscsi_session *session,
|
||||
struct iscsi_kern_conn_info *info,
|
||||
struct iscsi_conn **new_conn,
|
||||
struct iscsit_transport *t)
|
||||
{
|
||||
int res = 0;
|
||||
struct isert_conn_dev *dev;
|
||||
struct iscsi_conn *conn;
|
||||
struct iscsi_cmnd *cmnd;
|
||||
struct file *filp = fget(info->fd);
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
|
||||
lockdep_assert_held(&session->target->target_mutex);
|
||||
#endif
|
||||
|
||||
if (unlikely(!filp)) {
|
||||
res = -EBADF;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev = filp->private_data;
|
||||
|
||||
cmnd = dev->login_rsp;
|
||||
|
||||
sBUG_ON(cmnd == NULL);
|
||||
dev->login_rsp = NULL;
|
||||
|
||||
*new_conn = dev->conn;
|
||||
res = isert_set_session_params(dev->conn, &session->sess_params,
|
||||
&session->tgt_params);
|
||||
|
||||
if (!res)
|
||||
dev->conn = NULL;
|
||||
|
||||
fput(filp);
|
||||
|
||||
conn = *new_conn;
|
||||
|
||||
if (unlikely(res))
|
||||
goto cleanup_conn;
|
||||
|
||||
conn->transport = t;
|
||||
|
||||
res = iscsi_init_conn(session, info, conn);
|
||||
if (unlikely(res))
|
||||
goto cleanup_conn;
|
||||
|
||||
conn->rd_state = 1;
|
||||
isert_dev_release(dev);
|
||||
isert_set_priv(conn, NULL);
|
||||
|
||||
res = isert_login_rsp_tx(cmnd, true, false);
|
||||
vunmap(dev->sg_virt);
|
||||
dev->sg_virt = NULL;
|
||||
|
||||
if (unlikely(res))
|
||||
goto cleanup_iscsi_conn;
|
||||
|
||||
#ifndef CONFIG_SCST_PROC
|
||||
res = conn_sysfs_add(conn);
|
||||
if (unlikely(res))
|
||||
goto cleanup_iscsi_conn;
|
||||
#endif
|
||||
|
||||
list_add_tail(&conn->conn_list_entry, &session->conn_list);
|
||||
|
||||
goto out;
|
||||
|
||||
cleanup_iscsi_conn:
|
||||
conn->rd_state = 0;
|
||||
if (conn->nop_in_interval > 0)
|
||||
cancel_delayed_work_sync(&conn->nop_in_delayed_work);
|
||||
cleanup_conn:
|
||||
conn->session = NULL;
|
||||
isert_close_connection(conn);
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static unsigned int isert_listen_poll(struct file *filp,
|
||||
struct poll_table_struct *wait)
|
||||
{
|
||||
struct isert_listener_dev *dev = filp->private_data;
|
||||
unsigned int mask = 0;
|
||||
|
||||
poll_wait(filp, &dev->waitqueue, wait);
|
||||
|
||||
if (have_new_connection(dev))
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static int isert_listen_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct isert_listener_dev *dev;
|
||||
|
||||
dev = container_of(inode->i_cdev, struct isert_listener_dev, cdev);
|
||||
|
||||
if (!atomic_dec_and_test(&dev->available)) {
|
||||
atomic_inc(&dev->available);
|
||||
return -EBUSY; /* already open */
|
||||
}
|
||||
|
||||
filp->private_data = dev; /* for other methods */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int isert_listen_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct isert_listener_dev *dev = filp->private_data;
|
||||
struct isert_conn_dev *conn_dev;
|
||||
|
||||
/* No need for locking here, since the chardev is being closed */
|
||||
while (!list_empty(&dev->new_conn_list)) {
|
||||
conn_dev = list_first_entry(&dev->new_conn_list,
|
||||
struct isert_conn_dev,
|
||||
conn_list_entry);
|
||||
|
||||
isert_del_timer(conn_dev);
|
||||
if (conn_dev->conn) {
|
||||
isert_close_connection(conn_dev->conn);
|
||||
conn_dev->conn = NULL;
|
||||
}
|
||||
list_del(&conn_dev->conn_list_entry);
|
||||
}
|
||||
|
||||
atomic_inc(&dev->available);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t isert_listen_read(struct file *filp, char __user *buf,
|
||||
size_t count, loff_t *f_pos)
|
||||
{
|
||||
struct isert_listener_dev *dev = filp->private_data;
|
||||
struct isert_conn_dev *conn_dev;
|
||||
int res = 0;
|
||||
char k_buff[sizeof("/dev/") + sizeof(ISER_CONN_DEV_PREFIX) + 3 + 1];
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (!have_new_connection(dev)) {
|
||||
if (filp->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
res = wait_event_freezable(dev->waitqueue,
|
||||
!have_new_connection(dev));
|
||||
if (res < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
sBUG_ON(list_empty(&dev->new_conn_list));
|
||||
|
||||
spin_lock(&dev->conn_lock);
|
||||
conn_dev = list_first_entry(&dev->new_conn_list, struct isert_conn_dev,
|
||||
conn_list_entry);
|
||||
list_move(&conn_dev->conn_list_entry, &dev->curr_conn_list);
|
||||
kref_get(&conn_dev->kref);
|
||||
spin_unlock(&dev->conn_lock);
|
||||
|
||||
res = snprintf(k_buff, sizeof(k_buff), "/dev/"ISER_CONN_DEV_PREFIX"%d",
|
||||
conn_dev->idx);
|
||||
++res; /* copy trailing \0 as well */
|
||||
|
||||
if (copy_to_user(buf, k_buff, res))
|
||||
res = -EFAULT;
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static long isert_listen_ioctl(struct file *filp, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct isert_listener_dev *dev = filp->private_data;
|
||||
int res = 0, rc;
|
||||
void __user *ptr = (void __user *)arg;
|
||||
void *portal;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
switch (cmd) {
|
||||
case SET_LISTEN_ADDR:
|
||||
rc = copy_from_user(&dev->info, ptr, sizeof(dev->info));
|
||||
if (rc != 0) {
|
||||
PRINT_ERROR("Failed to copy %d user's bytes\n", rc);
|
||||
res = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (dev->free_portal_idx >= ISERT_MAX_PORTALS) {
|
||||
PRINT_ERROR("Maximum number of portals exceeded: %d\n",
|
||||
ISERT_MAX_PORTALS);
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
portal = isert_portal_add((struct sockaddr *)&dev->info.addr,
|
||||
dev->info.addr_len);
|
||||
if (!portal) {
|
||||
PRINT_ERROR("Unable to add portal of size %zu\n",
|
||||
dev->info.addr_len);
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
dev->portal_h[dev->free_portal_idx++] = portal;
|
||||
break;
|
||||
|
||||
default:
|
||||
PRINT_ERROR("Invalid ioctl cmd %x", cmd);
|
||||
res = -EINVAL;
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
int isert_conn_established(struct iscsi_conn *iscsi_conn,
|
||||
struct sockaddr *from_addr, int addr_len)
|
||||
{
|
||||
return add_new_connection(&isert_listen_dev, iscsi_conn);
|
||||
}
|
||||
|
||||
int isert_connection_closed(struct iscsi_conn *iscsi_conn)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (iscsi_conn->rd_state) {
|
||||
res = isert_handle_close_connection(iscsi_conn);
|
||||
} else {
|
||||
struct isert_conn_dev *dev = isert_get_priv(iscsi_conn);
|
||||
|
||||
if (dev) {
|
||||
isert_del_timer(dev);
|
||||
dev->state = CS_DISCONNECTED;
|
||||
if (dev->login_req) {
|
||||
res = isert_task_abort(dev->login_req);
|
||||
dev->login_req = NULL;
|
||||
}
|
||||
|
||||
dev->conn = NULL;
|
||||
wake_up(&dev->waitqueue);
|
||||
isert_dev_release(dev);
|
||||
}
|
||||
|
||||
isert_free_connection(iscsi_conn);
|
||||
}
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static bool will_read_block(struct isert_conn_dev *dev)
|
||||
{
|
||||
bool res;
|
||||
|
||||
spin_lock(&dev->pdu_lock);
|
||||
res = (dev->login_req == NULL) && (dev->state != CS_DISCONNECTED);
|
||||
spin_unlock(&dev->pdu_lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int isert_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct isert_conn_dev *dev; /* device information */
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
dev = container_of(inode->i_cdev, struct isert_conn_dev, cdev);
|
||||
|
||||
if (!atomic_dec_and_test(&dev->available)) {
|
||||
atomic_inc(&dev->available);
|
||||
res = -EBUSY; /* already open */
|
||||
goto out;
|
||||
}
|
||||
|
||||
filp->private_data = dev; /* for other methods */
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int isert_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct isert_conn_dev *dev = filp->private_data;
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
vunmap(dev->sg_virt);
|
||||
dev->sg_virt = NULL;
|
||||
dev->is_discovery = 0;
|
||||
|
||||
if (dev->conn) {
|
||||
isert_close_connection(dev->conn);
|
||||
dev->conn = NULL;
|
||||
}
|
||||
|
||||
isert_del_timer(dev);
|
||||
|
||||
isert_dev_release(dev);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static char *isert_vmap_sg(struct page **pages, struct scatterlist *sgl,
|
||||
int n_ents)
|
||||
{
|
||||
unsigned int i;
|
||||
struct scatterlist *sg;
|
||||
void *vaddr;
|
||||
|
||||
for_each_sg(sgl, sg, n_ents, i)
|
||||
pages[i] = sg_page(sg);
|
||||
|
||||
vaddr = vmap(pages, n_ents, 0, PAGE_KERNEL);
|
||||
|
||||
return vaddr;
|
||||
}
|
||||
|
||||
static ssize_t isert_read(struct file *filp, char __user *buf, size_t count,
|
||||
loff_t *f_pos)
|
||||
{
|
||||
struct isert_conn_dev *dev = filp->private_data;
|
||||
size_t to_read;
|
||||
|
||||
if (will_read_block(dev)) {
|
||||
int ret;
|
||||
if (filp->f_flags & O_NONBLOCK)
|
||||
return -EAGAIN;
|
||||
ret = wait_event_freezable(dev->waitqueue,
|
||||
!will_read_block(dev));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (dev->state == CS_DISCONNECTED)
|
||||
return -EPIPE;
|
||||
|
||||
to_read = min(count, dev->read_len);
|
||||
if (copy_to_user(buf, dev->read_buf, to_read))
|
||||
return -EFAULT;
|
||||
|
||||
dev->read_len -= to_read;
|
||||
dev->read_buf += to_read;
|
||||
|
||||
switch (dev->state) {
|
||||
case CS_REQ_BHS:
|
||||
if (dev->read_len == 0) {
|
||||
dev->read_len = dev->login_req->bufflen;
|
||||
dev->sg_virt = isert_vmap_sg(dev->pages,
|
||||
dev->login_req->sg,
|
||||
dev->login_req->sg_cnt);
|
||||
if (!dev->sg_virt)
|
||||
return -ENOMEM;
|
||||
dev->read_buf = dev->sg_virt + ISER_HDRS_SZ;
|
||||
dev->state = CS_REQ_DATA;
|
||||
}
|
||||
break;
|
||||
|
||||
case CS_REQ_DATA:
|
||||
if (dev->read_len == 0) {
|
||||
vunmap(dev->sg_virt);
|
||||
dev->sg_virt = NULL;
|
||||
|
||||
spin_lock(&dev->pdu_lock);
|
||||
dev->login_req = NULL;
|
||||
dev->state = CS_REQ_FINISHED;
|
||||
spin_unlock(&dev->pdu_lock);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
sBUG();
|
||||
}
|
||||
|
||||
return to_read;
|
||||
}
|
||||
|
||||
static ssize_t isert_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t *f_pos)
|
||||
{
|
||||
struct isert_conn_dev *dev = filp->private_data;
|
||||
size_t to_write;
|
||||
|
||||
if (dev->state == CS_DISCONNECTED)
|
||||
return -EPIPE;
|
||||
|
||||
to_write = min(count, dev->write_len);
|
||||
if (copy_from_user(dev->write_buf, buf, to_write))
|
||||
return -EFAULT;
|
||||
|
||||
dev->write_len -= to_write;
|
||||
dev->write_buf += to_write;
|
||||
|
||||
switch (dev->state) {
|
||||
case CS_RSP_BHS:
|
||||
if (dev->write_len == 0) {
|
||||
dev->state = CS_RSP_DATA;
|
||||
dev->sg_virt = isert_vmap_sg(dev->pages,
|
||||
dev->login_rsp->sg,
|
||||
dev->login_rsp->sg_cnt);
|
||||
if (!dev->sg_virt)
|
||||
return -ENOMEM;
|
||||
dev->write_buf = dev->sg_virt + ISER_HDRS_SZ;
|
||||
dev->write_len = dev->login_rsp->bufflen -
|
||||
sizeof(dev->login_rsp->pdu.bhs);
|
||||
iscsi_cmnd_get_length(&dev->login_rsp->pdu);
|
||||
}
|
||||
break;
|
||||
|
||||
case CS_RSP_DATA:
|
||||
break;
|
||||
|
||||
default:
|
||||
sBUG();
|
||||
}
|
||||
|
||||
return to_write;
|
||||
}
|
||||
|
||||
static bool is_last_login_rsp(struct iscsi_login_rsp_hdr *rsp)
|
||||
{
|
||||
return (rsp->flags & ISCSI_FLG_TRANSIT) &&
|
||||
((rsp->flags & ISCSI_FLG_NSG_MASK) == ISCSI_FLG_NSG_FULL_FEATURE);
|
||||
}
|
||||
|
||||
static long isert_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct isert_conn_dev *dev = filp->private_data;
|
||||
int res = 0, rc;
|
||||
int val;
|
||||
void __user *ptr = (void __user *)arg;
|
||||
struct iscsi_cmnd *cmnd;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (dev->state == CS_DISCONNECTED) {
|
||||
res = -EPIPE;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case RDMA_CORK:
|
||||
rc = copy_from_user(&val, ptr, sizeof(val));
|
||||
if (unlikely(rc != 0)) {
|
||||
PRINT_ERROR("Failed to copy %d user's bytes", rc);
|
||||
res = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
if (val) {
|
||||
if (!dev->login_rsp) {
|
||||
cmnd = isert_alloc_login_rsp_pdu(dev->conn);
|
||||
if (!cmnd) {
|
||||
res = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
dev->login_rsp = cmnd;
|
||||
dev->write_buf = (char *)&cmnd->pdu.bhs;
|
||||
dev->write_len = sizeof(cmnd->pdu.bhs);
|
||||
dev->state = CS_RSP_BHS;
|
||||
}
|
||||
} else {
|
||||
struct iscsi_login_rsp_hdr *rsp;
|
||||
bool last;
|
||||
|
||||
if (!dev->login_rsp) {
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev->state = CS_RSP_FINISHED;
|
||||
rsp = (struct iscsi_login_rsp_hdr *)(&dev->login_rsp->pdu.bhs);
|
||||
last = is_last_login_rsp(rsp);
|
||||
|
||||
dev->login_rsp->bufflen -= dev->write_len;
|
||||
|
||||
if (!last || dev->is_discovery) {
|
||||
res = isert_login_rsp_tx(dev->login_rsp,
|
||||
last,
|
||||
dev->is_discovery);
|
||||
vunmap(dev->sg_virt);
|
||||
dev->sg_virt = NULL;
|
||||
dev->login_rsp = NULL;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case GET_PORTAL_ADDR:
|
||||
{
|
||||
struct isert_addr_info addr;
|
||||
|
||||
res = isert_get_target_addr(dev->conn,
|
||||
(struct sockaddr *)&addr.addr,
|
||||
&addr.addr_len);
|
||||
if (unlikely(res))
|
||||
goto out;
|
||||
|
||||
rc = copy_to_user(ptr, &addr, sizeof(addr));
|
||||
if (rc)
|
||||
res = -EFAULT;
|
||||
}
|
||||
break;
|
||||
|
||||
case DISCOVERY_SESSION:
|
||||
rc = copy_from_user(&val, ptr, sizeof(val));
|
||||
if (unlikely(rc != 0)) {
|
||||
PRINT_ERROR("Failed to copy %d user's bytes", rc);
|
||||
res = -EFAULT;
|
||||
goto out;
|
||||
}
|
||||
dev->is_discovery = val;
|
||||
break;
|
||||
|
||||
default:
|
||||
PRINT_ERROR("Invalid ioctl cmd %x", cmd);
|
||||
res = -EINVAL;
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static unsigned int isert_poll(struct file *filp,
|
||||
struct poll_table_struct *wait)
|
||||
{
|
||||
struct isert_conn_dev *dev = filp->private_data;
|
||||
unsigned int mask = 0;
|
||||
|
||||
poll_wait(filp, &dev->waitqueue, wait);
|
||||
|
||||
if (!dev->conn)
|
||||
mask |= POLLHUP | POLLERR;
|
||||
if (!will_read_block(dev))
|
||||
mask |= POLLIN | POLLRDNORM;
|
||||
|
||||
mask |= POLLOUT | POLLWRNORM;
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
int isert_login_req_rx(struct iscsi_cmnd *login_req)
|
||||
{
|
||||
struct isert_conn_dev *dev = isert_get_priv(login_req->conn);
|
||||
int res = 0;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
if (!dev) {
|
||||
PRINT_ERROR("Received PDU %p on invalid connection",
|
||||
login_req);
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (dev->state) {
|
||||
case CS_INIT:
|
||||
case CS_RSP_FINISHED:
|
||||
if (dev->login_req != NULL) {
|
||||
sBUG();
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
|
||||
case CS_REQ_BHS: /* Got login request before done handling old one */
|
||||
break;
|
||||
|
||||
case CS_REQ_DATA:
|
||||
case CS_REQ_FINISHED:
|
||||
case CS_RSP_BHS:
|
||||
case CS_RSP_DATA:
|
||||
PRINT_WARNING("Received login PDU while handling previous one. State:%d",
|
||||
dev->state);
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
|
||||
default:
|
||||
sBUG();
|
||||
res = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
||||
spin_lock(&dev->pdu_lock);
|
||||
dev->login_req = login_req;
|
||||
dev->read_len = sizeof(login_req->pdu.bhs);
|
||||
dev->read_buf = (char *)&login_req->pdu.bhs;
|
||||
dev->state = CS_REQ_BHS;
|
||||
spin_unlock(&dev->pdu_lock);
|
||||
|
||||
wake_up(&dev->waitqueue);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
static dev_t devno;
|
||||
|
||||
static const struct file_operations listener_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.read = isert_listen_read,
|
||||
.unlocked_ioctl = isert_listen_ioctl,
|
||||
.compat_ioctl = isert_listen_ioctl,
|
||||
.poll = isert_listen_poll,
|
||||
.open = isert_listen_open,
|
||||
.release = isert_listen_release,
|
||||
};
|
||||
|
||||
static const struct file_operations conn_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.read = isert_read,
|
||||
.write = isert_write,
|
||||
.unlocked_ioctl = isert_ioctl,
|
||||
.compat_ioctl = isert_ioctl,
|
||||
.poll = isert_poll,
|
||||
.open = isert_open,
|
||||
.release = isert_release,
|
||||
};
|
||||
|
||||
static void __init isert_setup_cdev(struct isert_conn_dev *dev,
|
||||
unsigned int index)
|
||||
{
|
||||
int err;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
dev->devno = MKDEV(isert_major, index + 1);
|
||||
|
||||
cdev_init(&dev->cdev, &conn_fops);
|
||||
dev->cdev.owner = THIS_MODULE;
|
||||
dev->cdev.ops = &conn_fops;
|
||||
dev->idx = index;
|
||||
init_waitqueue_head(&dev->waitqueue);
|
||||
dev->login_req = NULL;
|
||||
dev->login_rsp = NULL;
|
||||
spin_lock_init(&dev->pdu_lock);
|
||||
atomic_set(&dev->available, 1);
|
||||
kref_init(&dev->kref);
|
||||
dev->state = CS_INIT;
|
||||
err = cdev_add(&dev->cdev, dev->devno, 1);
|
||||
/* Fail gracefully if need be */
|
||||
if (err)
|
||||
PRINT_ERROR("Error %d adding "ISER_CONN_DEV_PREFIX"%d", err,
|
||||
index);
|
||||
|
||||
dev->dev = device_create(isert_class, NULL, dev->devno, NULL,
|
||||
ISER_CONN_DEV_PREFIX"%d", index);
|
||||
|
||||
TRACE_EXIT();
|
||||
}
|
||||
|
||||
static void __init isert_setup_listener_cdev(struct isert_listener_dev *dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
dev->devno = MKDEV(isert_major, 0);
|
||||
|
||||
cdev_init(&dev->cdev, &listener_fops);
|
||||
dev->cdev.owner = THIS_MODULE;
|
||||
dev->cdev.ops = &listener_fops;
|
||||
init_waitqueue_head(&dev->waitqueue);
|
||||
INIT_LIST_HEAD(&dev->new_conn_list);
|
||||
INIT_LIST_HEAD(&dev->curr_conn_list);
|
||||
spin_lock_init(&dev->conn_lock);
|
||||
atomic_set(&dev->available, 1);
|
||||
err = cdev_add(&dev->cdev, dev->devno, 1);
|
||||
/* Fail gracefully if need be */
|
||||
if (err)
|
||||
PRINT_ERROR("Error %d adding isert_scst", err);
|
||||
|
||||
dev->dev = device_create(isert_class, NULL, dev->devno, NULL,
|
||||
"isert_scst");
|
||||
|
||||
TRACE_EXIT();
|
||||
}
|
||||
|
||||
int __init isert_init_login_devs(unsigned int ndevs)
|
||||
{
|
||||
int res;
|
||||
unsigned int i;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
n_devs = ndevs;
|
||||
|
||||
res = alloc_chrdev_region(&devno, 0, n_devs,
|
||||
"isert_scst");
|
||||
isert_major = MAJOR(devno);
|
||||
|
||||
if (res < 0) {
|
||||
PRINT_ERROR("isert: can't get major %d\n", isert_major);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* allocate the devices -- we can't have them static, as the number
|
||||
* can be specified at load time
|
||||
*/
|
||||
isert_conn_devices = kzalloc(n_devs * sizeof(struct isert_conn_dev),
|
||||
GFP_KERNEL);
|
||||
if (!isert_conn_devices) {
|
||||
res = -ENOMEM;
|
||||
goto fail; /* Make this more graceful */
|
||||
}
|
||||
|
||||
isert_class = class_create(THIS_MODULE, "isert_scst");
|
||||
|
||||
isert_setup_listener_cdev(&isert_listen_dev);
|
||||
|
||||
/* Initialize each device. */
|
||||
for (i = 0; i < n_devs; i++)
|
||||
isert_setup_cdev(&isert_conn_devices[i], i);
|
||||
|
||||
res = isert_datamover_init();
|
||||
if (res) {
|
||||
PRINT_ERROR("Unable to initialize datamover: %d\n", res);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
fail:
|
||||
isert_cleanup_login_devs();
|
||||
goto out;
|
||||
}
|
||||
|
||||
void isert_close_all_portals(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < isert_listen_dev.free_portal_idx; ++i)
|
||||
isert_portal_remove(isert_listen_dev.portal_h[i]);
|
||||
isert_listen_dev.free_portal_idx = 0;
|
||||
}
|
||||
|
||||
void isert_cleanup_login_devs(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
isert_close_all_portals();
|
||||
|
||||
isert_datamover_cleanup();
|
||||
|
||||
if (isert_conn_devices) {
|
||||
for (i = 0; i < n_devs; i++) {
|
||||
device_destroy(isert_class,
|
||||
isert_conn_devices[i].devno);
|
||||
cdev_del(&isert_conn_devices[i].cdev);
|
||||
}
|
||||
kfree(isert_conn_devices);
|
||||
}
|
||||
|
||||
device_destroy(isert_class, isert_listen_dev.devno);
|
||||
cdev_del(&isert_listen_dev.cdev);
|
||||
|
||||
if (isert_class)
|
||||
class_destroy(isert_class);
|
||||
|
||||
unregister_chrdev_region(devno, n_devs);
|
||||
|
||||
TRACE_EXIT();
|
||||
}
|
||||
+26
-22
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "iscsi.h"
|
||||
#include "digest.h"
|
||||
#include "iscsit_transport.h"
|
||||
|
||||
/* Read data states */
|
||||
enum rx_state {
|
||||
@@ -407,10 +408,10 @@ static void close_conn(struct iscsi_conn *conn)
|
||||
|
||||
if (conn->active_close) {
|
||||
/* We want all our already send operations to complete */
|
||||
conn->sock->ops->shutdown(conn->sock, RCV_SHUTDOWN);
|
||||
conn->transport->iscsit_conn_close(conn, RCV_SHUTDOWN);
|
||||
} else {
|
||||
conn->sock->ops->shutdown(conn->sock,
|
||||
RCV_SHUTDOWN|SEND_SHUTDOWN);
|
||||
conn->transport->iscsit_conn_close(conn,
|
||||
RCV_SHUTDOWN|SEND_SHUTDOWN);
|
||||
}
|
||||
|
||||
mutex_lock(&session->target->target_mutex);
|
||||
@@ -486,15 +487,13 @@ static void close_conn(struct iscsi_conn *conn)
|
||||
}
|
||||
}
|
||||
|
||||
iscsi_make_conn_wr_active(conn);
|
||||
conn->transport->iscsit_make_conn_wr_active(conn);
|
||||
|
||||
/* That's for active close only, actually */
|
||||
if (time_after(jiffies, start_waiting + CONN_WAIT_TIMEOUT) &&
|
||||
!wait_expired) {
|
||||
TRACE_CONN_CLOSE("Wait time expired (conn %p, "
|
||||
"sk_state %d)",
|
||||
conn, conn->sock->sk->sk_state);
|
||||
conn->sock->ops->shutdown(conn->sock, SEND_SHUTDOWN);
|
||||
TRACE_CONN_CLOSE("Wait time expired (conn %p)", conn);
|
||||
conn->transport->iscsit_conn_close(conn, SEND_SHUTDOWN);
|
||||
wait_expired = 1;
|
||||
shut_start_waiting = jiffies;
|
||||
}
|
||||
@@ -504,9 +503,8 @@ static void close_conn(struct iscsi_conn *conn)
|
||||
conn->deleting ? CONN_DEL_SHUT_TIMEOUT :
|
||||
CONN_REG_SHUT_TIMEOUT)) {
|
||||
TRACE_CONN_CLOSE("Wait time after shutdown expired "
|
||||
"(conn %p, sk_state %d)", conn,
|
||||
conn->sock->sk->sk_state);
|
||||
conn->sock->sk->sk_prot->disconnect(conn->sock->sk, 0);
|
||||
"(conn %p)", conn);
|
||||
conn->transport->iscsit_conn_close(conn, 0);
|
||||
shut_expired = 1;
|
||||
}
|
||||
|
||||
@@ -522,17 +520,21 @@ static void close_conn(struct iscsi_conn *conn)
|
||||
|
||||
trace_conn_close(conn);
|
||||
|
||||
/* It might never be called for being closed conn */
|
||||
__iscsi_write_space_ready(conn);
|
||||
if (!conn->session->sess_params.rdma_extensions) {
|
||||
/* It might never be called for being closed conn */
|
||||
__iscsi_write_space_ready(conn);
|
||||
|
||||
iscsi_check_closewait(conn);
|
||||
iscsi_check_closewait(conn);
|
||||
}
|
||||
}
|
||||
|
||||
write_lock_bh(&conn->sock->sk->sk_callback_lock);
|
||||
conn->sock->sk->sk_state_change = conn->old_state_change;
|
||||
conn->sock->sk->sk_data_ready = conn->old_data_ready;
|
||||
conn->sock->sk->sk_write_space = conn->old_write_space;
|
||||
write_unlock_bh(&conn->sock->sk->sk_callback_lock);
|
||||
if (!conn->session->sess_params.rdma_extensions) {
|
||||
write_lock_bh(&conn->sock->sk->sk_callback_lock);
|
||||
conn->sock->sk->sk_state_change = conn->old_state_change;
|
||||
conn->sock->sk->sk_data_ready = conn->old_data_ready;
|
||||
conn->sock->sk->sk_write_space = conn->old_write_space;
|
||||
write_unlock_bh(&conn->sock->sk->sk_callback_lock);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
bool t;
|
||||
@@ -595,7 +597,7 @@ static int close_conn_thr(void *arg)
|
||||
}
|
||||
|
||||
/* No locks */
|
||||
static void start_close_conn(struct iscsi_conn *conn)
|
||||
void start_close_conn(struct iscsi_conn *conn)
|
||||
{
|
||||
struct task_struct *t;
|
||||
|
||||
@@ -611,6 +613,7 @@ static void start_close_conn(struct iscsi_conn *conn)
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(start_close_conn);
|
||||
|
||||
static inline void iscsi_conn_init_read(struct iscsi_conn *conn,
|
||||
void __user *data, size_t len)
|
||||
@@ -635,7 +638,7 @@ static void iscsi_conn_prepare_read_ahs(struct iscsi_conn *conn,
|
||||
return;
|
||||
}
|
||||
|
||||
static struct iscsi_cmnd *iscsi_get_send_cmnd(struct iscsi_conn *conn)
|
||||
struct iscsi_cmnd *iscsi_get_send_cmnd(struct iscsi_conn *conn)
|
||||
{
|
||||
struct iscsi_cmnd *cmnd = NULL;
|
||||
|
||||
@@ -674,6 +677,7 @@ static struct iscsi_cmnd *iscsi_get_send_cmnd(struct iscsi_conn *conn)
|
||||
out:
|
||||
return cmnd;
|
||||
}
|
||||
EXPORT_SYMBOL(iscsi_get_send_cmnd);
|
||||
|
||||
/* Returns number of bytes left to receive or <0 for error */
|
||||
static int do_recv(struct iscsi_conn *conn)
|
||||
@@ -831,7 +835,7 @@ static int process_read_io(struct iscsi_conn *conn, int *closed)
|
||||
switch (conn->read_state) {
|
||||
case RX_INIT_BHS:
|
||||
EXTRACHECKS_BUG_ON(conn->read_cmnd != NULL);
|
||||
cmnd = cmnd_alloc(conn, NULL);
|
||||
cmnd = conn->transport->iscsit_alloc_cmd(conn, NULL);
|
||||
conn->read_cmnd = cmnd;
|
||||
iscsi_conn_init_read(cmnd->conn,
|
||||
(void __force __user *)&cmnd->pdu.bhs,
|
||||
|
||||
@@ -98,12 +98,13 @@ static void log_params(struct iscsi_sess_params *params)
|
||||
iscsi_get_bool_value(params->data_sequence_inorder),
|
||||
params->error_recovery_level);
|
||||
PRINT_INFO(" HeaderDigest %s, DataDigest %s, OFMarker %s, "
|
||||
"IFMarker %s, OFMarkInt %d, IFMarkInt %d",
|
||||
"IFMarker %s, OFMarkInt %d, IFMarkInt %d, RDMAExtensions %s",
|
||||
iscsi_get_digest_name(params->header_digest, hdigest_name),
|
||||
iscsi_get_digest_name(params->data_digest, ddigest_name),
|
||||
iscsi_get_bool_value(params->ofmarker),
|
||||
iscsi_get_bool_value(params->ifmarker),
|
||||
params->ofmarkint, params->ifmarkint);
|
||||
params->ofmarkint, params->ifmarkint,
|
||||
iscsi_get_bool_value(params->rdma_extensions));
|
||||
}
|
||||
|
||||
/* target_mutex supposed to be locked */
|
||||
@@ -135,6 +136,11 @@ static void sess_params_check(struct iscsi_kern_params_info *info)
|
||||
CHECK_PARAM(info, iparams, ofmarker, 0, 0);
|
||||
CHECK_PARAM(info, iparams, ifmarker, 0, 0);
|
||||
|
||||
/* iSER related parameters */
|
||||
CHECK_PARAM(info, iparams, rdma_extensions, 0, 1);
|
||||
CHECK_PARAM(info, iparams, target_recv_data_length, 512, max_len);
|
||||
CHECK_PARAM(info, iparams, initiator_recv_data_length, 512, max_len);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -163,6 +169,11 @@ static void sess_params_set(struct iscsi_sess_params *params,
|
||||
SET_PARAM(params, info, iparams, ifmarker);
|
||||
SET_PARAM(params, info, iparams, ofmarkint);
|
||||
SET_PARAM(params, info, iparams, ifmarkint);
|
||||
|
||||
/* iSER related parameters */
|
||||
SET_PARAM(params, info, iparams, rdma_extensions);
|
||||
SET_PARAM(params, info, iparams, target_recv_data_length);
|
||||
SET_PARAM(params, info, iparams, initiator_recv_data_length);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -190,6 +201,11 @@ static void sess_params_get(struct iscsi_sess_params *params,
|
||||
GET_PARAM(params, info, iparams, ifmarker);
|
||||
GET_PARAM(params, info, iparams, ofmarkint);
|
||||
GET_PARAM(params, info, iparams, ifmarkint);
|
||||
|
||||
/* iSER related parameters */
|
||||
GET_PARAM(params, info, iparams, rdma_extensions);
|
||||
GET_PARAM(params, info, iparams, target_recv_data_length);
|
||||
GET_PARAM(params, info, iparams, initiator_recv_data_length);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -345,9 +345,11 @@ void target_del_all_sess(struct iscsi_target *target, int flags)
|
||||
TRACE_EXIT();
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL(target_del_all_sess);
|
||||
|
||||
void target_del_all(void)
|
||||
{
|
||||
struct iscsit_transport *transport;
|
||||
struct iscsi_target *target, *t;
|
||||
bool first = true;
|
||||
|
||||
@@ -355,6 +357,14 @@ void target_del_all(void)
|
||||
|
||||
TRACE_MGMT_DBG("%s", "Deleting all targets");
|
||||
|
||||
transport = iscsit_get_transport(ISCSI_TCP);
|
||||
if (transport && transport->iscsit_close_all_portals)
|
||||
transport->iscsit_close_all_portals();
|
||||
|
||||
transport = iscsit_get_transport(ISCSI_RDMA);
|
||||
if (transport && transport->iscsit_close_all_portals)
|
||||
transport->iscsit_close_all_portals();
|
||||
|
||||
/* Not the best, ToDo */
|
||||
while (1) {
|
||||
mutex_lock(&target_mgmt_mutex);
|
||||
|
||||
+4
-47
@@ -27,59 +27,17 @@
|
||||
|
||||
#include "iscsid.h"
|
||||
|
||||
#define CTL_DEVICE "/dev/iscsi-scst-ctl"
|
||||
#define CTL_DEVICE "iscsi-scst-ctl"
|
||||
|
||||
int kernel_open(void)
|
||||
{
|
||||
FILE *f;
|
||||
char devname[256];
|
||||
char buf[256];
|
||||
int devn;
|
||||
int ctlfd = -1;
|
||||
int err;
|
||||
struct iscsi_kern_register_info reg;
|
||||
|
||||
if (!(f = fopen("/proc/devices", "r"))) {
|
||||
err = -errno;
|
||||
perror("Cannot open control path to the driver");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
devn = 0;
|
||||
while (!feof(f)) {
|
||||
if (!fgets(buf, sizeof(buf), f)) {
|
||||
break;
|
||||
}
|
||||
if (sscanf(buf, "%d %s", &devn, devname) != 2) {
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(devname, "iscsi-scst-ctl")) {
|
||||
break;
|
||||
}
|
||||
devn = 0;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
if (!devn) {
|
||||
err = -ENOENT;
|
||||
printf("cannot find iscsictl in /proc/devices - "
|
||||
"make sure the module is loaded\n");
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
unlink(CTL_DEVICE);
|
||||
if (mknod(CTL_DEVICE, (S_IFCHR | 0600), (devn << 8))) {
|
||||
err = -errno;
|
||||
printf("cannot create %s %s\n", CTL_DEVICE, strerror(errno));
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
ctlfd = open(CTL_DEVICE, O_RDWR);
|
||||
if (ctlfd < 0) {
|
||||
err = -errno;
|
||||
printf("cannot open %s %s\n", CTL_DEVICE, strerror(errno));
|
||||
goto out_err;
|
||||
}
|
||||
ctlfd = create_and_open_dev(CTL_DEVICE, 0);
|
||||
if (ctlfd < 0)
|
||||
goto out;
|
||||
|
||||
memset(®, 0, sizeof(reg));
|
||||
reg.version = (uintptr_t)ISCSI_SCST_INTERFACE_VERSION;
|
||||
@@ -103,7 +61,6 @@ out:
|
||||
out_close:
|
||||
close(ctlfd);
|
||||
|
||||
out_err:
|
||||
ctlfd = err;
|
||||
goto out;
|
||||
}
|
||||
|
||||
+233
-27
@@ -30,6 +30,7 @@
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
@@ -169,6 +170,224 @@ static void create_listen_socket(struct pollfd *array)
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static struct connection *alloc_and_init_conn(int fd)
|
||||
{
|
||||
struct pollfd *pollfd;
|
||||
struct connection *conn = NULL;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < INCOMING_MAX; i++) {
|
||||
if (!incoming[i])
|
||||
break;
|
||||
}
|
||||
if (i >= INCOMING_MAX) {
|
||||
log_error("Unable to find incoming slot? %d\n", i);
|
||||
goto out;
|
||||
}
|
||||
|
||||
conn = conn_alloc();
|
||||
if (!conn) {
|
||||
log_error("Fail to allocate %s", "conn\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
conn->fd = fd;
|
||||
incoming[i] = conn;
|
||||
|
||||
pollfd = &poll_array[POLL_INCOMING + i];
|
||||
pollfd->fd = fd;
|
||||
pollfd->events = POLLIN;
|
||||
pollfd->revents = 0;
|
||||
|
||||
conn_read_pdu(conn);
|
||||
set_non_blocking(fd);
|
||||
|
||||
out:
|
||||
return conn;
|
||||
}
|
||||
|
||||
static int transmit_iser(int fd, bool start)
|
||||
{
|
||||
int opt = start;
|
||||
return ioctl(fd, RDMA_CORK, &opt, sizeof(opt));
|
||||
}
|
||||
|
||||
static int cork_transmit_iser(int fd)
|
||||
{
|
||||
return transmit_iser(fd, true);
|
||||
}
|
||||
|
||||
static int uncork_transmit_iser(int fd)
|
||||
{
|
||||
return transmit_iser(fd, false);
|
||||
}
|
||||
|
||||
static void create_iser_listen_socket(struct pollfd *array)
|
||||
{
|
||||
struct addrinfo hints, *res, *res0;
|
||||
char servname[64];
|
||||
int rc, i;
|
||||
int iser_fd;
|
||||
struct isert_addr_info info;
|
||||
|
||||
iser_fd = create_and_open_dev("isert_scst", 1);
|
||||
|
||||
poll_array[POLL_ISER_LISTEN].fd = iser_fd;
|
||||
if (iser_fd != -1) {
|
||||
poll_array[POLL_ISER_LISTEN].events = POLLIN;
|
||||
|
||||
/* RDMAExtensions */
|
||||
session_keys[key_rdma_extensions].max = 1;
|
||||
session_keys[key_rdma_extensions].local_def = 1;
|
||||
} else {
|
||||
poll_array[POLL_ISER_LISTEN].events = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
memset(servname, 0, sizeof(servname));
|
||||
snprintf(servname, sizeof(servname), "%d", server_port);
|
||||
|
||||
memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
rc = getaddrinfo(server_address, servname, &hints, &res0);
|
||||
if (rc != 0) {
|
||||
log_error("Unable to get address info (%s)!",
|
||||
get_error_str(rc));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for (res = res0; res && i < ISERT_MAX_PORTALS; res = res->ai_next) {
|
||||
memcpy(&info.addr, res->ai_addr, res->ai_addrlen);
|
||||
info.addr_len = res->ai_addrlen;
|
||||
|
||||
rc = ioctl(iser_fd, SET_LISTEN_ADDR, &info);
|
||||
if (rc != 0) {
|
||||
log_error("Unable to set address info (%s)!",
|
||||
strerror(rc));
|
||||
}
|
||||
++i;
|
||||
}
|
||||
|
||||
freeaddrinfo(res0);
|
||||
}
|
||||
|
||||
static int iser_getsockname(int fd, struct sockaddr *name, socklen_t *namelen)
|
||||
{
|
||||
struct isert_addr_info addr;
|
||||
int ret;
|
||||
|
||||
ret = ioctl(fd, GET_PORTAL_ADDR, &addr, sizeof(addr));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
memcpy(name, &addr.addr, addr.addr_len);
|
||||
*namelen = addr.addr_len;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iser_is_discovery(int fd)
|
||||
{
|
||||
int val = 1;
|
||||
|
||||
return ioctl(fd, DISCOVERY_SESSION, &val, sizeof(val));
|
||||
}
|
||||
|
||||
static void iser_accept(int fd)
|
||||
{
|
||||
char buff[256];
|
||||
int ret, conn_fd;
|
||||
struct connection *conn;
|
||||
char target_portal[ISCSI_PORTAL_LEN], target_portal_port[NI_MAXSERV];
|
||||
struct isert_addr_info addr;
|
||||
|
||||
ret = read(fd, buff, sizeof(buff));
|
||||
if (ret == -1)
|
||||
goto out;
|
||||
|
||||
conn_fd = open(buff, O_RDWR);
|
||||
if (conn_fd == -1) {
|
||||
log_error("open(iser_connection) %s failed: %s\n",
|
||||
buff, strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = ioctl(conn_fd, GET_PORTAL_ADDR, &addr, sizeof(addr));
|
||||
if (ret) {
|
||||
log_error("ioctl(GET_PORTAL_ADDR) failed: %s\n",
|
||||
strerror(errno));
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
ret = getnameinfo((struct sockaddr *)&addr, sizeof(addr), target_portal,
|
||||
sizeof(target_portal), target_portal_port,
|
||||
sizeof(target_portal_port),
|
||||
NI_NUMERICHOST | NI_NUMERICSERV);
|
||||
if (ret != 0) {
|
||||
log_error("Target portal getnameinfo() failed: %s!",
|
||||
get_error_str(ret));
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
log_info("iSER Connect to %s:%s", target_portal, target_portal_port);
|
||||
|
||||
if (conn_blocked) {
|
||||
log_warning("Connection refused due to blocking\n");
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
conn = alloc_and_init_conn(conn_fd);
|
||||
if (!conn)
|
||||
goto out_close;
|
||||
|
||||
conn->target_portal = strdup(target_portal);
|
||||
if (conn->target_portal == NULL) {
|
||||
log_error("Unable to duplicate target portal %s", target_portal);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
conn->cork_transmit = cork_transmit_iser;
|
||||
conn->uncork_transmit = uncork_transmit_iser;
|
||||
conn->getsockname = iser_getsockname;
|
||||
conn->is_discovery = iser_is_discovery;
|
||||
conn->is_iser = true;
|
||||
incoming_cnt++;
|
||||
|
||||
out:
|
||||
return;
|
||||
|
||||
out_free:
|
||||
conn_free(conn);
|
||||
|
||||
out_close:
|
||||
close(conn_fd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int transmit_sock(int fd, bool start)
|
||||
{
|
||||
int opt = start;
|
||||
return setsockopt(fd, SOL_TCP, TCP_CORK, &opt, sizeof(opt));
|
||||
}
|
||||
|
||||
static int cork_transmit_sock(int fd)
|
||||
{
|
||||
return transmit_sock(fd, true);
|
||||
}
|
||||
|
||||
static int uncork_transmit_sock(int fd)
|
||||
{
|
||||
return transmit_sock(fd, false);
|
||||
}
|
||||
|
||||
static int tcp_is_discovery(int fd)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void accept_connection(int listen)
|
||||
{
|
||||
union {
|
||||
@@ -177,9 +396,8 @@ static void accept_connection(int listen)
|
||||
struct sockaddr_in6 sin6;
|
||||
} from, to;
|
||||
socklen_t namesize;
|
||||
struct pollfd *pollfd;
|
||||
struct connection *conn;
|
||||
int fd, i, rc;
|
||||
int fd, rc;
|
||||
char initiator_addr[ISCSI_PORTAL_LEN], initiator_port[NI_MAXSERV];
|
||||
char target_portal[ISCSI_PORTAL_LEN], target_portal_port[NI_MAXSERV];
|
||||
|
||||
@@ -238,36 +456,22 @@ static void accept_connection(int listen)
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
for (i = 0; i < INCOMING_MAX; i++) {
|
||||
if (!incoming[i])
|
||||
break;
|
||||
}
|
||||
if (i >= INCOMING_MAX) {
|
||||
log_error("Unable to find incoming slot? %d\n", i);
|
||||
conn = alloc_and_init_conn(fd);
|
||||
if (!conn)
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
if (!(conn = conn_alloc())) {
|
||||
log_error("Fail to allocate %s", "conn\n");
|
||||
goto out_close;
|
||||
}
|
||||
|
||||
conn->fd = fd;
|
||||
conn->target_portal = strdup(target_portal);
|
||||
if (conn->target_portal == NULL) {
|
||||
log_error("Unable to duplicate target portal %s", target_portal);
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
incoming[i] = conn;
|
||||
conn->cork_transmit = cork_transmit_sock;
|
||||
conn->uncork_transmit = uncork_transmit_sock;
|
||||
conn->getsockname = getsockname;
|
||||
conn->is_discovery = tcp_is_discovery;
|
||||
conn_read_pdu(conn);
|
||||
|
||||
set_non_blocking(fd);
|
||||
pollfd = &poll_array[POLL_INCOMING + i];
|
||||
pollfd->fd = fd;
|
||||
pollfd->events = POLLIN;
|
||||
pollfd->revents = 0;
|
||||
|
||||
incoming_cnt++;
|
||||
|
||||
out:
|
||||
@@ -296,7 +500,7 @@ void isns_set_fd(int isns, int scn_listen, int scn)
|
||||
|
||||
static void event_conn(struct connection *conn, struct pollfd *pollfd)
|
||||
{
|
||||
int res, opt;
|
||||
int res;
|
||||
|
||||
again:
|
||||
switch (conn->iostate) {
|
||||
@@ -367,8 +571,7 @@ again:
|
||||
case IOSTATE_WRITE_AHS:
|
||||
case IOSTATE_WRITE_DATA:
|
||||
write_again:
|
||||
opt = 1;
|
||||
setsockopt(pollfd->fd, SOL_TCP, TCP_CORK, &opt, sizeof(opt));
|
||||
conn->cork_transmit(pollfd->fd);
|
||||
res = write(pollfd->fd, conn->buffer, conn->rwsize);
|
||||
if (res < 0) {
|
||||
if (errno != EINTR && errno != EAGAIN) {
|
||||
@@ -408,8 +611,7 @@ again:
|
||||
goto write_again;
|
||||
}
|
||||
case IOSTATE_WRITE_DATA:
|
||||
opt = 0;
|
||||
setsockopt(pollfd->fd, SOL_TCP, TCP_CORK, &opt, sizeof(opt));
|
||||
conn->uncork_transmit(pollfd->fd);
|
||||
cmnd_finish(conn);
|
||||
|
||||
switch (conn->state) {
|
||||
@@ -447,6 +649,7 @@ static void event_loop(void)
|
||||
int res, i;
|
||||
|
||||
create_listen_socket(poll_array + POLL_LISTEN);
|
||||
create_iser_listen_socket(poll_array);
|
||||
|
||||
poll_array[POLL_IPC].fd = ipc_fd;
|
||||
poll_array[POLL_IPC].events = POLLIN;
|
||||
@@ -520,6 +723,9 @@ static void event_loop(void)
|
||||
if (poll_array[POLL_SCN].revents)
|
||||
isns_scn_handle(0);
|
||||
|
||||
if (poll_array[POLL_ISER_LISTEN].revents)
|
||||
iser_accept(poll_array[POLL_ISER_LISTEN].fd);
|
||||
|
||||
for (i = 0; i < INCOMING_MAX; i++) {
|
||||
struct connection *conn = incoming[i];
|
||||
struct pollfd *pollfd = &poll_array[POLL_INCOMING + i];
|
||||
|
||||
@@ -41,6 +41,10 @@ static struct iscsi_key login_keys[] = {
|
||||
{"InitiatorAlias",},
|
||||
{"SessionType",},
|
||||
{"TargetName",},
|
||||
{"InitiatorRecvDataSegmentLength",},
|
||||
{"MaxAHSLength",},
|
||||
{"TaggedBufferForSolicitedDataOnly",},
|
||||
{"iSERHelloRequired",},
|
||||
{NULL,},
|
||||
};
|
||||
|
||||
@@ -369,6 +373,26 @@ static void text_scan_login(struct connection *conn)
|
||||
}
|
||||
}
|
||||
|
||||
if (conn->is_iser) {
|
||||
switch (idx) {
|
||||
case key_rdma_extensions:
|
||||
if (val != 1) {
|
||||
login_rsp_ini_err(conn, ISCSI_STATUS_INIT_ERR);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case key_initial_r2t:
|
||||
val = 1;
|
||||
break;
|
||||
case key_immediate_data:
|
||||
val = 0;
|
||||
break;
|
||||
}
|
||||
} else if (idx == key_rdma_extensions && val != 0) {
|
||||
login_rsp_ini_err(conn, ISCSI_STATUS_INIT_ERR);
|
||||
goto out;
|
||||
}
|
||||
|
||||
params_check_val(session_keys, idx, &val);
|
||||
params_set_val(session_keys, conn->session_params, idx, &val);
|
||||
|
||||
@@ -502,6 +526,11 @@ static void login_start(struct connection *conn)
|
||||
|
||||
if (session_type) {
|
||||
if (!strcmp(session_type, "Discovery")) {
|
||||
int ret = conn->is_discovery(conn->fd);
|
||||
if (ret) {
|
||||
login_rsp_tgt_err(conn, ISCSI_STATUS_MISSING_FIELDS);
|
||||
return;
|
||||
}
|
||||
conn->session_type = SESSION_DISCOVERY;
|
||||
} else if (strcmp(session_type, "Normal")) {
|
||||
login_rsp_ini_err(conn, ISCSI_STATUS_INV_SESSION_TYPE);
|
||||
@@ -613,6 +642,15 @@ static int login_finish(struct connection *conn)
|
||||
{
|
||||
int res = 0;
|
||||
|
||||
if (conn->is_iser &&
|
||||
conn->session_params[key_target_recv_data_length].key_state == KEY_STATE_START) {
|
||||
char buf[32] = "\0";
|
||||
params_val_to_str(session_keys, key_target_recv_data_length,
|
||||
session_keys[key_target_recv_data_length].local_def,
|
||||
buf, sizeof(buf));
|
||||
text_key_add(conn, "TargetRecvDataSegmentLength", buf);
|
||||
}
|
||||
|
||||
switch (conn->session_type) {
|
||||
case SESSION_NORMAL:
|
||||
if (!conn->sess)
|
||||
|
||||
@@ -27,8 +27,10 @@
|
||||
#include "types.h"
|
||||
#ifdef INSIDE_KERNEL_TREE
|
||||
#include <scst/iscsi_scst.h>
|
||||
#include <scst/isert_scst.h>
|
||||
#else
|
||||
#include "iscsi_scst.h"
|
||||
#include "isert_scst.h"
|
||||
#endif
|
||||
#include "iscsi_hdr.h"
|
||||
#include "param.h"
|
||||
@@ -126,6 +128,13 @@ struct connection {
|
||||
} auth;
|
||||
|
||||
struct __qelem clist;
|
||||
|
||||
bool is_iser;
|
||||
|
||||
int (*cork_transmit)(int fd);
|
||||
int (*uncork_transmit)(int fd);
|
||||
int (*getsockname)(int fd, struct sockaddr *name, socklen_t *namelen);
|
||||
int (*is_discovery)(int fd);
|
||||
};
|
||||
|
||||
#define IOSTATE_FREE 0
|
||||
@@ -221,6 +230,7 @@ extern int conn_blocked;
|
||||
enum {
|
||||
POLL_LISTEN,
|
||||
POLL_IPC = POLL_LISTEN + LISTEN_MAX,
|
||||
POLL_ISER_LISTEN,
|
||||
POLL_NL,
|
||||
POLL_ISNS,
|
||||
POLL_SCN_LISTEN,
|
||||
|
||||
@@ -18,9 +18,74 @@
|
||||
#include <netinet/tcp.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "iscsid.h"
|
||||
|
||||
int create_and_open_dev(const char *dev, int readonly)
|
||||
{
|
||||
FILE *f;
|
||||
char devname[256];
|
||||
char buf[256];
|
||||
int devn;
|
||||
int ctlfd = -1;
|
||||
int err;
|
||||
int flags;
|
||||
|
||||
f = fopen("/proc/devices", "r");
|
||||
if (!f) {
|
||||
err = -errno;
|
||||
perror("Cannot open control path to the driver");
|
||||
goto out;
|
||||
}
|
||||
|
||||
devn = 0;
|
||||
while (!feof(f)) {
|
||||
if (!fgets(buf, sizeof(buf), f))
|
||||
break;
|
||||
if (sscanf(buf, "%d %s", &devn, devname) != 2)
|
||||
continue;
|
||||
if (!strcmp(devname, dev))
|
||||
break;
|
||||
devn = 0;
|
||||
}
|
||||
|
||||
fclose(f);
|
||||
if (!devn) {
|
||||
err = -ENOENT;
|
||||
printf("cannot find %s in /proc/devices - "
|
||||
"make sure the module is loaded\n", dev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
sprintf(devname, "/dev/%s", dev);
|
||||
|
||||
unlink(devname);
|
||||
if (mknod(devname, (S_IFCHR | 0600), (devn << 8))) {
|
||||
err = -errno;
|
||||
printf("cannot create %s %s\n", devname, strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (readonly)
|
||||
flags = O_RDONLY;
|
||||
else
|
||||
flags = O_RDWR;
|
||||
|
||||
err = ctlfd = open(devname, flags);
|
||||
if (ctlfd < 0) {
|
||||
err = -errno;
|
||||
printf("cannot open %s %s\n", devname, strerror(errno));
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
}
|
||||
|
||||
void set_non_blocking(int fd)
|
||||
{
|
||||
int res = fcntl(fd, F_GETFL);
|
||||
|
||||
@@ -107,5 +107,6 @@ static inline int list_length_is_one(const struct __qelem *head)
|
||||
|
||||
extern void set_non_blocking(int fd);
|
||||
extern void sock_set_keepalive(int sock, int timeout);
|
||||
extern int create_and_open_dev(const char *dev, int readonly);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -382,5 +382,13 @@ struct iscsi_key session_keys[] = {
|
||||
{"IFMarker", 0, 0, 0, 0, 0, &and_ops},
|
||||
{"OFMarkInt", 2048, 2048, 1, 65535, 0, &marker_ops},
|
||||
{"IFMarkInt", 2048, 2048, 1, 65535, 0, &marker_ops},
|
||||
{"RDMAExtensions", 0, 0, 0, 0, 1, &and_ops},
|
||||
{"TargetRecvDataSegmentLength", 8192, 512, 512, -1, 0, &minimum_ops},
|
||||
{"InitiatorRecvDataSegmentLength", 8192, -1, 512, -1, 0, &minimum_ops},
|
||||
{"MaxAHSLength", 256, 0, 0, -1, 0, &minimum_ops},
|
||||
{"TaggedBufferForSolicitedDataOnly", 0, 0, 0, 0, 0, &and_ops},
|
||||
{"iSERHelloRequired", 0, 0, 0, 0, 0, &and_ops},
|
||||
{"MaxOutstandingUnexpectedPDUs", 0, 0, 0, -1, 0, &minimum_ops},
|
||||
{NULL,},
|
||||
};
|
||||
|
||||
|
||||
@@ -238,10 +238,11 @@ void target_list_build(struct connection *conn, char *target_name)
|
||||
char portal[NI_MAXHOST];
|
||||
int family, i;
|
||||
|
||||
if (getsockname(conn->fd, (struct sockaddr *) &ss1, &slen)) {
|
||||
if (conn->getsockname(conn->fd, (struct sockaddr *) &ss1, &slen)) {
|
||||
log_error("getsockname failed: %m");
|
||||
return;
|
||||
}
|
||||
|
||||
family = ss1.ss_family;
|
||||
|
||||
list_for_each_entry(target, &targets_list, tlist) {
|
||||
|
||||
@@ -100,6 +100,7 @@ rm -rf /usr/local/include/scst
|
||||
/lib/modules/%{kver}/extra/fcst.ko
|
||||
/lib/modules/%{kver}/extra/ib_srpt.ko
|
||||
/lib/modules/%{kver}/extra/iscsi-scst.ko
|
||||
/lib/modules/%{kver}/extra/isert-scst.ko
|
||||
/lib/modules/%{kver}/extra/qla2x00tgt.ko
|
||||
/lib/modules/%{kver}/extra/qla2xxx_scst.ko
|
||||
/lib/modules/%{kver}/extra/scst.ko
|
||||
|
||||
@@ -159,6 +159,7 @@ parse_scst_conf() {
|
||||
x86_64|i686)
|
||||
SCST_OPT_MODULES="crc32c-intel $SCST_OPT_MODULES";;
|
||||
esac
|
||||
SCST_MODULES="$SCST_MODULES isert_scst"
|
||||
SCST_OPT_MODULES="crc32c $SCST_OPT_MODULES"
|
||||
SCST_DAEMONS="${ISCSI_DAEMON} $SCST_DAEMONS"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user