diff --git a/iscsi-scst/Makefile b/iscsi-scst/Makefile index 405a63cdd..28df16de5 100644 --- a/iscsi-scst/Makefile +++ b/iscsi-scst/Makefile @@ -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) @@ -72,6 +122,8 @@ install: all $(MAKE) -C $(KDIR) SCST_INC_DIR=$(SCST_INC_DIR) SUBDIRS=$(KMOD) \ $$([ -n "$(DESTDIR)$(INSTALL_MOD_PATH)" ] && echo DEPMOD=true) \ modules_install + $(MAKE) -C $(KDIR) SCST_INC_DIR=$(SCST_INC_DIR) SUBDIRS=$(ISERTMOD) \ + modules_install uninstall: rm -f $(DESTDIR)$(SBINDIR)/iscsi-scstd \ @@ -79,7 +131,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) @@ -87,6 +140,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 @@ -96,6 +150,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 @@ -103,17 +158,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 @@ -123,6 +185,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 @@ -132,6 +201,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 @@ -141,6 +217,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 diff --git a/iscsi-scst/include/iscsi_scst.h b/iscsi-scst/include/iscsi_scst.h index b1f9c0ddd..a79cf56d8 100644 --- a/iscsi-scst/include/iscsi_scst.h +++ b/iscsi-scst/include/iscsi_scst.h @@ -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, }; diff --git a/iscsi-scst/kernel/Makefile b/iscsi-scst/kernel/Makefile index 803e76c4e..cfe3e4928 100644 --- a/iscsi-scst/kernel/Makefile +++ b/iscsi-scst/kernel/Makefile @@ -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 diff --git a/iscsi-scst/kernel/conn.c b/iscsi-scst/kernel/conn.c index 7407ab195..668c552fc 100644 --- a/iscsi-scst/kernel/conn.c +++ b/iscsi-scst/kernel/conn.c @@ -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) @@ -273,7 +274,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; @@ -345,6 +346,7 @@ out_err: conn_sysfs_del(conn); goto out; } +EXPORT_SYMBOL(conn_sysfs_add); #endif /* CONFIG_SCST_PROC */ @@ -429,7 +431,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; @@ -442,10 +444,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) { @@ -752,7 +760,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); @@ -778,7 +786,7 @@ static void conn_activate(struct iscsi_conn *conn) */ __iscsi_state_change(conn->sock->sk); - return; + return 0; } /* @@ -820,8 +828,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; @@ -915,7 +934,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); @@ -950,6 +969,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); @@ -988,6 +1045,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; lockdep_assert_held(&session->target->target_mutex); @@ -1001,7 +1059,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; @@ -1013,7 +1080,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; diff --git a/iscsi-scst/kernel/iscsi.c b/iscsi-scst/kernel/iscsi.c index 17f4fa971..831d12fa7 100644 --- a/iscsi-scst/kernel/iscsi.c +++ b/iscsi-scst/kernel/iscsi.c @@ -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) @@ -1739,7 +1782,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; @@ -1796,16 +1839,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]); @@ -1831,48 +1945,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 @@ -1883,18 +1956,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 */ @@ -1916,11 +1977,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); @@ -1980,7 +2039,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) { @@ -1988,7 +2048,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; @@ -2055,7 +2116,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); @@ -2666,6 +2727,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; @@ -2689,11 +2758,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); @@ -3146,6 +3212,7 @@ out: TRACE_EXIT_RES(res); return res; } +EXPORT_SYMBOL(cmnd_rx_start); void cmnd_rx_end(struct iscsi_cmnd *cmnd) { @@ -3177,6 +3244,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) @@ -3194,11 +3303,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) @@ -3225,8 +3331,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 */ @@ -3287,6 +3399,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); @@ -3296,7 +3454,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)); @@ -3371,19 +3528,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); @@ -3394,32 +3541,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: @@ -3428,7 +3550,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; } @@ -3631,7 +3753,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(); @@ -3654,7 +3775,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; @@ -3679,15 +3800,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; @@ -3809,7 +3925,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; @@ -3951,6 +4067,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; @@ -4106,6 +4246,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"); @@ -4256,6 +4400,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 diff --git a/iscsi-scst/kernel/iscsi.h b/iscsi-scst/kernel/iscsi.h index 2ee4da92c..6410a9cd0 100644 --- a/iscsi-scst/kernel/iscsi.h +++ b/iscsi-scst/kernel/iscsi.h @@ -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 */ @@ -308,6 +314,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; @@ -555,7 +562,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 @@ -607,6 +614,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); @@ -829,4 +837,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__ */ diff --git a/iscsi-scst/kernel/iscsi_dbg.h b/iscsi-scst/kernel/iscsi_dbg.h index b62240f4d..170a23217 100644 --- a/iscsi-scst/kernel/iscsi_dbg.h +++ b/iscsi-scst/kernel/iscsi_dbg.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) diff --git a/iscsi-scst/kernel/nthread.c b/iscsi-scst/kernel/nthread.c index 57d439665..2af4188e7 100644 --- a/iscsi-scst/kernel/nthread.c +++ b/iscsi-scst/kernel/nthread.c @@ -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, diff --git a/iscsi-scst/kernel/param.c b/iscsi-scst/kernel/param.c index 2430777d2..702d29ca3 100644 --- a/iscsi-scst/kernel/param.c +++ b/iscsi-scst/kernel/param.c @@ -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; } diff --git a/iscsi-scst/kernel/target.c b/iscsi-scst/kernel/target.c index 85ce05496..0c3280d3f 100644 --- a/iscsi-scst/kernel/target.c +++ b/iscsi-scst/kernel/target.c @@ -331,9 +331,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; @@ -341,6 +343,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); diff --git a/iscsi-scst/usr/ctldev.c b/iscsi-scst/usr/ctldev.c index c82b84d26..1164fc7d4 100644 --- a/iscsi-scst/usr/ctldev.c +++ b/iscsi-scst/usr/ctldev.c @@ -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; } diff --git a/iscsi-scst/usr/iscsi_scstd.c b/iscsi-scst/usr/iscsi_scstd.c index 56f51bf21..b0875c761 100644 --- a/iscsi-scst/usr/iscsi_scstd.c +++ b/iscsi-scst/usr/iscsi_scstd.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -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]; diff --git a/iscsi-scst/usr/iscsid.c b/iscsi-scst/usr/iscsid.c index a3b3f3049..2e104a756 100644 --- a/iscsi-scst/usr/iscsid.c +++ b/iscsi-scst/usr/iscsid.c @@ -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) diff --git a/iscsi-scst/usr/iscsid.h b/iscsi-scst/usr/iscsid.h index 2f75ed295..456b72a25 100644 --- a/iscsi-scst/usr/iscsid.h +++ b/iscsi-scst/usr/iscsid.h @@ -27,8 +27,10 @@ #include "types.h" #ifdef INSIDE_KERNEL_TREE #include +#include #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, diff --git a/iscsi-scst/usr/misc.c b/iscsi-scst/usr/misc.c index 89eb06c38..92c5d96c7 100644 --- a/iscsi-scst/usr/misc.c +++ b/iscsi-scst/usr/misc.c @@ -18,9 +18,74 @@ #include #include #include +#include +#include +#include +#include #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); diff --git a/iscsi-scst/usr/misc.h b/iscsi-scst/usr/misc.h index 4e5fb968f..37000efb2 100644 --- a/iscsi-scst/usr/misc.h +++ b/iscsi-scst/usr/misc.h @@ -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 diff --git a/iscsi-scst/usr/param.c b/iscsi-scst/usr/param.c index 710b5f1d5..27a3f8ad1 100644 --- a/iscsi-scst/usr/param.c +++ b/iscsi-scst/usr/param.c @@ -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,}, }; + diff --git a/iscsi-scst/usr/target.c b/iscsi-scst/usr/target.c index 128faad38..952201f30 100644 --- a/iscsi-scst/usr/target.c +++ b/iscsi-scst/usr/target.c @@ -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) { diff --git a/scst.spec.in b/scst.spec.in index 4c2cc3f52..be3f821b6 100644 --- a/scst.spec.in +++ b/scst.spec.in @@ -102,6 +102,7 @@ find /lib/modules/%{kver} -name ib_srpt.ko -exec rm {} \; /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 diff --git a/scstadmin/init.d/scst b/scstadmin/init.d/scst index c92180a0a..99bc1f6ce 100755 --- a/scstadmin/init.d/scst +++ b/scstadmin/init.d/scst @@ -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