Merge "Project import generated by Copybara." into 11.0.10
diff --git a/scst/.gitignore b/scst/.gitignore
index b2b65ad..60d04ac 100644
--- a/scst/.gitignore
+++ b/scst/.gitignore
@@ -2,6 +2,7 @@
 # :3,$!LC_ALL=C sort -fu
 
 *.cmd
+*.gcno
 *.ko
 *.ko.unsigned
 *.mod
diff --git a/scst/.mailmap b/scst/.mailmap
new file mode 100644
index 0000000..33cde67
--- /dev/null
+++ b/scst/.mailmap
@@ -0,0 +1,17 @@
+#
+# This list is used by git-shortlog to fix a few botched name translations
+# in the git archive, either because the author's full name was messed up
+# and/or not always written the same way, making contributions from the
+# same person appearing not to be so or badly displayed. Also allows for
+# old email addresses to map to new email addresses.
+#
+# For format details, see "MAPPING AUTHORS" in "man git-shortlog".
+#
+# Please keep this list dictionary sorted.
+#
+Bart Van Assche <bvanassche@acm.org> Bart Van Assche <bart@debian10-vm>
+Bart Van Assche <bvanassche@acm.org> Bart Van Assche <bart.vanassche@sandisk.com>
+Bart Van Assche <bvanassche@acm.org> Bart Van Assche <bart.vanassche@wdc.com>
+Bart Van Assche <bvanassche@acm.org> Bart Van Assche <bvanassche@users.noreply.github.com>
+Gleb Chesnokov <Chesnokov.G@raidix.com> Chesnokov Gleb <Chesnokov.G@raidix.com>
+Gleb Chesnokov <gleb.chesnokov@scst.dev> <Chesnokov.G@raidix.com>
diff --git a/scst/INSTALL.md b/scst/INSTALL.md
index 9b03148..bbea35d 100644
--- a/scst/INSTALL.md
+++ b/scst/INSTALL.md
@@ -41,9 +41,22 @@
 Since the above step installs several kernel modules into directory
 /lib/modules/$(uname -r), that step has to be repeated every time a new kernel
 or a kernel update has been installed. If you want to avoid this, install the
-scst-dkms package instead of the scst package. That will cause SCST to be
-rebuilt and installed every time a kernel version is booted for which the SCST
-kernel modules had not yet been built.
+scst-dkms package instead of the scst package.
+
+For example, if you want to have dkms support for your SCST rpm install, then
+you would use the following command to make your SCST packages:
+
+    make rpm-dkms 
+
+or
+
+    make scst-dkms-rpm
+
+make rpm-dkms also builds scstadmin packages in addition to the SCST dkms
+packages. Both make commands will create rpm packages that will cause SCST to be
+automatically rebuilt and installed every time a new kernel version is
+installed and booted for which the SCST kernel modules had not yet been built
+so that SCST rpm packages will not need to be rebuilt after each kernel update.
 
 ## Configuring SCST
 
diff --git a/scst/Makefile b/scst/Makefile
index 01845a6..8687ce8 100644
--- a/scst/Makefile
+++ b/scst/Makefile
@@ -35,13 +35,31 @@
 		cat $(KDIR)/include/config/kernel.release 2>/dev/null || \
 		make -s -C $(KDIR) kernelversion))
      endif
+else
+     ifndef KVER
+	KVER=$(strip $(shell uname -r))
+     endif
+     KDIR=/lib/modules/$(KVER)/build
 endif
 
+OLD_QLA_INI_DIR=qla2x00t
+OLD_QLA_DIR=$(OLD_QLA_INI_DIR)/qla2x00-target
+
+NEW_QLA_INI_DIR=qla2x00t-32gbit
+NEW_QLA_DIR=$(NEW_QLA_INI_DIR)/qla2x00-target
+
+ifeq ($(QLA_32GBIT),no)
+    QLA_INI_DIR=$(OLD_QLA_INI_DIR)
+    QLA_DIR=$(OLD_QLA_DIR)
+else
+    QLA_INI_DIR=$(NEW_QLA_INI_DIR)
+    QLA_DIR=$(NEW_QLA_DIR)
+endif
+
+
 SCST_DIR=scst
 DOC_DIR=doc
 SCSTADM_DIR=scstadmin
-QLA_INI_DIR=qla2x00t
-QLA_DIR=qla2x00t/qla2x00-target
 USR_DIR=usr
 SRP_DIR=srpt
 SCST_LOCAL_DIR=scst_local
@@ -53,12 +71,19 @@
 REVISION ?= $(shell if [ -e .svn ]; then				\
 		      svn info | sed -n 's/^Revision:[[:blank:]]*/./p';	\
 		    elif [ -e .git ]; then				\
+                      echo -n .;					\
 		      git log | grep -c ^commit;			\
 		    fi)
-VERSION = $(shell echo -n "$$(sed -n 's/^\#define[[:blank:]]SCST_VERSION_NAME[[:blank:]]*\"\([^-]*\).*\"/\1/p' scst/include/scst_const.h)")
-DEBIAN_REVISION=1
+VERSION_WITHOUT_REVISION := $(shell echo -n "$$(sed -n 's/^\#define[[:blank:]]SCST_VERSION_NAME[[:blank:]]*\"\([^-]*\).*\"/\1/p' scst/include/scst_const.h)")
+VERSION := $(VERSION_WITHOUT_REVISION)$(REVISION)
+DEBIAN_REVISION=1.1
 RPMTOPDIR ?= $(shell if [ $$(id -u) = 0 ]; then echo /usr/src/packages;\
 		else echo $$PWD/rpmbuilddir; fi)
+SCST_SOURCE_FILES = $(shell if [ -e scripts/list-source-files ]; then	\
+				scripts/list-source-files;		\
+			else						\
+				echo scripts-source-files-is-missing;	\
+			fi)
 
 help:
 	@echo "		all               : make all"
@@ -129,11 +154,20 @@
 		$(SCST_LOCAL_DIR) $(FCST_DIR) $(USR_DIR) $(SCSTADM_DIR); do \
 		$(MAKE) -j$$(nproc) -C "$$d" $@ || break;		    \
 	done
-	find . -type d -name "rpmbuilddir" | xargs rm -rf
 
 tags:
 	find . -type f -name "*.[ch]" | ctags --c-kinds=+p --fields=+iaS --extra=+q -e -L-
 
+cov-build:
+	-for d in $(SCST_DIR) $(ISCSI_DIR) $(OLD_QLA_DIR) $(NEW_QLA_DIR) $(SRP_DIR)  \
+		$(SCST_LOCAL_DIR) $(FCST_DIR) $(USR_DIR) $(SCSTADM_DIR); do	     \
+		if [[ $$d = $(OLD_QLA_DIR) || $$d = $(NEW_QLA_DIR) ]]; then	     \
+			BUILD_2X_MODULE=y $(MAKE) -j$$(nproc) -C "$$d" all || break; \
+		else								     \
+			$(MAKE) -j$$(nproc) -C "$$d" all || break;		     \
+		fi								     \
+	done
+
 scst:
 	cd $(SCST_DIR) && $(MAKE) all
 
@@ -288,8 +322,9 @@
 	mkdir "$${name}-$(3)" &&					\
 	{								\
 	  {								\
-	    scripts/list-source-files &&				\
+	    scripts/list-source-files | grep -v '/\.gitignore' &&	\
 	    if [ -e debian/changelog ]; then echo debian/changelog; fi;	\
+	    if [ -e debian/compat ]; then echo debian/compat; fi;	\
 	  } |								\
 	  $(4) |							\
 	  tar -T- -cf- |						\
@@ -299,8 +334,10 @@
 	tar -c$(1) -f "$${name}-$(3).tar.$(2)" "$${name}-$(3)" &&	\
 	rm -rf "$${name}-$(3)"
 
-scst-dist-gzip:
-	$(call make-scst-dist,j,bz2,$(VERSION),grep -E '^doc/|^fcst/|^iscsi-scst/|^Makefile|^qla2x00t(|_git)/|^scripts/|^scst.spec|^scst/|^scst_local/|^srpt/|^usr/|^scstadmin/')
+scst-dist-gzip: scst-$(VERSION).tar.bz2
+
+scst-$(VERSION).tar.bz2: $(SCST_SOURCE_FILES)
+	$(call make-scst-dist,j,bz2,$(VERSION),grep -E '^debian/|^doc/|^fcst/|^iscsi-scst/|^Makefile|^qla2x00t(|-32gbit)/|^scripts/|^scst.spec|^scst/|^scst_local/|^srpt/|^usr/|^scstadmin/')
 
 scst-rpm:
 	name=scst &&							\
@@ -314,9 +351,10 @@
 	    -e "s|@depmod@|$(shell which depmod)|g"			\
 		<$${name}.spec.in >$${name}.spec &&			\
 	MAKE="$(MAKE)" rpmbuild --define="%_topdir $${rpmtopdir}"	\
-					--define="%rpm_release $(REVISION)"                     \
 	    $(if $(KVER),--define="%kversion $(KVER)")			\
 	    $(if $(KDIR),--define="%kdir $(KDIR)")			\
+		--define="debug_package %{nil}" \
+		--define="__strip /bin/true" \
 	    -ba $${name}.spec &&					\
 	rm -f $${name}-$(VERSION).tar.bz2
 
@@ -339,10 +377,15 @@
 rpm:
 	$(MAKE) scst-rpm
 	$(MAKE) -C scstadmin rpm
-	# generate version file for fileio_tgt
-	$(MAKE) -C scst/src ../include/scst_itf_ver.h
-	$(MAKE) -C usr/fileio rpm
+	@if [ "$$(id -u)" != 0 ]; then			\
+	    echo;					\
+	    echo "The following RPMs have been built:";	\
+	    find -name '*.rpm';				\
+	fi
 
+rpm-dkms:
+	$(MAKE) scst-dkms-rpm
+	$(MAKE) -C scstadmin rpm
 	@if [ "$$(id -u)" != 0 ]; then			\
 	    echo;					\
 	    echo "The following RPMs have been built:";	\
@@ -353,25 +396,31 @@
 	sed 's/%{scst_version}/$(VERSION)-$(DEBIAN_REVISION)/'		\
 	  <debian/changelog.in >debian/changelog
 
-../scst_$(VERSION).orig.tar.gz: debian/changelog Makefile
+debian/compat:
+	dpkg-query -W --showformat='$${Version}\n' debhelper 2>/dev/null | \
+	sed 's/\..*//' >$@
+
+../scst_$(VERSION).orig.tar.gz: debian/changelog debian/compat Makefile	\
+		$(SCST_SOURCE_FILES)
 	$(call make-scst-dist,z,gz,$(VERSION),cat) &&			\
 	mv "scst-$(VERSION).tar.gz" "$@"
 
-../scst_$(VERSION).orig.tar.xz: debian/changelog Makefile
+../scst_$(VERSION).orig.tar.xz: debian/changelog debian/compat Makefile	\
+		$(SCST_SOURCE_FILES)
 	$(call make-scst-dist,J,xz,$(VERSION),cat) &&			\
 	mv "scst-$(VERSION).tar.xz" "$@"
 
 dpkg: ../scst_$(VERSION).orig.tar.gz
-	@if [ -z "$$DEBEMAIL" ]; then					\
-	  echo "Error: \$$DEBEMAIL has not been set";			\
-	  false;							\
-	fi &&								\
-	if [ -z "$$DEBFULLNAME" ]; then					\
-	  echo "Error: \$$DEBFULLNAME has not been set";		\
-	  false;							\
-	fi &&								\
+	@[ -z "$$DEBEMAIL" ] || export DEBEMAIL=bvanassche@acm.org &&	\
+	[ -z "$$DEBFULLNAME" ] || export DEBFULLNAME="Bart Van Assche" &&\
+	echo "KDIR=$(KDIR)" &&						\
+	echo "KVER=$(KVER)" &&						\
 	sed 's/%{scst_version}/$(VERSION)/'				\
 	  <debian/scst.dkms.in >debian/scst.dkms &&			\
+	sed 's/%{KVER}/$(KVER)/'					\
+	  <debian/scst.preinst.in >debian/scst.preinst &&		\
+	sed 's/%{KVER}/$(KVER)/'					\
+	  <debian/scst.postinst.in >debian/scst.postinst &&		\
 	output_files=(							\
 		../*_$(VERSION)-$(DEBIAN_REVISION)_*.deb		\
 		../*_$(VERSION)-$(DEBIAN_REVISION)_*.ddeb		\
@@ -392,11 +441,8 @@
 	else								\
 	  buildopts+=(-j4);						\
 	fi &&								\
-	if false; then							\
-	  dpkg-buildpackage "$${buildopts[@]}";				\
-	else								\
-	  debuild "$${buildopts[@]}" --lintian-opts --profile debian;	\
-	fi &&								\
+	DEB_CC_SET="$(CC)" DEB_KVER_SET=$(KVER) DEB_KDIR_SET=$(KDIR) DEB_QLA_DIR_SET=$(QLA_DIR) \
+	   DEB_QLA_INI_DIR_SET=$(QLA_INI_DIR) debuild "$${buildopts[@]}" --lintian-opts --profile debian && \
 	mkdir -p dpkg &&						\
 	for f in "$${output_files[@]}" ../scst_$(VERSION).orig.tar.[gx]z; do\
 		mv $$f dpkg || true;					\
@@ -406,6 +452,13 @@
 
 release-archive:
 	$(MAKE) 2release
+	scripts/generate-release-archive scst "$(VERSION_WITHOUT_REVISION)"
+	md5sum ../scst-$(VERSION_WITHOUT_REVISION).tar.bz2	\
+	  > ../scst-$(VERSION_WITHOUT_REVISION).tar.bz2.md5sum
+	$(MAKE) 2debug
+
+multiple-release-archives:
+	$(MAKE) 2release
 	for m in $$(find -name Makefile |			\
 		    xargs grep -l '^release-archive:' |		\
 		    grep -v '^\./Makefile');			\
diff --git a/scst/README.md b/scst/README.md
index 58d6c7f..5330deb 100644
--- a/scst/README.md
+++ b/scst/README.md
@@ -1,3 +1,5 @@
+[![Coverity](https://img.shields.io/coverity/scan/25131.svg)](https://scan.coverity.com/projects/scst-project)
+
 # Overview
 
 This is the source code repository of the SCST project. SCST is a collection
@@ -17,13 +19,22 @@
 
 ## QLogic target driver
 
-Two QLogic target drivers are included in the SCST project. The driver in
-the qla2x00t directory is a very stable driver that supports up to 16 Gb/s
-adapters. It is very stable, well tested and actively used in many production
-setups.
+Two QLogic target drivers are included in the SCST project.
 
-There is also a newer driver that supports 32 Gb/s FC in the qla2x00t-32gbit
-directory. That driver has not yet reached the same maturity level as the
-old qla2x00t driver.
+The default driver is located in qla2x00t-32gbit directory and it supports up
+to 32 Gb/s FC. It is the newer one.
+
+May anyone wish to switch back to the older driver that only supported up to
+16 Gb/s adapters, it is located in qla2x00t directory. To make use of the
+older driver build scst with environment variable `QLA_32GBIT=no` set.
 
 Vladislav Bolkhovitin <vst@vlnb.net>, http://scst.sourceforge.net
+
+## Sourceforge achievements
+<p align="middle">
+<img src="./www/images/sourceforge_badges/oss-users-love-us-white.svg" width="125" />
+<img src="./www/images/sourceforge_badges/oss-community-choice-white.svg" width="125" />
+<img src="./www/images/sourceforge_badges/oss-sf-favorite-white.svg" width="125" />
+<img src="./www/images/sourceforge_badges/oss-community-leader-white.svg" width="125" />
+<img src="./www/images/sourceforge_badges/oss-open-source-excellence-white.svg" width="125" />
+</p>
diff --git a/scst/README.performance b/scst/README.performance
new file mode 100644
index 0000000..7a41d64
--- /dev/null
+++ b/scst/README.performance
@@ -0,0 +1,67 @@
+SCST Performance
+================
+
+A question that is asked often is how to tune performance. This means how to
+improve the IOPS and/or bandwidth measured at the initiator side. In this
+README it is explained how to optimize storage performance.
+
+Local storage
+-------------
+Start with measuring the performance of the local block device(s). IOPS can be
+measured e.g. as follows:
+
+    fio --ioengine=libaio --rw=randread --ioscheduler=none --numjobs=$(nproc) \
+    --runtime=60 --group_reporting=1 --gtod_reduce=1 --norandommap \
+    --thread --buffered=0 --iodepth=256 --iodepth_batch=128 --bs=4k \
+    --name=bdev --filename=/dev/...
+
+The bandwidth supported by a block device can be measured by increasing the
+block size to a larger value in the above command, e.g. --bs=1M.
+
+Storage network
+---------------
+Start with measuring the network bandwidth using your favorite tool, e.g.
+netperf for non-RDMA networks or ib_write_bw for RDMA networks.
+
+Next, add a nullio LUN to SCST, e.g. by adding the following in /etc/scst.conf:
+
+    HANDLER vdisk_nullio {
+        DEVICE disk09 {
+            blocksize 4096
+            size_mb   256
+        }
+    }
+
+    TARGET_DRIVER ... {
+        TARGET ... {
+	    LUN ... disk09
+	}
+    }
+
+After a nullio LUN has been added, verify that this LUN is visible at the
+initiator side. If it is not visible at the initiator side, consider
+rescanning LUNs or disconnecting and reconnecting the initiator system to the
+SCST server.
+
+Once the nullio LUN is visible at the initiator side, measure IOPS and
+bandwidth. The bandwidth should be close to the network bandwidth. When using
+the Linux iSCSI initiator this may require configuring multiple iSCSI sessions
+and activating multipathd on top of the multiple iSCSI sessions. The sequence
+for logging in with iSCSI and activating multiple iSCSI sessions is as follows:
+
+    iscsiadm -m iface -I iface2 -o new
+    iscsiadm -m iface -I iface2 -o update -n iface.initiatorname -v ${iqn2}
+    iscsiadm -m discovery -t st -p ${scst_ip_address}
+    iscsiadm -m discovery -t st -p ${scst_ip_address} -I iface2
+    iscsiadm -m node -p ${scst_ip_address} -l
+    iscsiadm -m node -p ${scst_ip_address} -I iface2 -l
+
+SCST Configuration
+------------------
+If the number of IOPS measured at the initiator side is significantly lower
+than the minimum of the IOPS supported by the local storage and the storage
+network, further tuning is required. Look up in /proc/interrupts which CPU
+cores process the most network and storage interrupts and configure the SCST
+kernel threads such that these run on other CPU cores than those that process
+the most interrupts by configuring the cpu_mask attribute. More information
+about the SCST cpu_mask sysfs attribute is available in the SCST README.
diff --git a/scst/SVN_TAGS b/scst/SVN_TAGS
index 94c9b32..95113d4 100644
--- a/scst/SVN_TAGS
+++ b/scst/SVN_TAGS
@@ -27,3 +27,5 @@
 3.3.0					7830 on the 3.3.x branch
 3.4.x branch start                      8675, which is a copy of trunk r8674
 3.4.0					8681 on the trunk
+3.5.x branch start			9293 on the trunk
+3.5.0 	     				9293 on the trunk
diff --git a/scst/debian/compat b/scst/debian/compat
deleted file mode 100644
index ec63514..0000000
--- a/scst/debian/compat
+++ /dev/null
@@ -1 +0,0 @@
-9
diff --git a/scst/debian/rules b/scst/debian/rules
index b7ca759..0efc12d 100755
--- a/scst/debian/rules
+++ b/scst/debian/rules
@@ -13,7 +13,15 @@
 # package maintainers to append LDFLAGS
 #export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
 
-SUBDIRS=scst fcst iscsi-scst qla2x00t/qla2x00-target scst_local scstadmin srpt
+# rules won't see variables unless they're using DEB_foo_SET syntax. So use that as
+# an intermediary. Also, export variables for sub-makes to be able to see them.
+export KVER=$(DEB_KVER_SET)
+export KDIR=$(DEB_KDIR_SET)
+export CC=$(DEB_CC_SET)
+export QLA_DIR=$(DEB_QLA_DIR_SET)
+export QLA_INI_DIR=$(DEB_QLA_INI_DIR_SET)
+
+SUBDIRS=scst $(shell grep -qw '^CONFIG_LIBFC' /boot/config-$(KVER) && echo fcst) iscsi-scst $(QLA_DIR) scst_local scstadmin srpt
 DESTDIR=$(CURDIR)/debian/tmp
 VERSION:=$(shell head -n1 debian/changelog | sed 's/.*(\([0-9.]*\).*).*/\1/')
 
@@ -24,18 +32,20 @@
 clean:
 	dh_testdir &&							\
 	dh_prep -Xqla_isp/TAGS -Xdebian/changelog &&			\
-	scripts/clean-source-tree -x debian/changelog &&		\
-	rm -f scstadmin/scstadmin
+	scripts/clean-source-tree -x debian/changelog -x debian/compat -x debian/scst.preinst \
+	    -x debian/scst.postinst
 
 build:
+	[ -n "$(QLA_INI_DIR)" ] &&					\
 	make 2release &&						\
 	export BUILD_2X_MODULE=y &&					\
-	export CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y &&	\
+	export CONFIG_SCSI_QLA_FC=y &&					\
+	export CONFIG_SCSI_QLA2XXX_TARGET=y &&				\
 	for d in $(SUBDIRS); do $(MAKE) -C $$d; done &&			\
 	{								\
 		echo dkms.conf &&					\
 		echo Makefile &&					\
-		for d in fcst iscsi-scst qla2x00t scst scst_local srpt; do\
+		for d in fcst iscsi-scst $(QLA_INI_DIR) scst scst_local srpt; do\
 			echo $$d;					\
 		done;							\
 	} | sed "s,^,usr/src/scst-$(VERSION)/," >debian/scst-dkms.install
@@ -50,7 +60,8 @@
 	export PREFIX=/usr &&						\
 	export DESTDIR="$(DESTDIR)" &&					\
 	export BUILD_2X_MODULE=y &&					\
-	export CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y &&	\
+	export CONFIG_SCSI_QLA_FC=y &&					\
+	export CONFIG_SCSI_QLA2XXX_TARGET=y &&				\
 	for d in $(SUBDIRS); do						\
 	    if [ $$d = scst ]; then					\
 		{ $(MAKE) -C $$d install || break; }			\
@@ -69,7 +80,7 @@
 	cp debian/scst.dkms						\
 		$(DESTDIR)/usr/src/scst-$(VERSION)/dkms.conf &&		\
 	scripts/list-source-files |					\
-	grep -E '^Makefile$$|^(fcst|iscsi-scst|qla2x00t|scst|scst_local|srpt)/'|\
+	grep -E '^Makefile$$|^(fcst|iscsi-scst|$(QLA_INI_DIR)|scst|scst_local|srpt)/'|\
 	tar -T- -cf- |							\
 	tar -C $(DESTDIR)/usr/src/scst-$(VERSION) -xf- &&		\
 	find $(DESTDIR) -type f -print0 | xargs -0 -r chmod 0644 &&	\
@@ -97,10 +108,8 @@
 override_dh_installinit:
 	dh_installinit --onlyscripts
 
-# dh_make generated override targets
-# This is example for Cmake (See https://bugs.debian.org/641051 )
-#override_dh_auto_configure:
-#	dh_auto_configure -- #	-DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH)
+override_dh_auto_configure:
+	true
 
 .PHONY: clean binary binary-arch binary-indep build build-arch build-indep \
 	install
diff --git a/scst/debian/scst.dkms.in b/scst/debian/scst.dkms.in
index 615e76b..adba7de 100644
--- a/scst/debian/scst.dkms.in
+++ b/scst/debian/scst.dkms.in
@@ -1,7 +1,7 @@
 PACKAGE_VERSION="${PACKAGE_VERSION}"
 PACKAGE_NAME="scst"
 AUTOINSTALL=yes
-MAKE[0]="export KVER=${kernelver} KDIR=${kernel_source_dir} BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y && make 2release && make -sC scst && make -sC fcst && make -sC iscsi-scst && make -sC qla2x00t/qla2x00-target && make -sC scst_local && make -sC srpt"
+MAKE[0]="export KVER=${kernelver} KDIR=${kernel_source_dir} BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y && make 2release && make -sC scst && make -sC fcst && make -sC iscsi-scst && make -sC qla2x00t-32gbit/qla2x00-target && make -sC scst_local && make -sC srpt"
 CLEAN="make clean"
 # Remove any existing ib_srpt.ko kernel modules
 PRE_INSTALL="find /lib/modules/${kernelver} -name ib_srpt.ko -exec rm {} \;"
@@ -21,10 +21,10 @@
 BUILT_MODULE_LOCATION[3]="iscsi-scst/kernel/isert-scst"
 DEST_MODULE_LOCATION[3]="/extra"
 BUILT_MODULE_NAME[4]="qla2x00tgt"
-BUILT_MODULE_LOCATION[4]="qla2x00t/qla2x00-target"
+BUILT_MODULE_LOCATION[4]="qla2x00t-32gbit/qla2x00-target"
 DEST_MODULE_LOCATION[4]="/extra"
 BUILT_MODULE_NAME[5]="qla2xxx_scst"
-BUILT_MODULE_LOCATION[5]="qla2x00t"
+BUILT_MODULE_LOCATION[5]="qla2x00t-32gbit"
 DEST_MODULE_LOCATION[5]="/extra"
 BUILT_MODULE_NAME[6]="scst_cdrom"
 BUILT_MODULE_LOCATION[6]="scst/src/dev_handlers"
diff --git a/scst/debian/scst.postinst b/scst/debian/scst.postinst.in
similarity index 97%
rename from scst/debian/scst.postinst
rename to scst/debian/scst.postinst.in
index d1569e3..f535eeb 100644
--- a/scst/debian/scst.postinst
+++ b/scst/debian/scst.postinst.in
@@ -23,7 +23,7 @@
 	mkdir -p /var/lib/scst/dif_tags
 	mkdir -p /var/lib/scst/pr
 	mkdir -p /var/lib/scst/vdev_mode_pages
-	depmod;;
+	depmod "%{KVER}";;
 
     abort-upgrade|abort-remove|abort-deconfigure)
 	;;
diff --git a/scst/debian/scst.preinst b/scst/debian/scst.preinst.in
similarity index 93%
rename from scst/debian/scst.preinst
rename to scst/debian/scst.preinst.in
index 4d5af2e..199a4e2 100644
--- a/scst/debian/scst.preinst
+++ b/scst/debian/scst.preinst.in
@@ -17,7 +17,7 @@
 case "$1" in
     install)
 	# Remove any existing ib_srpt.ko kernel modules
-	find "/lib/modules/$(uname -r)" -name ib_srpt.ko -exec rm {} \;
+	find "/lib/modules/%{KVER}" -name ib_srpt.ko -exec rm {} \;
 	# Remove files installed by "make install"
 	rm -f /usr/local/man/man5/iscsi-scstd.conf.5
 	rm -f /usr/local/man/man8/iscsi-scst-adm.8
diff --git a/scst/doc/scst_pg.sgml b/scst/doc/scst_pg.sgml
index 5750db6..81dd910 100644
--- a/scst/doc/scst_pg.sgml
+++ b/scst/doc/scst_pg.sgml
@@ -163,11 +163,10 @@
 
 	int threads_num;
 
-	int (*detect) (struct scst_tgt_template *tgt_template);
 	int (*release)(struct scst_tgt *tgt);
 
 	int (*xmit_response)(struct scst_cmd *cmd);
-	int (* rdy_to_xfer)(struct scst_cmd *cmd);
+	int (*rdy_to_xfer)(struct scst_cmd *cmd);
 
 	void (*on_hw_pending_cmd_timeout) (struct scst_cmd *cmd);
 
@@ -230,13 +229,6 @@
 It is the target driver's duty to ensure that not more, than that number
 of threads, are blocked in those functions at any time.
 
-<item><bf/int (*detect) (struct scst_tgt_template *tgt_template)/ - this
-function is intended to detect the target adapters that are present in
-the system. Each found adapter should be registered by calling
-<it/scst_register_target()/. The function should return a value >= 0 to
-signify the number of detected target adapters. A negative value should
-be returned whenever there is an error. Must be defined.
-
 <item><bf/int (*release)(struct scst_tgt *tgt)/ - this function is
 intended to free up resources allocated to the device. The function
 should return 0 to indicate successful release or a negative value if
diff --git a/scst/doc/scst_user_spec.sgml b/scst/doc/scst_user_spec.sgml
index f3d8679..e6ba972 100644
--- a/scst/doc/scst_user_spec.sgml
+++ b/scst/doc/scst_user_spec.sgml
@@ -10,7 +10,7 @@
 	<name>Vladislav Bolkhovitin</name>
 </author>
 
-<date>Version 3.5.0</date>
+<date>Version 3.7.0</date>
 
 <toc>
 
diff --git a/scst/fcst/Kbuild b/scst/fcst/Kbuild
index 1954f60..be21875 100644
--- a/scst/fcst/Kbuild
+++ b/scst/fcst/Kbuild
@@ -1,5 +1,5 @@
 KBUILD_EXTRA_SYMBOLS=$(src)/../scst/src/Module.symvers
-ccflags-y += -I$(src)/../scst/include
+ccflags-y += -I$(src)/../scst/include $(shell if [ -e include/scsi ]; then header_dir=.; else header_dir=$$(sed -n 's/^include[[:blank:]]\+\(.*\)\/Makefile$$/\1/p;s/^MAKEARGS := -C \([^ ]*\) .*/\1/p' Makefile); fi; if false; then echo "header_dir=$${header_dir}" >&2; fi; grep -qw fc_fill_fc_hdr "$${header_dir}/include/scsi/fc_encode.h" 2>/dev/null && echo -DFC_FILL_FC_HDR_IN_SCSI_FC_ENCODE_H)
 
 obj-$(CONFIG_FCST) += fcst.o
 
diff --git a/scst/fcst/fcst.h b/scst/fcst/fcst.h
index 7191c0d..a29556c 100644
--- a/scst/fcst/fcst.h
+++ b/scst/fcst/fcst.h
@@ -27,10 +27,18 @@
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) || \
 	defined(CONFIG_SUSE_KERNEL) && \
 	LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
+/*
+ * See also commit 768c72cc34a2 ("scsi: libfc: Replace ->exch_done callback
+ * with function call") # v4.10
+ * and commit 9625cc483b8c ("scsi: libfc: Replace ->seq_release callback with
+ * function call") # v4.10
+ * and commit c6865b30be7e ("scsi: libfc: Replace ->seq_start_next callback
+ * with function call") # v4.10.
+ */
 #define NEW_LIBFC_API
 #endif
 
-#define FT_VERSION	"3.5.0"
+#define FT_VERSION	"3.7.0"
 #define FT_MODULE	"fcst"
 
 #define FT_MAX_HW_PENDING_TIME	20	/* max I/O time in seconds */
diff --git a/scst/fcst/ft_cmd.c b/scst/fcst/ft_cmd.c
index e14e534..3519a0a 100644
--- a/scst/fcst/ft_cmd.c
+++ b/scst/fcst/ft_cmd.c
@@ -14,10 +14,21 @@
  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  * SOFTWARE.
  */
+
+#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <scsi/libfc.h>
+/*
+ * See also upstream commit e31ac898ac29 ("scsi: libfc: Move scsi/fc_encode.h
+ * to libfc"). That commit moved fc_fill_fc_hdr() from <scsi/fc_encode.h> into
+ * <scsi/fc_frame.h>.
+ */
+#if defined(FC_FILL_FC_HDR_IN_SCSI_FC_ENCODE_H)
 #include <scsi/fc_encode.h>
+#else
+#include <scsi/fc_frame.h>
+#endif
 #include "fcst.h"
 
 /*
@@ -118,14 +129,12 @@
 		lport->tt.exch_done(sp);
 #endif
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
 	if (fr_seq(fp))
 #ifdef NEW_LIBFC_API
 		fc_seq_release(fr_seq(fp));
 #else
 		lport->tt.seq_release(fr_seq(fp));
 #endif
-#endif
 
 	fc_frame_free(fp);
 	kfree(fcmd);
@@ -394,9 +403,6 @@
 	struct fcp_resp_with_ext *fcp;
 	struct fcp_resp_rsp_info *info;
 	struct fc_lport *lport;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
-	struct fc_exch *ep;
-#endif
 
 	fh = fc_frame_header_get(rx_fp);
 	FT_IO_DBG("FCP error response: did %x oxid %x status %x code %x\n",
@@ -407,11 +413,8 @@
 		len += sizeof(*info);
 	fp = fc_frame_alloc(lport, len);
 	if (!fp)
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
-		goto out;
-#else
 		return;
-#endif
+
 	fcp = fc_frame_payload_get(fp, len);
 	memset(fcp, 0, len);
 	fcp->resp.fr_status = status;
@@ -422,17 +425,6 @@
 		info->rsp_code = code;
 	}
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
-	sp = fr_seq(rx_fp);
-	sp = lport->tt.seq_start_next(sp);
-	ep = fc_seq_exch(sp);
-	fc_fill_fc_hdr(fp, FC_RCTL_DD_CMD_STATUS, ep->did, ep->sid, FC_TYPE_FCP,
-		       FC_FC_EX_CTX | FC_FC_LAST_SEQ | FC_FC_END_SEQ, 0);
-
-	lport->tt.seq_send(lport, sp, fp);
-out:
-	;
-#else
 	fc_fill_reply_hdr(fp, rx_fp, FC_RCTL_DD_CMD_STATUS, 0);
 	sp = fr_seq(fp);
 	if (sp)
@@ -443,7 +435,6 @@
 #endif
 	else
 		lport->tt.frame_send(lport, fp);
-#endif
 }
 
 /*
@@ -502,13 +493,7 @@
 
 	scst_rx_mgmt_params_init(&params);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) || \
-	defined(CONFIG_SUSE_KERNEL) && \
-	LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 101)
 	params.lun = fcp->fc_lun.scsi_lun;
-#else
-	params.lun = fcp->fc_lun;
-#endif
 	params.lun_len = sizeof(fcp->fc_lun);
 	params.lun_set = 1;
 	params.atomic = SCST_ATOMIC;
@@ -564,9 +549,6 @@
 
 	lport = sess->tport->lport;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
-	sp = fr_seq(fp);
-#else
 #ifdef NEW_LIBFC_API
 	sp = fc_seq_assign(lport, fp);
 #else
@@ -574,7 +556,6 @@
 #endif
 	if (!sp)
 		goto busy;
-#endif
 
 	fcmd = kzalloc(sizeof(*fcmd), GFP_ATOMIC);
 	if (!fcmd)
@@ -603,16 +584,9 @@
 	cdb_len += sizeof(fcp->fc_cdb);
 	data_len = ntohl(*(__be32 *)(fcp->fc_cdb + cdb_len));
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) || \
-	defined(CONFIG_SUSE_KERNEL) && \
-	LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 101)
 	cmd = scst_rx_cmd(sess->scst_sess, fcp->fc_lun.scsi_lun,
 			  sizeof(fcp->fc_lun), fcp->fc_cdb, cdb_len,
 			  SCST_ATOMIC);
-#else
-	cmd = scst_rx_cmd(sess->scst_sess, fcp->fc_lun, sizeof(fcp->fc_lun),
-			  fcp->fc_cdb, cdb_len, SCST_ATOMIC);
-#endif
 	if (!cmd)
 		goto busy;
 	fcmd->scst_cmd = cmd;
@@ -686,30 +660,6 @@
 static void ft_cmd_ls_rjt(struct fc_frame *rx_fp, enum fc_els_rjt_reason reason,
 			  enum fc_els_rjt_explan explan)
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
-	struct fc_seq *sp = fr_seq(rx_fp);
-	struct fc_frame *fp;
-	struct fc_els_ls_rjt *rjt;
-	struct fc_lport *lport;
-	struct fc_exch *ep;
-
-	ep = fc_seq_exch(sp);
-	lport = ep->lp;
-	fp = fc_frame_alloc(lport, sizeof(*rjt));
-	if (!fp)
-		return;
-
-	rjt = fc_frame_payload_get(fp, sizeof(*rjt));
-	memset(rjt, 0, sizeof(*rjt));
-	rjt->er_cmd = ELS_LS_RJT;
-	rjt->er_reason = reason;
-	rjt->er_explan = explan;
-
-	sp = lport->tt.seq_start_next(sp);
-	fc_fill_fc_hdr(fp, FC_RCTL_ELS_REP, ep->did, ep->sid, FC_TYPE_FCP,
-		       FC_FC_EX_CTX | FC_FC_END_SEQ | FC_FC_LAST_SEQ, 0);
-	lport->tt.seq_send(lport, sp, fp);
-#else
 	struct fc_seq_els_data rjt_data;
 
 	rjt_data.reason = reason;
@@ -719,7 +669,6 @@
 #else
 	fr_dev(rx_fp)->tt.seq_els_rsp_send(rx_fp, ELS_LS_RJT, &rjt_data);
 #endif
-#endif
 }
 
 /*
@@ -758,9 +707,6 @@
 	default:
 		pr_info("%s: unhandled frame r_ctl %x\n", __func__,
 			fh->fh_r_ctl);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
-		sess->tport->lport->tt.exch_done(fr_seq(fp));
-#endif
 		fc_frame_free(fp);
 		break;
 	}
diff --git a/scst/fcst/ft_io.c b/scst/fcst/ft_io.c
index 1ab44f4..face322 100644
--- a/scst/fcst/ft_io.c
+++ b/scst/fcst/ft_io.c
@@ -19,10 +19,21 @@
  * You should have received a copy of the GNU General Public License along with
  * this program.
  */
+
+#include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <scsi/libfc.h>
+/*
+ * See also upstream commit e31ac898ac29 ("scsi: libfc: Move scsi/fc_encode.h
+ * to libfc"). That commit moved fc_fill_fc_hdr() from <scsi/fc_encode.h> into
+ * <scsi/fc_frame.h>.
+ */
+#if defined(FC_FILL_FC_HDR_IN_SCSI_FC_ENCODE_H)
 #include <scsi/fc_encode.h>
+#else
+#include <scsi/fc_frame.h>
+#endif
 #include "fcst.h"
 
 /*
diff --git a/scst/fcst/ft_sess.c b/scst/fcst/ft_sess.c
index ef5991e..5deafb7 100644
--- a/scst/fcst/ft_sess.c
+++ b/scst/fcst/ft_sess.c
@@ -81,18 +81,6 @@
 	return tport;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0)
-/*
- * Free tport via RCU.
- */
-static void ft_tport_rcu_free(struct rcu_head *rcu)
-{
-	struct ft_tport *tport = container_of(rcu, struct ft_tport, rcu);
-
-	kfree(tport);
-}
-#endif
-
 /*
  * Delete target local port, if any, associated with the local port.
  * Caller holds ft_lport_lock.
@@ -111,11 +99,7 @@
 	rcu_assign_pointer(*(void __force __rcu **)&lport->prov[FC_TYPE_FCP],
 			   NULL);
 	tport->lport = NULL;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
 	kfree_rcu(tport, rcu);
-#else
-	call_rcu(&tport->rcu, ft_tport_rcu_free);
-#endif
 }
 
 /*
@@ -180,9 +164,6 @@
 {
 	struct ft_tport *tport;
 	struct hlist_head *head;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
-	struct hlist_node *pos;
-#endif
 	struct ft_sess *sess;
 
 	rcu_read_lock();
@@ -192,11 +173,7 @@
 		goto out;
 
 	head = &tport->hash[ft_sess_hash(port_id)];
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
-	hlist_for_each_entry_rcu(sess, pos, head, hash) {
-#else
 	hlist_for_each_entry_rcu(sess, head, hash) {
-#endif
 		if (sess->port_id == port_id) {
 			if (!kref_get_unless_zero(&sess->kref))
 				sess = NULL;
@@ -221,9 +198,6 @@
 	struct ft_sess *sess;
 	struct scst_session *scst_sess;
 	struct hlist_head *head;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
-	struct hlist_node *pos;
-#endif
 	u32 port_id;
 	char name[FT_NAMELEN];
 
@@ -234,11 +208,7 @@
 	}
 
 	head = &tport->hash[ft_sess_hash(port_id)];
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
-	hlist_for_each_entry_rcu(sess, pos, head, hash) {
-#else
 	hlist_for_each_entry_rcu(sess, head, hash) {
-#endif
 		if (sess->port_id == port_id) {
 			sess->params = fcp_parm;
 			return 0;
@@ -299,17 +269,10 @@
 static struct ft_sess *ft_sess_delete(struct ft_tport *tport, u32 port_id)
 {
 	struct hlist_head *head;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
-	struct hlist_node *pos;
-#endif
 	struct ft_sess *sess;
 
 	head = &tport->hash[ft_sess_hash(port_id)];
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
-	hlist_for_each_entry_rcu(sess, pos, head, hash) {
-#else
 	hlist_for_each_entry_rcu(sess, head, hash) {
-#endif
 		if (sess->port_id == port_id) {
 			ft_sess_unhash(sess);
 			return sess;
@@ -348,7 +311,9 @@
 		u8	__resv1[7];
 		__be64	port_name;	/* N_Port Name */
 		u8	__resv2[8];
-	} __attribute__((__packed__)) *id;
+	} *id;
+
+	BUILD_BUG_ON(sizeof(*id) != 24);
 
 	if (!scst_sess)
 		return SCSI_TRANSPORTID_PROTOCOLID_FCP2;
@@ -432,7 +397,7 @@
 }
 
 /**
- * tcm_fcp_prli() - Handle incoming or outgoing PRLI for the FCP target
+ * ft_prli() - Handle incoming or outgoing PRLI for the FCP target
  * @rdata: remote port private
  * @spp_len: service parameter page length
  * @rspp: received service parameter page (NULL for outgoing PRLI)
@@ -502,25 +467,12 @@
 	rdata->prli_count--;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) && !defined(RHEL_MAJOR)
-static inline u32 fc_frame_sid(const struct fc_frame *fp)
-{
-	return ntoh24(fc_frame_header_get(fp)->fh_s_id);
-}
-#endif
-
 /*
  * Handle incoming FCP request.
  * Caller has verified that the frame is type FCP.
  * Note that this may be called directly from the softirq context.
  */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) \
-	&& (!defined(RHEL_MAJOR) || RHEL_MAJOR -0 <= 5)
-static void ft_recv(struct fc_lport *lport, struct fc_seq *sp,
-		    struct fc_frame *fp)
-#else
 static void ft_recv(struct fc_lport *lport, struct fc_frame *fp)
-#endif
 {
 	struct ft_sess *sess;
 	u32 sid = fc_frame_sid(fp);
@@ -530,10 +482,6 @@
 	sess = ft_sess_get(lport, sid);
 	if (!sess) {
 		FT_SESS_DBG("sid %x sess lookup failed\n", sid);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) \
-	&& (!defined(RHEL_MAJOR) || RHEL_MAJOR -0 <= 5)
-		lport->tt.exch_done(sp);
-#endif
 		/* TBD XXX - if FCP_CMND, send LOGO */
 		fc_frame_free(fp);
 		return;
@@ -553,9 +501,6 @@
 {
 	struct ft_tport *tport;
 	struct hlist_head *head;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
-	struct hlist_node *pos;
-#endif
 	struct ft_sess *sess;
 
 	tport = scst_tgt_get_tgt_priv(tgt);
@@ -563,11 +508,7 @@
 	tport->lport->service_params &= ~FCP_SPPF_TARG_FCN;
 
 	for (head = tport->hash; head < &tport->hash[FT_SESS_HASH_SIZE]; head++)
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0)
-		hlist_for_each_entry_rcu(sess, pos, head, hash)
-#else
 		hlist_for_each_entry_rcu(sess, head, hash)
-#endif
 			ft_sess_close(sess);
 
 	synchronize_rcu();
diff --git a/scst/iscsi-scst/Makefile b/scst/iscsi-scst/Makefile
index 73befbe..557088e 100644
--- a/scst/iscsi-scst/Makefile
+++ b/scst/iscsi-scst/Makefile
@@ -168,7 +168,6 @@
 	  $(shell [ -n "$(PASS_CC_TO_MAKE)" ] && echo CC="$(CC)")	\
 	  $$([ -n "$(DEPMOD)" ] && echo "DEPMOD=$(DEPMOD)")		\
 	  CONFIG_MODULE_SIG_ALL= modules_install
-		chmod u+x $(INSTALL_DIR)/*.ko
 	echo "$@:  INFINIBAND_ENABLED = $(INFINIBAND_ENABLED)"
 	if $(INFINIBAND_ENABLED); then					\
 	  (cd $(ISERTMOD) && KDIR=$(KDIR) ../../../scripts/sign-modules);\
@@ -215,12 +214,6 @@
 	echo "$(call run_conftest,cm_event_mod,				\
 		-DCM_HANDLER_EVENT_MODIFIER=const,-DCM_HANDLER_EVENT_MODIFIER=)" >"$@"
 
-conftest/cm_listen/result-$(KVER).txt:					\
-	conftest/cm_listen/cm_listen.c					\
-	conftest/cm_listen/Kbuild
-	echo "$(call run_conftest,cm_listen,				\
-		-DIB_CM_LISTEN_TAKES_FOURTH_ARG)" >"$@"
-
 conftest/create_cq/result-$(KVER).txt:					\
 	conftest/create_cq/create_cq.c					\
 	conftest/create_cq/Kbuild
diff --git a/scst/iscsi-scst/README b/scst/iscsi-scst/README
index 687d52b..14f323b 100644
--- a/scst/iscsi-scst/README
+++ b/scst/iscsi-scst/README
@@ -1,7 +1,7 @@
 iSCSI SCST target driver
 ========================
 
-Version 3.5.0, 21 December 2020
+Version 3.7.0, 26 December 2022
 ----------------------------
 
 ISCSI-SCST is a deeply reworked fork of iSCSI Enterprise Target (IET)
@@ -183,7 +183,7 @@
 
  - allowed_portal[num] - optional attribute, which specifies, on which
    portals (target's IP addresses) this target will be available. If not
-   specified (default) the target will be available on all all portals.
+   specified (default) the target will be available on all portals.
    As soon as at least one allowed_portal specified, the target will be
    accessible for initiators only on the specified portals. There might
    be any number of the allowed_portal attributes. The portals
@@ -738,7 +738,7 @@
 
 will disable all portals.
 
-2. If you want to want to allow only only specific set of initiators be
+2. If you want to want to allow only specific set of initiators be
 able to connect to your target, you should don't add any default LUNs
 for the target and create for allowed initiators a security group to
 which they will be assigned.
@@ -852,7 +852,7 @@
 the same speed as via any single port. Thus, using such adapters in MPIO
 configuration can't improve performance. To allow MPIO to have double
 performance you should either use separate network adapters, or find a
-dual-port adapter capable to to transfer data simultaneously on both
+dual-port adapter capable to transfer data simultaneously on both
 ports. You can check it by running 2 iperf's through both ports in
 parallel.
 
diff --git a/scst/iscsi-scst/README_in-tree b/scst/iscsi-scst/README_in-tree
index 50a6bb1..0413184 100644
--- a/scst/iscsi-scst/README_in-tree
+++ b/scst/iscsi-scst/README_in-tree
@@ -66,7 +66,7 @@
 
  - allowed_portal[num] - optional attribute, which specifies, on which
    portals (target's IP addresses) this target will be available. If not
-   specified (default) the target will be available on all all portals.
+   specified (default) the target will be available on all portals.
    As soon as at least one allowed_portal specified, the target will be
    accessible for initiators only on the specified portals. There might
    be any number of the allowed_portal attributes. The portals
@@ -582,7 +582,7 @@
 
 will disable all portals.
 
-2. If you want to want to allow only only specific set of initiators be
+2. If you want to want to allow only specific set of initiators be
 able to connect to your target, you should don't add any default LUNs
 for the target and create for allowed initiators a security group to
 which they will be assigned.
@@ -694,7 +694,7 @@
 the same speed as via any single port. Thus, using such adapters in MPIO
 configuration can't improve performance. To allow MPIO to have double
 performance you should either use separate network adapters, or find a
-dual-port adapter capable to to transfer data simultaneously on both
+dual-port adapter capable to transfer data simultaneously on both
 ports. You can check it by running 2 iperf's through both ports in
 parallel.
 
diff --git a/scst/iscsi-scst/doc/manpages/iscsi-scstd.8 b/scst/iscsi-scst/doc/manpages/iscsi-scstd.8
index 6167a65..895db2a 100644
--- a/scst/iscsi-scst/doc/manpages/iscsi-scstd.8
+++ b/scst/iscsi-scst/doc/manpages/iscsi-scstd.8
@@ -42,8 +42,8 @@
 .BI \-g\  GID ,\ \-\-gid= GID
 Specify running group id, default is current gid.
 .TP
-.BI \-a\  address ,\ \-\-address= address
-Specify on which local address the server should listen, default is any.
+.BI \-a\  address\ ... ,\ \-\-address= address\ ...
+Specify on which space-separated list of local addresses the server should listen, default is any.
 .TP
 .BI \-p\  port ,\ \-\-port= port
 Specify on which port the server should listen, default is 3260.
diff --git a/scst/iscsi-scst/include/iscsi_scst.h b/scst/iscsi-scst/include/iscsi_scst.h
index d6b7acd..985621c 100644
--- a/scst/iscsi-scst/include/iscsi_scst.h
+++ b/scst/iscsi-scst/include/iscsi_scst.h
@@ -20,6 +20,12 @@
 #include <linux/uaccess.h>  /* mm_segment_t */
 #include <linux/version.h>
 
+#ifdef INSIDE_KERNEL_TREE
+#include <scst/backport.h>
+#else
+#include "backport.h"
+#endif
+
 /* <asm/uaccess.h> */
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
@@ -32,6 +38,21 @@
  * also https://lwn.net/Articles/832121/. The definitions below make it easy
  * to write kernel code that is compatible with all kernel versions.
  */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) ||		\
+	(defined(RHEL_RELEASE_CODE) &&				\
+	 RHEL_RELEASE_CODE -0 >= RHEL_RELEASE_VERSION(9, 2))
+/*
+ * Backport mm_segment_t to save compatibility with older kernel versions.
+ *
+ * mm_segment_t was dropped in commit
+ * 967747bbc084 ("uaccess: remove CONFIG_SET_FS") # v5.18
+ */
+typedef struct {
+	/* empty dummy */
+} mm_segment_t;
+#endif
+
 #define KERNEL_DS ((mm_segment_t) { })
 static inline mm_segment_t get_fs(void) { return ((mm_segment_t) { }); }
 static inline void set_fs(mm_segment_t seg) { }
@@ -180,7 +201,6 @@
 	u32 cid;
 	u32 code;
 	u32 cookie;
-	char target_name[ISCSI_NAME_LEN];
 	u32 param1_size;
 	u32 param2_size;
 };
diff --git a/scst/iscsi-scst/include/iscsi_scst_ver.h b/scst/iscsi-scst/include/iscsi_scst_ver.h
index de8a336..da4d91f 100644
--- a/scst/iscsi-scst/include/iscsi_scst_ver.h
+++ b/scst/iscsi-scst/include/iscsi_scst_ver.h
@@ -17,4 +17,4 @@
 
 #define ISCSI_VERSION_STRING_SUFFIX
 
-#define ISCSI_VERSION_STRING	"3.5.0" ISCSI_VERSION_STRING_SUFFIX
+#define ISCSI_VERSION_STRING	"3.7.0" ISCSI_VERSION_STRING_SUFFIX
diff --git a/scst/iscsi-scst/kernel/config.c b/scst/iscsi-scst/kernel/config.c
index f3a5749..307775f 100644
--- a/scst/iscsi-scst/kernel/config.c
+++ b/scst/iscsi-scst/kernel/config.c
@@ -501,11 +501,9 @@
 	struct iscsi_attr *tgt_attr;
 	struct list_head *attrs_list;
 	const char *name;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 	static struct lock_class_key __key;
 #endif
-#endif
 
 	TRACE_ENTRY();
 
@@ -551,14 +549,9 @@
 	list_add(&tgt_attr->attrs_list_entry, attrs_list);
 
 	tgt_attr->attr.attr.name = tgt_attr->name;
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35)
-	tgt_attr->attr.attr.owner = THIS_MODULE;
-#endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 	tgt_attr->attr.attr.key = &__key;
 #endif
-#endif
 	tgt_attr->attr.attr.mode = attr_info->mode & (S_IRUGO | S_IWUGO);
 	tgt_attr->attr.show = iscsi_attr_show;
 	tgt_attr->attr.store = iscsi_attr_store;
diff --git a/scst/iscsi-scst/kernel/conn.c b/scst/iscsi-scst/kernel/conn.c
index a27ac09..031e6c3 100644
--- a/scst/iscsi-scst/kernel/conn.c
+++ b/scst/iscsi-scst/kernel/conn.c
@@ -26,13 +26,11 @@
 #include "iscsi.h"
 #include "digest.h"
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
 #if defined(CONFIG_LOCKDEP) && !defined(CONFIG_SCST_PROC)
 static struct lock_class_key scst_conn_key;
 static struct lockdep_map scst_conn_dep_map =
 	STATIC_LOCKDEP_MAP_INIT("iscsi_conn_kref", &scst_conn_key);
 #endif
-#endif
 
 static int print_conn_state(char *p, size_t size, struct iscsi_conn *conn)
 {
@@ -132,19 +130,10 @@
 	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)->saddr));
-#else
 			"%pI4", &inet_sk(sk)->inet_saddr);
-#endif
 		break;
 #ifdef CONFIG_IPV6
 	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)->saddr));
-#else
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 13, 0)
 		pos = scnprintf(buf, size, "[%pI6]", &inet6_sk(sk)->saddr);
 #else
@@ -152,7 +141,6 @@
 #endif
 #endif
 		break;
-#endif
 	default:
 		pos = scnprintf(buf, size, "Unknown family %d",
 			sk->sk_family);
@@ -580,18 +568,10 @@
 	return;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void conn_nop_in_delayed_work_fn(void *p)
-#else
 static void conn_nop_in_delayed_work_fn(struct work_struct *work)
-#endif
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	struct iscsi_conn *conn = p;
-#else
 	struct iscsi_conn *conn = container_of(work, struct iscsi_conn,
 					       nop_in_delayed_work.work);
-#endif
 	unsigned long next_timeout = 0;
 
 	TRACE_ENTRY();
@@ -889,13 +869,8 @@
 	conn->conn_thr_pool = session->sess_thr_pool;
 
 	conn->nop_in_ttt = 0;
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20))
 	INIT_DELAYED_WORK(&conn->nop_in_delayed_work,
 			  conn_nop_in_delayed_work_fn);
-#else
-	INIT_WORK(&conn->nop_in_delayed_work, conn_nop_in_delayed_work_fn,
-		conn);
-#endif
 	conn->last_rcv_time = jiffies;
 	conn->data_rsp_timeout = session->tgt_params.rsp_timeout * HZ;
 	conn->nop_in_interval = session->tgt_params.nop_in_interval * HZ;
diff --git a/scst/iscsi-scst/kernel/digest.c b/scst/iscsi-scst/kernel/digest.c
index 5b87519..baca70b 100644
--- a/scst/iscsi-scst/kernel/digest.c
+++ b/scst/iscsi-scst/kernel/digest.c
@@ -39,12 +39,9 @@
 }
 
 /**
- * initialize support for digest calculation.
- *
- * digest_init -
+ * digest_init - initialize support for digest calculation.
  * @conn: ptr to connection to make use of digests
- *
- * @return: 0 on success, < 0 on error
+ * Returns: 0 on success, < 0 on error
  */
 int digest_init(struct iscsi_conn *conn)
 {
@@ -142,10 +139,10 @@
 	if (unlikely(crc != cmnd->hdigest)) {
 		PRINT_ERROR("%s", "RX header digest failed");
 		return -EIO;
-	} else {
-		TRACE_DBG("RX header digest OK for cmd %p", cmnd);
 	}
 
+	TRACE_DBG("RX header digest OK for cmd %p", cmnd);
+
 	return 0;
 }
 
diff --git a/scst/iscsi-scst/kernel/event.c b/scst/iscsi-scst/kernel/event.c
index f879191..f40234c 100644
--- a/scst/iscsi-scst/kernel/event.c
+++ b/scst/iscsi-scst/kernel/event.c
@@ -49,11 +49,7 @@
 {
 	u32 pid;
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0))
-	pid = NETLINK_CB(skb).pid;
-#else
 	pid = NETLINK_CB(skb).portid;
-#endif
 	WARN_ON(pid == 0);
 
 	iscsid_pid = pid;
@@ -61,11 +57,7 @@
 	return 0;
 }
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24))
-static int event_recv_skb(struct sk_buff *skb)
-#else
 static void event_recv_skb(struct sk_buff *skb)
-#endif
 {
 	int err;
 	struct nlmsghdr	*nlh;
@@ -87,27 +79,9 @@
 	}
 
 out:
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24))
-	return 0;
-#else
 	return;
-#endif
 }
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24))
-static void event_recv(struct sock *sk, int length)
-{
-	struct sk_buff *skb;
-
-	while ((skb = skb_dequeue(&sk->sk_receive_queue))) {
-		if (event_recv_skb(skb) && skb->len)
-			skb_queue_head(&sk->sk_receive_queue, skb);
-		else
-			kfree_skb(skb);
-	}
-}
-#endif
-
 /* event_mutex supposed to be held */
 static int __event_send(const void *buf, int buf_len)
 {
@@ -155,7 +129,7 @@
 {
 	int err;
 	static DEFINE_MUTEX(event_mutex);
-	struct iscsi_kern_event event;
+	struct iscsi_kern_event event = {};
 	int param1_size, param2_size;
 
 	param1_size = (param1 != NULL) ? strlen(param1) : 0;
@@ -196,30 +170,14 @@
 {
 	iscsi_net_ns = kobj_ns_grab_current(KOBJ_NS_TYPE_NET);
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22))
-	nl = netlink_kernel_create(NETLINK_ISCSI_SCST, 1, event_recv,
-		THIS_MODULE);
-#elif (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24))
-	nl = netlink_kernel_create(NETLINK_ISCSI_SCST, 1, event_recv, NULL,
-				   THIS_MODULE);
-#elif (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0))
-	nl = netlink_kernel_create(iscsi_net_ns, NETLINK_ISCSI_SCST, 1,
-				   event_recv_skb, NULL, THIS_MODULE);
-#else
 	{
 		struct netlink_kernel_cfg cfg = {
 			.input = event_recv_skb,
 			.groups = 1,
 		};
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0))
-		nl = netlink_kernel_create(iscsi_net_ns, NETLINK_ISCSI_SCST,
-				   THIS_MODULE, &cfg);
-#else
 		nl = netlink_kernel_create(iscsi_net_ns, NETLINK_ISCSI_SCST,
 					   &cfg);
-#endif
 	}
-#endif
 	if (!nl)
 		goto drop_ns;
 
@@ -234,12 +192,7 @@
 
 void event_exit(void)
 {
-#if (LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24))
-	if (nl)
-		sock_release(nl->sk_socket);
-#else
 	netlink_kernel_release(nl);
-#endif
 	kobj_ns_drop(KOBJ_NS_TYPE_NET, iscsi_net_ns);
 	iscsi_net_ns = NULL;
 }
diff --git a/scst/iscsi-scst/kernel/iscsi.c b/scst/iscsi-scst/kernel/iscsi.c
index 198680d..bb367d1 100644
--- a/scst/iscsi-scst/kernel/iscsi.c
+++ b/scst/iscsi-scst/kernel/iscsi.c
@@ -2469,18 +2469,10 @@
 
 static mempool_t *iscsi_cmnd_abort_mempool;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void iscsi_cmnd_abort_fn(void *ctx)
-#else
 static void iscsi_cmnd_abort_fn(struct work_struct *work)
-#endif
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	struct iscsi_cmnd_abort_params *params = ctx;
-#else
 	struct iscsi_cmnd_abort_params *params = container_of(work,
 		struct iscsi_cmnd_abort_params, iscsi_cmnd_abort_work);
-#endif
 	struct scst_cmd *scst_cmd = params->scst_cmd;
 	struct iscsi_session *session = scst_sess_get_tgt_priv(scst_cmd->sess);
 	struct iscsi_conn *conn;
@@ -2489,7 +2481,8 @@
 
 	TRACE_ENTRY();
 
-	TRACE(TRACE_MGMT,"Checking aborted scst_cmd %p (cmnd %p)", scst_cmd, cmnd);
+	TRACE_MGMT_DBG("Checking aborted scst_cmd %p (cmnd %p)", scst_cmd,
+		       cmnd);
 
 	mutex_lock(&session->target->target_mutex);
 
@@ -2539,11 +2532,7 @@
 	}
 
 	memset(params, 0, sizeof(*params));
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	INIT_WORK(&params->iscsi_cmnd_abort_work, iscsi_cmnd_abort_fn, params);
-#else
 	INIT_WORK(&params->iscsi_cmnd_abort_work, iscsi_cmnd_abort_fn);
-#endif
 	params->scst_cmd = scst_cmd;
 
 	scst_cmd_get(scst_cmd);
@@ -3277,19 +3266,10 @@
 	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;
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 	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) && \
 	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 7)
 		pos = scnprintf(buf, size, "[%pI6]", &inet6_sk(sk)->daddr);
@@ -3298,7 +3278,6 @@
 #endif
 #endif
 		break;
-#endif
 	default:
 		pos = scnprintf(buf, size, "Unknown family %d",
 			sk->sk_family);
diff --git a/scst/iscsi-scst/kernel/iscsi.h b/scst/iscsi-scst/kernel/iscsi.h
index 7a5895e..dfd61cf 100644
--- a/scst/iscsi-scst/kernel/iscsi.h
+++ b/scst/iscsi-scst/kernel/iscsi.h
@@ -130,11 +130,7 @@
 };
 
 #define ISCSI_HASH_ORDER	8
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
 #define	cmnd_hashfn(itt)	hash_32(itt, ISCSI_HASH_ORDER)
-#else
-#define	cmnd_hashfn(itt)	hash_long(itt, ISCSI_HASH_ORDER)
-#endif
 
 struct iscsi_session {
 	struct iscsi_target *target;
@@ -315,11 +311,7 @@
 	/* Doesn't need any protection */
 	u16 cid;
 
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20))
 	struct delayed_work nop_in_delayed_work;
-#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 */
diff --git a/scst/iscsi-scst/kernel/isert-scst/iser.h b/scst/iscsi-scst/kernel/isert-scst/iser.h
index a638c29..c543c8b 100644
--- a/scst/iscsi-scst/kernel/isert-scst/iser.h
+++ b/scst/iscsi-scst/kernel/isert-scst/iser.h
@@ -66,6 +66,8 @@
 	/* protected by dev_list_mutex */
 	struct list_head	conn_list; /* head of conns list */
 	enum isert_portal_state	state;
+	struct work_struct      work;
+	struct workqueue_struct *reinit_id_wq;
 	int			refcnt;
 };
 
@@ -88,13 +90,13 @@
 };
 
 struct isert_device;
-struct isert_connection;
+struct isert_conn;
 
 struct isert_wr {
 	enum isert_wr_op	wr_op;
 	struct isert_buf	*buf;
 
-	struct isert_connection	*conn;
+	struct isert_conn       *conn;
 	struct isert_cmnd	*pdu;
 
 	struct isert_device	*isert_dev;
@@ -168,7 +170,7 @@
 #define ISERT_CONNECTION_CLOSE		5
 #define ISERT_IN_PORTAL_LIST		6
 
-struct isert_connection {
+struct isert_conn {
 	struct iscsi_conn	iscsi ____cacheline_aligned;
 
 	int			repost_threshold ____cacheline_aligned;
@@ -224,13 +226,16 @@
 	struct work_struct	close_work;
 	struct work_struct	drain_work;
 	struct work_struct	discon_work;
-	struct work_struct	free_work;
+	struct work_struct	release_work;
 	struct isert_wr		drain_wr_sq;
 	struct isert_wr		drain_wr_rq;
 	struct kref		kref;
 
 	struct isert_portal	*portal;
 	void			*priv_data; /* for connection tracking */
+
+	wait_queue_head_t       rem_wait;
+	atomic_t                dev_removed;
 };
 
 struct isert_device {
@@ -264,8 +269,8 @@
 	struct workqueue_struct	*conn_wq;
 };
 
-#define _ptr_to_u64(p)		(u64)(unsigned long)(p)
-#define _u64_to_ptr(v)		(void *)(unsigned long)(v)
+#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);
@@ -286,33 +291,30 @@
 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);
+struct isert_portal *isert_portal_create(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,
+int isert_post_recv(struct isert_conn *isert_conn,
 		    struct isert_wr *first_wr, int num_wr);
-int isert_post_send(struct isert_connection *isert_conn,
+int isert_post_send(struct isert_conn *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);
-void isert_post_drain(struct isert_connection *isert_conn);
-void isert_sched_conn_free(struct isert_connection *isert_conn);
+int isert_alloc_conn_resources(struct isert_conn *isert_conn);
+void isert_free_conn_resources(struct isert_conn *isert_conn);
+void isert_put_conn(struct isert_conn *isert_conn);
+void isert_conn_disconnect(struct isert_conn *isert_conn);
+void isert_post_drain(struct isert_conn *isert_conn);
+void isert_sched_conn_free(struct isert_conn *isert_conn);
 
-static inline struct isert_connection *isert_conn_zalloc(void)
+static inline struct isert_conn *isert_conn_zalloc(void)
 {
 	return kmem_cache_zalloc(isert_conn_cache, GFP_KERNEL);
 }
 
-static inline void isert_conn_kfree(struct isert_connection *isert_conn)
+static inline void isert_conn_kfree(struct isert_conn *isert_conn)
 {
 	kmem_cache_free(isert_conn_cache, isert_conn);
 }
@@ -322,12 +324,12 @@
 			     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_conn *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_conn *isert_conn,
 		  struct isert_cmnd *pdu,
 		  struct ib_sge *sge,
 		  int sg_offset,
@@ -357,23 +359,23 @@
 	kmem_cache_free(isert_cmnd_cache, cmnd);
 }
 
-struct isert_cmnd *isert_rx_pdu_alloc(struct isert_connection *isert_conn,
+struct isert_cmnd *isert_rx_pdu_alloc(struct isert_conn *isert_conn,
 				      size_t size);
-struct isert_cmnd *isert_tx_pdu_alloc(struct isert_connection *isert_conn,
+struct isert_cmnd *isert_tx_pdu_alloc(struct isert_conn *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_conn *isert_conn);
+int isert_pdu_send(struct isert_conn *isert_conn,
 		   struct isert_cmnd *tx_pdu);
 
 int isert_prepare_rdma(struct isert_cmnd *isert_pdu,
-		       struct isert_connection *isert_conn,
+		       struct isert_conn *isert_conn,
 		       enum isert_wr_op op);
-int isert_pdu_post_rdma_write(struct isert_connection *isert_conn,
+int isert_pdu_post_rdma_write(struct isert_conn *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,
+int isert_pdu_post_rdma_read(struct isert_conn *isert_conn,
 			     struct isert_cmnd *isert_cmd,
 			     int wr_cnt);
 
diff --git a/scst/iscsi-scst/kernel/isert-scst/iser_buf.c b/scst/iscsi-scst/kernel/isert-scst/iser_buf.c
index acbdee4..2112758 100644
--- a/scst/iscsi-scst/kernel/isert-scst/iser_buf.c
+++ b/scst/iscsi-scst/kernel/isert-scst/iser_buf.c
@@ -197,7 +197,7 @@
 }
 
 void isert_wr_set_fields(struct isert_wr *wr,
-			 struct isert_connection *isert_conn,
+			 struct isert_conn *isert_conn,
 			 struct isert_cmnd *pdu)
 {
 	struct isert_device *isert_dev = isert_conn->isert_dev;
@@ -210,7 +210,7 @@
 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_conn *isert_conn,
 		  struct isert_cmnd *pdu,
 		  struct ib_sge *sge,
 		  int sg_offset,
diff --git a/scst/iscsi-scst/kernel/isert-scst/iser_datamover.c b/scst/iscsi-scst/kernel/isert-scst/iser_datamover.c
index 42dd2c0..d8241c0 100644
--- a/scst/iscsi-scst/kernel/isert-scst/iser_datamover.c
+++ b/scst/iscsi-scst/kernel/isert-scst/iser_datamover.c
@@ -63,8 +63,8 @@
 			size_t *addr_len)
 {
 	int ret;
-	struct isert_connection *isert_conn = container_of(iscsi_conn,
-						struct isert_connection, iscsi);
+	struct isert_conn *isert_conn = container_of(iscsi_conn,
+						struct isert_conn, iscsi);
 	struct sockaddr *peer_sa = (struct sockaddr *)&isert_conn->peer_addr;
 
 	ret = isert_get_addr_size(peer_sa, addr_len);
@@ -80,8 +80,8 @@
 			  size_t *addr_len)
 {
 	int ret;
-	struct isert_connection *isert_conn = container_of(iscsi_conn,
-						struct isert_connection, iscsi);
+	struct isert_conn *isert_conn = container_of(iscsi_conn,
+						struct isert_conn, iscsi);
 	struct sockaddr *self_sa = (struct sockaddr *)&isert_conn->self_addr;
 
 	ret = isert_get_addr_size(self_sa, addr_len);
@@ -106,17 +106,17 @@
 
 void isert_free_connection(struct iscsi_conn *iscsi_conn)
 {
-	struct isert_connection *isert_conn = container_of(iscsi_conn,
-						struct isert_connection, iscsi);
+	struct isert_conn *isert_conn = container_of(iscsi_conn,
+						struct isert_conn, iscsi);
 
 	isert_post_drain(isert_conn);
-	isert_conn_free(isert_conn);
+	isert_put_conn(isert_conn);
 }
 
 struct iscsi_cmnd *isert_alloc_login_rsp_pdu(struct iscsi_conn *iscsi_conn)
 {
-	struct isert_connection *isert_conn = container_of(iscsi_conn,
-						struct isert_connection, iscsi);
+	struct isert_conn *isert_conn = container_of(iscsi_conn,
+						struct isert_conn, iscsi);
 	struct isert_cmnd *isert_pdu = isert_conn->login_rsp_pdu;
 
 	isert_tx_pdu_init(isert_pdu, isert_conn);
@@ -126,8 +126,8 @@
 static struct iscsi_cmnd *isert_alloc_scsi_pdu(struct iscsi_conn *iscsi_conn,
 					       int fake)
 {
-	struct isert_connection *isert_conn = container_of(iscsi_conn,
-						struct isert_connection, iscsi);
+	struct isert_conn *isert_conn = container_of(iscsi_conn,
+						struct isert_conn, iscsi);
 	struct isert_cmnd *isert_pdu;
 
 again:
@@ -159,8 +159,8 @@
 {
 	struct isert_cmnd *isert_pdu = container_of(iscsi_pdu,
 						    struct isert_cmnd, iscsi);
-	struct isert_connection *isert_conn = container_of(iscsi_pdu->conn,
-						struct isert_connection, iscsi);
+	struct isert_conn *isert_conn = container_of(iscsi_pdu->conn,
+						struct isert_conn, iscsi);
 
 	isert_tx_pdu_init_iscsi(isert_pdu);
 
@@ -180,8 +180,8 @@
 /* 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 = container_of(login_rsp->conn,
-						struct isert_connection, iscsi);
+	struct isert_conn *isert_conn = container_of(login_rsp->conn,
+						struct isert_conn, iscsi);
 	int err;
 
 	if (last && !discovery) {
@@ -210,8 +210,8 @@
 			     struct iscsi_sess_params *sess_params,
 			     struct iscsi_tgt_params *tgt_params)
 {
-	struct isert_connection *isert_conn = container_of(iscsi_conn,
-						struct isert_connection, iscsi);
+	struct isert_conn *isert_conn = container_of(iscsi_conn,
+						struct isert_conn, iscsi);
 
 	isert_conn->queue_depth = tgt_params->queued_cmnds;
 
@@ -228,8 +228,8 @@
 {
 	struct isert_cmnd *isert_cmnd = container_of(iscsi_cmnd,
 						    struct isert_cmnd, iscsi);
-	struct isert_connection *isert_conn = container_of(iscsi_cmnd->conn,
-						struct isert_connection, iscsi);
+	struct isert_conn *isert_conn = container_of(iscsi_cmnd->conn,
+						struct isert_conn, iscsi);
 	int err;
 
 	isert_tx_pdu_convert_from_iscsi(isert_cmnd, iscsi_cmnd);
@@ -242,8 +242,8 @@
 {
 	struct isert_cmnd *isert_cmnd = container_of(iscsi_cmnd,
 						    struct isert_cmnd, iscsi);
-	struct isert_connection *isert_conn = container_of(iscsi_cmnd->conn,
-						struct isert_connection, iscsi);
+	struct isert_conn *isert_conn = container_of(iscsi_cmnd->conn,
+						struct isert_conn, iscsi);
 	int ret;
 
 	ret = isert_prepare_rdma(isert_cmnd, isert_conn, ISER_WR_RDMA_READ);
@@ -260,8 +260,8 @@
 {
 	struct isert_cmnd *isert_cmnd = container_of(iscsi_cmnd,
 						    struct isert_cmnd, iscsi);
-	struct isert_connection *isert_conn = container_of(iscsi_cmnd->conn,
-						struct isert_connection, iscsi);
+	struct isert_conn *isert_conn = container_of(iscsi_cmnd->conn,
+						struct isert_conn, iscsi);
 	struct isert_cmnd *isert_rsp = container_of(iscsi_rsp,
 						    struct isert_cmnd, iscsi);
 	int ret;
@@ -278,8 +278,8 @@
 
 int isert_close_connection(struct iscsi_conn *iscsi_conn)
 {
-	struct isert_connection *isert_conn = container_of(iscsi_conn,
-						struct isert_connection, iscsi);
+	struct isert_conn *isert_conn = container_of(iscsi_conn,
+						struct isert_conn, iscsi);
 
 	isert_conn_disconnect(isert_conn);
 
@@ -293,16 +293,16 @@
 
 void *isert_get_priv(struct iscsi_conn *iscsi_conn)
 {
-	struct isert_connection *isert_conn = container_of(iscsi_conn,
-						struct isert_connection, iscsi);
+	struct isert_conn *isert_conn = container_of(iscsi_conn,
+						struct isert_conn, iscsi);
 
 	return isert_conn->priv_data;
 }
 
 void isert_set_priv(struct iscsi_conn *iscsi_conn, void *priv)
 {
-	struct isert_connection *isert_conn = container_of(iscsi_conn,
-					struct isert_connection, iscsi);
+	struct isert_conn *isert_conn = container_of(iscsi_conn,
+					struct isert_conn, iscsi);
 
 	isert_conn->priv_data = priv;
 }
diff --git a/scst/iscsi-scst/kernel/isert-scst/iser_global.c b/scst/iscsi-scst/kernel/isert-scst/iser_global.c
index cee6398..e6e0ec2 100644
--- a/scst/iscsi-scst/kernel/isert-scst/iser_global.c
+++ b/scst/iscsi-scst/kernel/isert-scst/iser_global.c
@@ -138,30 +138,36 @@
 	spin_lock_init(&isert_glob.portal_lock);
 	init_waitqueue_head(&isert_glob.portal_wq);
 
-	isert_glob.conn_wq = create_workqueue("isert_conn_wq");
+	isert_glob.conn_wq = alloc_workqueue("isert_conn_wq", WQ_MEM_RECLAIM, 1);
 	if (!isert_glob.conn_wq) {
 		PRINT_ERROR("Failed to alloc iser conn work queue");
 		return -ENOMEM;
 	}
 
-	isert_cmnd_cache = KMEM_CACHE(isert_cmnd,
-				     SCST_SLAB_FLAGS|SLAB_HWCACHE_ALIGN);
+	isert_cmnd_cache = KMEM_CACHE_USERCOPY(isert_cmnd,
+						SCST_SLAB_FLAGS|SLAB_HWCACHE_ALIGN,
+						iscsi);
 	if (!isert_cmnd_cache) {
-		destroy_workqueue(isert_glob.conn_wq);
 		PRINT_ERROR("Failed to alloc iser command cache");
-		return -ENOMEM;
+		goto free_wq;
 	}
 
-	isert_conn_cache = KMEM_CACHE(isert_connection,
+	isert_conn_cache = KMEM_CACHE(isert_conn,
 				     SCST_SLAB_FLAGS|SLAB_HWCACHE_ALIGN);
 	if (!isert_conn_cache) {
-		destroy_workqueue(isert_glob.conn_wq);
-		kmem_cache_destroy(isert_cmnd_cache);
 		PRINT_ERROR("Failed to alloc iser connection cache");
-		return -ENOMEM;
+		goto free_cmnd_cache;
 	}
 
 	return 0;
+
+free_cmnd_cache:
+	kmem_cache_destroy(isert_cmnd_cache);
+
+free_wq:
+	destroy_workqueue(isert_glob.conn_wq);
+
+	return -ENOMEM;
 }
 
 void isert_global_cleanup(void)
@@ -169,10 +175,8 @@
 	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);
+	kmem_cache_destroy(isert_cmnd_cache);
+	kmem_cache_destroy(isert_conn_cache);
 }
 
 int isert_get_addr_size(struct sockaddr *sa, size_t *addr_len)
diff --git a/scst/iscsi-scst/kernel/isert-scst/iser_pdu.c b/scst/iscsi-scst/kernel/isert-scst/iser_pdu.c
index da2c1e0..b4b5b5e 100644
--- a/scst/iscsi-scst/kernel/isert-scst/iser_pdu.c
+++ b/scst/iscsi-scst/kernel/isert-scst/iser_pdu.c
@@ -43,7 +43,7 @@
 #include "iser_datamover.h"
 
 static inline int isert_pdu_rx_buf_init(struct isert_cmnd *isert_pdu,
-				 struct isert_connection *isert_conn)
+				 struct isert_conn *isert_conn)
 {
 	struct isert_buf *isert_buf = &isert_pdu->buf;
 
@@ -53,7 +53,7 @@
 }
 
 static inline int isert_pdu_tx_buf_init(struct isert_cmnd *isert_pdu,
-				 struct isert_connection *isert_conn)
+				 struct isert_conn *isert_conn)
 {
 	struct isert_buf *isert_buf = &isert_pdu->buf;
 
@@ -78,7 +78,7 @@
  * 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 isert_conn *isert_conn)
 {
 	struct iscsi_cmnd *iscsi_cmnd = &isert_pdu->iscsi;
 	int err = isert_pdu_rx_buf_init(isert_pdu, isert_conn);
@@ -105,7 +105,7 @@
  * of the iscsi pdu struct
  */
 void isert_tx_pdu_init(struct isert_cmnd *isert_pdu,
-		       struct isert_connection *isert_conn)
+		       struct isert_conn *isert_conn)
 {
 	struct iscsi_cmnd *iscsi_cmnd = &isert_pdu->iscsi;
 	struct isert_buf *isert_buf = &isert_pdu->buf;
@@ -142,7 +142,7 @@
 	return;
 }
 
-static inline int isert_pdu_prepare_send(struct isert_connection *isert_conn,
+static inline int isert_pdu_prepare_send(struct isert_conn *isert_conn,
 					  struct isert_cmnd *tx_pdu)
 {
 	struct isert_device *isert_dev = isert_conn->isert_dev;
@@ -168,7 +168,7 @@
 }
 
 static int isert_alloc_for_rdma(struct isert_cmnd *pdu, int sge_cnt,
-				struct isert_connection *isert_conn)
+				struct isert_conn *isert_conn)
 {
 	struct isert_wr *wr;
 	struct ib_sge *sg_pool;
@@ -234,7 +234,7 @@
 }
 
 int isert_prepare_rdma(struct isert_cmnd *isert_pdu,
-		       struct isert_connection *isert_conn,
+		       struct isert_conn *isert_conn,
 		       enum isert_wr_op op)
 {
 	struct isert_buf *isert_buf = &isert_pdu->rdma_buf;
@@ -323,7 +323,7 @@
 	isert_pdu_kfree(pdu);
 }
 
-struct isert_cmnd *isert_rx_pdu_alloc(struct isert_connection *isert_conn,
+struct isert_cmnd *isert_rx_pdu_alloc(struct isert_conn *isert_conn,
 				      size_t size)
 {
 	struct isert_cmnd *pdu = NULL;
@@ -371,7 +371,7 @@
 	return pdu;
 }
 
-struct isert_cmnd *isert_tx_pdu_alloc(struct isert_connection *isert_conn,
+struct isert_cmnd *isert_tx_pdu_alloc(struct isert_conn *isert_conn,
 				      size_t size)
 {
 	struct isert_cmnd *pdu = NULL;
@@ -437,7 +437,7 @@
 	isert_link_recv_wrs(&from_pdu->wr[0], &to_pdu->wr[0]);
 }
 
-int isert_alloc_conn_resources(struct isert_connection *isert_conn)
+int isert_alloc_conn_resources(struct isert_conn *isert_conn)
 {
 	struct isert_cmnd *pdu, *prev_pdu = NULL, *first_pdu = NULL;
 	/* RFC states that minimum receive data size is 512 */
@@ -496,8 +496,8 @@
 
 static int isert_reinit_rx_pdu(struct isert_cmnd *pdu)
 {
-	struct isert_connection *isert_conn = container_of(pdu->iscsi.conn,
-						struct isert_connection, iscsi);
+	struct isert_conn *isert_conn = container_of(pdu->iscsi.conn,
+						struct isert_conn, iscsi);
 
 	pdu->is_rstag_valid = 0;
 	pdu->is_wstag_valid = 0;
@@ -510,8 +510,8 @@
 int isert_rx_pdu_done(struct isert_cmnd *pdu)
 {
 	int err;
-	struct isert_connection *isert_conn = container_of(pdu->iscsi.conn,
-						struct isert_connection, iscsi);
+	struct isert_conn *isert_conn = container_of(pdu->iscsi.conn,
+						struct isert_conn, iscsi);
 
 	TRACE_ENTRY();
 
@@ -539,7 +539,7 @@
 	return err;
 }
 
-void isert_free_conn_resources(struct isert_connection *isert_conn)
+void isert_free_conn_resources(struct isert_conn *isert_conn)
 {
 	struct isert_cmnd *pdu;
 
@@ -577,7 +577,7 @@
 	TRACE_EXIT();
 }
 
-int isert_pdu_send(struct isert_connection *isert_conn,
+int isert_pdu_send(struct isert_conn *isert_conn,
 		   struct isert_cmnd *tx_pdu)
 {
 	int err;
@@ -607,7 +607,7 @@
 	return err;
 }
 
-int isert_pdu_post_rdma_write(struct isert_connection *isert_conn,
+int isert_pdu_post_rdma_write(struct isert_conn *isert_conn,
 			      struct isert_cmnd *isert_cmd,
 			      struct isert_cmnd *isert_rsp,
 			      int wr_cnt)
@@ -634,7 +634,7 @@
 	return err;
 }
 
-int isert_pdu_post_rdma_read(struct isert_connection *isert_conn,
+int isert_pdu_post_rdma_read(struct isert_conn *isert_conn,
 			     struct isert_cmnd *isert_cmd, int wr_cnt)
 {
 	int err;
diff --git a/scst/iscsi-scst/kernel/isert-scst/iser_rdma.c b/scst/iscsi-scst/kernel/isert-scst/iser_rdma.c
index d4ac072..bad41bf 100644
--- a/scst/iscsi-scst/kernel/isert-scst/iser_rdma.c
+++ b/scst/iscsi-scst/kernel/isert-scst/iser_rdma.c
@@ -54,6 +54,8 @@
 static DEFINE_MUTEX(dev_list_mutex);
 
 static void isert_portal_free(struct isert_portal *portal);
+static struct rdma_cm_id *
+isert_setup_id(struct isert_portal *portal);
 
 static int isert_num_recv_posted_on_err(struct ib_recv_wr *first_ib_wr,
 					BAD_WR_MODIFIER struct ib_recv_wr *bad_wr)
@@ -67,7 +69,7 @@
 	return num_posted;
 }
 
-int isert_post_recv(struct isert_connection *isert_conn,
+int isert_post_recv(struct isert_conn *isert_conn,
 		    struct isert_wr *first_wr,
 		    int num_wr)
 {
@@ -110,7 +112,7 @@
 	return num_posted;
 }
 
-int isert_post_send(struct isert_connection *isert_conn,
+int isert_post_send(struct isert_conn *isert_conn,
 		    struct isert_wr *first_wr,
 		    int num_wr)
 {
@@ -145,7 +147,7 @@
 	return err;
 }
 
-static void isert_post_drain_sq(struct isert_connection *isert_conn)
+static void isert_post_drain_sq(struct isert_conn *isert_conn)
 {
 	BAD_WR_MODIFIER struct ib_send_wr *bad_wr;
 	struct isert_wr *drain_wr_sq = &isert_conn->drain_wr_sq;
@@ -177,7 +179,7 @@
 	}
 }
 
-static void isert_post_drain_rq(struct isert_connection *isert_conn)
+static void isert_post_drain_rq(struct isert_conn *isert_conn)
 {
 	BAD_WR_MODIFIER struct ib_recv_wr *bad_wr;
 	struct isert_wr *drain_wr_rq = &isert_conn->drain_wr_rq;
@@ -197,7 +199,7 @@
 	}
 }
 
-void isert_post_drain(struct isert_connection *isert_conn)
+void isert_post_drain(struct isert_conn *isert_conn)
 {
 	if (!test_and_set_bit(ISERT_DRAIN_POSTED, &isert_conn->flags)) {
 		mutex_lock(&isert_conn->state_mutex);
@@ -208,7 +210,7 @@
 	}
 }
 
-void isert_conn_disconnect(struct isert_connection *isert_conn)
+void isert_conn_disconnect(struct isert_conn *isert_conn)
 {
 	int err;
 
@@ -468,7 +470,7 @@
 static void isert_handle_wc(struct ib_wc *wc)
 {
 	struct isert_wr *wr = _u64_to_ptr(wc->wr_id);
-	struct isert_connection *isert_conn;
+	struct isert_conn *isert_conn;
 
 	TRACE_ENTRY();
 
@@ -578,122 +580,71 @@
 	}
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void isert_discon_do_work(void *ctx)
-#else
 static void isert_discon_do_work(struct work_struct *work)
-#endif
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	struct isert_connection *isert_conn = ctx;
-#else
-	struct isert_connection *isert_conn =
-		container_of(work, struct isert_connection, discon_work);
-#endif
+	struct isert_conn *isert_conn =
+		container_of(work, struct isert_conn, discon_work);
 
 	/* notify upper layer */
 	isert_connection_closed(&isert_conn->iscsi);
 }
 
-static void isert_sched_discon(struct isert_connection *isert_conn)
+static void isert_sched_discon(struct isert_conn *isert_conn)
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	INIT_WORK(&isert_conn->discon_work, isert_discon_do_work, isert_conn);
-#else
 	INIT_WORK(&isert_conn->discon_work, isert_discon_do_work);
-#endif
 	isert_conn_queue_work(&isert_conn->discon_work);
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void isert_conn_drained_do_work(void *ctx)
-#else
 static void isert_conn_drained_do_work(struct work_struct *work)
-#endif
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	struct isert_connection *isert_conn = ctx;
-#else
-	struct isert_connection *isert_conn =
-		container_of(work, struct isert_connection, drain_work);
-#endif
+	struct isert_conn *isert_conn =
+		container_of(work, struct isert_conn, drain_work);
 
-	isert_conn_free(isert_conn);
+	isert_put_conn(isert_conn);
 }
 
-static void isert_sched_conn_drained(struct isert_connection *isert_conn)
+static void isert_sched_conn_drained(struct isert_conn *isert_conn)
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	INIT_WORK(&isert_conn->drain_work, isert_conn_drained_do_work,
-		  isert_conn);
-#else
 	INIT_WORK(&isert_conn->drain_work, isert_conn_drained_do_work);
-#endif
 	isert_conn_queue_work(&isert_conn->drain_work);
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void isert_conn_closed_do_work(void *ctx)
-#else
 static void isert_conn_closed_do_work(struct work_struct *work)
-#endif
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	struct isert_connection *isert_conn = ctx;
-#else
-	struct isert_connection *isert_conn =
-		container_of(work, struct isert_connection, close_work);
-#endif
+	struct isert_conn *isert_conn =
+		container_of(work, struct isert_conn, close_work);
 
 	if (!test_bit(ISERT_CONNECTION_ABORTED, &isert_conn->flags))
 		isert_connection_abort(&isert_conn->iscsi);
 
-	isert_conn_free(isert_conn);
+	isert_put_conn(isert_conn);
 }
 
-static void isert_sched_conn_closed(struct isert_connection *isert_conn)
+static void isert_sched_conn_closed(struct isert_conn *isert_conn)
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	INIT_WORK(&isert_conn->close_work, isert_conn_closed_do_work,
-		  isert_conn);
-#else
 	INIT_WORK(&isert_conn->close_work, isert_conn_closed_do_work);
-#endif
 	isert_conn_queue_work(&isert_conn->close_work);
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void isert_conn_free_do_work(void *ctx)
-#else
-static void isert_conn_free_do_work(struct work_struct *work)
-#endif
+static void isert_release_work(struct work_struct *work)
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	struct isert_connection *isert_conn = ctx;
-#else
-	struct isert_connection *isert_conn =
-		container_of(work, struct isert_connection, free_work);
-#endif
+	struct isert_conn *isert_conn =
+		container_of(work, struct isert_conn, release_work);
 
-	isert_conn_free(isert_conn);
+	isert_put_conn(isert_conn);
 }
 
-void isert_sched_conn_free(struct isert_connection *isert_conn)
+void isert_sched_conn_free(struct isert_conn *isert_conn)
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	INIT_WORK(&isert_conn->free_work, isert_conn_free_do_work,
-		  isert_conn);
-#else
-	INIT_WORK(&isert_conn->free_work, isert_conn_free_do_work);
-#endif
-	isert_conn_queue_work(&isert_conn->free_work);
+	INIT_WORK(&isert_conn->release_work, isert_release_work);
+	isert_conn_queue_work(&isert_conn->release_work);
 }
 
 static void isert_handle_wc_error(struct ib_wc *wc)
 {
 	struct isert_wr *wr = _u64_to_ptr(wc->wr_id);
 	struct isert_cmnd *isert_pdu = wr->pdu;
-	struct isert_connection *isert_conn = wr->conn;
+	struct isert_conn *isert_conn = wr->conn;
 	struct isert_buf *isert_buf = wr->buf;
 	struct isert_device *isert_dev = wr->isert_dev;
 	struct ib_device *ib_dev = isert_dev->ib_dev;
@@ -788,18 +739,10 @@
 }
 
 /* callback function for isert_dev->[cq]->cq_comp_work */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) && \
-	!defined(BACKPORT_LINUX_WORKQUEUE_TO_2_6_19)
-/* A vanilla 2.6.19 or older kernel without backported OFED kernel headers. */
-static void isert_cq_comp_work_cb(void *ctx)
-{
-	struct isert_cq *cq_desc = ctx;
-#else
 static void isert_cq_comp_work_cb(struct work_struct *work)
 {
 	struct isert_cq *cq_desc =
 		container_of(work, struct isert_cq, cq_comp_work);
-#endif
 	int ret;
 
 	TRACE_ENTRY();
@@ -828,12 +771,8 @@
 {
 	struct isert_cq *cq_desc = context;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	queue_work(cq_desc->cq_workqueue, &cq_desc->cq_comp_work);
-#else
 	queue_work_on(smp_processor_id(), cq_desc->cq_workqueue,
 		      &cq_desc->cq_comp_work);
-#endif
 }
 
 static const char *ib_event_type_str(enum ib_event_type ev_type)
@@ -887,7 +826,7 @@
 	struct ib_device *ib_dev = isert_dev->ib_dev;
 	char *dev_name = ib_dev->name;
 	enum ib_event_type ev_type = async_ev->event;
-	struct isert_connection *isert_conn;
+	struct isert_conn *isert_conn;
 
 	TRACE_ENTRY();
 
@@ -1037,26 +976,12 @@
 
 		cq_desc->dev = isert_dev;
 		cq_desc->idx = i;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-		INIT_WORK(&cq_desc->cq_comp_work, isert_cq_comp_work_cb, NULL);
-#else
 		INIT_WORK(&cq_desc->cq_comp_work, isert_cq_comp_work_cb);
-#endif
 
 		snprintf(wq_name, sizeof(wq_name), "isert_cq_%p", cq_desc);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
-		cq_desc->cq_workqueue = create_singlethread_workqueue(wq_name);
-#else
-#if LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 36)
-		cq_desc->cq_workqueue = alloc_workqueue(wq_name,
-							WQ_CPU_INTENSIVE|
-							WQ_RESCUER, 1);
-#else
 		cq_desc->cq_workqueue = alloc_workqueue(wq_name,
 							WQ_CPU_INTENSIVE|
 							WQ_MEM_RECLAIM, 1);
-#endif
-#endif
 		if (unlikely(!cq_desc->cq_workqueue)) {
 			PRINT_ERROR("Failed to alloc iser cq work queue for dev:%s",
 				    ib_dev->name);
@@ -1159,15 +1084,7 @@
 	for (i = 0; i < isert_dev->num_cqs; ++i) {
 		struct isert_cq *cq_desc = &isert_dev->cq_desc[i];
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
-		/*
-		 * cancel_work_sync() was introduced in 2.6.22. We can
-		 * only wait until all scheduled work is done.
-		 */
-		flush_workqueue(cq_desc->cq_workqueue);
-#else
 		cancel_work_sync(&cq_desc->cq_comp_work);
-#endif
 
 		ib_destroy_cq(cq_desc->cq);
 		destroy_workqueue(cq_desc->cq_workqueue);
@@ -1206,7 +1123,7 @@
 	return min_idx;
 }
 
-static int isert_conn_qp_create(struct isert_connection *isert_conn)
+static int isert_conn_qp_create(struct isert_conn *isert_conn)
 {
 	struct rdma_cm_id *cm_id = isert_conn->cm_id;
 	struct isert_device *isert_dev = isert_conn->isert_dev;
@@ -1266,10 +1183,24 @@
 	goto out;
 }
 
-static struct isert_connection *isert_conn_create(struct rdma_cm_id *cm_id,
+static void
+isert_init_conn(struct isert_conn *isert_conn)
+{
+	isert_conn->state = ISER_CONN_INIT;
+	INIT_LIST_HEAD(&isert_conn->rx_buf_list);
+	INIT_LIST_HEAD(&isert_conn->tx_free_list);
+	INIT_LIST_HEAD(&isert_conn->tx_busy_list);
+	spin_lock_init(&isert_conn->tx_lock);
+	spin_lock_init(&isert_conn->post_recv_lock);
+	init_waitqueue_head(&isert_conn->rem_wait);
+	kref_init(&isert_conn->kref);
+	mutex_init(&isert_conn->state_mutex);
+}
+
+static struct isert_conn *isert_conn_create(struct rdma_cm_id *cm_id,
 						struct isert_device *isert_dev)
 {
-	struct isert_connection *isert_conn;
+	struct isert_conn *isert_conn;
 	int err;
 	struct isert_cq *cq;
 
@@ -1281,7 +1212,9 @@
 		err = -ENOMEM;
 		goto fail_alloc;
 	}
-	isert_conn->state = ISER_CONN_INIT;
+
+	isert_init_conn(isert_conn);
+
 	isert_conn->cm_id = cm_id;
 	isert_conn->isert_dev = isert_dev;
 
@@ -1307,12 +1240,6 @@
 		goto fail_login_req_pdu;
 	}
 
-	INIT_LIST_HEAD(&isert_conn->rx_buf_list);
-	INIT_LIST_HEAD(&isert_conn->tx_free_list);
-	INIT_LIST_HEAD(&isert_conn->tx_busy_list);
-	spin_lock_init(&isert_conn->tx_lock);
-	spin_lock_init(&isert_conn->post_recv_lock);
-
 	isert_conn->login_req_pdu = isert_rx_pdu_alloc(isert_conn,
 						       ISER_MAX_LOGIN_RDSL);
 	if (unlikely(!isert_conn->login_req_pdu)) {
@@ -1340,9 +1267,6 @@
 		goto fail_post_recv;
 	}
 
-	kref_init(&isert_conn->kref);
-	mutex_init(&isert_conn->state_mutex);
-
 	TRACE_EXIT();
 	return isert_conn;
 
@@ -1370,11 +1294,11 @@
 		isert_device_release(isert_dev);
 }
 
-static void isert_kref_free(struct kref *kref)
+static void isert_release_kref(struct kref *kref)
 {
 	struct isert_conn_dev *dev;
-	struct isert_connection *isert_conn =
-		container_of(kref, struct isert_connection, kref);
+	struct isert_conn *isert_conn =
+		container_of(kref, struct isert_conn, kref);
 	struct isert_device *isert_dev = isert_conn->isert_dev;
 	struct isert_cq *cq = isert_conn->qp->recv_cq->cq_context;
 
@@ -1384,8 +1308,11 @@
 
 	isert_free_conn_resources(isert_conn);
 
-	rdma_destroy_id(isert_conn->cm_id);
-	isert_conn->cm_id = NULL;
+	if (isert_conn->cm_id &&
+	    !atomic_read(&isert_conn->dev_removed)) {
+		rdma_destroy_id(isert_conn->cm_id);
+		isert_conn->cm_id = NULL;
+	}
 
 	dev = isert_get_priv(&isert_conn->iscsi);
 	if (dev) {
@@ -1408,30 +1335,35 @@
 		isert_portal_free(isert_conn->portal);
 	mutex_unlock(&dev_list_mutex);
 
-	isert_conn_kfree(isert_conn);
 
-	module_put(THIS_MODULE);
+	if (atomic_read(&isert_conn->dev_removed)) {
+		atomic_set(&isert_conn->dev_removed, 0);
+		wake_up_interruptible(&isert_conn->rem_wait);
+	} else {
+		isert_conn_kfree(isert_conn);
+		module_put(THIS_MODULE);
+	}
 
 	TRACE_EXIT();
 }
 
-void isert_conn_free(struct isert_connection *isert_conn)
+void isert_put_conn(struct isert_conn *isert_conn)
 {
 	sBUG_ON(kref_read(&isert_conn->kref) == 0);
-	kref_put(&isert_conn->kref, isert_kref_free);
+	kref_put(&isert_conn->kref, isert_release_kref);
 }
 
 static int isert_cm_disconnected_handler(struct rdma_cm_id *cm_id,
 					 struct rdma_cm_event *event)
 {
-	struct isert_connection *isert_conn = cm_id->qp->qp_context;
+	struct isert_conn *isert_conn = cm_id->qp->qp_context;
 
 	if (!test_and_set_bit(ISERT_CONNECTION_CLOSE, &isert_conn->flags))
 		isert_sched_conn_closed(isert_conn);
 	return 0;
 }
 
-static void isert_immediate_conn_close(struct isert_connection *isert_conn)
+static void isert_immediate_conn_close(struct isert_conn *isert_conn)
 {
 	set_bit(ISERT_CONNECTION_ABORTED, &isert_conn->flags);
 	set_bit(ISERT_CONNECTION_CLOSE, &isert_conn->flags);
@@ -1441,8 +1373,8 @@
 	 * one from the init and two from the connect request,
 	 * thus it is safe to deref directly before the sched_conn_free.
 	 */
-	isert_conn_free(isert_conn);
-	isert_conn_free(isert_conn);
+	isert_put_conn(isert_conn);
+	isert_put_conn(isert_conn);
 	isert_sched_conn_free(isert_conn);
 }
 
@@ -1453,7 +1385,7 @@
 	struct isert_portal *portal = cm_id->context;
 	struct ib_device *ib_dev = cm_id->device;
 	struct isert_device *isert_dev;
-	struct isert_connection *isert_conn;
+	struct isert_conn *isert_conn;
 	struct rdma_conn_param *ini_conn_param;
 	struct rdma_conn_param tgt_conn_param;
 	struct isert_cm_hdr cm_hdr = { 0 };
@@ -1525,36 +1457,8 @@
 		goto fail_accept;
 	}
 
-	switch (isert_conn->peer_addr.ss_family) {
-	case AF_INET:
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)
-		PRINT_INFO("iser accepted connection cm_id:%p "
-			   NIPQUAD_FMT "->" NIPQUAD_FMT, cm_id,
-			   NIPQUAD(((struct sockaddr_in *)&isert_conn->peer_addr)->sin_addr.s_addr),
-			   NIPQUAD(((struct sockaddr_in *)&isert_conn->self_addr)->sin_addr.s_addr));
-#else
-		PRINT_INFO("iser accepted connection cm_id:%p %pI4->%pI4",
-			   cm_id,
-			   &((struct sockaddr_in *)&isert_conn->peer_addr)->sin_addr.s_addr,
-			   &((struct sockaddr_in *)&isert_conn->self_addr)->sin_addr.s_addr);
-#endif
-		break;
-	case AF_INET6:
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
-		PRINT_INFO("iser accepted connection cm_id:%p "
-			   NIP6_FMT "->" NIP6_FMT, cm_id,
-			   NIP6(((struct sockaddr_in6 *)&isert_conn->peer_addr)->sin6_addr),
-			   NIP6(((struct sockaddr_in6 *)&isert_conn->self_addr)->sin6_addr));
-#else
-		PRINT_INFO("iser accepted connection cm_id:%p %pI6->%pI6",
-			   cm_id,
-			   &((struct sockaddr_in6 *)&isert_conn->peer_addr)->sin6_addr,
-			   &((struct sockaddr_in6 *)&isert_conn->self_addr)->sin6_addr);
-#endif
-		break;
-	default:
-		PRINT_INFO("iser accepted connection cm_id:%p", cm_id);
-	}
+	PRINT_INFO("iser accepted connection cm_id:%p %pISpc->%pISpc",
+		   cm_id, &isert_conn->peer_addr, &isert_conn->self_addr);
 
 	mutex_lock(&dev_list_mutex);
 	list_add_tail(&isert_conn->portal_node, &portal->conn_list);
@@ -1587,7 +1491,7 @@
 static int isert_cm_connect_handler(struct rdma_cm_id *cm_id,
 				    struct rdma_cm_event *event)
 {
-	struct isert_connection *isert_conn = cm_id->qp->qp_context;
+	struct isert_conn *isert_conn = cm_id->qp->qp_context;
 	int push_saved_pdu = 0;
 	int ret = 0;
 
@@ -1625,85 +1529,89 @@
 	return ret;
 }
 
-static int isert_cm_disconnect_handler(struct rdma_cm_id *cm_id,
-				       struct rdma_cm_event *event)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0) && \
+	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 7)
+static const char *const cma_events[] = {
+	[RDMA_CM_EVENT_ADDR_RESOLVED]	 = "address resolved",
+	[RDMA_CM_EVENT_ADDR_ERROR]	 = "address error",
+	[RDMA_CM_EVENT_ROUTE_RESOLVED]	 = "route resolved ",
+	[RDMA_CM_EVENT_ROUTE_ERROR]	 = "route error",
+	[RDMA_CM_EVENT_CONNECT_REQUEST]	 = "connect request",
+	[RDMA_CM_EVENT_CONNECT_RESPONSE] = "connect response",
+	[RDMA_CM_EVENT_CONNECT_ERROR]	 = "connect error",
+	[RDMA_CM_EVENT_UNREACHABLE]	 = "unreachable",
+	[RDMA_CM_EVENT_REJECTED]	 = "rejected",
+	[RDMA_CM_EVENT_ESTABLISHED]	 = "established",
+	[RDMA_CM_EVENT_DISCONNECTED]	 = "disconnected",
+	[RDMA_CM_EVENT_DEVICE_REMOVAL]	 = "device removal",
+	[RDMA_CM_EVENT_MULTICAST_JOIN]	 = "multicast join",
+	[RDMA_CM_EVENT_MULTICAST_ERROR]	 = "multicast error",
+	[RDMA_CM_EVENT_ADDR_CHANGE]	 = "address change",
+	[RDMA_CM_EVENT_TIMEWAIT_EXIT]	 = "timewait exit",
+};
+
+static const char *rdma_event_msg(enum rdma_cm_event_type event)
 {
-	struct isert_connection *isert_conn = cm_id->qp->qp_context;
+	size_t index = event;
 
-	isert_conn_disconnect(isert_conn);
-
-	return 0;
+	return (index < ARRAY_SIZE(cma_events) && cma_events[index]) ?
+			cma_events[index] : "unrecognized event";
 }
+#endif
 
-static const char *cm_event_type_str(enum rdma_cm_event_type ev_type)
-{
-	switch (ev_type) {
-	case RDMA_CM_EVENT_ADDR_RESOLVED:
-		return "ADDRESS_RESOLVED";
-	case RDMA_CM_EVENT_ADDR_ERROR:
-		return "ADDESS_ERROR";
-	case RDMA_CM_EVENT_ROUTE_RESOLVED:
-		return "ROUTE_RESOLVED";
-	case RDMA_CM_EVENT_ROUTE_ERROR:
-		return "ROUTE_ERROR";
-	case RDMA_CM_EVENT_CONNECT_REQUEST:
-		return "CONNECT_REQUEST";
-	case RDMA_CM_EVENT_CONNECT_RESPONSE:
-		return "CONNECT_RESPONSE";
-	case RDMA_CM_EVENT_CONNECT_ERROR:
-		return "CONNECT_ERROR";
-	case RDMA_CM_EVENT_UNREACHABLE:
-		return "UNREACHABLE";
-	case RDMA_CM_EVENT_REJECTED:
-		return "REJECTED";
-	case RDMA_CM_EVENT_ESTABLISHED:
-		return "ESTABLISHED";
-	case RDMA_CM_EVENT_DISCONNECTED:
-		return "DISCONNECTED";
-	case RDMA_CM_EVENT_DEVICE_REMOVAL:
-		return "DEVICE_REMOVAL";
-	case RDMA_CM_EVENT_MULTICAST_JOIN:
-		return "MULTICAST_JOIN";
-	case RDMA_CM_EVENT_MULTICAST_ERROR:
-		return "MULTICAST_ERROR";
-	case RDMA_CM_EVENT_ADDR_CHANGE:
-		return "ADDR_CHANGE";
-	case RDMA_CM_EVENT_TIMEWAIT_EXIT:
-		return "TIMEWAIT_EXIT";
-	default:
-		return "UNKNOWN";
-	}
-}
-
-static int isert_handle_failure(struct isert_connection *conn)
+static int isert_handle_failure(struct isert_conn *conn)
 {
 	isert_conn_disconnect(conn);
 	return 0;
 }
 
-static int isert_cm_evt_listener_handler(struct rdma_cm_id *cm_id,
-					 struct rdma_cm_event *cm_ev)
+static void isert_portal_reinit_id_work(struct work_struct *w)
 {
-	enum rdma_cm_event_type ev_type;
-	struct isert_portal *portal;
-	int err = 0;
+	struct isert_portal *portal = container_of(w, struct isert_portal, work);
 
-	ev_type = cm_ev->event;
+	rdma_destroy_id(portal->cm_id);
+
+	portal->cm_id = isert_setup_id(portal);
+	if (IS_ERR(portal->cm_id)) {
+		PRINT_ERROR("Failed to create rdma id, err:%ld\n",
+				PTR_ERR(portal->cm_id));
+		portal->cm_id = NULL;
+	}
+}
+
+static int isert_cm_evt_listener_handler(struct rdma_cm_id *cm_id,
+					 enum rdma_cm_event_type event)
+{
+	struct isert_portal *portal;
+	int ret = -1;
+
 	portal = cm_id->context;
 
-	switch (ev_type) {
+	switch (event) {
 	case RDMA_CM_EVENT_DEVICE_REMOVAL:
 		portal->cm_id = NULL;
-		err = -EINVAL;
 		break;
-
+	case RDMA_CM_EVENT_ADDR_CHANGE:
+		queue_work(portal->reinit_id_wq, &portal->work);
+		ret = 0;
+		break;
 	default:
 		PRINT_INFO("Listener event:%s(%d), ignored",
-			   cm_event_type_str(ev_type), ev_type);
+			   rdma_event_msg(event), event);
 		break;
 	}
 
-	return err;
+	return ret;
+}
+
+static int isert_cm_disconnect_handler(struct rdma_cm_id *cm_id,
+				       enum rdma_cm_event_type event)
+{
+	struct isert_conn *isert_conn = cm_id->qp->qp_context;
+
+	isert_conn_disconnect(isert_conn);
+
+	return 0;
 }
 
 static int isert_cm_evt_handler(struct rdma_cm_id *cm_id,
@@ -1718,17 +1626,20 @@
 	ev_type = cm_ev->event;
 	portal = cm_id->context;
 	PRINT_INFO("isert_cm_evt:%s(%d) status:%d portal:%p cm_id:%p",
-		   cm_event_type_str(ev_type), ev_type, cm_ev->status,
+		   rdma_event_msg(ev_type), ev_type, cm_ev->status,
 		   portal, cm_id);
 
 	if (portal->cm_id == cm_id) {
-		err = isert_cm_evt_listener_handler(cm_id, cm_ev);
+		err = isert_cm_evt_listener_handler(cm_id, ev_type);
 		goto out;
 	}
 
 	switch (ev_type) {
 	case RDMA_CM_EVENT_CONNECT_REQUEST:
 		err = isert_cm_conn_req_handler(cm_id, cm_ev);
+		if (unlikely(err))
+			PRINT_ERROR("Failed to handle RDMA_CM_EVENT_CONNECT_REQUEST, err:%d",
+				    err);
 		break;
 
 	case RDMA_CM_EVENT_ESTABLISHED:
@@ -1739,17 +1650,36 @@
 
 	case RDMA_CM_EVENT_CONNECT_ERROR:
 	case RDMA_CM_EVENT_REJECTED:
-		err = isert_cm_disconnect_handler(cm_id, cm_ev);
+		err = isert_cm_disconnect_handler(cm_id, ev_type);
 		break;
 
 	case RDMA_CM_EVENT_ADDR_CHANGE:
 	case RDMA_CM_EVENT_DISCONNECTED:
-	case RDMA_CM_EVENT_DEVICE_REMOVAL:
 	case RDMA_CM_EVENT_TIMEWAIT_EXIT:
-		isert_cm_disconnect_handler(cm_id, cm_ev);
+		isert_cm_disconnect_handler(cm_id, ev_type);
 		err = isert_cm_disconnected_handler(cm_id, cm_ev);
 		break;
+	case RDMA_CM_EVENT_DEVICE_REMOVAL: {
+		struct isert_conn *isert_conn = cm_id->qp->qp_context;
 
+		atomic_set(&isert_conn->dev_removed, 1);
+
+		isert_cm_disconnect_handler(cm_id, ev_type);
+		isert_cm_disconnected_handler(cm_id, cm_ev);
+
+		wait_event_interruptible(isert_conn->rem_wait,
+					 !atomic_read(&isert_conn->dev_removed));
+
+		isert_conn_kfree(isert_conn);
+		module_put(THIS_MODULE);
+		/*
+		 * return non-zero from the callback to destroy
+		 * the rdma cm id
+		 */
+		err = 1;
+
+		break;
+	}
 	case RDMA_CM_EVENT_MULTICAST_JOIN:
 	case RDMA_CM_EVENT_MULTICAST_ERROR:
 		PRINT_ERROR("UD-related event:%d, ignored", ev_type);
@@ -1766,7 +1696,7 @@
 	/* We can receive this instead of RDMA_CM_EVENT_ESTABLISHED */
 	case RDMA_CM_EVENT_UNREACHABLE:
 		{
-			struct isert_connection *isert_conn =
+			struct isert_conn *isert_conn =
 				cm_id->qp->qp_context;
 
 			mutex_lock(&isert_conn->state_mutex);
@@ -1782,19 +1712,69 @@
 		break;
 	}
 
-	if (unlikely(err))
-		PRINT_ERROR("Failed to handle rdma cm evt:%d, err:%d",
-			    ev_type, err);
-
 out:
 	TRACE_EXIT_RES(err);
 	return err;
 }
 
+static struct rdma_cm_id *
+isert_setup_id(struct isert_portal *portal)
+{
+	struct rdma_cm_id *id;
+	struct sockaddr *sa;
+	int ret;
+
+	sa = (struct sockaddr *)&portal->addr;
+
+#if !RDMA_CREATE_ID_TAKES_NET_ARG
+	id = rdma_create_id(isert_cm_evt_handler, portal, RDMA_PS_TCP,
+			       IB_QPT_RC);
+#else
+	id = rdma_create_id(iscsi_net_ns, isert_cm_evt_handler, portal,
+			       RDMA_PS_TCP, IB_QPT_RC);
+#endif
+	if (IS_ERR(id)) {
+		ret = PTR_ERR(id);
+		PRINT_ERROR("Failed to create rdma id, err:%d", ret);
+		goto out;
+	}
+
+	/*
+	 * Allow both IPv4 and IPv6 sockets to bind a single port
+	 * at the same time.
+	 */
+	ret = rdma_set_afonly(id, 1);
+	if (ret) {
+		PRINT_ERROR("Failed to set afonly, err:%d", ret);
+		goto out_id;
+	}
+
+	ret = rdma_bind_addr(id, sa);
+	if (ret) {
+		PRINT_ERROR("Failed to bind rdma addr, err:%d", ret);
+		goto out_id;
+	}
+
+	ret = rdma_listen(id, ISER_LISTEN_BACKLOG);
+	if (ret) {
+		PRINT_ERROR("Failed rdma listen, err:%d", ret);
+		goto out_id;
+	}
+
+	PRINT_INFO("iser portal with cm_id %p listens on %pISpc", id, sa);
+
+	return id;
+
+out_id:
+	rdma_destroy_id(id);
+out:
+	return ERR_PTR(ret);
+}
+
 /* create a portal, after listening starts all events
  * are received in isert_cm_evt_handler()
  */
-struct isert_portal *isert_portal_create(void)
+struct isert_portal *isert_portal_create(struct sockaddr *sa, size_t addr_len)
 {
 	struct isert_portal *portal;
 	struct rdma_cm_id *cm_id;
@@ -1813,35 +1793,35 @@
 		goto err_alloc;
 	}
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0) && \
-	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 <= 5)
-	cm_id = rdma_create_id(isert_cm_evt_handler, portal, RDMA_PS_TCP);
-#elif !RDMA_CREATE_ID_TAKES_NET_ARG
-	cm_id = rdma_create_id(isert_cm_evt_handler, portal, RDMA_PS_TCP,
-			       IB_QPT_RC);
-#else
-	cm_id = rdma_create_id(iscsi_net_ns, isert_cm_evt_handler, portal,
-			       RDMA_PS_TCP, IB_QPT_RC);
-#endif
+	portal->reinit_id_wq = alloc_ordered_workqueue("isert_reinit_id_wq", WQ_MEM_RECLAIM);
+	if (unlikely(!portal->reinit_id_wq)) {
+		PRINT_ERROR("Unable to allocate reinit workqueue");
+		err = -ENOMEM;
+		goto free_portal;
+	}
+
+	INIT_WORK(&portal->work, isert_portal_reinit_id_work);
+
+	INIT_LIST_HEAD(&portal->conn_list);
+	memcpy(&portal->addr, sa, addr_len);
+
+	cm_id = isert_setup_id(portal);
 	if (IS_ERR(cm_id)) {
 		err = PTR_ERR(cm_id);
 		PRINT_ERROR("Failed to create rdma id, err:%d", err);
-		goto create_id_err;
+		goto free_wq;
 	}
+
 	portal->cm_id = cm_id;
-
-	INIT_LIST_HEAD(&portal->conn_list);
 	isert_portal_list_add(portal);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)
-	rdma_set_afonly(cm_id, 1);
-#endif
-
 	PRINT_INFO("Created iser portal cm_id:%p", cm_id);
 out:
 	return portal;
 
-create_id_err:
+free_wq:
+	destroy_workqueue(portal->reinit_id_wq);
+free_portal:
 	kfree(portal);
 	portal = ERR_PTR(err);
 err_alloc:
@@ -1849,64 +1829,6 @@
 	goto out;
 }
 
-int isert_portal_listen(struct isert_portal *portal,
-			struct sockaddr *sa,
-			size_t addr_len)
-{
-	int err;
-
-	TRACE_ENTRY();
-	err = rdma_bind_addr(portal->cm_id, sa);
-	if (err) {
-		PRINT_WARNING("Failed to bind rdma addr, err:%d", err);
-		goto out;
-	}
-	err = rdma_listen(portal->cm_id, ISER_LISTEN_BACKLOG);
-	if (err) {
-		PRINT_ERROR("Failed rdma listen, err:%d", err);
-		goto out;
-	}
-	memcpy(&portal->addr, sa, addr_len);
-
-	switch (sa->sa_family) {
-	case AF_INET:
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)
-		PRINT_INFO("iser portal cm_id:%p listens on: "
-			   NIPQUAD_FMT ":%d", portal->cm_id,
-			   NIPQUAD(((struct sockaddr_in *)sa)->sin_addr.s_addr),
-			   (int)ntohs(((struct sockaddr_in *)sa)->sin_port));
-#else
-		PRINT_INFO("iser portal cm_id:%p listens on: %pI4:%d",
-			   portal->cm_id,
-			   &((struct sockaddr_in *)sa)->sin_addr.s_addr,
-			   (int)ntohs(((struct sockaddr_in *)sa)->sin_port));
-#endif
-		break;
-	case AF_INET6:
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
-		PRINT_INFO("iser portal cm_id:%p listens on: "
-			   NIP6_FMT " %d",
-			   portal->cm_id,
-			   NIP6(((struct sockaddr_in6 *)sa)->sin6_addr),
-			   (int)ntohs(((struct sockaddr_in6 *)sa)->sin6_port));
-#else
-		PRINT_INFO("iser portal cm_id:%p listens on: %pI6 %d",
-			   portal->cm_id,
-			   &((struct sockaddr_in6 *)sa)->sin6_addr,
-			   (int)ntohs(((struct sockaddr_in6 *)sa)->sin6_port));
-#endif
-		break;
-	default:
-		PRINT_ERROR("Unknown address family");
-		err = -EINVAL;
-		goto out;
-	}
-
-out:
-	TRACE_EXIT_RES(err);
-	return err;
-}
-
 static void isert_portal_free(struct isert_portal *portal)
 {
 	lockdep_assert_held(&dev_list_mutex);
@@ -1914,6 +1836,8 @@
 	if (portal->refcnt > 0)
 		return;
 
+	destroy_workqueue(portal->reinit_id_wq);
+
 	kfree(portal);
 	module_put(THIS_MODULE);
 
@@ -1922,7 +1846,7 @@
 
 void isert_portal_release(struct isert_portal *portal)
 {
-	struct isert_connection *conn;
+	struct isert_conn *conn;
 
 	PRINT_INFO("iser portal cm_id:%p releasing", portal->cm_id);
 
@@ -1948,17 +1872,5 @@
 
 struct isert_portal *isert_portal_start(struct sockaddr *sa, size_t addr_len)
 {
-	struct isert_portal *portal;
-	int err;
-
-	portal = isert_portal_create();
-	if (IS_ERR(portal))
-		return portal;
-
-	err = isert_portal_listen(portal, sa, addr_len);
-	if (err) {
-		isert_portal_release(portal);
-		portal = ERR_PTR(err);
-	}
-	return portal;
+	return isert_portal_create(sa, addr_len);
 }
diff --git a/scst/iscsi-scst/kernel/isert-scst/isert.c b/scst/iscsi-scst/kernel/isert-scst/isert.c
index 3c99816..5e47d8e 100644
--- a/scst/iscsi-scst/kernel/isert-scst/isert.c
+++ b/scst/iscsi-scst/kernel/isert-scst/isert.c
@@ -432,25 +432,13 @@
 
 	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;
 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
 	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));
-#else
 		pos = scnprintf(buf, size, "[%pI6]",
 			&((struct sockaddr_in6 *)&ss)->sin6_addr);
-#endif
 		break;
 #endif
 	default:
@@ -515,8 +503,8 @@
 MODULE_AUTHOR("Yan Burman");
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_IMPORT_NS(SCST);
-#define DRV_VERSION		"3.5.0" "#" __stringify(OFED_FLAVOR)
-#define DRV_RELDATE		"21 December 2020"
+#define DRV_VERSION		"3.7.0" "#" __stringify(OFED_FLAVOR)
+#define DRV_RELDATE		"26 December 2022"
 MODULE_DESCRIPTION("iSER target transport driver "
 		   "v" DRV_VERSION " (" DRV_RELDATE ")");
 module_init(isert_init_module);
diff --git a/scst/iscsi-scst/kernel/isert-scst/isert.h b/scst/iscsi-scst/kernel/isert-scst/isert.h
index dd541a1..9d5600e 100644
--- a/scst/iscsi-scst/kernel/isert-scst/isert.h
+++ b/scst/iscsi-scst/kernel/isert-scst/isert.h
@@ -44,11 +44,7 @@
 #include <linux/wait.h>
 #include <linux/init.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>
diff --git a/scst/iscsi-scst/kernel/isert-scst/isert_login.c b/scst/iscsi-scst/kernel/isert-scst/isert_login.c
index be91262..43abc5a 100644
--- a/scst/iscsi-scst/kernel/isert-scst/isert_login.c
+++ b/scst/iscsi-scst/kernel/isert-scst/isert_login.c
@@ -42,11 +42,7 @@
 #ifndef INSIDE_KERNEL_TREE
 #include <linux/version.h>
 #endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
 #include <linux/freezer.h>
-#else
-#define wait_event_freezable(wq, cond) ({ wait_event(wq, cond); 0; })
-#endif
 #include <linux/file.h>
 #include "isert_dbg.h"
 #include "../iscsi.h"
@@ -119,18 +115,10 @@
 	mutex_unlock(&isert_listen_dev.conn_lock);
 }
 
-#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);
 }
@@ -166,11 +154,7 @@
 		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
 
 	timer_setup(&conn_dev->tmo_timer, isert_conn_timer_fn, 0);
 	conn_dev->tmo_timer.expires = jiffies + 60 * HZ;
@@ -486,8 +470,8 @@
 
 void isert_connection_abort(struct iscsi_conn *iscsi_conn)
 {
-	struct isert_connection *isert_conn =
-		container_of(iscsi_conn, struct isert_connection, iscsi);
+	struct isert_conn *isert_conn =
+		container_of(iscsi_conn, struct isert_conn, iscsi);
 
 	TRACE_ENTRY();
 
@@ -953,9 +937,7 @@
 			    index);
 
 	dev->dev = device_create(isert_class, NULL, dev->devno,
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
 				 NULL,
-#endif
 				 ISER_CONN_DEV_PREFIX"%d", index);
 
 	TRACE_EXIT();
@@ -983,9 +965,7 @@
 		PRINT_ERROR("Error %d adding isert_scst", err);
 
 	dev->dev = device_create(isert_class, NULL, dev->devno,
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
 				 NULL,
-#endif
 				 "isert_scst");
 
 	TRACE_EXIT();
diff --git a/scst/iscsi-scst/kernel/nthread.c b/scst/iscsi-scst/kernel/nthread.c
index 575674b..e91a885 100644
--- a/scst/iscsi-scst/kernel/nthread.c
+++ b/scst/iscsi-scst/kernel/nthread.c
@@ -1020,10 +1020,10 @@
 					&r->write_timeout_list_entry);
 				inserted = true;
 				break;
-			} else {
-				TRACE_DBG("Skipping op %x req %p (tt %ld)",
-					cmnd_opcode(r), r, tt);
 			}
+
+			TRACE_DBG("Skipping op %x req %p (tt %ld)",
+				  cmnd_opcode(r), r, tt);
 		}
 		if (!inserted) {
 			TRACE_DBG("Add NOP IN req %p in the tail", req);
@@ -1315,19 +1315,23 @@
 			    (unsigned long long)conn->session->sid,
 			    conn->cid, conn->write_cmnd);
 	}
-	if (ref_cmd_to_parent &&
-	    ((ref_cmd->scst_cmd != NULL) || (ref_cmd->scst_aen != NULL))) {
-		if (ref_cmd->scst_state == ISCSI_CMD_STATE_AEN)
-			scst_set_aen_delivery_status(ref_cmd->scst_aen,
-				SCST_AEN_RES_FAILED);
-		else
-			scst_set_delivery_status(ref_cmd->scst_cmd,
-				SCST_CMD_DELIVERY_FAILED);
+
+	if (ref_cmd_to_parent) {
+		if (ref_cmd->scst_state == ISCSI_CMD_STATE_AEN) {
+			if (ref_cmd->scst_aen)
+				scst_set_aen_delivery_status(ref_cmd->scst_aen,
+					SCST_AEN_RES_FAILED);
+		} else {
+			if (ref_cmd->scst_cmd)
+				scst_set_delivery_status(ref_cmd->scst_cmd,
+					SCST_CMD_DELIVERY_FAILED);
+		}
 	}
+
 	goto out;
 }
 
-static int exit_tx(struct iscsi_conn *conn, int res)
+static void exit_tx(struct iscsi_conn *conn, int res)
 {
 	iscsi_extracheck_is_wr_thread(conn);
 
@@ -1351,7 +1355,8 @@
 		mark_conn_closed(conn);
 		break;
 	}
-	return res;
+
+	return;
 }
 
 static int tx_ddigest(struct iscsi_cmnd *cmnd, int state)
@@ -1373,7 +1378,7 @@
 		if (!cmnd->conn->write_size)
 			cmnd->conn->write_state = state;
 	} else
-		res = exit_tx(cmnd->conn, res);
+		exit_tx(cmnd->conn, res);
 
 	return res;
 }
@@ -1420,7 +1425,7 @@
 		if (!cmnd->conn->write_size)
 			cmnd->conn->write_state = state;
 	} else
-		res = exit_tx(cmnd->conn, res);
+		exit_tx(cmnd->conn, res);
 
 	return res;
 }
@@ -1436,7 +1441,7 @@
 		if (!conn->write_size)
 			conn->write_state = state;
 	} else
-		res = exit_tx(conn, res);
+		exit_tx(conn, res);
 
 	return res;
 }
@@ -1506,9 +1511,6 @@
 		sBUG();
 	}
 
-	if (res == 0)
-		goto out;
-
 	if (conn->write_state != TX_END)
 		goto out;
 
diff --git a/scst/iscsi-scst/kernel/session.c b/scst/iscsi-scst/kernel/session.c
index 022e6ef..59c1f3e 100644
--- a/scst/iscsi-scst/kernel/session.c
+++ b/scst/iscsi-scst/kernel/session.c
@@ -17,9 +17,7 @@
 #ifndef INSIDE_KERNEL_TREE
 #include <linux/version.h>
 #endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
 #include <linux/export.h>
-#endif
 
 #include "iscsi_trace_flag.h"
 #include "iscsi.h"
diff --git a/scst/iscsi-scst/resource_agents/SCSTTarget b/scst/iscsi-scst/resource_agents/SCSTTarget
index 3512004..48235ff 100644
--- a/scst/iscsi-scst/resource_agents/SCSTTarget
+++ b/scst/iscsi-scst/resource_agents/SCSTTarget
@@ -122,7 +122,7 @@
 <actions>
 <action name="start"        timeout="10" />
 <action name="stop"         timeout="180" />
-<action name="status "      timeout="10" interval="10" depth="0" />
+<action name="status"       timeout="10" interval="10" depth="0" />
 <action name="monitor"      timeout="10" interval="10" depth="0" />
 <action name="meta-data"    timeout="5" />
 <action name="validate-all"   timeout="10" />
diff --git a/scst/iscsi-scst/usr/Makefile b/scst/iscsi-scst/usr/Makefile
index c94a9d4..e50aad6 100644
--- a/scst/iscsi-scst/usr/Makefile
+++ b/scst/iscsi-scst/usr/Makefile
@@ -18,7 +18,7 @@
         PREFIX=/usr/local
 endif
 
-cc-option = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null \
+cc-option = $(shell if $(CC) $(1) -Werror -S -o /dev/null -xc /dev/null \
              > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi ;)
 
 SRCS_D = iscsid.c iscsi_scstd.c conn.c session.c target.c message.c ctldev.c \
diff --git a/scst/iscsi-scst/usr/iscsi_adm.c b/scst/iscsi-scst/usr/iscsi_adm.c
index 7468250..19af947 100644
--- a/scst/iscsi-scst/usr/iscsi_adm.c
+++ b/scst/iscsi-scst/usr/iscsi_adm.c
@@ -375,7 +375,7 @@
 static int sess_handle(int op, u32 set, u32 tid, u64 sid, char *params)
 {
 	int err = -EINVAL;
-	struct iscsi_adm_req req;
+	struct iscsi_adm_req req = {};
 
 	if (op == OP_NEW || op == OP_UPDATE) {
 		fprintf(stderr, "Unsupported.\n");
diff --git a/scst/iscsi-scst/usr/iscsi_adm.h b/scst/iscsi-scst/usr/iscsi_adm.h
index ae96b8c..2cce717 100644
--- a/scst/iscsi-scst/usr/iscsi_adm.h
+++ b/scst/iscsi-scst/usr/iscsi_adm.h
@@ -74,7 +74,6 @@
 	u32 tid;
 	u64 sid;
 	u32 cid;
-	u32 lun;
 
 	union {
 		struct msg_trgt trgt;
diff --git a/scst/iscsi-scst/usr/iscsi_scstd.c b/scst/iscsi-scst/usr/iscsi_scstd.c
index c86ac11..55b7d10 100644
--- a/scst/iscsi-scst/usr/iscsi_scstd.c
+++ b/scst/iscsi-scst/usr/iscsi_scstd.c
@@ -40,7 +40,7 @@
 #include "iscsid.h"
 #include "iscsi_adm.h"
 
-static char *server_address;
+static char *server_addresses[ADDR_MAX];
 uint16_t server_port = ISCSI_LISTEN_PORT;
 
 struct pollfd poll_array[POLL_MAX];
@@ -79,13 +79,13 @@
 iSCSI target daemon.\n\
   -c, --config=[path]     Execute in the config file.\n");
 		printf("\
-  -f, --foreground        make the program run in the foreground\n\
-  -d, --debug debuglevel  print debugging information\n\
-  -u, --uid=uid           run as uid, default is current user\n\
-  -g, --gid=gid           run as gid, default is current user group\n\
-  -a, --address=address   listen on specified local address instead of all\n\
-  -p, --port=port         listen on specified port instead of 3260\n\
-  -h, --help              display this help and exit\n\
+  -f, --foreground           make the program run in the foreground\n\
+  -d, --debug debuglevel     print debugging information\n\
+  -u, --uid=uid              run as uid, default is current user\n\
+  -g, --gid=gid              run as gid, default is current user group\n\
+  -a, --address=address ...  listen on specified space-separated list of local address instead of all\n\
+  -p, --port=port            listen on specified port instead of 3260\n\
+  -h, --help                 display this help and exit\n\
 ");
 	}
 	exit(1);
@@ -103,7 +103,7 @@
 {
 	struct addrinfo hints, *res, *res0;
 	char servname[64];
-	int i, sock, opt, rc;
+	int i, k, sock, opt, rc;
 
 	memset(servname, 0, sizeof(servname));
 	snprintf(servname, sizeof(servname), "%d", server_port);
@@ -112,59 +112,75 @@
 	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 < LISTEN_MAX; res = res->ai_next) {
-		sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
-		if (sock < 0) {
-			log_error("Unable to create server socket (%s) %d %d %d!",
-				  strerror(errno), res->ai_family,
-				  res->ai_socktype, res->ai_protocol);
-			continue;
+	for (k = 0; k < ADDR_MAX; k++) {
+		char *server_address;
+
+		server_address = server_addresses[k];
+		if (k > 0 && server_address == NULL)
+			break;
+
+		if (i == LISTEN_MAX) {
+			log_error("Cannot handle address %s! Too many were specified.", server_address);
+			exit(1);
 		}
 
-		sock_set_keepalive(sock, 50);
-
-		opt = 1;
-		if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))
-			log_warning("Unable to set SO_REUSEADDR on server socket (%s)!",
-				    strerror(errno));
-		opt = 1;
-		if (res->ai_family == AF_INET6 &&
-		    setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt))) {
-			log_error("Unable to restrict IPv6 socket (%s)", strerror(errno));
-			close(sock);
-			continue;
+		rc = getaddrinfo(server_address, servname, &hints, &res0);
+		if (rc != 0) {
+			log_error("Unable to get address info [%s] (%s)!",
+				  server_address, get_error_str(rc));
+			exit(1);
 		}
 
-		if (bind(sock, res->ai_addr, res->ai_addrlen)) {
-			log_error("Unable to bind server socket (%s)!", strerror(errno));
-			close(sock);
-			continue;
+		for (res = res0; res && i < LISTEN_MAX; res = res->ai_next) {
+			sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+			if (sock < 0) {
+				log_error("Unable to create server socket (%s) %d %d %d!",
+					  strerror(errno), res->ai_family,
+					  res->ai_socktype, res->ai_protocol);
+				continue;
+			}
+
+			sock_set_keepalive(sock, 50);
+
+			opt = 1;
+			if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)))
+				log_warning("Unable to set SO_REUSEADDR on server socket (%s)!",
+					    strerror(errno));
+			opt = 1;
+			if (res->ai_family == AF_INET6 &&
+				setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt))) {
+				log_error("Unable to restrict IPv6 socket (%s)", strerror(errno));
+				close(sock);
+				continue;
+			}
+
+			if (bind(sock, res->ai_addr, res->ai_addrlen)) {
+				log_error("Unable to bind server socket (%s)!", strerror(errno));
+				close(sock);
+				continue;
+			}
+
+			if (listen(sock, INCOMING_MAX)) {
+				log_error("Unable to listen to server socket (%s)!", strerror(errno));
+				close(sock);
+				continue;
+			}
+
+			set_non_blocking(sock);
+
+			array[i].fd = sock;
+			array[i].events = POLLIN;
+
+			i++;
 		}
 
-		if (listen(sock, INCOMING_MAX)) {
-			log_error("Unable to listen to server socket (%s)!", strerror(errno));
-			close(sock);
-			continue;
-		}
+		if (res)
+			log_error("Unable to listen on all available sockets.");
 
-		set_non_blocking(sock);
-
-		array[i].fd = sock;
-		array[i].events = POLLIN;
-
-		i++;
+		freeaddrinfo(res0);
 	}
 
-	freeaddrinfo(res0);
-
 	if (i == 0)
 		exit(1);
 }
@@ -226,7 +242,8 @@
 {
 	struct addrinfo hints, *res, *res0;
 	char servname[64];
-	int rc, i;
+	char *server_address;
+	int rc, i, k;
 	int iser_fd;
 	struct isert_addr_info info;
 
@@ -252,27 +269,42 @@
 	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;
+	for (k = 0; k < ADDR_MAX; k++) {
+		server_address = server_addresses[k];
 
-		rc = ioctl(iser_fd, SET_LISTEN_ADDR, &info);
-		if (rc != 0) {
-			log_error("Unable to set listen address (%s)!",
-				strerror(errno));
+		if (k > 0 && server_address == NULL)
+			break;
+
+		if (i == ISERT_MAX_PORTALS) {
+			log_error("iSER: Cannot handle address %s! Too many were specified.", server_address);
+			exit(1);
 		}
-		++i;
-	}
 
-	freeaddrinfo(res0);
+		rc = getaddrinfo(server_address, servname, &hints, &res0);
+		if (rc != 0) {
+			log_error("iSER: Unable to get address info[%s] (%s)!",
+				server_address, get_error_str(rc));
+			exit(1);
+		}
+
+		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("iSER: Unable to set listen address (%s)!",
+					strerror(errno));
+			}
+			++i;
+		}
+
+		if (res)
+			log_error("iSER: Unable to listen on all available sockets.");
+
+		freeaddrinfo(res0);
+	}
 }
 
 static int iser_getsockname(int fd, struct sockaddr *name, socklen_t *namelen)
@@ -874,13 +906,28 @@
 		case 'g':
 			gid = strtoul(optarg, NULL, 0);
 			break;
-		case 'a':
+		case 'a': {
+			char *server_address, *token;
+			int i = 0;
+
 			server_address = strdup(optarg);
 			if (server_address == NULL) {
 				perror("strdup failed");
 				exit(-1);
 			}
+
+			token = strtok(server_address, " ");
+
+			while ((i < ADDR_MAX) && token) {
+				log_debug(0, "Address to listen: %s\n", token);
+				server_addresses[i] = token;
+
+				i++;
+				token = strtok(NULL, " ");
+			}
+
 			break;
+		}
 		case 'p':
 			server_port = (uint16_t)strtoul(optarg, NULL, 0);
 			break;
diff --git a/scst/iscsi-scst/usr/iscsid.h b/scst/iscsi-scst/usr/iscsid.h
index 93df505..78f036c 100644
--- a/scst/iscsi-scst/usr/iscsid.h
+++ b/scst/iscsi-scst/usr/iscsid.h
@@ -52,7 +52,7 @@
 	struct __qelem entry;
 
 	unsigned int len;
-	char data[0];
+	char data[];
 };
 
 struct PDU {
@@ -225,7 +225,8 @@
 extern int ctrl_fd;
 extern int conn_blocked;
 
-#define LISTEN_MAX		8
+#define ADDR_MAX		32
+#define LISTEN_MAX		32
 #define INCOMING_MAX		256
 
 enum {
diff --git a/scst/iscsi-scst/usr/isns_proto.h b/scst/iscsi-scst/usr/isns_proto.h
index 84438f9..48c3461 100644
--- a/scst/iscsi-scst/usr/isns_proto.h
+++ b/scst/iscsi-scst/usr/isns_proto.h
@@ -32,7 +32,7 @@
 	uint16_t flags;
 	uint16_t transaction;
 	uint16_t sequence;
-	uint32_t pdu[0];
+	uint32_t pdu[];
 } __attribute__ ((packed));
 
 struct isns_tlv {
diff --git a/scst/iscsi-scst/usr/message.c b/scst/iscsi-scst/usr/message.c
index 4e41acf..341fbbf 100644
--- a/scst/iscsi-scst/usr/message.c
+++ b/scst/iscsi-scst/usr/message.c
@@ -58,8 +58,8 @@
 {
 	int err = 0;
 
-	log_debug(1, "request %u, tid %u, sid 0x%" PRIx64 ", cid %u, lun %u",
-		req->rcmnd, req->tid, req->sid, req->cid, req->lun);
+	log_debug(1, "request %u, tid %u, sid 0x%" PRIx64 ", cid %u",
+		req->rcmnd, req->tid, req->sid, req->cid);
 
 	switch (req->rcmnd) {
 	case C_TRGT_NEW:
diff --git a/scst/nightly/conf/nightly.conf b/scst/nightly/conf/nightly.conf
index 82b0c2c..5606c45 100644
--- a/scst/nightly/conf/nightly.conf
+++ b/scst/nightly/conf/nightly.conf
@@ -3,33 +3,45 @@
 ABT_DETAILS="x86_64"
 ABT_JOBS=5
 ABT_KERNELS="  \
-5.9.10         \
+6.1.1          \
+6.0.15-nc      \
+5.19.17-nc     \
+5.18.19-nc     \
+5.17.15-nc     \
+5.16.20-nc     \
+5.15.85-nc     \
+5.14.21-nc     \
+5.13.19-nc     \
+5.12.19-nc     \
+5.11.22-nc     \
+5.10.161-nc    \
+5.9.16-nc      \
 5.8.18-nc      \
 5.7.19-nc      \
 5.6.19-nc      \
 5.5.19-nc      \
-5.4.80-nc      \
+5.4.228-nc     \
 5.3.18-nc      \
 5.2.21-nc      \
 5.1.21-nc      \
 5.0.21-nc      \
 4.20.17-nc     \
-4.19.160-nc    \
+4.19.269-nc    \
 4.18.20-nc     \
 4.17.19-nc     \
 4.16.18-nc     \
 4.15.18-nc     \
-4.14.209-nc    \
+4.14.302-nc    \
 4.13.16-nc     \
 4.12.14-nc     \
 4.11.12-nc     \
 4.10.17-nc     \
-4.9.246-nc     \
+4.9.336-nc     \
 4.8.17-nc      \
 4.7.10-nc      \
 4.6.7-nc       \
 4.5.7-nc       \
-4.4.246-nc     \
+4.4.302-nc     \
 4.3.6-nc       \
 4.2.8-nc       \
 4.1.52-nc      \
@@ -44,35 +56,30 @@
 3.12.74-nc     \
 3.11.10-nc     \
 3.10.108-nc    \
-3.9.11-nc      \
-3.8.13-nc      \
-3.7.10-nc      \
-3.6.11-nc      \
-3.5.7-nc       \
-3.4.113-nc     \
-3.3.8-nc       \
-3.2.102-nc     \
-3.1.10-nc      \
-3.0.101-nc     \
-2.6.39.4-nc    \
-2.6.38.8-nc    \
-2.6.37.6-nc    \
-2.6.36.4-nc    \
-2.6.35.9-nc    \
-2.6.34.7-nc    \
-2.6.33.7-nc    \
-2.6.32.27-nc   \
-2.6.31.14-nc   \
-4.18.0-240.1.1.el8_3^CentOS^8.3.2011-nc	\
+5.14.0-162.6.1.el9_1^AlmaLinux^9.1-nc    \
+5.14.0-70.30.1.el9_0^AlmaLinux^9.0-nc    \
+4.18.0-425.3.1.el8^AlmaLinux^8.7-nc      \
+4.18.0-372.32.1.el8_6^AlmaLinux^8.6-nc   \
+4.18.0-348.2.1.el8_5^CentOS^8.5.2111-nc  \
+4.18.0-305.3.1.el8^CentOS^8.4.2105-nc    \
+4.18.0-240.15.1.el8_3^CentOS^8.3.2011-nc \
 4.18.0-193.28.1.el8_2^CentOS^8.2.2004-nc \
-4.18.0-147.8.1.el8_1^CentOS^8.1.1911-nc	\
-4.18.0-80.11.2.el8_0^CentOS^8.0.1905-nc	\
-3.10.0-1127.19.1.el7^CentOS^7.8.2003-nc	\
-3.10.0-1062.18.1.el7^CentOS^7.7.1908-nc	\
-3.10.0-957.27.2.el7^CentOS^7.6.1810-nc	\
-3.10.0-862.14.4.el7^CentOS^7.5.1804-nc	\
-2.6.32-754.29.2.el6^CentOS^6.10-nc	\
-2.6.32-696.30.1.el6^CentOS^6.9-nc	\
-5.4.17-2036.101.2.el7uek^UEK^7-nc	\
-4.14.35-2025.403.3.el7uek^UEK^7-nc	\
+4.18.0-147.8.1.el8_1^CentOS^8.1.1911-nc \
+4.18.0-80.11.2.el8_0^CentOS^8.0.1905-nc \
+3.10.0-1160.el7^CentOS^7.9.2009-nc      \
+3.10.0-1127.19.1.el7^CentOS^7.8.2003-nc \
+3.10.0-1062.18.1.el7^CentOS^7.7.1908-nc \
+3.10.0-957.27.2.el7^CentOS^7.6.1810-nc  \
+3.10.0-862.14.4.el7^CentOS^7.5.1804-nc  \
+5.15.0-5.76.5.1.el9uek^UEK^9-nc         \
+5.15.0-5.76.5.1.el8uek^UEK^8-nc         \
+5.4.17-2136.314.6.3.el8uek^UEK^8-nc     \
+5.4.17-2102.206.1.el8uek^UEK^8-nc       \
+5.4.17-2036.104.5.el8uek^UEK^8-nc       \
+5.4.17-2011.7.4.el8uek^UEK^8-nc         \
+5.4.17-2136.314.6.3.el7uek^UEK^7-nc     \
+5.4.17-2102.206.1.el7uek^UEK^7-nc       \
+5.4.17-2036.104.5.el7uek^UEK^7-nc       \
+5.4.17-2011.7.4.el7uek^UEK^7-nc         \
+4.1.12-124.48.6.el6uek^UEK^6-nc         \
 "
diff --git a/scst/qla2x00t-32gbit/Kbuild b/scst/qla2x00t-32gbit/Kbuild
index f25885d..053a4d9 100644
--- a/scst/qla2x00t-32gbit/Kbuild
+++ b/scst/qla2x00t-32gbit/Kbuild
@@ -9,4 +9,5 @@
 qla2xxx_scst-objs := \
 		qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \
 		qla_dbg.o qla_sup.o qla_attr.o qla_mid.o qla_dfs.o qla_bsg.o \
-		qla_nx.o qla_mr.o qla_nx2.o qla_target.o qla_tmpl.o qla_nvme.o
+		qla_nx.o qla_mr.o qla_nx2.o qla_target.o qla_tmpl.o qla_nvme.o \
+		qla_edif.o
diff --git a/scst/qla2x00t-32gbit/Makefile b/scst/qla2x00t-32gbit/Makefile
index 30c8bec..ddcaafc 100644
--- a/scst/qla2x00t-32gbit/Makefile
+++ b/scst/qla2x00t-32gbit/Makefile
@@ -4,7 +4,8 @@
 
 qla2xxx-y := qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \
 		qla_dbg.o qla_sup.o qla_attr.o qla_mid.o qla_dfs.o qla_bsg.o \
-		qla_nx.o qla_mr.o qla_nx2.o qla_target.o qla_tmpl.o qla_nvme.o
+		qla_nx.o qla_mr.o qla_nx2.o qla_target.o qla_tmpl.o qla_nvme.o \
+		qla_edif.o
 
 obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx.o
 
@@ -49,7 +50,7 @@
 all:
 	$(MAKE) -C $(KDIR) M=$(shell pwd)				\
 	  $(shell [ -n "$(PASS_CC_TO_MAKE)" ] && echo CC="$(CC)")	\
-	  $(CONFIG_SCSI_QLA2XXX_TARGET)=CONFIG_SCSI_QLA2XXX_TARGET
+	  CONFIG_SCSI_QLA2XXX_TARGET=$(CONFIG_SCSI_QLA2XXX_TARGET)
 
 install: all
 	KDIR=$(KDIR) ../scripts/sign-modules
diff --git a/scst/qla2x00t-32gbit/Makefile_in-tree b/scst/qla2x00t-32gbit/Makefile_in-tree
index 9017a0a..d6f65c4 100644
--- a/scst/qla2x00t-32gbit/Makefile_in-tree
+++ b/scst/qla2x00t-32gbit/Makefile_in-tree
@@ -1,8 +1,10 @@
 # SPDX-License-Identifier: GPL-2.0
 
-qla2xxx-y := qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \
+qla2xxx-y := \
+		qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \
 		qla_dbg.o qla_sup.o qla_attr.o qla_mid.o qla_dfs.o qla_bsg.o \
-		qla_nx.o qla_mr.o qla_nx2.o qla_target.o qla_tmpl.o qla_nvme.o
+		qla_nx.o qla_mr.o qla_nx2.o qla_target.o qla_tmpl.o qla_nvme.o \
+		qla_edif.o
 qla2x00tgt-objs := qla_tgt.o scst_qla2xxx.o
 
 obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx.o
diff --git a/scst/qla2x00t-32gbit/btree-backport.h b/scst/qla2x00t-32gbit/btree-backport.h
deleted file mode 100644
index e1800bd..0000000
--- a/scst/qla2x00t-32gbit/btree-backport.h
+++ /dev/null
@@ -1,286 +0,0 @@
-#ifndef _BTREE_BACKPORT_H_
-#define _BTREE_BACKPORT_H_
-
-struct btree_head32 {
-	struct list_head	head;
-};
-
-struct btree_node32 {
-	struct list_head	entry;
-	u32			key;
-	void			*val;
-};
-
-struct btree_head64 {
-	struct list_head	head;
-};
-
-struct btree_node64 {
-	struct list_head	entry;
-	u64			key;
-	void			*val;
-};
-
-/**
- * btree_init - initialise a btree
- *
- * @head: the btree head to initialise
- *
- * This function allocates the memory pool that the
- * btree needs. Returns zero or a negative error code
- * (-%ENOMEM) when memory allocation fails.
- */
-static inline int __must_check btree_init32(struct btree_head32 *head)
-{
-	INIT_LIST_HEAD(&head->head);
-	return 0;
-}
-
-static inline int __must_check btree_init64(struct btree_head64 *head)
-{
-	INIT_LIST_HEAD(&head->head);
-	return 0;
-}
-
-/**
- * btree_destroy - destroy mempool
- *
- * @head: the btree head to destroy
- *
- * This function destroys the internal memory pool, use only
- * when using btree_init(), not with btree_init_mempool().
- */
-static inline void btree_destroy32(struct btree_head32 *head)
-{
-}
-
-static inline void btree_destroy64(struct btree_head64 *head)
-{
-}
-
-/**
- * btree_lookup - look up a key in the btree
- *
- * @head: the btree to look in
- * @geo: the btree geometry
- * @key: the key to look up
- *
- * This function returns the value for the given key, or %NULL.
- */
-static inline void *btree_lookup32(struct btree_head32 *head, u32 key)
-{
-	struct btree_node32 *n;
-
-	list_for_each_entry(n, &head->head, entry) {
-		if (n->key == key)
-			return n->val;
-	}
-	return NULL;
-}
-
-static inline void *btree_lookup64(struct btree_head64 *head, u64 key)
-{
-	struct btree_node64 *n;
-
-	list_for_each_entry(n, &head->head, entry) {
-		if (n->key == key)
-			return n->val;
-	}
-	return NULL;
-}
-
-/**
- * btree_insert - insert an entry into the btree
- *
- * @head: the btree to add to
- * @geo: the btree geometry
- * @key: the key to add (must not already be present)
- * @val: the value to add (must not be %NULL)
- * @gfp: allocation flags for node allocations
- *
- * This function returns 0 if the item could be added, or an
- * error code if it failed (may fail due to memory pressure).
- */
-static inline int __must_check btree_insert32(struct btree_head32 *head,
-				u32 key, void *val, gfp_t gfp)
-{
-	struct btree_node32 *n, *p;
-
-	n = kmalloc(sizeof(*n), gfp);
-	if (IS_ERR(n))
-		return PTR_ERR(n);
-	n->key = key;
-	n->val = val;
-	list_for_each_entry(p, &head->head, entry) {
-		if (p->key > key)
-			break;
-	}
-	list_add(&n->entry, p->entry.prev);
-	return 0;
-}
-
-static inline int __must_check btree_insert64(struct btree_head64 *head,
-				u64 key, void *val, gfp_t gfp)
-{
-	struct btree_node64 *n, *p;
-
-	n = kmalloc(sizeof(*n), gfp);
-	if (IS_ERR(n))
-		return PTR_ERR(n);
-	n->key = key;
-	n->val = val;
-	list_for_each_entry(p, &head->head, entry) {
-		if (p->key > key)
-			break;
-	}
-	list_add(&n->entry, p->entry.prev);
-	return 0;
-}
-
-/**
- * btree_update - update an entry in the btree
- *
- * @head: the btree to update
- * @geo: the btree geometry
- * @key: the key to update
- * @val: the value to change it to (must not be %NULL)
- *
- * This function returns 0 if the update was successful, or
- * -%ENOENT if the key could not be found.
- */
-static inline int btree_update32(struct btree_head32 *head, u32 key, void *val)
-{
-	struct btree_node32 *p;
-
-	list_for_each_entry(p, &head->head, entry) {
-		if (p->key == key) {
-			p->val = val;
-			return 0;
-		}
-	}
-	return -ENOENT;
-}
-
-/**
- * btree_remove - remove an entry from the btree
- *
- * @head: the btree to update
- * @geo: the btree geometry
- * @key: the key to remove
- *
- * This function returns the removed entry, or %NULL if the key
- * could not be found.
- */
-static inline void *btree_remove32(struct btree_head32 *head, u32 key)
-{
-	struct btree_node32 *p;
-	void *val;
-
-	list_for_each_entry(p, &head->head, entry) {
-		if (p->key == key) {
-			val = p->val;
-			list_del(&p->entry);
-			kfree(p);
-			return val;
-		}
-	}
-	return NULL;
-}
-
-static inline void *btree_remove64(struct btree_head64 *head, u64 key)
-{
-	struct btree_node64 *p;
-	void *val;
-
-	list_for_each_entry(p, &head->head, entry) {
-		if (p->key == key) {
-			val = p->val;
-			list_del(&p->entry);
-			kfree(p);
-			return val;
-		}
-	}
-	return NULL;
-}
-
-/**
- * btree_last - get last entry in btree
- *
- * @head: btree head
- * @geo: btree geometry
- * @key: last key
- *
- * Returns the last entry in the btree, and sets @key to the key
- * of that entry; returns NULL if the tree is empty, in that case
- * key is not changed.
- */
-static inline void *btree_last32(struct btree_head32 *head, u32 *key)
-{
-	struct btree_node32 *p;
-
-	if (list_empty(&head->head))
-		return NULL;
-	p = list_last_entry(&head->head, typeof(*p), entry);
-	*key = p->key;
-	return p->val;
-}
-
-static inline void *btree_last64(struct btree_head64 *head, u64 *key)
-{
-	struct btree_node64 *p;
-
-	if (list_empty(&head->head))
-		return NULL;
-	p = list_last_entry(&head->head, typeof(*p), entry);
-	*key = p->key;
-	return p->val;
-}
-
-/**
- * btree_get_prev - get previous entry
- *
- * @head: btree head
- * @geo: btree geometry
- * @key: pointer to key
- *
- * The function returns the next item right before the value pointed to by
- * @key, and updates @key with its key, or returns %NULL when there is no
- * entry with a key smaller than the given key.
- */
-static inline void *btree_get_prev32(struct btree_head32 *head, u32 *key)
-{
-	struct btree_node32 *p;
-
-	list_for_each_entry_reverse(p, &head->head, entry) {
-		if (p->key < *key) {
-			*key = p->key;
-			return p->val;
-		}
-	}
-	return NULL;
-}
-
-static inline void *btree_get_prev64(struct btree_head64 *head, u64 *key)
-{
-	struct btree_node64 *p;
-
-	list_for_each_entry_reverse(p, &head->head, entry) {
-		if (p->key < *key) {
-			*key = p->key;
-			return p->val;
-		}
-	}
-	return NULL;
-}
-
-#define btree_for_each_safe32(head, key, val)	\
-	for (val = btree_last32(head, &key);	\
-	     val;				\
-	     val = btree_get_prev32(head, &key))
-
-#define btree_for_each_safe64(head, key, val)	\
-	for (val = btree_last64(head, &key);	\
-	     val;				\
-	     val = btree_get_prev64(head, &key))
-
-#endif /* _BTREE_BACKPORT_H_ */
diff --git a/scst/qla2x00t-32gbit/qla2x00-target/qla_tgt.c b/scst/qla2x00t-32gbit/qla2x00-target/qla_tgt.c
index 03fb20c..36b2896 100644
--- a/scst/qla2x00t-32gbit/qla2x00-target/qla_tgt.c
+++ b/scst/qla2x00t-32gbit/qla2x00-target/qla_tgt.c
@@ -47,9 +47,10 @@
 
 size_t qlt_add_vtarget(u64 port_name, u64 node_name, u64 parent_host)
 {
+	struct fc_vport *vport;
 	struct Scsi_Host *shost = NULL;
+	scsi_qla_host_t *vha = NULL, *npiv_vha;
 	struct qla_tgt *tgt;
-	scsi_qla_host_t *vha = NULL;
 	struct fc_vport_identifiers vid;
 	uint8_t parent_wwn[WWN_SIZE];
 
@@ -78,8 +79,14 @@
 	vid.disable = false;            /* always enabled */
 
 	/* We only allow support on Channel 0 !!! */
-	if (!fc_vport_create(shost, 0, &vid))
+	vport = fc_vport_create(shost, 0, &vid);
+	if (!vport) {
+		pr_err("fc_vport_create failed for qla2xxx_npiv\n");
 		return -EINVAL;
+	}
+
+	npiv_vha = (struct scsi_qla_host *) vport->dd_data;
+	scsi_host_get(npiv_vha->host);
 
 	return 0;
 }
diff --git a/scst/qla2x00t-32gbit/qla2x00-target/scst_qla2xxx.c b/scst/qla2x00t-32gbit/qla2x00-target/scst_qla2xxx.c
index de20da8..045d57c 100644
--- a/scst/qla2x00t-32gbit/qla2x00-target/scst_qla2xxx.c
+++ b/scst/qla2x00t-32gbit/qla2x00-target/scst_qla2xxx.c
@@ -487,8 +487,7 @@
 	      vha->host_no, vha->vp_idx, cmd, cmd->atio.u.isp24.exchange_addr,
 	      scst_cmd_get_queue_type(cmd->scst_cmd));
 
-	/* we're being call by wq, so do direct */
-	scst_cmd_init_done(cmd->scst_cmd, SCST_CONTEXT_DIRECT);
+	scst_cmd_init_done(cmd->scst_cmd, scst_work_context);
 
 out:
 	TRACE_EXIT_RES(res);
@@ -667,6 +666,26 @@
 	return res;
 }
 
+static struct qla_tgt_cmd *
+sqa_qla2xxx_find_cmd_by_tag(struct fc_port *fcport, uint64_t tag)
+{
+	struct scst_session *sess = fcport->se_sess->fabric_sess_ptr;
+	struct qla_tgt_cmd *qla_cmd = NULL;
+	struct scst_cmd *cmd;
+	unsigned long flags;
+
+	spin_lock_irqsave(&sess->sess_list_lock, flags);
+	list_for_each_entry(cmd, &sess->sess_cmd_list, sess_cmd_list_entry) {
+		if (cmd->tag == tag) {
+			qla_cmd = scst_cmd_get_tgt_priv(cmd);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&sess->sess_list_lock, flags);
+
+	return qla_cmd;
+}
+
 static void sqa_qla2xxx_free_cmd(struct qla_tgt_cmd *cmd)
 {
 	struct scst_cmd *scst_cmd = cmd->scst_cmd;
@@ -905,7 +924,16 @@
 static ssize_t sqa_hw_target_show(struct kobject *kobj,
 				  struct kobj_attribute *attr, char *buf)
 {
-	return sprintf(buf, "%d\n", 1);
+	struct scst_tgt *scst_tgt;
+	struct sqa_scst_tgt *sqa_tgt;
+	struct qla_tgt *tgt;
+
+	scst_tgt = container_of(kobj, struct scst_tgt, tgt_kobj);
+	sqa_tgt = scst_tgt_get_tgt_priv(scst_tgt);
+
+	tgt = sqa_tgt->qla_tgt;
+
+	return sprintf(buf, "%d\n", (tgt->vha->vp_idx == 0) ? 1 : 0);
 }
 
 static ssize_t sqa_node_name_show(struct kobject *kobj,
@@ -1294,8 +1322,7 @@
 #else
 	sqa_tgt->tag_num = tag_num;
 	sqa_tgt->tgt_tag_pool = kzalloc(BITS_TO_LONGS(tag_num), GFP_KERNEL);
-	res = IS_ERR(sqa_tgt->tgt_tag_pool) ? PTR_ERR(sqa_tgt->tgt_tag_pool) :
-		0;
+	res = PTR_ERR_OR_ZERO(sqa_tgt->tgt_tag_pool);
 #endif
 	if (res < 0) {
 		pr_err("Unable to init se_sess->tgt_tag_pool, tag_num: %u\n",
@@ -1408,6 +1435,39 @@
 	scst_unregister_target(sqa_tgt->scst_tgt);
 	TRACE_EXIT();
 }
+
+static void sqa_qla2xxx_drop_lport(struct qla_tgt *tgt)
+{
+	struct scsi_qla_host *vha = tgt->vha;
+
+	TRACE_ENTRY();
+
+	if (vha->vha_tgt.qla_tgt->tgt_stop &&
+			!vha->vha_tgt.qla_tgt->tgt_stopped) {
+		PRINT_INFO("sqatgt(%ld/%d): calling qlt_stop_phase2.\n",
+				vha->host_no, vha->vp_idx);
+		qlt_stop_phase2(vha->vha_tgt.qla_tgt);
+	}
+
+	qlt_lport_deregister(vha);
+
+	TRACE_EXIT();
+}
+
+static void sqa_qla2xxx_npiv_drop_lport(struct qla_tgt *tgt)
+{
+	struct scsi_qla_host *npiv_vha = tgt->vha;
+	struct qla_hw_data *ha = npiv_vha->hw;
+	scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
+
+	TRACE_ENTRY();
+
+	scsi_host_put(npiv_vha->host);
+	scsi_host_put(base_vha->host);
+
+	TRACE_EXIT();
+}
+
 /*
  * Must be called under tgt_host_action_mutex or sqa_unreg_rwsem write
  * locked.
@@ -1421,21 +1481,20 @@
 	TRACE_ENTRY();
 
 	if (vha->vha_tgt.target_lport_ptr) {
+
 		if (!vha->vha_tgt.qla_tgt->tgt_stop &&
-		    !vha->vha_tgt.qla_tgt->tgt_stopped) {
+				!vha->vha_tgt.qla_tgt->tgt_stopped) {
 			PRINT_INFO("sqatgt(%ld:%d: calling qlt_stop_phase1.\n",
-			vha->host_no, vha->vp_idx);
+					vha->host_no, vha->vp_idx);
 			qlt_stop_phase1(vha->vha_tgt.qla_tgt);
 		}
 
-		if (vha->vha_tgt.qla_tgt->tgt_stop &&
-		    !vha->vha_tgt.qla_tgt->tgt_stopped) {
-			PRINT_INFO("sqatgt(%ld/%d): calling qlt_stop_phase2.\n",
-			    vha->host_no, vha->vp_idx);
-			qlt_stop_phase2(vha->vha_tgt.qla_tgt);
-		}
-		qlt_lport_deregister(tgt->vha);
+		if (vha->vp_idx)
+			sqa_qla2xxx_npiv_drop_lport(tgt);
+		else
+			sqa_qla2xxx_drop_lport(tgt);
 	}
+
 	scst_tgt_set_tgt_priv(scst_tgt, NULL);
 
 	mutex_lock(&sqa_mutex);
@@ -1444,7 +1503,7 @@
 	mutex_unlock(&sqa_mutex);
 
 	TRACE(TRACE_MGMT, "sqatgt(%ld/%d): Target release finished sqa_tgt %p",
-	    vha->host_no, tgt->vha->vp_idx, sqa_tgt);
+	    vha->host_no, vha->vp_idx, sqa_tgt);
 
 	kfree(sqa_tgt);
 
@@ -1859,6 +1918,7 @@
 	.handle_cmd		    = sqa_qla2xxx_handle_cmd,
 	.handle_data		    = sqa_qla2xxx_handle_data,
 	.handle_tmr		    = sqa_qla2xxx_handle_tmr,
+	.find_cmd_by_tag	    = sqa_qla2xxx_find_cmd_by_tag,
 	.get_cmd		    = sqa_qla2xxx_get_cmd,
 	.rel_cmd		    = sqa_qla2xxx_rel_cmd,
 	.free_cmd		    = sqa_qla2xxx_free_cmd,
@@ -2113,7 +2173,6 @@
 				vha->host_no);
 
 			qlt_stop_phase1(sqa_tgt->qla_tgt);
-			scst_unregister_target(sqa_tgt->scst_tgt);
 			qlt_del_vtarget(wwn_to_u64(vha->port_name));
 		}
 	}
diff --git a/scst/qla2x00t-32gbit/qla2x00-target/scst_qla2xxx.h b/scst/qla2x00t-32gbit/qla2x00-target/scst_qla2xxx.h
index a3bf655..8ff88f5 100644
--- a/scst/qla2x00t-32gbit/qla2x00-target/scst_qla2xxx.h
+++ b/scst/qla2x00t-32gbit/qla2x00-target/scst_qla2xxx.h
@@ -19,8 +19,8 @@
 
 /* Driver version number */
 #define Q2T_VERSION(a, b, c, d)	(((a) << 030) + ((b) << 020) + (c) << 010 + (d))
-#define Q2T_VERSION_CODE	Q2T_VERSION(3, 5, 0, 0)
-#define Q2T_VERSION_STRING	"3.5.0"
+#define Q2T_VERSION_CODE	Q2T_VERSION(3, 7, 0, 0)
+#define Q2T_VERSION_STRING	"3.7.0"
 
 #define SQA_DEFAULT_TAGS 2048
 
diff --git a/scst/qla2x00t-32gbit/qla_attr.c b/scst/qla2x00t-32gbit/qla_attr.c
index 469f0ee..67e2133 100644
--- a/scst/qla2x00t-32gbit/qla_attr.c
+++ b/scst/qla2x00t-32gbit/qla_attr.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
 #include "qla_target.h"
@@ -157,6 +156,14 @@
 			       vha->host_no);
 		}
 		break;
+	case 10:
+		if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+			ql_log(ql_log_info, vha, 0x70e9,
+			       "Issuing MPI firmware dump on host#%ld.\n",
+			       vha->host_no);
+			ha->isp_ops->mpi_fw_dump(vha, 0);
+		}
+		break;
 	}
 	return count;
 }
@@ -548,7 +555,7 @@
 	if (!capable(CAP_SYS_ADMIN))
 		return -EINVAL;
 
-	if (IS_NOCACHE_VPD_TYPE(ha))
+	if (!IS_NOCACHE_VPD_TYPE(ha))
 		goto skip;
 
 	faddr = ha->flt_region_vpd << 2;
@@ -703,6 +710,12 @@
 		ql_log(ql_log_info, vha, 0x706e,
 		    "Issuing ISP reset.\n");
 
+		if (vha->hw->flags.port_isolated) {
+			ql_log(ql_log_info, vha, 0x706e,
+			       "Port is isolated, returning.\n");
+			return -EINVAL;
+		}
+
 		scsi_block_requests(vha->host);
 		if (IS_QLA82XX(ha)) {
 			ha->flags.isp82xx_no_md_cap = 1;
@@ -732,7 +745,7 @@
 		ql_log(ql_log_info, vha, 0x706f,
 		    "Issuing MPI reset.\n");
 
-		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+		if (IS_QLA83XX(ha)) {
 			uint32_t idc_control;
 
 			qla83xx_idc_lock(vha, 0);
@@ -744,8 +757,6 @@
 			qla83xx_idc_audit(vha, IDC_AUDIT_TIMESTAMP);
 			qla83xx_idc_unlock(vha, 0);
 			break;
-		} else if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
-			qla27xx_reset_mpi(vha);
 		} else {
 			/* Make sure FC side is not in reset */
 			WARN_ON_ONCE(qla2x00_wait_for_hba_online(vha) !=
@@ -940,9 +951,9 @@
 	if (!capable(CAP_SYS_ADMIN) || off != 0 || count > DCBX_TLV_DATA_SIZE)
 		return 0;
 
+	mutex_lock(&vha->hw->optrom_mutex);
 	if (ha->dcbx_tlv)
 		goto do_read;
-	mutex_lock(&vha->hw->optrom_mutex);
 	if (qla2x00_chip_is_down(vha)) {
 		mutex_unlock(&vha->hw->optrom_mutex);
 		return 0;
@@ -1045,9 +1056,6 @@
 			continue;
 		if (iter->type == 3 && !(IS_CNA_CAPABLE(ha)))
 			continue;
-		if (iter->type == 0x27 &&
-		    (!IS_QLA27XX(ha) || !IS_QLA28XX(ha)))
-			continue;
 
 		sysfs_remove_bin_file(&host->shost_gendev.kobj,
 		    iter->attr);
@@ -1857,6 +1865,18 @@
 	return strlen(buf);
 }
 
+static const struct {
+	u16 rate;
+	char *str;
+} port_speed_str[] = {
+	{ PORT_SPEED_4GB, "4" },
+	{ PORT_SPEED_8GB, "8" },
+	{ PORT_SPEED_16GB, "16" },
+	{ PORT_SPEED_32GB, "32" },
+	{ PORT_SPEED_64GB, "64" },
+	{ PORT_SPEED_10GB, "10" },
+};
+
 static ssize_t
 qla2x00_port_speed_show(struct device *dev, struct device_attribute *attr,
     char *buf)
@@ -1864,7 +1884,8 @@
 	struct scsi_qla_host *vha = shost_priv(dev_to_shost(dev));
 	struct qla_hw_data *ha = vha->hw;
 	ssize_t rval;
-	char *spd[7] = {"0", "0", "0", "4", "8", "16", "32"};
+	u16 i;
+	char *speed = "Unknown";
 
 	rval = qla2x00_get_data_rate(vha);
 	if (rval != QLA_SUCCESS) {
@@ -1873,9 +1894,40 @@
 		return -EINVAL;
 	}
 
-	return scnprintf(buf, PAGE_SIZE, "%s\n", spd[ha->link_data_rate]);
+	for (i = 0; i < ARRAY_SIZE(port_speed_str); i++) {
+		if (port_speed_str[i].rate != ha->link_data_rate)
+			continue;
+		speed = port_speed_str[i].str;
+		break;
+	}
+
+	return scnprintf(buf, PAGE_SIZE, "%s\n", speed);
 }
 
+static ssize_t
+qla2x00_mpi_pause_store(struct device *dev,
+	struct device_attribute *attr, const char *buf, size_t count)
+{
+	scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+	int rval = 0;
+
+	if (sscanf(buf, "%d", &rval) != 1)
+		return -EINVAL;
+
+	ql_log(ql_log_warn, vha, 0x7089, "Pausing MPI...\n");
+
+	rval = qla83xx_wr_reg(vha, 0x002012d4, 0x30000001);
+
+	if (rval != QLA_SUCCESS) {
+		ql_log(ql_log_warn, vha, 0x708a, "Unable to pause MPI.\n");
+		count = 0;
+	}
+
+	return count;
+}
+
+static DEVICE_ATTR(mpi_pause, S_IWUSR, NULL, qla2x00_mpi_pause_store);
+
 /* ----- */
 
 static ssize_t
@@ -2425,7 +2477,7 @@
 static DEVICE_ATTR(port_no, 0444, qla2x00_port_no_show, NULL);
 static DEVICE_ATTR(fw_attr, 0444, qla2x00_fw_attr_show, NULL);
 
-
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)
 struct device_attribute *qla2x00_host_attrs[] = {
 	&dev_attr_driver_version,
 	&dev_attr_fw_version,
@@ -2469,6 +2521,7 @@
 	&dev_attr_port_no,
 	&dev_attr_fw_attr,
 	&dev_attr_dport_diagnostics,
+	&dev_attr_mpi_pause,
 	NULL, /* reserve for qlini_mode */
 	NULL, /* reserve for ql2xiniexchg */
 	NULL, /* reserve for ql2xexchoffld */
@@ -2489,6 +2542,78 @@
 	attr++;
 	*attr = &dev_attr_ql2xexchoffld;
 }
+#else
+static struct attribute *qla2x00_host_attrs[] = {
+	&dev_attr_driver_version.attr,
+	&dev_attr_fw_version.attr,
+	&dev_attr_serial_num.attr,
+	&dev_attr_isp_name.attr,
+	&dev_attr_isp_id.attr,
+	&dev_attr_model_name.attr,
+	&dev_attr_model_desc.attr,
+	&dev_attr_pci_info.attr,
+	&dev_attr_link_state.attr,
+	&dev_attr_zio.attr,
+	&dev_attr_zio_timer.attr,
+	&dev_attr_beacon.attr,
+	&dev_attr_beacon_config.attr,
+	&dev_attr_optrom_bios_version.attr,
+	&dev_attr_optrom_efi_version.attr,
+	&dev_attr_optrom_fcode_version.attr,
+	&dev_attr_optrom_fw_version.attr,
+	&dev_attr_84xx_fw_version.attr,
+	&dev_attr_total_isp_aborts.attr,
+	&dev_attr_serdes_version.attr,
+	&dev_attr_mpi_version.attr,
+	&dev_attr_phy_version.attr,
+	&dev_attr_flash_block_size.attr,
+	&dev_attr_vlan_id.attr,
+	&dev_attr_vn_port_mac_address.attr,
+	&dev_attr_fabric_param.attr,
+	&dev_attr_fw_state.attr,
+	&dev_attr_optrom_gold_fw_version.attr,
+	&dev_attr_thermal_temp.attr,
+	&dev_attr_diag_requests.attr,
+	&dev_attr_diag_megabytes.attr,
+	&dev_attr_fw_dump_size.attr,
+	&dev_attr_allow_cna_fw_dump.attr,
+	&dev_attr_pep_version.attr,
+	&dev_attr_min_supported_speed.attr,
+	&dev_attr_max_supported_speed.attr,
+	&dev_attr_zio_threshold.attr,
+	&dev_attr_dif_bundle_statistics.attr,
+	&dev_attr_port_speed.attr,
+	&dev_attr_port_no.attr,
+	&dev_attr_fw_attr.attr,
+	&dev_attr_dport_diagnostics.attr,
+	&dev_attr_mpi_pause.attr,
+	&dev_attr_qlini_mode.attr,
+	&dev_attr_ql2xiniexchg.attr,
+	&dev_attr_ql2xexchoffld.attr,
+	NULL,
+};
+
+static umode_t qla_host_attr_is_visible(struct kobject *kobj,
+					struct attribute *attr, int i)
+{
+	if (ql2x_ini_mode != QLA2XXX_INI_MODE_DUAL &&
+	    (attr == &dev_attr_qlini_mode.attr ||
+	     attr == &dev_attr_ql2xiniexchg.attr ||
+	     attr == &dev_attr_ql2xexchoffld.attr))
+		return 0;
+	return attr->mode;
+}
+
+static const struct attribute_group qla2x00_host_attr_group = {
+	.is_visible = qla_host_attr_is_visible,
+	.attrs = qla2x00_host_attrs
+};
+
+const struct attribute_group *qla2x00_host_groups[] = {
+	&qla2x00_host_attr_group,
+	NULL
+};
+#endif
 
 /* Host attributes. */
 
@@ -2637,7 +2762,17 @@
 static inline void
 qla2x00_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout)
 {
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
+	fc_port_t *fcport = *(fc_port_t **)rport->dd_data;
+#endif
+
 	rport->dev_loss_tmo = timeout ? timeout : 1;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
+	if (IS_ENABLED(CONFIG_NVME_FC) && fcport && fcport->nvme_remote_port)
+		nvme_fc_set_remoteport_devloss(fcport->nvme_remote_port,
+					       rport->dev_loss_tmo);
+#endif
 }
 
 static void
@@ -2650,17 +2785,27 @@
 	if (!fcport)
 		return;
 
-	/* Now that the rport has been deleted, set the fcport state to
-	   FCS_DEVICE_DEAD */
-	qla2x00_set_fcport_state(fcport, FCS_DEVICE_DEAD);
+	ql_dbg(ql_dbg_async, fcport->vha, 0x5101,
+	       DBG_FCPORT_PRFMT(fcport, "dev_loss_tmo expiry, rport_state=%d",
+				rport->port_state));
+
+	/*
+	 * Now that the rport has been deleted, set the fcport state to
+	 * FCS_DEVICE_DEAD, if the fcport is still lost.
+	 */
+	if (fcport->scan_state != QLA_FCPORT_FOUND)
+		qla2x00_set_fcport_state(fcport, FCS_DEVICE_DEAD);
 
 	/*
 	 * Transport has effectively 'deleted' the rport, clear
 	 * all local references.
 	 */
 	spin_lock_irqsave(host->host_lock, flags);
-	fcport->rport = fcport->drport = NULL;
-	*((fc_port_t **)rport->dd_data) = NULL;
+	/* Confirm port has not reappeared before clearing pointers. */
+	if (rport->port_state != FC_PORTSTATE_ONLINE) {
+		fcport->rport = fcport->drport = NULL;
+		*((fc_port_t **)rport->dd_data) = NULL;
+	}
 	spin_unlock_irqrestore(host->host_lock, flags);
 
 	if (test_bit(ABORT_ISP_ACTIVE, &fcport->vha->dpc_flags))
@@ -2693,14 +2838,24 @@
 	/*
 	 * At this point all fcport's software-states are cleared.  Perform any
 	 * final cleanup of firmware resources (PCBs and XCBs).
+	 *
+	 * Attempt to cleanup only lost devices.
 	 */
 	if (fcport->loop_id != FC_NO_LOOP_ID) {
-		if (IS_FWI2_CAPABLE(fcport->vha->hw))
-			fcport->vha->hw->isp_ops->fabric_logout(fcport->vha,
-			    fcport->loop_id, fcport->d_id.b.domain,
-			    fcport->d_id.b.area, fcport->d_id.b.al_pa);
-		else
+		if (IS_FWI2_CAPABLE(fcport->vha->hw) &&
+		    fcport->scan_state != QLA_FCPORT_FOUND) {
+			if (fcport->loop_id != FC_NO_LOOP_ID)
+				fcport->logout_on_delete = 1;
+
+			if (!EDIF_NEGOTIATION_PENDING(fcport)) {
+				ql_dbg(ql_dbg_disc, fcport->vha, 0x911e,
+				       "%s %d schedule session deletion\n", __func__,
+				       __LINE__);
+				qlt_schedule_sess_for_deletion(fcport);
+			}
+		} else if (!IS_FWI2_CAPABLE(fcport->vha->hw)) {
 			qla2x00_port_logout(fcport->vha, fcport);
+		}
 	}
 }
 
@@ -2712,6 +2867,9 @@
 	if (IS_QLAFX00(vha->hw))
 		return 0;
 
+	if (vha->hw->flags.port_isolated)
+		return 0;
+
 	qla2x00_loop_reset(vha);
 	return 0;
 }
@@ -2726,6 +2884,9 @@
 	struct link_statistics *stats;
 	dma_addr_t stats_dma;
 	struct fc_host_statistics *p = &vha->fc_host_stat;
+	struct qla_qpair *qpair;
+	int i;
+	u64 ib = 0, ob = 0, ir = 0, or = 0;
 
 	memset(p, -1, sizeof(*p));
 
@@ -2762,6 +2923,27 @@
 	if (rval != QLA_SUCCESS)
 		goto done_free;
 
+	/* --- */
+	for (i = 0; i < vha->hw->max_qpairs; i++) {
+		qpair = vha->hw->queue_pair_map[i];
+		if (!qpair)
+			continue;
+		ir += qpair->counters.input_requests;
+		or += qpair->counters.output_requests;
+		ib += qpair->counters.input_bytes;
+		ob += qpair->counters.output_bytes;
+	}
+	ir += ha->base_qpair->counters.input_requests;
+	or += ha->base_qpair->counters.output_requests;
+	ib += ha->base_qpair->counters.input_bytes;
+	ob += ha->base_qpair->counters.output_bytes;
+
+	ir += vha->qla_stats.input_requests;
+	or += vha->qla_stats.output_requests;
+	ib += vha->qla_stats.input_bytes;
+	ob += vha->qla_stats.output_bytes;
+	/* --- */
+
 	p->link_failure_count = le32_to_cpu(stats->link_fail_cnt);
 	p->loss_of_sync_count = le32_to_cpu(stats->loss_sync_cnt);
 	p->loss_of_signal_count = le32_to_cpu(stats->loss_sig_cnt);
@@ -2781,15 +2963,16 @@
 			p->rx_words = le64_to_cpu(stats->fpm_recv_word_cnt);
 			p->tx_words = le64_to_cpu(stats->fpm_xmit_word_cnt);
 		} else {
-			p->rx_words = vha->qla_stats.input_bytes;
-			p->tx_words = vha->qla_stats.output_bytes;
+			p->rx_words = ib >> 2;
+			p->tx_words = ob >> 2;
 		}
 	}
+
 	p->fcp_control_requests = vha->qla_stats.control_requests;
-	p->fcp_input_requests = vha->qla_stats.input_requests;
-	p->fcp_output_requests = vha->qla_stats.output_requests;
-	p->fcp_input_megabytes = vha->qla_stats.input_bytes >> 20;
-	p->fcp_output_megabytes = vha->qla_stats.output_bytes >> 20;
+	p->fcp_input_requests = ir;
+	p->fcp_output_requests = or;
+	p->fcp_input_megabytes  = ib >> 20;
+	p->fcp_output_megabytes = ob >> 20;
 	p->seconds_since_last_reset =
 	    get_jiffies_64() - vha->qla_stats.jiffies_at_last_reset;
 	do_div(p->seconds_since_last_reset, HZ);
@@ -2809,9 +2992,18 @@
 	struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
 	struct link_statistics *stats;
 	dma_addr_t stats_dma;
+	int i;
+	struct qla_qpair *qpair;
 
 	memset(&vha->qla_stats, 0, sizeof(vha->qla_stats));
 	memset(&vha->fc_host_stat, 0, sizeof(vha->fc_host_stat));
+	for (i = 0; i < vha->hw->max_qpairs; i++) {
+		qpair = vha->hw->queue_pair_map[i];
+		if (!qpair)
+			continue;
+		memset(&qpair->counters, 0, sizeof(qpair->counters));
+	}
+	memset(&ha->base_qpair->counters, 0, sizeof(qpair->counters));
 
 	vha->qla_stats.jiffies_at_last_reset = get_jiffies_64();
 
@@ -2828,7 +3020,10 @@
 
 		/* reset firmware statistics */
 		rval = qla24xx_get_isp_stats(base_vha, stats, stats_dma, BIT_0);
-		WARN_ONCE(rval != QLA_SUCCESS, "rval = %d\n", rval);
+		if (rval != QLA_SUCCESS)
+			ql_log(ql_log_warn, vha, 0x70de,
+			       "Resetting ISP statistics failed: rval = %d\n",
+			       rval);
 
 		dma_free_coherent(&ha->pdev->dev, sizeof(*stats),
 		    stats, stats_dma);
@@ -3056,6 +3251,9 @@
 	qla2x00_wait_for_sess_deletion(vha);
 
 	qla_nvme_delete(vha);
+	qla_enode_stop(vha);
+	qla_edb_stop(vha);
+
 	vha->flags.delete_progress = 1;
 
 	qlt_remove_target(ha, vha);
@@ -3203,11 +3401,34 @@
 	.bsg_timeout = qla24xx_bsg_timeout,
 };
 
+static uint
+qla2x00_get_host_supported_speeds(scsi_qla_host_t *vha, uint speeds)
+{
+	uint supported_speeds = FC_PORTSPEED_UNKNOWN;
+
+	if (speeds & FDMI_PORT_SPEED_64GB)
+		supported_speeds |= FC_PORTSPEED_64GBIT;
+	if (speeds & FDMI_PORT_SPEED_32GB)
+		supported_speeds |= FC_PORTSPEED_32GBIT;
+	if (speeds & FDMI_PORT_SPEED_16GB)
+		supported_speeds |= FC_PORTSPEED_16GBIT;
+	if (speeds & FDMI_PORT_SPEED_8GB)
+		supported_speeds |= FC_PORTSPEED_8GBIT;
+	if (speeds & FDMI_PORT_SPEED_4GB)
+		supported_speeds |= FC_PORTSPEED_4GBIT;
+	if (speeds & FDMI_PORT_SPEED_2GB)
+		supported_speeds |= FC_PORTSPEED_2GBIT;
+	if (speeds & FDMI_PORT_SPEED_1GB)
+		supported_speeds |= FC_PORTSPEED_1GBIT;
+
+	return supported_speeds;
+}
+
 void
 qla2x00_init_host_attr(scsi_qla_host_t *vha)
 {
 	struct qla_hw_data *ha = vha->hw;
-	u32 speeds = FC_PORTSPEED_UNKNOWN;
+	u32 speeds = 0, fdmi_speed = 0;
 
 	fc_host_dev_loss_tmo(vha->host) = ha->port_down_retry_count;
 	fc_host_node_name(vha->host) = wwn_to_u64(vha->node_name);
@@ -3217,46 +3438,8 @@
 	fc_host_max_npiv_vports(vha->host) = ha->max_npiv_vports;
 	fc_host_npiv_vports_inuse(vha->host) = ha->cur_vport_count;
 
-	if (IS_CNA_CAPABLE(ha))
-		speeds = FC_PORTSPEED_10GBIT;
-	else if (IS_QLA28XX(ha) || IS_QLA27XX(ha)) {
-		if (ha->max_supported_speed == 2) {
-			if (ha->min_supported_speed <= 6)
-				speeds |= FC_PORTSPEED_64GBIT;
-		}
-		if (ha->max_supported_speed == 2 ||
-		    ha->max_supported_speed == 1) {
-			if (ha->min_supported_speed <= 5)
-				speeds |= FC_PORTSPEED_32GBIT;
-		}
-		if (ha->max_supported_speed == 2 ||
-		    ha->max_supported_speed == 1 ||
-		    ha->max_supported_speed == 0) {
-			if (ha->min_supported_speed <= 4)
-				speeds |= FC_PORTSPEED_16GBIT;
-		}
-		if (ha->max_supported_speed == 1 ||
-		    ha->max_supported_speed == 0) {
-			if (ha->min_supported_speed <= 3)
-				speeds |= FC_PORTSPEED_8GBIT;
-		}
-		if (ha->max_supported_speed == 0) {
-			if (ha->min_supported_speed <= 2)
-				speeds |= FC_PORTSPEED_4GBIT;
-		}
-	} else if (IS_QLA2031(ha))
-		speeds = FC_PORTSPEED_16GBIT|FC_PORTSPEED_8GBIT|
-			FC_PORTSPEED_4GBIT;
-	else if (IS_QLA25XX(ha) || IS_QLAFX00(ha))
-		speeds = FC_PORTSPEED_8GBIT|FC_PORTSPEED_4GBIT|
-			FC_PORTSPEED_2GBIT|FC_PORTSPEED_1GBIT;
-	else if (IS_QLA24XX_TYPE(ha))
-		speeds = FC_PORTSPEED_4GBIT|FC_PORTSPEED_2GBIT|
-			FC_PORTSPEED_1GBIT;
-	else if (IS_QLA23XX(ha))
-		speeds = FC_PORTSPEED_2GBIT|FC_PORTSPEED_1GBIT;
-	else
-		speeds = FC_PORTSPEED_1GBIT;
+	fdmi_speed = qla25xx_fdmi_port_speed_capability(ha);
+	speeds = qla2x00_get_host_supported_speeds(vha, fdmi_speed);
 
 	fc_host_supported_speeds(vha->host) = speeds;
 }
diff --git a/scst/qla2x00t-32gbit/qla_bsg.c b/scst/qla2x00t-32gbit/qla_bsg.c
index 9943d2d..517f680 100644
--- a/scst/qla2x00t-32gbit/qla_bsg.c
+++ b/scst/qla2x00t-32gbit/qla_bsg.c
@@ -1,10 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
+#include "qla_gbl.h"
 
 #include <linux/kthread.h>
 #include <linux/vmalloc.h>
@@ -12,24 +12,6 @@
 
 #ifdef NEW_LIBFC_API
 #include <linux/bsg-lib.h>
-#else
-static inline struct Scsi_Host *fc_bsg_to_shost(struct fc_bsg_job *job)
-{
-	return job->shost;
-}
-
-static inline struct fc_rport *fc_bsg_to_rport(struct fc_bsg_job *job)
-{
-	return job->rport;
-}
-
-static inline void bsg_job_done_backport(struct fc_bsg_job *job, int result,
-					 unsigned int reply_payload_rcv_len)
-{
-	job->job_done(job);
-}
-
-#define bsg_job_done bsg_job_done_backport
 #endif
 
 static void qla2xxx_free_fcport_work(struct work_struct *work)
@@ -43,27 +25,25 @@
 /* BSG support for ELS/CT pass through */
 void qla2x00_bsg_job_done(srb_t *sp, int res)
 {
-#ifndef NEW_LIBFC_API
-	struct fc_bsg_job *bsg_job = sp->u.bsg_job;
-#else
-	struct bsg_job *bsg_job = sp->u.bsg_job;
-#endif
+	BSG_JOB_TYPE *bsg_job = sp->u.bsg_job;
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 
+	ql_dbg(ql_dbg_user, sp->vha, 0x7009,
+	    "%s: sp hdl %x, result=%x bsg ptr %p\n",
+	    __func__, sp->handle, res, bsg_job);
+
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
+
 	bsg_reply->result = res;
 	bsg_job_done(bsg_job, bsg_reply->result,
 		       bsg_reply->reply_payload_rcv_len);
-	sp->free(sp);
 }
 
 void qla2x00_bsg_sp_free(srb_t *sp)
 {
 	struct qla_hw_data *ha = sp->vha->hw;
-#ifndef NEW_LIBFC_API
-	struct fc_bsg_job *bsg_job = sp->u.bsg_job;
-#else
-	struct bsg_job *bsg_job = sp->u.bsg_job;
-#endif
+	BSG_JOB_TYPE *bsg_job = sp->u.bsg_job;
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 	struct qla_mt_iocb_rqst_fx00 *piocb_rqst;
 
@@ -81,11 +61,19 @@
 			    bsg_job->reply_payload.sg_list,
 			    bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
 	} else {
-		dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
-		    bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
 
-		dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
-		    bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+		if (sp->remap.remapped) {
+			dma_pool_free(ha->purex_dma_pool, sp->remap.rsp.buf,
+			    sp->remap.rsp.dma);
+			dma_pool_free(ha->purex_dma_pool, sp->remap.req.buf,
+			    sp->remap.req.dma);
+		} else {
+			dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
+				bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+
+			dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
+				bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+		}
 	}
 
 	if (sp->type == SRB_CT_CMD ||
@@ -152,13 +140,8 @@
 	return ret;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla24xx_proc_fcp_prio_cfg_cmd(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla24xx_proc_fcp_prio_cfg_cmd(struct bsg_job *bsg_job)
-#endif
+qla24xx_proc_fcp_prio_cfg_cmd(BSG_JOB_TYPE *bsg_job)
 {
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
 	struct fc_bsg_request *bsg_request = bsg_job->request;
@@ -285,13 +268,8 @@
 	return ret;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla2x00_process_els(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla2x00_process_els(struct bsg_job *bsg_job)
-#endif
+qla2x00_process_els(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 	struct fc_rport *rport;
@@ -304,6 +282,7 @@
 	int req_sg_cnt, rsp_sg_cnt;
 	int rval =  (DID_ERROR << 16);
 	uint16_t nextlid = 0;
+	uint32_t els_cmd = 0;
 
 	if (bsg_request->msgcode == FC_BSG_RPT_ELS) {
 		rport = fc_bsg_to_rport(bsg_job);
@@ -317,6 +296,9 @@
 		vha = shost_priv(host);
 		ha = vha->hw;
 		type = "FC_BSG_HST_ELS_NOLOGIN";
+		els_cmd = bsg_request->rqst_data.h_els.command_code;
+		if (els_cmd == ELS_AUTH_ELS)
+			return qla_edif_process_els(vha, bsg_job);
 	}
 
 	if (!vha->flags.online) {
@@ -453,7 +435,7 @@
 	goto done_free_fcport;
 
 done_free_fcport:
-	if (bsg_request->msgcode == FC_BSG_RPT_ELS)
+	if (bsg_request->msgcode != FC_BSG_RPT_ELS)
 		qla2x00_free_fcport(fcport);
 done:
 	return rval;
@@ -473,13 +455,8 @@
 	return iocbs;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla2x00_process_ct(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla2x00_process_ct(struct bsg_job *bsg_job)
-#endif
+qla2x00_process_ct(BSG_JOB_TYPE *bsg_job)
 {
 	srb_t *sp;
 	struct fc_bsg_request *bsg_request = bsg_job->request;
@@ -756,13 +733,8 @@
 	return rval;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla2x00_process_loopback(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla2x00_process_loopback(struct bsg_job *bsg_job)
-#endif
+qla2x00_process_loopback(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
@@ -1004,13 +976,8 @@
 	return rval;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla84xx_reset(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla84xx_reset(struct bsg_job *bsg_job)
-#endif
+qla84xx_reset(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
@@ -1045,13 +1012,8 @@
 	return rval;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla84xx_updatefw(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla84xx_updatefw(struct bsg_job *bsg_job)
-#endif
+qla84xx_updatefw(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
@@ -1160,13 +1122,8 @@
 	return rval;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla84xx_mgmt_cmd(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla84xx_mgmt_cmd(struct bsg_job *bsg_job)
-#endif
+qla84xx_mgmt_cmd(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
@@ -1361,13 +1318,8 @@
 	return rval;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla24xx_iidma(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla24xx_iidma(struct bsg_job *bsg_job)
-#endif
+qla24xx_iidma(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
@@ -1455,15 +1407,9 @@
 	return rval;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla2x00_optrom_setup(struct fc_bsg_job *bsg_job, scsi_qla_host_t *vha,
+qla2x00_optrom_setup(BSG_JOB_TYPE *bsg_job, scsi_qla_host_t *vha,
 	uint8_t is_update)
-#else
-static int
-qla2x00_optrom_setup(struct bsg_job *bsg_job, scsi_qla_host_t *vha,
-	uint8_t is_update)
-#endif
 {
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 	uint32_t start = 0;
@@ -1531,13 +1477,8 @@
 	return 0;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla2x00_read_optrom(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla2x00_read_optrom(struct bsg_job *bsg_job)
-#endif
+qla2x00_read_optrom(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
@@ -1573,13 +1514,8 @@
 	return rval;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla2x00_update_optrom(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla2x00_update_optrom(struct bsg_job *bsg_job)
-#endif
+qla2x00_update_optrom(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
@@ -1619,13 +1555,8 @@
 	return rval;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla2x00_update_fru_versions(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla2x00_update_fru_versions(struct bsg_job *bsg_job)
-#endif
+qla2x00_update_fru_versions(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
@@ -1677,13 +1608,8 @@
 	return 0;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla2x00_read_fru_status(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla2x00_read_fru_status(struct bsg_job *bsg_job)
-#endif
+qla2x00_read_fru_status(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
@@ -1733,13 +1659,8 @@
 	return 0;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla2x00_write_fru_status(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla2x00_write_fru_status(struct bsg_job *bsg_job)
-#endif
+qla2x00_write_fru_status(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
@@ -1785,13 +1706,8 @@
 	return 0;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla2x00_write_i2c(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla2x00_write_i2c(struct bsg_job *bsg_job)
-#endif
+qla2x00_write_i2c(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
@@ -1836,13 +1752,8 @@
 	return 0;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla2x00_read_i2c(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla2x00_read_i2c(struct bsg_job *bsg_job)
-#endif
+qla2x00_read_i2c(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
@@ -1891,13 +1802,8 @@
 	return 0;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla24xx_process_bidir_cmd(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla24xx_process_bidir_cmd(struct bsg_job *bsg_job)
-#endif
+qla24xx_process_bidir_cmd(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
@@ -2074,13 +1980,8 @@
 	return 0;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qlafx00_mgmt_cmd(struct fc_bsg_job *bsg_job)
-#else
-static int
-qlafx00_mgmt_cmd(struct bsg_job *bsg_job)
-#endif
+qlafx00_mgmt_cmd(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
@@ -2202,13 +2103,8 @@
 	return rval;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla26xx_serdes_op(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla26xx_serdes_op(struct bsg_job *bsg_job)
-#endif
+qla26xx_serdes_op(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
@@ -2249,13 +2145,8 @@
 	return 0;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla8044_serdes_op(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla8044_serdes_op(struct bsg_job *bsg_job)
-#endif
+qla8044_serdes_op(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
@@ -2296,13 +2187,8 @@
 	return 0;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla27xx_get_flash_upd_cap(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla27xx_get_flash_upd_cap(struct bsg_job *bsg_job)
-#endif
+qla27xx_get_flash_upd_cap(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
@@ -2333,13 +2219,8 @@
 	return 0;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla27xx_set_flash_upd_cap(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla27xx_set_flash_upd_cap(struct bsg_job *bsg_job)
-#endif
+qla27xx_set_flash_upd_cap(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
@@ -2384,13 +2265,8 @@
 	return 0;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla27xx_get_bbcr_data(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla27xx_get_bbcr_data(struct bsg_job *bsg_job)
-#endif
+qla27xx_get_bbcr_data(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
@@ -2448,13 +2324,8 @@
 	return 0;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla2x00_get_priv_stats(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla2x00_get_priv_stats(struct bsg_job *bsg_job)
-#endif
+qla2x00_get_priv_stats(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
@@ -2512,13 +2383,8 @@
 	return 0;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla2x00_do_dport_diagnostics(struct fc_bsg_job *bsg_job)
-#else
-static int
-qla2x00_do_dport_diagnostics(struct bsg_job *bsg_job)
-#endif
+qla2x00_do_dport_diagnostics(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
@@ -2561,13 +2427,91 @@
 	return 0;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla2x00_get_flash_image_status(struct fc_bsg_job *bsg_job)
-#else
+qla2x00_do_dport_diagnostics_v2(BSG_JOB_TYPE *bsg_job)
+{
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
+	scsi_qla_host_t *vha = shost_priv(host);
+	int rval;
+	struct qla_dport_diag_v2 *dd;
+	mbx_cmd_t mc;
+	mbx_cmd_t *mcp = &mc;
+	uint16_t options;
+
+	if (!IS_DPORT_CAPABLE(vha->hw))
+		return -EPERM;
+
+	dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+	if (!dd)
+		return -ENOMEM;
+
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+			bsg_job->request_payload.sg_cnt, dd, sizeof(*dd));
+
+	options  = dd->options;
+
+	/*  Check dport Test in progress */
+	if (options == QLA_GET_DPORT_RESULT_V2 &&
+	    vha->dport_status & DPORT_DIAG_IN_PROGRESS) {
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+					EXT_STATUS_DPORT_DIAG_IN_PROCESS;
+		goto dportcomplete;
+	}
+
+	/*  Check chip reset in progress and start/restart requests arrive */
+	if (vha->dport_status & DPORT_DIAG_CHIP_RESET_IN_PROGRESS &&
+	    (options == QLA_START_DPORT_TEST_V2 ||
+	     options == QLA_RESTART_DPORT_TEST_V2)) {
+		vha->dport_status &= ~DPORT_DIAG_CHIP_RESET_IN_PROGRESS;
+	}
+
+	/*  Check chip reset in progress and get result request arrive */
+	if (vha->dport_status & DPORT_DIAG_CHIP_RESET_IN_PROGRESS &&
+	    options == QLA_GET_DPORT_RESULT_V2) {
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+					EXT_STATUS_DPORT_DIAG_NOT_RUNNING;
+		goto dportcomplete;
+	}
+
+	rval = qla26xx_dport_diagnostics_v2(vha, dd, mcp);
+
+	if (rval == QLA_SUCCESS) {
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+					EXT_STATUS_OK;
+		if (options == QLA_START_DPORT_TEST_V2 ||
+		    options == QLA_RESTART_DPORT_TEST_V2) {
+			dd->mbx1 = mcp->mb[0];
+			dd->mbx2 = mcp->mb[1];
+			vha->dport_status |=  DPORT_DIAG_IN_PROGRESS;
+		} else if (options == QLA_GET_DPORT_RESULT_V2) {
+			dd->mbx1 = le16_to_cpu(vha->dport_data[1]);
+			dd->mbx2 = le16_to_cpu(vha->dport_data[2]);
+		}
+	} else {
+		dd->mbx1 = mcp->mb[0];
+		dd->mbx2 = mcp->mb[1];
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] =
+				EXT_STATUS_DPORT_DIAG_ERR;
+	}
+
+dportcomplete:
+	sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+			    bsg_job->reply_payload.sg_cnt, dd, sizeof(*dd));
+
+	bsg_reply->reply_payload_rcv_len = sizeof(*dd);
+	bsg_job->reply_len = sizeof(*bsg_reply);
+	bsg_reply->result = DID_OK << 16;
+	bsg_job_done(bsg_job, bsg_reply->result,
+		     bsg_reply->reply_payload_rcv_len);
+
+	kfree(dd);
+
+	return 0;
+}
+
 static int
-qla2x00_get_flash_image_status(struct bsg_job *bsg_job)
-#endif
+qla2x00_get_flash_image_status(BSG_JOB_TYPE *bsg_job)
 {
 	scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
@@ -2578,19 +2522,23 @@
 	qla27xx_get_active_image(vha, &active_regions);
 	regions.global_image = active_regions.global;
 
+	if (IS_QLA27XX(ha))
+		regions.nvme_params = QLA27XX_PRIMARY_IMAGE;
+
 	if (IS_QLA28XX(ha)) {
 		qla28xx_get_aux_images(vha, &active_regions);
 		regions.board_config = active_regions.aux.board_config;
 		regions.vpd_nvram = active_regions.aux.vpd_nvram;
 		regions.npiv_config_0_1 = active_regions.aux.npiv_config_0_1;
 		regions.npiv_config_2_3 = active_regions.aux.npiv_config_2_3;
+		regions.nvme_params = active_regions.aux.nvme_params;
 	}
 
 	ql_dbg(ql_dbg_user, vha, 0x70e1,
-	    "%s(%lu): FW=%u BCFG=%u VPDNVR=%u NPIV01=%u NPIV02=%u\n",
+	    "%s(%lu): FW=%u BCFG=%u VPDNVR=%u NPIV01=%u NPIV02=%u NVME_PARAMS=%u\n",
 	    __func__, vha->host_no, regions.global_image,
 	    regions.board_config, regions.vpd_nvram,
-	    regions.npiv_config_0_1, regions.npiv_config_2_3);
+	    regions.npiv_config_0_1, regions.npiv_config_2_3, regions.nvme_params);
 
 	sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
 	    bsg_job->reply_payload.sg_cnt, &regions, sizeof(regions));
@@ -2605,16 +2553,336 @@
 	return 0;
 }
 
-#ifndef NEW_LIBFC_API
 static int
-qla2x00_process_vendor_specific(struct fc_bsg_job *bsg_job)
-#else
+qla2x00_manage_host_stats(BSG_JOB_TYPE *bsg_job)
+{
+	scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct ql_vnd_mng_host_stats_param *req_data;
+	struct ql_vnd_mng_host_stats_resp rsp_data;
+	u32 req_data_len;
+	int ret = 0;
+
+	if (!vha->flags.online) {
+		ql_log(ql_log_warn, vha, 0x0000, "Host is not online.\n");
+		return -EIO;
+	}
+
+	req_data_len = bsg_job->request_payload.payload_len;
+
+	if (req_data_len != sizeof(struct ql_vnd_mng_host_stats_param)) {
+		ql_log(ql_log_warn, vha, 0x0000, "req_data_len invalid.\n");
+		return -EIO;
+	}
+
+	req_data = kzalloc(sizeof(*req_data), GFP_KERNEL);
+	if (!req_data) {
+		ql_log(ql_log_warn, vha, 0x0000, "req_data memory allocation failure.\n");
+		return -ENOMEM;
+	}
+
+	/* Copy the request buffer in req_data */
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+			  bsg_job->request_payload.sg_cnt, req_data,
+			  req_data_len);
+
+	switch (req_data->action) {
+	case QLA_STOP:
+		ret = qla2xxx_stop_stats(vha->host, req_data->stat_type);
+		break;
+	case QLA_START:
+		ret = qla2xxx_start_stats(vha->host, req_data->stat_type);
+		break;
+	case QLA_CLEAR:
+		ret = qla2xxx_reset_stats(vha->host, req_data->stat_type);
+		break;
+	default:
+		ql_log(ql_log_warn, vha, 0x0000, "Invalid action.\n");
+		ret = -EIO;
+		break;
+	}
+
+	kfree(req_data);
+
+	/* Prepare response */
+	rsp_data.status = ret;
+	bsg_job->reply_payload.payload_len = sizeof(struct ql_vnd_mng_host_stats_resp);
+
+	bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_OK;
+	bsg_reply->reply_payload_rcv_len =
+		sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+				    bsg_job->reply_payload.sg_cnt,
+				    &rsp_data,
+				    sizeof(struct ql_vnd_mng_host_stats_resp));
+
+	bsg_reply->result = DID_OK;
+	bsg_job_done(bsg_job, bsg_reply->result,
+		     bsg_reply->reply_payload_rcv_len);
+
+	return ret;
+}
+
 static int
-qla2x00_process_vendor_specific(struct bsg_job *bsg_job)
-#endif
+qla2x00_get_host_stats(BSG_JOB_TYPE *bsg_job)
+{
+	scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct ql_vnd_stats_param *req_data;
+	struct ql_vnd_host_stats_resp rsp_data;
+	u32 req_data_len;
+	int ret = 0;
+	u64 ini_entry_count = 0;
+	u64 entry_count = 0;
+	u64 tgt_num = 0;
+	u64 tmp_stat_type = 0;
+	u64 response_len = 0;
+	void *data;
+
+	req_data_len = bsg_job->request_payload.payload_len;
+
+	if (req_data_len != sizeof(struct ql_vnd_stats_param)) {
+		ql_log(ql_log_warn, vha, 0x0000, "req_data_len invalid.\n");
+		return -EIO;
+	}
+
+	req_data = kzalloc(sizeof(*req_data), GFP_KERNEL);
+	if (!req_data) {
+		ql_log(ql_log_warn, vha, 0x0000, "req_data memory allocation failure.\n");
+		return -ENOMEM;
+	}
+
+	/* Copy the request buffer in req_data */
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+			  bsg_job->request_payload.sg_cnt, req_data, req_data_len);
+
+	/* Copy stat type to work on it */
+	tmp_stat_type = req_data->stat_type;
+
+	if (tmp_stat_type & QLA2XX_TGT_SHT_LNK_DOWN) {
+		/* Num of tgts connected to this host */
+		tgt_num = qla2x00_get_num_tgts(vha);
+		/* unset BIT_17 */
+		tmp_stat_type &= ~(1 << 17);
+	}
+
+	/* Total ini stats */
+	ini_entry_count = qla2x00_count_set_bits(tmp_stat_type);
+
+	/* Total number of entries */
+	entry_count = ini_entry_count + tgt_num;
+
+	response_len = sizeof(struct ql_vnd_host_stats_resp) +
+		(sizeof(struct ql_vnd_stat_entry) * entry_count);
+
+	if (response_len > bsg_job->reply_payload.payload_len) {
+		rsp_data.status = EXT_STATUS_BUFFER_TOO_SMALL;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_BUFFER_TOO_SMALL;
+		bsg_job->reply_payload.payload_len = sizeof(struct ql_vnd_mng_host_stats_resp);
+
+		bsg_reply->reply_payload_rcv_len =
+			sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+					    bsg_job->reply_payload.sg_cnt, &rsp_data,
+					    sizeof(struct ql_vnd_mng_host_stats_resp));
+
+		bsg_reply->result = DID_OK;
+		bsg_job_done(bsg_job, bsg_reply->result,
+			     bsg_reply->reply_payload_rcv_len);
+		goto host_stat_out;
+	}
+
+	data = kzalloc(response_len, GFP_KERNEL);
+	if (!data) {
+		ret = -ENOMEM;
+		goto host_stat_out;
+	}
+
+	ret = qla2xxx_get_ini_stats(fc_bsg_to_shost(bsg_job), req_data->stat_type,
+				    data, response_len);
+
+	rsp_data.status = EXT_STATUS_OK;
+	bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_OK;
+
+	bsg_reply->reply_payload_rcv_len = sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+							       bsg_job->reply_payload.sg_cnt,
+							       data, response_len);
+	bsg_reply->result = DID_OK;
+	bsg_job_done(bsg_job, bsg_reply->result,
+		     bsg_reply->reply_payload_rcv_len);
+
+	kfree(data);
+host_stat_out:
+	kfree(req_data);
+	return ret;
+}
+
+static struct fc_rport *
+qla2xxx_find_rport(scsi_qla_host_t *vha, uint32_t tgt_num)
+{
+	fc_port_t *fcport = NULL;
+
+	list_for_each_entry(fcport, &vha->vp_fcports, list) {
+		if (fcport->rport->number == tgt_num)
+			return fcport->rport;
+	}
+	return NULL;
+}
+
+static int
+qla2x00_get_tgt_stats(BSG_JOB_TYPE *bsg_job)
+{
+	scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct ql_vnd_tgt_stats_param *req_data;
+	u32 req_data_len;
+	int ret = 0;
+	u64 response_len = 0;
+	struct ql_vnd_tgt_stats_resp *data = NULL;
+	struct fc_rport *rport = NULL;
+
+	if (!vha->flags.online) {
+		ql_log(ql_log_warn, vha, 0x0000, "Host is not online.\n");
+		return -EIO;
+	}
+
+	req_data_len = bsg_job->request_payload.payload_len;
+
+	if (req_data_len != sizeof(struct ql_vnd_stat_entry)) {
+		ql_log(ql_log_warn, vha, 0x0000, "req_data_len invalid.\n");
+		return -EIO;
+	}
+
+	req_data = kzalloc(sizeof(*req_data), GFP_KERNEL);
+	if (!req_data) {
+		ql_log(ql_log_warn, vha, 0x0000, "req_data memory allocation failure.\n");
+		return -ENOMEM;
+	}
+
+	/* Copy the request buffer in req_data */
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+			  bsg_job->request_payload.sg_cnt,
+			  req_data, req_data_len);
+
+	response_len = sizeof(struct ql_vnd_tgt_stats_resp) +
+		sizeof(struct ql_vnd_stat_entry);
+
+	/* structure + size for one entry */
+	data = kzalloc(response_len, GFP_KERNEL);
+	if (!data) {
+		kfree(req_data);
+		return -ENOMEM;
+	}
+
+	if (response_len > bsg_job->reply_payload.payload_len) {
+		data->status = EXT_STATUS_BUFFER_TOO_SMALL;
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_BUFFER_TOO_SMALL;
+		bsg_job->reply_payload.payload_len = sizeof(struct ql_vnd_mng_host_stats_resp);
+
+		bsg_reply->reply_payload_rcv_len =
+			sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+					    bsg_job->reply_payload.sg_cnt, data,
+					    sizeof(struct ql_vnd_tgt_stats_resp));
+
+		bsg_reply->result = DID_OK;
+		bsg_job_done(bsg_job, bsg_reply->result,
+			     bsg_reply->reply_payload_rcv_len);
+		goto tgt_stat_out;
+	}
+
+	rport = qla2xxx_find_rport(vha, req_data->tgt_id);
+	if (!rport) {
+		ql_log(ql_log_warn, vha, 0x0000, "target %d not found.\n", req_data->tgt_id);
+		ret = EXT_STATUS_INVALID_PARAM;
+		data->status = EXT_STATUS_INVALID_PARAM;
+		goto reply;
+	}
+
+	ret = qla2xxx_get_tgt_stats(fc_bsg_to_shost(bsg_job), req_data->stat_type,
+				    rport, (void *)data, response_len);
+
+	bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_OK;
+reply:
+	bsg_reply->reply_payload_rcv_len =
+		sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+				    bsg_job->reply_payload.sg_cnt, data,
+				    response_len);
+	bsg_reply->result = DID_OK;
+	bsg_job_done(bsg_job, bsg_reply->result,
+		     bsg_reply->reply_payload_rcv_len);
+
+tgt_stat_out:
+	kfree(data);
+	kfree(req_data);
+
+	return ret;
+}
+
+static int
+qla2x00_manage_host_port(BSG_JOB_TYPE *bsg_job)
+{
+	scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct ql_vnd_mng_host_port_param *req_data;
+	struct ql_vnd_mng_host_port_resp rsp_data;
+	u32 req_data_len;
+	int ret = 0;
+
+	req_data_len = bsg_job->request_payload.payload_len;
+
+	if (req_data_len != sizeof(struct ql_vnd_mng_host_port_param)) {
+		ql_log(ql_log_warn, vha, 0x0000, "req_data_len invalid.\n");
+		return -EIO;
+	}
+
+	req_data = kzalloc(sizeof(*req_data), GFP_KERNEL);
+	if (!req_data) {
+		ql_log(ql_log_warn, vha, 0x0000, "req_data memory allocation failure.\n");
+		return -ENOMEM;
+	}
+
+	/* Copy the request buffer in req_data */
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+			  bsg_job->request_payload.sg_cnt, req_data, req_data_len);
+
+	switch (req_data->action) {
+	case QLA_ENABLE:
+		ret = qla2xxx_enable_port(vha->host);
+		break;
+	case QLA_DISABLE:
+		ret = qla2xxx_disable_port(vha->host);
+		break;
+	default:
+		ql_log(ql_log_warn, vha, 0x0000, "Invalid action.\n");
+		ret = -EIO;
+		break;
+	}
+
+	kfree(req_data);
+
+	/* Prepare response */
+	rsp_data.status = ret;
+	bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_OK;
+	bsg_job->reply_payload.payload_len = sizeof(struct ql_vnd_mng_host_port_resp);
+
+	bsg_reply->reply_payload_rcv_len =
+		sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+				    bsg_job->reply_payload.sg_cnt, &rsp_data,
+				    sizeof(struct ql_vnd_mng_host_port_resp));
+	bsg_reply->result = DID_OK;
+	bsg_job_done(bsg_job, bsg_reply->result,
+		     bsg_reply->reply_payload_rcv_len);
+
+	return ret;
+}
+
+static int
+qla2x00_process_vendor_specific(struct scsi_qla_host *vha,
+				BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 
+	ql_dbg(ql_dbg_edif, vha, 0x911b, "%s FC_BSG_HST_VENDOR cmd[0]=0x%x\n",
+	    __func__, bsg_request->rqst_data.h_vendor.vendor_cmd[0]);
+
 	switch (bsg_request->rqst_data.h_vendor.vendor_cmd[0]) {
 	case QL_VND_LOOPBACK:
 		return qla2x00_process_loopback(bsg_job);
@@ -2683,21 +2951,37 @@
 	case QL_VND_DPORT_DIAGNOSTICS:
 		return qla2x00_do_dport_diagnostics(bsg_job);
 
+	case QL_VND_DPORT_DIAGNOSTICS_V2:
+		return qla2x00_do_dport_diagnostics_v2(bsg_job);
+
+	case QL_VND_EDIF_MGMT:
+		return qla_edif_app_mgmt(bsg_job);
+
 	case QL_VND_SS_GET_FLASH_IMAGE_STATUS:
 		return qla2x00_get_flash_image_status(bsg_job);
 
+	case QL_VND_MANAGE_HOST_STATS:
+		return qla2x00_manage_host_stats(bsg_job);
+
+	case QL_VND_GET_HOST_STATS:
+		return qla2x00_get_host_stats(bsg_job);
+
+	case QL_VND_GET_TGT_STATS:
+		return qla2x00_get_tgt_stats(bsg_job);
+
+	case QL_VND_MANAGE_HOST_PORT:
+		return qla2x00_manage_host_port(bsg_job);
+
+	case QL_VND_MBX_PASSTHRU:
+		return qla2x00_mailbox_passthru(bsg_job);
+
 	default:
 		return -ENOSYS;
 	}
 }
 
-#ifndef NEW_LIBFC_API
 int
-qla24xx_bsg_request(struct fc_bsg_job *bsg_job)
-#else
-int
-qla24xx_bsg_request(struct bsg_job *bsg_job)
-#endif
+qla24xx_bsg_request(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
@@ -2718,15 +3002,34 @@
 		vha = shost_priv(host);
 	}
 
+	/* Disable port will bring down the chip, allow enable command */
+	if (bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_MANAGE_HOST_PORT ||
+	    bsg_request->rqst_data.h_vendor.vendor_cmd[0] == QL_VND_GET_HOST_STATS)
+		goto skip_chip_chk;
+
+	if (vha->hw->flags.port_isolated) {
+		bsg_reply->result = DID_ERROR;
+		/* operation not permitted */
+		return -EPERM;
+	}
+
 	if (qla2x00_chip_is_down(vha)) {
 		ql_dbg(ql_dbg_user, vha, 0x709f,
 		    "BSG: ISP abort active/needed -- cmd=%d.\n",
 		    bsg_request->msgcode);
+		SET_DID_STATUS(bsg_reply->result, DID_ERROR);
 		return -EBUSY;
 	}
 
-	ql_dbg(ql_dbg_user, vha, 0x7000,
-	    "Entered %s msgcode=0x%x.\n", __func__, bsg_request->msgcode);
+	if (test_bit(PFLG_DRIVER_REMOVING, &vha->pci_flags)) {
+		SET_DID_STATUS(bsg_reply->result, DID_ERROR);
+		return -EIO;
+	}
+
+skip_chip_chk:
+	ql_dbg(ql_dbg_user + ql_dbg_verbose, vha, 0x7000,
+	    "Entered %s msgcode=0x%x. bsg ptr %px\n",
+	    __func__, bsg_request->msgcode, bsg_job);
 
 	switch (bsg_request->msgcode) {
 	case FC_BSG_RPT_ELS:
@@ -2737,7 +3040,7 @@
 		ret = qla2x00_process_ct(bsg_job);
 		break;
 	case FC_BSG_HST_VENDOR:
-		ret = qla2x00_process_vendor_specific(bsg_job);
+		ret = qla2x00_process_vendor_specific(vha, bsg_job);
 		break;
 	case FC_BSG_HST_ADD_RPORT:
 	case FC_BSG_HST_DEL_RPORT:
@@ -2746,16 +3049,15 @@
 		ql_log(ql_log_warn, vha, 0x705a, "Unsupported BSG request.\n");
 		break;
 	}
+
+	ql_dbg(ql_dbg_user + ql_dbg_verbose, vha, 0x7000,
+	    "%s done with return %x\n", __func__, ret);
+
 	return ret;
 }
 
-#ifndef NEW_LIBFC_API
 int
-qla24xx_bsg_timeout(struct fc_bsg_job *bsg_job)
-#else
-int
-qla24xx_bsg_timeout(struct bsg_job *bsg_job)
-#endif
+qla24xx_bsg_timeout(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
@@ -2765,6 +3067,15 @@
 	unsigned long flags;
 	struct req_que *req;
 
+	ql_log(ql_log_info, vha, 0x708b, "%s CMD timeout. bsg ptr %p.\n",
+	    __func__, bsg_job);
+
+	if (qla2x00_isp_reg_stat(ha)) {
+		ql_log(ql_log_info, vha, 0x9007,
+		    "PCI/Register disconnect.\n");
+		qla_pci_set_eeh_busy(vha);
+	}
+
 	/* find the bsg job from the active list of commands */
 	spin_lock_irqsave(&ha->hardware_lock, flags);
 	for (que = 0; que < ha->max_req_queues; que++) {
@@ -2774,27 +3085,27 @@
 
 		for (cnt = 1; cnt < req->num_outstanding_cmds; cnt++) {
 			sp = req->outstanding_cmds[cnt];
-			if (sp) {
-				if (((sp->type == SRB_CT_CMD) ||
-					(sp->type == SRB_ELS_CMD_HST) ||
-					(sp->type == SRB_FXIOCB_BCMD))
-					&& (sp->u.bsg_job == bsg_job)) {
-					req->outstanding_cmds[cnt] = NULL;
-					spin_unlock_irqrestore(&ha->hardware_lock, flags);
-					if (ha->isp_ops->abort_command(sp)) {
-						ql_log(ql_log_warn, vha, 0x7089,
-						    "mbx abort_command "
-						    "failed.\n");
-						bsg_reply->result = -EIO;
-					} else {
-						ql_dbg(ql_dbg_user, vha, 0x708a,
-						    "mbx abort_command "
-						    "success.\n");
-						bsg_reply->result = 0;
-					}
-					spin_lock_irqsave(&ha->hardware_lock, flags);
-					goto done;
+			if (sp &&
+			    (sp->type == SRB_CT_CMD ||
+			     sp->type == SRB_ELS_CMD_HST ||
+			     sp->type == SRB_ELS_CMD_HST_NOLOGIN ||
+			     sp->type == SRB_FXIOCB_BCMD) &&
+			    sp->u.bsg_job == bsg_job) {
+				req->outstanding_cmds[cnt] = NULL;
+				spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+				if (!ha->flags.eeh_busy && ha->isp_ops->abort_command(sp)) {
+					ql_log(ql_log_warn, vha, 0x7089,
+					    "mbx abort_command failed.\n");
+					bsg_reply->result = -EIO;
+				} else {
+					ql_dbg(ql_dbg_user, vha, 0x708a,
+					    "mbx abort_command success.\n");
+					bsg_reply->result = 0;
 				}
+				spin_lock_irqsave(&ha->hardware_lock, flags);
+				goto done;
+
 			}
 		}
 	}
@@ -2805,6 +3116,52 @@
 
 done:
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 	return 0;
 }
+
+int qla2x00_mailbox_passthru(BSG_JOB_TYPE *bsg_job)
+{
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	scsi_qla_host_t *vha = shost_priv(fc_bsg_to_shost(bsg_job));
+	int ret = -EINVAL;
+	int ptsize = sizeof(struct qla_mbx_passthru);
+	struct qla_mbx_passthru *req_data = NULL;
+	uint32_t req_data_len;
+
+	req_data_len = bsg_job->request_payload.payload_len;
+	if (req_data_len != ptsize) {
+		ql_log(ql_log_warn, vha, 0xf0a3, "req_data_len invalid.\n");
+		return -EIO;
+	}
+	req_data = kzalloc(ptsize, GFP_KERNEL);
+	if (!req_data) {
+		ql_log(ql_log_warn, vha, 0xf0a4,
+		       "req_data memory allocation failure.\n");
+		return -ENOMEM;
+	}
+
+	/* Copy the request buffer in req_data */
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+			  bsg_job->request_payload.sg_cnt, req_data, ptsize);
+	ret = qla_mailbox_passthru(vha, req_data->mbx_in, req_data->mbx_out);
+
+	/* Copy the req_data in  request buffer */
+	sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+			    bsg_job->reply_payload.sg_cnt, req_data, ptsize);
+
+	bsg_reply->reply_payload_rcv_len = ptsize;
+	if (ret == QLA_SUCCESS)
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_OK;
+	else
+		bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = EXT_STATUS_ERR;
+
+	bsg_job->reply_len = sizeof(*bsg_job->reply);
+	bsg_reply->result = DID_OK << 16;
+	bsg_job_done(bsg_job, bsg_reply->result, bsg_reply->reply_payload_rcv_len);
+
+	kfree(req_data);
+
+	return ret;
+}
diff --git a/scst/qla2x00t-32gbit/qla_bsg.h b/scst/qla2x00t-32gbit/qla_bsg.h
index 7594fad..d38dab0 100644
--- a/scst/qla2x00t-32gbit/qla_bsg.h
+++ b/scst/qla2x00t-32gbit/qla_bsg.h
@@ -1,8 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #ifndef __QLA_BSG_H
 #define __QLA_BSG_H
@@ -32,6 +31,13 @@
 #define QL_VND_DPORT_DIAGNOSTICS	0x19
 #define QL_VND_GET_PRIV_STATS_EX	0x1A
 #define QL_VND_SS_GET_FLASH_IMAGE_STATUS	0x1E
+#define QL_VND_EDIF_MGMT                0X1F
+#define QL_VND_MANAGE_HOST_STATS	0x23
+#define QL_VND_GET_HOST_STATS		0x24
+#define QL_VND_GET_TGT_STATS		0x25
+#define QL_VND_MANAGE_HOST_PORT		0x26
+#define QL_VND_MBX_PASSTHRU		0x2B
+#define QL_VND_DPORT_DIAGNOSTICS_V2	0x2C
 
 /* BSG Vendor specific subcode returns */
 #define EXT_STATUS_OK			0
@@ -41,6 +47,7 @@
 #define EXT_STATUS_DATA_OVERRUN		7
 #define EXT_STATUS_DATA_UNDERRUN	8
 #define EXT_STATUS_MAILBOX		11
+#define EXT_STATUS_BUFFER_TOO_SMALL	16
 #define EXT_STATUS_NO_MEMORY		17
 #define EXT_STATUS_DEVICE_OFFLINE	22
 
@@ -54,6 +61,9 @@
 #define EXT_STATUS_TIMEOUT		30
 #define EXT_STATUS_THREAD_FAILED	31
 #define EXT_STATUS_DATA_CMP_FAILED	32
+#define EXT_STATUS_DPORT_DIAG_ERR	40
+#define EXT_STATUS_DPORT_DIAG_IN_PROCESS	41
+#define EXT_STATUS_DPORT_DIAG_NOT_RUNNING	42
 
 /* BSG definations for interpreting CommandSent field */
 #define INT_DEF_LB_LOOPBACK_CMD         0
@@ -151,7 +161,7 @@
 	uint16_t rsrvd;
 	struct qla84_mgmt_param mgmtp;/* parameters for cmd */
 	uint32_t len; /* bytes in payload following this struct */
-	uint8_t payload[0]; /* payload for cmd */
+	uint8_t payload[]; /* payload for cmd */
 };
 
 struct qla_bsg_a84_mgmt {
@@ -182,6 +192,12 @@
 	uint16_t speed;
 } __attribute__ ((packed));
 
+struct qla_mbx_passthru {
+	uint16_t reserved1[2];
+	uint16_t mbx_in[32];
+	uint16_t mbx_out[32];
+	uint32_t reserved2[16];
+} __packed;
 
 /* FRU VPD */
 
@@ -204,7 +220,7 @@
 
 struct qla_image_version_list {
 	uint32_t count;
-	struct qla_image_version version[0];
+	struct qla_image_version version[];
 } __packed;
 
 struct qla_status_reg {
@@ -276,6 +292,17 @@
 	uint8_t  unused[62];
 } __packed;
 
+#define QLA_GET_DPORT_RESULT_V2		0  /* Get Result */
+#define QLA_RESTART_DPORT_TEST_V2	1  /* Restart test */
+#define QLA_START_DPORT_TEST_V2		2  /* Start test */
+struct qla_dport_diag_v2 {
+	uint16_t options;
+	uint16_t mbx1;
+	uint16_t mbx2;
+	uint8_t  unused[58];
+	uint8_t buf[1024]; /* Test Result */
+} __packed;
+
 /* D_Port options */
 #define QLA_DPORT_RESULT	0x0
 #define QLA_DPORT_START		0x2
@@ -287,7 +314,10 @@
 	uint8_t vpd_nvram;
 	uint8_t npiv_config_0_1;
 	uint8_t npiv_config_2_3;
-	uint8_t reserved[32];
+	uint8_t nvme_params;
+	uint8_t reserved[31];
 } __packed;
 
+#include "qla_edif_bsg.h"
+
 #endif
diff --git a/scst/qla2x00t-32gbit/qla_dbg.c b/scst/qla2x00t-32gbit/qla_dbg.c
index 1be811a..d9f9dda 100644
--- a/scst/qla2x00t-32gbit/qla_dbg.c
+++ b/scst/qla2x00t-32gbit/qla_dbg.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 
 /*
@@ -13,10 +12,9 @@
  * ----------------------------------------------------------------------
  * | Module Init and Probe        |       0x0199       |                |
  * | Mailbox commands             |       0x1206       | 0x11a5-0x11ff	|
- * | Device Discovery             |       0x2134       | 0x210e-0x2116  |
- * |				  | 		       | 0x211a         |
+ * | Device Discovery             |       0x2134       | 0x210e-0x2115  |
  * |                              |                    | 0x211c-0x2128  |
- * |                              |                    | 0x212a-0x2134  |
+ * |                              |                    | 0x212c-0x2134  |
  * | Queue Command and IO tracing |       0x3074       | 0x300b         |
  * |                              |                    | 0x3027-0x3028  |
  * |                              |                    | 0x303d-0x3041  |
@@ -114,8 +112,13 @@
 	uint32_t stat;
 	ulong i, j, timer = 6000000;
 	int rval = QLA_FUNCTION_FAILED;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+
+	if (qla_pci_disconnected(vha, reg))
+		return rval;
+
 	for (i = 0; i < ram_dwords; i += dwords, addr += dwords) {
 		if (i + dwords > ram_dwords)
 			dwords = ram_dwords - i;
@@ -139,6 +142,9 @@
 		while (timer--) {
 			udelay(5);
 
+			if (qla_pci_disconnected(vha, reg))
+				return rval;
+
 			stat = rd_reg_dword(&reg->host_status);
 			/* Check for pending interrupts. */
 			if (!(stat & HSRX_RISC_INT))
@@ -193,9 +199,13 @@
 	uint32_t dwords = qla2x00_gid_list_size(ha) / 4;
 	uint32_t stat;
 	ulong i, j, timer = 6000000;
+	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
 	clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
 
+	if (qla_pci_disconnected(vha, reg))
+		return rval;
+
 	for (i = 0; i < ram_dwords; i += dwords, addr += dwords) {
 		if (i + dwords > ram_dwords)
 			dwords = ram_dwords - i;
@@ -203,6 +213,7 @@
 		wrt_reg_word(&reg->mailbox0, MBC_DUMP_RISC_RAM_EXTENDED);
 		wrt_reg_word(&reg->mailbox1, LSW(addr));
 		wrt_reg_word(&reg->mailbox8, MSW(addr));
+		wrt_reg_word(&reg->mailbox10, 0);
 
 		wrt_reg_word(&reg->mailbox2, MSW(LSD(dump_dma)));
 		wrt_reg_word(&reg->mailbox3, LSW(LSD(dump_dma)));
@@ -216,8 +227,10 @@
 		ha->flags.mbox_int = 0;
 		while (timer--) {
 			udelay(5);
-			stat = rd_reg_dword(&reg->host_status);
+			if (qla_pci_disconnected(vha, reg))
+				return rval;
 
+			stat = rd_reg_dword(&reg->host_status);
 			/* Check for pending interrupts. */
 			if (!(stat & HSRX_RISC_INT))
 				continue;
@@ -2442,15 +2455,18 @@
 /****************************************************************************/
 
 /* Write the debug message prefix into @pbuf. */
-static void ql_dbg_prefix(char *pbuf, int pbuf_size,
+static void ql_dbg_prefix(char *pbuf, int pbuf_size, struct pci_dev *pdev,
 			  const scsi_qla_host_t *vha, uint msg_id)
 {
 	if (vha) {
 		const struct pci_dev *pdev = vha->hw->pdev;
 
 		/* <module-name> [<dev-name>]-<msg-id>:<host>: */
-		snprintf(pbuf, pbuf_size, "%s [%s]-%04x:%ld: ", QL_MSGHDR,
+		snprintf(pbuf, pbuf_size, "%s [%s]-%04x:%lu: ", QL_MSGHDR,
 			 dev_name(&(pdev->dev)), msg_id, vha->host_no);
+	} else if (pdev) {
+		snprintf(pbuf, pbuf_size, "%s [%s]-%04x: : ", QL_MSGHDR,
+			 dev_name(&pdev->dev), msg_id);
 	} else {
 		/* <module-name> [<dev-name>]-<msg-id>: : */
 		snprintf(pbuf, pbuf_size, "%s [%s]-%04x: : ", QL_MSGHDR,
@@ -2478,18 +2494,36 @@
 	struct va_format vaf;
 	char pbuf[64];
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+	ql_ktrace(1, level, pbuf, NULL, vha, id, fmt);
+
+	if (!ql_mask_match(level))
+		return;
+
+	if (!pbuf[0]) /* set by ql_ktrace */
+		ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), NULL, vha, id);
+#else
+	if (!ql_mask_match(level) && !trace_ql_dbg_log_enabled())
+		return;
+#endif
+
 	va_start(va, fmt);
 
 	vaf.fmt = fmt;
 	vaf.va = &va;
 
-	ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), vha, id);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+	pr_warn("%s%pV", pbuf, &vaf);
+#else
+	ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), NULL, vha, id);
 
 	if (!ql_mask_match(level))
 		trace_ql_dbg_log(pbuf, &vaf);
 	else
 		pr_warn("%s%pV", pbuf, &vaf);
 
+#endif
+
 	va_end(va);
 
 }
@@ -2517,6 +2551,11 @@
 
 	if (pdev == NULL)
 		return;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+	ql_ktrace(1, level, pbuf, pdev, NULL, id, fmt);
+#endif
+
 	if (!ql_mask_match(level))
 		return;
 
@@ -2525,7 +2564,14 @@
 	vaf.fmt = fmt;
 	vaf.va = &va;
 
-	ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), NULL, id + ql_dbg_offset);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+	if (!pbuf[0]) /* set by ql_ktrace */
+		ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), pdev, NULL,
+			      id + ql_dbg_offset);
+#else
+	ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), pdev, NULL,
+		      id + ql_dbg_offset);
+#endif
 	pr_warn("%s%pV", pbuf, &vaf);
 
 	va_end(va);
@@ -2554,7 +2600,14 @@
 	if (level > ql_errlev)
 		return;
 
-	ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), vha, id);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+	ql_ktrace(0, level, pbuf, NULL, vha, id, fmt);
+
+	if (!pbuf[0]) /* set by ql_ktrace */
+		ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), NULL, vha, id);
+#else
+	ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), NULL, vha, id);
+#endif
 
 	va_start(va, fmt);
 
@@ -2605,7 +2658,14 @@
 	if (level > ql_errlev)
 		return;
 
-	ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), NULL, id);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+	ql_ktrace(0, level, pbuf, pdev, NULL, id, fmt);
+
+	if (!pbuf[0]) /* set by ql_ktrace */
+		ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), pdev, NULL, id);
+#else
+	ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), pdev, NULL, id);
+#endif
 
 	va_start(va, fmt);
 
@@ -2700,7 +2760,16 @@
 	if (level > ql_errlev)
 		return;
 
-	ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), qpair ? qpair->vha : NULL, id);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+	ql_ktrace(0, level, pbuf, NULL, qpair ? qpair->vha : NULL, id, fmt);
+
+	if (!pbuf[0]) /* set by ql_ktrace */
+		ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), NULL,
+			      qpair ? qpair->vha : NULL, id);
+#else
+	ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), NULL, qpair ? qpair->vha : NULL, id);
+
+#endif
 
 	va_start(va, fmt);
 
@@ -2746,6 +2815,10 @@
 	struct va_format vaf;
 	char pbuf[128];
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+	ql_ktrace(1, level, pbuf, NULL, qpair ? qpair->vha : NULL, id, fmt);
+#endif
+
 	if (!ql_mask_match(level))
 		return;
 
@@ -2754,8 +2827,15 @@
 	vaf.fmt = fmt;
 	vaf.va = &va;
 
-	ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), qpair ? qpair->vha : NULL,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+	if (!pbuf[0]) /* set by ql_ktrace */
+		ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), NULL,
+			      qpair ? qpair->vha : NULL, id + ql_dbg_offset);
+#else
+	ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), NULL, qpair ? qpair->vha : NULL,
 		      id + ql_dbg_offset);
+#endif
+
 	pr_warn("%s%pV", pbuf, &vaf);
 
 	va_end(va);
diff --git a/scst/qla2x00t-32gbit/qla_dbg.h b/scst/qla2x00t-32gbit/qla_dbg.h
index e1d7de6..4ea3463 100644
--- a/scst/qla2x00t-32gbit/qla_dbg.h
+++ b/scst/qla2x00t-32gbit/qla_dbg.h
@@ -1,8 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 
 #include "qla_def.h"
@@ -309,7 +308,7 @@
 };
 
 #define QL_MSGHDR "qla2xxx"
-#define QL_DBG_DEFAULT1_MASK    0x1e400000
+#define QL_DBG_DEFAULT1_MASK    0x1e600000
 
 #define ql_log_fatal		0 /* display fatal errors */
 #define ql_log_warn		1 /* display critical errors */
@@ -368,6 +367,7 @@
 #define ql_dbg_tgt_mgt	0x00002000 /* Target mode management */
 #define ql_dbg_tgt_tmr	0x00001000 /* Target mode task management */
 #define ql_dbg_tgt_dif  0x00000800 /* Target mode dif */
+#define ql_dbg_edif	0x00000400 /* edif and purex debug */
 
 extern int qla27xx_dump_mpi_ram(struct qla_hw_data *, uint32_t, uint32_t *,
 	uint32_t, void **);
@@ -383,5 +383,52 @@
 	if (ql2xextended_error_logging == 1)
 		ql2xextended_error_logging = QL_DBG_DEFAULT1_MASK;
 
-	return (level & ql2xextended_error_logging) == level;
+	return level && ((level & ql2xextended_error_logging) == level);
 }
+
+static inline int
+ql_mask_match_ext(uint level, int *log_tunable)
+{
+	if (*log_tunable == 1)
+		*log_tunable = QL_DBG_DEFAULT1_MASK;
+
+	return (level & *log_tunable) == level;
+}
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+
+/* Assumes local variable pbuf and pbuf_ready present. */
+#define ql_ktrace(dbg_msg, level, pbuf, pdev, vha, id, fmt) do {	\
+	struct va_format _vaf;						\
+	va_list _va;							\
+	u32 dbg_off = dbg_msg ? ql_dbg_offset : 0;			\
+									\
+	pbuf[0] = 0;							\
+	if (!trace_ql_dbg_log_enabled())				\
+		break;							\
+									\
+	if (dbg_msg && !ql_mask_match_ext(level,			\
+				&ql2xextended_error_logging_ktrace))	\
+		break;							\
+									\
+	ql_dbg_prefix(pbuf, ARRAY_SIZE(pbuf), pdev, vha, id + dbg_off);	\
+									\
+	va_start(_va, fmt);						\
+	_vaf.fmt = fmt;							\
+	_vaf.va = &_va;							\
+									\
+	trace_ql_dbg_log(pbuf, &_vaf);					\
+									\
+	va_end(_va);							\
+} while (0)
+
+#define QLA_ENABLE_KERNEL_TRACING
+
+#ifdef QLA_ENABLE_KERNEL_TRACING
+#define QLA_TRACE_ENABLE(_tr) \
+	trace_array_set_clr_event(_tr, "qla", NULL, true)
+#else /* QLA_ENABLE_KERNEL_TRACING */
+#define QLA_TRACE_ENABLE(_tr)
+#endif /* QLA_ENABLE_KERNEL_TRACING */
+
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0) */
diff --git a/scst/qla2x00t-32gbit/qla_def.h b/scst/qla2x00t-32gbit/qla_def.h
index 87709f0..78adee8 100644
--- a/scst/qla2x00t-32gbit/qla_def.h
+++ b/scst/qla2x00t-32gbit/qla_def.h
@@ -1,8 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #ifndef __QLA_DEF_H
 #define __QLA_DEF_H
@@ -25,15 +24,9 @@
 #include <linux/firmware.h>
 #include <linux/aer.h>
 #include <linux/mutex.h>
-#include <linux/version.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
-/*
- * See also commit 5db53f3e80de ("[LogFS] add new flash file system") # v2.6.34.
- */
 #include <linux/btree.h>
-#else
-#include "btree-backport.h"
-#endif
+#include <linux/version.h>
+#include <linux/bsg-lib.h>	/* struct bsg_job */
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_host.h>
@@ -46,18 +39,44 @@
 	defined(CONFIG_SUSE_KERNEL) && \
 	LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
 #define NEW_LIBFC_API
+#define BSG_JOB_TYPE struct bsg_job
+#else
+#define BSG_JOB_TYPE struct fc_bsg_job
+
+static inline struct Scsi_Host *fc_bsg_to_shost(struct fc_bsg_job *job)
+{
+	return job->shost;
+}
+
+static inline struct fc_rport *fc_bsg_to_rport(struct fc_bsg_job *job)
+{
+	return job->rport;
+}
+
+static inline void bsg_job_done_backport(struct fc_bsg_job *job, int result,
+					 unsigned int reply_payload_rcv_len)
+{
+	job->job_done(job);
+}
+
+#define bsg_job_done bsg_job_done_backport
 #endif
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) &&	\
 	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 7)
 #error
 #error ***This version of qla2xxx does not support distributions based on***
-#error ***kernels before Linux kernel v3.15.***
+#error ***kernels before Linux kernel v3.18.***
 #error
 #endif
 
 #include <uapi/scsi/fc/fc_els.h>
 
+#define QLA_DFS_DEFINE_DENTRY(_debugfs_file_name) \
+	struct dentry *dfs_##_debugfs_file_name
+#define QLA_DFS_ROOT_DEFINE_DENTRY(_debugfs_file_name) \
+	struct dentry *qla_dfs_##_debugfs_file_name
+
 /* Big endian Fibre Channel S_ID (source ID) or D_ID (destination ID). */
 typedef struct {
 	uint8_t domain;
@@ -72,6 +91,28 @@
 	uint8_t domain;
 } le_id_t;
 
+/*
+ * 24 bit port ID type definition.
+ */
+typedef union {
+	uint32_t b24 : 24;
+	struct {
+#ifdef __BIG_ENDIAN
+		uint8_t domain;
+		uint8_t area;
+		uint8_t al_pa;
+#elif defined(__LITTLE_ENDIAN)
+		uint8_t al_pa;
+		uint8_t area;
+		uint8_t domain;
+#else
+#error "__BIG_ENDIAN or __LITTLE_ENDIAN must be defined!"
+#endif
+		uint8_t rsvd_1;
+	} b;
+} port_id_t;
+#define INVALID_PORT_ID	0xFFFFFF
+
 #include "qla_bsg.h"
 #include "qla_dsd.h"
 #include "qla_nx.h"
@@ -79,7 +120,7 @@
 #include "qla_nvme.h"
 #define QLA2XXX_DRIVER_NAME	"qla2xxx"
 #define QLA2XXX_APIDEV		"ql2xapidev"
-#define QLA2XXX_MANUFACTURER	"QLogic Corporation"
+#define QLA2XXX_MANUFACTURER	"Marvell Semiconductor, Inc."
 
 /*
  * We have MAILBOX_REGISTER_COUNT sized arrays in a few places,
@@ -342,6 +383,13 @@
 	u32			size;
 	u8			sent;
 };
+
+struct els_reject {
+	struct fc_els_ls_rjt *c;
+	dma_addr_t  cdma;
+	u16 size;
+};
+
 /*
  * Timeout timer counts in seconds
  */
@@ -368,6 +416,8 @@
 #define FW_MAX_EXCHANGES_CNT (32 * 1024)
 #define REDUCE_EXCHANGES_CNT  (8 * 1024)
 
+#define SET_DID_STATUS(stat_var, status) (stat_var = status << 16)
+
 struct req_que;
 struct qla_tgt_sess;
 
@@ -393,32 +443,11 @@
 #define SRB_CRC_CTX_DSD_VALID		BIT_5	/* DIF: dsd_list valid */
 #define SRB_WAKEUP_ON_COMP		BIT_6
 #define SRB_DIF_BUNDL_DMA_VALID		BIT_7   /* DIF: DMA list valid */
+#define SRB_EDIF_CLEANUP_DELETE		BIT_9
 
 /* To identify if a srb is of T10-CRC type. @sp => srb_t pointer */
 #define IS_PROT_IO(sp)	(sp->flags & SRB_CRC_CTX_DSD_VALID)
-
-/*
- * 24 bit port ID type definition.
- */
-typedef union {
-	uint32_t b24 : 24;
-
-	struct {
-#ifdef __BIG_ENDIAN
-		uint8_t domain;
-		uint8_t area;
-		uint8_t al_pa;
-#elif defined(__LITTLE_ENDIAN)
-		uint8_t al_pa;
-		uint8_t area;
-		uint8_t domain;
-#else
-#error "__BIG_ENDIAN or __LITTLE_ENDIAN must be defined!"
-#endif
-		uint8_t rsvd_1;
-	} b;
-} port_id_t;
-#define INVALID_PORT_ID	0xFFFFFF
+#define ISP_REG16_DISCONNECT 0xFFFF
 
 static inline le_id_t be_id_to_le(be_id_t id)
 {
@@ -505,6 +534,7 @@
 #define SRB_LOGIN_SKIP_PRLI	BIT_2
 #define SRB_LOGIN_NVME_PRLI	BIT_3
 #define SRB_LOGIN_PRLI_ONLY	BIT_4
+#define SRB_LOGIN_FCSP		BIT_5
 			uint16_t data[2];
 			u32 iop[2];
 		} logio;
@@ -609,6 +639,10 @@
 			u16 cmd;
 			u16 vp_index;
 		} ctrlvp;
+		struct {
+			struct edif_sa_ctl	*sa_ctl;
+			struct qla_sa_update_frame sa_frame;
+		} sa_update;
 	} u;
 
 	struct timer_list timer;
@@ -639,6 +673,21 @@
 #define SRB_PRLI_CMD	21
 #define SRB_CTRL_VP	22
 #define SRB_PRLO_CMD	23
+#define SRB_SA_UPDATE	25
+#define SRB_ELS_CMD_HST_NOLOGIN 26
+#define SRB_SA_REPLACE	27
+
+struct qla_els_pt_arg {
+	u8 els_opcode;
+	u8 vp_idx;
+	__le16 nport_handle;
+	u16 control_flags, ox_id;
+	__le32 rx_xchg_address;
+	port_id_t did, sid;
+	u32 tx_len, tx_byte_count, rx_len, rx_byte_count;
+	dma_addr_t tx_addr, rx_addr;
+
+};
 
 enum {
 	TYPE_SRB,
@@ -646,6 +695,23 @@
 	TYPE_TGT_TMCMD,		/* task management */
 };
 
+struct iocb_resource {
+	u8 res_type;
+	u8 pad;
+	u16 iocb_cnt;
+};
+
+struct bsg_cmd {
+#ifndef NEW_LIBFC_API
+	struct fc_bsg_job *bsg_job;
+#else
+	struct bsg_job *bsg_job;
+#endif
+	union {
+		struct qla_els_pt_arg els_arg;
+	} u;
+};
+
 typedef struct srb {
 	/*
 	 * Do not move cmd_type field, it needs to
@@ -653,6 +719,7 @@
 	 */
 	uint8_t cmd_type;
 	uint8_t pad[3];
+	struct iocb_resource iores;
 	struct kref cmd_kref;	/* need to migrate ref_count over to this */
 	void *priv;
 	wait_queue_head_t nvme_ls_waitq;
@@ -681,7 +748,21 @@
 		struct bsg_job *bsg_job;
 #endif
 		struct srb_cmd scmd;
+		struct bsg_cmd bsg_cmd;
 	} u;
+	struct {
+		bool remapped;
+		struct {
+			dma_addr_t dma;
+			void *buf;
+			uint len;
+		} req;
+		struct {
+			dma_addr_t dma;
+			void *buf;
+			uint len;
+		} rsp;
+	} remap;
 	/*
 	 * Report completion status @res and call sp_put(@sp). @res is
 	 * an NVMe status code, a SCSI result (e.g. DID_OK << 16) or a
@@ -695,6 +776,11 @@
 	 * code.
 	 */
 	void (*put_fn)(struct kref *kref);
+
+	/*
+	 * Report completion for asynchronous commands.
+	 */
+	void (*async_done)(struct srb *sp, int res);
 } srb_t;
 
 #define GET_CMD_SP(sp) (sp->u.scmd.cmd)
@@ -1137,6 +1223,12 @@
 
 /* ISP mailbox loopback echo diagnostic error code */
 #define MBS_LB_RESET	0x17
+
+/* AEN mailbox Port Diagnostics test */
+#define AEN_START_DIAG_TEST		0x0	/* start the diagnostics */
+#define AEN_DONE_DIAG_TEST_WITH_NOERR	0x1	/* Done with no errors */
+#define AEN_DONE_DIAG_TEST_WITH_ERR	0x2	/* Done with error.*/
+
 /*
  * Firmware options 1, 2, 3.
  */
@@ -1547,7 +1639,7 @@
 	 * BIT_12 = Remote Write Optimization (1 - Enabled, 0 - Disabled)
 	 * BIT 11-0 = Reserved
 	 */
-	uint16_t flags;
+	__le16	flags;
 	uint8_t	reserved1[32];
 	uint16_t discard_OHRB_timeout_value;
 	uint16_t remote_write_opt_queue_num;
@@ -1652,7 +1744,7 @@
 	 */
 	uint8_t	 firmware_options[2];
 
-	uint16_t frame_payload_size;
+	__le16	frame_payload_size;
 	__le16	max_iocb_allocation;
 	__le16	execution_throttle;
 	uint8_t	 retry_count;
@@ -2121,6 +2213,12 @@
 #define CS_COMPLETE_CHKCOND	0x30	/* Error? */
 #define CS_IOCB_ERROR		0x31	/* Generic error for IOCB request
 					   failure */
+#define CS_REJECT_RECEIVED	0x4E	/* Reject received */
+#define CS_EDIF_AUTH_ERROR	0x63	/* decrypt error */
+#define CS_EDIF_PAD_LEN_ERROR	0x65	/* pad > frame size, not 4byte align */
+#define CS_EDIF_INV_REQ		0x66	/* invalid request */
+#define CS_EDIF_SPI_ERROR	0x67	/* rx frame unable to locate sa */
+#define CS_EDIF_HDR_ERROR	0x69	/* data frame != expected len */
 #define CS_BAD_PAYLOAD		0x80	/* Driver defined */
 #define CS_UNKNOWN		0x81	/* Driver defined */
 #define CS_RETRY		0x82	/* Driver defined */
@@ -2312,6 +2410,7 @@
 			__le16	nport_handle;
 			uint16_t reserved_2;
 			__le16	flags;
+#define NOTIFY24XX_FLAGS_FCSP		BIT_5
 #define NOTIFY24XX_FLAGS_GLOBAL_TPRLO   BIT_1
 #define NOTIFY24XX_FLAGS_PUREX_IOCB     BIT_0
 			__le16	srr_rx_id;
@@ -2395,11 +2494,9 @@
  */
 typedef enum {
 	FCT_UNKNOWN,
-	FCT_RSCN,
-	FCT_SWITCH,
-	FCT_BROADCAST,
-	FCT_INITIATOR,
-	FCT_TARGET,
+	FCT_BROADCAST = 0x01,
+	FCT_INITIATOR = 0x02,
+	FCT_TARGET    = 0x04,
 	FCT_NVME_INITIATOR = 0x10,
 	FCT_NVME_TARGET = 0x20,
 	FCT_NVME_DISCOVERY = 0x40,
@@ -2442,6 +2539,7 @@
 	DSC_LOGIN_COMPLETE,
 	DSC_ADISC,
 	DSC_DELETE_PEND,
+	DSC_LOGIN_AUTH_PEND,
 };
 
 enum login_state {	/* FW control Target side */
@@ -2469,12 +2567,6 @@
 	struct list_head list;
 	struct scsi_qla_host *vha;
 
-	uint8_t node_name[WWN_SIZE];
-	uint8_t port_name[WWN_SIZE];
-	port_id_t d_id;
-	uint16_t loop_id;
-	uint16_t old_loop_id;
-
 	unsigned int conf_compl_supported:1;
 	unsigned int deleted:2;
 	unsigned int free_pending:1;
@@ -2491,15 +2583,26 @@
 	unsigned int n2n_flag:1;
 	unsigned int explicit_logout:1;
 	unsigned int prli_pend_timer:1;
+	unsigned int do_prli_nvme:1;
+
+	uint8_t nvme_flag;
+
+	uint8_t node_name[WWN_SIZE];
+	uint8_t port_name[WWN_SIZE];
+	port_id_t d_id;
+	uint16_t loop_id;
+	uint16_t old_loop_id;
 
 	struct completion nvme_del_done;
 	uint32_t nvme_prli_service_param;
+#define NVME_PRLI_SP_PI_CTRL	BIT_9
+#define NVME_PRLI_SP_SLER	BIT_8
 #define NVME_PRLI_SP_CONF       BIT_7
 #define NVME_PRLI_SP_INITIATOR  BIT_5
 #define NVME_PRLI_SP_TARGET     BIT_4
 #define NVME_PRLI_SP_DISCOVERY  BIT_3
 #define NVME_PRLI_SP_FIRST_BURST	BIT_0
-	uint8_t nvme_flag;
+
 	uint32_t nvme_first_burst_size;
 #define NVME_FLAG_REGISTERED 4
 #define NVME_FLAG_DELETING 2
@@ -2510,6 +2613,8 @@
 	int generation;
 
 	struct se_session *se_sess;
+	struct list_head sess_cmd_list;
+	spinlock_t sess_cmd_lock;
 	struct kref sess_kref;
 	struct qla_tgt *tgt;
 	unsigned long expires;
@@ -2571,6 +2676,39 @@
 	u8 last_login_state;
 	u16 n2n_link_reset_cnt;
 	u16 n2n_chip_reset;
+
+	struct dentry *dfs_rport_dir;
+
+	u64 tgt_short_link_down_cnt;
+	u64 tgt_link_down_time;
+	u64 dev_loss_tmo;
+	/*
+	 * EDIF parameters for encryption.
+	 */
+	struct {
+		uint32_t	enable:1;	/* device is edif enabled/req'd */
+		uint32_t	app_stop:2;
+		uint32_t	aes_gmac:1;
+		uint32_t	app_sess_online:1;
+		uint32_t	tx_sa_set:1;
+		uint32_t	rx_sa_set:1;
+		uint32_t	tx_sa_pending:1;
+		uint32_t	rx_sa_pending:1;
+		uint32_t	tx_rekey_cnt;
+		uint32_t	rx_rekey_cnt;
+		uint64_t	tx_bytes;
+		uint64_t	rx_bytes;
+		uint8_t		sess_down_acked;
+		uint8_t		auth_state;
+		uint16_t	authok:1;
+		uint16_t	rekey_cnt;
+		struct list_head edif_indx_list;
+		spinlock_t  indx_list_lock;
+
+		struct list_head tx_sa_list;
+		struct list_head rx_sa_list;
+		spinlock_t	sa_list_lock;
+	} edif;
 } fc_port_t;
 
 enum {
@@ -2595,24 +2733,28 @@
 /*
  * Fibre channel port/lun states.
  */
-#define FCS_UNCONFIGURED	1
-#define FCS_DEVICE_DEAD		2
-#define FCS_DEVICE_LOST		3
-#define FCS_ONLINE		4
+enum {
+	FCS_UNKNOWN,
+	FCS_UNCONFIGURED,
+	FCS_DEVICE_DEAD,
+	FCS_DEVICE_LOST,
+	FCS_ONLINE,
+};
 
 extern const char *const port_state_str[5];
 
-static const char * const port_dstate_str[] = {
-	"DELETED",
-	"GNN_ID",
-	"GNL",
-	"LOGIN_PEND",
-	"LOGIN_FAILED",
-	"GPDB",
-	"UPD_FCPORT",
-	"LOGIN_COMPLETE",
-	"ADISC",
-	"DELETE_PEND"
+static const char *const port_dstate_str[] = {
+	[DSC_DELETED]		= "DELETED",
+	[DSC_GNN_ID]		= "GNN_ID",
+	[DSC_GNL]		= "GNL",
+	[DSC_LOGIN_PEND]	= "LOGIN_PEND",
+	[DSC_LOGIN_FAILED]	= "LOGIN_FAILED",
+	[DSC_GPDB]		= "GPDB",
+	[DSC_UPD_FCPORT]	= "UPD_FCPORT",
+	[DSC_LOGIN_COMPLETE]	= "LOGIN_COMPLETE",
+	[DSC_ADISC]		= "ADISC",
+	[DSC_DELETE_PEND]	= "DELETE_PEND",
+	[DSC_LOGIN_AUTH_PEND]	= "LOGIN_AUTH_PEND",
 };
 
 /*
@@ -2624,6 +2766,8 @@
 #define FCF_ASYNC_SENT		BIT_3
 #define FCF_CONF_COMP_SUPPORTED BIT_4
 #define FCF_ASYNC_ACTIVE	BIT_5
+#define FCF_FCSP_DEVICE		BIT_6
+#define FCF_EDIF_DELETE		BIT_7
 
 /* No loop ID flag. */
 #define FC_NO_LOOP_ID		0x1000
@@ -2715,7 +2859,7 @@
 /*
  * FDMI HBA attribute types.
  */
-#define FDMI1_HBA_ATTR_COUNT			9
+#define FDMI1_HBA_ATTR_COUNT			10
 #define FDMI2_HBA_ATTR_COUNT			17
 
 #define FDMI_HBA_NODE_NAME			0x1
@@ -2812,7 +2956,11 @@
 #define FDMI_PORT_SPEED_8GB		0x10
 #define FDMI_PORT_SPEED_16GB		0x20
 #define FDMI_PORT_SPEED_32GB		0x40
-#define FDMI_PORT_SPEED_64GB		0x80
+#define FDMI_PORT_SPEED_20GB		0x80
+#define FDMI_PORT_SPEED_40GB		0x100
+#define FDMI_PORT_SPEED_128GB		0x200
+#define FDMI_PORT_SPEED_64GB		0x400
+#define FDMI_PORT_SPEED_256GB		0x800
 #define FDMI_PORT_SPEED_UNKNOWN		0x8000
 
 #define FC_CLASS_2	0x04
@@ -3118,6 +3266,8 @@
 #define GFF_NVME_OFFSET		23 /* type = 28h */
 		struct {
 			uint8_t fc4_features[128];
+#define FC4_FF_TARGET    BIT_0
+#define FC4_FF_INITIATOR BIT_1
 		} gff_id;
 		struct {
 			uint8_t reserved;
@@ -3310,8 +3460,10 @@
 	void (*fw_dump)(struct scsi_qla_host *vha);
 	void (*mpi_fw_dump)(struct scsi_qla_host *, int);
 
+	/* Context: task, might sleep */
 	int (*beacon_on) (struct scsi_qla_host *);
 	int (*beacon_off) (struct scsi_qla_host *);
+
 	void (*beacon_blink) (struct scsi_qla_host *);
 
 	void *(*read_optrom)(struct scsi_qla_host *, void *,
@@ -3322,7 +3474,10 @@
 	int (*get_flash_version) (struct scsi_qla_host *, void *);
 	int (*start_scsi) (srb_t *);
 	int (*start_scsi_mq) (srb_t *);
+
+	/* Context: task, might sleep */
 	int (*abort_isp) (struct scsi_qla_host *);
+
 	int (*iospace_config)(struct qla_hw_data *);
 	int (*initialize_adapter)(struct scsi_qla_host *);
 };
@@ -3389,6 +3544,7 @@
 	QLA_EVT_SP_RETRY,
 	QLA_EVT_IIDMA,
 	QLA_EVT_ELS_PLOGI,
+	QLA_EVT_SA_REPLACE,
 };
 
 
@@ -3447,6 +3603,11 @@
 			u8 fc4_type;
 			srb_t *sp;
 		} gpnft;
+		struct {
+			struct edif_sa_ctl	*sa_ctl;
+			fc_port_t *fcport;
+			uint16_t nport_handle;
+		} sa_update;
 	 } u;
 };
 
@@ -3535,6 +3696,14 @@
 	uint64_t num_term_xchg_sent;
 };
 
+struct qla_counters {
+	uint64_t input_bytes;
+	uint64_t input_requests;
+	uint64_t output_bytes;
+	uint64_t output_requests;
+
+};
+
 struct qla_qpair;
 
 /* Response queue data structure */
@@ -3593,6 +3762,15 @@
 	uint8_t req_pkt[REQUEST_ENTRY_SIZE];
 };
 
+struct qla_fw_resources {
+	u16 iocbs_total;
+	u16 iocbs_limit;
+	u16 iocbs_qp_limit;
+	u16 iocbs_used;
+};
+
+#define QLA_IOCB_PCT_LIMIT 95
+
 /*Queue pair data structure */
 struct qla_qpair {
 	spinlock_t qp_lock;
@@ -3619,6 +3797,7 @@
 	uint32_t enable_class_2:1;
 	uint32_t enable_explicit_conf:1;
 	uint32_t use_shadow_reg:1;
+	uint32_t rcv_intr:1;
 
 	uint16_t id;			/* qp number used with FW */
 	uint16_t vp_idx;		/* vport ID */
@@ -3634,13 +3813,20 @@
 	struct qla_msix_entry *msix; /* point to &ha->msix_entries[x] */
 	struct qla_hw_data *hw;
 	struct work_struct q_work;
+	struct qla_counters counters;
+
 	struct list_head qp_list_elem; /* vha->qp_list */
 	struct list_head hints_list;
-	uint16_t cpuid;
+
 	uint16_t retry_term_cnt;
 	__le32	retry_term_exchg_addr;
 	uint64_t retry_term_jiff;
 	struct qla_tgt_counters tgt_counters;
+	uint16_t cpuid;
+	struct qla_fw_resources fwres ____cacheline_aligned;
+	u32	cmd_cnt;
+	u32	cmd_completion_cnt;
+	u32	prev_completion_cnt;
 };
 
 /* Place holder for FW buffer parameters */
@@ -3797,7 +3983,7 @@
 	__le32 __iomem *atio_q_in;
 	__le32 __iomem *atio_q_out;
 
-	struct qla_tgt_func_tmpl *tgt_ops;
+	const struct qla_tgt_func_tmpl *tgt_ops;
 	struct qla_tgt_vp_map *tgt_vp_map;
 
 	int saved_set;
@@ -3824,7 +4010,6 @@
 	int num_act_qpairs;
 #define DEFAULT_NAQP 2
 	spinlock_t atio_lock ____cacheline_aligned;
-	struct btree_head32 host_map;
 };
 
 #define MAX_QFULL_CMDS_ALLOC	8192
@@ -3839,6 +4024,13 @@
 	u32 num_mpi_reset;
 };
 
+/* refer to pcie_do_recovery reference */
+typedef enum {
+	QLA_PCI_RESUME,
+	QLA_PCI_ERR_DETECTED,
+	QLA_PCI_MMIO_ENABLED,
+	QLA_PCI_SLOT_RESET,
+} pci_error_state_t;
 /*
  * Qlogic host adapter specific data structure.
 */
@@ -3847,6 +4039,7 @@
 	/* SRB cache. */
 #define SRB_MIN_REQ     128
 	mempool_t       *srb_mempool;
+	u8 port_name[WWN_SIZE];
 
 	volatile struct {
 		uint32_t	mbox_int		:1;
@@ -3907,7 +4100,14 @@
 		uint32_t	scm_supported_f:1;
 				/* Enabled in Driver */
 		uint32_t	scm_enabled:1;
-		uint32_t	max_req_queue_warned:1;
+		uint32_t	edif_hw:1;
+		uint32_t	edif_enabled:1;
+		uint32_t	n2n_fw_acc_sec:1;
+		uint32_t	plogi_template_valid:1;
+		uint32_t	port_isolated:1;
+		uint32_t	eeh_flush:2;
+#define EEH_FLUSH_RDY  1
+#define EEH_FLUSH_DONE 2
 	} flags;
 
 	uint16_t max_exchg;
@@ -3942,6 +4142,7 @@
 	uint32_t		rsp_que_len;
 	uint32_t		req_que_off;
 	uint32_t		rsp_que_off;
+	unsigned long		eeh_jif;
 
 	/* Multi queue data structs */
 	device_reg_t *mqiobase;
@@ -4124,15 +4325,28 @@
 #define IS_OEM_001(ha)          ((ha)->device_type & DT_OEM_001)
 #define HAS_EXTENDED_IDS(ha)    ((ha)->device_type & DT_EXTENDED_IDS)
 #define IS_CT6_SUPPORTED(ha)	((ha)->device_type & DT_CT6_SUPPORTED)
-#define IS_MQUE_CAPABLE(ha)	((ha)->mqenable || IS_QLA83XX(ha) || \
-				IS_QLA27XX(ha) || IS_QLA28XX(ha))
+#define IS_MQUE_CAPABLE(ha)	(IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
+				 IS_QLA28XX(ha))
 #define IS_BIDI_CAPABLE(ha) \
     (IS_QLA25XX(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
 /* Bit 21 of fw_attributes decides the MCTP capabilities */
 #define IS_MCTP_CAPABLE(ha)	(IS_QLA2031(ha) && \
 				((ha)->fw_attributes_ext[0] & BIT_0))
-#define IS_PI_UNINIT_CAPABLE(ha)	(IS_QLA83XX(ha) || IS_QLA27XX(ha))
-#define IS_PI_IPGUARD_CAPABLE(ha)	(IS_QLA83XX(ha) || IS_QLA27XX(ha))
+#define QLA_ABTS_FW_ENABLED(_ha)       ((_ha)->fw_attributes_ext[0] & BIT_14)
+#define QLA_SRB_NVME_LS(_sp) ((_sp)->type == SRB_NVME_LS)
+#define QLA_SRB_NVME_CMD(_sp) ((_sp)->type == SRB_NVME_CMD)
+#define QLA_NVME_IOS(_sp) (QLA_SRB_NVME_CMD(_sp) || QLA_SRB_NVME_LS(_sp))
+#define QLA_LS_ABTS_WAIT_ENABLED(_sp) \
+	(QLA_SRB_NVME_LS(_sp) && QLA_ABTS_FW_ENABLED(_sp->fcport->vha->hw))
+#define QLA_CMD_ABTS_WAIT_ENABLED(_sp) \
+	(QLA_SRB_NVME_CMD(_sp) && QLA_ABTS_FW_ENABLED(_sp->fcport->vha->hw))
+#define QLA_ABTS_WAIT_ENABLED(_sp) \
+	(QLA_NVME_IOS(_sp) && QLA_ABTS_FW_ENABLED(_sp->fcport->vha->hw))
+
+#define IS_PI_UNINIT_CAPABLE(ha)	(IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
+					 IS_QLA28XX(ha))
+#define IS_PI_IPGUARD_CAPABLE(ha)	(IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
+					 IS_QLA28XX(ha))
 #define IS_PI_DIFB_DIX0_CAPABLE(ha)	(0)
 #define IS_PI_SPLIT_DET_CAPABLE_HBA(ha)	(IS_QLA83XX(ha) || IS_QLA27XX(ha) || \
 					IS_QLA28XX(ha))
@@ -4154,6 +4368,10 @@
 #define USE_ASYNC_SCAN(ha) (IS_QLA25XX(ha) || IS_QLA81XX(ha) ||\
 	IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
 
+#define IS_ZIO_THRESHOLD_CAPABLE(ha) \
+	((IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) &&\
+	 (ha->zio_mode == QLA_ZIO_MODE_6))
+
 	/* HBA serial number */
 	uint8_t		serial0;
 	uint8_t		serial1;
@@ -4241,7 +4459,7 @@
 	/* Extended Logins  */
 	void		*exlogin_buf;
 	dma_addr_t	exlogin_buf_dma;
-	int		exlogin_size;
+	uint32_t	exlogin_size;
 
 #define ENABLE_EXCHANGE_OFFLD	BIT_2
 
@@ -4252,7 +4470,8 @@
 	int 		exchoffld_count;
 
 	/* n2n */
-	struct els_plogi_payload plogi_els_payld;
+	struct fc_els_flogi plogi_els_payld;
+#define LOGIN_TEMPLATE_SIZE (sizeof(struct fc_els_flogi) - 4)
 
 	void            *swl;
 
@@ -4300,6 +4519,8 @@
 #define FW_ATTR_EXT0_SCM_BROCADE	0x00001000
 	/* Cisco fabric attached */
 #define FW_ATTR_EXT0_SCM_CISCO		0x00002000
+#define FW_ATTR_EXT0_NVME2	BIT_13
+#define FW_ATTR_EXT0_EDIF	BIT_5
 	uint16_t	fw_attributes_ext[2];
 	uint32_t	fw_memory_size;
 	uint32_t	fw_transfer_size;
@@ -4470,7 +4691,9 @@
 	struct qla_chip_state_84xx *cs84xx;
 	struct isp_operations *isp_ops;
 	struct workqueue_struct *wq;
+	struct work_struct heartbeat_work;
 	struct qlfc_fw fw_buf;
+	unsigned long last_heartbeat_run_jiffies;
 
 	/* FCP_CMND priority support */
 	struct qla_fcp_prio_cfg *fcp_prio_cfg;
@@ -4570,8 +4793,25 @@
 #define DEFAULT_ZIO_THRESHOLD 5
 
 	struct qla_hw_data_stat stat;
+	pci_error_state_t pci_error_state;
+	struct dma_pool *purex_dma_pool;
+	struct btree_head32 host_map;
+
+#define EDIF_NUM_SA_INDEX	512
+#define EDIF_TX_SA_INDEX_BASE	EDIF_NUM_SA_INDEX
+	void *edif_rx_sa_id_map;
+	void *edif_tx_sa_id_map;
+	spinlock_t sadb_fp_lock;
+
+	struct list_head sadb_tx_index_list;
+	struct list_head sadb_rx_index_list;
+	spinlock_t sadb_lock;	/* protects list */
+	struct els_reject elsrej;
+	u8 edif_post_stop_cnt_down;
 };
 
+#define RX_ELS_SIZE (roundup(sizeof(struct enode) + ELS_MAX_PAYLOAD, SMP_CACHE_BYTES))
+
 struct active_regions {
 	uint8_t global;
 	struct {
@@ -4579,6 +4819,7 @@
 		uint8_t vpd_nvram;
 		uint8_t npiv_config_0_1;
 		uint8_t npiv_config_2_3;
+		uint8_t nvme_params;
 	} aux;
 };
 
@@ -4610,6 +4851,8 @@
 	} iocb;
 };
 
+#include "qla_edif.h"
+
 #define SCM_FLAG_RDF_REJECT		0x00
 #define SCM_FLAG_RDF_COMPLETED		0x01
 
@@ -4649,6 +4892,7 @@
 		uint32_t	qpairs_rsp_created:1;
 		uint32_t	nvme_enabled:1;
 		uint32_t        nvme_first_burst:1;
+		uint32_t        nvme2_enabled:1;
 	} flags;
 
 	atomic_t	loop_state;
@@ -4689,7 +4933,7 @@
 #define FX00_CRITEMP_RECOVERY	25
 #define FX00_HOST_INFO_RESEND	26
 #define QPAIR_ONLINE_CHECK_NEEDED	27
-#define SET_NVME_ZIO_THRESHOLD_NEEDED	28
+#define DO_EEH_RECOVERY		28
 #define DETECT_SFP_CHANGE	29
 #define N2N_LOGIN_NEEDED	30
 #define IOCB_WORK_ACTIVE	31
@@ -4747,7 +4991,6 @@
 
 	/* list of commands waiting on workqueue */
 	struct list_head	qla_cmd_list;
-	struct list_head	qla_sess_op_cmd_list;
 	struct list_head	unknown_atio_list;
 	spinlock_t		cmd_list_lock;
 	struct delayed_work	unknown_atio_work;
@@ -4807,6 +5050,8 @@
 	uint16_t ql2xexchoffld;
 	uint16_t ql2xiniexchg;
 
+	struct dentry *dfs_rport_root;
+
 	struct purex_list {
 		struct list_head head;
 		spinlock_t lock;
@@ -4828,6 +5073,19 @@
 	uint8_t	scm_fabric_connection_flags;
 
 	unsigned int irq_offset;
+
+	u64 hw_err_cnt;
+	u64 interface_err_cnt;
+	u64 cmd_timeout_cnt;
+	u64 reset_cmd_err_cnt;
+	u64 link_down_time;
+	u64 short_link_down_cnt;
+	struct edif_dbell e_dbell;
+	struct pur_core pur_cinfo;
+
+#define DPORT_DIAG_IN_PROGRESS                 BIT_0
+#define DPORT_DIAG_CHIP_RESET_IN_PROGRESS      BIT_1
+	uint16_t dport_status;
 } scsi_qla_host_t;
 
 struct qla27xx_image_status {
@@ -4846,6 +5104,7 @@
 #define QLA28XX_AUX_IMG_VPD_NVRAM		BIT_1
 #define QLA28XX_AUX_IMG_NPIV_CONFIG_0_1		BIT_2
 #define QLA28XX_AUX_IMG_NPIV_CONFIG_2_3		BIT_3
+#define QLA28XX_AUX_IMG_NVME_PARAMS		BIT_4
 
 #define SET_VP_IDX	1
 #define SET_AL_PA	2
@@ -4923,17 +5182,17 @@
 		(test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) || \
 			 test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
 
-#define QLA_VHA_MARK_BUSY(__vha, __bail) do {		\
-	atomic_inc(&__vha->vref_count);			\
-	mb();						\
-	if (__vha->flags.delete_progress) {		\
-		atomic_dec(&__vha->vref_count);		\
-		wake_up(&__vha->vref_waitq);		\
-		__bail = 1;				\
-	} else {					\
-		__bail = 0;				\
-	}						\
-} while (0)
+static inline bool qla_vha_mark_busy(scsi_qla_host_t *vha)
+{
+	atomic_inc(&vha->vref_count);
+	mb();
+	if (vha->flags.delete_progress) {
+		atomic_dec(&vha->vref_count);
+		wake_up(&vha->vref_waitq);
+		return true;
+	}
+	return false;
+}
 
 #define QLA_VHA_MARK_NOT_BUSY(__vha) do {		\
 	atomic_dec(&__vha->vref_count);			\
@@ -4952,8 +5211,7 @@
 } while (0)
 
 #define QLA_QPAIR_MARK_NOT_BUSY(__qpair)		\
-	atomic_dec(&__qpair->ref_count);		\
-
+	atomic_dec(&__qpair->ref_count)
 
 #define QLA_ENA_CONF(_ha) {\
     int i;\
@@ -4999,6 +5257,9 @@
 #define QLA_BUSY			0x107
 #define QLA_ALREADY_REGISTERED		0x109
 #define QLA_OS_TIMER_EXPIRED		0x10a
+#define QLA_ERR_NO_QPAIR		0x10b
+#define QLA_ERR_NOT_FOUND		0x10c
+#define QLA_ERR_FROM_FW			0x10d
 
 #define NVRAM_DELAY()		udelay(10)
 
@@ -5019,8 +5280,6 @@
 
 #define	QLA_DSDS_PER_IOCB	37
 
-#define CMD_SP(Cmnd)		((Cmnd)->SCp.ptr)
-
 #define QLA_SG_ALL	1024
 
 enum nexus_wait_type {
@@ -5029,6 +5288,43 @@
 	WAIT_LUN,
 };
 
+#define INVALID_EDIF_SA_INDEX	0xffff
+#define RX_DELETE_NO_EDIF_SA_INDEX	0xfffe
+
+#define QLA_SKIP_HANDLE QLA_TGT_SKIP_HANDLE
+
+/* edif hash element */
+struct edif_list_entry {
+	uint16_t handle;			/* nport_handle */
+	uint32_t update_sa_index;
+	uint32_t delete_sa_index;
+	uint32_t count;				/* counter for filtering sa_index */
+#define EDIF_ENTRY_FLAGS_CLEANUP	0x01	/* this index is being cleaned up */
+	uint32_t flags;				/* used by sadb cleanup code */
+	fc_port_t *fcport;			/* needed by rx delay timer function */
+	struct timer_list timer;		/* rx delay timer */
+	struct list_head next;
+};
+
+#define EDIF_TX_INDX_BASE 512
+#define EDIF_RX_INDX_BASE 0
+#define EDIF_RX_DELETE_FILTER_COUNT 3	/* delay queuing rx delete until this many */
+
+/* entry in the sa_index free pool */
+
+struct sa_index_pair {
+	uint16_t sa_index;
+	uint32_t spi;
+};
+
+/* edif sa_index data structure */
+struct edif_sa_index_entry {
+	struct sa_index_pair sa_pair[2];
+	fc_port_t *fcport;
+	uint16_t handle;
+	struct list_head next;
+};
+
 /* Refer to SNIA SFF 8247 */
 struct sff_8247_a0 {
 	u8 txid;	/* transceiver id */
@@ -5130,6 +5426,8 @@
 	 ha->current_topology == ISP_CFG_N || \
 	 !ha->current_topology)
 
+#define QLA_N2N_WAIT_TIME	5 /* 2 * ra_tov(n2n) + 1 */
+
 #define NVME_TYPE(fcport) \
 	(fcport->fc4_type & FS_FC4TYPE_NVME) \
 
@@ -5142,16 +5440,88 @@
 #define NVME_FCP_TARGET(fcport) \
 	(FCP_TYPE(fcport) && NVME_TYPE(fcport)) \
 
+#define NVME_PRIORITY(ha, fcport) \
+	(NVME_FCP_TARGET(fcport) && \
+	 (ha->fc4_type_priority == FC4_PRIORITY_NVME))
+
 #define NVME_TARGET(ha, fcport) \
-	((NVME_FCP_TARGET(fcport) && \
-	(ha->fc4_type_priority == FC4_PRIORITY_NVME)) || \
+	(fcport->do_prli_nvme || \
 	NVME_ONLY_TARGET(fcport)) \
 
 #define PRLI_PHASE(_cls) \
 	((_cls == DSC_LS_PRLI_PEND) || (_cls == DSC_LS_PRLI_COMP))
 
+enum ql_vnd_host_stat_action {
+	QLA_STOP = 0,
+	QLA_START,
+	QLA_CLEAR,
+};
+
+struct ql_vnd_mng_host_stats_param {
+	u32 stat_type;
+	enum ql_vnd_host_stat_action action;
+} __packed;
+
+struct ql_vnd_mng_host_stats_resp {
+	u32 status;
+} __packed;
+
+struct ql_vnd_stats_param {
+	u32 stat_type;
+} __packed;
+
+struct ql_vnd_tgt_stats_param {
+	s32 tgt_id;
+	u32 stat_type;
+} __packed;
+
+enum ql_vnd_host_port_action {
+	QLA_ENABLE = 0,
+	QLA_DISABLE,
+};
+
+struct ql_vnd_mng_host_port_param {
+	enum ql_vnd_host_port_action action;
+} __packed;
+
+struct ql_vnd_mng_host_port_resp {
+	u32 status;
+} __packed;
+
+struct ql_vnd_stat_entry {
+	u32 stat_type;	/* Failure type */
+	u32 tgt_num;	/* Target Num */
+	u64 cnt;	/* Counter value */
+} __packed;
+
+struct ql_vnd_stats {
+	u64 entry_count; /* Num of entries */
+	u64 rservd;
+	struct ql_vnd_stat_entry entry[]; /* Place holder of entries */
+} __packed;
+
+struct ql_vnd_host_stats_resp {
+	u32 status;
+	struct ql_vnd_stats stats;
+} __packed;
+
+struct ql_vnd_tgt_stats_resp {
+	u32 status;
+	struct ql_vnd_stats stats;
+} __packed;
+
 #include "qla_target.h"
 #include "qla_gbl.h"
 #include "qla_dbg.h"
 #include "qla_inline.h"
+
+#define IS_SESSION_DELETED(_fcport) (_fcport->disc_state == DSC_DELETE_PEND || \
+				      _fcport->disc_state == DSC_DELETED)
+
+#define DBG_FCPORT_PRFMT(_fp, _fmt, _args...) \
+	"%s: %8phC: " _fmt " (state=%d disc_state=%d scan_state=%d loopid=0x%x deleted=%d flags=0x%x)\n", \
+	__func__, _fp->port_name, ##_args, atomic_read(&_fp->state), \
+	_fp->disc_state, _fp->scan_state, _fp->loop_id, _fp->deleted, \
+	_fp->flags
+
 #endif
diff --git a/scst/qla2x00t-32gbit/qla_dfs.c b/scst/qla2x00t-32gbit/qla_dfs.c
index e62b211..038352a 100644
--- a/scst/qla2x00t-32gbit/qla_dfs.c
+++ b/scst/qla2x00t-32gbit/qla_dfs.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
 
@@ -12,6 +11,144 @@
 static struct dentry *qla2x00_dfs_root;
 static atomic_t qla2x00_dfs_root_count;
 
+#define QLA_DFS_RPORT_DEVLOSS_TMO	1
+
+static int
+qla_dfs_rport_get(struct fc_port *fp, int attr_id, u64 *val)
+{
+	switch (attr_id) {
+	case QLA_DFS_RPORT_DEVLOSS_TMO:
+		/* Only supported for FC-NVMe devices that are registered. */
+		if (!(fp->nvme_flag & NVME_FLAG_REGISTERED))
+			return -EIO;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
+		*val = fp->nvme_remote_port->dev_loss_tmo;
+		break;
+#else
+		return -EINVAL;
+#endif
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int
+qla_dfs_rport_set(struct fc_port *fp, int attr_id, u64 val)
+{
+	switch (attr_id) {
+	case QLA_DFS_RPORT_DEVLOSS_TMO:
+		/* Only supported for FC-NVMe devices that are registered. */
+		if (!(fp->nvme_flag & NVME_FLAG_REGISTERED))
+			return -EIO;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0) && IS_ENABLED(CONFIG_NVME_FC)
+		return nvme_fc_set_remoteport_devloss(fp->nvme_remote_port,
+						      val);
+#else /* CONFIG_NVME_FC */
+		return -EINVAL;
+#endif /* CONFIG_NVME_FC */
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+#define DEFINE_QLA_DFS_RPORT_RW_ATTR(_attr_id, _attr)		\
+static int qla_dfs_rport_##_attr##_get(void *data, u64 *val)	\
+{								\
+	struct fc_port *fp = data;				\
+	return qla_dfs_rport_get(fp, _attr_id, val);		\
+}								\
+static int qla_dfs_rport_##_attr##_set(void *data, u64 val)	\
+{								\
+	struct fc_port *fp = data;				\
+	return qla_dfs_rport_set(fp, _attr_id, val);		\
+}								\
+DEFINE_DEBUGFS_ATTRIBUTE(qla_dfs_rport_##_attr##_fops,		\
+		qla_dfs_rport_##_attr##_get,			\
+		qla_dfs_rport_##_attr##_set, "%llu\n")
+
+/*
+ * Wrapper for getting fc_port fields.
+ *
+ * _attr    : Attribute name.
+ * _get_val : Accessor macro to retrieve the value.
+ */
+#define DEFINE_QLA_DFS_RPORT_FIELD_GET(_attr, _get_val)			\
+static int qla_dfs_rport_field_##_attr##_get(void *data, u64 *val)	\
+{									\
+	struct fc_port *fp = data;					\
+	*val = _get_val;						\
+	return 0;							\
+}									\
+DEFINE_DEBUGFS_ATTRIBUTE(qla_dfs_rport_field_##_attr##_fops,		\
+		qla_dfs_rport_field_##_attr##_get,			\
+		NULL, "%llu\n")
+
+#define DEFINE_QLA_DFS_RPORT_ACCESS(_attr, _get_val) \
+	DEFINE_QLA_DFS_RPORT_FIELD_GET(_attr, _get_val)
+
+#define DEFINE_QLA_DFS_RPORT_FIELD(_attr) \
+	DEFINE_QLA_DFS_RPORT_FIELD_GET(_attr, fp->_attr)
+
+DEFINE_QLA_DFS_RPORT_RW_ATTR(QLA_DFS_RPORT_DEVLOSS_TMO, dev_loss_tmo);
+
+DEFINE_QLA_DFS_RPORT_FIELD(disc_state);
+DEFINE_QLA_DFS_RPORT_FIELD(scan_state);
+DEFINE_QLA_DFS_RPORT_FIELD(fw_login_state);
+DEFINE_QLA_DFS_RPORT_FIELD(login_pause);
+DEFINE_QLA_DFS_RPORT_FIELD(flags);
+DEFINE_QLA_DFS_RPORT_FIELD(nvme_flag);
+DEFINE_QLA_DFS_RPORT_FIELD(last_rscn_gen);
+DEFINE_QLA_DFS_RPORT_FIELD(rscn_gen);
+DEFINE_QLA_DFS_RPORT_FIELD(login_gen);
+DEFINE_QLA_DFS_RPORT_FIELD(loop_id);
+DEFINE_QLA_DFS_RPORT_FIELD_GET(port_id, fp->d_id.b24);
+DEFINE_QLA_DFS_RPORT_FIELD_GET(sess_kref, kref_read(&fp->sess_kref));
+
+void
+qla2x00_dfs_create_rport(scsi_qla_host_t *vha, struct fc_port *fp)
+{
+	char wwn[32];
+
+#define QLA_CREATE_RPORT_FIELD_ATTR(_attr)			\
+	debugfs_create_file(#_attr, 0400, fp->dfs_rport_dir,	\
+		fp, &qla_dfs_rport_field_##_attr##_fops)
+
+	if (!vha->dfs_rport_root || fp->dfs_rport_dir)
+		return;
+
+	sprintf(wwn, "pn-%016llx", wwn_to_u64(fp->port_name));
+	fp->dfs_rport_dir = debugfs_create_dir(wwn, vha->dfs_rport_root);
+	if (!fp->dfs_rport_dir)
+		return;
+	if (NVME_TARGET(vha->hw, fp))
+		debugfs_create_file("dev_loss_tmo", 0600, fp->dfs_rport_dir,
+				    fp, &qla_dfs_rport_dev_loss_tmo_fops);
+
+	QLA_CREATE_RPORT_FIELD_ATTR(disc_state);
+	QLA_CREATE_RPORT_FIELD_ATTR(scan_state);
+	QLA_CREATE_RPORT_FIELD_ATTR(fw_login_state);
+	QLA_CREATE_RPORT_FIELD_ATTR(login_pause);
+	QLA_CREATE_RPORT_FIELD_ATTR(flags);
+	QLA_CREATE_RPORT_FIELD_ATTR(nvme_flag);
+	QLA_CREATE_RPORT_FIELD_ATTR(last_rscn_gen);
+	QLA_CREATE_RPORT_FIELD_ATTR(rscn_gen);
+	QLA_CREATE_RPORT_FIELD_ATTR(login_gen);
+	QLA_CREATE_RPORT_FIELD_ATTR(loop_id);
+	QLA_CREATE_RPORT_FIELD_ATTR(port_id);
+	QLA_CREATE_RPORT_FIELD_ATTR(sess_kref);
+}
+
+void
+qla2x00_dfs_remove_rport(scsi_qla_host_t *vha, struct fc_port *fp)
+{
+	if (!vha->dfs_rport_root || !fp->dfs_rport_dir)
+		return;
+	debugfs_remove_recursive(fp->dfs_rport_dir);
+	fp->dfs_rport_dir = NULL;
+}
+
 static int
 qla2x00_dfs_tgt_sess_show(struct seq_file *s, void *unused)
 {
@@ -37,89 +174,63 @@
 	return 0;
 }
 
-static int
-qla2x00_dfs_tgt_sess_open(struct inode *inode, struct file *file)
-{
-	scsi_qla_host_t *vha = inode->i_private;
-
-	return single_open(file, qla2x00_dfs_tgt_sess_show, vha);
-}
-
-static const struct file_operations dfs_tgt_sess_ops = {
-	.open		= qla2x00_dfs_tgt_sess_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(qla2x00_dfs_tgt_sess);
 
 static int
 qla2x00_dfs_tgt_port_database_show(struct seq_file *s, void *unused)
 {
 	scsi_qla_host_t *vha = s->private;
 	struct qla_hw_data *ha = vha->hw;
-	struct gid_list_info *gid_list, *gid;
+	struct gid_list_info *gid_list;
 	dma_addr_t gid_list_dma;
 	fc_port_t fc_port;
+	char *id_iter;
 	int rc, i;
 	uint16_t entries, loop_id;
-	struct qla_tgt *tgt = vha->vha_tgt.qla_tgt;
 
 	seq_printf(s, "%s\n", vha->host_str);
-	if (tgt) {
-		gid_list = dma_alloc_coherent(&ha->pdev->dev,
-		    qla2x00_gid_list_size(ha),
-		    &gid_list_dma, GFP_KERNEL);
-		if (!gid_list) {
-			ql_dbg(ql_dbg_user, vha, 0x7018,
-			    "DMA allocation failed for %u\n",
-			     qla2x00_gid_list_size(ha));
-			return 0;
-		}
-
-		rc = qla24xx_gidlist_wait(vha, gid_list, gid_list_dma,
-		    &entries);
-		if (rc != QLA_SUCCESS)
-			goto out_free_id_list;
-
-		gid = gid_list;
-
-		seq_puts(s, "Port Name	Port ID 	Loop ID\n");
-
-		for (i = 0; i < entries; i++) {
-			loop_id = le16_to_cpu(gid->loop_id);
-			memset(&fc_port, 0, sizeof(fc_port_t));
-
-			fc_port.loop_id = loop_id;
-
-			rc = qla24xx_gpdb_wait(vha, &fc_port, 0);
-			seq_printf(s, "%8phC  %02x%02x%02x  %d\n",
-				fc_port.port_name, fc_port.d_id.b.domain,
-				fc_port.d_id.b.area, fc_port.d_id.b.al_pa,
-				fc_port.loop_id);
-			gid = (void *)gid + ha->gid_list_info_size;
-		}
-out_free_id_list:
-		dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha),
-		    gid_list, gid_list_dma);
+	gid_list = dma_alloc_coherent(&ha->pdev->dev,
+				      qla2x00_gid_list_size(ha),
+				      &gid_list_dma, GFP_KERNEL);
+	if (!gid_list) {
+		ql_dbg(ql_dbg_user, vha, 0x7018,
+		       "DMA allocation failed for %u\n",
+		       qla2x00_gid_list_size(ha));
+		return 0;
 	}
 
+	rc = qla24xx_gidlist_wait(vha, gid_list, gid_list_dma,
+				  &entries);
+	if (rc != QLA_SUCCESS)
+		goto out_free_id_list;
+
+	id_iter = (char *)gid_list;
+
+	seq_puts(s, "Port Name	Port ID		Loop ID\n");
+
+	for (i = 0; i < entries; i++) {
+		struct gid_list_info *gid =
+			(struct gid_list_info *)id_iter;
+		loop_id = le16_to_cpu(gid->loop_id);
+		memset(&fc_port, 0, sizeof(fc_port_t));
+
+		fc_port.loop_id = loop_id;
+
+		rc = qla24xx_gpdb_wait(vha, &fc_port, 0);
+		seq_printf(s, "%8phC  %02x%02x%02x  %d\n",
+			   fc_port.port_name, fc_port.d_id.b.domain,
+			   fc_port.d_id.b.area, fc_port.d_id.b.al_pa,
+			   fc_port.loop_id);
+		id_iter += ha->gid_list_info_size;
+	}
+out_free_id_list:
+	dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha),
+			  gid_list, gid_list_dma);
+
 	return 0;
 }
 
-static int
-qla2x00_dfs_tgt_port_database_open(struct inode *inode, struct file *file)
-{
-	scsi_qla_host_t *vha = inode->i_private;
-
-	return single_open(file, qla2x00_dfs_tgt_port_database_show, vha);
-}
-
-static const struct file_operations dfs_tgt_port_database_ops = {
-	.open		= qla2x00_dfs_tgt_port_database_open,
-	.read		= seq_read,
-	.llseek		= seq_lseek,
-	.release	= single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(qla2x00_dfs_tgt_port_database);
 
 static int
 qla_dfs_fw_resource_cnt_show(struct seq_file *s, void *unused)
@@ -127,6 +238,8 @@
 	struct scsi_qla_host *vha = s->private;
 	uint16_t mb[MAX_IOCB_MB_REG];
 	int rc;
+	struct qla_hw_data *ha = vha->hw;
+	u16 iocbs_used, i;
 
 	rc = qla24xx_res_count_wait(vha, mb, SIZEOF_IOCB_MB_REG);
 	if (rc != QLA_SUCCESS) {
@@ -151,23 +264,22 @@
 		    mb[23]);
 	}
 
+	if (ql2xenforce_iocb_limit) {
+		/* lock is not require. It's an estimate. */
+		iocbs_used = ha->base_qpair->fwres.iocbs_used;
+		for (i = 0; i < ha->max_qpairs; i++) {
+			if (ha->queue_pair_map[i])
+				iocbs_used += ha->queue_pair_map[i]->fwres.iocbs_used;
+		}
+
+		seq_printf(s, "Driver: estimate iocb used [%d] high water limit [%d]\n",
+			   iocbs_used, ha->base_qpair->fwres.iocbs_limit);
+	}
+
 	return 0;
 }
 
-static int
-qla_dfs_fw_resource_cnt_open(struct inode *inode, struct file *file)
-{
-	struct scsi_qla_host *vha = inode->i_private;
-
-	return single_open(file, qla_dfs_fw_resource_cnt_show, vha);
-}
-
-static const struct file_operations dfs_fw_resource_cnt_ops = {
-	.open           = qla_dfs_fw_resource_cnt_open,
-	.read           = seq_read,
-	.llseek         = seq_lseek,
-	.release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(qla_dfs_fw_resource_cnt);
 
 static int
 qla_dfs_tgt_counters_show(struct seq_file *s, void *unused)
@@ -178,6 +290,10 @@
 		core_qla_snd_status, qla_core_ret_sta_ctio, core_qla_free_cmd,
 		num_q_full_sent, num_alloc_iocb_failed, num_term_xchg_sent;
 	u16 i;
+	fc_port_t *fcport = NULL;
+
+	if (qla2x00_chip_is_down(vha))
+		return 0;
 
 	qla_core_sbt_cmd = qpair->tgt_counters.qla_core_sbt_cmd;
 	core_qla_que_buf = qpair->tgt_counters.core_qla_que_buf;
@@ -241,23 +357,34 @@
 		vha->qla_stats.qla_dif_stats.dif_ref_tag_err);
 	seq_printf(s, "DIF App tag err = %d\n",
 		vha->qla_stats.qla_dif_stats.dif_app_tag_err);
+
+	seq_puts(s, "\n");
+	seq_puts(s, "Initiator Error Counters\n");
+	seq_printf(s, "HW Error Count =		%14lld\n",
+		   vha->hw_err_cnt);
+	seq_printf(s, "Link Down Count =	%14lld\n",
+		   vha->short_link_down_cnt);
+	seq_printf(s, "Interface Err Count =	%14lld\n",
+		   vha->interface_err_cnt);
+	seq_printf(s, "Cmd Timeout Count =	%14lld\n",
+		   vha->cmd_timeout_cnt);
+	seq_printf(s, "Reset Count =		%14lld\n",
+		   vha->reset_cmd_err_cnt);
+	seq_puts(s, "\n");
+
+	list_for_each_entry(fcport, &vha->vp_fcports, list) {
+		if (!fcport->rport)
+			continue;
+
+		seq_printf(s, "Target Num = %7d Link Down Count = %14lld\n",
+			   fcport->rport->number, fcport->tgt_short_link_down_cnt);
+	}
+	seq_puts(s, "\n");
+
 	return 0;
 }
 
-static int
-qla_dfs_tgt_counters_open(struct inode *inode, struct file *file)
-{
-	struct scsi_qla_host *vha = inode->i_private;
-
-	return single_open(file, qla_dfs_tgt_counters_show, vha);
-}
-
-static const struct file_operations dfs_tgt_counters_ops = {
-	.open           = qla_dfs_tgt_counters_open,
-	.read           = seq_read,
-	.llseek         = seq_lseek,
-	.release        = single_release,
-};
+DEFINE_SHOW_ATTRIBUTE(qla_dfs_tgt_counters);
 
 static int
 qla2x00_dfs_fce_show(struct seq_file *s, void *unused)
@@ -366,6 +493,99 @@
 	return 0;
 }
 
+/*
+ * Helper macros for setting up debugfs entries.
+ * _name: The name of the debugfs entry
+ * _ctx_struct: The context that was passed when creating the debugfs file
+ *
+ * QLA_DFS_SETUP_RD could be used when there is only a show function.
+ * - show function take the name qla_dfs_<sysfs-name>_show
+ *
+ * QLA_DFS_SETUP_RW could be used when there are both show and write functions.
+ * - show function take the name  qla_dfs_<sysfs-name>_show
+ * - write function take the name qla_dfs_<sysfs-name>_write
+ *
+ * To have a new debugfs entry, do:
+ * 1. Create a "struct dentry *" in the appropriate structure in the format
+ * dfs_<sysfs-name>
+ * 2. Setup debugfs entries using QLA_DFS_SETUP_RD / QLA_DFS_SETUP_RW
+ * 3. Create debugfs file in qla2x00_dfs_setup() using QLA_DFS_CREATE_FILE
+ * or QLA_DFS_ROOT_CREATE_FILE
+ * 4. Remove debugfs file in qla2x00_dfs_remove() using QLA_DFS_REMOVE_FILE
+ * or QLA_DFS_ROOT_REMOVE_FILE
+ *
+ * Example for creating "TEST" sysfs file:
+ * 1. struct qla_hw_data { ... struct dentry *dfs_TEST; }
+ * 2. QLA_DFS_SETUP_RD(TEST, scsi_qla_host_t);
+ * 3. In qla2x00_dfs_setup():
+ * QLA_DFS_CREATE_FILE(ha, TEST, 0600, ha->dfs_dir, vha);
+ * 4. In qla2x00_dfs_remove():
+ * QLA_DFS_REMOVE_FILE(ha, TEST);
+ */
+#define QLA_DFS_SETUP_RD(_name, _ctx_struct)				\
+static int								\
+qla_dfs_##_name##_open(struct inode *inode, struct file *file)		\
+{									\
+	_ctx_struct *__ctx = inode->i_private;				\
+									\
+	return single_open(file, qla_dfs_##_name##_show, __ctx);	\
+}									\
+									\
+static const struct file_operations qla_dfs_##_name##_ops = {		\
+	.open           = qla_dfs_##_name##_open,			\
+	.read           = seq_read,					\
+	.llseek         = seq_lseek,					\
+	.release        = single_release,				\
+};
+
+#define QLA_DFS_SETUP_RW(_name, _ctx_struct)				\
+static int								\
+qla_dfs_##_name##_open(struct inode *inode, struct file *file)		\
+{									\
+	_ctx_struct *__ctx = inode->i_private;				\
+									\
+	return single_open(file, qla_dfs_##_name##_show, __ctx);	\
+}									\
+									\
+static const struct file_operations qla_dfs_##_name##_ops = {		\
+	.open           = qla_dfs_##_name##_open,			\
+	.read           = seq_read,					\
+	.llseek         = seq_lseek,					\
+	.release        = single_release,				\
+	.write		= qla_dfs_##_name##_write,			\
+};
+
+#define QLA_DFS_ROOT_CREATE_FILE(_name, _perm, _ctx)			\
+	do {								\
+		if (!qla_dfs_##_name)					\
+			qla_dfs_##_name = debugfs_create_file(#_name,	\
+					_perm, qla2x00_dfs_root, _ctx,	\
+					&qla_dfs_##_name##_ops);	\
+	} while (0)
+
+#define QLA_DFS_ROOT_REMOVE_FILE(_name)					\
+	do {								\
+		if (qla_dfs_##_name) {					\
+			debugfs_remove(qla_dfs_##_name);		\
+			qla_dfs_##_name = NULL;				\
+		}							\
+	} while (0)
+
+#define QLA_DFS_CREATE_FILE(_struct, _name, _perm, _parent, _ctx)	\
+	do {								\
+		(_struct)->dfs_##_name = debugfs_create_file(#_name,	\
+					_perm, _parent, _ctx,		\
+					&qla_dfs_##_name##_ops)		\
+	} while (0)
+
+#define QLA_DFS_REMOVE_FILE(_struct, _name)				\
+	do {								\
+		if ((_struct)->dfs_##_name) {				\
+			debugfs_remove((_struct)->dfs_##_name);		\
+			(_struct)->dfs_##_name = NULL;			\
+		}							\
+	} while (0)
+
 static int
 qla_dfs_naqp_open(struct inode *inode, struct file *file)
 {
@@ -459,23 +679,35 @@
 
 create_nodes:
 	ha->dfs_fw_resource_cnt = debugfs_create_file("fw_resource_count",
-	    S_IRUSR, ha->dfs_dir, vha, &dfs_fw_resource_cnt_ops);
+	    S_IRUSR, ha->dfs_dir, vha, &qla_dfs_fw_resource_cnt_fops);
 
 	ha->dfs_tgt_counters = debugfs_create_file("tgt_counters", S_IRUSR,
-	    ha->dfs_dir, vha, &dfs_tgt_counters_ops);
+	    ha->dfs_dir, vha, &qla_dfs_tgt_counters_fops);
 
 	ha->tgt.dfs_tgt_port_database = debugfs_create_file("tgt_port_database",
-	    S_IRUSR,  ha->dfs_dir, vha, &dfs_tgt_port_database_ops);
+	    S_IRUSR,  ha->dfs_dir, vha, &qla2x00_dfs_tgt_port_database_fops);
 
 	ha->dfs_fce = debugfs_create_file("fce", S_IRUSR, ha->dfs_dir, vha,
 	    &dfs_fce_ops);
 
 	ha->tgt.dfs_tgt_sess = debugfs_create_file("tgt_sess",
-		S_IRUSR, ha->dfs_dir, vha, &dfs_tgt_sess_ops);
+		S_IRUSR, ha->dfs_dir, vha, &qla2x00_dfs_tgt_sess_fops);
 
-	if (IS_QLA27XX(ha) || IS_QLA83XX(ha) || IS_QLA28XX(ha))
+	if (IS_QLA27XX(ha) || IS_QLA83XX(ha) || IS_QLA28XX(ha)) {
 		ha->tgt.dfs_naqp = debugfs_create_file("naqp",
 		    0400, ha->dfs_dir, vha, &dfs_naqp_ops);
+		if (!ha->tgt.dfs_naqp) {
+			ql_log(ql_log_warn, vha, 0xd011,
+			       "Unable to create debugFS naqp node.\n");
+			goto out;
+		}
+	}
+	vha->dfs_rport_root = debugfs_create_dir("rports", ha->dfs_dir);
+	if (!vha->dfs_rport_root) {
+		ql_log(ql_log_warn, vha, 0xd012,
+		       "Unable to create debugFS rports node.\n");
+		goto out;
+	}
 out:
 	return 0;
 }
@@ -515,6 +747,11 @@
 		ha->dfs_fce = NULL;
 	}
 
+	if (vha->dfs_rport_root) {
+		debugfs_remove_recursive(vha->dfs_rport_root);
+		vha->dfs_rport_root = NULL;
+	}
+
 	if (ha->dfs_dir) {
 		debugfs_remove(ha->dfs_dir);
 		ha->dfs_dir = NULL;
diff --git a/scst/qla2x00t-32gbit/qla_edif.c b/scst/qla2x00t-32gbit/qla_edif.c
new file mode 100644
index 0000000..3545184
--- /dev/null
+++ b/scst/qla2x00t-32gbit/qla_edif.c
@@ -0,0 +1,3685 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Marvell Fibre Channel HBA Driver
+ * Copyright (c)  2021     Marvell
+ */
+#include "qla_def.h"
+#include "qla_edif.h"
+
+#include <linux/kthread.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <scsi/scsi_tcq.h>
+
+static struct edif_sa_index_entry *qla_edif_sadb_find_sa_index_entry(uint16_t nport_handle,
+		struct list_head *sa_list);
+static uint16_t qla_edif_sadb_get_sa_index(fc_port_t *fcport,
+		struct qla_sa_update_frame *sa_frame);
+static int qla_edif_sadb_delete_sa_index(fc_port_t *fcport, uint16_t nport_handle,
+		uint16_t sa_index);
+static int qla_pur_get_pending(scsi_qla_host_t *, fc_port_t *, BSG_JOB_TYPE *);
+
+struct edb_node {
+	struct  list_head	list;
+	uint32_t		ntype;
+	union {
+		port_id_t	plogi_did;
+		uint32_t	async;
+		port_id_t	els_sid;
+		struct edif_sa_update_aen	sa_aen;
+	} u;
+};
+
+static struct els_sub_cmd {
+	uint16_t cmd;
+	const char *str;
+} sc_str[] = {
+	{SEND_ELS, "send ELS"},
+	{SEND_ELS_REPLY, "send ELS Reply"},
+	{PULL_ELS, "retrieve ELS"},
+};
+
+const char *sc_to_str(uint16_t cmd)
+{
+	int i;
+	struct els_sub_cmd *e;
+
+	for (i = 0; i < ARRAY_SIZE(sc_str); i++) {
+		e = sc_str + i;
+		if (cmd == e->cmd)
+			return e->str;
+	}
+	return "unknown";
+}
+
+static struct edb_node *qla_edb_getnext(scsi_qla_host_t *vha)
+{
+	unsigned long   flags;
+	struct edb_node *edbnode = NULL;
+
+	spin_lock_irqsave(&vha->e_dbell.db_lock, flags);
+
+	/* db nodes are fifo - no qualifications done */
+	if (!list_empty(&vha->e_dbell.head)) {
+		edbnode = list_first_entry(&vha->e_dbell.head,
+					   struct edb_node, list);
+		list_del_init(&edbnode->list);
+	}
+
+	spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags);
+
+	return edbnode;
+}
+
+static void qla_edb_node_free(scsi_qla_host_t *vha, struct edb_node *node)
+{
+	list_del_init(&node->list);
+	kfree(node);
+}
+
+static struct edif_list_entry *qla_edif_list_find_sa_index(fc_port_t *fcport,
+		uint16_t handle)
+{
+	struct edif_list_entry *entry;
+	struct edif_list_entry *tentry;
+	struct list_head *indx_list = &fcport->edif.edif_indx_list;
+
+	list_for_each_entry_safe(entry, tentry, indx_list, next) {
+		if (entry->handle == handle)
+			return entry;
+	}
+	return NULL;
+}
+
+/* timeout called when no traffic and delayed rx sa_index delete */
+static void qla2x00_sa_replace_iocb_timeout(struct timer_list *t)
+{
+	struct edif_list_entry *edif_entry = from_timer(edif_entry, t, timer);
+	fc_port_t *fcport = edif_entry->fcport;
+	struct scsi_qla_host *vha = fcport->vha;
+	struct  edif_sa_ctl *sa_ctl;
+	uint16_t nport_handle;
+	unsigned long flags = 0;
+
+	ql_dbg(ql_dbg_edif, vha, 0x3069,
+	    "%s:  nport_handle 0x%x,  SA REPL Delay Timeout, %8phC portid=%06x\n",
+	    __func__, edif_entry->handle, fcport->port_name, fcport->d_id.b24);
+
+	/*
+	 * if delete_sa_index is valid then no one has serviced this
+	 * delayed delete
+	 */
+	spin_lock_irqsave(&fcport->edif.indx_list_lock, flags);
+
+	/*
+	 * delete_sa_index is invalidated when we find the new sa_index in
+	 * the incoming data stream.  If it is not invalidated then we are
+	 * still looking for the new sa_index because there is no I/O and we
+	 * need to just force the rx delete and move on.  Otherwise
+	 * we could get another rekey which will result in an error 66.
+	 */
+	if (edif_entry->delete_sa_index != INVALID_EDIF_SA_INDEX) {
+		uint16_t delete_sa_index = edif_entry->delete_sa_index;
+
+		edif_entry->delete_sa_index = INVALID_EDIF_SA_INDEX;
+		nport_handle = edif_entry->handle;
+		spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags);
+
+		sa_ctl = qla_edif_find_sa_ctl_by_index(fcport,
+		    delete_sa_index, 0);
+
+		if (sa_ctl) {
+			ql_dbg(ql_dbg_edif, vha, 0x3063,
+			    "%s: sa_ctl: %p, delete index %d, update index: %d, lid: 0x%x\n",
+			    __func__, sa_ctl, delete_sa_index, edif_entry->update_sa_index,
+			    nport_handle);
+
+			sa_ctl->flags = EDIF_SA_CTL_FLG_DEL;
+			set_bit(EDIF_SA_CTL_REPL, &sa_ctl->state);
+			qla_post_sa_replace_work(fcport->vha, fcport,
+			    nport_handle, sa_ctl);
+
+		} else {
+			ql_dbg(ql_dbg_edif, vha, 0x3063,
+			    "%s: sa_ctl not found for delete_sa_index: %d\n",
+			    __func__, edif_entry->delete_sa_index);
+		}
+	} else {
+		spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags);
+	}
+}
+
+/*
+ * create a new list entry for this nport handle and
+ * add an sa_update index to the list - called for sa_update
+ */
+static int qla_edif_list_add_sa_update_index(fc_port_t *fcport,
+		uint16_t sa_index, uint16_t handle)
+{
+	struct edif_list_entry *entry;
+	unsigned long flags = 0;
+
+	/* if the entry exists, then just update the sa_index */
+	entry = qla_edif_list_find_sa_index(fcport, handle);
+	if (entry) {
+		entry->update_sa_index = sa_index;
+		entry->count = 0;
+		return 0;
+	}
+
+	/*
+	 * This is the normal path - there should be no existing entry
+	 * when update is called.  The exception is at startup
+	 * when update is called for the first two sa_indexes
+	 * followed by a delete of the first sa_index
+	 */
+	entry = kzalloc((sizeof(struct edif_list_entry)), GFP_ATOMIC);
+	if (!entry)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&entry->next);
+	entry->handle = handle;
+	entry->update_sa_index = sa_index;
+	entry->delete_sa_index = INVALID_EDIF_SA_INDEX;
+	entry->count = 0;
+	entry->flags = 0;
+	timer_setup(&entry->timer, qla2x00_sa_replace_iocb_timeout, 0);
+	spin_lock_irqsave(&fcport->edif.indx_list_lock, flags);
+	list_add_tail(&entry->next, &fcport->edif.edif_indx_list);
+	spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags);
+	return 0;
+}
+
+/* remove an entry from the list */
+static void qla_edif_list_delete_sa_index(fc_port_t *fcport, struct edif_list_entry *entry)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&fcport->edif.indx_list_lock, flags);
+	list_del(&entry->next);
+	spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags);
+}
+
+int qla_post_sa_replace_work(struct scsi_qla_host *vha,
+	 fc_port_t *fcport, uint16_t nport_handle, struct edif_sa_ctl *sa_ctl)
+{
+	struct qla_work_evt *e;
+
+	e = qla2x00_alloc_work(vha, QLA_EVT_SA_REPLACE);
+	if (!e)
+		return QLA_FUNCTION_FAILED;
+
+	e->u.sa_update.fcport = fcport;
+	e->u.sa_update.sa_ctl = sa_ctl;
+	e->u.sa_update.nport_handle = nport_handle;
+	fcport->flags |= FCF_ASYNC_ACTIVE;
+	return qla2x00_post_work(vha, e);
+}
+
+static void
+qla_edif_sa_ctl_init(scsi_qla_host_t *vha, struct fc_port  *fcport)
+{
+	ql_dbg(ql_dbg_edif, vha, 0x2058,
+	    "Init SA_CTL List for fcport - nn %8phN pn %8phN portid=%06x.\n",
+	    fcport->node_name, fcport->port_name, fcport->d_id.b24);
+
+	fcport->edif.tx_rekey_cnt = 0;
+	fcport->edif.rx_rekey_cnt = 0;
+
+	fcport->edif.tx_bytes = 0;
+	fcport->edif.rx_bytes = 0;
+}
+
+static int qla_bsg_check(scsi_qla_host_t *vha, BSG_JOB_TYPE *bsg_job,
+fc_port_t *fcport)
+{
+	struct extra_auth_els *p;
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	struct qla_bsg_auth_els_request *req =
+	    (struct qla_bsg_auth_els_request *)bsg_job->request;
+
+	if (!vha->hw->flags.edif_enabled) {
+		ql_dbg(ql_dbg_edif, vha, 0x9105,
+		    "%s edif not enabled\n", __func__);
+		goto done;
+	}
+	if (DBELL_INACTIVE(vha)) {
+		ql_dbg(ql_dbg_edif, vha, 0x09102,
+		    "%s doorbell not enabled\n", __func__);
+		goto done;
+	}
+
+	p = &req->e;
+
+	/* Get response */
+	if (p->sub_cmd == PULL_ELS) {
+		struct qla_bsg_auth_els_reply *rpl =
+			(struct qla_bsg_auth_els_reply *)bsg_job->reply;
+
+		qla_pur_get_pending(vha, fcport, bsg_job);
+
+		ql_dbg(ql_dbg_edif, vha, 0x911d,
+			"%s %s %8phN sid=%x. xchg %x, nb=%xh bsg ptr %p\n",
+			__func__, sc_to_str(p->sub_cmd), fcport->port_name,
+			fcport->d_id.b24, rpl->rx_xchg_address,
+			rpl->r.reply_payload_rcv_len, bsg_job);
+
+		goto done;
+	}
+	return 0;
+
+done:
+
+	bsg_job_done(bsg_job, bsg_reply->result,
+			bsg_reply->reply_payload_rcv_len);
+	return -EIO;
+}
+
+fc_port_t *
+qla2x00_find_fcport_by_pid(scsi_qla_host_t *vha, port_id_t *id)
+{
+	fc_port_t *f, *tf;
+
+	f = NULL;
+	list_for_each_entry_safe(f, tf, &vha->vp_fcports, list) {
+		if (f->d_id.b24 == id->b24)
+			return f;
+	}
+	return NULL;
+}
+
+/**
+ * qla_edif_app_check(): check for valid application id.
+ * @vha: host adapter pointer
+ * @appid: application id
+ * Return: false = fail, true = pass
+ */
+static bool
+qla_edif_app_check(scsi_qla_host_t *vha, struct app_id appid)
+{
+	/* check that the app is allow/known to the driver */
+
+	if (appid.app_vid != EDIF_APP_ID) {
+		ql_dbg(ql_dbg_edif, vha, 0x911d, "%s app id not ok (%x)",
+		    __func__, appid.app_vid);
+		return false;
+	}
+
+	if (appid.version != EDIF_VERSION1) {
+		ql_dbg(ql_dbg_edif, vha, 0x911d, "%s app version is not ok (%x)",
+		    __func__, appid.version);
+		return false;
+	}
+
+	return true;
+}
+
+static void
+qla_edif_free_sa_ctl(fc_port_t *fcport, struct edif_sa_ctl *sa_ctl,
+	int index)
+{
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&fcport->edif.sa_list_lock, flags);
+	list_del(&sa_ctl->next);
+	spin_unlock_irqrestore(&fcport->edif.sa_list_lock, flags);
+	if (index >= 512)
+		fcport->edif.tx_rekey_cnt--;
+	else
+		fcport->edif.rx_rekey_cnt--;
+	kfree(sa_ctl);
+}
+
+/* return an index to the freepool */
+static void qla_edif_add_sa_index_to_freepool(fc_port_t *fcport, int dir,
+		uint16_t sa_index)
+{
+	void *sa_id_map;
+	struct scsi_qla_host *vha = fcport->vha;
+	struct qla_hw_data *ha = vha->hw;
+	unsigned long flags = 0;
+	u16 lsa_index = sa_index;
+
+	ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x3063,
+	    "%s: entry\n", __func__);
+
+	if (dir) {
+		sa_id_map = ha->edif_tx_sa_id_map;
+		lsa_index -= EDIF_TX_SA_INDEX_BASE;
+	} else {
+		sa_id_map = ha->edif_rx_sa_id_map;
+	}
+
+	spin_lock_irqsave(&ha->sadb_fp_lock, flags);
+	clear_bit(lsa_index, sa_id_map);
+	spin_unlock_irqrestore(&ha->sadb_fp_lock, flags);
+	ql_dbg(ql_dbg_edif, vha, 0x3063,
+	    "%s: index %d added to free pool\n", __func__, sa_index);
+}
+
+static void __qla2x00_release_all_sadb(struct scsi_qla_host *vha,
+	struct fc_port *fcport, struct edif_sa_index_entry *entry,
+	int pdir)
+{
+	struct edif_list_entry *edif_entry;
+	struct  edif_sa_ctl *sa_ctl;
+	int i, dir;
+	int key_cnt = 0;
+
+	for (i = 0; i < 2; i++) {
+		if (entry->sa_pair[i].sa_index == INVALID_EDIF_SA_INDEX)
+			continue;
+
+		if (fcport->loop_id != entry->handle) {
+			ql_dbg(ql_dbg_edif, vha, 0x3063,
+			    "%s: ** WARNING %d** entry handle: 0x%x, lid: 0x%x, sa_index: %d\n",
+			    __func__, i, entry->handle, fcport->loop_id,
+			    entry->sa_pair[i].sa_index);
+		}
+
+		/* release the sa_ctl */
+		sa_ctl = qla_edif_find_sa_ctl_by_index(fcport,
+				entry->sa_pair[i].sa_index, pdir);
+		if (sa_ctl &&
+		    qla_edif_find_sa_ctl_by_index(fcport, sa_ctl->index, pdir)) {
+			ql_dbg(ql_dbg_edif, vha, 0x3063,
+			    "%s: freeing sa_ctl for index %d\n", __func__, sa_ctl->index);
+			qla_edif_free_sa_ctl(fcport, sa_ctl, sa_ctl->index);
+		} else {
+			ql_dbg(ql_dbg_edif, vha, 0x3063,
+			    "%s: sa_ctl NOT freed, sa_ctl: %p\n", __func__, sa_ctl);
+		}
+
+		/* Release the index */
+		ql_dbg(ql_dbg_edif, vha, 0x3063,
+			"%s: freeing sa_index %d, nph: 0x%x\n",
+			__func__, entry->sa_pair[i].sa_index, entry->handle);
+
+		dir = (entry->sa_pair[i].sa_index <
+			EDIF_TX_SA_INDEX_BASE) ? 0 : 1;
+		qla_edif_add_sa_index_to_freepool(fcport, dir,
+			entry->sa_pair[i].sa_index);
+
+		/* Delete timer on RX */
+		if (pdir != SAU_FLG_TX) {
+			edif_entry =
+				qla_edif_list_find_sa_index(fcport, entry->handle);
+			if (edif_entry) {
+				ql_dbg(ql_dbg_edif, vha, 0x5033,
+				    "%s: remove edif_entry %p, update_sa_index: 0x%x, delete_sa_index: 0x%x\n",
+				    __func__, edif_entry, edif_entry->update_sa_index,
+				    edif_entry->delete_sa_index);
+				qla_edif_list_delete_sa_index(fcport, edif_entry);
+				/*
+				 * valid delete_sa_index indicates there is a rx
+				 * delayed delete queued
+				 */
+				if (edif_entry->delete_sa_index !=
+						INVALID_EDIF_SA_INDEX) {
+					del_timer(&edif_entry->timer);
+
+					/* build and send the aen */
+					fcport->edif.rx_sa_set = 1;
+					fcport->edif.rx_sa_pending = 0;
+					qla_edb_eventcreate(vha,
+							VND_CMD_AUTH_STATE_SAUPDATE_COMPL,
+							QL_VND_SA_STAT_SUCCESS,
+							QL_VND_RX_SA_KEY, fcport);
+				}
+				ql_dbg(ql_dbg_edif, vha, 0x5033,
+				    "%s: release edif_entry %p, update_sa_index: 0x%x, delete_sa_index: 0x%x\n",
+				    __func__, edif_entry, edif_entry->update_sa_index,
+				    edif_entry->delete_sa_index);
+
+				kfree(edif_entry);
+			}
+		}
+		key_cnt++;
+	}
+	ql_dbg(ql_dbg_edif, vha, 0x3063,
+	    "%s: %d %s keys released\n",
+	    __func__, key_cnt, pdir ? "tx" : "rx");
+}
+
+/* find an release all outstanding sadb sa_indicies */
+void qla2x00_release_all_sadb(struct scsi_qla_host *vha, struct fc_port *fcport)
+{
+	struct edif_sa_index_entry *entry, *tmp;
+	struct qla_hw_data *ha = vha->hw;
+	unsigned long flags;
+
+	ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x3063,
+	    "%s: Starting...\n", __func__);
+
+	spin_lock_irqsave(&ha->sadb_lock, flags);
+
+	list_for_each_entry_safe(entry, tmp, &ha->sadb_rx_index_list, next) {
+		if (entry->fcport == fcport) {
+			list_del(&entry->next);
+			spin_unlock_irqrestore(&ha->sadb_lock, flags);
+			__qla2x00_release_all_sadb(vha, fcport, entry, 0);
+			kfree(entry);
+			spin_lock_irqsave(&ha->sadb_lock, flags);
+			break;
+		}
+	}
+
+	list_for_each_entry_safe(entry, tmp, &ha->sadb_tx_index_list, next) {
+		if (entry->fcport == fcport) {
+			list_del(&entry->next);
+			spin_unlock_irqrestore(&ha->sadb_lock, flags);
+
+			__qla2x00_release_all_sadb(vha, fcport, entry, SAU_FLG_TX);
+
+			kfree(entry);
+			spin_lock_irqsave(&ha->sadb_lock, flags);
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&ha->sadb_lock, flags);
+}
+
+/**
+ * qla_edif_app_start:  application has announce its present
+ * @vha: host adapter pointer
+ * @bsg_job: user request
+ *
+ * Set/activate doorbell.  Reset current sessions and re-login with
+ * secure flag.
+ */
+static int
+qla_edif_app_start(scsi_qla_host_t *vha, BSG_JOB_TYPE *bsg_job)
+{
+	int32_t			rval = 0;
+	struct fc_bsg_reply	*bsg_reply = bsg_job->reply;
+	struct app_start	appstart;
+	struct app_start_reply	appreply;
+	struct fc_port  *fcport, *tf;
+
+	ql_log(ql_log_info, vha, 0x1313,
+	       "EDIF application registration with driver, FC device connections will be re-established.\n");
+
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+	    bsg_job->request_payload.sg_cnt, &appstart,
+	    sizeof(struct app_start));
+
+	ql_dbg(ql_dbg_edif, vha, 0x911d, "%s app_vid=%x app_start_flags %x\n",
+	     __func__, appstart.app_info.app_vid, appstart.app_start_flags);
+
+	if (DBELL_INACTIVE(vha)) {
+		/* mark doorbell as active since an app is now present */
+		vha->e_dbell.db_flags |= EDB_ACTIVE;
+	} else {
+		goto out;
+	}
+
+	if (N2N_TOPO(vha->hw)) {
+		list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list)
+			fcport->n2n_link_reset_cnt = 0;
+
+		if (vha->hw->flags.n2n_fw_acc_sec) {
+			list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list)
+				qla_edif_sa_ctl_init(vha, fcport);
+
+			/*
+			 * While authentication app was not running, remote device
+			 * could still try to login with this local port.  Let's
+			 * clear the state and try again.
+			 */
+			qla2x00_wait_for_sess_deletion(vha);
+
+			/* bounce the link to get the other guy to relogin */
+			if (!vha->hw->flags.n2n_bigger) {
+				set_bit(N2N_LINK_RESET, &vha->dpc_flags);
+				qla2xxx_wake_dpc(vha);
+			}
+		} else {
+			qla2x00_wait_for_hba_online(vha);
+			set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+			qla2xxx_wake_dpc(vha);
+			qla2x00_wait_for_hba_online(vha);
+		}
+	} else {
+		list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) {
+			ql_dbg(ql_dbg_edif, vha, 0x2058,
+			       "FCSP - nn %8phN pn %8phN portid=%06x.\n",
+			       fcport->node_name, fcport->port_name,
+			       fcport->d_id.b24);
+			ql_dbg(ql_dbg_edif, vha, 0xf084,
+			       "%s: se_sess %p / sess %p from port %8phC "
+			       "loop_id %#04x s_id %06x logout %d "
+			       "keep %d els_logo %d disc state %d auth state %d"
+			       "stop state %d\n",
+			       __func__, fcport->se_sess, fcport,
+			       fcport->port_name, fcport->loop_id,
+			       fcport->d_id.b24, fcport->logout_on_delete,
+			       fcport->keep_nport_handle, fcport->send_els_logo,
+			       fcport->disc_state, fcport->edif.auth_state,
+			       fcport->edif.app_stop);
+
+			if (atomic_read(&vha->loop_state) == LOOP_DOWN)
+				break;
+
+			fcport->login_retry = vha->hw->login_retry_count;
+
+			fcport->edif.app_stop = 0;
+			fcport->edif.app_sess_online = 0;
+
+			if (fcport->scan_state != QLA_FCPORT_FOUND)
+				continue;
+
+			if (fcport->port_type == FCT_UNKNOWN &&
+			    !fcport->fc4_features)
+				rval = qla24xx_async_gffid(vha, fcport, true);
+
+			if (!rval && !(fcport->fc4_features & FC4_FF_TARGET ||
+			    fcport->port_type & (FCT_TARGET|FCT_NVME_TARGET)))
+				continue;
+
+			rval = 0;
+
+			ql_dbg(ql_dbg_edif, vha, 0x911e,
+			       "%s wwpn %8phC calling qla_edif_reset_auth_wait\n",
+			       __func__, fcport->port_name);
+			qlt_schedule_sess_for_deletion(fcport);
+			qla_edif_sa_ctl_init(vha, fcport);
+		}
+		set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
+	}
+
+	if (vha->pur_cinfo.enode_flags != ENODE_ACTIVE) {
+		/* mark as active since an app is now present */
+		vha->pur_cinfo.enode_flags = ENODE_ACTIVE;
+	} else {
+		ql_dbg(ql_dbg_edif, vha, 0x911f, "%s enode already active\n",
+		     __func__);
+	}
+
+out:
+	appreply.host_support_edif = vha->hw->flags.edif_enabled;
+	appreply.edif_enode_active = vha->pur_cinfo.enode_flags;
+	appreply.edif_edb_active = vha->e_dbell.db_flags;
+	appreply.version = EDIF_VERSION1;
+
+	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+
+	SET_DID_STATUS(bsg_reply->result, DID_OK);
+
+	bsg_reply->reply_payload_rcv_len = sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+							       bsg_job->reply_payload.sg_cnt,
+							       &appreply,
+							       sizeof(struct app_start_reply));
+
+	ql_dbg(ql_dbg_edif, vha, 0x911d,
+	    "%s app start completed with 0x%x\n",
+	    __func__, rval);
+
+	return rval;
+}
+
+/**
+ * qla_edif_app_stop - app has announced it's exiting.
+ * @vha: host adapter pointer
+ * @bsg_job: user space command pointer
+ *
+ * Free any in flight messages, clear all doorbell events
+ * to application. Reject any message relate to security.
+ */
+static int
+qla_edif_app_stop(scsi_qla_host_t *vha, BSG_JOB_TYPE *bsg_job)
+{
+	struct app_stop         appstop;
+	struct fc_bsg_reply     *bsg_reply = bsg_job->reply;
+	struct fc_port  *fcport, *tf;
+
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+	    bsg_job->request_payload.sg_cnt, &appstop,
+	    sizeof(struct app_stop));
+
+	ql_dbg(ql_dbg_edif, vha, 0x911d, "%s Stopping APP: app_vid=%x\n",
+	    __func__, appstop.app_info.app_vid);
+
+	/* Call db stop and enode stop functions */
+
+	/* if we leave this running short waits are operational < 16 secs */
+	qla_enode_stop(vha);        /* stop enode */
+	qla_edb_stop(vha);          /* stop db */
+
+	list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) {
+		if (!(fcport->flags & FCF_FCSP_DEVICE))
+			continue;
+
+		if (fcport->flags & FCF_FCSP_DEVICE) {
+			ql_dbg(ql_dbg_edif, vha, 0xf084,
+			    "%s: sess %p from port %8phC lid %#04x s_id %06x logout %d keep %d els_logo %d\n",
+			    __func__, fcport,
+			    fcport->port_name, fcport->loop_id, fcport->d_id.b24,
+			    fcport->logout_on_delete, fcport->keep_nport_handle,
+			    fcport->send_els_logo);
+
+			if (atomic_read(&vha->loop_state) == LOOP_DOWN)
+				break;
+
+			fcport->edif.app_stop = 1;
+			ql_dbg(ql_dbg_edif, vha, 0x911e,
+				"%s wwpn %8phC calling qla_edif_reset_auth_wait\n",
+				__func__, fcport->port_name);
+
+			fcport->send_els_logo = 1;
+			qlt_schedule_sess_for_deletion(fcport);
+		}
+	}
+
+	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+	SET_DID_STATUS(bsg_reply->result, DID_OK);
+
+	/* no return interface to app - it assumes we cleaned up ok */
+
+	return 0;
+}
+
+static int
+qla_edif_app_chk_sa_update(scsi_qla_host_t *vha, fc_port_t *fcport,
+		struct app_plogi_reply *appplogireply)
+{
+	int	ret = 0;
+
+	if (!(fcport->edif.rx_sa_set && fcport->edif.tx_sa_set)) {
+		ql_dbg(ql_dbg_edif, vha, 0x911e,
+		    "%s: wwpn %8phC Both SA indexes has not been SET TX %d, RX %d.\n",
+		    __func__, fcport->port_name, fcport->edif.tx_sa_set,
+		    fcport->edif.rx_sa_set);
+		appplogireply->prli_status = 0;
+		ret = 1;
+	} else  {
+		ql_dbg(ql_dbg_edif, vha, 0x911e,
+		    "%s wwpn %8phC Both SA(s) updated.\n", __func__,
+		    fcport->port_name);
+		fcport->edif.rx_sa_set = fcport->edif.tx_sa_set = 0;
+		fcport->edif.rx_sa_pending = fcport->edif.tx_sa_pending = 0;
+		appplogireply->prli_status = 1;
+	}
+	return ret;
+}
+
+/**
+ * qla_edif_app_authok - authentication by app succeeded.  Driver can proceed
+ *   with prli
+ * @vha: host adapter pointer
+ * @bsg_job: user request
+ */
+static int
+qla_edif_app_authok(scsi_qla_host_t *vha, BSG_JOB_TYPE *bsg_job)
+{
+	struct auth_complete_cmd appplogiok;
+	struct app_plogi_reply	appplogireply = {0};
+	struct fc_bsg_reply	*bsg_reply = bsg_job->reply;
+	fc_port_t		*fcport = NULL;
+	port_id_t		portid = {0};
+
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+	    bsg_job->request_payload.sg_cnt, &appplogiok,
+	    sizeof(struct auth_complete_cmd));
+
+	/* silent unaligned access warning */
+	portid.b.domain = appplogiok.u.d_id.b.domain;
+	portid.b.area   = appplogiok.u.d_id.b.area;
+	portid.b.al_pa  = appplogiok.u.d_id.b.al_pa;
+
+	appplogireply.version = EDIF_VERSION1;
+	switch (appplogiok.type) {
+	case PL_TYPE_WWPN:
+		fcport = qla2x00_find_fcport_by_wwpn(vha,
+		    appplogiok.u.wwpn, 0);
+		if (!fcport)
+			ql_dbg(ql_dbg_edif, vha, 0x911d,
+			    "%s wwpn lookup failed: %8phC\n",
+			    __func__, appplogiok.u.wwpn);
+		break;
+	case PL_TYPE_DID:
+		fcport = qla2x00_find_fcport_by_pid(vha, &portid);
+		if (!fcport)
+			ql_dbg(ql_dbg_edif, vha, 0x911d,
+			    "%s d_id lookup failed: %x\n", __func__,
+			    portid.b24);
+		break;
+	default:
+		ql_dbg(ql_dbg_edif, vha, 0x911d,
+		    "%s undefined type: %x\n", __func__,
+		    appplogiok.type);
+		break;
+	}
+
+	if (!fcport) {
+		SET_DID_STATUS(bsg_reply->result, DID_ERROR);
+		goto errstate_exit;
+	}
+
+	/*
+	 * if port is online then this is a REKEY operation
+	 * Only do sa update checking
+	 */
+	if (atomic_read(&fcport->state) == FCS_ONLINE) {
+		ql_dbg(ql_dbg_edif, vha, 0x911d,
+		    "%s Skipping PRLI complete based on rekey\n", __func__);
+		appplogireply.prli_status = 1;
+		SET_DID_STATUS(bsg_reply->result, DID_OK);
+		qla_edif_app_chk_sa_update(vha, fcport, &appplogireply);
+		goto errstate_exit;
+	}
+
+	/* make sure in AUTH_PENDING or else reject */
+	if (fcport->disc_state != DSC_LOGIN_AUTH_PEND) {
+		ql_dbg(ql_dbg_edif, vha, 0x911e,
+		    "%s wwpn %8phC is not in auth pending state (%x)\n",
+		    __func__, fcport->port_name, fcport->disc_state);
+		SET_DID_STATUS(bsg_reply->result, DID_OK);
+		appplogireply.prli_status = 0;
+		goto errstate_exit;
+	}
+
+	SET_DID_STATUS(bsg_reply->result, DID_OK);
+	appplogireply.prli_status = 1;
+	fcport->edif.authok = 1;
+	if (!(fcport->edif.rx_sa_set && fcport->edif.tx_sa_set)) {
+		ql_dbg(ql_dbg_edif, vha, 0x911e,
+		    "%s: wwpn %8phC Both SA indexes has not been SET TX %d, RX %d.\n",
+		    __func__, fcport->port_name, fcport->edif.tx_sa_set,
+		    fcport->edif.rx_sa_set);
+		SET_DID_STATUS(bsg_reply->result, DID_OK);
+		appplogireply.prli_status = 0;
+		goto errstate_exit;
+
+	} else {
+		ql_dbg(ql_dbg_edif, vha, 0x911e,
+		    "%s wwpn %8phC Both SA(s) updated.\n", __func__,
+		    fcport->port_name);
+		fcport->edif.rx_sa_set = fcport->edif.tx_sa_set = 0;
+		fcport->edif.rx_sa_pending = fcport->edif.tx_sa_pending = 0;
+	}
+
+	if (qla_ini_mode_enabled(vha)) {
+		ql_dbg(ql_dbg_edif, vha, 0x911e,
+		    "%s AUTH complete - RESUME with prli for wwpn %8phC\n",
+		    __func__, fcport->port_name);
+		qla24xx_post_prli_work(vha, fcport);
+	}
+
+errstate_exit:
+	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+	bsg_reply->reply_payload_rcv_len = sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+							       bsg_job->reply_payload.sg_cnt,
+							       &appplogireply,
+							       sizeof(struct app_plogi_reply));
+
+	return 0;
+}
+
+/**
+ * qla_edif_app_authfail - authentication by app has failed.  Driver is given
+ *   notice to tear down current session.
+ * @vha: host adapter pointer
+ * @bsg_job: user request
+ */
+static int
+qla_edif_app_authfail(scsi_qla_host_t *vha, BSG_JOB_TYPE *bsg_job)
+{
+	int32_t			rval = 0;
+	struct auth_complete_cmd appplogifail;
+	struct fc_bsg_reply	*bsg_reply = bsg_job->reply;
+	fc_port_t		*fcport = NULL;
+	port_id_t		portid = {0};
+
+	ql_dbg(ql_dbg_edif, vha, 0x911d, "%s app auth fail\n", __func__);
+
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+	    bsg_job->request_payload.sg_cnt, &appplogifail,
+	    sizeof(struct auth_complete_cmd));
+
+	/* silent unaligned access warning */
+	portid.b.domain = appplogifail.u.d_id.b.domain;
+	portid.b.area   = appplogifail.u.d_id.b.area;
+	portid.b.al_pa  = appplogifail.u.d_id.b.al_pa;
+
+	/*
+	 * TODO: edif: app has failed this plogi. Inform driver to
+	 * take any action (if any).
+	 */
+	switch (appplogifail.type) {
+	case PL_TYPE_WWPN:
+		fcport = qla2x00_find_fcport_by_wwpn(vha,
+		    appplogifail.u.wwpn, 0);
+		SET_DID_STATUS(bsg_reply->result, DID_OK);
+		break;
+	case PL_TYPE_DID:
+		fcport = qla2x00_find_fcport_by_pid(vha, &portid);
+		if (!fcport)
+			ql_dbg(ql_dbg_edif, vha, 0x911d,
+			    "%s d_id lookup failed: %x\n", __func__,
+			    portid.b24);
+		SET_DID_STATUS(bsg_reply->result, DID_OK);
+		break;
+	default:
+		ql_dbg(ql_dbg_edif, vha, 0x911e,
+		    "%s undefined type: %x\n", __func__,
+		    appplogifail.type);
+		bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+		SET_DID_STATUS(bsg_reply->result, DID_ERROR);
+		rval = -1;
+		break;
+	}
+
+	ql_dbg(ql_dbg_edif, vha, 0x911d,
+	    "%s fcport is 0x%p\n", __func__, fcport);
+
+	if (fcport) {
+		/* set/reset edif values and flags */
+		ql_dbg(ql_dbg_edif, vha, 0x911e,
+		    "%s reset the auth process - %8phC, loopid=%x portid=%06x.\n",
+		    __func__, fcport->port_name, fcport->loop_id, fcport->d_id.b24);
+
+		if (qla_ini_mode_enabled(fcport->vha)) {
+			fcport->send_els_logo = 1;
+			qlt_schedule_sess_for_deletion(fcport);
+		}
+	}
+
+	return rval;
+}
+
+/**
+ * qla_edif_app_getfcinfo - app would like to read session info (wwpn, nportid,
+ *   [initiator|target] mode.  It can specific session with specific nport id or
+ *   all sessions.
+ * @vha: host adapter pointer
+ * @bsg_job: user request pointer
+ */
+static int
+qla_edif_app_getfcinfo(scsi_qla_host_t *vha, BSG_JOB_TYPE *bsg_job)
+{
+	int32_t			rval = 0;
+	int32_t			pcnt = 0;
+	struct fc_bsg_reply	*bsg_reply = bsg_job->reply;
+	struct app_pinfo_req	app_req;
+	struct app_pinfo_reply	*app_reply;
+	port_id_t		tdid;
+
+	ql_dbg(ql_dbg_edif, vha, 0x911d, "%s app get fcinfo\n", __func__);
+
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+	    bsg_job->request_payload.sg_cnt, &app_req,
+	    sizeof(struct app_pinfo_req));
+
+	app_reply = kzalloc((sizeof(struct app_pinfo_reply) +
+	    sizeof(struct app_pinfo) * app_req.num_ports), GFP_KERNEL);
+
+	if (!app_reply) {
+		SET_DID_STATUS(bsg_reply->result, DID_ERROR);
+		rval = -1;
+	} else {
+		struct fc_port	*fcport = NULL, *tf;
+
+		app_reply->version = EDIF_VERSION1;
+
+		list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) {
+			if (!(fcport->flags & FCF_FCSP_DEVICE))
+				continue;
+
+			tdid = app_req.remote_pid;
+
+			ql_dbg(ql_dbg_edif, vha, 0x2058,
+			    "APP request entry - portid=%06x.\n", tdid.b24);
+
+			/* Ran out of space */
+			if (pcnt >= app_req.num_ports)
+				break;
+
+			if (tdid.b24 != 0 && tdid.b24 != fcport->d_id.b24)
+				continue;
+
+			if (!N2N_TOPO(vha->hw)) {
+				if (fcport->scan_state != QLA_FCPORT_FOUND)
+					continue;
+
+				if (fcport->port_type == FCT_UNKNOWN &&
+				    !fcport->fc4_features)
+					rval = qla24xx_async_gffid(vha, fcport,
+								   true);
+
+				if (!rval &&
+				    !(fcport->fc4_features & FC4_FF_TARGET ||
+				      fcport->port_type &
+				      (FCT_TARGET | FCT_NVME_TARGET)))
+					continue;
+			}
+
+			rval = 0;
+
+			app_reply->ports[pcnt].version = EDIF_VERSION1;
+			app_reply->ports[pcnt].remote_type =
+				VND_CMD_RTYPE_UNKNOWN;
+			if (fcport->port_type & (FCT_NVME_TARGET | FCT_TARGET))
+				app_reply->ports[pcnt].remote_type |=
+					VND_CMD_RTYPE_TARGET;
+			if (fcport->port_type & (FCT_NVME_INITIATOR | FCT_INITIATOR))
+				app_reply->ports[pcnt].remote_type |=
+					VND_CMD_RTYPE_INITIATOR;
+
+			app_reply->ports[pcnt].remote_pid = fcport->d_id;
+
+			ql_dbg(ql_dbg_edif, vha, 0x2058,
+			    "Found FC_SP fcport - nn %8phN pn %8phN pcnt %d portid=%06x secure %d.\n",
+			    fcport->node_name, fcport->port_name, pcnt,
+			    fcport->d_id.b24, fcport->flags & FCF_FCSP_DEVICE);
+
+			switch (fcport->edif.auth_state) {
+			case VND_CMD_AUTH_STATE_ELS_RCVD:
+				if (fcport->disc_state == DSC_LOGIN_AUTH_PEND) {
+					fcport->edif.auth_state = VND_CMD_AUTH_STATE_NEEDED;
+					app_reply->ports[pcnt].auth_state =
+						VND_CMD_AUTH_STATE_NEEDED;
+				} else {
+					app_reply->ports[pcnt].auth_state =
+						VND_CMD_AUTH_STATE_ELS_RCVD;
+				}
+				break;
+			default:
+				app_reply->ports[pcnt].auth_state = fcport->edif.auth_state;
+				break;
+			}
+
+			memcpy(app_reply->ports[pcnt].remote_wwpn,
+			    fcport->port_name, 8);
+
+			app_reply->ports[pcnt].remote_state =
+				(atomic_read(&fcport->state) ==
+				    FCS_ONLINE ? 1 : 0);
+
+			pcnt++;
+
+			if (tdid.b24 != 0)
+				break;
+		}
+		app_reply->port_count = pcnt;
+		SET_DID_STATUS(bsg_reply->result, DID_OK);
+	}
+
+
+	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+	bsg_reply->reply_payload_rcv_len = sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+							       bsg_job->reply_payload.sg_cnt,
+							       app_reply,
+							       sizeof(struct app_pinfo_reply) + sizeof(struct app_pinfo) * pcnt);
+
+	kfree(app_reply);
+
+	return rval;
+}
+
+/**
+ * qla_edif_app_getstats - app would like to read various statistics info
+ * @vha: host adapter pointer
+ * @bsg_job: user request
+ */
+static int32_t
+qla_edif_app_getstats(scsi_qla_host_t *vha, BSG_JOB_TYPE *bsg_job)
+{
+	int32_t			rval = 0;
+	struct fc_bsg_reply	*bsg_reply = bsg_job->reply;
+	uint32_t size;
+
+	struct app_sinfo_req	app_req;
+	struct app_stats_reply	*app_reply;
+	uint32_t pcnt = 0;
+
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+	    bsg_job->request_payload.sg_cnt, &app_req,
+	    sizeof(struct app_sinfo_req));
+	if (app_req.num_ports == 0) {
+		ql_dbg(ql_dbg_async, vha, 0x911d,
+		   "%s app did not indicate number of ports to return\n",
+		    __func__);
+		SET_DID_STATUS(bsg_reply->result, DID_ERROR);
+		rval = -1;
+	}
+
+	size = sizeof(struct app_stats_reply) +
+	    (sizeof(struct app_sinfo) * app_req.num_ports);
+
+	app_reply = kzalloc(size, GFP_KERNEL);
+	if (!app_reply) {
+		SET_DID_STATUS(bsg_reply->result, DID_ERROR);
+		rval = -1;
+	} else {
+		struct fc_port	*fcport = NULL, *tf;
+
+		app_reply->version = EDIF_VERSION1;
+
+		list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) {
+			if (fcport->edif.enable) {
+				if (pcnt > app_req.num_ports)
+					break;
+
+				app_reply->elem[pcnt].rekey_count =
+				    fcport->edif.rekey_cnt;
+				app_reply->elem[pcnt].tx_bytes =
+				    fcport->edif.tx_bytes;
+				app_reply->elem[pcnt].rx_bytes =
+				    fcport->edif.rx_bytes;
+
+				memcpy(app_reply->elem[pcnt].remote_wwpn,
+				    fcport->port_name, 8);
+
+				pcnt++;
+			}
+		}
+		app_reply->elem_count = pcnt;
+		SET_DID_STATUS(bsg_reply->result, DID_OK);
+	}
+
+	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+	bsg_reply->reply_payload_rcv_len =
+	    sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+	       bsg_job->reply_payload.sg_cnt, app_reply,
+	       sizeof(struct app_stats_reply) + (sizeof(struct app_sinfo) * pcnt));
+
+	kfree(app_reply);
+
+	return rval;
+}
+
+static int32_t
+qla_edif_ack(scsi_qla_host_t *vha, BSG_JOB_TYPE *bsg_job)
+{
+	struct fc_port *fcport;
+	struct aen_complete_cmd ack;
+	struct fc_bsg_reply     *bsg_reply = bsg_job->reply;
+
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+			  bsg_job->request_payload.sg_cnt, &ack, sizeof(ack));
+
+	ql_dbg(ql_dbg_edif, vha, 0x70cf,
+	       "%s: %06x event_code %x\n",
+	       __func__, ack.port_id.b24, ack.event_code);
+
+	fcport = qla2x00_find_fcport_by_pid(vha, &ack.port_id);
+	SET_DID_STATUS(bsg_reply->result, DID_OK);
+
+	if (!fcport) {
+		ql_dbg(ql_dbg_edif, vha, 0x70cf,
+		       "%s: unable to find fcport %06x \n",
+		       __func__, ack.port_id.b24);
+		return 0;
+	}
+
+	switch (ack.event_code) {
+	case VND_CMD_AUTH_STATE_SESSION_SHUTDOWN:
+		fcport->edif.sess_down_acked = 1;
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static int qla_edif_consume_dbell(scsi_qla_host_t *vha, BSG_JOB_TYPE *bsg_job)
+{
+	struct fc_bsg_reply	*bsg_reply = bsg_job->reply;
+	u32 sg_skip, reply_payload_len;
+	bool keep;
+	struct edb_node *dbnode = NULL;
+	struct edif_app_dbell ap;
+	int dat_size = 0;
+
+	sg_skip = 0;
+	reply_payload_len = bsg_job->reply_payload.payload_len;
+
+	while ((reply_payload_len - sg_skip) >= sizeof(struct edb_node)) {
+		dbnode = qla_edb_getnext(vha);
+		if (dbnode) {
+			keep = true;
+			dat_size = 0;
+			ap.event_code = dbnode->ntype;
+			switch (dbnode->ntype) {
+			case VND_CMD_AUTH_STATE_SESSION_SHUTDOWN:
+			case VND_CMD_AUTH_STATE_NEEDED:
+				ap.port_id = dbnode->u.plogi_did;
+				dat_size += sizeof(ap.port_id);
+				break;
+			case VND_CMD_AUTH_STATE_ELS_RCVD:
+				ap.port_id = dbnode->u.els_sid;
+				dat_size += sizeof(ap.port_id);
+				break;
+			case VND_CMD_AUTH_STATE_SAUPDATE_COMPL:
+				ap.port_id = dbnode->u.sa_aen.port_id;
+				memcpy(&ap.event_data, &dbnode->u,
+				    sizeof(struct edif_sa_update_aen));
+				dat_size += sizeof(struct edif_sa_update_aen);
+				break;
+			default:
+				keep = false;
+				ql_log(ql_log_warn, vha, 0x09102,
+					"%s unknown DB type=%d %p\n",
+					__func__, dbnode->ntype, dbnode);
+				break;
+			}
+			ap.event_data_size = dat_size;
+			/* 8 = sizeof(ap.event_code + ap.event_data_size) */
+			dat_size += 8;
+			if (keep)
+				sg_skip += sg_pcopy_from_buffer(bsg_job->reply_payload.sg_list,
+						bsg_job->reply_payload.sg_cnt,
+						&ap, dat_size, sg_skip);
+
+			ql_dbg(ql_dbg_edif, vha, 0x09102,
+				"%s Doorbell consumed : type=%d %p\n",
+				__func__, dbnode->ntype, dbnode);
+
+			kfree(dbnode);
+		} else {
+			break;
+		}
+	}
+
+	SET_DID_STATUS(bsg_reply->result, DID_OK);
+	bsg_reply->reply_payload_rcv_len = sg_skip;
+	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+
+	return 0;
+}
+
+static void __qla_edif_dbell_bsg_done(scsi_qla_host_t *vha, BSG_JOB_TYPE *bsg_job,
+	u32 delay)
+{
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+
+	/* small sleep for doorbell events to accumulate */
+	if (delay)
+		msleep(delay);
+
+	qla_edif_consume_dbell(vha, bsg_job);
+
+	bsg_job_done(bsg_job, bsg_reply->result, bsg_reply->reply_payload_rcv_len);
+}
+
+static void qla_edif_dbell_bsg_done(scsi_qla_host_t *vha)
+{
+	unsigned long flags;
+	BSG_JOB_TYPE *prev_bsg_job = NULL;
+
+	spin_lock_irqsave(&vha->e_dbell.db_lock, flags);
+	if (vha->e_dbell.dbell_bsg_job) {
+		prev_bsg_job = vha->e_dbell.dbell_bsg_job;
+		vha->e_dbell.dbell_bsg_job = NULL;
+	}
+	spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags);
+
+	if (prev_bsg_job)
+		__qla_edif_dbell_bsg_done(vha, prev_bsg_job, 0);
+}
+
+static int
+qla_edif_dbell_bsg(scsi_qla_host_t *vha, BSG_JOB_TYPE *bsg_job)
+{
+	unsigned long flags;
+	bool return_bsg = false;
+
+	/* flush previous dbell bsg */
+	qla_edif_dbell_bsg_done(vha);
+
+	spin_lock_irqsave(&vha->e_dbell.db_lock, flags);
+	if (list_empty(&vha->e_dbell.head) && DBELL_ACTIVE(vha)) {
+		/*
+		 * when the next db event happens, bsg_job will return.
+		 * Otherwise, timer will return it.
+		 */
+		vha->e_dbell.dbell_bsg_job = bsg_job;
+		vha->e_dbell.bsg_expire = jiffies + 10 * HZ;
+	} else {
+		return_bsg = true;
+	}
+	spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags);
+
+	if (return_bsg)
+		__qla_edif_dbell_bsg_done(vha, bsg_job, 1);
+
+	return 0;
+}
+
+int32_t
+qla_edif_app_mgmt(BSG_JOB_TYPE *bsg_job)
+{
+	struct fc_bsg_request	*bsg_request = bsg_job->request;
+	struct fc_bsg_reply	*bsg_reply = bsg_job->reply;
+	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
+	scsi_qla_host_t		*vha = shost_priv(host);
+	struct app_id		appcheck;
+	bool done = true;
+	int32_t         rval = 0;
+	uint32_t	vnd_sc = bsg_request->rqst_data.h_vendor.vendor_cmd[1];
+	u32 level = ql_dbg_edif;
+
+	/* doorbell is high traffic */
+	if (vnd_sc == QL_VND_SC_READ_DBELL)
+		level = 0;
+
+	ql_dbg(level, vha, 0x911d, "%s vnd subcmd=%x\n",
+	    __func__, vnd_sc);
+
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+	    bsg_job->request_payload.sg_cnt, &appcheck,
+	    sizeof(struct app_id));
+
+	if (!vha->hw->flags.edif_enabled ||
+		test_bit(VPORT_DELETE, &vha->dpc_flags)) {
+		ql_dbg(level, vha, 0x911d,
+		    "%s edif not enabled or vp delete. bsg ptr done %p. dpc_flags %lx\n",
+		    __func__, bsg_job, vha->dpc_flags);
+
+		SET_DID_STATUS(bsg_reply->result, DID_ERROR);
+		goto done;
+	}
+
+	if (!qla_edif_app_check(vha, appcheck)) {
+		ql_dbg(level, vha, 0x911d,
+		    "%s app checked failed.\n",
+		    __func__);
+
+		bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+		SET_DID_STATUS(bsg_reply->result, DID_ERROR);
+		goto done;
+	}
+
+	switch (vnd_sc) {
+	case QL_VND_SC_SA_UPDATE:
+		done = false;
+		rval = qla24xx_sadb_update(bsg_job);
+		break;
+	case QL_VND_SC_APP_START:
+		rval = qla_edif_app_start(vha, bsg_job);
+		break;
+	case QL_VND_SC_APP_STOP:
+		rval = qla_edif_app_stop(vha, bsg_job);
+		break;
+	case QL_VND_SC_AUTH_OK:
+		rval = qla_edif_app_authok(vha, bsg_job);
+		break;
+	case QL_VND_SC_AUTH_FAIL:
+		rval = qla_edif_app_authfail(vha, bsg_job);
+		break;
+	case QL_VND_SC_GET_FCINFO:
+		rval = qla_edif_app_getfcinfo(vha, bsg_job);
+		break;
+	case QL_VND_SC_GET_STATS:
+		rval = qla_edif_app_getstats(vha, bsg_job);
+		break;
+	case QL_VND_SC_AEN_COMPLETE:
+		rval = qla_edif_ack(vha, bsg_job);
+		break;
+	case QL_VND_SC_READ_DBELL:
+		rval = qla_edif_dbell_bsg(vha, bsg_job);
+		done = false;
+		break;
+	default:
+		ql_dbg(ql_dbg_edif, vha, 0x911d, "%s unknown cmd=%x\n",
+		    __func__,
+		    bsg_request->rqst_data.h_vendor.vendor_cmd[1]);
+		rval = EXT_STATUS_INVALID_PARAM;
+		done = false;
+		break;
+	}
+
+done:
+	if (done) {
+		ql_dbg(level, vha, 0x7009,
+		    "%s: %d  bsg ptr done %p\n", __func__, __LINE__, bsg_job);
+		bsg_job_done(bsg_job, bsg_reply->result,
+		    bsg_reply->reply_payload_rcv_len);
+	}
+
+	return rval;
+}
+
+static struct edif_sa_ctl *
+qla_edif_add_sa_ctl(fc_port_t *fcport, struct qla_sa_update_frame *sa_frame,
+	int dir)
+{
+	struct	edif_sa_ctl *sa_ctl;
+	struct qla_sa_update_frame *sap;
+	int	index = sa_frame->fast_sa_index;
+	unsigned long flags = 0;
+
+	sa_ctl = kzalloc(sizeof(*sa_ctl), GFP_KERNEL);
+	if (!sa_ctl) {
+		/* couldn't get space */
+		ql_dbg(ql_dbg_edif, fcport->vha, 0x9100,
+		    "unable to allocate SA CTL\n");
+		return NULL;
+	}
+
+	/*
+	 * need to allocate sa_index here and save it
+	 * in both sa_ctl->index and sa_frame->fast_sa_index;
+	 * If alloc fails then delete sa_ctl and return NULL
+	 */
+	INIT_LIST_HEAD(&sa_ctl->next);
+	sap = &sa_ctl->sa_frame;
+	*sap = *sa_frame;
+	sa_ctl->index = index;
+	sa_ctl->fcport = fcport;
+	sa_ctl->flags = 0;
+	sa_ctl->state = 0L;
+	ql_dbg(ql_dbg_edif, fcport->vha, 0x9100,
+	    "%s: Added sa_ctl %p, index %d, state 0x%lx\n",
+	    __func__, sa_ctl, sa_ctl->index, sa_ctl->state);
+	spin_lock_irqsave(&fcport->edif.sa_list_lock, flags);
+	if (dir == SAU_FLG_TX)
+		list_add_tail(&sa_ctl->next, &fcport->edif.tx_sa_list);
+	else
+		list_add_tail(&sa_ctl->next, &fcport->edif.rx_sa_list);
+	spin_unlock_irqrestore(&fcport->edif.sa_list_lock, flags);
+
+	return sa_ctl;
+}
+
+void
+qla_edif_flush_sa_ctl_lists(fc_port_t *fcport)
+{
+	struct edif_sa_ctl *sa_ctl, *tsa_ctl;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&fcport->edif.sa_list_lock, flags);
+
+	list_for_each_entry_safe(sa_ctl, tsa_ctl, &fcport->edif.tx_sa_list,
+	    next) {
+		list_del(&sa_ctl->next);
+		kfree(sa_ctl);
+	}
+
+	list_for_each_entry_safe(sa_ctl, tsa_ctl, &fcport->edif.rx_sa_list,
+	    next) {
+		list_del(&sa_ctl->next);
+		kfree(sa_ctl);
+	}
+
+	spin_unlock_irqrestore(&fcport->edif.sa_list_lock, flags);
+}
+
+struct edif_sa_ctl *
+qla_edif_find_sa_ctl_by_index(fc_port_t *fcport, int index, int dir)
+{
+	struct edif_sa_ctl *sa_ctl, *tsa_ctl;
+	struct list_head *sa_list;
+
+	if (dir == SAU_FLG_TX)
+		sa_list = &fcport->edif.tx_sa_list;
+	else
+		sa_list = &fcport->edif.rx_sa_list;
+
+	list_for_each_entry_safe(sa_ctl, tsa_ctl, sa_list, next) {
+		if (test_bit(EDIF_SA_CTL_USED, &sa_ctl->state) &&
+		    sa_ctl->index == index)
+			return sa_ctl;
+	}
+	return NULL;
+}
+
+/* add the sa to the correct list */
+static int
+qla24xx_check_sadb_avail_slot(BSG_JOB_TYPE *bsg_job, fc_port_t *fcport,
+	struct qla_sa_update_frame *sa_frame)
+{
+	struct edif_sa_ctl *sa_ctl = NULL;
+	int dir;
+	uint16_t sa_index;
+
+	dir = (sa_frame->flags & SAU_FLG_TX);
+
+	/* map the spi to an sa_index */
+	sa_index = qla_edif_sadb_get_sa_index(fcport, sa_frame);
+	if (sa_index == RX_DELETE_NO_EDIF_SA_INDEX) {
+		/* process rx delete */
+		ql_dbg(ql_dbg_edif, fcport->vha, 0x3063,
+		    "%s: rx delete for lid 0x%x, spi 0x%x, no entry found\n",
+		    __func__, fcport->loop_id, sa_frame->spi);
+
+		/* build and send the aen */
+		fcport->edif.rx_sa_set = 1;
+		fcport->edif.rx_sa_pending = 0;
+		qla_edb_eventcreate(fcport->vha,
+		    VND_CMD_AUTH_STATE_SAUPDATE_COMPL,
+		    QL_VND_SA_STAT_SUCCESS,
+		    QL_VND_RX_SA_KEY, fcport);
+
+		/* force a return of good bsg status; */
+		return RX_DELETE_NO_EDIF_SA_INDEX;
+	} else if (sa_index == INVALID_EDIF_SA_INDEX) {
+		ql_dbg(ql_dbg_edif, fcport->vha, 0x9100,
+		    "%s: Failed to get sa_index for spi 0x%x, dir: %d\n",
+		    __func__, sa_frame->spi, dir);
+		return INVALID_EDIF_SA_INDEX;
+	}
+
+	ql_dbg(ql_dbg_edif, fcport->vha, 0x9100,
+	    "%s: index %d allocated to spi 0x%x, dir: %d, nport_handle: 0x%x\n",
+	    __func__, sa_index, sa_frame->spi, dir, fcport->loop_id);
+
+	/* This is a local copy of sa_frame. */
+	sa_frame->fast_sa_index = sa_index;
+	/* create the sa_ctl */
+	sa_ctl = qla_edif_add_sa_ctl(fcport, sa_frame, dir);
+	if (!sa_ctl) {
+		ql_dbg(ql_dbg_edif, fcport->vha, 0x9100,
+		    "%s: Failed to add sa_ctl for spi 0x%x, dir: %d, sa_index: %d\n",
+		    __func__, sa_frame->spi, dir, sa_index);
+		return -1;
+	}
+
+	set_bit(EDIF_SA_CTL_USED, &sa_ctl->state);
+
+	if (dir == SAU_FLG_TX)
+		fcport->edif.tx_rekey_cnt++;
+	else
+		fcport->edif.rx_rekey_cnt++;
+
+	ql_dbg(ql_dbg_edif, fcport->vha, 0x9100,
+	    "%s: Found sa_ctl %p, index %d, state 0x%lx, tx_cnt %d, rx_cnt %d, nport_handle: 0x%x\n",
+	    __func__, sa_ctl, sa_ctl->index, sa_ctl->state,
+	    fcport->edif.tx_rekey_cnt,
+	    fcport->edif.rx_rekey_cnt, fcport->loop_id);
+
+	return 0;
+}
+
+#define QLA_SA_UPDATE_FLAGS_RX_KEY      0x0
+#define QLA_SA_UPDATE_FLAGS_TX_KEY      0x2
+#define EDIF_MSLEEP_INTERVAL 100
+#define EDIF_RETRY_COUNT  50
+
+int
+qla24xx_sadb_update(BSG_JOB_TYPE *bsg_job)
+{
+	struct	fc_bsg_reply	*bsg_reply = bsg_job->reply;
+	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
+	scsi_qla_host_t *vha = shost_priv(host);
+	fc_port_t		*fcport = NULL;
+	srb_t			*sp = NULL;
+	struct edif_list_entry *edif_entry = NULL;
+	int			found = 0;
+	int			rval = 0;
+	int result = 0, cnt;
+	struct qla_sa_update_frame sa_frame;
+	struct srb_iocb *iocb_cmd;
+	port_id_t portid;
+
+	ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x911d,
+	    "%s entered, vha: 0x%p\n", __func__, vha);
+
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+	    bsg_job->request_payload.sg_cnt, &sa_frame,
+	    sizeof(struct qla_sa_update_frame));
+
+	/* Check if host is online */
+	if (!vha->flags.online) {
+		ql_log(ql_log_warn, vha, 0x70a1, "Host is not online\n");
+		rval = -EIO;
+		SET_DID_STATUS(bsg_reply->result, DID_ERROR);
+		goto done;
+	}
+
+	if (DBELL_INACTIVE(vha)) {
+		ql_log(ql_log_warn, vha, 0x70a1, "App not started\n");
+		rval = -EIO;
+		SET_DID_STATUS(bsg_reply->result, DID_ERROR);
+		goto done;
+	}
+
+	/* silent unaligned access warning */
+	portid.b.domain = sa_frame.port_id.b.domain;
+	portid.b.area   = sa_frame.port_id.b.area;
+	portid.b.al_pa  = sa_frame.port_id.b.al_pa;
+
+	fcport = qla2x00_find_fcport_by_pid(vha, &portid);
+	if (fcport) {
+		found = 1;
+		if (sa_frame.flags == QLA_SA_UPDATE_FLAGS_TX_KEY)
+			fcport->edif.tx_bytes = 0;
+		if (sa_frame.flags == QLA_SA_UPDATE_FLAGS_RX_KEY)
+			fcport->edif.rx_bytes = 0;
+	}
+
+	if (!found) {
+		ql_dbg(ql_dbg_edif, vha, 0x70a3, "Failed to find port= %06x\n",
+		    sa_frame.port_id.b24);
+		rval = -EINVAL;
+		SET_DID_STATUS(bsg_reply->result, DID_NO_CONNECT);
+		goto done;
+	}
+
+	/* make sure the nport_handle is valid */
+	if (fcport->loop_id == FC_NO_LOOP_ID) {
+		ql_dbg(ql_dbg_edif, vha, 0x70e1,
+		    "%s: %8phN lid=FC_NO_LOOP_ID, spi: 0x%x, DS %d, returning NO_CONNECT\n",
+		    __func__, fcport->port_name, sa_frame.spi,
+		    fcport->disc_state);
+		rval = -EINVAL;
+		SET_DID_STATUS(bsg_reply->result, DID_NO_CONNECT);
+		goto done;
+	}
+
+	/* allocate and queue an sa_ctl */
+	result = qla24xx_check_sadb_avail_slot(bsg_job, fcport, &sa_frame);
+
+	/* failure of bsg */
+	if (result == INVALID_EDIF_SA_INDEX) {
+		ql_dbg(ql_dbg_edif, vha, 0x70e1,
+		    "%s: %8phN, skipping update.\n",
+		    __func__, fcport->port_name);
+		rval = -EINVAL;
+		SET_DID_STATUS(bsg_reply->result, DID_ERROR);
+		goto done;
+
+	/* rx delete failure */
+	} else if (result == RX_DELETE_NO_EDIF_SA_INDEX) {
+		ql_dbg(ql_dbg_edif, vha, 0x70e1,
+		    "%s: %8phN, skipping rx delete.\n",
+		    __func__, fcport->port_name);
+		SET_DID_STATUS(bsg_reply->result, DID_OK);
+		goto done;
+	}
+
+	ql_dbg(ql_dbg_edif, vha, 0x70e1,
+	    "%s: %8phN, sa_index in sa_frame: %d flags %xh\n",
+	    __func__, fcport->port_name, sa_frame.fast_sa_index,
+	    sa_frame.flags);
+
+	/* looking for rx index and delete */
+	if (((sa_frame.flags & SAU_FLG_TX) == 0) &&
+	    (sa_frame.flags & SAU_FLG_INV)) {
+		uint16_t nport_handle = fcport->loop_id;
+		uint16_t sa_index = sa_frame.fast_sa_index;
+
+		/*
+		 * make sure we have an existing rx key, otherwise just process
+		 * this as a straight delete just like TX
+		 * This is NOT a normal case, it indicates an error recovery or key cleanup
+		 * by the ipsec code above us.
+		 */
+		edif_entry = qla_edif_list_find_sa_index(fcport, fcport->loop_id);
+		if (!edif_entry) {
+			ql_dbg(ql_dbg_edif, vha, 0x911d,
+			    "%s: WARNING: no active sa_index for nport_handle 0x%x, forcing delete for sa_index 0x%x\n",
+			    __func__, fcport->loop_id, sa_index);
+			goto force_rx_delete;
+		}
+
+		/*
+		 * if we have a forced delete for rx, remove the sa_index from the edif list
+		 * and proceed with normal delete.  The rx delay timer should not be running
+		 */
+		if ((sa_frame.flags & SAU_FLG_FORCE_DELETE) == SAU_FLG_FORCE_DELETE) {
+			qla_edif_list_delete_sa_index(fcport, edif_entry);
+			ql_dbg(ql_dbg_edif, vha, 0x911d,
+			    "%s: FORCE DELETE flag found for nport_handle 0x%x, sa_index 0x%x, forcing DELETE\n",
+			    __func__, fcport->loop_id, sa_index);
+			kfree(edif_entry);
+			goto force_rx_delete;
+		}
+
+		/*
+		 * delayed rx delete
+		 *
+		 * if delete_sa_index is not invalid then there is already
+		 * a delayed index in progress, return bsg bad status
+		 */
+		if (edif_entry->delete_sa_index != INVALID_EDIF_SA_INDEX) {
+			struct edif_sa_ctl *sa_ctl;
+
+			ql_dbg(ql_dbg_edif, vha, 0x911d,
+			    "%s: delete for lid 0x%x, delete_sa_index %d is pending\n",
+			    __func__, edif_entry->handle, edif_entry->delete_sa_index);
+
+			/* free up the sa_ctl that was allocated with the sa_index */
+			sa_ctl = qla_edif_find_sa_ctl_by_index(fcport, sa_index,
+			    (sa_frame.flags & SAU_FLG_TX));
+			if (sa_ctl) {
+				ql_dbg(ql_dbg_edif, vha, 0x3063,
+				    "%s: freeing sa_ctl for index %d\n",
+				    __func__, sa_ctl->index);
+				qla_edif_free_sa_ctl(fcport, sa_ctl, sa_ctl->index);
+			}
+
+			/* release the sa_index */
+			ql_dbg(ql_dbg_edif, vha, 0x3063,
+			    "%s: freeing sa_index %d, nph: 0x%x\n",
+			    __func__, sa_index, nport_handle);
+			qla_edif_sadb_delete_sa_index(fcport, nport_handle, sa_index);
+
+			rval = -EINVAL;
+			SET_DID_STATUS(bsg_reply->result, DID_ERROR);
+			goto done;
+		}
+
+		fcport->edif.rekey_cnt++;
+
+		/* configure and start the rx delay timer */
+		edif_entry->fcport = fcport;
+		edif_entry->timer.expires = jiffies + RX_DELAY_DELETE_TIMEOUT * HZ;
+
+		ql_dbg(ql_dbg_edif, vha, 0x911d,
+		    "%s: adding timer, entry: %p, delete sa_index %d, lid 0x%x to edif_list\n",
+		    __func__, edif_entry, sa_index, nport_handle);
+
+		/*
+		 * Start the timer when we queue the delayed rx delete.
+		 * This is an activity timer that goes off if we have not
+		 * received packets with the new sa_index
+		 */
+		add_timer(&edif_entry->timer);
+
+		/*
+		 * sa_delete for rx key with an active rx key including this one
+		 * add the delete rx sa index to the hash so we can look for it
+		 * in the rsp queue.  Do this after making any changes to the
+		 * edif_entry as part of the rx delete.
+		 */
+
+		ql_dbg(ql_dbg_edif, vha, 0x911d,
+		    "%s: delete sa_index %d, lid 0x%x to edif_list. bsg done ptr %p\n",
+		    __func__, sa_index, nport_handle, bsg_job);
+
+		edif_entry->delete_sa_index = sa_index;
+
+		bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+		bsg_reply->result = DID_OK << 16;
+
+		goto done;
+
+	/*
+	 * rx index and update
+	 * add the index to the list and continue with normal update
+	 */
+	} else if (((sa_frame.flags & SAU_FLG_TX) == 0) &&
+	    ((sa_frame.flags & SAU_FLG_INV) == 0)) {
+		/* sa_update for rx key */
+		uint32_t nport_handle = fcport->loop_id;
+		uint16_t sa_index = sa_frame.fast_sa_index;
+		int result;
+
+		/*
+		 * add the update rx sa index to the hash so we can look for it
+		 * in the rsp queue and continue normally
+		 */
+
+		ql_dbg(ql_dbg_edif, vha, 0x911d,
+		    "%s:  adding update sa_index %d, lid 0x%x to edif_list\n",
+		    __func__, sa_index, nport_handle);
+
+		result = qla_edif_list_add_sa_update_index(fcport, sa_index,
+		    nport_handle);
+		if (result) {
+			ql_dbg(ql_dbg_edif, vha, 0x911d,
+			    "%s: SA_UPDATE failed to add new sa index %d to list for lid 0x%x\n",
+			    __func__, sa_index, nport_handle);
+		}
+	}
+	if (sa_frame.flags & SAU_FLG_GMAC_MODE)
+		fcport->edif.aes_gmac = 1;
+	else
+		fcport->edif.aes_gmac = 0;
+
+force_rx_delete:
+	/*
+	 * sa_update for both rx and tx keys, sa_delete for tx key
+	 * immediately process the request
+	 */
+	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
+	if (!sp) {
+		rval = -ENOMEM;
+		SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY);
+		goto done;
+	}
+
+	sp->type = SRB_SA_UPDATE;
+	sp->name = "bsg_sa_update";
+	sp->u.bsg_job = bsg_job;
+	/* sp->free = qla2x00_bsg_sp_free; */
+	sp->free = qla2x00_rel_sp;
+	sp->done = qla2x00_bsg_job_done;
+	iocb_cmd = &sp->u.iocb_cmd;
+	iocb_cmd->u.sa_update.sa_frame  = sa_frame;
+	cnt = 0;
+retry:
+	rval = qla2x00_start_sp(sp);
+	switch (rval) {
+	case QLA_SUCCESS:
+		break;
+	case EAGAIN:
+		msleep(EDIF_MSLEEP_INTERVAL);
+		cnt++;
+		if (cnt < EDIF_RETRY_COUNT)
+			goto retry;
+
+		fallthrough;
+	default:
+		ql_log(ql_dbg_edif, vha, 0x70e3,
+		       "%s qla2x00_start_sp failed=%d.\n",
+		       __func__, rval);
+
+		qla2x00_rel_sp(sp);
+		rval = -EIO;
+		SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY);
+		goto done;
+	}
+
+	ql_dbg(ql_dbg_edif, vha, 0x911d,
+	    "%s:  %s sent, hdl=%x, portid=%06x.\n",
+	    __func__, sp->name, sp->handle, fcport->d_id.b24);
+
+	fcport->edif.rekey_cnt++;
+	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+	SET_DID_STATUS(bsg_reply->result, DID_OK);
+
+	return 0;
+
+/*
+ * send back error status
+ */
+done:
+	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+	ql_dbg(ql_dbg_edif, vha, 0x911d,
+	    "%s:status: FAIL, result: 0x%x, bsg ptr done %p\n",
+	    __func__, bsg_reply->result, bsg_job);
+	bsg_job_done(bsg_job, bsg_reply->result,
+	    bsg_reply->reply_payload_rcv_len);
+
+	return 0;
+}
+
+static void
+qla_enode_free(scsi_qla_host_t *vha, struct enode *node)
+{
+	node->ntype = N_UNDEF;
+	kfree(node);
+}
+
+/**
+ * qla_enode_init - initialize enode structs & lock
+ * @vha: host adapter pointer
+ *
+ * should only be called when driver attaching
+ */
+void
+qla_enode_init(scsi_qla_host_t *vha)
+{
+	struct	qla_hw_data *ha = vha->hw;
+	char	name[32];
+
+	if (vha->pur_cinfo.enode_flags == ENODE_ACTIVE) {
+		/* list still active - error */
+		ql_dbg(ql_dbg_edif, vha, 0x09102, "%s enode still active\n",
+		    __func__);
+		return;
+	}
+
+	/* initialize lock which protects pur_core & init list */
+	spin_lock_init(&vha->pur_cinfo.pur_lock);
+	INIT_LIST_HEAD(&vha->pur_cinfo.head);
+
+	snprintf(name, sizeof(name), "%s_%d_purex", QLA2XXX_DRIVER_NAME,
+	    ha->pdev->device);
+}
+
+/**
+ * qla_enode_stop - stop and clear and enode data
+ * @vha: host adapter pointer
+ *
+ * called when app notified it is exiting
+ */
+void
+qla_enode_stop(scsi_qla_host_t *vha)
+{
+	unsigned long flags;
+	struct enode *node, *q;
+
+	if (vha->pur_cinfo.enode_flags != ENODE_ACTIVE) {
+		/* doorbell list not enabled */
+		ql_dbg(ql_dbg_edif, vha, 0x09102,
+		    "%s enode not active\n", __func__);
+		return;
+	}
+
+	/* grab lock so list doesn't move */
+	spin_lock_irqsave(&vha->pur_cinfo.pur_lock, flags);
+
+	vha->pur_cinfo.enode_flags &= ~ENODE_ACTIVE; /* mark it not active */
+
+	/* hopefully this is a null list at this point */
+	list_for_each_entry_safe(node, q, &vha->pur_cinfo.head, list) {
+		ql_dbg(ql_dbg_edif, vha, 0x910f,
+		    "%s freeing enode type=%x, cnt=%x\n", __func__, node->ntype,
+		    node->dinfo.nodecnt);
+		list_del_init(&node->list);
+		qla_enode_free(vha, node);
+	}
+	spin_unlock_irqrestore(&vha->pur_cinfo.pur_lock, flags);
+}
+
+static void qla_enode_clear(scsi_qla_host_t *vha, port_id_t portid)
+{
+	unsigned    long flags;
+	struct enode    *e, *tmp;
+	struct purexevent   *purex;
+	LIST_HEAD(enode_list);
+
+	if (vha->pur_cinfo.enode_flags != ENODE_ACTIVE) {
+		ql_dbg(ql_dbg_edif, vha, 0x09102,
+		       "%s enode not active\n", __func__);
+		return;
+	}
+	spin_lock_irqsave(&vha->pur_cinfo.pur_lock, flags);
+	list_for_each_entry_safe(e, tmp, &vha->pur_cinfo.head, list) {
+		purex = &e->u.purexinfo;
+		if (purex->pur_info.pur_sid.b24 == portid.b24) {
+			ql_dbg(ql_dbg_edif, vha, 0x911d,
+			    "%s free ELS sid=%06x. xchg %x, nb=%xh\n",
+			    __func__, portid.b24,
+			    purex->pur_info.pur_rx_xchg_address,
+			    purex->pur_info.pur_bytes_rcvd);
+
+			list_del_init(&e->list);
+			list_add_tail(&e->list, &enode_list);
+		}
+	}
+	spin_unlock_irqrestore(&vha->pur_cinfo.pur_lock, flags);
+
+	list_for_each_entry_safe(e, tmp, &enode_list, list) {
+		list_del_init(&e->list);
+		qla_enode_free(vha, e);
+	}
+}
+
+/*
+ *  allocate enode struct and populate buffer
+ *  returns: enode pointer with buffers
+ *           NULL on error
+ */
+static struct enode *
+qla_enode_alloc(scsi_qla_host_t *vha, uint32_t ntype)
+{
+	struct enode		*node;
+	struct purexevent	*purex;
+
+	node = kzalloc(RX_ELS_SIZE, GFP_ATOMIC);
+	if (!node)
+		return NULL;
+
+	purex = &node->u.purexinfo;
+	purex->msgp = (u8 *)(node + 1);
+	purex->msgp_len = ELS_MAX_PAYLOAD;
+
+	node->ntype = ntype;
+	INIT_LIST_HEAD(&node->list);
+	return node;
+}
+
+static void
+qla_enode_add(scsi_qla_host_t *vha, struct enode *ptr)
+{
+	unsigned long flags;
+
+	ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x9109,
+	    "%s add enode for type=%x, cnt=%x\n",
+	    __func__, ptr->ntype, ptr->dinfo.nodecnt);
+
+	spin_lock_irqsave(&vha->pur_cinfo.pur_lock, flags);
+	list_add_tail(&ptr->list, &vha->pur_cinfo.head);
+	spin_unlock_irqrestore(&vha->pur_cinfo.pur_lock, flags);
+
+	return;
+}
+
+static struct enode *
+qla_enode_find(scsi_qla_host_t *vha, uint32_t ntype, uint32_t p1, uint32_t p2)
+{
+	struct enode		*node_rtn = NULL;
+	struct enode		*list_node, *q;
+	unsigned long		flags;
+	uint32_t		sid;
+	struct purexevent	*purex;
+
+	/* secure the list from moving under us */
+	spin_lock_irqsave(&vha->pur_cinfo.pur_lock, flags);
+
+	list_for_each_entry_safe(list_node, q, &vha->pur_cinfo.head, list) {
+
+		/* node type determines what p1 and p2 are */
+		purex = &list_node->u.purexinfo;
+		sid = p1;
+
+		if (purex->pur_info.pur_sid.b24 == sid) {
+			/* found it and its complete */
+			node_rtn = list_node;
+			list_del(&list_node->list);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&vha->pur_cinfo.pur_lock, flags);
+
+	return node_rtn;
+}
+
+/**
+ * qla_pur_get_pending - read/return authentication message sent
+ *  from remote port
+ * @vha: host adapter pointer
+ * @fcport: session pointer
+ * @bsg_job: user request where the message is copy to.
+ */
+static int
+qla_pur_get_pending(scsi_qla_host_t *vha, fc_port_t *fcport,
+	BSG_JOB_TYPE *bsg_job)
+{
+	struct enode		*ptr;
+	struct purexevent	*purex;
+	struct qla_bsg_auth_els_reply *rpl =
+	    (struct qla_bsg_auth_els_reply *)bsg_job->reply;
+
+	bsg_job->reply_len = sizeof(*rpl);
+
+	ptr = qla_enode_find(vha, N_PUREX, fcport->d_id.b24, PUR_GET);
+	if (!ptr) {
+		ql_dbg(ql_dbg_edif, vha, 0x9111,
+		    "%s no enode data found for %8phN sid=%06x\n",
+		    __func__, fcport->port_name, fcport->d_id.b24);
+		SET_DID_STATUS(rpl->r.result, DID_IMM_RETRY);
+		return -EIO;
+	}
+
+	/*
+	 * enode is now off the linked list and is ours to deal with
+	 */
+	purex = &ptr->u.purexinfo;
+
+	/* Copy info back to caller */
+	rpl->rx_xchg_address = purex->pur_info.pur_rx_xchg_address;
+
+	SET_DID_STATUS(rpl->r.result, DID_OK);
+	rpl->r.reply_payload_rcv_len =
+	    sg_pcopy_from_buffer(bsg_job->reply_payload.sg_list,
+		bsg_job->reply_payload.sg_cnt, purex->msgp,
+		purex->pur_info.pur_bytes_rcvd, 0);
+
+	/* data copy / passback completed - destroy enode */
+	qla_enode_free(vha, ptr);
+
+	return 0;
+}
+
+/* it is assume qpair lock is held */
+static int
+qla_els_reject_iocb(scsi_qla_host_t *vha, struct qla_qpair *qp,
+	struct qla_els_pt_arg *a)
+{
+	struct els_entry_24xx *els_iocb;
+
+	els_iocb = __qla2x00_alloc_iocbs(qp, NULL);
+	if (!els_iocb) {
+		ql_log(ql_log_warn, vha, 0x700c,
+		    "qla2x00_alloc_iocbs failed.\n");
+		return QLA_FUNCTION_FAILED;
+	}
+
+	qla_els_pt_iocb(vha, els_iocb, a);
+
+	ql_dbg(ql_dbg_edif, vha, 0x0183,
+	    "Sending ELS reject ox_id %04x s:%06x -> d:%06x\n",
+	    a->ox_id, a->sid.b24, a->did.b24);
+	ql_dump_buffer(ql_dbg_edif + ql_dbg_verbose, vha, 0x0185,
+	    vha->hw->elsrej.c, sizeof(*vha->hw->elsrej.c));
+	/* flush iocb to mem before notifying hw doorbell */
+	wmb();
+	qla2x00_start_iocbs(vha, qp->req);
+	return 0;
+}
+
+void
+qla_edb_init(scsi_qla_host_t *vha)
+{
+	if (DBELL_ACTIVE(vha)) {
+		/* list already init'd - error */
+		ql_dbg(ql_dbg_edif, vha, 0x09102,
+		    "edif db already initialized, cannot reinit\n");
+		return;
+	}
+
+	/* initialize lock which protects doorbell & init list */
+	spin_lock_init(&vha->e_dbell.db_lock);
+	INIT_LIST_HEAD(&vha->e_dbell.head);
+}
+
+static void qla_edb_clear(scsi_qla_host_t *vha, port_id_t portid)
+{
+	unsigned long flags;
+	struct edb_node *e, *tmp;
+	port_id_t sid;
+	LIST_HEAD(edb_list);
+
+	if (DBELL_INACTIVE(vha)) {
+		/* doorbell list not enabled */
+		ql_dbg(ql_dbg_edif, vha, 0x09102,
+		       "%s doorbell not enabled\n", __func__);
+		return;
+	}
+
+	/* grab lock so list doesn't move */
+	spin_lock_irqsave(&vha->e_dbell.db_lock, flags);
+	list_for_each_entry_safe(e, tmp, &vha->e_dbell.head, list) {
+		switch (e->ntype) {
+		case VND_CMD_AUTH_STATE_NEEDED:
+		case VND_CMD_AUTH_STATE_SESSION_SHUTDOWN:
+			sid = e->u.plogi_did;
+			break;
+		case VND_CMD_AUTH_STATE_ELS_RCVD:
+			sid = e->u.els_sid;
+			break;
+		case VND_CMD_AUTH_STATE_SAUPDATE_COMPL:
+			/* app wants to see this  */
+			continue;
+		default:
+			ql_log(ql_log_warn, vha, 0x09102,
+			       "%s unknown node type: %x\n", __func__, e->ntype);
+			sid.b24 = 0;
+			break;
+		}
+		if (sid.b24 == portid.b24) {
+			ql_dbg(ql_dbg_edif, vha, 0x910f,
+			       "%s free doorbell event : node type = %x %p\n",
+			       __func__, e->ntype, e);
+			list_del_init(&e->list);
+			list_add_tail(&e->list, &edb_list);
+		}
+	}
+	spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags);
+
+	list_for_each_entry_safe(e, tmp, &edb_list, list)
+		qla_edb_node_free(vha, e);
+}
+
+/* function called when app is stopping */
+
+void
+qla_edb_stop(scsi_qla_host_t *vha)
+{
+	unsigned long flags;
+	struct edb_node *node, *q;
+
+	if (DBELL_INACTIVE(vha)) {
+		/* doorbell list not enabled */
+		ql_dbg(ql_dbg_edif, vha, 0x09102,
+		    "%s doorbell not enabled\n", __func__);
+		return;
+	}
+
+	/* grab lock so list doesn't move */
+	spin_lock_irqsave(&vha->e_dbell.db_lock, flags);
+
+	vha->e_dbell.db_flags &= ~EDB_ACTIVE; /* mark it not active */
+	/* hopefully this is a null list at this point */
+	list_for_each_entry_safe(node, q, &vha->e_dbell.head, list) {
+		ql_dbg(ql_dbg_edif, vha, 0x910f,
+		    "%s freeing edb_node type=%x\n",
+		    __func__, node->ntype);
+		qla_edb_node_free(vha, node);
+	}
+	spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags);
+
+	qla_edif_dbell_bsg_done(vha);
+}
+
+static struct edb_node *
+qla_edb_node_alloc(scsi_qla_host_t *vha, uint32_t ntype)
+{
+	struct edb_node	*node;
+
+	node = kzalloc(sizeof(*node), GFP_ATOMIC);
+	if (!node) {
+		/* couldn't get space */
+		ql_dbg(ql_dbg_edif, vha, 0x9100,
+		    "edb node unable to be allocated\n");
+		return NULL;
+	}
+
+	node->ntype = ntype;
+	INIT_LIST_HEAD(&node->list);
+	return node;
+}
+
+/* adds a already allocated enode to the linked list */
+static bool
+qla_edb_node_add(scsi_qla_host_t *vha, struct edb_node *ptr)
+{
+	unsigned long		flags;
+
+	if (DBELL_INACTIVE(vha)) {
+		/* doorbell list not enabled */
+		ql_dbg(ql_dbg_edif, vha, 0x09102,
+		    "%s doorbell not enabled\n", __func__);
+		return false;
+	}
+
+	spin_lock_irqsave(&vha->e_dbell.db_lock, flags);
+	list_add_tail(&ptr->list, &vha->e_dbell.head);
+	spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags);
+
+	return true;
+}
+
+/* adds event to doorbell list */
+void
+qla_edb_eventcreate(scsi_qla_host_t *vha, uint32_t dbtype,
+	uint32_t data, uint32_t data2, fc_port_t	*sfcport)
+{
+	struct edb_node	*edbnode;
+	fc_port_t *fcport = sfcport;
+	port_id_t id;
+
+	if (!vha->hw->flags.edif_enabled) {
+		/* edif not enabled */
+		return;
+	}
+
+	if (DBELL_INACTIVE(vha)) {
+		if (fcport)
+			fcport->edif.auth_state = dbtype;
+		/* doorbell list not enabled */
+		ql_dbg(ql_dbg_edif, vha, 0x09102,
+		    "%s doorbell not enabled (type=%d\n", __func__, dbtype);
+		return;
+	}
+
+	edbnode = qla_edb_node_alloc(vha, dbtype);
+	if (!edbnode) {
+		ql_dbg(ql_dbg_edif, vha, 0x09102,
+		    "%s unable to alloc db node\n", __func__);
+		return;
+	}
+
+	if (!fcport) {
+		id.b.domain = (data >> 16) & 0xff;
+		id.b.area = (data >> 8) & 0xff;
+		id.b.al_pa = data & 0xff;
+		ql_dbg(ql_dbg_edif, vha, 0x09222,
+		    "%s: Arrived s_id: %06x\n", __func__,
+		    id.b24);
+		fcport = qla2x00_find_fcport_by_pid(vha, &id);
+		if (!fcport) {
+			ql_dbg(ql_dbg_edif, vha, 0x09102,
+			    "%s can't find fcport for sid= 0x%x - ignoring\n",
+			__func__, id.b24);
+			kfree(edbnode);
+			return;
+		}
+	}
+
+	/* populate the edb node */
+	switch (dbtype) {
+	case VND_CMD_AUTH_STATE_NEEDED:
+	case VND_CMD_AUTH_STATE_SESSION_SHUTDOWN:
+		edbnode->u.plogi_did.b24 = fcport->d_id.b24;
+		break;
+	case VND_CMD_AUTH_STATE_ELS_RCVD:
+		edbnode->u.els_sid.b24 = fcport->d_id.b24;
+		break;
+	case VND_CMD_AUTH_STATE_SAUPDATE_COMPL:
+		edbnode->u.sa_aen.port_id = fcport->d_id;
+		edbnode->u.sa_aen.status =  data;
+		edbnode->u.sa_aen.key_type =  data2;
+		edbnode->u.sa_aen.version = EDIF_VERSION1;
+		break;
+	default:
+		ql_dbg(ql_dbg_edif, vha, 0x09102,
+			"%s unknown type: %x\n", __func__, dbtype);
+		kfree(edbnode);
+		edbnode = NULL;
+		break;
+	}
+
+	if (edbnode) {
+		if (!qla_edb_node_add(vha, edbnode)) {
+			ql_dbg(ql_dbg_edif, vha, 0x09102,
+			    "%s unable to add dbnode\n", __func__);
+			kfree(edbnode);
+			return;
+		}
+		ql_dbg(ql_dbg_edif, vha, 0x09102,
+		    "%s Doorbell produced : type=%d %p\n", __func__, dbtype, edbnode);
+		qla_edif_dbell_bsg_done(vha);
+		if (fcport)
+			fcport->edif.auth_state = dbtype;
+	}
+}
+
+void
+qla_edif_timer(scsi_qla_host_t *vha)
+{
+	struct qla_hw_data *ha = vha->hw;
+
+	if (!vha->vp_idx && N2N_TOPO(ha) && ha->flags.n2n_fw_acc_sec) {
+		if (DBELL_INACTIVE(vha) &&
+		    ha->edif_post_stop_cnt_down) {
+			ha->edif_post_stop_cnt_down--;
+
+			/*
+			 * turn off auto 'Plogi Acc + secure=1' feature
+			 * Set Add FW option[3]
+			 * BIT_15, if.
+			 */
+			if (ha->edif_post_stop_cnt_down == 0) {
+				ql_dbg(ql_dbg_async, vha, 0x911d,
+				       "%s chip reset to turn off PLOGI ACC + secure\n",
+				       __func__);
+				set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+			}
+		} else {
+			ha->edif_post_stop_cnt_down = 60;
+		}
+	}
+
+	if (vha->e_dbell.dbell_bsg_job && time_after_eq(jiffies, vha->e_dbell.bsg_expire))
+		qla_edif_dbell_bsg_done(vha);
+}
+
+static void qla_noop_sp_done(srb_t *sp, int res)
+{
+	sp->fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
+}
+
+/*
+ * Called from work queue
+ * build and send the sa_update iocb to delete an rx sa_index
+ */
+int
+qla24xx_issue_sa_replace_iocb(scsi_qla_host_t *vha, struct qla_work_evt *e)
+{
+	srb_t *sp;
+	fc_port_t	*fcport = NULL;
+	struct srb_iocb *iocb_cmd = NULL;
+	int rval = QLA_SUCCESS;
+	struct	edif_sa_ctl *sa_ctl = e->u.sa_update.sa_ctl;
+	uint16_t nport_handle = e->u.sa_update.nport_handle;
+
+	ql_dbg(ql_dbg_edif, vha, 0x70e6,
+	    "%s: starting,  sa_ctl: %p\n", __func__, sa_ctl);
+
+	if (!sa_ctl) {
+		ql_dbg(ql_dbg_edif, vha, 0x70e6,
+		    "sa_ctl allocation failed\n");
+		rval =  -ENOMEM;
+		goto done;
+	}
+
+	fcport = sa_ctl->fcport;
+
+	/* Alloc SRB structure */
+	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
+	if (!sp) {
+		ql_dbg(ql_dbg_edif, vha, 0x70e6,
+		 "SRB allocation failed\n");
+		rval = -ENOMEM;
+		goto done;
+	}
+
+	fcport->flags |= FCF_ASYNC_SENT;
+	iocb_cmd = &sp->u.iocb_cmd;
+	iocb_cmd->u.sa_update.sa_ctl = sa_ctl;
+
+	ql_dbg(ql_dbg_edif, vha, 0x3073,
+	    "Enter: SA REPL portid=%06x, sa_ctl %p, index %x, nport_handle: 0x%x\n",
+	    fcport->d_id.b24, sa_ctl, sa_ctl->index, nport_handle);
+	/*
+	 * if this is a sadb cleanup delete, mark it so the isr can
+	 * take the correct action
+	 */
+	if (sa_ctl->flags & EDIF_SA_CTL_FLG_CLEANUP_DEL) {
+		/* mark this srb as a cleanup delete */
+		sp->flags |= SRB_EDIF_CLEANUP_DELETE;
+		ql_dbg(ql_dbg_edif, vha, 0x70e6,
+		    "%s: sp 0x%p flagged as cleanup delete\n", __func__, sp);
+	}
+
+	sp->type = SRB_SA_REPLACE;
+	sp->name = "SA_REPLACE";
+	sp->fcport = fcport;
+	sp->free = qla2x00_rel_sp;
+	sp->done = qla_noop_sp_done;
+
+	rval = qla2x00_start_sp(sp);
+
+	if (rval != QLA_SUCCESS) {
+		goto done_free_sp;
+	}
+
+	return rval;
+done_free_sp:
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
+	fcport->flags &= ~FCF_ASYNC_SENT;
+done:
+	fcport->flags &= ~FCF_ASYNC_ACTIVE;
+	return rval;
+}
+
+void qla24xx_sa_update_iocb(srb_t *sp, struct sa_update_28xx *sa_update_iocb)
+{
+	int	itr = 0;
+	struct	scsi_qla_host		*vha = sp->vha;
+	struct	qla_sa_update_frame	*sa_frame =
+		&sp->u.iocb_cmd.u.sa_update.sa_frame;
+	u8 flags = 0;
+
+	switch (sa_frame->flags & (SAU_FLG_INV | SAU_FLG_TX)) {
+	case 0:
+		ql_dbg(ql_dbg_edif, vha, 0x911d,
+		    "%s: EDIF SA UPDATE RX IOCB  vha: 0x%p  index: %d\n",
+		    __func__, vha, sa_frame->fast_sa_index);
+		break;
+	case 1:
+		ql_dbg(ql_dbg_edif, vha, 0x911d,
+		    "%s: EDIF SA DELETE RX IOCB  vha: 0x%p  index: %d\n",
+		    __func__, vha, sa_frame->fast_sa_index);
+		flags |= SA_FLAG_INVALIDATE;
+		break;
+	case 2:
+		ql_dbg(ql_dbg_edif, vha, 0x911d,
+		    "%s: EDIF SA UPDATE TX IOCB  vha: 0x%p  index: %d\n",
+		    __func__, vha, sa_frame->fast_sa_index);
+		flags |= SA_FLAG_TX;
+		break;
+	case 3:
+		ql_dbg(ql_dbg_edif, vha, 0x911d,
+		    "%s: EDIF SA DELETE TX IOCB  vha: 0x%p  index: %d\n",
+		    __func__, vha, sa_frame->fast_sa_index);
+		flags |= SA_FLAG_TX | SA_FLAG_INVALIDATE;
+		break;
+	}
+
+	sa_update_iocb->entry_type = SA_UPDATE_IOCB_TYPE;
+	sa_update_iocb->entry_count = 1;
+	sa_update_iocb->sys_define = 0;
+	sa_update_iocb->entry_status = 0;
+	sa_update_iocb->handle = sp->handle;
+	sa_update_iocb->u.nport_handle = cpu_to_le16(sp->fcport->loop_id);
+	sa_update_iocb->vp_index = sp->fcport->vha->vp_idx;
+	sa_update_iocb->port_id[0] = sp->fcport->d_id.b.al_pa;
+	sa_update_iocb->port_id[1] = sp->fcport->d_id.b.area;
+	sa_update_iocb->port_id[2] = sp->fcport->d_id.b.domain;
+
+	sa_update_iocb->flags = flags;
+	sa_update_iocb->salt = cpu_to_le32(sa_frame->salt);
+	sa_update_iocb->spi = cpu_to_le32(sa_frame->spi);
+	sa_update_iocb->sa_index = cpu_to_le16(sa_frame->fast_sa_index);
+
+	sa_update_iocb->sa_control |= SA_CNTL_ENC_FCSP;
+	if (sp->fcport->edif.aes_gmac)
+		sa_update_iocb->sa_control |= SA_CNTL_AES_GMAC;
+
+	if (sa_frame->flags & SAU_FLG_KEY256) {
+		sa_update_iocb->sa_control |= SA_CNTL_KEY256;
+		for (itr = 0; itr < 32; itr++)
+			sa_update_iocb->sa_key[itr] = sa_frame->sa_key[itr];
+	} else {
+		sa_update_iocb->sa_control |= SA_CNTL_KEY128;
+		for (itr = 0; itr < 16; itr++)
+			sa_update_iocb->sa_key[itr] = sa_frame->sa_key[itr];
+	}
+
+	ql_dbg(ql_dbg_edif, vha, 0x921d,
+	    "%s SAU Port ID = %02x%02x%02x, flags=%xh, index=%u, ctl=%xh, SPI 0x%x flags 0x%x hdl=%x gmac %d\n",
+	    __func__, sa_update_iocb->port_id[2], sa_update_iocb->port_id[1],
+	    sa_update_iocb->port_id[0], sa_update_iocb->flags, sa_update_iocb->sa_index,
+	    sa_update_iocb->sa_control, sa_update_iocb->spi, sa_frame->flags, sp->handle,
+	    sp->fcport->edif.aes_gmac);
+
+	if (sa_frame->flags & SAU_FLG_TX)
+		sp->fcport->edif.tx_sa_pending = 1;
+	else
+		sp->fcport->edif.rx_sa_pending = 1;
+
+	sp->fcport->vha->qla_stats.control_requests++;
+}
+
+void
+qla24xx_sa_replace_iocb(srb_t *sp, struct sa_update_28xx *sa_update_iocb)
+{
+	struct	scsi_qla_host		*vha = sp->vha;
+	struct srb_iocb *srb_iocb = &sp->u.iocb_cmd;
+	struct	edif_sa_ctl		*sa_ctl = srb_iocb->u.sa_update.sa_ctl;
+	uint16_t nport_handle = sp->fcport->loop_id;
+
+	sa_update_iocb->entry_type = SA_UPDATE_IOCB_TYPE;
+	sa_update_iocb->entry_count = 1;
+	sa_update_iocb->sys_define = 0;
+	sa_update_iocb->entry_status = 0;
+	sa_update_iocb->handle = sp->handle;
+
+	sa_update_iocb->u.nport_handle = cpu_to_le16(nport_handle);
+
+	sa_update_iocb->vp_index = sp->fcport->vha->vp_idx;
+	sa_update_iocb->port_id[0] = sp->fcport->d_id.b.al_pa;
+	sa_update_iocb->port_id[1] = sp->fcport->d_id.b.area;
+	sa_update_iocb->port_id[2] = sp->fcport->d_id.b.domain;
+
+	/* Invalidate the index. salt, spi, control & key are ignore */
+	sa_update_iocb->flags = SA_FLAG_INVALIDATE;
+	sa_update_iocb->salt = 0;
+	sa_update_iocb->spi = 0;
+	sa_update_iocb->sa_index = cpu_to_le16(sa_ctl->index);
+	sa_update_iocb->sa_control = 0;
+
+	ql_dbg(ql_dbg_edif, vha, 0x921d,
+	    "%s SAU DELETE RX Port ID = %02x:%02x:%02x, lid %d flags=%xh, index=%u, hdl=%x\n",
+	    __func__, sa_update_iocb->port_id[2], sa_update_iocb->port_id[1],
+	    sa_update_iocb->port_id[0], nport_handle, sa_update_iocb->flags,
+	    sa_update_iocb->sa_index, sp->handle);
+
+	sp->fcport->vha->qla_stats.control_requests++;
+}
+
+void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp)
+{
+	struct purex_entry_24xx *p = *pkt;
+	struct enode		*ptr;
+	int		sid;
+	u16 totlen;
+	struct purexevent	*purex;
+	struct scsi_qla_host *host = NULL;
+	int rc;
+	struct fc_port *fcport;
+	struct qla_els_pt_arg a;
+	be_id_t beid;
+
+	memset(&a, 0, sizeof(a));
+
+	a.els_opcode = ELS_AUTH_ELS;
+	a.nport_handle = p->nport_handle;
+	a.rx_xchg_address = p->rx_xchg_addr;
+	a.did.b.domain = p->s_id[2];
+	a.did.b.area   = p->s_id[1];
+	a.did.b.al_pa  = p->s_id[0];
+	a.tx_byte_count = a.tx_len = sizeof(struct fc_els_ls_rjt);
+	a.tx_addr = vha->hw->elsrej.cdma;
+	a.vp_idx = vha->vp_idx;
+	a.control_flags = EPD_ELS_RJT;
+	a.ox_id = le16_to_cpu(p->ox_id);
+
+	sid = p->s_id[0] | (p->s_id[1] << 8) | (p->s_id[2] << 16);
+
+	totlen = (le16_to_cpu(p->frame_size) & 0x0fff) - PURX_ELS_HEADER_SIZE;
+	if (le16_to_cpu(p->status_flags) & 0x8000) {
+		totlen = le16_to_cpu(p->trunc_frame_size);
+		qla_els_reject_iocb(vha, (*rsp)->qpair, &a);
+		__qla_consume_iocb(vha, pkt, rsp);
+		return;
+	}
+
+	if (totlen > ELS_MAX_PAYLOAD) {
+		ql_dbg(ql_dbg_edif, vha, 0x0910d,
+		    "%s WARNING: verbose ELS frame received (totlen=%x)\n",
+		    __func__, totlen);
+		qla_els_reject_iocb(vha, (*rsp)->qpair, &a);
+		__qla_consume_iocb(vha, pkt, rsp);
+		return;
+	}
+
+	if (!vha->hw->flags.edif_enabled) {
+		/* edif support not enabled */
+		ql_dbg(ql_dbg_edif, vha, 0x910e, "%s edif not enabled\n",
+		    __func__);
+		qla_els_reject_iocb(vha, (*rsp)->qpair, &a);
+		__qla_consume_iocb(vha, pkt, rsp);
+		return;
+	}
+
+	ptr = qla_enode_alloc(vha, N_PUREX);
+	if (!ptr) {
+		ql_dbg(ql_dbg_edif, vha, 0x09109,
+		    "WARNING: enode alloc failed for sid=%x\n",
+		    sid);
+		qla_els_reject_iocb(vha, (*rsp)->qpair, &a);
+		__qla_consume_iocb(vha, pkt, rsp);
+		return;
+	}
+
+	purex = &ptr->u.purexinfo;
+	purex->pur_info.pur_sid = a.did;
+	purex->pur_info.pur_bytes_rcvd = totlen;
+	purex->pur_info.pur_rx_xchg_address = le32_to_cpu(p->rx_xchg_addr);
+	purex->pur_info.pur_nphdl = le16_to_cpu(p->nport_handle);
+	purex->pur_info.pur_did.b.domain =  p->d_id[2];
+	purex->pur_info.pur_did.b.area =  p->d_id[1];
+	purex->pur_info.pur_did.b.al_pa =  p->d_id[0];
+	purex->pur_info.vp_idx = p->vp_idx;
+
+	a.sid = purex->pur_info.pur_did;
+
+	rc = __qla_copy_purex_to_buffer(vha, pkt, rsp, purex->msgp,
+		purex->msgp_len);
+	if (rc) {
+		qla_els_reject_iocb(vha, (*rsp)->qpair, &a);
+		qla_enode_free(vha, ptr);
+		return;
+	}
+	beid.al_pa = purex->pur_info.pur_did.b.al_pa;
+	beid.area   = purex->pur_info.pur_did.b.area;
+	beid.domain = purex->pur_info.pur_did.b.domain;
+	host = qla_find_host_by_d_id(vha, beid);
+	if (!host) {
+		ql_log(ql_log_fatal, vha, 0x508b,
+		    "%s Drop ELS due to unable to find host %06x\n",
+		    __func__, purex->pur_info.pur_did.b24);
+
+		qla_els_reject_iocb(vha, (*rsp)->qpair, &a);
+		qla_enode_free(vha, ptr);
+		return;
+	}
+
+	fcport = qla2x00_find_fcport_by_pid(host, &purex->pur_info.pur_sid);
+
+	if (DBELL_INACTIVE(vha)) {
+		ql_dbg(ql_dbg_edif, host, 0x0910c, "%s e_dbell.db_flags =%x %06x\n",
+		    __func__, host->e_dbell.db_flags,
+		    fcport ? fcport->d_id.b24 : 0);
+
+		qla_els_reject_iocb(host, (*rsp)->qpair, &a);
+		qla_enode_free(host, ptr);
+		return;
+	}
+
+	if (fcport && EDIF_SESSION_DOWN(fcport)) {
+		ql_dbg(ql_dbg_edif, host, 0x13b6,
+		    "%s terminate exchange. Send logo to 0x%x\n",
+		    __func__, a.did.b24);
+
+		a.tx_byte_count = a.tx_len = 0;
+		a.tx_addr = 0;
+		a.control_flags = EPD_RX_XCHG;  /* EPD_RX_XCHG = terminate cmd */
+		qla_els_reject_iocb(host, (*rsp)->qpair, &a);
+		qla_enode_free(host, ptr);
+		/* send logo to let remote port knows to tear down session */
+		fcport->send_els_logo = 1;
+		qlt_schedule_sess_for_deletion(fcport);
+		return;
+	}
+
+	/* add the local enode to the list */
+	qla_enode_add(host, ptr);
+
+	ql_dbg(ql_dbg_edif, host, 0x0910c,
+	    "%s COMPLETE purex->pur_info.pur_bytes_rcvd =%xh s:%06x -> d:%06x xchg=%xh\n",
+	    __func__, purex->pur_info.pur_bytes_rcvd, purex->pur_info.pur_sid.b24,
+	    purex->pur_info.pur_did.b24, purex->pur_info.pur_rx_xchg_address);
+
+	qla_edb_eventcreate(host, VND_CMD_AUTH_STATE_ELS_RCVD, sid, 0, NULL);
+}
+
+static uint16_t  qla_edif_get_sa_index_from_freepool(fc_port_t *fcport, int dir)
+{
+	struct scsi_qla_host *vha = fcport->vha;
+	struct qla_hw_data *ha = vha->hw;
+	void *sa_id_map;
+	unsigned long flags = 0;
+	u16 sa_index;
+
+	ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x3063,
+	    "%s: entry\n", __func__);
+
+	if (dir)
+		sa_id_map = ha->edif_tx_sa_id_map;
+	else
+		sa_id_map = ha->edif_rx_sa_id_map;
+
+	spin_lock_irqsave(&ha->sadb_fp_lock, flags);
+	sa_index = find_first_zero_bit(sa_id_map, EDIF_NUM_SA_INDEX);
+	if (sa_index >=  EDIF_NUM_SA_INDEX) {
+		spin_unlock_irqrestore(&ha->sadb_fp_lock, flags);
+		return INVALID_EDIF_SA_INDEX;
+	}
+	set_bit(sa_index, sa_id_map);
+	spin_unlock_irqrestore(&ha->sadb_fp_lock, flags);
+
+	if (dir)
+		sa_index += EDIF_TX_SA_INDEX_BASE;
+
+	ql_dbg(ql_dbg_edif, vha, 0x3063,
+	    "%s: index retrieved from free pool %d\n", __func__, sa_index);
+
+	return sa_index;
+}
+
+/* find an sadb entry for an nport_handle */
+static struct edif_sa_index_entry *
+qla_edif_sadb_find_sa_index_entry(uint16_t nport_handle,
+		struct list_head *sa_list)
+{
+	struct edif_sa_index_entry *entry;
+	struct edif_sa_index_entry *tentry;
+	struct list_head *indx_list = sa_list;
+
+	list_for_each_entry_safe(entry, tentry, indx_list, next) {
+		if (entry->handle == nport_handle)
+			return entry;
+	}
+	return NULL;
+}
+
+/* remove an sa_index from the nport_handle and return it to the free pool */
+static int qla_edif_sadb_delete_sa_index(fc_port_t *fcport, uint16_t nport_handle,
+		uint16_t sa_index)
+{
+	struct edif_sa_index_entry *entry;
+	struct list_head *sa_list;
+	int dir = (sa_index < EDIF_TX_SA_INDEX_BASE) ? 0 : 1;
+	int slot = 0;
+	int free_slot_count = 0;
+	scsi_qla_host_t *vha = fcport->vha;
+	struct qla_hw_data *ha = vha->hw;
+	unsigned long flags = 0;
+
+	ql_dbg(ql_dbg_edif, vha, 0x3063,
+	    "%s: entry\n", __func__);
+
+	if (dir)
+		sa_list = &ha->sadb_tx_index_list;
+	else
+		sa_list = &ha->sadb_rx_index_list;
+
+	entry = qla_edif_sadb_find_sa_index_entry(nport_handle, sa_list);
+	if (!entry) {
+		ql_dbg(ql_dbg_edif, vha, 0x3063,
+		    "%s: no entry found for nport_handle 0x%x\n",
+		    __func__, nport_handle);
+		return -1;
+	}
+
+	spin_lock_irqsave(&ha->sadb_lock, flags);
+	/*
+	 * each tx/rx direction has up to 2 sa indexes/slots. 1 slot for in flight traffic
+	 * the other is use at re-key time.
+	 */
+	for (slot = 0; slot < 2; slot++) {
+		if (entry->sa_pair[slot].sa_index == sa_index) {
+			entry->sa_pair[slot].sa_index = INVALID_EDIF_SA_INDEX;
+			entry->sa_pair[slot].spi = 0;
+			free_slot_count++;
+			qla_edif_add_sa_index_to_freepool(fcport, dir, sa_index);
+		} else if (entry->sa_pair[slot].sa_index == INVALID_EDIF_SA_INDEX) {
+			free_slot_count++;
+		}
+	}
+
+	if (free_slot_count == 2) {
+		list_del(&entry->next);
+		kfree(entry);
+	}
+	spin_unlock_irqrestore(&ha->sadb_lock, flags);
+
+	ql_dbg(ql_dbg_edif, vha, 0x3063,
+	    "%s: sa_index %d removed, free_slot_count: %d\n",
+	    __func__, sa_index, free_slot_count);
+
+	return 0;
+}
+
+void
+qla28xx_sa_update_iocb_entry(scsi_qla_host_t *v, struct req_que *req,
+	struct sa_update_28xx *pkt)
+{
+	const char *func = "SA_UPDATE_RESPONSE_IOCB";
+	srb_t *sp;
+	struct edif_sa_ctl *sa_ctl;
+	int old_sa_deleted = 1;
+	uint16_t nport_handle;
+	struct scsi_qla_host *vha;
+
+	sp = qla2x00_get_sp_from_handle(v, func, req, pkt);
+
+	if (!sp) {
+		ql_dbg(ql_dbg_edif, v, 0x3063,
+			"%s: no sp found for pkt\n", __func__);
+		return;
+	}
+	/* use sp->vha due to npiv */
+	vha = sp->vha;
+
+	switch (pkt->flags & (SA_FLAG_INVALIDATE | SA_FLAG_TX)) {
+	case 0:
+		ql_dbg(ql_dbg_edif, vha, 0x3063,
+		    "%s: EDIF SA UPDATE RX IOCB  vha: 0x%p  index: %d\n",
+		    __func__, vha, pkt->sa_index);
+		break;
+	case 1:
+		ql_dbg(ql_dbg_edif, vha, 0x3063,
+		    "%s: EDIF SA DELETE RX IOCB  vha: 0x%p  index: %d\n",
+		    __func__, vha, pkt->sa_index);
+		break;
+	case 2:
+		ql_dbg(ql_dbg_edif, vha, 0x3063,
+		    "%s: EDIF SA UPDATE TX IOCB  vha: 0x%p  index: %d\n",
+		    __func__, vha, pkt->sa_index);
+		break;
+	case 3:
+		ql_dbg(ql_dbg_edif, vha, 0x3063,
+		    "%s: EDIF SA DELETE TX IOCB  vha: 0x%p  index: %d\n",
+		    __func__, vha, pkt->sa_index);
+		break;
+	}
+
+	/*
+	 * dig the nport handle out of the iocb, fcport->loop_id can not be trusted
+	 * to be correct during cleanup sa_update iocbs.
+	 */
+	nport_handle = sp->fcport->loop_id;
+
+	ql_dbg(ql_dbg_edif, vha, 0x3063,
+	    "%s: %8phN comp status=%x old_sa_info=%x new_sa_info=%x lid %d, index=0x%x pkt_flags %xh hdl=%x\n",
+	    __func__, sp->fcport->port_name, pkt->u.comp_sts, pkt->old_sa_info, pkt->new_sa_info,
+	    nport_handle, pkt->sa_index, pkt->flags, sp->handle);
+
+	/* if rx delete, remove the timer */
+	if ((pkt->flags & (SA_FLAG_INVALIDATE | SA_FLAG_TX)) ==  SA_FLAG_INVALIDATE) {
+		struct edif_list_entry *edif_entry;
+
+		sp->fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE);
+
+		edif_entry = qla_edif_list_find_sa_index(sp->fcport, nport_handle);
+		if (edif_entry) {
+			ql_dbg(ql_dbg_edif, vha, 0x5033,
+			    "%s: removing edif_entry %p, new sa_index: 0x%x\n",
+			    __func__, edif_entry, pkt->sa_index);
+			qla_edif_list_delete_sa_index(sp->fcport, edif_entry);
+			del_timer(&edif_entry->timer);
+
+			ql_dbg(ql_dbg_edif, vha, 0x5033,
+			    "%s: releasing edif_entry %p, new sa_index: 0x%x\n",
+			    __func__, edif_entry, pkt->sa_index);
+
+			kfree(edif_entry);
+		}
+	}
+
+	/*
+	 * if this is a delete for either tx or rx, make sure it succeeded.
+	 * The new_sa_info field should be 0xffff on success
+	 */
+	if (pkt->flags & SA_FLAG_INVALIDATE)
+		old_sa_deleted = (le16_to_cpu(pkt->new_sa_info) == 0xffff) ? 1 : 0;
+
+	/* Process update and delete the same way */
+
+	/* If this is an sadb cleanup delete, bypass sending events to IPSEC */
+	if (sp->flags & SRB_EDIF_CLEANUP_DELETE) {
+		sp->fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE);
+		ql_dbg(ql_dbg_edif, vha, 0x3063,
+		    "%s: nph 0x%x, sa_index %d removed from fw\n",
+		    __func__, sp->fcport->loop_id, pkt->sa_index);
+
+	} else if ((pkt->entry_status == 0) && (pkt->u.comp_sts == 0) &&
+	    old_sa_deleted) {
+		/*
+		 * Note: Wa are only keeping track of latest SA,
+		 * so we know when we can start enableing encryption per I/O.
+		 * If all SA's get deleted, let FW reject the IOCB.
+
+		 * TODO: edif: don't set enabled here I think
+		 * TODO: edif: prli complete is where it should be set
+		 */
+		ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x3063,
+			"SA(%x)updated for s_id %02x%02x%02x\n",
+			pkt->new_sa_info,
+			pkt->port_id[2], pkt->port_id[1], pkt->port_id[0]);
+		sp->fcport->edif.enable = 1;
+		if (pkt->flags & SA_FLAG_TX) {
+			sp->fcport->edif.tx_sa_set = 1;
+			sp->fcport->edif.tx_sa_pending = 0;
+			qla_edb_eventcreate(vha, VND_CMD_AUTH_STATE_SAUPDATE_COMPL,
+				QL_VND_SA_STAT_SUCCESS,
+				QL_VND_TX_SA_KEY, sp->fcport);
+		} else {
+			sp->fcport->edif.rx_sa_set = 1;
+			sp->fcport->edif.rx_sa_pending = 0;
+			qla_edb_eventcreate(vha, VND_CMD_AUTH_STATE_SAUPDATE_COMPL,
+				QL_VND_SA_STAT_SUCCESS,
+				QL_VND_RX_SA_KEY, sp->fcport);
+		}
+	} else {
+		ql_dbg(ql_dbg_edif, vha, 0x3063,
+		    "%s: %8phN SA update FAILED: sa_index: %d, new_sa_info %d, %02x%02x%02x\n",
+		    __func__, sp->fcport->port_name, pkt->sa_index, pkt->new_sa_info,
+		    pkt->port_id[2], pkt->port_id[1], pkt->port_id[0]);
+
+		if (pkt->flags & SA_FLAG_TX)
+			qla_edb_eventcreate(vha, VND_CMD_AUTH_STATE_SAUPDATE_COMPL,
+				(le16_to_cpu(pkt->u.comp_sts) << 16) | QL_VND_SA_STAT_FAILED,
+				QL_VND_TX_SA_KEY, sp->fcport);
+		else
+			qla_edb_eventcreate(vha, VND_CMD_AUTH_STATE_SAUPDATE_COMPL,
+				(le16_to_cpu(pkt->u.comp_sts) << 16) | QL_VND_SA_STAT_FAILED,
+				QL_VND_RX_SA_KEY, sp->fcport);
+	}
+
+	/* for delete, release sa_ctl, sa_index */
+	if (pkt->flags & SA_FLAG_INVALIDATE) {
+		/* release the sa_ctl */
+		sa_ctl = qla_edif_find_sa_ctl_by_index(sp->fcport,
+		    le16_to_cpu(pkt->sa_index), (pkt->flags & SA_FLAG_TX));
+		if (sa_ctl &&
+		    qla_edif_find_sa_ctl_by_index(sp->fcport, sa_ctl->index,
+			(pkt->flags & SA_FLAG_TX)) != NULL) {
+			ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x3063,
+			    "%s: freeing sa_ctl for index %d\n",
+			    __func__, sa_ctl->index);
+			qla_edif_free_sa_ctl(sp->fcport, sa_ctl, sa_ctl->index);
+		} else {
+			ql_dbg(ql_dbg_edif, vha, 0x3063,
+			    "%s: sa_ctl NOT freed, sa_ctl: %p\n",
+			    __func__, sa_ctl);
+		}
+		ql_dbg(ql_dbg_edif, vha, 0x3063,
+		    "%s: freeing sa_index %d, nph: 0x%x\n",
+		    __func__, le16_to_cpu(pkt->sa_index), nport_handle);
+		qla_edif_sadb_delete_sa_index(sp->fcport, nport_handle,
+		    le16_to_cpu(pkt->sa_index));
+	/*
+	 * check for a failed sa_update and remove
+	 * the sadb entry.
+	 */
+	} else if (pkt->u.comp_sts) {
+		ql_dbg(ql_dbg_edif, vha, 0x3063,
+		    "%s: freeing sa_index %d, nph: 0x%x\n",
+		    __func__, pkt->sa_index, nport_handle);
+		qla_edif_sadb_delete_sa_index(sp->fcport, nport_handle,
+		    le16_to_cpu(pkt->sa_index));
+		switch (le16_to_cpu(pkt->u.comp_sts)) {
+		case CS_PORT_EDIF_UNAVAIL:
+		case CS_PORT_EDIF_LOGOUT:
+			qlt_schedule_sess_for_deletion(sp->fcport);
+			break;
+		default:
+			break;
+		}
+	}
+
+	sp->done(sp, 0);
+}
+
+/**
+ * qla28xx_start_scsi_edif() - Send a SCSI type 6 command to the ISP
+ * @sp: command to send to the ISP
+ *
+ * Return: non-zero if a failure occurred, else zero.
+ */
+int
+qla28xx_start_scsi_edif(srb_t *sp)
+{
+	int             nseg;
+	unsigned long   flags;
+	struct scsi_cmnd *cmd;
+	uint32_t        *clr_ptr;
+	uint32_t        index, i;
+	uint32_t        handle;
+	uint16_t        cnt;
+	int16_t        req_cnt;
+	uint16_t        tot_dsds;
+	__be32 *fcp_dl;
+	uint8_t additional_cdb_len;
+	struct ct6_dsd *ctx;
+	struct scsi_qla_host *vha = sp->vha;
+	struct qla_hw_data *ha = vha->hw;
+	struct cmd_type_6 *cmd_pkt;
+	struct dsd64	*cur_dsd;
+	uint8_t		avail_dsds = 0;
+	struct scatterlist *sg;
+	struct req_que *req = sp->qpair->req;
+	spinlock_t *lock = sp->qpair->qp_lock_ptr;
+
+	/* Setup device pointers. */
+	cmd = GET_CMD_SP(sp);
+
+	/* So we know we haven't pci_map'ed anything yet */
+	tot_dsds = 0;
+
+	/* Send marker if required */
+	if (vha->marker_needed != 0) {
+		if (qla2x00_marker(vha, sp->qpair, 0, 0, MK_SYNC_ALL) !=
+			QLA_SUCCESS) {
+			ql_log(ql_log_warn, vha, 0x300c,
+			    "qla2x00_marker failed for cmd=%p.\n", cmd);
+			return QLA_FUNCTION_FAILED;
+		}
+		vha->marker_needed = 0;
+	}
+
+	/* Acquire ring specific lock */
+	spin_lock_irqsave(lock, flags);
+
+	/* Check for room in outstanding command list. */
+	handle = req->current_outstanding_cmd;
+	for (index = 1; index < req->num_outstanding_cmds; index++) {
+		handle++;
+		if (handle == req->num_outstanding_cmds)
+			handle = 1;
+		if (!req->outstanding_cmds[handle])
+			break;
+	}
+	if (index == req->num_outstanding_cmds)
+		goto queuing_error;
+
+	/* Map the sg table so we have an accurate count of sg entries needed */
+	if (scsi_sg_count(cmd)) {
+		nseg = dma_map_sg(&ha->pdev->dev, scsi_sglist(cmd),
+		    scsi_sg_count(cmd), cmd->sc_data_direction);
+		if (unlikely(!nseg))
+			goto queuing_error;
+	} else {
+		nseg = 0;
+	}
+
+	tot_dsds = nseg;
+	req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
+
+	sp->iores.res_type = RESOURCE_INI;
+	sp->iores.iocb_cnt = req_cnt;
+	if (qla_get_iocbs(sp->qpair, &sp->iores))
+		goto queuing_error;
+
+	if (req->cnt < (req_cnt + 2)) {
+		cnt = IS_SHADOW_REG_CAPABLE(ha) ? *req->out_ptr :
+		    rd_reg_dword(req->req_q_out);
+		if (req->ring_index < cnt)
+			req->cnt = cnt - req->ring_index;
+		else
+			req->cnt = req->length -
+			    (req->ring_index - cnt);
+		if (req->cnt < (req_cnt + 2))
+			goto queuing_error;
+	}
+
+	ctx = sp->u.scmd.ct6_ctx =
+	    mempool_alloc(ha->ctx_mempool, GFP_ATOMIC);
+	if (!ctx) {
+		ql_log(ql_log_fatal, vha, 0x3010,
+		    "Failed to allocate ctx for cmd=%p.\n", cmd);
+		goto queuing_error;
+	}
+
+	memset(ctx, 0, sizeof(struct ct6_dsd));
+	ctx->fcp_cmnd = dma_pool_zalloc(ha->fcp_cmnd_dma_pool,
+	    GFP_ATOMIC, &ctx->fcp_cmnd_dma);
+	if (!ctx->fcp_cmnd) {
+		ql_log(ql_log_fatal, vha, 0x3011,
+		    "Failed to allocate fcp_cmnd for cmd=%p.\n", cmd);
+		goto queuing_error;
+	}
+
+	/* Initialize the DSD list and dma handle */
+	INIT_LIST_HEAD(&ctx->dsd_list);
+	ctx->dsd_use_cnt = 0;
+
+	if (cmd->cmd_len > 16) {
+		additional_cdb_len = cmd->cmd_len - 16;
+		if ((cmd->cmd_len % 4) != 0) {
+			/*
+			 * SCSI command bigger than 16 bytes must be
+			 * multiple of 4
+			 */
+			ql_log(ql_log_warn, vha, 0x3012,
+			    "scsi cmd len %d not multiple of 4 for cmd=%p.\n",
+			    cmd->cmd_len, cmd);
+			goto queuing_error_fcp_cmnd;
+		}
+		ctx->fcp_cmnd_len = 12 + cmd->cmd_len + 4;
+	} else {
+		additional_cdb_len = 0;
+		ctx->fcp_cmnd_len = 12 + 16 + 4;
+	}
+
+	cmd_pkt = (struct cmd_type_6 *)req->ring_ptr;
+	cmd_pkt->handle = make_handle(req->id, handle);
+
+	/*
+	 * Zero out remaining portion of packet.
+	 * tagged queuing modifier -- default is TSK_SIMPLE (0).
+	 */
+	clr_ptr = (uint32_t *)cmd_pkt + 2;
+	memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+	cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
+
+	/* No data transfer */
+	if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) {
+		cmd_pkt->byte_count = cpu_to_le32(0);
+		goto no_dsds;
+	}
+
+	/* Set transfer direction */
+	if (cmd->sc_data_direction == DMA_TO_DEVICE) {
+		cmd_pkt->control_flags = cpu_to_le16(CF_WRITE_DATA);
+		vha->qla_stats.output_bytes += scsi_bufflen(cmd);
+		vha->qla_stats.output_requests++;
+		sp->fcport->edif.tx_bytes += scsi_bufflen(cmd);
+	} else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
+		cmd_pkt->control_flags = cpu_to_le16(CF_READ_DATA);
+		vha->qla_stats.input_bytes += scsi_bufflen(cmd);
+		vha->qla_stats.input_requests++;
+		sp->fcport->edif.rx_bytes += scsi_bufflen(cmd);
+	}
+
+	cmd_pkt->control_flags |= cpu_to_le16(CF_EN_EDIF);
+	cmd_pkt->control_flags &= ~(cpu_to_le16(CF_NEW_SA));
+
+	/* One DSD is available in the Command Type 6 IOCB */
+	avail_dsds = 1;
+	cur_dsd = &cmd_pkt->fcp_dsd;
+
+	/* Load data segments */
+	scsi_for_each_sg(cmd, sg, tot_dsds, i) {
+		dma_addr_t      sle_dma;
+		cont_a64_entry_t *cont_pkt;
+
+		/* Allocate additional continuation packets? */
+		if (avail_dsds == 0) {
+			/*
+			 * Five DSDs are available in the Continuation
+			 * Type 1 IOCB.
+			 */
+			cont_pkt = qla2x00_prep_cont_type1_iocb(vha, req);
+			cur_dsd = cont_pkt->dsd;
+			avail_dsds = 5;
+		}
+
+		sle_dma = sg_dma_address(sg);
+		put_unaligned_le64(sle_dma, &cur_dsd->address);
+		cur_dsd->length = cpu_to_le32(sg_dma_len(sg));
+		cur_dsd++;
+		avail_dsds--;
+	}
+
+no_dsds:
+	/* Set NPORT-ID and LUN number*/
+	cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+	cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
+	cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
+	cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+	cmd_pkt->vp_index = sp->vha->vp_idx;
+
+	cmd_pkt->entry_type = COMMAND_TYPE_6;
+
+	/* Set total data segment count. */
+	cmd_pkt->entry_count = (uint8_t)req_cnt;
+
+	int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
+	host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
+
+	/* build FCP_CMND IU */
+	int_to_scsilun(cmd->device->lun, &ctx->fcp_cmnd->lun);
+	ctx->fcp_cmnd->additional_cdb_len = additional_cdb_len;
+
+	if (cmd->sc_data_direction == DMA_TO_DEVICE)
+		ctx->fcp_cmnd->additional_cdb_len |= 1;
+	else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
+		ctx->fcp_cmnd->additional_cdb_len |= 2;
+
+	/* Populate the FCP_PRIO. */
+	if (ha->flags.fcp_prio_enabled)
+		ctx->fcp_cmnd->task_attribute |=
+		    sp->fcport->fcp_prio << 3;
+
+	memcpy(ctx->fcp_cmnd->cdb, cmd->cmnd, cmd->cmd_len);
+
+	fcp_dl = (__be32 *)(ctx->fcp_cmnd->cdb + 16 +
+	    additional_cdb_len);
+	*fcp_dl = htonl((uint32_t)scsi_bufflen(cmd));
+
+	cmd_pkt->fcp_cmnd_dseg_len = cpu_to_le16(ctx->fcp_cmnd_len);
+	put_unaligned_le64(ctx->fcp_cmnd_dma, &cmd_pkt->fcp_cmnd_dseg_address);
+
+	sp->flags |= SRB_FCP_CMND_DMA_VALID;
+	cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd));
+	/* Set total data segment count. */
+	cmd_pkt->entry_count = (uint8_t)req_cnt;
+	cmd_pkt->entry_status = 0;
+
+	/* Build command packet. */
+	req->current_outstanding_cmd = handle;
+	req->outstanding_cmds[handle] = sp;
+	sp->handle = handle;
+	cmd->host_scribble = (unsigned char *)(unsigned long)handle;
+	req->cnt -= req_cnt;
+
+	/* Adjust ring index. */
+	wmb();
+	req->ring_index++;
+	if (req->ring_index == req->length) {
+		req->ring_index = 0;
+		req->ring_ptr = req->ring;
+	} else {
+		req->ring_ptr++;
+	}
+
+	sp->qpair->cmd_cnt++;
+	/* Set chip new ring index. */
+	wrt_reg_dword(req->req_q_in, req->ring_index);
+
+	spin_unlock_irqrestore(lock, flags);
+
+	return QLA_SUCCESS;
+
+queuing_error_fcp_cmnd:
+	dma_pool_free(ha->fcp_cmnd_dma_pool, ctx->fcp_cmnd, ctx->fcp_cmnd_dma);
+queuing_error:
+	if (tot_dsds)
+		scsi_dma_unmap(cmd);
+
+	if (sp->u.scmd.ct6_ctx) {
+		mempool_free(sp->u.scmd.ct6_ctx, ha->ctx_mempool);
+		sp->u.scmd.ct6_ctx = NULL;
+	}
+	qla_put_iocbs(sp->qpair, &sp->iores);
+	spin_unlock_irqrestore(lock, flags);
+
+	return QLA_FUNCTION_FAILED;
+}
+
+/**********************************************
+ * edif update/delete sa_index list functions *
+ **********************************************/
+
+/* clear the edif_indx_list for this port */
+void qla_edif_list_del(fc_port_t *fcport)
+{
+	struct edif_list_entry *indx_lst;
+	struct edif_list_entry *tindx_lst;
+	struct list_head *indx_list = &fcport->edif.edif_indx_list;
+	unsigned long flags = 0;
+
+	spin_lock_irqsave(&fcport->edif.indx_list_lock, flags);
+	list_for_each_entry_safe(indx_lst, tindx_lst, indx_list, next) {
+		list_del(&indx_lst->next);
+		kfree(indx_lst);
+	}
+	spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags);
+}
+
+/******************
+ * SADB functions *
+ ******************/
+
+/* allocate/retrieve an sa_index for a given spi */
+static uint16_t qla_edif_sadb_get_sa_index(fc_port_t *fcport,
+		struct qla_sa_update_frame *sa_frame)
+{
+	struct edif_sa_index_entry *entry;
+	struct list_head *sa_list;
+	uint16_t sa_index;
+	int dir = sa_frame->flags & SAU_FLG_TX;
+	int slot = 0;
+	int free_slot = -1;
+	scsi_qla_host_t *vha = fcport->vha;
+	struct qla_hw_data *ha = vha->hw;
+	unsigned long flags = 0;
+	uint16_t nport_handle = fcport->loop_id;
+
+	ql_dbg(ql_dbg_edif, vha, 0x3063,
+	    "%s: entry  fc_port: %p, nport_handle: 0x%x\n",
+	    __func__, fcport, nport_handle);
+
+	if (dir)
+		sa_list = &ha->sadb_tx_index_list;
+	else
+		sa_list = &ha->sadb_rx_index_list;
+
+	entry = qla_edif_sadb_find_sa_index_entry(nport_handle, sa_list);
+	if (!entry) {
+		if ((sa_frame->flags & (SAU_FLG_TX | SAU_FLG_INV)) == SAU_FLG_INV) {
+			ql_dbg(ql_dbg_edif, vha, 0x3063,
+			    "%s: rx delete request with no entry\n", __func__);
+			return RX_DELETE_NO_EDIF_SA_INDEX;
+		}
+
+		/* if there is no entry for this nport, add one */
+		entry = kzalloc((sizeof(struct edif_sa_index_entry)), GFP_ATOMIC);
+		if (!entry)
+			return INVALID_EDIF_SA_INDEX;
+
+		sa_index = qla_edif_get_sa_index_from_freepool(fcport, dir);
+		if (sa_index == INVALID_EDIF_SA_INDEX) {
+			kfree(entry);
+			return INVALID_EDIF_SA_INDEX;
+		}
+
+		INIT_LIST_HEAD(&entry->next);
+		entry->handle = nport_handle;
+		entry->fcport = fcport;
+		entry->sa_pair[0].spi = sa_frame->spi;
+		entry->sa_pair[0].sa_index = sa_index;
+		entry->sa_pair[1].spi = 0;
+		entry->sa_pair[1].sa_index = INVALID_EDIF_SA_INDEX;
+		spin_lock_irqsave(&ha->sadb_lock, flags);
+		list_add_tail(&entry->next, sa_list);
+		spin_unlock_irqrestore(&ha->sadb_lock, flags);
+		ql_dbg(ql_dbg_edif, vha, 0x3063,
+		    "%s: Created new sadb entry for nport_handle 0x%x, spi 0x%x, returning sa_index %d\n",
+		    __func__, nport_handle, sa_frame->spi, sa_index);
+
+		return sa_index;
+	}
+
+	spin_lock_irqsave(&ha->sadb_lock, flags);
+
+	/* see if we already have an entry for this spi */
+	for (slot = 0; slot < 2; slot++) {
+		if (entry->sa_pair[slot].sa_index == INVALID_EDIF_SA_INDEX) {
+			free_slot = slot;
+		} else {
+			if (entry->sa_pair[slot].spi == sa_frame->spi) {
+				spin_unlock_irqrestore(&ha->sadb_lock, flags);
+				ql_dbg(ql_dbg_edif, vha, 0x3063,
+				    "%s: sadb slot %d entry for lid 0x%x, spi 0x%x found, sa_index %d\n",
+				    __func__, slot, entry->handle, sa_frame->spi,
+				    entry->sa_pair[slot].sa_index);
+				return entry->sa_pair[slot].sa_index;
+			}
+		}
+	}
+	spin_unlock_irqrestore(&ha->sadb_lock, flags);
+
+	/* both slots are used */
+	if (free_slot == -1) {
+		ql_dbg(ql_dbg_edif, vha, 0x3063,
+		    "%s: WARNING: No free slots in sadb for nport_handle 0x%x, spi: 0x%x\n",
+		    __func__, entry->handle, sa_frame->spi);
+		ql_dbg(ql_dbg_edif, vha, 0x3063,
+		    "%s: Slot 0  spi: 0x%x  sa_index: %d,  Slot 1  spi: 0x%x  sa_index: %d\n",
+		    __func__, entry->sa_pair[0].spi, entry->sa_pair[0].sa_index,
+		    entry->sa_pair[1].spi, entry->sa_pair[1].sa_index);
+
+		return INVALID_EDIF_SA_INDEX;
+	}
+
+	/* there is at least one free slot, use it */
+	sa_index = qla_edif_get_sa_index_from_freepool(fcport, dir);
+	if (sa_index == INVALID_EDIF_SA_INDEX) {
+		ql_dbg(ql_dbg_edif, fcport->vha, 0x3063,
+		    "%s: empty freepool!!\n", __func__);
+		return INVALID_EDIF_SA_INDEX;
+	}
+
+	spin_lock_irqsave(&ha->sadb_lock, flags);
+	entry->sa_pair[free_slot].spi = sa_frame->spi;
+	entry->sa_pair[free_slot].sa_index = sa_index;
+	spin_unlock_irqrestore(&ha->sadb_lock, flags);
+	ql_dbg(ql_dbg_edif, fcport->vha, 0x3063,
+	    "%s: sadb slot %d entry for nport_handle 0x%x, spi 0x%x added, returning sa_index %d\n",
+	    __func__, free_slot, entry->handle, sa_frame->spi, sa_index);
+
+	return sa_index;
+}
+
+/* release any sadb entries -- only done at teardown */
+void qla_edif_sadb_release(struct qla_hw_data *ha)
+{
+	struct edif_sa_index_entry *entry, *tmp;
+
+	list_for_each_entry_safe(entry, tmp, &ha->sadb_rx_index_list, next) {
+		list_del(&entry->next);
+		kfree(entry);
+	}
+
+	list_for_each_entry_safe(entry, tmp, &ha->sadb_tx_index_list, next) {
+		list_del(&entry->next);
+		kfree(entry);
+	}
+}
+
+/**************************
+ * sadb freepool functions
+ **************************/
+
+/* build the rx and tx sa_index free pools -- only done at fcport init */
+int qla_edif_sadb_build_free_pool(struct qla_hw_data *ha)
+{
+	ha->edif_tx_sa_id_map =
+	    kcalloc(BITS_TO_LONGS(EDIF_NUM_SA_INDEX), sizeof(long), GFP_KERNEL);
+
+	if (!ha->edif_tx_sa_id_map) {
+		ql_log_pci(ql_log_fatal, ha->pdev, 0x0009,
+		    "Unable to allocate memory for sadb tx.\n");
+		return -ENOMEM;
+	}
+
+	ha->edif_rx_sa_id_map =
+	    kcalloc(BITS_TO_LONGS(EDIF_NUM_SA_INDEX), sizeof(long), GFP_KERNEL);
+	if (!ha->edif_rx_sa_id_map) {
+		kfree(ha->edif_tx_sa_id_map);
+		ha->edif_tx_sa_id_map = NULL;
+		ql_log_pci(ql_log_fatal, ha->pdev, 0x0009,
+		    "Unable to allocate memory for sadb rx.\n");
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+/* release the free pool - only done during fcport teardown */
+void qla_edif_sadb_release_free_pool(struct qla_hw_data *ha)
+{
+	kfree(ha->edif_tx_sa_id_map);
+	ha->edif_tx_sa_id_map = NULL;
+	kfree(ha->edif_rx_sa_id_map);
+	ha->edif_rx_sa_id_map = NULL;
+}
+
+static void __chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
+		fc_port_t *fcport, uint32_t handle, uint16_t sa_index)
+{
+	struct edif_list_entry *edif_entry;
+	struct edif_sa_ctl *sa_ctl;
+	uint16_t delete_sa_index = INVALID_EDIF_SA_INDEX;
+	unsigned long flags = 0;
+	uint16_t nport_handle = fcport->loop_id;
+	uint16_t cached_nport_handle;
+
+	spin_lock_irqsave(&fcport->edif.indx_list_lock, flags);
+	edif_entry = qla_edif_list_find_sa_index(fcport, nport_handle);
+	if (!edif_entry) {
+		spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags);
+		return;		/* no pending delete for this handle */
+	}
+
+	/*
+	 * check for no pending delete for this index or iocb does not
+	 * match rx sa_index
+	 */
+	if (edif_entry->delete_sa_index == INVALID_EDIF_SA_INDEX ||
+	    edif_entry->update_sa_index != sa_index) {
+		spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags);
+		return;
+	}
+
+	/*
+	 * wait until we have seen at least EDIF_DELAY_COUNT transfers before
+	 * queueing RX delete
+	 */
+	if (edif_entry->count++ < EDIF_RX_DELETE_FILTER_COUNT) {
+		spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags);
+		return;
+	}
+
+	ql_dbg(ql_dbg_edif, vha, 0x5033,
+	    "%s: invalidating delete_sa_index,  update_sa_index: 0x%x sa_index: 0x%x, delete_sa_index: 0x%x\n",
+	    __func__, edif_entry->update_sa_index, sa_index, edif_entry->delete_sa_index);
+
+	delete_sa_index = edif_entry->delete_sa_index;
+	edif_entry->delete_sa_index = INVALID_EDIF_SA_INDEX;
+	cached_nport_handle = edif_entry->handle;
+	spin_unlock_irqrestore(&fcport->edif.indx_list_lock, flags);
+
+	/* sanity check on the nport handle */
+	if (nport_handle != cached_nport_handle) {
+		ql_dbg(ql_dbg_edif, vha, 0x3063,
+		    "%s: POST SA DELETE nport_handle mismatch: lid: 0x%x, edif_entry nph: 0x%x\n",
+		    __func__, nport_handle, cached_nport_handle);
+	}
+
+	/* find the sa_ctl for the delete and schedule the delete */
+	sa_ctl = qla_edif_find_sa_ctl_by_index(fcport, delete_sa_index, 0);
+	if (sa_ctl) {
+		ql_dbg(ql_dbg_edif, vha, 0x3063,
+		    "%s: POST SA DELETE sa_ctl: %p, index recvd %d\n",
+		    __func__, sa_ctl, sa_index);
+		ql_dbg(ql_dbg_edif, vha, 0x3063,
+		    "delete index %d, update index: %d, nport handle: 0x%x, handle: 0x%x\n",
+		    delete_sa_index,
+		    edif_entry->update_sa_index, nport_handle, handle);
+
+		sa_ctl->flags = EDIF_SA_CTL_FLG_DEL;
+		set_bit(EDIF_SA_CTL_REPL, &sa_ctl->state);
+		qla_post_sa_replace_work(fcport->vha, fcport,
+		    nport_handle, sa_ctl);
+	} else {
+		ql_dbg(ql_dbg_edif, vha, 0x3063,
+		    "%s: POST SA DELETE sa_ctl not found for delete_sa_index: %d\n",
+		    __func__, delete_sa_index);
+	}
+}
+
+void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
+		srb_t *sp, struct sts_entry_24xx *sts24)
+{
+	fc_port_t *fcport = sp->fcport;
+	/* sa_index used by this iocb */
+	struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+	uint32_t handle;
+
+	handle = (uint32_t)LSW(sts24->handle);
+
+	/* find out if this status iosb is for a scsi read */
+	if (cmd->sc_data_direction != DMA_FROM_DEVICE)
+		return;
+
+	return __chk_edif_rx_sa_delete_pending(vha, fcport, handle,
+	   le16_to_cpu(sts24->edif_sa_index));
+}
+
+void qlt_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, fc_port_t *fcport,
+		struct ctio7_from_24xx *pkt)
+{
+	__chk_edif_rx_sa_delete_pending(vha, fcport,
+	    pkt->handle, le16_to_cpu(pkt->edif_sa_index));
+}
+
+static void qla_parse_auth_els_ctl(struct srb *sp)
+{
+	struct qla_els_pt_arg *a = &sp->u.bsg_cmd.u.els_arg;
+	BSG_JOB_TYPE *bsg_job = sp->u.bsg_cmd.bsg_job;
+	struct fc_bsg_request *request = bsg_job->request;
+	struct qla_bsg_auth_els_request *p =
+	    (struct qla_bsg_auth_els_request *)bsg_job->request;
+
+	a->tx_len = a->tx_byte_count = sp->remap.req.len;
+	a->tx_addr = sp->remap.req.dma;
+	a->rx_len = a->rx_byte_count = sp->remap.rsp.len;
+	a->rx_addr = sp->remap.rsp.dma;
+
+	if (p->e.sub_cmd == SEND_ELS_REPLY) {
+		a->control_flags = p->e.extra_control_flags << 13;
+		a->rx_xchg_address = cpu_to_le32(p->e.extra_rx_xchg_address);
+		if (p->e.extra_control_flags == BSG_CTL_FLAG_LS_ACC)
+			a->els_opcode = ELS_LS_ACC;
+		else if (p->e.extra_control_flags == BSG_CTL_FLAG_LS_RJT)
+			a->els_opcode = ELS_LS_RJT;
+	}
+	a->did = sp->fcport->d_id;
+	a->els_opcode =  request->rqst_data.h_els.command_code;
+	a->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+	a->vp_idx = sp->vha->vp_idx;
+}
+
+int qla_edif_process_els(scsi_qla_host_t *vha, BSG_JOB_TYPE *bsg_job)
+{
+	struct fc_bsg_request *bsg_request = bsg_job->request;
+	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
+	fc_port_t *fcport = NULL;
+	struct qla_hw_data *ha = vha->hw;
+	srb_t *sp;
+	int rval =  (DID_ERROR << 16), cnt;
+	port_id_t d_id;
+	struct qla_bsg_auth_els_request *p =
+	    (struct qla_bsg_auth_els_request *)bsg_job->request;
+	struct qla_bsg_auth_els_reply *rpl =
+	    (struct qla_bsg_auth_els_reply *)bsg_job->reply;
+
+	rpl->version = EDIF_VERSION1;
+
+	d_id.b.al_pa = bsg_request->rqst_data.h_els.port_id[2];
+	d_id.b.area = bsg_request->rqst_data.h_els.port_id[1];
+	d_id.b.domain = bsg_request->rqst_data.h_els.port_id[0];
+
+	/* find matching d_id in fcport list */
+	fcport = qla2x00_find_fcport_by_pid(vha, &d_id);
+	if (!fcport) {
+		ql_dbg(ql_dbg_edif, vha, 0x911a,
+		    "%s fcport not find online portid=%06x.\n",
+		    __func__, d_id.b24);
+		SET_DID_STATUS(bsg_reply->result, DID_ERROR);
+		return -EIO;
+	}
+
+	if (qla_bsg_check(vha, bsg_job, fcport))
+		return 0;
+
+	if (EDIF_SESS_DELETE(fcport)) {
+		ql_dbg(ql_dbg_edif, vha, 0x910d,
+		    "%s ELS code %x, no loop id.\n", __func__,
+		    bsg_request->rqst_data.r_els.els_code);
+		SET_DID_STATUS(bsg_reply->result, DID_BAD_TARGET);
+		return -ENXIO;
+	}
+
+	if (!vha->flags.online) {
+		ql_log(ql_log_warn, vha, 0x7005, "Host not online.\n");
+		SET_DID_STATUS(bsg_reply->result, DID_BAD_TARGET);
+		rval = -EIO;
+		goto done;
+	}
+
+	/* pass through is supported only for ISP 4Gb or higher */
+	if (!IS_FWI2_CAPABLE(ha)) {
+		ql_dbg(ql_dbg_user, vha, 0x7001,
+		    "ELS passthru not supported for ISP23xx based adapters.\n");
+		SET_DID_STATUS(bsg_reply->result, DID_BAD_TARGET);
+		rval = -EPERM;
+		goto done;
+	}
+
+	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
+	if (!sp) {
+		ql_dbg(ql_dbg_user, vha, 0x7004,
+		    "Failed get sp pid=%06x\n", fcport->d_id.b24);
+		rval = -ENOMEM;
+		SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY);
+		goto done;
+	}
+
+	sp->remap.req.len = bsg_job->request_payload.payload_len;
+	sp->remap.req.buf = dma_pool_alloc(ha->purex_dma_pool,
+	    GFP_KERNEL, &sp->remap.req.dma);
+	if (!sp->remap.req.buf) {
+		ql_dbg(ql_dbg_user, vha, 0x7005,
+		    "Failed allocate request dma len=%x\n",
+		    bsg_job->request_payload.payload_len);
+		rval = -ENOMEM;
+		SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY);
+		goto done_free_sp;
+	}
+
+	sp->remap.rsp.len = bsg_job->reply_payload.payload_len;
+	sp->remap.rsp.buf = dma_pool_alloc(ha->purex_dma_pool,
+	    GFP_KERNEL, &sp->remap.rsp.dma);
+	if (!sp->remap.rsp.buf) {
+		ql_dbg(ql_dbg_user, vha, 0x7006,
+		    "Failed allocate response dma len=%x\n",
+		    bsg_job->reply_payload.payload_len);
+		rval = -ENOMEM;
+		SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY);
+		goto done_free_remap_req;
+	}
+	sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+	    bsg_job->request_payload.sg_cnt, sp->remap.req.buf,
+	    sp->remap.req.len);
+	sp->remap.remapped = true;
+
+	sp->type = SRB_ELS_CMD_HST_NOLOGIN;
+	sp->name = "SPCN_BSG_HST_NOLOGIN";
+	sp->u.bsg_cmd.bsg_job = bsg_job;
+	qla_parse_auth_els_ctl(sp);
+
+	sp->free = qla2x00_bsg_sp_free;
+	sp->done = qla2x00_bsg_job_done;
+
+	cnt = 0;
+retry:
+	rval = qla2x00_start_sp(sp);
+	switch (rval) {
+	case QLA_SUCCESS:
+		ql_dbg(ql_dbg_edif, vha, 0x700a,
+		       "%s %s %8phN xchg %x ctlflag %x hdl %x reqlen %xh bsg ptr %p\n",
+		       __func__, sc_to_str(p->e.sub_cmd), fcport->port_name,
+		       p->e.extra_rx_xchg_address, p->e.extra_control_flags,
+		       sp->handle, sp->remap.req.len, bsg_job);
+		break;
+	case EAGAIN:
+		msleep(EDIF_MSLEEP_INTERVAL);
+		cnt++;
+		if (cnt < EDIF_RETRY_COUNT)
+			goto retry;
+		fallthrough;
+	default:
+		ql_log(ql_log_warn, vha, 0x700e,
+		    "%s qla2x00_start_sp failed = %d\n", __func__, rval);
+		SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY);
+		rval = -EIO;
+		goto done_free_remap_rsp;
+	}
+	return rval;
+
+done_free_remap_rsp:
+	dma_pool_free(ha->purex_dma_pool, sp->remap.rsp.buf,
+	    sp->remap.rsp.dma);
+done_free_remap_req:
+	dma_pool_free(ha->purex_dma_pool, sp->remap.req.buf,
+	    sp->remap.req.dma);
+done_free_sp:
+	qla2x00_rel_sp(sp);
+
+done:
+	return rval;
+}
+
+void qla_edif_sess_down(struct scsi_qla_host *vha, struct fc_port *sess)
+{
+	u16 cnt = 0;
+
+	if (sess->edif.app_sess_online && DBELL_ACTIVE(vha)) {
+		ql_dbg(ql_dbg_disc, vha, 0xf09c,
+			"%s: sess %8phN send port_offline event\n",
+			__func__, sess->port_name);
+		sess->edif.app_sess_online = 0;
+		sess->edif.sess_down_acked = 0;
+		qla_edb_eventcreate(vha, VND_CMD_AUTH_STATE_SESSION_SHUTDOWN,
+		    sess->d_id.b24, 0, sess);
+		qla2x00_post_aen_work(vha, FCH_EVT_PORT_OFFLINE, sess->d_id.b24);
+
+		while (!READ_ONCE(sess->edif.sess_down_acked) &&
+		       !test_bit(VPORT_DELETE, &vha->dpc_flags)) {
+			msleep(100);
+			cnt++;
+			if (cnt > 100)
+				break;
+		}
+		sess->edif.sess_down_acked = 0;
+		ql_dbg(ql_dbg_disc, vha, 0xf09c,
+		       "%s: sess %8phN port_offline event completed\n",
+		       __func__, sess->port_name);
+	}
+}
+
+void qla_edif_clear_appdata(struct scsi_qla_host *vha, struct fc_port *fcport)
+{
+	if (!(fcport->flags & FCF_FCSP_DEVICE))
+		return;
+
+	qla_edb_clear(vha, fcport->d_id);
+	qla_enode_clear(vha, fcport->d_id);
+}
diff --git a/scst/qla2x00t-32gbit/qla_edif.h b/scst/qla2x00t-32gbit/qla_edif.h
new file mode 100644
index 0000000..e0f721f
--- /dev/null
+++ b/scst/qla2x00t-32gbit/qla_edif.h
@@ -0,0 +1,156 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Marvell Fibre Channel HBA Driver
+ * Copyright (c)  2021    Marvell
+ */
+#ifndef __QLA_EDIF_H
+#define __QLA_EDIF_H
+
+struct qla_scsi_host;
+#define EDIF_APP_ID 0x73730001
+
+#define EDIF_MAX_INDEX	2048
+struct edif_sa_ctl {
+	struct list_head next;
+	uint16_t	del_index;
+	uint16_t	index;
+	uint16_t	slot;
+	uint16_t	flags;
+#define	EDIF_SA_CTL_FLG_REPL		BIT_0
+#define	EDIF_SA_CTL_FLG_DEL		BIT_1
+#define EDIF_SA_CTL_FLG_CLEANUP_DEL BIT_4
+	// Invalidate Index bit and mirrors QLA_SA_UPDATE_FLAGS_DELETE
+	unsigned long   state;
+#define EDIF_SA_CTL_USED	1	/* Active Sa update  */
+#define EDIF_SA_CTL_PEND	2	/* Waiting for slot */
+#define EDIF_SA_CTL_REPL	3	/* Active Replace and Delete */
+#define EDIF_SA_CTL_DEL		4	/* Delete Pending */
+	struct fc_port	*fcport;
+#ifndef NEW_LIBFC_API
+	struct fc_bsg_job *bsg_job;
+#else
+	struct bsg_job *bsg_job;
+#endif
+	struct qla_sa_update_frame sa_frame;
+};
+
+enum enode_flags_t {
+	ENODE_ACTIVE = 0x1,
+};
+
+struct pur_core {
+	enum enode_flags_t	enode_flags;
+	spinlock_t		pur_lock;
+	struct  list_head	head;
+};
+
+enum db_flags_t {
+	EDB_ACTIVE = BIT_0,
+};
+
+#define DBELL_ACTIVE(_v) (_v->e_dbell.db_flags & EDB_ACTIVE)
+#define DBELL_INACTIVE(_v) (!(_v->e_dbell.db_flags & EDB_ACTIVE))
+
+struct edif_dbell {
+	enum db_flags_t		db_flags;
+	spinlock_t		db_lock;
+	struct  list_head	head;
+#ifndef NEW_LIBFC_API
+	struct fc_bsg_job *dbell_bsg_job;
+#else
+	struct bsg_job *dbell_bsg_job;
+#endif
+	unsigned long bsg_expire;
+};
+
+#define SA_UPDATE_IOCB_TYPE            0x71    /* Security Association Update IOCB entry */
+struct sa_update_28xx {
+	uint8_t entry_type;             /* Entry type. */
+	uint8_t entry_count;            /* Entry count. */
+	uint8_t sys_define;             /* System Defined. */
+	uint8_t entry_status;           /* Entry Status. */
+
+	uint32_t handle;                /* IOCB System handle. */
+
+	union {
+		__le16 nport_handle;  /* in: N_PORT handle. */
+		__le16 comp_sts;              /* out: completion status */
+#define CS_PORT_EDIF_UNAVAIL	0x28
+#define CS_PORT_EDIF_LOGOUT	0x29
+#define CS_PORT_EDIF_SUPP_NOT_RDY 0x64
+#define CS_PORT_EDIF_INV_REQ      0x66
+	} u;
+	uint8_t vp_index;
+	uint8_t reserved_1;
+	uint8_t port_id[3];
+	uint8_t flags;
+#define SA_FLAG_INVALIDATE BIT_0
+#define SA_FLAG_TX	   BIT_1 // 1=tx, 0=rx
+
+	uint8_t sa_key[32];     /* 256 bit key */
+	__le32 salt;
+	__le32 spi;
+	uint8_t sa_control;
+#define SA_CNTL_ENC_FCSP        (1 << 3)
+#define SA_CNTL_ENC_OPD         (2 << 3)
+#define SA_CNTL_ENC_MSK         (3 << 3)  // mask bits 4,3
+#define SA_CNTL_AES_GMAC	(1 << 2)
+#define SA_CNTL_KEY256          (2 << 0)
+#define SA_CNTL_KEY128          0
+
+	uint8_t reserved_2;
+	__le16 sa_index;   // reserve: bit 11-15
+	__le16 old_sa_info;
+	__le16 new_sa_info;
+};
+
+#define        NUM_ENTRIES     256
+#define        PUR_GET         1
+
+struct dinfo {
+	int		nodecnt;
+	int		lstate;
+};
+
+struct pur_ninfo {
+	port_id_t       pur_sid;
+	port_id_t	pur_did;
+	uint8_t		vp_idx;
+	short           pur_bytes_rcvd;
+	unsigned short  pur_nphdl;
+	unsigned int    pur_rx_xchg_address;
+};
+
+struct purexevent {
+	struct  pur_ninfo	pur_info;
+	unsigned char		*msgp;
+	u32			msgp_len;
+};
+
+#define	N_UNDEF		0
+#define	N_PUREX		1
+struct enode {
+	struct list_head	list;
+	struct dinfo		dinfo;
+	uint32_t		ntype;
+	union {
+		struct purexevent	purexinfo;
+	} u;
+};
+
+#define RX_ELS_SIZE (roundup(sizeof(struct enode) + ELS_MAX_PAYLOAD, SMP_CACHE_BYTES))
+
+#define EDIF_SESSION_DOWN(_s) \
+	(qla_ini_mode_enabled(_s->vha) && (_s->disc_state == DSC_DELETE_PEND || \
+	 _s->disc_state == DSC_DELETED || \
+	 !_s->edif.app_sess_online))
+
+#define EDIF_NEGOTIATION_PENDING(_fcport) \
+	(DBELL_ACTIVE(_fcport->vha) && \
+	 (_fcport->disc_state == DSC_LOGIN_AUTH_PEND))
+
+#define EDIF_SESS_DELETE(_s) \
+	(qla_ini_mode_enabled(_s->vha) && (_s->disc_state == DSC_DELETE_PEND || \
+	 _s->disc_state == DSC_DELETED))
+
+#endif	/* __QLA_EDIF_H */
diff --git a/scst/qla2x00t-32gbit/qla_edif_bsg.h b/scst/qla2x00t-32gbit/qla_edif_bsg.h
new file mode 100644
index 0000000..0931f4e
--- /dev/null
+++ b/scst/qla2x00t-32gbit/qla_edif_bsg.h
@@ -0,0 +1,258 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Marvell Fibre Channel HBA Driver
+ * Copyright (C)  2018-	    Marvell
+ *
+ */
+#ifndef __QLA_EDIF_BSG_H
+#define __QLA_EDIF_BSG_H
+
+#define EDIF_VERSION1 1
+
+/* BSG Vendor specific commands */
+#define	ELS_MAX_PAYLOAD		2112
+#ifndef	WWN_SIZE
+#define WWN_SIZE		8
+#endif
+#define VND_CMD_APP_RESERVED_SIZE	28
+#define VND_CMD_PAD_SIZE                3
+enum auth_els_sub_cmd {
+	SEND_ELS = 0,
+	SEND_ELS_REPLY,
+	PULL_ELS,
+};
+
+struct extra_auth_els {
+	enum auth_els_sub_cmd sub_cmd;
+	uint32_t        extra_rx_xchg_address;
+	uint8_t         extra_control_flags;
+#define BSG_CTL_FLAG_INIT       0
+#define BSG_CTL_FLAG_LS_ACC     1
+#define BSG_CTL_FLAG_LS_RJT     2
+#define BSG_CTL_FLAG_TRM        3
+	uint8_t		version;
+	uint8_t		pad[2];
+	uint8_t		reserved[VND_CMD_APP_RESERVED_SIZE];
+} __packed;
+
+struct qla_bsg_auth_els_request {
+	struct fc_bsg_request r;
+	struct extra_auth_els e;
+};
+
+struct qla_bsg_auth_els_reply {
+	struct fc_bsg_reply r;
+	uint32_t rx_xchg_address;
+	uint8_t version;
+	uint8_t pad[VND_CMD_PAD_SIZE];
+	uint8_t reserved[VND_CMD_APP_RESERVED_SIZE];
+};
+
+struct app_id {
+	int		app_vid;
+	uint8_t		version;
+	uint8_t		pad[VND_CMD_PAD_SIZE];
+	uint8_t		reserved[VND_CMD_APP_RESERVED_SIZE];
+} __packed;
+
+struct app_start_reply {
+	uint32_t	host_support_edif;
+	uint32_t	edif_enode_active;
+	uint32_t	edif_edb_active;
+	uint8_t		version;
+	uint8_t		pad[VND_CMD_PAD_SIZE];
+	uint8_t		reserved[VND_CMD_APP_RESERVED_SIZE];
+} __packed;
+
+struct app_start {
+	struct app_id	app_info;
+	uint8_t         app_start_flags;
+	uint8_t		version;
+	uint8_t		pad[2];
+	uint8_t		reserved[VND_CMD_APP_RESERVED_SIZE];
+} __packed;
+
+struct app_stop {
+	struct app_id	app_info;
+	uint8_t		version;
+	uint8_t		pad[VND_CMD_PAD_SIZE];
+	uint8_t		reserved[VND_CMD_APP_RESERVED_SIZE];
+} __packed;
+
+struct app_plogi_reply {
+	uint32_t	prli_status;
+	uint8_t		version;
+	uint8_t		pad[VND_CMD_PAD_SIZE];
+	uint8_t		reserved[VND_CMD_APP_RESERVED_SIZE];
+} __packed;
+
+struct app_pinfo_req {
+	struct app_id app_info;
+	uint8_t	 num_ports;
+	port_id_t remote_pid;
+	uint8_t		version;
+	uint8_t		pad[VND_CMD_PAD_SIZE];
+	uint8_t		reserved[VND_CMD_APP_RESERVED_SIZE];
+} __packed;
+
+struct app_pinfo {
+	port_id_t remote_pid;
+	uint8_t	remote_wwpn[WWN_SIZE];
+	uint8_t	remote_type;
+#define	VND_CMD_RTYPE_UNKNOWN		0
+#define	VND_CMD_RTYPE_TARGET		1
+#define	VND_CMD_RTYPE_INITIATOR		2
+	uint8_t	remote_state;
+	uint8_t	auth_state;
+	uint8_t	version;
+	uint8_t	pad[VND_CMD_PAD_SIZE];
+	uint8_t	reserved[VND_CMD_APP_RESERVED_SIZE];
+} __packed;
+
+/* AUTH States */
+#define	VND_CMD_AUTH_STATE_UNDEF	0
+#define	VND_CMD_AUTH_STATE_SESSION_SHUTDOWN	1
+#define	VND_CMD_AUTH_STATE_NEEDED	2
+#define	VND_CMD_AUTH_STATE_ELS_RCVD	3
+#define	VND_CMD_AUTH_STATE_SAUPDATE_COMPL 4
+
+struct app_pinfo_reply {
+	uint8_t		port_count;
+	uint8_t		version;
+	uint8_t		pad[VND_CMD_PAD_SIZE];
+	uint8_t		reserved[VND_CMD_APP_RESERVED_SIZE];
+	struct app_pinfo ports[];
+} __packed;
+
+struct app_sinfo_req {
+	struct app_id	app_info;
+	uint8_t		num_ports;
+	uint8_t		version;
+	uint8_t		pad[VND_CMD_PAD_SIZE];
+	uint8_t		reserved[VND_CMD_APP_RESERVED_SIZE];
+} __packed;
+
+struct app_sinfo {
+	uint8_t	remote_wwpn[WWN_SIZE];
+	int64_t	rekey_count;
+	uint8_t	rekey_mode;
+	int64_t	tx_bytes;
+	int64_t	rx_bytes;
+} __packed;
+
+struct app_stats_reply {
+	uint8_t		elem_count;
+	uint8_t		version;
+	uint8_t		pad[VND_CMD_PAD_SIZE];
+	uint8_t		reserved[VND_CMD_APP_RESERVED_SIZE];
+	struct app_sinfo elem[];
+} __packed;
+
+struct qla_sa_update_frame {
+	struct app_id	app_info;
+	uint16_t	flags;
+#define SAU_FLG_INV		0x01	/* delete key */
+#define SAU_FLG_TX		0x02	/* 1=tx, 0 = rx */
+#define SAU_FLG_FORCE_DELETE	0x08
+#define SAU_FLG_GMAC_MODE	0x20	/*
+					 * GMAC mode is cleartext for the IO
+					 * (i.e. NULL encryption)
+					 */
+#define SAU_FLG_KEY128          0x40
+#define SAU_FLG_KEY256          0x80
+	uint16_t        fast_sa_index:10,
+			reserved:6;
+	uint32_t	salt;
+	uint32_t	spi;
+	uint8_t		sa_key[32];
+	uint8_t		node_name[WWN_SIZE];
+	uint8_t		port_name[WWN_SIZE];
+	port_id_t	port_id;
+	uint8_t		version;
+	uint8_t		pad[VND_CMD_PAD_SIZE];
+	uint8_t		reserved2[VND_CMD_APP_RESERVED_SIZE];
+} __packed;
+
+#define	QL_VND_SC_UNDEF		0
+#define	QL_VND_SC_SA_UPDATE	1
+#define	QL_VND_SC_APP_START	2
+#define	QL_VND_SC_APP_STOP	3
+#define	QL_VND_SC_AUTH_OK	4
+#define	QL_VND_SC_AUTH_FAIL	5
+#define	QL_VND_SC_REKEY_CONFIG	6
+#define	QL_VND_SC_GET_FCINFO	7
+#define	QL_VND_SC_GET_STATS	8
+#define QL_VND_SC_AEN_COMPLETE  9
+#define QL_VND_SC_READ_DBELL	10
+
+/*
+ * bsg caller to provide empty buffer for doorbell events.
+ *
+ * sg_io_v4.din_xferp  = empty buffer for door bell events
+ * sg_io_v4.dout_xferp = struct edif_read_dbell *buf
+ */
+struct edif_read_dbell {
+	struct app_id app_info;
+	uint8_t version;
+	uint8_t pad[VND_CMD_PAD_SIZE];
+	uint8_t reserved[VND_CMD_APP_RESERVED_SIZE];
+};
+
+
+/* Application interface data structure for rtn data */
+#define	EXT_DEF_EVENT_DATA_SIZE	64
+struct edif_app_dbell {
+	uint32_t	event_code;
+	uint32_t	event_data_size;
+	union  {
+		port_id_t	port_id;
+		uint8_t		event_data[EXT_DEF_EVENT_DATA_SIZE];
+	};
+} __packed;
+
+struct edif_sa_update_aen {
+	port_id_t port_id;
+	uint32_t key_type;	/* Tx (1) or RX (2) */
+	uint32_t status;	/* 0 succes,  1 failed, 2 timeout , 3 error */
+	uint8_t	version;
+	uint8_t	pad[VND_CMD_PAD_SIZE];
+	uint8_t	reserved[VND_CMD_APP_RESERVED_SIZE];
+} __packed;
+
+#define	QL_VND_SA_STAT_SUCCESS	0
+#define	QL_VND_SA_STAT_FAILED	1
+#define	QL_VND_SA_STAT_TIMEOUT	2
+#define	QL_VND_SA_STAT_ERROR	3
+
+#define	QL_VND_RX_SA_KEY	1
+#define	QL_VND_TX_SA_KEY	2
+
+/* App defines for plogi auth'd ok and plogi auth bad requests */
+struct auth_complete_cmd {
+	struct app_id app_info;
+#define PL_TYPE_WWPN    1
+#define PL_TYPE_DID     2
+	uint32_t    type;
+	union {
+		uint8_t  wwpn[WWN_SIZE];
+		port_id_t d_id;
+	} u;
+	uint8_t	version;
+	uint8_t	pad[VND_CMD_PAD_SIZE];
+	uint8_t	reserved[VND_CMD_APP_RESERVED_SIZE];
+} __packed;
+
+struct aen_complete_cmd {
+	struct app_id app_info;
+	port_id_t   port_id;
+	uint32_t    event_code;
+	uint8_t     version;
+	uint8_t     pad[VND_CMD_PAD_SIZE];
+	uint8_t     reserved[VND_CMD_APP_RESERVED_SIZE];
+} __packed;
+
+#define RX_DELAY_DELETE_TIMEOUT 20
+
+#define FCH_EVT_VENDOR_UNIQUE_VPORT_DOWN  1
+
+#endif	/* QLA_EDIF_BSG_H */
diff --git a/scst/qla2x00t-32gbit/qla_fw.h b/scst/qla2x00t-32gbit/qla_fw.h
index f2d560d..de7b1a3 100644
--- a/scst/qla2x00t-32gbit/qla_fw.h
+++ b/scst/qla2x00t-32gbit/qla_fw.h
@@ -1,16 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #ifndef __QLA_FW_H
 #define __QLA_FW_H
 
 #include <linux/version.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) || defined(RHEL_MAJOR)
 #include <linux/nvme.h>
-#endif
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
 #include <linux/nvme-fc.h>
 #endif
@@ -95,10 +92,11 @@
 	uint8_t port_name[WWN_SIZE];
 	uint8_t node_name[WWN_SIZE];
 
-	uint8_t reserved_3[4];
+	uint8_t reserved_3[2];
+	uint16_t nvme_first_burst_size;
 	uint16_t prli_nvme_svc_param_word_0;	/* Bits 15-0 of word 0 */
 	uint16_t prli_nvme_svc_param_word_3;	/* Bits 15-0 of word 3 */
-	uint16_t nvme_first_burst_size;
+	uint8_t secure_login;
 	uint8_t reserved_4[14];
 };
 
@@ -502,6 +500,9 @@
 	struct scsi_lun lun;		/* FCP LUN (BE). */
 
 	__le16	control_flags;		/* Control flags. */
+#define CF_NEW_SA			BIT_12
+#define CF_EN_EDIF			BIT_9
+#define CF_ADDITIONAL_PARAM_BLK		BIT_8
 #define CF_DIF_SEG_DESCR_ENABLE		BIT_3
 #define CF_DATA_SEG_DESCR_ENABLE	BIT_2
 #define CF_READ_DATA			BIT_1
@@ -624,6 +625,7 @@
 	union {
 		__le16 reserved_1;
 		__le16	nvme_rsp_pyld_len;
+		__le16 edif_sa_index;	 /* edif sa_index used for initiator read data */
 	};
 
 	__le16	state_flags;		/* State flags. */
@@ -631,7 +633,7 @@
 #define SF_NVME_ERSP            BIT_6
 #define SF_FCP_RSP_DMA		BIT_0
 
-	__le16	retry_delay;
+	__le16	status_qualifier;
 	__le16	scsi_status;		/* SCSI status. */
 #define SS_CONFIRMATION_REQ		BIT_12
 
@@ -815,9 +817,10 @@
 #define EPD_ELS_COMMAND		(0 << 13)
 #define EPD_ELS_ACC		(1 << 13)
 #define EPD_ELS_RJT		(2 << 13)
-#define EPD_RX_XCHG		(3 << 13)
+#define EPD_RX_XCHG		(3 << 13)  /* terminate exchange */
 #define ECF_CLR_PASSTHRU_PEND	BIT_12
 #define ECF_INCL_FRAME_HDR	BIT_11
+#define ECF_SEC_LOGIN		BIT_3
 
 	union {
 		struct {
@@ -909,6 +912,7 @@
 #define LCF_FCP2_OVERRIDE	BIT_9	/* Set/Reset word 3 of PRLI. */
 #define LCF_CLASS_2		BIT_8	/* Enable class 2 during PLOGI. */
 #define LCF_FREE_NPORT		BIT_7	/* Release NPORT handle after LOGO. */
+#define LCF_COMMON_FEAT		BIT_7	/* PLOGI - Set Common Features Field */
 #define LCF_EXPL_LOGO		BIT_6	/* Perform an explicit LOGO. */
 #define LCF_NVME_PRLI		BIT_6   /* Perform NVME FC4 PRLI */
 #define LCF_SKIP_PRLI		BIT_5	/* Skip PRLI after PLOGI. */
@@ -933,6 +937,8 @@
 	uint8_t rsp_size;		/* Response size in 32bit words. */
 
 	__le32	io_parameter[11];	/* General I/O parameters. */
+#define LIO_COMM_FEAT_FCSP	BIT_21
+#define LIO_COMM_FEAT_CIO	BIT_31
 #define LSC_SCODE_NOLINK	0x01
 #define LSC_SCODE_NOIOCB	0x02
 #define LSC_SCODE_NOXCB		0x03
@@ -995,11 +1001,18 @@
 
 	uint32_t handle;		/* System handle. */
 
-	__le16	nport_handle;		/* N_PORT handle. */
-					/* or Completion status. */
+	union {
+		__le16 nport_handle;            /* N_PORT handle. */
+		__le16 comp_status;             /* Completion status. */
+	};
 
 	__le16	options;		/* Options. */
 #define AOF_NO_ABTS		BIT_0	/* Do not send any ABTS. */
+#define AOF_NO_RRQ		BIT_1   /* Do not send RRQ. */
+#define AOF_ABTS_TIMEOUT	BIT_2   /* Disable logout on ABTS timeout. */
+#define AOF_ABTS_RTY_CNT	BIT_3   /* Use driver specified retry count. */
+#define AOF_RSP_TIMEOUT		BIT_4   /* Use specified response timeout. */
+
 
 	uint32_t handle_to_abort;	/* System handle to abort. */
 
@@ -1008,8 +1021,20 @@
 
 	uint8_t port_id[3];		/* PortID of destination port. */
 	uint8_t vp_index;
-
-	uint8_t reserved_2[12];
+	u8	reserved_2[4];
+	union {
+		struct {
+			__le16 abts_rty_cnt;
+			__le16 rsp_timeout;
+		} drv;
+		struct {
+			u8	ba_rjt_vendorUnique;
+			u8	ba_rjt_reasonCodeExpl;
+			u8	ba_rjt_reasonCode;
+			u8	reserved_3;
+		} fw;
+	};
+	u8	reserved_4[4];
 };
 
 #define ABTS_RCV_TYPE		0x54
@@ -1660,6 +1685,7 @@
 #define FLT_REG_VPD_SEC_27XX_1	0x52
 #define FLT_REG_VPD_SEC_27XX_2	0xD8
 #define FLT_REG_VPD_SEC_27XX_3	0xDA
+#define FLT_REG_NVME_PARAMS_27XX	0x21
 
 /* 28xx */
 #define FLT_REG_AUX_IMG_PRI_28XX	0x125
@@ -1676,6 +1702,8 @@
 #define FLT_REG_MPI_SEC_28XX		0xF0
 #define FLT_REG_PEP_PRI_28XX		0xD1
 #define FLT_REG_PEP_SEC_28XX		0xF1
+#define FLT_REG_NVME_PARAMS_PRI_28XX	0x14E
+#define FLT_REG_NVME_PARAMS_SEC_28XX	0x179
 
 struct qla_flt_region {
 	__le16	code;
@@ -1691,7 +1719,7 @@
 	__le16	length;
 	__le16	checksum;
 	__le16	unused;
-	struct qla_flt_region region[0];
+	struct qla_flt_region region[];
 };
 
 #define FLT_REGION_SIZE		16
diff --git a/scst/qla2x00t-32gbit/qla_gbl.h b/scst/qla2x00t-32gbit/qla_gbl.h
index 83470ec..7567728 100644
--- a/scst/qla2x00t-32gbit/qla_gbl.h
+++ b/scst/qla2x00t-32gbit/qla_gbl.h
@@ -1,8 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #ifndef __QLA_GBL_H
 #define	__QLA_GBL_H
@@ -13,6 +12,7 @@
  * Global Function Prototypes in qla_init.c source file.
  */
 extern int qla2x00_initialize_adapter(scsi_qla_host_t *);
+extern int qla24xx_post_prli_work(struct scsi_qla_host *vha, fc_port_t *fcport);
 
 extern int qla2100_pci_config(struct scsi_qla_host *);
 extern int qla2300_pci_config(struct scsi_qla_host *);
@@ -70,8 +70,6 @@
 extern int qla2x00_async_adisc(struct scsi_qla_host *, fc_port_t *,
     uint16_t *);
 extern int qla2x00_async_tm_cmd(fc_port_t *, uint32_t, uint32_t, uint32_t);
-extern void qla2x00_async_login_done(struct scsi_qla_host *, fc_port_t *,
-    uint16_t *);
 struct qla_work_evt *qla2x00_alloc_work(struct scsi_qla_host *,
     enum qla_work_type);
 extern int qla24xx_async_gnl(struct scsi_qla_host *, fc_port_t *);
@@ -129,6 +127,22 @@
 void qla_rscn_replay(fc_port_t *fcport);
 void qla24xx_free_purex_item(struct purex_item *item);
 extern bool qla24xx_risc_firmware_invalid(uint32_t *);
+void qla_init_iocb_limit(scsi_qla_host_t *);
+
+void qla_edif_list_del(fc_port_t *fcport);
+void qla_edif_sadb_release(struct qla_hw_data *ha);
+int qla_edif_sadb_build_free_pool(struct qla_hw_data *ha);
+void qla_edif_sadb_release_free_pool(struct qla_hw_data *ha);
+void qla_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha,
+		srb_t *sp, struct sts_entry_24xx *sts24);
+void qlt_chk_edif_rx_sa_delete_pending(scsi_qla_host_t *vha, fc_port_t *fcport,
+		struct ctio7_from_24xx *ctio);
+void qla2x00_release_all_sadb(struct scsi_qla_host *vha, struct fc_port *fcport);
+int qla_edif_process_els(scsi_qla_host_t *vha, BSG_JOB_TYPE *bsgjob);
+void qla_edif_sess_down(struct scsi_qla_host *vha, struct fc_port *sess);
+void qla_edif_clear_appdata(struct scsi_qla_host *vha,
+			    struct fc_port *fcport);
+const char *sc_to_str(uint16_t cmd);
 
 /*
  * Global Data in qla_os.c source file.
@@ -147,6 +161,9 @@
 extern int ql2xsmartsan;
 extern int ql2xallocfwdump;
 extern int ql2xextended_error_logging;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+extern int ql2xextended_error_logging_ktrace;
+#endif
 extern int ql2xiidmaenable;
 extern int ql2xmqsupport;
 extern int ql2xfwloadbin;
@@ -157,7 +174,6 @@
 extern int ql2xgffidenable;
 extern int ql2xenabledif;
 extern int ql2xenablehba_err_chk;
-extern int ql2xtargetreset;
 extern int ql2xdontresethba;
 #if 1
 extern uint ql2xmaxlun;
@@ -176,9 +192,12 @@
 extern int ql2xautodetectsfp;
 extern int ql2xenablemsix;
 extern int qla2xuseresexchforels;
-extern int ql2xexlogins;
 extern int ql2xdifbundlinginternalbuffers;
 extern int ql2xfulldump_on_mpifail;
+extern int ql2xsecenable;
+extern int ql2xenforce_iocb_limit;
+extern int ql2xabts_wait_nvme;
+extern u32 ql2xnvme_queues;
 
 extern int qla2x00_loop_reset(scsi_qla_host_t *);
 extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int);
@@ -221,10 +240,10 @@
 extern int __qla83xx_set_drv_presence(scsi_qla_host_t *vha);
 extern int qla83xx_clear_drv_presence(scsi_qla_host_t *vha);
 extern int __qla83xx_clear_drv_presence(scsi_qla_host_t *vha);
-extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32);
 
 extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32);
 extern void qla2x00_disable_board_on_pci_error(struct work_struct *);
+extern void qla_eeh_work(struct work_struct *);
 extern void qla2x00_sp_compl(srb_t *sp, int);
 extern void qla2xxx_qpair_sp_free_dma(srb_t *sp);
 extern void qla2xxx_qpair_sp_compl(srb_t *sp, int);
@@ -236,6 +255,10 @@
 void qla2x00_wait_for_sess_deletion(scsi_qla_host_t *);
 void qla24xx_process_purex_rdp(struct scsi_qla_host *vha,
 			       struct purex_item *pkt);
+void qla_pci_set_eeh_busy(struct scsi_qla_host *);
+void qla_schedule_eeh_work(struct scsi_qla_host *);
+struct edif_sa_ctl *qla_edif_find_sa_ctl_by_index(fc_port_t *fcport,
+						  int index, int dir);
 
 /*
  * Global Functions in qla_mid.c source file.
@@ -259,7 +282,6 @@
 extern scsi_qla_host_t *qla24xx_create_vhost(struct fc_vport *);
 
 extern void qla2x00_sp_free_dma(srb_t *sp);
-extern char *qla2x00_get_fw_version_str(struct scsi_qla_host *, char *);
 
 extern void qla2x00_mark_device_lost(scsi_qla_host_t *, fc_port_t *, int);
 extern void qla2x00_mark_all_devices_lost(scsi_qla_host_t *);
@@ -280,7 +302,10 @@
 /*
  * Global Function Prototypes in qla_iocb.c source file.
  */
-
+void qla_els_pt_iocb(struct scsi_qla_host *vha,
+	struct els_entry_24xx *pkt, struct qla_els_pt_arg *a);
+cont_a64_entry_t *qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha,
+		struct req_que *que);
 extern uint16_t qla2x00_calc_iocbs_32(uint16_t);
 extern uint16_t qla2x00_calc_iocbs_64(uint16_t);
 extern void qla2x00_build_scsi_iocbs_32(srb_t *, cmd_entry_t *, uint16_t);
@@ -296,7 +321,8 @@
 extern int qla24xx_dif_start_scsi(srb_t *);
 extern int qla2x00_start_bidir(srb_t *, struct scsi_qla_host *, uint32_t);
 extern int qla2xxx_dif_start_scsi_mq(srb_t *);
-extern void qla2x00_init_timer(srb_t *sp, unsigned long tmo);
+extern void qla2x00_init_async_sp(srb_t *sp, unsigned long tmo,
+				  void (*done)(struct srb *, int));
 extern unsigned long qla2x00_get_async_timeout(struct scsi_qla_host *);
 
 extern void *qla2x00_alloc_iocbs(struct scsi_qla_host *, srb_t *);
@@ -310,6 +336,10 @@
 	struct dsd64 *, uint16_t, struct qla_tgt_cmd *);
 extern int qla24xx_get_one_block_sg(uint32_t, struct qla2_sgx *, uint32_t *);
 extern int qla24xx_configure_prot_mode(srb_t *, uint16_t *);
+extern int qla24xx_issue_sa_replace_iocb(scsi_qla_host_t *vha,
+	struct qla_work_evt *e);
+void qla2x00_sp_release(struct kref *kref);
+void qla2x00_els_dcmd2_iocb_timeout(void *data);
 
 /*
  * Global Function Prototypes in qla_mbx.c source file.
@@ -408,7 +438,8 @@
 qla2x00_get_resource_cnts(scsi_qla_host_t *);
 
 extern int
-qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map);
+qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map,
+		u8 *num_entries);
 
 extern int
 qla2x00_get_link_status(scsi_qla_host_t *, uint16_t, struct link_statistics *,
@@ -529,6 +560,10 @@
 extern int
 qla26xx_dport_diagnostics(scsi_qla_host_t *, void *, uint, uint);
 
+extern int
+qla26xx_dport_diagnostics_v2(scsi_qla_host_t *,
+			     struct qla_dport_diag_v2 *,  mbx_cmd_t *);
+
 int qla24xx_send_mb_cmd(struct scsi_qla_host *, mbx_cmd_t *);
 int qla24xx_gpdb_wait(struct scsi_qla_host *, fc_port_t *, u8);
 int qla24xx_gidlist_wait(struct scsi_qla_host *, void *, dma_addr_t,
@@ -549,6 +584,7 @@
     uint32_t *);
 extern int qla2xxx_write_remote_register(scsi_qla_host_t *, uint32_t,
     uint32_t);
+void qla_no_op_mb(struct scsi_qla_host *vha);
 
 /*
  * Global Function Prototypes in qla_isr.c source file.
@@ -577,11 +613,11 @@
 fc_port_t *qla2x00_find_fcport_by_loopid(scsi_qla_host_t *, uint16_t);
 fc_port_t *qla2x00_find_fcport_by_wwpn(scsi_qla_host_t *, u8 *, u8);
 fc_port_t *qla2x00_find_fcport_by_nportid(scsi_qla_host_t *, port_id_t *, u8);
+void __qla_consume_iocb(struct scsi_qla_host *vha, void **pkt, struct rsp_que **rsp);
 
 /*
  * Global Function Prototypes in qla_sup.c source file.
  */
-extern void qla2x00_release_nvram_protection(scsi_qla_host_t *);
 extern int qla24xx_read_flash_data(scsi_qla_host_t *, uint32_t *,
     uint32_t, uint32_t);
 extern uint8_t *qla2x00_read_nvram_data(scsi_qla_host_t *, void *, uint32_t,
@@ -639,6 +675,12 @@
 
 extern void qla2xxx_flash_npiv_conf(scsi_qla_host_t *);
 extern int qla24xx_read_fcp_prio_cfg(scsi_qla_host_t *);
+extern int qla2x00_mailbox_passthru(BSG_JOB_TYPE *bsg_job);
+int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha, void **pkt,
+	struct rsp_que **rsp, u8 *buf, u32 buf_len);
+
+int qla_mailbox_passthru(scsi_qla_host_t *vha, uint16_t *mbx_in,
+			 uint16_t *mbx_out);
 
 /*
  * Global Function Prototypes in qla_dbg.c source file.
@@ -685,8 +727,6 @@
 	struct ct_sns_rsp *, const char *);
 extern void qla2x00_async_iocb_timeout(void *data);
 
-extern void qla2x00_free_fcport(fc_port_t *);
-
 extern int qla24xx_post_gpnid_work(struct scsi_qla_host *, port_id_t *);
 extern int qla24xx_async_gpnid(scsi_qla_host_t *, port_id_t *);
 void qla24xx_handle_gpnid_event(scsi_qla_host_t *, struct event_arg *);
@@ -696,7 +736,7 @@
 void qla24xx_handle_gpsc_event(scsi_qla_host_t *, struct event_arg *);
 int qla2x00_mgmt_svr_login(scsi_qla_host_t *);
 void qla24xx_handle_gffid_event(scsi_qla_host_t *vha, struct event_arg *ea);
-int qla24xx_async_gffid(scsi_qla_host_t *vha, fc_port_t *fcport);
+int qla24xx_async_gffid(scsi_qla_host_t *vha, fc_port_t *fcport, bool);
 int qla24xx_async_gpnft(scsi_qla_host_t *, u8, srb_t *);
 void qla24xx_async_gpnft_done(scsi_qla_host_t *, srb_t *);
 void qla24xx_async_gnnft_done(scsi_qla_host_t *, srb_t *);
@@ -708,13 +748,18 @@
 void qla24xx_handle_gfpnid_event(scsi_qla_host_t *, struct event_arg *);
 void qla24xx_sp_unmap(scsi_qla_host_t *, srb_t *);
 void qla_scan_work_fn(struct work_struct *);
+uint qla25xx_fdmi_port_speed_capability(struct qla_hw_data *);
+uint qla25xx_fdmi_port_speed_currently(struct qla_hw_data *);
 
 /*
  * Global Function Prototypes in qla_attr.c source file.
  */
 struct device_attribute;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)
 extern struct device_attribute *qla2x00_host_attrs[];
-extern struct device_attribute *qla2x00_host_attrs_dm[];
+#else
+extern const struct attribute_group *qla2x00_host_groups[];
+#endif
 struct fc_function_template;
 extern struct fc_function_template qla2xxx_transport_functions;
 extern struct fc_function_template qla2xxx_transport_vport_functions;
@@ -728,7 +773,9 @@
 extern int qla24xx_update_all_fcp_prio(scsi_qla_host_t *);
 extern int qla24xx_fcp_prio_cfg_valid(scsi_qla_host_t *,
 	struct qla_fcp_prio_cfg *, uint8_t);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)
 void qla_insert_tgt_attrs(void);
+#endif
 /*
  * Global Function Prototypes in qla_dfs.c source file.
  */
@@ -749,12 +796,6 @@
 extern int qla25xx_delete_req_que(struct scsi_qla_host *, struct req_que *);
 extern int qla25xx_delete_rsp_que(struct scsi_qla_host *, struct rsp_que *);
 extern int qla25xx_delete_queues(struct scsi_qla_host *);
-extern uint16_t qla24xx_rd_req_reg(struct qla_hw_data *, uint16_t);
-extern uint16_t qla25xx_rd_req_reg(struct qla_hw_data *, uint16_t);
-extern void qla24xx_wrt_req_reg(struct qla_hw_data *, uint16_t, uint16_t);
-extern void qla25xx_wrt_req_reg(struct qla_hw_data *, uint16_t, uint16_t);
-extern void qla25xx_wrt_rsp_reg(struct qla_hw_data *, uint16_t, uint16_t);
-extern void qla24xx_wrt_rsp_reg(struct qla_hw_data *, uint16_t, uint16_t);
 
 /* qlafx00 related functions */
 extern int qlafx00_pci_config(struct scsi_qla_host *);
@@ -791,7 +832,6 @@
 extern void qlafx00_fxdisc_iocb(srb_t *, struct fxdisc_entry_fx00 *);
 extern void qlafx00_timer_routine(scsi_qla_host_t *);
 extern int qlafx00_rescan_isp(scsi_qla_host_t *);
-extern int qlafx00_loop_reset(scsi_qla_host_t *vha);
 
 /* qla82xx related functions */
 
@@ -840,8 +880,6 @@
 extern void qla82xx_set_drv_active(scsi_qla_host_t *);
 extern int qla82xx_wr_32(struct qla_hw_data *, ulong, u32);
 extern int qla82xx_rd_32(struct qla_hw_data *, ulong);
-extern int qla82xx_rdmem(struct qla_hw_data *, u64, void *, int);
-extern int qla82xx_wrmem(struct qla_hw_data *, u64, void *, int);
 
 /* ISP 8021 IDC */
 extern void qla82xx_clear_drv_active(struct qla_hw_data *);
@@ -863,7 +901,7 @@
 extern int qla81xx_set_led_config(scsi_qla_host_t *, uint16_t *);
 extern int qla81xx_get_led_config(scsi_qla_host_t *, uint16_t *);
 extern int qla82xx_mbx_beacon_ctl(scsi_qla_host_t *, int);
-extern char *qdev_state(uint32_t);
+extern const char *qdev_state(uint32_t);
 extern void qla82xx_clear_pending_mbx(scsi_qla_host_t *);
 extern int qla82xx_read_temperature(scsi_qla_host_t *);
 extern int qla8044_read_temperature(scsi_qla_host_t *);
@@ -871,18 +909,16 @@
 extern int ql26xx_led_config(scsi_qla_host_t *, uint16_t, uint16_t *);
 
 /* BSG related functions */
-#ifndef NEW_LIBFC_API
-extern int qla24xx_bsg_request(struct fc_bsg_job *);
-extern int qla24xx_bsg_timeout(struct fc_bsg_job *);
-#else
-extern int qla24xx_bsg_request(struct bsg_job *);
-extern int qla24xx_bsg_timeout(struct bsg_job *);
-#endif
+extern int qla24xx_bsg_request(BSG_JOB_TYPE *);
+extern int qla24xx_bsg_timeout(BSG_JOB_TYPE *);
 extern int qla84xx_reset_chip(scsi_qla_host_t *, uint16_t);
 extern int qla2x00_issue_iocb_timeout(scsi_qla_host_t *, void *,
 	dma_addr_t, size_t, uint32_t);
 extern int qla2x00_get_idma_speed(scsi_qla_host_t *, uint16_t,
 	uint16_t *, uint16_t *);
+extern int qla24xx_sadb_update(BSG_JOB_TYPE *bsg_job);
+extern int qla_post_sa_replace_work(struct scsi_qla_host *vha,
+	 fc_port_t *fcport, uint16_t nport_handle, struct edif_sa_ctl *sa_ctl);
 
 /* 83xx related functions */
 void qla83xx_fw_dump(scsi_qla_host_t *vha);
@@ -927,6 +963,7 @@
 extern void qlt_handle_abts_recv(struct scsi_qla_host *, struct rsp_que *,
 	response_t *);
 
+struct scsi_qla_host *qla_find_host_by_d_id(struct scsi_qla_host *vha, be_id_t d_id);
 int qla24xx_async_notify_ack(scsi_qla_host_t *, fc_port_t *,
 	struct imm_ntfy_from_isp *, int);
 void qla24xx_do_nack_work(struct scsi_qla_host *, struct qla_work_evt *);
@@ -939,14 +976,61 @@
 void qla24xx_delete_sess_fn(struct work_struct *);
 void qlt_unknown_atio_work_fn(struct work_struct *);
 void qlt_update_host_map(struct scsi_qla_host *, port_id_t);
-void qlt_remove_target_resources(struct qla_hw_data *);
+void qla_remove_hostmap(struct qla_hw_data *ha);
 void qlt_clr_qp_table(struct scsi_qla_host *vha);
 void qlt_set_mode(struct scsi_qla_host *);
 int qla2x00_set_data_rate(scsi_qla_host_t *vha, uint16_t mode);
 extern void qla24xx_process_purex_list(struct purex_list *);
+extern void qla2x00_dfs_create_rport(scsi_qla_host_t *vha, struct fc_port *fp);
+extern void qla2x00_dfs_remove_rport(scsi_qla_host_t *vha, struct fc_port *fp);
+extern void qla_wait_nvme_release_cmd_kref(srb_t *sp);
+extern void qla_nvme_abort_set_option
+		(struct abort_entry_24xx *abt, srb_t *sp);
+extern void qla_nvme_abort_process_comp_status
+		(struct abort_entry_24xx *abt, srb_t *sp);
 
 /* nvme.c */
 void qla_nvme_unregister_remote_port(struct fc_port *fcport);
-void qla27xx_reset_mpi(scsi_qla_host_t *vha);
+
+/* qla_edif.c */
+fc_port_t *qla2x00_find_fcport_by_pid(scsi_qla_host_t *vha, port_id_t *id);
+void qla_edb_eventcreate(scsi_qla_host_t *vha, uint32_t dbtype, uint32_t data, uint32_t data2,
+		fc_port_t *fcport);
+void qla_edb_stop(scsi_qla_host_t *vha);
+int32_t qla_edif_app_mgmt(BSG_JOB_TYPE *bsg_job);
+void qla_enode_init(scsi_qla_host_t *vha);
+void qla_enode_stop(scsi_qla_host_t *vha);
+void qla_edif_flush_sa_ctl_lists(fc_port_t *fcport);
+void qla_edb_init(scsi_qla_host_t *vha);
+void qla_edif_timer(scsi_qla_host_t *vha);
+int qla28xx_start_scsi_edif(srb_t *sp);
+void qla24xx_sa_update_iocb(srb_t *sp, struct sa_update_28xx *sa_update_iocb);
+void qla24xx_sa_replace_iocb(srb_t *sp, struct sa_update_28xx *sa_update_iocb);
+void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp);
+void qla28xx_sa_update_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
+		struct sa_update_28xx *pkt);
 void qla_handle_els_plogi_done(scsi_qla_host_t *vha, struct event_arg *ea);
+
+#define QLA2XX_HW_ERROR			BIT_0
+#define QLA2XX_SHT_LNK_DWN		BIT_1
+#define QLA2XX_INT_ERR			BIT_2
+#define QLA2XX_CMD_TIMEOUT		BIT_3
+#define QLA2XX_RESET_CMD_ERR		BIT_4
+#define QLA2XX_TGT_SHT_LNK_DOWN		BIT_17
+
+#define QLA2XX_MAX_LINK_DOWN_TIME	100
+
+int qla2xxx_start_stats(struct Scsi_Host *shost, u32 flags);
+int qla2xxx_stop_stats(struct Scsi_Host *shost, u32 flags);
+int qla2xxx_reset_stats(struct Scsi_Host *shost, u32 flags);
+
+int qla2xxx_get_ini_stats(struct Scsi_Host *shost, u32 flags, void *data, u64 size);
+int qla2xxx_get_tgt_stats(struct Scsi_Host *shost, u32 flags,
+			  struct fc_rport *rport, void *data, u64 size);
+int qla2xxx_disable_port(struct Scsi_Host *shost);
+int qla2xxx_enable_port(struct Scsi_Host *shost);
+
+uint64_t qla2x00_get_num_tgts(scsi_qla_host_t *vha);
+uint64_t qla2x00_count_set_bits(u32 num);
+
 #endif /* _QLA_GBL_H */
diff --git a/scst/qla2x00t-32gbit/qla_gs.c b/scst/qla2x00t-32gbit/qla_gs.c
index b569fd6..64ab070 100644
--- a/scst/qla2x00t-32gbit/qla_gs.c
+++ b/scst/qla2x00t-32gbit/qla_gs.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
 #include "qla_target.h"
@@ -530,7 +529,6 @@
 		if (!e)
 			goto err2;
 
-		del_timer(&sp->u.iocb_cmd.timer);
 		e->u.iosb.sp = sp;
 		qla2x00_post_work(vha, e);
 		return;
@@ -557,8 +555,8 @@
 			sp->u.iocb_cmd.u.ctarg.rsp = NULL;
 		}
 
-		sp->free(sp);
-
+		/* ref: INIT */
+		kref_put(&sp->cmd_kref, qla2x00_sp_release);
 		return;
 	}
 
@@ -593,13 +591,15 @@
 	if (!vha->flags.online)
 		goto done;
 
+	/* ref: INIT */
 	sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL);
 	if (!sp)
 		goto done;
 
 	sp->type = SRB_CT_PTHRU_CMD;
 	sp->name = "rft_id";
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla2x00_async_sns_sp_done);
 
 	sp->u.iocb_cmd.u.ctarg.req = dma_alloc_coherent(&vha->hw->pdev->dev,
 	    sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.req_dma,
@@ -633,14 +633,12 @@
 	ct_req->req.rft_id.port_id = port_id_to_be_id(vha->d_id);
 	ct_req->req.rft_id.fc4_types[2] = 0x01;		/* FCP-3 */
 
-	if (vha->flags.nvme_enabled)
+	if (vha->flags.nvme_enabled && qla_ini_mode_enabled(vha))
 		ct_req->req.rft_id.fc4_types[6] = 1;    /* NVMe type 28h */
 
 	sp->u.iocb_cmd.u.ctarg.req_size = RFT_ID_REQ_SIZE;
 	sp->u.iocb_cmd.u.ctarg.rsp_size = RFT_ID_RSP_SIZE;
 	sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
-	sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
-	sp->done = qla2x00_async_sns_sp_done;
 
 	ql_dbg(ql_dbg_disc, vha, 0xffff,
 	    "Async-%s - hdl=%x portid %06x.\n",
@@ -654,7 +652,8 @@
 	}
 	return rval;
 done_free_sp:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 done:
 	return rval;
 }
@@ -677,8 +676,7 @@
 		return (QLA_SUCCESS);
 	}
 
-	return qla_async_rffid(vha, &vha->d_id, qlt_rff_id(vha),
-	    FC4_TYPE_FCP_SCSI);
+	return qla_async_rffid(vha, &vha->d_id, qlt_rff_id(vha), type);
 }
 
 static int qla_async_rffid(scsi_qla_host_t *vha, port_id_t *d_id,
@@ -689,13 +687,15 @@
 	srb_t *sp;
 	struct ct_sns_pkt *ct_sns;
 
+	/* ref: INIT */
 	sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL);
 	if (!sp)
 		goto done;
 
 	sp->type = SRB_CT_PTHRU_CMD;
 	sp->name = "rff_id";
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla2x00_async_sns_sp_done);
 
 	sp->u.iocb_cmd.u.ctarg.req = dma_alloc_coherent(&vha->hw->pdev->dev,
 	    sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.req_dma,
@@ -728,13 +728,11 @@
 	/* Prepare CT arguments -- port_id, FC-4 feature, FC-4 type */
 	ct_req->req.rff_id.port_id = port_id_to_be_id(*d_id);
 	ct_req->req.rff_id.fc4_feature = fc4feature;
-	ct_req->req.rff_id.fc4_type = fc4type;		/* SCSI - FCP */
+	ct_req->req.rff_id.fc4_type = fc4type;		/* SCSI-FCP or FC-NVMe */
 
 	sp->u.iocb_cmd.u.ctarg.req_size = RFF_ID_REQ_SIZE;
 	sp->u.iocb_cmd.u.ctarg.rsp_size = RFF_ID_RSP_SIZE;
 	sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
-	sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
-	sp->done = qla2x00_async_sns_sp_done;
 
 	ql_dbg(ql_dbg_disc, vha, 0xffff,
 	    "Async-%s - hdl=%x portid %06x feature %x type %x.\n",
@@ -750,7 +748,8 @@
 	return rval;
 
 done_free_sp:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 done:
 	return rval;
 }
@@ -780,13 +779,15 @@
 	srb_t *sp;
 	struct ct_sns_pkt *ct_sns;
 
+	/* ref: INIT */
 	sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL);
 	if (!sp)
 		goto done;
 
 	sp->type = SRB_CT_PTHRU_CMD;
 	sp->name = "rnid";
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla2x00_async_sns_sp_done);
 
 	sp->u.iocb_cmd.u.ctarg.req = dma_alloc_coherent(&vha->hw->pdev->dev,
 	    sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.req_dma,
@@ -824,9 +825,6 @@
 	sp->u.iocb_cmd.u.ctarg.rsp_size = RNN_ID_RSP_SIZE;
 	sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
 
-	sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
-	sp->done = qla2x00_async_sns_sp_done;
-
 	ql_dbg(ql_dbg_disc, vha, 0xffff,
 	    "Async-%s - hdl=%x portid %06x\n",
 	    sp->name, sp->handle, d_id->b24);
@@ -841,7 +839,8 @@
 	return rval;
 
 done_free_sp:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 done:
 	return rval;
 }
@@ -887,13 +886,15 @@
 	srb_t *sp;
 	struct ct_sns_pkt *ct_sns;
 
+	/* ref: INIT */
 	sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL);
 	if (!sp)
 		goto done;
 
 	sp->type = SRB_CT_PTHRU_CMD;
 	sp->name = "rsnn_nn";
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla2x00_async_sns_sp_done);
 
 	sp->u.iocb_cmd.u.ctarg.req = dma_alloc_coherent(&vha->hw->pdev->dev,
 	    sizeof(struct ct_sns_pkt), &sp->u.iocb_cmd.u.ctarg.req_dma,
@@ -937,9 +938,6 @@
 	sp->u.iocb_cmd.u.ctarg.rsp_size = RSNN_NN_RSP_SIZE;
 	sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
 
-	sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
-	sp->done = qla2x00_async_sns_sp_done;
-
 	ql_dbg(ql_dbg_disc, vha, 0xffff,
 	    "Async-%s - hdl=%x.\n",
 	    sp->name, sp->handle);
@@ -954,7 +952,8 @@
 	return rval;
 
 done_free_sp:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 done:
 	return rval;
 }
@@ -1248,7 +1247,7 @@
 }
 
 /**
- * qla2x00_snd_rft_id() - SNS Register FC-4 TYPEs (RFT_ID) supported by the HBA.
+ * qla2x00_sns_rft_id() - SNS Register FC-4 TYPEs (RFT_ID) supported by the HBA.
  * @vha: HA context
  *
  * This command uses the old Exectute SNS Command mailbox routine.
@@ -1480,7 +1479,7 @@
 }
 
 /**
- * qla2x00_prep_ct_req() - Prepare common CT request fields for SNS query.
+ * qla2x00_prep_ct_fdmi_req() - Prepare common CT request fields for SNS query.
  * @p: CT request buffer
  * @cmd: GS command
  * @rsp_size: response size in bytes
@@ -1502,7 +1501,7 @@
 	return &p->p.req;
 }
 
-static uint
+uint
 qla25xx_fdmi_port_speed_capability(struct qla_hw_data *ha)
 {
 	uint speeds = 0;
@@ -1538,7 +1537,8 @@
 	}
 	if (IS_QLA2031(ha)) {
 		if ((ha->pdev->subsystem_vendor == 0x103C) &&
-		    (ha->pdev->subsystem_device == 0x8002)) {
+		    ((ha->pdev->subsystem_device == 0x8002) ||
+		    (ha->pdev->subsystem_device == 0x8086))) {
 			speeds = FDMI_PORT_SPEED_16GB;
 		} else {
 			speeds = FDMI_PORT_SPEED_16GB|FDMI_PORT_SPEED_8GB|
@@ -1546,7 +1546,7 @@
 		}
 		return speeds;
 	}
-	if (IS_QLA25XX(ha))
+	if (IS_QLA25XX(ha) || IS_QLAFX00(ha))
 		return FDMI_PORT_SPEED_8GB|FDMI_PORT_SPEED_4GB|
 			FDMI_PORT_SPEED_2GB|FDMI_PORT_SPEED_1GB;
 	if (IS_QLA24XX_TYPE(ha))
@@ -1556,7 +1556,8 @@
 		return FDMI_PORT_SPEED_2GB|FDMI_PORT_SPEED_1GB;
 	return FDMI_PORT_SPEED_1GB;
 }
-static uint
+
+uint
 qla25xx_fdmi_port_speed_currently(struct qla_hw_data *ha)
 {
 	switch (ha->link_data_rate) {
@@ -1582,7 +1583,7 @@
 }
 
 /**
- * qla2x00_hba_attributes() perform HBA attributes registration
+ * qla2x00_hba_attributes() - perform HBA attributes registration
  * @vha: HA context
  * @entries: number of entries to use
  * @callopt: Option to issue extended or standard FDMI
@@ -1595,7 +1596,6 @@
 	unsigned int callopt)
 {
 	struct qla_hw_data *ha = vha->hw;
-	struct init_cb_24xx *icb24 = (void *)ha->init_cb;
 	struct new_utsname *p_sysid = utsname();
 	struct ct_fdmi_hba_attr *eiter;
 	uint16_t alen;
@@ -1616,7 +1616,7 @@
 	eiter->type = cpu_to_be16(FDMI_HBA_MANUFACTURER);
 	alen = scnprintf(
 		eiter->a.manufacturer, sizeof(eiter->a.manufacturer),
-		"%s", "QLogic Corporation");
+		"%s", QLA2XXX_MANUFACTURER);
 	alen += FDMI_ATTR_ALIGNMENT(alen);
 	alen += FDMI_ATTR_TYPELEN(eiter);
 	eiter->len = cpu_to_be16(alen);
@@ -1730,8 +1730,6 @@
 	size += alen;
 	ql_dbg(ql_dbg_disc, vha, 0x20a8,
 	    "FIRMWARE VERSION = %s.\n", eiter->a.fw_version);
-	if (callopt == CALLOPT_FDMI1)
-		goto done;
 	/* OS Name and Version */
 	eiter = entries + size;
 	eiter->type = cpu_to_be16(FDMI_HBA_OS_NAME_AND_VERSION);
@@ -1754,18 +1752,20 @@
 	size += alen;
 	ql_dbg(ql_dbg_disc, vha, 0x20a9,
 	    "OS VERSION = %s.\n", eiter->a.os_version);
+	if (callopt == CALLOPT_FDMI1)
+		goto done;
 	/* MAX CT Payload Length */
 	eiter = entries + size;
 	eiter->type = cpu_to_be16(FDMI_HBA_MAXIMUM_CT_PAYLOAD_LENGTH);
-	eiter->a.max_ct_len = cpu_to_be32(le16_to_cpu(IS_FWI2_CAPABLE(ha) ?
-		icb24->frame_payload_size : ha->init_cb->frame_payload_size));
+	eiter->a.max_ct_len = cpu_to_be32(ha->frame_payload_size >> 2);
+
 	alen = sizeof(eiter->a.max_ct_len);
 	alen += FDMI_ATTR_TYPELEN(eiter);
 	eiter->len = cpu_to_be16(alen);
 	size += alen;
 	ql_dbg(ql_dbg_disc, vha, 0x20aa,
 	    "CT PAYLOAD LENGTH = 0x%x.\n", be32_to_cpu(eiter->a.max_ct_len));
-	/* Node Sybolic Name */
+	/* Node Symbolic Name */
 	eiter = entries + size;
 	eiter->type = cpu_to_be16(FDMI_HBA_NODE_SYMBOLIC_NAME);
 	alen = qla2x00_get_sym_node_name(vha, eiter->a.sym_name,
@@ -1837,7 +1837,7 @@
 }
 
 /**
- * qla2x00_port_attributes() perform Port attributes registration
+ * qla2x00_port_attributes() - perform Port attributes registration
  * @vha: HA context
  * @entries: number of entries to use
  * @callopt: Option to issue extended or standard FDMI
@@ -1850,7 +1850,6 @@
 	unsigned int callopt)
 {
 	struct qla_hw_data *ha = vha->hw;
-	struct init_cb_24xx *icb24 = (void *)ha->init_cb;
 	struct new_utsname *p_sysid = utsname();
 	char *hostname = p_sysid ?
 		p_sysid->nodename : fc_host_system_hostname(vha->host);
@@ -1902,8 +1901,7 @@
 	/* Max frame size. */
 	eiter = entries + size;
 	eiter->type = cpu_to_be16(FDMI_PORT_MAX_FRAME_SIZE);
-	eiter->a.max_frame_size = cpu_to_be32(le16_to_cpu(IS_FWI2_CAPABLE(ha) ?
-		icb24->frame_payload_size : ha->init_cb->frame_payload_size));
+	eiter->a.max_frame_size = cpu_to_be32(ha->frame_payload_size);
 	alen = sizeof(eiter->a.max_frame_size);
 	alen += FDMI_ATTR_TYPELEN(eiter);
 	eiter->len = cpu_to_be16(alen);
@@ -2272,7 +2270,7 @@
 }
 
 /**
- * qla2x00_fdmi_rprt() perform RPRT registration
+ * qla2x00_fdmi_rprt() - perform RPRT registration
  * @vha: HA context
  * @callopt: Option to issue extended or standard FDMI
  *           command parameter
@@ -2826,6 +2824,10 @@
 	if (fcport->disc_state == DSC_DELETE_PEND)
 		return;
 
+	/* We will figure-out what happen after AUTH completes */
+	if (fcport->disc_state == DSC_LOGIN_AUTH_PEND)
+		return;
+
 	if (ea->sp->gen2 != fcport->login_gen) {
 		/* target side must have changed it. */
 		ql_dbg(ql_dbg_disc, vha, 0x20d3,
@@ -2888,7 +2890,8 @@
 	qla24xx_handle_gpsc_event(vha, &ea);
 
 done:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 }
 
 int qla24xx_async_gpsc(scsi_qla_host_t *vha, fc_port_t *fcport)
@@ -2900,6 +2903,7 @@
 	if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT))
 		return rval;
 
+	/* ref: INIT */
 	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
 	if (!sp)
 		goto done;
@@ -2908,8 +2912,8 @@
 	sp->name = "gpsc";
 	sp->gen1 = fcport->rscn_gen;
 	sp->gen2 = fcport->login_gen;
-
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla24xx_async_gpsc_sp_done);
 
 	/* CT_IU preamble  */
 	ct_req = qla24xx_prep_ct_fm_req(fcport->ct_desc.ct_sns, GPSC_CMD,
@@ -2927,9 +2931,6 @@
 	sp->u.iocb_cmd.u.ctarg.rsp_size = GPSC_RSP_SIZE;
 	sp->u.iocb_cmd.u.ctarg.nport_handle = vha->mgmt_svr_loop_id;
 
-	sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
-	sp->done = qla24xx_async_gpsc_sp_done;
-
 	ql_dbg(ql_dbg_disc, vha, 0x205e,
 	    "Async-%s %8phC hdl=%x loopid=%x portid=%02x%02x%02x.\n",
 	    sp->name, fcport->port_name, sp->handle,
@@ -2942,7 +2943,8 @@
 	return rval;
 
 done_free_sp:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 done:
 	return rval;
 }
@@ -2991,7 +2993,8 @@
 		break;
 	}
 
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 }
 
 void qla24xx_handle_gpnid_event(scsi_qla_host_t *vha, struct event_arg *ea)
@@ -3130,13 +3133,15 @@
 	if (res) {
 		if (res == QLA_FUNCTION_TIMEOUT) {
 			qla24xx_post_gpnid_work(sp->vha, &ea.id);
-			sp->free(sp);
+			/* ref: INIT */
+			kref_put(&sp->cmd_kref, qla2x00_sp_release);
 			return;
 		}
 	} else if (sp->gen1) {
 		/* There was another RSCN for this Nport ID */
 		qla24xx_post_gpnid_work(sp->vha, &ea.id);
-		sp->free(sp);
+		/* ref: INIT */
+		kref_put(&sp->cmd_kref, qla2x00_sp_release);
 		return;
 	}
 
@@ -3157,7 +3162,8 @@
 				  sp->u.iocb_cmd.u.ctarg.rsp_dma);
 		sp->u.iocb_cmd.u.ctarg.rsp = NULL;
 
-		sp->free(sp);
+		/* ref: INIT */
+		kref_put(&sp->cmd_kref, qla2x00_sp_release);
 		return;
 	}
 
@@ -3177,6 +3183,7 @@
 	if (!vha->flags.online)
 		goto done;
 
+	/* ref: INIT */
 	sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL);
 	if (!sp)
 		goto done;
@@ -3185,14 +3192,16 @@
 	sp->name = "gpnid";
 	sp->u.iocb_cmd.u.ctarg.id = *id;
 	sp->gen1 = 0;
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla2x00_async_gpnid_sp_done);
 
 	spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
 	list_for_each_entry(tsp, &vha->gpnid_list, elem) {
 		if (tsp->u.iocb_cmd.u.ctarg.id.b24 == id->b24) {
 			tsp->gen1++;
 			spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
-			sp->free(sp);
+			/* ref: INIT */
+			kref_put(&sp->cmd_kref, qla2x00_sp_release);
 			goto done;
 		}
 	}
@@ -3233,9 +3242,6 @@
 	sp->u.iocb_cmd.u.ctarg.rsp_size = GPN_ID_RSP_SIZE;
 	sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
 
-	sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
-	sp->done = qla2x00_async_gpnid_sp_done;
-
 	ql_dbg(ql_dbg_disc, vha, 0x2067,
 	    "Async-%s hdl=%x ID %3phC.\n", sp->name,
 	    sp->handle, &ct_req->req.port_id.port_id);
@@ -3265,25 +3271,18 @@
 			sp->u.iocb_cmd.u.ctarg.rsp_dma);
 		sp->u.iocb_cmd.u.ctarg.rsp = NULL;
 	}
-
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 done:
 	return rval;
 }
 
-void qla24xx_handle_gffid_event(scsi_qla_host_t *vha, struct event_arg *ea)
-{
-	fc_port_t *fcport = ea->fcport;
-
-	qla24xx_post_gnl_work(vha, fcport);
-}
 
 void qla24xx_async_gffid_sp_done(srb_t *sp, int res)
 {
 	struct scsi_qla_host *vha = sp->vha;
 	fc_port_t *fcport = sp->fcport;
 	struct ct_sns_rsp *ct_rsp;
-	struct event_arg ea;
 	uint8_t fc4_scsi_feat;
 	uint8_t fc4_nvme_feat;
 
@@ -3291,10 +3290,10 @@
 	       "Async done-%s res %x ID %x. %8phC\n",
 	       sp->name, res, fcport->d_id.b24, fcport->port_name);
 
-	fcport->flags &= ~FCF_ASYNC_SENT;
-	ct_rsp = &fcport->ct_desc.ct_sns->p.rsp;
+	ct_rsp = sp->u.iocb_cmd.u.ctarg.rsp;
 	fc4_scsi_feat = ct_rsp->rsp.gff_id.fc4_features[GFF_FCP_SCSI_OFFSET];
 	fc4_nvme_feat = ct_rsp->rsp.gff_id.fc4_features[GFF_NVME_OFFSET];
+	sp->rc = res;
 
 	/*
 	 * FC-GS-7, 5.2.3.12 FC-4 Features - format
@@ -3315,68 +3314,129 @@
 		}
 	}
 
-	memset(&ea, 0, sizeof(ea));
-	ea.sp = sp;
-	ea.fcport = sp->fcport;
-	ea.rc = res;
+	if (sp->flags & SRB_WAKEUP_ON_COMP) {
+		complete(sp->comp);
+	} else  {
+		if (sp->u.iocb_cmd.u.ctarg.req) {
+			dma_free_coherent(&vha->hw->pdev->dev,
+				sp->u.iocb_cmd.u.ctarg.req_allocated_size,
+				sp->u.iocb_cmd.u.ctarg.req,
+				sp->u.iocb_cmd.u.ctarg.req_dma);
+			sp->u.iocb_cmd.u.ctarg.req = NULL;
+		}
 
-	qla24xx_handle_gffid_event(vha, &ea);
-	sp->free(sp);
+		if (sp->u.iocb_cmd.u.ctarg.rsp) {
+			dma_free_coherent(&vha->hw->pdev->dev,
+				sp->u.iocb_cmd.u.ctarg.rsp_allocated_size,
+				sp->u.iocb_cmd.u.ctarg.rsp,
+				sp->u.iocb_cmd.u.ctarg.rsp_dma);
+			sp->u.iocb_cmd.u.ctarg.rsp = NULL;
+		}
+
+		/* ref: INIT */
+		kref_put(&sp->cmd_kref, qla2x00_sp_release);
+		/* we should not be here */
+		dump_stack();
+	}
 }
 
 /* Get FC4 Feature with Nport ID. */
-int qla24xx_async_gffid(scsi_qla_host_t *vha, fc_port_t *fcport)
+int qla24xx_async_gffid(scsi_qla_host_t *vha, fc_port_t *fcport, bool wait)
 {
 	int rval = QLA_FUNCTION_FAILED;
 	struct ct_sns_req       *ct_req;
 	srb_t *sp;
+	DECLARE_COMPLETION_ONSTACK(comp);
 
-	if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT))
+	/* this routine does not have handling for no wait */
+	if (!vha->flags.online || !wait)
 		return rval;
 
+	/* ref: INIT */
 	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
 	if (!sp)
 		return rval;
 
-	fcport->flags |= FCF_ASYNC_SENT;
 	sp->type = SRB_CT_PTHRU_CMD;
 	sp->name = "gffid";
 	sp->gen1 = fcport->rscn_gen;
 	sp->gen2 = fcport->login_gen;
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla24xx_async_gffid_sp_done);
+	sp->comp = &comp;
+	sp->u.iocb_cmd.timeout = qla2x00_els_dcmd2_iocb_timeout;
 
-	sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+	if (wait)
+		sp->flags = SRB_WAKEUP_ON_COMP;
+
+	sp->u.iocb_cmd.u.ctarg.req_allocated_size = sizeof(struct ct_sns_pkt);
+	sp->u.iocb_cmd.u.ctarg.req = dma_alloc_coherent(&vha->hw->pdev->dev,
+				sp->u.iocb_cmd.u.ctarg.req_allocated_size,
+				&sp->u.iocb_cmd.u.ctarg.req_dma,
+	    GFP_KERNEL);
+	if (!sp->u.iocb_cmd.u.ctarg.req) {
+		ql_log(ql_log_warn, vha, 0xd041,
+		       "%s: Failed to allocate ct_sns request.\n",
+		       __func__);
+		goto done_free_sp;
+	}
+
+	sp->u.iocb_cmd.u.ctarg.rsp_allocated_size = sizeof(struct ct_sns_pkt);
+	sp->u.iocb_cmd.u.ctarg.rsp = dma_alloc_coherent(&vha->hw->pdev->dev,
+				sp->u.iocb_cmd.u.ctarg.rsp_allocated_size,
+				&sp->u.iocb_cmd.u.ctarg.rsp_dma,
+	    GFP_KERNEL);
+	if (!sp->u.iocb_cmd.u.ctarg.rsp) {
+		ql_log(ql_log_warn, vha, 0xd041,
+		       "%s: Failed to allocate ct_sns response.\n",
+		       __func__);
+		goto done_free_sp;
+	}
 
 	/* CT_IU preamble  */
-	ct_req = qla2x00_prep_ct_req(fcport->ct_desc.ct_sns, GFF_ID_CMD,
-	    GFF_ID_RSP_SIZE);
+	ct_req = qla2x00_prep_ct_req(sp->u.iocb_cmd.u.ctarg.req, GFF_ID_CMD, GFF_ID_RSP_SIZE);
 
 	ct_req->req.gff_id.port_id[0] = fcport->d_id.b.domain;
 	ct_req->req.gff_id.port_id[1] = fcport->d_id.b.area;
 	ct_req->req.gff_id.port_id[2] = fcport->d_id.b.al_pa;
 
-	sp->u.iocb_cmd.u.ctarg.req = fcport->ct_desc.ct_sns;
-	sp->u.iocb_cmd.u.ctarg.req_dma = fcport->ct_desc.ct_sns_dma;
-	sp->u.iocb_cmd.u.ctarg.rsp = fcport->ct_desc.ct_sns;
-	sp->u.iocb_cmd.u.ctarg.rsp_dma = fcport->ct_desc.ct_sns_dma;
 	sp->u.iocb_cmd.u.ctarg.req_size = GFF_ID_REQ_SIZE;
 	sp->u.iocb_cmd.u.ctarg.rsp_size = GFF_ID_RSP_SIZE;
 	sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
 
-	sp->done = qla24xx_async_gffid_sp_done;
-
-	ql_dbg(ql_dbg_disc, vha, 0x2132,
-	    "Async-%s hdl=%x  %8phC.\n", sp->name,
-	    sp->handle, fcport->port_name);
-
 	rval = qla2x00_start_sp(sp);
-	if (rval != QLA_SUCCESS)
-		goto done_free_sp;
 
-	return rval;
+	if (rval != QLA_SUCCESS) {
+		rval = QLA_FUNCTION_FAILED;
+		goto done_free_sp;
+	} else {
+		ql_dbg(ql_dbg_disc, vha, 0x3074,
+		       "Async-%s hdl=%x portid %06x\n",
+		       sp->name, sp->handle, fcport->d_id.b24);
+	}
+
+	wait_for_completion(sp->comp);
+	rval = sp->rc;
+
 done_free_sp:
-	sp->free(sp);
-	fcport->flags &= ~FCF_ASYNC_SENT;
+	if (sp->u.iocb_cmd.u.ctarg.req) {
+		dma_free_coherent(&vha->hw->pdev->dev,
+				  sp->u.iocb_cmd.u.ctarg.req_allocated_size,
+				  sp->u.iocb_cmd.u.ctarg.req,
+				  sp->u.iocb_cmd.u.ctarg.req_dma);
+		sp->u.iocb_cmd.u.ctarg.req = NULL;
+	}
+
+	if (sp->u.iocb_cmd.u.ctarg.rsp) {
+		dma_free_coherent(&vha->hw->pdev->dev,
+				  sp->u.iocb_cmd.u.ctarg.rsp_allocated_size,
+				  sp->u.iocb_cmd.u.ctarg.rsp,
+				  sp->u.iocb_cmd.u.ctarg.rsp_dma);
+		sp->u.iocb_cmd.u.ctarg.rsp = NULL;
+	}
+
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 	return rval;
 }
 
@@ -3443,6 +3503,10 @@
 			list_for_each_entry(fcport, &vha->vp_fcports, list) {
 				if ((fcport->flags & FCF_FABRIC_DEVICE) != 0) {
 					fcport->scan_state = QLA_FCPORT_SCAN;
+					if (fcport->loop_id == FC_NO_LOOP_ID)
+						fcport->logout_on_delete = 0;
+					else
+						fcport->logout_on_delete = 1;
 				}
 			}
 			goto login_logout;
@@ -3494,7 +3558,16 @@
 				continue;
 			fcport->scan_state = QLA_FCPORT_FOUND;
 			fcport->last_rscn_gen = fcport->rscn_gen;
+			fcport->fc4_type = rp->fc4type;
 			found = true;
+
+			if (fcport->scan_needed) {
+				if (NVME_PRIORITY(vha->hw, fcport))
+					fcport->do_prli_nvme = 1;
+				else
+					fcport->do_prli_nvme = 0;
+			}
+
 			/*
 			 * If device was not a fabric device before.
 			 */
@@ -3556,13 +3629,14 @@
 				do_delete) {
 				if (fcport->loop_id != FC_NO_LOOP_ID) {
 					if (fcport->flags & FCF_FCP2_DEVICE)
-						fcport->logout_on_delete = 0;
+						continue;
 
-					ql_dbg(ql_dbg_disc, vha, 0x20f0,
-					    "%s %d %8phC post del sess\n",
-					    __func__, __LINE__,
-					    fcport->port_name);
+					ql_log(ql_log_warn, vha, 0x20f0,
+					       "%s %d %8phC post del sess\n",
+					       __func__, __LINE__,
+					       fcport->port_name);
 
+					fcport->tgt_link_down_time = 0;
 					qlt_schedule_sess_for_deletion(fcport);
 					continue;
 				}
@@ -3748,7 +3822,6 @@
 	    "Async done-%s res %x FC4Type %x\n",
 	    sp->name, res, sp->gen2);
 
-	del_timer(&sp->u.iocb_cmd.timer);
 	sp->rc = res;
 	if (res) {
 		unsigned long flags;
@@ -3873,9 +3946,8 @@
 	sp->name = "gnnft";
 	sp->gen1 = vha->hw->base_qpair->chip_reset;
 	sp->gen2 = fc4_type;
-
-	sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla2x00_async_gpnft_gnnft_sp_done);
 
 	memset(sp->u.iocb_cmd.u.ctarg.rsp, 0, sp->u.iocb_cmd.u.ctarg.rsp_size);
 	memset(sp->u.iocb_cmd.u.ctarg.req, 0, sp->u.iocb_cmd.u.ctarg.req_size);
@@ -3891,8 +3963,6 @@
 	sp->u.iocb_cmd.u.ctarg.req_size = GNN_FT_REQ_SIZE;
 	sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
 
-	sp->done = qla2x00_async_gpnft_gnnft_sp_done;
-
 	ql_dbg(ql_dbg_disc, vha, 0xffff,
 	    "Async-%s hdl=%x FC4Type %x.\n", sp->name,
 	    sp->handle, ct_req->req.gpn_ft.port_type);
@@ -3919,8 +3989,8 @@
 		    sp->u.iocb_cmd.u.ctarg.rsp_dma);
 		sp->u.iocb_cmd.u.ctarg.rsp = NULL;
 	}
-
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 
 	spin_lock_irqsave(&vha->work_lock, flags);
 	vha->scan.scan_flags &= ~SF_SCANNING;
@@ -3972,9 +4042,12 @@
 		ql_dbg(ql_dbg_disc + ql_dbg_verbose, vha, 0xffff,
 		    "%s: Performing FCP Scan\n", __func__);
 
-		if (sp)
-			sp->free(sp); /* should not happen */
+		if (sp) {
+			/* ref: INIT */
+			kref_put(&sp->cmd_kref, qla2x00_sp_release);
+		}
 
+		/* ref: INIT */
 		sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL);
 		if (!sp) {
 			spin_lock_irqsave(&vha->work_lock, flags);
@@ -4019,6 +4092,7 @@
 			    sp->u.iocb_cmd.u.ctarg.req,
 			    sp->u.iocb_cmd.u.ctarg.req_dma);
 			sp->u.iocb_cmd.u.ctarg.req = NULL;
+			/* ref: INIT */
 			qla2x00_rel_sp(sp);
 			return rval;
 		}
@@ -4038,9 +4112,8 @@
 	sp->name = "gpnft";
 	sp->gen1 = vha->hw->base_qpair->chip_reset;
 	sp->gen2 = fc4_type;
-
-	sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla2x00_async_gpnft_gnnft_sp_done);
 
 	rspsz = sp->u.iocb_cmd.u.ctarg.rsp_size;
 	memset(sp->u.iocb_cmd.u.ctarg.rsp, 0, sp->u.iocb_cmd.u.ctarg.rsp_size);
@@ -4055,8 +4128,6 @@
 
 	sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
 
-	sp->done = qla2x00_async_gpnft_gnnft_sp_done;
-
 	ql_dbg(ql_dbg_disc, vha, 0xffff,
 	    "Async-%s hdl=%x FC4Type %x.\n", sp->name,
 	    sp->handle, ct_req->req.gpn_ft.port_type);
@@ -4084,7 +4155,8 @@
 		sp->u.iocb_cmd.u.ctarg.rsp = NULL;
 	}
 
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 
 	spin_lock_irqsave(&vha->work_lock, flags);
 	vha->scan.scan_flags &= ~SF_SCANNING;
@@ -4148,7 +4220,8 @@
 
 	qla24xx_handle_gnnid_event(vha, &ea);
 
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 }
 
 int qla24xx_async_gnnid(scsi_qla_host_t *vha, fc_port_t *fcport)
@@ -4161,6 +4234,7 @@
 		return rval;
 
 	qla2x00_set_fcport_disc_state(fcport, DSC_GNN_ID);
+	/* ref: INIT */
 	sp = qla2x00_get_sp(vha, fcport, GFP_ATOMIC);
 	if (!sp)
 		goto done;
@@ -4170,9 +4244,8 @@
 	sp->name = "gnnid";
 	sp->gen1 = fcport->rscn_gen;
 	sp->gen2 = fcport->login_gen;
-
-	sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla2x00_async_gnnid_sp_done);
 
 	/* CT_IU preamble  */
 	ct_req = qla2x00_prep_ct_req(fcport->ct_desc.ct_sns, GNN_ID_CMD,
@@ -4191,8 +4264,6 @@
 	sp->u.iocb_cmd.u.ctarg.rsp_size = GNN_ID_RSP_SIZE;
 	sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
 
-	sp->done = qla2x00_async_gnnid_sp_done;
-
 	ql_dbg(ql_dbg_disc, vha, 0xffff,
 	    "Async-%s - %8phC hdl=%x loopid=%x portid %06x.\n",
 	    sp->name, fcport->port_name,
@@ -4204,7 +4275,8 @@
 	return rval;
 
 done_free_sp:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 	fcport->flags &= ~FCF_ASYNC_SENT;
 done:
 	return rval;
@@ -4278,7 +4350,8 @@
 
 	qla24xx_handle_gfpnid_event(vha, &ea);
 
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 }
 
 int qla24xx_async_gfpnid(scsi_qla_host_t *vha, fc_port_t *fcport)
@@ -4290,6 +4363,7 @@
 	if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT))
 		return rval;
 
+	/* ref: INIT */
 	sp = qla2x00_get_sp(vha, fcport, GFP_ATOMIC);
 	if (!sp)
 		goto done;
@@ -4298,9 +4372,8 @@
 	sp->name = "gfpnid";
 	sp->gen1 = fcport->rscn_gen;
 	sp->gen2 = fcport->login_gen;
-
-	sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla2x00_async_gfpnid_sp_done);
 
 	/* CT_IU preamble  */
 	ct_req = qla2x00_prep_ct_req(fcport->ct_desc.ct_sns, GFPN_ID_CMD,
@@ -4319,8 +4392,6 @@
 	sp->u.iocb_cmd.u.ctarg.rsp_size = GFPN_ID_RSP_SIZE;
 	sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS;
 
-	sp->done = qla2x00_async_gfpnid_sp_done;
-
 	ql_dbg(ql_dbg_disc, vha, 0xffff,
 	    "Async-%s - %8phC hdl=%x loopid=%x portid %06x.\n",
 	    sp->name, fcport->port_name,
@@ -4333,7 +4404,8 @@
 	return rval;
 
 done_free_sp:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 done:
 	return rval;
 }
diff --git a/scst/qla2x00t-32gbit/qla_init.c b/scst/qla2x00t-32gbit/qla_init.c
index eecde32..25860c0 100644
--- a/scst/qla2x00t-32gbit/qla_init.c
+++ b/scst/qla2x00t-32gbit/qla_init.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
 #include "qla_gbl.h"
@@ -35,7 +34,6 @@
 static struct qla_chip_state_84xx *qla84xx_get_chip(struct scsi_qla_host *);
 static int qla84xx_init_chip(scsi_qla_host_t *);
 static int qla25xx_init_queues(struct qla_hw_data *);
-static int qla24xx_post_prli_work(struct scsi_qla_host*, fc_port_t *);
 static void qla24xx_handle_gpdb_event(scsi_qla_host_t *vha,
 				      struct event_arg *ea);
 static void qla24xx_handle_prli_done_event(struct scsi_qla_host *,
@@ -49,10 +47,20 @@
 {
 	srb_t *sp = from_timer(sp, t, u.iocb_cmd.timer);
 	struct srb_iocb *iocb;
+	scsi_qla_host_t *vha = sp->vha;
 
 	WARN_ON(irqs_disabled());
 	iocb = &sp->u.iocb_cmd;
 	iocb->timeout(sp);
+
+	/* ref: TMR */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
+
+	if (vha && qla2x00_isp_reg_stat(vha->hw)) {
+		ql_log(ql_log_info, vha, 0x9008,
+		    "PCI/Register disconnect.\n");
+		qla_pci_set_eeh_busy(vha);
+	}
 }
 
 void qla2x00_sp_free(srb_t *sp)
@@ -63,6 +71,16 @@
 	qla2x00_rel_sp(sp);
 }
 
+void qla2xxx_rel_done_warning(srb_t *sp, int res)
+{
+	WARN_ONCE(1, "Calling done() of an already freed srb %p object\n", sp);
+}
+
+void qla2xxx_rel_free_warning(srb_t *sp)
+{
+	WARN_ONCE(1, "Calling free() of an already freed srb %p object\n", sp);
+}
+
 /* Asynchronous Login/Logout Routines -------------------------------------- */
 
 unsigned long
@@ -117,8 +135,13 @@
 	}
 	spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
 
-	if (sp->cmd_sp)
+	if (sp->cmd_sp) {
+		/*
+		 * This done function should take care of
+		 * original command ref: INIT
+		 */
 		sp->cmd_sp->done(sp->cmd_sp, QLA_OS_TIMER_EXPIRED);
+	}
 
 	abt->u.abt.comp_status = cpu_to_le16(CS_TIMEOUT);
 	sp->done(sp, QLA_OS_TIMER_EXPIRED);
@@ -127,12 +150,16 @@
 static void qla24xx_abort_sp_done(srb_t *sp, int res)
 {
 	struct srb_iocb *abt = &sp->u.iocb_cmd;
+	srb_t *orig_sp = sp->cmd_sp;
 
-	del_timer(&sp->u.iocb_cmd.timer);
+	if (orig_sp)
+		qla_wait_nvme_release_cmd_kref(orig_sp);
+
 	if (sp->flags & SRB_WAKEUP_ON_COMP)
 		complete(&abt->u.abt.comp);
 	else
-		sp->free(sp);
+		/* ref: INIT */
+		kref_put(&sp->cmd_kref, qla2x00_sp_release);
 }
 
 int qla24xx_async_abort_cmd(srb_t *cmd_sp, bool wait)
@@ -142,11 +169,13 @@
 	srb_t *sp;
 	int rval = QLA_FUNCTION_FAILED;
 
+	/* ref: INIT for ABTS command */
 	sp = qla2xxx_get_qpair_sp(cmd_sp->vha, cmd_sp->qpair, cmd_sp->fcport,
 				  GFP_ATOMIC);
 	if (!sp)
-		return rval;
+		return QLA_MEMORY_ALLOC_FAILED;
 
+	qla_vha_mark_busy(vha);
 	abt_iocb = &sp->u.iocb_cmd;
 	sp->type = SRB_ABT_CMD;
 	sp->name = "abort";
@@ -155,31 +184,31 @@
 	if (wait)
 		sp->flags = SRB_WAKEUP_ON_COMP;
 
-	abt_iocb->timeout = qla24xx_abort_iocb_timeout;
 	init_completion(&abt_iocb->u.abt.comp);
 	/* FW can send 2 x ABTS's timeout/20s */
-	qla2x00_init_timer(sp, 42);
+	qla2x00_init_async_sp(sp, 42, qla24xx_abort_sp_done);
+	sp->u.iocb_cmd.timeout = qla24xx_abort_iocb_timeout;
 
 	abt_iocb->u.abt.cmd_hndl = cmd_sp->handle;
 	abt_iocb->u.abt.req_que_no = cpu_to_le16(cmd_sp->qpair->req->id);
 
-	sp->done = qla24xx_abort_sp_done;
-
 	ql_dbg(ql_dbg_async, vha, 0x507c,
 	       "Abort command issued - hdl=%x, type=%x\n", cmd_sp->handle,
 	       cmd_sp->type);
 
 	rval = qla2x00_start_sp(sp);
 	if (rval != QLA_SUCCESS) {
-		sp->free(sp);
+		/* ref: INIT */
+		kref_put(&sp->cmd_kref, qla2x00_sp_release);
 		return rval;
 	}
 
 	if (wait) {
 		wait_for_completion(&abt_iocb->u.abt.comp);
 		rval = abt_iocb->u.abt.comp_status == CS_COMPLETE ?
-			QLA_SUCCESS : QLA_FUNCTION_FAILED;
-		sp->free(sp);
+			QLA_SUCCESS : QLA_ERR_FROM_FW;
+		/* ref: INIT */
+		kref_put(&sp->cmd_kref, qla2x00_sp_release);
 	}
 
 	return rval;
@@ -274,26 +303,13 @@
 		ea.iop[0] = lio->u.logio.iop[0];
 		ea.iop[1] = lio->u.logio.iop[1];
 		ea.sp = sp;
+		if (res)
+			ea.data[0] = MBS_COMMAND_ERROR;
 		qla24xx_handle_plogi_done_event(vha, &ea);
 	}
 
-	sp->free(sp);
-}
-
-static inline bool
-fcport_is_smaller(fc_port_t *fcport)
-{
-	if (wwn_to_u64(fcport->port_name) <
-	    wwn_to_u64(fcport->vha->port_name))
-		return true;
-	else
-		return false;
-}
-
-static inline bool
-fcport_is_bigger(fc_port_t *fcport)
-{
-	return !fcport_is_smaller(fcport);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 }
 
 int
@@ -312,6 +328,7 @@
 		return rval;
 	}
 
+	/* ref: INIT */
 	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
 	if (!sp)
 		goto done;
@@ -324,27 +341,33 @@
 	sp->name = "login";
 	sp->gen1 = fcport->rscn_gen;
 	sp->gen2 = fcport->login_gen;
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla2x00_async_login_sp_done);
 
 	lio = &sp->u.iocb_cmd;
-	lio->timeout = qla2x00_async_iocb_timeout;
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
-
-	sp->done = qla2x00_async_login_sp_done;
-	if (N2N_TOPO(fcport->vha->hw) && fcport_is_bigger(fcport))
+	if (N2N_TOPO(fcport->vha->hw) && fcport_is_bigger(fcport)) {
 		lio->u.logio.flags |= SRB_LOGIN_PRLI_ONLY;
-	else
-		lio->u.logio.flags |= SRB_LOGIN_COND_PLOGI;
+	} else {
+		if (vha->hw->flags.edif_enabled &&
+		    DBELL_ACTIVE(vha)) {
+			lio->u.logio.flags |=
+				(SRB_LOGIN_FCSP | SRB_LOGIN_SKIP_PRLI);
+		} else {
+			lio->u.logio.flags |= SRB_LOGIN_COND_PLOGI;
+		}
+	}
 
 	if (NVME_TARGET(vha->hw, fcport))
 		lio->u.logio.flags |= SRB_LOGIN_SKIP_PRLI;
 
-	ql_dbg(ql_dbg_disc, vha, 0x2072,
-	    "Async-login - %8phC hdl=%x, loopid=%x portid=%02x%02x%02x "
-		"retries=%d.\n", fcport->port_name, sp->handle, fcport->loop_id,
-	    fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa,
-	    fcport->login_retry);
-
 	rval = qla2x00_start_sp(sp);
+
+	ql_dbg(ql_dbg_disc, vha, 0x2072,
+	       "Async-login - %8phC hdl=%x, loopid=%x portid=%06x retries=%d %s.\n",
+	       fcport->port_name, sp->handle, fcport->loop_id,
+	       fcport->d_id.b24, fcport->login_retry,
+	       lio->u.logio.flags & SRB_LOGIN_FCSP ? "FCSP" : "");
+
 	if (rval != QLA_SUCCESS) {
 		fcport->flags |= FCF_LOGIN_NEEDED;
 		set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
@@ -354,7 +377,8 @@
 	return rval;
 
 done_free_sp:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 	fcport->flags &= ~FCF_ASYNC_SENT;
 done:
 	fcport->flags &= ~FCF_ASYNC_ACTIVE;
@@ -365,36 +389,33 @@
 {
 	sp->fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE);
 	sp->fcport->login_gen++;
-	qlt_logo_completion_handler(sp->fcport, res);
-	sp->free(sp);
+	qlt_logo_completion_handler(sp->fcport, sp->u.iocb_cmd.u.logio.data[0]);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 }
 
 int
 qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport)
 {
 	srb_t *sp;
-	struct srb_iocb *lio;
 	int rval = QLA_FUNCTION_FAILED;
 
 	fcport->flags |= FCF_ASYNC_SENT;
+	/* ref: INIT */
 	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
 	if (!sp)
 		goto done;
 
 	sp->type = SRB_LOGOUT_CMD;
 	sp->name = "logout";
-
-	lio = &sp->u.iocb_cmd;
-	lio->timeout = qla2x00_async_iocb_timeout;
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
-
-	sp->done = qla2x00_async_logout_sp_done;
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla2x00_async_logout_sp_done),
 
 	ql_dbg(ql_dbg_disc, vha, 0x2070,
-	    "Async-logout - hdl=%x loop-id=%x portid=%02x%02x%02x %8phC.\n",
+	    "Async-logout - hdl=%x loop-id=%x portid=%02x%02x%02x %8phC explicit %d.\n",
 	    sp->handle, fcport->loop_id, fcport->d_id.b.domain,
 		fcport->d_id.b.area, fcport->d_id.b.al_pa,
-		fcport->port_name);
+		fcport->port_name, fcport->explicit_logout);
 
 	rval = qla2x00_start_sp(sp);
 	if (rval != QLA_SUCCESS)
@@ -402,7 +423,8 @@
 	return rval;
 
 done_free_sp:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 done:
 	fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE);
 	return rval;
@@ -428,29 +450,26 @@
 	if (!test_bit(UNLOADING, &vha->dpc_flags))
 		qla2x00_post_async_prlo_done_work(sp->fcport->vha, sp->fcport,
 		    lio->u.logio.data);
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 }
 
 int
 qla2x00_async_prlo(struct scsi_qla_host *vha, fc_port_t *fcport)
 {
 	srb_t *sp;
-	struct srb_iocb *lio;
 	int rval;
 
 	rval = QLA_FUNCTION_FAILED;
+	/* ref: INIT */
 	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
 	if (!sp)
 		goto done;
 
 	sp->type = SRB_PRLO_CMD;
 	sp->name = "prlo";
-
-	lio = &sp->u.iocb_cmd;
-	lio->timeout = qla2x00_async_iocb_timeout;
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
-
-	sp->done = qla2x00_async_prlo_sp_done;
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla2x00_async_prlo_sp_done);
 
 	ql_dbg(ql_dbg_disc, vha, 0x2070,
 	    "Async-prlo - hdl=%x loop-id=%x portid=%02x%02x%02x.\n",
@@ -464,7 +483,8 @@
 	return rval;
 
 done_free_sp:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 done:
 	fcport->flags &= ~FCF_ASYNC_ACTIVE;
 	return rval;
@@ -547,10 +567,12 @@
 	ea.iop[1] = lio->u.logio.iop[1];
 	ea.fcport = sp->fcport;
 	ea.sp = sp;
+	if (res)
+		ea.data[0] = MBS_COMMAND_ERROR;
 
 	qla24xx_handle_adisc_event(vha, &ea);
-
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 }
 
 int
@@ -561,26 +583,34 @@
 	struct srb_iocb *lio;
 	int rval = QLA_FUNCTION_FAILED;
 
+	if (IS_SESSION_DELETED(fcport)) {
+		ql_log(ql_log_warn, vha, 0xffff,
+		       "%s: %8phC is being delete - not sending command.\n",
+		       __func__, fcport->port_name);
+		fcport->flags &= ~FCF_ASYNC_ACTIVE;
+		return rval;
+	}
+
 	if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT))
 		return rval;
 
 	fcport->flags |= FCF_ASYNC_SENT;
+	/* ref: INIT */
 	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
 	if (!sp)
 		goto done;
 
 	sp->type = SRB_ADISC_CMD;
 	sp->name = "adisc";
-
-	lio = &sp->u.iocb_cmd;
-	lio->timeout = qla2x00_async_iocb_timeout;
 	sp->gen1 = fcport->rscn_gen;
 	sp->gen2 = fcport->login_gen;
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla2x00_async_adisc_sp_done);
 
-	sp->done = qla2x00_async_adisc_sp_done;
-	if (data[1] & QLA_LOGIO_LOGIN_RETRIED)
+	if (data[1] & QLA_LOGIO_LOGIN_RETRIED) {
+		lio = &sp->u.iocb_cmd;
 		lio->u.logio.flags |= SRB_LOGIN_RETRIED;
+	}
 
 	ql_dbg(ql_dbg_disc, vha, 0x206f,
 	    "Async-adisc - hdl=%x loopid=%x portid=%06x %8phC.\n",
@@ -593,7 +623,8 @@
 	return rval;
 
 done_free_sp:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 done:
 	fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE);
 	qla2x00_post_async_adisc_work(vha, fcport, data);
@@ -679,11 +710,11 @@
 
 	fcport = ea->fcport;
 	ql_dbg(ql_dbg_disc, vha, 0xffff,
-	    "%s %8phC DS %d LS rc %d %d login %d|%d rscn %d|%d lid %d\n",
+	    "%s %8phC DS %d LS rc %d %d login %d|%d rscn %d|%d lid %d edif %d\n",
 	    __func__, fcport->port_name, fcport->disc_state,
 	    fcport->fw_login_state, ea->rc,
 	    fcport->login_gen, fcport->last_login_gen,
-	    fcport->rscn_gen, fcport->last_rscn_gen, vha->loop_id);
+	    fcport->rscn_gen, fcport->last_rscn_gen, vha->loop_id, fcport->edif.enable);
 
 	if (fcport->disc_state == DSC_DELETE_PEND)
 		return;
@@ -705,6 +736,7 @@
 		ql_dbg(ql_dbg_disc, vha, 0x20e0,
 		    "%s %8phC login gen changed\n",
 		    __func__, fcport->port_name);
+		set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
 		return;
 	}
 
@@ -796,7 +828,7 @@
 		default:
 			switch (current_login_state) {
 			case DSC_LS_PRLI_COMP:
-				ql_dbg(ql_dbg_disc + ql_dbg_verbose,
+				ql_dbg(ql_dbg_disc,
 				    vha, 0x20e4, "%s %d %8phC post gpdb\n",
 				    __func__, __LINE__, fcport->port_name);
 
@@ -808,6 +840,13 @@
 				qla2x00_post_async_adisc_work(vha, fcport,
 				    data);
 				break;
+			case DSC_LS_PLOGI_COMP:
+				if (vha->hw->flags.edif_enabled) {
+					/* check to see if App support Secure */
+					qla24xx_post_gpdb_work(vha, fcport, 0);
+					break;
+				}
+				fallthrough;
 			case DSC_LS_PORT_UNAVAIL:
 			default:
 				if (fcport->loop_id == FC_NO_LOOP_ID) {
@@ -835,6 +874,7 @@
 				 */
 				qla2x00_set_fcport_disc_state(fcport,
 				    DSC_DELETED);
+				set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
 				break;
 			case DSC_LS_PRLI_COMP:
 				if ((e->prli_svc_param_word_3[0] & BIT_4) == 0)
@@ -847,6 +887,12 @@
 				    data);
 				break;
 			case DSC_LS_PLOGI_COMP:
+				if (vha->hw->flags.edif_enabled &&
+				    DBELL_ACTIVE(vha)) {
+					/* check to see if App support secure or not */
+					qla24xx_post_gpdb_work(vha, fcport, 0);
+					break;
+				}
 				if (fcport_is_bigger(fcport)) {
 					/* local adapter is smaller */
 					if (fcport->loop_id != FC_NO_LOOP_ID)
@@ -944,6 +990,9 @@
 				set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
 			}
 			break;
+		case ISP_CFG_NL:
+			qla24xx_fcport_handle_login(vha, fcport);
+			break;
 		default:
 			break;
 		}
@@ -967,8 +1016,6 @@
 	    sp->name, res, sp->u.iocb_cmd.u.mbx.in_mb[1],
 	    sp->u.iocb_cmd.u.mbx.in_mb[2]);
 
-	if (res == QLA_FUNCTION_TIMEOUT)
-		return;
 
 	sp->fcport->flags &= ~(FCF_ASYNC_SENT|FCF_ASYNC_ACTIVE);
 	memset(&ea, 0, sizeof(ea));
@@ -1006,8 +1053,8 @@
 	spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
 
 	list_for_each_entry_safe(fcport, tf, &h, gnl_entry) {
-		list_del_init(&fcport->gnl_entry);
 		spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
+		list_del_init(&fcport->gnl_entry);
 		fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE);
 		spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
 		ea.fcport = fcport;
@@ -1061,13 +1108,13 @@
 	}
 	spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
 
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 }
 
 int qla24xx_async_gnl(struct scsi_qla_host *vha, fc_port_t *fcport)
 {
 	srb_t *sp;
-	struct srb_iocb *mbx;
 	int rval = QLA_FUNCTION_FAILED;
 	unsigned long flags;
 	u16 *mb;
@@ -1092,6 +1139,7 @@
 	vha->gnl.sent = 1;
 	spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
 
+	/* ref: INIT */
 	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
 	if (!sp)
 		goto done;
@@ -1100,10 +1148,8 @@
 	sp->name = "gnlist";
 	sp->gen1 = fcport->rscn_gen;
 	sp->gen2 = fcport->login_gen;
-
-	mbx = &sp->u.iocb_cmd;
-	mbx->timeout = qla2x00_async_iocb_timeout;
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha)+2);
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla24xx_async_gnl_sp_done);
 
 	mb = sp->u.iocb_cmd.u.mbx.out_mb;
 	mb[0] = MBC_PORT_NODE_NAME_LIST;
@@ -1115,8 +1161,6 @@
 	mb[8] = vha->gnl.size;
 	mb[9] = vha->vp_idx;
 
-	sp->done = qla24xx_async_gnl_sp_done;
-
 	ql_dbg(ql_dbg_disc, vha, 0x20da,
 	    "Async-%s - OUT WWPN %8phC hndl %x\n",
 	    sp->name, fcport->port_name, sp->handle);
@@ -1128,7 +1172,8 @@
 	return rval;
 
 done_free_sp:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 done:
 	fcport->flags &= ~(FCF_ASYNC_ACTIVE | FCF_ASYNC_SENT);
 	return rval;
@@ -1174,13 +1219,16 @@
 	dma_pool_free(ha->s_dma_pool, sp->u.iocb_cmd.u.mbx.in,
 		sp->u.iocb_cmd.u.mbx.in_dma);
 
-	sp->free(sp);
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 }
 
-static int qla24xx_post_prli_work(struct scsi_qla_host *vha, fc_port_t *fcport)
+int qla24xx_post_prli_work(struct scsi_qla_host *vha, fc_port_t *fcport)
 {
 	struct qla_work_evt *e;
 
+	if (vha->host->active_mode == MODE_TARGET)
+		return QLA_FUNCTION_FAILED;
+
 	e = qla2x00_alloc_work(vha, QLA_EVT_PRLI);
 	if (!e)
 		return QLA_FUNCTION_FAILED;
@@ -1197,7 +1245,7 @@
 	struct event_arg ea;
 
 	ql_dbg(ql_dbg_disc, vha, 0x2129,
-	    "%s %8phC res %d \n", __func__,
+	    "%s %8phC res %x\n", __func__,
 	    sp->fcport->port_name, res);
 
 	sp->fcport->flags &= ~FCF_ASYNC_SENT;
@@ -1210,11 +1258,15 @@
 		ea.iop[0] = lio->u.logio.iop[0];
 		ea.iop[1] = lio->u.logio.iop[1];
 		ea.sp = sp;
+		if (res == QLA_OS_TIMER_EXPIRED)
+			ea.data[0] = QLA_OS_TIMER_EXPIRED;
+		else if (res)
+			ea.data[0] = MBS_COMMAND_ERROR;
 
 		qla24xx_handle_prli_done_event(vha, &ea);
 	}
 
-	sp->free(sp);
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 }
 
 int
@@ -1247,21 +1299,20 @@
 
 	sp->type = SRB_PRLI_CMD;
 	sp->name = "prli";
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla2x00_async_prli_sp_done);
 
 	lio = &sp->u.iocb_cmd;
-	lio->timeout = qla2x00_async_iocb_timeout;
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
-
-	sp->done = qla2x00_async_prli_sp_done;
 	lio->u.logio.flags = 0;
 
 	if (NVME_TARGET(vha->hw, fcport))
 		lio->u.logio.flags |= SRB_LOGIN_NVME_PRLI;
 
 	ql_dbg(ql_dbg_disc, vha, 0x211b,
-	    "Async-prli - %8phC hdl=%x, loopid=%x portid=%06x retries=%d %s.\n",
+	    "Async-prli - %8phC hdl=%x, loopid=%x portid=%06x retries=%d fc4type %x priority %x %s.\n",
 	    fcport->port_name, sp->handle, fcport->loop_id, fcport->d_id.b24,
-	    fcport->login_retry, NVME_TARGET(vha->hw, fcport) ? "nvme" : "fc");
+	    fcport->login_retry, fcport->fc4_type, vha->hw->fc4_type_priority,
+	    NVME_TARGET(vha->hw, fcport) ? "nvme" : "fcp");
 
 	rval = qla2x00_start_sp(sp);
 	if (rval != QLA_SUCCESS) {
@@ -1273,7 +1324,8 @@
 	return rval;
 
 done_free_sp:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 	fcport->flags &= ~FCF_ASYNC_SENT;
 	return rval;
 }
@@ -1302,14 +1354,21 @@
 	struct port_database_24xx *pd;
 	struct qla_hw_data *ha = vha->hw;
 
-	if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT) ||
-	    fcport->loop_id == FC_NO_LOOP_ID) {
+	if (IS_SESSION_DELETED(fcport)) {
 		ql_log(ql_log_warn, vha, 0xffff,
-		    "%s: %8phC - not sending command.\n",
-		    __func__, fcport->port_name);
+		       "%s: %8phC is being delete - not sending command.\n",
+		       __func__, fcport->port_name);
+		fcport->flags &= ~FCF_ASYNC_ACTIVE;
 		return rval;
 	}
 
+	if (!vha->flags.online || fcport->flags & FCF_ASYNC_SENT) {
+		ql_log(ql_log_warn, vha, 0xffff,
+		    "%s: %8phC online %d flags %x - not sending command.\n",
+		    __func__, fcport->port_name, vha->flags.online, fcport->flags);
+		goto done;
+	}
+
 	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
 	if (!sp)
 		goto done;
@@ -1321,10 +1380,8 @@
 	sp->name = "gpdb";
 	sp->gen1 = fcport->rscn_gen;
 	sp->gen2 = fcport->login_gen;
-
-	mbx = &sp->u.iocb_cmd;
-	mbx->timeout = qla2x00_async_iocb_timeout;
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla24xx_async_gpdb_sp_done);
 
 	pd = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &pd_dma);
 	if (pd == NULL) {
@@ -1343,11 +1400,10 @@
 	mb[9] = vha->vp_idx;
 	mb[10] = opt;
 
-	mbx->u.mbx.in = pd;
+	mbx = &sp->u.iocb_cmd;
+	mbx->u.mbx.in = (void *)pd;
 	mbx->u.mbx.in_dma = pd_dma;
 
-	sp->done = qla24xx_async_gpdb_sp_done;
-
 	ql_dbg(ql_dbg_disc, vha, 0x20dc,
 	    "Async-%s %8phC hndl %x opt %x\n",
 	    sp->name, fcport->port_name, sp->handle, opt);
@@ -1361,7 +1417,7 @@
 	if (pd)
 		dma_pool_free(ha->s_dma_pool, pd, pd_dma);
 
-	sp->free(sp);
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 	fcport->flags &= ~FCF_ASYNC_SENT;
 done:
 	fcport->flags &= ~FCF_ASYNC_ACTIVE;
@@ -1400,6 +1456,56 @@
 	spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
 }
 
+static int	qla_chk_secure_login(scsi_qla_host_t	*vha, fc_port_t *fcport,
+	struct port_database_24xx *pd)
+{
+	int rc = 0;
+
+	if (pd->secure_login) {
+		ql_dbg(ql_dbg_disc, vha, 0x104d,
+		    "Secure Login established on %8phC\n",
+		    fcport->port_name);
+		fcport->flags |= FCF_FCSP_DEVICE;
+	} else {
+		ql_dbg(ql_dbg_disc, vha, 0x104d,
+		    "non-Secure Login %8phC",
+		    fcport->port_name);
+		fcport->flags &= ~FCF_FCSP_DEVICE;
+	}
+	if (vha->hw->flags.edif_enabled) {
+		if (fcport->flags & FCF_FCSP_DEVICE) {
+			qla2x00_set_fcport_disc_state(fcport, DSC_LOGIN_AUTH_PEND);
+			/* Start edif prli timer & ring doorbell for app */
+			fcport->edif.rx_sa_set = 0;
+			fcport->edif.tx_sa_set = 0;
+			fcport->edif.rx_sa_pending = 0;
+			fcport->edif.tx_sa_pending = 0;
+
+			qla2x00_post_aen_work(vha, FCH_EVT_PORT_ONLINE,
+			    fcport->d_id.b24);
+
+			if (DBELL_ACTIVE(vha)) {
+				ql_dbg(ql_dbg_disc, vha, 0x20ef,
+				    "%s %d %8phC EDIF: post DB_AUTH: AUTH needed\n",
+				    __func__, __LINE__, fcport->port_name);
+				fcport->edif.app_sess_online = 1;
+
+				qla_edb_eventcreate(vha, VND_CMD_AUTH_STATE_NEEDED,
+				    fcport->d_id.b24, 0, fcport);
+			}
+
+			rc = 1;
+		} else if (qla_ini_mode_enabled(vha) || qla_dual_mode_enabled(vha)) {
+			ql_dbg(ql_dbg_disc, vha, 0x2117,
+			    "%s %d %8phC post prli\n",
+			    __func__, __LINE__, fcport->port_name);
+			qla24xx_post_prli_work(vha, fcport);
+			rc = 1;
+		}
+	}
+	return rc;
+}
+
 static
 void qla24xx_handle_gpdb_event(scsi_qla_host_t *vha, struct event_arg *ea)
 {
@@ -1413,12 +1519,15 @@
 	fcport->flags &= ~FCF_ASYNC_SENT;
 
 	ql_dbg(ql_dbg_disc, vha, 0x20d2,
-	    "%s %8phC DS %d LS %d fc4_type %x rc %d\n", __func__,
+	    "%s %8phC DS %d LS %x fc4_type %x rc %x\n", __func__,
 	    fcport->port_name, fcport->disc_state, pd->current_login_state,
 	    fcport->fc4_type, ea->rc);
 
-	if (fcport->disc_state == DSC_DELETE_PEND)
+	if (fcport->disc_state == DSC_DELETE_PEND) {
+		ql_dbg(ql_dbg_disc, vha, 0x20d5, "%s %d %8phC\n",
+		       __func__, __LINE__, fcport->port_name);
 		return;
+	}
 
 	if (NVME_TARGET(vha->hw, fcport))
 		ls = pd->current_login_state >> 4;
@@ -1435,6 +1544,8 @@
 	} else if (ea->sp->gen1 != fcport->rscn_gen) {
 		qla_rscn_replay(fcport);
 		qlt_schedule_sess_for_deletion(fcport);
+		ql_dbg(ql_dbg_disc, vha, 0x20d5, "%s %d %8phC, ls %x\n",
+		       __func__, __LINE__, fcport->port_name, ls);
 		return;
 	}
 
@@ -1442,8 +1553,14 @@
 	case PDS_PRLI_COMPLETE:
 		__qla24xx_parse_gpdb(vha, fcport, pd);
 		break;
-	case PDS_PLOGI_PENDING:
 	case PDS_PLOGI_COMPLETE:
+		if (qla_chk_secure_login(vha, fcport, pd)) {
+			ql_dbg(ql_dbg_disc, vha, 0x20d5, "%s %d %8phC, ls %x\n",
+			       __func__, __LINE__, fcport->port_name, ls);
+			return;
+		}
+		fallthrough;
+	case PDS_PLOGI_PENDING:
 	case PDS_PRLI_PENDING:
 	case PDS_PRLI2_PENDING:
 		/* Set discovery state back to GNL to Relogin attempt */
@@ -1452,6 +1569,8 @@
 			qla2x00_set_fcport_disc_state(fcport, DSC_GNL);
 			set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
 		}
+		ql_dbg(ql_dbg_disc, vha, 0x20d5, "%s %d %8phC, ls %x\n",
+		       __func__, __LINE__, fcport->port_name, ls);
 		return;
 	case PDS_LOGO_PENDING:
 	case PDS_PORT_UNAVAILABLE:
@@ -1469,6 +1588,11 @@
 	u8 login = 0;
 	int rc;
 
+	ql_dbg(ql_dbg_disc, vha, 0x307b,
+	    "%s %8phC DS %d LS %d lid %d retries=%d\n",
+	    __func__, fcport->port_name, fcport->disc_state,
+	    fcport->fw_login_state, fcport->loop_id, fcport->login_retry);
+
 	if (qla_tgt_mode_enabled(vha))
 		return;
 
@@ -1520,13 +1644,15 @@
 	u16 sec;
 
 	ql_dbg(ql_dbg_disc, vha, 0x20d8,
-	    "%s %8phC DS %d LS %d P %d fl %x confl %p rscn %d|%d login %d lid %d scan %d\n",
+	    "%s %8phC DS %d LS %d P %d fl %x confl %p rscn %d|%d login %d lid %d scan %d fc4type %x\n",
 	    __func__, fcport->port_name, fcport->disc_state,
 	    fcport->fw_login_state, fcport->login_pause, fcport->flags,
 	    fcport->conflict, fcport->last_rscn_gen, fcport->rscn_gen,
-	    fcport->login_gen, fcport->loop_id, fcport->scan_state);
+	    fcport->login_gen, fcport->loop_id, fcport->scan_state,
+	    fcport->fc4_type);
 
-	if (fcport->scan_state != QLA_FCPORT_FOUND)
+	if (fcport->scan_state != QLA_FCPORT_FOUND ||
+	    fcport->disc_state == DSC_DELETE_PEND)
 		return 0;
 
 	if ((fcport->loop_id != FC_NO_LOOP_ID) &&
@@ -1547,7 +1673,7 @@
 	if (vha->host->active_mode == MODE_TARGET && !N2N_TOPO(vha->hw))
 		return 0;
 
-	if (fcport->flags & FCF_ASYNC_SENT) {
+	if (fcport->flags & (FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE)) {
 		set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
 		return 0;
 	}
@@ -1644,8 +1770,16 @@
 		break;
 
 	case DSC_LOGIN_PEND:
-		if (fcport->fw_login_state == DSC_LS_PLOGI_COMP)
+		if (vha->hw->flags.edif_enabled)
+			break;
+
+		if (fcport->fw_login_state == DSC_LS_PLOGI_COMP) {
+			ql_dbg(ql_dbg_disc, vha, 0x2118,
+			       "%s %d %8phC post %s PRLI\n",
+			       __func__, __LINE__, fcport->port_name,
+			       NVME_TARGET(vha->hw, fcport) ? "NVME" : "FC");
 			qla24xx_post_prli_work(vha, fcport);
+		}
 		break;
 
 	case DSC_UPD_FCPORT:
@@ -1695,10 +1829,76 @@
 	fc_port_t *fcport;
 	unsigned long flags;
 
-	fcport = qla2x00_find_fcport_by_nportid(vha, &ea->id, 1);
-	if (fcport) {
-		fcport->scan_needed = 1;
-		fcport->rscn_gen++;
+	switch (ea->id.b.rsvd_1) {
+	case RSCN_PORT_ADDR:
+		fcport = qla2x00_find_fcport_by_nportid(vha, &ea->id, 1);
+		if (fcport) {
+			if (fcport->flags & FCF_FCP2_DEVICE &&
+			    atomic_read(&fcport->state) == FCS_ONLINE) {
+				ql_dbg(ql_dbg_disc, vha, 0x2115,
+				       "Delaying session delete for FCP2 portid=%06x %8phC ",
+					fcport->d_id.b24, fcport->port_name);
+				return;
+			}
+
+			if (vha->hw->flags.edif_enabled && DBELL_ACTIVE(vha)) {
+				/*
+				 * On ipsec start by remote port, Target port
+				 * may use RSCN to trigger initiator to
+				 * relogin. If driver is already in the
+				 * process of a relogin, then ignore the RSCN
+				 * and allow the current relogin to continue.
+				 * This reduces thrashing of the connection.
+				 */
+				if (atomic_read(&fcport->state) == FCS_ONLINE) {
+					/*
+					 * If state = online, then set scan_needed=1 to do relogin.
+					 * Otherwise we're already in the middle of a relogin
+					 */
+					fcport->scan_needed = 1;
+					fcport->rscn_gen++;
+				}
+			} else {
+				fcport->scan_needed = 1;
+				fcport->rscn_gen++;
+			}
+		}
+		break;
+	case RSCN_AREA_ADDR:
+		list_for_each_entry(fcport, &vha->vp_fcports, list) {
+			if (fcport->flags & FCF_FCP2_DEVICE &&
+			    atomic_read(&fcport->state) == FCS_ONLINE)
+				continue;
+
+			if ((ea->id.b24 & 0xffff00) == (fcport->d_id.b24 & 0xffff00)) {
+				fcport->scan_needed = 1;
+				fcport->rscn_gen++;
+			}
+		}
+		break;
+	case RSCN_DOM_ADDR:
+		list_for_each_entry(fcport, &vha->vp_fcports, list) {
+			if (fcport->flags & FCF_FCP2_DEVICE &&
+			    atomic_read(&fcport->state) == FCS_ONLINE)
+				continue;
+
+			if ((ea->id.b24 & 0xff0000) == (fcport->d_id.b24 & 0xff0000)) {
+				fcport->scan_needed = 1;
+				fcport->rscn_gen++;
+			}
+		}
+		break;
+	case RSCN_FAB_ADDR:
+	default:
+		list_for_each_entry(fcport, &vha->vp_fcports, list) {
+			if (fcport->flags & FCF_FCP2_DEVICE &&
+			    atomic_read(&fcport->state) == FCS_ONLINE)
+				continue;
+
+			fcport->scan_needed = 1;
+			fcport->rscn_gen++;
+		}
+		break;
 	}
 
 	spin_lock_irqsave(&vha->work_lock, flags);
@@ -1740,6 +1940,13 @@
 void qla_handle_els_plogi_done(scsi_qla_host_t *vha,
 				      struct event_arg *ea)
 {
+	if (N2N_TOPO(vha->hw) && fcport_is_smaller(ea->fcport) &&
+	    vha->hw->flags.edif_enabled) {
+		/* check to see if App support Secure */
+		qla24xx_post_gpdb_work(vha, ea->fcport, 0);
+		return;
+	}
+
 	/* for pure Target Mode, PRLI will not be initiated */
 	if (vha->host->active_mode == MODE_TARGET)
 		return;
@@ -1813,22 +2020,22 @@
 	srb_t *sp;
 	int rval = QLA_FUNCTION_FAILED;
 
+	/* ref: INIT */
 	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
 	if (!sp)
 		goto done;
 
-	tm_iocb = &sp->u.iocb_cmd;
+	qla_vha_mark_busy(vha);
 	sp->type = SRB_TM_CMD;
 	sp->name = "tmf";
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha),
+			      qla2x00_tmf_sp_done);
+	sp->u.iocb_cmd.timeout = qla2x00_tmf_iocb_timeout;
 
-	tm_iocb->timeout = qla2x00_tmf_iocb_timeout;
+	tm_iocb = &sp->u.iocb_cmd;
 	init_completion(&tm_iocb->u.tmf.comp);
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha));
-
 	tm_iocb->u.tmf.flags = flags;
 	tm_iocb->u.tmf.lun = lun;
-	tm_iocb->u.tmf.data = tag;
-	sp->done = qla2x00_tmf_sp_done;
 
 	ql_dbg(ql_dbg_taskm, vha, 0x802f,
 	    "Async-tmf hdl=%x loop-id=%x portid=%02x%02x%02x.\n",
@@ -1858,7 +2065,8 @@
 	}
 
 done_free_sp:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 	fcport->flags &= ~FCF_ASYNC_SENT;
 done:
 	return rval;
@@ -1884,7 +2092,7 @@
 
 	if (handle == req->num_outstanding_cmds) {
 		/* Command not found. */
-		return QLA_FUNCTION_FAILED;
+		return QLA_ERR_NOT_FOUND;
 	}
 	if (sp->type == SRB_FXIOCB_DCMD)
 		return qlafx00_fx_disc(vha, &vha->hw->mr.fcport,
@@ -1896,6 +2104,7 @@
 static void
 qla24xx_handle_prli_done_event(struct scsi_qla_host *vha, struct event_arg *ea)
 {
+	struct srb *sp;
 	WARN_ONCE(!qla2xxx_is_valid_mbs(ea->data[0]), "mbs: %#x\n",
 		  ea->data[0]);
 
@@ -1916,33 +2125,58 @@
 		qla24xx_post_gpdb_work(vha, ea->fcport, 0);
 		break;
 	default:
-		if ((ea->iop[0] == LSC_SCODE_ELS_REJECT) &&
-		    (ea->iop[1] == 0x50000)) {   /* reson 5=busy expl:0x0 */
-			set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
-			ea->fcport->fw_login_state = DSC_LS_PLOGI_COMP;
-			break;
-		}
+		sp = ea->sp;
+		ql_dbg(ql_dbg_disc, vha, 0x2118,
+		       "%s %d %8phC priority %s, fc4type %x prev try %s\n",
+		       __func__, __LINE__, ea->fcport->port_name,
+		       vha->hw->fc4_type_priority == FC4_PRIORITY_FCP ?
+		       "FCP" : "NVMe", ea->fcport->fc4_type,
+		       (sp->u.iocb_cmd.u.logio.flags & SRB_LOGIN_NVME_PRLI) ?
+			"NVME" : "FCP");
 
-		/*
-		 * Retry PRLI with other FC-4 type if failure occurred on dual
-		 * FCP/NVMe port
-		 */
 		if (NVME_FCP_TARGET(ea->fcport)) {
-			ql_dbg(ql_dbg_disc, vha, 0x2118,
-				"%s %d %8phC post %s prli\n",
-				__func__, __LINE__, ea->fcport->port_name,
-				(ea->fcport->fc4_type & FS_FC4TYPE_NVME) ?
-				"NVMe" : "FCP");
-			if (vha->hw->fc4_type_priority == FC4_PRIORITY_NVME)
-				ea->fcport->fc4_type &= ~FS_FC4TYPE_NVME;
+			if (sp->u.iocb_cmd.u.logio.flags & SRB_LOGIN_NVME_PRLI)
+				ea->fcport->do_prli_nvme = 0;
 			else
-				ea->fcport->fc4_type &= ~FS_FC4TYPE_FCP;
+				ea->fcport->do_prli_nvme = 1;
+		} else {
+			ea->fcport->do_prli_nvme = 0;
 		}
 
-		ea->fcport->flags &= ~FCF_ASYNC_SENT;
-		ea->fcport->keep_nport_handle = 0;
-		ea->fcport->logout_on_delete = 1;
-		qlt_schedule_sess_for_deletion(ea->fcport);
+		if (N2N_TOPO(vha->hw)) {
+			if (ea->fcport->n2n_link_reset_cnt ==
+			    vha->hw->login_retry_count &&
+			    ea->fcport->flags & FCF_FCSP_DEVICE) {
+				/* remote authentication app just started */
+				ea->fcport->n2n_link_reset_cnt = 0;
+			}
+
+			if (ea->fcport->n2n_link_reset_cnt <
+			    vha->hw->login_retry_count) {
+				ea->fcport->n2n_link_reset_cnt++;
+				vha->relogin_jif = jiffies + 2 * HZ;
+				/*
+				 * PRLI failed. Reset link to kick start
+				 * state machine
+				 */
+				set_bit(N2N_LINK_RESET, &vha->dpc_flags);
+				qla2xxx_wake_dpc(vha);
+			} else {
+				ql_log(ql_log_warn, vha, 0x2119,
+				       "%s %d %8phC Unable to reconnect\n",
+				       __func__, __LINE__,
+				       ea->fcport->port_name);
+			}
+		} else {
+			/*
+			 * switch connect. login failed. Take connection down
+			 * and allow relogin to retrigger
+			 */
+			ea->fcport->flags &= ~FCF_ASYNC_SENT;
+			ea->fcport->keep_nport_handle = 0;
+			ea->fcport->logout_on_delete = 1;
+			qlt_schedule_sess_for_deletion(ea->fcport);
+		}
 		break;
 	}
 }
@@ -2003,38 +2237,45 @@
 		 * force a relogin attempt via implicit LOGO, PLOGI, and PRLI
 		 * requests.
 		 */
-		if (NVME_TARGET(vha->hw, ea->fcport)) {
-			ql_dbg(ql_dbg_disc, vha, 0x2117,
-				"%s %d %8phC post prli\n",
-				__func__, __LINE__, ea->fcport->port_name);
-			qla24xx_post_prli_work(vha, ea->fcport);
-		} else {
-			ql_dbg(ql_dbg_disc, vha, 0x20ea,
-			    "%s %d %8phC LoopID 0x%x in use with %06x. post gpdb\n",
-			    __func__, __LINE__, ea->fcport->port_name,
-			    ea->fcport->loop_id, ea->fcport->d_id.b24);
-
+		if (vha->hw->flags.edif_enabled) {
 			set_bit(ea->fcport->loop_id, vha->hw->loop_id_map);
 			spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
 			ea->fcport->chip_reset = vha->hw->base_qpair->chip_reset;
 			ea->fcport->logout_on_delete = 1;
 			ea->fcport->send_els_logo = 0;
-			ea->fcport->fw_login_state = DSC_LS_PRLI_COMP;
+			ea->fcport->fw_login_state = DSC_LS_PLOGI_COMP;
 			spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
 
 			qla24xx_post_gpdb_work(vha, ea->fcport, 0);
+		} else {
+			if (NVME_TARGET(vha->hw, fcport)) {
+				ql_dbg(ql_dbg_disc, vha, 0x2117,
+				    "%s %d %8phC post prli\n",
+				    __func__, __LINE__, fcport->port_name);
+				qla24xx_post_prli_work(vha, fcport);
+			} else {
+				ql_dbg(ql_dbg_disc, vha, 0x20ea,
+				    "%s %d %8phC LoopID 0x%x in use with %06x. post gpdb\n",
+				    __func__, __LINE__, fcport->port_name,
+				    fcport->loop_id, fcport->d_id.b24);
+
+				set_bit(fcport->loop_id, vha->hw->loop_id_map);
+				spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
+				fcport->chip_reset = vha->hw->base_qpair->chip_reset;
+				fcport->logout_on_delete = 1;
+				fcport->send_els_logo = 0;
+				fcport->fw_login_state = DSC_LS_PRLI_COMP;
+				spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
+
+				qla24xx_post_gpdb_work(vha, fcport, 0);
+			}
 		}
 		break;
 	case MBS_COMMAND_ERROR:
 		ql_dbg(ql_dbg_disc, vha, 0x20eb, "%s %d %8phC cmd error %x\n",
 		    __func__, __LINE__, ea->fcport->port_name, ea->data[1]);
 
-		ea->fcport->flags &= ~FCF_ASYNC_SENT;
-		qla2x00_set_fcport_disc_state(ea->fcport, DSC_LOGIN_FAILED);
-		if (ea->data[1] & QLA_LOGIO_LOGIN_RETRIED)
-			set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
-		else
-			qla2x00_mark_device_lost(vha, ea->fcport, 1);
+		qlt_schedule_sess_for_deletion(ea->fcport);
 		break;
 	case MBS_LOOP_ID_USED:
 		/* data[1] = IO PARAM 1 = nport ID  */
@@ -2257,6 +2498,9 @@
 	    ha->fc4_type_priority != FC4_PRIORITY_NVME)
 		ha->fc4_type_priority = FC4_PRIORITY_FCP;
 
+	/* BVA: Ignore the NVRAM configuration and force the default to FCP. */
+	ha->fc4_type_priority = FC4_PRIORITY_FCP;
+
 	ql_log(ql_log_info, vha, 0xffff, "FC4 priority set to %s\n",
 	       ha->fc4_type_priority == FC4_PRIORITY_FCP ? "FCP" : "NVMe");
 
@@ -2720,6 +2964,49 @@
 	return qla81xx_write_mpi_register(vha, mb);
 }
 
+static int
+qla_chk_risc_recovery(scsi_qla_host_t *vha)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+	__le16 __iomem *mbptr = &reg->mailbox0;
+	int i;
+	u16 mb[32];
+	int rc = QLA_SUCCESS;
+
+	if (!IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+		return rc;
+
+	/* this check is only valid after RISC reset */
+	mb[0] = rd_reg_word(mbptr);
+	mbptr++;
+	if (mb[0] == 0xf) {
+		rc = QLA_FUNCTION_FAILED;
+
+		for (i = 1; i < 32; i++) {
+			mb[i] = rd_reg_word(mbptr);
+			mbptr++;
+		}
+
+		ql_log(ql_log_warn, vha, 0x1015,
+		       "RISC reset failed. mb[0-7] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
+		       mb[0], mb[1], mb[2], mb[3], mb[4], mb[5], mb[6], mb[7]);
+		ql_log(ql_log_warn, vha, 0x1015,
+		       "RISC reset failed. mb[8-15] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
+		       mb[8], mb[9], mb[10], mb[11], mb[12], mb[13], mb[14],
+		       mb[15]);
+		ql_log(ql_log_warn, vha, 0x1015,
+		       "RISC reset failed. mb[16-23] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
+		       mb[16], mb[17], mb[18], mb[19], mb[20], mb[21], mb[22],
+		       mb[23]);
+		ql_log(ql_log_warn, vha, 0x1015,
+		       "RISC reset failed. mb[24-31] %04xh %04xh %04xh %04xh %04xh %04xh %04xh %04xh\n",
+		       mb[24], mb[25], mb[26], mb[27], mb[28], mb[29], mb[30],
+		       mb[31]);
+	}
+	return rc;
+}
+
 /**
  * qla24xx_reset_risc() - Perform full reset of ISP24xx RISC.
  * @vha: HA context
@@ -2736,6 +3023,7 @@
 	uint16_t wd;
 	static int abts_cnt; /* ISP abort retry counts */
 	int rval = QLA_SUCCESS;
+	int print = 1;
 
 	spin_lock_irqsave(&ha->hardware_lock, flags);
 
@@ -2824,17 +3112,26 @@
 	rd_reg_dword(&reg->hccr);
 
 	wrt_reg_dword(&reg->hccr, HCCRX_CLR_RISC_RESET);
+	mdelay(10);
 	rd_reg_dword(&reg->hccr);
 
-	rd_reg_word(&reg->mailbox0);
-	for (cnt = 60; rd_reg_word(&reg->mailbox0) != 0 &&
-	    rval == QLA_SUCCESS; cnt--) {
+	wd = rd_reg_word(&reg->mailbox0);
+	for (cnt = 300; wd != 0 && rval == QLA_SUCCESS; cnt--) {
 		barrier();
-		if (cnt)
-			udelay(5);
-		else
+		if (cnt) {
+			mdelay(1);
+			if (print && qla_chk_risc_recovery(vha))
+				print = 0;
+
+			wd = rd_reg_word(&reg->mailbox0);
+		} else {
 			rval = QLA_FUNCTION_TIMEOUT;
+
+			ql_log(ql_log_warn, vha, 0x015e,
+			       "RISC reset timeout\n");
+		}
 	}
+
 	if (rval == QLA_SUCCESS)
 		set_bit(RISC_RDY_AFT_RESET, &ha->fw_dump_cap_flags);
 
@@ -3224,6 +3521,14 @@
 	struct rsp_que *rsp = ha->rsp_q_map[0];
 	struct qla2xxx_fw_dump *fw_dump;
 
+	if (ha->fw_dump) {
+		ql_dbg(ql_dbg_init, vha, 0x00bd,
+		    "Firmware dump already allocated.\n");
+		return;
+	}
+
+	ha->fw_dumped = 0;
+	ha->fw_dump_cap_flags = 0;
 	dump_size = fixed_size = mem_size = eft_size = fce_size = mq_size = 0;
 	req_q_size = rsp_q_size = 0;
 
@@ -3234,7 +3539,7 @@
 		mem_size = (ha->fw_memory_size - 0x11000 + 1) *
 		    sizeof(uint16_t);
 	} else if (IS_FWI2_CAPABLE(ha)) {
-		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
+		if (IS_QLA83XX(ha))
 			fixed_size = offsetof(struct qla83xx_fw_dump, ext_mem);
 		else if (IS_QLA81XX(ha))
 			fixed_size = offsetof(struct qla81xx_fw_dump, ext_mem);
@@ -3246,8 +3551,7 @@
 		mem_size = (ha->fw_memory_size - 0x100000 + 1) *
 		    sizeof(uint32_t);
 		if (ha->mqenable) {
-			if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha) &&
-			    !IS_QLA28XX(ha))
+			if (!IS_QLA83XX(ha))
 				mq_size = sizeof(struct qla2xxx_mq_chain);
 			/*
 			 * Allocate maximum buffer size for all queues - Q0.
@@ -3288,6 +3592,8 @@
 			    j, fwdt->dump_size);
 			dump_size += fwdt->dump_size;
 		}
+		/* Add space for spare MPI fw dump. */
+		dump_size += ha->fwdt[1].dump_size;
 	} else {
 		req_q_size = req->length * sizeof(request_t);
 		rsp_q_size = rsp->length * sizeof(response_t);
@@ -3327,8 +3633,7 @@
 				    "Re-Allocated (%d KB) and save firmware dump.\n",
 				    dump_size / 1024);
 			} else {
-				if (ha->fw_dump)
-					vfree(ha->fw_dump);
+				vfree(ha->fw_dump);
 				ha->fw_dump = fw_dump;
 
 				ha->fw_dump_len = ha->fw_dump_alloc_len =
@@ -3622,6 +3927,31 @@
 	return ha->flags.lr_detected;
 }
 
+void qla_init_iocb_limit(scsi_qla_host_t *vha)
+{
+	u16 i, num_qps;
+	u32 limit;
+	struct qla_hw_data *ha = vha->hw;
+
+	num_qps = ha->num_qpairs + 1;
+	limit = (ha->orig_fw_iocb_count * QLA_IOCB_PCT_LIMIT) / 100;
+
+	ha->base_qpair->fwres.iocbs_total = ha->orig_fw_iocb_count;
+	ha->base_qpair->fwres.iocbs_limit = limit;
+	ha->base_qpair->fwres.iocbs_qp_limit = limit / num_qps;
+	ha->base_qpair->fwres.iocbs_used = 0;
+	for (i = 0; i < ha->max_qpairs; i++) {
+		if (ha->queue_pair_map[i])  {
+			ha->queue_pair_map[i]->fwres.iocbs_total =
+				ha->orig_fw_iocb_count;
+			ha->queue_pair_map[i]->fwres.iocbs_limit = limit;
+			ha->queue_pair_map[i]->fwres.iocbs_qp_limit =
+				limit / num_qps;
+			ha->queue_pair_map[i]->fwres.iocbs_used = 0;
+		}
+	}
+}
+
 /**
  * qla2x00_setup_chip() - Load and start RISC firmware.
  * @vha: HA context
@@ -3690,9 +4020,7 @@
 					goto execute_fw_with_lr;
 				}
 
-				if ((IS_QLA83XX(ha) || IS_QLA27XX(ha) ||
-				    IS_QLA28XX(ha)) &&
-				    (ha->zio_mode == QLA_ZIO_MODE_6))
+				if (IS_ZIO_THRESHOLD_CAPABLE(ha))
 					qla27xx_set_zio_threshold(vha,
 					    ha->last_zio_threshold);
 
@@ -3723,6 +4051,7 @@
 						    MIN_MULTI_ID_FABRIC - 1;
 				}
 				qla2x00_get_resource_cnts(vha);
+				qla_init_iocb_limit(vha);
 
 				/*
 				 * Allocate the array of outstanding commands
@@ -3749,7 +4078,8 @@
 		}
 
 		/* Enable PUREX PASSTHRU */
-		if (ql2xrdpenable || ha->flags.scm_supported_f)
+		if (ql2xrdpenable || ha->flags.scm_supported_f ||
+		    ha->flags.edif_enabled)
 			qla25xx_set_els_cmds_supported(vha);
 	} else
 		goto failed;
@@ -3782,8 +4112,7 @@
 			    ha->fw_major_version, ha->fw_minor_version,
 			    ha->fw_subminor_version);
 
-			if (IS_QLA83XX(ha) || IS_QLA27XX(ha) ||
-			    IS_QLA28XX(ha)) {
+			if (IS_QLA83XX(ha)) {
 				ha->flags.fac_supported = 0;
 				rval = QLA_SUCCESS;
 			}
@@ -3934,7 +4263,7 @@
 	}
 
 	/* Move PUREX, ABTS RX & RIDA to ATIOQ */
-	if (ql2xmvasynctoatio &&
+	if (ql2xmvasynctoatio && !ha->flags.edif_enabled &&
 	    (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))) {
 		if (qla_tgt_mode_enabled(vha) ||
 		    qla_dual_mode_enabled(vha))
@@ -3953,16 +4282,30 @@
 		    qla_dual_mode_enabled(vha))
 			ha->fw_options[2] |= BIT_4;
 		else
-			ha->fw_options[2] &= ~BIT_4;
+			ha->fw_options[2] &= ~(BIT_4);
 
 		/* Reserve 1/2 of emergency exchanges for ELS.*/
 		if (qla2xuseresexchforels)
 			ha->fw_options[2] |= BIT_8;
 		else
 			ha->fw_options[2] &= ~BIT_8;
+
+		/*
+		 * N2N: set Secure=1 for PLOGI ACC and
+		 * fw shal not send PRLI after PLOGI Acc
+		 */
+		if (ha->flags.edif_enabled &&
+		    DBELL_ACTIVE(vha)) {
+			ha->fw_options[3] |= BIT_15;
+			ha->flags.n2n_fw_acc_sec = 1;
+		} else {
+			ha->fw_options[3] &= ~BIT_15;
+			ha->flags.n2n_fw_acc_sec = 0;
+		}
 	}
 
-	if (ql2xrdpenable || ha->flags.scm_supported_f)
+	if (ql2xrdpenable || ha->flags.scm_supported_f ||
+	    ha->flags.edif_enabled)
 		ha->fw_options[1] |= ADD_FO1_ENABLE_PUREX_IOCB;
 
 	/* Enable Async 8130/8131 events -- transceiver insertion/removal */
@@ -4161,8 +4504,6 @@
 
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
-	ql_dbg(ql_dbg_init, vha, 0x00d1, "Issue init firmware.\n");
-
 	if (IS_QLAFX00(ha)) {
 		rval = qlafx00_init_firmware(vha, ha->init_cb_size);
 		goto next_check;
@@ -4171,6 +4512,12 @@
 	/* Update any ISP specific firmware options before initialization. */
 	ha->isp_ops->update_fw_options(vha);
 
+	ql_dbg(ql_dbg_init, vha, 0x00d1,
+	       "Issue init firmware FW opt 1-3= %08x %08x %08x.\n",
+	       le32_to_cpu(mid_init_cb->init_cb.firmware_options_1),
+	       le32_to_cpu(mid_init_cb->init_cb.firmware_options_2),
+	       le32_to_cpu(mid_init_cb->init_cb.firmware_options_3));
+
 	if (ha->flags.npiv_supported) {
 		if (ha->operating_mode == LOOP && !IS_CNA_CAPABLE(ha))
 			ha->max_npiv_vports = MIN_MULTI_ID_FABRIC - 1;
@@ -4192,8 +4539,14 @@
 			 BIT_6) != 0;
 		ql_dbg(ql_dbg_init, vha, 0x00bc, "FA-WWPN Support: %s.\n",
 		    (ha->flags.fawwpn_enabled) ? "enabled" : "disabled");
+		/* Init_cb will be reused for other command(s).  Save a backup copy of port_name */
+		memcpy(ha->port_name, ha->init_cb->port_name, WWN_SIZE);
 	}
 
+	/* ELS pass through payload is limit by frame size. */
+	if (ha->flags.edif_enabled)
+		mid_init_cb->init_cb.frame_payload_size = cpu_to_le16(ELS_MAX_PAYLOAD);
+
 	rval = qla2x00_init_firmware(vha, ha->init_cb_size);
 next_check:
 	if (rval) {
@@ -4228,8 +4581,6 @@
 	if (IS_QLAFX00(vha->hw))
 		return qlafx00_fw_ready(vha);
 
-	rval = QLA_SUCCESS;
-
 	/* Time to wait for loop down */
 	if (IS_P3P_TYPE(ha))
 		min_wait = 30;
@@ -4405,11 +4756,11 @@
 	/* initialize */
 	ha->min_external_loopid = SNS_FIRST_LOOP_ID;
 	ha->operating_mode = LOOP;
-	ha->switch_cap = 0;
 
 	switch (topo) {
 	case 0:
 		ql_dbg(ql_dbg_disc, vha, 0x200b, "HBA in NL topology.\n");
+		ha->switch_cap = 0;
 		ha->current_topology = ISP_CFG_NL;
 		strcpy(connect_type, "(Loop)");
 		break;
@@ -4423,6 +4774,7 @@
 
 	case 2:
 		ql_dbg(ql_dbg_disc, vha, 0x200d, "HBA in N P2P topology.\n");
+		ha->switch_cap = 0;
 		ha->operating_mode = P2P;
 		ha->current_topology = ISP_CFG_N;
 		strcpy(connect_type, "(N_Port-to-N_Port)");
@@ -4439,6 +4791,7 @@
 	default:
 		ql_dbg(ql_dbg_disc, vha, 0x200f,
 		    "HBA in unknown topology %x, using NL.\n", topo);
+		ha->switch_cap = 0;
 		ha->current_topology = ISP_CFG_NL;
 		strcpy(connect_type, "(Loop)");
 		break;
@@ -4451,7 +4804,10 @@
 	id.b.al_pa = al_pa;
 	id.b.rsvd_1 = 0;
 	spin_lock_irqsave(&ha->hardware_lock, flags);
-	if (!(topo == 2 && ha->flags.n2n_bigger))
+	if (vha->hw->flags.edif_enabled) {
+		if (topo != 2)
+			qlt_update_host_map(vha, id);
+	} else if (!(topo == 2 && ha->flags.n2n_bigger))
 		qlt_update_host_map(vha, id);
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
@@ -4603,18 +4959,18 @@
 			nv->firmware_options[1] = BIT_7 | BIT_5;
 			nv->add_firmware_options[0] = BIT_5;
 			nv->add_firmware_options[1] = BIT_5 | BIT_4;
-			nv->frame_payload_size = 2048;
+			nv->frame_payload_size = cpu_to_le16(2048);
 			nv->special_options[1] = BIT_7;
 		} else if (IS_QLA2200(ha)) {
 			nv->firmware_options[0] = BIT_2 | BIT_1;
 			nv->firmware_options[1] = BIT_7 | BIT_5;
 			nv->add_firmware_options[0] = BIT_5;
 			nv->add_firmware_options[1] = BIT_5 | BIT_4;
-			nv->frame_payload_size = 1024;
+			nv->frame_payload_size = cpu_to_le16(1024);
 		} else if (IS_QLA2100(ha)) {
 			nv->firmware_options[0] = BIT_3 | BIT_1;
 			nv->firmware_options[1] = BIT_5;
-			nv->frame_payload_size = 1024;
+			nv->frame_payload_size = cpu_to_le16(1024);
 		}
 
 		nv->max_iocb_allocation = cpu_to_le16(256);
@@ -4925,6 +5281,9 @@
 	fcport->login_retry = vha->hw->login_retry_count;
 	fcport->chip_reset = vha->hw->base_qpair->chip_reset;
 	fcport->logout_on_delete = 1;
+	fcport->tgt_link_down_time = QLA2XX_MAX_LINK_DOWN_TIME;
+	fcport->tgt_short_link_down_cnt = 0;
+	fcport->dev_loss_tmo = 0;
 
 	if (!fcport->ct_desc.ct_sns) {
 		ql_log(ql_log_warn, vha, 0xd049,
@@ -4939,6 +5298,16 @@
 	INIT_LIST_HEAD(&fcport->gnl_entry);
 	INIT_LIST_HEAD(&fcport->list);
 
+	INIT_LIST_HEAD(&fcport->sess_cmd_list);
+	spin_lock_init(&fcport->sess_cmd_lock);
+
+	spin_lock_init(&fcport->edif.sa_list_lock);
+	INIT_LIST_HEAD(&fcport->edif.tx_sa_list);
+	INIT_LIST_HEAD(&fcport->edif.rx_sa_list);
+
+	spin_lock_init(&fcport->edif.indx_list_lock);
+	INIT_LIST_HEAD(&fcport->edif.edif_indx_list);
+
 	return fcport;
 }
 
@@ -4952,11 +5321,39 @@
 
 		fcport->ct_desc.ct_sns = NULL;
 	}
+
+	qla_edif_flush_sa_ctl_lists(fcport);
 	list_del(&fcport->list);
 	qla2x00_clear_loop_id(fcport);
+
+	qla_edif_list_del(fcport);
+
 	kfree(fcport);
 }
 
+static void qla_get_login_template(scsi_qla_host_t *vha)
+{
+	struct qla_hw_data *ha = vha->hw;
+	int rval;
+	u32 *bp, sz;
+	__be32 *q;
+
+	memset(ha->init_cb, 0, ha->init_cb_size);
+	sz = min_t(int, sizeof(struct fc_els_flogi), ha->init_cb_size);
+	rval = qla24xx_get_port_login_templ(vha, ha->init_cb_dma,
+					    ha->init_cb, sz);
+	if (rval != QLA_SUCCESS) {
+		ql_dbg(ql_dbg_init, vha, 0x00d1,
+		       "PLOGI ELS param read fail.\n");
+		return;
+	}
+	q = (__be32 *)&ha->plogi_els_payld.fl_csp;
+
+	bp = (uint32_t *)ha->init_cb;
+	cpu_to_be32_array(q, bp, sz / 4);
+	ha->flags.plogi_template_valid = 1;
+}
+
 /*
  * qla2x00_configure_loop
  *      Updates Fibre Channel Device Database with what is actually on loop.
@@ -5000,6 +5397,7 @@
 	clear_bit(RSCN_UPDATE, &vha->dpc_flags);
 
 	qla2x00_get_data_rate(vha);
+	qla_get_login_template(vha);
 
 	/* Determine what we need to do */
 	if ((ha->current_topology == ISP_CFG_FL ||
@@ -5049,6 +5447,14 @@
 			ha->flags.fw_init_done = 1;
 
 			/*
+			 * use link up to wake up app to get ready for
+			 * authentication.
+			 */
+			if (ha->flags.edif_enabled && DBELL_INACTIVE(vha))
+				qla2x00_post_aen_work(vha, FCH_EVT_LINKUP,
+						      ha->link_data_rate);
+
+			/*
 			 * Process any ATIO queue entries that came in
 			 * while we weren't online.
 			 */
@@ -5067,7 +5473,8 @@
 		    "%s *** FAILED ***.\n", __func__);
 	} else {
 		ql_dbg(ql_dbg_disc, vha, 0x206b,
-		    "%s: exiting normally.\n", __func__);
+		    "%s: exiting normally. local port wwpn %8phN id %06x)\n",
+		    __func__, vha->port_name, vha->d_id.b24);
 	}
 
 	/* Restore state if a resync event occurred during processing */
@@ -5084,32 +5491,13 @@
 
 static int qla2x00_configure_n2n_loop(scsi_qla_host_t *vha)
 {
-	struct qla_hw_data *ha = vha->hw;
 	unsigned long flags;
 	fc_port_t *fcport;
-	int rval;
 
-	if (test_and_clear_bit(N2N_LOGIN_NEEDED, &vha->dpc_flags)) {
-		/* borrowing */
-		u32 *bp, sz;
+	ql_dbg(ql_dbg_disc, vha, 0x206a, "%s %d.\n", __func__, __LINE__);
 
-		memset(ha->init_cb, 0, ha->init_cb_size);
-		sz = min_t(int, sizeof(struct els_plogi_payload),
-			   ha->init_cb_size);
-		rval = qla24xx_get_port_login_templ(vha, ha->init_cb_dma,
-						    ha->init_cb, sz);
-		if (rval == QLA_SUCCESS) {
-			__be32 *q = &ha->plogi_els_payld.data[0];
-
-			bp = (uint32_t *)ha->init_cb;
-			cpu_to_be32_array(q, bp, sz / 4);
-			memcpy(bp, q, sizeof(ha->plogi_els_payld.data));
-		} else {
-			ql_dbg(ql_dbg_init, vha, 0x00d1,
-			       "PLOGI ELS param read fail.\n");
-			goto skip_login;
-		}
-	}
+	if (test_and_clear_bit(N2N_LOGIN_NEEDED, &vha->dpc_flags))
+		set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
 
 	list_for_each_entry(fcport, &vha->vp_fcports, list) {
 		if (fcport->n2n_flag) {
@@ -5118,7 +5506,6 @@
 		}
 	}
 
-skip_login:
 	spin_lock_irqsave(&vha->work_lock, flags);
 	vha->scan.scan_retry++;
 	spin_unlock_irqrestore(&vha->work_lock, flags);
@@ -5130,6 +5517,22 @@
 	return QLA_FUNCTION_FAILED;
 }
 
+static void
+qla_reinitialize_link(scsi_qla_host_t *vha)
+{
+	int rval;
+
+	atomic_set(&vha->loop_state, LOOP_DOWN);
+	atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+	rval = qla2x00_full_login_lip(vha);
+	if (rval == QLA_SUCCESS) {
+		ql_dbg(ql_dbg_disc, vha, 0xd050, "Link reinitialized\n");
+	} else {
+		ql_dbg(ql_dbg_disc, vha, 0xd051,
+			"Link reinitialization failed (%d)\n", rval);
+	}
+}
+
 /*
  * qla2x00_configure_local_loop
  *	Updates Fibre Channel Device Database with local loop devices.
@@ -5144,7 +5547,6 @@
 qla2x00_configure_local_loop(scsi_qla_host_t *vha)
 {
 	int		rval, rval2;
-	int		found_devs;
 	int		found;
 	fc_port_t	*fcport, *new_fcport;
 	uint16_t	index;
@@ -5159,7 +5561,6 @@
 	if (N2N_TOPO(ha))
 		return qla2x00_configure_n2n_loop(vha);
 
-	found_devs = 0;
 	new_fcport = NULL;
 	entries = MAX_FIBRE_DEVICES_LOOP;
 
@@ -5181,6 +5582,19 @@
 		spin_unlock_irqrestore(&vha->work_lock, flags);
 
 		if (vha->scan.scan_retry < MAX_SCAN_RETRIES) {
+			u8 loop_map_entries = 0;
+			int rc;
+
+			rc = qla2x00_get_fcal_position_map(vha, NULL,
+						&loop_map_entries);
+			if (rc == QLA_SUCCESS && loop_map_entries > 1) {
+				/*
+				 * There are devices that are still not logged
+				 * in. Reinitialize to give them a chance.
+				 */
+				qla_reinitialize_link(vha);
+				return QLA_FUNCTION_FAILED;
+			}
 			set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
 			set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
 		}
@@ -5269,6 +5683,13 @@
 			memcpy(fcport->node_name, new_fcport->node_name,
 			    WWN_SIZE);
 			fcport->scan_state = QLA_FCPORT_FOUND;
+			if (fcport->login_retry == 0) {
+				fcport->login_retry = vha->hw->login_retry_count;
+				ql_dbg(ql_dbg_disc, vha, 0x2135,
+				    "Port login retry %8phN, lid 0x%04x retry cnt=%d.\n",
+				    fcport->port_name, fcport->loop_id,
+				    fcport->login_retry);
+			}
 			found++;
 			break;
 		}
@@ -5298,8 +5719,6 @@
 
 		/* Base iIDMA settings on HBA port speed. */
 		fcport->fp_speed = ha->link_data_rate;
-
-		found_devs++;
 	}
 
 	list_for_each_entry(fcport, &vha->vp_fcports, list) {
@@ -5417,6 +5836,7 @@
 	spin_lock_irqsave(fcport->vha->host->host_lock, flags);
 	*((fc_port_t **)rport->dd_data) = fcport;
 	spin_unlock_irqrestore(fcport->vha->host->host_lock, flags);
+	fcport->dev_loss_tmo = rport->dev_loss_tmo;
 
 	rport->supported_classes = fcport->supported_classes;
 
@@ -5432,13 +5852,14 @@
 	if (fcport->port_type & FCT_NVME_DISCOVERY)
 		rport_ids.roles |= FC_PORT_ROLE_NVME_DISCOVERY;
 
+	fc_remote_port_rolechg(rport, rport_ids.roles);
+
 	ql_dbg(ql_dbg_disc, vha, 0x20ee,
-	    "%s %8phN. rport %p is %s mode\n",
-	    __func__, fcport->port_name, rport,
+	    "%s: %8phN. rport %ld:0:%d (%p) is %s mode\n",
+	    __func__, fcport->port_name, vha->host_no,
+	    rport->scsi_target_id, rport,
 	    (fcport->port_type == FCT_TARGET) ? "tgt" :
 	    ((fcport->port_type & FCT_NVME) ? "nvme" : "ini"));
-
-	fc_remote_port_rolechg(rport, rport_ids.roles);
 }
 
 /*
@@ -5475,6 +5896,11 @@
 		fcport->logout_on_delete = 1;
 	fcport->n2n_chip_reset = fcport->n2n_link_reset_cnt = 0;
 
+	if (fcport->tgt_link_down_time < fcport->dev_loss_tmo) {
+		fcport->tgt_short_link_down_cnt++;
+		fcport->tgt_link_down_time = QLA2XX_MAX_LINK_DOWN_TIME;
+	}
+
 	switch (vha->hw->current_topology) {
 	case ISP_CFG_N:
 	case ISP_CFG_NL:
@@ -5486,12 +5912,7 @@
 
 	qla2x00_iidma_fcport(vha, fcport);
 
-	if (NVME_TARGET(vha->hw, fcport)) {
-		qla_nvme_register_remote(vha, fcport);
-		qla2x00_set_fcport_disc_state(fcport, DSC_LOGIN_COMPLETE);
-		qla2x00_set_fcport_state(fcport, FCS_ONLINE);
-		return;
-	}
+	qla2x00_dfs_create_rport(vha, fcport);
 
 	qla24xx_update_fcport_fcp_prio(vha, fcport);
 
@@ -5514,6 +5935,9 @@
 		break;
 	}
 
+	if (NVME_TARGET(vha->hw, fcport))
+		qla_nvme_register_remote(vha, fcport);
+
 	qla2x00_set_fcport_state(fcport, FCS_ONLINE);
 
 	if (IS_IIDMA_CAPABLE(vha->hw) && vha->hw->flags.gpsc_supported) {
@@ -5547,6 +5971,10 @@
 
 	qla2x00_update_fcport(fcport->vha, fcport);
 
+	ql_dbg(ql_dbg_disc, fcport->vha, 0x911e,
+	       "%s rscn gen %d/%d next DS %d\n", __func__,
+	       rscn_gen, fcport->rscn_gen, fcport->next_disc_state);
+
 	if (rscn_gen != fcport->rscn_gen) {
 		/* RSCN(s) came in while registration */
 		switch (fcport->next_disc_state) {
@@ -5907,6 +6335,9 @@
 				break;
 			}
 
+			if (fcport->login_retry == 0)
+				fcport->login_retry =
+					vha->hw->login_retry_count;
 			/*
 			 * If device was not a fabric device before.
 			 */
@@ -6313,13 +6744,13 @@
 qla2x00_update_fcports(scsi_qla_host_t *base_vha)
 {
 	fc_port_t *fcport;
-	struct scsi_qla_host *vha;
+	struct scsi_qla_host *vha, *tvp;
 	struct qla_hw_data *ha = base_vha->hw;
 	unsigned long flags;
 
 	spin_lock_irqsave(&ha->vport_slock, flags);
 	/* Go with deferred removal of rport references. */
-	list_for_each_entry(vha, &base_vha->hw->vp_list, list) {
+	list_for_each_entry_safe(vha, tvp, &base_vha->hw->vp_list, list) {
 		atomic_inc(&vha->vref_count);
 		list_for_each_entry(fcport, &vha->vp_fcports, list) {
 			if (fcport->drport &&
@@ -6428,29 +6859,6 @@
 	return rval;
 }
 
-static const char *
-qla83xx_dev_state_to_string(uint32_t dev_state)
-{
-	switch (dev_state) {
-	case QLA8XXX_DEV_COLD:
-		return "COLD/RE-INIT";
-	case QLA8XXX_DEV_INITIALIZING:
-		return "INITIALIZING";
-	case QLA8XXX_DEV_READY:
-		return "READY";
-	case QLA8XXX_DEV_NEED_RESET:
-		return "NEED RESET";
-	case QLA8XXX_DEV_NEED_QUIESCENT:
-		return "NEED QUIESCENT";
-	case QLA8XXX_DEV_FAILED:
-		return "FAILED";
-	case QLA8XXX_DEV_QUIESCENT:
-		return "QUIESCENT";
-	default:
-		return "Unknown";
-	}
-}
-
 /* Assumes idc-lock always held on entry */
 void
 qla83xx_idc_audit(scsi_qla_host_t *vha, int audit_type)
@@ -6504,9 +6912,8 @@
 		ql_log(ql_log_info, vha, 0xb056, "HW State: NEED RESET.\n");
 		qla83xx_idc_audit(vha, IDC_AUDIT_TIMESTAMP);
 	} else {
-		const char *state = qla83xx_dev_state_to_string(dev_state);
-
-		ql_log(ql_log_info, vha, 0xb057, "HW State: %s.\n", state);
+		ql_log(ql_log_info, vha, 0xb057, "HW State: %s.\n",
+				qdev_state(dev_state));
 
 		/* SV: XXX: Is timeout required here? */
 		/* Wait for IDC state change READY -> NEED_RESET */
@@ -6664,7 +7071,8 @@
 qla2x00_quiesce_io(scsi_qla_host_t *vha)
 {
 	struct qla_hw_data *ha = vha->hw;
-	struct scsi_qla_host *vp;
+	struct scsi_qla_host *vp, *tvp;
+	unsigned long flags;
 
 	ql_dbg(ql_dbg_dpc, vha, 0x401d,
 	    "Quiescing I/O - ha=%p.\n", ha);
@@ -6673,8 +7081,18 @@
 	if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
 		atomic_set(&vha->loop_state, LOOP_DOWN);
 		qla2x00_mark_all_devices_lost(vha);
-		list_for_each_entry(vp, &ha->vp_list, list)
+
+		spin_lock_irqsave(&ha->vport_slock, flags);
+		list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) {
+			atomic_inc(&vp->vref_count);
+			spin_unlock_irqrestore(&ha->vport_slock, flags);
+
 			qla2x00_mark_all_devices_lost(vp);
+
+			spin_lock_irqsave(&ha->vport_slock, flags);
+			atomic_dec(&vp->vref_count);
+		}
+		spin_unlock_irqrestore(&ha->vport_slock, flags);
 	} else {
 		if (!atomic_read(&vha->loop_down_timer))
 			atomic_set(&vha->loop_down_timer,
@@ -6689,7 +7107,7 @@
 qla2x00_abort_isp_cleanup(scsi_qla_host_t *vha)
 {
 	struct qla_hw_data *ha = vha->hw;
-	struct scsi_qla_host *vp;
+	struct scsi_qla_host *vp, *tvp;
 	unsigned long flags;
 	fc_port_t *fcport;
 	u16 i;
@@ -6724,10 +7142,16 @@
 	ha->flags.fw_init_done = 0;
 	ha->chip_reset++;
 	ha->base_qpair->chip_reset = ha->chip_reset;
+	ha->base_qpair->cmd_cnt = ha->base_qpair->cmd_completion_cnt = 0;
+	ha->base_qpair->prev_completion_cnt = 0;
 	for (i = 0; i < ha->max_qpairs; i++) {
-		if (ha->queue_pair_map[i])
+		if (ha->queue_pair_map[i]) {
 			ha->queue_pair_map[i]->chip_reset =
 				ha->base_qpair->chip_reset;
+			ha->queue_pair_map[i]->cmd_cnt =
+			    ha->queue_pair_map[i]->cmd_completion_cnt = 0;
+			ha->base_qpair->prev_completion_cnt = 0;
+		}
 	}
 
 	/* purge MBox commands */
@@ -6753,7 +7177,7 @@
 		qla2x00_mark_all_devices_lost(vha);
 
 		spin_lock_irqsave(&ha->vport_slock, flags);
-		list_for_each_entry(vp, &ha->vp_list, list) {
+		list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) {
 			atomic_inc(&vp->vref_count);
 			spin_unlock_irqrestore(&ha->vport_slock, flags);
 
@@ -6775,7 +7199,7 @@
 		fcport->scan_state = 0;
 	}
 	spin_lock_irqsave(&ha->vport_slock, flags);
-	list_for_each_entry(vp, &ha->vp_list, list) {
+	list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) {
 		atomic_inc(&vp->vref_count);
 		spin_unlock_irqrestore(&ha->vport_slock, flags);
 
@@ -6787,22 +7211,18 @@
 	}
 	spin_unlock_irqrestore(&ha->vport_slock, flags);
 
-	if (!ha->flags.eeh_busy) {
-		/* Make sure for ISP 82XX IO DMA is complete */
-		if (IS_P3P_TYPE(ha)) {
-			qla82xx_chip_reset_cleanup(vha);
-			ql_log(ql_log_info, vha, 0x00b4,
-			    "Done chip reset cleanup.\n");
+	/* Make sure for ISP 82XX IO DMA is complete */
+	if (IS_P3P_TYPE(ha)) {
+		qla82xx_chip_reset_cleanup(vha);
+		ql_log(ql_log_info, vha, 0x00b4,
+		       "Done chip reset cleanup.\n");
 
-			/* Done waiting for pending commands.
-			 * Reset the online flag.
-			 */
-			vha->flags.online = 0;
-		}
-
-		/* Requeue all commands in outstanding command list. */
-		qla2x00_abort_all_cmds(vha, DID_RESET << 16);
+		/* Done waiting for pending commands. Reset online flag */
+		vha->flags.online = 0;
 	}
+
+	/* Requeue all commands in outstanding command list. */
+	qla2x00_abort_all_cmds(vha, DID_RESET << 16);
 	/* memory barrier */
 	wmb();
 }
@@ -6823,13 +7243,25 @@
 	int rval;
 	uint8_t        status = 0;
 	struct qla_hw_data *ha = vha->hw;
-	struct scsi_qla_host *vp;
+	struct scsi_qla_host *vp, *tvp;
 	struct req_que *req = ha->req_q_map[0];
 	unsigned long flags;
 
 	if (vha->flags.online) {
 		qla2x00_abort_isp_cleanup(vha);
 
+		vha->dport_status |= DPORT_DIAG_CHIP_RESET_IN_PROGRESS;
+		vha->dport_status &= ~DPORT_DIAG_IN_PROGRESS;
+
+		if (vha->hw->flags.port_isolated)
+			return status;
+
+		if (qla2x00_isp_reg_stat(ha)) {
+			ql_log(ql_log_info, vha, 0x803f,
+			       "ISP Abort - ISP reg disconnect, exiting.\n");
+			return status;
+		}
+
 		if (test_and_clear_bit(ISP_ABORT_TO_ROM, &vha->dpc_flags)) {
 			ha->flags.chip_reset_done = 1;
 			vha->flags.online = 1;
@@ -6859,7 +7291,8 @@
 				return 0;
 			break;
 		case QLA2XXX_INI_MODE_DUAL:
-			if (!qla_dual_mode_enabled(vha))
+			if (!qla_dual_mode_enabled(vha) &&
+			    !qla_ini_mode_enabled(vha))
 				return 0;
 			break;
 		case QLA2XXX_INI_MODE_ENABLED:
@@ -6869,8 +7302,18 @@
 
 		ha->isp_ops->get_flash_version(vha, req->ring);
 
+		if (qla2x00_isp_reg_stat(ha)) {
+			ql_log(ql_log_info, vha, 0x803f,
+			       "ISP Abort - ISP reg disconnect pre nvram config, exiting.\n");
+			return status;
+		}
 		ha->isp_ops->nvram_config(vha);
 
+		if (qla2x00_isp_reg_stat(ha)) {
+			ql_log(ql_log_info, vha, 0x803f,
+			       "ISP Abort - ISP reg disconnect post nvmram config, exiting.\n");
+			return status;
+		}
 		if (!qla2x00_restart_isp(vha)) {
 			clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
 
@@ -6951,11 +7394,16 @@
 
 	}
 
+	if (vha->hw->flags.port_isolated) {
+		qla2x00_abort_isp_cleanup(vha);
+		return status;
+	}
+
 	if (!status) {
 		ql_dbg(ql_dbg_taskm, vha, 0x8022, "%s succeeded.\n", __func__);
 		qla2x00_configure_hba(vha);
 		spin_lock_irqsave(&ha->vport_slock, flags);
-		list_for_each_entry(vp, &ha->vp_list, list) {
+		list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) {
 			if (vp->vp_idx) {
 				atomic_inc(&vp->vref_count);
 				spin_unlock_irqrestore(&ha->vport_slock, flags);
@@ -7109,10 +7557,9 @@
 	unsigned long flags = 0;
 	struct qla_hw_data *ha = vha->hw;
 	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
-	int rval = QLA_SUCCESS;
 
 	if (IS_P3P_TYPE(ha))
-		return rval;
+		return QLA_SUCCESS;
 
 	vha->flags.online = 0;
 	ha->isp_ops->disable_intrs(ha);
@@ -7127,7 +7574,7 @@
 	if (IS_NOPOLLING_TYPE(ha))
 		ha->isp_ops->enable_intrs(ha);
 
-	return rval;
+	return QLA_SUCCESS;
 }
 
 /* On sparc systems, obtain port and node WWN from firmware
@@ -7483,6 +7930,9 @@
 
 	active_regions->aux.npiv_config_2_3 =
 	    qla28xx_component_bitmask(aux, QLA28XX_AUX_IMG_NPIV_CONFIG_2_3);
+
+	active_regions->aux.nvme_params =
+	    qla28xx_component_bitmask(aux, QLA28XX_AUX_IMG_NVME_PARAMS);
 }
 
 static int
@@ -7591,11 +8041,12 @@
 	}
 
 	ql_dbg(ql_dbg_init, vha, 0x018f,
-	    "aux images active: BCFG=%u VPD/NVR=%u NPIV0/1=%u NPIV2/3=%u\n",
+	    "aux images active: BCFG=%u VPD/NVR=%u NPIV0/1=%u NPIV2/3=%u, NVME=%u\n",
 	    active_regions->aux.board_config,
 	    active_regions->aux.vpd_nvram,
 	    active_regions->aux.npiv_config_0_1,
-	    active_regions->aux.npiv_config_2_3);
+	    active_regions->aux.npiv_config_2_3,
+	    active_regions->aux.nvme_params);
 }
 
 void
@@ -7778,8 +8229,7 @@
 	templates = (risc_attr & BIT_9) ? 2 : 1;
 	ql_dbg(ql_dbg_init, vha, 0x0160, "-> templates = %u\n", templates);
 	for (j = 0; j < templates; j++, fwdt++) {
-		if (fwdt->template)
-			vfree(fwdt->template);
+		vfree(fwdt->template);
 		fwdt->template = NULL;
 		fwdt->length = 0;
 
@@ -7839,8 +8289,7 @@
 	return QLA_SUCCESS;
 
 failed:
-	if (fwdt->template)
-		vfree(fwdt->template);
+	vfree(fwdt->template);
 	fwdt->template = NULL;
 	fwdt->length = 0;
 
@@ -8036,8 +8485,7 @@
 	templates = (risc_attr & BIT_9) ? 2 : 1;
 	ql_dbg(ql_dbg_init, vha, 0x0170, "-> templates = %u\n", templates);
 	for (j = 0; j < templates; j++, fwdt++) {
-		if (fwdt->template)
-			vfree(fwdt->template);
+		vfree(fwdt->template);
 		fwdt->template = NULL;
 		fwdt->length = 0;
 
@@ -8097,8 +8545,7 @@
 	return QLA_SUCCESS;
 
 failed:
-	if (fwdt->template)
-		vfree(fwdt->template);
+	vfree(fwdt->template);
 	fwdt->template = NULL;
 	fwdt->length = 0;
 
@@ -8645,7 +9092,7 @@
 {
 	int status, rval;
 	struct qla_hw_data *ha = vha->hw;
-	struct scsi_qla_host *vp;
+	struct scsi_qla_host *vp, *tvp;
 	unsigned long flags;
 
 	status = qla2x00_init_rings(vha);
@@ -8717,7 +9164,7 @@
 		    "qla82xx_restart_isp succeeded.\n");
 
 		spin_lock_irqsave(&ha->vport_slock, flags);
-		list_for_each_entry(vp, &ha->vp_list, list) {
+		list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) {
 			if (vp->vp_idx) {
 				atomic_inc(&vp->vref_count);
 				spin_unlock_irqrestore(&ha->vport_slock, flags);
@@ -9094,3 +9541,215 @@
 fail:
 	return ret;
 }
+
+uint64_t
+qla2x00_count_set_bits(uint32_t num)
+{
+	/* Brian Kernighan's Algorithm */
+	u64 count = 0;
+
+	while (num) {
+		num &= (num - 1);
+		count++;
+	}
+	return count;
+}
+
+uint64_t
+qla2x00_get_num_tgts(scsi_qla_host_t *vha)
+{
+	fc_port_t *f, *tf;
+	u64 count = 0;
+
+	f = NULL;
+	tf = NULL;
+
+	list_for_each_entry_safe(f, tf, &vha->vp_fcports, list) {
+		if (f->port_type != FCT_TARGET)
+			continue;
+		count++;
+	}
+	return count;
+}
+
+int qla2xxx_reset_stats(struct Scsi_Host *host, u32 flags)
+{
+	scsi_qla_host_t *vha = shost_priv(host);
+	fc_port_t *fcport = NULL;
+	unsigned long int_flags;
+
+	if (flags & QLA2XX_HW_ERROR)
+		vha->hw_err_cnt = 0;
+	if (flags & QLA2XX_SHT_LNK_DWN)
+		vha->short_link_down_cnt = 0;
+	if (flags & QLA2XX_INT_ERR)
+		vha->interface_err_cnt = 0;
+	if (flags & QLA2XX_CMD_TIMEOUT)
+		vha->cmd_timeout_cnt = 0;
+	if (flags & QLA2XX_RESET_CMD_ERR)
+		vha->reset_cmd_err_cnt = 0;
+	if (flags & QLA2XX_TGT_SHT_LNK_DOWN) {
+		spin_lock_irqsave(&vha->hw->tgt.sess_lock, int_flags);
+		list_for_each_entry(fcport, &vha->vp_fcports, list) {
+			fcport->tgt_short_link_down_cnt = 0;
+			fcport->tgt_link_down_time = QLA2XX_MAX_LINK_DOWN_TIME;
+		}
+		spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, int_flags);
+	}
+	vha->link_down_time = QLA2XX_MAX_LINK_DOWN_TIME;
+	return 0;
+}
+
+int qla2xxx_start_stats(struct Scsi_Host *host, u32 flags)
+{
+	return qla2xxx_reset_stats(host, flags);
+}
+
+int qla2xxx_stop_stats(struct Scsi_Host *host, u32 flags)
+{
+	return qla2xxx_reset_stats(host, flags);
+}
+
+int qla2xxx_get_ini_stats(struct Scsi_Host *host, u32 flags,
+			  void *data, u64 size)
+{
+	scsi_qla_host_t *vha = shost_priv(host);
+	struct ql_vnd_host_stats_resp *resp = (struct ql_vnd_host_stats_resp *)data;
+	struct ql_vnd_stats *rsp_data = &resp->stats;
+	u64 ini_entry_count = 0;
+	u64 i = 0;
+	u64 entry_count = 0;
+	u64 num_tgt = 0;
+	u32 tmp_stat_type = 0;
+	fc_port_t *fcport = NULL;
+	unsigned long int_flags;
+
+	/* Copy stat type to work on it */
+	tmp_stat_type = flags;
+
+	if (tmp_stat_type & BIT_17) {
+		num_tgt = qla2x00_get_num_tgts(vha);
+		/* unset BIT_17 */
+		tmp_stat_type &= ~(1 << 17);
+	}
+	ini_entry_count = qla2x00_count_set_bits(tmp_stat_type);
+
+	entry_count = ini_entry_count + num_tgt;
+
+	rsp_data->entry_count = entry_count;
+
+	i = 0;
+	if (flags & QLA2XX_HW_ERROR) {
+		rsp_data->entry[i].stat_type = QLA2XX_HW_ERROR;
+		rsp_data->entry[i].tgt_num = 0x0;
+		rsp_data->entry[i].cnt = vha->hw_err_cnt;
+		i++;
+	}
+
+	if (flags & QLA2XX_SHT_LNK_DWN) {
+		rsp_data->entry[i].stat_type = QLA2XX_SHT_LNK_DWN;
+		rsp_data->entry[i].tgt_num = 0x0;
+		rsp_data->entry[i].cnt = vha->short_link_down_cnt;
+		i++;
+	}
+
+	if (flags & QLA2XX_INT_ERR) {
+		rsp_data->entry[i].stat_type = QLA2XX_INT_ERR;
+		rsp_data->entry[i].tgt_num = 0x0;
+		rsp_data->entry[i].cnt = vha->interface_err_cnt;
+		i++;
+	}
+
+	if (flags & QLA2XX_CMD_TIMEOUT) {
+		rsp_data->entry[i].stat_type = QLA2XX_CMD_TIMEOUT;
+		rsp_data->entry[i].tgt_num = 0x0;
+		rsp_data->entry[i].cnt = vha->cmd_timeout_cnt;
+		i++;
+	}
+
+	if (flags & QLA2XX_RESET_CMD_ERR) {
+		rsp_data->entry[i].stat_type = QLA2XX_RESET_CMD_ERR;
+		rsp_data->entry[i].tgt_num = 0x0;
+		rsp_data->entry[i].cnt = vha->reset_cmd_err_cnt;
+		i++;
+	}
+
+	/* i will continue from previous loop, as target
+	 * entries are after initiator
+	 */
+	if (flags & QLA2XX_TGT_SHT_LNK_DOWN) {
+		spin_lock_irqsave(&vha->hw->tgt.sess_lock, int_flags);
+		list_for_each_entry(fcport, &vha->vp_fcports, list) {
+			if (fcport->port_type != FCT_TARGET)
+				continue;
+			if (!fcport->rport)
+				continue;
+			rsp_data->entry[i].stat_type = QLA2XX_TGT_SHT_LNK_DOWN;
+			rsp_data->entry[i].tgt_num = fcport->rport->number;
+			rsp_data->entry[i].cnt = fcport->tgt_short_link_down_cnt;
+			i++;
+		}
+		spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, int_flags);
+	}
+	resp->status = EXT_STATUS_OK;
+
+	return 0;
+}
+
+int qla2xxx_get_tgt_stats(struct Scsi_Host *host, u32 flags,
+			  struct fc_rport *rport, void *data, u64 size)
+{
+	struct ql_vnd_tgt_stats_resp *tgt_data = data;
+	fc_port_t *fcport = *(fc_port_t **)rport->dd_data;
+
+	tgt_data->status = 0;
+	tgt_data->stats.entry_count = 1;
+	tgt_data->stats.entry[0].stat_type = flags;
+	tgt_data->stats.entry[0].tgt_num = rport->number;
+	tgt_data->stats.entry[0].cnt = fcport->tgt_short_link_down_cnt;
+
+	return 0;
+}
+
+int qla2xxx_disable_port(struct Scsi_Host *host)
+{
+	scsi_qla_host_t *vha = shost_priv(host);
+
+	vha->hw->flags.port_isolated = 1;
+
+	if (qla2x00_isp_reg_stat(vha->hw)) {
+		ql_log(ql_log_info, vha, 0x9006,
+		    "PCI/Register disconnect, exiting.\n");
+		qla_pci_set_eeh_busy(vha);
+		return FAILED;
+	}
+	if (qla2x00_chip_is_down(vha))
+		return 0;
+
+	if (vha->flags.online) {
+		qla2x00_abort_isp_cleanup(vha);
+		qla2x00_wait_for_sess_deletion(vha);
+	}
+
+	return 0;
+}
+
+int qla2xxx_enable_port(struct Scsi_Host *host)
+{
+	scsi_qla_host_t *vha = shost_priv(host);
+
+	if (qla2x00_isp_reg_stat(vha->hw)) {
+		ql_log(ql_log_info, vha, 0x9001,
+		    "PCI/Register disconnect, exiting.\n");
+		qla_pci_set_eeh_busy(vha);
+		return FAILED;
+	}
+
+	vha->hw->flags.port_isolated = 0;
+	/* Set the flag to 1, so that isp_abort can proceed */
+	vha->flags.online = 1;
+	set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+	qla2xxx_wake_dpc(vha);
+
+	return 0;
+}
diff --git a/scst/qla2x00t-32gbit/qla_inline.h b/scst/qla2x00t-32gbit/qla_inline.h
index 80b9ef7..27d2a5c 100644
--- a/scst/qla2x00t-32gbit/qla_inline.h
+++ b/scst/qla2x00t-32gbit/qla_inline.h
@@ -1,8 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 
 #ifdef INSIDE_KERNEL_TREE
@@ -193,6 +192,8 @@
 	sp->vha = vha;
 	sp->qpair = qpair;
 	sp->cmd_type = TYPE_SRB;
+	/* ref : INIT - normal flow */
+	kref_init(&sp->cmd_kref);
 	INIT_LIST_HEAD(&sp->elem);
 }
 
@@ -215,10 +216,15 @@
 	return sp;
 }
 
+void qla2xxx_rel_done_warning(srb_t *sp, int res);
+void qla2xxx_rel_free_warning(srb_t *sp);
+
 static inline void
 qla2xxx_rel_qpair_sp(struct qla_qpair *qpair, srb_t *sp)
 {
 	sp->qpair = NULL;
+	sp->done = qla2xxx_rel_done_warning;
+	sp->free = qla2xxx_rel_free_warning;
 	mempool_free(sp, qpair->srb_mempool);
 	QLA_QPAIR_MARK_NOT_BUSY(qpair);
 }
@@ -227,11 +233,9 @@
 qla2x00_get_sp(scsi_qla_host_t *vha, fc_port_t *fcport, gfp_t flag)
 {
 	srb_t *sp = NULL;
-	uint8_t bail;
 	struct qla_qpair *qpair;
 
-	QLA_VHA_MARK_BUSY(vha, bail);
-	if (unlikely(bail))
+	if (unlikely(qla_vha_mark_busy(vha)))
 		return NULL;
 
 	qpair = vha->hw->base_qpair;
@@ -274,11 +278,41 @@
 }
 
 static inline void
-qla2x00_set_retry_delay_timestamp(fc_port_t *fcport, uint16_t retry_delay)
+qla2x00_set_retry_delay_timestamp(fc_port_t *fcport, uint16_t sts_qual)
 {
-	if (retry_delay)
-		fcport->retry_delay_timestamp = jiffies +
-		    (retry_delay * HZ / 10);
+	u8 scope;
+	u16 qual;
+#define SQ_SCOPE_MASK		0xc000 /* SAM-6 rev5 5.3.2 */
+#define SQ_SCOPE_SHIFT		14
+#define SQ_QUAL_MASK		0x3fff
+
+#define SQ_MAX_WAIT_SEC		60 /* Max I/O hold off time in seconds. */
+#define SQ_MAX_WAIT_TIME	(SQ_MAX_WAIT_SEC * 10) /* in 100ms. */
+
+	if (!sts_qual) /* Common case. */
+		return;
+
+	scope = (sts_qual & SQ_SCOPE_MASK) >> SQ_SCOPE_SHIFT;
+	/* Handle only scope 1 or 2, which is for I-T nexus. */
+	if (scope != 1 && scope != 2)
+		return;
+
+	/* Skip processing, if retry delay timer is already in effect. */
+	if (fcport->retry_delay_timestamp &&
+	    time_before(jiffies, fcport->retry_delay_timestamp))
+		return;
+
+	qual = sts_qual & SQ_QUAL_MASK;
+	if (qual < 1 || qual > 0x3fef)
+		return;
+	qual = min(qual, (u16)SQ_MAX_WAIT_TIME);
+
+	/* qual is expressed in 100ms increments. */
+	fcport->retry_delay_timestamp = jiffies + (qual * HZ / 10);
+
+	ql_log(ql_log_warn, fcport->vha, 0x5101,
+	       "%8phC: I/O throttling requested (status qualifier = %04xh), holding off I/Os for %ums.\n",
+	       fcport->port_name, sts_qual, qual * 100);
 }
 
 static inline bool
@@ -351,3 +385,120 @@
 
 	return (data >> 6) & BIT_0 ? FC4_PRIORITY_FCP : FC4_PRIORITY_NVME;
 }
+
+enum {
+	RESOURCE_NONE,
+	RESOURCE_INI,
+};
+
+static inline int
+qla_get_iocbs(struct qla_qpair *qp, struct iocb_resource *iores)
+{
+	u16 iocbs_used, i;
+	struct qla_hw_data *ha = qp->vha->hw;
+
+	if (!ql2xenforce_iocb_limit) {
+		iores->res_type = RESOURCE_NONE;
+		return 0;
+	}
+
+	if ((iores->iocb_cnt + qp->fwres.iocbs_used) < qp->fwres.iocbs_qp_limit) {
+		qp->fwres.iocbs_used += iores->iocb_cnt;
+		return 0;
+	} else {
+		/* no need to acquire qpair lock. It's just rough calculation */
+		iocbs_used = ha->base_qpair->fwres.iocbs_used;
+		for (i = 0; i < ha->max_qpairs; i++) {
+			if (ha->queue_pair_map[i])
+				iocbs_used += ha->queue_pair_map[i]->fwres.iocbs_used;
+		}
+
+		if ((iores->iocb_cnt + iocbs_used) < qp->fwres.iocbs_limit) {
+			qp->fwres.iocbs_used += iores->iocb_cnt;
+			return 0;
+		} else {
+			iores->res_type = RESOURCE_NONE;
+			return -ENOSPC;
+		}
+	}
+}
+
+static inline void
+qla_put_iocbs(struct qla_qpair *qp, struct iocb_resource *iores)
+{
+	switch (iores->res_type) {
+	case RESOURCE_NONE:
+		break;
+	default:
+		if (qp->fwres.iocbs_used >= iores->iocb_cnt) {
+			qp->fwres.iocbs_used -= iores->iocb_cnt;
+		} else {
+			// should not happen
+			qp->fwres.iocbs_used = 0;
+		}
+		break;
+	}
+	iores->res_type = RESOURCE_NONE;
+}
+
+#define ISP_REG_DISCONNECT 0xffffffffU
+/**************************************************************************
+ * qla2x00_isp_reg_stat
+ *
+ * Description:
+ *        Read the host status register of ISP before aborting the command.
+ *
+ * Input:
+ *       ha = pointer to host adapter structure.
+ *
+ *
+ * Returns:
+ *       Either true or false.
+ *
+ * Note: Return true if there is register disconnect.
+ **************************************************************************/
+static inline
+uint32_t qla2x00_isp_reg_stat(struct qla_hw_data *ha)
+{
+	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+	struct device_reg_82xx __iomem *reg82 = &ha->iobase->isp82;
+
+	if (IS_P3P_TYPE(ha))
+		return ((rd_reg_dword(&reg82->host_int)) == ISP_REG_DISCONNECT);
+	else
+		return ((rd_reg_dword(&reg->host_status)) ==
+			ISP_REG_DISCONNECT);
+}
+
+static inline
+bool qla_pci_disconnected(struct scsi_qla_host *vha,
+			  struct device_reg_24xx __iomem *reg)
+{
+	uint32_t stat;
+	bool ret = false;
+
+	stat = rd_reg_dword(&reg->host_status);
+	if (stat == 0xffffffff) {
+		ql_log(ql_log_info, vha, 0x8041,
+		       "detected PCI disconnect.\n");
+		qla_schedule_eeh_work(vha);
+		ret = true;
+	}
+	return ret;
+}
+
+static inline bool
+fcport_is_smaller(fc_port_t *fcport)
+{
+	if (wwn_to_u64(fcport->port_name) <
+		wwn_to_u64(fcport->vha->port_name))
+		return true;
+	else
+		return false;
+}
+
+static inline bool
+fcport_is_bigger(fc_port_t *fcport)
+{
+	return !fcport_is_smaller(fcport);
+}
diff --git a/scst/qla2x00t-32gbit/qla_iocb.c b/scst/qla2x00t-32gbit/qla_iocb.c
index a2aa5b3..8d0d4a2 100644
--- a/scst/qla2x00t-32gbit/qla_iocb.c
+++ b/scst/qla2x00t-32gbit/qla_iocb.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
 #include "qla_target.h"
@@ -119,7 +118,7 @@
  *
  * Returns a pointer to the continuation type 1 IOCB packet.
  */
-static inline cont_a64_entry_t *
+cont_a64_entry_t *
 qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha, struct req_que *req)
 {
 	cont_a64_entry_t *cont_pkt;
@@ -146,7 +145,6 @@
 qla24xx_configure_prot_mode(srb_t *sp, uint16_t *fw_prot_opts)
 {
 	struct scsi_cmnd *cmd = GET_CMD_SP(sp);
-	uint8_t	guard = scsi_host_get_guard(cmd->device->host);
 
 	/* We always use DIFF Bundling for best performance */
 	*fw_prot_opts = 0;
@@ -167,16 +165,25 @@
 		break;
 	case SCSI_PROT_READ_PASS:
 	case SCSI_PROT_WRITE_PASS:
-		if (guard & SHOST_DIX_GUARD_IP)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+		if (cmd->prot_flags & SCSI_PROT_IP_CHECKSUM)
 			*fw_prot_opts |= PO_MODE_DIF_TCP_CKSUM;
 		else
 			*fw_prot_opts |= PO_MODE_DIF_PASS;
+#endif
 		break;
 	default:	/* Normal Request */
 		*fw_prot_opts |= PO_MODE_DIF_PASS;
 		break;
 	}
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+	if (!(cmd->prot_flags & SCSI_PROT_GUARD_CHECK))
+		*fw_prot_opts |= PO_DISABLE_GUARD_CHECK;
+#else
+		WARN_ON_ONCE(true);
+#endif
+
 	return scsi_prot_sg_count(cmd);
 }
 
@@ -492,7 +499,7 @@
 }
 
 /**
- * qla2x00_marker() - Send a marker IOCB to the firmware.
+ * __qla2x00_marker() - Send a marker IOCB to the firmware.
  * @vha: HA context
  * @qpair: queue pair pointer
  * @loop_id: loop ID
@@ -594,6 +601,7 @@
 	uint32_t dsd_list_len;
 	struct dsd_dma *dsd_ptr;
 	struct ct6_dsd *ctx;
+	struct qla_qpair *qpair = sp->qpair;
 
 	cmd = GET_CMD_SP(sp);
 
@@ -612,12 +620,12 @@
 	/* Set transfer direction */
 	if (cmd->sc_data_direction == DMA_TO_DEVICE) {
 		cmd_pkt->control_flags = cpu_to_le16(CF_WRITE_DATA);
-		vha->qla_stats.output_bytes += scsi_bufflen(cmd);
-		vha->qla_stats.output_requests++;
+		qpair->counters.output_bytes += scsi_bufflen(cmd);
+		qpair->counters.output_requests++;
 	} else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
 		cmd_pkt->control_flags = cpu_to_le16(CF_READ_DATA);
-		vha->qla_stats.input_bytes += scsi_bufflen(cmd);
-		vha->qla_stats.input_requests++;
+		qpair->counters.input_bytes += scsi_bufflen(cmd);
+		qpair->counters.input_requests++;
 	}
 
 	cur_seg = scsi_sglist(cmd);
@@ -704,6 +712,7 @@
 	struct scsi_cmnd *cmd;
 	struct scatterlist *sg;
 	int i;
+	struct qla_qpair *qpair = sp->qpair;
 
 	cmd = GET_CMD_SP(sp);
 
@@ -721,12 +730,12 @@
 	/* Set transfer direction */
 	if (cmd->sc_data_direction == DMA_TO_DEVICE) {
 		cmd_pkt->task_mgmt_flags = cpu_to_le16(TMF_WRITE_DATA);
-		vha->qla_stats.output_bytes += scsi_bufflen(cmd);
-		vha->qla_stats.output_requests++;
+		qpair->counters.output_bytes += scsi_bufflen(cmd);
+		qpair->counters.output_requests++;
 	} else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
 		cmd_pkt->task_mgmt_flags = cpu_to_le16(TMF_READ_DATA);
-		vha->qla_stats.input_bytes += scsi_bufflen(cmd);
-		vha->qla_stats.input_requests++;
+		qpair->counters.input_bytes += scsi_bufflen(cmd);
+		qpair->counters.input_requests++;
 	}
 
 	/* One DSD is available in the Command Type 3 IOCB */
@@ -771,74 +780,23 @@
 {
 	struct scsi_cmnd *cmd = GET_CMD_SP(sp);
 
-	switch (scsi_get_prot_type(cmd)) {
-	case SCSI_PROT_DIF_TYPE0:
-		/*
-		 * No check for ql2xenablehba_err_chk, as it would be an
-		 * I/O error if hba tag generation is not done.
-		 */
-		pkt->ref_tag = cpu_to_le32((uint32_t)
-		    (0xffffffff & scsi_get_lba(cmd)));
+	pkt->ref_tag = cpu_to_le32(scsi_prot_ref_tag(cmd));
 
-		if (!qla2x00_hba_err_chk_enabled(sp))
-			break;
-
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
+	if (cmd->prot_flags & SCSI_PROT_REF_CHECK &&
+	    qla2x00_hba_err_chk_enabled(sp)) {
 		pkt->ref_tag_mask[0] = 0xff;
 		pkt->ref_tag_mask[1] = 0xff;
 		pkt->ref_tag_mask[2] = 0xff;
 		pkt->ref_tag_mask[3] = 0xff;
-		break;
-
-	/*
-	 * For TYPE 2 protection: 16 bit GUARD + 32 bit REF tag has to
-	 * match LBA in CDB + N
-	 */
-	case SCSI_PROT_DIF_TYPE2:
-		pkt->app_tag = cpu_to_le16(0);
-		pkt->app_tag_mask[0] = 0x0;
-		pkt->app_tag_mask[1] = 0x0;
-
-		pkt->ref_tag = cpu_to_le32((uint32_t)
-		    (0xffffffff & scsi_get_lba(cmd)));
-
-		if (!qla2x00_hba_err_chk_enabled(sp))
-			break;
-
-		/* enable ALL bytes of the ref tag */
-		pkt->ref_tag_mask[0] = 0xff;
-		pkt->ref_tag_mask[1] = 0xff;
-		pkt->ref_tag_mask[2] = 0xff;
-		pkt->ref_tag_mask[3] = 0xff;
-		break;
-
-	/* For Type 3 protection: 16 bit GUARD only */
-	case SCSI_PROT_DIF_TYPE3:
-		pkt->ref_tag_mask[0] = pkt->ref_tag_mask[1] =
-			pkt->ref_tag_mask[2] = pkt->ref_tag_mask[3] =
-								0x00;
-		break;
-
-	/*
-	 * For TYpe 1 protection: 16 bit GUARD tag, 32 bit REF tag, and
-	 * 16 bit app tag.
-	 */
-	case SCSI_PROT_DIF_TYPE1:
-		pkt->ref_tag = cpu_to_le32((uint32_t)
-		    (0xffffffff & scsi_get_lba(cmd)));
-		pkt->app_tag = cpu_to_le16(0);
-		pkt->app_tag_mask[0] = 0x0;
-		pkt->app_tag_mask[1] = 0x0;
-
-		if (!qla2x00_hba_err_chk_enabled(sp))
-			break;
-
-		/* enable ALL bytes of the ref tag */
-		pkt->ref_tag_mask[0] = 0xff;
-		pkt->ref_tag_mask[1] = 0xff;
-		pkt->ref_tag_mask[2] = 0xff;
-		pkt->ref_tag_mask[3] = 0xff;
-		break;
 	}
+#else
+	WARN_ON_ONCE(true);
+#endif
+
+	pkt->app_tag = cpu_to_le16(0);
+	pkt->app_tag_mask[0] = 0x0;
+	pkt->app_tag_mask[1] = 0x0;
 }
 
 int
@@ -904,7 +862,7 @@
 	memset(&sgx, 0, sizeof(struct qla2_sgx));
 	if (sp) {
 		cmd = GET_CMD_SP(sp);
-		prot_int = cmd->device->sector_size;
+		prot_int = scsi_prot_interval(cmd);
 
 		sgx.tot_bytes = scsi_bufflen(cmd);
 		sgx.cur_sg = scsi_sglist(cmd);
@@ -1605,12 +1563,17 @@
 	uint16_t	req_cnt;
 	uint16_t	tot_dsds;
 	struct req_que *req = NULL;
+	struct rsp_que *rsp;
 	struct scsi_cmnd *cmd = GET_CMD_SP(sp);
 	struct scsi_qla_host *vha = sp->vha;
 	struct qla_hw_data *ha = vha->hw;
 
+	if (sp->fcport->edif.enable  && (sp->fcport->flags & FCF_FCSP_DEVICE))
+		return qla28xx_start_scsi_edif(sp);
+
 	/* Setup device pointers. */
 	req = vha->req;
+	rsp = req->rsp;
 
 	/* So we know we haven't pci_map'ed anything yet */
 	tot_dsds = 0;
@@ -1641,9 +1604,21 @@
 
 	tot_dsds = nseg;
 	req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
+
+	sp->iores.res_type = RESOURCE_INI;
+	sp->iores.iocb_cnt = req_cnt;
+	if (qla_get_iocbs(sp->qpair, &sp->iores))
+		goto queuing_error;
+
 	if (req->cnt < (req_cnt + 2)) {
-		cnt = IS_SHADOW_REG_CAPABLE(ha) ? *req->out_ptr :
-		    rd_reg_dword_relaxed(req->req_q_out);
+		if (IS_SHADOW_REG_CAPABLE(ha)) {
+			cnt = *req->out_ptr;
+		} else {
+			cnt = rd_reg_dword_relaxed(req->req_q_out);
+			if (qla2x00_check_reg16_for_disconnect(vha, cnt))
+				goto queuing_error;
+		}
+
 		if (req->ring_index < cnt)
 			req->cnt = cnt - req->ring_index;
 		else
@@ -1701,11 +1676,17 @@
 	} else
 		req->ring_ptr++;
 
+	sp->qpair->cmd_cnt++;
 	sp->flags |= SRB_DMA_VALID;
 
 	/* Set chip new ring index. */
 	wrt_reg_dword(req->req_q_in, req->ring_index);
 
+	/* Manage unprocessed RIO/ZIO commands in response queue. */
+	if (vha->flags.process_response_queue &&
+	    rsp->ring_ptr->signature != RESPONSE_PROCESSED)
+		qla24xx_process_response_queue(vha, rsp);
+
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
 	return QLA_SUCCESS;
 
@@ -1713,6 +1694,7 @@
 	if (tot_dsds)
 		scsi_dma_unmap(cmd);
 
+	qla_put_iocbs(sp->qpair, &sp->iores);
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
 	return QLA_FUNCTION_FAILED;
@@ -1826,9 +1808,20 @@
 	/* Total Data and protection sg segment(s) */
 	tot_prot_dsds = nseg;
 	tot_dsds += nseg;
+
+	sp->iores.res_type = RESOURCE_INI;
+	sp->iores.iocb_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
+	if (qla_get_iocbs(sp->qpair, &sp->iores))
+		goto queuing_error;
+
 	if (req->cnt < (req_cnt + 2)) {
-		cnt = IS_SHADOW_REG_CAPABLE(ha) ? *req->out_ptr :
-		    rd_reg_dword_relaxed(req->req_q_out);
+		if (IS_SHADOW_REG_CAPABLE(ha)) {
+			cnt = *req->out_ptr;
+		} else {
+			cnt = rd_reg_dword_relaxed(req->req_q_out);
+			if (qla2x00_check_reg16_for_disconnect(vha, cnt))
+				goto queuing_error;
+		}
 		if (req->ring_index < cnt)
 			req->cnt = cnt - req->ring_index;
 		else
@@ -1886,9 +1879,15 @@
 	} else
 		req->ring_ptr++;
 
+	sp->qpair->cmd_cnt++;
 	/* Set chip new ring index. */
 	wrt_reg_dword(req->req_q_in, req->ring_index);
 
+	/* Manage unprocessed RIO/ZIO commands in response queue. */
+	if (vha->flags.process_response_queue &&
+	    rsp->ring_ptr->signature != RESPONSE_PROCESSED)
+		qla24xx_process_response_queue(vha, rsp);
+
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
 	return QLA_SUCCESS;
@@ -1900,7 +1899,9 @@
 	}
 	/* Cleanup will be performed by the caller (queuecommand) */
 
+	qla_put_iocbs(sp->qpair, &sp->iores);
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
 	return QLA_FUNCTION_FAILED;
 }
 
@@ -1922,16 +1923,21 @@
 	uint16_t	req_cnt;
 	uint16_t	tot_dsds;
 	struct req_que *req = NULL;
+	struct rsp_que *rsp;
 	struct scsi_cmnd *cmd = GET_CMD_SP(sp);
 	struct scsi_qla_host *vha = sp->fcport->vha;
 	struct qla_hw_data *ha = vha->hw;
 	struct qla_qpair *qpair = sp->qpair;
 
+	if (sp->fcport->edif.enable && (sp->fcport->flags & FCF_FCSP_DEVICE))
+		return qla28xx_start_scsi_edif(sp);
+
 	/* Acquire qpair specific lock */
 	spin_lock_irqsave(&qpair->qp_lock, flags);
 
 	/* Setup qpair pointers */
 	req = qpair->req;
+	rsp = qpair->rsp;
 
 	/* So we know we haven't pci_map'ed anything yet */
 	tot_dsds = 0;
@@ -1961,9 +1967,21 @@
 
 	tot_dsds = nseg;
 	req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
+
+	sp->iores.res_type = RESOURCE_INI;
+	sp->iores.iocb_cnt = req_cnt;
+	if (qla_get_iocbs(sp->qpair, &sp->iores))
+		goto queuing_error;
+
 	if (req->cnt < (req_cnt + 2)) {
-		cnt = IS_SHADOW_REG_CAPABLE(ha) ? *req->out_ptr :
-		    rd_reg_dword_relaxed(req->req_q_out);
+		if (IS_SHADOW_REG_CAPABLE(ha)) {
+			cnt = *req->out_ptr;
+		} else {
+			cnt = rd_reg_dword_relaxed(req->req_q_out);
+			if (qla2x00_check_reg16_for_disconnect(vha, cnt))
+				goto queuing_error;
+		}
+
 		if (req->ring_index < cnt)
 			req->cnt = cnt - req->ring_index;
 		else
@@ -2021,11 +2039,17 @@
 	} else
 		req->ring_ptr++;
 
+	sp->qpair->cmd_cnt++;
 	sp->flags |= SRB_DMA_VALID;
 
 	/* Set chip new ring index. */
 	wrt_reg_dword(req->req_q_in, req->ring_index);
 
+	/* Manage unprocessed RIO/ZIO commands in response queue. */
+	if (vha->flags.process_response_queue &&
+	    rsp->ring_ptr->signature != RESPONSE_PROCESSED)
+		qla24xx_process_response_queue(vha, rsp);
+
 	spin_unlock_irqrestore(&qpair->qp_lock, flags);
 	return QLA_SUCCESS;
 
@@ -2033,6 +2057,7 @@
 	if (tot_dsds)
 		scsi_dma_unmap(cmd);
 
+	qla_put_iocbs(sp->qpair, &sp->iores);
 	spin_unlock_irqrestore(&qpair->qp_lock, flags);
 
 	return QLA_FUNCTION_FAILED;
@@ -2161,9 +2186,21 @@
 	/* Total Data and protection sg segment(s) */
 	tot_prot_dsds = nseg;
 	tot_dsds += nseg;
+
+	sp->iores.res_type = RESOURCE_INI;
+	sp->iores.iocb_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
+	if (qla_get_iocbs(sp->qpair, &sp->iores))
+		goto queuing_error;
+
 	if (req->cnt < (req_cnt + 2)) {
-		cnt = IS_SHADOW_REG_CAPABLE(ha) ? *req->out_ptr :
-		    rd_reg_dword_relaxed(req->req_q_out);
+		if (IS_SHADOW_REG_CAPABLE(ha)) {
+			cnt = *req->out_ptr;
+		} else {
+			cnt = rd_reg_dword_relaxed(req->req_q_out);
+			if (qla2x00_check_reg16_for_disconnect(vha, cnt))
+				goto queuing_error;
+		}
+
 		if (req->ring_index < cnt)
 			req->cnt = cnt - req->ring_index;
 		else
@@ -2219,6 +2256,7 @@
 	} else
 		req->ring_ptr++;
 
+	sp->qpair->cmd_cnt++;
 	/* Set chip new ring index. */
 	wrt_reg_dword(req->req_q_in, req->ring_index);
 
@@ -2238,7 +2276,9 @@
 	}
 	/* Cleanup will be performed by the caller (queuecommand) */
 
+	qla_put_iocbs(sp->qpair, &sp->iores);
 	spin_unlock_irqrestore(&qpair->qp_lock, flags);
+
 	return QLA_FUNCTION_FAILED;
 }
 
@@ -2283,6 +2323,11 @@
 			cnt = qla2x00_debounce_register(
 			    ISP_REQ_Q_OUT(ha, &reg->isp));
 
+		if (!qpair->use_shadow_reg && cnt == ISP_REG16_DISCONNECT) {
+			qla_schedule_eeh_work(vha);
+			return NULL;
+		}
+
 		if  (req->ring_index < cnt)
 			req->cnt = cnt - req->ring_index;
 		else
@@ -2354,6 +2399,17 @@
 		if (sp->vha->flags.nvme_first_burst)
 			logio->io_parameter[0] =
 				cpu_to_le32(NVME_PRLI_SP_FIRST_BURST);
+		if (sp->vha->flags.nvme2_enabled) {
+			/* Set service parameter BIT_7 for NVME CONF support */
+			logio->io_parameter[0] |=
+				cpu_to_le32(NVME_PRLI_SP_CONF);
+			/* Set service parameter BIT_8 for SLER support */
+			logio->io_parameter[0] |=
+				cpu_to_le32(NVME_PRLI_SP_SLER);
+			/* Set service parameter BIT_9 for PI control support */
+			logio->io_parameter[0] |=
+				cpu_to_le32(NVME_PRLI_SP_PI_CTRL);
+		}
 	}
 
 	logio->nport_handle = cpu_to_le16(sp->fcport->loop_id);
@@ -2379,6 +2435,12 @@
 			logio->control_flags |= cpu_to_le16(LCF_COND_PLOGI);
 		if (lio->u.logio.flags & SRB_LOGIN_SKIP_PRLI)
 			logio->control_flags |= cpu_to_le16(LCF_SKIP_PRLI);
+		if (lio->u.logio.flags & SRB_LOGIN_FCSP) {
+			logio->control_flags |=
+			    cpu_to_le16(LCF_COMMON_FEAT | LCF_SKIP_PRLI);
+			logio->io_parameter[0] =
+			    cpu_to_le32(LIO_COMM_FEAT_FCSP | LIO_COMM_FEAT_CIO);
+		}
 	}
 	logio->nport_handle = cpu_to_le16(sp->fcport->loop_id);
 	logio->port_id[0] = sp->fcport->d_id.b.al_pa;
@@ -2514,11 +2576,38 @@
 	}
 }
 
-void qla2x00_init_timer(srb_t *sp, unsigned long tmo)
+static void
+qla2x00_async_done(struct srb *sp, int res)
+{
+	if (del_timer(&sp->u.iocb_cmd.timer)) {
+		/*
+		 * Successfully cancelled the timeout handler
+		 * ref: TMR
+		 */
+		if (kref_put(&sp->cmd_kref, qla2x00_sp_release))
+			return;
+	}
+	sp->async_done(sp, res);
+}
+
+void
+qla2x00_sp_release(struct kref *kref)
+{
+	struct srb *sp = container_of(kref, struct srb, cmd_kref);
+
+	sp->free(sp);
+}
+
+void
+qla2x00_init_async_sp(srb_t *sp, unsigned long tmo,
+		     void (*done)(struct srb *sp, int res))
 {
 	timer_setup(&sp->u.iocb_cmd.timer, qla2x00_sp_timeout, 0);
-	sp->u.iocb_cmd.timer.expires = jiffies + tmo * HZ;
+	sp->done = qla2x00_async_done;
+	sp->async_done = done;
 	sp->free = qla2x00_sp_free;
+	sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
+	sp->u.iocb_cmd.timer.expires = jiffies + tmo * HZ;
 	if (IS_QLAFX00(sp->vha->hw) && sp->type == SRB_FXIOCB_DCMD)
 		init_completion(&sp->u.iocb_cmd.u.fxiocb.fxiocb_comp);
 	sp->start_timer = 1;
@@ -2605,7 +2694,9 @@
 	       return -ENOMEM;
 	}
 
-	/* Alloc SRB structure */
+	/* Alloc SRB structure
+	 * ref: INIT
+	 */
 	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
 	if (!sp) {
 		kfree(fcport);
@@ -2626,18 +2717,19 @@
 	sp->type = SRB_ELS_DCMD;
 	sp->name = "ELS_DCMD";
 	sp->fcport = fcport;
-	elsio->timeout = qla2x00_els_dcmd_iocb_timeout;
-	qla2x00_init_timer(sp, ELS_DCMD_TIMEOUT);
-	init_completion(&sp->u.iocb_cmd.u.els_logo.comp);
-	sp->done = qla2x00_els_dcmd_sp_done;
+	qla2x00_init_async_sp(sp, ELS_DCMD_TIMEOUT,
+			      qla2x00_els_dcmd_sp_done);
 	sp->free = qla2x00_els_dcmd_sp_free;
+	sp->u.iocb_cmd.timeout = qla2x00_els_dcmd_iocb_timeout;
+	init_completion(&sp->u.iocb_cmd.u.els_logo.comp);
 
 	elsio->u.els_logo.els_logo_pyld = dma_alloc_coherent(&ha->pdev->dev,
 			    DMA_POOL_SIZE, &elsio->u.els_logo.els_logo_pyld_dma,
 			    GFP_KERNEL);
 
 	if (!elsio->u.els_logo.els_logo_pyld) {
-		sp->free(sp);
+		/* ref: INIT */
+		kref_put(&sp->cmd_kref, qla2x00_sp_release);
 		return QLA_FUNCTION_FAILED;
 	}
 
@@ -2660,7 +2752,8 @@
 
 	rval = qla2x00_start_sp(sp);
 	if (rval != QLA_SUCCESS) {
-		sp->free(sp);
+		/* ref: INIT */
+		kref_put(&sp->cmd_kref, qla2x00_sp_release);
 		return QLA_FUNCTION_FAILED;
 	}
 
@@ -2671,7 +2764,8 @@
 
 	wait_for_completion(&elsio->u.els_logo.comp);
 
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 	return rval;
 }
 
@@ -2702,7 +2796,10 @@
 	els_iocb->s_id[0] = vha->d_id.b.domain;
 
 	if (elsio->u.els_logo.els_cmd == ELS_DCMD_PLOGI) {
-		els_iocb->control_flags = 0;
+		if (vha->hw->flags.edif_enabled)
+			els_iocb->control_flags = cpu_to_le16(ECF_SEC_LOGIN);
+		else
+			els_iocb->control_flags = 0;
 		els_iocb->tx_byte_count = els_iocb->tx_len =
 			cpu_to_le32(sizeof(struct els_plogi_payload));
 		put_unaligned_le64(elsio->u.els_plogi.els_plogi_pyld_dma,
@@ -2719,7 +2816,6 @@
 		    (uint8_t *)els_iocb,
 		    sizeof(*els_iocb));
 	} else {
-		els_iocb->control_flags = cpu_to_le16(1 << 13);
 		els_iocb->tx_byte_count =
 			cpu_to_le32(sizeof(struct els_logo_payload));
 		put_unaligned_le64(elsio->u.els_logo.els_logo_pyld_dma,
@@ -2739,7 +2835,7 @@
 	sp->vha->qla_stats.control_requests++;
 }
 
-static void
+void
 qla2x00_els_dcmd2_iocb_timeout(void *data)
 {
 	srb_t *sp = data;
@@ -2802,7 +2898,9 @@
 	    sp->name, res, sp->handle, fcport->d_id.b24, fcport->port_name);
 
 	fcport->flags &= ~(FCF_ASYNC_SENT|FCF_ASYNC_ACTIVE);
-	del_timer(&sp->u.iocb_cmd.timer);
+	/* For edif, set logout on delete to ensure any residual key from FW is flushed.*/
+	fcport->logout_on_delete = 1;
+	fcport->chip_reset = vha->hw->base_qpair->chip_reset;
 
 	if (sp->flags & SRB_WAKEUP_ON_COMP)
 		complete(&lio->u.els_plogi.comp);
@@ -2879,6 +2977,7 @@
 					set_bit(ISP_ABORT_NEEDED,
 					    &vha->dpc_flags);
 					qla2xxx_wake_dpc(vha);
+					break;
 				}
 				fallthrough;
 			default:
@@ -2888,9 +2987,7 @@
 				    fw_status[0], fw_status[1], fw_status[2]);
 
 				fcport->flags &= ~FCF_ASYNC_SENT;
-				qla2x00_set_fcport_disc_state(fcport,
-				    DSC_LOGIN_FAILED);
-				set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
+				qlt_schedule_sess_for_deletion(fcport);
 				break;
 			}
 			break;
@@ -2902,8 +2999,7 @@
 			    fw_status[0], fw_status[1], fw_status[2]);
 
 			sp->fcport->flags &= ~FCF_ASYNC_SENT;
-			qla2x00_set_fcport_disc_state(fcport, DSC_LOGIN_FAILED);
-			set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
+			qlt_schedule_sess_for_deletion(fcport);
 			break;
 		}
 
@@ -2912,7 +3008,8 @@
 			struct srb_iocb *elsio = &sp->u.iocb_cmd;
 
 			qla2x00_els_dcmd2_free(vha, &elsio->u.els_plogi);
-			sp->free(sp);
+			/* ref: INIT */
+			kref_put(&sp->cmd_kref, qla2x00_sp_release);
 			return;
 		}
 		e->u.iosb.sp = sp;
@@ -2930,7 +3027,9 @@
 	int rval = QLA_SUCCESS;
 	void	*ptr, *resp_ptr;
 
-	/* Alloc SRB structure */
+	/* Alloc SRB structure
+	 * ref: INIT
+	 */
 	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
 	if (!sp) {
 		ql_log(ql_log_info, vha, 0x70e6,
@@ -2943,19 +3042,18 @@
 	qla2x00_set_fcport_disc_state(fcport, DSC_LOGIN_PEND);
 	elsio = &sp->u.iocb_cmd;
 	ql_dbg(ql_dbg_io, vha, 0x3073,
-	    "Enter: PLOGI portid=%06x\n", fcport->d_id.b24);
+	       "%s Enter: PLOGI portid=%06x\n", __func__, fcport->d_id.b24);
+
+	if (wait)
+		sp->flags = SRB_WAKEUP_ON_COMP;
 
 	sp->type = SRB_ELS_DCMD;
 	sp->name = "ELS_DCMD";
 	sp->fcport = fcport;
+	qla2x00_init_async_sp(sp, ELS_DCMD_TIMEOUT + 2,
+			     qla2x00_els_dcmd2_sp_done);
+	sp->u.iocb_cmd.timeout = qla2x00_els_dcmd2_iocb_timeout;
 
-	elsio->timeout = qla2x00_els_dcmd2_iocb_timeout;
-	if (wait)
-		sp->flags = SRB_WAKEUP_ON_COMP;
-
-	qla2x00_init_timer(sp, ELS_DCMD_TIMEOUT + 2);
-
-	sp->done = qla2x00_els_dcmd2_sp_done;
 	elsio->u.els_plogi.tx_size = elsio->u.els_plogi.rx_size = DMA_POOL_SIZE;
 
 	ptr = elsio->u.els_plogi.els_plogi_pyld =
@@ -2981,12 +3079,17 @@
 	memset(ptr, 0, sizeof(struct els_plogi_payload));
 	memset(resp_ptr, 0, sizeof(struct els_plogi_payload));
 	memcpy(elsio->u.els_plogi.els_plogi_pyld->data,
-	    &ha->plogi_els_payld.data,
-	    sizeof(elsio->u.els_plogi.els_plogi_pyld->data));
+	    &ha->plogi_els_payld.fl_csp, LOGIN_TEMPLATE_SIZE);
 
 	elsio->u.els_plogi.els_cmd = els_opcode;
 	elsio->u.els_plogi.els_plogi_pyld->opcode = els_opcode;
 
+	if (els_opcode == ELS_DCMD_PLOGI && DBELL_ACTIVE(vha)) {
+		struct fc_els_flogi *p = ptr;
+
+		p->fl_csp.sp_features |= cpu_to_be16(FC_SP_FT_SEC);
+	}
+
 	ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x3073, "PLOGI buffer:\n");
 	ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x0109,
 	    (uint8_t *)elsio->u.els_plogi.els_plogi_pyld,
@@ -3015,19 +3118,53 @@
 out:
 	fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE);
 	qla2x00_els_dcmd2_free(vha, &elsio->u.els_plogi);
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 done:
 	return rval;
 }
 
+/* it is assume qpair lock is held */
+void qla_els_pt_iocb(struct scsi_qla_host *vha,
+	struct els_entry_24xx *els_iocb,
+	struct qla_els_pt_arg *a)
+{
+	els_iocb->entry_type = ELS_IOCB_TYPE;
+	els_iocb->entry_count = 1;
+	els_iocb->sys_define = 0;
+	els_iocb->entry_status = 0;
+	els_iocb->handle = QLA_SKIP_HANDLE;
+	els_iocb->nport_handle = a->nport_handle;
+	els_iocb->rx_xchg_address = a->rx_xchg_address;
+	els_iocb->tx_dsd_count = cpu_to_le16(1);
+	els_iocb->vp_index = a->vp_idx;
+	els_iocb->sof_type = EST_SOFI3;
+	els_iocb->rx_dsd_count = cpu_to_le16(0);
+	els_iocb->opcode = a->els_opcode;
+
+	els_iocb->d_id[0] = a->did.b.al_pa;
+	els_iocb->d_id[1] = a->did.b.area;
+	els_iocb->d_id[2] = a->did.b.domain;
+	/* For SID the byte order is different than DID */
+	els_iocb->s_id[1] = vha->d_id.b.al_pa;
+	els_iocb->s_id[2] = vha->d_id.b.area;
+	els_iocb->s_id[0] = vha->d_id.b.domain;
+
+	els_iocb->control_flags = cpu_to_le16(a->control_flags);
+
+	els_iocb->tx_byte_count = cpu_to_le32(a->tx_byte_count);
+	els_iocb->tx_len = cpu_to_le32(a->tx_len);
+	put_unaligned_le64(a->tx_addr, &els_iocb->tx_address);
+
+	els_iocb->rx_byte_count = cpu_to_le32(a->rx_byte_count);
+	els_iocb->rx_len = cpu_to_le32(a->rx_len);
+	put_unaligned_le64(a->rx_addr, &els_iocb->rx_address);
+}
+
 static void
 qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
 {
-#ifndef NEW_LIBFC_API
-	struct fc_bsg_job *bsg_job = sp->u.bsg_job;
-#else
-	struct bsg_job *bsg_job = sp->u.bsg_job;
-#endif
+	BSG_JOB_TYPE *bsg_job = sp->u.bsg_job;
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 
         els_iocb->entry_type = ELS_IOCB_TYPE;
@@ -3077,11 +3214,7 @@
 	uint16_t tot_dsds;
 	scsi_qla_host_t *vha = sp->vha;
 	struct qla_hw_data *ha = vha->hw;
-#ifndef NEW_LIBFC_API
-	struct fc_bsg_job *bsg_job = sp->u.bsg_job;
-#else
-	struct bsg_job *bsg_job = sp->u.bsg_job;
-#endif
+	BSG_JOB_TYPE *bsg_job = sp->u.bsg_job;
 	int entry_count = 1;
 
 	memset(ct_iocb, 0, sizeof(ms_iocb_entry_t));
@@ -3148,11 +3281,7 @@
 	uint16_t cmd_dsds, rsp_dsds;
 	scsi_qla_host_t *vha = sp->vha;
 	struct qla_hw_data *ha = vha->hw;
-#ifndef NEW_LIBFC_API
-	struct fc_bsg_job *bsg_job = sp->u.bsg_job;
-#else
-	struct bsg_job *bsg_job = sp->u.bsg_job;
-#endif
+	BSG_JOB_TYPE *bsg_job = sp->u.bsg_job;
 	int entry_count = 1;
 	cont_a64_entry_t *cont_pkt = NULL;
 
@@ -3553,6 +3682,7 @@
 	struct srb_iocb *aio = &sp->u.iocb_cmd;
 	scsi_qla_host_t *vha = sp->vha;
 	struct req_que *req = sp->qpair->req;
+	srb_t *orig_sp = sp->cmd_sp;
 
 	memset(abt_iocb, 0, sizeof(struct abort_entry_24xx));
 	abt_iocb->entry_type = ABORT_IOCB_TYPE;
@@ -3569,6 +3699,11 @@
 			    aio->u.abt.cmd_hndl);
 	abt_iocb->vp_index = vha->vp_idx;
 	abt_iocb->req_que_no = aio->u.abt.req_que_no;
+
+	/* need to pass original sp */
+	if (orig_sp)
+		qla_nvme_abort_set_option(abt_iocb, orig_sp);
+
 	/* Send the command to the firmware */
 	wmb();
 }
@@ -3620,16 +3755,25 @@
 	nack->u.isp24.srr_reject_code = 0;
 	nack->u.isp24.srr_reject_code_expl = 0;
 	nack->u.isp24.vp_index = ntfy->u.isp24.vp_index;
+
+	if (ntfy->u.isp24.status_subcode == ELS_PLOGI &&
+	    (le16_to_cpu(ntfy->u.isp24.flags) & NOTIFY24XX_FLAGS_FCSP) &&
+	    sp->vha->hw->flags.edif_enabled) {
+		ql_dbg(ql_dbg_disc, sp->vha, 0x3074,
+		    "%s PLOGI NACK sent with FC SECURITY bit, hdl=%x, loopid=%x, to pid %06x\n",
+		    sp->name, sp->handle, sp->fcport->loop_id,
+		    sp->fcport->d_id.b24);
+		nack->u.isp24.flags |= cpu_to_le16(NOTIFY_ACK_FLAGS_FCSP);
+	}
 }
 
 /*
  * Build NVME LS request
  */
-static int
+static void
 qla_nvme_ls(srb_t *sp, struct pt_ls4_request *cmd_pkt)
 {
 	struct srb_iocb *nvme;
-	int     rval = QLA_SUCCESS;
 
 	nvme = &sp->u.iocb_cmd;
 	cmd_pkt->entry_type = PT_LS4_REQUEST;
@@ -3649,8 +3793,6 @@
 	cmd_pkt->rx_byte_count = cpu_to_le32(nvme->u.nvme.rsp_len);
 	cmd_pkt->dsd[1].length = cpu_to_le32(nvme->u.nvme.rsp_len);
 	put_unaligned_le64(nvme->u.nvme.rsp_dma, &cmd_pkt->dsd[1].address);
-
-	return rval;
 }
 
 static void
@@ -3697,6 +3839,9 @@
 	void *pkt;
 	unsigned long flags;
 
+	if (vha->hw->flags.eeh_busy)
+		return -EIO;
+
 	spin_lock_irqsave(qp->qp_lock_ptr, flags);
 	pkt = __qla2x00_alloc_iocbs(sp->qpair, sp);
 	if (!pkt) {
@@ -3724,6 +3869,10 @@
 	case SRB_ELS_CMD_HST:
 		qla24xx_els_iocb(sp, pkt);
 		break;
+	case SRB_ELS_CMD_HST_NOLOGIN:
+		qla_els_pt_iocb(sp->vha, pkt,  &sp->u.bsg_cmd.u.els_arg);
+		((struct els_entry_24xx *)pkt)->handle = sp->handle;
+		break;
 	case SRB_CT_CMD:
 		IS_FWI2_CAPABLE(ha) ?
 		    qla24xx_ct_iocb(sp, pkt) :
@@ -3771,12 +3920,25 @@
 	case SRB_PRLO_CMD:
 		qla24xx_prlo_iocb(sp, pkt);
 		break;
+	case SRB_SA_UPDATE:
+		qla24xx_sa_update_iocb(sp, pkt);
+		break;
+	case SRB_SA_REPLACE:
+		qla24xx_sa_replace_iocb(sp, pkt);
+		break;
 	default:
 		break;
 	}
 
-	if (sp->start_timer)
+	if (sp->start_timer) {
+		/* ref: TMR timer ref
+		 * this code should be just before start_iocbs function
+		 * This will make sure that caller function don't to do
+		 * kref_put even on failure
+		 */
+		kref_get(&sp->cmd_kref);
 		add_timer(&sp->u.iocb_cmd.timer);
+	}
 
 	wmb();
 	qla2x00_start_iocbs(vha, qp->req);
@@ -3796,11 +3958,7 @@
 	struct scatterlist *sg;
 	int index;
 	int entry_count = 1;
-#ifndef NEW_LIBFC_API
-	struct fc_bsg_job *bsg_job = sp->u.bsg_job;
-#else
-	struct bsg_job *bsg_job = sp->u.bsg_job;
-#endif
+	BSG_JOB_TYPE *bsg_job = sp->u.bsg_job;
 
 	/*Update entry type to indicate bidir command */
 	put_unaligned_le32(COMMAND_BIDIRECTIONAL, &cmd_pkt->entry_type);
@@ -3918,8 +4076,14 @@
 
 	/* Check for room on request queue. */
 	if (req->cnt < req_cnt + 2) {
-		cnt = IS_SHADOW_REG_CAPABLE(ha) ? *req->out_ptr :
-		    rd_reg_dword_relaxed(req->req_q_out);
+		if (IS_SHADOW_REG_CAPABLE(ha)) {
+			cnt = *req->out_ptr;
+		} else {
+			cnt = rd_reg_dword_relaxed(req->req_q_out);
+			if (qla2x00_check_reg16_for_disconnect(vha, cnt))
+				goto queuing_error;
+		}
+
 		if  (req->ring_index < cnt)
 			req->cnt = cnt - req->ring_index;
 		else
@@ -3958,5 +4122,6 @@
 	qla2x00_start_iocbs(vha, req);
 queuing_error:
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
 	return rval;
 }
diff --git a/scst/qla2x00t-32gbit/qla_isr.c b/scst/qla2x00t-32gbit/qla_isr.c
index 4323b97..a216840 100644
--- a/scst/qla2x00t-32gbit/qla_isr.c
+++ b/scst/qla2x00t-32gbit/qla_isr.c
@@ -1,11 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
 #include "qla_target.h"
+#include "qla_gbl.h"
 
 #include <linux/delay.h>
 #include <linux/slab.h>
@@ -57,15 +57,15 @@
 	ql_dump_buffer(ql_dbg_init + ql_dbg_verbose, vha, 0x508f,
 		       pkt, pkt_size);
 
-	fc_host_fpin_rcv(vha->host, pkt_size, (char *)pkt);
+	fc_host_fpin_rcv(vha->host, pkt_size, (char *)pkt, 0);
 }
 
 const char *const port_state_str[] = {
-	"Unknown",
-	"UNCONFIGURED",
-	"DEAD",
-	"LOST",
-	"ONLINE"
+	[FCS_UNKNOWN]		= "Unknown",
+	[FCS_UNCONFIGURED]	= "UNCONFIGURED",
+	[FCS_DEVICE_DEAD]	= "DEAD",
+	[FCS_DEVICE_LOST]	= "LOST",
+	[FCS_ONLINE]		= "ONLINE"
 };
 
 static void
@@ -182,6 +182,149 @@
 }
 
 /**
+ * __qla_consume_iocb - this routine is used to tell fw driver has processed
+ *   or consumed the head IOCB along with the continuation IOCB's from the
+ *   provided respond queue.
+ * @vha: host adapter pointer
+ * @pkt: pointer to current packet.  On return, this pointer shall move
+ *       to the next packet.
+ * @rsp: respond queue pointer.
+ *
+ * it is assumed pkt is the head iocb, not the continuation iocbk
+ */
+void __qla_consume_iocb(struct scsi_qla_host *vha,
+	void **pkt, struct rsp_que **rsp)
+{
+	struct rsp_que *rsp_q = *rsp;
+	response_t *new_pkt;
+	uint16_t entry_count_remaining;
+	struct purex_entry_24xx *purex = *pkt;
+
+	entry_count_remaining = purex->entry_count;
+	while (entry_count_remaining > 0) {
+		new_pkt = rsp_q->ring_ptr;
+		*pkt = new_pkt;
+
+		rsp_q->ring_index++;
+		if (rsp_q->ring_index == rsp_q->length) {
+			rsp_q->ring_index = 0;
+			rsp_q->ring_ptr = rsp_q->ring;
+		} else {
+			rsp_q->ring_ptr++;
+		}
+
+		new_pkt->signature = RESPONSE_PROCESSED;
+		/* flush signature */
+		wmb();
+		--entry_count_remaining;
+	}
+}
+
+/**
+ * __qla_copy_purex_to_buffer - extract ELS payload from Purex IOCB
+ *    and save to provided buffer
+ * @vha: host adapter pointer
+ * @pkt: pointer Purex IOCB
+ * @rsp: respond queue
+ * @buf: extracted ELS payload copy here
+ * @buf_len: buffer length
+ */
+int __qla_copy_purex_to_buffer(struct scsi_qla_host *vha,
+	void **pkt, struct rsp_que **rsp, u8 *buf, u32 buf_len)
+{
+	struct purex_entry_24xx *purex = *pkt;
+	struct rsp_que *rsp_q = *rsp;
+	sts_cont_entry_t *new_pkt;
+	uint16_t no_bytes = 0, total_bytes = 0, pending_bytes = 0;
+	uint16_t buffer_copy_offset = 0;
+	uint16_t entry_count_remaining;
+	u16 tpad;
+
+	entry_count_remaining = purex->entry_count;
+	total_bytes = (le16_to_cpu(purex->frame_size) & 0x0FFF)
+		- PURX_ELS_HEADER_SIZE;
+
+	/*
+	 * end of payload may not end in 4bytes boundary.  Need to
+	 * round up / pad for room to swap, before saving data
+	 */
+	tpad = roundup(total_bytes, 4);
+
+	if (buf_len < tpad) {
+		ql_dbg(ql_dbg_async, vha, 0x5084,
+		    "%s buffer is too small %d < %d\n",
+		    __func__, buf_len, tpad);
+		__qla_consume_iocb(vha, pkt, rsp);
+		return -EIO;
+	}
+
+	pending_bytes = total_bytes = tpad;
+	no_bytes = (pending_bytes > sizeof(purex->els_frame_payload))  ?
+	    sizeof(purex->els_frame_payload) : pending_bytes;
+
+	memcpy(buf, &purex->els_frame_payload[0], no_bytes);
+	buffer_copy_offset += no_bytes;
+	pending_bytes -= no_bytes;
+	--entry_count_remaining;
+
+	((response_t *)purex)->signature = RESPONSE_PROCESSED;
+	/* flush signature */
+	wmb();
+
+	do {
+		while ((total_bytes > 0) && (entry_count_remaining > 0)) {
+			new_pkt = (sts_cont_entry_t *)rsp_q->ring_ptr;
+			*pkt = new_pkt;
+
+			if (new_pkt->entry_type != STATUS_CONT_TYPE) {
+				ql_log(ql_log_warn, vha, 0x507a,
+				    "Unexpected IOCB type, partial data 0x%x\n",
+				    buffer_copy_offset);
+				break;
+			}
+
+			rsp_q->ring_index++;
+			if (rsp_q->ring_index == rsp_q->length) {
+				rsp_q->ring_index = 0;
+				rsp_q->ring_ptr = rsp_q->ring;
+			} else {
+				rsp_q->ring_ptr++;
+			}
+			no_bytes = (pending_bytes > sizeof(new_pkt->data)) ?
+			    sizeof(new_pkt->data) : pending_bytes;
+			if ((buffer_copy_offset + no_bytes) <= total_bytes) {
+				memcpy((buf + buffer_copy_offset), new_pkt->data,
+				    no_bytes);
+				buffer_copy_offset += no_bytes;
+				pending_bytes -= no_bytes;
+				--entry_count_remaining;
+			} else {
+				ql_log(ql_log_warn, vha, 0x5044,
+				    "Attempt to copy more that we got, optimizing..%x\n",
+				    buffer_copy_offset);
+				memcpy((buf + buffer_copy_offset), new_pkt->data,
+				    total_bytes - buffer_copy_offset);
+			}
+
+			((response_t *)new_pkt)->signature = RESPONSE_PROCESSED;
+			/* flush signature */
+			wmb();
+		}
+
+		if (pending_bytes != 0 || entry_count_remaining != 0) {
+			ql_log(ql_log_fatal, vha, 0x508b,
+			    "Dropping partial Data, underrun bytes = 0x%x, entry cnts 0x%x\n",
+			    total_bytes, entry_count_remaining);
+			return -EIO;
+		}
+	} while (entry_count_remaining > 0);
+
+	be32_to_cpu_array((u32 *)buf, (__be32 *)buf, total_bytes >> 2);
+
+	return 0;
+}
+
+/**
  * qla2100_intr_handler() - Process interrupts for the ISP2100 and ISP2200.
  * @irq: interrupt number
  * @dev_id: SCSI driver HA context
@@ -282,12 +425,7 @@
 		if (!test_and_set_bit(PFLG_DISCONNECTED, &vha->pci_flags) &&
 		    !test_bit(PFLG_DRIVER_REMOVING, &vha->pci_flags) &&
 		    !test_bit(PFLG_DRIVER_PROBING, &vha->pci_flags)) {
-			/*
-			 * Schedule this (only once) on the default system
-			 * workqueue so that all the adapter workqueues and the
-			 * DPC thread can be shutdown cleanly.
-			 */
-			schedule_work(&vha->hw->board_disable);
+			qla_schedule_eeh_work(vha);
 		}
 		return true;
 	} else
@@ -522,7 +660,7 @@
 qla2x00_get_link_speed_str(struct qla_hw_data *ha, uint16_t speed)
 {
 	static const char *const link_speeds[] = {
-		"1", "2", "?", "4", "8", "16", "32", "10"
+		"1", "2", "?", "4", "8", "16", "32", "64", "10"
 	};
 #define	QLA_LAST_SPEED (ARRAY_SIZE(link_speeds) - 1)
 
@@ -774,12 +912,12 @@
 qla27xx_handle_8200_aen(scsi_qla_host_t *vha, uint16_t *mb)
 {
 	struct qla_hw_data *ha = vha->hw;
-	bool reset_isp_needed = 0;
+	bool reset_isp_needed = false;
 
 	ql_log(ql_log_warn, vha, 0x02f0,
 	       "MPI Heartbeat stop. MPI reset is%s needed. "
 	       "MB0[%xh] MB1[%xh] MB2[%xh] MB3[%xh]\n",
-	       mb[0] & BIT_8 ? "" : " not",
+	       mb[1] & BIT_8 ? "" : " not",
 	       mb[0], mb[1], mb[2], mb[3]);
 
 	if ((mb[1] & BIT_8) == 0)
@@ -790,7 +928,7 @@
 
 	if (ql2xfulldump_on_mpifail) {
 		ha->isp_ops->fw_dump(vha);
-		reset_isp_needed = 1;
+		reset_isp_needed = true;
 	}
 
 	ha->isp_ops->mpi_fw_dump(vha, 1);
@@ -856,8 +994,8 @@
  * @vha: SCSI driver HA context
  * @pkt: ELS packet
  */
-static struct purex_item *
-qla24xx_copy_std_pkt(struct scsi_qla_host *vha, void *pkt)
+static struct purex_item
+*qla24xx_copy_std_pkt(struct scsi_qla_host *vha, void *pkt)
 {
 	struct purex_item *item;
 
@@ -1072,6 +1210,9 @@
 
 	case MBA_SYSTEM_ERR:		/* System Error */
 		mbx = 0;
+
+		vha->hw_err_cnt++;
+
 		if (IS_QLA81XX(ha) || IS_QLA83XX(ha) ||
 		    IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
 			u16 m[4];
@@ -1125,6 +1266,8 @@
 		ql_log(ql_log_warn, vha, 0x5006,
 		    "ISP Request Transfer Error (%x).\n",  mb[1]);
 
+		vha->hw_err_cnt++;
+
 		set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
 		break;
 
@@ -1132,6 +1275,8 @@
 		ql_log(ql_log_warn, vha, 0x5007,
 		    "ISP Response Transfer Error (%x).\n", mb[1]);
 
+		vha->hw_err_cnt++;
+
 		set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
 		break;
 
@@ -1189,12 +1334,18 @@
 		vha->flags.management_server_logged_in = 0;
 		qla2x00_post_aen_work(vha, FCH_EVT_LINKUP, ha->link_data_rate);
 
+		if (vha->link_down_time < vha->hw->port_down_retry_count) {
+			vha->short_link_down_cnt++;
+			vha->link_down_time = QLA2XX_MAX_LINK_DOWN_TIME;
+		}
+
 		break;
 
 	case MBA_LOOP_DOWN:		/* Loop Down Event */
 		SAVE_TOPO(ha);
 		ha->flags.lip_ae = 0;
 		ha->current_topology = 0;
+		vha->link_down_time = 0;
 
 		mbx = (IS_QLA81XX(ha) || IS_QLA8031(ha))
 			? rd_reg_word(&reg24->mailbox4) : 0;
@@ -1215,9 +1366,7 @@
 			if (!vha->vp_idx) {
 				if (ha->flags.fawwpn_enabled &&
 				    (ha->current_topology == ISP_CFG_F)) {
-					void *wwpn = ha->init_cb->port_name;
-
-					memcpy(vha->port_name, wwpn, WWN_SIZE);
+					memcpy(vha->port_name, ha->port_name, WWN_SIZE);
 					fc_host_port_name(vha->host) =
 					    wwn_to_u64(vha->port_name);
 					ql_dbg(ql_dbg_init + ql_dbg_verbose,
@@ -1455,9 +1604,9 @@
 		if (ha->flags.npiv_supported && vha->vp_idx != (mb[3] & 0xff))
 			break;
 
-		ql_dbg(ql_dbg_async, vha, 0x5013,
-		    "RSCN database changed -- %04x %04x %04x.\n",
-		    mb[1], mb[2], mb[3]);
+		ql_log(ql_log_warn, vha, 0x5013,
+		       "RSCN database changed -- %04x %04x %04x.\n",
+		       mb[1], mb[2], mb[3]);
 
 		rscn_entry = ((mb[1] & 0xff) << 16) | mb[2];
 		host_pid = (vha->d_id.b.domain << 16) | (vha->d_id.b.area << 8)
@@ -1516,6 +1665,7 @@
 		ql_dbg(ql_dbg_async, vha, 0x5016,
 		    "Discard RND Frame -- %04x %04x %04x.\n",
 		    mb[1], mb[2], mb[3]);
+		vha->interface_err_cnt++;
 		break;
 
 	case MBA_TRACE_NOTIFICATION:
@@ -1605,6 +1755,7 @@
 
 	case MBA_IDC_AEN:
 		if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
+			vha->hw_err_cnt++;
 			qla27xx_handle_8200_aen(vha, mb);
 		} else if (IS_QLA83XX(ha)) {
 			mb[4] = rd_reg_word(&reg24->mailbox4);
@@ -1620,6 +1771,9 @@
 		break;
 
 	case MBA_DPORT_DIAGNOSTICS:
+		if ((mb[1] & 0xF) == AEN_DONE_DIAG_TEST_WITH_NOERR ||
+		    (mb[1] & 0xF) == AEN_DONE_DIAG_TEST_WITH_ERR)
+			vha->dport_status &= ~DPORT_DIAG_IN_PROGRESS;
 		ql_dbg(ql_dbg_async, vha, 0x5052,
 		    "D-Port Diagnostics: %04x %04x %04x %04x\n",
 		    mb[0], mb[1], mb[2], mb[3]);
@@ -1654,8 +1808,6 @@
 	case MBA_TEMPERATURE_ALERT:
 		ql_dbg(ql_dbg_async, vha, 0x505e,
 		    "TEMPERATURE ALERT: %04x %04x %04x\n", mb[1], mb[2], mb[3]);
-		if (mb[1] == 0x12)
-			schedule_work(&ha->board_disable);
 		break;
 
 	case MBA_TRANS_INSERT:
@@ -1728,35 +1880,38 @@
 {
 	struct qla_hw_data *ha = vha->hw;
 	sts_entry_t *pkt = iocb;
-	srb_t *sp = NULL;
+	srb_t *sp;
 	uint16_t index;
 
+	if (pkt->handle == QLA_SKIP_HANDLE)
+		return NULL;
+
 	index = LSW(pkt->handle);
 	if (index >= req->num_outstanding_cmds) {
 		ql_log(ql_log_warn, vha, 0x5031,
-			   "Invalid command index (%x) type %8ph.\n",
-			   index, iocb);
+			   "%s: Invalid command index (%x) type %8ph.\n",
+			   func, index, iocb);
 		if (IS_P3P_TYPE(ha))
 			set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
 		else
 			set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
-		goto done;
+		return NULL;
 	}
 	sp = req->outstanding_cmds[index];
 	if (!sp) {
 		ql_log(ql_log_warn, vha, 0x5032,
-		    "Invalid completion handle (%x) -- timed-out.\n", index);
-		return sp;
+			"%s: Invalid completion handle (%x) -- timed-out.\n",
+			func, index);
+		return NULL;
 	}
 	if (sp->handle != index) {
 		ql_log(ql_log_warn, vha, 0x5033,
-		    "SRB handle (%x) mismatch %x.\n", sp->handle, index);
+			"%s: SRB handle (%x) mismatch %x.\n", func,
+			sp->handle, index);
 		return NULL;
 	}
 
 	req->outstanding_cmds[index] = NULL;
-
-done:
 	return sp;
 }
 
@@ -1851,6 +2006,7 @@
     struct mbx_24xx_entry *pkt)
 {
 	const char func[] = "MBX-IOCB2";
+	struct qla_hw_data *ha = vha->hw;
 	srb_t *sp;
 	struct srb_iocb *si;
 	u16 sz, i;
@@ -1860,6 +2016,18 @@
 	if (!sp)
 		return;
 
+	if (sp->type == SRB_SCSI_CMD ||
+	    sp->type == SRB_NVME_CMD ||
+	    sp->type == SRB_TM_CMD) {
+		ql_log(ql_log_warn, vha, 0x509d,
+			"Inconsistent event entry type %d\n", sp->type);
+		if (IS_P3P_TYPE(ha))
+			set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
+		else
+			set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+		return;
+	}
+
 	si = &sp->u.iocb_cmd;
 	sz = min(ARRAY_SIZE(pkt->mb), ARRAY_SIZE(sp->u.iocb_cmd.u.mbx.in_mb));
 
@@ -1896,11 +2064,7 @@
 	const char func[] = "CT_IOCB";
 	const char *type;
 	srb_t *sp;
-#ifndef NEW_LIBFC_API
-	struct fc_bsg_job *bsg_job;
-#else
-	struct bsg_job *bsg_job;
-#endif
+	BSG_JOB_TYPE *bsg_job;
 	struct fc_bsg_reply *bsg_reply;
 	uint16_t comp_status;
 	int res = 0;
@@ -1966,33 +2130,69 @@
 }
 
 static void
-qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
+qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req,
     struct sts_entry_24xx *pkt, int iocb_type)
 {
 	struct els_sts_entry_24xx *ese = (struct els_sts_entry_24xx *)pkt;
 	const char func[] = "ELS_CT_IOCB";
 	const char *type;
 	srb_t *sp;
-#ifndef NEW_LIBFC_API
-	struct fc_bsg_job *bsg_job;
-#else
-	struct bsg_job *bsg_job;
-#endif
+	BSG_JOB_TYPE *bsg_job;
 	struct fc_bsg_reply *bsg_reply;
 	uint16_t comp_status;
 	uint32_t fw_status[3];
-	int res;
+	int res, logit = 1;
 	struct srb_iocb *els;
+	uint n;
+	scsi_qla_host_t *vha;
+	struct els_sts_entry_24xx *e = (struct els_sts_entry_24xx *)pkt;
 
-	sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
+	sp = qla2x00_get_sp_from_handle(v, func, req, pkt);
 	if (!sp)
 		return;
+	bsg_job = sp->u.bsg_job;
+	vha = sp->vha;
 
 	type = NULL;
+
+	comp_status = fw_status[0] = le16_to_cpu(pkt->comp_status);
+	fw_status[1] = le32_to_cpu(((struct els_sts_entry_24xx *)pkt)->error_subcode_1);
+	fw_status[2] = le32_to_cpu(((struct els_sts_entry_24xx *)pkt)->error_subcode_2);
+
 	switch (sp->type) {
 	case SRB_ELS_CMD_RPT:
 	case SRB_ELS_CMD_HST:
+		type = "rpt hst";
+		break;
+	case SRB_ELS_CMD_HST_NOLOGIN:
 		type = "els";
+		{
+			struct els_entry_24xx *els = (void *)pkt;
+			struct qla_bsg_auth_els_request *p =
+				(struct qla_bsg_auth_els_request *)bsg_job->request;
+
+			ql_dbg(ql_dbg_user, vha, 0x700f,
+			     "%s %s. portid=%02x%02x%02x status %x xchg %x bsg ptr %p\n",
+			     __func__, sc_to_str(p->e.sub_cmd),
+			     e->d_id[2], e->d_id[1], e->d_id[0],
+			     comp_status, p->e.extra_rx_xchg_address, bsg_job);
+
+			if (!(le16_to_cpu(els->control_flags) & ECF_PAYLOAD_DESCR_MASK)) {
+				if (sp->remap.remapped) {
+					n = sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+						bsg_job->reply_payload.sg_cnt,
+						sp->remap.rsp.buf,
+						sp->remap.rsp.len);
+					ql_dbg(ql_dbg_user + ql_dbg_verbose, vha, 0x700e,
+					   "%s: SG copied %x of %x\n",
+					   __func__, n, sp->remap.rsp.len);
+				} else {
+					ql_dbg(ql_dbg_user, vha, 0x700f,
+					   "%s: NOT REMAPPED (error)...!!!\n",
+					   __func__);
+				}
+			}
+		}
 		break;
 	case SRB_CT_CMD:
 		type = "ct pass-through";
@@ -2022,10 +2222,6 @@
 		return;
 	}
 
-	comp_status = fw_status[0] = le16_to_cpu(pkt->comp_status);
-	fw_status[1] = le32_to_cpu(ese->error_subcode_1);
-	fw_status[2] = le32_to_cpu(ese->error_subcode_2);
-
 	if (iocb_type == ELS_IOCB_TYPE) {
 		els = &sp->u.iocb_cmd;
 		els->u.els_plogi.fw_status[0] = cpu_to_le32(fw_status[0]);
@@ -2039,15 +2235,63 @@
 				res =  DID_OK << 16;
 				els->u.els_plogi.len = cpu_to_le16(le32_to_cpu(
 					ese->total_byte_count));
+
+				if (sp->remap.remapped &&
+				    ((u8 *)sp->remap.rsp.buf)[0] == ELS_LS_ACC) {
+					ql_dbg(ql_dbg_user, vha, 0x503f,
+					    "%s IOCB Done LS_ACC %02x%02x%02x -> %02x%02x%02x",
+					    __func__, e->s_id[0], e->s_id[2], e->s_id[1],
+					    e->d_id[2], e->d_id[1], e->d_id[0]);
+					logit = 0;
+				}
+
+			} else if (comp_status == CS_PORT_LOGGED_OUT) {
+				ql_dbg(ql_dbg_disc, vha, 0x911e,
+				       "%s %d schedule session deletion\n",
+				       __func__, __LINE__);
+
+				els->u.els_plogi.len = 0;
+				res = DID_IMM_RETRY << 16;
+				qlt_schedule_sess_for_deletion(sp->fcport);
 			} else {
 				els->u.els_plogi.len = 0;
 				res = DID_ERROR << 16;
 			}
+
+			if (sp->remap.remapped &&
+			    ((u8 *)sp->remap.rsp.buf)[0] == ELS_LS_RJT) {
+				if (logit) {
+					ql_dbg(ql_dbg_user, vha, 0x503f,
+					    "%s IOCB Done LS_RJT hdl=%x comp_status=0x%x\n",
+					    type, sp->handle, comp_status);
+
+					ql_dbg(ql_dbg_user, vha, 0x503f,
+					    "subcode 1=0x%x subcode 2=0x%x bytes=0x%x %02x%02x%02x -> %02x%02x%02x\n",
+					    fw_status[1], fw_status[2],
+					    le32_to_cpu(((struct els_sts_entry_24xx *)
+						pkt)->total_byte_count),
+					    e->s_id[0], e->s_id[2], e->s_id[1],
+					    e->d_id[2], e->d_id[1], e->d_id[0]);
+				}
+				if (sp->fcport && sp->fcport->flags & FCF_FCSP_DEVICE &&
+				    sp->type == SRB_ELS_CMD_HST_NOLOGIN) {
+					ql_dbg(ql_dbg_edif, vha, 0x911e,
+					    "%s rcv reject. Sched delete\n", __func__);
+					qlt_schedule_sess_for_deletion(sp->fcport);
+				}
+			} else if (logit) {
+				ql_log(ql_log_info, vha, 0x503f,
+				    "%s IOCB Done hdl=%x comp_status=0x%x\n",
+				    type, sp->handle, comp_status);
+				ql_log(ql_log_info, vha, 0x503f,
+				    "subcode 1=0x%x subcode 2=0x%x bytes=0x%x %02x%02x%02x -> %02x%02x%02x\n",
+				    fw_status[1], fw_status[2],
+				    le32_to_cpu(((struct els_sts_entry_24xx *)
+				    pkt)->total_byte_count),
+				    e->s_id[0], e->s_id[2], e->s_id[1],
+				    e->d_id[2], e->d_id[1], e->d_id[0]);
+			}
 		}
-		ql_dbg(ql_dbg_disc, vha, 0x503f,
-		    "ELS IOCB Done -%s hdl=%x comp_status=0x%x error subcode 1=0x%x error subcode 2=0x%x total_byte=0x%x\n",
-		    type, sp->handle, comp_status, fw_status[1], fw_status[2],
-		    le32_to_cpu(ese->total_byte_count));
 		goto els_ct_done;
 	}
 
@@ -2106,6 +2350,7 @@
 	struct srb_iocb *lio;
 	uint16_t *data;
 	uint32_t iop[2];
+	int logit = 1;
 
 	sp = qla2x00_get_sp_from_handle(vha, func, req, logio);
 	if (!sp)
@@ -2152,6 +2397,10 @@
 		if (sp->type != SRB_LOGIN_CMD)
 			goto logio_done;
 
+		lio->u.logio.iop[1] = le32_to_cpu(logio->io_parameter[5]);
+		if (le32_to_cpu(logio->io_parameter[5]) & LIO_COMM_FEAT_FCSP)
+			fcport->flags |= FCF_FCSP_DEVICE;
+
 		iop[0] = le32_to_cpu(logio->io_parameter[0]);
 		if (iop[0] & BIT_4) {
 			fcport->port_type = FCT_TARGET;
@@ -2179,9 +2428,11 @@
 	case LSC_SCODE_PORTID_USED:
 		data[0] = MBS_PORT_ID_USED;
 		data[1] = LSW(iop[1]);
+		logit = 0;
 		break;
 	case LSC_SCODE_NPORT_USED:
 		data[0] = MBS_LOOP_ID_USED;
+		logit = 0;
 		break;
 	case LSC_SCODE_CMD_FAILED:
 		if (iop[1] == 0x0606) {
@@ -2214,12 +2465,20 @@
 		break;
 	}
 
-	ql_dbg(ql_dbg_async, sp->vha, 0x5037,
-	    "Async-%s failed: handle=%x pid=%06x wwpn=%8phC comp_status=%x iop0=%x iop1=%x\n",
-	    type, sp->handle, fcport->d_id.b24, fcport->port_name,
-	    le16_to_cpu(logio->comp_status),
-	    le32_to_cpu(logio->io_parameter[0]),
-	    le32_to_cpu(logio->io_parameter[1]));
+	if (logit)
+		ql_log(ql_log_warn, sp->vha, 0x5037, "Async-%s failed: "
+		       "handle=%x pid=%06x wwpn=%8phC comp_status=%x iop0=%x iop1=%x\n",
+		       type, sp->handle, fcport->d_id.b24, fcport->port_name,
+		       le16_to_cpu(logio->comp_status),
+		       le32_to_cpu(logio->io_parameter[0]),
+		       le32_to_cpu(logio->io_parameter[1]));
+	else
+		ql_dbg(ql_dbg_disc, sp->vha, 0x5037, "Async-%s failed: "
+		       "handle=%x pid=%06x wwpn=%8phC comp_status=%x iop0=%x iop1=%x\n",
+		       type, sp->handle, fcport->d_id.b24, fcport->port_name,
+		       le16_to_cpu(logio->comp_status),
+		       le32_to_cpu(logio->io_parameter[0]),
+		       le32_to_cpu(logio->io_parameter[1]));
 
 logio_done:
 	sp->done(sp, 0);
@@ -2234,11 +2493,13 @@
 	srb_t *sp;
 	struct srb_iocb *iocb;
 	struct sts_entry_24xx *sts = (struct sts_entry_24xx *)tsk;
+	u16 comp_status;
 
 	sp = qla2x00_get_sp_from_handle(vha, func, req, tsk);
 	if (!sp)
 		return;
 
+	comp_status = le16_to_cpu(sts->comp_status);
 	iocb = &sp->u.iocb_cmd;
 	type = sp->name;
 	fcport = sp->fcport;
@@ -2252,10 +2513,11 @@
 	} else if (sts->comp_status != cpu_to_le16(CS_COMPLETE)) {
 		ql_log(ql_log_warn, fcport->vha, 0x5039,
 		    "Async-%s error - hdl=%x completion status(%x).\n",
-		    type, sp->handle, sts->comp_status);
+		    type, sp->handle, comp_status);
 		iocb->u.tmf.data = QLA_FUNCTION_FAILED;
 	} else if ((le16_to_cpu(sts->scsi_status) &
 	    SS_RESPONSE_INFO_LEN_VALID)) {
+		host_to_fcp_swap(sts->data, sizeof(sts->data));
 		if (le32_to_cpu(sts->rsp_data_len) < 4) {
 			ql_log(ql_log_warn, fcport->vha, 0x503b,
 			    "Async-%s error - hdl=%x not enough response(%d).\n",
@@ -2268,6 +2530,30 @@
 		}
 	}
 
+	switch (comp_status) {
+	case CS_PORT_LOGGED_OUT:
+	case CS_PORT_CONFIG_CHG:
+	case CS_PORT_BUSY:
+	case CS_INCOMPLETE:
+	case CS_PORT_UNAVAILABLE:
+	case CS_TIMEOUT:
+	case CS_RESET:
+		if (atomic_read(&fcport->state) == FCS_ONLINE) {
+			ql_dbg(ql_dbg_disc, fcport->vha, 0x3021,
+			       "-Port to be marked lost on fcport=%02x%02x%02x, current port state= %s comp_status %x.\n",
+			       fcport->d_id.b.domain, fcport->d_id.b.area,
+			       fcport->d_id.b.al_pa,
+			       port_state_str[FCS_ONLINE],
+			       comp_status);
+
+			qlt_schedule_sess_for_deletion(fcport);
+		}
+		break;
+
+	default:
+		break;
+	}
+
 	if (iocb->u.tmf.data != QLA_SUCCESS)
 		ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, sp->vha, 0x5055,
 		    sts, sizeof(*sts));
@@ -2295,6 +2581,8 @@
 
 	if (unlikely(iocb->u.nvme.aen_op))
 		atomic_dec(&sp->vha->hw->nvme_active_aen_cnt);
+	else
+		sp->qpair->cmd_completion_cnt++;
 
 	if (unlikely(comp_status != CS_COMPLETE))
 		logit = 1;
@@ -2367,9 +2655,9 @@
 		tgt_xfer_len = 0;
 #endif
 		if (fd->transferred_length != tgt_xfer_len) {
-			ql_dbg(ql_dbg_io, fcport->vha, 0x3079,
-				"Dropped frame(s) detected (sent/rcvd=%u/%u).\n",
-				tgt_xfer_len, fd->transferred_length);
+			ql_log(ql_log_warn, fcport->vha, 0x3079,
+			       "Dropped frame(s) detected (sent/rcvd=%u/%u).\n",
+			       tgt_xfer_len, fd->transferred_length);
 			logit = 1;
 		} else if (le16_to_cpu(comp_status) == CS_DATA_UNDERRUN) {
 			/*
@@ -2381,7 +2669,7 @@
 	}
 
 	if (unlikely(logit))
-		ql_log(ql_log_warn, fcport->vha, 0x5060,
+		ql_dbg(ql_dbg_io, fcport->vha, 0x5060,
 		   "NVME-%s ERR Handling - hdl=%x status(%x) tr_len:%x resid=%x  ox_id=%x\n",
 		   sp->name, sp->handle, comp_status,
 		   fd->transferred_length, le32_to_cpu(sts->residual_len),
@@ -2399,6 +2687,15 @@
 	case CS_PORT_UNAVAILABLE:
 	case CS_PORT_LOGGED_OUT:
 		fcport->nvme_flag |= NVME_FLAG_RESETTING;
+		if (atomic_read(&fcport->state) == FCS_ONLINE) {
+			ql_dbg(ql_dbg_disc, fcport->vha, 0x3021,
+			       "Port to be marked lost on fcport=%06x, current "
+			       "port state= %s comp_status %x.\n",
+			       fcport->d_id.b24, port_state_str[FCS_ONLINE],
+			       comp_status);
+
+			qlt_schedule_sess_for_deletion(fcport);
+		}
 		fallthrough;
 	case CS_ABORTED:
 	case CS_PORT_BUSY:
@@ -2678,31 +2975,22 @@
 
 	/* check guard */
 	if (e_guard != a_guard) {
-		scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
-		    0x10, 0x1);
-		set_driver_byte(cmd, DRIVER_SENSE);
+		scsi_build_sense(cmd, 1, ILLEGAL_REQUEST, 0x10, 0x1);
 		set_host_byte(cmd, DID_ABORT);
-		cmd->result |= SAM_STAT_CHECK_CONDITION;
 		return 1;
 	}
 
 	/* check ref tag */
 	if (e_ref_tag != a_ref_tag) {
-		scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
-		    0x10, 0x3);
-		set_driver_byte(cmd, DRIVER_SENSE);
+		scsi_build_sense(cmd, 1, ILLEGAL_REQUEST, 0x10, 0x3);
 		set_host_byte(cmd, DID_ABORT);
-		cmd->result |= SAM_STAT_CHECK_CONDITION;
 		return 1;
 	}
 
 	/* check appl tag */
 	if (e_app_tag != a_app_tag) {
-		scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
-		    0x10, 0x2);
-		set_driver_byte(cmd, DRIVER_SENSE);
+		scsi_build_sense(cmd, 1, ILLEGAL_REQUEST, 0x10, 0x2);
 		set_host_byte(cmd, DID_ABORT);
-		cmd->result |= SAM_STAT_CHECK_CONDITION;
 		return 1;
 	}
 
@@ -2719,11 +3007,7 @@
 	uint16_t	scsi_status;
 	uint16_t thread_id;
 	uint32_t rval = EXT_STATUS_OK;
-#ifndef NEW_LIBFC_API
-	struct fc_bsg_job *bsg_job = NULL;
-#else
-	struct bsg_job *bsg_job = NULL;
-#endif
+	BSG_JOB_TYPE *bsg_job = NULL;
 	struct fc_bsg_request *bsg_request;
 	struct fc_bsg_reply *bsg_reply;
 	sts_entry_t *sts = pkt;
@@ -2890,7 +3174,7 @@
 	int logit = 1;
 	int res = 0;
 	uint16_t state_flags = 0;
-	uint16_t retry_delay = 0;
+	uint16_t sts_qual = 0;
 
 	if (IS_FWI2_CAPABLE(ha)) {
 		comp_status = le16_to_cpu(sts24->comp_status);
@@ -2936,6 +3220,7 @@
 		}
 		return;
 	}
+	qla_put_iocbs(sp->qpair, &sp->iores);
 
 	if (sp->cmd_type != TYPE_SRB) {
 		req->outstanding_cmds[handle] = NULL;
@@ -2964,6 +3249,9 @@
 	}
 
 	/* Fast path completion. */
+	qla_chk_edif_rx_sa_delete_pending(vha, sp, sts24);
+	sp->qpair->cmd_completion_cnt++;
+
 	if (comp_status == CS_COMPLETE && scsi_status == 0) {
 		qla2x00_process_completed_request(vha, req, handle);
 
@@ -2988,8 +3276,6 @@
 	sense_len = par_sense_len = rsp_info_len = resid_len =
 	    fw_resid_len = 0;
 	if (IS_FWI2_CAPABLE(ha)) {
-		u16 sts24_retry_delay = le16_to_cpu(sts24->retry_delay);
-
 		if (scsi_status & SS_SENSE_LEN_VALID)
 			sense_len = le32_to_cpu(sts24->sense_len);
 		if (scsi_status & SS_RESPONSE_INFO_LEN_VALID)
@@ -3003,13 +3289,7 @@
 		host_to_fcp_swap(sts24->data, sizeof(sts24->data));
 		ox_id = le16_to_cpu(sts24->ox_id);
 		par_sense_len = sizeof(sts24->data);
-		/* Valid values of the retry delay timer are 0x1-0xffef */
-		if (sts24_retry_delay > 0 && sts24_retry_delay < 0xfff1) {
-			retry_delay = sts24_retry_delay & 0x3fff;
-			ql_dbg(ql_dbg_io, sp->vha, 0x3033,
-			    "%s: scope=%#x retry_delay=%#x\n", __func__,
-			    sts24_retry_delay >> 14, retry_delay);
-		}
+		sts_qual = le16_to_cpu(sts24->status_qualifier);
 	} else {
 		if (scsi_status & SS_SENSE_LEN_VALID)
 			sense_len = le16_to_cpu(sts->req_sense_length);
@@ -3047,9 +3327,9 @@
 	 * Check retry_delay_timer value if we receive a busy or
 	 * queue full.
 	 */
-	if (lscsi_status == SAM_STAT_TASK_SET_FULL ||
-	    lscsi_status == SAM_STAT_BUSY)
-		qla2x00_set_retry_delay_timestamp(fcport, retry_delay);
+	if (unlikely(lscsi_status == SAM_STAT_TASK_SET_FULL ||
+		     lscsi_status == SAM_STAT_BUSY))
+		qla2x00_set_retry_delay_timestamp(fcport, sts_qual);
 
 	/*
 	 * Based on Host and scsi status generate status code for Linux
@@ -3101,9 +3381,11 @@
 		scsi_set_resid(cp, resid);
 		if (scsi_status & SS_RESIDUAL_UNDER) {
 			if (IS_FWI2_CAPABLE(ha) && fw_resid_len != resid_len) {
-				ql_dbg(ql_dbg_io, fcport->vha, 0x301d,
-				    "Dropped frame(s) detected (0x%x of 0x%x bytes).\n",
-				    resid, scsi_bufflen(cp));
+				ql_log(ql_log_warn, fcport->vha, 0x301d,
+				       "Dropped frame(s) detected (0x%x of 0x%x bytes).\n",
+				       resid, scsi_bufflen(cp));
+
+				vha->interface_err_cnt++;
 
 				res = DID_ERROR << 16 | lscsi_status;
 				goto check_scsi_status;
@@ -3126,9 +3408,11 @@
 			 * task not completed.
 			 */
 
-			ql_dbg(ql_dbg_io, fcport->vha, 0x301f,
-			    "Dropped frame(s) detected (0x%x of 0x%x bytes).\n",
-			    resid, scsi_bufflen(cp));
+			ql_log(ql_log_warn, fcport->vha, 0x301f,
+			       "Dropped frame(s) detected (0x%x of 0x%x bytes).\n",
+			       resid, scsi_bufflen(cp));
+
+			vha->interface_err_cnt++;
 
 			res = DID_ERROR << 16 | lscsi_status;
 			goto check_scsi_status;
@@ -3172,6 +3456,7 @@
 	case CS_PORT_UNAVAILABLE:
 	case CS_TIMEOUT:
 	case CS_RESET:
+	case CS_EDIF_INV_REQ:
 
 		/*
 		 * We are going to have the fc class block the rport
@@ -3212,6 +3497,7 @@
 
 	case CS_TRANSPORT:
 		res = DID_ERROR << 16;
+		vha->hw_err_cnt++;
 
 		if (!IS_PI_SPLIT_DET_CAPABLE(ha))
 			break;
@@ -3232,6 +3518,7 @@
 		ql_dump_buffer(ql_dbg_tgt + ql_dbg_verbose, vha, 0xe0ee,
 		    pkt, sizeof(*sts24));
 		res = DID_ERROR << 16;
+		vha->hw_err_cnt++;
 		break;
 	default:
 		res = DID_ERROR << 16;
@@ -3241,14 +3528,12 @@
 out:
 	if (logit)
 		ql_dbg(ql_dbg_io, fcport->vha, 0x3022,
-		    "FCP command status: 0x%x-0x%x (0x%x) nexus=%ld:%d:%llu "
-		    "portid=%02x%02x%02x oxid=0x%x cdb=%10phN len=0x%x "
-		    "rsp_info=0x%x resid=0x%x fw_resid=0x%x sp=%p cp=%p.\n",
-		    comp_status, scsi_status, res, vha->host_no,
-		    cp->device->id, (u64)cp->device->lun, fcport->d_id.b.domain,
-		    fcport->d_id.b.area, fcport->d_id.b.al_pa, ox_id,
-		    cp->cmnd, scsi_bufflen(cp), rsp_info_len,
-		    resid_len, fw_resid_len, sp, cp);
+		       "FCP command status: 0x%x-0x%x (0x%x) nexus=%ld:%d:%llu portid=%02x%02x%02x oxid=0x%x cdb=%10phN len=0x%x rsp_info=0x%x resid=0x%x fw_resid=0x%x sp=%p cp=%p.\n",
+		       comp_status, scsi_status, res, vha->host_no,
+		       cp->device->id, (u64)cp->device->lun, fcport->d_id.b.domain,
+		       fcport->d_id.b.area, fcport->d_id.b.al_pa, ox_id,
+		       cp->cmnd, scsi_bufflen(cp), rsp_info_len,
+		       resid_len, fw_resid_len, sp, cp);
 
 	if (rsp->status_srb == NULL)
 		sp->done(sp, res);
@@ -3356,11 +3641,13 @@
 	default:
 		sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
 		if (sp) {
+			qla_put_iocbs(sp->qpair, &sp->iores);
 			sp->done(sp, res);
 			return 0;
 		}
 		break;
 
+	case SA_UPDATE_IOCB_TYPE:
 	case ABTS_RESP_24XX:
 	case CTIO_TYPE7:
 	case CTIO_CRC2:
@@ -3415,6 +3702,7 @@
 {
 	const char func[] = "ABT_IOCB";
 	srb_t *sp;
+	srb_t *orig_sp = NULL;
 	struct srb_iocb *abt;
 
 	sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
@@ -3422,7 +3710,12 @@
 		return;
 
 	abt = &sp->u.iocb_cmd;
-	abt->u.abt.comp_status = pkt->nport_handle;
+	abt->u.abt.comp_status = pkt->comp_status;
+	orig_sp = sp->cmd_sp;
+	/* Need to pass original sp */
+	if (orig_sp)
+		qla_nvme_abort_process_comp_status(pkt, orig_sp);
+
 	sp->done(sp, 0);
 }
 
@@ -3442,6 +3735,46 @@
 }
 
 /**
+ * qla_chk_cont_iocb_avail - check for all continuation iocbs are available
+ *   before iocb processing can start.
+ * @vha: host adapter pointer
+ * @rsp: respond queue
+ * @pkt: head iocb describing how many continuation iocb
+ * Return: 0 all iocbs has arrived, xx- all iocbs have not arrived.
+ */
+static int qla_chk_cont_iocb_avail(struct scsi_qla_host *vha,
+	struct rsp_que *rsp, response_t *pkt, u32 rsp_q_in)
+{
+	int start_pkt_ring_index;
+	u32 iocb_cnt = 0;
+	int rc = 0;
+
+	if (pkt->entry_count == 1)
+		return rc;
+
+	/* ring_index was pre-increment. set it back to current pkt */
+	if (rsp->ring_index == 0)
+		start_pkt_ring_index = rsp->length - 1;
+	else
+		start_pkt_ring_index = rsp->ring_index - 1;
+
+	if (rsp_q_in < start_pkt_ring_index)
+		/* q in ptr is wrapped */
+		iocb_cnt = rsp->length - start_pkt_ring_index + rsp_q_in;
+	else
+		iocb_cnt = rsp_q_in - start_pkt_ring_index;
+
+	if (iocb_cnt < pkt->entry_count)
+		rc = -EIO;
+
+	ql_dbg(ql_dbg_init, vha, 0x5091,
+	       "%s - ring %p pkt %p entry count %d iocb_cnt %d rsp_q_in %d rc %d\n",
+	       __func__, rsp->ring, pkt, pkt->entry_count, iocb_cnt, rsp_q_in, rc);
+
+	return rc;
+}
+
+/**
  * qla24xx_process_response_queue() - Process response queue entries.
  * @vha: SCSI driver HA context
  * @rsp: response queue
@@ -3453,15 +3786,32 @@
 	struct qla_hw_data *ha = vha->hw;
 	struct purex_entry_24xx *purex_entry;
 	struct purex_item *pure_item;
+	u16 rsp_in = 0, cur_ring_index;
+	int is_shadow_hba;
 
 	if (!ha->flags.fw_started)
 		return;
 
-	if (rsp->qpair->cpuid != raw_smp_processor_id())
+	if (rsp->qpair->cpuid != raw_smp_processor_id() ||
+	    !rsp->qpair->rcv_intr) {
+		rsp->qpair->rcv_intr = 1;
 		qla_cpu_update(rsp->qpair, raw_smp_processor_id());
+	}
 
-	while (rsp->ring_ptr->signature != RESPONSE_PROCESSED) {
+#define __update_rsp_in(_is_shadow_hba, _rsp, _rsp_in)			\
+	do {								\
+		_rsp_in = _is_shadow_hba ? *(_rsp)->in_ptr :		\
+				rd_reg_dword_relaxed((_rsp)->rsp_q_in);	\
+	} while (0)
+
+	is_shadow_hba = IS_SHADOW_REG_CAPABLE(ha);
+
+	__update_rsp_in(is_shadow_hba, rsp, rsp_in);
+
+	while (rsp->ring_index != rsp_in &&
+		       rsp->ring_ptr->signature != RESPONSE_PROCESSED) {
 		pkt = (struct sts_entry_24xx *)rsp->ring_ptr;
+		cur_ring_index = rsp->ring_index;
 
 		rsp->ring_index++;
 		if (rsp->ring_index == rsp->length) {
@@ -3573,18 +3923,43 @@
 				}
 				pure_item = qla27xx_copy_fpin_pkt(vha,
 							  (void **)&pkt, &rsp);
+				__update_rsp_in(is_shadow_hba, rsp, rsp_in);
 				if (!pure_item)
 					break;
 				qla24xx_queue_purex_item(vha, pure_item,
 						 qla27xx_process_purex_fpin);
 				break;
 
+			case ELS_AUTH_ELS:
+				if (qla_chk_cont_iocb_avail(vha, rsp, (response_t *)pkt, rsp_in)) {
+					/*
+					 * ring_ptr and ring_index were
+					 * pre-incremented above. Reset them
+					 * back to current. Wait for next
+					 * interrupt with all IOCBs to arrive
+					 * and re-process.
+					 */
+					rsp->ring_ptr = (response_t *)pkt;
+					rsp->ring_index = cur_ring_index;
+
+					ql_dbg(ql_dbg_init, vha, 0x5091,
+					    "Defer processing ELS opcode %#x...\n",
+					    purex_entry->els_frame_payload[3]);
+					return;
+				}
+				qla24xx_auth_els(vha, (void **)&pkt, &rsp);
+				break;
 			default:
 				ql_log(ql_log_warn, vha, 0x509c,
 				       "Discarding ELS Request opcode 0x%x\n",
 				       purex_entry->els_frame_payload[3]);
 			}
 			break;
+		case SA_UPDATE_IOCB_TYPE:
+			qla28xx_sa_update_iocb_entry(vha, rsp->req,
+				(struct sa_update_28xx *)pkt);
+			break;
+
 		default:
 			/* Type Not Supported. */
 			ql_dbg(ql_dbg_async, vha, 0x5042,
@@ -3840,6 +4215,7 @@
 			    hccr);
 
 			qla2xxx_check_risc_status(vha);
+			vha->hw_err_cnt++;
 
 			ha->isp_ops->fw_dump(vha);
 			set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
@@ -3908,7 +4284,7 @@
 	}
 	ha = qpair->hw;
 
-	queue_work(ha->wq, &qpair->q_work);
+	queue_work_on(smp_processor_id(), ha->wq, &qpair->q_work);
 
 	return IRQ_HANDLED;
 }
@@ -3934,7 +4310,7 @@
 	wrt_reg_dword(&reg->hccr, HCCRX_CLR_RISC_INT);
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
-	queue_work(ha->wq, &qpair->q_work);
+	queue_work_on(smp_processor_id(), ha->wq, &qpair->q_work);
 
 	return IRQ_HANDLED;
 }
@@ -4047,10 +4423,12 @@
 	if (USER_CTRL_IRQ(ha) || !ha->mqiobase) {
 		/* user wants to control IRQ setting for target mode */
 		ret = pci_alloc_irq_vectors(ha->pdev, min_vecs,
-		    ha->msix_count, PCI_IRQ_MSIX);
+		    min((u16)ha->msix_count, (u16)(num_online_cpus() + min_vecs)),
+		    PCI_IRQ_MSIX);
 	} else
 		ret = pci_alloc_irq_vectors_affinity(ha->pdev, min_vecs,
-		    ha->msix_count, PCI_IRQ_MSIX | PCI_IRQ_AFFINITY,
+		    min((u16)ha->msix_count, (u16)(num_online_cpus() + min_vecs)),
+		    PCI_IRQ_MSIX | PCI_IRQ_AFFINITY,
 		    &desc);
 #endif
 
@@ -4164,16 +4542,12 @@
 	}
 
 	/* Enable MSI-X vector for response queue update for queue 0 */
-	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
-		if (ha->msixbase && ha->mqiobase &&
-		    (ha->max_rsp_queues > 1 || ha->max_req_queues > 1 ||
-		     ql2xmqsupport))
-			ha->mqenable = 1;
-	} else
-		if (ha->mqiobase &&
-		    (ha->max_rsp_queues > 1 || ha->max_req_queues > 1 ||
-		     ql2xmqsupport))
-			ha->mqenable = 1;
+	if (IS_MQUE_CAPABLE(ha) &&
+	    (ha->msixbase && ha->mqiobase && ha->max_qpairs))
+		ha->mqenable = 1;
+	else
+		ha->mqenable = 0;
+
 	ql_dbg(ql_dbg_multiq, vha, 0xc005,
 	    "mqiobase=%p, max_rsp_queues=%d, max_req_queues=%d.\n",
 	    ha->mqiobase, ha->max_rsp_queues, ha->max_req_queues);
@@ -4278,6 +4652,8 @@
 		ql_dbg(ql_dbg_init, vha, 0x0125,
 		    "INTa mode: Enabled.\n");
 		ha->flags.mr_intr_valid = 1;
+		/* Set max_qpair to 0, as MSI-X and MSI in not enabled */
+		ha->max_qpairs = 0;
 	}
 
 clear_risc_ints:
diff --git a/scst/qla2x00t-32gbit/qla_mbx.c b/scst/qla2x00t-32gbit/qla_mbx.c
index 53c7321..359595a 100644
--- a/scst/qla2x00t-32gbit/qla_mbx.c
+++ b/scst/qla2x00t-32gbit/qla_mbx.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
 #include "qla_target.h"
@@ -10,6 +9,12 @@
 #include <linux/delay.h>
 #include <linux/gfp.h>
 
+#ifdef CONFIG_PPC
+#define IS_PPCARCH      true
+#else
+#define IS_PPCARCH      false
+#endif
+
 static struct mb_cmd_name {
 	uint16_t cmd;
 	const char *str;
@@ -103,7 +108,7 @@
 	int		rval, i;
 	unsigned long    flags = 0;
 	device_reg_t *reg;
-	uint8_t		abort_active;
+	uint8_t		abort_active, eeh_delay;
 	uint8_t		io_lock_on;
 	uint16_t	command = 0;
 	uint16_t	*iptr;
@@ -137,7 +142,7 @@
 		    "PCI error, exiting.\n");
 		return QLA_FUNCTION_TIMEOUT;
 	}
-
+	eeh_delay = 0;
 	reg = ha->iobase;
 	io_lock_on = base_vha->flags.init_done;
 
@@ -160,10 +165,10 @@
 	}
 
 	/* check if ISP abort is active and return cmd with timeout */
-	if ((test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) ||
-	    test_bit(ISP_ABORT_RETRY, &base_vha->dpc_flags) ||
-	    test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) &&
-	    !is_rom_cmd(mcp->mb[0])) {
+	if (((test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) ||
+	      test_bit(ISP_ABORT_RETRY, &base_vha->dpc_flags) ||
+	      test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) &&
+	      !is_rom_cmd(mcp->mb[0])) || ha->flags.eeh_busy) {
 		ql_log(ql_log_info, vha, 0x1005,
 		    "Cmd 0x%x aborted with timeout since ISP Abort is pending\n",
 		    mcp->mb[0]);
@@ -181,11 +186,16 @@
 		ql_log(ql_log_warn, vha, 0xd035,
 		    "Cmd access timeout, cmd=0x%x, Exiting.\n",
 		    mcp->mb[0]);
+		vha->hw_err_cnt++;
 		atomic_dec(&ha->num_pend_mbx_stage1);
 		return QLA_FUNCTION_TIMEOUT;
 	}
 	atomic_dec(&ha->num_pend_mbx_stage1);
-	if (ha->flags.purge_mbox || chip_reset != ha->chip_reset) {
+	if (ha->flags.purge_mbox || chip_reset != ha->chip_reset ||
+	    ha->flags.eeh_busy) {
+		ql_log(ql_log_warn, vha, 0xd035,
+		       "Error detected: purge[%d] eeh[%d] cmd=0x%x, Exiting.\n",
+		       ha->flags.purge_mbox, ha->flags.eeh_busy, mcp->mb[0]);
 		rval = QLA_ABORTED;
 		goto premature_exit;
 	}
@@ -228,6 +238,8 @@
 			ql_dbg(ql_dbg_mbx, vha, 0x1112,
 			    "mbox[%d]<-0x%04x\n", cnt, *iptr);
 			wrt_reg_word(optr, *iptr);
+		} else {
+			wrt_reg_word(optr, 0);
 		}
 
 		mboxes >>= 1;
@@ -264,7 +276,15 @@
 		atomic_inc(&ha->num_pend_mbx_stage3);
 		if (!wait_for_completion_timeout(&ha->mbx_intr_comp,
 		    mcp->tov * HZ)) {
+			ql_dbg(ql_dbg_mbx, vha, 0x117a,
+			    "cmd=%x Timeout.\n", command);
+			spin_lock_irqsave(&ha->hardware_lock, flags);
+			clear_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags);
+			spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
 			if (chip_reset != ha->chip_reset) {
+				eeh_delay = ha->flags.eeh_busy ? 1 : 0;
+
 				spin_lock_irqsave(&ha->hardware_lock, flags);
 				ha->flags.mbox_busy = 0;
 				spin_unlock_irqrestore(&ha->hardware_lock,
@@ -274,14 +294,10 @@
 				rval = QLA_ABORTED;
 				goto premature_exit;
 			}
-			ql_dbg(ql_dbg_mbx, vha, 0x117a,
-			    "cmd=%x Timeout.\n", command);
-			spin_lock_irqsave(&ha->hardware_lock, flags);
-			clear_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags);
-			spin_unlock_irqrestore(&ha->hardware_lock, flags);
-
 		} else if (ha->flags.purge_mbox ||
 		    chip_reset != ha->chip_reset) {
+			eeh_delay = ha->flags.eeh_busy ? 1 : 0;
+
 			spin_lock_irqsave(&ha->hardware_lock, flags);
 			ha->flags.mbox_busy = 0;
 			spin_unlock_irqrestore(&ha->hardware_lock, flags);
@@ -308,6 +324,7 @@
 				atomic_dec(&ha->num_pend_mbx_stage2);
 				ql_dbg(ql_dbg_mbx, vha, 0x1012,
 				    "Pending mailbox timeout, exiting.\n");
+				vha->hw_err_cnt++;
 				rval = QLA_FUNCTION_TIMEOUT;
 				goto premature_exit;
 			}
@@ -322,6 +339,8 @@
 		while (!ha->flags.mbox_int) {
 			if (ha->flags.purge_mbox ||
 			    chip_reset != ha->chip_reset) {
+				eeh_delay = ha->flags.eeh_busy ? 1 : 0;
+
 				spin_lock_irqsave(&ha->hardware_lock, flags);
 				ha->flags.mbox_busy = 0;
 				spin_unlock_irqrestore(&ha->hardware_lock,
@@ -419,6 +438,7 @@
 			    "mb[0-3]=[0x%x 0x%x 0x%x 0x%x] mb7 0x%x host_status 0x%x hccr 0x%x\n",
 			    command, ictrl, jiffies, mb[0], mb[1], mb[2], mb[3],
 			    mb[7], host_status, hccr);
+			vha->hw_err_cnt++;
 
 		} else {
 			mb[0] = RD_MAILBOX_REG(ha, &reg->isp, 0);
@@ -426,6 +446,7 @@
 			ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1119,
 			    "MBX Command timeout for cmd %x, iocontrol=%x jiffies=%lx "
 			    "mb[0]=0x%x\n", command, ictrl, jiffies, mb[0]);
+			vha->hw_err_cnt++;
 		}
 		ql_dump_regs(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1019);
 
@@ -498,6 +519,7 @@
 				    "mb[0]=0x%x, eeh_busy=0x%x. Scheduling ISP "
 				    "abort.\n", command, mcp->mb[0],
 				    ha->flags.eeh_busy);
+				vha->hw_err_cnt++;
 				set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
 				qla2xxx_wake_dpc(vha);
 			}
@@ -522,11 +544,13 @@
 				    "Mailbox cmd timeout occurred, cmd=0x%x, "
 				    "mb[0]=0x%x. Scheduling ISP abort ",
 				    command, mcp->mb[0]);
+				vha->hw_err_cnt++;
 				set_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags);
 				clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
 				/* Allow next mbx cmd to come in. */
 				complete(&ha->mbx_cmd_comp);
-				if (ha->isp_ops->abort_isp(vha)) {
+				if (ha->isp_ops->abort_isp(vha) &&
+				    !ha->flags.eeh_busy) {
 					/* Failed. retry later. */
 					set_bit(ISP_ABORT_NEEDED,
 					    &vha->dpc_flags);
@@ -579,6 +603,17 @@
 		ql_dbg(ql_dbg_mbx, base_vha, 0x1021, "Done %s.\n", __func__);
 	}
 
+	i = 500;
+	while (i && eeh_delay && (ha->pci_error_state < QLA_PCI_SLOT_RESET)) {
+		/*
+		 * The caller of this mailbox encounter pci error.
+		 * Hold the thread until PCIE link reset complete to make
+		 * sure caller does not unmap dma while recovery is
+		 * in progress.
+		 */
+		msleep(1);
+		i--;
+	}
 	return rval;
 }
 
@@ -626,6 +661,7 @@
 		ql_dbg(ql_dbg_mbx, vha, 0x1023,
 		    "Failed=%x mb[0]=%x mb[1]=%x.\n",
 		    rval, mcp->mb[0], mcp->mb[1]);
+		vha->hw_err_cnt++;
 	} else {
 		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1024,
 		    "Done %s.\n", __func__);
@@ -635,6 +671,7 @@
 }
 
 #define	NVME_ENABLE_FLAG	BIT_3
+#define	EDIF_HW_SUPPORT		BIT_10
 
 /*
  * qla2x00_execute_fw
@@ -660,7 +697,7 @@
 	mbx_cmd_t *mcp = &mc;
 	u8 semaphore = 0;
 #define EXE_FW_FORCE_SEMAPHORE BIT_7
-	u8 retry = 3;
+	u8 retry = 5;
 
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1025,
 	    "Entered %s.\n", __func__);
@@ -699,6 +736,9 @@
 				vha->min_supported_speed =
 				    nv->min_supported_speed;
 			}
+
+			if (IS_PPCARCH)
+				mcp->mb[11] |= BIT_4;
 		}
 
 		if (ha->flags.exlogins_enabled)
@@ -711,7 +751,7 @@
 			mcp->mb[11] |= EXE_FW_FORCE_SEMAPHORE;
 
 		mcp->out_mb |= MBX_4 | MBX_3 | MBX_2 | MBX_1 | MBX_11;
-		mcp->in_mb |= MBX_3 | MBX_2 | MBX_1;
+		mcp->in_mb |= MBX_5 | MBX_3 | MBX_2 | MBX_1;
 	} else {
 		mcp->mb[1] = LSW(risc_addr);
 		mcp->out_mb |= MBX_1;
@@ -735,8 +775,15 @@
 			goto again;
 		}
 
+		if (retry) {
+			retry--;
+			ql_dbg(ql_dbg_async, vha, 0x509d,
+			    "Exe FW retry: mb[0]=%x retry[%d]\n", mcp->mb[0], retry);
+			goto again;
+		}
 		ql_dbg(ql_dbg_mbx, vha, 0x1026,
 		    "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+		vha->hw_err_cnt++;
 		return rval;
 	}
 
@@ -766,6 +813,12 @@
 		}
 	}
 
+	if (IS_QLA28XX(ha) && (mcp->mb[5] & EDIF_HW_SUPPORT)) {
+		ha->flags.edif_hw = 1;
+		ql_log(ql_log_info, vha, 0xffff,
+		    "%s: edif HW\n", __func__);
+	}
+
 done:
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1028,
 	    "Done %s.\n", __func__);
@@ -845,7 +898,7 @@
  * Context:
  *	Kernel context.
  */
-#define CONFIG_XLOGINS_MEM	0x3
+#define CONFIG_XLOGINS_MEM	0x9
 int
 qla_set_exlogin_mem_cfg(scsi_qla_host_t *vha, dma_addr_t phys_addr)
 {
@@ -872,8 +925,9 @@
 	mcp->flags = 0;
 	rval = qla2x00_mailbox_command(vha, mcp);
 	if (rval != QLA_SUCCESS) {
-		/*EMPTY*/
-		ql_dbg(ql_dbg_mbx, vha, 0x111b, "Failed=%x.\n", rval);
+		ql_dbg(ql_dbg_mbx, vha, 0x111b,
+		       "EXlogin Failed=%x. MB0=%x MB11=%x\n",
+		       rval, mcp->mb[0], mcp->mb[11]);
 	} else {
 		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x118c,
 		    "Done %s.\n", __func__);
@@ -1092,6 +1146,21 @@
 			    "%s: FC-NVMe is Enabled (0x%x)\n",
 			     __func__, ha->fw_attributes_h);
 		}
+
+		/* BIT_13 of Extended FW Attributes informs about NVMe2 support */
+		if (ha->fw_attributes_ext[0] & FW_ATTR_EXT0_NVME2) {
+			ql_log(ql_log_info, vha, 0xd302,
+			       "Firmware supports NVMe2 0x%x\n",
+			       ha->fw_attributes_ext[0]);
+			vha->flags.nvme2_enabled = 1;
+		}
+
+		if (IS_QLA28XX(ha) && ha->flags.edif_hw && ql2xsecenable &&
+		    (ha->fw_attributes_ext[0] & FW_ATTR_EXT0_EDIF)) {
+			ha->flags.edif_enabled = 1;
+			ql_log(ql_log_info, vha, 0xffff,
+			       "%s: edif is enabled\n", __func__);
+		}
 	}
 
 	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
@@ -1121,12 +1190,18 @@
 		if (ha->flags.scm_supported_a &&
 		    (ha->fw_attributes_ext[0] & FW_ATTR_EXT0_SCM_SUPPORTED)) {
 			ha->flags.scm_supported_f = 1;
-			memset(ha->sf_init_cb, 0, sizeof(struct init_sf_cb));
-			ha->sf_init_cb->flags |= BIT_13;
+			ha->sf_init_cb->flags |= cpu_to_le16(BIT_13);
 		}
 		ql_log(ql_log_info, vha, 0x11a3, "SCM in FW: %s\n",
 		       (ha->flags.scm_supported_f) ? "Supported" :
 		       "Not Supported");
+
+		if (vha->flags.nvme2_enabled) {
+			/* set BIT_15 of special feature control block for SLER */
+			ha->sf_init_cb->flags |= cpu_to_le16(BIT_15);
+			/* set BIT_14 of special feature control block for PI CTRL*/
+			ha->sf_init_cb->flags |= cpu_to_le16(BIT_14);
+		}
 	}
 
 failed:
@@ -1299,6 +1374,7 @@
 	if (rval != QLA_SUCCESS) {
 		/*EMPTY*/
 		ql_dbg(ql_dbg_mbx, vha, 0x1033, "Failed=%x.\n", rval);
+		vha->hw_err_cnt++;
 	} else {
 		/*EMPTY*/
 		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1034,
@@ -1636,10 +1712,8 @@
 		mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10;
 	if (IS_FWI2_CAPABLE(vha->hw))
 		mcp->in_mb |= MBX_19|MBX_18|MBX_17|MBX_16;
-	if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw)) {
-		mcp->in_mb |= MBX_15;
-		mcp->out_mb |= MBX_7|MBX_21|MBX_22|MBX_23;
-	}
+	if (IS_QLA27XX(vha->hw) || IS_QLA28XX(vha->hw))
+		mcp->in_mb |= MBX_15|MBX_21|MBX_22|MBX_23;
 
 	mcp->tov = MBX_TOV_SECONDS;
 	mcp->flags = 0;
@@ -1822,7 +1896,7 @@
 		mcp->out_mb |= MBX_14|MBX_13|MBX_12|MBX_11|MBX_10;
 	}
 
-	if (ha->flags.scm_supported_f) {
+	if (ha->flags.scm_supported_f || vha->flags.nvme2_enabled) {
 		mcp->mb[1] |= BIT_1;
 		mcp->mb[16] = MSW(ha->sf_init_cb_dma);
 		mcp->mb[17] = LSW(ha->sf_init_cb_dma);
@@ -2972,8 +3046,7 @@
 		ha->orig_fw_iocb_count = mcp->mb[10];
 		if (ha->flags.npiv_supported)
 			ha->max_npiv_vports = mcp->mb[11];
-		if (IS_QLA81XX(ha) || IS_QLA83XX(ha) || IS_QLA27XX(ha) ||
-		    IS_QLA28XX(ha))
+		if (IS_QLA81XX(ha) || IS_QLA83XX(ha))
 			ha->fw_max_fcf_count = mcp->mb[12];
 	}
 
@@ -2995,7 +3068,8 @@
  *	Kernel context.
  */
 int
-qla2x00_get_fcal_position_map(scsi_qla_host_t *vha, char *pos_map)
+qla2x00_get_fcal_position_map(scsi_qla_host_t *vha, char *pos_map,
+		u8 *num_entries)
 {
 	int rval;
 	mbx_cmd_t mc;
@@ -3035,6 +3109,8 @@
 
 		if (pos_map)
 			memcpy(pos_map, pmap, FCAL_MAP_SIZE);
+		if (num_entries)
+			*num_entries = pmap[0];
 	}
 	dma_pool_free(ha->s_dma_pool, pmap, pmap_dma);
 
@@ -3177,7 +3253,7 @@
 	fc_port_t	*fcport = sp->fcport;
 	struct scsi_qla_host *vha = fcport->vha;
 	struct qla_hw_data *ha = vha->hw;
-	struct req_que *req = vha->req;
+	struct req_que *req;
 	struct qla_qpair *qpair = sp->qpair;
 
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x108c,
@@ -3186,7 +3262,7 @@
 	if (sp->qpair)
 		req = sp->qpair->req;
 	else
-		return QLA_FUNCTION_FAILED;
+		return QLA_ERR_NO_QPAIR;
 
 	if (ql2xasynctmfenable)
 		return qla24xx_async_abort_command(sp);
@@ -3199,7 +3275,7 @@
 	spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
 	if (handle == req->num_outstanding_cmds) {
 		/* Command not found. */
-		return QLA_FUNCTION_FAILED;
+		return QLA_ERR_NOT_FOUND;
 	}
 
 	abt = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL, &abt_dma);
@@ -3220,6 +3296,8 @@
 	abt->vp_index = fcport->vha->vp_idx;
 
 	abt->req_que_no = cpu_to_le16(req->id);
+	/* Need to pass original sp */
+	qla_nvme_abort_set_option(abt, sp);
 
 	rval = qla2x00_issue_iocb(vha, abt, abt_dma, 0);
 	if (rval != QLA_SUCCESS) {
@@ -3242,6 +3320,10 @@
 		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1091,
 		    "Done %s.\n", __func__);
 	}
+	if (rval == QLA_SUCCESS)
+		qla_nvme_abort_process_comp_status(abt, sp);
+
+	qla_wait_nvme_release_cmd_kref(sp);
 
 	dma_pool_free(ha->s_dma_pool, abt, abt_dma);
 
@@ -3979,10 +4061,12 @@
 
 			if (fcport) {
 				fcport->plogi_nack_done_deadline = jiffies + HZ;
-				fcport->dm_login_expire = jiffies + 2*HZ;
+				fcport->dm_login_expire = jiffies +
+					QLA_N2N_WAIT_TIME * HZ;
 				fcport->scan_state = QLA_FCPORT_FOUND;
 				fcport->n2n_flag = 1;
 				fcport->keep_nport_handle = 1;
+				fcport->login_retry = vha->hw->login_retry_count;
 				fcport->fc4_type = FS_FC4TYPE_FCP;
 				if (vha->flags.nvme_enabled)
 					fcport->fc4_type |= FS_FC4TYPE_NVME;
@@ -4015,7 +4099,6 @@
 
 			set_bit(N2N_LOGIN_NEEDED, &vha->dpc_flags);
 			return;
-			break;
 		case TOPO_FL:
 			ha->current_topology = ISP_CFG_FL;
 			break;
@@ -4124,6 +4207,16 @@
 				rptid_entry->u.f2.remote_nport_id[1];
 			fcport->d_id.b.al_pa =
 				rptid_entry->u.f2.remote_nport_id[0];
+
+			/*
+			 * For the case where remote port sending PRLO, FW
+			 * sends up RIDA Format 2 as an indication of session
+			 * loss. In other word, FW state change from PRLI
+			 * complete back to PLOGI complete. Delete the
+			 * session and let relogin drive the reconnect.
+			 */
+			if (atomic_read(&fcport->state) == FCS_ONLINE)
+				qlt_schedule_sess_for_deletion(fcport);
 		}
 	}
 }
@@ -4265,7 +4358,8 @@
 	if (MSW(addr) || IS_FWI2_CAPABLE(vha->hw)) {
 		mcp->mb[0] = MBC_DUMP_RISC_RAM_EXTENDED;
 		mcp->mb[8] = MSW(addr);
-		mcp->out_mb = MBX_8|MBX_0;
+		mcp->mb[10] = 0;
+		mcp->out_mb = MBX_10|MBX_8|MBX_0;
 	} else {
 		mcp->mb[0] = MBC_DUMP_RISC_RAM;
 		mcp->out_mb = MBX_0;
@@ -4897,7 +4991,7 @@
 	return rval;
 }
 
-#define PUREX_CMD_COUNT	2
+#define PUREX_CMD_COUNT	4
 int
 qla25xx_set_els_cmds_supported(scsi_qla_host_t *vha)
 {
@@ -4905,6 +4999,7 @@
 	mbx_cmd_t mc;
 	mbx_cmd_t *mcp = &mc;
 	uint8_t *els_cmd_map;
+	uint8_t active_cnt = 0;
 	dma_addr_t els_cmd_map_dma;
 	uint8_t cmd_opcode[PUREX_CMD_COUNT];
 	uint8_t i, index, purex_bit;
@@ -4926,10 +5021,20 @@
 	}
 
 	/* List of Purex ELS */
-	cmd_opcode[0] = ELS_FPIN;
-	cmd_opcode[1] = ELS_RDP;
+	if (ql2xrdpenable) {
+		cmd_opcode[active_cnt] = ELS_RDP;
+		active_cnt++;
+	}
+	if (ha->flags.scm_supported_f) {
+		cmd_opcode[active_cnt] = ELS_FPIN;
+		active_cnt++;
+	}
+	if (ha->flags.edif_enabled) {
+		cmd_opcode[active_cnt] = ELS_AUTH_ELS;
+		active_cnt++;
+	}
 
-	for (i = 0; i < PUREX_CMD_COUNT; i++) {
+	for (i = 0; i < active_cnt; i++) {
 		index = cmd_opcode[i] / 8;
 		purex_bit = cmd_opcode[i] % 8;
 		els_cmd_map[index] |= 1 << purex_bit;
@@ -4962,45 +5067,6 @@
 	return rval;
 }
 
-int
-qla24xx_get_buffer_credits(scsi_qla_host_t *vha, struct buffer_credit_24xx *bbc,
-	dma_addr_t bbc_dma)
-{
-	mbx_cmd_t mc;
-	mbx_cmd_t *mcp = &mc;
-	int rval;
-
-	if (!IS_FWI2_CAPABLE(vha->hw))
-		return QLA_FUNCTION_FAILED;
-
-	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x118e,
-	    "Entered %s.\n", __func__);
-
-	mcp->mb[0] = MBC_GET_RNID_PARAMS;
-	mcp->mb[1] = RNID_BUFFER_CREDITS << 8;
-	mcp->mb[2] = MSW(LSD(bbc_dma));
-	mcp->mb[3] = LSW(LSD(bbc_dma));
-	mcp->mb[6] = MSW(MSD(bbc_dma));
-	mcp->mb[7] = LSW(MSD(bbc_dma));
-	mcp->mb[8] = sizeof(*bbc) / sizeof(*bbc->parameter);
-	mcp->out_mb = MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
-	mcp->in_mb = MBX_1|MBX_0;
-	mcp->buf_size = sizeof(*bbc);
-	mcp->flags = MBX_DMA_IN;
-	mcp->tov = MBX_TOV_SECONDS;
-	rval = qla2x00_mailbox_command(vha, mcp);
-
-	if (rval != QLA_SUCCESS) {
-		ql_dbg(ql_dbg_mbx, vha, 0x118f,
-		    "Failed=%x mb[0]=%x,%x.\n", rval, mcp->mb[0], mcp->mb[1]);
-	} else {
-		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1190,
-		    "Done %s.\n", __func__);
-	}
-
-	return rval;
-}
-
 static int
 qla2x00_read_asic_temperature(scsi_qla_host_t *vha, uint16_t *temp)
 {
@@ -5574,7 +5640,7 @@
 	mcp->out_mb = MBX_1|MBX_0;
 	mcp->in_mb = MBX_2|MBX_1|MBX_0;
 	if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))
-		mcp->in_mb |= MBX_3;
+		mcp->in_mb |= MBX_4|MBX_3;
 	mcp->tov = MBX_TOV_SECONDS;
 	mcp->flags = 0;
 	rval = qla2x00_mailbox_command(vha, mcp);
@@ -6410,6 +6476,54 @@
 	return rval;
 }
 
+int
+qla26xx_dport_diagnostics_v2(scsi_qla_host_t *vha,
+			     struct qla_dport_diag_v2 *dd,  mbx_cmd_t *mcp)
+{
+	int rval;
+	dma_addr_t dd_dma;
+	uint size = sizeof(dd->buf);
+	uint16_t options = dd->options;
+
+	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x119f,
+	       "Entered %s.\n", __func__);
+
+	dd_dma = dma_map_single(&vha->hw->pdev->dev,
+				dd->buf, size, DMA_FROM_DEVICE);
+	if (dma_mapping_error(&vha->hw->pdev->dev, dd_dma)) {
+		ql_log(ql_log_warn, vha, 0x1194,
+		       "Failed to map dma buffer.\n");
+		return QLA_MEMORY_ALLOC_FAILED;
+	}
+
+	memset(dd->buf, 0, size);
+
+	mcp->mb[0] = MBC_DPORT_DIAGNOSTICS;
+	mcp->mb[1] = options;
+	mcp->mb[2] = MSW(LSD(dd_dma));
+	mcp->mb[3] = LSW(LSD(dd_dma));
+	mcp->mb[6] = MSW(MSD(dd_dma));
+	mcp->mb[7] = LSW(MSD(dd_dma));
+	mcp->mb[8] = size;
+	mcp->out_mb = MBX_8 | MBX_7 | MBX_6 | MBX_3 | MBX_2 | MBX_1 | MBX_0;
+	mcp->in_mb = MBX_3 | MBX_2 | MBX_1 | MBX_0;
+	mcp->buf_size = size;
+	mcp->flags = MBX_DMA_IN;
+	mcp->tov = MBX_TOV_SECONDS * 4;
+	rval = qla2x00_mailbox_command(vha, mcp);
+
+	if (rval != QLA_SUCCESS) {
+		ql_dbg(ql_dbg_mbx, vha, 0x1195, "Failed=%x.\n", rval);
+	} else {
+		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1196,
+		       "Done %s.\n", __func__);
+	}
+
+	dma_unmap_single(&vha->hw->pdev->dev, dd_dma, size, DMA_FROM_DEVICE);
+
+	return rval;
+}
+
 static void qla2x00_async_mb_sp_done(srb_t *sp, int res)
 {
 	sp->u.iocb_cmd.u.mbx.rc = res;
@@ -6432,23 +6546,21 @@
 	if (!vha->hw->flags.fw_started)
 		goto done;
 
+	/* ref: INIT */
 	sp = qla2x00_get_sp(vha, NULL, GFP_KERNEL);
 	if (!sp)
 		goto done;
 
-	sp->type = SRB_MB_IOCB;
-	sp->name = mb_to_str(mcp->mb[0]);
-
 	c = &sp->u.iocb_cmd;
-	c->timeout = qla2x00_async_iocb_timeout;
 	init_completion(&c->u.mbx.comp);
 
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+	sp->type = SRB_MB_IOCB;
+	sp->name = mb_to_str(mcp->mb[0]);
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla2x00_async_mb_sp_done);
 
 	memcpy(sp->u.iocb_cmd.u.mbx.out_mb, mcp->mb, SIZEOF_IOCB_MB_REG);
 
-	sp->done = qla2x00_async_mb_sp_done;
-
 	rval = qla2x00_start_sp(sp);
 	if (rval != QLA_SUCCESS) {
 		ql_dbg(ql_dbg_mbx, vha, 0x1018,
@@ -6480,7 +6592,8 @@
 	}
 
 done_free_sp:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 done:
 	return rval;
 }
@@ -6578,6 +6691,12 @@
 	fcport->d_id.b.al_pa = pd->port_id[2];
 	fcport->d_id.b.rsvd_1 = 0;
 
+	ql_dbg(ql_dbg_disc, vha, 0x2062,
+	     "%8phC SVC Param w3 %02x%02x",
+	     fcport->port_name,
+	     pd->prli_svc_param_word_3[1],
+	     pd->prli_svc_param_word_3[0]);
+
 	if (NVME_TARGET(vha->hw, fcport)) {
 		fcport->port_type = FCT_NVME;
 		if ((pd->prli_svc_param_word_3[0] & BIT_5) == 0)
@@ -6929,3 +7048,63 @@
 
 	return rval;
 }
+
+/**
+ * qla_no_op_mb(): This MB is used to check if FW is still alive and
+ * able to generate an interrupt. Otherwise, a timeout will trigger
+ * FW dump + reset
+ * @vha: host adapter pointer
+ * Return: None
+ */
+void qla_no_op_mb(struct scsi_qla_host *vha)
+{
+	mbx_cmd_t mc;
+	mbx_cmd_t *mcp = &mc;
+	int rval;
+
+	memset(&mc, 0, sizeof(mc));
+	mcp->mb[0] = 0; // noop cmd= 0
+	mcp->out_mb = MBX_0;
+	mcp->in_mb = MBX_0;
+	mcp->tov = 5;
+	mcp->flags = 0;
+	rval = qla2x00_mailbox_command(vha, mcp);
+
+	if (rval) {
+		ql_dbg(ql_dbg_async, vha, 0x7071,
+			"Failed %s %x\n", __func__, rval);
+	}
+}
+
+int qla_mailbox_passthru(scsi_qla_host_t *vha,
+			 uint16_t *mbx_in, uint16_t *mbx_out)
+{
+	mbx_cmd_t mc;
+	mbx_cmd_t *mcp = &mc;
+	int rval = -EINVAL;
+
+	memset(&mc, 0, sizeof(mc));
+	/* Receiving all 32 register's contents */
+	memcpy(&mcp->mb, (char *)mbx_in, (32 * sizeof(uint16_t)));
+
+	mcp->out_mb = 0xFFFFFFFF;
+	mcp->in_mb = 0xFFFFFFFF;
+
+	mcp->tov = MBX_TOV_SECONDS;
+	mcp->flags = 0;
+	mcp->bufp = NULL;
+
+	rval = qla2x00_mailbox_command(vha, mcp);
+
+	if (rval != QLA_SUCCESS) {
+		ql_dbg(ql_dbg_mbx, vha, 0xf0a2,
+			"Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+	} else {
+		ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0xf0a3, "Done %s.\n",
+		       __func__);
+		/* passing all 32 register's contents */
+		memcpy(mbx_out, &mcp->mb, 32 * sizeof(uint16_t));
+	}
+
+	return rval;
+}
diff --git a/scst/qla2x00t-32gbit/qla_mid.c b/scst/qla2x00t-32gbit/qla_mid.c
index 15efe2f..16a9f22 100644
--- a/scst/qla2x00t-32gbit/qla_mid.c
+++ b/scst/qla2x00t-32gbit/qla_mid.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
 #include "qla_gbl.h"
@@ -66,7 +65,7 @@
 	uint16_t vp_id;
 	struct qla_hw_data *ha = vha->hw;
 	unsigned long flags = 0;
-	u8 i;
+	u32 i, bailout;
 
 	mutex_lock(&ha->vport_lock);
 	/*
@@ -76,21 +75,29 @@
 	 * ensures no active vp_list traversal while the vport is removed
 	 * from the queue)
 	 */
-	for (i = 0; i < 10; i++) {
-		if (wait_event_timeout(vha->vref_waitq,
-		    !atomic_read(&vha->vref_count), HZ) > 0)
-			break;
-	}
+	bailout = 0;
+	for (i = 0; i < 500; i++) {
+		spin_lock_irqsave(&ha->vport_slock, flags);
+		if (atomic_read(&vha->vref_count) == 0) {
+			list_del(&vha->list);
+			qlt_update_vp_map(vha, RESET_VP_IDX);
+			bailout = 1;
+		}
+		spin_unlock_irqrestore(&ha->vport_slock, flags);
 
-	spin_lock_irqsave(&ha->vport_slock, flags);
-	if (atomic_read(&vha->vref_count)) {
-		ql_dbg(ql_dbg_vport, vha, 0xfffa,
-		    "vha->vref_count=%u timeout\n", vha->vref_count.counter);
-		vha->vref_count = (atomic_t)ATOMIC_INIT(0);
+		if (bailout)
+			break;
+		else
+			msleep(20);
 	}
-	list_del(&vha->list);
-	qlt_update_vp_map(vha, RESET_VP_IDX);
-	spin_unlock_irqrestore(&ha->vport_slock, flags);
+	if (!bailout) {
+		ql_log(ql_log_info, vha, 0xfffa,
+			"vha->vref_count=%u timeout\n", vha->vref_count.counter);
+		spin_lock_irqsave(&ha->vport_slock, flags);
+		list_del(&vha->list);
+		qlt_update_vp_map(vha, RESET_VP_IDX);
+		spin_unlock_irqrestore(&ha->vport_slock, flags);
+	}
 
 	vp_id = vha->vp_idx;
 	ha->num_vhosts--;
@@ -159,6 +166,14 @@
 	int ret = QLA_SUCCESS;
 	fc_port_t *fcport;
 
+	if (vha->hw->flags.edif_enabled) {
+		if (DBELL_ACTIVE(vha))
+			qla2x00_post_aen_work(vha, FCH_EVT_VENDOR_UNIQUE,
+			    FCH_EVT_VENDOR_UNIQUE_VPORT_DOWN);
+		/* delete sessions and flush sa_indexes */
+		qla2x00_wait_for_sess_deletion(vha);
+	}
+
 	if (vha->hw->flags.fw_started)
 		ret = qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL);
 
@@ -167,7 +182,8 @@
 	list_for_each_entry(fcport, &vha->vp_fcports, list)
 		fcport->logout_on_delete = 0;
 
-	qla2x00_mark_all_devices_lost(vha);
+	if (!vha->hw->flags.edif_enabled)
+		qla2x00_wait_for_sess_deletion(vha);
 
 	/* Remove port id from vp target map */
 	spin_lock_irqsave(&vha->hw->hardware_lock, flags);
@@ -258,13 +274,13 @@
 void
 qla2x00_alert_all_vps(struct rsp_que *rsp, uint16_t *mb)
 {
-	scsi_qla_host_t *vha;
+	scsi_qla_host_t *vha, *tvp;
 	struct qla_hw_data *ha = rsp->hw;
 	int i = 0;
 	unsigned long flags;
 
 	spin_lock_irqsave(&ha->vport_slock, flags);
-	list_for_each_entry(vha, &ha->vp_list, list) {
+	list_for_each_entry_safe(vha, tvp, &ha->vp_list, list) {
 		if (vha->vp_idx) {
 			if (test_bit(VPORT_DELETE, &vha->dpc_flags))
 				continue;
@@ -417,7 +433,7 @@
 qla2x00_do_dpc_all_vps(scsi_qla_host_t *vha)
 {
 	struct qla_hw_data *ha = vha->hw;
-	scsi_qla_host_t *vp;
+	scsi_qla_host_t *vp, *tvp;
 	unsigned long flags = 0;
 
 	if (vha->vp_idx)
@@ -431,7 +447,7 @@
 		return;
 
 	spin_lock_irqsave(&ha->vport_slock, flags);
-	list_for_each_entry(vp, &ha->vp_list, list) {
+	list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) {
 		if (vp->vp_idx) {
 			atomic_inc(&vp->vref_count);
 			spin_unlock_irqrestore(&ha->vport_slock, flags);
@@ -579,7 +595,6 @@
 	}
 	kfree(req->outstanding_cmds);
 	kfree(req);
-	req = NULL;
 }
 
 static void
@@ -605,7 +620,6 @@
 		mutex_unlock(&ha->vport_lock);
 	}
 	kfree(rsp);
-	rsp = NULL;
 }
 
 int
@@ -808,11 +822,9 @@
 {
 	unsigned long flags;
 	struct qla_qpair *qpair = container_of(work, struct qla_qpair, q_work);
-	struct scsi_qla_host *vha;
-	struct qla_hw_data *ha = qpair->hw;
+	struct scsi_qla_host *vha = qpair->vha;
 
 	spin_lock_irqsave(&qpair->qp_lock, flags);
-	vha = pci_get_drvdata(ha->pdev);
 	qla24xx_process_response_queue(vha, qpair->rsp);
 	spin_unlock_irqrestore(&qpair->qp_lock, flags);
 
@@ -955,6 +967,7 @@
 	if (vp_index == 0 || vp_index >= ha->max_npiv_vports)
 		return QLA_PARAMETER_ERROR;
 
+	/* ref: INIT */
 	sp = qla2x00_get_sp(base_vha, NULL, GFP_KERNEL);
 	if (!sp)
 		return rval;
@@ -962,9 +975,8 @@
 	sp->type = SRB_CTRL_VP;
 	sp->name = "ctrl_vp";
 	sp->comp = &comp;
-	sp->done = qla_ctrlvp_sp_done;
-	sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla_ctrlvp_sp_done);
 	sp->u.iocb_cmd.u.ctrlvp.cmd = cmd;
 	sp->u.iocb_cmd.u.ctrlvp.vp_index = vp_index;
 
@@ -998,6 +1010,7 @@
 		break;
 	}
 done:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 	return rval;
 }
diff --git a/scst/qla2x00t-32gbit/qla_mr.c b/scst/qla2x00t-32gbit/qla_mr.c
index e2d854f..84c129f 100644
--- a/scst/qla2x00t-32gbit/qla_mr.c
+++ b/scst/qla2x00t-32gbit/qla_mr.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
 #include <linux/delay.h>
@@ -517,7 +516,7 @@
 }
 
 /**
- * qlafx00_warm_reset() - Perform warm reset of iSA(CPUs being reset on SOC).
+ * qlafx00_soc_cpu_reset() - Perform warm reset of iSA(CPUs being reset on SOC).
  * @vha: HA context
  *
  */
@@ -740,29 +739,6 @@
 }
 
 int
-qlafx00_loop_reset(scsi_qla_host_t *vha)
-{
-	int ret;
-	struct fc_port *fcport;
-	struct qla_hw_data *ha = vha->hw;
-
-	if (ql2xtargetreset) {
-		list_for_each_entry(fcport, &vha->vp_fcports, list) {
-			if (fcport->port_type != FCT_TARGET)
-				continue;
-
-			ret = ha->isp_ops->target_reset(fcport, 0, 0);
-			if (ret != QLA_SUCCESS) {
-				ql_dbg(ql_dbg_taskm, vha, 0x803d,
-				    "Bus Reset failed: Reset=%d "
-				    "d_id=%x.\n", ret, fcport->d_id.b24);
-			}
-		}
-	}
-	return QLA_SUCCESS;
-}
-
-int
 qlafx00_iospace_config(struct qla_hw_data *ha)
 {
 	if (pci_request_selected_regions(ha->pdev, ha->bars,
@@ -1821,17 +1797,18 @@
 	struct register_host_info *preg_hsi;
 	struct new_utsname *p_sysid = NULL;
 
+	/* ref: INIT */
 	sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
 	if (!sp)
 		goto done;
 
 	sp->type = SRB_FXIOCB_DCMD;
 	sp->name = "fxdisc";
+	qla2x00_init_async_sp(sp, FXDISC_TIMEOUT,
+			      qla2x00_fxdisc_sp_done);
+	sp->u.iocb_cmd.timeout = qla2x00_fxdisc_iocb_timeout;
 
 	fdisc = &sp->u.iocb_cmd;
-	fdisc->timeout = qla2x00_fxdisc_iocb_timeout;
-	qla2x00_init_timer(sp, FXDISC_TIMEOUT);
-
 	switch (fx_type) {
 	case FXDISC_GET_CONFIG_INFO:
 	fdisc->u.fxiocb.flags =
@@ -1932,7 +1909,6 @@
 	}
 
 	fdisc->u.fxiocb.req_func_type = cpu_to_le16(fx_type);
-	sp->done = qla2x00_fxdisc_sp_done;
 
 	rval = qla2x00_start_sp(sp);
 	if (rval != QLA_SUCCESS)
@@ -2008,7 +1984,8 @@
 		dma_free_coherent(&ha->pdev->dev, fdisc->u.fxiocb.req_len,
 		    fdisc->u.fxiocb.req_addr, fdisc->u.fxiocb.req_dma_handle);
 done_free_sp:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 done:
 	return rval;
 }
@@ -2216,11 +2193,7 @@
 {
 	const char func[] = "IOSB_IOCB";
 	srb_t *sp;
-#ifndef NEW_LIBFC_API
-	struct fc_bsg_job *bsg_job;
-#else
-	struct bsg_job *bsg_job;
-#endif
+	BSG_JOB_TYPE *bsg_job;
 	struct fc_bsg_reply *bsg_reply;
 	struct srb_iocb *iocb_job;
 	int res = 0;
@@ -2876,7 +2849,7 @@
 }
 
 /**
- * qlafx00x_mbx_completion() - Process mailbox command completions.
+ * qlafx00_mbx_completion() - Process mailbox command completions.
  * @vha: SCSI driver HA context
  * @mb0: value to be written into mailbox register 0
  */
@@ -3258,11 +3231,7 @@
 {
 	struct srb_iocb *fxio = &sp->u.iocb_cmd;
 	struct qla_mt_iocb_rqst_fx00 *piocb_rqst;
-#ifndef NEW_LIBFC_API
-	struct fc_bsg_job *bsg_job;
-#else
-	struct bsg_job *bsg_job;
-#endif
+	BSG_JOB_TYPE *bsg_job;
 	struct fc_bsg_request *bsg_request;
 	struct fxdisc_entry_fx00 fx_iocb;
 	uint8_t entry_cnt = 1;
diff --git a/scst/qla2x00t-32gbit/qla_mr.h b/scst/qla2x00t-32gbit/qla_mr.h
index 968fc76..4f63aff 100644
--- a/scst/qla2x00t-32gbit/qla_mr.h
+++ b/scst/qla2x00t-32gbit/qla_mr.h
@@ -1,8 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #ifndef __QLA_MR_H
 #define __QLA_MR_H
@@ -177,6 +176,10 @@
 	uint8_t flags;
 	uint8_t reserved_1;
 
+	/*
+	 * Use array size 1 below to prevent that Coverity complains about
+	 * the append_dsd64() calls for the two arrays below.
+	 */
 	struct dsd64 dseg_rq[1];
 	struct dsd64 dseg_rsp[1];
 
diff --git a/scst/qla2x00t-32gbit/qla_nvme.c b/scst/qla2x00t-32gbit/qla_nvme.c
index a4a06e5..9c5ccfd 100644
--- a/scst/qla2x00t-32gbit/qla_nvme.c
+++ b/scst/qla2x00t-32gbit/qla_nvme.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2017 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 
 #include <linux/version.h>
@@ -14,6 +13,8 @@
 #include <linux/delay.h>
 #include <linux/nvme.h>
 #include <linux/nvme-fc.h>
+#include <linux/blk-mq-pci.h>
+#include <linux/blk-mq.h>
 
 static struct nvme_fc_port_template qla_nvme_fc_transport;
 
@@ -50,9 +51,11 @@
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
 	/*
 	 * See also commit 9dd9686b1419 ("scsi: qla2xxx: Add changes for
-	 * devloss timeout in driver") # v4.17.
+	 * devloss timeout in driver") # v4.17. See also commit dd8d0bf6fb72
+	 * ("scsi: qla2xxx: Fix I/O failures during remote port toggle
+	 * testing") # v5.10.
 	 */
-	req.dev_loss_tmo = NVME_FC_DEV_LOSS_TMO;
+	req.dev_loss_tmo = fcport->dev_loss_tmo;
 #endif
 
 	if (fcport->nvme_prli_service_param & NVME_PRLI_SP_INITIATOR)
@@ -80,6 +83,19 @@
 		return ret;
 	}
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
+	nvme_fc_set_remoteport_devloss(fcport->nvme_remote_port,
+				       fcport->dev_loss_tmo);
+#endif
+
+	if (fcport->nvme_prli_service_param & NVME_PRLI_SP_SLER)
+		ql_log(ql_log_info, vha, 0x212a,
+		       "PortID:%06x Supports SLER\n", req.port_id);
+
+	if (fcport->nvme_prli_service_param & NVME_PRLI_SP_PI_CTRL)
+		ql_log(ql_log_info, vha, 0x212b,
+		       "PortID:%06x Supports PI control\n", req.port_id);
+
 	rport = fcport->nvme_remote_port->private;
 	rport->fcport = fcport;
 
@@ -95,8 +111,9 @@
 	struct qla_hw_data *ha;
 	struct qla_qpair *qpair;
 
-	if (!qidx)
-		qidx++;
+	/* Map admin queue and 1st IO queue to index 0 */
+	if (qidx)
+		qidx--;
 
 	vha = (struct scsi_qla_host *)lport->private;
 	ha = vha->hw;
@@ -112,19 +129,24 @@
 		return -EINVAL;
 	}
 
-	if (ha->queue_pair_map[qidx]) {
-		*handle = ha->queue_pair_map[qidx];
-		ql_log(ql_log_info, vha, 0x2121,
-		    "Returning existing qpair of %p for idx=%x\n",
-		    *handle, qidx);
-		return 0;
-	}
+	/* Use base qpair if max_qpairs is 0 */
+	if (!ha->max_qpairs) {
+		qpair = ha->base_qpair;
+	} else {
+		if (ha->queue_pair_map[qidx]) {
+			*handle = ha->queue_pair_map[qidx];
+			ql_log(ql_log_info, vha, 0x2121,
+			       "Returning existing qpair of %p for idx=%x\n",
+			       *handle, qidx);
+			return 0;
+		}
 
-	qpair = qla2xxx_create_qpair(vha, 5, vha->vp_idx, true);
-	if (qpair == NULL) {
-		ql_log(ql_log_warn, vha, 0x2122,
-		    "Failed to allocate qpair\n");
-		return -EINVAL;
+		qpair = qla2xxx_create_qpair(vha, 5, vha->vp_idx, true);
+		if (!qpair) {
+			ql_log(ql_log_warn, vha, 0x2122,
+			       "Failed to allocate qpair\n");
+			return -EINVAL;
+		}
 	}
 	*handle = qpair;
 
@@ -163,6 +185,18 @@
 	qla2xxx_rel_qpair_sp(sp->qpair, sp);
 }
 
+static void qla_nvme_ls_unmap(struct srb *sp, struct nvmefc_ls_req *fd)
+{
+	if (sp->flags & SRB_DMA_VALID) {
+		struct srb_iocb *nvme = &sp->u.iocb_cmd;
+		struct qla_hw_data *ha = sp->fcport->vha->hw;
+
+		dma_unmap_single(&ha->pdev->dev, nvme->u.nvme.cmd_dma,
+				 fd->rqstlen, DMA_TO_DEVICE);
+		sp->flags &= ~SRB_DMA_VALID;
+	}
+}
+
 static void qla_nvme_release_ls_cmd_kref(struct kref *kref)
 {
 	struct srb *sp = container_of(kref, struct srb, cmd_kref);
@@ -179,6 +213,8 @@
 	spin_unlock_irqrestore(&priv->cmd_lock, flags);
 
 	fd = priv->fd;
+
+	qla_nvme_ls_unmap(sp, fd);
 	fd->done(fd, priv->comp_status);
 out:
 	qla2x00_rel_sp(sp);
@@ -225,13 +261,15 @@
 	srb_t *sp = priv->sp;
 	fc_port_t *fcport = sp->fcport;
 	struct qla_hw_data *ha = fcport->vha->hw;
-	int rval;
+	int rval, abts_done_called = 1;
+	bool io_wait_for_abort_done;
+	uint32_t handle;
 
 	ql_dbg(ql_dbg_io, fcport->vha, 0xffff,
-	       "%s called for sp=%p, hndl=%x on fcport=%p deleted=%d\n",
-	       __func__, sp, sp->handle, fcport, fcport->deleted);
+	       "%s called for sp=%p, hndl=%x on fcport=%p desc=%p deleted=%d\n",
+	       __func__, sp, sp->handle, fcport, sp->u.iocb_cmd.u.nvme.desc, fcport->deleted);
 
-	if (!ha->flags.fw_started && fcport->deleted)
+	if (!ha->flags.fw_started || fcport->deleted == QLA_SESS_DELETED)
 		goto out;
 
 	if (ha->flags.host_shutting_down) {
@@ -242,13 +280,36 @@
 		goto out;
 	}
 
+	/*
+	 * sp may not be valid after abort_command if return code is either
+	 * SUCCESS or ERR_FROM_FW codes, so cache the value here.
+	 */
+	io_wait_for_abort_done = ql2xabts_wait_nvme &&
+					QLA_ABTS_WAIT_ENABLED(sp);
+	handle = sp->handle;
+
 	rval = ha->isp_ops->abort_command(sp);
 
 	ql_dbg(ql_dbg_io, fcport->vha, 0x212b,
 	    "%s: %s command for sp=%p, handle=%x on fcport=%p rval=%x\n",
 	    __func__, (rval != QLA_SUCCESS) ? "Failed to abort" : "Aborted",
-	    sp, sp->handle, fcport, rval);
+	    sp, handle, fcport, rval);
 
+	/*
+	 * If async tmf is enabled, the abort callback is called only on
+	 * return codes QLA_SUCCESS and QLA_ERR_FROM_FW.
+	 */
+	if (ql2xasynctmfenable &&
+	    rval != QLA_SUCCESS && rval != QLA_ERR_FROM_FW)
+		abts_done_called = 0;
+
+	/*
+	 * Returned before decreasing kref so that I/O requests
+	 * are waited until ABTS complete. This kref is decreased
+	 * at qla24xx_abort_sp_done function.
+	 */
+	if (abts_done_called && io_wait_for_abort_done)
+		return;
 out:
 	/* kref_get was done before work was schedule. */
 	kref_put(&sp->cmd_kref, sp->put_fn);
@@ -288,8 +349,7 @@
 	struct qla_hw_data *ha;
 	srb_t           *sp;
 
-
-	if (!fcport || (fcport && fcport->deleted))
+	if (!fcport || fcport->deleted)
 		return rval;
 
 	vha = fcport->vha;
@@ -325,6 +385,8 @@
 	dma_sync_single_for_device(&ha->pdev->dev, nvme->u.nvme.cmd_dma,
 	    fd->rqstlen, DMA_TO_DEVICE);
 
+	sp->flags |= SRB_DMA_VALID;
+
 	rval = qla2x00_start_sp(sp);
 	if (rval != QLA_SUCCESS) {
 		ql_log(ql_log_warn, vha, 0x700e,
@@ -332,6 +394,7 @@
 		wake_up(&sp->nvme_ls_waitq);
 		sp->priv = NULL;
 		priv->sp = NULL;
+		qla_nvme_ls_unmap(sp, fd);
 		qla2x00_rel_sp(sp);
 		return rval;
 	}
@@ -373,16 +436,19 @@
 	uint16_t	avail_dsds;
 	struct dsd64	*cur_dsd;
 	struct req_que *req = NULL;
+	struct rsp_que *rsp = NULL;
 	struct scsi_qla_host *vha = sp->fcport->vha;
 	struct qla_hw_data *ha = vha->hw;
 	struct qla_qpair *qpair = sp->qpair;
 	struct srb_iocb *nvme = &sp->u.iocb_cmd;
 	struct scatterlist *sgl, *sg;
 	struct nvmefc_fcp_req *fd = nvme->u.nvme.desc;
+	struct nvme_fc_cmd_iu *cmd = fd->cmdaddr;
 	uint32_t        rval = QLA_SUCCESS;
 
 	/* Setup qpair pointers */
 	req = qpair->req;
+	rsp = qpair->rsp;
 	tot_dsds = fd->sg_cnt;
 
 	/* Acquire qpair specific lock */
@@ -395,8 +461,13 @@
 	}
 	req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
 	if (req->cnt < (req_cnt + 2)) {
-		cnt = IS_SHADOW_REG_CAPABLE(ha) ? *req->out_ptr :
-		    rd_reg_dword_relaxed(req->req_q_out);
+		if (IS_SHADOW_REG_CAPABLE(ha)) {
+			cnt = *req->out_ptr;
+		} else {
+			cnt = rd_reg_dword_relaxed(req->req_q_out);
+			if (qla2x00_check_reg16_for_disconnect(vha, cnt))
+				goto queuing_error;
+		}
 
 		if (req->ring_index < cnt)
 			req->cnt = cnt - req->ring_index;
@@ -410,8 +481,6 @@
 	}
 
 	if (unlikely(!fd->sqid)) {
-		struct nvme_fc_cmd_iu *cmd = fd->cmdaddr;
-
 		if (cmd->sqe.common.opcode == nvme_admin_async_event) {
 			nvme->u.nvme.aen_op = 1;
 			atomic_inc(&ha->nvme_active_aen_cnt);
@@ -439,8 +508,8 @@
 	/* No data transfer how do we check buffer len == 0?? */
 	if (fd->io_dir == NVMEFC_FCP_READ) {
 		cmd_pkt->control_flags = cpu_to_le16(CF_READ_DATA);
-		vha->qla_stats.input_bytes += fd->payload_length;
-		vha->qla_stats.input_requests++;
+		qpair->counters.input_bytes += fd->payload_length;
+		qpair->counters.input_requests++;
 	} else if (fd->io_dir == NVMEFC_FCP_WRITE) {
 		cmd_pkt->control_flags = cpu_to_le16(CF_WRITE_DATA);
 		if ((vha->flags.nvme_first_burst) &&
@@ -452,12 +521,21 @@
 				cmd_pkt->control_flags |=
 					cpu_to_le16(CF_NVME_FIRST_BURST_ENABLE);
 		}
-		vha->qla_stats.output_bytes += fd->payload_length;
-		vha->qla_stats.output_requests++;
+		qpair->counters.output_bytes += fd->payload_length;
+		qpair->counters.output_requests++;
 	} else if (fd->io_dir == 0) {
 		cmd_pkt->control_flags = 0;
 	}
 
+	if (sp->fcport->edif.enable && fd->io_dir != 0)
+		cmd_pkt->control_flags |= cpu_to_le16(CF_EN_EDIF);
+
+	/* Set BIT_13 of control flags for Async event */
+	if (vha->flags.nvme2_enabled &&
+	    cmd->sqe.common.opcode == nvme_admin_async_event) {
+		cmd_pkt->control_flags |= cpu_to_le16(CF_ADMIN_ASYNC_EVENT);
+	}
+
 	/* Set NPORT-ID */
 	cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
 	cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
@@ -525,11 +603,20 @@
 		req->ring_ptr++;
 	}
 
+	/* ignore nvme async cmd due to long timeout */
+	if (!nvme->u.nvme.aen_op)
+		sp->qpair->cmd_cnt++;
+
 	/* Set chip new ring index. */
 	wrt_reg_dword(req->req_q_in, req->ring_index);
 
+	if (vha->flags.process_response_queue &&
+	    rsp->ring_ptr->signature != RESPONSE_PROCESSED)
+		qla24xx_process_response_queue(vha, rsp);
+
 queuing_error:
 	spin_unlock_irqrestore(&qpair->qp_lock, flags);
+
 	return rval;
 }
 
@@ -541,7 +628,7 @@
 	fc_port_t *fcport;
 	struct srb_iocb *nvme;
 	struct scsi_qla_host *vha;
-	int rval = -ENODEV;
+	int rval;
 	srb_t *sp;
 	struct qla_qpair *qpair = hw_queue_handle;
 	struct nvme_private *priv = fd->private;
@@ -549,16 +636,22 @@
 
 	if (!priv) {
 		/* nvme association has been torn down */
-		return rval;
+		return -ENODEV;
 	}
 
 	fcport = qla_rport->fcport;
 
-	if (!qpair || !fcport || (qpair && !qpair->fw_started) ||
-	    (fcport && fcport->deleted))
-		return rval;
+	if (unlikely(!qpair || !fcport || fcport->deleted))
+		return -EBUSY;
+
+	if (!(fcport->nvme_flag & NVME_FLAG_REGISTERED))
+		return -ENODEV;
 
 	vha = fcport->vha;
+
+	if (test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))
+		return -EBUSY;
+
 	/*
 	 * If we know the dev is going away while the transport is still sending
 	 * IO's return busy back to stall the IO Q.  This happens when the
@@ -585,6 +678,7 @@
 	sp->put_fn = qla_nvme_release_fcp_cmd_kref;
 	sp->qpair = qpair;
 	sp->vha = vha;
+	sp->cmd_sp = sp;
 	nvme = &sp->u.iocb_cmd;
 	nvme->u.nvme.desc = fd;
 
@@ -601,6 +695,20 @@
 	return rval;
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0)
+/*
+ * See also commit 01d838164b4c ("nvme-fc: add support for ->map_queues")
+ * # v5.16.
+ */
+static void qla_nvme_map_queues(struct nvme_fc_local_port *lport,
+		struct blk_mq_queue_map *map)
+{
+	struct scsi_qla_host *vha = lport->private;
+
+	blk_mq_pci_map_queues(map, vha->hw->pdev, vha->irq_offset);
+}
+#endif
+
 static void qla_nvme_localport_delete(struct nvme_fc_local_port *lport)
 {
 	struct scsi_qla_host *vha = lport->private;
@@ -627,15 +735,6 @@
 }
 
 static struct nvme_fc_port_template qla_nvme_fc_transport = {
-#if 0
-	/*
-	 * See also commit 863fbae929c7 ("nvme_fc: add module to ops template
-	 * to allow module references"). See also commit 8c5c66052920
-	 * ("nvme-fc: Revert "add module to ops template to allow module
-	 * references"") # v5.7-rc1.
-	 */
-	.module	= THIS_MODULE,
-#endif
 	.localport_delete = qla_nvme_localport_delete,
 	.remoteport_delete = qla_nvme_remoteport_delete,
 	.create_queue   = qla_nvme_alloc_queue,
@@ -644,7 +743,10 @@
 	.ls_abort	= qla_nvme_ls_abort,
 	.fcp_io		= qla_nvme_post_cmd,
 	.fcp_abort	= qla_nvme_fcp_abort,
-	.max_hw_queues  = 8,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0)
+	.map_queues	= qla_nvme_map_queues,
+#endif
+	.max_hw_queues  = DEF_NVME_HW_QUEUES,
 	.max_sgl_segments = 1024,
 	.max_dif_sgl_segments = 64,
 	.dma_boundary = 0xFFFFFFFF,
@@ -661,11 +763,11 @@
 	if (!IS_ENABLED(CONFIG_NVME_FC))
 		return;
 
-	ql_log(ql_log_warn, NULL, 0x2112,
+	ql_log(ql_log_warn, fcport->vha, 0x2112,
 	    "%s: unregister remoteport on %p %8phN\n",
 	    __func__, fcport, fcport->port_name);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
 	if (test_bit(PFLG_DRIVER_REMOVING, &fcport->vha->pci_flags))
 		nvme_fc_set_remoteport_devloss(fcport->nvme_remote_port, 0);
 #endif
@@ -712,33 +814,52 @@
 	ha = vha->hw;
 	tmpl = &qla_nvme_fc_transport;
 
-	WARN_ON(vha->nvme_local_port);
-
-	if (ha->max_req_queues < 3) {
-		if (!ha->flags.max_req_queue_warned)
-			ql_log(ql_log_info, vha, 0x2120,
-			       "%s: Disabling FC-NVME due to lack of free queue pairs (%d).\n",
-			       __func__, ha->max_req_queues);
-		ha->flags.max_req_queue_warned = 1;
-		return ret;
+	if (ql2xnvme_queues < MIN_NVME_HW_QUEUES) {
+		ql_log(ql_log_warn, vha, 0xfffd,
+		    "ql2xnvme_queues=%d is lower than minimum queues: %d. Resetting ql2xnvme_queues to:%d\n",
+		    ql2xnvme_queues, MIN_NVME_HW_QUEUES, DEF_NVME_HW_QUEUES);
+		ql2xnvme_queues = DEF_NVME_HW_QUEUES;
+	} else if (ql2xnvme_queues > (ha->max_qpairs - 1)) {
+		ql_log(ql_log_warn, vha, 0xfffd,
+		       "ql2xnvme_queues=%d is greater than available IRQs: %d. Resetting ql2xnvme_queues to: %d\n",
+		       ql2xnvme_queues, (ha->max_qpairs - 1),
+		       (ha->max_qpairs - 1));
+		ql2xnvme_queues = ((ha->max_qpairs - 1));
 	}
 
 	qla_nvme_fc_transport.max_hw_queues =
-	    min((uint8_t)(qla_nvme_fc_transport.max_hw_queues),
-		(uint8_t)(ha->max_req_queues - 2));
+	    min((uint8_t)(ql2xnvme_queues),
+		(uint8_t)((ha->max_qpairs - 1) ? (ha->max_qpairs - 1) : 1));
+
+	ql_log(ql_log_info, vha, 0xfffb,
+	       "Number of NVME queues used for this port: %d\n",
+	    qla_nvme_fc_transport.max_hw_queues);
+
 
 	pinfo.node_name = wwn_to_u64(vha->node_name);
 	pinfo.port_name = wwn_to_u64(vha->port_name);
 	pinfo.port_role = FC_PORT_ROLE_NVME_INITIATOR;
 	pinfo.port_id = vha->d_id.b24;
 
-	ql_log(ql_log_info, vha, 0xffff,
-	    "register_localport: host-traddr=nn-0x%llx:pn-0x%llx on portID:%x\n",
-	    pinfo.node_name, pinfo.port_name, pinfo.port_id);
-	qla_nvme_fc_transport.dma_boundary = vha->host->dma_boundary;
+	mutex_lock(&ha->vport_lock);
+	/*
+	 * Check again for nvme_local_port to see if any other thread raced
+	 * with this one and finished registration.
+	 */
+	if (!vha->nvme_local_port) {
+		ql_log(ql_log_info, vha, 0xffff,
+		    "register_localport: host-traddr=nn-0x%llx:pn-0x%llx on portID:%x\n",
+		    pinfo.node_name, pinfo.port_name, pinfo.port_id);
+		qla_nvme_fc_transport.dma_boundary = vha->host->dma_boundary;
 
-	ret = nvme_fc_register_localport(&pinfo, tmpl,
-	    get_device(&ha->pdev->dev), &vha->nvme_local_port);
+		ret = nvme_fc_register_localport(&pinfo, tmpl,
+						 get_device(&ha->pdev->dev),
+						 &vha->nvme_local_port);
+		mutex_unlock(&ha->vport_lock);
+	} else {
+		mutex_unlock(&ha->vport_lock);
+		return 0;
+	}
 	if (ret) {
 		ql_log(ql_log_warn, vha, 0xffff,
 		    "register_localport failed: ret=%x\n", ret);
@@ -756,3 +877,85 @@
 }
 
 #endif
+
+void qla_nvme_abort_set_option(struct abort_entry_24xx *abt, srb_t *orig_sp)
+{
+	struct qla_hw_data *ha;
+
+	if (!(ql2xabts_wait_nvme && QLA_ABTS_WAIT_ENABLED(orig_sp)))
+		return;
+
+	ha = orig_sp->fcport->vha->hw;
+
+	WARN_ON_ONCE(abt->options & cpu_to_le16(BIT_0));
+	/* Use Driver Specified Retry Count */
+	abt->options |= cpu_to_le16(AOF_ABTS_RTY_CNT);
+	abt->drv.abts_rty_cnt = cpu_to_le16(2);
+	/* Use specified response timeout */
+	abt->options |= cpu_to_le16(AOF_RSP_TIMEOUT);
+	/* set it to 2 * r_a_tov in secs */
+	abt->drv.rsp_timeout = cpu_to_le16(2 * (ha->r_a_tov / 10));
+}
+
+void qla_nvme_abort_process_comp_status(struct abort_entry_24xx *abt, srb_t *orig_sp)
+{
+	u16	comp_status;
+	struct scsi_qla_host *vha;
+
+	if (!(ql2xabts_wait_nvme && QLA_ABTS_WAIT_ENABLED(orig_sp)))
+		return;
+
+	vha = orig_sp->fcport->vha;
+
+	comp_status = le16_to_cpu(abt->comp_status);
+	switch (comp_status) {
+	case CS_RESET:		/* reset event aborted */
+	case CS_ABORTED:	/* IOCB was cleaned */
+	/* N_Port handle is not currently logged in */
+	case CS_TIMEOUT:
+	/* N_Port handle was logged out while waiting for ABTS to complete */
+	case CS_PORT_UNAVAILABLE:
+	/* Firmware found that the port name changed */
+	case CS_PORT_LOGGED_OUT:
+	/* BA_RJT was received for the ABTS */
+	case CS_PORT_CONFIG_CHG:
+		ql_dbg(ql_dbg_async, vha, 0xf09d,
+		       "Abort I/O IOCB completed with error, comp_status=%x\n",
+		comp_status);
+		break;
+
+	/* BA_RJT was received for the ABTS */
+	case CS_REJECT_RECEIVED:
+		ql_dbg(ql_dbg_async, vha, 0xf09e,
+		       "BA_RJT was received for the ABTS rjt_vendorUnique = %u",
+			abt->fw.ba_rjt_vendorUnique);
+		ql_dbg(ql_dbg_async + ql_dbg_mbx, vha, 0xf09e,
+		       "ba_rjt_reasonCodeExpl = %u, ba_rjt_reasonCode = %u\n",
+		       abt->fw.ba_rjt_reasonCodeExpl, abt->fw.ba_rjt_reasonCode);
+		break;
+
+	case CS_COMPLETE:
+		ql_dbg(ql_dbg_async + ql_dbg_verbose, vha, 0xf09f,
+		       "IOCB request is completed successfully comp_status=%x\n",
+		comp_status);
+		break;
+
+	case CS_IOCB_ERROR:
+		ql_dbg(ql_dbg_async, vha, 0xf0a0,
+		       "IOCB request is failed, comp_status=%x\n", comp_status);
+		break;
+
+	default:
+		ql_dbg(ql_dbg_async, vha, 0xf0a1,
+		       "Invalid Abort IO IOCB Completion Status %x\n",
+		comp_status);
+		break;
+	}
+}
+
+inline void qla_wait_nvme_release_cmd_kref(srb_t *orig_sp)
+{
+	if (!(ql2xabts_wait_nvme && QLA_ABTS_WAIT_ENABLED(orig_sp)))
+		return;
+	kref_put(&orig_sp->cmd_kref, orig_sp->put_fn);
+}
diff --git a/scst/qla2x00t-32gbit/qla_nvme.h b/scst/qla2x00t-32gbit/qla_nvme.h
index 01376c6..78f9dd1 100644
--- a/scst/qla2x00t-32gbit/qla_nvme.h
+++ b/scst/qla2x00t-32gbit/qla_nvme.h
@@ -1,8 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2017 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #ifndef __QLA_NVME_H
 #define __QLA_NVME_H
@@ -17,8 +16,8 @@
 #include "qla_def.h"
 #include "qla_dsd.h"
 
-/* default dev loss time (seconds) before transport tears down ctrl */
-#define NVME_FC_DEV_LOSS_TMO  30
+#define MIN_NVME_HW_QUEUES 1
+#define DEF_NVME_HW_QUEUES 8
 
 #define NVME_ATIO_CMD_OFF 32
 #define NVME_FIRST_PACKET_CMDLEN (64 - NVME_ATIO_CMD_OFF)
@@ -60,6 +59,7 @@
 	uint64_t rsvd;
 
 	__le16	control_flags;		/* Control Flags */
+#define CF_ADMIN_ASYNC_EVENT		BIT_13
 #define CF_NVME_FIRST_BURST_ENABLE	BIT_11
 #define CF_DIF_SEG_DESCR_ENABLE         BIT_3
 #define CF_DATA_SEG_DESCR_ENABLE        BIT_2
diff --git a/scst/qla2x00t-32gbit/qla_nx.c b/scst/qla2x00t-32gbit/qla_nx.c
index 0c9f94e..a6f7d3e 100644
--- a/scst/qla2x00t-32gbit/qla_nx.c
+++ b/scst/qla2x00t-32gbit/qla_nx.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
 #include <linux/delay.h>
@@ -335,20 +334,20 @@
 };
 
 /* Device states */
-static char *q_dev_state[] = {
-	 "Unknown",
-	"Cold",
-	"Initializing",
-	"Ready",
-	"Need Reset",
-	"Need Quiescent",
-	"Failed",
-	"Quiescent",
+static const char *const q_dev_state[] = {
+	[QLA8XXX_DEV_UNKNOWN]		= "Unknown",
+	[QLA8XXX_DEV_COLD]		= "Cold/Re-init",
+	[QLA8XXX_DEV_INITIALIZING]	= "Initializing",
+	[QLA8XXX_DEV_READY]		= "Ready",
+	[QLA8XXX_DEV_NEED_RESET]	= "Need Reset",
+	[QLA8XXX_DEV_NEED_QUIESCENT]	= "Need Quiescent",
+	[QLA8XXX_DEV_FAILED]		= "Failed",
+	[QLA8XXX_DEV_QUIESCENT]		= "Quiescent",
 };
 
-char *qdev_state(uint32_t dev_state)
+const char *qdev_state(uint32_t dev_state)
 {
-	return q_dev_state[dev_state];
+	return (dev_state < MAX_STATES) ? q_dev_state[dev_state] : "Unknown";
 }
 
 /*
@@ -489,29 +488,26 @@
 	return data;
 }
 
-#define IDC_LOCK_TIMEOUT 100000000
+/*
+ * Context: task, might sleep
+ */
 int qla82xx_idc_lock(struct qla_hw_data *ha)
 {
-	int i;
-	int done = 0, timeout = 0;
+	const int delay_ms = 100, timeout_ms = 2000;
+	int done, total = 0;
 
-	while (!done) {
+	might_sleep();
+
+	while (true) {
 		/* acquire semaphore5 from PCI HW block */
 		done = qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM5_LOCK));
 		if (done == 1)
 			break;
-		if (timeout >= IDC_LOCK_TIMEOUT)
+		if (WARN_ON_ONCE(total >= timeout_ms))
 			return -1;
 
-		timeout++;
-
-		/* Yield CPU */
-		if (!in_interrupt())
-			schedule();
-		else {
-			for (i = 0; i < 20; i++)
-				cpu_relax();
-		}
+		total += delay_ms;
+		msleep(delay_ms);
 	}
 
 	return 0;
@@ -973,7 +969,7 @@
 static int
 qla82xx_flash_wait_write_finish(struct qla_hw_data *ha)
 {
-	uint32_t val;
+	uint32_t val = 0;
 	int i, ret;
 	scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
 
@@ -1074,7 +1070,8 @@
 		return ret;
 	}
 
-	if (qla82xx_flash_set_write_enable(ha))
+	ret = qla82xx_flash_set_write_enable(ha);
+	if (ret < 0)
 		goto done_write;
 
 	qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_WDATA, data);
@@ -2176,7 +2173,6 @@
 	struct qla_hw_data *ha;
 	struct rsp_que *rsp;
 	struct device_reg_82xx __iomem *reg;
-	int status = 0;
 	uint32_t stat;
 	uint32_t host_int = 0;
 	uint16_t mb[8];
@@ -2205,7 +2201,6 @@
 		case 0x10:
 		case 0x11:
 			qla82xx_mbx_completion(vha, MSW(stat));
-			status |= MBX_INTERRUPT;
 			break;
 		case 0x12:
 			mb[0] = MSW(stat);
@@ -3073,8 +3068,7 @@
 
 	ql_log(ql_log_info, vha, 0x00b6,
 	    "Device state is 0x%x = %s.\n",
-	    dev_state,
-	    dev_state < MAX_STATES ? qdev_state(dev_state) : "Unknown");
+	    dev_state, qdev_state(dev_state));
 
 	/* Force to DEV_COLD unless someone else is starting a reset */
 	if (dev_state != QLA8XXX_DEV_INITIALIZING &&
@@ -3197,8 +3191,7 @@
 	old_dev_state = dev_state;
 	ql_log(ql_log_info, vha, 0x009b,
 	    "Device state is 0x%x = %s.\n",
-	    dev_state,
-	    dev_state < MAX_STATES ? qdev_state(dev_state) : "Unknown");
+	    dev_state, qdev_state(dev_state));
 
 	/* wait for 30 seconds for device to go ready */
 	dev_init_timeout = jiffies + (ha->fcoe_dev_init_timeout * HZ);
@@ -3219,9 +3212,7 @@
 		if (loopcount < 5) {
 			ql_log(ql_log_info, vha, 0x009d,
 			    "Device state is 0x%x = %s.\n",
-			    dev_state,
-			    dev_state < MAX_STATES ? qdev_state(dev_state) :
-			    "Unknown");
+			    dev_state, qdev_state(dev_state));
 		}
 
 		switch (dev_state) {
@@ -3451,8 +3442,7 @@
 	} else
 		ql_log(ql_log_info, vha, 0xb031,
 		    "Device state is 0x%x = %s.\n",
-		    dev_state,
-		    dev_state < MAX_STATES ? qdev_state(dev_state) : "Unknown");
+		    dev_state, qdev_state(dev_state));
 }
 
 /*
diff --git a/scst/qla2x00t-32gbit/qla_nx.h b/scst/qla2x00t-32gbit/qla_nx.h
index 93344a0..6dc80c8 100644
--- a/scst/qla2x00t-32gbit/qla_nx.h
+++ b/scst/qla2x00t-32gbit/qla_nx.h
@@ -1,8 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #ifndef __QLA_NX_H
 #define __QLA_NX_H
@@ -541,14 +540,18 @@
 #define QLA82XX_CRB_DRV_IDC_VERSION  (QLA82XX_CAM_RAM(0x174))
 
 /* Every driver should use these Device State */
-#define QLA8XXX_DEV_COLD		1
-#define QLA8XXX_DEV_INITIALIZING	2
-#define QLA8XXX_DEV_READY		3
-#define QLA8XXX_DEV_NEED_RESET		4
-#define QLA8XXX_DEV_NEED_QUIESCENT	5
-#define QLA8XXX_DEV_FAILED		6
-#define QLA8XXX_DEV_QUIESCENT		7
-#define	MAX_STATES			8 /* Increment if new state added */
+enum {
+	QLA8XXX_DEV_UNKNOWN,
+	QLA8XXX_DEV_COLD,
+	QLA8XXX_DEV_INITIALIZING,
+	QLA8XXX_DEV_READY,
+	QLA8XXX_DEV_NEED_RESET,
+	QLA8XXX_DEV_NEED_QUIESCENT,
+	QLA8XXX_DEV_FAILED,
+	QLA8XXX_DEV_QUIESCENT,
+	MAX_STATES, /* Increment if new state added */
+};
+
 #define QLA8XXX_BAD_VALUE		0xbad0bad0
 
 #define QLA82XX_IDC_VERSION			1
diff --git a/scst/qla2x00t-32gbit/qla_nx2.c b/scst/qla2x00t-32gbit/qla_nx2.c
index 50e5760..41ff6fb 100644
--- a/scst/qla2x00t-32gbit/qla_nx2.c
+++ b/scst/qla2x00t-32gbit/qla_nx2.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 
 #include <linux/vmalloc.h>
@@ -140,7 +139,7 @@
 	uint32_t mask)
 {
 	unsigned long timeout;
-	uint32_t temp;
+	uint32_t temp = 0;
 
 	/* jiffies after 100ms */
 	timeout = jiffies + msecs_to_jiffies(TIMEOUT_100_MS);
@@ -660,7 +659,7 @@
 qla8044_poll_reg(struct scsi_qla_host *vha, uint32_t addr,
 	int duration, uint32_t test_mask, uint32_t test_result)
 {
-	uint32_t value;
+	uint32_t value = 0;
 	int timeout_error;
 	uint8_t retries;
 	int ret_val = QLA_SUCCESS;
@@ -1939,8 +1938,7 @@
 	dev_state = qla8044_rd_direct(vha, QLA8044_CRB_DEV_STATE_INDEX);
 	ql_dbg(ql_dbg_p3p, vha, 0xb0ce,
 	    "Device state is 0x%x = %s\n",
-	    dev_state, dev_state < MAX_STATES ?
-	    qdev_state(dev_state) : "Unknown");
+	    dev_state, qdev_state(dev_state));
 
 	/* wait for 30 seconds for device to go ready */
 	dev_init_timeout = jiffies + (ha->fcoe_dev_init_timeout * HZ);
@@ -1953,8 +1951,7 @@
 				ql_log(ql_log_warn, vha, 0xb0cf,
 				    "%s: Device Init Failed 0x%x = %s\n",
 				    QLA2XXX_DRIVER_NAME, dev_state,
-				    dev_state < MAX_STATES ?
-				    qdev_state(dev_state) : "Unknown");
+				    qdev_state(dev_state));
 				qla8044_wr_direct(vha,
 				    QLA8044_CRB_DEV_STATE_INDEX,
 				    QLA8XXX_DEV_FAILED);
@@ -1964,8 +1961,7 @@
 		dev_state = qla8044_rd_direct(vha, QLA8044_CRB_DEV_STATE_INDEX);
 		ql_log(ql_log_info, vha, 0xb0d0,
 		    "Device state is 0x%x = %s\n",
-		    dev_state, dev_state < MAX_STATES ?
-		    qdev_state(dev_state) : "Unknown");
+		    dev_state, qdev_state(dev_state));
 
 		/* NOTE: Make sure idc unlocked upon exit of switch statement */
 		switch (dev_state) {
@@ -2029,7 +2025,7 @@
 }
 
 /**
- * qla4_8xxx_check_temp - Check the ISP82XX temperature.
+ * qla8044_check_temp - Check the ISP82XX temperature.
  * @vha: adapter block pointer.
  *
  * Note: The caller should not hold the idc lock.
@@ -2227,19 +2223,16 @@
 		if (opcode & QLA82XX_DBG_OPCODE_WR) {
 			qla8044_wr_reg_indirect(vha, crb_addr,
 			    crb_entry->value_1);
-			opcode &= ~QLA82XX_DBG_OPCODE_WR;
 		}
 
 		if (opcode & QLA82XX_DBG_OPCODE_RW) {
 			qla8044_rd_reg_indirect(vha, crb_addr, &read_value);
 			qla8044_wr_reg_indirect(vha, crb_addr, read_value);
-			opcode &= ~QLA82XX_DBG_OPCODE_RW;
 		}
 
 		if (opcode & QLA82XX_DBG_OPCODE_AND) {
 			qla8044_rd_reg_indirect(vha, crb_addr, &read_value);
 			read_value &= crb_entry->value_2;
-			opcode &= ~QLA82XX_DBG_OPCODE_AND;
 			if (opcode & QLA82XX_DBG_OPCODE_OR) {
 				read_value |= crb_entry->value_3;
 				opcode &= ~QLA82XX_DBG_OPCODE_OR;
@@ -2250,7 +2243,6 @@
 			qla8044_rd_reg_indirect(vha, crb_addr, &read_value);
 			read_value |= crb_entry->value_3;
 			qla8044_wr_reg_indirect(vha, crb_addr, read_value);
-			opcode &= ~QLA82XX_DBG_OPCODE_OR;
 		}
 		if (opcode & QLA82XX_DBG_OPCODE_POLL) {
 			poll_time = crb_entry->crb_strd.poll_timeout;
@@ -2270,7 +2262,6 @@
 					    crb_addr, &read_value);
 				}
 			} while (1);
-			opcode &= ~QLA82XX_DBG_OPCODE_POLL;
 		}
 
 		if (opcode & QLA82XX_DBG_OPCODE_RDSTATE) {
@@ -2284,7 +2275,6 @@
 			qla8044_rd_reg_indirect(vha, addr, &read_value);
 			index = crb_entry->crb_ctrl.state_index_v;
 			tmplt_hdr->saved_state_array[index] = read_value;
-			opcode &= ~QLA82XX_DBG_OPCODE_RDSTATE;
 		}
 
 		if (opcode & QLA82XX_DBG_OPCODE_WRSTATE) {
@@ -2304,7 +2294,6 @@
 			}
 
 			qla8044_wr_reg_indirect(vha, addr, read_value);
-			opcode &= ~QLA82XX_DBG_OPCODE_WRSTATE;
 		}
 
 		if (opcode & QLA82XX_DBG_OPCODE_MDSTATE) {
@@ -2317,7 +2306,6 @@
 			read_value |= crb_entry->value_3;
 			read_value += crb_entry->value_1;
 			tmplt_hdr->saved_state_array[index] = read_value;
-			opcode &= ~QLA82XX_DBG_OPCODE_MDSTATE;
 		}
 		crb_addr += crb_entry->crb_strd.addr_stride;
 	}
@@ -2595,7 +2583,7 @@
 	struct qla8044_minidump_entry_hdr *entry_hdr,
 	uint32_t **d_ptr)
 {
-	uint32_t r_addr, s_stride, s_addr, s_value, loop_cnt, i, r_value;
+	uint32_t r_addr, s_stride, s_addr, s_value, loop_cnt, i, r_value = 0;
 	struct qla8044_minidump_entry_mux *mux_hdr;
 	uint32_t *data_ptr = *d_ptr;
 
diff --git a/scst/qla2x00t-32gbit/qla_nx2.h b/scst/qla2x00t-32gbit/qla_nx2.h
index 8ba7c1d..2fc902a 100644
--- a/scst/qla2x00t-32gbit/qla_nx2.h
+++ b/scst/qla2x00t-32gbit/qla_nx2.h
@@ -1,8 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 
 #ifndef __QLA_NX2_H
diff --git a/scst/qla2x00t-32gbit/qla_os.c b/scst/qla2x00t-32gbit/qla_os.c
index d614f4a..16917bc 100644
--- a/scst/qla2x00t-32gbit/qla_os.c
+++ b/scst/qla2x00t-32gbit/qla_os.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
 
@@ -21,6 +20,11 @@
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
 #include <linux/refcount.h>
 #endif
+#include <linux/crash_dump.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+#include <linux/trace_events.h>
+#include <linux/trace.h>
+#endif
 
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsicam.h>
@@ -48,11 +52,20 @@
  */
 struct kmem_cache *srb_cachep;
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+static struct trace_array *qla_trc_array;
+#endif
+
 int ql2xfulldump_on_mpifail;
 module_param(ql2xfulldump_on_mpifail, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(ql2xfulldump_on_mpifail,
 		 "Set this to take full dump on MPI hang.");
 
+int ql2xenforce_iocb_limit = 1;
+module_param(ql2xenforce_iocb_limit, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ql2xenforce_iocb_limit,
+		 "Enforce IOCB throttling, to avoid FW congestion. (default: 1)");
+
 /*
  * CT6 CTX allocation cache
  */
@@ -62,6 +75,11 @@
  */
 uint ql_errlev = 0x8001;
 
+int ql2xsecenable;
+module_param(ql2xsecenable, int, S_IRUGO);
+MODULE_PARM_DESC(ql2xsecenable,
+	"Enable/disable security. 0(Default) - Security disabled. 1 - Security enabled.");
+
 static int ql2xenableclass2;
 module_param(ql2xenableclass2, int, S_IRUGO|S_IRUSR);
 MODULE_PARM_DESC(ql2xenableclass2,
@@ -120,6 +138,13 @@
 		"ql2xextended_error_logging=1).\n"
 		"\t\tDo LOGICAL OR of the value to enable more than one level");
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+int ql2xextended_error_logging_ktrace = 1;
+module_param(ql2xextended_error_logging_ktrace, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ql2xextended_error_logging_ktrace,
+		"Same BIT definition as ql2xextended_error_logging, but used to control logging to kernel trace buffer (default=1).\n");
+#endif
+
 int ql2xshiftctondsd = 6;
 module_param(ql2xshiftctondsd, int, S_IRUGO);
 MODULE_PARM_DESC(ql2xshiftctondsd,
@@ -205,12 +230,6 @@
 		" 0 -- Regular doorbell.\n"
 		" 1 -- CAMRAM doorbell (faster).\n");
 
-int ql2xtargetreset = 1;
-module_param(ql2xtargetreset, int, S_IRUGO);
-MODULE_PARM_DESC(ql2xtargetreset,
-		 "Enable target reset."
-		 "Default is 1 - use hw defaults.");
-
 int ql2xgffidenable;
 module_param(ql2xgffidenable, int, S_IRUGO);
 MODULE_PARM_DESC(ql2xgffidenable,
@@ -341,14 +360,44 @@
 		"Enables RDP responses. "
 		"0 - no RDP responses (default). "
 		"1 - provide RDP responses.");
+int ql2xabts_wait_nvme = 1;
+module_param(ql2xabts_wait_nvme, int, 0444);
+MODULE_PARM_DESC(ql2xabts_wait_nvme,
+		 "To wait for ABTS response on I/O timeouts for NVMe. (default: 1)");
+
+
+static u32 ql2xdelay_before_pci_error_handling = 5;
+module_param(ql2xdelay_before_pci_error_handling, uint, 0644);
+MODULE_PARM_DESC(ql2xdelay_before_pci_error_handling,
+	"Number of seconds delayed before qla begin PCI error self-handling (default: 5).\n");
 
 static void qla2x00_clear_drv_active(struct qla_hw_data *);
 static void qla2x00_free_device(scsi_qla_host_t *);
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
-static int qla2xxx_map_queues(struct Scsi_Host *shost);
+
+/*
+ * See also commit a4e1d0b76e7b ("block: Change the return type of
+ * blk_mq_map_queues() into void") # v6.1.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(9, 2))
+#define MAP_QUEUES_RET int
+#else
+#define MAP_QUEUES_RET void
+#endif
+
+static MAP_QUEUES_RET qla2xxx_map_queues(struct Scsi_Host *shost);
 #endif
 static void qla2x00_destroy_deferred_work(struct qla_hw_data *);
 
+u32 ql2xnvme_queues = DEF_NVME_HW_QUEUES;
+module_param(ql2xnvme_queues, uint, S_IRUGO);
+MODULE_PARM_DESC(ql2xnvme_queues,
+	"Number of NVMe Queues that can be configured.\n"
+	"Final value will be min(ql2xnvme_queues, num_cpus,num_chip_queues)\n"
+	"1 - Minimum number of queues supported\n"
+	"8 - Default value");
 
 static struct scsi_transport_template *qla2xxx_transport_template = NULL;
 struct scsi_transport_template *qla2xxx_transport_vport_template = NULL;
@@ -739,10 +788,11 @@
 	struct scsi_cmnd *cmd = GET_CMD_SP(sp);
 	struct completion *comp = sp->comp;
 
-	sp->free(sp);
+	/* kref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 	cmd->result = res;
-	CMD_SP(cmd) = NULL;
-	cmd->scsi_done(cmd);
+	sp->type = 0;
+	scsi_done(cmd);
 	if (comp)
 		complete(comp);
 }
@@ -830,31 +880,17 @@
 	struct scsi_cmnd *cmd = GET_CMD_SP(sp);
 	struct completion *comp = sp->comp;
 
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 	cmd->result = res;
-	CMD_SP(cmd) = NULL;
-	cmd->scsi_done(cmd);
+	sp->type = 0;
+	scsi_done(cmd);
 	if (comp)
 		complete(comp);
 }
 
-#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 >= 2
-static int
-qla2xxx_queuecommand_wrk(struct Scsi_Host *host, struct scsi_cmnd *cmd);
-
-static int qla2xxx_queuecommand(struct scsi_cmnd *scmnd,
-				void (*done)(struct scsi_cmnd *))
-{
-	scmnd->scsi_done = done;
-	return qla2xxx_queuecommand_wrk(scmnd->device->host, scmnd);
-}
-
-static int
-qla2xxx_queuecommand_wrk(struct Scsi_Host *host, struct scsi_cmnd *cmd)
-#else
 static int
 qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
-#endif
 {
 	scsi_qla_host_t *vha = shost_priv(host);
 	fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
@@ -876,7 +912,7 @@
 		uint16_t hwq;
 		struct qla_qpair *qpair = NULL;
 
-		tag = blk_mq_unique_tag(cmd->request);
+		tag = blk_mq_unique_tag(scsi_cmd_to_rq(cmd));
 		hwq = blk_mq_unique_tag_to_hwq(tag);
 		qpair = ha->queue_pair_map[hwq];
 
@@ -917,8 +953,8 @@
 			goto qc24_fail_command;
 	}
 
-	if (!fcport) {
-		cmd->result = DID_NO_CONNECT << 16;
+	if (!fcport || fcport->deleted) {
+		cmd->result = DID_IMM_RETRY << 16;
 		goto qc24_fail_command;
 	}
 
@@ -947,12 +983,11 @@
 		goto qc24_target_busy;
 
 	sp = scsi_cmd_priv(cmd);
+	/* ref: INIT */
 	qla2xxx_init_sp(sp, vha, vha->hw->base_qpair, fcport);
 
 	sp->u.scmd.cmd = cmd;
 	sp->type = SRB_SCSI_CMD;
-
-	CMD_SP(cmd) = (void *)sp;
 	sp->free = qla2x00_sp_free_dma;
 	sp->done = qla2x00_sp_compl;
 
@@ -966,13 +1001,14 @@
 	return 0;
 
 qc24_host_busy_free_sp:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 
 qc24_target_busy:
 	return SCSI_MLQUEUE_TARGET_BUSY;
 
 qc24_fail_command:
-	cmd->scsi_done(cmd);
+	scsi_done(cmd);
 
 	return 0;
 }
@@ -990,7 +1026,7 @@
 	srb_t *sp;
 	int rval;
 
-	rval = rport ? fc_remote_port_chkready(rport) : FC_PORTSTATE_OFFLINE;
+	rval = rport ? fc_remote_port_chkready(rport) : (DID_NO_CONNECT << 16);
 	if (rval) {
 		cmd->result = rval;
 		ql_dbg(ql_dbg_io + ql_dbg_verbose, vha, 0x3076,
@@ -999,11 +1035,18 @@
 		goto qc24_fail_command;
 	}
 
-	if (!fcport) {
+	if (!qpair->online) {
+		ql_dbg(ql_dbg_io, vha, 0x3077,
+		       "qpair not online. eeh_busy=%d.\n", ha->flags.eeh_busy);
 		cmd->result = DID_NO_CONNECT << 16;
 		goto qc24_fail_command;
 	}
 
+	if (!fcport || fcport->deleted) {
+		cmd->result = DID_IMM_RETRY << 16;
+		goto qc24_fail_command;
+	}
+
 	if (atomic_read(&fcport->state) != FCS_ONLINE || fcport->deleted) {
 		if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD ||
 			atomic_read(&base_vha->loop_state) == LOOP_DEAD) {
@@ -1029,11 +1072,11 @@
 		goto qc24_target_busy;
 
 	sp = scsi_cmd_priv(cmd);
+	/* ref: INIT */
 	qla2xxx_init_sp(sp, vha, qpair, fcport);
 
 	sp->u.scmd.cmd = cmd;
 	sp->type = SRB_SCSI_CMD;
-	CMD_SP(cmd) = (void *)sp;
 	sp->free = qla2xxx_qpair_sp_free_dma;
 	sp->done = qla2xxx_qpair_sp_compl;
 
@@ -1041,26 +1084,20 @@
 	if (rval != QLA_SUCCESS) {
 		ql_dbg(ql_dbg_io + ql_dbg_verbose, vha, 0x3078,
 		    "Start scsi failed rval=%d for cmd=%p.\n", rval, cmd);
-		if (rval == QLA_INTERFACE_ERROR)
-			goto qc24_free_sp_fail_command;
 		goto qc24_host_busy_free_sp;
 	}
 
 	return 0;
 
 qc24_host_busy_free_sp:
-	sp->free(sp);
+	/* ref: INIT */
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 
 qc24_target_busy:
 	return SCSI_MLQUEUE_TARGET_BUSY;
 
-qc24_free_sp_fail_command:
-	sp->free(sp);
-	CMD_SP(cmd) = NULL;
-	qla2xxx_rel_qpair_sp(sp->qpair, sp);
-
 qc24_fail_command:
-	cmd->scsi_done(cmd);
+	scsi_done(cmd);
 
 	return 0;
 }
@@ -1085,6 +1122,7 @@
 	unsigned long wait_iter = ABORT_WAIT_ITER;
 	scsi_qla_host_t *vha = shost_priv(cmd->device->host);
 	struct qla_hw_data *ha = vha->hw;
+	srb_t *sp = scsi_cmd_priv(cmd);
 	int ret = QLA_SUCCESS;
 
 	if (unlikely(pci_channel_offline(ha->pdev)) || ha->flags.eeh_busy) {
@@ -1093,10 +1131,9 @@
 		return ret;
 	}
 
-	while (CMD_SP(cmd) && wait_iter--) {
+	while (sp->type && wait_iter--)
 		msleep(ABORT_POLLING_PERIOD);
-	}
-	if (CMD_SP(cmd))
+	if (sp->type)
 		ret = QLA_FUNCTION_FAILED;
 
 	return ret;
@@ -1148,12 +1185,28 @@
 	struct qla_hw_data *ha = vha->hw;
 	unsigned long flags;
 	int res;
+	/* Return 0 = sleep, x=wake */
 
 	spin_lock_irqsave(&ha->tgt.sess_lock, flags);
 	ql_dbg(ql_dbg_init, vha, 0x00ec,
 	    "tgt %p, fcport_count=%d\n",
 	    vha, vha->fcport_count);
 	res = (vha->fcport_count == 0);
+	if  (res) {
+		struct fc_port *fcport;
+
+		list_for_each_entry(fcport, &vha->vp_fcports, list) {
+			if (fcport->deleted != QLA_SESS_DELETED) {
+				/* session(s) may not be fully logged in
+				 * (ie fcport_count=0), but session
+				 * deletion thread(s) may be inflight.
+				 */
+
+				res = 0;
+				break;
+			}
+		}
+	}
 	spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
 
 	return res;
@@ -1235,35 +1288,6 @@
 	return return_status;
 }
 
-#define ISP_REG_DISCONNECT 0xffffffffU
-/**************************************************************************
-* qla2x00_isp_reg_stat
-*
-* Description:
-*	Read the host status register of ISP before aborting the command.
-*
-* Input:
-*	ha = pointer to host adapter structure.
-*
-*
-* Returns:
-*	Either true or false.
-*
-* Note:	Return true if there is register disconnect.
-**************************************************************************/
-static inline
-uint32_t qla2x00_isp_reg_stat(struct qla_hw_data *ha)
-{
-	struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
-	struct device_reg_82xx __iomem *reg82 = &ha->iobase->isp82;
-
-	if (IS_P3P_TYPE(ha))
-		return ((rd_reg_dword(&reg82->host_int)) == ISP_REG_DISCONNECT);
-	else
-		return ((rd_reg_dword(&reg->host_status)) ==
-			ISP_REG_DISCONNECT);
-}
-
 /**************************************************************************
 * qla2xxx_eh_abort
 *
@@ -1293,22 +1317,27 @@
 	uint32_t ratov_j;
 	struct qla_qpair *qpair;
 	unsigned long flags;
+	int fast_fail_status = SUCCESS;
 
 	if (qla2x00_isp_reg_stat(ha)) {
 		ql_log(ql_log_info, vha, 0x8042,
 		    "PCI/Register disconnect, exiting.\n");
+		qla_pci_set_eeh_busy(vha);
 		return FAILED;
 	}
 
+	/* Save any FAST_IO_FAIL value to return later if abort succeeds */
 	ret = fc_block_scsi_eh(cmd);
 	if (ret != 0)
-		return ret;
+		fast_fail_status = ret;
 
 	sp = scsi_cmd_priv(cmd);
 	qpair = sp->qpair;
 
+	vha->cmd_timeout_cnt++;
+
 	if ((sp->fcport && sp->fcport->deleted) || !qpair)
-		return SUCCESS;
+		return fast_fail_status != SUCCESS ? fast_fail_status : FAILED;
 
 	spin_lock_irqsave(qpair->qp_lock_ptr, flags);
 	sp->comp = &comp;
@@ -1343,7 +1372,7 @@
 			    __func__, ha->r_a_tov/10);
 			ret = FAILED;
 		} else {
-			ret = SUCCESS;
+			ret = fast_fail_status;
 		}
 		break;
 	default:
@@ -1363,21 +1392,20 @@
 /*
  * Returns: QLA_SUCCESS or QLA_FUNCTION_FAILED.
  */
-int
-qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t,
-	uint64_t l, enum nexus_wait_type type)
+static int
+__qla2x00_eh_wait_for_pending_commands(struct qla_qpair *qpair, unsigned int t,
+				       uint64_t l, enum nexus_wait_type type)
 {
 	int cnt, match, status;
 	unsigned long flags;
-	struct qla_hw_data *ha = vha->hw;
-	struct req_que *req;
+	scsi_qla_host_t *vha = qpair->vha;
+	struct req_que *req = qpair->req;
 	srb_t *sp;
 	struct scsi_cmnd *cmd;
 
 	status = QLA_SUCCESS;
 
-	spin_lock_irqsave(&ha->hardware_lock, flags);
-	req = vha->req;
+	spin_lock_irqsave(qpair->qp_lock_ptr, flags);
 	for (cnt = 1; status == QLA_SUCCESS &&
 		cnt < req->num_outstanding_cmds; cnt++) {
 		sp = req->outstanding_cmds[cnt];
@@ -1404,15 +1432,35 @@
 		if (!match)
 			continue;
 
-		spin_unlock_irqrestore(&ha->hardware_lock, flags);
+		spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
 		status = qla2x00_eh_wait_on_command(cmd);
-		spin_lock_irqsave(&ha->hardware_lock, flags);
+		spin_lock_irqsave(qpair->qp_lock_ptr, flags);
 	}
-	spin_unlock_irqrestore(&ha->hardware_lock, flags);
+	spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
 
 	return status;
 }
 
+int
+qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t,
+				     uint64_t l, enum nexus_wait_type type)
+{
+	struct qla_qpair *qpair;
+	struct qla_hw_data *ha = vha->hw;
+	int i, status = QLA_SUCCESS;
+
+	status = __qla2x00_eh_wait_for_pending_commands(ha->base_qpair, t, l,
+							type);
+	for (i = 0; status == QLA_SUCCESS && i < ha->max_qpairs; i++) {
+		qpair = ha->queue_pair_map[i];
+		if (!qpair)
+			continue;
+		status = __qla2x00_eh_wait_for_pending_commands(qpair, t, l,
+								type);
+	}
+	return status;
+}
+
 static char *reset_errors[] = {
 	"HBA not online",
 	"HBA not ready",
@@ -1421,27 +1469,36 @@
 };
 
 static int
-__qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
-    struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, uint64_t, int))
+qla2xxx_eh_device_reset(struct scsi_cmnd *cmd)
 {
-	scsi_qla_host_t *vha = shost_priv(cmd->device->host);
-	fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
+	struct scsi_device *sdev = cmd->device;
+	scsi_qla_host_t *vha = shost_priv(sdev->host);
+	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+	fc_port_t *fcport = (struct fc_port *) sdev->hostdata;
+	struct qla_hw_data *ha = vha->hw;
 	int err;
 
+	if (qla2x00_isp_reg_stat(ha)) {
+		ql_log(ql_log_info, vha, 0x803e,
+		    "PCI/Register disconnect, exiting.\n");
+		qla_pci_set_eeh_busy(vha);
+		return FAILED;
+	}
+
 	if (!fcport) {
 		return FAILED;
 	}
 
-	err = fc_block_scsi_eh(cmd);
+	err = fc_block_rport(rport);
 	if (err != 0)
 		return err;
 
 	if (fcport->deleted)
-		return SUCCESS;
+		return FAILED;
 
 	ql_log(ql_log_info, vha, 0x8009,
-	    "%s RESET ISSUED nexus=%ld:%d:%llu cmd=%p.\n", name, vha->host_no,
-	    cmd->device->id, (u64)cmd->device->lun, cmd);
+	    "DEVICE RESET ISSUED nexus=%ld:%d:%llu cmd=%p.\n", vha->host_no,
+	    sdev->id, (u64)sdev->lun, cmd);
 
 	err = 0;
 	if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
@@ -1450,64 +1507,100 @@
 		goto eh_reset_failed;
 	}
 	err = 2;
-	if (do_reset(fcport, cmd->device->lun, 1)
+	if (ha->isp_ops->lun_reset(fcport, sdev->lun, 1)
 		!= QLA_SUCCESS) {
 		ql_log(ql_log_warn, vha, 0x800c,
 		    "do_reset failed for cmd=%p.\n", cmd);
 		goto eh_reset_failed;
 	}
 	err = 3;
-	if (qla2x00_eh_wait_for_pending_commands(vha, cmd->device->id,
-	    cmd->device->lun, type) != QLA_SUCCESS) {
+	if (qla2x00_eh_wait_for_pending_commands(vha, sdev->id,
+	    sdev->lun, WAIT_LUN) != QLA_SUCCESS) {
 		ql_log(ql_log_warn, vha, 0x800d,
 		    "wait for pending cmds failed for cmd=%p.\n", cmd);
 		goto eh_reset_failed;
 	}
 
 	ql_log(ql_log_info, vha, 0x800e,
-	    "%s RESET SUCCEEDED nexus:%ld:%d:%llu cmd=%p.\n", name,
-	    vha->host_no, cmd->device->id, (u64)cmd->device->lun, cmd);
+	    "DEVICE RESET SUCCEEDED nexus:%ld:%d:%llu cmd=%p.\n",
+	    vha->host_no, sdev->id, (u64)sdev->lun, cmd);
 
 	return SUCCESS;
 
 eh_reset_failed:
 	ql_log(ql_log_info, vha, 0x800f,
-	    "%s RESET FAILED: %s nexus=%ld:%d:%llu cmd=%p.\n", name,
-	    reset_errors[err], vha->host_no, cmd->device->id,
-	    (u64)cmd->device->lun, cmd);
+	    "DEVICE RESET FAILED: %s nexus=%ld:%d:%llu cmd=%p.\n",
+	    reset_errors[err], vha->host_no, sdev->id, (u64)sdev->lun,
+	    cmd);
+	vha->reset_cmd_err_cnt++;
 	return FAILED;
 }
 
 static int
-qla2xxx_eh_device_reset(struct scsi_cmnd *cmd)
-{
-	scsi_qla_host_t *vha = shost_priv(cmd->device->host);
-	struct qla_hw_data *ha = vha->hw;
-
-	if (qla2x00_isp_reg_stat(ha)) {
-		ql_log(ql_log_info, vha, 0x803e,
-		    "PCI/Register disconnect, exiting.\n");
-		return FAILED;
-	}
-
-	return __qla2xxx_eh_generic_reset("DEVICE", WAIT_LUN, cmd,
-	    ha->isp_ops->lun_reset);
-}
-
-static int
 qla2xxx_eh_target_reset(struct scsi_cmnd *cmd)
 {
-	scsi_qla_host_t *vha = shost_priv(cmd->device->host);
+	struct scsi_device *sdev = cmd->device;
+	struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+	scsi_qla_host_t *vha = shost_priv(rport_to_shost(rport));
 	struct qla_hw_data *ha = vha->hw;
+	fc_port_t *fcport = *(fc_port_t **)rport->dd_data;
+	int err;
 
 	if (qla2x00_isp_reg_stat(ha)) {
 		ql_log(ql_log_info, vha, 0x803f,
 		    "PCI/Register disconnect, exiting.\n");
+		qla_pci_set_eeh_busy(vha);
 		return FAILED;
 	}
 
-	return __qla2xxx_eh_generic_reset("TARGET", WAIT_TARGET, cmd,
-	    ha->isp_ops->target_reset);
+	if (!fcport) {
+		return FAILED;
+	}
+
+	err = fc_block_rport(rport);
+	if (err != 0)
+		return err;
+
+	if (fcport->deleted)
+		return FAILED;
+
+	ql_log(ql_log_info, vha, 0x8009,
+	    "TARGET RESET ISSUED nexus=%ld:%d cmd=%p.\n", vha->host_no,
+	    sdev->id, cmd);
+
+	err = 0;
+	if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
+		ql_log(ql_log_warn, vha, 0x800a,
+		    "Wait for hba online failed for cmd=%p.\n", cmd);
+		goto eh_reset_failed;
+	}
+	err = 2;
+	if (ha->isp_ops->target_reset(fcport, 0, 0) != QLA_SUCCESS) {
+		ql_log(ql_log_warn, vha, 0x800c,
+		    "target_reset failed for cmd=%p.\n", cmd);
+		goto eh_reset_failed;
+	}
+	err = 3;
+	if (qla2x00_eh_wait_for_pending_commands(vha, sdev->id,
+	    0, WAIT_TARGET) != QLA_SUCCESS) {
+		ql_log(ql_log_warn, vha, 0x800d,
+		    "wait for pending cmds failed for cmd=%p.\n", cmd);
+		goto eh_reset_failed;
+	}
+
+	ql_log(ql_log_info, vha, 0x800e,
+	    "TARGET RESET SUCCEEDED nexus:%ld:%d cmd=%p.\n",
+	    vha->host_no, sdev->id, cmd);
+
+	return SUCCESS;
+
+eh_reset_failed:
+	ql_log(ql_log_info, vha, 0x800f,
+	    "TARGET RESET FAILED: %s nexus=%ld:%d:%llu cmd=%p.\n",
+	    reset_errors[err], vha->host_no, cmd->device->id, (u64)cmd->device->lun,
+	    cmd);
+	vha->reset_cmd_err_cnt++;
+	return FAILED;
 }
 
 /**************************************************************************
@@ -1529,7 +1622,6 @@
 qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
 {
 	scsi_qla_host_t *vha = shost_priv(cmd->device->host);
-	fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
 	int ret = FAILED;
 	unsigned int id;
 	uint64_t lun;
@@ -1538,21 +1630,13 @@
 	if (qla2x00_isp_reg_stat(ha)) {
 		ql_log(ql_log_info, vha, 0x8040,
 		    "PCI/Register disconnect, exiting.\n");
+		qla_pci_set_eeh_busy(vha);
 		return FAILED;
 	}
 
 	id = cmd->device->id;
 	lun = cmd->device->lun;
 
-	if (!fcport) {
-		return ret;
-	}
-
-	ret = fc_block_scsi_eh(cmd);
-	if (ret != 0)
-		return ret;
-	ret = FAILED;
-
 	if (qla2x00_chip_is_down(vha))
 		return ret;
 
@@ -1615,7 +1699,7 @@
 	if (qla2x00_isp_reg_stat(ha)) {
 		ql_log(ql_log_info, vha, 0x8041,
 		    "PCI/Register disconnect, exiting.\n");
-		schedule_work(&ha->board_disable);
+		qla_pci_set_eeh_busy(vha);
 		return SUCCESS;
 	}
 
@@ -1689,27 +1773,10 @@
 qla2x00_loop_reset(scsi_qla_host_t *vha)
 {
 	int ret;
-	struct fc_port *fcport;
 	struct qla_hw_data *ha = vha->hw;
 
-	if (IS_QLAFX00(ha)) {
-		return qlafx00_loop_reset(vha);
-	}
-
-	if (ql2xtargetreset == 1 && ha->flags.enable_target_reset) {
-		list_for_each_entry(fcport, &vha->vp_fcports, list) {
-			if (fcport->port_type != FCT_TARGET)
-				continue;
-
-			ret = ha->isp_ops->target_reset(fcport, 0, 0);
-			if (ret != QLA_SUCCESS) {
-				ql_dbg(ql_dbg_taskm, vha, 0x802c,
-				    "Bus Reset failed: Reset=%d "
-				    "d_id=%x.\n", ret, fcport->d_id.b24);
-			}
-		}
-	}
-
+	if (IS_QLAFX00(ha))
+		return QLA_SUCCESS;
 
 	if (ha->flags.enable_lip_full_login && !IS_CNA_CAPABLE(ha)) {
 		atomic_set(&vha->loop_state, LOOP_DOWN);
@@ -1793,7 +1860,7 @@
 
 		spin_lock_irqsave(qp->qp_lock_ptr, *flags);
 #if HAVE_SCSI_MQ
-		if (ret_cmd && blk_mq_request_started(cmd->request))
+		if (ret_cmd && blk_mq_request_started(scsi_cmd_to_rq(cmd)))
 			sp->done(sp, res);
 #else
 		if (ret_cmd && list_empty(&cmd->request->queuelist))
@@ -2799,6 +2866,16 @@
 	return atomic_read(&vha->loop_state) == LOOP_READY;
 }
 
+static void qla_heartbeat_work_fn(struct work_struct *work)
+{
+	struct qla_hw_data *ha = container_of(work,
+		struct qla_hw_data, heartbeat_work);
+	struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+
+	if (!ha->flags.mbox_busy && base_vha->flags.init_done)
+		qla_no_op_mb(base_vha);
+}
+
 static void qla2x00_iocb_work_fn(struct work_struct *work)
 {
 	struct scsi_qla_host *vha = container_of(work,
@@ -2821,6 +2898,29 @@
 	spin_unlock_irqrestore(&vha->work_lock, flags);
 }
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+static void
+qla_trace_init(void)
+{
+	qla_trc_array = trace_array_get_by_name("qla2xxx");
+	if (!qla_trc_array) {
+		ql_log(ql_log_fatal, NULL, 0x0001,
+		       "Unable to create qla2xxx trace instance, instance logging will be disabled.\n");
+		return;
+	}
+
+	QLA_TRACE_ENABLE(qla_trc_array);
+}
+
+static void
+qla_trace_uninit(void)
+{
+	if (!qla_trc_array)
+		return;
+	trace_array_put(qla_trc_array);
+}
+#endif
+
 /*
  * PCI driver interface
  */
@@ -2877,6 +2977,11 @@
 			return ret;
 	}
 
+	if (is_kdump_kernel()) {
+		ql2xmqsupport = 0;
+		ql2xallocfwdump = 0;
+	}
+
 	/* This may fail but that's ok */
 	pci_enable_pcie_error_reporting(pdev);
 
@@ -2894,6 +2999,17 @@
 	spin_lock_init(&ha->tgt.sess_lock);
 	spin_lock_init(&ha->tgt.atio_lock);
 
+	spin_lock_init(&ha->sadb_lock);
+	INIT_LIST_HEAD(&ha->sadb_tx_index_list);
+	INIT_LIST_HEAD(&ha->sadb_rx_index_list);
+
+	spin_lock_init(&ha->sadb_fp_lock);
+
+	if (qla_edif_sadb_build_free_pool(ha)) {
+		kfree(ha);
+		goto  disable_device;
+	}
+
 	atomic_set(&ha->nvme_active_aen_cnt, 0);
 
 	/* Clear our data area */
@@ -3092,8 +3208,8 @@
 		ha->portnum = PCI_FUNC(ha->pdev->devfn);
 		ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
 		ha->mbx_count = MAILBOX_REGISTER_COUNT;
-		req_length = REQUEST_ENTRY_CNT_24XX;
-		rsp_length = RESPONSE_ENTRY_CNT_2300;
+		req_length = REQUEST_ENTRY_CNT_83XX;
+		rsp_length = RESPONSE_ENTRY_CNT_83XX;
 		ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX;
 		ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
 		ha->init_cb_size = sizeof(struct mid_init_cb_81xx);
@@ -3183,6 +3299,10 @@
 	ha->mr.fcport.supported_classes = FC_COS_UNSPECIFIED;
 	ha->mr.fcport.scan_state = 1;
 
+	qla2xxx_reset_stats(host, QLA2XX_HW_ERROR | QLA2XX_SHT_LNK_DWN |
+			    QLA2XX_INT_ERR | QLA2XX_CMD_TIMEOUT |
+			    QLA2XX_RESET_CMD_ERR | QLA2XX_TGT_SHT_LNK_DOWN);
+
 	/* Set the SG table size based on ISP type */
 	if (!IS_FWI2_CAPABLE(ha)) {
 		if (IS_QLA2100(ha))
@@ -3216,7 +3336,7 @@
 	    host->max_cmd_len, host->max_channel, (u64)host->max_lun,
 	    host->transportt, sht->vendor_id);
 
-	INIT_WORK(&base_vha->iocb_work, qla2x00_iocb_work_fn);
+	INIT_WORK(&ha->heartbeat_work, qla_heartbeat_work_fn);
 
 	/* Set up the irqs */
 	ret = qla2x00_request_irqs(ha, rsp);
@@ -3309,7 +3429,7 @@
 	    "req->req_q_in=%p req->req_q_out=%p rsp->rsp_q_in=%p rsp->rsp_q_out=%p.\n",
 	    req->req_q_in, req->req_q_out, rsp->rsp_q_in, rsp->rsp_q_out);
 
-	ha->wq = alloc_workqueue("qla2xxx_wq", 0, 0);
+	ha->wq = alloc_workqueue("qla2xxx_wq", WQ_MEM_RECLAIM, 0);
 	if (unlikely(!ha->wq)) {
 		ret = -ENOMEM;
 		goto probe_failed;
@@ -3351,6 +3471,10 @@
 	    host->can_queue, base_vha->req,
 	    base_vha->mgmt_svr_loop_id, host->sg_tablesize);
 
+	/* Check if FW supports MQ or not for ISP25xx */
+	if (IS_QLA25XX(ha) && !(ha->fw_attributes & BIT_6))
+		ha->mqenable = 0;
+
 	if (ha->mqenable) {
 		bool startit = false;
 
@@ -3364,6 +3488,7 @@
 		for (i = 0; i < ha->max_qpairs; i++)
 			qla2xxx_create_qpair(base_vha, 5, 0, startit);
 	}
+	qla_init_iocb_limit(base_vha);
 
 	if (ha->flags.running_gold_fw)
 		goto skip_dpc;
@@ -3478,7 +3603,7 @@
 		qla_dual_mode_enabled(base_vha))
 		scsi_scan_host(host);
 	else
-		ql_dbg(ql_dbg_init, base_vha, 0x0122,
+		ql_log(ql_log_info, base_vha, 0x0122,
 			"skipping scsi_scan_host() for non-initiator port\n");
 
 	qla2x00_alloc_sysfs_attr(base_vha);
@@ -3516,6 +3641,8 @@
 	return 0;
 
 probe_failed:
+	qla_enode_stop(base_vha);
+	qla_edb_stop(base_vha);
 	if (base_vha->gnl.l) {
 		dma_free_coherent(&ha->pdev->dev, base_vha->gnl.size,
 				base_vha->gnl.l, base_vha->gnl.ldma);
@@ -3738,8 +3865,7 @@
 		if (ha->mqiobase)
 			iounmap(ha->mqiobase);
 
-		if ((IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) &&
-		    ha->msixbase)
+		if (ha->msixbase)
 			iounmap(ha->msixbase);
 	}
 }
@@ -3818,6 +3944,8 @@
 		base_vha->gnl.size, base_vha->gnl.l, base_vha->gnl.ldma);
 
 	base_vha->gnl.l = NULL;
+	qla_enode_stop(base_vha);
+	qla_edb_stop(base_vha);
 
 	vfree(base_vha->scan.l);
 
@@ -3851,7 +3979,6 @@
 	qla2x00_free_sysfs_attr(base_vha, true);
 
 	fc_remove_host(base_vha->host);
-	qlt_remove_target_resources(ha);
 
 	scsi_remove_host(base_vha->host);
 
@@ -3874,13 +4001,15 @@
 static inline void
 qla24xx_free_purex_list(struct purex_list *list)
 {
-	struct list_head *item, *next;
+	struct purex_item *item, *next;
 	ulong flags;
 
 	spin_lock_irqsave(&list->lock, flags);
-	list_for_each_safe(item, next, &list->head) {
-		list_del(item);
-		kfree(list_entry(item, struct purex_item, list));
+	list_for_each_entry_safe(item, next, &list->head, list) {
+		list_del(&item->list);
+		if (item == &item->vha->default_item)
+			continue;
+		kfree(item);
 	}
 	spin_unlock_irqrestore(&list->lock, flags);
 }
@@ -3911,7 +4040,6 @@
 
 	/* Flush the work queue and remove it */
 	if (ha->wq) {
-		flush_workqueue(ha->wq);
 		destroy_workqueue(ha->wq);
 		ha->wq = NULL;
 	}
@@ -3923,6 +4051,9 @@
 
 	qla82xx_md_free(vha);
 
+	qla_edif_sadb_release_free_pool(ha);
+	qla_edif_sadb_release(ha);
+
 	qla2x00_free_queues(ha);
 }
 
@@ -3975,6 +4106,7 @@
 		qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST);
 		qla2x00_schedule_rport_del(vha, fcport);
 	}
+
 	/*
 	 * We may need to retry the login, so don't change the state of the
 	 * port but do the retries.
@@ -3997,6 +4129,16 @@
 	    "Mark all dev lost\n");
 
 	list_for_each_entry(fcport, &vha->vp_fcports, list) {
+		if (fcport->loop_id != FC_NO_LOOP_ID &&
+		    (fcport->flags & FCF_FCP2_DEVICE) &&
+		    fcport->port_type == FCT_TARGET &&
+		    !qla2x00_reset_active(vha)) {
+			ql_dbg(ql_dbg_disc, vha, 0x211a,
+			       "Delaying session delete for FCP2 flags 0x%x port_type = 0x%x port_id=%06x %phC",
+			       fcport->flags, fcport->port_type,
+			       fcport->d_id.b24, fcport->port_name);
+			continue;
+		}
 		fcport->scan_state = 0;
 		qlt_schedule_sess_for_deletion(fcport);
 	}
@@ -4028,15 +4170,20 @@
 	struct req_que **req, struct rsp_que **rsp)
 {
 	char	name[16];
+	int rc;
 
 	ha->init_cb = dma_alloc_coherent(&ha->pdev->dev, ha->init_cb_size,
 		&ha->init_cb_dma, GFP_KERNEL);
 	if (!ha->init_cb)
 		goto fail;
 
-	if (qlt_mem_alloc(ha) < 0)
+	rc = btree_init32(&ha->host_map);
+	if (rc)
 		goto fail_free_init_cb;
 
+	if (qlt_mem_alloc(ha) < 0)
+		goto fail_free_btree;
+
 	ha->gid_list = dma_alloc_coherent(&ha->pdev->dev,
 		qla2x00_gid_list_size(ha), &ha->gid_list_dma, GFP_KERNEL);
 	if (!ha->gid_list)
@@ -4046,7 +4193,7 @@
 	if (!ha->srb_mempool)
 		goto fail_free_gid_list;
 
-	if (IS_P3P_TYPE(ha)) {
+	if (IS_P3P_TYPE(ha) || IS_QLA27XX(ha) || (ql2xsecenable && IS_QLA28XX(ha))) {
 		/* Allocate cache for CT6 Ctx. */
 		if (!ctx_cachep) {
 			ctx_cachep = kmem_cache_create("qla2xxx_ctx",
@@ -4080,7 +4227,7 @@
 	    "init_cb=%p gid_list=%p, srb_mempool=%p s_dma_pool=%p.\n",
 	    ha->init_cb, ha->gid_list, ha->srb_mempool, ha->s_dma_pool);
 
-	if (IS_P3P_TYPE(ha) || ql2xenabledif) {
+	if (IS_P3P_TYPE(ha) || ql2xenabledif || (IS_QLA28XX(ha) && ql2xsecenable)) {
 		ha->dl_dma_pool = dma_pool_create(name, &ha->pdev->dev,
 			DSD_LIST_DMA_POOL_SIZE, 8, 0);
 		if (!ha->dl_dma_pool) {
@@ -4121,7 +4268,7 @@
 					ql_dbg_pci(ql_dbg_init, ha->pdev,
 					    0xe0ee, "%s: failed alloc dsd\n",
 					    __func__);
-					return 1;
+					return -ENOMEM;
 				}
 				ha->dif_bundle_kallocs++;
 
@@ -4269,7 +4416,7 @@
 
 	/* Get consistent memory allocated for Special Features-CB. */
 	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
-		ha->sf_init_cb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
+		ha->sf_init_cb = dma_pool_zalloc(ha->s_dma_pool, GFP_KERNEL,
 						&ha->sf_init_cb_dma);
 		if (!ha->sf_init_cb)
 			goto fail_sf_init_cb;
@@ -4320,8 +4467,36 @@
 		goto fail_flt_buffer;
 	}
 
+	/* allocate the purex dma pool */
+	ha->purex_dma_pool = dma_pool_create(name, &ha->pdev->dev,
+	    ELS_MAX_PAYLOAD, 8, 0);
+
+	if (!ha->purex_dma_pool) {
+		ql_dbg_pci(ql_dbg_init, ha->pdev, 0x011b,
+		    "Unable to allocate purex_dma_pool.\n");
+		goto fail_flt;
+	}
+
+	ha->elsrej.size = sizeof(struct fc_els_ls_rjt) + 16;
+	ha->elsrej.c = dma_alloc_coherent(&ha->pdev->dev,
+	    ha->elsrej.size, &ha->elsrej.cdma, GFP_KERNEL);
+
+	if (!ha->elsrej.c) {
+		ql_dbg_pci(ql_dbg_init, ha->pdev, 0xffff,
+		    "Alloc failed for els reject cmd.\n");
+		goto fail_elsrej;
+	}
+	ha->elsrej.c->er_cmd = ELS_LS_RJT;
+	ha->elsrej.c->er_reason = ELS_RJT_LOGIC;
+	ha->elsrej.c->er_explan = ELS_EXPL_UNAB_DATA;
 	return 0;
 
+fail_elsrej:
+	dma_pool_destroy(ha->purex_dma_pool);
+fail_flt:
+	dma_free_coherent(&ha->pdev->dev, SFP_DEV_SIZE,
+	    ha->flt, ha->flt_dma);
+
 fail_flt_buffer:
 	dma_free_coherent(&ha->pdev->dev, SFP_DEV_SIZE,
 	    ha->sfp_data, ha->sfp_data_dma);
@@ -4412,6 +4587,8 @@
 	ha->gid_list_dma = 0;
 fail_free_tgt_mem:
 	qlt_mem_free(ha);
+fail_free_btree:
+	btree_destroy32(&ha->host_map);
 fail_free_init_cb:
 	dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, ha->init_cb,
 	ha->init_cb_dma);
@@ -4427,11 +4604,12 @@
 qla2x00_set_exlogins_buffer(scsi_qla_host_t *vha)
 {
 	int rval;
-	uint16_t	size, max_cnt, temp;
+	uint16_t	size, max_cnt;
+	uint32_t temp;
 	struct qla_hw_data *ha = vha->hw;
 
 	/* Return if we don't need to alloacate any extended logins */
-	if (!ql2xexlogins)
+	if (ql2xexlogins <= MAX_FIBRE_DEVICES_2400)
 		return QLA_SUCCESS;
 
 	if (!IS_EXLOGIN_OFFLD_CAPABLE(ha))
@@ -4673,8 +4851,7 @@
 		dma_free_coherent(&ha->pdev->dev,
 		    EFT_SIZE, ha->eft, ha->eft_dma);
 
-	if (ha->fw_dump)
-		vfree(ha->fw_dump);
+	vfree(ha->fw_dump);
 
 	ha->fce = NULL;
 	ha->fce_dma = 0;
@@ -4688,8 +4865,7 @@
 	ha->fw_dump_len = 0;
 
 	for (j = 0; j < 2; j++, fwdt++) {
-		if (fwdt->template)
-			vfree(fwdt->template);
+		vfree(fwdt->template);
 		fwdt->template = NULL;
 		fwdt->length = 0;
 	}
@@ -4829,10 +5005,21 @@
 	ha->dif_bundl_pool = NULL;
 
 	qlt_mem_free(ha);
+	qla_remove_hostmap(ha);
 
 	if (ha->init_cb)
 		dma_free_coherent(&ha->pdev->dev, ha->init_cb_size,
 			ha->init_cb, ha->init_cb_dma);
+
+	dma_pool_destroy(ha->purex_dma_pool);
+	ha->purex_dma_pool = NULL;
+
+	if (ha->elsrej.c) {
+		dma_free_coherent(&ha->pdev->dev, ha->elsrej.size,
+		    ha->elsrej.c, ha->elsrej.cdma);
+		ha->elsrej.c = NULL;
+	}
+
 	ha->init_cb = NULL;
 	ha->init_cb_dma = 0;
 
@@ -4879,7 +5066,6 @@
 	INIT_LIST_HEAD(&vha->work_list);
 	INIT_LIST_HEAD(&vha->list);
 	INIT_LIST_HEAD(&vha->qla_cmd_list);
-	INIT_LIST_HEAD(&vha->qla_sess_op_cmd_list);
 	INIT_LIST_HEAD(&vha->logo_list);
 	INIT_LIST_HEAD(&vha->plogi_ack_list);
 	INIT_LIST_HEAD(&vha->qp_list);
@@ -4894,6 +5080,9 @@
 	spin_lock_init(&vha->cmd_list_lock);
 	init_waitqueue_head(&vha->fcport_waitQ);
 	init_waitqueue_head(&vha->vref_waitq);
+	qla_enode_init(vha);
+	qla_edb_init(vha);
+
 
 	vha->gnl.size = sizeof(struct get_name_list_extended) *
 			(ha->max_loop_id + 1);
@@ -4920,7 +5109,7 @@
 	}
 	INIT_DELAYED_WORK(&vha->scan.scan_work, qla_scan_work_fn);
 
-	sprintf(vha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, vha->host_no);
+	sprintf(vha->host_str, "%s_%lu", QLA2XXX_DRIVER_NAME, vha->host_no);
 	ql_dbg(ql_dbg_init, vha, 0x0041,
 	    "Allocated the host=%p hw=%p vha=%p dev_name=%s",
 	    vha->host, vha->hw, vha,
@@ -4933,13 +5122,11 @@
 qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type)
 {
 	struct qla_work_evt *e;
-	uint8_t bail;
 
 	if (test_bit(UNLOADING, &vha->dpc_flags))
 		return NULL;
 
-	QLA_VHA_MARK_BUSY(vha, bail);
-	if (bail)
+	if (qla_vha_mark_busy(vha))
 		return NULL;
 
 	e = kzalloc(sizeof(struct qla_work_evt), GFP_ATOMIC);
@@ -5049,7 +5236,7 @@
 
 	switch (code) {
 	case QLA_UEVENT_CODE_FW_DUMP:
-		snprintf(event_string, sizeof(event_string), "FW_DUMP=%ld",
+		snprintf(event_string, sizeof(event_string), "FW_DUMP=%lu",
 		    vha->host_no);
 		break;
 	default:
@@ -5093,11 +5280,7 @@
 	qla2x00_set_fcport_disc_state(fcport, DSC_UPD_FCPORT);
 	spin_unlock_irqrestore(&fcport->vha->work_lock, flags);
 
-#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 <= 6
-	schedule_work(&fcport->reg_work);
-#else
 	queue_work(system_unbound_wq, &fcport->reg_work);
-#endif
 }
 
 static
@@ -5135,12 +5318,20 @@
 			fcport->d_id = e->u.new_sess.id;
 			fcport->flags |= FCF_FABRIC_DEVICE;
 			fcport->fw_login_state = DSC_LS_PLOGI_PEND;
+			fcport->tgt_short_link_down_cnt = 0;
 
 			memcpy(fcport->port_name, e->u.new_sess.port_name,
 			    WWN_SIZE);
 
 			fcport->fc4_type = e->u.new_sess.fc4_type;
+			if (NVME_PRIORITY(vha->hw, fcport))
+				fcport->do_prli_nvme = 1;
+			else
+				fcport->do_prli_nvme = 0;
+
 			if (e->u.new_sess.fc4_type & FS_FCP_IS_N2N) {
+				fcport->dm_login_expire = jiffies +
+					QLA_N2N_WAIT_TIME * HZ;
 				fcport->fc4_type = FS_FC4TYPE_FCP;
 				fcport->n2n_flag = 1;
 				if (vha->flags.nvme_enabled)
@@ -5385,6 +5576,9 @@
 			qla24xx_els_dcmd2_iocb(vha, ELS_DCMD_PLOGI,
 			    e->u.fcport.fcport, false);
 			break;
+		case QLA_EVT_SA_REPLACE:
+			rc = qla24xx_issue_sa_replace_iocb(vha, e);
+			break;
 		}
 
 		if (rc == EAGAIN) {
@@ -5434,6 +5628,7 @@
 		if (atomic_read(&fcport->state) != FCS_ONLINE &&
 		    fcport->login_retry) {
 			if (fcport->scan_state != QLA_FCPORT_FOUND ||
+			    fcport->disc_state == DSC_LOGIN_AUTH_PEND ||
 			    fcport->disc_state == DSC_LOGIN_COMPLETE)
 				continue;
 
@@ -5446,6 +5641,11 @@
 					ea.fcport = fcport;
 					qla24xx_handle_relogin_event(vha, &ea);
 				} else if (vha->hw->current_topology ==
+					 ISP_CFG_NL &&
+					IS_QLA2XXX_MIDTYPE(vha->hw)) {
+					(void)qla24xx_fcport_handle_login(vha,
+									fcport);
+				} else if (vha->hw->current_topology ==
 				    ISP_CFG_NL) {
 					fcport->login_retry--;
 					status =
@@ -5662,25 +5862,10 @@
 	}
 }
 
-static void
-qla83xx_wait_logic(void)
-{
-	int i;
-
-	/* Yield CPU */
-	if (!in_interrupt()) {
-		/*
-		 * Wait about 200ms before retrying again.
-		 * This controls the number of retries for single
-		 * lock operation.
-		 */
-		msleep(100);
-		schedule();
-	} else {
-		for (i = 0; i < 20; i++)
-			cpu_relax(); /* This a nop instr on i386 */
-	}
-}
+/*
+ * Control the frequency of IDC lock retries
+ */
+#define QLA83XX_WAIT_LOGIC_MS	100
 
 static int
 qla83xx_force_lock_recovery(scsi_qla_host_t *base_vha)
@@ -5770,7 +5955,7 @@
 		goto exit;
 
 	if (o_drv_lockid == n_drv_lockid) {
-		qla83xx_wait_logic();
+		msleep(QLA83XX_WAIT_LOGIC_MS);
 		goto retry_lockid;
 	} else
 		return QLA_SUCCESS;
@@ -5779,6 +5964,9 @@
 	return rval;
 }
 
+/*
+ * Context: task, can sleep
+ */
 void
 qla83xx_idc_lock(scsi_qla_host_t *base_vha, uint16_t requester_id)
 {
@@ -5786,6 +5974,8 @@
 	uint32_t lock_owner;
 	struct qla_hw_data *ha = base_vha->hw;
 
+	might_sleep();
+
 	/* IDC-lock implementation using driver-lock/lock-id remote registers */
 retry_lock:
 	if (qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCK, &data)
@@ -5804,7 +5994,7 @@
 			/* Retry/Perform IDC-Lock recovery */
 			if (qla83xx_idc_lock_recovery(base_vha)
 			    == QLA_SUCCESS) {
-				qla83xx_wait_logic();
+				msleep(QLA83XX_WAIT_LOGIC_MS);
 				goto retry_lock;
 			} else
 				ql_log(ql_log_warn, base_vha, 0xb075,
@@ -5862,98 +6052,6 @@
 	return true;
 }
 
-static uint
-qla25xx_rdp_port_speed_capability(struct qla_hw_data *ha)
-{
-	if (IS_CNA_CAPABLE(ha))
-		return RDP_PORT_SPEED_10GB;
-
-	if (IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
-		unsigned int speeds = 0;
-
-		if (ha->max_supported_speed == 2) {
-			if (ha->min_supported_speed <= 6)
-				speeds |= RDP_PORT_SPEED_64GB;
-		}
-
-		if (ha->max_supported_speed == 2 ||
-		    ha->max_supported_speed == 1) {
-			if (ha->min_supported_speed <= 5)
-				speeds |= RDP_PORT_SPEED_32GB;
-		}
-
-		if (ha->max_supported_speed == 2 ||
-		    ha->max_supported_speed == 1 ||
-		    ha->max_supported_speed == 0) {
-			if (ha->min_supported_speed <= 4)
-				speeds |= RDP_PORT_SPEED_16GB;
-		}
-
-		if (ha->max_supported_speed == 1 ||
-		    ha->max_supported_speed == 0) {
-			if (ha->min_supported_speed <= 3)
-				speeds |= RDP_PORT_SPEED_8GB;
-		}
-
-		if (ha->max_supported_speed == 0) {
-			if (ha->min_supported_speed <= 2)
-				speeds |= RDP_PORT_SPEED_4GB;
-		}
-
-		return speeds;
-	}
-
-	if (IS_QLA2031(ha))
-		return RDP_PORT_SPEED_16GB|RDP_PORT_SPEED_8GB|
-		       RDP_PORT_SPEED_4GB;
-
-	if (IS_QLA25XX(ha))
-		return RDP_PORT_SPEED_8GB|RDP_PORT_SPEED_4GB|
-		       RDP_PORT_SPEED_2GB|RDP_PORT_SPEED_1GB;
-
-	if (IS_QLA24XX_TYPE(ha))
-		return RDP_PORT_SPEED_4GB|RDP_PORT_SPEED_2GB|
-		       RDP_PORT_SPEED_1GB;
-
-	if (IS_QLA23XX(ha))
-		return RDP_PORT_SPEED_2GB|RDP_PORT_SPEED_1GB;
-
-	return RDP_PORT_SPEED_1GB;
-}
-
-static uint
-qla25xx_rdp_port_speed_currently(struct qla_hw_data *ha)
-{
-	switch (ha->link_data_rate) {
-	case PORT_SPEED_1GB:
-		return RDP_PORT_SPEED_1GB;
-
-	case PORT_SPEED_2GB:
-		return RDP_PORT_SPEED_2GB;
-
-	case PORT_SPEED_4GB:
-		return RDP_PORT_SPEED_4GB;
-
-	case PORT_SPEED_8GB:
-		return RDP_PORT_SPEED_8GB;
-
-	case PORT_SPEED_10GB:
-		return RDP_PORT_SPEED_10GB;
-
-	case PORT_SPEED_16GB:
-		return RDP_PORT_SPEED_16GB;
-
-	case PORT_SPEED_32GB:
-		return RDP_PORT_SPEED_32GB;
-
-	case PORT_SPEED_64GB:
-		return RDP_PORT_SPEED_64GB;
-
-	default:
-		return RDP_PORT_SPEED_UNKNOWN;
-	}
-}
-
 /*
  * Function Name: qla24xx_process_purex_iocb
  *
@@ -5973,12 +6071,10 @@
 	dma_addr_t rsp_els_dma;
 	dma_addr_t rsp_payload_dma;
 	dma_addr_t stat_dma;
-	dma_addr_t bbc_dma;
 	dma_addr_t sfp_dma;
 	struct els_entry_24xx *rsp_els = NULL;
 	struct rdp_rsp_payload *rsp_payload = NULL;
 	struct link_statistics *stat = NULL;
-	struct buffer_credit_24xx *bbc = NULL;
 	uint8_t *sfp = NULL;
 	uint16_t sfp_flags = 0;
 	uint rsp_payload_length = sizeof(*rsp_payload);
@@ -6022,9 +6118,6 @@
 	stat = dma_alloc_coherent(&ha->pdev->dev, sizeof(*stat),
 	    &stat_dma, GFP_KERNEL);
 
-	bbc = dma_alloc_coherent(&ha->pdev->dev, sizeof(*bbc),
-	    &bbc_dma, GFP_KERNEL);
-
 	/* Prepare Response IOCB */
 	rsp_els->entry_type = ELS_IOCB_TYPE;
 	rsp_els->entry_count = 1;
@@ -6120,9 +6213,9 @@
 	rsp_payload->port_speed_desc.desc_len =
 	    cpu_to_be32(RDP_DESC_LEN(rsp_payload->port_speed_desc));
 	rsp_payload->port_speed_desc.speed_capab = cpu_to_be16(
-	    qla25xx_rdp_port_speed_capability(ha));
+	    qla25xx_fdmi_port_speed_capability(ha));
 	rsp_payload->port_speed_desc.operating_speed = cpu_to_be16(
-	    qla25xx_rdp_port_speed_currently(ha));
+	    qla25xx_fdmi_port_speed_currently(ha));
 
 	/* Link Error Status Descriptor */
 	rsp_payload->ls_err_desc.desc_tag = cpu_to_be32(0x10002);
@@ -6178,13 +6271,10 @@
 	rsp_payload->buffer_credit_desc.attached_fcport_b2b = cpu_to_be32(0);
 	rsp_payload->buffer_credit_desc.fcport_rtt = cpu_to_be32(0);
 
-	if (bbc) {
-		memset(bbc, 0, sizeof(*bbc));
-		rval = qla24xx_get_buffer_credits(vha, bbc, bbc_dma);
-		if (!rval) {
-			rsp_payload->buffer_credit_desc.fcport_b2b =
-			    cpu_to_be32(LSW(bbc->parameter[0]));
-		}
+	if (ha->flags.plogi_template_valid) {
+		uint32_t tmp =
+		be16_to_cpu(ha->plogi_els_payld.fl_csp.sp_bb_cred);
+		rsp_payload->buffer_credit_desc.fcport_b2b = cpu_to_be32(tmp);
 	}
 
 	if (rsp_payload_length < sizeof(*rsp_payload))
@@ -6362,9 +6452,6 @@
 	}
 
 dealloc:
-	if (bbc)
-		dma_free_coherent(&ha->pdev->dev, sizeof(*bbc),
-		    bbc, bbc_dma);
 	if (stat)
 		dma_free_coherent(&ha->pdev->dev, sizeof(*stat),
 		    stat, stat_dma);
@@ -6379,7 +6466,8 @@
 		    rsp_els, rsp_els_dma);
 }
 
-void qla24xx_free_purex_item(struct purex_item *item)
+void
+qla24xx_free_purex_item(struct purex_item *item)
 {
 	if (item == &item->vha->default_item)
 		memset(&item->vha->default_item, 0, sizeof(struct purex_item));
@@ -6404,6 +6492,9 @@
 	}
 }
 
+/*
+ * Context: task, can sleep
+ */
 void
 qla83xx_idc_unlock(scsi_qla_host_t *base_vha, uint16_t requester_id)
 {
@@ -6414,6 +6505,8 @@
 	uint32_t data;
 	struct qla_hw_data *ha = base_vha->hw;
 
+	might_sleep();
+
 	/* IDC-unlock implementation using driver-unlock/lock-id
 	 * remote registers
 	 */
@@ -6429,7 +6522,7 @@
 			/* SV: XXX: IDC unlock retrying needed here? */
 
 			/* Retry for IDC-unlock */
-			qla83xx_wait_logic();
+			msleep(QLA83XX_WAIT_LOGIC_MS);
 			retry++;
 			ql_dbg(ql_dbg_p3p, base_vha, 0xb064,
 			    "Failed to release IDC lock, retrying=%d\n", retry);
@@ -6437,7 +6530,7 @@
 		}
 	} else if (retry < 10) {
 		/* Retry for IDC-unlock */
-		qla83xx_wait_logic();
+		msleep(QLA83XX_WAIT_LOGIC_MS);
 		retry++;
 		ql_dbg(ql_dbg_p3p, base_vha, 0xb065,
 		    "Failed to read drv-lockid, retrying=%d\n", retry);
@@ -6453,7 +6546,7 @@
 	if (qla83xx_access_control(base_vha, options, 0, 0, NULL)) {
 		if (retry < 10) {
 			/* Retry for IDC-unlock */
-			qla83xx_wait_logic();
+			msleep(QLA83XX_WAIT_LOGIC_MS);
 			retry++;
 			ql_dbg(ql_dbg_p3p, base_vha, 0xb066,
 			    "Failed to release IDC lock, retrying=%d\n", retry);
@@ -6813,6 +6906,9 @@
 
 		schedule();
 
+		if (test_and_clear_bit(DO_EEH_RECOVERY, &base_vha->dpc_flags))
+			qla_pci_set_eeh_busy(base_vha);
+
 		if (!base_vha->flags.init_done || ha->flags.mbox_busy)
 			goto end_loop;
 
@@ -7106,26 +7202,21 @@
 			mutex_unlock(&ha->mq_lock);
 		}
 
-		if (test_and_clear_bit(SET_NVME_ZIO_THRESHOLD_NEEDED,
-		    &base_vha->dpc_flags)) {
-			ql_log(ql_log_info, base_vha, 0xffffff,
-				"nvme: SET ZIO Activity exchange threshold to %d.\n",
-						ha->nvme_last_rptd_aen);
-			if (qla27xx_set_zio_threshold(base_vha,
-			    ha->nvme_last_rptd_aen)) {
-				ql_log(ql_log_info, base_vha, 0xffffff,
-				    "nvme: Unable to SET ZIO Activity exchange threshold to %d.\n",
-				    ha->nvme_last_rptd_aen);
-			}
-		}
-
 		if (test_and_clear_bit(SET_ZIO_THRESHOLD_NEEDED,
-		    &base_vha->dpc_flags)) {
+				       &base_vha->dpc_flags)) {
+			u16 threshold = ha->nvme_last_rptd_aen + ha->last_zio_threshold;
+
+			if (threshold > ha->orig_fw_xcb_count)
+				threshold = ha->orig_fw_xcb_count;
+
 			ql_log(ql_log_info, base_vha, 0xffffff,
-			    "SET ZIO Activity exchange threshold to %d.\n",
-			    ha->last_zio_threshold);
-			qla27xx_set_zio_threshold(base_vha,
-			    ha->last_zio_threshold);
+			       "SET ZIO Activity exchange threshold to %d.\n",
+			       threshold);
+			if (qla27xx_set_zio_threshold(base_vha, threshold)) {
+				ql_log(ql_log_info, base_vha, 0xffffff,
+				       "Unable to SET ZIO Activity exchange threshold to %d.\n",
+				       threshold);
+			}
 		}
 
 		if (!IS_QLAFX00(ha))
@@ -7192,6 +7283,104 @@
 	}
 }
 
+static bool qla_do_heartbeat(struct scsi_qla_host *vha)
+{
+	struct qla_hw_data *ha = vha->hw;
+	u32 cmpl_cnt;
+	u16 i;
+	bool do_heartbeat = false;
+
+	/*
+	 * Allow do_heartbeat only if we don’t have any active interrupts,
+	 * but there are still IOs outstanding with firmware.
+	 */
+	cmpl_cnt = ha->base_qpair->cmd_completion_cnt;
+	if (cmpl_cnt == ha->base_qpair->prev_completion_cnt &&
+	    cmpl_cnt != ha->base_qpair->cmd_cnt) {
+		do_heartbeat = true;
+		goto skip;
+	}
+	ha->base_qpair->prev_completion_cnt = cmpl_cnt;
+
+	for (i = 0; i < ha->max_qpairs; i++) {
+		if (ha->queue_pair_map[i]) {
+			cmpl_cnt = ha->queue_pair_map[i]->cmd_completion_cnt;
+			if (cmpl_cnt == ha->queue_pair_map[i]->prev_completion_cnt &&
+			    cmpl_cnt != ha->queue_pair_map[i]->cmd_cnt) {
+				do_heartbeat = true;
+				break;
+			}
+			ha->queue_pair_map[i]->prev_completion_cnt = cmpl_cnt;
+		}
+	}
+
+skip:
+	return do_heartbeat;
+}
+
+static void qla_heart_beat(struct scsi_qla_host *vha, u16 dpc_started)
+{
+	struct qla_hw_data *ha = vha->hw;
+
+	if (vha->vp_idx)
+		return;
+
+	if (vha->hw->flags.eeh_busy || qla2x00_chip_is_down(vha))
+		return;
+
+	/*
+	 * dpc thread cannot run if heartbeat is running at the same time.
+	 * We also do not want to starve heartbeat task. Therefore, do
+	 * heartbeat task at least once every 5 seconds.
+	 */
+	if (dpc_started &&
+	    time_before(jiffies, ha->last_heartbeat_run_jiffies + 5 * HZ))
+		return;
+
+	if (qla_do_heartbeat(vha)) {
+		ha->last_heartbeat_run_jiffies = jiffies;
+		queue_work(ha->wq, &ha->heartbeat_work);
+	}
+}
+
+static void qla_wind_down_chip(scsi_qla_host_t *vha)
+{
+	struct qla_hw_data *ha = vha->hw;
+
+	if (!ha->flags.eeh_busy)
+		return;
+	if (ha->pci_error_state)
+		/* system is trying to recover */
+		return;
+
+	/*
+	 * Current system is not handling PCIE error.  At this point, this is
+	 * best effort to wind down the adapter.
+	 */
+	if (time_after_eq(jiffies, ha->eeh_jif + ql2xdelay_before_pci_error_handling * HZ) &&
+	    !ha->flags.eeh_flush) {
+		ql_log(ql_log_info, vha, 0x9009,
+		    "PCI Error detected, attempting to reset hardware.\n");
+
+		ha->isp_ops->reset_chip(vha);
+		ha->isp_ops->disable_intrs(ha);
+
+		ha->flags.eeh_flush = EEH_FLUSH_RDY;
+		ha->eeh_jif = jiffies;
+
+	} else if (ha->flags.eeh_flush == EEH_FLUSH_RDY &&
+	    time_after_eq(jiffies, ha->eeh_jif +  5 * HZ)) {
+		pci_clear_master(ha->pdev);
+
+		/* flush all command */
+		qla2x00_abort_isp_cleanup(vha);
+		ha->flags.eeh_flush = EEH_FLUSH_DONE;
+
+		ql_log(ql_log_info, vha, 0x900a,
+		    "PCI Error handling complete, all IOs aborted.\n");
+	}
+}
+
 /**************************************************************************
 *   qla2x00_timer
 *
@@ -7211,8 +7400,12 @@
 	uint16_t        w;
 	struct qla_hw_data *ha = vha->hw;
 	struct req_que *req;
+	unsigned long flags;
+	fc_port_t *fcport = NULL;
 
 	if (ha->flags.eeh_busy) {
+		qla_wind_down_chip(vha);
+
 		ql_dbg(ql_dbg_timer, vha, 0x6000,
 		    "EEH = %d, restarting timer.\n",
 		    ha->flags.eeh_busy);
@@ -7242,6 +7435,16 @@
 	if (!vha->vp_idx && IS_QLAFX00(ha))
 		qlafx00_timer_routine(vha);
 
+	if (vha->link_down_time < QLA2XX_MAX_LINK_DOWN_TIME)
+		vha->link_down_time++;
+
+	spin_lock_irqsave(&vha->hw->tgt.sess_lock, flags);
+	list_for_each_entry(fcport, &vha->vp_fcports, list) {
+		if (fcport->tgt_link_down_time < QLA2XX_MAX_LINK_DOWN_TIME)
+			fcport->tgt_link_down_time++;
+	}
+	spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
+
 	/* Loop down handler. */
 	if (atomic_read(&vha->loop_down_timer) > 0 &&
 	    !(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) &&
@@ -7323,6 +7526,10 @@
 		}
 	}
 
+	/* check if edif running */
+	if (vha->hw->flags.edif_enabled)
+		qla_edif_timer(vha);
+
 	/* Process any deferred work. */
 	if (!list_empty(&vha->work_list)) {
 		unsigned long flags;
@@ -7340,22 +7547,22 @@
 	 * FC-NVME
 	 * see if the active AEN count has changed from what was last reported.
 	 */
+	index = atomic_read(&ha->nvme_active_aen_cnt);
 	if (!vha->vp_idx &&
-	    (atomic_read(&ha->nvme_active_aen_cnt) != ha->nvme_last_rptd_aen) &&
+	    (index != ha->nvme_last_rptd_aen) &&
 	    ha->zio_mode == QLA_ZIO_MODE_6 &&
 	    !ha->flags.host_shutting_down) {
+		ha->nvme_last_rptd_aen = atomic_read(&ha->nvme_active_aen_cnt);
 		ql_log(ql_log_info, vha, 0x3002,
 		    "nvme: Sched: Set ZIO exchange threshold to %d.\n",
 		    ha->nvme_last_rptd_aen);
-		ha->nvme_last_rptd_aen = atomic_read(&ha->nvme_active_aen_cnt);
-		set_bit(SET_NVME_ZIO_THRESHOLD_NEEDED, &vha->dpc_flags);
+		set_bit(SET_ZIO_THRESHOLD_NEEDED, &vha->dpc_flags);
 		start_dpc++;
 	}
 
 	if (!vha->vp_idx &&
-	    (atomic_read(&ha->zio_threshold) != ha->last_zio_threshold) &&
-	    (ha->zio_mode == QLA_ZIO_MODE_6) &&
-	    (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha))) {
+	    atomic_read(&ha->zio_threshold) != ha->last_zio_threshold &&
+	    IS_ZIO_THRESHOLD_CAPABLE(ha)) {
 		ql_log(ql_log_info, vha, 0x3002,
 		    "Sched: Set ZIO exchange threshold to %d.\n",
 		    ha->last_zio_threshold);
@@ -7364,6 +7571,8 @@
 		start_dpc++;
 	}
 
+	/* borrowing w to signify dpc will run */
+	w = 0;
 	/* Schedule the DPC routine if needed */
 	if ((test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) ||
 	    test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags) ||
@@ -7396,8 +7605,11 @@
 		    test_bit(RELOGIN_NEEDED, &vha->dpc_flags),
 		    test_bit(PROCESS_PUREX_IOCB, &vha->dpc_flags));
 		qla2xxx_wake_dpc(vha);
+		w = 1;
 	}
 
+	qla_heart_beat(vha, w);
+
 	qla2x00_restart_timer(vha, WATCH_INTERVAL);
 }
 
@@ -7517,11 +7729,13 @@
 	struct qla_hw_data *ha = vha->hw;
 	scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
 	struct qla_qpair *qpair = NULL;
-	struct scsi_qla_host *vp;
+	struct scsi_qla_host *vp, *tvp;
 	fc_port_t *fcport;
 	int i;
 	unsigned long flags;
 
+	ql_dbg(ql_dbg_aer, vha, 0x9000,
+	       "%s\n", __func__);
 	ha->chip_reset++;
 
 	ha->base_qpair->chip_reset = ha->chip_reset;
@@ -7531,34 +7745,22 @@
 			    ha->base_qpair->chip_reset;
 	}
 
-	/* purge MBox commands */
-	if (atomic_read(&ha->num_pend_mbx_stage3)) {
-		clear_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags);
-		complete(&ha->mbx_intr_comp);
-	}
-
-	i = 0;
-
-	while (atomic_read(&ha->num_pend_mbx_stage3) ||
-	    atomic_read(&ha->num_pend_mbx_stage2) ||
-	    atomic_read(&ha->num_pend_mbx_stage1)) {
-		msleep(20);
-		i++;
-		if (i > 50)
-			break;
-	}
-
-	ha->flags.purge_mbox = 0;
+	/*
+	 * purge mailbox might take a while. Slot Reset/chip reset
+	 * will take care of the purge
+	 */
 
 	mutex_lock(&ha->mq_lock);
+	ha->base_qpair->online = 0;
 	list_for_each_entry(qpair, &base_vha->qp_list, qp_list_elem)
 		qpair->online = 0;
+	wmb();
 	mutex_unlock(&ha->mq_lock);
 
 	qla2x00_mark_all_devices_lost(vha);
 
 	spin_lock_irqsave(&ha->vport_slock, flags);
-	list_for_each_entry(vp, &ha->vp_list, list) {
+	list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) {
 		atomic_inc(&vp->vref_count);
 		spin_unlock_irqrestore(&ha->vport_slock, flags);
 		qla2x00_mark_all_devices_lost(vp);
@@ -7572,7 +7774,7 @@
 		fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT);
 
 	spin_lock_irqsave(&ha->vport_slock, flags);
-	list_for_each_entry(vp, &ha->vp_list, list) {
+	list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) {
 		atomic_inc(&vp->vref_count);
 		spin_unlock_irqrestore(&ha->vport_slock, flags);
 		list_for_each_entry(fcport, &vp->vp_fcports, list)
@@ -7589,28 +7791,32 @@
 {
 	scsi_qla_host_t *vha = pci_get_drvdata(pdev);
 	struct qla_hw_data *ha = vha->hw;
+	pci_ers_result_t ret = PCI_ERS_RESULT_NEED_RESET;
 
-	ql_dbg(ql_dbg_aer, vha, 0x9000,
-	    "PCI error detected, state %x.\n", state);
+	ql_log(ql_log_warn, vha, 0x9000,
+	       "PCI error detected, state %x.\n", state);
+	ha->pci_error_state = QLA_PCI_ERR_DETECTED;
 
 	if (!atomic_read(&pdev->enable_cnt)) {
 		ql_log(ql_log_info, vha, 0xffff,
 			"PCI device is disabled,state %x\n", state);
-		return PCI_ERS_RESULT_NEED_RESET;
+		ret = PCI_ERS_RESULT_NEED_RESET;
+		goto out;
 	}
 
 	switch (state) {
 	case pci_channel_io_normal:
-		ha->flags.eeh_busy = 0;
+		qla_pci_set_eeh_busy(vha);
 		if (ql2xmqsupport || ql2xnvmeenable) {
 			set_bit(QPAIR_ONLINE_CHECK_NEEDED, &vha->dpc_flags);
 			qla2xxx_wake_dpc(vha);
 		}
-		return PCI_ERS_RESULT_CAN_RECOVER;
+		ret = PCI_ERS_RESULT_CAN_RECOVER;
+		break;
 	case pci_channel_io_frozen:
-		ha->flags.eeh_busy = 1;
-		qla_pci_error_cleanup(vha);
-		return PCI_ERS_RESULT_NEED_RESET;
+		qla_pci_set_eeh_busy(vha);
+		ret = PCI_ERS_RESULT_NEED_RESET;
+		break;
 	case pci_channel_io_perm_failure:
 		ha->flags.pci_channel_io_perm_failure = 1;
 		qla2x00_abort_all_cmds(vha, DID_NO_CONNECT << 16);
@@ -7618,9 +7824,12 @@
 			set_bit(QPAIR_ONLINE_CHECK_NEEDED, &vha->dpc_flags);
 			qla2xxx_wake_dpc(vha);
 		}
-		return PCI_ERS_RESULT_DISCONNECT;
+		ret = PCI_ERS_RESULT_DISCONNECT;
 	}
-	return PCI_ERS_RESULT_NEED_RESET;
+out:
+	ql_dbg(ql_dbg_aer, vha, 0x600d,
+	       "PCI error detected returning [%x].\n", ret);
+	return ret;
 }
 
 static pci_ers_result_t
@@ -7634,9 +7843,20 @@
 	struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
 	struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
 
+	ql_log(ql_log_warn, base_vha, 0x9000,
+	       "mmio enabled\n");
+
+	ha->pci_error_state = QLA_PCI_MMIO_ENABLED;
+
 	if (IS_QLA82XX(ha))
 		return PCI_ERS_RESULT_RECOVERED;
 
+	if (qla2x00_isp_reg_stat(ha)) {
+		ql_log(ql_log_info, base_vha, 0x803f,
+		    "During mmio enabled, PCI/Register disconnect still detected.\n");
+		goto out;
+	}
+
 	spin_lock_irqsave(&ha->hardware_lock, flags);
 	if (IS_QLA2100(ha) || IS_QLA2200(ha)){
 		stat = rd_reg_word(&reg->hccr);
@@ -7657,10 +7877,12 @@
 		ql_log(ql_log_info, base_vha, 0x9003,
 		    "RISC paused -- mmio_enabled, Dumping firmware.\n");
 		qla2xxx_dump_fw(base_vha);
-
-		return PCI_ERS_RESULT_NEED_RESET;
-	} else
-		return PCI_ERS_RESULT_RECOVERED;
+	}
+out:
+	/* set PCI_ERS_RESULT_NEED_RESET to trigger call to qla2xxx_pci_slot_reset */
+	ql_dbg(ql_dbg_aer, base_vha, 0x600d,
+	       "mmio enabled returning.\n");
+	return PCI_ERS_RESULT_NEED_RESET;
 }
 
 static pci_ers_result_t
@@ -7672,9 +7894,10 @@
 	int rc;
 	struct qla_qpair *qpair = NULL;
 
-	ql_dbg(ql_dbg_aer, base_vha, 0x9004,
-	    "Slot Reset.\n");
+	ql_log(ql_log_warn, base_vha, 0x9004,
+	       "Slot Reset.\n");
 
+	ha->pci_error_state = QLA_PCI_SLOT_RESET;
 	/* Workaround: qla2xxx driver which access hardware earlier
 	 * needs error state to be pci_channel_io_online.
 	 * Otherwise mailbox command timesout.
@@ -7708,16 +7931,24 @@
 		qpair->online = 1;
 	mutex_unlock(&ha->mq_lock);
 
+	ha->flags.eeh_busy = 0;
 	base_vha->flags.online = 1;
 	set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
-	if (ha->isp_ops->abort_isp(base_vha) == QLA_SUCCESS)
-		ret =  PCI_ERS_RESULT_RECOVERED;
+	ha->isp_ops->abort_isp(base_vha);
 	clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
 
+	if (qla2x00_isp_reg_stat(ha)) {
+		ha->flags.eeh_busy = 1;
+		qla_pci_error_cleanup(base_vha);
+		ql_log(ql_log_warn, base_vha, 0x9005,
+		       "Device unable to recover from PCI error.\n");
+	} else {
+		ret =  PCI_ERS_RESULT_RECOVERED;
+	}
 
 exit_slot_reset:
 	ql_dbg(ql_dbg_aer, base_vha, 0x900e,
-	    "slot_reset return %x.\n", ret);
+	    "Slot Reset returning %x.\n", ret);
 
 	return ret;
 }
@@ -7729,16 +7960,58 @@
 	struct qla_hw_data *ha = base_vha->hw;
 	int ret;
 
-	ql_dbg(ql_dbg_aer, base_vha, 0x900f,
-	    "pci_resume.\n");
+	ql_log(ql_log_warn, base_vha, 0x900f,
+	       "Pci Resume.\n");
 
-	ha->flags.eeh_busy = 0;
 
 	ret = qla2x00_wait_for_hba_online(base_vha);
 	if (ret != QLA_SUCCESS) {
 		ql_log(ql_log_fatal, base_vha, 0x9002,
 		    "The device failed to resume I/O from slot/link_reset.\n");
 	}
+	ha->pci_error_state = QLA_PCI_RESUME;
+	ql_dbg(ql_dbg_aer, base_vha, 0x600d,
+	       "Pci Resume returning.\n");
+}
+
+void qla_pci_set_eeh_busy(struct scsi_qla_host *vha)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+	bool do_cleanup = false;
+	unsigned long flags;
+
+	if (ha->flags.eeh_busy)
+		return;
+
+	spin_lock_irqsave(&base_vha->work_lock, flags);
+	if (!ha->flags.eeh_busy) {
+		ha->eeh_jif = jiffies;
+		ha->flags.eeh_flush = 0;
+
+		ha->flags.eeh_busy = 1;
+		do_cleanup = true;
+	}
+	spin_unlock_irqrestore(&base_vha->work_lock, flags);
+
+	if (do_cleanup)
+		qla_pci_error_cleanup(base_vha);
+}
+
+/*
+ * this routine will schedule a task to pause IO from interrupt context
+ * if caller sees a PCIE error event (register read = 0xf's)
+ */
+void qla_schedule_eeh_work(struct scsi_qla_host *vha)
+{
+	struct qla_hw_data *ha = vha->hw;
+	struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+
+	if (ha->flags.eeh_busy)
+		return;
+
+	set_bit(DO_EEH_RECOVERY, &base_vha->dpc_flags);
+	qla2xxx_wake_dpc(base_vha);
 }
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
@@ -7804,21 +8077,20 @@
 #endif
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
-static int qla2xxx_map_queues(struct Scsi_Host *shost)
+static MAP_QUEUES_RET qla2xxx_map_queues(struct Scsi_Host *shost)
 {
-	int rc;
 	scsi_qla_host_t *vha = (scsi_qla_host_t *)shost->hostdata;
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) ||	\
 	(defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 8)
 	struct blk_mq_queue_map *qmap = &shost->tag_set.map[0];
 
 	if (USER_CTRL_IRQ(vha->hw) || !vha->hw->mqiobase)
-		rc = blk_mq_map_queues(qmap);
+		blk_mq_map_queues(qmap);
 	else
-		rc = blk_mq_pci_map_queues(qmap, vha->hw->pdev, vha->irq_offset);
+		blk_mq_pci_map_queues(qmap, vha->hw->pdev, vha->irq_offset);
 #else
 	if (USER_CTRL_IRQ(vha->hw))
-		rc = blk_mq_map_queues(&shost->tag_set);
+		blk_mq_map_queues(&shost->tag_set);
 	else
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0) &&		\
 	(!defined(UEK_KABI_RENAME) ||				\
@@ -7828,12 +8100,13 @@
 		 * See also commit f23f5bece686 ("blk-mq: Allow PCI vector
 		 * offset for mapping queues") # v4.17.
 		 */
-		rc = blk_mq_pci_map_queues(&shost->tag_set, vha->hw->pdev);
+		blk_mq_pci_map_queues(&shost->tag_set, vha->hw->pdev);
 #else
-		rc = blk_mq_pci_map_queues(&shost->tag_set, vha->hw->pdev, 0);
+		blk_mq_pci_map_queues(&shost->tag_set, vha->hw->pdev, 0);
 #endif
 #endif
-	return rc;
+
+	return (MAP_QUEUES_RET) 0;
 }
 #endif
 
@@ -7846,6 +8119,11 @@
 	.eh_timed_out		= fc_eh_timed_out,
 #endif
 	.eh_abort_handler	= qla2xxx_eh_abort,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
+	/* See also commit 60bee27ba2df ("scsi: core: No retries on abort
+	 * success") */
+	.eh_should_retry_cmd	= fc_eh_should_retry_cmd,
+#endif
 	.eh_device_reset_handler = qla2xxx_eh_device_reset,
 	.eh_target_reset_handler = qla2xxx_eh_target_reset,
 	.eh_bus_reset_handler	= qla2xxx_eh_bus_reset,
@@ -7870,7 +8148,11 @@
 	.sg_tablesize		= SG_ALL,
 
 	.max_sectors		= 0xFFFF,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)
 	.shost_attrs		= qla2x00_host_attrs,
+#else
+	.shost_groups		= qla2x00_host_groups,
+#endif
 
 	.supported_mode		= MODE_INITIATOR,
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
@@ -7891,12 +8173,7 @@
 }
 #endif
 
-static
-/* See also commit 494530284f16 ("PCI: Make pci_error_handlers const") # v3.7 */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0) || defined(RHEL_MAJOR)
-const
-#endif
-struct pci_error_handlers qla2xxx_err_handler = {
+static const struct pci_error_handlers qla2xxx_err_handler = {
 	.error_detected = qla2xxx_pci_error_detected,
 	.mmio_enabled = qla2xxx_pci_mmio_enabled,
 	.slot_reset = qla2xxx_pci_slot_reset,
@@ -7988,7 +8265,7 @@
 	BUILD_BUG_ON(sizeof(struct cmd_type_7_fx00) != 64);
 	BUILD_BUG_ON(sizeof(struct cmd_type_crc_2) != 64);
 	BUILD_BUG_ON(sizeof(struct ct_entry_24xx) != 64);
-	BUILD_BUG_ON(sizeof(struct ct_fdmi1_hba_attributes) != 2344);
+	BUILD_BUG_ON(sizeof(struct ct_fdmi1_hba_attributes) != 2604);
 	BUILD_BUG_ON(sizeof(struct ct_fdmi2_hba_attributes) != 4424);
 	BUILD_BUG_ON(sizeof(struct ct_fdmi2_port_attributes) != 4164);
 	BUILD_BUG_ON(sizeof(struct ct_fdmi_hba_attr) != 260);
@@ -8053,6 +8330,10 @@
 	BUILD_BUG_ON(sizeof(sw_info_t) != 32);
 	BUILD_BUG_ON(sizeof(target_id_t) != 2);
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+	qla_trace_init();
+#endif
+
 	/* Allocate cache for SRBs. */
 	srb_cachep = kmem_cache_create("qla2xxx_srbs", sizeof(srb_t), 0,
 	    SLAB_HWCACHE_ALIGN, NULL);
@@ -8083,8 +8364,10 @@
 	if (ql2xextended_error_logging == 1)
 		ql2xextended_error_logging = QL_DBG_DEFAULT1_MASK;
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)
 	if (ql2x_ini_mode == QLA2XXX_INI_MODE_DUAL)
 		qla_insert_tgt_attrs();
+#endif
 
 	qla2xxx_transport_template =
 	    fc_attach_transport(&qla2xxx_transport_functions);
@@ -8134,6 +8417,10 @@
 
 destroy_cache:
 	kmem_cache_destroy(srb_cachep);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+	qla_trace_uninit();
+#endif
 	return ret;
 }
 
@@ -8152,6 +8439,10 @@
 	fc_release_transport(qla2xxx_transport_template);
 	qlt_exit();
 	kmem_cache_destroy(srb_cachep);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 5, 0)
+	qla_trace_uninit();
+#endif
 }
 
 module_init(qla2x00_module_init);
@@ -8160,7 +8451,6 @@
 MODULE_AUTHOR("QLogic Corporation");
 MODULE_DESCRIPTION("QLogic Fibre Channel HBA Driver");
 MODULE_LICENSE("GPL");
-MODULE_VERSION(QLA2XXX_VERSION);
 MODULE_FIRMWARE(FW_FILE_ISP21XX);
 MODULE_FIRMWARE(FW_FILE_ISP22XX);
 MODULE_FIRMWARE(FW_FILE_ISP2300);
diff --git a/scst/qla2x00t-32gbit/qla_settings.h b/scst/qla2x00t-32gbit/qla_settings.h
index 2fb7ebf..a5f3000 100644
--- a/scst/qla2x00t-32gbit/qla_settings.h
+++ b/scst/qla2x00t-32gbit/qla_settings.h
@@ -1,8 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #define MAX_RETRIES_OF_ISP_ABORT	5
 
diff --git a/scst/qla2x00t-32gbit/qla_sup.c b/scst/qla2x00t-32gbit/qla_sup.c
index 411b8a9..c092a6b 100644
--- a/scst/qla2x00t-32gbit/qla_sup.c
+++ b/scst/qla2x00t-32gbit/qla_sup.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
 
@@ -845,7 +844,7 @@
 				ha->flt_region_nvram = start;
 			break;
 		case FLT_REG_IMG_PRI_27XX:
-			if (IS_QLA27XX(ha) && !IS_QLA28XX(ha))
+			if (IS_QLA27XX(ha) || IS_QLA28XX(ha))
 				ha->flt_region_img_status_pri = start;
 			break;
 		case FLT_REG_IMG_SEC_27XX:
@@ -1357,7 +1356,7 @@
 		    flash_data_addr(ha, faddr), le32_to_cpu(*dwptr));
 		if (ret) {
 			ql_dbg(ql_dbg_user, vha, 0x7006,
-			    "Failed slopw write %x (%x)\n", faddr, *dwptr);
+			    "Failed slow write %x (%x)\n", faddr, *dwptr);
 			break;
 		}
 	}
@@ -2622,10 +2621,11 @@
 }
 
 static int
-qla28xx_extract_sfub_and_verify(struct scsi_qla_host *vha, uint32_t *buf,
+qla28xx_extract_sfub_and_verify(struct scsi_qla_host *vha, __le32 *buf,
     uint32_t len, uint32_t buf_size_without_sfub, uint8_t *sfub_buf)
 {
-	uint32_t *p, check_sum = 0;
+	uint32_t check_sum = 0;
+	__le32 *p;
 	int i;
 
 	p = buf + buf_size_without_sfub;
@@ -2635,14 +2635,14 @@
 	    sizeof(struct secure_flash_update_block));
 
 	for (i = 0; i < (sizeof(struct secure_flash_update_block) >> 2); i++)
-		check_sum += p[i];
+		check_sum += le32_to_cpu(p[i]);
 
 	check_sum = (~check_sum) + 1;
 
-	if (check_sum != p[i]) {
+	if (check_sum != le32_to_cpu(p[i])) {
 		ql_log(ql_log_warn, vha, 0x7097,
 		    "SFUB checksum failed, 0x%x, 0x%x\n",
-		    check_sum, p[i]);
+		    check_sum, le32_to_cpu(p[i]));
 		return QLA_COMMAND_ERROR;
 	}
 
@@ -2722,7 +2722,7 @@
 	if (ha->flags.secure_adapter && region.attribute) {
 
 		ql_log(ql_log_warn + ql_dbg_verbose, vha, 0xffff,
-		    "Region %x is secure\n", region.code);
+		    "Region %x is secure\n", le16_to_cpu(region.code));
 
 		switch (le16_to_cpu(region.code)) {
 		case FLT_REG_FW:
@@ -2776,7 +2776,7 @@
 		default:
 			ql_log(ql_log_warn + ql_dbg_verbose, vha,
 			    0xffff, "Secure region %x not supported\n",
-			    region.code);
+			    le16_to_cpu(region.code));
 			rval = QLA_COMMAND_ERROR;
 			goto done;
 		}
@@ -2791,8 +2791,8 @@
 			goto done;
 		}
 
-		rval = qla28xx_extract_sfub_and_verify(vha, dwptr, dwords,
-			buf_size_without_sfub, (uint8_t *)sfub);
+		rval = qla28xx_extract_sfub_and_verify(vha, (__le32 *)dwptr,
+			dwords, buf_size_without_sfub, (uint8_t *)sfub);
 
 		if (rval != QLA_SUCCESS)
 			goto done;
@@ -2936,7 +2936,6 @@
 		liter += dburst - 1;
 		faddr += dburst - 1;
 		dwptr += dburst - 1;
-		continue;
 	}
 
 write_protect:
diff --git a/scst/qla2x00t-32gbit/qla_target.c b/scst/qla2x00t-32gbit/qla_target.c
index 64038f2..02bb0c1 100644
--- a/scst/qla2x00t-32gbit/qla_target.c
+++ b/scst/qla2x00t-32gbit/qla_target.c
@@ -47,24 +47,17 @@
 MODULE_PARM_DESC(ql2xtgt_tape_enable,
 		"Enables Sequence level error recovery (aka FC Tape). Default is 0 - no SLER. 1 - Enable SLER.");
 
-static char *qlini_mode = QLA2XXX_INI_MODE_STR_ENABLED;
+static char *qlini_mode = QLA2XXX_INI_MODE_STR_EXCLUSIVE;
 module_param(qlini_mode, charp, S_IRUGO);
 MODULE_PARM_DESC(qlini_mode,
 	"Determines when initiator mode will be enabled. Possible values: "
-	"\"exclusive\" - initiator mode will be enabled on load, "
+	"\"exclusive\" (default) - initiator mode will be enabled on load, "
 	"disabled on enabling target mode and then on disabling target mode "
 	"enabled back; "
 	"\"disabled\" - initiator mode will never be enabled; "
 	"\"dual\" - Initiator Modes will be enabled. Target Mode can be activated "
-	"when ready "
-	"\"enabled\" (default) - initiator mode will always stay enabled.");
-
-static int ql_dm_tgt_ex_pct = 0;
-module_param(ql_dm_tgt_ex_pct, int, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(ql_dm_tgt_ex_pct,
-	"For Dual Mode (qlini_mode=dual), this parameter determines "
-	"the percentage of exchanges/cmds FW will allocate resources "
-	"for Target mode.");
+	"when ready; "
+	"\"enabled\" - initiator mode will always stay enabled.");
 
 int ql2xuctrlirq = 1;
 module_param(ql2xuctrlirq, int, 0644);
@@ -199,8 +192,7 @@
 	return QLA_SUCCESS;
 }
 
-static inline
-struct scsi_qla_host *qlt_find_host_by_d_id(struct scsi_qla_host *vha,
+struct scsi_qla_host *qla_find_host_by_d_id(struct scsi_qla_host *vha,
 					    be_id_t d_id)
 {
 	struct scsi_qla_host *host;
@@ -213,7 +205,7 @@
 
 	key = be_to_port_id(d_id).b24;
 
-	host = btree_lookup32(&vha->hw->tgt.host_map, key);
+	host = btree_lookup32(&vha->hw->host_map, key);
 	if (!host)
 		ql_dbg(ql_dbg_tgt_mgt + ql_dbg_verbose, vha, 0xf005,
 		    "Unable to find host %06x\n", key);
@@ -314,7 +306,7 @@
 			goto abort;
 		}
 
-		host = qlt_find_host_by_d_id(vha, u->atio.u.isp24.fcp_hdr.d_id);
+		host = qla_find_host_by_d_id(vha, u->atio.u.isp24.fcp_hdr.d_id);
 		if (host != NULL) {
 			ql_dbg(ql_dbg_async + ql_dbg_verbose, vha, 0x502f,
 			    "Requeuing unknown ATIO_TYPE7 %p\n", u);
@@ -363,7 +355,7 @@
 	switch (atio->u.raw.entry_type) {
 	case ATIO_TYPE7:
 	{
-		struct scsi_qla_host *host = qlt_find_host_by_d_id(vha,
+		struct scsi_qla_host *host = qla_find_host_by_d_id(vha,
 		    atio->u.isp24.fcp_hdr.d_id);
 		if (unlikely(NULL == host)) {
 			ql_dbg(ql_dbg_tgt, vha, 0xe03e,
@@ -592,6 +584,18 @@
 		sp->fcport->logout_on_delete = 1;
 		sp->fcport->plogi_nack_done_deadline = jiffies + HZ;
 		sp->fcport->send_els_logo = 0;
+
+		if (sp->fcport->flags & FCF_FCSP_DEVICE) {
+			ql_dbg(ql_dbg_edif, vha, 0x20ef,
+			    "%s %8phC edif: PLOGI- AUTH WAIT\n", __func__,
+			    sp->fcport->port_name);
+			qla2x00_set_fcport_disc_state(sp->fcport,
+			    DSC_LOGIN_AUTH_PEND);
+			qla2x00_post_aen_work(vha, FCH_EVT_PORT_ONLINE,
+			    sp->fcport->d_id.b24);
+			qla_edb_eventcreate(vha, VND_CMD_AUTH_STATE_NEEDED, sp->fcport->d_id.b24,
+			    0, sp->fcport);
+		}
 		break;
 
 	case SRB_NACK_PRLI:
@@ -624,7 +628,7 @@
 	}
 	spin_unlock_irqrestore(&vha->hw->tgt.sess_lock, flags);
 
-	sp->free(sp);
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 }
 
 int qla24xx_async_notify_ack(scsi_qla_host_t *vha, fc_port_t *fcport,
@@ -639,6 +643,9 @@
 	case SRB_NACK_PLOGI:
 		fcport->fw_login_state = DSC_LS_PLOGI_PEND;
 		c = "PLOGI";
+		if (vha->hw->flags.edif_enabled &&
+		    (le16_to_cpu(ntfy->u.isp24.flags) & NOTIFY24XX_FLAGS_FCSP))
+			fcport->flags |= FCF_FCSP_DEVICE;
 		break;
 	case SRB_NACK_PRLI:
 		fcport->fw_login_state = DSC_LS_PRLI_PEND;
@@ -657,12 +664,10 @@
 
 	sp->type = type;
 	sp->name = "nack";
-
-	sp->u.iocb_cmd.timeout = qla2x00_async_iocb_timeout;
-	qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha)+2);
+	qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2,
+			      qla2x00_async_nack_sp_done);
 
 	sp->u.iocb_cmd.u.nack.ntfy = ntfy;
-	sp->done = qla2x00_async_nack_sp_done;
 
 	ql_dbg(ql_dbg_disc, vha, 0x20f4,
 	    "Async-%s %8phC hndl %x %s\n",
@@ -675,7 +680,7 @@
 	return rval;
 
 done_free_sp:
-	sp->free(sp);
+	kref_put(&sp->cmd_kref, qla2x00_sp_release);
 done:
 	fcport->flags &= ~FCF_ASYNC_SENT;
 	return rval;
@@ -708,7 +713,12 @@
 void qla24xx_delete_sess_fn(struct work_struct *work)
 {
 	fc_port_t *fcport = container_of(work, struct fc_port, del_work);
-	struct qla_hw_data *ha = fcport->vha->hw;
+	struct qla_hw_data *ha = NULL;
+
+	if (!fcport || !fcport->vha || !fcport->vha->hw)
+		return;
+
+	ha = fcport->vha->hw;
 
 	if (fcport->se_sess) {
 		ha->tgt.tgt_ops->shutdown_sess(fcport);
@@ -932,6 +942,11 @@
 	qlt_port_logo_t *tmp;
 	int res;
 
+	if (test_bit(PFLG_DRIVER_REMOVING, &vha->pci_flags)) {
+		res = 0;
+		goto out;
+	}
+
 	mutex_lock(&vha->vha_tgt.tgt_mutex);
 
 	list_for_each_entry(tmp, &vha->logo_list, list) {
@@ -952,6 +967,7 @@
 	list_del(&logo->list);
 	mutex_unlock(&vha->vha_tgt.tgt_mutex);
 
+out:
 	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf098,
 	    "Finished LOGO to %02x:%02x:%02x, dropped %d cmds, res = %#x\n",
 	    logo->id.b.domain, logo->id.b.area, logo->id.b.al_pa,
@@ -987,6 +1003,7 @@
 
 			logo.id = sess->d_id;
 			logo.cmd_count = 0;
+			INIT_LIST_HEAD(&logo.list);
 			if (!own)
 				qlt_send_first_logo(vha, &logo);
 			sess->send_els_logo = 0;
@@ -996,8 +1013,8 @@
 			int rc;
 
 			if (!own ||
-			    (own &&
-			     (own->iocb.u.isp24.status_subcode == ELS_PLOGI))) {
+			     (own->iocb.u.isp24.status_subcode == ELS_PLOGI)) {
+				sess->logout_completed = 0;
 				rc = qla2x00_post_async_logout_work(vha, sess,
 				    NULL);
 				if (rc != QLA_SUCCESS)
@@ -1024,6 +1041,25 @@
 			sess->nvme_flag |= NVME_FLAG_DELETING;
 			qla_nvme_unregister_remote_port(sess);
 		}
+
+		if (ha->flags.edif_enabled &&
+		    (!own || (own &&
+			      own->iocb.u.isp24.status_subcode == ELS_PLOGI))) {
+			sess->edif.authok = 0;
+			if (!ha->flags.host_shutting_down) {
+				ql_dbg(ql_dbg_edif, vha, 0x911e,
+				       "%s wwpn %8phC calling qla2x00_release_all_sadb\n",
+				       __func__, sess->port_name);
+				qla2x00_release_all_sadb(vha, sess);
+			} else {
+				ql_dbg(ql_dbg_edif, vha, 0x911e,
+				       "%s bypassing release_all_sadb\n",
+				       __func__);
+			}
+
+			qla_edif_clear_appdata(vha, sess);
+			qla_edif_sess_down(vha, sess);
+		}
 	}
 
 	/*
@@ -1045,7 +1081,12 @@
 			}
 			msleep(100);
 			cnt++;
-			if (cnt > 200)
+			/*
+			 * Driver timeout is set to 22 Sec, update count value to loop
+			 * long enough for log-out to complete before advancing. Otherwise,
+			 * straddling logout can interfere with re-login attempt.
+			 */
+			if (cnt > 230)
 				break;
 		}
 
@@ -1126,6 +1167,8 @@
 	spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
 	sess->free_pending = 0;
 
+	qla2x00_dfs_remove_rport(vha, sess);
+
 	ql_dbg(ql_dbg_disc, vha, 0xf001,
 	    "Unregistration of sess %p %8phC finished fcp_cnt %d\n",
 		sess, sess->port_name, vha->fcport_count);
@@ -1244,14 +1287,15 @@
 	case DSC_DELETE_PEND:
 		return;
 	case DSC_DELETED:
-		if (tgt && tgt->tgt_stop && (tgt->sess_count == 0))
-			wake_up_all(&tgt->waitQ);
-		if (sess->vha->fcport_count == 0)
-			wake_up_all(&sess->vha->fcport_waitQ);
-
 		if (!sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN] &&
-			!sess->plogi_link[QLT_PLOGI_LINK_CONFLICT])
+			!sess->plogi_link[QLT_PLOGI_LINK_CONFLICT]) {
+			if (tgt && tgt->tgt_stop && tgt->sess_count == 0)
+				wake_up_all(&tgt->waitQ);
+
+			if (sess->vha->fcport_count == 0)
+				wake_up_all(&sess->vha->fcport_waitQ);
 			return;
+		}
 		break;
 	case DSC_UPD_FCPORT:
 		/*
@@ -1285,9 +1329,9 @@
 
 	qla24xx_chk_fcp_state(sess);
 
-	ql_dbg(ql_dbg_disc, sess->vha, 0xe001,
-	    "Scheduling sess %p for deletion %8phC\n",
-	    sess, sess->port_name);
+	ql_dbg(ql_log_warn, sess->vha, 0xe001,
+	    "Scheduling sess %p for deletion %8phC fc4_type %x\n",
+	    sess, sess->port_name, sess->fc4_type);
 
 	WARN_ON(!queue_work(sess->vha->hw->wq, &sess->del_work));
 }
@@ -1528,11 +1572,11 @@
 	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf009,
 	    "Waiting for sess works (tgt %p)", tgt);
 	spin_lock_irqsave(&tgt->sess_work_lock, flags);
-	while (!list_empty(&tgt->sess_works_list)) {
+	do {
 		spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
-		flush_scheduled_work();
+		flush_work(&tgt->sess_work);
 		spin_lock_irqsave(&tgt->sess_work_lock, flags);
-	}
+	} while (!list_empty(&tgt->sess_works_list));
 	spin_unlock_irqrestore(&tgt->sess_work_lock, flags);
 
 	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00a,
@@ -1571,10 +1615,12 @@
 		return;
 	}
 
+	mutex_lock(&tgt->ha->optrom_mutex);
 	mutex_lock(&vha->vha_tgt.tgt_mutex);
 	tgt->tgt_stop = 0;
 	tgt->tgt_stopped = 1;
 	mutex_unlock(&vha->vha_tgt.tgt_mutex);
+	mutex_unlock(&tgt->ha->optrom_mutex);
 
 	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00c, "Stop of tgt %p finished\n",
 	    tgt);
@@ -1726,6 +1772,12 @@
 	nack->u.isp24.srr_reject_code_expl = srr_explan;
 	nack->u.isp24.vp_index = ntfy->u.isp24.vp_index;
 
+	/* TODO qualify this with EDIF enable */
+	if (ntfy->u.isp24.status_subcode == ELS_PLOGI &&
+	    (le16_to_cpu(ntfy->u.isp24.flags) & NOTIFY24XX_FLAGS_FCSP)) {
+		nack->u.isp24.flags |= cpu_to_le16(NOTIFY_ACK_FLAGS_FCSP);
+	}
+
 	ql_dbg(ql_dbg_tgt, vha, 0xe005,
 	    "qla_target(%d): Sending 24xx Notify Ack %d\n",
 	    vha->vp_idx, nack->u.isp24.status);
@@ -1983,17 +2035,6 @@
 
 	key = sid_to_key(s_id);
 	spin_lock_irqsave(&vha->cmd_list_lock, flags);
-	list_for_each_entry(op, &vha->qla_sess_op_cmd_list, cmd_list) {
-		uint32_t op_key;
-		u64 op_lun;
-
-		op_key = sid_to_key(op->atio.u.isp24.fcp_hdr.s_id);
-		op_lun = scsilun_to_int(
-			(struct scsi_lun *)&op->atio.u.isp24.fcp_cmnd.lun);
-		if (op_key == key && op_lun == lun)
-			op->aborted = true;
-	}
-
 	list_for_each_entry(op, &vha->unknown_atio_list, cmd_list) {
 		uint32_t op_key;
 		u64 op_lun;
@@ -2095,6 +2136,7 @@
 	struct qla_hw_data *ha = vha->hw;
 	struct qla_tgt_mgmt_cmd *mcmd;
 	struct qla_qpair_hint *h = &vha->vha_tgt.qla_tgt->qphints[0];
+	struct qla_tgt_cmd *abort_cmd;
 
 	ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00f,
 	    "qla_target(%d): task abort (tag=%d)\n",
@@ -2124,19 +2166,21 @@
 	mcmd->se_cmd.cpuid = h->cpuid;
 #endif
 
-	if (ha->tgt.tgt_ops->find_cmd_by_tag) {
-		struct qla_tgt_cmd *abort_cmd;
-
-		abort_cmd = ha->tgt.tgt_ops->find_cmd_by_tag(sess,
+	abort_cmd = ha->tgt.tgt_ops->find_cmd_by_tag(sess,
 				le32_to_cpu(abts->exchange_addr_to_abort));
-		if (abort_cmd && abort_cmd->qpair) {
-			mcmd->qpair = abort_cmd->qpair;
+	if (!abort_cmd) {
+		mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool);
+		return -EIO;
+	}
+	mcmd->unpacked_lun = abort_cmd->se_cmd.orig_fe_lun;
+
+	if (abort_cmd->qpair) {
+		mcmd->qpair = abort_cmd->qpair;
 #if HAVE_SE_CMD_CPUID
-			mcmd->se_cmd.cpuid = abort_cmd->se_cmd.cpuid;
+		mcmd->se_cmd.cpuid = abort_cmd->se_cmd.cpuid;
 #endif
-			mcmd->abort_io_attr = abort_cmd->atio.u.isp24.attr;
-			mcmd->flags = QLA24XX_MGMT_ABORT_IO_ATTR_VALID;
-		}
+		mcmd->abort_io_attr = abort_cmd->atio.u.isp24.attr;
+		mcmd->flags = QLA24XX_MGMT_ABORT_IO_ATTR_VALID;
 	}
 
 	INIT_WORK(&mcmd->work, qlt_do_tmr_work);
@@ -2591,6 +2635,7 @@
 	struct ctio7_to_24xx *pkt;
 	struct atio_from_isp *atio = &prm->cmd->atio;
 	uint16_t temp;
+	struct qla_tgt_cmd      *cmd = prm->cmd;
 
 	pkt = (struct ctio7_to_24xx *)qpair->req->ring_ptr;
 	prm->pkt = pkt;
@@ -2623,6 +2668,15 @@
 	pkt->u.status0.ox_id = cpu_to_le16(temp);
 	pkt->u.status0.relative_offset = cpu_to_le32(prm->cmd->offset);
 
+	if (cmd->edif) {
+		if (cmd->dma_data_direction == DMA_TO_DEVICE)
+			prm->cmd->sess->edif.rx_bytes += cmd->bufflen;
+		if (cmd->dma_data_direction == DMA_FROM_DEVICE)
+			prm->cmd->sess->edif.tx_bytes += cmd->bufflen;
+
+		pkt->u.status0.edif_flags |= EF_EN_EDIF;
+	}
+
 	return 0;
 }
 
@@ -3251,8 +3305,7 @@
 	if (!qpair->fw_started || (cmd->reset_count != qpair->chip_reset) ||
 	    (cmd->sess && cmd->sess->deleted)) {
 		cmd->state = QLA_TGT_STATE_PROCESSED;
-		res = 0;
-		goto free;
+		return 0;
 	}
 
 	ql_dbg_qp(ql_dbg_tgt, qpair, 0xe018,
@@ -3263,8 +3316,9 @@
 
 	res = qlt_pre_xmit_response(cmd, &prm, xmit_type, scsi_status,
 	    &full_req_cnt);
-	if (unlikely(res != 0))
-		goto free;
+	if (unlikely(res != 0)) {
+		return res;
+	}
 
 	spin_lock_irqsave(qpair->qp_lock_ptr, flags);
 
@@ -3283,9 +3337,8 @@
 			"RESET-RSP online/active/old-count/new-count = %d/%d/%d/%d.\n",
 			vha->flags.online, qla2x00_reset_active(vha),
 			cmd->reset_count, qpair->chip_reset);
-		spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
 		res = 0;
-		goto free;
+		goto out_unmap_unlock;
 	}
 
 	/* Does F/W have an IOCBs for this request */
@@ -3320,8 +3373,10 @@
 			if (xmit_type & QLA_TGT_XMIT_STATUS) {
 				pkt->u.status0.scsi_status =
 				    cpu_to_le16(prm.rq_result);
-				pkt->u.status0.residual =
-				    cpu_to_le32(prm.residual);
+				if (!cmd->edif)
+					pkt->u.status0.residual =
+						cpu_to_le32(prm.residual);
+
 				pkt->u.status0.flags |= cpu_to_le16(
 				    CTIO7_FLAGS_SEND_STATUS);
 				if (qlt_need_explicit_conf(cmd, 0)) {
@@ -3392,8 +3447,6 @@
 	qlt_unmap_sg(vha, cmd);
 	spin_unlock_irqrestore(qpair->qp_lock_ptr, flags);
 
-free:
-	vha->hw->tgt.tgt_ops->free_cmd(cmd);
 	return res;
 }
 EXPORT_SYMBOL(qlt_xmit_response);
@@ -3414,10 +3467,6 @@
 	prm.sg = NULL;
 	prm.req_cnt = 1;
 
-	/* Calculate number of entries and segments required */
-	if (qlt_pci_map_calc_cnt(&prm) != 0)
-		return -EAGAIN;
-
 	if (!qpair->fw_started || (cmd->reset_count != qpair->chip_reset) ||
 	    (cmd->sess && cmd->sess->deleted)) {
 		/*
@@ -3435,6 +3484,10 @@
 		return 0;
 	}
 
+	/* Calculate number of entries and segments required */
+	if (qlt_pci_map_calc_cnt(&prm) != 0)
+		return -EAGAIN;
+
 	spin_lock_irqsave(qpair->qp_lock_ptr, flags);
 	/* Does F/W have an IOCBs for this request */
 	res = qlt_check_reserve_free_req(qpair, prm.req_cnt);
@@ -3820,6 +3873,9 @@
 
 	spin_lock_irqsave(&cmd->cmd_lock, flags);
 	if (cmd->aborted) {
+		if (cmd->sg_mapped)
+			qlt_unmap_sg(vha, cmd);
+
 		spin_unlock_irqrestore(&cmd->cmd_lock, flags);
 		/*
 		 * It's normal to see 2 calls in this path:
@@ -3828,7 +3884,7 @@
 		 */
 		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf016, "multiple abort. %p\n",
 		       cmd);
-		return EIO;
+		return -EIO;
 	}
 	cmd->aborted = 1;
 	cmd->trc_flags |= TRC_ABORT;
@@ -3850,16 +3906,11 @@
 
 	BUG_ON(cmd->cmd_in_wq);
 
-	if (cmd->sg_mapped)
-		qlt_unmap_sg(cmd->vha, cmd);
-
 	if (!cmd->q_full)
 		qlt_decr_num_pend_cmds(cmd->vha);
 
 	BUG_ON(cmd->sg_mapped);
 	cmd->jiffies_at_free = get_jiffies_64();
-	if (unlikely(cmd->free_sg))
-		kfree(cmd->sg);
 
 	if (!sess || !sess->se_sess) {
 		WARN_ON(1);
@@ -3983,6 +4034,12 @@
 	if (cmd == NULL)
 		return;
 
+	if ((le16_to_cpu(((struct ctio7_from_24xx *)ctio)->flags) & CTIO7_FLAGS_DATA_OUT) &&
+	    cmd->sess) {
+		qlt_chk_edif_rx_sa_delete_pending(vha, cmd->sess,
+		    (struct ctio7_from_24xx *)ctio);
+	}
+
 	se_cmd = &cmd->se_cmd;
 	cmd->cmd_sent_to_fw = 0;
 
@@ -4053,6 +4110,16 @@
 			qlt_handle_dif_error(qpair, cmd, ctio);
 			return;
 		}
+
+		case CTIO_FAST_AUTH_ERR:
+		case CTIO_FAST_INCOMP_PAD_LEN:
+		case CTIO_FAST_INVALID_REQ:
+		case CTIO_FAST_SPI_ERR:
+			ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05b,
+			    "qla_target(%d): CTIO with EDIF error status 0x%x received (state %x, se_cmd %p\n",
+			    vha->vp_idx, status, cmd->state, se_cmd);
+			break;
+
 		default:
 			ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05b,
 			    "qla_target(%d): CTIO with error status 0x%x received (state %x, se_cmd %p\n",
@@ -4340,6 +4407,7 @@
 
 	cmd->cmd_type = TYPE_TGT_CMD;
 	memcpy(&cmd->atio, atio, sizeof(*atio));
+	INIT_LIST_HEAD(&cmd->sess_cmd_list);
 	cmd->state = QLA_TGT_STATE_NEW;
 	cmd->tgt = vha->vha_tgt.qla_tgt;
 	qlt_incr_num_pend_cmds(vha);
@@ -4356,6 +4424,7 @@
 	qlt_assign_qpair(vha, cmd);
 	cmd->reset_count = vha->hw->base_qpair->chip_reset;
 	cmd->vp_idx = vha->vp_idx;
+	cmd->edif = sess->edif.enable;
 
 	return cmd;
 }
@@ -4713,15 +4782,6 @@
 	       ((u32)s_id->b.al_pa));
 
 	spin_lock_irqsave(&vha->cmd_list_lock, flags);
-	list_for_each_entry(op, &vha->qla_sess_op_cmd_list, cmd_list) {
-		uint32_t op_key = sid_to_key(op->atio.u.isp24.fcp_hdr.s_id);
-
-		if (op_key == key) {
-			op->aborted = true;
-			count++;
-		}
-	}
-
 	list_for_each_entry(op, &vha->unknown_atio_list, cmd_list) {
 		uint32_t op_key = sid_to_key(op->atio.u.isp24.fcp_hdr.s_id);
 
@@ -4787,6 +4847,34 @@
 		goto out;
 	}
 
+	if (vha->hw->flags.edif_enabled &&
+	    !(vha->e_dbell.db_flags & EDB_ACTIVE) &&
+	    iocb->u.isp24.status_subcode == ELS_PLOGI &&
+	    !(le16_to_cpu(iocb->u.isp24.flags) & NOTIFY24XX_FLAGS_FCSP)) {
+		ql_dbg(ql_dbg_disc, vha, 0xffff,
+			"%s %d Term INOT due to app not available lid=%d, NportID %06X ",
+			__func__, __LINE__, loop_id, port_id.b24);
+		qlt_send_term_imm_notif(vha, iocb, 1);
+		goto out;
+	}
+
+	if (vha->hw->flags.edif_enabled) {
+		if (DBELL_INACTIVE(vha)) {
+			ql_dbg(ql_dbg_disc, vha, 0xffff,
+			       "%s %d Term INOT due to app not started lid=%d, NportID %06X ",
+			       __func__, __LINE__, loop_id, port_id.b24);
+			qlt_send_term_imm_notif(vha, iocb, 1);
+			goto out;
+		} else if (iocb->u.isp24.status_subcode == ELS_PLOGI &&
+			   !(le16_to_cpu(iocb->u.isp24.flags) & NOTIFY24XX_FLAGS_FCSP)) {
+			ql_dbg(ql_dbg_disc, vha, 0xffff,
+			       "%s %d Term INOT due to unsecure lid=%d, NportID %06X ",
+			       __func__, __LINE__, loop_id, port_id.b24);
+			qlt_send_term_imm_notif(vha, iocb, 1);
+			goto out;
+		}
+	}
+
 	pla = qlt_plogi_ack_find_add(vha, &port_id, iocb);
 	if (!pla) {
 		ql_dbg(ql_dbg_disc + ql_dbg_verbose, vha, 0xffff,
@@ -4852,6 +4940,20 @@
 	qlt_plogi_ack_link(vha, pla, sess, QLT_PLOGI_LINK_SAME_WWN);
 	sess->d_id = port_id;
 	sess->login_gen++;
+	sess->loop_id = loop_id;
+
+	if (iocb->u.isp24.status_subcode == ELS_PLOGI) {
+		/* remote port has assigned Port ID */
+		if (N2N_TOPO(vha->hw) && fcport_is_bigger(sess))
+			vha->d_id = sess->d_id;
+
+		ql_dbg(ql_dbg_disc, vha, 0xffff,
+		    "%s %8phC - send port online\n",
+		    __func__, sess->port_name);
+
+		qla2x00_post_aen_work(vha, FCH_EVT_PORT_ONLINE,
+		    sess->d_id.b24);
+	}
 
 	if (iocb->u.isp24.status_subcode == ELS_PRLI) {
 		sess->fw_login_state = DSC_LS_PRLI_PEND;
@@ -4964,6 +5066,16 @@
 			sess = qla2x00_find_fcport_by_wwpn(vha,
 			    iocb->u.isp24.port_name, 1);
 
+			if (vha->hw->flags.edif_enabled && sess &&
+			    (!(sess->flags & FCF_FCSP_DEVICE) ||
+			     !sess->edif.authok)) {
+				ql_dbg(ql_dbg_disc, vha, 0xffff,
+				       "%s %d %8phC Term PRLI due to unauthorize PRLI\n",
+				       __func__, __LINE__, iocb->u.isp24.port_name);
+				qlt_send_term_imm_notif(vha, iocb, 1);
+				break;
+			}
+
 			if (sess && sess->plogi_link[QLT_PLOGI_LINK_SAME_WWN]) {
 				ql_dbg(ql_dbg_disc, vha, 0xffff,
 				    "%s %d %8phC Term PRLI due to PLOGI ACK not completed\n",
@@ -5012,6 +5124,16 @@
 			bool delete = false;
 			int sec;
 
+			if (vha->hw->flags.edif_enabled && sess &&
+			    (!(sess->flags & FCF_FCSP_DEVICE) ||
+			     !sess->edif.authok)) {
+				ql_dbg(ql_dbg_disc, vha, 0xffff,
+				       "%s %d %8phC Term PRLI due to unauthorize prli\n",
+				       __func__, __LINE__, iocb->u.isp24.port_name);
+				qlt_send_term_imm_notif(vha, iocb, 1);
+				break;
+			}
+
 			spin_lock_irqsave(&tgt->ha->tgt.sess_lock, flags);
 			switch (sess->fw_login_state) {
 			case DSC_LS_PLOGI_PEND:
@@ -5201,7 +5323,8 @@
 }
 
 /*
- * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire
+ * ha->hardware_lock supposed to be held on entry.
+ * Might drop it, then reacquire.
  */
 static void qlt_handle_imm_notify(struct scsi_qla_host *vha,
 	struct imm_ntfy_from_isp *iocb)
@@ -5541,8 +5664,7 @@
 			    "%s: Unexpected cmd in QFull list %p\n", __func__,
 			    cmd);
 
-		list_del(&cmd->cmd_list);
-		list_add_tail(&cmd->cmd_list, &free_list);
+		list_move_tail(&cmd->cmd_list, &free_list);
 
 		/* piggy back on hardware_lock for protection */
 		vha->hw->tgt.num_qfull_cmds_alloc--;
@@ -6282,69 +6404,6 @@
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
 }
 
-static void qlt_tmr_work(struct qla_tgt *tgt,
-	struct qla_tgt_sess_work_param *prm)
-{
-	struct atio_from_isp *a = &prm->tm_iocb2;
-	struct scsi_qla_host *vha = tgt->vha;
-	struct qla_hw_data *ha = vha->hw;
-	struct fc_port *sess;
-	unsigned long flags;
-	be_id_t s_id;
-	int rc;
-	u64 unpacked_lun;
-	int fn;
-	void *iocb;
-
-	spin_lock_irqsave(&ha->tgt.sess_lock, flags);
-
-	if (tgt->tgt_stop)
-		goto out_term2;
-
-	s_id = prm->tm_iocb2.u.isp24.fcp_hdr.s_id;
-	sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, s_id);
-	if (!sess) {
-		spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
-
-		sess = qlt_make_local_sess(vha, s_id);
-		/* sess has got an extra creation ref */
-
-		spin_lock_irqsave(&ha->tgt.sess_lock, flags);
-		if (!sess)
-			goto out_term2;
-	} else {
-		if (sess->deleted) {
-			goto out_term2;
-		}
-
-		if (!kref_get_unless_zero(&sess->sess_kref)) {
-			ql_dbg(ql_dbg_tgt_tmr, vha, 0xf020,
-			    "%s: kref_get fail %8phC\n",
-			     __func__, sess->port_name);
-			goto out_term2;
-		}
-	}
-
-	iocb = a;
-	fn = a->u.isp24.fcp_cmnd.task_mgmt_flags;
-	unpacked_lun =
-	    scsilun_to_int((struct scsi_lun *)&a->u.isp24.fcp_cmnd.lun);
-
-	rc = qlt_issue_task_mgmt(sess, unpacked_lun, fn, iocb, 0);
-	spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
-
-	ha->tgt.tgt_ops->put_sess(sess);
-
-	if (rc != 0)
-		goto out_term;
-	return;
-
-out_term2:
-	spin_unlock_irqrestore(&ha->tgt.sess_lock, flags);
-out_term:
-	qlt_send_term_exchange(ha->base_qpair, NULL, &prm->tm_iocb2, 1, 0);
-}
-
 static void qlt_sess_work_fn(struct work_struct *work)
 {
 	struct qla_tgt *tgt = container_of(work, struct qla_tgt, sess_work);
@@ -6371,9 +6430,6 @@
 		case QLA_TGT_SESS_WORK_ABORT:
 			qlt_abort_work(tgt, prm);
 			break;
-		case QLA_TGT_SESS_WORK_TM:
-			qlt_tmr_work(tgt, prm);
-			break;
 		default:
 			BUG_ON(1);
 			break;
@@ -6460,7 +6516,6 @@
 	tgt->ha = ha;
 	tgt->vha = base_vha;
 	init_waitqueue_head(&tgt->waitQ);
-	INIT_LIST_HEAD(&tgt->del_sess_list);
 	spin_lock_init(&tgt->sess_work_lock);
 	INIT_WORK(&tgt->sess_work, qlt_sess_work_fn);
 	INIT_LIST_HEAD(&tgt->sess_works_list);
@@ -6505,15 +6560,15 @@
 	return 0;
 }
 
-void qlt_remove_target_resources(struct qla_hw_data *ha)
+void qla_remove_hostmap(struct qla_hw_data *ha)
 {
 	struct scsi_qla_host *node;
 	u32 key = 0;
 
-	btree_for_each_safe32(&ha->tgt.host_map, key, node)
-		btree_remove32(&ha->tgt.host_map, key);
+	btree_for_each_safe32(&ha->host_map, key, node)
+		btree_remove32(&ha->host_map, key);
 
-	btree_destroy32(&ha->tgt.host_map);
+	btree_destroy32(&ha->host_map);
 }
 
 static void qlt_lport_dump(struct scsi_qla_host *vha, u64 wwpn,
@@ -6526,7 +6581,7 @@
 }
 
 /**
- * qla_tgt_lport_register - register lport with external module
+ * qlt_lport_register - register lport with external module
  *
  * @target_lport_ptr: pointer for tcm_qla2xxx specific lport data
  * @phys_wwpn: physical port WWPN
@@ -6602,7 +6657,7 @@
 EXPORT_SYMBOL(qlt_lport_register);
 
 /**
- * qla_tgt_lport_deregister - Degister lport
+ * qlt_lport_deregister - Degister lport
  *
  * @vha:  Registered scsi_qla_host pointer
  */
@@ -6754,6 +6809,9 @@
 	mutex_init(&vha->vha_tgt.tgt_mutex);
 	mutex_init(&vha->vha_tgt.tgt_host_action_mutex);
 
+	INIT_LIST_HEAD(&vha->unknown_atio_list);
+	INIT_DELAYED_WORK(&vha->unknown_atio_work, qlt_unknown_atio_work_fn);
+
 	qlt_clear_mode(vha);
 
 	/*
@@ -6883,14 +6941,8 @@
 
 	if (ha->flags.msix_enabled) {
 		if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
-			if (IS_QLA2071(ha)) {
-				/* 4 ports Baker: Enable Interrupt Handshake */
-				icb->msix_atio = 0;
-				icb->firmware_options_2 |= cpu_to_le32(BIT_26);
-			} else {
-				icb->msix_atio = cpu_to_le16(msix->entry);
-				icb->firmware_options_2 &= cpu_to_le32(~BIT_26);
-			}
+			icb->msix_atio = cpu_to_le16(msix->entry);
+			icb->firmware_options_2 &= cpu_to_le32(~BIT_26);
 			ql_dbg(ql_dbg_init, vha, 0xf072,
 			    "Registering ICB vector 0x%x for atio que.\n",
 			    msix->entry);
@@ -7141,13 +7193,11 @@
 void
 qlt_probe_one_stage1(struct scsi_qla_host *base_vha, struct qla_hw_data *ha)
 {
-	int rc;
-
+	mutex_init(&base_vha->vha_tgt.tgt_mutex);
 	if (!QLA_TGT_MODE_ENABLED())
 		return;
 
-	if  ((ql2xenablemsix == 0) || IS_QLA83XX(ha) || IS_QLA27XX(ha) ||
-	    IS_QLA28XX(ha)) {
+	if  (ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) {
 		ISP_ATIO_Q_IN(base_vha) = &ha->mqiobase->isp25mq.atio_q_in;
 		ISP_ATIO_Q_OUT(base_vha) = &ha->mqiobase->isp25mq.atio_q_out;
 	} else {
@@ -7155,7 +7205,6 @@
 		ISP_ATIO_Q_OUT(base_vha) = &ha->iobase->isp24.atio_q_out;
 	}
 
-	mutex_init(&base_vha->vha_tgt.tgt_mutex);
 	mutex_init(&base_vha->vha_tgt.tgt_host_action_mutex);
 
 	INIT_LIST_HEAD(&base_vha->unknown_atio_list);
@@ -7164,11 +7213,6 @@
 
 	qlt_clear_mode(base_vha);
 
-	rc = btree_init32(&ha->tgt.host_map);
-	if (rc)
-		ql_log(ql_log_info, base_vha, 0xd03d,
-		    "Unable to initialize ha->host_map btree\n");
-
 	qlt_update_vp_map(base_vha, SET_VP_IDX);
 }
 
@@ -7289,21 +7333,20 @@
 	u32 key;
 	int rc;
 
-	if (!QLA_TGT_MODE_ENABLED())
-		return;
-
 	key = vha->d_id.b24;
 
 	switch (cmd) {
 	case SET_VP_IDX:
+		if (!QLA_TGT_MODE_ENABLED())
+			return;
 		vha->hw->tgt.tgt_vp_map[vha->vp_idx].vha = vha;
 		break;
 	case SET_AL_PA:
-		slot = btree_lookup32(&vha->hw->tgt.host_map, key);
+		slot = btree_lookup32(&vha->hw->host_map, key);
 		if (!slot) {
 			ql_dbg(ql_dbg_tgt_mgt, vha, 0xf018,
 			    "Save vha in host_map %p %06x\n", vha, key);
-			rc = btree_insert32(&vha->hw->tgt.host_map,
+			rc = btree_insert32(&vha->hw->host_map,
 				key, vha, GFP_ATOMIC);
 			if (rc)
 				ql_log(ql_log_info, vha, 0xd03e,
@@ -7313,17 +7356,19 @@
 		}
 		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf019,
 		    "replace existing vha in host_map %p %06x\n", vha, key);
-		btree_update32(&vha->hw->tgt.host_map, key, vha);
+		btree_update32(&vha->hw->host_map, key, vha);
 		break;
 	case RESET_VP_IDX:
+		if (!QLA_TGT_MODE_ENABLED())
+			return;
 		vha->hw->tgt.tgt_vp_map[vha->vp_idx].vha = NULL;
 		break;
 	case RESET_AL_PA:
 		ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01a,
 		   "clear vha in host_map %p %06x\n", vha, key);
-		slot = btree_lookup32(&vha->hw->tgt.host_map, key);
+		slot = btree_lookup32(&vha->hw->host_map, key);
 		if (slot)
-			btree_remove32(&vha->hw->tgt.host_map, key);
+			btree_remove32(&vha->hw->host_map, key);
 		vha->d_id.b24 = 0;
 		break;
 	}
diff --git a/scst/qla2x00t-32gbit/qla_target.h b/scst/qla2x00t-32gbit/qla_target.h
index f696ffb..9e2d8b7 100644
--- a/scst/qla2x00t-32gbit/qla_target.h
+++ b/scst/qla2x00t-32gbit/qla_target.h
@@ -143,7 +143,6 @@
 	(min(1270, ((ql) > 0) ? (QLA_TGT_DATASEGS_PER_CMD_24XX + \
 		QLA_TGT_DATASEGS_PER_CONT_24XX*((ql) - 1)) : 0))
 #endif
-#endif
 
 #define GET_TARGET_ID(ha, iocb) ((HAS_EXTENDED_IDS(ha))			\
 			 ? le16_to_cpu((iocb)->u.isp2x.target.extended)	\
@@ -204,6 +203,7 @@
 	uint8_t  reserved[2];
 	__le16	ox_id;
 } __packed;
+#define NOTIFY_ACK_FLAGS_FCSP		BIT_5
 #define NOTIFY_ACK_FLAGS_TERMINATE	BIT_3
 #define NOTIFY_ACK_SRR_FLAGS_ACCEPT	0
 #define NOTIFY_ACK_SRR_FLAGS_REJECT	1
@@ -266,11 +266,16 @@
 #define CTIO_PORT_LOGGED_OUT		0x29
 #define CTIO_PORT_CONF_CHANGED		0x2A
 #define CTIO_SRR_RECEIVED		0x45
+#define CTIO_FAST_AUTH_ERR		0x63
+#define CTIO_FAST_INCOMP_PAD_LEN	0x65
+#define CTIO_FAST_INVALID_REQ		0x66
+#define CTIO_FAST_SPI_ERR		0x67
 #endif
 
 #ifndef CTIO_RET_TYPE
 #define CTIO_RET_TYPE	0x17		/* CTIO return entry */
 #define ATIO_TYPE7 0x06 /* Accept target I/O entry for 24xx */
+#endif
 
 struct fcp_hdr {
 	uint8_t  r_ctl;
@@ -435,7 +440,16 @@
 		struct {
 			__le16	reserved1;
 			__le16 flags;
-			__le32	residual;
+			union {
+				__le32	residual;
+				struct {
+					uint8_t rsvd1;
+					uint8_t edif_flags;
+#define EF_EN_EDIF	BIT_0
+#define EF_NEW_SA	BIT_1
+					uint16_t rsvd2;
+				};
+			};
 			__le16 ox_id;
 			__le16	scsi_status;
 			__le32	relative_offset;
@@ -473,7 +487,7 @@
 	uint8_t  vp_index;
 	uint8_t  reserved1[5];
 	__le32	exchange_address;
-	__le16	reserved2;
+	__le16	edif_sa_index;
 	__le16	flags;
 	__le32	residual;
 	__le16	ox_id;
@@ -722,15 +736,11 @@
 
 int qla2x00_wait_for_hba_online(struct scsi_qla_host *);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
 /*
  * See also commit c66ac9db8d4a ("[SCSI] target: Add LIO target core v4.0.0")
  * # v2.6.38.
  */
 #include <target/target_core_base.h>
-#else
-#include "target_core_base-backport.h"
-#endif
 
 #define QLA_TGT_TIMEOUT			10	/* in seconds */
 
@@ -834,9 +844,6 @@
 	/* Count of sessions refering qla_tgt. Protected by hardware_lock. */
 	int sess_count;
 
-	/* Protected by hardware_lock */
-	struct list_head del_sess_list;
-
 	spinlock_t sess_work_lock;
 	struct list_head sess_works_list;
 	struct work_struct sess_work;
@@ -891,6 +898,7 @@
 	uint8_t cmd_type;
 	uint8_t pad[7];
 	struct se_cmd se_cmd;
+	struct list_head sess_cmd_list;
 	struct scst_cmd *scst_cmd;
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0)
 	/*
@@ -914,12 +922,12 @@
 	/* to save extra sess dereferences */
 	unsigned int conf_compl_supported:1;
 	unsigned int sg_mapped:1;
-	unsigned int free_sg:1;
 	unsigned int write_data_transferred:1;
 	unsigned int q_full:1;
 	unsigned int term_exchg:1;
 	unsigned int cmd_sent_to_fw:1;
 	unsigned int cmd_in_wq:1;
+	unsigned int edif:1;
 
 	/*
 	 * This variable may be set from outside the LIO and I/O completion
@@ -976,7 +984,6 @@
 	struct list_head sess_works_list_entry;
 
 #define QLA_TGT_SESS_WORK_ABORT	1
-#define QLA_TGT_SESS_WORK_TM	2
 	int type;
 
 	union {
@@ -1113,8 +1120,6 @@
 	struct init_cb_81xx *);
 extern void qlt_81xx_config_nvram_stage1(struct scsi_qla_host *,
 	struct nvram_81xx *);
-extern int qlt_24xx_process_response_error(struct scsi_qla_host *,
-	struct sts_entry_24xx *);
 extern void qlt_modify_vp_config(struct scsi_qla_host *,
 	struct vp_config_entry_24xx *);
 extern void qlt_probe_one_stage1(struct scsi_qla_host *, struct qla_hw_data *);
diff --git a/scst/qla2x00t-32gbit/qla_tmpl.c b/scst/qla2x00t-32gbit/qla_tmpl.c
index 8dc82cf..b0a74b0 100644
--- a/scst/qla2x00t-32gbit/qla_tmpl.c
+++ b/scst/qla2x00t-32gbit/qla_tmpl.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 #include "qla_def.h"
 #include "qla_tmpl.h"
@@ -12,33 +11,6 @@
 #define IOBASE(vha)	IOBAR(ISPREG(vha))
 #define INVALID_ENTRY ((struct qla27xx_fwdt_entry *)0xffffffffffffffffUL)
 
-/* hardware_lock assumed held. */
-static void
-qla27xx_write_remote_reg(struct scsi_qla_host *vha,
-			 u32 addr, u32 data)
-{
-	struct device_reg_24xx __iomem *reg = &vha->hw->iobase->isp24;
-
-	ql_dbg(ql_dbg_misc, vha, 0xd300,
-	       "%s: addr/data = %xh/%xh\n", __func__, addr, data);
-
-	wrt_reg_dword(&reg->iobase_addr, 0x40);
-	wrt_reg_dword(&reg->iobase_c4, data);
-	wrt_reg_dword(&reg->iobase_window, addr);
-}
-
-void
-qla27xx_reset_mpi(scsi_qla_host_t *vha)
-{
-	ql_dbg(ql_dbg_misc + ql_dbg_verbose, vha, 0xd301,
-	       "Entered %s.\n", __func__);
-
-	qla27xx_write_remote_reg(vha, 0x104050, 0x40004);
-	qla27xx_write_remote_reg(vha, 0x10405c, 0x4);
-
-	vha->hw->stat.num_mpi_reset++;
-}
-
 static inline void
 qla27xx_insert16(uint16_t value, void *buf, ulong *len)
 {
@@ -463,8 +435,13 @@
 {
 	ql_dbg(ql_dbg_misc, vha, 0xd20a,
 	    "%s: reset risc [%lx]\n", __func__, *len);
-	if (buf)
-		WARN_ON_ONCE(qla24xx_soft_reset(vha->hw) != QLA_SUCCESS);
+	if (buf) {
+		if (qla24xx_soft_reset(vha->hw) != QLA_SUCCESS) {
+			ql_dbg(ql_dbg_async, vha, 0x5001,
+			    "%s: unable to soft reset\n", __func__);
+			return INVALID_ENTRY;
+		}
+	}
 
 	return qla27xx_next_entry(ent);
 }
@@ -906,8 +883,8 @@
 	uint8_t v[] = { 0, 0, 0, 0, 0, 0 };
 
 	WARN_ON_ONCE(sscanf(qla2x00_version_str,
-			    "%hhu.%hhu.%hhu.%hhu.%hhu.%hhu",
-			    v+0, v+1, v+2, v+3, v+4, v+5) != 6);
+			    "%hhu.%hhu.%hhu.%hhu",
+			    v + 0, v + 1, v + 2, v + 3) != 4);
 
 	tmp->driver_info[0] = cpu_to_le32(
 		v[3] << 24 | v[2] << 16 | v[1] << 8 | v[0]);
@@ -956,7 +933,8 @@
 static inline int
 qla27xx_verify_template_checksum(struct qla27xx_fwdt_template *tmp)
 {
-	return qla27xx_template_checksum(tmp, tmp->template_size) == 0;
+	return qla27xx_template_checksum(tmp,
+		le32_to_cpu(tmp->template_size)) == 0;
 }
 
 static inline int
@@ -972,7 +950,7 @@
 	ulong len = 0;
 
 	if (qla27xx_fwdt_template_valid(tmp)) {
-		len = tmp->template_size;
+		len = le32_to_cpu(tmp->template_size);
 		tmp = memcpy(buf, tmp, len);
 		ql27xx_edit_template(vha, tmp);
 		qla27xx_walk_template(vha, tmp, buf, &len);
@@ -988,7 +966,7 @@
 	ulong len = 0;
 
 	if (qla27xx_fwdt_template_valid(tmp)) {
-		len = tmp->template_size;
+		len = le32_to_cpu(tmp->template_size);
 		qla27xx_walk_template(vha, tmp, NULL, &len);
 	}
 
@@ -1000,7 +978,7 @@
 {
 	struct qla27xx_fwdt_template *tmp = p;
 
-	return tmp->template_size;
+	return le32_to_cpu(tmp->template_size);
 }
 
 int
@@ -1028,22 +1006,25 @@
 qla27xx_mpi_fwdump(scsi_qla_host_t *vha, int hardware_locked)
 {
 	ulong flags = 0;
-	bool need_mpi_reset = true;
 
-#ifndef __CHECKER__
 	if (!hardware_locked)
 		spin_lock_irqsave(&vha->hw->hardware_lock, flags);
-#endif
 	if (!vha->hw->mpi_fw_dump) {
 		ql_log(ql_log_warn, vha, 0x02f3, "-> mpi_fwdump no buffer\n");
-	} else if (vha->hw->mpi_fw_dumped) {
-		ql_log(ql_log_warn, vha, 0x02f4,
-		       "-> MPI firmware already dumped (%p) -- ignoring request\n",
-		       vha->hw->mpi_fw_dump);
 	} else {
 		struct fwdt *fwdt = &vha->hw->fwdt[1];
 		ulong len;
 		void *buf = vha->hw->mpi_fw_dump;
+		bool walk_template_only = false;
+
+		if (vha->hw->mpi_fw_dumped) {
+			/* Use the spare area for any further dumps. */
+			buf += fwdt->dump_size;
+			walk_template_only = true;
+			ql_log(ql_log_warn, vha, 0x02f4,
+			       "-> MPI firmware already dumped -- dump saving to temporary buffer %p.\n",
+			       buf);
+		}
 
 		ql_log(ql_log_warn, vha, 0x02f5, "-> fwdt1 running...\n");
 		if (!fwdt->template) {
@@ -1058,9 +1039,10 @@
 			ql_log(ql_log_warn, vha, 0x02f7,
 			       "-> fwdt1 fwdump residual=%+ld\n",
 			       fwdt->dump_size - len);
-		} else {
-			need_mpi_reset = false;
 		}
+		vha->hw->stat.num_mpi_reset++;
+		if (walk_template_only)
+			goto bailout;
 
 		vha->hw->mpi_fw_dump_len = len;
 		vha->hw->mpi_fw_dumped = 1;
@@ -1072,12 +1054,8 @@
 	}
 
 bailout:
-	if (need_mpi_reset)
-		qla27xx_reset_mpi(vha);
-#ifndef __CHECKER__
 	if (!hardware_locked)
 		spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
-#endif
 }
 
 void
diff --git a/scst/qla2x00t-32gbit/qla_tmpl.h b/scst/qla2x00t-32gbit/qla_tmpl.h
index 89280b3..6e0987e 100644
--- a/scst/qla2x00t-32gbit/qla_tmpl.h
+++ b/scst/qla2x00t-32gbit/qla_tmpl.h
@@ -1,8 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 
 #ifndef __QLA_DMP27_H__
@@ -13,7 +12,7 @@
 struct __packed qla27xx_fwdt_template {
 	__le32 template_type;
 	__le32 entry_offset;
-	uint32_t template_size;
+	__le32 template_size;
 	uint32_t count;		/* borrow field for running/residual count */
 
 	__le32 entry_count;
diff --git a/scst/qla2x00t-32gbit/qla_version.h b/scst/qla2x00t-32gbit/qla_version.h
index 8ccd9ba..03f3e2c 100644
--- a/scst/qla2x00t-32gbit/qla_version.h
+++ b/scst/qla2x00t-32gbit/qla_version.h
@@ -1,15 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * QLogic Fibre Channel HBA Driver
  * Copyright (c)  2003-2014 QLogic Corporation
- *
- * See LICENSE.qla2xxx for copyright and licensing details.
  */
 /*
  * Driver version
  */
-#define QLA2XXX_VERSION      "10.01.00.25-k"
+#define QLA2XXX_VERSION      "10.02.07.900-k"
 
 #define QLA_DRIVER_MAJOR_VER	10
-#define QLA_DRIVER_MINOR_VER	1
-#define QLA_DRIVER_PATCH_VER	0
-#define QLA_DRIVER_BETA_VER	0
+#define QLA_DRIVER_MINOR_VER	2
+#define QLA_DRIVER_PATCH_VER	7
+#define QLA_DRIVER_BETA_VER	900
diff --git a/scst/qla2x00t-32gbit/target_core_base-backport.h b/scst/qla2x00t-32gbit/target_core_base-backport.h
deleted file mode 100644
index 5f2e42b..0000000
--- a/scst/qla2x00t-32gbit/target_core_base-backport.h
+++ /dev/null
@@ -1,29 +0,0 @@
-#ifndef _TARGET_CORE_BASE_BACKPORT_H_
-#define _TARGET_CORE_BASE_BACKPORT_H_
-
-#define TRANSPORT_SENSE_BUFFER 96
-
-enum se_cmd_flags_table {
-	SCF_OVERFLOW_BIT		= 0x00001000,
-	SCF_UNDERFLOW_BIT		= 0x00002000,
-};
-
-/* for sam_task_attr */
-#define TCM_SIMPLE_TAG	0x20
-#define TCM_HEAD_TAG	0x21
-#define TCM_ORDERED_TAG	0x22
-#define TCM_ACA_TAG	0x24
-
-struct se_cmd {
-	u64	tag; /* SAM command identifier aka task tag */
-	u32	se_cmd_flags;
-	u32	residual_count;
-	u64	t_task_lba;
-	int	cpuid;
-};
-
-struct se_session {
-	void	*fabric_sess_ptr;
-};
-
-#endif /* _TARGET_CORE_BASE_BACKPORT_H_ */
diff --git a/scst/qla2x00t/Makefile b/scst/qla2x00t/Makefile
index 84fb09b..dfaa980 100644
--- a/scst/qla2x00t/Makefile
+++ b/scst/qla2x00t/Makefile
@@ -48,7 +48,7 @@
 all:
 	$(MAKE) -C $(KDIR) M=$(shell pwd)				\
 	  $(shell [ -n "$(PASS_CC_TO_MAKE)" ] && echo CC="$(CC)")	\
-	  $(CONFIG_SCSI_QLA2XXX_TARGET)=CONFIG_SCSI_QLA2XXX_TARGET
+	  CONFIG_SCSI_QLA2XXX_TARGET=$(CONFIG_SCSI_QLA2XXX_TARGET)
 
 install: all
 	KDIR=$(KDIR) ../scripts/sign-modules
diff --git a/scst/qla2x00t/doc/Makefile b/scst/qla2x00t/doc/Makefile
new file mode 100644
index 0000000..069466a
--- /dev/null
+++ b/scst/qla2x00t/doc/Makefile
@@ -0,0 +1,2 @@
+check:
+	find -name '*.html' | while read f; do tidy -e < "$$f" | sed "s|^-e|$$f|"; done
diff --git a/scst/qla2x00t/doc/qla2x00t-howto.html b/scst/qla2x00t/doc/qla2x00t-howto.html
index 5f8e8a2..ded4c1a 100644
--- a/scst/qla2x00t/doc/qla2x00t-howto.html
+++ b/scst/qla2x00t/doc/qla2x00t-howto.html
@@ -63,7 +63,7 @@
   Copy the firmware image (.BIN) file to the /lib/firmware directory and
   rename it such that the kernel driver can find it. The file name that should
   be used for the firmware file depends on the RISC controller ID:
-  <table border="1">
+  <table border="1" summary="">
     <tr><th><b>ISP Model</b></th><th><b>Firmware file<br>name</b></th></tr>
     <tr><td>ISP 21XX</td><td>ql2100_fw.bin</td></tr>
     <tr><td>ISP 22XX</td><td>ql2200_fw.bin</td></tr>
@@ -231,6 +231,31 @@
   <pre>[root@proj ]# make -C scstadmin -s install</pre>
 </li>
 
+<li id="qlini_mode"> Initiator and target modes <br><br>
+  The qla2xxx_scst module has parameter "qlini_mode", which determines when initiator mode will be enabled.<br>
+  Possible values:
+<ul>
+<li>"exclusive" (default) - initiator mode will be enabled on load, disabled on enabling target mode and then on disabling target mode enabled back.</li>
+<li>"disabled" - initiator mode will never be enabled.</li>
+<li>"dual" - initiator mode will be enabled. Target mode can be activated when ready (only <b>qla2x00t-32gbit</b>).</li>
+<li>"enabled" - initiator mode will always stay enabled.</li>
+</ul>
+
+<br>
+  Usage of mode "disabled" is recommended, if you have incorrectly functioning your target's initiators, which if once seen a port in initiator mode, later refuse to see it as a target.
+<br><br>
+
+<b>qla2x00t</b> (old qlogic driver): <br><br>
+  Use mode "enabled" if you need your QLA adapters to work in both initiator and target modes at the same time.<br>
+  You can always see which modes are currently active in active_mode sysfs attribute.<br>
+  In all the modes you can at any time use sysfs attribute ini_mode_force_reverse to force enable or disable initiator mode on any particular port. Setting this attribute to 1 will reverse current status of the initiator mode from enabled to disabled and vice versa.<br>
+<br>
+<b>qla2x00t-32gbit</b> (new qlogic driver): <br><br>
+  Use mode "dual" if you need your QLA adapters to work in both initiator and target modes at the same time. In this mode, each qlogic host has individual <b>qlini_mode</b>, <b>ql2xexchoffld</b>, <b>ql2xiniexchg</b> attributes that can be changed dynamically.<br>
+  For example, you can change qlini_mode to "disabled" for specific qlogic host:
+  <pre>echo "disabled" > /sys/devices/pci0000:80/0000:80:02.0/0000:81:00.0/host1/scsi_host/host1/qlini_mode</pre>
+</li>
+
 <li id="target-mode">
   To see the device on the initiator we have to add it in the LUNs set of our
   target.<br>  We must have a LUN with number 0 (LUs numeration must not start
@@ -241,8 +266,8 @@
 
 <pre>modprobe qla2x00tgt
 find /sys/kernel/scst_tgt/targets/qla2x00t -name enabled | \
-while read f; do echo 1 >$f & done; wait
-find /sys -name issue_lip | while read f; do echo 1 >$f & done; wait
+while read f; do echo 1 >$f &amp; done; wait
+find /sys -name issue_lip | while read f; do echo 1 >$f &amp; done; wait
 {
 cat &lt;&lt;EOF
 HANDLER vdisk_fileio {
diff --git a/scst/qla2x00t/qla2x00-target/README b/scst/qla2x00t/qla2x00-target/README
index 28b1058..e16a136 100644
--- a/scst/qla2x00t/qla2x00-target/README
+++ b/scst/qla2x00t/qla2x00-target/README
@@ -1,7 +1,7 @@
 Target driver for QLogic 2[2-6]xx/8[1-3]xx Fibre Channel cards
 ==============================================================
 
-Version 3.5.0, 21 December 2020
+Version 3.7.0, 26 December 2022
 ----------------------------
 
 This is target driver for QLogic 2[2-6]xx/8[1-3]xx Fibre Channel cards.
diff --git a/scst/qla2x00t/qla2x00-target/qla2x00t.c b/scst/qla2x00t/qla2x00-target/qla2x00t.c
index 1c61b78..c6aa31e 100644
--- a/scst/qla2x00t/qla2x00-target/qla2x00t.c
+++ b/scst/qla2x00t/qla2x00-target/qla2x00t.c
@@ -197,15 +197,8 @@
 #define ENABLE_NPIV 0 /* NPIV does not work */
 
 #if ENABLE_NPIV
-#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \
-     defined(FC_VPORT_CREATE_DEFINED))
 static ssize_t q2t_add_vtarget(const char *target_name, char *params);
 static ssize_t q2t_del_vtarget(const char *target_name);
-#else
-#warning Patch scst_fc_vport_create was not applied on\
- your kernel. Adding NPIV targets using SCST sysfs interface will be disabled.
-#endif /*((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \
-	  defined(FC_VPORT_CREATE_DEFINED))*/
 #endif /* ENABLE_NPIV */
 
 /*
@@ -242,12 +235,8 @@
 	.enable_target = q2t_enable_tgt,
 	.is_target_enabled = q2t_is_tgt_enabled,
 #if ENABLE_NPIV
-#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \
-     defined(FC_VPORT_CREATE_DEFINED))
 	.add_target = q2t_add_vtarget,
 	.del_target = q2t_del_vtarget,
-#endif /*((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \
-	  defined(FC_VPORT_CREATE_DEFINED))*/
 	.add_target_parameters = "node_name, parent_host",
 #endif
 	.tgtt_attrs = q2tt_attrs,
@@ -2357,7 +2346,7 @@
 	sBUG_ON(prm->cmd->sg_cnt == 0);
 
 	prm->sg = prm->cmd->sg;
-	prm->seg_cnt = pci_map_sg(prm->tgt->vha->hw->pdev, prm->cmd->sg,
+	prm->seg_cnt = dma_map_sg(&prm->tgt->vha->hw->pdev->dev, prm->cmd->sg,
 		prm->cmd->sg_cnt, prm->cmd->dma_data_direction);
 	if (unlikely(prm->seg_cnt == 0))
 		goto out_err;
@@ -2387,7 +2376,7 @@
 static inline void q2t_unmap_sg(scsi_qla_host_t *vha, struct q2t_cmd *cmd)
 {
 	EXTRACHECKS_BUG_ON(!cmd->sg_mapped);
-	pci_unmap_sg(vha->hw->pdev, cmd->sg, cmd->sg_cnt,
+	dma_unmap_sg(&vha->hw->pdev->dev, cmd->sg, cmd->sg_cnt,
 	    cmd->dma_data_direction);
 	cmd->sg_mapped = 0;
 }
@@ -6458,8 +6447,6 @@
 }
 
 #if ENABLE_NPIV
-#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \
-     defined(FC_VPORT_CREATE_DEFINED))
 static ssize_t q2t_add_vtarget(const char *target_name, char *params)
 {
 	int res;
@@ -6571,8 +6558,6 @@
 	TRACE_EXIT_RES(res);
 	return res;
 }
-#endif /*((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \
-	  defined(FC_VPORT_CREATE_DEFINED))*/
 #endif /* ENABLE_NPIV */
 
 static int q2t_get_initiator_port_transport_id(struct scst_tgt *tgt,
diff --git a/scst/qla2x00t/qla2x00-target/qla2x00t.h b/scst/qla2x00t/qla2x00-target/qla2x00t.h
index 2ed0f4c..7993dbe 100644
--- a/scst/qla2x00t/qla2x00-target/qla2x00t.h
+++ b/scst/qla2x00t/qla2x00-target/qla2x00t.h
@@ -34,8 +34,8 @@
 
 /* Version numbers, the same as for the kernel */
 #define Q2T_VERSION(a, b, c, d)	(((a) << 030) + ((b) << 020) + (c) << 010 + (d))
-#define Q2T_VERSION_CODE	Q2T_VERSION(3, 5, 0, 0)
-#define Q2T_VERSION_STRING	"3.5.0"
+#define Q2T_VERSION_CODE	Q2T_VERSION(3, 7, 0, 0)
+#define Q2T_VERSION_STRING	"3.7.0"
 #define Q2T_PROC_VERSION_NAME	"version"
 
 #define Q2T_MAX_CDB_LEN             16
diff --git a/scst/qla2x00t/qla2x_tgt.h b/scst/qla2x00t/qla2x_tgt.h
index 0d5c37c..36e8c94 100644
--- a/scst/qla2x00t/qla2x_tgt.h
+++ b/scst/qla2x00t/qla2x_tgt.h
@@ -132,13 +132,9 @@
 }
 
 extern void qla2xxx_add_targets(void);
-#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \
-     defined(FC_VPORT_CREATE_DEFINED))
 extern size_t
 qla2xxx_add_vtarget(u64 port_name, u64 node_name, u64 parent_host);
 extern size_t qla2xxx_del_vtarget(u64 port_name);
-#endif /*((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \
-	  defined(FC_VPORT_CREATE_DEFINED))*/
 
 extern void qla_unknown_atio_work_fn(struct work_struct *work);
 
diff --git a/scst/qla2x00t/qla_attr.c b/scst/qla2x00t/qla_attr.c
index f541a68..bfa29e5 100644
--- a/scst/qla2x00t/qla_attr.c
+++ b/scst/qla2x00t/qla_attr.c
@@ -159,7 +159,7 @@
 
 	set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
 	qla2xxx_wake_dpc(base_vha);
-	qla2x00_wait_for_hba_online(vha);
+	WARN_ON_ONCE(qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS);
 
 out:
 	return size;
@@ -419,7 +419,7 @@
 		printk(KERN_INFO "Reconfiguring fabric on %ld\n",
 			vha->host_no);
 		qla2x00_configure_fabric(vha);
-		/* fall through */
+		fallthrough;
 
 	default:
 		printk(KERN_INFO "Resyncing loop on %ld\n",
@@ -441,13 +441,7 @@
 #endif /* CONFIG_SCSI_QLA2XXX_TARGET */
 
 static ssize_t
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) && \
-	(!defined(RHEL_RELEASE_CODE) || \
-	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1))
-qla2x00_sysfs_read_fw_dump(
-#else
 qla2x00_sysfs_read_fw_dump(struct file *file,
-#endif
 			   struct kobject *kobj,
 			   struct bin_attribute *bin_attr,
 			   char *buf, loff_t off, size_t count)
@@ -476,13 +470,7 @@
 }
 
 static ssize_t
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) && \
-	(!defined(RHEL_RELEASE_CODE) || \
-	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1))
-qla2x00_sysfs_write_fw_dump(
-#else
 qla2x00_sysfs_write_fw_dump(struct file *file,
-#endif
 			    struct kobject *kobj,
 			    struct bin_attribute *bin_attr,
 			    char *buf, loff_t off, size_t count)
@@ -561,13 +549,7 @@
 };
 
 static ssize_t
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) && \
-	(!defined(RHEL_RELEASE_CODE) || \
-	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1))
-qla2x00_sysfs_read_nvram(
-#else
 qla2x00_sysfs_read_nvram(struct file *file,
-#endif
 			 struct kobject *kobj,
 			 struct bin_attribute *bin_attr,
 			 char *buf, loff_t off, size_t count)
@@ -587,13 +569,7 @@
 }
 
 static ssize_t
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) && \
-	(!defined(RHEL_RELEASE_CODE) || \
-	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1))
-qla2x00_sysfs_write_nvram(
-#else
 qla2x00_sysfs_write_nvram(struct file *file,
-#endif
 			  struct kobject *kobj,
 			  struct bin_attribute *bin_attr,
 			  char *buf, loff_t off, size_t count)
@@ -662,13 +638,7 @@
 };
 
 static ssize_t
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) && \
-	(!defined(RHEL_RELEASE_CODE) || \
-	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1))
-qla2x00_sysfs_read_optrom(
-#else
 qla2x00_sysfs_read_optrom(struct file *file,
-#endif
 			  struct kobject *kobj,
 			  struct bin_attribute *bin_attr,
 			  char *buf, loff_t off, size_t count)
@@ -685,13 +655,7 @@
 }
 
 static ssize_t
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) && \
-	(!defined(RHEL_RELEASE_CODE) || \
-	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1))
-qla2x00_sysfs_write_optrom(
-#else
 qla2x00_sysfs_write_optrom(struct file *file,
-#endif
 			   struct kobject *kobj,
 			   struct bin_attribute *bin_attr,
 			   char *buf, loff_t off, size_t count)
@@ -723,13 +687,7 @@
 };
 
 static ssize_t
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) && \
-	(!defined(RHEL_RELEASE_CODE) || \
-	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1))
-qla2x00_sysfs_write_optrom_ctl(
-#else
 qla2x00_sysfs_write_optrom_ctl(struct file *file,
-#endif
 			       struct kobject *kobj,
 			       struct bin_attribute *bin_attr,
 			       char *buf, loff_t off, size_t count)
@@ -894,13 +852,7 @@
 };
 
 static ssize_t
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) && \
-	(!defined(RHEL_RELEASE_CODE) || \
-	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1))
-qla2x00_sysfs_read_vpd(
-#else
 qla2x00_sysfs_read_vpd(struct file *file,
-#endif
 		       struct kobject *kobj,
 		       struct bin_attribute *bin_attr,
 		       char *buf, loff_t off, size_t count)
@@ -922,13 +874,7 @@
 }
 
 static ssize_t
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) && \
-	(!defined(RHEL_RELEASE_CODE) || \
-	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1))
-qla2x00_sysfs_write_vpd(
-#else
 qla2x00_sysfs_write_vpd(struct file *file,
-#endif
 			struct kobject *kobj,
 			struct bin_attribute *bin_attr,
 			char *buf, loff_t off, size_t count)
@@ -982,13 +928,7 @@
 };
 
 static ssize_t
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) && \
-	(!defined(RHEL_RELEASE_CODE) || \
-	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1))
-qla2x00_sysfs_read_sfp(
-#else
 qla2x00_sysfs_read_sfp(struct file *file,
-#endif
 		       struct kobject *kobj,
 		       struct bin_attribute *bin_attr,
 		       char *buf, loff_t off, size_t count)
@@ -1050,13 +990,7 @@
 };
 
 static ssize_t
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) && \
-	(!defined(RHEL_RELEASE_CODE) || \
-	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1))
-qla2x00_sysfs_write_reset(
-#else
 qla2x00_sysfs_write_reset(struct file *file,
-#endif
 			struct kobject *kobj,
 			struct bin_attribute *bin_attr,
 			char *buf, loff_t off, size_t count)
@@ -1096,7 +1030,7 @@
 		    "Issuing MPI reset.\n");
 
 		/* Make sure FC side is not in reset */
-		qla2x00_wait_for_hba_online(vha);
+		WARN_ON_ONCE(qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS);
 
 		/* Issue MPI reset */
 		scsi_block_requests(vha->host);
@@ -1132,13 +1066,7 @@
 };
 
 static ssize_t
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) && \
-	(!defined(RHEL_RELEASE_CODE) || \
-	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1))
-qla2x00_sysfs_read_xgmac_stats(
-#else
 qla2x00_sysfs_read_xgmac_stats(struct file *file,
-#endif
 		       struct kobject *kobj,
 		       struct bin_attribute *bin_attr,
 		       char *buf, loff_t off, size_t count)
@@ -1191,13 +1119,7 @@
 };
 
 static ssize_t
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) && \
-	(!defined(RHEL_RELEASE_CODE) || \
-	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1))
-qla2x00_sysfs_read_dcbx_tlv(
-#else
 qla2x00_sysfs_read_dcbx_tlv(struct file *file,
-#endif
 		       struct kobject *kobj,
 		       struct bin_attribute *bin_attr,
 		       char *buf, loff_t off, size_t count)
@@ -1803,6 +1725,7 @@
 static DEVICE_ATTR(fw_state, S_IRUGO, qla2x00_fw_state_show, NULL);
 static DEVICE_ATTR(thermal_temp, S_IRUGO, qla2x00_thermal_temp_show, NULL);
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)
 struct device_attribute *qla2x00_host_attrs[] = {
 	&dev_attr_driver_version,
 	&dev_attr_fw_version,
@@ -1839,6 +1762,53 @@
 	&dev_attr_thermal_temp,
 	NULL,
 };
+#else
+static struct attribute *qla2x00_host_attrs[] = {
+	&dev_attr_driver_version.attr,
+	&dev_attr_fw_version.attr,
+	&dev_attr_serial_num.attr,
+	&dev_attr_isp_name.attr,
+	&dev_attr_isp_id.attr,
+	&dev_attr_model_name.attr,
+	&dev_attr_model_desc.attr,
+	&dev_attr_pci_info.attr,
+	&dev_attr_link_state.attr,
+	&dev_attr_zio.attr,
+	&dev_attr_zio_timer.attr,
+	&dev_attr_beacon.attr,
+	&dev_attr_optrom_bios_version.attr,
+	&dev_attr_optrom_efi_version.attr,
+	&dev_attr_optrom_fcode_version.attr,
+	&dev_attr_optrom_fw_version.attr,
+	&dev_attr_84xx_fw_version.attr,
+	&dev_attr_class2_enabled.attr,
+#ifdef CONFIG_SCSI_QLA2XXX_TARGET
+	&dev_attr_ini_mode_force_reverse.attr,
+	&dev_attr_resource_counts.attr,
+	&dev_attr_port_database.attr,
+#endif
+	&dev_attr_total_isp_aborts.attr,
+	&dev_attr_mpi_version.attr,
+	&dev_attr_phy_version.attr,
+	&dev_attr_flash_block_size.attr,
+	&dev_attr_vlan_id.attr,
+	&dev_attr_vn_port_mac_address.attr,
+	&dev_attr_fabric_param.attr,
+	&dev_attr_fw_state.attr,
+	&dev_attr_optrom_gold_fw_version.attr,
+	&dev_attr_thermal_temp.attr,
+	NULL,
+};
+
+static const struct attribute_group qla2x00_host_attr_group = {
+       .attrs = qla2x00_host_attrs
+};
+
+const struct attribute_group *qla2x00_host_groups[] = {
+	&qla2x00_host_attr_group,
+	NULL
+};
+#endif
 
 /* Host attributes. */
 
diff --git a/scst/qla2x00t/qla_bsg.c b/scst/qla2x00t/qla_bsg.c
index 3239e77..4053a7b 100644
--- a/scst/qla2x00t/qla_bsg.c
+++ b/scst/qla2x00t/qla_bsg.c
@@ -20,21 +20,17 @@
 {
 	job->req->errors = result;
 }
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
-static inline void set_bsg_result(struct bsg_job *job, int result)
-{
-	job->req->errors = result;
-}
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0) &&	\
-	!defined(CONFIG_SUSE_KERNEL)
-static inline void set_bsg_result(struct bsg_job *job, int result)
-{
-	scsi_req(job->req)->result = result;
-}
 #else
 static inline void set_bsg_result(struct bsg_job *job, int result)
 {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
+	job->req->errors = result;
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0) &&	\
+	!defined(CONFIG_SUSE_KERNEL)
+	scsi_req(job->req)->result = result;
+#else
 	scsi_req(blk_mq_rq_from_pdu(job))->result = result;
+#endif
 }
 #endif
 
@@ -64,11 +60,7 @@
 {
 	srb_t *sp = ptr;
 	struct scsi_qla_host *vha = (scsi_qla_host_t *)data;
-#ifndef NEW_LIBFC_API
-	struct fc_bsg_job *bsg_job = sp->u.bsg_job;
-#else
-	struct bsg_job *bsg_job = sp->u.bsg_job;
-#endif
+	BSG_JOB_TYPE *bsg_job = sp->u.bsg_job;
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 
 	bsg_reply->result = res;
@@ -81,11 +73,7 @@
 {
 	srb_t *sp = ptr;
 	struct scsi_qla_host *vha = (scsi_qla_host_t *)data;
-#ifndef NEW_LIBFC_API
-	struct fc_bsg_job *bsg_job = sp->u.bsg_job;
-#else
-	struct bsg_job *bsg_job = sp->u.bsg_job;
-#endif
+	BSG_JOB_TYPE *bsg_job = sp->u.bsg_job;
 	struct qla_hw_data *ha = vha->hw;
 
 	dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
@@ -155,12 +143,7 @@
 	return ret;
 }
 
-static int
-#ifndef NEW_LIBFC_API
-qla24xx_proc_fcp_prio_cfg_cmd(struct fc_bsg_job *bsg_job)
-#else
-qla24xx_proc_fcp_prio_cfg_cmd(struct bsg_job *bsg_job)
-#endif
+static int qla24xx_proc_fcp_prio_cfg_cmd(BSG_JOB_TYPE *bsg_job)
 {
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
 	struct fc_bsg_request *bsg_request = bsg_job->request;
@@ -292,12 +275,7 @@
 	return ret;
 }
 
-static int
-#ifndef NEW_LIBFC_API
-qla2x00_process_els(struct fc_bsg_job *bsg_job)
-#else
-qla2x00_process_els(struct bsg_job *bsg_job)
-#endif
+static int qla2x00_process_els(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 	struct fc_rport *rport;
@@ -308,7 +286,7 @@
 	srb_t *sp;
 	const char *type;
 	int req_sg_cnt, rsp_sg_cnt;
-	int rval =  (DRIVER_ERROR << 16);
+	int rval = DID_ERROR << 16;
 	uint16_t nextlid = 0;
 
 #ifdef __COVERITY__
@@ -479,12 +457,7 @@
 	return iocbs;
 }
 
-static int
-#ifndef NEW_LIBFC_API
-qla2x00_process_ct(struct fc_bsg_job *bsg_job)
-#else
-qla2x00_process_ct(struct bsg_job *bsg_job)
-#endif
+static int qla2x00_process_ct(BSG_JOB_TYPE *bsg_job)
 {
 	srb_t *sp;
 #ifndef NEW_LIBFC_API
@@ -495,7 +468,7 @@
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 	scsi_qla_host_t *vha = shost_priv(host);
 	struct qla_hw_data *ha = vha->hw;
-	int rval = (DRIVER_ERROR << 16);
+	int rval = DID_ERROR << 16;
 	int req_sg_cnt, rsp_sg_cnt;
 	uint16_t loop_id;
 	struct fc_port *fcport;
@@ -708,12 +681,7 @@
 	return rval;
 }
 
-static int
-#ifndef NEW_LIBFC_API
-qla2x00_process_loopback(struct fc_bsg_job *bsg_job)
-#else
-qla2x00_process_loopback(struct bsg_job *bsg_job)
-#endif
+static int qla2x00_process_loopback(BSG_JOB_TYPE *bsg_job)
 {
 #ifndef NEW_LIBFC_API
 	struct Scsi_Host *host = bsg_job->shost;
@@ -949,12 +917,7 @@
 	return rval;
 }
 
-static int
-#ifndef NEW_LIBFC_API
-qla84xx_reset(struct fc_bsg_job *bsg_job)
-#else
-qla84xx_reset(struct bsg_job *bsg_job)
-#endif
+static int qla84xx_reset(BSG_JOB_TYPE *bsg_job)
 {
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
 	struct fc_bsg_request *bsg_request = bsg_job->request;
@@ -994,12 +957,7 @@
 	return rval;
 }
 
-static int
-#ifndef NEW_LIBFC_API
-qla84xx_updatefw(struct fc_bsg_job *bsg_job)
-#else
-qla84xx_updatefw(struct bsg_job *bsg_job)
-#endif
+static int qla84xx_updatefw(BSG_JOB_TYPE *bsg_job)
 {
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
 	struct fc_bsg_request *bsg_request = bsg_job->request;
@@ -1115,12 +1073,7 @@
 	return rval;
 }
 
-static int
-#ifndef NEW_LIBFC_API
-qla84xx_mgmt_cmd(struct fc_bsg_job *bsg_job)
-#else
-qla84xx_mgmt_cmd(struct bsg_job *bsg_job)
-#endif
+static int qla84xx_mgmt_cmd(BSG_JOB_TYPE *bsg_job)
 {
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
 	struct fc_bsg_request *bsg_request = bsg_job->request;
@@ -1324,12 +1277,7 @@
 	return rval;
 }
 
-static int
-#ifndef NEW_LIBFC_API
-qla24xx_iidma(struct fc_bsg_job *bsg_job)
-#else
-qla24xx_iidma(struct bsg_job *bsg_job)
-#endif
+static int qla24xx_iidma(BSG_JOB_TYPE *bsg_job)
 {
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
 	struct fc_bsg_request *bsg_request = bsg_job->request;
@@ -1423,13 +1371,8 @@
 	return rval;
 }
 
-static int
-#ifndef NEW_LIBFC_API
-qla2x00_optrom_setup(struct fc_bsg_job *bsg_job, scsi_qla_host_t *vha,
-#else
-qla2x00_optrom_setup(struct bsg_job *bsg_job, scsi_qla_host_t *vha,
-#endif
-    uint8_t is_update)
+static int qla2x00_optrom_setup(BSG_JOB_TYPE *bsg_job, scsi_qla_host_t *vha,
+				uint8_t is_update)
 {
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 	uint32_t start = 0;
@@ -1497,12 +1440,7 @@
 	return 0;
 }
 
-static int
-#ifndef NEW_LIBFC_API
-qla2x00_read_optrom(struct fc_bsg_job *bsg_job)
-#else
-qla2x00_read_optrom(struct bsg_job *bsg_job)
-#endif
+static int qla2x00_read_optrom(BSG_JOB_TYPE *bsg_job)
 {
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
@@ -1534,12 +1472,7 @@
 	return rval;
 }
 
-static int
-#ifndef NEW_LIBFC_API
-qla2x00_update_optrom(struct fc_bsg_job *bsg_job)
-#else
-qla2x00_update_optrom(struct bsg_job *bsg_job)
-#endif
+static int qla2x00_update_optrom(BSG_JOB_TYPE *bsg_job)
 {
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
@@ -1570,12 +1503,7 @@
 	return rval;
 }
 
-static int
-#ifndef NEW_LIBFC_API
-qla2x00_update_fru_versions(struct fc_bsg_job *bsg_job)
-#else
-qla2x00_update_fru_versions(struct bsg_job *bsg_job)
-#endif
+static int qla2x00_update_fru_versions(BSG_JOB_TYPE *bsg_job)
 {
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
@@ -1626,12 +1554,7 @@
 	return 0;
 }
 
-static int
-#ifndef NEW_LIBFC_API
-qla2x00_read_fru_status(struct fc_bsg_job *bsg_job)
-#else
-qla2x00_read_fru_status(struct bsg_job *bsg_job)
-#endif
+static int qla2x00_read_fru_status(BSG_JOB_TYPE *bsg_job)
 {
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
@@ -1680,12 +1603,7 @@
 	return 0;
 }
 
-static int
-#ifndef NEW_LIBFC_API
-qla2x00_write_fru_status(struct fc_bsg_job *bsg_job)
-#else
-qla2x00_write_fru_status(struct bsg_job *bsg_job)
-#endif
+static int qla2x00_write_fru_status(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
 	struct Scsi_Host *host = fc_bsg_to_shost(bsg_job);
@@ -1730,12 +1648,7 @@
 	return 0;
 }
 
-static int
-#ifndef NEW_LIBFC_API
-qla2x00_process_vendor_specific(struct fc_bsg_job *bsg_job)
-#else
-qla2x00_process_vendor_specific(struct bsg_job *bsg_job)
-#endif
+static int qla2x00_process_vendor_specific(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
@@ -1783,12 +1696,7 @@
 	}
 }
 
-int
-#ifndef NEW_LIBFC_API
-qla24xx_bsg_request(struct fc_bsg_job *bsg_job)
-#else
-qla24xx_bsg_request(struct bsg_job *bsg_job)
-#endif
+int qla24xx_bsg_request(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
@@ -1849,12 +1757,7 @@
 	return ret;
 }
 
-int
-#ifndef NEW_LIBFC_API
-qla24xx_bsg_timeout(struct fc_bsg_job *bsg_job)
-#else
-qla24xx_bsg_timeout(struct bsg_job *bsg_job)
-#endif
+int qla24xx_bsg_timeout(BSG_JOB_TYPE *bsg_job)
 {
 	struct fc_bsg_request *bsg_request = bsg_job->request;
 	struct fc_bsg_reply *bsg_reply = bsg_job->reply;
diff --git a/scst/qla2x00t/qla_bsg.h b/scst/qla2x00t/qla_bsg.h
index 6a6725e..a680434 100644
--- a/scst/qla2x00t/qla_bsg.h
+++ b/scst/qla2x00t/qla_bsg.h
@@ -121,7 +121,7 @@
 	uint16_t rsrvd;
 	struct qla84_mgmt_param mgmtp;/* parameters for cmd */
 	uint32_t len; /* bytes in payload following this struct */
-	uint8_t payload[0]; /* payload for cmd */
+	uint8_t payload[]; /* payload for cmd */
 };
 
 struct qla_bsg_a84_mgmt {
diff --git a/scst/qla2x00t/qla_def.h b/scst/qla2x00t/qla_def.h
index 049caca..19bdec1 100644
--- a/scst/qla2x00t/qla_def.h
+++ b/scst/qla2x00t/qla_def.h
@@ -31,19 +31,15 @@
 	defined(CONFIG_SUSE_KERNEL) && \
 	LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
 #define NEW_LIBFC_API
+#define BSG_JOB_TYPE struct bsg_job
+#else
+#define BSG_JOB_TYPE struct fc_bsg_job
 #endif
 
 #ifdef NEW_LIBFC_API
 #include <linux/bsg-lib.h>
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
-#error
-#error ***This version of qla2xxx does not support distributions based on***
-#error ***kernels less than 2.6.32.***
-#error
-#endif
-
 #include <scsi/scsi.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_device.h>
@@ -3193,8 +3189,6 @@
 
 #define        QLA_DSDS_PER_IOCB       37
 
-#define CMD_SP(Cmnd)           ((Cmnd)->SCp.ptr)
-
 #define QLA_SG_ALL     1024
 
 enum nexus_wait_type {
diff --git a/scst/qla2x00t/qla_gbl.h b/scst/qla2x00t/qla_gbl.h
index 7b94d60..936bc2d 100644
--- a/scst/qla2x00t/qla_gbl.h
+++ b/scst/qla2x00t/qla_gbl.h
@@ -496,7 +496,11 @@
  * Global Function Prototypes in qla_attr.c source file.
  */
 struct device_attribute;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)
 extern struct device_attribute *qla2x00_host_attrs[];
+#else
+extern const struct attribute_group *qla2x00_host_groups[];
+#endif
 struct fc_function_template;
 extern struct fc_function_template qla2xxx_transport_functions;
 extern struct fc_function_template qla2xxx_transport_vport_functions;
@@ -605,13 +609,8 @@
 extern void qla82xx_clear_pending_mbx(scsi_qla_host_t *);
 
 /* BSG related functions */
-#ifdef NEW_LIBFC_API
-extern int qla24xx_bsg_request(struct bsg_job *);
-extern int qla24xx_bsg_timeout(struct bsg_job *);
-#else
-extern int qla24xx_bsg_request(struct fc_bsg_job *);
-extern int qla24xx_bsg_timeout(struct fc_bsg_job *);
-#endif
+extern int qla24xx_bsg_request(BSG_JOB_TYPE *);
+extern int qla24xx_bsg_timeout(BSG_JOB_TYPE *);
 extern int qla84xx_reset_chip(scsi_qla_host_t *, uint16_t);
 extern int qla2x00_issue_iocb_timeout(scsi_qla_host_t *, void *,
 	dma_addr_t, size_t, uint32_t);
diff --git a/scst/qla2x00t/qla_gs.c b/scst/qla2x00t/qla_gs.c
index 4eedcec..ec76353 100644
--- a/scst/qla2x00t/qla_gs.c
+++ b/scst/qla2x00t/qla_gs.c
@@ -1015,7 +1015,7 @@
 }
 
 /**
- * qla2x00_snd_rft_id() - SNS Register FC-4 TYPEs (RFT_ID) supported by the HBA.
+ * qla2x00_sns_rft_id() - SNS Register FC-4 TYPEs (RFT_ID) supported by the HBA.
  * @vha: HA context
  *
  * This command uses the old Exectute SNS Command mailbox routine.
@@ -1251,7 +1251,7 @@
 }
 
 /**
- * qla2x00_prep_ct_req() - Prepare common CT request fields for SNS query.
+ * qla2x00_prep_ct_fdmi_req() - Prepare common CT request fields for SNS query.
  * @p: CT request buffer
  * @cmd: GS command
  * @rsp_size: response size in bytes
diff --git a/scst/qla2x00t/qla_iocb.c b/scst/qla2x00t/qla_iocb.c
index fe327ea..7584de9 100644
--- a/scst/qla2x00t/qla_iocb.c
+++ b/scst/qla2x00t/qla_iocb.c
@@ -504,7 +504,7 @@
 EXPORT_SYMBOL(qla2x00_start_iocbs);
 
 /**
- * qla2x00_marker() - Send a marker IOCB to the firmware.
+ * __qla2x00_marker() - Send a marker IOCB to the firmware.
  * @vha: HA context
  * @req: Request queue
  * @rsp: ...
@@ -1875,7 +1875,7 @@
 {
 	struct scsi_cmnd *cmd = GET_CMD_SP(sp);
 	struct qla_hw_data *ha = sp->fcport->vha->hw;
-	int affinity = scst_blk_rq_cpu(cmd->request);
+	int affinity = scst_blk_rq_cpu(scsi_cmd_to_rq(cmd));
 
 	if (ha->flags.cpu_affinity_enabled && affinity >= 0 &&
 		affinity < ha->max_rsp_queues - 1)
@@ -2099,12 +2099,8 @@
 static void
 qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
 {
-#ifndef NEW_LIBFC_API
-	struct fc_bsg_job *bsg_job = sp->u.bsg_job;
-#else
-	struct bsg_job *bsg_job = sp->u.bsg_job;
+	BSG_JOB_TYPE *bsg_job = sp->u.bsg_job;
 	struct fc_bsg_request *bsg_request = bsg_job->request;
-#endif
 
 	els_iocb->entry_type = ELS_IOCB_TYPE;
 	els_iocb->entry_count = 1;
@@ -2119,13 +2115,8 @@
 
 	els_iocb->opcode =
 	    sp->type == SRB_ELS_CMD_RPT ?
-#ifndef NEW_LIBFC_API
-	    bsg_job->request->rqst_data.r_els.els_code :
-	    bsg_job->request->rqst_data.h_els.command_code;
-#else
 	    bsg_request->rqst_data.r_els.els_code :
 	    bsg_request->rqst_data.h_els.command_code;
-#endif
 	els_iocb->port_id[0] = sp->fcport->d_id.b.al_pa;
 	els_iocb->port_id[1] = sp->fcport->d_id.b.area;
 	els_iocb->port_id[2] = sp->fcport->d_id.b.domain;
@@ -2160,11 +2151,7 @@
 	uint16_t tot_dsds;
 	scsi_qla_host_t *vha = sp->fcport->vha;
 	struct qla_hw_data *ha = vha->hw;
-#ifndef NEW_LIBFC_API
-	struct fc_bsg_job *bsg_job = sp->u.bsg_job;
-#else
-	struct bsg_job *bsg_job = sp->u.bsg_job;
-#endif
+	BSG_JOB_TYPE *bsg_job = sp->u.bsg_job;
 	int loop_iterartion = 0;
 	int entry_count = 1;
 
@@ -2239,11 +2226,7 @@
 	uint16_t tot_dsds;
 	scsi_qla_host_t *vha = sp->fcport->vha;
 	struct qla_hw_data *ha = vha->hw;
-#ifndef NEW_LIBFC_API
-	struct fc_bsg_job *bsg_job = sp->u.bsg_job;
-#else
-	struct bsg_job *bsg_job = sp->u.bsg_job;
-#endif
+	BSG_JOB_TYPE *bsg_job = sp->u.bsg_job;
 	int loop_iterartion = 0;
 	int entry_count = 1;
 
diff --git a/scst/qla2x00t/qla_isr.c b/scst/qla2x00t/qla_isr.c
index bc5b4bf..d5b1181 100644
--- a/scst/qla2x00t/qla_isr.c
+++ b/scst/qla2x00t/qla_isr.c
@@ -885,7 +885,7 @@
 	case MBA_CHG_IN_CONNECTION:	/* Change in connection mode */
 		if (IS_QLA2100(ha))
 			break;
-		/* fall through */
+		fallthrough;
 	case MBA_RESET:			/* Reset */
 	case MBA_SYSTEM_ERR:		/* System Error */
 	case MBA_REQ_TRANSFER_ERR:	/* Request Transfer Error */
@@ -1083,12 +1083,8 @@
 	const char func[] = "CT_IOCB";
 	const char *type;
 	srb_t *sp;
-#ifndef NEW_LIBFC_API
-	struct fc_bsg_job *bsg_job;
-#else
-	struct bsg_job *bsg_job;
+	BSG_JOB_TYPE *bsg_job;
 	struct fc_bsg_reply *bsg_reply;
-#endif
 	uint16_t comp_status;
 	int res;
 
@@ -1097,9 +1093,7 @@
 		return;
 
 	bsg_job = sp->u.bsg_job;
-#ifdef NEW_LIBFC_API
 	bsg_reply = bsg_job->reply;
-#endif
 
 	type = "ct pass-through";
 
@@ -1108,52 +1102,32 @@
 	/* return FC_CTELS_STATUS_OK and leave the decoding of the ELS/CT
 	 * fc payload  to the caller
 	 */
-#ifndef NEW_LIBFC_API
-	bsg_job->reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK;
-#else
 	bsg_reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK;
-#endif
 	bsg_job->reply_len = sizeof(struct fc_bsg_reply);
 
 	if (comp_status != CS_COMPLETE) {
 		if (comp_status == CS_DATA_UNDERRUN) {
 			res = DID_OK << 16;
-#ifndef NEW_LIBFC_API
-			bsg_job->reply->reply_payload_rcv_len =
-#else
 			bsg_reply->reply_payload_rcv_len =
-#endif
 			    le16_to_cpu(((sts_entry_t *)pkt)->rsp_info_len);
 
 			ql_log(ql_log_warn, vha, 0x5048,
 			    "CT pass-through-%s error "
 			    "comp_status-status=0x%x total_byte = 0x%x.\n",
 			    type, comp_status,
-#ifndef NEW_LIBFC_API
-			    bsg_job->reply->reply_payload_rcv_len);
-#else
 			    bsg_reply->reply_payload_rcv_len);
-#endif
 		} else {
 			ql_log(ql_log_warn, vha, 0x5049,
 			    "CT pass-through-%s error "
 			    "comp_status-status=0x%x.\n", type, comp_status);
 			res = DID_ERROR << 16;
-#ifndef NEW_LIBFC_API
-			bsg_job->reply->reply_payload_rcv_len = 0;
-#else
 			bsg_reply->reply_payload_rcv_len = 0;
-#endif
 		}
 		ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5035,
 		    (uint8_t *)pkt, sizeof(*pkt));
 	} else {
 		res =  DID_OK << 16;
-#ifndef NEW_LIBFC_API
-		bsg_job->reply->reply_payload_rcv_len =
-#else
 		bsg_reply->reply_payload_rcv_len =
-#endif
 		    bsg_job->reply_payload.payload_len;
 		bsg_job->reply_len = 0;
 	}
@@ -1167,12 +1141,8 @@
 	const char func[] = "ELS_CT_IOCB";
 	const char *type;
 	srb_t *sp;
-#ifndef NEW_LIBFC_API
-	struct fc_bsg_job *bsg_job;
-#else
-	struct bsg_job *bsg_job;
+	BSG_JOB_TYPE *bsg_job;
 	struct fc_bsg_reply *bsg_reply;
-#endif
 	uint16_t comp_status;
 	uint32_t fw_status[3];
 	uint8_t *fw_sts_ptr;
@@ -1181,9 +1151,7 @@
 	if (!sp)
 		return;
 	bsg_job = sp->u.bsg_job;
-#ifdef NEW_LIBFC_API
 	bsg_reply = bsg_job->reply;
-#endif
 
 	type = NULL;
 	switch (sp->type) {
@@ -1207,20 +1175,12 @@
 	/* return FC_CTELS_STATUS_OK and leave the decoding of the ELS/CT
 	 * fc payload  to the caller
 	 */
-#ifndef NEW_LIBFC_API
-	bsg_job->reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK;
-#else
 	bsg_reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK;
-#endif
 	bsg_job->reply_len = sizeof(struct fc_bsg_reply) + sizeof(fw_status);
 
 	if (comp_status != CS_COMPLETE) {
 		if (comp_status == CS_DATA_UNDERRUN) {
-#ifndef NEW_LIBFC_API
-			bsg_job->reply->reply_payload_rcv_len =
-#else
 			bsg_reply->reply_payload_rcv_len =
-#endif
 			    le16_to_cpu(((struct els_sts_entry_24xx *)pkt)->total_byte_count);
 
 			ql_dbg(ql_dbg_user, vha, 0x503f,
@@ -1241,11 +1201,7 @@
 				pkt)->error_subcode_1),
 			    le16_to_cpu(((struct els_sts_entry_24xx *)
 				    pkt)->error_subcode_2));
-#ifndef NEW_LIBFC_API
-			bsg_job->reply->reply_payload_rcv_len = 0;
-#else
 			bsg_reply->reply_payload_rcv_len = 0;
-#endif
 			fw_sts_ptr = bsg_job_sense(bsg_job) +
 				sizeof(struct fc_bsg_reply);
 			memcpy(fw_sts_ptr, fw_status, sizeof(fw_status));
@@ -1253,11 +1209,7 @@
 		ql_dump_buffer(ql_dbg_user + ql_dbg_buffer, vha, 0x5056,
 				(uint8_t *)pkt, sizeof(*pkt));
 	} else {
-#ifndef NEW_LIBFC_API
-		bsg_job->reply->reply_payload_rcv_len = bsg_job->reply_payload.payload_len;
-#else
 		bsg_reply->reply_payload_rcv_len = bsg_job->reply_payload.payload_len;
-#endif
 		bsg_job->reply_len = 0;
 	}
 	sp->done(vha, sp, 0);
@@ -1667,31 +1619,22 @@
 
 	/* check guard */
 	if (e_guard != a_guard) {
-		scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
-		    0x10, 0x1);
-		set_driver_byte(cmd, DRIVER_SENSE);
+		scsi_build_sense(cmd, 1, ILLEGAL_REQUEST, 0x10, 0x1);
 		set_host_byte(cmd, DID_ABORT);
-		cmd->result |= SAM_STAT_CHECK_CONDITION << 1;
 		return 1;
 	}
 
 	/* check ref tag */
 	if (e_ref_tag != a_ref_tag) {
-		scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
-		    0x10, 0x3);
-		set_driver_byte(cmd, DRIVER_SENSE);
+		scsi_build_sense(cmd, 1, ILLEGAL_REQUEST, 0x10, 0x3);
 		set_host_byte(cmd, DID_ABORT);
-		cmd->result |= SAM_STAT_CHECK_CONDITION << 1;
 		return 1;
 	}
 
 	/* check appl tag */
 	if (e_app_tag != a_app_tag) {
-		scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
-		    0x10, 0x2);
-		set_driver_byte(cmd, DRIVER_SENSE);
+		scsi_build_sense(cmd, 1, ILLEGAL_REQUEST, 0x10, 0x2);
 		set_host_byte(cmd, DID_ABORT);
-		cmd->result |= SAM_STAT_CHECK_CONDITION << 1;
 		return 1;
 	}
 
@@ -2251,7 +2194,7 @@
 		case ABTS_RECV_24XX:
 			/* ensure that the ATIO queue is empty */
 			qla24xx_process_atio_queue(vha);
-			/* fall through */
+			fallthrough;
 		case ABTS_RESP_24XX:
 		case CTIO_TYPE7:
 		case NOTIFY_ACK_TYPE:
diff --git a/scst/qla2x00t/qla_mbx.c b/scst/qla2x00t/qla_mbx.c
index 202a87e..86133c9 100644
--- a/scst/qla2x00t/qla_mbx.c
+++ b/scst/qla2x00t/qla_mbx.c
@@ -1819,7 +1819,7 @@
     uint16_t cmd_size, size_t buf_size)
 {
 	int rval;
-	mbx_cmd_t mc;
+	mbx_cmd_t mc = {};
 	mbx_cmd_t *mcp = &mc;
 
 	ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x105d,
diff --git a/scst/qla2x00t/qla_nx.c b/scst/qla2x00t/qla_nx.c
index c08decc..780377f 100644
--- a/scst/qla2x00t/qla_nx.c
+++ b/scst/qla2x00t/qla_nx.c
@@ -1181,11 +1181,7 @@
 	ql_log(ql_log_info, vha, 0x0072,
 	    "%d CRB init values found in ROM.\n", n);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)
 	buf = kmalloc_array(n, sizeof(struct crb_addr_pair), GFP_KERNEL);
-#else
-	buf = kmalloc(n * sizeof(struct crb_addr_pair), GFP_KERNEL);
-#endif
 	if (buf == NULL) {
 		ql_log(ql_log_fatal, vha, 0x010c,
 		    "Unable to allocate memory.\n");
diff --git a/scst/qla2x00t/qla_os.c b/scst/qla2x00t/qla_os.c
index 05e6e8d..92d5b24 100644
--- a/scst/qla2x00t/qla_os.c
+++ b/scst/qla2x00t/qla_os.c
@@ -13,10 +13,6 @@
 #include <linux/mutex.h>
 #include <linux/version.h>
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
-#error Kernel 2.6.26 or above is needed to build the qla2x00t driver
-#endif
-
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsicam.h>
 #include <scsi/scsi_transport.h>
@@ -249,38 +245,28 @@
 static int qla2xxx_scan_finished(struct Scsi_Host *, unsigned long time);
 static void qla2xxx_scan_start(struct Scsi_Host *);
 static void qla2xxx_slave_destroy(struct scsi_device *);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)
 static int qla2xxx_queuecommand_lck(struct scsi_cmnd *cmd,
 		void (*fn)(struct scsi_cmnd *));
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
-static DEF_SCSI_QCMD(qla2xxx_queuecommand);
+#else
+static int qla2xxx_queuecommand_lck(struct scsi_cmnd *cmd);
 #endif
+static DEF_SCSI_QCMD(qla2xxx_queuecommand);
 static int qla2xxx_eh_abort(struct scsi_cmnd *);
 static int qla2xxx_eh_device_reset(struct scsi_cmnd *);
 static int qla2xxx_eh_target_reset(struct scsi_cmnd *);
 static int qla2xxx_eh_bus_reset(struct scsi_cmnd *);
 static int qla2xxx_eh_host_reset(struct scsi_cmnd *);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && \
-	!defined(CONFIG_SUSE_KERNEL) && \
-	(!defined(RHEL_RELEASE_CODE) || \
-	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1))
-static int qla2x00_change_queue_depth(struct scsi_device *, int);
-#else
-static int qla2x00_change_queue_depth(struct scsi_device *, int, int);
-#endif
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
+static int qla2x00_change_queue_depth(struct scsi_device *, int, int);
 static int qla2x00_change_queue_type(struct scsi_device *, int);
 #endif
 
 struct scsi_host_template qla2xxx_driver_template = {
 	.module			= THIS_MODULE,
 	.name			= QLA2XXX_DRIVER_NAME,
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
-	.queuecommand		= qla2xxx_queuecommand_lck,
-#else
 	.queuecommand		= qla2xxx_queuecommand,
-#endif
 
 	.eh_abort_handler	= qla2xxx_eh_abort,
 	.eh_device_reset_handler = qla2xxx_eh_device_reset,
@@ -306,7 +292,11 @@
 	.sg_tablesize		= SG_ALL,
 
 	.max_sectors		= 0xFFFF,
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)
 	.shost_attrs		= qla2x00_host_attrs,
+#else
+	.shost_groups		= qla2x00_host_groups,
+#endif
 #ifdef CONFIG_SCSI_QLA2XXX_TARGET
 	.supported_mode		= MODE_INITIATOR | MODE_TARGET,
 #endif /* CONFIG_SCSI_QLA2XXX_TARGET */
@@ -460,7 +450,7 @@
 			    "Failed to create request queue.\n");
 			goto fail;
 		}
-		ha->wq = create_workqueue("qla2xxx_wq");
+		ha->wq = alloc_workqueue("qla2xxx_wq", WQ_MEM_RECLAIM, 1);
 		vha->req = ha->req_q_map[req];
 		options |= BIT_1;
 		for (ques = 1; ques < ha->max_rsp_queues; ques++) {
@@ -654,7 +644,7 @@
 		ctx1 = NULL;
 	}
 
-	CMD_SP(cmd) = NULL;
+	sp->type = 0;
 	mempool_free(sp, ha->srb_mempool);
 }
 
@@ -674,11 +664,15 @@
 		return;
 
 	qla2x00_sp_free_dma(ha, sp);
-	cmd->scsi_done(cmd);
+	scsi_done(cmd);
 }
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)
 static int
 qla2xxx_queuecommand_lck(struct scsi_cmnd *cmd, void (*done)(struct scsi_cmnd *))
+#else
+static int qla2xxx_queuecommand_lck(struct scsi_cmnd *cmd)
+#endif
 {
 	scsi_qla_host_t *vha = shost_priv(cmd->device->host);
 	fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
@@ -756,8 +750,9 @@
 	sp->u.scmd.cmd = cmd;
 	sp->type = SRB_SCSI_CMD;
 	atomic_set(&sp->ref_count, 1);
-	CMD_SP(cmd) = (void *)sp;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)
 	cmd->scsi_done = done;
+#endif
 	sp->free = qla2x00_sp_free_dma;
 	sp->done = qla2x00_sp_compl;
 
@@ -785,7 +780,7 @@
 
 qc24_fail_command:
 	spin_lock_irq(vha->host->host_lock);
-	done(cmd);
+	scsi_done(cmd);
 
 	return 0;
 }
@@ -811,6 +806,7 @@
 	unsigned long wait_iter = ABORT_WAIT_ITER;
 	scsi_qla_host_t *vha = shost_priv(cmd->device->host);
 	struct qla_hw_data *ha = vha->hw;
+	srb_t *sp = scsi_cmd_priv(cmd);
 	int ret = QLA_SUCCESS;
 
 	if (unlikely(pci_channel_offline(ha->pdev)) || ha->flags.eeh_busy) {
@@ -819,10 +815,10 @@
 		return ret;
 	}
 
-	while (CMD_SP(cmd) && wait_iter--) {
+	while (sp->type && wait_iter--) {
 		msleep(ABORT_POLLING_PERIOD);
 	}
-	if (CMD_SP(cmd))
+	if (sp->type)
 		ret = QLA_FUNCTION_FAILED;
 
 	return ret;
@@ -949,25 +945,6 @@
 	atomic_inc(&sp->ref_count);
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && !defined(CONFIG_SUSE_KERNEL)
-static void
-qla2x00_block_error_handler(struct scsi_cmnd *cmnd)
-{
-	struct Scsi_Host *shost = cmnd->device->host;
-	struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
-	unsigned long flags;
-
-	spin_lock_irqsave(shost->host_lock, flags);
-	while (rport->port_state == FC_PORTSTATE_BLOCKED) {
-		spin_unlock_irqrestore(shost->host_lock, flags);
-		msleep(1000);
-		spin_lock_irqsave(shost->host_lock, flags);
-	}
-	spin_unlock_irqrestore(shost->host_lock, flags);
-	return;
-}
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && !defined(CONFIG_SUSE_KERNEL) */
-
 /**************************************************************************
 * qla2xxx_eh_abort
 *
@@ -994,23 +971,17 @@
 	int wait = 0;
 	struct qla_hw_data *ha = vha->hw;
 
-	if (!CMD_SP(cmd))
-		return SUCCESS;
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && !defined(CONFIG_SUSE_KERNEL)
-	qla2x00_block_error_handler(cmd);
-#else
 	ret = fc_block_scsi_eh(cmd);
 	if (ret != SUCCESS && ret != 0)
 		return ret;
-#endif
+
 	ret = SUCCESS;
 
 	id = cmd->device->id;
 	lun = cmd->device->lun;
 
 	spin_lock_irqsave(&ha->hardware_lock, flags);
-	sp = (srb_t *) CMD_SP(cmd);
+	sp = scsi_cmd_priv(cmd);
 	if (!sp) {
 		spin_unlock_irqrestore(&ha->hardware_lock, flags);
 		return SUCCESS;
@@ -1038,7 +1009,7 @@
 	spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
 	/* Did the command return during mailbox execution? */
-	if (ret == FAILED && !CMD_SP(cmd))
+	if (ret == FAILED && !sp->type)
 		ret = SUCCESS;
 
 	/* Wait for the command to be returned. */
@@ -1126,13 +1097,9 @@
 		return FAILED;
 	}
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && !defined(CONFIG_SUSE_KERNEL)
-	qla2x00_block_error_handler(cmd);
-#else
 	err = fc_block_scsi_eh(cmd);
 	if (err != SUCCESS && err != 0)
 		return err;
-#endif
 
 	ql_log(ql_log_info, vha, 0x8009,
 	    "%s RESET ISSUED nexus=%ld:%d:%lld cmd=%p.\n", name, vha->host_no,
@@ -1146,7 +1113,7 @@
 	}
 	err = 2;
 	if (do_reset(fcport, cmd->device->lun,
-		     scst_blk_rq_cpu(cmd->request) + 1)
+		     scst_blk_rq_cpu(scsi_cmd_to_rq(cmd)) + 1)
 		!= QLA_SUCCESS) {
 		ql_log(ql_log_warn, vha, 0x800c,
 		    "do_reset failed for cmd=%p.\n", cmd);
@@ -1224,13 +1191,10 @@
 		return ret;
 	}
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && !defined(CONFIG_SUSE_KERNEL)
-	qla2x00_block_error_handler(cmd);
-#else
 	ret = fc_block_scsi_eh(cmd);
 	if (ret != SUCCESS && ret != 0)
 		return ret;
-#endif
+
 	ret = FAILED;
 
 	ql_log(ql_log_info, vha, 0x8012,
@@ -1462,20 +1426,7 @@
 	sdev->hostdata = NULL;
 }
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && \
-	!defined(CONFIG_SUSE_KERNEL) && \
-	(!defined(RHEL_RELEASE_CODE) || \
-	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1))
-
-static int
-qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth)
-{
-	scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
-	return sdev->queue_depth;
-}
-
-#else /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && !defined(CONFIG_SUSE_KERNEL) && (!defined(RHEL_RELEASE_CODE) || RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1)) */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
 
 static void qla2x00_handle_queue_full(struct scsi_device *sdev, int qdepth)
 {
@@ -1532,9 +1483,6 @@
 	return sdev->queue_depth;
 }
 
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && !defined(CONFIG_SUSE_KERNEL) && (!defined(RHEL_RELEASE_CODE) || RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1)) */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0)
 static int
 qla2x00_change_queue_type(struct scsi_device *sdev, int tag_type)
 {
@@ -1567,7 +1515,7 @@
 	if (!dma_set_mask(&ha->pdev->dev, DMA_BIT_MASK(64))) {
 		/* Any upper-dword bits set? */
 		if (MSD(dma_get_required_mask(&ha->pdev->dev)) &&
-		    !pci_set_consistent_dma_mask(ha->pdev, DMA_BIT_MASK(64))) {
+		    !dma_set_coherent_mask(&ha->pdev->dev, DMA_BIT_MASK(64))) {
 			/* Ok, a 64bit DMA mask is applicable. */
 			ha->enable_64bit_addressing = 1;
 			ha->isp_ops->calc_req_entries = qla2x00_calc_iocbs_64;
@@ -1577,7 +1525,7 @@
 	}
 
 	dma_set_mask(&ha->pdev->dev, DMA_BIT_MASK(32));
-	pci_set_consistent_dma_mask(ha->pdev, DMA_BIT_MASK(32));
+	dma_set_coherent_mask(&ha->pdev->dev, DMA_BIT_MASK(32));
 }
 
 static void
@@ -2287,8 +2235,6 @@
 }
 EXPORT_SYMBOL(qla2xxx_add_targets);
 
-#if ((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \
-     defined(FC_VPORT_CREATE_DEFINED))
 size_t qla2xxx_add_vtarget(u64 port_name, u64 node_name, u64 parent_host)
 {
 	struct Scsi_Host *shost = NULL;
@@ -2368,12 +2314,6 @@
 }
 EXPORT_SYMBOL(qla2xxx_del_vtarget);
 
-#else
-#warning "Patch scst_fc_vport_create was not applied on\
- your kernel. Adding NPIV targets using SCST sysfs interface will be disabled."
-#endif /*((LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 28)) || \
-	  defined(FC_VPORT_CREATE_DEFINED))*/
-
 void qla_unknown_atio_work_fn(struct work_struct *work)
 {
 	struct qla_hw_data *ha = container_of(work, struct qla_hw_data,
@@ -2385,9 +2325,6 @@
 #endif /* CONFIG_SCSI_QLA2XXX_TARGET */
 
 static int
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0))
-__devinit
-#endif
 qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
 {
 	int	ret = -ENODEV;
@@ -4604,11 +4541,7 @@
 		ql_dbg(ql_dbg_aer, base_vha, 0x9007,
 		    "Finding pci device at function = 0x%x.\n", fn);
 		other_pdev =
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33) && !defined(CONFIG_SUSE_KERNEL)
-		    pci_get_bus_and_slot(
-#else
 		    pci_get_domain_bus_and_slot(pci_domain_nr(ha->pdev->bus),
-#endif
 		    ha->pdev->bus->number, PCI_DEVFN(PCI_SLOT(ha->pdev->devfn),
 		    fn));
 
@@ -4773,7 +4706,8 @@
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 7, 0) &&		\
 	(!defined(RHEL_RELEASE_CODE) ||				\
-	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(8, 3))
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(8, 3)) &&	\
+	!defined(UEK_KABI_RENAME)
 	pci_cleanup_aer_uncorrect_error_status(pdev);
 #endif
 
@@ -4968,8 +4902,7 @@
 	pci_unregister_driver(&qla2xxx_pci_driver);
 	qla2x00_release_firmware();
 	kmem_cache_destroy(srb_cachep);
-	if (ctx_cachep)
-		kmem_cache_destroy(ctx_cachep);
+	kmem_cache_destroy(ctx_cachep);
 	fc_release_transport(qla2xxx_transport_template);
 	fc_release_transport(qla2xxx_transport_vport_template);
 }
diff --git a/scst/qla2x00t/qla_sup.c b/scst/qla2x00t/qla_sup.c
index 18b745e..cadecdb 100644
--- a/scst/qla2x00t/qla_sup.c
+++ b/scst/qla2x00t/qla_sup.c
@@ -2329,8 +2329,7 @@
 				sec_mask = 0x10000;
 				break;
 			}
-			/* Fall through... */
-
+			fallthrough;
 		case 0x1f: /* Atmel flash. */
 			/* 512k sector size. */
 			if (flash_id == 0x13) {
@@ -2338,8 +2337,7 @@
 				sec_mask =   0x80000000;
 				break;
 			}
-			/* Fall through... */
-
+			fallthrough;
 		case 0x01: /* AMD flash. */
 			if (flash_id == 0x38 || flash_id == 0x40 ||
 			    flash_id == 0x4f) {
@@ -2371,7 +2369,7 @@
 				sec_mask = 0x1e000;
 				break;
 			}
-			/* Fall through... */
+			fallthrough;
 		default:
 			/* Default to 16 kb sector size. */
 			rest_addr = 0x3fff;
diff --git a/scst/scripts/checkpatch b/scst/scripts/checkpatch
index 249d1d0..8cdcfcd 100755
--- a/scst/scripts/checkpatch
+++ b/scst/scripts/checkpatch
@@ -10,4 +10,4 @@
     SPDX_LICENSE_TAG
     SYMBOLIC_PERMS
 )
-../linux-kernel/scripts/checkpatch.pl -f --show-types --ignore="$(echo "${ignore[@]}" | sed 's/ /,/g')" $(list-source-files | grep -vE '^debian/|^fcst/linux-patches|patch$|pdf$|png$|^ibmvstgt/|^iscsi-scst/usr|^mpt/|^mvsas_tgt/|^qla|^scripts/|^scstadmin/|^usr/|^www/') | sed 's/^#[0-9]*: FILE: \(.*\):/\1:1:/'
+../linux-kernel/scripts/checkpatch.pl -f --show-types --ignore="$(echo "${ignore[@]}" | sed 's/ /,/g')" $(list-source-files | grep -vE '^debian/|^fcst/linux-patches|patch$|pdf$|png$|^iscsi-scst/usr|^qla|^scripts/|^scstadmin/|^usr/|^www/') | sed 's/^#[0-9]*: FILE: \(.*\):/\1:1:/'
diff --git a/scst/scripts/generate-kernel-patch b/scst/scripts/generate-kernel-patch
index c8c4e55..7a3bcb9 100755
--- a/scst/scripts/generate-kernel-patch
+++ b/scst/scripts/generate-kernel-patch
@@ -27,11 +27,10 @@
 source "$(dirname "$0")/kernel-functions"
 
 function usage {
-  echo "Usage: $0 [-d] [-h] [-m] [-n] [-p <dir>] [-s] [-u] <kernel version>"
+  echo "Usage: $0 [-d] [-h] [-n] [-p <dir>] [-s] [-u] <kernel version>"
   echo "where: "
   echo "        -d - enable patch specialization debugging"
   echo "        -h - show this text"
-  echo "        -m - add mpt target driver"
   echo "        -n - do not delete code disabled via preprocessor statements"
   echo "        -p - generate multiple patches instead of one big patch into"\
        "the specified directory."
@@ -114,7 +113,7 @@
   local releasevermajor="$1"
   local releaseverminor="$2"
   case "$distro" in
-      CentOS)
+      CentOS|AlmaLinux)
 	  if [ -n "$releasevermajor" ]; then
 	      ao=(
 		  -v "RHEL_MAJOR=$releasevermajor"
@@ -125,7 +124,7 @@
 	  fi
 	  ;;
       UEK)
-	  ao=(-v UEK_KABI_RENAME=1)
+	  ao=(-v UEK_KABI_RENAME=1 -v UEK_RELEASE=${releasevermajor})
 	  ;;
   esac
   local kver3
@@ -248,7 +247,7 @@
 
 # See also commit 89d9a567952b ("[SCSI] add support for per-host cmd pools";
 # v3.15).
-if kernel_version_lt "$kver" 3.15; then
+if kernel_version_lt "$kver" 3.18; then
     qla2x00t="true"
     qla2x00t_32gbit="false"
 else
@@ -489,7 +488,7 @@
 
 (
 for f in iscsi-scst/include/*h; do
-    [ -e "$f" ] || continue	
+    [ -e "$f" ] || continue
     case "${f}" in
       "iscsi-scst/include/iscsi_scst_itf_ver.h")
           ;;
@@ -578,7 +577,7 @@
   add_file "qla2x00t-32gbit/qla2x00-target/README" \
            "Documentation/scst/README.qla2x00t" \
   | process_patch "qla2x00t-doc.diff"
-    
+
 fi \
 | process_patch "qla2x00t.diff"
 
@@ -642,186 +641,3 @@
   add_file "scst_local/scst_local.c" "drivers/scst/scst_local/scst_local.c"
 ) \
 | process_patch "scst_16_local.diff"
-
-
-
-# Directory drivers/scsi/ibmvstgt
-
-{
-  ( cd ibmvstgt 2>/dev/null && ./generate-in-tree-patches "${kver}" ) &&
-  if [ -e "ibmvstgt/in-tree-patches/${kver}" ]; then
-    if [ "${multiple_patches}" = "true" ]; then
-      cat <<EOF
-[SCSI] ibmvstgt: Port from tgt to SCST
-
-The ibmvstgt and libsrp kernel modules as included in the 2.6.37 kernel are
-based on the tgt SCSI target framework. Both kernel modules need the scsi_tgt
-kernel module and the tgtd user space process in order to function
-properly. This patch modifies the ibmvstgt and libsrp kernel modules such that
-both use the SCST storage target framework instead of tgt. As a result,
-neither the scsi_tgt kernel module nor the tgtd user space process are any
-more necessary when using the ibmvstgt driver.
-
-This patch introduces one backwards-incompatible change, namely that the path
-of the ibmvstgt sysfs attributes is modified. This change is unavoidable
-because this patch dissociates ibmvstgt SRP sessions from a SCSI host
-instance.  Since the user space STGT driver ibmvio was the only user of
-these attributes, that shouldn't be an issue.
-
-Changes in ibmvstgt compared to kernel 2.6.36:
-- Increased maximum data size for a single SRP command from 128 KB to 64 MB
-  such that an initiator is not forced to split large transfers into
-  multiple SCSI commands.
-- The maximum RDMA transfer size supported by a single H_COPY_RDMA call is
-  queried at driver initialization time from the open firmware tree / larger
-  transfers than 128 KB are now supported too.
-- If DMA mapping fails while handling a READ or WRITE command, the offending
-  command is retried until the associated data has been transferred instead of
-  reporting to the ibmvscsi client that the SCSI command failed.
-- VSCSI command/response queue: one element has been reserved for management
-  datagrams since these fall outside the SRP credit mechanism. Added a compile-
-  time check whether the size of this queue is a power of two.
-- Fixed a race condition which in theory could have caused the VSCSI receive
-  queue to overflow: srp_iu_put() is now invoked before a response is sent back
-  to the initiator instead of after.
-- Moved enum iue_flags from libsrp to ibmvstgt because it is ibmvstgt-specific.
-- Removed a variable that was modified but never read from ibmvstgt_rdma().
-- ibmvstgt_probe(): changed the datatype of the variable "dma" from
-  unsigned * into const unsigned * such that a cast could be removed.
-- Fixed all compiler and sparse warnings (C=2 CF=-D__CHECK_ENDIAN__).
-
-Changes in libsrp compared to kernel 2.6.36:
-- Renamed vscsis_data_length() into srp_data_length() and exported
-  this function.
-- All error messages reported via printk() do now have prefix KERN_ERR.
-- modified srp_target_alloc() and srp_target_free() such that the
-  driver-private data reflects whether or not target data has been allocated.
-  This change was necessary to avoid that ibmvstgt_remove() triggers a
-  NULL-pointer dereference if ibmvstgt_probe() failed.
-- srp_transfer_data(): All three return statements related to DMA mapping
-  failure do now return -ENOMEM instead of 0, -EIO and -ENOMEM.
-- srp_direct_data(): Removed the ext_desc argument since not used.
-- srp_direct_data() and srp_indirect_data(): Use DMA_TO/FROM_DEVICE
-  instead of DMA_BIDIRECTIONAL for the buffers mapped for transferring data
-  via DMA.
-- struct srp_target: eliminated the information unit linked list and also the
-  V_FLYING flag since both were duplicating information managed by the SCST
-  core.
-- Fixed all compiler and sparse warnings (C=2 CF=-D__CHECK_ENDIAN__).
-
-Tests performed on a backport to kernel version 2.6.18 of this driver with a
-Linux initiator system:
-- Verified that the kernel module ibmvstgt loads and initializes successfully
-  and also that the client connects after loading.
-- Verified that all virtual disks configured in scst_vdisk were discovered by
-  the client after rescanning the SCSI bus.
-- Verified that after unloading and reloading ibmvstgt and after client
-  recovery that the initiator devices were functioning normally.
-- Verified that after a client reboot ibmvscsic reconnected with the target
-  and that the target devices were again usable.
-- Performed IO stress testing on the device.
-- Verified that SCSI task abortion works correctly.
-- Performed basic I/O performance testing. With a RAM disk as target linear
-  direct I/O throughput was above 2 GB/s and a random I/O test resulted in
-  about 30000 IOPS for all block sizes between 512 bytes and 16 KB.
-  Both initiator and target were dual core POWER6 LPAR systems.
-
-Note: ibmvstgt is the only user of libsrp.
-
-Signed-off-by: Bart Van Assche <bvanassche@acm.org>
-Cc: Fujita Tomonori <fujita.tomonori@lab.ntt.co.jp>
-Cc: Brian King <brking@linux.vnet.ibm.com>
-Cc: Robert Jennings <rcj@linux.vnet.ibm.com>
-
-EOF
-    fi
-    for f in \
-      drivers/scsi/ibmvscsi/ibmvstgt.c \
-      drivers/scsi/libsrp.c \
-      include/scsi/libsrp.h \
-      include/scsi/srp.h
-    do
-      patch="ibmvstgt/in-tree-patches/${kver}/$(basename $f).patch"
-      if [ -e "${patch}" ]; then
-        add_patch "${patch}" $f
-      fi
-    done
-    add_file "ibmvstgt/README.sysfs" "Documentation/powerpc/ibmvstgt.txt"
-  fi
-} \
-| process_patch "scst_18_ibmvstgt.diff"
-
-{
-  ( cd ibmvstgt 2>/dev/null && ./generate-in-tree-patches "${kver}" ) &&
-  if [ -e "ibmvstgt/in-tree-patches/${kver}" ]; then
-    if [ "${multiple_patches}" = "true" ]; then
-      cat <<EOF
-[SCSI] tgt: Removal
-
-Because of the conversion of the ibmvstgt driver from tgt to SCST, and because
-the ibmvstgt driver was the only user of scsi_tgt, the scsi_tgt kernel module,
-the CONFIG_SCSI_TGT, CONFIG_SCSI_SRP_TGT_ATTRS and CONFIG_SCSI_FC_TGT_ATTRS
-kbuild variable, the scsi_host_template member variables transfer_response,
-supportedmode and active_mode and the constants MODE_UNKNOWN, MODE_INITIATOR
-and MODE_TARGET are no longer needed.
-
-Note: this patch applies cleanly on a 2.6.35 kernel tree. The patch tool
-however complains about the defconfig changes when trying to apply this patch
-on a 2.6.36 kernel tree.
-
-Signed-off-by: Bart Van Assche <bvanassche@acm.org>
-
-EOF
-    fi
-    for f in \
-      arch/arm/configs/at572d940hfek_defconfig \
-      arch/arm/configs/cam60_defconfig \
-      arch/arm/configs/s3c2410_defconfig \
-      arch/m68k/configs/amiga_defconfig \
-      arch/m68k/configs/apollo_defconfig \
-      arch/m68k/configs/atari_defconfig \
-      arch/m68k/configs/bvme6000_defconfig \
-      arch/m68k/configs/hp300_defconfig \
-      arch/m68k/configs/mac_defconfig \
-      arch/m68k/configs/multi_defconfig \
-      arch/m68k/configs/mvme147_defconfig \
-      arch/m68k/configs/mvme16x_defconfig \
-      arch/m68k/configs/q40_defconfig \
-      arch/m68k/configs/sun3_defconfig \
-      arch/m68k/configs/sun3x_defconfig \
-      arch/mips/configs/bcm47xx_defconfig \
-      arch/mips/configs/decstation_defconfig \
-      arch/mips/configs/ip22_defconfig \
-      arch/mips/configs/ip27_defconfig \
-      arch/mips/configs/ip32_defconfig \
-      arch/mips/configs/jazz_defconfig \
-      arch/mips/configs/malta_defconfig \
-      arch/mips/configs/markeins_defconfig \
-      arch/mips/configs/pnx8550-jbs_defconfig \
-      arch/mips/configs/pnx8550-stb810_defconfig \
-      arch/mips/configs/rm200_defconfig \
-      arch/mips/configs/tb0226_defconfig \
-      arch/mips/configs/tb0287_defconfig \
-      arch/powerpc/configs/52xx/motionpro_defconfig \
-      arch/powerpc/configs/86xx/mpc8610_hpcd_defconfig \
-      arch/powerpc/configs/mpc5200_defconfig \
-      drivers/scsi/Kconfig \
-      drivers/scsi/Makefile \
-      drivers/scsi/hosts.c \
-      drivers/scsi/scsi_sysfs.c \
-      drivers/scsi/scsi_tgt_if.c \
-      drivers/scsi/scsi_tgt_lib.c \
-      drivers/scsi/scsi_tgt_priv.h \
-      drivers/scsi/scsi_transport_fc.c \
-      drivers/scsi/scsi_transport_fc_internal.h \
-      drivers/scsi/scsi_transport_srp.c \
-      drivers/scsi/scsi_transport_srp_internal.h \
-      include/scsi/scsi_host.h \
-      include/scsi/scsi_tgt.h \
-      include/scsi/scsi_tgt_if.h
-    do
-      add_patch "ibmvstgt/in-tree-patches/${kver}/$(basename $f).patch" $f
-    done
-  fi
-} \
-| process_patch "scst_19_scsi_tgt.diff"
diff --git a/scst/scripts/generate-release-archive b/scst/scripts/generate-release-archive
index 632d7cc..68698ad 100755
--- a/scst/scripts/generate-release-archive
+++ b/scst/scripts/generate-release-archive
@@ -20,6 +20,9 @@
     fi
 fi
 
-tar --owner=root --group=root --transform="s|^|$name-$version/|" \
-	-cjf $name-$version.tar.bz2 $files &&
-ls -l $name-$version.tar.bz2
+result=../$name-$version.tar.bz2
+rm -f "${result}"
+for f in "${files[@]}"; do echo "$f"; done | \
+    tar --owner=root --group=root --transform="s|^|$name-$version/|;s|^$name-$version/../scst/include/backport.h$|../scst/include/backport.h|;s|^$name-$version/scstadmin.sysfs$|scstadmin.sysfs|" \
+	-cjf "${result}" -T- &&
+    ls -l "${result}"
diff --git a/scst/scripts/kernel-functions b/scst/scripts/kernel-functions
index c50668a..336baf8 100644
--- a/scst/scripts/kernel-functions
+++ b/scst/scripts/kernel-functions
@@ -119,9 +119,9 @@
 	{ extract_kernel_archive "$kver" && mv "linux-$kver" "linux-$1"; } ||
 	return $?
     fi
-    mv "linux-$1" ".." || return $?
+    mv "linux-$1" .. || return $?
     cd "../linux-$1" || return $?
-  )
+  ) || return $?
   rmdir "${tmpdir}"
 }
 
@@ -142,7 +142,7 @@
 		# versions do not support recent gcc versions. See also commit
 		# 9c695203a7dd ("compiler-gcc.h: gcc-4.5 needs noclone and
 		# noinline on __naked functions") # v2.6.35.
-		if kernel_version_le 2.6.35 "$1"; then
+		if kernel_version_le 2.6.34.2 "$1"; then
 		    patch -f -s -p1 <<'EOF'
 diff --git a/include/linux/compiler-gcc.h b/include/linux/compiler-gcc.h
 index 02ae99e8e6d3..47e12c19c965 100644
@@ -690,7 +690,7 @@
 
 This is because both dtc-lexer as well as dtc-parser define the same
 global symbol yyloc. Before with -fcommon those were merged into one
-defintion. The proper solution would be to to mark this as "extern",
+defintion. The proper solution would be to mark this as "extern",
 however that leads to:
 
   dtc-lexer.l:26:16: error: redundant redeclaration of 'yylloc' [-Werror=redundant-decls]
@@ -726,6 +726,84 @@
  extern bool treesource_error;
  
  /* CAUTION: this will stop working if we ever use yyless() or yyunput() */
+diff --git a/scripts/dtc/dtc-lexer.lex.c_shipped b/scripts/dtc/dtc-lexer.lex.c_shipped
+index 5c6c3fd557d7..b3b7270300de 100644
+--- a/scripts/dtc/dtc-lexer.lex.c_shipped
++++ b/scripts/dtc/dtc-lexer.lex.c_shipped
+@@ -23,7 +23,6 @@ LINECOMMENT	"//".*\n
+ #include "srcpos.h"
+ #include "dtc-parser.tab.h"
+ 
+-YYLTYPE yylloc;
+ extern bool treesource_error;
+ 
+ /* CAUTION: this will stop working if we ever use yyless() or yyunput() */
+EOF
+    fi
+
+    # See also commit 52a9dab6d892 ("libsubcmd: Fix use-after-free for
+    # realloc(..., 0)") # v5.17
+    if kernel_version_le 2.6.38 "$1" && kernel_version_lt "$1" 5.17; then
+	patch -p1 -f -s <<'EOF'
+From 52a9dab6d892763b2a8334a568bd4e2c1a6fde66 Mon Sep 17 00:00:00 2001
+From: Kees Cook <keescook@chromium.org>
+Date: Sun, 13 Feb 2022 10:24:43 -0800
+Subject: [PATCH] libsubcmd: Fix use-after-free for realloc(..., 0)
+
+GCC 12 correctly reports a potential use-after-free condition in the
+xrealloc helper. Fix the warning by avoiding an implicit "free(ptr)"
+when size == 0:
+
+In file included from help.c:12:
+In function 'xrealloc',
+    inlined from 'add_cmdname' at help.c:24:2: subcmd-util.h:56:23: error: pointer may be used after 'realloc' [-Werror=use-after-free]
+   56 |                 ret = realloc(ptr, size);
+      |                       ^~~~~~~~~~~~~~~~~~
+subcmd-util.h:52:21: note: call to 'realloc' here
+   52 |         void *ret = realloc(ptr, size);
+      |                     ^~~~~~~~~~~~~~~~~~
+subcmd-util.h:58:31: error: pointer may be used after 'realloc' [-Werror=use-after-free]
+   58 |                         ret = realloc(ptr, 1);
+      |                               ^~~~~~~~~~~~~~~
+subcmd-util.h:52:21: note: call to 'realloc' here
+   52 |         void *ret = realloc(ptr, size);
+      |                     ^~~~~~~~~~~~~~~~~~
+
+Fixes: 2f4ce5ec1d447beb ("perf tools: Finalize subcmd independence")
+Reported-by: Valdis Klētnieks <valdis.kletnieks@vt.edu>
+Signed-off-by: Kees Kook <keescook@chromium.org>
+Tested-by: Valdis Klētnieks <valdis.kletnieks@vt.edu>
+Tested-by: Justin M. Forbes <jforbes@fedoraproject.org>
+Acked-by: Josh Poimboeuf <jpoimboe@redhat.com>
+Cc: linux-hardening@vger.kernel.org
+Cc: Valdis Klētnieks <valdis.kletnieks@vt.edu>
+Link: http://lore.kernel.org/lkml/20220213182443.4037039-1-keescook@chromium.org
+Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
+---
+ tools/lib/subcmd/subcmd-util.h | 11 ++---------
+ 1 file changed, 2 insertions(+), 9 deletions(-)
+
+diff --git a/tools/lib/subcmd/subcmd-util.h b/tools/lib/subcmd/subcmd-util.h
+index 794a375dad36..b2aec04fce8f 100644
+--- a/tools/lib/subcmd/subcmd-util.h
++++ b/tools/lib/subcmd/subcmd-util.h
+@@ -50,15 +50,8 @@ static NORETURN inline void die(const char *err, ...)
+ static inline void *xrealloc(void *ptr, size_t size)
+ {
+ 	void *ret = realloc(ptr, size);
+-	if (!ret && !size)
+-		ret = realloc(ptr, 1);
+-	if (!ret) {
+-		ret = realloc(ptr, size);
+-		if (!ret && !size)
+-			ret = realloc(ptr, 1);
+-		if (!ret)
+-			die("Out of memory, realloc failed");
+-	}
++	if (!ret)
++		die("Out of memory, realloc failed");
+ 	return ret;
+ }
 EOF
     fi
 
@@ -754,7 +832,7 @@
 			      tr '\n' ' ')"
 	for url in "${urls[@]}"; do
 	    case "$distro" in
-		CentOS)
+		CentOS|AlmaLinux)
 		    wget -q -nc "${url}/kernel-${kver}.src.rpm" && break
 		    ;;
 		UEK)
@@ -769,19 +847,21 @@
     (
 	cd "${tmpdir}" &&
 	    case "$distro" in
-		CentOS)
+		CentOS|AlmaLinux)
 		    rpm2cpio "${kernel_downloads}/kernel-${kver}.src.rpm" |
 			cpio -i --make-directories --quiet &&
-			tar xaf "linux-${kver}.tar."* &&
-			mv "linux-${kver}" ".." &&
-			cd "../linux-${kver}"
+			tar xaf "linux-${kver}"*.tar.* &&
+			if [ -e "linux-${kver}" ]; then
+			    mv "linux-${kver}" ..
+			else
+			    mv "linux-${kver}"*[^z] "../linux-${kver}"
+			fi
 		    ;;
 		UEK)
 		    rpm2cpio "${kernel_downloads}/kernel-uek-${kver}.src.rpm" |
 			cpio -i --make-directories --quiet &&
 			tar xaf "linux-${kver/-*}.tar."* &&
-			mv "linux-${kver/-*}" "../linux-${kver}" &&
-			cd "../linux-${kver}"
+			mv "linux-${kver/-*}" "../linux-${kver}"
 		    ;;
 		*)
 		    echo "Error: unknown distro $distro"
diff --git a/scst/scripts/list-source-files b/scst/scripts/list-source-files
index 06b22a3..56e3f1c 100755
--- a/scst/scripts/list-source-files
+++ b/scst/scripts/list-source-files
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/bin/bash
 
 list_source_files() {
   local d r
@@ -24,14 +24,14 @@
       done
     )
   elif [ -e "$r/.git" ]; then
-    subdir="${d#${r}}"
+    subdir="${d#"${r}"}"
     if [ "$r" != "" ]; then
       ( cd "$d" && git ls-tree --name-only -r HEAD ) | sed "s|^$subdir/||"
     else
       echo "Ignored directory $1" >&2
     fi
   elif [ -e "$r/.hg" ]; then
-    subdir="${d#${r}}"
+    subdir="${d#"${r}"}"
     if [ -n "${subdir}" ]; then
       subdir="${subdir#/}/"
       hg manifest | sed -n "s|^$subdir||p"
@@ -41,16 +41,35 @@
   else
     (
       cd "$d" &&
-      find . -type f -o -type l | \
-      sed -e 's|^\./||' \
-	  -e '\|\.k\{0,1\}o\(\.\(cm\)\{0,1\}d\)\{0,1\}$|d' \
-	  -e '\|\.mod\(\.c\)\{0,1\}$|d' \
-	  -e '\|/conftest/.*/result-.*\.txt$|d' \
-	  -e '\|/modules\.order$|d' \
-	  -e '\,/Module\.\(symver\|marker\)s$,d' \
-	  -e '\,/\.tmp_versions\(/\|$\),d'
+      find . -type f -o -type l |
+      sed -e 's/^\.\///'				\
+	  -e '/\.depend_\(adm\|d\|f\)$/d'		\
+	  -e '/\.o$/d'					\
+	  -e '/\.o\.d$/d'				\
+	  -e '/\.o\.cmd$/d'				\
+	  -e '/\.ko$/d'					\
+	  -e '/\.ko\.cmd$/d'				\
+	  -e '/\.mod$/d'				\
+	  -e '/\.mod\.c$/d'				\
+	  -e '/\.mod\.cmd$/d'				\
+	  -e '/\/Module\.\(symver\|marker\)s$/d'	\
+	  -e '/\/\.Module\.symvers\.cmd$/d'		\
+	  -e '/\/\.modules\.order\.cmd$/d'		\
+	  -e '/\/\.tmp_versions\(\/\|$\)/d'		\
+	  -e '/\/blib\//d'				\
+	  -e '/\/conftest\/.*\/build-output-.*\.txt$/d'	\
+	  -e '/\/conftest\/.*\/result-.*\.txt$/d'	\
+	  -e '/\/modules\.order$/d'			\
+	  -e '/\/rpmbuilddir\//d'			\
+	  -e '/^iscsi-scst\/usr\/iscsi-scst-adm$/d'	\
+	  -e '/^iscsi-scst\/usr\/iscsi-scstd$/d'	\
+	  -e '/^rpmbuilddir\//d'			\
+	  -e '/^usr\/fileio\/fileio_tgt$/d'		\
+	  -e '/^usr\/stpgd\/stpgd$/d'			\
+	  -e '/debian\/tmp\//d'				\
+	  -e '/~$/d'
     )
-  fi
+  fi | sort
 }
 
 if [ $# = 0 ]; then
diff --git a/scst/scripts/rhel-rpm-functions b/scst/scripts/rhel-rpm-functions
index 3385587..7ca3ef5 100644
--- a/scst/scripts/rhel-rpm-functions
+++ b/scst/scripts/rhel-rpm-functions
@@ -35,10 +35,16 @@
 		    echo "http://ftp.redhat.com/redhat/rhel/rc/7/Server/source/tree/Packages";;
 	    esac
 	    ;;
+	"AlmaLinux")
+	    case $releasever in
+		[89]*)
+		    echo "https://repo.almalinux.org/vault/${releasever}/BaseOS/Source/Packages/";;
+	    esac
+	    ;;
 	"Oracle Linux Server"|"UEK")
 	    echo "http://public-yum.oracle.com/repo/OracleLinux/OL${releasevermajor}/${releaseverminor}/base/${arch}/getPackageSource"
 	    echo "http://public-yum.oracle.com/repo/OracleLinux/OL${releasevermajor}/latest/${arch}/getPackageSource"
-	    for ((i=4;i<=6;i++)); do
+	    for ((i=4;i<=7;i++)); do
 		echo "http://public-yum.oracle.com/repo/OracleLinux/OL${releasevermajor}/UEKR$i/${arch}/getPackageSource"
 	    done
 	    ;;
@@ -89,10 +95,16 @@
 	    ;;
 	"Red Hat Enterprise Linux"*)
 	    echo "";;
+	"AlmaLinux")
+	    case $releasever in
+		[89]*)
+		    echo "https://mirror.yandex.ru/almalinux/${releasever}/BaseOS/${arch}/os/Packages/";;
+	    esac
+	    ;;
 	"Oracle Linux Server"|"UEK")
 	    echo "http://public-yum.oracle.com/repo/OracleLinux/OL${releasevermajor}/${releaseverminor}/base/${arch}/getPackageSource"
 	    echo "http://public-yum.oracle.com/repo/OracleLinux/OL${releasevermajor}/latest/${arch}/getPackageSource"
-	    for ((i=4;i<=6;i++)); do
+	    for ((i=4;i<=7;i++)); do
 		echo "http://public-yum.oracle.com/repo/OracleLinux/OL${releasevermajor}/UEKR$i/${arch}/getPackageSource"
 	    done
 	    ;;
diff --git a/scst/scripts/run-regression-tests b/scst/scripts/run-regression-tests
index 3520f1d..d5f65d8 100755
--- a/scst/scripts/run-regression-tests
+++ b/scst/scripts/run-regression-tests
@@ -291,6 +291,7 @@
 CONFIG_IWLWIFI_DEVICE_TRACING			\
 CONFIG_IWM_TRACING				\
 CONFIG_KALLMODSYMS				\
+CONFIG_KCOV					\
 CONFIG_KVM_MMU_AUDIT				\
 CONFIG_MAC80211_DRIVER_API_TRACER		\
 CONFIG_MMIOTRACE				\
@@ -302,6 +303,7 @@
 CONFIG_SECURITY_SELINUX				\
 CONFIG_STACK_TRACER				\
 CONFIG_STACK_VALIDATION				\
+CONFIG_TARGET_CORE				\
 CONFIG_TRACEPOINTS				\
 CONFIG_TRACER_MAX_TRACE				\
 CONFIG_TRACE_BRANCH_PROFILING			\
@@ -385,7 +387,7 @@
   if (cd "${outputdir}/linux-$k" \
       && make -s modules_prepare \
       && make -s scripts \
-      && for subdir; do LC_ALL=C make -k M="${subdir}"; done
+      && for subdir; do LC_ALL=C make -j$(nproc) -k M="${subdir}"; done
      ) &> "${outputfile}"
   then
     local errors warnings
@@ -678,11 +680,13 @@
   k="${kv}"
 
   generate_kernel_patch "$k" "${generate_kernel_patch_options}" || continue
-  (
+  if ! (
       cd "${outputdir}" &&
 	  download_and_extract_kernel_tree "$k"
-  ) ||
+  ); then
+      echo "Error: download_and_extract_kernel_tree $k failed"
       continue
+  fi
   k="${k/^*}"
   if [ "${run_checkpatch}" = "true" ]; then
     run_checkpatch "$k"
diff --git a/scst/scripts/specialize-patch b/scst/scripts/specialize-patch
index ce46d06..e5f508a 100755
--- a/scst/scripts/specialize-patch
+++ b/scst/scripts/specialize-patch
@@ -31,10 +31,14 @@
 # Convert a kernel version in the x.y.z format into numeric form, just like
 # the KERNEL_VERSION() macro.
 
+function version_number(a, b, c) {
+  return a * 65536 + b * 256 + (c > 255 ? 255 : c);
+}
+
 function version_code(kver, array) {
   if (!match(kver, "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$", array))
     match(kver, "^([0-9]+)\\.([0-9]+)$", array)
-  return 65536*array[1] + 256*array[2] + array[3]
+  return version_number(array[1], array[2], array[3]);
 }
 
 
@@ -64,6 +68,11 @@
     stmnt = "+#if !defined(" arg[1] ")"
   }
 
+  # See also commit c9c9762d4d44 ("block: return the correct bvec when checking
+  # for gaps"; v5.14-rc1).
+  gsub("defined\\(bio_multiple_segments\\)",
+       "(LINUX_VERSION_CODE >= KERNEL_VERSION(5, 13, 0))", stmnt)
+
   gsub("defined\\(REGISTER_MAD_AGENT_HAS_FLAGS_ARG\\)",
        "(LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0) || " \
        "RHEL_RELEASE_CODE -0 >= 6 * 256 + 9)", stmnt)
@@ -91,8 +100,12 @@
        "(LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) \\&\\& " \
        "RHEL_RELEASE_CODE -0 < 7 * 256 + 5)", stmnt)
 
+  gsub("defined\\(IB_CM_LISTEN_TAKES_THIRD_ARG\\)",
+       "(LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0))", stmnt)
+
   gsub("IB_CLIENT_ADD_ONE_RETURNS_INT",
-       "(LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0))", stmnt)
+       "(LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) || " \
+       "RHEL_RELEASE_CODE -0 >= 8 * 256 + 4)", stmnt)
 
   gsub("defined\\(IB_CLIENT_REMOVE_TAKES_TWO_ARGS\\)",
        "(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0) || " \
@@ -161,7 +174,8 @@
        "RHEL_RELEASE_CODE -0 < 8 * 256 + 2)", stmnt)
 
   gsub("RDMA_REJECT_HAS_FOUR_ARGS",
-       "(LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0))", stmnt)
+       "(LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) || " \
+       "RHEL_RELEASE_CODE -0 >= 8 * 256 + 4)", stmnt)
 
   gsub("defined(ENABLE_NPIV)", 0, stmnt)
 
@@ -172,7 +186,7 @@
   pattern="KERNEL_VERSION\\([[:blank:]]*([0-9]+)[[:blank:]]*,[[:blank:]]*([0-9]+)[[:blank:]]*,[[:blank:]]*([0-9]+)[[:blank:]]*\\)"
   while (match(stmnt, pattern, op) != 0)
   {
-    sub(pattern, op[1] * 65536 + op[2] * 256 + op[3], stmnt)
+    sub(pattern, version_number(op[1], op[2], op[3]), stmnt)
   }
 
   gsub("defined\\(INSIDE_KERNEL_TREE\\)", "1", stmnt)
@@ -238,6 +252,8 @@
 
   gsub("defined\\(_COMPAT_LINUX_MM_H\\)", "0", stmnt)
   gsub("defined\\(UEK_KABI_RENAME\\)", UEK_KABI_RENAME ? "1" : "0", stmnt)
+  gsub("defined\\(UEK_RELEASE\\)", UEK_RELEASE ? "1" : "0", stmnt)
+  gsub("UEK_RELEASE", UEK_RELEASE ? UEK_RELEASE : "0", stmnt)
 
   if (SCST_IO_CONTEXT != "")
   {
@@ -338,6 +354,12 @@
       sub(pattern, (op[1] != 0) && (op[2] != 0), stmnt)
     }
 
+    pattern="0[[:blank:]]*&&[[:blank:]]*defined\\([A-Za-z0-9_]*\\)"
+    while (match(stmnt, pattern, op) != 0)
+    {
+      sub(pattern, 0, stmnt)
+    }
+
     pattern="^+#(if|elif)[[:blank:]]*([01])[[:blank:]]*&&[[:blank:]]*(!*[[:blank:]]*defined[[:blank:]]*\\([[:blank:]]*[A-Za-z0-9_]*[[:blank:]]*\\))$"
     while (match(stmnt, pattern, op) != 0)
     {
@@ -422,6 +444,7 @@
       || $0 ~ "IB_CLIENT_ADD_ONE_RETURNS_INT"                           \
       || $0 ~ "IB_CLIENT_REMOVE_TAKES_TWO_ARGS"				\
       || $0 ~ "IB_CM_LISTEN_TAKES_FOURTH_ARG"				\
+      || $0 ~ "IB_CM_LISTEN_TAKES_THIRD_ARG"				\
       || $0 ~ "IB_CREATE_CQ_HAS_INIT_ATTR"				\
       || $0 ~ "IB_PD_HAS_LOCAL_DMA_LKEY"				\
       || $0 ~ "IB_QUERY_GID_HAS_ATTR_ARG"				\
@@ -437,8 +460,10 @@
       || $0 ~ "RHEL_RELEASE_CODE"					\
       || $0 ~ "SOCK_RECVMSG_HAS_FOUR_ARGS"				\
       || $0 ~ "UEK_KABI_RENAME"						\
-      || $0 ~ "_COMPAT_LINUX_MM_H"					\
+      || $0 ~ "UEK_RELEASE"						\
       || $0 ~ "USE_PRE_440_WR_STRUCTURE"				\
+      || $0 ~ "_COMPAT_LINUX_MM_H"					\
+      || $0 ~ "bio_multiple_segments"                                   \
       || generating_upstream_patch_defined				\
          && $0 ~ "GENERATING_UPSTREAM_PATCH"				\
       || $0 ~ "CONFIG_TCP_ZERO_COPY_TRANSFER_COMPLETION_NOTIFICATION"	\
diff --git a/scst/scripts/update-version b/scst/scripts/update-version
index aa1107b..7800efb 100755
--- a/scst/scripts/update-version
+++ b/scst/scripts/update-version
@@ -17,12 +17,21 @@
 fv=$major.$minor.$release$suffix
 reldate=$(date "+%d %B %Y")
 
-sed -i "s,^\(#define[[:blank:]]*\(FT_VERSION\|ISCSI_VERSION_STRING\|Q2T_VERSION_STRING\|SCST_LOCAL_VERSION\|SCST_VERSION_NAME\|DRV_VERSION\|VERSION_STR\)[[:blank:]]*\)\"[^\"]*\",\1\"$major.$minor.$release$suffix\"," iscsi-scst/kernel/isert-scst/isert.c scst_local/scst_local.c srpt/src/ib_srpt.c fcst/fcst.h iscsi-scst/include/iscsi_scst_ver.h qla2x00t/qla2x00-target/qla2x00t.h scst/include/scst_const.h usr/include/version.h
+sed -i "s,^\(#define[[:blank:]]*\(FT_VERSION\|ISCSI_VERSION_STRING\|Q2T_VERSION_STRING\|SCST_LOCAL_VERSION\|SCST_VERSION_NAME\|DRV_VERSION\|VERSION_STR\)[[:blank:]]*\)\"[^\"]*\",\1\"$major.$minor.$release$suffix\"," \
+    iscsi-scst/kernel/isert-scst/isert.c          \
+    scst_local/scst_local.c                       \
+    srpt/src/ib_srpt.c                            \
+    fcst/fcst.h                                   \
+    iscsi-scst/include/iscsi_scst_ver.h           \
+    qla2x00t/qla2x00-target/qla2x00t.h            \
+    qla2x00t-32gbit/qla2x00-target/scst_qla2xxx.h \
+    scst/include/scst_const.h                     \
+    usr/include/version.h
 sed -i "s,^\(<date>Version[[:blank:]]*\)[^[:blank:]]*\(</date>\),\1$fv\2," doc/scst_user_spec.sgml
 sed -i "s/^Version .*/Version $fv, $reldate/" iscsi-scst/README qla2x00t/qla2x00-target/README scst/README usr/fileio/README
 sed -i "s/^\(\(static const char \*scst_local_version_date\|#define[[:blank:]]*DRV_RELDATE\)[[:blank:]]*\)\"[^\"]*\"/\1\"$reldate\"/" iscsi-scst/kernel/isert-scst/isert.c scst_local/scst_local.c srpt/src/ib_srpt.c
 sed -i "s/^\(#define[[:blank:]]*[^[:blank:]]*_REV[[:blank:]]*\)\"[[:blank:]0-9]*\"/\1\"$(printf "%-4s" ${major}${minor}${release})\"/" usr/fileio/common.h scst/src/dev_handlers/scst_vdisk.c
-sed -i "s/^\(#define[[:blank:]]*SCST_VERSION_CODE[[:blank:]]*\).*/\1SCST_VERSION($major, $minor, $release, 0)/" scst/include/scst.h
+sed -i "s/^\(#define[[:blank:]]*SCST_VERSION_CODE[[:blank:]]*\).*/\1SCST_VERSION($major, $minor, $release, 0)/" scst/include/scst_const.h
 sed -i "s/^\(#define[[:blank:]]*Q2T_VERSION_CODE[[:blank:]]*\).*/\1Q2T_VERSION($major, $minor, $release, 0)/" \
     qla2x00t/qla2x00-target/qla2x00t.h \
     qla2x00t-32gbit/qla2x00-target/scst_qla2xxx.h
diff --git a/scst/scst-dkms.spec.in b/scst/scst-dkms.spec.in
index 1ed6bf9..693c0da 100644
--- a/scst/scst-dkms.spec.in
+++ b/scst/scst-dkms.spec.in
@@ -46,11 +46,16 @@
 # Fedora
 %define kernel_devel_rpm kernel-devel
 %else
+%if %([ %{kernel_rpm} = kernel-uek-core ]; echo $((1-$?)))
+# UEK 7
+%define kernel_devel_rpm kernel-uek-devel
+%else
 # Other Linux distros
 %define kernel_devel_rpm %{kernel_rpm}-devel
 %endif
 %endif
 %endif
+%endif
 %{echo:kernel_devel_rpm=%{kernel_devel_rpm}
 }
 %endif
@@ -132,7 +137,7 @@
 %build
 export KVER=%{kversion} PREFIX=%{_prefix}
 export BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y
-for d in scst fcst iscsi-scst qla2x00t/qla2x00-target scst_local srpt; do
+for d in scst fcst iscsi-scst qla2x00t-32gbit/qla2x00-target scst_local srpt; do
     %{make} -C $d
 done
 
@@ -142,7 +147,7 @@
 for d in scst; do
     DESTDIR=%{buildroot} %{make} -C $d install
 done
-for d in fcst iscsi-scst qla2x00t/qla2x00-target scst_local srpt; do
+for d in fcst iscsi-scst qla2x00t-32gbit/qla2x00-target scst_local srpt; do
     DESTDIR=%{buildroot} INSTALL_MOD_PATH=%{buildroot} %{make} -C $d install
 done
 rm -f %{buildroot}/lib/modules/%{kversion}/[Mm]odule*
@@ -157,7 +162,7 @@
 PACKAGE_VERSION="%{dkms_version}"
 PACKAGE_NAME="%{kmod_name}"
 AUTOINSTALL=yes
-MAKE[0]="export KVER=${kernelver} KDIR=${kernel_source_dir} BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y && make 2release && make -sC scst && make -sC fcst && make -sC iscsi-scst && make -sC qla2x00t/qla2x00-target && make -sC scst_local && make -sC srpt && cp */*.ko */*/*.ko *scst*/*/*/*.ko ."
+MAKE[0]="export KVER=${kernelver} KDIR=${kernel_source_dir} BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y && make 2release && make -sC scst && make -sC fcst && make -sC iscsi-scst && make -sC qla2x00t-32gbit/qla2x00-target && make -sC scst_local && make -sC srpt && cp */*.ko */*/*.ko *scst*/*/*/*.ko ."
 CLEAN="make clean"
 # Remove any existing ib_srpt.ko kernel modules
 PRE_INSTALL="find /lib/modules/${kernelver} -name ib_srpt.ko -exec rm {} \;"
diff --git a/scst/scst.spec.in b/scst/scst.spec.in
index c1a88de..01d863a 100644
--- a/scst/scst.spec.in
+++ b/scst/scst.spec.in
@@ -51,11 +51,16 @@
 # Fedora
 %define kernel_devel_rpm kernel-devel
 %else
+%if %([ %{kernel_rpm} = kernel-uek-core ]; echo $((1-$?)))
+# UEK 7
+%define kernel_devel_rpm kernel-uek-devel
+%else
 # Other Linux distros
 %define kernel_devel_rpm %{kernel_rpm}-devel
 %endif
 %endif
 %endif
+%endif
 %{echo:kernel_devel_rpm=%{kernel_devel_rpm}
 }
 %endif
@@ -67,7 +72,7 @@
 
 Name:		%{kmod_name}-%{kversion}
 Version:	%{rpm_version}
-Release:	%{rpm_release}%{?dist}
+Release:	%{pkgrel}%{?dist}
 Summary:	SCST mid-layer kernel drivers
 Group:		System/Kernel
 License:	GPLv2
@@ -125,7 +130,7 @@
 export PREFIX=%{_prefix}
 export BUILD_2X_MODULE=y CONFIG_SCSI_QLA_FC=y CONFIG_SCSI_QLA2XXX_TARGET=y
 make 2release
-for d in scst iscsi-scst scst_local; do
+for d in scst fcst iscsi-scst qla2x00t-32gbit/qla2x00-target scst_local srpt usr; do
     %{make} -C $d
 done
 
@@ -137,7 +142,7 @@
 for d in scst; do
     DESTDIR=%{buildroot} %{make} -C $d install
 done
-for d in iscsi-scst scst_local; do
+for d in fcst iscsi-scst qla2x00t-32gbit/qla2x00-target scst_local srpt usr; do
     DESTDIR=%{buildroot} INSTALL_MOD_PATH=%{buildroot} %{make} -C $d install
 done
 # Set the executable bit such that /usr/lib/rpm/find-debuginfo.sh can find the
@@ -163,10 +168,13 @@
 @depmod@ -a %{kversion}
 
 %files
-%defattr(-,root,root,0755)
-%dir /lib/modules/%{kversion}/extra
+%defattr(0644,root,root,0755)
+/lib/modules/%{kversion}/extra/fcst.ko
+/lib/modules/%{kversion}/extra/ib_srpt.ko
 /lib/modules/%{kversion}/extra/iscsi-scst.ko
 /lib/modules/%{kversion}/extra/isert-scst.ko
+/lib/modules/%{kversion}/extra/qla2x00tgt.ko
+/lib/modules/%{kversion}/extra/qla2xxx_scst.ko
 /lib/modules/%{kversion}/extra/scst.ko
 /lib/modules/%{kversion}/extra/scst_local.ko
 %dir /lib/modules/%{kversion}/extra/dev_handlers
@@ -188,6 +196,9 @@
 %dir /var/lib/scst/dif_tags
 %dir /var/lib/scst/pr
 %dir /var/lib/scst/vdev_mode_pages
+/usr/bin/scst/fileio_tgt
+/usr/bin/scst/scst_on_stpg
+/usr/sbin/stpgd
 
 %files devel
 %defattr(-,root,root,0755)
@@ -203,8 +214,6 @@
 /usr/include/scst/scst_user.h
 
 %changelog
-* Sat Apr 4 2020 Jim McCarthy <jim.mccarthy@actifio.com>
-- Changed list of build artifacts to match Actifio deliverables.
 * Sun Mar  8 2020 Bart Van Assche <bvanassche@acm.org>
 - Added support for the CentOSPlus kernel.
 * Tue Oct  8 2019 Bart Van Assche <bvanassche@acm.org>
diff --git a/scst/scst/ChangeLog b/scst/scst/ChangeLog
index 049ea3c..ed7f614 100644
--- a/scst/scst/ChangeLog
+++ b/scst/scst/ChangeLog
@@ -1,3 +1,73 @@
+Summary of changes between versions 3.6 and 3.7
+-----------------------------------------------
+- The SCST event subsystem works again reliably.
+- Fixed a race condition when replacing a LUN under load.
+- Fixed handling of INQUIRY/SENSE commands that comes with buffer size 0.
+- The behavior of the on_alua_state_change_*() callback functions has been
+  fixed such that these are also invoked for devices that are not in any
+  target.
+- Error handling for iscsi-scst has been improved such that data sending
+  failure no longer crashes the system.
+- An improvement has been added to iscsi-scstd to allow multiple addresses to
+  be specified for the server to listen on.
+- Fixed a hang when unregistering a SCST device due to incorrect device
+  reference counter management in copy manager.
+- Fixed copy manager device update (for auto_cm_assignment=1) such that it no
+  longer corrupts the designator list.
+- Support for scst_tgt_template detect() method has been dropped. This method
+  was declared obsolete in 2015.
+- The scst_user device handler has been made compatible with the
+  qla2x00t-32gbit driver.
+- qla2x00t-32gbit driver: NPIV support has been improved.
+- qla2x00t-32gbit driver: Target mode usage has been simplified by changing
+  the default qlini_mode to exclusive.
+- qla2x00t-32gbit driver: Updated from Linux kernel version v5.15 to v6.1.
+
+The kernel versions supported by this release are:
+* Kernel.org kernel versions v3.10..v6.1.
+* Debian / Ubuntu kernels based on upstream kernel versions v3.10..v6.1.
+* RHEL / CentOS / AlmaLinux 7.x, 8.0..8.7 and 9.0..9.1 kernels.
+* UEK version 4, 5, 6 and 7 kernels.
+
+Summary of changes between versions 3.5 and 3.6
+-----------------------------------------------
+- Made the command processing path slightly faster by removing two atomic
+  instructions from the command processing path.
+- Added support for the READ and WRITE DYN RUNTIME ATTR commands and also
+  for SERVICE ACTION IN(12).
+- An infinite loop in the code that sets CHECK CONDITION has been fixed.
+- A deadlock has been fixed in the code for assigning a device handler to a
+  vdisk.
+- Support for adding a vdisk_blockio device with a non-existent filename has
+  been restored.
+- The async mode of vdisk_fileio has been made compatible with filesystems that
+  use the iomap code, e.g. XFS.
+- A "INFO: rcu_sched self-detected stall" issue has been fixed.
+- Support in the copy manager for auto_cm_assignment=0 has been fixed.
+- Standards-compliance of the copy manager has been improved. Designators with
+  a length above 20 bytes are now rejected instead of being accepted.
+- The copy manager no longer suspends activity when adding a LUN.
+- A bug has been fixed in the vdisk resync_size functionality.
+- The tape device handler now sets 'block_shift' correctly.
+- Improved the code for building a Debian package (dpkg).
+- scst_local: the SCSI host number is now available in sysfs.
+- scst-isert: this driver has been made compatible with
+  CONFIG_HARDENED_USERCOPY.
+- scst-isert: support for RDMA_CV_EVENT_ADDR_CHANGE has been added.
+- scst-isert: a hang in iscsi_release() has been fixed.
+- The top-level Makefile has been modified such that the qla2x00t-32gbit driver
+  is built by default instead of the qla2x00t driver. The qla2x00t driver can
+  be selected by passing QLA_32GBIT=no as argument to make.
+- qla2x00t-32gbit driver: updated from Linux kernel version v5.10 to v5.15.
+- scstadmin: the -force option is now passed to removeGroup().
+- scstadmin: improved performance of the scstadmin function make_path().
+
+The kernel versions supported by this release are:
+* Kernel.org kernel versions v3.10..v5.15.
+* Debian / Ubuntu kernels based on upstream kernel versions v3.10..v5.15.
+* RHEL / CentOS 7.x and 8.0..8.5 kernels.
+* UEK version 6, 7 and 8 kernels.
+
 Summary of changes between versions 3.4 and 3.5
 -----------------------------------------------
 - Added the forward_src and forward_dst sysfs attributes. Removed
diff --git a/scst/scst/README b/scst/scst/README
index a28e844..cd64387 100644
--- a/scst/scst/README
+++ b/scst/scst/README
@@ -1,7 +1,7 @@
 Generic SCSI target mid-level for Linux (SCST)
 ==============================================
 
-Version 3.5.0, 21 December 2020
+Version 3.7.0, 26 December 2022
 ----------------------------
 
 SCST is designed to provide unified, consistent interface between SCSI
@@ -1196,7 +1196,7 @@
  - rotational - if set, this device reported as rotational. Otherwise,
    it is reported as non-rotational (SSD, etc.)
 
- - zero_copy - ignored. For zero-copy I/O, set the async flag and
+ - zero_copy - obsolete. For zero-copy I/O, set the async flag and
    possibly also the o_direct flag and use Linux kernel v4.10 or later.
 
  - dif_mode - specifies which T10-PI, or DIF, mode this device will use.
diff --git a/scst/scst/include/backport.h b/scst/scst/include/backport.h
index c58d43b..343c131 100644
--- a/scst/scst/include/backport.h
+++ b/scst/scst/include/backport.h
@@ -30,15 +30,11 @@
 	(defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 8)
 #include <linux/blk-mq.h>
 #endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
 #include <linux/bsg-lib.h>	/* struct bsg_job */
-#endif
 #include <linux/dmapool.h>
 #include <linux/eventpoll.h>
 #include <linux/iocontext.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36)
 #include <linux/kobject_ns.h>
-#endif
 #include <linux/scatterlist.h>	/* struct scatterlist */
 #include <linux/slab.h>		/* kmalloc() */
 #include <linux/stddef.h>	/* sizeof_field() */
@@ -46,13 +42,12 @@
 #include <linux/vmalloc.h>
 #include <linux/workqueue.h>
 #include <linux/writeback.h>	/* sync_page_range() */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24)
 #include <net/net_namespace.h>  /* init_net */
-#endif
 #include <rdma/ib_verbs.h>
 #include <scsi/scsi_cmnd.h>	/* struct scsi_cmnd */
+#include <scsi/scsi_eh.h>	/* scsi_build_sense_buffer() */
 struct scsi_target;
-#include <scsi/scsi_transport_fc.h> /* struct fc_bsg_job */
+#include <scsi/scsi_transport_fc.h> /* struct bsg_job */
 #include <asm/unaligned.h>	/* get_unaligned_be64() */
 
 /* <asm-generic/barrier.h> */
@@ -64,26 +59,6 @@
 #define smp_mb__after_atomic_dec smp_mb__after_atomic
 #endif
 
-/* <asm-generic/bug.h> */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) && !defined(WARN)
-/* See also commit a8f18b909c0a3f22630846207035c8b84bb252b8 */
-#define WARN(condition, format...) do {		\
-	if (unlikely(condition)) {		\
-		printk(KERN_WARNING format);	\
-		WARN_ON(true);			\
-	}					\
-} while (0)
-#endif
-
-/* <asm-generic/fcntl.h> */
-
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32)
-#ifndef O_DSYNC
-#define O_DSYNC O_SYNC
-#endif
-#endif
-
 /* <asm/msr.h> */
 
 #ifdef CONFIG_X86
@@ -102,6 +77,15 @@
 #define tsc_khz 1000
 #endif
 
+/* <linux/err.h> */
+
+/*
+ * See also commit 6e8b8726ad50 ("PTR_RET is now PTR_ERR_OR_ZERO") # v3.12
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0) && !defined(RHEL_RELEASE_CODE)
+#define PTR_ERR_OR_ZERO(p) PTR_RET(p)
+#endif
+
 /* <linux/bio.h> */
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0) &&	\
@@ -126,17 +110,78 @@
 }
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
+/*
+ * See also commit a8affc03a9b3 ("block: rename BIO_MAX_PAGES to BIO_MAX_VECS")
+ * # v5.12.
+ */
+#define BIO_MAX_VECS BIO_MAX_PAGES
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(9, 1))
+/*
+ * See also commit 609be1066731 ("block: pass a block_device and opf to
+ * bio_alloc_bioset") # v5.18
+ */
+static inline
+struct bio *bio_alloc_bioset_backport(struct block_device *bdev,
+		unsigned short nr_vecs, unsigned int opf, gfp_t gfp_mask,
+		struct bio_set *bs)
+{
+	/*
+	 * Check that @bdev and @opf parameters are zeros.
+	 *
+	 * The old API expects these parameters to be set implicitly.
+	 * Therefore, warn about using an explicit setting that would
+	 * cause these parameters to be lost.
+	 */
+	WARN_ON_ONCE(bdev || opf);
+
+	return bio_alloc_bioset(gfp_mask, nr_vecs, bs);
+}
+
+#define bio_alloc_bioset bio_alloc_bioset_backport
+
+/*
+ * See also commit 07888c665b40 ("block: pass a block_device and opf to
+ * bio_alloc") # v5.18
+ */
+static inline
+struct bio *bio_alloc_backport(struct block_device *bdev,
+		unsigned short nr_vecs, unsigned int opf, gfp_t gfp_mask)
+{
+	/*
+	 * Check that @bdev and @opf parameters are zeros.
+	 *
+	 * The old API expects these parameters to be set implicitly.
+	 * Therefore, warn about using an explicit setting that would
+	 * cause these parameters to be lost.
+	 */
+	WARN_ON_ONCE(bdev || opf);
+
+	return bio_alloc(gfp_mask, nr_vecs);
+}
+
+#define bio_alloc bio_alloc_backport
+
+#endif
+
+/* <linux/blk_types.h> */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0)
+enum {
+	REQ_OP_SCSI_IN	= REQ_OP_DRV_IN,
+	REQ_OP_SCSI_OUT	= REQ_OP_DRV_OUT,
+};
+#endif
+
 /* <linux/blk-mq.h> */
 
 static inline unsigned int scst_blk_rq_cpu(struct request *rq)
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
-	/*
-	 * See also commit c7c22e4d5c1f ("block: add support for IO CPU
-	 * affinity") # v2.6.28.
-	 */
-	return 0;
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 21, 0) &&	\
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 21, 0) &&	\
 	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 8)
 	return rq->cpu;
 #else
@@ -144,52 +189,54 @@
 #endif
 }
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 19, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(9, 1))
+/*
+ * See also commit e2e530867245 ("blk-mq: remove the done argument to
+ * blk_execute_rq_nowait") # v5.19.
+ */
+static inline
+void blk_execute_rq_nowait_backport(struct request *rq, bool at_head)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
+	/*
+	 * See also commit 8eeed0b554b9 ("block: remove unnecessary argument from
+	 * blk_execute_rq_nowait") # v5.12.
+	 */
+	blk_execute_rq_nowait(rq->q, NULL, rq, at_head, rq->end_io);
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 17, 0)
+	/*
+	 * See also commit b84ba30b6c7a ("block: remove the gendisk argument to
+	 * blk_execute_rq") # v5.17.
+	 */
+	blk_execute_rq_nowait(NULL, rq, at_head, rq->end_io);
+#else
+	blk_execute_rq_nowait(rq, at_head, rq->end_io);
+#endif
+}
+
+#define blk_execute_rq_nowait blk_execute_rq_nowait_backport
+#endif
+
 /* <linux/blkdev.h> */
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
-static inline unsigned int queue_max_hw_sectors(struct request_queue *q)
-{
-	return q->max_hw_sectors;
-}
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
-/* See also commit ac481c20ef8f ("block: Topology ioctls") # v2.6.32 */
-static inline int bdev_io_opt(struct block_device *bdev)
-{
-	return 0;
-}
-#endif
-
-/* <linux/bsg-lib.h> */
-
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 19, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(9, 1))
 /*
- * Note: the function bsg_job_sense() exists only in SCST but not in any
- * upstream kernel.
+ * See also commit 44abff2c0b97 ("block: decouple REQ_OP_SECURE_ERASE
+ * from REQ_OP_DISCARD") # v5.19.
  */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) &&	\
-     !defined(CONFIG_SUSE_KERNEL)) ||			\
-    (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) &&	\
-     defined(CONFIG_SUSE_KERNEL))
-static inline void *bsg_job_sense(struct fc_bsg_job *job)
+static inline
+int blkdev_issue_discard_backport(struct block_device *bdev, sector_t sector,
+		sector_t nr_sects, gfp_t gfp_mask)
 {
-	return job->req->sense;
+	return blkdev_issue_discard(bdev, sector, nr_sects, gfp_mask, 0);
 }
-#else
-static inline void *bsg_job_sense(struct bsg_job *job)
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
-	return job->req->sense;
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0) &&	\
-	!defined(CONFIG_SUSE_KERNEL)
-	return scsi_req(job->req)->sense;
-#else
-	return scsi_req(blk_mq_rq_from_pdu(job))->sense;
+
+#define blkdev_issue_discard blkdev_issue_discard_backport
 #endif
-}
-#endif
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) */
 
 /* <linux/byteorder/generic.h> */
 /*
@@ -207,25 +254,18 @@
 	for (i = 0; i < len; i++)
 		dst[i] = cpu_to_be32(src[i]);
 }
+
+static inline void be32_to_cpu_array(u32 *dst, const __be32 *src, size_t len)
+{
+	int i;
+
+	for (i = 0; i < len; i++)
+		dst[i] = be32_to_cpu(src[i]);
+}
 #endif
 
 /* <linux/compiler.h> */
 
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 20)
-#ifndef __printf
-#define __printf(a, b) __attribute__((format(printf, a, b)))
-#endif
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21)
-#ifndef __aligned
-#define __aligned(x) __attribute__((aligned(x)))
-#endif
-#ifndef __packed
-#define __packed __attribute__((packed))
-#endif
-#endif
-
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 19, 0) && !defined(READ_ONCE)
 /*
  * See also patch "kernel: Provide READ_ONCE and ASSIGN_ONCE" (commit ID
@@ -233,18 +273,6 @@
  */
 #define READ_ONCE(x) (*(volatile typeof(x) *)&(x))
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
-#define ACCESS_ONCE(x) READ_ONCE(x)
-#endif
-
-#endif
-
-/*
- * See also commit e0fdb0e050ea ("percpu: add __percpu for sparse.")
- * # v2.6.34.
- */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) && !defined(__percpu)
-#define __percpu
 #endif
 
 /* <linux/compiler_attributes.h> */
@@ -265,119 +293,37 @@
 #endif
 #endif
 
-/* <linux/cpumask.h> */
+/* <linux/debugfs.h> */
 
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 20) && !defined(BACKPORT_LINUX_CPUMASK_H)
-#define nr_cpu_ids NR_CPUS
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28) && defined(__LINUX_CPUMASK_H)
 /*
- * See also patch "cpumask: introduce new API, without changing anything"
- * (commit ID 2d3854a37e8b).
+ * See also commit c64688081490 ("debugfs: add support for self-protecting
+ * attribute file fops") # v4.7.
  */
-typedef cpumask_t cpumask_var_t[1];
-#define cpumask_bits(maskp) ((maskp)->bits)
-#ifdef CONFIG_CPUMASK_OFFSTACK
-/*
- * Assuming NR_CPUS is huge, a runtime limit is more efficient.  Also,
- * not all bits may be allocated.
- */
-#define nr_cpumask_bits nr_cpu_ids
-#else
-#define nr_cpumask_bits NR_CPUS
-#endif
-
-#ifdef CONFIG_CPUMASK_OFFSTACK
-bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags);
-void free_cpumask_var(cpumask_var_t mask);
-#else
-static inline void free_cpumask_var(cpumask_var_t mask)
-{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)
+#define DEFINE_DEBUGFS_ATTRIBUTE(__fops, __get, __set, __fmt)		\
+static int __fops ## _open(struct inode *inode, struct file *file)	\
+{									\
+	__simple_attr_check_format(__fmt, 0ull);			\
+	return simple_attr_open(inode, file, __get, __set, __fmt);	\
+}									\
+static const struct file_operations __fops = {				\
+	.owner	 = THIS_MODULE,						\
+	.open	 = __fops ## _open,					\
+	.release = simple_attr_release,					\
+	.read	 = debugfs_attr_read,					\
+	.write	 = debugfs_attr_write,					\
+	.llseek  = no_llseek,						\
 }
 
-static inline bool alloc_cpumask_var(cpumask_var_t *mask, gfp_t flags)
+static inline ssize_t debugfs_attr_read(struct file *file, char __user *buf,
+					size_t len, loff_t *ppos)
 {
-	return true;
+	return -ENOENT;
 }
-#endif
-
-/* verify cpu argument to cpumask_* operators */
-static inline unsigned int cpumask_check(unsigned int cpu)
+static inline ssize_t debugfs_attr_write(struct file *file,
+		const char __user *buf, size_t len, loff_t *ppos)
 {
-#ifdef CONFIG_DEBUG_PER_CPU_MAPS
-	WARN_ON_ONCE(cpu >= nr_cpumask_bits);
-#endif /* CONFIG_DEBUG_PER_CPU_MAPS */
-	return cpu;
-}
-
-/**
- * cpumask_next - get the next cpu in a cpumask
- * @n: the cpu prior to the place to search (ie. return will be > @n)
- * @srcp: the cpumask pointer
- *
- * Returns >= nr_cpu_ids if no further cpus set.
- */
-static inline unsigned int cpumask_next(int n, const cpumask_t *srcp)
-{
-	/* -1 is a legal arg here. */
-	if (n != -1)
-		cpumask_check(n);
-	return find_next_bit(cpumask_bits(srcp), nr_cpumask_bits, n+1);
-}
-
-/**
- * for_each_cpu - iterate over every cpu in a mask
- * @cpu: the (optionally unsigned) integer iterator
- * @mask: the cpumask pointer
- *
- * After the loop, cpu is >= nr_cpu_ids.
- */
-#define for_each_cpu(cpu, mask)                         \
-	for ((cpu) = -1;                                \
-		(cpu) = cpumask_next((cpu), (mask)),    \
-		(cpu) < nr_cpu_ids;)
-
-/**
- * cpumask_set_cpu - set a cpu in a cpumask
- * @cpu: cpu number (< nr_cpu_ids)
- * @dstp: the cpumask pointer
- */
-static inline void cpumask_set_cpu(unsigned int cpu, cpumask_t *dstp)
-{
-	set_bit(cpu, cpumask_bits(dstp));
-}
-
-/**
- * cpumask_copy - *dstp = *srcp
- * @dstp: the result
- * @srcp: the input cpumask
- */
-static inline void cpumask_copy(cpumask_t *dstp,
-				const cpumask_t *srcp)
-{
-	bitmap_copy(cpumask_bits(dstp), cpumask_bits(srcp), nr_cpumask_bits);
-}
-
-/**
- * cpumask_setall - set all cpus (< nr_cpu_ids) in a cpumask
- * @dstp: the cpumask pointer
- */
-static inline void cpumask_setall(cpumask_t *dstp)
-{
-	bitmap_fill(cpumask_bits(dstp), nr_cpumask_bits);
-}
-
-/**
- * cpumask_equal - *src1p == *src2p
- * @src1p: the first input
- * @src2p: the second input
- */
-static inline bool cpumask_equal(const cpumask_t *src1p,
-				 const cpumask_t *src2p)
-{
-	return bitmap_equal(cpumask_bits(src1p), cpumask_bits(src2p),
-			    nr_cpumask_bits);
+	return -ENOENT;
 }
 #endif
 
@@ -394,13 +340,6 @@
 
 /* <linux/dlm.h> */
 
-/* See also commit 0f8e0d9a317406612700426fad3efab0b7bbc467 */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
-enum {
-	DLM_LSFL_NEWEXCL = 0
-};
-#endif
-
 /* <linux/dmapool.h> */
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) && \
@@ -467,34 +406,6 @@
 
 /* <linux/fs.h> */
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0) && \
-	!defined(CONFIG_COMPAT_KERNEL_3_12)
-/*
- * See also patch "new helper: file_inode(file)" (commit ID
- * 496ad9aa8ef448058e36ca7a787c61f2e63f0f54). See also patch
- * "kill f_dentry macro" (commit ID 78d28e651f97).
- */
-static inline struct inode *file_inode(const struct file *f)
-{
-	return f->f_dentry->d_inode;
-}
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
-static inline int vfs_fsync_backport(struct file *file, int datasync)
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
-	struct inode *inode = file_inode(file);
-
-	return sync_page_range(inode, file->f_mapping, 0, i_size_read(inode));
-#else
-	return vfs_fsync(file, file->f_path.dentry, datasync);
-#endif
-}
-
-#define vfs_fsync vfs_fsync_backport
-#endif
-
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0)
 /* See also commit dde0c2e79848 ("fs: add IOCB_SYNC and IOCB_DSYNC") */
 #define IOCB_DSYNC 0
@@ -539,7 +450,6 @@
 #define kernel_read(file, buf, count, pos)			\
 	kernel_read_backport((file), (buf), (count), (pos))
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) || defined(RHEL_MAJOR)
 /*
  * See also commit 7bb307e894d5 ("export kernel_write(), convert open-coded
  * instances") # v3.9.
@@ -561,30 +471,11 @@
 }
 
 #define kernel_write kernel_write_backport
-#else
-ssize_t kernel_write(struct file *file, const void *buf, size_t count,
-		     loff_t *pos);
-#endif
-#endif
-
-/* <linux/interrupt.h> */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) && !defined(RHEL_MAJOR)
-/*
- * See also commit cd7eab44e994 ("genirq: Add IRQ affinity notifiers";
- * v2.6.39).
- */
-struct irq_affinity_notify;
-static inline int
-irq_set_affinity_notifier(unsigned int irq, struct irq_affinity_notify *notify)
-{
-	return 0;
-}
 #endif
 
 /* <linux/iocontext.h> */
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25) ||	  \
-	LINUX_VERSION_CODE >= KERNEL_VERSION(4, 21, 0) || \
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 21, 0) || \
 	(defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 8)
 
 static inline struct io_context *
@@ -606,146 +497,6 @@
 #define put_io_context scst_put_io_context
 #define ioc_task_link scst_ioc_task_link
 
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25) && \
-	LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
-static inline struct io_context *get_task_io_context(struct task_struct *task,
-						     gfp_t gfp_flags, int node)
-{
-	WARN_ON_ONCE(task != current);
-	return get_io_context(gfp_flags, node);
-}
-#endif
-
-/* <linux/kconfig.h> */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) && !defined(RHEL_MAJOR)
-/*
- * See also commit 2a11c8ea20bf ("kconfig: Introduce IS_ENABLED(), IS_BUILTIN()
- * and IS_MODULE()") # v3.1.
- */
-#define __ARG_PLACEHOLDER_1 0,
-#define __take_second_arg(__ignored, val, ...) val
-#define __or(x, y)			___or(x, y)
-#define ___or(x, y)			____or(__ARG_PLACEHOLDER_##x, y)
-#define ____or(arg1_or_junk, y)		__take_second_arg(arg1_or_junk 1, y)
-#define __is_defined(x)			___is_defined(x)
-#define ___is_defined(val)		____is_defined(__ARG_PLACEHOLDER_##val)
-#define ____is_defined(arg1_or_junk)	__take_second_arg(arg1_or_junk 1, 0)
-#define IS_BUILTIN(option) __is_defined(option)
-#define IS_MODULE(option) __is_defined(option##_MODULE)
-#define IS_ENABLED(option) __or(IS_BUILTIN(option), IS_MODULE(option))
-#endif
-
-/* <linux/kernel.h> */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19)
-#ifndef RHEL_RELEASE_CODE
-typedef _Bool bool;
-#endif
-#define true  1
-#define false 0
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
-#ifndef swap
-#define swap(a, b) \
-	do { typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while (0)
-#endif
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) &&	\
-	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 6 ||	\
-	 RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 < 1)
-extern int hex_to_bin(char ch);
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) && !defined(RHEL_MAJOR)
-/* See also commit 9b3be9f99203 ("Move round_up/down to kernel.h") # v2.6.34 */
-/*
- * This looks more complex than it should be. But we need to
- * get the type for the ~ right in round_down (it needs to be
- * as wide as the result!), and we want to evaluate the macro
- * arguments just once each.
- */
-#define __round_mask(x, y) ((__typeof__(x))((y)-1))
-/**
- * round_up - round up to next specified power of 2
- * @x: the value to round
- * @y: multiple to round up to (must be a power of 2)
- *
- * Rounds @x up to next multiple of @y (which must be a power of 2).
- * To perform arbitrary rounding up, use roundup() below.
- */
-#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1)
-/**
- * round_down - round down to next specified power of 2
- * @x: the value to round
- * @y: multiple to round down to (must be a power of 2)
- *
- * Rounds @x down to next multiple of @y (which must be a power of 2).
- * To perform arbitrary rounding down, use rounddown() below.
- */
-#define round_down(x, y) ((x) & ~__round_mask(x, y))
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
-/*
- * See also "lib: hex2bin converts ascii hexadecimal string to binary" (commit
- * dc88e46029486ed475c71fe1bb696d39511ac8fe).
- */
-static inline void hex2bin(u8 *dst, const char *src, size_t count)
-{
-	while (count--) {
-		*dst = hex_to_bin(*src++) << 4;
-		*dst += hex_to_bin(*src++);
-		dst++;
-	}
-}
-#endif
-
-/*
- * See also commit 33ee3b2e2eb9. That commit was introduced in kernel v2.6.39
- * and later backported to kernel v2.6.38.4.
- */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) &&		\
-	LINUX_VERSION_CODE != KERNEL_VERSION(2, 6, 38) &&	\
-	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 6)
-static inline int __must_check kstrtoull(const char *s, unsigned int base,
-					 unsigned long long *res)
-{
-	return strict_strtoull(s, base, res);
-}
-
-static inline int __must_check kstrtoll(const char *s, unsigned int base,
-					long long *res)
-{
-	return strict_strtoll(s, base, res);
-}
-
-static inline int __must_check kstrtoul(const char *s, unsigned int base,
-					unsigned long *res)
-{
-	return strict_strtoul(s, base, res);
-}
-
-static inline int __must_check kstrtol(const char *s, unsigned int base,
-				       long *res)
-{
-	return strict_strtol(s, base, res);
-}
-
-static inline int __must_check kstrtoint(const char *s, unsigned int base,
-					 int *result)
-{
-	long val;
-	int ret = strict_strtol(s, base, &val);
-
-	if (ret)
-		return ret;
-	*result = val;
-	if (*result != val)
-		return -EINVAL;
-	return 0;
-}
 #endif
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)
@@ -823,31 +574,11 @@
 #define get_user_pages get_user_pages_backport
 #endif
 
-/* <linux/kmod.h> */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)
-enum umh_wait {
-	UMH_NO_WAIT = -1,       /* don't wait at all */
-	UMH_WAIT_EXEC = 0,      /* wait for the exec, but not the process */
-	UMH_WAIT_PROC = 1,      /* wait for the process to complete */
-};
-#endif
-
 /* <linux/kobject_ns.h> */
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
-/*
- * See also commit 608b4b9548de ("netns: Teach network device kobjects which
- * namespace they are in.") # v2.6.35.
- */
-enum kobj_ns_type {
-	KOBJ_NS_TYPE_NET = 1,
-};
-#endif
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) &&		      \
-	LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0) &&	      \
-	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 7)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0) && 		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(7, 6))
 /*
  * See also commit 5f256becd868 ("[NET]: Basic network namespace
  * infrastructure."; v2.6.24). a685e08987d1 ("Delay struct net freeing while
@@ -871,46 +602,14 @@
 
 /* <linux/kref.h> */
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) &&		      \
-	!(LINUX_VERSION_CODE >> 8 == KERNEL_VERSION(3, 4, 0) >> 8 &&  \
-	  LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 41)) &&	      \
-	!(LINUX_VERSION_CODE >> 8 == KERNEL_VERSION(3, 2, 0) >> 8 &&  \
-	  LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 44)) &&	      \
-	(!defined(CONFIG_SUSE_KERNEL) ||			      \
-	 LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 101)) &&	      \
-	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 6 ||		      \
-	 (RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 < 6))
-/*
- * See also commit 4b20db3 (kref: Implement kref_get_unless_zero v3 -- v3.8).
- * See also commit e3a5505 in branch stable/linux-3.4.y (v3.4.41).
- * See also commit 3fa8ee5 in branch stable/linux-3.2.y (v3.2.44).
- * See also commit 6b9508d in the SuSE kernel tree.
- */
-static inline int __must_check kref_get_unless_zero(struct kref *kref)
-{
-	return atomic_add_unless(&kref->refcount, 1, 0);
-}
-#endif
-
 /* See also commit 2c935bc57221 */
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
 #define kref_read(kref) (atomic_read(&(kref)->refcount))
 #endif
 
-/* <linux/kthread.h> */
-
-/* See also commit 207205a2ba26 */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39) && \
-	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 6 || \
-	 RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 < 9)
-#define kthread_create_on_node(threadfn, data, node, namefmt, arg...)\
-	kthread_create((threadfn), (data), (namefmt), ##arg)
-#endif
-
 /* <linux/ktime.h> */
 
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) &&		\
-	LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0)) &&	\
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0)) &&	\
 	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 7)
 /**
  * ktime_before - Compare if a ktime_t value is smaller than another one.
@@ -943,10 +642,6 @@
 
 /* <linux/lockdep.h> */
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
-#define lockdep_assert_held(l) (void)(l)
-#endif
-
 /*
  * See also commit 108c14858b9e ("locking/lockdep: Add support for dynamic
  * keys").
@@ -1094,6 +789,30 @@
 }
 #endif
 
+/* <linux/shrinker.h> */
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 0, 0)
+/*
+ * See also commit e33c267ab70d ("mm: shrinkers: provide shrinkers with
+ * names") # v6.0.
+ */
+static inline
+int register_shrinker_backport(struct shrinker *shrinker, const char *fmt, ...)
+{
+/*
+ * See also commit 1d3d4437eae1 ("vmscan: per-node deferred work") # v3.12
+ */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
+	return register_shrinker(shrinker);
+#else
+	register_shrinker(shrinker);
+	return 0;
+#endif
+}
+
+#define register_shrinker register_shrinker_backport
+#endif
+
 /* <linux/module.h> */
 #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 4, 0)
 #define MODULE_IMPORT_NS(ns)
@@ -1140,30 +859,6 @@
 } __aligned(sizeof(u64));	/* alignment for other things alloc'd with */
 #endif
 
-/* <linux/pci.h> */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 7, 0) && !defined(RHEL_MAJOR)
-/*
- * See also commit 8c0d3a02c130 ("PCI: Add accessors for PCI Express
- * Capability") # v3.7.
- */
-static inline int pcie_capability_read_word(struct pci_dev *dev, int pos,
-					    u16 *val)
-{
-	WARN_ON_ONCE(true);
-	*val = 0;
-	return -EOPNOTSUPP;
-}
-
-static inline int pcie_capability_read_dword(struct pci_dev *dev, int pos,
-					     u32 *val)
-{
-	WARN_ON_ONCE(true);
-	*val = 0;
-	return -EOPNOTSUPP;
-}
-#endif
-
 /* <linux/percpu-refcount.h> */
 
 #if defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 7 ||	\
@@ -1171,7 +866,9 @@
 #include <linux/percpu-refcount.h>
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 3, 0) &&	\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(8, 3))
 /*
  * See also commit 09ed79d6d75f ("percpu_ref: introduce PERCPU_REF_ALLOW_REINIT
  * flag") # v5.3.
@@ -1185,7 +882,7 @@
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0) &&	\
 	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 7)
-typedef unsigned percpu_count_t;
+typedef unsigned int percpu_count_t;
 #define READ_REF_COUNT(ref) atomic_read(&(ref)->count)
 #else
 typedef unsigned long percpu_count_t;
@@ -1435,144 +1132,8 @@
 	return READ_REF_COUNT(ref) - !percpu_ref_is_dying(ref);
 }
 
-/* <linux/preempt.h> */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
-/*
- * See also patch "sched: Fix softirq time accounting" (commit ID
- * 75e1056f5c57050415b64cb761a3acc35d91f013).
- */
-#ifndef in_serving_softirq
-#define in_serving_softirq() in_softirq()
-#endif
-#endif
-
-/* <linux/printk.h> */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) && !defined(RHEL_MAJOR)
-#define KERN_CONT       ""
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
-/*
- * See also the following commits:
- * d091c2f5 - Introduction of pr_info() etc. in <linux/kernel.h>.
- * 311d0761 - Introduction of pr_cont() in <linux/kernel.h>.
- * 968ab183 - Moved pr_info() etc. from <linux/kernel.h> to <linux/printk.h>
- */
-#ifndef pr_emerg
-
-#ifndef pr_fmt
-#define pr_fmt(fmt) fmt
-#endif
-
-#define pr_emerg(fmt, ...)	printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_alert(fmt, ...)	printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_crit(fmt, ...)	printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_err(fmt, ...)	printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_warn(fmt, ...)	printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
-#define pr_notice(fmt, ...)	printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
-
-#endif /* pr_emerg */
-
-#ifndef pr_info
-#define pr_info(fmt, ...)	printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
-#endif
-
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28) */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
-#ifndef pr_cont
-#define pr_cont(fmt, ...)	printk(KERN_CONT fmt, ##__VA_ARGS__)
-#endif
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) */
-
-/* See also commit f036be96dd9c ("printk: introduce printk_once()") */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
-#define printk_once(fmt, ...)					\
-({								\
-	static bool __print_once __read_mostly;			\
-	bool __ret_print_once = !__print_once;			\
-								\
-	if (!__print_once) {					\
-		__print_once = true;				\
-		printk(fmt, ##__VA_ARGS__);			\
-	}							\
-	unlikely(__ret_print_once);				\
-})
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
-/*
- * See also commit 16cb839f1332 ("include/linux/printk.h: add pr_<level>_once
- * macros") # v2.6.38.
- */
-#define pr_warn_once(fmt, ...)					\
-	printk_once(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)
-/*
- * See also patch "kernel.h: add pr_warn for symmetry to dev_warn,
- * netdev_warn" (commit fc62f2f19edf46c9bdbd1a54725b56b18c43e94f).
- */
-#ifndef pr_warn
-#define pr_warn pr_warning
-#endif
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) && \
-	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 6)
-/*
- * See also patch "Add a dummy printk function for the maintenance of unused
- * printks" (commit 12fdff3fc2483f906ae6404a6e8dcf2550310b6f).
- */
-static inline __attribute__ ((format (printf, 1, 2)))
-int no_printk(const char *s, ...) { return 0; }
-#endif
-
-/* <linux/ratelimit.h> */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27)
-/* See also commit 717115e1a585 */
-
-#define DEFAULT_RATELIMIT_INTERVAL (5 * HZ)
-#define DEFAULT_RATELIMIT_BURST 10
-
-struct ratelimit_state {
-	int interval;
-	int burst;
-};
-
-#define DEFINE_RATELIMIT_STATE(name, interval, burst)	\
-	struct ratelimit_state name = {interval, burst,}
-
-static inline int __ratelimit(struct ratelimit_state *rs)
-{
-	return 1;
-}
-#endif
-
 /* <linux/rcupdate.h> */
 
-/* See also commit b62730baea32 */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34)
-#define rcu_dereference_protected(p, c) rcu_dereference(p)
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0) && !defined(kfree_rcu)
-typedef void (*rcu_callback_t)(struct rcu_head *);
-#define __is_kfree_rcu_offset(offset) ((offset) < 4096)
-#define kfree_call_rcu(head, rcb) call_rcu(head, rcb)
-#define __kfree_rcu(head, offset)				\
-	do {							\
-		BUILD_BUG_ON(!__is_kfree_rcu_offset(offset));	\
-		kfree_call_rcu(head, (rcu_callback_t)(unsigned long)(offset)); \
-	} while (0)
-#define kfree_rcu(ptr, rcu_head)				\
-	__kfree_rcu(&((ptr)->rcu_head), offsetof(typeof(*(ptr)), rcu_head))
-#endif
-
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0) &&	\
 	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 7 ||	\
 	 RHEL_MAJOR -0 == 7 && RHEL_MINOR -0 < 7)
@@ -1584,95 +1145,6 @@
 static inline void destroy_rcu_head(struct rcu_head *head) { }
 #endif
 
-/* <linux/scatterlist.h> */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
-/*
- * The macro's sg_page(), sg_virt(), sg_init_table(), sg_assign_page() and
- * sg_set_page() have been introduced in the 2.6.24 kernel. The definitions
- * below are backports of the 2.6.24 macro's for older kernels. There is one
- * exception however: when compiling SCST on a system with a pre-2.6.24 kernel
- * (e.g. RHEL 5.x) where the OFED kernel headers have been installed, do not
- * define the backported macro's because OFED has already defined these.
- */
-
-static inline bool sg_is_chain(struct scatterlist *sg)
-{
-	return false;
-}
-
-static inline struct scatterlist *sg_chain_ptr(struct scatterlist *sg)
-{
-	return NULL;
-}
-
-#define sg_is_last(sg) false
-
-#ifndef sg_page
-static inline struct page *sg_page(struct scatterlist *sg)
-{
-	return sg->page;
-}
-#endif
-
-static inline void *sg_virt(struct scatterlist *sg)
-{
-	return page_address(sg_page(sg)) + sg->offset;
-}
-
-static inline void sg_mark_end(struct scatterlist *sg)
-{
-}
-
-static inline void sg_unmark_end(struct scatterlist *sg)
-{
-}
-
-#ifndef __BACKPORT_LINUX_SCATTERLIST_H_TO_2_6_23__
-
-static inline void sg_init_table(struct scatterlist *sgl, unsigned int nents)
-{
-	memset(sgl, 0, sizeof(*sgl) * nents);
-}
-
-static inline void sg_assign_page(struct scatterlist *sg, struct page *page)
-{
-	sg->page = page;
-}
-
-static inline void sg_set_page(struct scatterlist *sg, struct page *page,
-			       unsigned int len, unsigned int offset)
-{
-	sg_assign_page(sg, page);
-	sg->offset = offset;
-	sg->length = len;
-}
-
-#ifndef for_each_sg
-/* See also commit 96b418c960af0d5c7185ff5c4af9376eb37ac9d3 */
-#define for_each_sg(sglist, sg, nr, __i)       \
-	for (__i = 0, sg = (sglist); __i < (nr); __i++, sg = sg_next_inline(sg))
-#endif /* for_each_sg */
-
-#endif /* __BACKPORT_LINUX_SCATTERLIST_H_TO_2_6_23__ */
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
-/*
- * See also commit c8164d8931fd ("scatterlist: introduce sg_unmark_end";
- * v3.10).
- */
-static inline void sg_unmark_end(struct scatterlist *sg)
-{
-	sg->page_link &= ~0x02;
-}
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) */
-
-/* <linux/sched.h> */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) && \
-	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 6)
-#define set_cpus_allowed_ptr(p, new_mask) set_cpus_allowed((p), *(new_mask))
-#endif
-
 /* <linux/sched/prio.h> */
 
 /*
@@ -1683,14 +1155,30 @@
 #define MIN_NICE -20
 #endif
 
-/* <linux/slab.h> */
+/* <linux/seq_file.h> */
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
-#define KMEM_CACHE(__struct, __flags) kmem_cache_create(#__struct,\
-	sizeof(struct __struct), __alignof__(struct __struct),\
-	(__flags), NULL, NULL)
+/*
+ * See also commit a08f06bb7a07 ("seq_file: Introduce DEFINE_SHOW_ATTRIBUTE()
+ * helper macro") # v4.16.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0)
+#define DEFINE_SHOW_ATTRIBUTE(__name)					\
+static int __name ## _open(struct inode *inode, struct file *file)	\
+{									\
+	return single_open(file, __name ## _show, inode->i_private);	\
+}									\
+									\
+static const struct file_operations __name ## _fops = {			\
+	.owner		= THIS_MODULE,					\
+	.open		= __name ## _open,				\
+	.read		= seq_read,					\
+	.llseek		= seq_lseek,					\
+	.release	= single_release,				\
+}
 #endif
 
+/* <linux/slab.h> */
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0) &&	\
 	!defined(_COMPAT_LINUX_MM_H)
 /*
@@ -1706,36 +1194,14 @@
 #define kmem_cache_destroy kmem_cache_destroy_backport
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0) &&	    \
-	!(LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 52) && \
-	  LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)) &&  \
-	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 6)
-static inline void *kmalloc_array(size_t n, size_t size, gfp_t flags)
-{
-	if (size != 0 && n > ULONG_MAX / size)
-		return NULL;
-	return kmalloc(n * size, flags);
-}
-#endif
-
 /*
  * See also commit 8eb8284b4129 ("usercopy: Prepare for usercopy
  * whitelisting").
- */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23)
-static inline struct kmem_cache *kmem_cache_create_usercopy(const char *name,
-			unsigned int size, unsigned int align,
-			unsigned long flags,
-			unsigned int useroffset, unsigned int usersize,
-			void (*ctor)(void *))
-{
-	return kmem_cache_create(name, size, align, flags, ctor, NULL);
-}
-/*
+ *
  * UEK4 is based on kernel v4.1.12 and does not have a backport of the v4.16
  * API. UEK5 is based on kernel v4.14.35 and has a backport of the v4.16 API.
  */
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0) &&	\
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 16, 0) &&	\
 	(!defined(UEK_KABI_RENAME) ||			\
 	 LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0))
 static inline struct kmem_cache *kmem_cache_create_usercopy(const char *name,
@@ -1760,11 +1226,16 @@
 /* <linux/sockptr.h> */
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
+#if !defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 8 ||	\
+	 RHEL_MAJOR -0 == 8 && RHEL_MINOR -0 < 4
 /* See also commit ba423fdaa589 ("net: add a new sockptr_t type") # v5.9 */
 static inline void __user *KERNEL_SOCKPTR(void *p)
 {
 	return (void __force __user *)p;
 }
+#else
+#define KERNEL_SOCKPTR(p) ((char __force __user *)p)
+#endif
 #endif
 
 /* <linux/stddef.h> */
@@ -1854,20 +1325,6 @@
 		(unsigned long)&(_name))
 #endif
 
-/* <linux/types.h> */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
-/*
- * See also patch "fix abuses of ptrdiff_t" (commit ID
- * 142956af525002c5378e7d91d81a01189841a785).
- */
-typedef unsigned long uintptr_t;
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
-char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap);
-#endif
-
 /* <linux/uio.h> */
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
@@ -1896,39 +1353,9 @@
 
 /* <linux/unaligned.h> */
 
-#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 <= 5
-static inline uint16_t get_unaligned_be16(const void *p)
-{
-	return be16_to_cpu(get_unaligned((__be16 *)p));
-}
-
-static inline void put_unaligned_be16(uint16_t i, void *p)
-{
-	put_unaligned(cpu_to_be16(i), (__be16 *)p);
-}
-
-static inline uint32_t get_unaligned_be32(const void *p)
-{
-	return be32_to_cpu(get_unaligned((__be32 *)p));
-}
-
-static inline void put_unaligned_be32(uint32_t i, void *p)
-{
-	put_unaligned(cpu_to_be32(i), (__be32 *)p);
-}
-
-static inline uint64_t get_unaligned_be64(const void *p)
-{
-	return be64_to_cpu(get_unaligned((__be64 *)p));
-}
-
-static inline void put_unaligned_be64(uint64_t i, void *p)
-{
-	put_unaligned(cpu_to_be64(i), (__be64 *)p);
-}
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 7, 0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 7, 0) && \
+	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 8 ||	\
+	 RHEL_MAJOR -0 == 8 && RHEL_MINOR -0 < 4)
 /* Only use get_unaligned_be24() if reading p - 1 is allowed. */
 static inline uint32_t get_unaligned_be24(const uint8_t *const p)
 {
@@ -1943,57 +1370,6 @@
 }
 #endif
 
-/* <linux/vmalloc.h> */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37) && \
-	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 5 || \
-	 RHEL_MAJOR -0 == 5 && RHEL_MINOR -0 < 10 || \
-	 RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 < 1)
-/*
- * See also patch "mm: add vzalloc() and vzalloc_node() helpers" (commit
- * e1ca7788dec6773b1a2bce51b7141948f2b8bccf).
- */
-static inline void *vzalloc(unsigned long size)
-{
-	return __vmalloc(size, GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO,
-			 PAGE_KERNEL);
-}
-#endif
-
-/* <linux/workqueue.h> */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
-/*
- * See also commit d320c03830b1 ("workqueue: s/__create_workqueue()/
- * alloc_workqueue()/, and add system workqueues") # v2.6.36.
- */
-static inline struct workqueue_struct *alloc_workqueue(const char *fmt,
-						       unsigned int flags,
-						       int max_active, ...)
-{
-	WARN_ON_ONCE(flags | max_active);
-	return create_workqueue(fmt);
-}
-#endif
-
-/*
- * See also commits 18aa9effad4a ("workqueue: implement WQ_NON_REENTRANT";
- * v2.6.36) and commits dbf2576e37da ("workqueue: make all workqueues
- * non-reentrant"; v3.7).
- */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) || \
-	LINUX_VERSION_CODE >= KERNEL_VERSION(3, 7, 0)
-#define WQ_NON_REENTRANT 0
-#endif
-
-/*
- * See also commit 226223ab3c41 ("workqueue: implement sysfs interface for
- * workqueues"; v3.10).
- */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
-#define WQ_SYSFS 0
-#endif
-
 /* <rdma/ib_verbs.h> */
 
 /* commit ed082d36 */
@@ -2008,10 +1384,40 @@
 	})
 #endif
 
+/* <scsi/scsi.h> */
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 14, 0)
+#ifndef msg_byte
+/*
+ * See also commit 54cf31d07aa8 ("scsi: core: Drop message byte helper";
+ * v5.14-rc1).
+ */
+static inline uint8_t msg_byte(uint32_t result)
+{
+	return (result >> 8) & 0xff;
+}
+#endif
+#ifndef host_byte
+static inline uint8_t host_byte(uint32_t result)
+{
+	return (result >> 16) & 0xff;
+}
+#endif
+#ifndef driver_byte
+/*
+ * See also commit 54c29086195f ("scsi: core: Drop the now obsolete driver_byte
+ * definitions"; v5.14-rc1).
+ */
+static inline uint8_t driver_byte(uint32_t result)
+{
+	return (result >> 24) & 0xff;
+}
+#endif
+#endif
+
 /* <scsi/scsi_cmnd.h> */
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24) || \
-	LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) || \
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) || \
 	(defined(RHEL_RELEASE_CODE) &&			 \
 	 RHEL_RELEASE_CODE -0 >= RHEL_RELEASE_VERSION(8, 3))
 /*
@@ -2035,8 +1441,110 @@
 }
 #endif
 
+/*
+ * The Debian 5.13.0 kernel has a scsi_build_sense() definition but does not
+ * define bio_multiple_segments() while the upstream 5.13.0 kernel defines
+ * bio_multiple_segments(). Hence the check two lines below for the Debian
+ * kernel.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0) && \
+	(LINUX_VERSION_CODE >> 8 != KERNEL_VERSION(5, 13, 0) >> 8 ||	\
+	 defined(bio_multiple_segments))
+/*
+ * See also commit f2b1e9c6f867 ("scsi: core: Introduce scsi_build_sense()";
+ * v5.14-rc1).
+ */
+static inline void scsi_build_sense(struct scsi_cmnd *scmd, int desc,
+                            u8 key, u8 asc, u8 ascq)
+{
+	scsi_build_sense_buffer(desc, scmd->sense_buffer, key, asc, ascq);
+	scmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION;
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(8, 7) ||	\
+	 RHEL_RELEASE_CODE -0 == RHEL_RELEASE_VERSION(9, 0))
+/*
+ * See also 51f3a4788928 ("scsi: core: Introduce the scsi_cmd_to_rq()
+ * function").
+ */
+static inline struct request *scsi_cmd_to_rq(struct scsi_cmnd *scmd)
+{
+	return scmd->request;
+}
+#endif
+
+/*
+ * See also commits 7ba46799d346 ("scsi: core: Add scsi_prot_ref_tag()
+ * helper") and ddd0bc756983 ("block: move ref_tag calculation func to the
+ * block layer"; v4.19).
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(9, 1))
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) ||		\
+	(defined(RHEL_RELEASE_CODE) &&				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(8, 7))
+static inline u32 scsi_prot_ref_tag(struct scsi_cmnd *scmd)
+{
+#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 == 7
+	WARN_ON_ONCE(true);
+	return 0;
+#else
+	struct request *rq = blk_mq_rq_from_pdu(scmd);
+
+	return t10_pi_ref_tag(rq);
+#endif
+}
+#endif
+#endif
+
+/*
+ * See also commit c611529e7cd3 ("sd: Honor block layer integrity handling
+ * flags"; v3.18).
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0)
+static inline unsigned int scsi_prot_interval(struct scsi_cmnd *scmd)
+{
+	/* To do: backport this function properly. */
+	WARN_ON_ONCE(true);
+	return 512;
+}
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(9, 1))
+/*
+ * See also commit 11b68e36b167 ("scsi: core: Call scsi_done directly"; v5.16)
+ */
+static inline void scsi_done(struct scsi_cmnd *cmd)
+{
+	return cmd->scsi_done(cmd);
+}
+#endif
+
 /* <scsi/scsi_request.h> */
 
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)
+/*
+ * See also commit 6aded12b10e0 ("scsi: core: Remove struct scsi_request") # v5.18
+ */
+static inline struct scsi_cmnd *scsi_req(struct request *rq)
+{
+	return blk_mq_rq_to_pdu(rq);
+}
+
+#define SREQ_SENSE(req) ((req)->sense_buffer)
+#define SREQ_CP(req)    ((req)->cmnd)
+#else
+#define SREQ_SENSE(req) ((req)->sense)
+#define SREQ_CP(req)    ((req)->cmd)
+#endif
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
 static inline struct request *scsi_req(struct request *rq)
 {
@@ -2047,13 +1555,8 @@
 {
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0)
 	rq->cmd_type = REQ_TYPE_BLOCK_PC;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
-	rq->data_len = 0;
-	rq->sector = (sector_t) -1;
-#else
 	rq->__data_len = 0;
 	rq->__sector = (sector_t) -1;
-#endif
 	rq->bio = rq->biotail = NULL;
 	memset(rq->__cmd, 0, sizeof(rq->__cmd));
 	rq->cmd = rq->__cmd;
@@ -2063,6 +1566,34 @@
 }
 #endif
 
+/* <linux/bsg-lib.h> */
+
+/*
+ * Note: the function bsg_job_sense() exists only in SCST but not in any
+ * upstream kernel.
+ */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) &&	\
+     !defined(CONFIG_SUSE_KERNEL)) ||			\
+    (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0) &&	\
+     defined(CONFIG_SUSE_KERNEL))
+static inline void *bsg_job_sense(struct fc_bsg_job *job)
+{
+	return job->req->sense;
+}
+#else
+static inline void *bsg_job_sense(struct bsg_job *job)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+	return job->req->sense;
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0) &&	\
+	!defined(CONFIG_SUSE_KERNEL)
+	return scsi_req(job->req)->sense;
+#else
+	return SREQ_SENSE(scsi_req(blk_mq_rq_from_pdu(job)));
+#endif
+}
+#endif
+
 /* <scsi/scsi_transport_fc.h> */
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 15, 0) &&	\
@@ -2106,16 +1637,44 @@
 #define wwn_to_u64(wwn) get_unaligned_be64(wwn)
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 3, 0) &&		\
+	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 8 ||		\
+	 RHEL_MAJOR -0 == 8 && RHEL_MINOR -0 < 9 ||		\
+	 RHEL_MAJOR -0 == 9 && RHEL_MINOR -0 < 3)
 /*
- * See also commit c39e0af64bce ("scsi: scsi_transport_fc: Add FPIN fc event
- * codes") # v5.2
+ * See also commit 64fd2ba977b1 ("scsi: scsi_transport_fc: Add an additional
+ * flag to fc_host_fpin_rcv()") # v6.3
  */
+static inline void
+fc_host_fpin_rcv_backport(struct Scsi_Host *shost, u32 fpin_len, char *fpin_buf,
+			  u8 event_acknowledge)
+{
 #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0) && \
 	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 8 ||	\
 	 RHEL_MAJOR -0 == 8 && RHEL_MINOR -0 < 2)
-static inline void
-fc_host_fpin_rcv(struct Scsi_Host *shost, u32 fpin_len, char *fpin_buf)
+	/*
+	 * See also commit c39e0af64bce ("scsi: scsi_transport_fc: Add FPIN fc event
+	 * codes") # v5.2
+	 */
+	return;
+#else
+	return fc_host_fpin_rcv(shost, fpin_len, fpin_buf);
+#endif
+}
+
+#define fc_host_fpin_rcv fc_host_fpin_rcv_backport
+#endif
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0)
+/*
+ * See also commit 67b465250e04 ("scsi: fc: start decoupling fc_block_scsi_eh
+ * from scsi_cmnd"; v4.14).
+ */
+static inline int fc_block_rport(struct fc_rport *rport)
 {
+	/* To do: backport this function. */
+	WARN_ON_ONCE(true);
+	return 0;
 }
 #endif
 
@@ -2132,7 +1691,10 @@
  * See also commit 62e9dd177732 ("scsi: qla2xxx: Change in PUREX to handle FPIN
  * ELS requests").
  */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) &&			\
+	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 8 ||			\
+	 RHEL_MAJOR -0 == 8 && RHEL_MINOR -0 < 4) &&			\
+	!(defined(UEK_KABI_RENAME) && defined(FC_PORTSPEED_256GBIT))
 #define ELS_RDP 0x18
 #endif
 
diff --git a/scst/scst/include/scst.h b/scst/scst/include/scst.h
index b5abe41..f83dc4b 100644
--- a/scst/scst/include/scst.h
+++ b/scst/scst/include/scst.h
@@ -45,10 +45,10 @@
 #define CONFIG_SCST_PER_DEVICE_CMD_COUNT_LIMIT
 #endif
 
-#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 <= 5
-#error RHEL 5 is no longer supported. Please upgrade to RHEL 6 or later.
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
-#error The SCST sysfs interface is supported from kernel version 2.6.26 on. Please upgrade to a newer kernel version.
+#if defined(RHEL_MAJOR) && RHEL_MAJOR -0 <= 6
+#error RHEL 6 is no longer supported. Please upgrade to RHEL 7 or later.
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
+#error The SCST has dropped support for kernels older than 3.10.0 since SCST v3.6. Please upgrade to a newer kernel version.
 #endif
 
 #include <scsi/scsi_cmnd.h>
@@ -1006,17 +1006,6 @@
 	void (*on_abort_cmd)(struct scst_cmd *cmd);
 
 	/*
-	 * This function should detect the target adapters that
-	 * are present in the system. The function should return a value
-	 * >= 0 to signify the number of detected target adapters.
-	 * A negative value should be returned whenever there is
-	 * an error.
-	 *
-	 * OBSOLETE
-	 */
-	int (*detect)(struct scst_tgt_template *tgt_template);
-
-	/*
 	 * This function should free up the resources allocated to the device.
 	 * The function should return 0 to indicate successful release
 	 * or a negative value if there are some issues with the release.
@@ -1874,11 +1863,7 @@
 	/* List entry for the sessions list inside ACG */
 	struct list_head acg_sess_list_entry;
 
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20))
 	struct delayed_work hw_pending_work;
-#else
-	struct work_struct hw_pending_work;
-#endif
 
 	/* Name of attached initiator */
 	const char *initiator_name;
@@ -1923,11 +1908,7 @@
 	 */
 	struct list_head sess_cm_list_id_list;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	struct work_struct sess_cm_list_id_cleanup_work;
-#else
 	struct delayed_work sess_cm_list_id_cleanup_work;
-#endif
 
 	/* sysfs release completion */
 	struct completion *sess_kobj_release_cmpl;
@@ -2081,7 +2062,7 @@
 
 	struct scst_session *sess;	/* corresponding session */
 
-	atomic_t *cpu_cmd_counter;
+	bool counted;
 
 	atomic_t cmd_ref;
 
@@ -2341,7 +2322,9 @@
 	uint8_t lba_off;	/* LBA offset in cdb */
 	uint8_t lba_len;	/* LBA length in cdb */
 	uint8_t len_off;	/* length offset in cdb */
-	uint8_t len_len;	/* length length in cdb */
+	uint8_t len_len;	/* length of length in cdb */
+	/* If not zero, logarithm base 2 of the maximum data buffer length. */
+	uint8_t log2_max_buf_len;
 	uint32_t op_flags;	/* various flags of this opcode */
 	const char *op_name;	/* op code SCSI full name */
 
@@ -2571,7 +2554,7 @@
 
 	struct scst_session *sess;
 
-	atomic_t *cpu_cmd_counter;
+	bool counted;
 
 	/* Mgmt cmd state, one of SCST_MCMD_STATE_* constants */
 	int state;
@@ -2845,6 +2828,9 @@
 	atomic_t dev_cmd_count;
 #endif
 
+	/* Number of copy manager designators update requests. */
+	atomic_t cm_update_req_cnt;
+
 	/*
 	 * One more than the number of commands associated with this device
 	 * and the number of SCST data structures holding a reference on this
@@ -4681,10 +4667,9 @@
 #endif
 	if (cmd->cdb_len == 32)
 		return get_unaligned_be16(&cmd->cdb[24]);
-	else {
-		/* cmd->dev must be alive at this point */
-		return be16_to_cpu(cmd->dev->dev_dif_static_app_tag);
-	}
+
+	/* cmd->dev must be alive at this point */
+	return be16_to_cpu(cmd->dev->dev_dif_static_app_tag);
 }
 
 /*
@@ -4699,12 +4684,11 @@
 #endif
 	if (cmd->cdb_len == 32)
 		return get_unaligned_be16(&cmd->cdb[26]);
-	else {
-		if (scst_get_dif_checks(cmd->cmd_dif_actions) & SCST_DIF_CHECK_APP_TAG)
-			return 0xFFFF;
-		else
-			return 0;
-	}
+
+	if (scst_get_dif_checks(cmd->cmd_dif_actions) & SCST_DIF_CHECK_APP_TAG)
+		return 0xFFFF;
+
+	return 0;
 }
 
 /*
@@ -5132,6 +5116,23 @@
 }
 
 /*
+ * Returns approximate higher rounded buffers count in pages
+ */
+static inline int scst_get_buf_page_count(struct scst_cmd *cmd)
+{
+	struct scatterlist *sg;
+	int page_cnt = 0, i;
+
+	if (unlikely(cmd->sg_cnt == 0))
+		return 1;
+
+	for (i = 0, sg = cmd->sg; i < cmd->sg_cnt; i++, sg = sg_next_inline(sg))
+		page_cnt += PAGE_ALIGN(sg->offset + sg->length) >> PAGE_SHIFT;
+
+	return page_cnt;
+}
+
+/*
  * Returns approximate higher rounded buffers count that
  * scst_get_out_buf_[first|next]() return.
  */
@@ -5150,12 +5151,11 @@
 }
 
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) && defined(CONFIG_LOCKDEP)
+#if defined(CONFIG_LOCKDEP)
 extern struct lockdep_map scst_suspend_dep_map;
 #endif
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && \
-	defined(CONFIG_DEBUG_LOCK_ALLOC)
+#if defined(CONFIG_DEBUG_LOCK_ALLOC)
 #define scst_assert_activity_suspended()		\
 	WARN_ON(debug_locks && !lock_is_held(&scst_suspend_dep_map))
 #else
@@ -5204,13 +5204,9 @@
 extern struct mutex scst_mutex;
 
 
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34))
 const struct sysfs_ops *scst_sysfs_get_sysfs_ops(void);
-#else
-struct sysfs_ops *scst_sysfs_get_sysfs_ops(void);
-#endif
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) && defined(CONFIG_LOCKDEP)
+#if defined(CONFIG_LOCKDEP)
 #define SCST_SET_DEP_MAP(work, dm) ((work)->dep_map = (dm))
 #define SCST_KOBJECT_PUT_AND_WAIT(kobj, category, c, dep_map) \
 	scst_kobject_put_and_wait(kobj, category, c, dep_map)
@@ -5522,11 +5518,9 @@
 void scst_sysfs_work_get(struct scst_sysfs_work_item *work);
 void scst_sysfs_work_put(struct scst_sysfs_work_item *work);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
 #ifdef CONFIG_LOCKDEP
 extern struct lockdep_map scst_dev_dep_map;
 #endif
-#endif
 
 
 char *scst_get_next_lexem(char **token_str);
@@ -5538,10 +5532,8 @@
 
 void scst_pass_through_cmd_done(void *data, char *sense, int result, int resid);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
 int scst_scsi_exec_async(struct scst_cmd *cmd, void *data,
 	void (*done)(void *data, char *sense, int result, int resid));
-#endif
 
 int scst_get_file_mode(const char *path);
 bool scst_parent_dir_exists(const char *path);
@@ -5551,6 +5543,9 @@
 	uint64_t sdd_blocks;
 };
 
+loff_t scst_file_size(const char *path, umode_t *mode);
+loff_t scst_bdev_size(const char *path);
+loff_t scst_file_or_bdev_size(const char *path);
 ssize_t scst_readv(struct file *file, const struct kvec *vec,
 		   unsigned long vlen, loff_t *pos);
 ssize_t scst_writev(struct file *file, const struct kvec *vec,
@@ -5577,16 +5572,11 @@
 	scsi_execute(sdev, cmd, data_direction, buffer, bufflen, sense,	\
 		     NULL /* sshdr */, timeout, retries, flags,		\
 		     0 /* rq_flags */, NULL /* resid */)
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
-#define scst_scsi_execute(sdev, cmd, data_direction, buffer, bufflen, sense, \
-			  timeout, retries, flags)			\
-	scsi_execute(sdev, cmd, data_direction, buffer, bufflen, sense,	\
-		     timeout, retries, flags, NULL /* resid */)
 #else
 #define scst_scsi_execute(sdev, cmd, data_direction, buffer, bufflen, sense, \
 			  timeout, retries, flags)			\
 	scsi_execute(sdev, cmd, data_direction, buffer, bufflen, sense,	\
-		     timeout, retries, flags)
+		     timeout, retries, flags, NULL /* resid */)
 #endif
 
 __be64 scst_pack_lun(const uint64_t lun, enum scst_lun_addr_method addr_method);
@@ -5602,9 +5592,6 @@
 int scst_write_file_transactional(const char *name, const char *name1,
 	const char *signature, int signature_len, const uint8_t *buf, int size);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
-void scst_path_put(struct nameidata *nd);
-#endif
 int scst_remove_file(const char *name);
 
 void scst_set_tp_soft_threshold_reached_UA(struct scst_tgt_dev *tgt_dev);
diff --git a/scst/scst/include/scst_const.h b/scst/scst/include/scst_const.h
index b32f10c..65b1439 100644
--- a/scst/scst/include/scst_const.h
+++ b/scst/scst/include/scst_const.h
@@ -44,9 +44,9 @@
  * and FIO_REV in usr/fileio/common.h as well.
  */
 #define SCST_VERSION(a, b, c, d)    (((a) << 24) + ((b) << 16) + ((c) << 8) + d)
-#define SCST_VERSION_CODE	    SCST_VERSION(3, 4, 0, 0)
+#define SCST_VERSION_CODE	    SCST_VERSION(3, 7, 0, 0)
 #define SCST_VERSION_STRING_SUFFIX
-#define SCST_VERSION_NAME	    "3.5.0"
+#define SCST_VERSION_NAME	    "3.7.0"
 #define SCST_VERSION_STRING	    SCST_VERSION_NAME SCST_VERSION_STRING_SUFFIX
 
 #define SCST_CONST_VERSION SCST_CONST_INTF_VER
@@ -356,15 +356,7 @@
 #define INIT_ELEMENT_STATUS         0x07
 #define INIT_ELEMENT_STATUS_RANGE   0x37
 #define PREVENT_ALLOW_MEDIUM        0x1E
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) \
-	&& (!defined(RHEL_MAJOR) || RHEL_MAJOR -0 <= 5)
-#define READ_ATTRIBUTE              0x8C
-#endif
 #define REQUEST_VOLUME_ADDRESS      0xB5
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38) \
-	&& (!defined(RHEL_MAJOR) || RHEL_MAJOR -0 <= 5)
-#define WRITE_ATTRIBUTE             0x8D
-#endif
 #if (!defined(__KERNEL__) || LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)) &&\
 	!defined(WRITE_VERIFY_16)
 #define WRITE_VERIFY_16             0x8E
@@ -373,8 +365,7 @@
 #ifndef VERIFY_12
 #define VERIFY_12                   0xAF
 #endif
-#if !defined(GENERATING_UPSTREAM_PATCH) || \
-	LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
+#if !defined(GENERATING_UPSTREAM_PATCH)
 /*
  * The constants below have been defined in the kernel header <scsi/scsi.h>
  * and hence are not needed when this header file is included in kernel code.
@@ -385,9 +376,6 @@
 /* Upstream commit 93aae17a (v2.6.38) */
 #define GET_EVENT_STATUS_NOTIFICATION 0x4a
 #endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
-#define VARIABLE_LENGTH_CMD   0x7f
-#endif
 #ifndef READ_16
 #define READ_16               0x88
 #endif
@@ -397,27 +385,11 @@
 #ifndef VERIFY_16
 #define VERIFY_16	      0x8f
 #endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 38)
-#ifndef MI_REPORT_IDENTIFYING_INFORMATION
-#define MI_REPORT_IDENTIFYING_INFORMATION 0x05
-#endif
-#ifndef MI_REPORT_SUPPORTED_OPERATION_CODES
-#define MI_REPORT_SUPPORTED_OPERATION_CODES 0x0c
-#endif
-#ifndef MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS
-#define MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS 0x0d
-#endif
-#endif
 #ifndef SAI_READ_CAPACITY_16
 /* values for service action in */
 #define	SAI_READ_CAPACITY_16  0x10
 #endif
 #endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)
-#ifndef SAI_GET_LBA_STATUS
-#define SAI_GET_LBA_STATUS    0x12
-#endif
-#endif
 #ifndef GENERATING_UPSTREAM_PATCH
 #ifndef REPORT_LUNS
 #define REPORT_LUNS           0xa0
@@ -440,14 +412,6 @@
 #define SYNCHRONIZE_CACHE_16  0x91
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)
-/*
- * From <scsi/scsi.h>. See also commit
- * f57e4502cea471c69782d4790c71d8414ab49a9d.
- */
-#define UNMAP 0x42
-#endif
-
 /* Subcodes of VARIABLE_LENGTH_CMD (0x7F) */
 #define SUBCODE_READ_32		0x09
 #define SUBCODE_VERIFY_32	0x0a
@@ -475,14 +439,6 @@
 #endif
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
-/*
- * From <linux/fs.h>. See also commit
- * d30a2605be9d5132d95944916e8f578fcfe4f976.
- */
-#define BLKDISCARD _IO(0x12, 119)
-#endif
-
 /*************************************************************
  **  SCSI Architecture Model (SAM) Status codes. Taken from SAM-3 draft
  **  T10/1561-D Revision 4 Draft dated 7th November 2002.
diff --git a/scst/scst/include/scst_debug.h b/scst/scst/include/scst_debug.h
index b789252..8f5f4ec 100644
--- a/scst/scst/include/scst_debug.h
+++ b/scst/scst/include/scst_debug.h
@@ -21,19 +21,9 @@
 #ifndef __SCST_DEBUG_H
 #define __SCST_DEBUG_H
 
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 32)
-#include <linux/autoconf.h>	/* for CONFIG_* */
-#else
 #include <generated/autoconf.h>	/* for CONFIG_* */
-#endif
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 19)
 #include <linux/bug.h>		/* for WARN_ON_ONCE */
-#endif
-
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27)
 #include <linux/ratelimit.h>
-#endif
 
 #ifdef INSIDE_KERNEL_TREE
 #include <scst/backport.h>
diff --git a/scst/scst/include/scst_event.h b/scst/scst/include/scst_event.h
index f18b1ea..3180237 100644
--- a/scst/scst/include/scst_event.h
+++ b/scst/scst/include/scst_event.h
@@ -61,11 +61,7 @@
 	int *pqueued_events_cnt;
 	union {
 		struct work_struct scst_event_queue_work;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-		struct work_struct event_timeout_work;
-#else
 		struct delayed_work event_timeout_work;
-#endif
 	};
 
 	struct scst_event event;
@@ -153,7 +149,7 @@
 	aligned_u64 stpg_cmd_tag;
 	uint8_t device_name[64];
 	uint16_t stpg_descriptors_cnt;
-	struct scst_event_stpg_descr stpg_descriptors[0];
+	struct scst_event_stpg_descr stpg_descriptors[];
 };
 
 #define SCST_EVENT_REG_VIRT_DEV		6
diff --git a/scst/scst/include/scst_user.h b/scst/scst/include/scst_user.h
index 92c1b12..8285c29 100644
--- a/scst/scst/include/scst_user.h
+++ b/scst/scst/include/scst_user.h
@@ -354,7 +354,7 @@
 	int16_t replies_done; /* out */
 	int16_t cmds_cnt; /* in/out */
 	int16_t pad;
-	struct scst_user_get_cmd cmds[0]; /* out */
+	struct scst_user_get_cmd cmds[]; /* out */
 };
 
 #define SCST_USER_REGISTER_DEVICE	_IOW('u', 1, struct scst_user_dev_desc)
diff --git a/scst/scst/kernel/in-tree/Kconfig.drivers.Linux.patch b/scst/scst/kernel/in-tree/Kconfig.drivers.Linux.patch
index 0d5a19f..347bf5d 100644
--- a/scst/scst/kernel/in-tree/Kconfig.drivers.Linux.patch
+++ b/scst/scst/kernel/in-tree/Kconfig.drivers.Linux.patch
@@ -1,13 +1,13 @@
 diff --git a/drivers/Kconfig b/drivers/Kconfig
-index aa43b91..c96860e 100644
+index 8bad63417a50..a61b1804fcf3 100644
 --- a/drivers/Kconfig
 +++ b/drivers/Kconfig
-@@ -24,6 +24,8 @@ source "drivers/ide/Kconfig"
+@@ -39,6 +39,8 @@ source "drivers/ata/Kconfig"
  
- source "drivers/scsi/Kconfig"
+ source "drivers/md/Kconfig"
  
 +source "drivers/scst/Kconfig"
 +
- source "drivers/ata/Kconfig"
+ source "drivers/target/Kconfig"
  
- source "drivers/md/Kconfig"
+ source "drivers/message/fusion/Kconfig"
diff --git a/scst/scst/kernel/in-tree/Makefile.drivers.Linux.patch b/scst/scst/kernel/in-tree/Makefile.drivers.Linux.patch
index e027411..4dbb006 100644
--- a/scst/scst/kernel/in-tree/Makefile.drivers.Linux.patch
+++ b/scst/scst/kernel/in-tree/Makefile.drivers.Linux.patch
@@ -1,12 +1,12 @@
 diff --git a/drivers/Makefile b/drivers/Makefile
-index 31cf17dee252..b45c17aee468 100644
+index 27c018bdf4de..6fc8c24fd0d6 100644
 --- a/drivers/Makefile
 +++ b/drivers/Makefile
-@@ -75,6 +75,7 @@ obj-$(CONFIG_DAX)		+= dax/
- obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf/
- obj-$(CONFIG_NUBUS)		+= nubus/
- obj-y				+= macintosh/
-+obj-$(CONFIG_SCST)		+= scst/
- obj-$(CONFIG_IDE)		+= ide/
+@@ -81,6 +81,7 @@ obj-y				+= macintosh/
  obj-y				+= scsi/
  obj-y				+= nvme/
+ obj-$(CONFIG_ATA)		+= ata/
++obj-$(CONFIG_SCST)		+= scst/
+ obj-$(CONFIG_TARGET_CORE)	+= target/
+ obj-$(CONFIG_MTD)		+= mtd/
+ obj-$(CONFIG_SPI)		+= spi/
diff --git a/scst/scst/src/Makefile b/scst/scst/src/Makefile
index 486ec8a..b036b33 100644
--- a/scst/scst/src/Makefile
+++ b/scst/scst/src/Makefile
@@ -89,13 +89,7 @@
 	false; fi
 	-rm -f $(INSTALL_DIR)/scsi_tgt.ko
 	KDIR=$(KDIR) ../../scripts/sign-modules
-	# Apparently on RHEL 8 and CentOS 8 the module installation can
-	# leave stale symlinks in /lib/modules/$(KVER)/weak-updates/. These
-	# symlinks may cause loading of SCST to fail, Remove these symlinks
-	# before installing SCST.
-	if [ -e /usr/sbin/weak-modules ]; then				\
-		/usr/sbin/weak-modules --remove-kernel;			\
-	fi
+	(cd dev_handlers; KDIR=$(KDIR) ../../../scripts/sign-modules)
 	$(MAKE) -C $(KDIR) M=$(shell pwd)/dev_handlers			\
 	  $(shell [ -n "$(PASS_CC_TO_MAKE)" ] && echo CC="$(CC)")	\
 	  INSTALL_MOD_DIR=extra/dev_handlers				\
diff --git a/scst/scst/src/certs/Makefile b/scst/scst/src/certs/Makefile
index 2c9d76f..f373d30 100644
--- a/scst/scst/src/certs/Makefile
+++ b/scst/scst/src/certs/Makefile
@@ -9,6 +9,9 @@
 	openssl req -new -nodes -utf8 -$(CONFIG_MODULE_SIG_HASH) -days 365000 \
 		-batch -x509 -config $< -outform DER -out scst_module_key.der \
 		-keyout scst_module_key.priv
+	# override those signing keys with the keys generated at kernel build time
+	openssl x509 -in "${KERNEL_FILES}/signing_key.pem" -out scst_module_key.der -outform DER
+	openssl pkey -in "${KERNEL_FILES}/signing_key.pem" -out scst_module_key.priv
 	chmod 600 $@
 
 .PHONY: module_signing_enabled
diff --git a/scst/scst/src/dev_handlers/scst_changer.c b/scst/scst/src/dev_handlers/scst_changer.c
index 53b6933..31b625c 100644
--- a/scst/scst/src/dev_handlers/scst_changer.c
+++ b/scst/scst/src/dev_handlers/scst_changer.c
@@ -83,12 +83,7 @@
 	do {
 		TRACE_DBG("%s", "Doing TEST_UNIT_READY");
 		rc = scsi_test_unit_ready(dev->scsi_dev,
-			SCST_GENERIC_CHANGER_TIMEOUT, CHANGER_RETRIES
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
-					  );
-#else
-					  , NULL);
-#endif
+			SCST_GENERIC_CHANGER_TIMEOUT, CHANGER_RETRIES, NULL);
 		TRACE_DBG("TEST_UNIT_READY done: %x", rc);
 	} while ((--retries > 0) && rc);
 
diff --git a/scst/scst/src/dev_handlers/scst_disk.c b/scst/scst/src/dev_handlers/scst_disk.c
index fd01b8d..c42c5d6 100644
--- a/scst/scst/src/dev_handlers/scst_disk.c
+++ b/scst/scst/src/dev_handlers/scst_disk.c
@@ -200,8 +200,6 @@
 	return res;
 }
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
-
 static bool disk_on_sg_tablesize_low(struct scst_cmd *cmd)
 {
 	bool res;
@@ -264,7 +262,7 @@
 
 	WARN_ON_ONCE(IS_ERR_VALUE((long)result));
 
-	if (status_byte(result) == GOOD)
+	if ((result & 0xff) == SAM_STAT_GOOD)
 		goto out_complete;
 
 	work->result = result;
@@ -466,7 +464,6 @@
 	goto out_done;
 }
 
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) */
 
 static enum scst_exec_res disk_perf_exec(struct scst_cmd *cmd)
 {
@@ -517,10 +514,8 @@
 	.attach =		disk_attach,
 	.detach =		disk_detach,
 	.parse =		disk_parse,
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
 	.exec =			disk_exec,
 	.on_sg_tablesize_low = disk_on_sg_tablesize_low,
-#endif
 	.dev_done =		disk_done,
 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
 	.default_trace_flags = SCST_DEFAULT_DEV_LOG_FLAGS,
@@ -538,9 +533,7 @@
 	.parse =		disk_parse,
 	.exec =			disk_perf_exec,
 	.dev_done =		disk_done,
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
 	.on_sg_tablesize_low = disk_on_sg_tablesize_low,
-#endif
 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
 	.default_trace_flags =	SCST_DEFAULT_DEV_LOG_FLAGS,
 	.trace_flags =		&trace_flag,
diff --git a/scst/scst/src/dev_handlers/scst_processor.c b/scst/scst/src/dev_handlers/scst_processor.c
index db259b9..d87abc5 100644
--- a/scst/scst/src/dev_handlers/scst_processor.c
+++ b/scst/scst/src/dev_handlers/scst_processor.c
@@ -83,12 +83,7 @@
 	do {
 		TRACE_DBG("%s", "Doing TEST_UNIT_READY");
 		rc = scsi_test_unit_ready(dev->scsi_dev,
-			SCST_GENERIC_PROCESSOR_TIMEOUT, PROCESSOR_RETRIES
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
-					  );
-#else
-					  , NULL);
-#endif
+			SCST_GENERIC_PROCESSOR_TIMEOUT, PROCESSOR_RETRIES, NULL);
 		TRACE_DBG("TEST_UNIT_READY done: %x", rc);
 	} while ((--retries > 0) && rc);
 
diff --git a/scst/scst/src/dev_handlers/scst_raid.c b/scst/scst/src/dev_handlers/scst_raid.c
index 84f647a..4a0938f 100644
--- a/scst/scst/src/dev_handlers/scst_raid.c
+++ b/scst/scst/src/dev_handlers/scst_raid.c
@@ -83,12 +83,7 @@
 	do {
 		TRACE_DBG("%s", "Doing TEST_UNIT_READY");
 		rc = scsi_test_unit_ready(dev->scsi_dev,
-			SCST_GENERIC_RAID_TIMEOUT, RAID_RETRIES
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
-					  );
-#else
-					  , NULL);
-#endif
+			SCST_GENERIC_RAID_TIMEOUT, RAID_RETRIES, NULL);
 		TRACE_DBG("TEST_UNIT_READY done: %x", rc);
 	} while ((--retries > 0) && rc);
 
diff --git a/scst/scst/src/dev_handlers/scst_tape.c b/scst/scst/src/dev_handlers/scst_tape.c
index 7c0ffd3..1bb0301 100644
--- a/scst/scst/src/dev_handlers/scst_tape.c
+++ b/scst/scst/src/dev_handlers/scst_tape.c
@@ -145,7 +145,7 @@
 	}
 
 	dev->block_size = TAPE_DEF_BLOCK_SIZE;
-	dev->block_shift = -1; /* not used */
+	dev->block_shift = scst_calc_block_shift(dev->block_size);
 
 	buffer = kmalloc(buffer_size, GFP_KERNEL);
 	if (!buffer) {
@@ -159,12 +159,7 @@
 	do {
 		TRACE_DBG("%s", "Doing TEST_UNIT_READY");
 		rc = scsi_test_unit_ready(dev->scsi_dev,
-			SCST_GENERIC_TAPE_SMALL_TIMEOUT, TAPE_RETRIES
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
-					  );
-#else
-					  , NULL);
-#endif
+			SCST_GENERIC_TAPE_SMALL_TIMEOUT, TAPE_RETRIES, NULL);
 		TRACE_DBG("TEST_UNIT_READY done: %x", rc);
 	} while ((--retries > 0) && rc);
 
@@ -203,7 +198,7 @@
 		res = -ENODEV;
 		goto out_free_buf;
 	}
-	dev->block_shift = -1; /* not used */
+	dev->block_shift = scst_calc_block_shift(dev->block_size);
 
 obtain:
 	res = scst_obtain_device_parameters(dev, NULL);
@@ -250,7 +245,7 @@
 	 * there are existing commands.
 	 */
 	dev->block_size = block_size;
-	dev->block_shift = -1; /* not used */
+	dev->block_shift = scst_calc_block_shift(dev->block_size);
 	return;
 }
 
diff --git a/scst/scst/src/dev_handlers/scst_user.c b/scst/scst/src/dev_handlers/scst_user.c
index f5b7d74..0ceb8d2 100644
--- a/scst/scst/src/dev_handlers/scst_user.c
+++ b/scst/scst/src/dev_handlers/scst_user.c
@@ -4120,11 +4120,7 @@
 			struct scst_user_reply_cmd r;
 		};
 	};
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21)
-	struct class_device *class_member;
-#else
 	struct device *dev;
-#endif
 
 	TRACE_ENTRY();
 
@@ -4174,25 +4170,14 @@
 		goto out_class;
 	}
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21)
-	class_member = class_device_create(dev_user_sysfs_class, NULL,
-				MKDEV(dev_user_major, 0), NULL, DEV_USER_NAME);
-	if (IS_ERR(class_member)) {
-		res = PTR_ERR(class_member);
-		goto out_chrdev;
-	}
-#else
 	dev = device_create(dev_user_sysfs_class, NULL,
 			    MKDEV(dev_user_major, 0),
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
 				NULL,
-#endif
 				DEV_USER_NAME);
 	if (IS_ERR(dev)) {
 		res = PTR_ERR(dev);
 		goto out_chrdev;
 	}
-#endif
 
 	cleanup_thread = kthread_run(dev_user_cleanup_thread, NULL,
 		"scst_usr_cleanupd");
@@ -4207,11 +4192,7 @@
 	return res;
 
 out_dev:
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21)
-	class_device_destroy(dev_user_sysfs_class, MKDEV(dev_user_major, 0));
-#else
 	device_destroy(dev_user_sysfs_class, MKDEV(dev_user_major, 0));
-#endif
 
 out_chrdev:
 	unregister_chrdev(dev_user_major, DEV_USER_NAME);
@@ -4242,11 +4223,7 @@
 		TRACE_MGMT_DBG("kthread_stop() failed: %d", rc);
 
 	unregister_chrdev(dev_user_major, DEV_USER_NAME);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21)
-	class_device_destroy(dev_user_sysfs_class, MKDEV(dev_user_major, 0));
-#else
 	device_destroy(dev_user_sysfs_class, MKDEV(dev_user_major, 0));
-#endif
 	class_destroy(dev_user_sysfs_class);
 
 	scst_unregister_virtual_dev_driver(&dev_user_devtype);
diff --git a/scst/scst/src/dev_handlers/scst_vdisk.c b/scst/scst/src/dev_handlers/scst_vdisk.c
index 6ba1032..a4703bd 100644
--- a/scst/scst/src/dev_handlers/scst_vdisk.c
+++ b/scst/scst/src/dev_handlers/scst_vdisk.c
@@ -6,7 +6,7 @@
  *  Copyright (C) 2007 Ming Zhang <blackmagic02881 at gmail dot com>
  *  Copyright (C) 2007 Ross Walker <rswwalker at hotmail dot com>
  *  Copyright (C) 2007 - 2018 Western Digital Corporation
- *  Copyright (C) 2008 - 2018 Bart Van Assche <bvanassche@acm.org>
+ *  Copyright (C) 2008 - 2020 Bart Van Assche <bvanassche@acm.org>
  *
  *  SCSI disk (type 0) and CDROM (type 5) dev handler using files
  *  on file systems or block devices (VDISK)
@@ -29,6 +29,7 @@
 #include <linux/aio.h>
 #include <linux/file.h>
 #include <linux/fs.h>
+#include <linux/pagemap.h>
 #include <linux/string.h>
 #include <linux/types.h>
 #include <linux/unistd.h>
@@ -39,10 +40,10 @@
 #include <linux/ctype.h>
 #include <linux/writeback.h>
 #include <linux/vmalloc.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
 #include <linux/atomic.h>
-#else
-#include <asm/atomic.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 16, 0) || \
+	(defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 9)
+#include <linux/blk-integrity.h>
 #endif
 #include <linux/kthread.h>
 #include <linux/sched.h>
@@ -53,9 +54,7 @@
 #include <linux/slab.h>
 #include <linux/bio.h>
 #include <linux/crc32c.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
 #include <linux/falloc.h>
-#endif
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
 #include <linux/sched/signal.h>
 #endif
@@ -76,7 +75,7 @@
 #define SCST_FIO_VENDOR			"SCST_FIO"
 #define SCST_BIO_VENDOR			"SCST_BIO"
 /* 4 byte ASCII Product Revision Level - left aligned */
-#define SCST_FIO_REV			"350 "
+#define SCST_FIO_REV			"370 "
 
 #define MAX_USN_LEN			(20+1) /* For '\0' */
 #define MAX_INQ_VEND_SPECIFIC_LEN	(INQ_BUF_SZ - 96)
@@ -165,7 +164,6 @@
 	unsigned int wt_flag:1;
 	unsigned int nv_cache:1;
 	unsigned int o_direct_flag:1;
-	unsigned int zero_copy:1;
 	unsigned int async:1;
 	unsigned int media_changed:1;
 	unsigned int prevent_allow_medium_removal:1;
@@ -191,12 +189,11 @@
 	struct file *fd;
 	struct file *dif_fd;
 	struct block_device *bdev;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
+	fmode_t bdev_mode;
 	struct bio_set *vdisk_bioset;
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
 	struct bio_set vdisk_bioset_struct;
 #endif
-#endif
 
 	uint64_t format_progress_to_do, format_progress_done;
 
@@ -263,8 +260,8 @@
 		} sync;
 		struct {
 			struct kiocb	iocb;
-			struct kvec	*kvec;
-			struct kvec	small_kvec[4];
+			struct bio_vec	*bvec;
+			struct bio_vec	small_bvec[4];
 		} async;
 	};
 	struct scst_cmd *cmd;
@@ -287,12 +284,8 @@
 
 typedef enum compl_status_e (*vdisk_op_fn)(struct vdisk_cmd_params *p);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
-#define DEF_NUM_THREADS		5
-#else
 /* Context RA patch supposed to be applied on the kernel */
 #define DEF_NUM_THREADS		8
-#endif
 static int num_threads = DEF_NUM_THREADS;
 
 module_param_named(num_threads, num_threads, int, S_IRUGO);
@@ -370,13 +363,6 @@
 
 	sBUG_ON(!name);
 
-	if (!virt_dev->dev_active) {
-		TRACE_MGMT_DBG("Skip opening for not active dev %s",
-			       virt_dev->dev->virt_name);
-		fd = ERR_PTR(-EMEDIUMTYPE);
-		goto out;
-	}
-
 	if (read_only)
 		open_flags |= O_RDONLY;
 	else
@@ -387,10 +373,6 @@
 	TRACE_DBG("Opening file %s, flags 0x%x", name, open_flags);
 	fd = filp_open(name, O_LARGEFILE | open_flags, 0600);
 	if (IS_ERR(fd)) {
-		PRINT_ERROR("JPM vdev_open_fd() failed for dev: %s", virt_dev->dev->virt_name);
-		PRINT_ERROR("JPM vdev_open_fd() virt_dev->o_direct_flag= %d, wt_flag= %d, nv_cache= %d", virt_dev->o_direct_flag, virt_dev->wt_flag, virt_dev->nv_cache);
-		PRINT_ERROR("JPM Failed opening file %s, flags 0x%x", name, open_flags);
-		PRINT_ERROR("JPM O_RDONLY: 0x%x, O_RDWR: 0x%x,  O_DIRECT: 0x%x, O_DSYNC: 0x%x", O_RDONLY, O_RDWR, O_DIRECT, O_DSYNC);
 		if (PTR_ERR(fd) == -EMEDIUMTYPE)
 			TRACE(TRACE_MINOR, "Unable to open %s with EMEDIUMTYPE, "
 				"DRBD passive?", name);
@@ -398,12 +380,10 @@
 			PRINT_ERROR("filp_open(%s) failed: %d", name, (int)PTR_ERR(fd));
 	}
 
-out:
 	TRACE_EXIT();
 	return fd;
 }
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
 static void vdev_flush_end_io(struct bio *bio, int error)
 {
@@ -440,7 +420,6 @@
 	TRACE_EXIT();
 	return;
 }
-#endif
 
 static int vdisk_blockio_flush(struct block_device *bdev, gfp_t gfp_mask,
 	bool report_error, struct scst_cmd *cmd, bool async)
@@ -449,17 +428,20 @@
 
 	TRACE_ENTRY();
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
 	if (async) {
-		struct bio *bio = bio_alloc(gfp_mask, 0);
+		struct bio *bio;
 
+		bio = bio_alloc(/*bdev=*/NULL, 0, /*opf=*/0, gfp_mask);
 		if (bio == NULL) {
 			res = -ENOMEM;
 			goto out_rep;
 		}
+
 		bio->bi_end_io = vdev_flush_end_io;
 		bio->bi_private = cmd;
+
 		bio_set_dev(bio, bdev);
+
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) ||	\
 	(defined(CONFIG_SUSE_KERNEL) &&			\
 	 LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
@@ -480,25 +462,20 @@
 #endif
 		goto out;
 	} else {
-#else
-	{
-#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)           \
-    && !(defined(CONFIG_SUSE_KERNEL)                        \
-	 && LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 34))
-		res = blkdev_issue_flush(bdev, NULL);
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
-		res = blkdev_issue_flush(bdev, gfp_mask, NULL, BLKDEV_IFL_WAIT);
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0) && \
+	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 8 ||	\
+	 RHEL_MAJOR -0 == 8 && RHEL_MINOR -0 < 4)
 		res = blkdev_issue_flush(bdev, gfp_mask, NULL);
-#else
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0) && \
+	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 8 ||	\
+	 RHEL_MAJOR -0 == 8 && RHEL_MINOR -0 < 6)
 		res = blkdev_issue_flush(bdev, gfp_mask);
+#else
+		res = blkdev_issue_flush(bdev);
 #endif
 	}
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
 out_rep:
-#endif
 	if ((res != 0) && report_error)
 		PRINT_ERROR("%s() failed: %d",
 			async ? "bio_alloc" : "blkdev_issue_flush", res);
@@ -509,50 +486,41 @@
 			scst_estimate_context());
 	}
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
 out:
-#endif
 	TRACE_EXIT_RES(res);
 	return res;
 }
 
 static void vdisk_blockio_check_flush_support(struct scst_vdisk_dev *virt_dev)
 {
-	struct inode *inode;
-	struct file *fd;
+	struct block_device *bdev;
 
 	TRACE_ENTRY();
 
-	if (!virt_dev->blockio || virt_dev->rd_only || virt_dev->nv_cache || virt_dev->wt_flag || !virt_dev->dev_active)
+	if (!virt_dev->blockio || virt_dev->rd_only || virt_dev->nv_cache ||
+	    virt_dev->wt_flag || !virt_dev->dev_active)
 		goto out;
 
-	fd = filp_open(virt_dev->filename, O_LARGEFILE, 0600);
-	if (IS_ERR(fd)) {
-		if ((PTR_ERR(fd) == -EMEDIUMTYPE) && virt_dev->blockio)
+	bdev = blkdev_get_by_path(virt_dev->filename, FMODE_READ,
+				  (void *)__func__);
+	if (IS_ERR(bdev)) {
+		if (PTR_ERR(bdev) == -EMEDIUMTYPE)
 			TRACE(TRACE_MINOR, "Unable to open %s with EMEDIUMTYPE, "
 				"DRBD passive?", virt_dev->filename);
 		else
-			PRINT_ERROR("filp_open(%s) failed: %ld",
-				virt_dev->filename, PTR_ERR(fd));
+			PRINT_ERROR("blkdev_get_by_path(%s) failed: %ld",
+				virt_dev->filename, PTR_ERR(bdev));
 		goto out;
 	}
 
-	inode = file_inode(fd);
-
-	if (!S_ISBLK(inode->i_mode)) {
-		PRINT_ERROR("%s is NOT a block device", virt_dev->filename);
-		goto out_close;
-	}
-
-	if (vdisk_blockio_flush(inode->i_bdev, GFP_KERNEL, false, NULL, false) != 0) {
+	if (vdisk_blockio_flush(bdev, GFP_KERNEL, false, NULL, false) != 0) {
 		PRINT_WARNING("Device %s doesn't support barriers, switching "
 			"to NV_CACHE mode. Read README for more details.",
 			virt_dev->filename);
 		virt_dev->nv_cache = 1;
 	}
 
-out_close:
-	filp_close(fd, NULL);
+	blkdev_put(bdev, FMODE_READ);
 
 out:
 	TRACE_EXIT();
@@ -561,46 +529,51 @@
 
 static void vdisk_check_tp_support(struct scst_vdisk_dev *virt_dev)
 {
+	struct block_device *bdev = NULL;
 	struct file *fd = NULL;
 	bool fd_open = false;
+	int res;
 
 	TRACE_ENTRY();
 
 	virt_dev->dev_thin_provisioned = 0;
 
-	if (virt_dev->rd_only || (virt_dev->filename == NULL) || !virt_dev->dev_active)
+	if (virt_dev->rd_only || !virt_dev->filename || !virt_dev->dev_active)
 		goto check;
 
-	fd = filp_open(virt_dev->filename, O_LARGEFILE, 0600);
-	if (IS_ERR(fd)) {
-		if ((PTR_ERR(fd) == -EMEDIUMTYPE) && virt_dev->blockio)
-			TRACE(TRACE_MINOR, "Unable to open %s with EMEDIUMTYPE, "
-				"DRBD passive?", virt_dev->filename);
+	if (virt_dev->blockio) {
+		bdev = blkdev_get_by_path(virt_dev->filename, FMODE_READ,
+					  (void *)__func__);
+		res = PTR_ERR_OR_ZERO(bdev);
+	} else {
+		fd = filp_open(virt_dev->filename, O_LARGEFILE, 0600);
+		res = PTR_ERR_OR_ZERO(fd);
+	}
+	if (res) {
+		if (res == -EMEDIUMTYPE && virt_dev->blockio)
+			TRACE(TRACE_MINOR,
+			      "Unable to open %s with EMEDIUMTYPE, DRBD passive?",
+			      virt_dev->filename);
 		else
-			PRINT_ERROR("filp_open(%s) failed: %ld",
-				virt_dev->filename, PTR_ERR(fd));
+			PRINT_ERROR("opening %s failed: %d",
+				    virt_dev->filename, res);
 		goto check;
 	}
+
 	fd_open = true;
 
 	if (virt_dev->blockio) {
-		struct inode *inode = file_inode(fd);
-
-		if (!S_ISBLK(inode->i_mode)) {
-			PRINT_ERROR("%s is NOT a block device",
-				virt_dev->filename);
-			goto check;
-		}
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) || (defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 19, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(9, 1))
 		virt_dev->dev_thin_provisioned =
-			blk_queue_discard(bdev_get_queue(inode->i_bdev));
+			blk_queue_discard(bdev_get_queue(bdev));
+#else
+		virt_dev->dev_thin_provisioned =
+			!!bdev_max_discard_sectors(bdev);
 #endif
 	} else {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
 		virt_dev->dev_thin_provisioned = (fd->f_op->fallocate != NULL);
-#else
-		virt_dev->dev_thin_provisioned = 0;
-#endif
 	}
 
 check:
@@ -616,7 +589,6 @@
 		if (virt_dev->thin_provisioned)
 			PRINT_INFO("Auto enable thin provisioning for device "
 				"%s", virt_dev->filename);
-
 	}
 
 	if (virt_dev->thin_provisioned) {
@@ -632,12 +604,10 @@
 		}
 
 		if (virt_dev->blockio) {
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32) || \
-	(defined(RHEL_MAJOR) && RHEL_MAJOR -0 >= 6)
 			struct request_queue *q;
 
 			sBUG_ON(!fd_open);
-			q = bdev_get_queue(file_inode(fd)->i_bdev);
+			q = bdev_get_queue(bdev);
 			virt_dev->unmap_opt_gran = q->limits.discard_granularity >> block_shift;
 			virt_dev->unmap_align = q->limits.discard_alignment >> block_shift;
 			if (virt_dev->unmap_opt_gran == virt_dev->unmap_align)
@@ -646,9 +616,6 @@
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0)
 			virt_dev->discard_zeroes_data = q->limits.discard_zeroes_data;
 #endif
-#else
-			sBUG();
-#endif
 		} else {
 			virt_dev->unmap_opt_gran = 1;
 			virt_dev->unmap_align = 0;
@@ -670,8 +637,12 @@
 			virt_dev->discard_zeroes_data);
 	}
 
-	if (fd_open)
-		filp_close(fd, NULL);
+	if (fd_open) {
+		if (virt_dev->blockio)
+			blkdev_put(bdev, FMODE_READ);
+		else
+			filp_close(fd, NULL);
+	}
 
 	TRACE_EXIT();
 	return;
@@ -681,69 +652,46 @@
 static int vdisk_get_file_size(const struct scst_vdisk_dev *virt_dev,
 	loff_t *file_size)
 {
-	struct inode *inode;
-	int res = 0;
-	struct file *fd;
+	loff_t res;
 
 	TRACE_ENTRY();
 
 	sBUG_ON(!virt_dev->filename);
 
 	if (!virt_dev->dev_active) {
-		TRACE_DBG("Not active dev %s, skip reexaming", virt_dev->dev->virt_name);
+		TRACE_DBG("Not active dev %s, skip reexaming",
+			  virt_dev->dev->virt_name);
 		res = -EMEDIUMTYPE;
 		goto out;
 	}
 
-	*file_size = 0;
-
-	fd = filp_open(virt_dev->filename, O_LARGEFILE | O_RDONLY, 0600);
-	if (IS_ERR(fd)) {
-		res = PTR_ERR(fd);
-		if ((res == -EMEDIUMTYPE) && virt_dev->blockio)
-			TRACE(TRACE_MINOR, "Unable to open %s with EMEDIUMTYPE, "
-				"DRBD passive?", virt_dev->filename);
-		else
-			PRINT_ERROR("filp_open(%s) failed: %d", virt_dev->filename, res);
+	res = scst_file_or_bdev_size(virt_dev->filename);
+	if (res == -EMEDIUMTYPE && virt_dev->blockio) {
+		TRACE(TRACE_MINOR,
+		      "Unable to open %s with EMEDIUMTYPE, DRBD passive?",
+		      virt_dev->filename);
 		goto out;
 	}
-
-	inode = file_inode(fd);
-
-	if (virt_dev->blockio && !S_ISBLK(inode->i_mode)) {
-		PRINT_ERROR("File %s is NOT a block device", virt_dev->filename);
-		res = -EINVAL;
-		goto out_close;
+	if (res < 0) {
+		PRINT_ERROR("opening %s failed: %lld", virt_dev->filename, res);
+		goto out;
 	}
-
-	if (S_ISREG(inode->i_mode)) {
-		/* Nothing to do */
-	} else if (S_ISBLK(inode->i_mode)) {
-		inode = inode->i_bdev->bd_inode;
-	} else {
-		PRINT_ERROR("File %s unsupported mode: mode=0%o\n",
-			    virt_dev->filename, inode->i_mode);
-		res = -EINVAL;
-		goto out_close;
-	}
-
-	*file_size = inode->i_size;
-
-out_close:
-	filp_close(fd, NULL);
+	*file_size = res;
+	res = 0;
 
 out:
 	TRACE_EXIT_RES(res);
 	return res;
 }
 
-/* scst_vdisk_mutex supposed to be held */
 static struct scst_vdisk_dev *vdev_find(const char *name)
 {
 	struct scst_vdisk_dev *res, *vv;
 
 	TRACE_ENTRY();
 
+	lockdep_assert_held(&scst_vdisk_mutex);
+
 	res = NULL;
 	list_for_each_entry(vv, &vdev_list, vdev_list_entry) {
 		if (strcmp(vv->name, name) == 0) {
@@ -1010,28 +958,20 @@
 {
 	int res;
 	struct scst_device *dev = virt_dev->dev;
-	struct inode *inode;
-	struct file *fd;
+	struct block_device *bdev;
 	struct blk_integrity *bi;
 	const char *bi_profile_name;
 
 	TRACE_ENTRY();
 
-	fd = vdev_open_fd(virt_dev, virt_dev->filename, virt_dev->rd_only);
-	if (IS_ERR(fd)) {
-		res = -EINVAL;
+	bdev = blkdev_get_by_path(virt_dev->filename, FMODE_READ,
+				  (void *)__func__);
+	if (IS_ERR(bdev)) {
+		res = PTR_ERR(bdev);
 		goto out;
 	}
 
-	inode = file_inode(fd);
-
-	if (!S_ISBLK(inode->i_mode)) {
-		PRINT_ERROR("%s is NOT a block device!", virt_dev->filename);
-		res = -EINVAL;
-		goto out_close;
-	}
-
-	bi = bdev_get_integrity(inode->i_bdev);
+	bi = bdev_get_integrity(bdev);
 	if (bi == NULL) {
 		TRACE_DBG("Block integrity not supported");
 		goto out_no_bi;
@@ -1103,7 +1043,7 @@
 	res = 0;
 
 out_close:
-	filp_close(fd, NULL);
+	blkdev_put(bdev, FMODE_READ);
 
 out:
 	TRACE_EXIT_RES(res);
@@ -1219,7 +1159,7 @@
 		}
 	}
 
-	if (virt_dev->dif_filename != NULL) {
+	if (virt_dev->dev_active && virt_dev->dif_filename != NULL) {
 		/* Check if it can be used */
 		struct file *dfd = vdev_open_fd(virt_dev, virt_dev->dif_filename,
 					virt_dev->rd_only);
@@ -1251,12 +1191,6 @@
 		}
 	}
 
-	if (virt_dev->zero_copy && virt_dev->o_direct_flag) {
-		PRINT_ERROR("%s: combining zero_copy with o_direct is not"
-			    " supported", virt_dev->filename);
-		res = -EINVAL;
-		goto out;
-	}
 	if (!virt_dev->async && virt_dev->o_direct_flag) {
 		PRINT_ERROR("%s: using o_direct without setting async is not"
 			    " supported", virt_dev->filename);
@@ -1356,21 +1290,39 @@
 	return;
 }
 
+static bool vdisk_is_open(const struct scst_vdisk_dev *virt_dev)
+{
+	return virt_dev->fd || virt_dev->bdev;
+}
+
 static int vdisk_open_fd(struct scst_vdisk_dev *virt_dev, bool read_only)
 {
 	int res;
 
 	sBUG_ON(!virt_dev->filename);
-	sBUG_ON(virt_dev->fd);
+	sBUG_ON(vdisk_is_open(virt_dev));
 
-	virt_dev->fd = vdev_open_fd(virt_dev, virt_dev->filename, read_only);
-	if (IS_ERR(virt_dev->fd)) {
-		res = PTR_ERR(virt_dev->fd);
+	if (!virt_dev->dev_active) {
+		TRACE_MGMT_DBG("Skip opening for not active dev %s",
+			       virt_dev->dev->virt_name);
+		res = -EMEDIUMTYPE;
+	} else if (virt_dev->blockio) {
+		virt_dev->bdev_mode = FMODE_READ;
+		if (!read_only)
+			virt_dev->bdev_mode |= FMODE_WRITE;
+		virt_dev->bdev = blkdev_get_by_path(virt_dev->filename,
+					virt_dev->bdev_mode, (void *)__func__);
+		res = PTR_ERR_OR_ZERO(virt_dev->bdev);
+	} else {
+		virt_dev->fd = vdev_open_fd(virt_dev, virt_dev->filename,
+					    read_only);
+		res = PTR_ERR_OR_ZERO(virt_dev->fd);
+	}
+	if (res) {
+		virt_dev->bdev = NULL;
 		virt_dev->fd = NULL;
 		goto out;
 	}
-	virt_dev->bdev = virt_dev->blockio ? file_inode(virt_dev->fd)->i_bdev : NULL;
-	res = 0;
 
 	/*
 	 * For block devices, get the optimal I/O size from the block device
@@ -1390,27 +1342,34 @@
 		}
 	}
 
-	TRACE_DBG("virt_dev %s: fd %p open (dif_fd %p)", virt_dev->name,
-		virt_dev->fd, virt_dev->dif_fd);
+	TRACE_DBG("virt_dev %s: fd %p %p open (dif_fd %p)", virt_dev->name,
+		  virt_dev->fd, virt_dev->bdev, virt_dev->dif_fd);
 
 out:
 	return res;
 
 out_close_fd:
-	filp_close(virt_dev->fd, NULL);
-	virt_dev->fd = NULL;
+	if (virt_dev->blockio) {
+		blkdev_put(virt_dev->bdev, virt_dev->bdev_mode);
+		virt_dev->bdev = NULL;
+	} else {
+		filp_close(virt_dev->fd, NULL);
+		virt_dev->fd = NULL;
+	}
 	goto out;
 }
 
 static void vdisk_close_fd(struct scst_vdisk_dev *virt_dev)
 {
-	TRACE_DBG("virt_dev %s: closing fd %p (dif_fd %p)", virt_dev->name,
-		virt_dev->fd, virt_dev->dif_fd);
+	TRACE_DBG("virt_dev %s: closing fd %p %p (dif_fd %p)", virt_dev->name,
+		  virt_dev->fd, virt_dev->bdev, virt_dev->dif_fd);
 
-	if (virt_dev->fd) {
+	if (virt_dev->bdev) {
+		blkdev_put(virt_dev->bdev, virt_dev->bdev_mode);
+		virt_dev->bdev = NULL;
+	} else if (virt_dev->fd) {
 		filp_close(virt_dev->fd, NULL);
 		virt_dev->fd = NULL;
-		virt_dev->bdev = NULL;
 	}
 	if (virt_dev->dif_fd) {
 		filp_close(virt_dev->dif_fd, NULL);
@@ -1418,6 +1377,65 @@
 	}
 }
 
+static int vdisk_reopen_fd(struct scst_vdisk_dev *virt_dev, bool read_only)
+{
+	/*
+	 * To do: make this function transactional. That means that it either
+	 * succeeds or does not modify the state of @virt_dev.
+	 */
+	vdisk_close_fd(virt_dev);
+	return vdisk_open_fd(virt_dev, read_only);
+}
+
+static int vdisk_activate_dev(struct scst_vdisk_dev *virt_dev, bool rd_only)
+{
+	int rc = 0;
+
+	virt_dev->dev_active = 1;
+
+	/*
+	 * Only re-open FD if tgt_dev_cnt is not zero,
+	 * otherwise we will leak reference.
+	 */
+	if (virt_dev->tgt_dev_cnt) {
+		int i=0;
+		while (i<=5) {
+			rc = vdisk_open_fd(virt_dev, rd_only);
+			if (rc!=0) {
+				PRINT_ERROR("JPM vdisk_open_fd() returned rc= %d for i= %d", rc, i);
+				i++;
+				msleep(100);
+			}
+			else {
+				break;
+			}
+		}
+
+		if (rc) {
+			PRINT_ERROR("vdev %s: Unable to open FD: rd_only=%d, rc=%d",
+			         virt_dev->name, rd_only, rc);
+			virt_dev->dev_active = 0;
+			goto out;
+		}
+	}
+
+	if (virt_dev->reexam_pending) {
+		rc = vdisk_reexamine(virt_dev);
+		WARN_ON(rc != 0);
+		virt_dev->reexam_pending = 0;
+	}
+
+out:
+	return rc;
+}
+
+static void vdisk_disable_dev(struct scst_vdisk_dev *virt_dev)
+{
+	/* Close the FD here */
+	vdisk_close_fd(virt_dev);
+	virt_dev->dev_active = 0;
+}
+
 /* Invoked with scst_mutex held, so no further locking is necessary here. */
 static int vdisk_attach_tgt(struct scst_tgt_dev *tgt_dev)
 {
@@ -1430,7 +1448,7 @@
 
 	virt_dev->tgt_dev_cnt++;
 
-	if (virt_dev->fd != NULL)
+	if (vdisk_is_open(virt_dev))
 		goto out;
 
 	if (!virt_dev->nullio && !virt_dev->cdrom_empty) {
@@ -1446,6 +1464,7 @@
 		}
 	} else {
 		virt_dev->fd = NULL;
+		virt_dev->bdev = NULL;
 		virt_dev->dif_fd = NULL;
 	}
 
@@ -1485,15 +1504,11 @@
 
 	/* BLOCKIO can be here for DIF tags fsync */
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 32)
-	res = sync_page_range(file_inode(file), file->f_mapping, loff, len);
-#else
 #if 0	/* For sparse files we might need to sync metadata as well */
 	res = generic_write_sync(file, loff, len);
 #else
 	res = filemap_write_and_wait_range(file->f_mapping, loff, len);
 #endif
-#endif
 	if (unlikely(res != 0)) {
 		PRINT_ERROR("sync range failed (%d)", res);
 		if (cmd != NULL) {
@@ -1801,7 +1816,6 @@
 
 	TRACE_ENTRY();
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 38)
 	TRACE_DBG("Fallocating range %lld, len %lld",
 		(unsigned long long)off, (unsigned long long)len);
 
@@ -1819,9 +1833,6 @@
 			SCST_LOAD_SENSE(scst_sense_write_error));
 		res = -EIO;
 	}
-#else
-	res = 0;
-#endif
 
 	TRACE_EXIT_RES(res);
 	return res;
@@ -1830,12 +1841,7 @@
 static int vdisk_unmap_range(struct scst_cmd *cmd,
 	struct scst_vdisk_dev *virt_dev, uint64_t start_lba, uint64_t blocks)
 {
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27)
 	int res, err;
-#else
-	int res;
-#endif
-	struct file *fd = virt_dev->fd;
 
 	TRACE_ENTRY();
 
@@ -1856,25 +1862,12 @@
 		  (unsigned long long)start_lba, blocks);
 
 	if (virt_dev->blockio) {
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 27)
+		struct block_device *bdev = virt_dev->bdev;
 		sector_t start_sector = start_lba << (cmd->dev->block_shift - 9);
 		sector_t nr_sects = blocks << (cmd->dev->block_shift - 9);
-		struct inode *inode = file_inode(fd);
 		gfp_t gfp = cmd->cmd_gfp_mask;
 
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 31)
-		err = blkdev_issue_discard(inode->i_bdev, start_sector, nr_sects, gfp);
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35)       \
-      && !(LINUX_VERSION_CODE == KERNEL_VERSION(2, 6, 34) \
-	   && defined(CONFIG_SUSE_KERNEL))
-		err = blkdev_issue_discard(inode->i_bdev, start_sector, nr_sects,
-				gfp, DISCARD_FL_WAIT);
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
-		err = blkdev_issue_discard(inode->i_bdev, start_sector, nr_sects,
-				gfp, BLKDEV_IFL_WAIT);
-#else
-		err = blkdev_issue_discard(inode->i_bdev, start_sector, nr_sects, gfp, 0);
-#endif
+		err = blkdev_issue_discard(bdev, start_sector, nr_sects, gfp);
 		if (unlikely(err != 0)) {
 			PRINT_ERROR("blkdev_issue_discard() for "
 				"LBA %lld, blocks %lld failed: %d",
@@ -1884,14 +1877,10 @@
 			res = -EIO;
 			goto out;
 		}
-#else
-		scst_set_cmd_error(cmd, SCST_LOAD_SENSE(scst_sense_invalid_opcode));
-		res = -EIO;
-		goto out;
-#endif
 	} else {
 		loff_t off = start_lba << cmd->dev->block_shift;
 		loff_t len = blocks << cmd->dev->block_shift;
+		struct file *fd = virt_dev->fd;
 
 		res = vdisk_unmap_file_range(cmd, virt_dev, off, len, fd);
 		if (unlikely(res != 0))
@@ -2840,27 +2829,9 @@
 struct bio_priv_sync {
 	struct completion c;
 	int error;
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 6, 0))
-	struct bio_set *bs;
-	struct completion c1;
-#endif
 };
 
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 6, 0))
-static void blockio_bio_destructor_sync(struct bio *bio)
-{
-	struct bio_priv_sync *s = bio->bi_private;
-
-	bio_free(bio, s->bs);
-	complete(&s->c1);
-}
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
-static int blockio_end_sync_io(struct bio *bio, unsigned int bytes_done,
-			       int error)
-{
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
 static void blockio_end_sync_io(struct bio *bio, int error)
 {
 #else
@@ -2875,11 +2846,6 @@
 #endif
 	struct bio_priv_sync *s = bio->bi_private;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
-	if (bio->bi_size)
-		return 1;
-#endif
-
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
 	if (!bio_flagged(bio, BIO_UPTODATE) && error == 0) {
 		PRINT_ERROR("Not up to date bio with error 0; returning -EIO");
@@ -2890,11 +2856,7 @@
 	s->error = error;
 	complete(&s->c);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
-	return 0;
-#else
 	return;
-#endif
 }
 
 /*
@@ -2913,10 +2875,6 @@
 {
 	struct bio_priv_sync s = {
 		COMPLETION_INITIALIZER_ONSTACK(s.c), 0,
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 6, 0))
-		virt_dev->vdisk_bioset,
-		COMPLETION_INITIALIZER_ONSTACK(s.c1)
-#endif
 	};
 	struct block_device *bdev = virt_dev->bdev;
 	const bool is_vmalloc = is_vmalloc_addr(buf);
@@ -2926,22 +2884,15 @@
 	int max_nr_vecs, rc;
 	unsigned int bytes, off;
 	ssize_t ret = -ENOMEM;
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 6, 0))
-	bool submitted = false;
-#endif
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
-	max_nr_vecs = BIO_MAX_PAGES;
+	max_nr_vecs = BIO_MAX_VECS;
 #else
-	max_nr_vecs = min(bio_get_nr_vecs(bdev), BIO_MAX_PAGES);
+	max_nr_vecs = min(bio_get_nr_vecs(bdev), BIO_MAX_VECS);
 #endif
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
-	bio = bio_alloc_bioset(GFP_KERNEL, max_nr_vecs, virt_dev->vdisk_bioset);
-#else
-	bio = bio_alloc(GFP_KERNEL, max_nr_vecs);
-#endif
-
+	bio = bio_alloc_bioset(/*bdev=*/NULL, max_nr_vecs, /*opf=*/0,
+			       GFP_KERNEL, virt_dev->vdisk_bioset);
 	if (!bio)
 		goto out;
 
@@ -2955,9 +2906,6 @@
 	bio_set_dev(bio, bdev);
 	bio->bi_end_io = blockio_end_sync_io;
 	bio->bi_private = &s;
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 6, 0))
-	bio->bi_destructor = blockio_bio_destructor_sync;
-#endif
 #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 14, 0)
 	bio->bi_sector = *loff >> 9;
 #else
@@ -2985,9 +2933,6 @@
 #else
 	submit_bio(bio);
 #endif
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 6, 0))
-	submitted = true;
-#endif
 	wait_for_completion(&s.c);
 	ret = (unsigned long)s.error;
 	if (likely(ret == 0)) {
@@ -2997,10 +2942,6 @@
 
 free:
 	bio_put(bio);
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 6, 0))
-	if (submitted)
-		wait_for_completion(&s.c1);
-#endif
 
 out:
 	return ret;
@@ -3166,28 +3107,74 @@
 	}
 }
 
-static bool vdisk_alloc_async_kvec(struct scst_cmd *cmd,
+static bool vdisk_alloc_async_bvec(struct scst_cmd *cmd,
 				   struct vdisk_cmd_params *p)
 {
 	int n;
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0)
+	n = scst_get_buf_page_count(cmd);
+#else
 	n = scst_get_buf_count(cmd);
-	if (n <= ARRAY_SIZE(p->async.small_kvec)) {
-		p->async.kvec = &p->async.small_kvec[0];
+#endif
+	if (n <= ARRAY_SIZE(p->async.small_bvec)) {
+		p->async.bvec = &p->async.small_bvec[0];
 		return true;
 	}
 
-	p->async.kvec = kmalloc_array(n, sizeof(*p->async.kvec),
+	p->async.bvec = kmalloc_array(n, sizeof(*p->async.bvec),
 				      cmd->cmd_gfp_mask);
-	if (p->async.kvec == NULL) {
-		PRINT_ERROR("Unable to allocate kvec (%d)", n);
+	if (p->async.bvec == NULL) {
+		PRINT_ERROR("Unable to allocate bvec (%d)", n);
 		return false;
 	}
 
 	return true;
 }
 
-static void fileio_async_complete(struct kiocb *iocb, long ret, long ret2)
+static inline
+struct bio_vec *vdisk_map_pages_to_bvec(struct bio_vec *bvec, struct page *page,
+					ssize_t length, int offset)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0)
+	ssize_t page_len = min_t(ssize_t, length, PAGE_SIZE - offset);
+#else
+	ssize_t page_len = length;
+#endif
+
+	*bvec++ = (struct bio_vec) {
+		.bv_page   = page,
+		.bv_offset = offset,
+		.bv_len    = page_len,
+	};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0)
+	length -= page_len;
+
+	while (length > 0) {
+		page++;
+		page_len = min_t(ssize_t, length, PAGE_SIZE);
+
+		*bvec++ = (struct bio_vec) {
+			.bv_page   = page,
+			.bv_offset = 0,
+			.bv_len    = page_len,
+		};
+
+		length -= page_len;
+	}
+#endif
+
+	return bvec;
+}
+
+static void fileio_async_complete(struct kiocb *iocb, long ret
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(9, 2))
+				  , long ret2
+#endif
+				  )
 {
 	struct vdisk_cmd_params *p = container_of(iocb, typeof(*p), async.iocb);
 	struct scst_cmd *cmd = p->cmd;
@@ -3211,12 +3198,12 @@
 			w->cmd = cmd;
 			schedule_work(&w->work);
 			return;
-		} else {
-			scst_set_busy(cmd);
 		}
+
+		scst_set_busy(cmd);
 	}
 	cmd->completed = 1;
-	cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, SCST_CONTEXT_SAME);
+	cmd->scst_cmd_done(cmd, SCST_CMD_STATE_DEFAULT, scst_estimate_context());
 }
 
 static enum compl_status_e fileio_exec_async(struct vdisk_cmd_params *p)
@@ -3227,10 +3214,10 @@
 	struct file *fd = virt_dev->fd;
 	struct iov_iter iter = { };
 	ssize_t length, total = 0;
-	struct kvec *kvec;
+	struct bio_vec *bvec;
+	struct page *page;
 	struct kiocb *iocb = &p->async.iocb;
-	uint8_t *address;
-	int sg_cnt = 0, dir, ret;
+	int offset, sg_cnt = 0, dir, ret;
 
 	switch (cmd->data_direction) {
 	case SCST_DATA_READ:
@@ -3244,28 +3231,32 @@
 		return CMD_FAILED;
 	}
 
-	if (!vdisk_alloc_async_kvec(cmd, p)) {
+	if (!vdisk_alloc_async_bvec(cmd, p)) {
 		scst_set_busy(cmd);
 		return CMD_SUCCEEDED;
 	}
 
 	p->execute_async = true;
 
-	kvec = p->async.kvec;
-	length = scst_get_buf_first(cmd, &address);
+	bvec = p->async.bvec;
+	length = scst_get_sg_page_first(cmd, &page, &offset);
 	while (length) {
-		*kvec++ = (struct kvec){
-			.iov_base = address,
-			.iov_len = length,
-		};
+		bvec = vdisk_map_pages_to_bvec(bvec, page, length, offset);
+
 		total += length;
 		sg_cnt++;
-		length = scst_get_buf_next(cmd, &address);
+		length = scst_get_sg_page_next(cmd, &page, &offset);
 	}
 
 	WARN_ON_ONCE(sg_cnt != cmd->sg_cnt);
 
-	iov_iter_kvec(&iter, dir, p->async.kvec, sg_cnt, total);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) || 	\
+		(defined(RHEL_RELEASE_CODE) && 		\
+		 RHEL_RELEASE_CODE -0 >= RHEL_RELEASE_VERSION(8, 2))
+	iov_iter_bvec(&iter, dir, p->async.bvec, sg_cnt, total);
+#else
+	iov_iter_bvec(&iter, ITER_BVEC | dir, p->async.bvec, sg_cnt, total);
+#endif
 	*iocb = (struct kiocb) {
 		.ki_pos = p->loff,
 		.ki_filp = fd,
@@ -3287,10 +3278,17 @@
 		else
 			break;
 	}
-	if (p->async.kvec != p->async.small_kvec)
-		kfree(p->async.kvec);
-	if (ret != -EIOCBQUEUED)
+	if (p->async.bvec != p->async.small_bvec)
+		kfree(p->async.bvec);
+	if (ret != -EIOCBQUEUED) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(9, 2))
 		fileio_async_complete(iocb, ret, 0);
+#else
+		fileio_async_complete(iocb, ret);
+#endif
+	}
 	/*
 	 * Return RUNNING_ASYNC even if fileio_async_complete() has been
 	 * called because that function calls cmd->scst_cmd_done().
@@ -3414,7 +3412,7 @@
 	if (unlikely(!vdisk_parse_offset(&p, cmd)))
 		goto err;
 
-	if (unlikely(virt_dev->fd == NULL)) {
+	if (unlikely(virt_dev->bdev == NULL)) {
 		if (!vdisk_no_fd_allowed_commands(cmd)) {
 			/*
 			 * We should not get here, unless the user space
@@ -4542,7 +4540,6 @@
 static int vdisk_set_wt(struct scst_vdisk_dev *virt_dev, int wt, bool read_only)
 {
 	int res = 0;
-	struct file *fd, *dif_fd = NULL;
 	bool old_wt = virt_dev->wt_flag;
 
 	TRACE_ENTRY();
@@ -4554,42 +4551,20 @@
 	virt_dev->wt_flag = wt;
 	spin_unlock(&virt_dev->flags_lock);
 
-	if (virt_dev->fd == NULL)
-		goto out;
-
 	/*
-	 * MODE SELECT is strictly serialized command, so it's safe here
-	 * to reopen fd.
+	 * MODE SELECT is a strictly serialized command so it's safe to reopen
+	 * the fd.
 	 */
-
-	fd = vdev_open_fd(virt_dev, virt_dev->filename, read_only);
-	if (IS_ERR(fd)) {
-		res = PTR_ERR(fd);
-		goto out_err;
+	if (vdisk_is_open(virt_dev)) {
+		res = vdisk_reopen_fd(virt_dev, read_only);
+		if (res < 0)
+			goto out_err;
 	}
 
-	if (virt_dev->dif_filename != NULL) {
-		dif_fd = vdev_open_fd(virt_dev, virt_dev->dif_filename, read_only);
-		if (IS_ERR(dif_fd)) {
-			res = PTR_ERR(dif_fd);
-			goto out_err_close_fd;
-		}
-	}
-
-	filp_close(virt_dev->fd, NULL);
-	if (virt_dev->dif_fd)
-		filp_close(virt_dev->dif_fd, NULL);
-
-	virt_dev->fd = fd;
-	virt_dev->dif_fd = dif_fd;
-
 out:
 	TRACE_EXIT_RES(res);
 	return res;
 
-out_err_close_fd:
-	filp_close(fd, NULL);
-
 out_err:
 	spin_lock(&virt_dev->flags_lock);
 	virt_dev->wt_flag = old_wt;
@@ -4976,17 +4951,6 @@
 	return CMD_SUCCEEDED;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
-/*
- * See also patch "block: Export I/O topology for block devices and partitions"
- * (commit ID c72758f33784).
- */
-static inline unsigned int queue_physical_block_size(struct request_queue *q)
-{
-	return 4096;
-}
-#endif
-
 static enum compl_status_e vdisk_exec_read_capacity16(struct vdisk_cmd_params *p)
 {
 	struct scst_cmd *cmd = p->cmd;
@@ -5723,10 +5687,6 @@
 struct scst_blockio_work {
 	atomic_t bios_inflight;
 	struct scst_cmd *cmd;
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 6, 0))
-	/* just to avoid extra dereferences */
-	struct bio_set *bioset;
-#endif
 };
 
 static inline void blockio_check_finish(struct scst_blockio_work *blockio_work)
@@ -5770,20 +5730,7 @@
 	kmem_cache_free(blockio_work_cachep, blockio_work);
 }
 
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 6, 0))
-static void blockio_bio_destructor(struct bio *bio)
-{
-	struct scst_blockio_work *blockio_work = bio->bi_private;
-
-	bio_free(bio, blockio_work->bioset);
-	blockio_check_finish(blockio_work);
-}
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
-static int blockio_endio(struct bio *bio, unsigned int bytes_done, int error)
-{
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
 static void blockio_endio(struct bio *bio, int error)
 {
 #else
@@ -5798,11 +5745,6 @@
 #endif
 	struct scst_blockio_work *blockio_work = bio->bi_private;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
-	if (bio->bi_size)
-		return 1;
-#endif
-
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
 	if (unlikely(!bio_flagged(bio, BIO_UPTODATE))) {
 		if (error == 0) {
@@ -5826,9 +5768,7 @@
 		 */
 		spin_lock_irqsave(&vdev_err_lock, flags);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
-		if (bio->bi_rw & (1 << BIO_RW)) {
-#elif (!defined(CONFIG_SUSE_KERNEL) &&			 \
+#if (!defined(CONFIG_SUSE_KERNEL) &&			 \
 	LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)) || \
 	LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)
 		if (bio->bi_rw & REQ_WRITE) {
@@ -5851,27 +5791,15 @@
 		spin_unlock_irqrestore(&vdev_err_lock, flags);
 	}
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)) || (LINUX_VERSION_CODE > KERNEL_VERSION(3, 6, 0))
 	blockio_check_finish(blockio_work);
-#endif
 
 	bio_put(bio);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
-	return 0;
-#else
 	return;
-#endif
 }
 
 static void vdisk_bio_set_failfast(struct bio *bio)
 {
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 27)
-	bio->bi_rw |= (1 << BIO_RW_FAILFAST);
-#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 35)
-	bio->bi_rw |= (1 << BIO_RW_FAILFAST_DEV) |
-		      (1 << BIO_RW_FAILFAST_TRANSPORT) |
-		      (1 << BIO_RW_FAILFAST_DRIVER);
-#elif (!defined(CONFIG_SUSE_KERNEL) &&			 \
+#if (!defined(CONFIG_SUSE_KERNEL) &&			 \
 	LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)) || \
 	LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)
 	bio->bi_rw |= REQ_FAILFAST_DEV |
@@ -5889,36 +5817,20 @@
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) ||			\
 defined(CONFIG_SUSE_KERNEL) && LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
 	bio->bi_opf |= REQ_SYNC;
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) ||			\
-	(defined(RHEL_MAJOR) &&						\
-	 (RHEL_MAJOR -0 > 6 || RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 > 0))
-	bio->bi_rw |= REQ_SYNC;
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
-	bio->bi_rw |= 1 << BIO_RW_SYNCIO;
 #else
-	bio->bi_rw |= 1 << BIO_RW_SYNC;
+	bio->bi_rw |= REQ_SYNC;
 #endif
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) ||			\
 defined(CONFIG_SUSE_KERNEL) && LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0)
 	bio->bi_opf |= REQ_PRIO;
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) ||			\
-	(defined(RHEL_MAJOR) &&						\
-	 (RHEL_MAJOR -0 > 6 || RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 > 0))
+#else
 	bio->bi_rw |= REQ_META;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 1, 0)
 	/*
 	 * Priority boosting was separated from REQ_META in commit 65299a3b
 	 * (kernel 3.1.0).
 	 */
 	bio->bi_rw |= REQ_PRIO;
 #endif
-#elif !defined(RHEL_MAJOR) || RHEL_MAJOR -0 >= 6
-	/*
-	 * BIO_* and REQ_* flags were unified in commit 7b6d91da (kernel
-	 * 2.6.36).
-	 */
-	bio->bi_rw |= BIO_RW_META;
-#endif
 }
 
 #if defined(CONFIG_BLK_DEV_INTEGRITY)
@@ -5964,7 +5876,7 @@
 	}
 
 	bip = bio_integrity_alloc(bio, gfp_mask, pages);
-	if (unlikely(bip == NULL)) {
+	if (IS_ERR_OR_NULL(bip)) {
 		PRINT_WARNING("Allocation of %d pages for DIF tags "
 			"failed! (dev %s)", pages, dev->virt_name);
 		goto out; /* proceed without integrity */
@@ -6033,9 +5945,7 @@
 	struct scst_vdisk_dev *virt_dev = dev->dh_priv;
 	int block_shift = dev->block_shift;
 	struct block_device *bdev = virt_dev->bdev;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
 	struct bio_set *bs = virt_dev->vdisk_bioset;
-#endif
 	struct request_queue *q = bdev_get_queue(bdev);
 	int length, max_nr_vecs = 0, offset;
 	struct page *page;
@@ -6044,9 +5954,7 @@
 	struct scst_blockio_work *blockio_work;
 	int bios = 0;
 	gfp_t gfp_mask = cmd->cmd_gfp_mask;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
 	struct blk_plug plug;
-#endif
 	struct scatterlist *dsg;
 	int dsg_offs, dsg_len;
 	bool dif = virt_dev->blk_integrity &&
@@ -6082,15 +5990,12 @@
 #endif
 
 	blockio_work->cmd = cmd;
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 6, 0))
-	blockio_work->bioset = bs;
-#endif
 
 	if (q)
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 3, 0)
-		max_nr_vecs = BIO_MAX_PAGES;
+		max_nr_vecs = BIO_MAX_VECS;
 #else
-		max_nr_vecs = min(bio_get_nr_vecs(bdev), BIO_MAX_PAGES);
+		max_nr_vecs = min(bio_get_nr_vecs(bdev), BIO_MAX_VECS);
 #endif
 	else
 		max_nr_vecs = 1;
@@ -6125,12 +6030,8 @@
 			int rc;
 
 			if (need_new_bio) {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
-				bio = bio_alloc_bioset(gfp_mask, max_nr_vecs, bs);
-#else
-				bio = bio_alloc(gfp_mask, max_nr_vecs);
-#endif
-
+				bio = bio_alloc_bioset(/*bdev=*/NULL, max_nr_vecs, /*opf=*/0,
+						       gfp_mask, bs);
 				if (!bio) {
 					PRINT_ERROR("Failed to create bio "
 						"for data segment %d (cmd %p)",
@@ -6149,13 +6050,8 @@
 #endif
 				bio_set_dev(bio, bdev);
 				bio->bi_private = blockio_work;
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)) && (LINUX_VERSION_CODE <= KERNEL_VERSION(3, 6, 0))
-				bio->bi_destructor = blockio_bio_destructor;
-#endif
 				if (write)
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
-					bio->bi_rw |= (1 << BIO_RW);
-#elif (!defined(CONFIG_SUSE_KERNEL) &&			\
+#if (!defined(CONFIG_SUSE_KERNEL) &&			\
 	LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)) || \
 	LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)
 					bio->bi_rw |= REQ_WRITE;
@@ -6221,9 +6117,7 @@
 	/* +1 to prevent erroneous too early command completion */
 	atomic_set(&blockio_work->bios_inflight, bios+1);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
 	blk_start_plug(&plug);
-#endif
 
 	while (hbio) {
 		bio = hbio;
@@ -6239,12 +6133,7 @@
 #endif
 	}
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 39)
 	blk_finish_plug(&plug);
-#else
-	if (q && q->unplug_fn)
-		q->unplug_fn(q);
-#endif
 
 	if ((dev->dev_dif_mode & SCST_DIF_MODE_DEV_STORE) &&
 	    (virt_dev->dif_fd != NULL) &&
@@ -6480,9 +6369,7 @@
 	if (!close)
 		return;
 
-	virt_dev->dev_active = 0;
-
-	vdisk_close_fd(virt_dev);
+	vdisk_disable_dev(virt_dev);
 
 	TRACE_EXIT();
 	return;
@@ -6512,36 +6399,9 @@
 	if (!open)
 		return;
 
-	virt_dev->dev_active = 1;
-
-	/*
-	 * only reopen fd if tgt_dev_cnt is not zero, otherwise we will
-	 * leak reference.
-	 */
-
-	if (virt_dev->tgt_dev_cnt) {
-		int i=0;
-		while (i<=5) {
-			rc = vdisk_open_fd(virt_dev, dev->dev_rd_only);
-			if (rc!=0) {
-				PRINT_ERROR("JPM vdisk_open_fd() returned rc= %d for i= %d", rc, i);
-				i++;
-				msleep(100);
-			}
-			else {
-				break;
-			}
-		}
-	}
-
-	if (rc == 0) {
-		if (virt_dev->reexam_pending) {
-			rc = vdisk_reexamine(virt_dev);
-			WARN_ON(rc != 0);
-			virt_dev->reexam_pending = 0;
-		}
-	} else {
-		PRINT_ERROR("dev %s: opening after ALUA state change to %s failed",
+	rc = vdisk_activate_dev(virt_dev, dev->dev_rd_only);
+	if (rc) {
+		PRINT_ERROR("dev %s: Activating after ALUA state change to %s failed",
 			    dev->virt_name,
 			    scst_alua_state_name(new_state));
 	}
@@ -6732,10 +6592,6 @@
 				(long long)be64_to_cpu(virt_dev->dif_static_app_tag_combined));
 	}
 
-	if (virt_dev->zero_copy)
-		i += snprintf(&buf[i], buf_size - i, "%sZERO_COPY",
-			(j == i) ? "(" : ", ");
-
 	if (virt_dev->async)
 		i += snprintf(&buf[i], buf_size - i, "%sASYNC",
 			(j == i) ? "(" : ", ");
@@ -6759,7 +6615,7 @@
 	sBUG_ON(virt_dev->nullio);
 	sBUG_ON(!virt_dev->filename);
 
-	if ((virt_dev->fd == NULL) || !virt_dev->dev_active) {
+	if ((!virt_dev->fd && !virt_dev->bdev) || !virt_dev->dev_active) {
 		res = -EMEDIUMTYPE;
 		goto out;
 	}
@@ -6799,7 +6655,6 @@
 	return res;
 }
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
 static int vdisk_create_bioset(struct scst_vdisk_dev *virt_dev)
 {
 	int res = 0;
@@ -6822,12 +6677,8 @@
 	}
 
 	if (virt_dev->dif_mode & SCST_DIF_MODE_DEV) {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
 		/* The same, pool size doesn't really matter */
 		res = bioset_integrity_create(virt_dev->vdisk_bioset, 2);
-#else
-		res = -EOPNOTSUPP;
-#endif
 		if (res != 0) {
 			PRINT_ERROR("Failed to create integrity bioset "
 				"(dev %s)", virt_dev->name);
@@ -6860,7 +6711,6 @@
 	bioset_free(virt_dev->vdisk_bioset);
 #endif
 }
-#endif
 
 static void vdev_inq_changed_fn(struct work_struct *work)
 {
@@ -6879,7 +6729,6 @@
 	return;
 }
 
-/* scst_vdisk_mutex supposed to be held */
 static int vdev_create_node(struct scst_dev_type *devt,
 	const char *name, int nodeid, struct scst_vdisk_dev **res_virt_dev)
 {
@@ -6887,6 +6736,8 @@
 	struct scst_vdisk_dev *virt_dev, *vv;
 	uint64_t dev_id_num;
 
+	lockdep_assert_held(&scst_vdisk_mutex);
+
 	res = -EEXIST;
 	if (vdev_find(name))
 		goto out;
@@ -6983,9 +6834,7 @@
 
 static void vdev_destroy(struct scst_vdisk_dev *virt_dev)
 {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
 	vdisk_free_bioset(virt_dev);
-#endif
 	kfree(virt_dev->filename);
 	kfree(virt_dev->dif_filename);
 	kfree(virt_dev);
@@ -7160,8 +7009,8 @@
 				}
 				if (dd == NULL)
 					break;
-				else
-					*dd = '|';
+
+				*dd = '|';
 				d = dd+1;
 			}
 			TRACE_DBG("DIF DEV mode %x", virt_dev->dif_mode);
@@ -7239,8 +7088,6 @@
 			virt_dev->thin_provisioned_manually_set = 1;
 			TRACE_DBG("THIN PROVISIONED %d",
 				virt_dev->thin_provisioned);
-		} else if (!strcasecmp("zero_copy", p)) {
-			virt_dev->zero_copy = !!ull_val;
 		} else if (!strcasecmp("async", p)) {
 			virt_dev->async = !!ull_val;
 		} else if (!strcasecmp("size", p)) {
@@ -7286,7 +7133,6 @@
 	return res;
 }
 
-/* scst_vdisk_mutex supposed to be held */
 static int vdev_fileio_add_device(const char *device_name, char *params)
 {
 	int res = 0;
@@ -7294,6 +7140,8 @@
 
 	TRACE_ENTRY();
 
+	lockdep_assert_held(&scst_vdisk_mutex);
+
 	res = vdev_create(&vdisk_file_devtype, device_name, &virt_dev);
 	if (res != 0)
 		goto out;
@@ -7349,7 +7197,6 @@
 	goto out;
 }
 
-/* scst_vdisk_mutex supposed to be held */
 static int vdev_blockio_add_device(const char *device_name, char *params)
 {
 	int res = 0;
@@ -7357,6 +7204,8 @@
 
 	TRACE_ENTRY();
 
+	lockdep_assert_held(&scst_vdisk_mutex);
+
 	res = vdev_create(&vdisk_blk_devtype, device_name, &virt_dev);
 	if (res != 0)
 		goto out;
@@ -7381,11 +7230,9 @@
 
 	vdev_check_node(&virt_dev, NUMA_NO_NODE);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
 	res = vdisk_create_bioset(virt_dev);
 	if (res != 0)
 		goto out_destroy;
-#endif
 
 	list_add_tail(&virt_dev->vdev_list_entry, &vdev_list);
 
@@ -7413,7 +7260,6 @@
 	goto out;
 }
 
-/* scst_vdisk_mutex supposed to be held */
 static int vdev_nullio_add_device(const char *device_name, char *params)
 {
 	int res = 0;
@@ -7421,6 +7267,8 @@
 
 	TRACE_ENTRY();
 
+	lockdep_assert_held(&scst_vdisk_mutex);
+
 	res = vdev_create(&vdisk_null_devtype, device_name, &virt_dev);
 	if (res != 0)
 		goto out;
@@ -7537,11 +7385,12 @@
 	cancel_work_sync(&virt_dev->vdev_inq_changed_work);
 }
 
-/* scst_vdisk_mutex supposed to be held */
 static void vdev_del_device(struct scst_vdisk_dev *virt_dev)
 {
 	TRACE_ENTRY();
 
+	lockdep_assert_held(&scst_vdisk_mutex);
+
 	scst_unregister_virtual_device(virt_dev->virt_id, vdev_on_free,
 				       virt_dev);
 
@@ -7584,7 +7433,6 @@
 	return res;
 }
 
-/* scst_vdisk_mutex supposed to be held */
 static ssize_t __vcdrom_add_device(const char *device_name, char *params)
 {
 	int res = 0;
@@ -7592,6 +7440,8 @@
 
 	TRACE_ENTRY();
 
+	lockdep_assert_held(&scst_vdisk_mutex);
+
 	res = vdev_create(&vcdrom_devtype, device_name, &virt_dev);
 	if (res != 0)
 		goto out;
@@ -7690,24 +7540,20 @@
 }
 
 
-static int vcdrom_change(struct scst_vdisk_dev *virt_dev,
-	char *buffer)
+static int vcdrom_change(struct scst_vdisk_dev *virt_dev, char *buffer)
 {
 	loff_t err;
-	char *old_fn, *p, *pp;
+	char *old_fn, *p;
 	bool old_empty;
-	struct file *old_fd;
-	struct file *old_dif_fd;
 	const char *filename = NULL;
 	int length = strlen(buffer);
 	int res = 0;
 
 	TRACE_ENTRY();
 
-	TRACE_DBG("virt_dev %s, empty %d, fd %p (dif_fd %p), filename %p", virt_dev->name,
-		virt_dev->cdrom_empty, virt_dev->fd, virt_dev->dif_fd, virt_dev->filename);
-
-	sBUG_ON(virt_dev->dif_fd); /* DIF is not supported for CDROMs */
+	TRACE_DBG("virt_dev %s, empty %d, fd %p (dif_fd %p), filename %p",
+		  virt_dev->name, virt_dev->cdrom_empty, virt_dev->fd,
+		  virt_dev->dif_fd, virt_dev->filename);
 
 	if (virt_dev->prevent_allow_medium_removal) {
 		PRINT_ERROR("Prevent medium removal for "
@@ -7718,16 +7564,16 @@
 
 	p = buffer;
 
+	/* Skip leading whitespace */
 	while (isspace(*p) && *p != '\0')
 		p++;
 	filename = p;
+	/* Strip trailing whitespace */
+	WARN_ON_ONCE(length == 0);
 	p = &buffer[length-1];
-	pp = &buffer[length];
-	while (isspace(*p) && (*p != '\0')) {
-		pp = p;
+	while (p > buffer && isspace(*p))
 		p--;
-	}
-	*pp = '\0';
+	p[1] = '\0';
 
 	res = scst_suspend_activity(SCST_SUSPEND_TIMEOUT_USER);
 	if (res != 0)
@@ -7737,8 +7583,6 @@
 	mutex_lock(&scst_mutex);
 
 	old_empty = virt_dev->cdrom_empty;
-	old_fd = virt_dev->fd;
-	old_dif_fd = virt_dev->dif_fd;
 	old_fn = virt_dev->filename;
 
 	if (*filename == '\0') {
@@ -7765,24 +7609,17 @@
 		res = vdisk_get_file_size(virt_dev, &err);
 		if (res != 0)
 			goto out_free_fn;
-		if (virt_dev->fd == NULL) {
-			res = vdisk_open_fd(virt_dev, true);
+		if (!vdisk_is_open(virt_dev)) {
+			res = vdisk_reopen_fd(virt_dev, true);
 			if (res != 0)
 				goto out_free_fn;
 			sBUG_ON(!virt_dev->fd);
-
-			TRACE_DBG("Closing old_fd %p", old_fd);
-			if (old_fd != NULL)
-				filp_close(old_fd, NULL);
-			if (old_dif_fd != NULL)
-				filp_close(old_dif_fd, NULL);
-			old_fd = NULL;
-			old_dif_fd = NULL;
 		}
 	} else {
 		err = 0;
 		virt_dev->filename = NULL;
 		virt_dev->fd = NULL;
+		virt_dev->bdev = NULL;
 	}
 
 	virt_dev->file_size = err;
@@ -7820,7 +7657,6 @@
 	return res;
 
 out_free_fn:
-	virt_dev->fd = old_fd;
 	kfree(virt_dev->filename);
 	virt_dev->filename = old_fn;
 
@@ -7940,7 +7776,7 @@
 
 	virt_dev = dev->dh_priv;
 
-	queue_ua = (virt_dev->fd != NULL);
+	queue_ua = vdisk_is_open(virt_dev);
 
 	if ((new_size & ((1 << virt_dev->blk_shift) - 1)) == 0) {
 		virt_dev->file_size = new_size;
@@ -8158,9 +7994,7 @@
 	virt_dev = dev->dh_priv;
 
 	pos = sprintf(buf, "%d\n%s", virt_dev->thin_provisioned,
-		      virt_dev->thin_provisioned_manually_set &&
-		      (virt_dev->thin_provisioned !=
-		       virt_dev->dev_thin_provisioned) ?
+		      virt_dev->thin_provisioned_manually_set ?
 		      SCST_SYSFS_KEY_MARK "\n" : "");
 
 	TRACE_EXIT_RES(pos);
@@ -9142,18 +8976,10 @@
 	}
 
 	write_lock(&vdisk_serial_rwlock);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) ||	\
-    defined(CONFIG_SUSE_KERNEL) &&			\
-    LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 76)
 	if (hex2bin(virt_dev->eui64_id, buf, count / 2) == 0)
 		virt_dev->eui64_id_len = count / 2;
 	else
 		res = -EINVAL;
-#else
-	memset(virt_dev->eui64_id, 0, sizeof(virt_dev->eui64_id));
-	hex2bin(virt_dev->eui64_id, buf, count / 2);
-	virt_dev->eui64_id_len = count / 2;
-#endif
 	write_unlock(&vdisk_serial_rwlock);
 
 	if (res >= 0)
@@ -9212,13 +9038,13 @@
 	case 2 * 8:
 		if (strchr("235", buf[0]))
 			break;
-		else
-			goto out;
+
+		goto out;
 	case 2 * 16:
 		if (strchr("6", buf[0]))
 			break;
-		else
-			goto out;
+
+		goto out;
 	default:
 		goto out;
 	}
@@ -9226,18 +9052,10 @@
 	res = count;
 
 	write_lock(&vdisk_serial_rwlock);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) ||	\
-    defined(CONFIG_SUSE_KERNEL) &&			\
-    LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 76)
 	if (hex2bin(virt_dev->naa_id, buf, c / 2) == 0)
 		virt_dev->naa_id_len = c / 2;
 	else
 		res = -EINVAL;
-#else
-	memset(virt_dev->naa_id, 0, sizeof(virt_dev->naa_id));
-	hex2bin(virt_dev->naa_id, buf, c / 2);
-	virt_dev->naa_id_len = c / 2;
-#endif
 	write_unlock(&vdisk_serial_rwlock);
 
 	if (res >= 0)
@@ -9441,36 +9259,24 @@
 	res = kstrtol(work->buf, 0, &dev_active);
 	if (res)
 		goto unlock;
-	res = -EINVAL;
-	if (dev_active < 0 || dev_active > 1)
+
+	if (dev_active < 0 || dev_active > 1) {
+		res = -EINVAL;
 		goto unlock;
-	if (dev_active != virt_dev->dev_active) {
-		res = 0;
-		if (dev_active == 0) {
-			/* Close the FD here */
-			vdisk_close_fd(virt_dev);
-			virt_dev->dev_active = dev_active;
-		} else {
-			/* Re-open FD if tgt_dev_cnt is not zero */
-			virt_dev->dev_active = dev_active;
-			if (virt_dev->tgt_dev_cnt)
-				res = vdisk_open_fd(virt_dev, dev->dev_rd_only);
-			if (res == 0) {
-				if (virt_dev->reexam_pending) {
-					res = vdisk_reexamine(virt_dev);
-					WARN_ON(res != 0);
-					virt_dev->reexam_pending = 0;
-				}
-			} else {
-				PRINT_ERROR("Unable to open FD on active -> "
-					"%ld (dev %s): %d", dev_active,
-					dev->virt_name, res);
-				virt_dev->dev_active = 0;
-				goto unlock;
-			}
-		}
+	}
+
+	if (dev_active == virt_dev->dev_active)
+		goto unlock;
+
+	if (dev_active == 0) {
+		vdisk_disable_dev(virt_dev);
 	} else {
-		res = 0;
+		res = vdisk_activate_dev(virt_dev, dev->dev_rd_only);
+		if (res) {
+			PRINT_ERROR("dev %s: Activating failed",
+				    dev->virt_name);
+			goto unlock;
+		}
 	}
 
 unlock:
@@ -9574,25 +9380,6 @@
 	return res;
 }
 
-static ssize_t vdev_zero_copy_show(struct kobject *kobj,
-					struct kobj_attribute *attr, char *buf)
-{
-	int pos = 0;
-	struct scst_device *dev;
-	struct scst_vdisk_dev *virt_dev;
-
-	TRACE_ENTRY();
-
-	dev = container_of(kobj, struct scst_device, dev_kobj);
-	virt_dev = dev->dh_priv;
-
-	pos = sprintf(buf, "%d\n%s", virt_dev->zero_copy,
-		      virt_dev->zero_copy ? SCST_SYSFS_KEY_MARK "\n" : "");
-
-	TRACE_EXIT_RES(pos);
-	return pos;
-}
-
 static ssize_t vdev_async_store(struct kobject *kobj,
 	struct kobj_attribute *attr, const char *buf, size_t count)
 {
@@ -9733,8 +9520,6 @@
 	__ATTR(inq_vend_specific, S_IWUSR|S_IRUGO,
 	       vdev_sysfs_inq_vend_specific_show,
 	       vdev_sysfs_inq_vend_specific_store);
-static struct kobj_attribute vdev_zero_copy_attr =
-	__ATTR(zero_copy, S_IRUGO, vdev_zero_copy_show, NULL);
 static struct kobj_attribute vdev_async_attr =
 	__ATTR(async, S_IWUSR|S_IRUGO, vdev_async_show, vdev_async_store);
 
@@ -9781,7 +9566,6 @@
 	&vdev_eui64_id_attr.attr,
 	&vdev_usn_attr.attr,
 	&vdev_inq_vend_specific_attr.attr,
-	&vdev_zero_copy_attr.attr,
 	&vdev_async_attr.attr,
 	NULL,
 };
@@ -9804,7 +9588,6 @@
 	"thin_provisioned",
 	"tst",
 	"write_through",
-	"zero_copy",
 	NULL
 };
 
@@ -10106,23 +9889,13 @@
 static int __init vdev_check_mode_pages_path(void)
 {
 	int res;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
-	struct nameidata nd;
-#else
 	struct path path;
-#endif
 
 	TRACE_ENTRY();
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
-	res = path_lookup(VDEV_MODE_PAGES_DIR, 0, &nd);
-	if (res == 0)
-		scst_path_put(&nd);
-#else
 	res = kern_path(VDEV_MODE_PAGES_DIR, 0, &path);
 	if (res == 0)
 		path_put(&path);
-#endif
 	if (res != 0) {
 		PRINT_WARNING("Unable to find %s (err %d), saved mode pages "
 			"disabled. You should create this directory manually "
diff --git a/scst/scst/src/scst_copy_mgr.c b/scst/scst/src/scst_copy_mgr.c
index 68e081b..874d0da 100644
--- a/scst/scst/src/scst_copy_mgr.c
+++ b/scst/scst/src/scst_copy_mgr.c
@@ -180,11 +180,7 @@
 
 struct scst_cm_retry {
 	struct scst_cmd *cm_retry_cmd;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	struct work_struct cm_retry_work;
-#else
 	struct delayed_work cm_retry_work;
-#endif
 	scst_cm_retry_fn_t cm_retry_fn;
 };
 
@@ -1950,12 +1946,12 @@
 			if (l->cm_list_id_state == SCST_CM_LIST_ID_STATE_PENDING_FREE) {
 				scst_cm_del_free_list_id(l);
 				break;
-			} else {
-				TRACE_DBG("List id %d already exists", list_id);
-				scst_set_cmd_error(cmd,
-					SCST_LOAD_SENSE(scst_sense_operation_in_progress));
-				goto out_unlock_free;
 			}
+
+			TRACE_DBG("List id %d already exists", list_id);
+			scst_set_cmd_error(cmd,
+				SCST_LOAD_SENSE(scst_sense_operation_in_progress));
+			goto out_unlock_free;
 		}
 	}
 
@@ -1975,16 +1971,10 @@
 	goto out;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-void sess_cm_list_id_cleanup_work_fn(void *p)
-{
-	struct scst_session *sess = p;
-#else
 void sess_cm_list_id_cleanup_work_fn(struct work_struct *work)
 {
 	struct scst_session *sess = container_of(work,
 			struct scst_session, sess_cm_list_id_cleanup_work.work);
-#endif
 	struct scst_cm_list_id *l, *t;
 	unsigned long cur_time = jiffies;
 	unsigned long flags;
@@ -2317,6 +2307,8 @@
 	return;
 }
 
+static void scst_cm_update_dev_fini(struct scst_device *dev);
+
 static void scst_cm_init_inq_finish(struct scst_cmd *cmd)
 {
 	int length, page_len, off, rc;
@@ -2351,7 +2343,7 @@
 		PRINT_CRIT_ERROR("Unable to perform initial INQUIRY for device "
 			"%s. Copy manager for this device will be disabled",
 			dev->virt_name);
-		goto out;
+		goto out_put_ref;
 	}
 
 	length = scst_get_buf_full(cmd, &buf, false);
@@ -2359,7 +2351,7 @@
 	if (unlikely(length <= 0)) {
 		if (length < 0)
 			PRINT_ERROR("scst_get_buf_full() failed: %d", length);
-		goto out;
+		goto out_put_ref;
 	}
 
 	TRACE_BUFF_FLAG(TRACE_DEBUG, "buf", buf, length);
@@ -2435,8 +2427,11 @@
 out_put:
 	scst_put_buf_full(cmd, buf);
 
-out:
+out_put_ref:
 	percpu_ref_put(&dev->refcnt);
+
+	scst_cm_update_dev_fini(dev);
+out:
 	TRACE_EXIT();
 	return;
 }
@@ -2444,15 +2439,17 @@
 static int scst_cm_send_init_inquiry(struct scst_device *dev,
 	unsigned int unpacked_lun, struct scst_cm_init_inq_priv *priv)
 {
-	int res = -EINVAL;
 	static const uint8_t inq_cdb[6] = { INQUIRY, 1, 0x83, 0x10, 0, 0 };
 	__be64 lun;
 	struct scst_cmd *cmd;
+	int res = 0;
 
 	TRACE_ENTRY();
 
-	if (WARN_ON_ONCE(!dev))
+	if (WARN_ON_ONCE(!dev)) {
+		res = -EINVAL;
 		goto out;
+	}
 
 	if (priv == NULL) {
 		priv = kzalloc(sizeof(*priv), GFP_KERNEL);
@@ -2483,18 +2480,18 @@
 
 	scst_cmd_init_done(cmd, SCST_CONTEXT_THREAD);
 
-	res = 0;
-
 out:
 	TRACE_EXIT_RES(res);
 	return res;
 
 out_free:
+	percpu_ref_put(&dev->refcnt);
+
 	kfree(priv);
 	goto out;
 }
 
-/* scst_mutex supposed to be held and activities suspended */
+/* scst_mutex supposed to be held */
 static bool scst_cm_is_lun_free(unsigned int lun)
 {
 	bool res = true;
@@ -2516,7 +2513,7 @@
 	return res;
 }
 
-/* scst_mutex supposed to be held and activities suspended */
+/* scst_mutex supposed to be held */
 static unsigned int scst_cm_get_lun(const struct scst_device *dev)
 {
 	unsigned int res = SCST_MAX_LUN;
@@ -2547,11 +2544,28 @@
 	return res;
 }
 
+static uint64_t scst_cm_get_free_lun(void)
+{
+	uint64_t lun;
+
+	while (1) {
+		lun = scst_cm_next_lun++;
+		if (lun == SCST_MAX_LUN) {
+			scst_cm_next_lun = 0;
+			continue;
+		}
+
+		if (scst_cm_is_lun_free(lun))
+			break;
+	}
+
+	return lun;
+}
+
 static int scst_cm_dev_register(struct scst_device *dev, uint64_t lun)
 {
 	int res;
 	struct scst_acg_dev *acg_dev;
-	bool add_lun;
 
 	TRACE_ENTRY();
 
@@ -2559,28 +2573,16 @@
 
 	TRACE_DBG("dev %s, LUN %ld", dev->virt_name, (unsigned long)lun);
 
-	if (scst_cm_get_lun(dev) != SCST_MAX_LUN) {
-		TRACE_DBG("Copy Manager already registered device %s",
-			  dev->virt_name);
-		res = 0;
-		goto out;
-	}
-
 	if (lun == SCST_MAX_LUN) {
-		add_lun = true;
-		while (1) {
-			lun = scst_cm_next_lun++;
-			if (lun == SCST_MAX_LUN) {
-				scst_cm_next_lun = 0;
-				continue;
-			}
-			if (scst_cm_is_lun_free(lun))
-				break;
+		if (scst_cm_get_lun(dev) != SCST_MAX_LUN) {
+			TRACE_DBG("Copy Manager already registered device %s",
+				  dev->virt_name);
+			res = 0;
+			goto out;
 		}
-	} else
-		add_lun = false;
 
-	if (add_lun) {
+		lun = scst_cm_get_free_lun();
+
 		res = scst_acg_add_lun(scst_cm_tgt->default_acg,
 			scst_cm_tgt->tgt_luns_kobj, dev, lun, SCST_ADD_LUN_CM,
 			&acg_dev);
@@ -2592,6 +2594,8 @@
 	scst_block_dev(dev);
 	spin_unlock_bh(&dev->dev_lock);
 
+	atomic_set(&dev->cm_update_req_cnt, 1);
+
 	res = scst_cm_send_init_inquiry(dev, lun, NULL);
 	if (res != 0)
 		goto out_unblock;
@@ -2605,6 +2609,8 @@
 	scst_unblock_dev(dev);
 	spin_unlock_bh(&dev->dev_lock);
 
+	atomic_set(&dev->cm_update_req_cnt, 0);
+
 	scst_acg_del_lun(scst_cm_tgt->default_acg, lun, false);
 
 out_err:
@@ -2612,17 +2618,13 @@
 	goto out;
 }
 
-/* scst_mutex supposed to be held and activities suspended */
-static void scst_cm_dev_unregister(struct scst_device *dev, bool del_lun)
+static void scst_cm_dev_free_designators(struct scst_device *dev)
 {
 	struct scst_cm_desig *des, *t;
-	u32 lun;
 
 	TRACE_ENTRY();
 
-	lockdep_assert_held(&scst_mutex);
-
-	TRACE_DBG("dev %s, del_lun %d", dev->virt_name, del_lun);
+	mutex_lock(&scst_cm_mutex);
 
 	list_for_each_entry_safe(des, t, &scst_cm_desig_list, cm_desig_list_entry) {
 		if (des->desig_tgt_dev->dev == dev) {
@@ -2632,63 +2634,135 @@
 		}
 	}
 
-	if (!del_lun)
-		goto out;
+	mutex_unlock(&scst_cm_mutex);
+
+	TRACE_EXIT();
+
+	return;
+}
+
+/* scst_mutex supposed to be held */
+static void scst_cm_dev_unregister(struct scst_device *dev)
+{
+	u32 lun;
+
+	TRACE_ENTRY();
+
+	lockdep_assert_held(&scst_mutex);
+
+	TRACE_DBG("Unregister CM dev %s", dev->virt_name);
+
+	scst_cm_dev_free_designators(dev);
 
 	lun = scst_cm_get_lun(dev);
 	if (lun != SCST_MAX_LUN)
 		scst_acg_del_lun(scst_cm_tgt->default_acg, lun, false);
 
-out:
 	TRACE_EXIT();
+
 	return;
 }
 
-void scst_cm_update_dev(struct scst_device *dev)
+static int __scst_cm_update_dev(struct scst_device *dev)
 {
 	unsigned int lun;
-	int rc, res;
+	int rc = 0;
 
 	TRACE_ENTRY();
 
 	TRACE_MGMT_DBG("copy manager: updating device %s", dev->virt_name);
 
-	if (!scst_auto_cm_assignment ||
-	    !dev->handler->auto_cm_assignment_possible)
-		goto out;
-
-	res = scst_suspend_activity(SCST_SUSPEND_TIMEOUT_UNLIMITED);
-	WARN_ON_ONCE(res);
-
 	mutex_lock(&scst_mutex);
 
-	scst_cm_dev_unregister(dev, false);
+	lun = scst_cm_get_lun(dev);
+	if (lun == SCST_MAX_LUN) {
+		/*
+		 * Verify that scst_unregister_virtual_device() is in progress.
+		 */
+		WARN_ON_ONCE(!dev->remove_completion);
+		rc = -EINVAL;
+		goto out_unlock;
+	}
+
+	scst_cm_dev_free_designators(dev);
 
 	spin_lock_bh(&dev->dev_lock);
 	scst_block_dev(dev);
 	spin_unlock_bh(&dev->dev_lock);
 
-	lun = scst_cm_get_lun(dev);
-	if (WARN_ON_ONCE(lun == SCST_MAX_LUN))
-		goto out_unblock;
-
 	rc = scst_cm_send_init_inquiry(dev, lun, NULL);
 	if (rc != 0)
 		goto out_unblock;
 
-out_resume:
+out_unlock:
 	mutex_unlock(&scst_mutex);
-	scst_resume_activity();
 
-out:
 	TRACE_EXIT();
-	return;
+
+	return rc;
 
 out_unblock:
 	spin_lock_bh(&dev->dev_lock);
 	scst_unblock_dev(dev);
 	spin_unlock_bh(&dev->dev_lock);
-	goto out_resume;
+
+	goto out_unlock;
+}
+
+static void
+scst_cm_update_dev_start(struct scst_device *dev)
+{
+	int update_req_cnt, rc;
+
+	update_req_cnt = atomic_inc_return(&dev->cm_update_req_cnt);
+	if (update_req_cnt > 1)
+		return;
+
+	rc = __scst_cm_update_dev(dev);
+	if (rc)
+		atomic_set(&dev->cm_update_req_cnt, 0);
+}
+
+static void
+scst_cm_update_dev_fini(struct scst_device *dev)
+{
+	int update_req_cnt, rc;
+
+	update_req_cnt = atomic_dec_return(&dev->cm_update_req_cnt);
+
+	WARN_ON_ONCE(update_req_cnt < 0);
+
+	if (update_req_cnt == 0)
+		return;
+
+	/*
+	 * If we have received at least one update, we must re-update the
+	 * designators information. We don't care about the exact number of
+	 * updates we've received since the inquiry was submitted, as only the
+	 * last one is indicative. So set dev->cm_update_req_cnt to 1 to avoid
+	 * unnecessary __scst_cm_update_dev() calls.
+	 */
+	atomic_set(&dev->cm_update_req_cnt, 1);
+
+	rc = __scst_cm_update_dev(dev);
+	if (rc)
+		atomic_set(&dev->cm_update_req_cnt, 0);
+}
+
+void scst_cm_update_dev(struct scst_device *dev)
+{
+	TRACE_ENTRY();
+
+	if (!scst_auto_cm_assignment ||
+	    !dev->handler->auto_cm_assignment_possible)
+		goto out;
+
+	scst_cm_update_dev_start(dev);
+
+out:
+	TRACE_EXIT();
+
+	return;
 }
 
 int scst_cm_on_dev_register(struct scst_device *dev)
@@ -2715,13 +2789,13 @@
 
 	lockdep_assert_held(&scst_mutex);
 
-	scst_cm_dev_unregister(dev, true);
+	scst_cm_dev_unregister(dev);
 
 	TRACE_EXIT();
 	return;
 }
 
-/* scst_mutex supposed to be held and activities suspended */
+/* scst_mutex supposed to be held */
 int scst_cm_on_add_acg(struct scst_acg *acg)
 {
 	int res = 0;
@@ -2755,7 +2829,7 @@
 	/* Nothing to do */
 }
 
-/* scst_mutex supposed to be held and activities suspended */
+/* scst_mutex supposed to be held */
 int scst_cm_on_add_lun(struct scst_acg_dev *acg_dev, uint64_t lun,
 	unsigned int *flags)
 {
@@ -2783,7 +2857,7 @@
 	return res;
 }
 
-/* scst_mutex supposed to be held and activities suspended */
+/* scst_mutex supposed to be held */
 bool scst_cm_on_del_lun(struct scst_acg_dev *acg_dev, bool gen_report_luns_changed)
 {
 	bool res = gen_report_luns_changed;
@@ -2795,7 +2869,7 @@
 	if (acg_dev->acg != scst_cm_tgt->default_acg)
 		goto out;
 
-	scst_cm_dev_unregister(acg_dev->dev, false);
+	scst_cm_dev_free_designators(acg_dev->dev);
 
 	res = false;
 
@@ -2945,9 +3019,14 @@
 			continue;
 		if (seg[5] != des->desig[1])
 			continue;
-		if (seg[7] > des->desig[3])
+		if (seg[7] > 20) {
+			PRINT_WARNING("Initiator sent non-compliant identification descriptor (len %u > 20)",
+				      seg[7]);
 			continue;
-		if (memcmp(&des->desig[4], &seg[8], min_t(int, seg[7], des->desig[3])) == 0) {
+		}
+		if (seg[7] != des->desig[3])
+			continue;
+		if (memcmp(&des->desig[4], &seg[8], seg[7]) == 0) {
 			TRACE_DBG("Tgt_dev %p (lun %lld) found",
 				des->desig_tgt_dev,
 				(unsigned long long)des->desig_tgt_dev->lun);
@@ -3736,11 +3815,7 @@
 
 static struct scst_tgt_template scst_cm_tgtt = {
 	.name			= SCST_CM_NAME,
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	.sg_tablesize		= SG_MAX_SINGLE_ALLOC,
-#else
 	.sg_tablesize		= 0xffff,
-#endif
 	.enabled_attr_not_needed = 1,
 	.dif_supported		= 1,
 	.hw_dif_type1_supported = 1,
diff --git a/scst/scst/src/scst_debug.c b/scst/scst/src/scst_debug.c
index ab47cdf..d211eb8 100644
--- a/scst/scst/src/scst_debug.c
+++ b/scst/scst/src/scst_debug.c
@@ -22,9 +22,7 @@
 #ifndef INSIDE_KERNEL_TREE
 #include <linux/version.h>
 #endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
 #include <linux/export.h>
-#endif
 
 #ifdef INSIDE_KERNEL_TREE
 #include <scst/scst.h>
@@ -44,9 +42,6 @@
 static inline int get_current_tid(void)
 {
 	/* Code should be the same as in sys_gettid() */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 24)
-	return current->pid;
-#else
 	if (in_interrupt()) {
 		/*
 		 * Unfortunately, task_pid_vnr() isn't IRQ-safe, so otherwise
@@ -55,7 +50,6 @@
 		return current->pid;
 	}
 	return task_pid_vnr(current);
-#endif
 }
 
 /*
diff --git a/scst/scst/src/scst_dlm.c b/scst/scst/src/scst_dlm.c
index dff421c..0468003 100644
--- a/scst/scst/src/scst_dlm.c
+++ b/scst/scst/src/scst_dlm.c
@@ -510,6 +510,13 @@
 		goto out;
 	}
 	pos = 0;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
+	/*
+	 * Commit 4d03e3cc5982 ("fs: don't allow kernel reads and writes
+	 * without iter ops") made kernel_read() depend on .read_iter.
+	 */
+	WARN_ON_ONCE(!f->f_op->read_iter);
+#endif
 	ret = kernel_read(f, buf, buf_len, &pos);
 	if (ret >= 0)
 		buf[min(ret, buf_len - 1)] = '\0';
@@ -536,9 +543,21 @@
 static int scst_dlm_filldir(void *arg, const char *name_arg, int name_len,
 			    loff_t curr_pos, u64 inode, unsigned int dtype)
 #else
-static int scst_dlm_filldir(struct dir_context *arg, const char *name_arg,
-			    int name_len, loff_t curr_pos, u64 inode,
-			    unsigned int dtype)
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
+/*
+ * See also commit 25885a35a720 ("Change calling conventions for filldir_t")
+ * # v6.1.
+ */
+#define DLM_FILLDIR_RET bool
+#else
+#define DLM_FILLDIR_RET int
+#endif
+
+static DLM_FILLDIR_RET
+scst_dlm_filldir(struct dir_context *arg, const char *name_arg,
+		 int name_len, loff_t curr_pos, u64 inode,
+		 unsigned int dtype)
 #endif
 {
 	char *p, *q, name[64];
@@ -573,7 +592,11 @@
 	(*entries)[i + 1] = '\0';
 
 out:
+#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)
 	return *entries ? 0 : -ENOMEM;
+#else
+	return *entries ? true : false;
+#endif
 }
 
 /*
@@ -804,6 +827,7 @@
 	char lsp_name[32], lock_name[32];
 	int res;
 	bool modified_lvb = false;
+	uint32_t flags;
 
 	if (pr_dlm->ls || !pr_dlm->cl_dev_id || in_interrupt() ||
 	    time_is_after_jiffies(pr_dlm->latest_lscr_attempt + 1 * HZ))
@@ -828,9 +852,18 @@
 
 	snprintf(lsp_name, sizeof(lsp_name), "%s%s", SCST_DLM_LOCKSPACE_PFX,
 		 pr_dlm->cl_dev_id);
+
+	flags = DLM_LSFL_NEWEXCL;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)
+	/*
+	 * See also commit 12cda13cfd53 ("fs: dlm: remove DLM_LSFL_FS from uapi")
+	 * # v6.1.
+	 */
+	flags |= DLM_LSFL_FS;
+#endif
+
 	res = scst_dlm_new_lockspace(lsp_name, strlen(lsp_name), &ls,
-				     DLM_LSFL_NEWEXCL | DLM_LSFL_FS,
-				     PR_DLM_LVB_LEN);
+				     flags, PR_DLM_LVB_LEN);
 	if (res) {
 		PRINT_ERROR("Creating DLM lockspace %s failed: %d", lsp_name,
 			    res);
@@ -1095,16 +1128,10 @@
 		queue_work(pr_dlm->from_wq, &pr_dlm->pre_upd_work);
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void scst_pre_join_work(void *p)
-{
-	struct scst_pr_dlm_data *pr_dlm = p;
-#else
 static void scst_pre_join_work(struct work_struct *work)
 {
 	struct scst_pr_dlm_data *pr_dlm = container_of(work,
 				struct scst_pr_dlm_data, pre_join_work);
-#endif
 	dlm_lockspace_t *ls;
 
 	mutex_lock(&pr_dlm->ls_mutex);
@@ -1118,16 +1145,10 @@
 	mutex_unlock(&pr_dlm->ls_mutex);
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void scst_pre_upd_work(void *p)
-{
-	struct scst_pr_dlm_data *pr_dlm = p;
-#else
 static void scst_pre_upd_work(struct work_struct *work)
 {
 	struct scst_pr_dlm_data *pr_dlm = container_of(work,
 				struct scst_pr_dlm_data, pre_upd_work);
-#endif
 	dlm_lockspace_t *ls;
 
 	mutex_lock(&pr_dlm->ls_mutex);
@@ -1158,16 +1179,10 @@
  * Note: the node that has invoked scst_trigger_lvb_update() holds PR_LOCK
  * in EX mode and waits until this function has finished.
  */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void scst_copy_to_dlm_work(void *p)
-{
-	struct scst_pr_dlm_data *pr_dlm = p;
-#else
 static void scst_copy_to_dlm_work(struct work_struct *work)
 {
 	struct scst_pr_dlm_data *pr_dlm = container_of(work,
 				struct scst_pr_dlm_data, copy_to_dlm_work);
-#endif
 	struct scst_device *dev = pr_dlm->dev;
 	dlm_lockspace_t *ls;
 	int res;
@@ -1231,16 +1246,10 @@
  * scst_pr_init_tgt_dev() and scst_pr_clear_tgt_dev() in scst_pres.c protect
  * these manipulations by locking the PR data structures for writing.
  */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void scst_copy_from_dlm_work(void *p)
-{
-	struct scst_pr_dlm_data *pr_dlm = p;
-#else
 static void scst_copy_from_dlm_work(struct work_struct *work)
 {
 	struct scst_pr_dlm_data *pr_dlm = container_of(work,
 				struct scst_pr_dlm_data, copy_from_dlm_work);
-#endif
 	struct scst_device *dev = pr_dlm->dev;
 	dlm_lockspace_t *ls;
 	int res = -ENOENT;
@@ -1292,16 +1301,10 @@
 }
 
 /* Tell other nodes to refresh their local state from the lock value blocks. */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void scst_reread_lvb_work(void *p)
-{
-	struct scst_pr_dlm_data *pr_dlm = p;
-#else
 static void scst_reread_lvb_work(struct work_struct *work)
 {
 	struct scst_pr_dlm_data *pr_dlm = container_of(work,
 				struct scst_pr_dlm_data, reread_lvb_work);
-#endif
 	dlm_lockspace_t *ls;
 	struct scst_lksb pr_lksb;
 	int res;
@@ -1323,16 +1326,10 @@
 }
 
 /* Tell other nodes to update the DLM lock value blocks. */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void scst_lvb_upd_work(void *p)
-{
-	struct scst_pr_dlm_data *pr_dlm = p;
-#else
 static void scst_lvb_upd_work(struct work_struct *work)
 {
 	struct scst_pr_dlm_data *pr_dlm = container_of(work,
 				struct scst_pr_dlm_data, lvb_upd_work);
-#endif
 	dlm_lockspace_t *ls;
 	struct scst_lksb lksb;
 	int res;
@@ -1392,21 +1389,12 @@
 	mutex_init(&pr_dlm->ls_cr_mutex);
 	mutex_init(&pr_dlm->ls_mutex);
 	pr_dlm->data_lksb.lksb.sb_lvbptr = pr_dlm->lvb;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	INIT_WORK(&pr_dlm->pre_join_work, scst_pre_join_work, pr_dlm);
-	INIT_WORK(&pr_dlm->pre_upd_work, scst_pre_upd_work, pr_dlm);
-	INIT_WORK(&pr_dlm->copy_from_dlm_work, scst_copy_from_dlm_work, pr_dlm);
-	INIT_WORK(&pr_dlm->copy_to_dlm_work, scst_copy_to_dlm_work, pr_dlm);
-	INIT_WORK(&pr_dlm->lvb_upd_work, scst_lvb_upd_work, pr_dlm);
-	INIT_WORK(&pr_dlm->reread_lvb_work, scst_reread_lvb_work, pr_dlm);
-#else
 	INIT_WORK(&pr_dlm->pre_join_work, scst_pre_join_work);
 	INIT_WORK(&pr_dlm->pre_upd_work, scst_pre_upd_work);
 	INIT_WORK(&pr_dlm->copy_from_dlm_work, scst_copy_from_dlm_work);
 	INIT_WORK(&pr_dlm->copy_to_dlm_work, scst_copy_to_dlm_work);
 	INIT_WORK(&pr_dlm->lvb_upd_work, scst_lvb_upd_work);
 	INIT_WORK(&pr_dlm->reread_lvb_work, scst_reread_lvb_work);
-#endif
 	pr_dlm->latest_lscr_attempt = jiffies - 100 * HZ;
 
 	res = -ENOMEM;
diff --git a/scst/scst/src/scst_event.c b/scst/scst/src/scst_event.c
index 724a0e7..0194a57 100644
--- a/scst/scst/src/scst_event.c
+++ b/scst/scst/src/scst_event.c
@@ -29,6 +29,7 @@
 
 #include "scst_priv.h"
 
+static struct workqueue_struct *scst_event_wq;
 static struct class *scst_event_sysfs_class;
 
 static int scst_event_major;
@@ -95,16 +96,10 @@
 	return res;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void scst_event_timeout_fn(void *p)
-{
-	struct scst_event_entry *event_entry = p;
-#else
 static void scst_event_timeout_fn(struct work_struct *work)
 {
 	struct scst_event_entry *event_entry = container_of(work,
 		struct scst_event_entry, event_timeout_work.work);
-#endif
 
 	TRACE_ENTRY();
 
@@ -211,20 +206,15 @@
 					break;
 				}
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-				INIT_WORK(&new_event_entry->event_timeout_work,
-					  scst_event_timeout_fn,
-					  new_event_entry);
-#else
 				INIT_DELAYED_WORK(&new_event_entry->event_timeout_work,
 						  scst_event_timeout_fn);
-#endif
 				if (new_event_entry->event_notify_fn != NULL) {
 					new_event_entry->event.event_id = atomic_inc_return(&base_event_id);
 					if (new_event_entry->event_timeout == 0)
 						new_event_entry->event_timeout = SCST_DEFAULT_EVENT_TIMEOUT;
-					schedule_delayed_work(&new_event_entry->event_timeout_work,
-						new_event_entry->event_timeout);
+
+					queue_delayed_work(scst_event_wq, &new_event_entry->event_timeout_work,
+							   new_event_entry->event_timeout);
 				}
 
 				list_add_tail(&new_event_entry->events_list_entry,
@@ -264,16 +254,10 @@
 	return;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void scst_event_queue_work_fn(void *p)
-{
-	struct scst_event_entry *e = p;
-#else
 static void scst_event_queue_work_fn(struct work_struct *work)
 {
 	struct scst_event_entry *e = container_of(work,
 		struct scst_event_entry, scst_event_queue_work);
-#endif
 
 	TRACE_ENTRY();
 
@@ -289,18 +273,14 @@
 {
 	TRACE_ENTRY();
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	INIT_WORK(&e->scst_event_queue_work, scst_event_queue_work_fn, e);
-#else
 	INIT_WORK(&e->scst_event_queue_work, scst_event_queue_work_fn);
-#endif
 
 	TRACE_DBG("Scheduling event entry %p", e);
 
 	e->event.event_code = event_code;
 	strlcpy(e->event.issuer_name, issuer_name, sizeof(e->event.issuer_name));
 
-	schedule_work(&e->scst_event_queue_work);
+	queue_work(scst_event_wq, &e->scst_event_queue_work);
 
 	TRACE_EXIT();
 	return;
@@ -613,9 +593,11 @@
 static int scst_event_get_event_from_user(struct scst_event_user __user *arg,
 	struct scst_event_entry **out_event_entry)
 {
-	int res, rc, event_entry_len;
+	int res, rc;
+	int event_entry_len, event_len;
 	uint32_t payload_len;
 	struct scst_event_entry *event_entry;
+	struct scst_event *event;
 
 	TRACE_ENTRY();
 
@@ -646,9 +628,10 @@
 
 	TRACE_MEM("Allocated event entry %p", event_entry);
 
-	rc = copy_from_user((u8 *)event_entry +
-			    offsetof(typeof(*event_entry), event), arg,
-			    event_entry_len);
+	event = &event_entry->event;
+	event_len = sizeof(*event) + payload_len;
+
+	rc = copy_from_user((u8 *)event, arg, event_len);
 	if (rc != 0) {
 		PRINT_ERROR("Failed to copy %d user's bytes", rc);
 		res = -EFAULT;
@@ -656,16 +639,17 @@
 	}
 
 	/* payload_len has been recopied, so recheck it. */
-	if (event_entry->event.payload_len != event_entry_len) {
-		PRINT_ERROR("Payload len changed while being read");
+	if (event->payload_len != payload_len) {
+		PRINT_ERROR("Payload len %d changed while being read: %d",
+				event->payload_len, payload_len);
 		res = -EINVAL;
 		goto out_free;
 	}
 
-	event_entry->event.issuer_name[sizeof(event_entry->event.issuer_name)-1] = '\0';
+	event->issuer_name[sizeof(event->issuer_name) - 1] = '\0';
 
 	TRACE_DBG("user event: event_code %d, issuer_name %s",
-		event_entry->event.event_code, event_entry->event.issuer_name);
+		event->event_code, event->issuer_name);
 
 	*out_event_entry = event_entry;
 
@@ -1123,19 +1107,22 @@
 int scst_event_init(void)
 {
 	int res = 0;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21)
-	struct class_device *class_member;
-#else
 	struct device *dev;
-#endif
 
 	TRACE_ENTRY();
 
+	scst_event_wq = alloc_workqueue("scst_event_wq", WQ_MEM_RECLAIM, 0);
+	if (unlikely(!scst_event_wq)) {
+		PRINT_ERROR("Failed to allocate scst_event_wq");
+		res = -ENOMEM;
+		goto out;
+	}
+
 	scst_event_sysfs_class = class_create(THIS_MODULE, SCST_EVENT_NAME);
 	if (IS_ERR(scst_event_sysfs_class)) {
-		PRINT_ERROR("%s", "Unable create sysfs class for SCST event");
+		PRINT_ERROR("Unable create sysfs class for SCST event");
 		res = PTR_ERR(scst_event_sysfs_class);
-		goto out;
+		goto out_wq;
 	}
 
 	scst_event_major = register_chrdev(0, SCST_EVENT_NAME, &scst_event_fops);
@@ -1145,15 +1132,6 @@
 		goto out_class;
 	}
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 21)
-	class_member = class_device_create(scst_event_sysfs_class, NULL,
-					   MKDEV(scst_event_major, 0), NULL,
-					   SCST_EVENT_NAME);
-	if (IS_ERR(class_member)) {
-		res = PTR_ERR(class_member);
-		goto out_chrdev;
-	}
-#else
 	dev = device_create(scst_event_sysfs_class, NULL,
 			    MKDEV(scst_event_major, 0),
 				NULL,
@@ -1162,7 +1140,6 @@
 		res = PTR_ERR(dev);
 		goto out_chrdev;
 	}
-#endif
 
 #ifdef CONFIG_EVENTS_WAIT_TEST
 	sysfs_create_file(kernel_kobj, &event_wait_test_attr.attr);
@@ -1172,12 +1149,15 @@
 	TRACE_EXIT_RES(res);
 	return res;
 
-
 out_chrdev:
 	unregister_chrdev(scst_event_major, SCST_EVENT_NAME);
 
 out_class:
 	class_destroy(scst_event_sysfs_class);
+
+out_wq:
+	destroy_workqueue(scst_event_wq);
+
 	goto out;
 }
 
@@ -1194,8 +1174,8 @@
 	device_destroy(scst_event_sysfs_class, MKDEV(scst_event_major, 0));
 	class_destroy(scst_event_sysfs_class);
 
-	/* Wait for all pending being queued events to process */
-	flush_scheduled_work();
+	/* All pending works will be drained by destroy_workqueue() */
+	destroy_workqueue(scst_event_wq);
 
 	TRACE_EXIT();
 	return;
diff --git a/scst/scst/src/scst_lib.c b/scst/scst/src/scst_lib.c
index bf7d266..34823ff 100644
--- a/scst/scst/src/scst_lib.c
+++ b/scst/scst/src/scst_lib.c
@@ -16,6 +16,7 @@
  *  GNU General Public License for more details.
  */
 
+#include <linux/version.h>
 #include <linux/aio.h>		/* struct kiocb for kernel v4.0 */
 #include <linux/init.h>
 #include <linux/kernel.h>
@@ -31,25 +32,18 @@
 #include <linux/ctype.h>
 #include <linux/delay.h>
 #include <linux/vmalloc.h>
-#include <asm/kmap_types.h>
 #include <asm/unaligned.h>
 #include <asm/checksum.h>
 #ifndef INSIDE_KERNEL_TREE
 #include <linux/version.h>
 #endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
 #include <linux/crc-t10dif.h>
-#endif
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
 #include <linux/sched/task_stack.h>
 #endif
 #include <linux/namei.h>
 #include <linux/mount.h>
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
-#include <linux/writeback.h>
-#endif
-
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
 #include <linux/t10-pi.h>
 #endif
@@ -76,93 +70,33 @@
 static DEFINE_SPINLOCK(scst_global_stpg_list_lock);
 static LIST_HEAD(scst_global_stpg_list);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void scst_put_acg_work(void *p);
-#else
 static void scst_put_acg_work(struct work_struct *work);
-#endif
 static void scst_free_acn(struct scst_acn *acn, bool reassign);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
 struct scsi_io_context {
 	void *data;
 	void (*done)(void *data, char *sense, int result, int resid);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+	/*
+	 * See commit 772c8f6f3bbd ("Merge tag 'for-4.11/linus-merge-signed'
+	 * of git://git.kernel.dk/linux-block")
+	 *
+	 * Both scsi_init_rq and scsi_init_request (later renamed to
+	 * scsi_mq_init_request in e7008ff5c61a) initialize the scsi_request
+	 * sense buffer, so we don't need to (nor should) provide our own.
+	 */
 	char sense[SCST_SENSE_BUFFERSIZE];
+#endif
 };
 static struct kmem_cache *scsi_io_context_cache;
-#endif
 static struct workqueue_struct *scst_release_acg_wq;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22) \
-    && (!defined(RHEL_RELEASE_CODE) || RHEL_RELEASE_CODE -0 < 5 * 256 + 3) \
-    && !defined(CONFIG_PPC)
-static int strncasecmp(const char *s1, const char *s2, size_t n)
-{
-	int c1, c2;
-
-	do {
-		c1 = tolower(*s1++);
-		c2 = tolower(*s2++);
-	} while ((--n > 0) && c1 == c2 && c1 != 0);
-	return c1 - c2;
-}
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
-char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)
-{
-	unsigned int len;
-	char *p;
-	va_list aq;
-
-	va_copy(aq, ap);
-	len = vsnprintf(NULL, 0, fmt, aq);
-	va_end(aq);
-
-	p = kmalloc(len + 1, gfp);
-	if (!p)
-		return NULL;
-
-	vsnprintf(p, len + 1, fmt, ap);
-
-	return p;
-}
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) &&	\
-	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 6 ||	\
-	 RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 < 1)
-/*
- * See also "lib: introduce common method to convert hex digits" (commit
- * 903788892ea0fc7fcaf7e8e5fac9a77379fc215b).
- */
-int hex_to_bin(char ch)
-{
-	if (ch >= '0' && ch <= '9')
-		return ch - '0';
-	ch = tolower(ch);
-	if (ch >= 'a' && ch <= 'f')
-		return ch - 'a' + 10;
-	return -1;
-}
-EXPORT_SYMBOL(hex_to_bin);
-#endif
-
 static int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-	    int nents_to_copy, size_t copy_len,
-	    enum km_type d_km_type, enum km_type s_km_type);
-#else
 	    int nents_to_copy, size_t copy_len);
-#endif
 
 static void scst_free_descriptors(struct scst_cmd *cmd);
 static bool sg_cmp(struct scatterlist *dst_sg, struct scatterlist *src_sg,
-	    int nents_to_cmp, size_t cmp_len, int *miscompare_offs
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-	    , enum km_type d_km_type, enum km_type s_km_type
-#endif
-	    );
+	    int nents_to_cmp, size_t cmp_len, int *miscompare_offs);
 
 const struct scst_opcode_descriptor scst_op_descr_inquiry = {
 	.od_opcode = INQUIRY,
@@ -492,6 +426,8 @@
 	const struct scst_sdbops *sdbops);
 static int get_cdb_info_var_len(struct scst_cmd *cmd,
 	const struct scst_sdbops *sdbops);
+static int get_cdb_info_dyn_runtime_attr(struct scst_cmd *cmd,
+	const struct scst_sdbops *sdbops);
 
 /*
  * +=====================================-============-======-
@@ -537,7 +473,7 @@
 	uint8_t info_lba_off;	/* LBA offset in cdb */
 	uint8_t info_lba_len;	/* LBA length in cdb */
 	uint8_t info_len_off;	/* length offset in cdb */
-	uint8_t info_len_len;	/* length length in cdb */
+	uint8_t info_len_len;	/* length of length in cdb */
 	uint8_t info_data_direction;
 				/*
 				 * init --> target: SCST_DATA_WRITE
@@ -545,6 +481,8 @@
 				 * target <--> init: SCST_DATA_READ|
 				 *		     SCST_DATA_WRITE
 				 */
+	/* If not zero, logarithm base 2 of the maximum data buffer length. */
+	uint8_t log2_max_buf_len;
 	uint32_t info_op_flags;	/* various flags of this opcode */
 	const char *info_op_name;/* op code SCSI full name */
 	int (*get_cdb_info)(struct scst_cmd *cmd, const struct scst_sdbops *sdbops);
@@ -871,6 +809,7 @@
 	 .info_data_direction = SCST_DATA_READ,
 	 .info_op_flags = SCST_IMPLICIT_HQ|SCST_REG_RESERVE_ALLOWED|
 		SCST_WRITE_EXCL_ALLOWED|SCST_EXCL_ACCESS_ALLOWED,
+	 .log2_max_buf_len = 3,
 	 .get_cdb_info = get_cdb_info_read_capacity},
 	{.ops = 0x25, .devkey = "      O         ",
 	 .info_op_name = "GET WINDOW",
@@ -1525,6 +1464,12 @@
 	 .info_op_flags = FLAG_NONE,
 	 .info_len_off = 6, .info_len_len = 4,
 	 .get_cdb_info = get_cdb_info_len_4},
+	{.ops = 0xAB, .devkey = " O              ",
+	 .info_op_name = "SERVICE ACTION IN(12)",
+	 .info_data_direction = SCST_DATA_READ,
+	 .info_op_flags = FLAG_NONE,
+	 .info_len_off = 6, .info_len_len = 4,
+	 .get_cdb_info = get_cdb_info_len_4},
 	{.ops = 0xAC, .devkey = "       O        ",
 	 .info_op_name = "ERASE(12)",
 	 .info_data_direction = SCST_DATA_NONE,
@@ -1654,7 +1599,7 @@
 	 .info_len_off = 6, .info_len_len = 4,
 	 .get_cdb_info = get_cdb_info_len_4},
 	{.ops = 0xBF, .devkey = "     O          ",
-	 .info_op_name = "SEND DVD STRUCTUE",
+	 .info_op_name = "SEND DVD STRUCTURE",
 	 .info_data_direction = SCST_DATA_WRITE,
 	 .info_op_flags = FLAG_NONE,
 	 .info_len_off = 8, .info_len_len = 4,
@@ -1665,6 +1610,18 @@
 	 .info_op_flags = FLAG_NONE,
 	 .info_len_off = 6, .info_len_len = 4,
 	 .get_cdb_info = get_cdb_info_len_4},
+	{.ops = 0xD1, .devkey = " O              ",
+	 .info_op_name = "READ DYN RUNTIME ATTR",
+	 .info_data_direction = SCST_DATA_READ,
+	 .info_op_flags = FLAG_NONE,
+	 .info_len_off = 6, .info_len_len = 4,
+	 .get_cdb_info = get_cdb_info_dyn_runtime_attr},
+	{.ops = 0xD2, .devkey = " O              ",
+	 .info_op_name = "WRITE DYN RUNTIME ATTR",
+	 .info_data_direction = SCST_DATA_WRITE,
+	 .info_op_flags = FLAG_NONE,
+	 .info_len_off = 6, .info_len_len = 4,
+	 .get_cdb_info = get_cdb_info_dyn_runtime_attr},
 	{.ops = 0xE7, .devkey = "        V       ",
 	 .info_op_name = "INIT ELEMENT STATUS WRANGE",
 	 .info_data_direction = SCST_DATA_NONE,
@@ -1829,15 +1786,15 @@
 static int scst_set_lun_not_supported_request_sense(struct scst_cmd *cmd,
 	int key, int asc, int ascq)
 {
-	int res;
 	int sense_len, len;
 	struct scatterlist *sg;
+	int res = 0;
 
 	TRACE_ENTRY();
 
 	if (cmd->status != 0) {
-		TRACE_MGMT_DBG("cmd %p already has status %x set", cmd,
-			cmd->status);
+		TRACE_MGMT_DBG("cmd %p already has status %x set",
+			       cmd, cmd->status);
 		res = -EEXIST;
 		goto out;
 	}
@@ -1849,6 +1806,12 @@
 	}
 
 	if (cmd->sg == NULL) {
+		if (cmd->bufflen == 0) {
+			int bufflen = cmd->cdb[4];
+
+			cmd->bufflen = bufflen ?: 18;
+		}
+
 		/*
 		 * If target driver preparing data buffer using tgt_alloc_data_buf()
 		 * callback, it is responsible to copy the sense to its buffer
@@ -1861,19 +1824,16 @@
 			goto go;
 		}
 
-		if (cmd->bufflen == 0)
-			cmd->bufflen = cmd->cdb[4];
-
 		cmd->sg = scst_alloc_sg(cmd->bufflen, GFP_ATOMIC, &cmd->sg_cnt);
 		if (cmd->sg == NULL) {
-			PRINT_ERROR("Unable to alloc sg for REQUEST SENSE"
-				"(sense %x/%x/%x)", key, asc, ascq);
+			PRINT_ERROR("Unable to alloc sg for REQUEST SENSE (sense %x/%x/%x)",
+				    key, asc, ascq);
 			res = 1;
 			goto out;
 		}
 
-		TRACE_MEM("sg %p alloced for sense for cmd %p (cnt %d, "
-			"len %d)", cmd->sg, cmd, cmd->sg_cnt, cmd->bufflen);
+		TRACE_MEM("sg %p (cnt %d, len %d) alloced for sense: cmd %p",
+			  cmd->sg, cmd->sg_cnt, cmd->bufflen, cmd);
 	}
 
 go:
@@ -1890,34 +1850,37 @@
 	cmd->data_direction = SCST_DATA_READ;
 	scst_set_resp_data_len(cmd, sense_len);
 
-	res = 0;
 	cmd->completed = 1;
 	cmd->resid_possible = 1;
 
 out:
 	TRACE_EXIT_RES(res);
+
 	return res;
 }
 
 static int scst_set_lun_not_supported_inquiry(struct scst_cmd *cmd)
 {
-	int res;
 	uint8_t *buf;
 	struct scatterlist *sg;
 	int len;
+	int res = 0;
 
 	TRACE_ENTRY();
 
 	if (cmd->status != 0) {
-		TRACE_MGMT_DBG("cmd %p already has status %x set", cmd,
-			cmd->status);
+		TRACE_MGMT_DBG("cmd %p already has status %x set",
+			       cmd, cmd->status);
 		res = -EEXIST;
 		goto out;
 	}
 
 	if (cmd->sg == NULL) {
-		if (cmd->bufflen == 0)
-			cmd->bufflen = min_t(int, 36, get_unaligned_be16(&cmd->cdb[3]));
+		if (cmd->bufflen == 0) {
+			int bufflen = get_unaligned_be16(&cmd->cdb[3]);
+
+			cmd->bufflen = bufflen ? min_t(int, 36, bufflen) : 36;
+		}
 
 		/*
 		 * If target driver preparing data buffer using tgt_alloc_data_buf()
@@ -1927,22 +1890,20 @@
 		if (cmd->tgt_i_data_buf_alloced && (cmd->tgt_i_sg != NULL)) {
 			cmd->sg = cmd->tgt_i_sg;
 			cmd->sg_cnt = cmd->tgt_i_sg_cnt;
-			TRACE_MEM("Tgt used for INQUIRY for not supported "
-				"LUN for cmd %p", cmd);
+			TRACE_MEM("Tgt used for INQUIRY (not supported LUN): cmd %p",
+				  cmd);
 			goto go;
 		}
 
 		cmd->sg = scst_alloc_sg(cmd->bufflen, GFP_ATOMIC, &cmd->sg_cnt);
 		if (cmd->sg == NULL) {
-			PRINT_ERROR("%s", "Unable to alloc sg for INQUIRY "
-				"for not supported LUN");
+			PRINT_ERROR("Unable to alloc sg for INQUIRY (not supported LUN)");
 			res = 1;
 			goto out;
 		}
 
-		TRACE_MEM("sg %p alloced for INQUIRY for not supported LUN for "
-			"cmd %p (cnt %d, len %d)", cmd->sg, cmd, cmd->sg_cnt,
-			cmd->bufflen);
+		TRACE_MEM("sg %p (cnt %d, len %d) allocated for INQUIRY (not supported LUN): cmd %p",
+			  cmd->sg, cmd->sg_cnt, cmd->bufflen, cmd);
 	}
 
 go:
@@ -1964,12 +1925,12 @@
 	cmd->data_direction = SCST_DATA_READ;
 	scst_set_resp_data_len(cmd, len);
 
-	res = 0;
 	cmd->completed = 1;
 	cmd->resid_possible = 1;
 
 out:
 	TRACE_EXIT_RES(res);
+
 	return res;
 }
 
@@ -3254,11 +3215,11 @@
 {
 	if (cmd->op_name)
 		return cmd->op_name;
-	else {
-		scnprintf(cmd->not_parsed_op_name,
-			sizeof(cmd->not_parsed_op_name), "0x%x", cmd->cdb[0]);
-		return cmd->not_parsed_op_name;
-	}
+
+	scnprintf(cmd->not_parsed_op_name,
+		sizeof(cmd->not_parsed_op_name), "0x%x", cmd->cdb[0]);
+
+	return cmd->not_parsed_op_name;
 }
 EXPORT_SYMBOL(scst_get_opcode_name);
 #endif
@@ -3985,18 +3946,10 @@
 	return res;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void scst_hw_pending_work_fn(void *p)
-#else
 static void scst_hw_pending_work_fn(struct work_struct *work)
-#endif
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	struct scst_session *sess = (struct scst_session *)p;
-#else
 	struct scst_session *sess = container_of(work, struct scst_session,
 						 hw_pending_work.work);
-#endif
 	struct scst_tgt_template *tgtt = sess->tgt->tgtt;
 	struct scst_cmd *cmd;
 	unsigned long cur_time = jiffies;
@@ -4200,11 +4153,7 @@
 	return;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void scst_ext_blocking_done_fn(void *p);
-#else
 static void scst_ext_blocking_done_fn(struct work_struct *work);
-#endif
 
 static int scst_dif_none(struct scst_cmd *cmd);
 #ifdef CONFIG_SCST_DIF_INJECT_CORRUPTED_TAGS
@@ -4276,6 +4225,7 @@
 #ifdef CONFIG_SCST_PER_DEVICE_CMD_COUNT_LIMIT
 	atomic_set(&dev->dev_cmd_count, 0);
 #endif
+	atomic_set(&dev->cm_update_req_cnt, 0);
 	scst_init_mem_lim(&dev->dev_mem_lim);
 	spin_lock_init(&dev->dev_lock);
 	lockdep_register_key(&dev->dev_lock_key);
@@ -4285,11 +4235,7 @@
 	INIT_LIST_HEAD(&dev->dev_tgt_dev_list);
 	INIT_LIST_HEAD(&dev->dev_acg_dev_list);
 	INIT_LIST_HEAD(&dev->ext_blockers_list);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	INIT_WORK(&dev->ext_blockers_work, scst_ext_blocking_done_fn, dev);
-#else
 	INIT_WORK(&dev->ext_blockers_work, scst_ext_blocking_done_fn);
-#endif
 	dev->dev_double_ua_possible = 1;
 	dev->queue_alg = SCST_QUEUE_ALG_1_UNRESTRICTED_REORDER;
 	dev->dev_numa_node_id = nodeid;
@@ -4476,7 +4422,7 @@
 	return res;
 }
 
-/* The activity supposed to be suspended and scst_mutex held */
+/* The caller must hold scst_mutex. */
 int scst_acg_add_lun(struct scst_acg *acg, struct kobject *parent,
 	struct scst_device *dev, uint64_t lun, unsigned int flags,
 	struct scst_acg_dev **out_acg_dev)
@@ -4489,7 +4435,7 @@
 
 	TRACE_ENTRY();
 
-	INIT_LIST_HEAD(&tmp_tgt_dev_list);
+	lockdep_assert_held(&scst_mutex);
 
 	res = scst_check_dif_compatibility(acg, dev);
 	if (res != 0)
@@ -4515,12 +4461,6 @@
 	list_add_tail(&acg_dev->acg_dev_list_entry, &acg->acg_dev_list);
 	list_add_tail(&acg_dev->dev_acg_dev_list_entry, &dev->dev_acg_dev_list);
 
-	if (!(flags & SCST_ADD_LUN_CM)) {
-		res = scst_cm_on_add_lun(acg_dev, lun, &flags);
-		if (res != 0)
-			goto out_free;
-	}
-
 	list_for_each_entry(sess, &acg->acg_sess_list, acg_sess_list_entry) {
 		res = scst_alloc_add_tgt_dev(sess, acg_dev, &tgt_dev);
 		if (res == -EPERM)
@@ -4532,6 +4472,12 @@
 			      &tmp_tgt_dev_list);
 	}
 
+	if (!(flags & SCST_ADD_LUN_CM)) {
+		res = scst_cm_on_add_lun(acg_dev, lun, &flags);
+		if (res != 0)
+			goto out_free;
+	}
+
 	res = scst_acg_dev_sysfs_create(acg_dev, parent);
 	if (res != 0)
 		goto out_on_del;
@@ -4887,16 +4833,10 @@
 	struct scst_acg		*acg;
 };
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void scst_put_acg_work(void *p)
-{
-	struct scst_acg_put_work *put_work = p;
-#else
 static void scst_put_acg_work(struct work_struct *work)
 {
 	struct scst_acg_put_work *put_work =
 		container_of(work, typeof(*put_work), work);
-#endif
 	struct scst_acg *acg = put_work->acg;
 
 	kfree(put_work);
@@ -4914,11 +4854,7 @@
 		return;
 	}
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	INIT_WORK(&put_work->work, scst_put_acg_work, put_work);
-#else
 	INIT_WORK(&put_work->work, scst_put_acg_work);
-#endif
 	put_work->acg = acg;
 
 	/*
@@ -5338,7 +5274,7 @@
 {
 	int res = 0;
 	struct scst_tgt_template *tgtt = sess->tgt->tgtt;
-	int ini_sg, ini_unchecked_isa_dma, ini_use_clustering;
+	int ini_sg, ini_unchecked_isa_dma = 0, ini_use_clustering;
 	struct scst_tgt_dev *tgt_dev;
 	struct scst_device *dev = acg_dev->dev;
 	struct list_head *head;
@@ -5395,7 +5331,9 @@
 		struct Scsi_Host *shost = dev->scsi_dev->host;
 
 		ini_sg = shost->sg_tablesize;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0)
 		ini_unchecked_isa_dma = shost->unchecked_isa_dma;
+#endif
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 21, 0)
 		ini_use_clustering = shost->use_clustering == ENABLE_CLUSTERING;
 #else
@@ -5821,7 +5759,7 @@
 	}
 
 	scst_sess_get(res->sess);
-	res->cpu_cmd_counter = scst_get();
+	scst_get_icmd(res);
 
 	TRACE(TRACE_SCSI, "New internal cmd %p (op %s)", res,
 		scst_get_opcode_name(res));
@@ -6000,22 +5938,68 @@
 }
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 9, 0) && !defined(RHEL_MAJOR)
-ssize_t kernel_write(struct file *file, const void *buf, size_t count,
-		     loff_t *pos)
+/**
+ * scst_file_size - returns the size of a regular file
+ * @path: Path of the file.
+ * @mode: If not NULL, the file mode will be stored in *@mode.
+ *
+ * Returns the file size or an error code.
+ */
+loff_t scst_file_size(const char *path, umode_t *mode)
 {
-	struct kvec iov = {
-		.iov_base = buf,
-		.iov_len = count
-	};
+	struct file *filp;
+	struct inode *inode;
+	loff_t res;
 
-	return scst_writev(file, &iov, 1, pos);
+	filp = filp_open(path, O_LARGEFILE | O_RDONLY, 0600);
+	if (IS_ERR(filp))
+		return PTR_ERR(filp);
+	inode = file_inode(filp);
+	if (mode)
+		*mode = inode->i_mode;
+	res = S_ISREG(inode->i_mode) ? i_size_read(file_inode(filp)) : -ENOTTY;
+	filp_close(filp, NULL);
+	return res;
 }
-EXPORT_SYMBOL(kernel_write);
-#endif
+EXPORT_SYMBOL(scst_file_size);
 
 /**
- * scst_writev - read data from a file into a kernel buffer
+ * scst_bdev_size - returns the size of a block device
+ * @path: Path of the block device.
+ *
+ * Returns the block device size or an error code.
+ */
+loff_t scst_bdev_size(const char *path)
+{
+	struct block_device *bdev;
+	loff_t res;
+
+	bdev = blkdev_get_by_path(path, FMODE_READ, (void *)__func__);
+	if (IS_ERR(bdev))
+		return PTR_ERR(bdev);
+	res = i_size_read(bdev->bd_inode);
+	blkdev_put(bdev, FMODE_READ);
+	return res;
+}
+EXPORT_SYMBOL(scst_bdev_size);
+
+loff_t scst_file_or_bdev_size(const char *path)
+{
+	enum { INVALID_FILE_MODE = 0 };
+	umode_t mode = INVALID_FILE_MODE;
+	loff_t res;
+
+	res = scst_file_size(path, &mode);
+	if (S_ISREG(mode))
+		return res;
+	if (mode != INVALID_FILE_MODE && !S_ISBLK(mode))
+		return -EINVAL;
+	return scst_bdev_size(path);
+}
+EXPORT_SYMBOL(scst_file_or_bdev_size);
+
+/**
+ * scst_readv - read data from a file into a kernel buffer
  * @file: File to read from.
  * @vec:  Pointer to first element of struct kvec array.
  * @vlen: Number of elements of the kvec array.
@@ -6823,11 +6807,7 @@
 		goto out_finish;
 	}
 
-	c = sg_cmp(cmd->sg, cwr_cmd->sg, 0, 0, &miscompare_offs
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-		, KM_USER0, KM_USER1
-#endif
-	);
+	c = sg_cmp(cmd->sg, cwr_cmd->sg, 0, 0, &miscompare_offs);
 	if (!c) {
 		scst_set_cmd_error_and_inf(cwr_cmd,
 			SCST_LOAD_SENSE(scst_sense_miscompare_error), miscompare_offs);
@@ -6941,6 +6921,14 @@
 		goto out_done;
 	}
 
+	if (cmd->bufflen != scst_cmd_get_expected_transfer_len_data(cmd)) {
+		PRINT_ERROR("COMPARE AND WRITE: data buffer length mismatch (CDB %u <> ini %u)",
+			    cmd->bufflen,
+			    scst_cmd_get_expected_transfer_len_data(cmd));
+		scst_set_invalid_field_in_cdb(cmd, 13/*NLB*/, 0);
+		goto out_done;
+	}
+
 	/* ToDo: HWALIGN'ed kmem_cache */
 	cwrp = kzalloc(sizeof(*cwrp), GFP_KERNEL);
 	if (cwrp == NULL) {
@@ -6952,14 +6940,6 @@
 	cwrp->cwr_orig_cmd = cmd;
 	cwrp->cwr_finish_fn = scst_cwr_read_cmd_finished;
 
-	if (cmd->bufflen != scst_cmd_get_expected_transfer_len_data(cmd)) {
-		PRINT_ERROR("COMPARE AND WRITE: data buffer length mismatch (CDB %u <> ini %u)",
-			    cmd->bufflen,
-			    scst_cmd_get_expected_transfer_len_data(cmd));
-		scst_set_invalid_field_in_cdb(cmd, 13/*NLB*/, 0);
-		goto out_done;
-	}
-
 	/*
 	 * As required by SBC, DIF PI, if any, is not checked for the read part
 	 */
@@ -7071,14 +7051,12 @@
 				       sense, 15, 0, 0);
 		TRACE_DBG("RELEASE done: %x", rc);
 
-		if (scsi_status_is_good(rc)) {
+		if (scsi_status_is_good(rc))
 			break;
-		} else {
-			PRINT_ERROR("RELEASE failed: %d", rc);
-			PRINT_BUFFER("RELEASE sense", sense, sizeof(sense));
-			scst_check_internal_sense(dev, rc, sense,
-				sizeof(sense));
-		}
+
+		PRINT_ERROR("RELEASE failed: %d", rc);
+		PRINT_BUFFER("RELEASE sense", sense, sizeof(sense));
+		scst_check_internal_sense(dev, rc, sense, sizeof(sense));
 	}
 
 out:
@@ -7148,15 +7126,9 @@
 	INIT_LIST_HEAD(&sess->init_deferred_cmd_list);
 	INIT_LIST_HEAD(&sess->init_deferred_mcmd_list);
 	INIT_LIST_HEAD(&sess->sess_cm_list_id_list);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
 	INIT_DELAYED_WORK(&sess->sess_cm_list_id_cleanup_work,
 			  sess_cm_list_id_cleanup_work_fn);
 	INIT_DELAYED_WORK(&sess->hw_pending_work, scst_hw_pending_work_fn);
-#else
-	INIT_WORK(&sess->sess_cm_list_id_cleanup_work,
-		  sess_cm_list_id_cleanup_work_fn, sess);
-	INIT_WORK(&sess->hw_pending_work, scst_hw_pending_work_fn, sess);
-#endif
 	spin_lock_init(&sess->lat_stats_lock);
 
 	sess->initiator_name = kstrdup(initiator_name, gfp_mask);
@@ -7473,10 +7445,8 @@
 
 	scst_sess_put(cmd->sess);
 
-	if (likely(cmd->cpu_cmd_counter)) {
-		scst_put(cmd->cpu_cmd_counter);
-		cmd->cpu_cmd_counter = NULL;
-	}
+	if (likely(cmd->counted))
+		scst_put_cmd(cmd);
 
 	EXTRACHECKS_BUG_ON(cmd->pre_alloced && cmd->internal);
 
@@ -7716,10 +7686,8 @@
 
 	scst_sess_put(mcmd->sess);
 
-	if (mcmd->cpu_cmd_counter) {
-		scst_put(mcmd->cpu_cmd_counter);
-		mcmd->cpu_cmd_counter = NULL;
-	}
+	if (mcmd->counted)
+		scst_put_mcmd(mcmd);
 
 	mempool_free(mcmd, scst_mgmt_mempool);
 
@@ -7945,7 +7913,6 @@
 	return;
 }
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
 struct blk_kern_sg_work {
 	atomic_t bios_inflight;
 	struct sg_table sg_table;
@@ -7971,6 +7938,8 @@
 	return;
 }
 
+static inline void scst_free_bio(struct bio *bio);
+
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 3, 0)
 static void blk_bio_map_kern_endio(struct bio *bio, int err)
 {
@@ -7994,53 +7963,17 @@
 				unsigned long flags;
 
 				local_irq_save(flags);	/* to protect KMs */
-				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-					, KM_BIO_DST_IRQ, KM_BIO_SRC_IRQ
-#endif
-					);
+				sg_copy(bw->src_sgl, bw->sg_table.sgl, 0, 0);
 				local_irq_restore(flags);
 			}
 			blk_free_kern_sg_work(bw);
 		}
 	}
 
-	bio_put(bio);
+	scst_free_bio(bio);
 	return;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
-/*
- * See also patch "block: Add blk_make_request(), takes bio, returns a
- * request" (commit 79eb63e9e5875b84341a3a05f8e6ae9cdb4bb6f6).
- */
-static struct request *blk_make_request(struct request_queue *q,
-					struct bio *bio,
-					gfp_t gfp_mask)
-{
-	struct request *rq = blk_get_request(q, bio_data_dir(bio), gfp_mask);
-
-	if (unlikely(!rq))
-		return ERR_PTR(-ENOMEM);
-
-	rq->cmd_type = REQ_TYPE_BLOCK_PC;
-
-	for ( ; bio; bio = bio->bi_next) {
-		struct bio *bounce_bio = bio;
-		int ret;
-
-		blk_queue_bounce(q, &bounce_bio);
-		ret = blk_rq_append_bio(q, rq, bounce_bio);
-		if (unlikely(ret)) {
-			blk_put_request(rq);
-			return ERR_PTR(ret);
-		}
-	}
-
-	return rq;
-}
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) */
-
 /*
  * Copy an sg-list. This function is related to bio_copy_kern() but duplicates
  * an sg-list instead of creating a bio out of a single kernel address range.
@@ -8078,7 +8011,11 @@
 	for_each_sg(new_sgl, sg, new_sgl_nents, i) {
 		struct page *pg;
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0)
 		pg = alloc_page(q->bounce_gfp | gfp_mask);
+#else
+		pg = alloc_page(gfp_mask);
+#endif
 		if (pg == NULL)
 			goto err_free_table;
 
@@ -8094,11 +8031,7 @@
 		 * sgl might have the last element in sgl not marked as last in
 		 * SG chaining.
 		 */
-		sg_copy(new_sgl, sgl, 0, to_copy
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-			, KM_USER0, KM_USER1
-#endif
-			);
+		sg_copy(new_sgl, sgl, 0, to_copy);
 	}
 
 out:
@@ -8116,16 +8049,8 @@
 	goto out;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
-static void bio_kmalloc_destructor(struct bio *bio)
-{
-	kfree(bio->bi_io_vec);
-	kfree(bio);
-}
-#endif
-
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0)
-static blk_mq_req_flags_t gfp_mask_to_flags(gfp_t gfp_mask)
+static blk_mq_req_flags_t scst_gfp_mask_to_flags(gfp_t gfp_mask)
 {
 	switch (gfp_mask) {
 	case GFP_KERNEL:
@@ -8140,6 +8065,124 @@
 }
 #endif
 
+/**
+ * scst_alloc_passthrough_request - Allocate a SCSI pass-through request.
+ * @q: Request queue.
+ * @rw: READ or WRITE.
+ * @gfp_mask: GFP_KERNEL, GFP_ATOMIC or GFP_NOIO.
+ *
+ * Returns
+ * A valid request pointer, NULL or an error pointer. The value NULL is only
+ * returned for the legacy block layer if allocation fails. The legacy block
+ * layer is only supported by kernel versions before v5.0.
+ */
+static inline struct request *
+scst_alloc_passthrough_request(struct request_queue *q, int rw, gfp_t gfp_mask)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+	return blk_get_request(q, rw, gfp_mask);
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)
+	return blk_get_request(q, rw == READ ? REQ_OP_SCSI_IN : REQ_OP_SCSI_OUT,
+			       gfp_mask);
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(9, 1))
+	return blk_get_request(q, rw == READ ? REQ_OP_SCSI_IN : REQ_OP_SCSI_OUT,
+			       scst_gfp_mask_to_flags(gfp_mask));
+#else
+	return blk_mq_alloc_request(q, rw == READ ? REQ_OP_DRV_IN : REQ_OP_DRV_OUT,
+				    scst_gfp_mask_to_flags(gfp_mask));
+#endif
+}
+
+/**
+ * scst_init_passthrough_request - Init a SCSI pass-through request.
+ * @rq: Request pointer.
+ */
+static void
+scst_init_passthrough_request(struct request *rq)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(9, 1))
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) ||	\
+	defined(CONFIG_SUSE_KERNEL)
+	scsi_req_init(scsi_req(rq));
+#else
+	scsi_req_init(rq);
+#endif
+
+#endif
+}
+
+/**
+ * scst_free_passthrough_request - Free a SCSI pass-through request.
+ * @rq: Request pointer.
+ */
+static void
+scst_free_passthrough_request(struct request *rq)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(9, 1))
+	blk_put_request(rq);
+#else
+	blk_mq_free_request(rq);
+#endif
+}
+
+/**
+ * scst_alloc_bio - Allocate a bio.
+ * @nr_vecs: Number of bio_vecs to allocate.
+ * @gfp_mask: The GFP_* mask given to the slab allocator.
+ *
+ * Returns
+ * Pointer to new bio on success, NULL on failure.
+ */
+static inline struct bio *
+scst_alloc_bio(unsigned short nr_vecs, gfp_t gfp_mask)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 19, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(9, 1))
+	return bio_kmalloc(gfp_mask, nr_vecs);
+#else
+	/*
+	 * See also commit 066ff571011d ("block: turn bio_kmalloc into a
+	 * simple kmalloc wrapper").
+	 */
+	struct bio *bio;
+
+	bio = bio_kmalloc(nr_vecs, gfp_mask);
+	if (bio)
+		bio_init(bio, NULL, bio->bi_inline_vecs, nr_vecs, 0);
+
+	return bio;
+#endif
+}
+
+/**
+ * scst_free_bio - Free a bio that was allocated with scst_alloc_bio().
+ * @bio: bio pointer.
+ */
+static inline void
+scst_free_bio(struct bio *bio)
+{
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 19, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(9, 1))
+	bio_put(bio);
+#else
+	/*
+	 * See also commit 066ff571011d ("block: turn bio_kmalloc into a
+	 * simple kmalloc wrapper").
+	 */
+	bio_uninit(bio);
+	kfree(bio);
+#endif
+}
+
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 8, 0) ||			\
 (defined(CONFIG_SUSE_KERNEL) && LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
 static struct request *blk_make_request(struct request_queue *q,
@@ -8148,25 +8191,11 @@
 {
 	struct request *rq;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
-	rq = blk_get_request(q, bio_data_dir(bio), gfp_mask);
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)
-	rq = blk_get_request(q, bio_data_dir(bio) == READ ? REQ_OP_SCSI_IN :
-			     REQ_OP_SCSI_OUT, gfp_mask);
-#else
-	rq = blk_get_request(q, bio_data_dir(bio) == READ ? REQ_OP_SCSI_IN :
-			     REQ_OP_SCSI_OUT, gfp_mask_to_flags(gfp_mask));
-#endif
-
-	if (IS_ERR(rq))
+	rq = scst_alloc_passthrough_request(q, bio_data_dir(bio), gfp_mask);
+	if (IS_ERR_OR_NULL(rq))
 		return rq;
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) ||	\
-	defined(CONFIG_SUSE_KERNEL)
-	scsi_req_init(scsi_req(rq));
-#else
-	scsi_req_init(rq);
-#endif
+	scst_init_passthrough_request(rq);
 
 	for_each_bio(bio) {
 		int ret;
@@ -8177,10 +8206,6 @@
 
 		blk_queue_bounce(q, &bounce_bio);
 		ret = blk_rq_append_bio(rq, bounce_bio);
-		if (unlikely(ret)) {
-			blk_put_request(rq);
-			return ERR_PTR(ret);
-		}
 		/*
 		 * See also commit 0abc2a10389f ("block: fix
 		 * blk_rq_append_bio"). That commit has been backported to
@@ -8189,17 +8214,15 @@
 #elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 11) &&	\
 	!defined(CONFIG_SUSE_KERNEL)
 		ret = blk_rq_append_bio(rq, bio);
-		if (unlikely(ret)) {
-			blk_put_request(rq);
-			return ERR_PTR(ret);
-		}
-#else
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 13, 0)
 		ret = blk_rq_append_bio(rq, &bio);
+#else
+		ret = blk_rq_append_bio(rq, bio);
+#endif
 		if (unlikely(ret)) {
-			blk_put_request(rq);
+			scst_free_passthrough_request(rq);
 			return ERR_PTR(ret);
 		}
-#endif
 	}
 
 	return rq;
@@ -8231,7 +8254,7 @@
 	 */
 	max_nr_vecs = min_t(int,
 		(PAGE_SIZE - sizeof(struct bio)) / sizeof(struct bio_vec),
-		BIO_MAX_PAGES);
+		BIO_MAX_VECS);
 
 	TRACE_DBG("max_nr_vecs %d, nents %d, reading %d", max_nr_vecs,
 		nents, reading);
@@ -8272,23 +8295,14 @@
 			int rc;
 
 			if (need_new_bio) {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
-				bio = bio_alloc_bioset(gfp_mask, max_nr_vecs, NULL);
-				if (bio)
-					bio->bi_destructor =
-						bio_kmalloc_destructor;
-#else
-				bio = bio_kmalloc(gfp_mask, max_nr_vecs);
-#endif
+				bio = scst_alloc_bio(max_nr_vecs, gfp_mask);
 				if (bio == NULL) {
 					rq = ERR_PTR(-ENOMEM);
 					goto out_free_bios;
 				}
 
 				if (!reading)
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
-					bio->bi_rw |= 1 << BIO_RW;
-#elif (!defined(CONFIG_SUSE_KERNEL) &&			\
+#if (!defined(CONFIG_SUSE_KERNEL) && \
 	LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)) || \
 	LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)
 					bio->bi_rw |= REQ_WRITE;
@@ -8364,7 +8378,7 @@
 	while (hbio != NULL) {
 		bio = hbio;
 		hbio = hbio->bi_next;
-		bio_put(bio);
+		scst_free_bio(bio);
 	}
 	goto out;
 }
@@ -8386,24 +8400,12 @@
 	struct request *rq;
 
 	if (!sgl) {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
-		rq = blk_get_request(q, reading ? READ : WRITE, gfp);
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 18, 0)
-		rq = blk_get_request(q, reading ? REQ_OP_SCSI_IN :
-				     REQ_OP_SCSI_OUT, gfp);
-#else
-		rq = blk_get_request(q, reading ? REQ_OP_SCSI_IN :
-				     REQ_OP_SCSI_OUT, gfp_mask_to_flags(gfp));
-#endif
-		if (unlikely(!rq))
+		rq = scst_alloc_passthrough_request(q, reading ? READ : WRITE,
+						    gfp);
+		if (IS_ERR_OR_NULL(rq))
 			return ERR_PTR(-ENOMEM);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) ||	\
-	defined(CONFIG_SUSE_KERNEL)
-		scsi_req_init(scsi_req(rq));
-#else
-		scsi_req_init(rq);
-#endif
+		scst_init_passthrough_request(rq);
 		goto out;
 	}
 
@@ -8428,7 +8430,6 @@
 out:
 	return rq;
 }
-#endif
 
 /*
  * Can switch to the next dst_sg element, so, to copy to strictly only
@@ -8437,12 +8438,7 @@
  */
 static int sg_copy_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
 			size_t *pdst_offs, struct scatterlist *src_sg,
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-			size_t copy_len,
-			enum km_type d_km_type, enum km_type s_km_type)
-#else
 			size_t copy_len)
-#endif
 {
 	int res = 0;
 	struct scatterlist *dst_sg;
@@ -8462,19 +8458,10 @@
 		void *saddr, *daddr;
 		size_t n;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-		saddr = kmap_atomic(src_page +
-					 (src_offs >> PAGE_SHIFT), s_km_type) +
-				    (src_offs & ~PAGE_MASK);
-		daddr = kmap_atomic(dst_page +
-					(dst_offs >> PAGE_SHIFT), d_km_type) +
-				    (dst_offs & ~PAGE_MASK);
-#else
 		saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
 			(src_offs & ~PAGE_MASK);
 		daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
 			(dst_offs & ~PAGE_MASK);
-#endif
 
 		if (((src_offs & ~PAGE_MASK) == 0) &&
 		    ((dst_offs & ~PAGE_MASK) == 0) &&
@@ -8493,13 +8480,8 @@
 		dst_offs += n;
 		src_offs += n;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-		kunmap_atomic(saddr, s_km_type);
-		kunmap_atomic(daddr, d_km_type);
-#else
 		kunmap_atomic(saddr);
 		kunmap_atomic(daddr);
-#endif
 
 		res += n;
 		copy_len -= n;
@@ -8540,12 +8522,7 @@
  *    NULL. Returns number of bytes copied.
  */
 static int sg_copy(struct scatterlist *dst_sg, struct scatterlist *src_sg,
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-	    int nents_to_copy, size_t copy_len,
-	    enum km_type d_km_type, enum km_type s_km_type)
-#else
 	    int nents_to_copy, size_t copy_len)
-#endif
 {
 	int res = 0;
 	size_t dst_len, dst_offs;
@@ -8561,11 +8538,7 @@
 
 	do {
 		int copied = sg_copy_elem(&dst_sg, &dst_len, &dst_offs,
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-				src_sg, copy_len, d_km_type, s_km_type);
-#else
 				src_sg, copy_len);
-#endif
 		copy_len -= copied;
 		res += copied;
 		if ((copy_len == 0) || (dst_sg == NULL))
@@ -8582,12 +8555,24 @@
 	return res;
 }
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 13, 0) &&	\
 	!defined(CONFIG_SUSE_KERNEL)
 static void scsi_end_async(struct request *req, int error)
 #else
-static void scsi_end_async(struct request *req, blk_status_t error)
+
+/*
+ * See also commit de671d6116b5 ("block: change request end_io handler to pass
+ * back a return value") # v6.1.
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(9, 2))
+#define RQ_END_IO_RET void
+#else
+#define RQ_END_IO_RET enum rq_end_io_ret
+#endif
+
+static RQ_END_IO_RET scsi_end_async(struct request *req, blk_status_t error)
 #endif
 {
 	struct scsi_io_context *sioc = req->end_io_data;
@@ -8610,6 +8595,7 @@
 	if (sioc->done) {
 		int resid_len;
 		long result;
+		char *sense;
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
 		result = scsi_req(req)->result;
@@ -8624,30 +8610,31 @@
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
 		resid_len = scsi_req(req)->resid_len;
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
-		resid_len = req->resid_len;
+		sense = SREQ_SENSE(scsi_req(req));
 #else
-		/*
-		 * A quote from commit c3a4d78c580d: "rq->data_len served two
-		 * purposes - the length of data buffer on issue and the
-		 * residual count on completion."
-		 */
-		resid_len = req->data_len;
+		resid_len = req->resid_len;
+		sense = sioc->sense;
 #endif
 
-		sioc->done(sioc->data, sioc->sense, result, resid_len);
+		sioc->done(sioc->data, sense, result, resid_len);
 	}
 
 	kmem_cache_free(scsi_io_context_cache, sioc);
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(9, 2))
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 21, 0) &&	\
 	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 8)
 	/* See also commit 92bc5a24844a ("block: remove __blk_put_request()") */
 	__blk_put_request(req->q, req);
 #else
-	blk_put_request(req);
+	scst_free_passthrough_request(req);
 #endif
 	return;
+#else
+	return RQ_END_IO_FREE;
+#endif
 }
 
 /**
@@ -8662,7 +8649,9 @@
 	int res = 0;
 	struct request_queue *q = cmd->dev->scsi_dev->request_queue;
 	struct request *rq;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0)
+	struct scsi_cmnd *req;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
 	struct scsi_request *req;
 #else
 	struct request *req;
@@ -8722,27 +8711,46 @@
 
 	req = scsi_req(rq);
 	req->cmd_len = cmd_len;
-	if (req->cmd_len <= BLK_MAX_CDB) {
-		memset(req->cmd, 0, BLK_MAX_CDB); /* ATAPI hates garbage after CDB */
-		memcpy(req->cmd, cmd->cdb, cmd->cdb_len);
-	} else
-		req->cmd = cmd->cdb;
+	if (req->cmd_len > MAX_COMMAND_SIZE) {
+		PRINT_ERROR("SCSI command length %d exceeds the limit %d",
+				req->cmd_len, MAX_COMMAND_SIZE);
+		res = -EINVAL;
+#ifdef QUEUE_FLAG_BIDI
+		goto out_free_unmap;
+#else
+		goto out_free_sioc;
+#endif
+	}
 
-	req->sense = sioc->sense;
+	memset(SREQ_CP(req), 0, MAX_COMMAND_SIZE); /* ATAPI hates garbage after CDB */
+	memcpy(SREQ_CP(req), cmd->cdb, cmd->cdb_len);
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+	/*
+	 * See commit 772c8f6f3bbd ("Merge tag 'for-4.11/linus-merge-signed'
+	 * of git://git.kernel.dk/linux-block")
+	 *
+	 * Both scsi_init_rq and scsi_init_request (later renamed to
+	 * scsi_mq_init_request in e7008ff5c61a) initialize the scsi_request
+	 * sense buffer, so we don't need to (nor should) provide our own.
+	 */
+	SREQ_SENSE(req) = sioc->sense;
 	req->sense_len = sizeof(sioc->sense);
+#endif
+
 	rq->timeout = cmd->timeout;
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0)
 	req->retries = cmd->retries;
 #else
 	rq->retries = cmd->retries;
 #endif
+	rq->end_io      = scsi_end_async;
 	rq->end_io_data = sioc;
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 35)
-	rq->cmd_flags |= REQ_FAILFAST_MASK;
-#endif
 
-	blk_execute_rq_nowait(rq->q, NULL, rq,
-		(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE), scsi_end_async);
+	rq->cmd_flags |= REQ_FAILFAST_MASK;
+
+	blk_execute_rq_nowait(rq,
+		(cmd->queue_type == SCST_CMD_QUEUE_HEAD_OF_QUEUE));
 out:
 	return res;
 
@@ -8769,7 +8777,7 @@
 	}
 	rq->bio = NULL;
 
-	blk_put_request(rq);
+	scst_free_passthrough_request(rq);
 #endif
 
 out_free_sioc:
@@ -8778,8 +8786,6 @@
 }
 EXPORT_SYMBOL(scst_scsi_exec_async);
 
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) */
-
 /*
  * Can switch to the next dst_sg element, so, to cmp to strictly only
  * one dst_sg element, it must be either last in the chain, or
@@ -8788,9 +8794,6 @@
 static int sg_cmp_elem(struct scatterlist **pdst_sg, size_t *pdst_len,
 			size_t *pdst_offs, struct scatterlist *src_sg,
 			size_t cmp_len, int *miscompare_offs,
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-			enum km_type d_km_type, enum km_type s_km_type,
-#endif
 			bool *cmp_res)
 {
 	int res = 0;
@@ -8814,17 +8817,10 @@
 		size_t n;
 		int rc;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-		saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT), s_km_type) +
-			(src_offs & ~PAGE_MASK);
-		daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT), d_km_type) +
-			(dst_offs & ~PAGE_MASK);
-#else
 		saddr = kmap_atomic(src_page + (src_offs >> PAGE_SHIFT)) +
 			(src_offs & ~PAGE_MASK);
 		daddr = kmap_atomic(dst_page + (dst_offs >> PAGE_SHIFT)) +
 			(dst_offs & ~PAGE_MASK);
-#endif
 
 		if (((src_offs & ~PAGE_MASK) == 0) &&
 		    ((dst_offs & ~PAGE_MASK) == 0) &&
@@ -8896,13 +8892,8 @@
 		dst_offs += n;
 		src_offs += n;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-		kunmap_atomic(saddr, s_km_type);
-		kunmap_atomic(daddr, d_km_type);
-#else
 		kunmap_atomic(saddr);
 		kunmap_atomic(daddr);
-#endif
 
 		res += n;
 		cmp_len -= n;
@@ -8928,13 +8919,8 @@
 	return res;
 
 out_unmap:
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-	kunmap_atomic(saddr, s_km_type);
-	kunmap_atomic(daddr, d_km_type);
-#else
 	kunmap_atomic(saddr);
 	kunmap_atomic(daddr);
-#endif
 	goto out;
 }
 
@@ -8957,11 +8943,7 @@
  * the same data in min(sg1_size, sg2_size) size will match!
  */
 static bool sg_cmp(struct scatterlist *dst_sg, struct scatterlist *src_sg,
-	    int nents_to_cmp, size_t cmp_len, int *miscompare_offs
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-	    , enum km_type d_km_type, enum km_type s_km_type
-#endif
-	    )
+	    int nents_to_cmp, size_t cmp_len, int *miscompare_offs)
 {
 	bool res = true;
 	size_t dst_len, dst_offs;
@@ -8991,9 +8973,6 @@
 
 		compared = sg_cmp_elem(&dst_sg, &dst_len, &dst_offs,
 				src_sg, cmp_len, miscompare_offs,
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-				d_km_type, s_km_type,
-#endif
 				&res);
 		if (!res) {
 			if (miscompare_offs != NULL) {
@@ -9032,9 +9011,6 @@
 	struct scatterlist *src_sg, *dst_sg;
 	struct scatterlist *src_sg_dif, *dst_sg_dif;
 	unsigned int to_copy, to_copy_dif;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-	int atomic = scst_cmd_atomic(cmd);
-#endif
 
 	TRACE_ENTRY();
 
@@ -9078,24 +9054,12 @@
 		goto out;
 	}
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-	sg_copy(dst_sg, src_sg, 0, to_copy,
-		atomic ? KM_SOFTIRQ0 : KM_USER0,
-		atomic ? KM_SOFTIRQ1 : KM_USER1);
-#else
 	sg_copy(dst_sg, src_sg, 0, to_copy);
-#endif
 
 	if ((src_sg_dif == NULL) || (dst_sg_dif == NULL) || (to_copy_dif == 0))
 		goto out;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 4, 0)
-	sg_copy(dst_sg_dif, src_sg_dif, 0, to_copy_dif,
-		atomic ? KM_SOFTIRQ0 : KM_USER0,
-		atomic ? KM_SOFTIRQ1 : KM_USER1);
-#else
 	sg_copy(dst_sg_dif, src_sg_dif, 0, to_copy_dif);
-#endif
 
 out:
 	TRACE_EXIT();
@@ -9251,12 +9215,7 @@
 
 static __be16 scst_dif_crc_fn(const void *data, unsigned int len)
 {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
 	return cpu_to_be16(crc_t10dif(data, len));
-#else
-	WARN_ON_ONCE(true);
-	return 0;
-#endif
 }
 
 static __be16 scst_dif_ip_fn(const void *data, unsigned int len)
@@ -11144,6 +11103,7 @@
 				SCST_REG_RESERVE_ALLOWED |
 				SCST_WRITE_EXCL_ALLOWED |
 				SCST_EXCL_ACCESS_ALLOWED;
+		cmd->log2_max_buf_len = 7;
 		break;
 	case SAI_GET_LBA_STATUS:
 		cmd->op_name = "GET LBA STATUS";
@@ -11575,13 +11535,12 @@
 
 	if (res != 0)
 		return res;
-	else {
+
 #ifdef CONFIG_SCST_DIF_INJECT_CORRUPTED_TAGS
-		EXTRACHECKS_BUG_ON(cmd->cdb[0] != READ_10);
-		cmd->cmd_corrupt_dif_tag = (cmd->cdb[6] & 0xE0) >> 5;
+	EXTRACHECKS_BUG_ON(cmd->cdb[0] != READ_10);
+	cmd->cmd_corrupt_dif_tag = (cmd->cdb[6] & 0xE0) >> 5;
 #endif
-		return scst_parse_rdprotect(cmd);
-	}
+	return scst_parse_rdprotect(cmd);
 }
 
 static int get_cdb_info_lba_4_len_2_wrprotect(struct scst_cmd *cmd,
@@ -11642,13 +11601,12 @@
 
 	if (res != 0)
 		return res;
-	else {
+
 #ifdef CONFIG_SCST_DIF_INJECT_CORRUPTED_TAGS
-		EXTRACHECKS_BUG_ON(cmd->cdb[0] != READ_16);
-		cmd->cmd_corrupt_dif_tag = (cmd->cdb[14] & 0xE0) >> 5;
+	EXTRACHECKS_BUG_ON(cmd->cdb[0] != READ_16);
+	cmd->cmd_corrupt_dif_tag = (cmd->cdb[14] & 0xE0) >> 5;
 #endif
-		return scst_parse_rdprotect(cmd);
-	}
+	return scst_parse_rdprotect(cmd);
 }
 
 static int get_cdb_info_lba_8_len_4_wrprotect(struct scst_cmd *cmd,
@@ -11737,6 +11695,16 @@
 	return get_cdb_info_write_same(cmd, sdbops, cmd->cdb[10] & 1 /*NDOB*/);
 }
 
+static int get_cdb_info_dyn_runtime_attr(struct scst_cmd *cmd,
+	const struct scst_sdbops *sdbops)
+{
+	/*
+	 * Read/write dyn runtime attr commands are non-standard, CDB len is 12
+	 */
+	cmd->cdb_len = 12;
+	return get_cdb_info_len_4(cmd, sdbops);
+}
+
 /**
  * scst_set_cmd_from_cdb_info() - Parse the SCSI CDB.
  * @cmd: SCSI command to parse.
@@ -11747,6 +11715,8 @@
 static int scst_set_cmd_from_cdb_info(struct scst_cmd *cmd,
 	const struct scst_sdbops *ptr)
 {
+	int res;
+
 	cmd->cdb_len = SCST_GET_CDB_LEN(cmd->cdb[0]);
 	cmd->cmd_naca = (cmd->cdb[cmd->cdb_len - 1] & CONTROL_BYTE_NACA_BIT);
 	cmd->cmd_linked = (cmd->cdb[cmd->cdb_len - 1] & CONTROL_BYTE_LINK_BIT);
@@ -11757,7 +11727,16 @@
 	cmd->lba_len = ptr->info_lba_len;
 	cmd->len_off = ptr->info_len_off;
 	cmd->len_len = ptr->info_len_len;
-	return (*ptr->get_cdb_info)(cmd, ptr);
+	cmd->log2_max_buf_len = ptr->log2_max_buf_len;
+	res = (*ptr->get_cdb_info)(cmd, ptr);
+	if (!cmd->log2_max_buf_len ||
+	    cmd->bufflen <= (1U << cmd->log2_max_buf_len))
+		return res;
+	PRINT_ERROR("Data buffer length %d is too big for SCSI command %s (max %d)",
+		    cmd->bufflen, scst_get_opcode_name(cmd),
+		    1U << cmd->log2_max_buf_len);
+	scst_set_invalid_field_in_cdb(cmd, cmd->len_off, 0);
+	return 1;
 }
 
 static int get_cdb_info_var_len(struct scst_cmd *cmd,
@@ -12077,7 +12056,7 @@
 			      ptr->devkey[9],	/* commdev */
 			      ptr->info_op_name);
 			TRACE_DBG("data direction %d, op flags 0x%x, lba off %d, "
-				"lba len %d, len off %d, len len %d",
+				"lba len %d, len off %d, len of len %d",
 				ptr->info_data_direction, ptr->info_op_flags,
 				ptr->info_lba_off, ptr->info_lba_len,
 				ptr->info_len_off, ptr->info_len_len);
@@ -12370,21 +12349,20 @@
 	 */
 
 	if (cmd->op_flags & SCST_TRANSFER_LEN_TYPE_FIXED && cmd->cdb[1] & 1) {
-		int block_size = cmd->dev->block_size;
-		uint64_t b, ob;
-		bool overflow;
+		uint32_t block_size = cmd->dev->block_size;
+		uint32_t block_shift = cmd->dev->block_shift;
+		bool overflow = shift_left_overflows(cmd->bufflen, block_shift) ||
+				shift_left_overflows(cmd->data_len, block_shift) ||
+				shift_left_overflows(cmd->out_bufflen, block_shift);
 
-		b = ((uint64_t)cmd->bufflen) * block_size;
-		ob = ((uint64_t)cmd->out_bufflen) * block_size;
-
-		overflow = (b > 0xFFFFFFFF) ||
-			   (ob > 0xFFFFFFFF);
+		BUILD_BUG_ON(sizeof(cmd->bufflen) != 4);
+		BUILD_BUG_ON(sizeof(cmd->out_bufflen) != 4);
 		if (unlikely(overflow)) {
 			PRINT_WARNING("bufflen %u, data_len %llu or out_bufflen"
 				      " %u too large for device %s (block size"
-				      " %u, b %llu, ob %llu)", cmd->bufflen,
+				      " %u)", cmd->bufflen,
 				      cmd->data_len, cmd->out_bufflen,
-				      cmd->dev->virt_name, block_size, b, ob);
+				      cmd->dev->virt_name, block_size);
 			PRINT_BUFFER("CDB", cmd->cdb, cmd->cdb_len);
 			scst_set_cmd_error(cmd, SCST_LOAD_SENSE(
 					scst_sense_block_out_range_error));
@@ -12392,12 +12370,9 @@
 			goto out;
 		}
 
-		cmd->bufflen = b;
-		cmd->out_bufflen = ob;
-
-		/* cmd->data_len is 64-bit, so can't overflow here */
-		BUILD_BUG_ON(sizeof(cmd->data_len) < 8);
-		cmd->data_len *= block_size;
+		cmd->bufflen <<= block_shift;
+		cmd->out_bufflen <<= block_shift;
+		cmd->data_len <<= block_shift;
 	}
 
 	if ((cmd->op_flags & (SCST_SMALL_TIMEOUT | SCST_LONG_TIMEOUT)) == 0)
@@ -12838,7 +12813,7 @@
 		sl = scst_set_sense(sense, sense_len, dev->d_sense,
 			SCST_LOAD_SENSE(scst_sense_reset_UA));
 		scst_dev_check_set_UA(dev, NULL, sense, sl);
-	} else if ((status_byte(result) == CHECK_CONDITION) &&
+	} else if ((result & 0xff) == SAM_STAT_CHECK_CONDITION &&
 		   scst_is_ua_sense(sense, sense_len))
 		scst_dev_check_set_UA(dev, NULL, sense, sense_len);
 
@@ -13719,7 +13694,7 @@
 			scst_check_internal_sense(dev, rc, sense_buffer,
 				sizeof(sense_buffer));
 #if 0
-			if ((status_byte(rc) == CHECK_CONDITION) &&
+			if ((rc & 0xff) == SAM_STAT_CHECK_CONDITION &&
 			    scst_sense_valid(sense_buffer)) {
 #else
 			/*
@@ -13759,9 +13734,11 @@
 					goto brk;
 				}
 				switch (driver_byte(rc)) {
+#if defined(DRIVER_BUSY) && defined(DRIVER_SOFT)
 				case DRIVER_BUSY:
 				case DRIVER_SOFT:
 					break;
+#endif
 				default:
 					goto brk;
 				}
@@ -14047,8 +14024,6 @@
 process_qerr:
 	scst_process_qerr(cmd);
 
-	scst_store_sense(cmd);
-
 	res = 0;
 
 out:
@@ -14495,11 +14470,11 @@
 	return;
 }
 
-/**
- ** We currently have only few saved parameters and it is impossible to get
- ** pointer on a bit field, so let's have a simple straightforward
- ** implementation.
- **/
+/*
+ * We currently have only few saved parameters and it is impossible to get
+ * pointer on a bit field, so let's have a simple straightforward
+ * implementation.
+ */
 
 #define SCST_TAS_LABEL		"TAS"
 #define SCST_QERR_LABEL		"QERR"
@@ -14878,16 +14853,10 @@
 	return;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void scst_ext_blocking_done_fn(void *p)
-{
-	struct scst_device *dev = p;
-#else
 static void scst_ext_blocking_done_fn(struct work_struct *work)
 {
 	struct scst_device *dev = container_of(work, struct scst_device,
 					ext_blockers_work);
-#endif
 
 	TRACE_ENTRY();
 
@@ -15098,20 +15067,6 @@
 }
 
 /* Abstract vfs_unlink() for different kernel versions (as possible) */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
-void scst_vfs_unlink_and_put_nd(struct nameidata *nd)
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
-	vfs_unlink(nd->dentry->d_parent->d_inode, nd->dentry);
-	dput(nd->dentry);
-	mntput(nd->mnt);
-#else
-	vfs_unlink(nd->path.dentry->d_parent->d_inode,
-		nd->path.dentry);
-	path_put(&nd->path);
-#endif
-}
-#endif
 
 void scst_vfs_unlink_and_put(struct path *path)
 {
@@ -15120,29 +15075,18 @@
 	(!defined(CONFIG_SUSE_KERNEL) || \
 	 LINUX_VERSION_CODE < KERNEL_VERSION(3, 12, 0))
 	vfs_unlink(path->dentry->d_parent->d_inode, path->dentry);
-#else
+#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 12, 0)
 	vfs_unlink(path->dentry->d_parent->d_inode, path->dentry, NULL);
+#else
+	vfs_unlink(&init_user_ns, path->dentry->d_parent->d_inode, path->dentry,
+		   NULL);
 #endif
 	path_put(path);
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
-void scst_path_put(struct nameidata *nd)
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
-	dput(nd->dentry);
-	mntput(nd->mnt);
-#else
-	path_put(&nd->path);
-#endif
-}
-EXPORT_SYMBOL(scst_path_put);
-#endif
-
 int scst_copy_file(const char *src, const char *dest)
 {
 	int res = 0;
-	struct inode *inode;
 	loff_t file_size, pos;
 	uint8_t *buf = NULL;
 	struct file *file_src = NULL, *file_dest = NULL;
@@ -15158,6 +15102,12 @@
 
 	TRACE_DBG("Copying '%s' into '%s'", src, dest);
 
+	file_size = scst_file_or_bdev_size(src);
+	if (file_size < 0) {
+		res = file_size;
+		goto out;
+	}
+
 	file_src = filp_open(src, O_RDONLY, 0);
 	if (IS_ERR(file_src)) {
 		res = PTR_ERR(file_src);
@@ -15173,20 +15123,6 @@
 		goto out_close;
 	}
 
-	inode = file_inode(file_src);
-
-	if (S_ISREG(inode->i_mode)) {
-		/* Nothing to do */
-	} else if (S_ISBLK(inode->i_mode)) {
-		inode = inode->i_bdev->bd_inode;
-	} else {
-		PRINT_ERROR("Invalid file mode 0x%x", inode->i_mode);
-		res = -EINVAL;
-		goto out_skip;
-	}
-
-	file_size = inode->i_size;
-
 	buf = vmalloc(file_size);
 	if (buf == NULL) {
 		res = -ENOMEM;
@@ -15232,27 +15168,15 @@
 int scst_remove_file(const char *name)
 {
 	int res = 0;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
-	struct nameidata nd;
-#else
 	struct path path;
-#endif
 
 	TRACE_ENTRY();
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
-	res = path_lookup(name, 0, &nd);
-	if (!res)
-		scst_vfs_unlink_and_put_nd(&nd);
-	else
-		TRACE_DBG("Unable to lookup file '%s' - error %d", name, res);
-#else
 	res = kern_path(name, 0, &path);
 	if (!res)
 		scst_vfs_unlink_and_put(&path);
 	else
 		TRACE_DBG("Unable to lookup file '%s' - error %d", name, res);
-#endif
 
 	TRACE_EXIT_RES(res);
 	return res;
@@ -15279,7 +15203,7 @@
 		res = PTR_ERR(file);
 		PRINT_ERROR("Unable to (re)create file '%s' - error %d",
 			name, res);
-		goto out_set_fs;
+		goto out_remove_file;
 	}
 
 	TRACE_DBG("Writing file '%s'", name);
@@ -15315,7 +15239,7 @@
 
 	filp_close(file, NULL);
 
-out_set_fs:
+out_remove_file:
 	if (res == 0)
 		scst_remove_file(name1);
 	else
@@ -15332,7 +15256,7 @@
 	filp_close(file, NULL);
 	if (res > 0)
 		res = -EIO;
-	goto out_set_fs;
+	goto out_remove_file;
 }
 EXPORT_SYMBOL_GPL(scst_write_file_transactional);
 
@@ -15341,13 +15265,18 @@
 {
 	int res;
 	struct file *file = NULL;
-	struct inode *inode;
 	loff_t file_size, pos;
 
 	TRACE_ENTRY();
 
 	TRACE_DBG("Loading file '%s'", file_name);
 
+	file_size = scst_file_or_bdev_size(file_name);
+	if (file_size < 0) {
+		res = file_size;
+		goto out;
+	}
+
 	file = filp_open(file_name, O_RDONLY, 0);
 	if (IS_ERR(file)) {
 		res = PTR_ERR(file);
@@ -15355,20 +15284,6 @@
 		goto out;
 	}
 
-	inode = file_inode(file);
-
-	if (S_ISREG(inode->i_mode)) {
-		/* Nothing to do */
-	} else if (S_ISBLK(inode->i_mode)) {
-		inode = inode->i_bdev->bd_inode;
-	} else {
-		PRINT_ERROR("Invalid file mode 0x%x", inode->i_mode);
-		res = -EINVAL;
-		goto out_close;
-	}
-
-	file_size = inode->i_size;
-
 	if (file_size > size) {
 		PRINT_ERROR("Supplied buffer (%d) too small (need %d)", size,
 			(int)file_size);
@@ -15495,9 +15410,6 @@
 	TRACE_BUFFER("scst_scsi_op_list", scst_scsi_op_list,
 		sizeof(scst_scsi_op_list));
 
-	scst_release_acg_wq = create_workqueue("scst_release_acg");
-	WARN_ON_ONCE(IS_ERR(scst_release_acg_wq));
-
 	TRACE_EXIT();
 	return;
 }
@@ -15506,37 +15418,45 @@
 {
 	int res = 0;
 
+	TRACE_ENTRY();
+
 	scst_scsi_op_list_init();
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
+	scst_release_acg_wq = alloc_workqueue("scst_release_acg", WQ_MEM_RECLAIM, 1);
+	if (unlikely(!scst_release_acg_wq)) {
+		PRINT_ERROR("Failed to allocate scst_release_acg_wq");
+		res = -ENOMEM;
+		goto out;
+	}
+
 	scsi_io_context_cache = kmem_cache_create("scst_scsi_io_context",
 					sizeof(struct scsi_io_context),
 					__alignof__(struct scsi_io_context),
 					SCST_SLAB_FLAGS|SLAB_HWCACHE_ALIGN, NULL);
 	if (!scsi_io_context_cache) {
-		PRINT_ERROR("%s", "Can't init scsi io context cache");
+		PRINT_ERROR("Can't init scsi io context cache");
 		res = -ENOMEM;
-		goto out;
+		goto free_wq;
 	}
 
 out:
-#endif
 	TRACE_EXIT_RES(res);
 	return res;
+
+free_wq:
+	destroy_workqueue(scst_release_acg_wq);
+	goto out;
 }
 
 void scst_lib_exit(void)
 {
-	/* Wait until any ongoing acg->put_work has finished. */
-	flush_workqueue(scst_release_acg_wq);
+	/* All pending works will be drained by destroy_workqueue() */
 	destroy_workqueue(scst_release_acg_wq);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30)
-	BUILD_BUG_ON(SCST_MAX_CDB_SIZE != BLK_MAX_CDB);
+	BUILD_BUG_ON(SCST_MAX_CDB_SIZE != MAX_COMMAND_SIZE);
 	BUILD_BUG_ON(SCST_SENSE_BUFFERSIZE < SCSI_SENSE_BUFFERSIZE);
 
 	kmem_cache_destroy(scsi_io_context_cache);
-#endif
 }
 
 #ifdef CONFIG_SCST_DEBUG
diff --git a/scst/scst/src/scst_main.c b/scst/scst/src/scst_main.c
index 1503447..d466ec7 100644
--- a/scst/scst/src/scst_main.c
+++ b/scst/scst/src/scst_main.c
@@ -47,12 +47,6 @@
 details.
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) && \
-	!defined(CONFIG_SCST_STRICT_SERIALIZING)
-#warning CONFIG_SCST_STRICT_SERIALIZING has not been defined. \
-Pass-through dev handlers will not work.
-#endif
-
 /*
  ** SCST global variables. They are all uninitialized to have their layout in
  ** memory be exactly as specified. Otherwise compiler puts zero-initialized
@@ -112,16 +106,15 @@
 unsigned long scst_trace_flag;
 #endif
 
-unsigned long scst_flags;
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
 unsigned long scst_poll_ns = SCST_DEF_POLL_NS;
-#endif
 
 int scst_max_tasklet_cmd = SCST_DEF_MAX_TASKLET_CMD;
 
 struct scst_cmd_threads scst_main_cmd_threads;
 
+static bool percpu_ref_killed;
+struct percpu_ref scst_cmd_count;
+struct percpu_ref scst_mcmd_count;
 struct scst_percpu_info scst_percpu_infos[NR_CPUS];
 
 spinlock_t scst_mcmd_lock;
@@ -140,7 +133,6 @@
 /* protected by scst_cmd_threads_mutex */
 static struct list_head scst_cmd_threads_list;
 
-int scst_threads;
 static struct task_struct *scst_init_cmd_thread;
 static struct task_struct *scst_mgmt_thread;
 static struct task_struct *scst_mgmt_cmd_thread;
@@ -150,13 +142,11 @@
  * several threads simultaneously.
  */
 static struct mutex scst_suspend_mutex;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
 #ifdef CONFIG_LOCKDEP
 static struct lock_class_key scst_suspend_key;
 struct lockdep_map scst_suspend_dep_map =
 	STATIC_LOCKDEP_MAP_INIT("scst_suspend_activity", &scst_suspend_key);
 #endif
-#endif
 
 /* Protected by scst_suspend_mutex */
 static int suspend_count;
@@ -165,34 +155,34 @@
 
 cpumask_t default_cpu_mask;
 
-static unsigned int scst_max_cmd_mem;
-unsigned int scst_max_dev_cmd_mem;
-int scst_forcibly_close_sessions;
-int scst_auto_cm_assignment = false;
-
 spinlock_t scst_measure_latency_lock;
 atomic_t scst_measure_latency;
 
+int scst_threads;
 module_param_named(scst_threads, scst_threads, int, S_IRUGO);
 MODULE_PARM_DESC(scst_threads, "SCSI target threads count");
 
+static unsigned int scst_max_cmd_mem;
 module_param_named(scst_max_cmd_mem, scst_max_cmd_mem, int, S_IRUGO);
 MODULE_PARM_DESC(scst_max_cmd_mem, "Maximum memory allowed to be consumed by "
 	"all SCSI commands of all devices at any given time in MB");
 
+unsigned int scst_max_dev_cmd_mem;
 module_param_named(scst_max_dev_cmd_mem, scst_max_dev_cmd_mem, int, S_IRUGO);
 MODULE_PARM_DESC(scst_max_dev_cmd_mem, "Maximum memory allowed to be consumed "
 	"by all SCSI commands of a device at any given time in MB");
 
-module_param_named(forcibly_close_sessions, scst_forcibly_close_sessions, int,
+bool scst_forcibly_close_sessions;
+module_param_named(forcibly_close_sessions, scst_forcibly_close_sessions, bool,
 		   S_IWUSR | S_IRUGO);
 MODULE_PARM_DESC(forcibly_close_sessions,
 "If enabled, close the sessions associated with an access control group (ACG)"
-" when an ACG is deleted via sysfs instead of returning -EBUSY");
+" when an ACG is deleted via sysfs instead of returning -EBUSY. (default: false)");
 
-module_param_named(auto_cm_assignment, scst_auto_cm_assignment, int,
+bool scst_auto_cm_assignment = true;
+module_param_named(auto_cm_assignment, scst_auto_cm_assignment, bool,
 		   S_IWUSR | S_IRUGO);
-MODULE_PARM_DESC(auto_cm_assignment, "Enables the copy managers auto registration");
+MODULE_PARM_DESC(auto_cm_assignment, "Enables the copy managers auto registration. (default: true)");
 
 struct scst_dev_type scst_null_devtype = {
 	.name = "none",
@@ -231,10 +221,6 @@
 		goto out;
 	}
 
-	if (vtt->detect)
-		PRINT_WARNING("detect() method is obsolete and scheduled for "
-			"removal (target driver %s)", vtt->name);
-
 	if (!vtt->release) {
 		PRINT_ERROR("Target driver %s must have "
 			"release() method.", vtt->name);
@@ -302,30 +288,12 @@
 	mutex_unlock(&scst_mutex2);
 	mutex_unlock(&scst_mutex);
 
-	TRACE_DBG("%s", "Calling target driver's detect()");
-	res = vtt->detect ? vtt->detect(vtt) : 0;
-	TRACE_DBG("Target driver's detect() returned %d", res);
-	if (res < 0) {
-		PRINT_ERROR("%s", "The detect() routine failed");
-		res = -EINVAL;
-		goto out_del;
-	}
-
 	PRINT_INFO("Target template %s registered successfully", vtt->name);
 
 out:
 	TRACE_EXIT_RES(res);
 	return res;
 
-out_del:
-	scst_tgtt_sysfs_del(vtt);
-
-	mutex_lock(&scst_mutex);
-
-	mutex_lock(&scst_mutex2);
-	list_del(&vtt->scst_template_list_entry);
-	mutex_unlock(&scst_mutex2);
-
 out_unlock:
 	mutex_unlock(&scst_mutex);
 	goto out;
@@ -796,13 +764,15 @@
 	return;
 }
 
+/*
+ * Number of SCST non-management commands, management commands and activities
+ * that are in progress. Must only be called if both scst_cmd_count and
+ * scst_mcmd_count are in atomic mode.
+ */
 int scst_get_cmd_counter(void)
 {
-	int i, res = 0;
-
-	for (i = 0; i < ARRAY_SIZE(scst_percpu_infos); i++)
-		res += atomic_read(&scst_percpu_infos[i].cpu_cmd_count);
-	return res;
+	return percpu_ref_read(&scst_cmd_count) +
+		percpu_ref_read(&scst_mcmd_count);
 }
 
 static int scst_susp_wait(unsigned long timeout)
@@ -820,7 +790,7 @@
 		t = min(timeout, SCST_SUSP_WAIT_REPORT_TIMEOUT);
 
 	res = wait_event_interruptible_timeout(scst_dev_cmd_waitQ,
-			(scst_get_cmd_counter() == 0), t);
+			percpu_ref_killed, t);
 	if (res > 0) {
 		res = 0;
 		goto out;
@@ -836,13 +806,13 @@
 
 	if (timeout != SCST_SUSPEND_TIMEOUT_UNLIMITED) {
 		res = wait_event_interruptible_timeout(scst_dev_cmd_waitQ,
-			(scst_get_cmd_counter() == 0), timeout - t);
+			percpu_ref_killed, timeout - t);
 		if (res == 0)
 			res = -EBUSY;
 		else if (res > 0)
 			res = 0;
 	} else {
-		wait_event(scst_dev_cmd_waitQ, scst_get_cmd_counter() == 0);
+		wait_event(scst_dev_cmd_waitQ, percpu_ref_killed);
 		res = 0;
 	}
 
@@ -855,17 +825,18 @@
 }
 
 /*
- * scst_suspend_activity() - globally suspend any activity
+ * scst_suspend_activity() - globally suspend activity
  *
  * Description:
- *    Globally suspends any activity and doesn't return, until there are any
- *    active commands (state after SCST_CMD_STATE_INIT). Timeout parameter sets
- *    max time this function will wait for suspending or interrupted by a
- *    signal with the corresponding error status < 0. If timeout is
- *    SCST_SUSPEND_TIMEOUT_UNLIMITED, then it will wait virtually forever.
- *    On success returns 0.
+ *    Globally suspends SCSI command and SCSI management command processing and
+ *    waits until all active commands have finished (state after
+ *    SCST_CMD_STATE_INIT). The timeout parameter defines the maximum time this
+ *    function will wait until activity has been suspended. If this function is
+ *    interrupted by a signal, it returns a negative value. If the timeout value
+ *    is SCST_SUSPEND_TIMEOUT_UNLIMITED, then it will wait virtually forever.
+ *    Returns 0 upon success.
  *
- *    New arriving commands stay in the suspended state until
+ *    Newly arriving commands remain in the suspended state until
  *    scst_resume_activity() is called.
  */
 int scst_suspend_activity(unsigned long timeout)
@@ -876,9 +847,7 @@
 
 	TRACE_ENTRY();
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
 	rwlock_acquire_read(&scst_suspend_dep_map, 0, 0, _RET_IP_);
-#endif
 
 	if (timeout != SCST_SUSPEND_TIMEOUT_UNLIMITED) {
 		res = mutex_lock_interruptible(&scst_suspend_mutex);
@@ -892,14 +861,9 @@
 	if (suspend_count > 1)
 		goto out_up;
 
-	set_bit(SCST_FLAG_SUSPENDING, &scst_flags);
-	set_bit(SCST_FLAG_SUSPENDED, &scst_flags);
-	/*
-	 * Assignment of SCST_FLAG_SUSPENDING and SCST_FLAG_SUSPENDED must be
-	 * ordered with cpu_cmd_count in scst_get(). Otherwise, lockless logic
-	 * of scst_get() users won't work.
-	 */
-	smp_mb__after_set_bit();
+	/* Cause scst_get_cmd() to fail. */
+	percpu_ref_killed = false;
+	percpu_ref_kill(&scst_cmd_count);
 
 	/*
 	 * See comment in scst_user.c::dev_user_task_mgmt_fn() for more
@@ -915,18 +879,17 @@
 			scst_get_cmd_counter());
 		rep = true;
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
 		lock_contended(&scst_suspend_dep_map, _RET_IP_);
-#endif
 	}
 
 	res = scst_susp_wait(timeout);
-	if (res != 0)
-		goto out_clear;
 
-	clear_bit(SCST_FLAG_SUSPENDING, &scst_flags);
-	/* See comment about smp_mb() above */
-	smp_mb__after_clear_bit();
+	/* Cause scst_get_mcmd() to fail. */
+	percpu_ref_killed = false;
+	percpu_ref_kill(&scst_mcmd_count);
+
+	if (res != 0)
+		goto out_resume;
 
 	if (scst_get_cmd_counter() != 0)
 		TRACE_MGMT_DBG("Waiting for %d active commands finally to "
@@ -954,21 +917,14 @@
 	mutex_unlock(&scst_suspend_mutex);
 
 out:
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
 	if (res == 0)
 		lock_acquired(&scst_suspend_dep_map, _RET_IP_);
 	else
 		rwlock_release(&scst_suspend_dep_map, _RET_IP_);
-#endif
 
 	TRACE_EXIT_RES(res);
 	return res;
 
-out_clear:
-	clear_bit(SCST_FLAG_SUSPENDING, &scst_flags);
-	/* See comment about smp_mb() above */
-	smp_mb__after_clear_bit();
-
 out_resume:
 	__scst_resume_activity();
 	EXTRACHECKS_BUG_ON(suspend_count != 0);
@@ -994,7 +950,8 @@
 	if (suspend_count > 0)
 		goto out;
 
-	clear_bit(SCST_FLAG_SUSPENDED, &scst_flags);
+	percpu_ref_resurrect(&scst_mcmd_count);
+	percpu_ref_resurrect(&scst_cmd_count);
 
 	mutex_lock(&scst_cmd_threads_mutex);
 	list_for_each_entry(l, &scst_cmd_threads_list, lists_list_entry) {
@@ -1004,7 +961,7 @@
 
 	/*
 	 * Wait until scst_init_thread() either is waiting or has reexamined
-	 * scst_flags.
+	 * scst_cmd_count.
 	 */
 	spin_lock_irq(&scst_init_lock);
 	spin_unlock_irq(&scst_init_lock);
@@ -1037,9 +994,7 @@
 {
 	TRACE_ENTRY();
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
 	rwlock_release(&scst_suspend_dep_map, _RET_IP_);
-#endif
 
 	mutex_lock(&scst_suspend_mutex);
 	__scst_resume_activity();
@@ -1379,8 +1334,10 @@
 	list_add_tail(&dev->dev_list_entry, &scst_dev_list);
 
 	res = scst_cm_on_dev_register(dev);
-	if (res != 0)
+	if (res != 0) {
+		sysfs_del = true;
 		goto out_unreg;
+	}
 
 	mutex_unlock(&scst_mutex);
 
@@ -1531,19 +1488,6 @@
 	if (res != 0)
 		goto out;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) && \
-	!defined(CONFIG_SCST_STRICT_SERIALIZING)
-	if (dev_type->exec == NULL) {
-		PRINT_ERROR("Pass-through dev handlers (handler \"%s\") not "
-			"supported. Consider applying on your kernel patch "
-			"scst_exec_req_fifo-<kernel-version> or define "
-			"CONFIG_SCST_STRICT_SERIALIZING", dev_type->name);
-		res = -EINVAL;
-		goto out;
-	}
-#endif
-
-
 	res = mutex_lock_interruptible(&scst_mutex);
 	if (res != 0)
 		goto out;
@@ -2195,22 +2139,14 @@
 }
 EXPORT_SYMBOL_GPL(scst_get_setup_id);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
-static int scst_add(struct class_device *cdev, struct class_interface *intf)
-#else
 static int scst_add(struct device *cdev, struct class_interface *intf)
-#endif
 {
 	struct scsi_device *scsidp;
 	int res = 0;
 
 	TRACE_ENTRY();
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
-	scsidp = to_scsi_device(cdev->dev);
-#else
 	scsidp = to_scsi_device(cdev->parent);
-#endif
 
 	if ((scsidp->host->hostt->name == NULL) ||
 	    (strcmp(scsidp->host->hostt->name, SCST_LOCAL_NAME) != 0))
@@ -2220,21 +2156,13 @@
 	return res;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
-static void scst_remove(struct class_device *cdev, struct class_interface *intf)
-#else
 static void scst_remove(struct device *cdev, struct class_interface *intf)
-#endif
 {
 	struct scsi_device *scsidp;
 
 	TRACE_ENTRY();
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
-	scsidp = to_scsi_device(cdev->dev);
-#else
 	scsidp = to_scsi_device(cdev->parent);
-#endif
 
 	if ((scsidp->host->hostt->name == NULL) ||
 	    (strcmp(scsidp->host->hostt->name, SCST_LOCAL_NAME) != 0))
@@ -2244,17 +2172,10 @@
 	return;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
-static struct class_interface scst_interface = {
-	.add = scst_add,
-	.remove = scst_remove,
-};
-#else
 static struct class_interface scst_interface = {
 	.add_dev = scst_add,
 	.remove_dev = scst_remove,
 };
-#endif
 
 static void __init scst_print_config(void)
 {
@@ -2323,6 +2244,13 @@
 		PRINT_INFO("%s", buf);
 }
 
+static void scst_suspended(struct percpu_ref *ref)
+{
+	WARN_ON_ONCE(ref != &scst_cmd_count && ref != &scst_mcmd_count);
+	percpu_ref_killed = true;
+	wake_up_all(&scst_dev_cmd_waitQ);
+}
+
 static int __init init_scst(void)
 {
 	int res, i;
@@ -2531,8 +2459,17 @@
 	if (res != 0)
 		goto out_destroy_sgv_pool;
 
+	res = percpu_ref_init(&scst_cmd_count, scst_suspended,
+			      PERCPU_REF_ALLOW_REINIT, GFP_KERNEL);
+	if (res != 0)
+		goto out_unreg_interface;
+
+	res = percpu_ref_init(&scst_mcmd_count, scst_suspended,
+			      PERCPU_REF_ALLOW_REINIT, GFP_KERNEL);
+	if (res != 0)
+		goto out_cmd_count;
+
 	for (i = 0; i < ARRAY_SIZE(scst_percpu_infos); i++) {
-		atomic_set(&scst_percpu_infos[i].cpu_cmd_count, 0);
 		spin_lock_init(&scst_percpu_infos[i].tasklet_lock);
 		INIT_LIST_HEAD(&scst_percpu_infos[i].tasklet_cmd_list);
 		tasklet_init(&scst_percpu_infos[i].tasklet,
@@ -2570,10 +2507,14 @@
 
 out_thread_free:
 	scst_stop_global_threads();
+	percpu_ref_exit(&scst_mcmd_count);
 
+out_cmd_count:
+	percpu_ref_exit(&scst_cmd_count);
+
+out_unreg_interface:
 	scsi_unregister_interface(&scst_interface);
 
-
 out_destroy_sgv_pool:
 	scst_sgv_pools_deinit();
 	scst_tg_cleanup();
@@ -2656,6 +2597,9 @@
 
 	scst_deinit_threads(&scst_main_cmd_threads);
 
+	percpu_ref_exit(&scst_mcmd_count);
+	percpu_ref_exit(&scst_cmd_count);
+
 	scsi_unregister_interface(&scst_interface);
 
 
diff --git a/scst/scst/src/scst_mem.c b/scst/scst/src/scst_mem.c
index a0bba76..fb438b5 100644
--- a/scst/scst/src/scst_mem.c
+++ b/scst/scst/src/scst_mem.c
@@ -52,13 +52,11 @@
 
 static struct sgv_pool *sgv_norm_clust_pool_main, *sgv_norm_pool_main, *sgv_dma_pool_main;
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
 #if defined(CONFIG_LOCKDEP) && !defined(CONFIG_SCST_PROC)
 static struct lock_class_key scst_pool_key;
 static struct lockdep_map scst_pool_dep_map =
 	STATIC_LOCKDEP_MAP_INIT("scst_pool_kref", &scst_pool_key);
 #endif
-#endif
 
 #ifndef CONFIG_SCST_NO_TOTAL_MEM_CHECKS
 static atomic_t sgv_pages_total = ATOMIC_INIT(0);
@@ -80,11 +78,7 @@
 static atomic_t sgv_other_total_alloc = ATOMIC_INIT(0);
 #endif
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23))
-static struct shrinker *sgv_shrinker;
-#else
 static struct shrinker sgv_shrinker;
-#endif
 
 static struct kmem_cache *sgv_pool_cachep;
 
@@ -340,17 +334,9 @@
 	return freed;
 }
 #else /* if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 35) && (!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 6)
-static int sgv_shrink(int nr, gfp_t gfpm)
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0)
-static int sgv_shrink(struct shrinker *shrinker, int nr, gfp_t gfpm)
-#else
 static int sgv_shrink(struct shrinker *shrinker, struct shrink_control *sc)
-#endif
 {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 0)
 	int nr = sc->nr_to_scan;
-#endif
 	int freed = 0;
 
 	TRACE_ENTRY();
@@ -366,19 +352,11 @@
 }
 #endif /* if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) */
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void sgv_purge_work_fn(void *p)
-#else
 static void sgv_purge_work_fn(struct work_struct *work)
-#endif
 {
 	unsigned long cur_time = jiffies;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	struct sgv_pool *pool = (struct sgv_pool *)p;
-#else
 	struct sgv_pool *pool = container_of(work, struct sgv_pool,
 					     sgv_purge_work.work);
-#endif
 
 	TRACE_ENTRY();
 
@@ -1379,12 +1357,7 @@
 	pool->caches[cache_num] = kmem_cache_create(
 		pool->cache_names[cache_num], size,
 		0, per_cpu ? SCST_SLAB_FLAGS :
-			     (SCST_SLAB_FLAGS|SLAB_HWCACHE_ALIGN), NULL
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23))
-		, NULL);
-#else
-		);
-#endif
+		(SCST_SLAB_FLAGS|SLAB_HWCACHE_ALIGN), NULL);
 	return;
 }
 
@@ -1458,11 +1431,7 @@
 	for (i = 0; i < pool->max_caches; i++)
 		INIT_LIST_HEAD(&pool->recycling_lists[i]);
 
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20))
 	INIT_DELAYED_WORK(&pool->sgv_purge_work, sgv_purge_work_fn);
-#else
-	INIT_WORK(&pool->sgv_purge_work, sgv_purge_work_fn, pool);
-#endif
 
 	spin_lock_bh(&sgv_pools_lock);
 	list_add_tail(&pool->sgv_pools_list_entry, &sgv_pools_list);
@@ -1487,11 +1456,8 @@
 
 out_free:
 	for (i = 0; i < pool->max_caches; i++) {
-		if (pool->caches[i]) {
-			kmem_cache_destroy(pool->caches[i]);
-			pool->caches[i] = NULL;
-		} else
-			break;
+		kmem_cache_destroy(pool->caches[i]);
+		pool->caches[i] = NULL;
 	}
 	goto out;
 }
@@ -1553,6 +1519,9 @@
 
 	TRACE_ENTRY();
 
+	if (unlikely(!pool))
+		goto out;
+
 	sgv_pool_flush(pool);
 
 	mutex_lock(&sgv_pools_mutex);
@@ -1568,13 +1537,13 @@
 	cancel_delayed_work_sync(&pool->sgv_purge_work);
 
 	for (i = 0; i < pool->max_caches; i++) {
-		if (pool->caches[i])
-			kmem_cache_destroy(pool->caches[i]);
+		kmem_cache_destroy(pool->caches[i]);
 		pool->caches[i] = NULL;
 	}
 
 	kmem_cache_free(sgv_pool_cachep, pool);
 
+out:
 	TRACE_EXIT();
 	return;
 }
@@ -1822,9 +1791,6 @@
 			goto out_free_per_cpu_dma;
 	}
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23))
-	sgv_shrinker = set_shrinker(DEFAULT_SEEKS, sgv_shrink);
-#else
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0)
 	sgv_shrinker.count_objects = sgv_can_be_shrunk;
 	sgv_shrinker.scan_objects = sgv_scan_shrink;
@@ -1832,8 +1798,10 @@
 	sgv_shrinker.shrink = sgv_shrink;
 #endif
 	sgv_shrinker.seeks = DEFAULT_SEEKS;
-	register_shrinker(&sgv_shrinker);
-#endif
+
+	res = register_shrinker(&sgv_shrinker, "scst-sgv");
+	if (unlikely(res))
+		goto out_free_per_cpu_dma;
 
 out:
 	TRACE_EXIT_RES(res);
@@ -1841,18 +1809,15 @@
 
 out_free_per_cpu_dma:
 	for (i = 0; i < nr_cpu_ids; i++)
-		if (sgv_dma_pool_per_cpu[i] != NULL)
-			sgv_pool_destroy(sgv_dma_pool_per_cpu[i]);
+		sgv_pool_destroy(sgv_dma_pool_per_cpu[i]);
 
 out_free_per_cpu_clust:
 	for (i = 0; i < nr_cpu_ids; i++)
-		if (sgv_norm_clust_pool_per_cpu[i] != NULL)
-			sgv_pool_destroy(sgv_norm_clust_pool_per_cpu[i]);
+		sgv_pool_destroy(sgv_norm_clust_pool_per_cpu[i]);
 
 out_free_per_cpu_norm:
 	for (i = 0; i < nr_cpu_ids; i++)
-		if (sgv_norm_pool_per_cpu[i] != NULL)
-			sgv_pool_destroy(sgv_norm_pool_per_cpu[i]);
+		sgv_pool_destroy(sgv_norm_pool_per_cpu[i]);
 
 	sgv_pool_destroy(sgv_dma_pool_main);
 
@@ -1866,7 +1831,7 @@
 	kmem_cache_destroy(sgv_pool_cachep);
 
 out_err:
-	res = -ENOMEM;
+	res = res ?: -ENOMEM;
 	goto out;
 }
 
@@ -1876,26 +1841,19 @@
 
 	TRACE_ENTRY();
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 23))
-	remove_shrinker(sgv_shrinker);
-#else
 	unregister_shrinker(&sgv_shrinker);
-#endif
 
 	sgv_pool_destroy(sgv_dma_pool_main);
 	for (i = 0; i < nr_cpu_ids; i++)
-		if (sgv_dma_pool_per_cpu[i] != NULL)
-			sgv_pool_destroy(sgv_dma_pool_per_cpu[i]);
+		sgv_pool_destroy(sgv_dma_pool_per_cpu[i]);
 
 	sgv_pool_destroy(sgv_norm_pool_main);
 	for (i = 0; i < nr_cpu_ids; i++)
-		if (sgv_norm_pool_per_cpu[i] != NULL)
-			sgv_pool_destroy(sgv_norm_pool_per_cpu[i]);
+		sgv_pool_destroy(sgv_norm_pool_per_cpu[i]);
 
 	sgv_pool_destroy(sgv_norm_clust_pool_main);
 	for (i = 0; i < nr_cpu_ids; i++)
-		if (sgv_norm_clust_pool_per_cpu[i] != NULL)
-			sgv_pool_destroy(sgv_norm_clust_pool_per_cpu[i]);
+		sgv_pool_destroy(sgv_norm_clust_pool_per_cpu[i]);
 
 	for (i = 0; i < nr_cpu_ids; i++)
 		sgv_norm_pool_global[i] = NULL;
@@ -2051,10 +2009,13 @@
 	__ATTR(stats, S_IRUGO | S_IWUSR, sgv_sysfs_stat_show,
 		sgv_sysfs_stat_reset);
 
-static struct attribute *sgv_attrs[] = {
+static struct attribute *sgv_pool_attrs[] = {
 	&sgv_stat_attr.attr,
 	NULL,
 };
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
+ATTRIBUTE_GROUPS(sgv_pool);
+#endif
 
 static void sgv_kobj_release(struct kobject *kobj)
 {
@@ -2073,7 +2034,11 @@
 static struct kobj_type sgv_pool_ktype = {
 	.sysfs_ops = &scst_sysfs_ops,
 	.release = sgv_kobj_release,
-	.default_attrs = sgv_attrs,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
+	.default_groups = sgv_pool_groups,
+#else
+	.default_attrs = sgv_pool_attrs,
+#endif
 };
 
 static int scst_sgv_sysfs_create(struct sgv_pool *pool)
@@ -2114,10 +2079,13 @@
 	__ATTR(global_stats, S_IRUGO | S_IWUSR, sgv_sysfs_global_stat_show,
 		sgv_sysfs_global_stat_reset);
 
-static struct attribute *sgv_default_attrs[] = {
+static struct attribute *sgv_def_attrs[] = {
 	&sgv_global_stat_attr.attr,
 	NULL,
 };
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
+ATTRIBUTE_GROUPS(sgv_def);
+#endif
 
 static void scst_sysfs_release(struct kobject *kobj)
 {
@@ -2127,7 +2095,11 @@
 static struct kobj_type sgv_ktype = {
 	.sysfs_ops = &scst_sysfs_ops,
 	.release = scst_sysfs_release,
-	.default_attrs = sgv_default_attrs,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
+	.default_groups = sgv_def_groups,
+#else
+	.default_attrs = sgv_def_attrs,
+#endif
 };
 
 /*
diff --git a/scst/scst/src/scst_mem.h b/scst/scst/src/scst_mem.h
index 6b64e1d..9b5439c 100644
--- a/scst/scst/src/scst_mem.h
+++ b/scst/scst/src/scst_mem.h
@@ -50,7 +50,7 @@
 	void *allocator_priv;
 	struct trans_tbl_ent *trans_tbl;
 	struct scatterlist *sg_entries;
-	struct scatterlist sg_entries_data[0];
+	struct scatterlist sg_entries_data[];
 };
 
 /*
@@ -108,11 +108,7 @@
 
 	struct sgv_pool_cache_acc cache_acc[SGV_POOL_ELEMENTS];
 
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20))
 	struct delayed_work sgv_purge_work;
-#else
-	struct work_struct sgv_purge_work;
-#endif
 
 	atomic_t big_alloc, big_pages, big_merged;
 	atomic_t other_alloc, other_pages, other_merged;
diff --git a/scst/scst/src/scst_pres.c b/scst/scst/src/scst_pres.c
index 96f3d77..ce86068 100644
--- a/scst/scst/src/scst_pres.c
+++ b/scst/scst/src/scst_pres.c
@@ -41,15 +41,6 @@
 #endif
 #include <linux/vmalloc.h>
 #include <asm/unaligned.h>
-#include <stdarg.h>
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
-#include <linux/mount.h>
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
-#include <linux/writeback.h>
-#endif
 
 #ifdef INSIDE_KERNEL_TREE
 #include <scst/scst.h>
@@ -61,6 +52,14 @@
 #include "scst_priv.h"
 #include "scst_pres.h"
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(9, 1))
+#include <stdarg.h>
+#else
+#include <linux/stdarg.h>
+#endif
+
 #define SCST_PR_ROOT_ENTRY	"pr"
 #define SCST_PR_FILE_SIGN	0xBBEEEEAAEEBBDD77LLU
 #define SCST_PR_FILE_VERSION	1LLU
@@ -71,7 +70,7 @@
 #define isblank(c)		((c) == ' ' || (c) == '\t')
 #endif
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32) && defined(CONFIG_LOCKDEP)
+#if defined(CONFIG_LOCKDEP)
 #define scst_assert_pr_mutex_held(dev)					\
 	do {								\
 		if (dev->dev_list_entry.next &&				\
@@ -173,8 +172,9 @@
 			if (tolower(tid_a[i]) != tolower(tid_b[i]))
 				return false;
 		return true;
-	} else
-		len = TID_COMMON_SIZE;
+	}
+
+	len = TID_COMMON_SIZE;
 
 	return memcmp(tid_a, tid_b, len) == 0;
 
@@ -660,7 +660,6 @@
 {
 	int res = 0, rc;
 	struct file *file = NULL;
-	struct inode *inode;
 	char *buf = NULL;
 	loff_t file_size, pos, data_size;
 	uint64_t sign, version;
@@ -676,6 +675,12 @@
 
 	TRACE_PR("Loading persistent file '%s'", file_name);
 
+	file_size = scst_file_or_bdev_size(file_name);
+	if (file_size < 0) {
+		res = file_size;
+		goto out;
+	}
+
 	file = filp_open(file_name, O_RDONLY, 0);
 	if (IS_ERR(file)) {
 		res = PTR_ERR(file);
@@ -683,19 +688,6 @@
 		goto out;
 	}
 
-	inode = file_inode(file);
-
-	if (S_ISREG(inode->i_mode)) {
-		/* Nothing to do */
-	} else if (S_ISBLK(inode->i_mode)) {
-		inode = inode->i_bdev->bd_inode;
-	} else {
-		PRINT_ERROR("Invalid file mode 0x%x", inode->i_mode);
-		goto out_close;
-	}
-
-	file_size = inode->i_size;
-
 	/* Let's limit the file size by some reasonable number */
 	if ((file_size == 0) || (file_size >= 15*1024*1024)) {
 		PRINT_ERROR("Invalid PR file size %d", (int)file_size);
@@ -1017,19 +1009,7 @@
 
 write_error_close:
 	filp_close(file, NULL);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
-	{
-		struct nameidata nd;
-		int rc;
 
-		rc = path_lookup(dev->pr_file_name, 0,	&nd);
-		if (!rc)
-			scst_vfs_unlink_and_put_nd(&nd);
-		else
-			TRACE_PR("Unable to lookup '%s' - error %d",
-				dev->pr_file_name, rc);
-	}
-#else
 	{
 		struct path path;
 		int rc;
@@ -1041,7 +1021,6 @@
 			TRACE_PR("Unable to lookup '%s' - error %d",
 				dev->pr_file_name, rc);
 	}
-#endif
 	goto out;
 }
 
diff --git a/scst/scst/src/scst_priv.h b/scst/scst/src/scst_priv.h
index 21061c7..ce5b5eb 100644
--- a/scst/scst/src/scst_priv.h
+++ b/scst/scst/src/scst_priv.h
@@ -22,9 +22,7 @@
 #include <linux/types.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
 #include <linux/export.h>
-#endif
 #include <scsi/scsi.h>
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_driver.h>
@@ -74,20 +72,6 @@
 
 #endif
 
-/**
- ** Bits for scst_flags
- **/
-
-/*
- * Set if new commands initialization is being suspended for a while.
- * Used to let TM commands execute while preparing the suspend, since
- * RESET or ABORT could be necessary to free SCSI commands.
- */
-#define SCST_FLAG_SUSPENDING		     0
-
-/* Set if new commands initialization is suspended for a while */
-#define SCST_FLAG_SUSPENDED		     1
-
 extern spinlock_t scst_measure_latency_lock;
 extern atomic_t scst_measure_latency;
 void scst_update_latency_stats(struct scst_cmd *cmd, int new_state);
@@ -168,8 +152,8 @@
 
 extern unsigned int scst_max_dev_cmd_mem;
 
-extern int scst_forcibly_close_sessions;
-extern int scst_auto_cm_assignment;
+extern bool scst_forcibly_close_sessions;
+extern bool scst_auto_cm_assignment;
 
 extern mempool_t *scst_mgmt_mempool;
 extern mempool_t *scst_mgmt_stub_mempool;
@@ -184,7 +168,6 @@
 extern struct kmem_cache *scst_tgtd_cachep;
 extern struct kmem_cache *scst_acgd_cachep;
 
-extern unsigned long scst_flags;
 extern struct list_head scst_template_list;
 extern struct list_head scst_dev_list;
 extern struct list_head scst_dev_type_list;
@@ -199,10 +182,8 @@
 #define SCST_DEF_MAX_TASKLET_CMD 10
 extern int scst_max_tasklet_cmd;
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
 #define SCST_DEF_POLL_NS 0
 extern unsigned long scst_poll_ns;
-#endif
 
 extern spinlock_t scst_init_lock;
 extern struct list_head scst_init_cmd_list;
@@ -217,8 +198,9 @@
 extern struct list_head scst_delayed_mgmt_cmd_list;
 extern wait_queue_head_t scst_mgmt_cmd_list_waitQ;
 
+extern struct percpu_ref scst_cmd_count;
+extern struct percpu_ref scst_mcmd_count;
 struct scst_percpu_info {
-	atomic_t cpu_cmd_count;
 	spinlock_t tasklet_lock;
 	struct list_head tasklet_cmd_list;
 	struct tasklet_struct tasklet;
@@ -244,9 +226,6 @@
 static inline bool scst_set_io_context(struct scst_cmd *cmd,
 				       struct io_context **old)
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)
-	return false;
-#else
 #ifdef CONFIG_SCST_TEST_IO_IN_SIRQ
 	return false;
 #else
@@ -273,7 +252,6 @@
 
 	return res;
 #endif
-#endif
 }
 
 static inline void scst_reset_io_context(struct scst_tgt_dev *tgt_dev,
@@ -439,34 +417,10 @@
 					 uint32_t flags,
 					 int lvblen)
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31)
-	return dlm_new_lockspace((char *)name, namelen, lockspace, flags,
-				 lvblen);
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0)
-	return dlm_new_lockspace(name, namelen, lockspace, flags, lvblen);
-#else
 	return dlm_new_lockspace(name, NULL, flags, lvblen, NULL, NULL, NULL,
 				 lockspace);
-#endif
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
-static inline int scst_exec_req(struct scsi_device *sdev,
-	const unsigned char *cmd, int cmd_len, int data_direction,
-	struct scatterlist *sgl, unsigned int bufflen, unsigned int nents,
-	int timeout, int retries, void *privdata,
-	void (*done)(void *, char *, int, int), gfp_t gfp)
-{
-#if defined(CONFIG_SCST_STRICT_SERIALIZING)
-	return scsi_execute_async(sdev, cmd, cmd_len, data_direction, (void *)sgl,
-		    bufflen, nents, timeout, retries, privdata, done, gfp);
-#else
-	WARN_ON(1);
-	return -1;
-#endif
-}
-#endif
-
 int scst_alloc_space(struct scst_cmd *cmd);
 
 int scst_lib_init(void);
@@ -512,11 +466,7 @@
 			   struct scst_tg_tgt *tg_tgt);
 
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
 extern const struct sysfs_ops scst_sysfs_ops;
-#else
-extern struct sysfs_ops scst_sysfs_ops;
-#endif
 int scst_sysfs_init(void);
 void scst_sysfs_cleanup(void);
 int scst_tgtt_sysfs_create(struct scst_tgt_template *tgtt);
@@ -671,51 +621,64 @@
 int scst_get_suspend_count(void);
 
 /*
- * Increases global SCST ref counters which prevent from entering into suspended
- * activities stage, so protects from any global management operations.
+ * Increase the global command count if it has not been 'killed'. Use this
+ * function to protect regular commands.
  */
-static inline atomic_t *scst_get(void)
+static inline bool __must_check scst_get_cmd(struct scst_cmd *cmd)
 {
-	atomic_t *a;
-
-	/*
-	 * Avoid that a high I/O load prevents activity to be suspended. See
-	 * also http://sourceforge.net/p/scst/mailman/message/34074831/.
-	 */
-	if (unlikely(test_bit(SCST_FLAG_SUSPENDING, &scst_flags)))
-		mdelay(100);
-
-	/*
-	 * We don't mind if we because of preemption inc counter from another
-	 * CPU as soon in the majority cases we will the correct one.
-	 */
-	a = &scst_percpu_infos[raw_smp_processor_id()].cpu_cmd_count;
-	atomic_inc(a);
-	TRACE_DBG("Incrementing cpu_cmd_count %p (new value %d)",
-		a, atomic_read(a));
-	/* See comment about smp_mb() in scst_suspend_activity() */
-	smp_mb__after_atomic_inc();
-
-	return a;
+	if (!percpu_ref_tryget_live(&scst_cmd_count))
+		return false;
+	cmd->counted = true;
+	return true;
 }
 
 /*
- * Decreases global SCST ref counters which prevent from entering into suspended
- * activities stage, so protects from any global management operations. On
- * all them zero, if suspending activities is waiting, it will be proceed.
+ * Increase the global management command count if it is not zero. Use this
+ * function to protect management commands.
  */
-static inline void scst_put(atomic_t *a)
+static inline bool __must_check scst_get_mcmd(struct scst_mgmt_cmd *mcmd)
 {
-	int f;
+	if (!percpu_ref_tryget_live(&scst_mcmd_count))
+		return false;
+	mcmd->counted = true;
+	return true;
+}
 
-	f = atomic_dec_and_test(a);
-	/* See comment about smp_mb() in scst_suspend_activity() */
-	if (unlikely(test_bit(SCST_FLAG_SUSPENDED, &scst_flags)) && f) {
-		TRACE_MGMT_DBG("%s", "Waking up scst_dev_cmd_waitQ");
-		wake_up_all(&scst_dev_cmd_waitQ);
-	}
-	TRACE_DBG("Decrementing cpu_cmd_count %p (new value %d)",
-	      a, atomic_read(a));
+/*
+ * Increase the global command count. Use this function to protect internal
+ * commands.
+ */
+static inline void scst_get_icmd(struct scst_cmd *cmd)
+{
+	percpu_ref_get(&scst_cmd_count);
+	cmd->counted = true;
+}
+
+/* Decrease the global SCST refcount which prevents suspending activity. */
+static inline void scst_put_cmd(struct scst_cmd *cmd)
+{
+	WARN_ON_ONCE(!cmd->counted);
+	cmd->counted = false;
+	percpu_ref_put(&scst_cmd_count);
+}
+
+static inline void scst_put_mcmd(struct scst_mgmt_cmd *mcmd)
+{
+	WARN_ON_ONCE(!mcmd->counted);
+	mcmd->counted = false;
+	percpu_ref_put(&scst_mcmd_count);
+}
+
+/* Whether or not activities are being suspended or have been suspended. */
+static inline bool scst_activity_suspended(void)
+{
+	return percpu_ref_is_dying(&scst_cmd_count);
+}
+
+/* Returns true if and only if regular commands have already been suspended. */
+static inline bool scst_mcmd_suspended(void)
+{
+	return percpu_ref_is_dying(&scst_mcmd_count);
 }
 
 int scst_get_cmd_counter(void);
@@ -765,9 +728,6 @@
 int scst_pr_init(struct scst_device *dev);
 void scst_pr_cleanup(struct scst_device *dev);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
-void scst_vfs_unlink_and_put_nd(struct nameidata *nd);
-#endif
 void scst_vfs_unlink_and_put(struct path *path);
 
 int scst_copy_file(const char *src, const char *dest);
@@ -811,11 +771,7 @@
 enum scst_exec_res scst_cm_ext_copy_exec(struct scst_cmd *cmd);
 enum scst_exec_res scst_cm_rcv_copy_res_exec(struct scst_cmd *cmd);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-void sess_cm_list_id_cleanup_work_fn(void *p);
-#else
 void sess_cm_list_id_cleanup_work_fn(struct work_struct *work);
-#endif
 void scst_cm_free_pending_list_ids(struct scst_session *sess);
 
 bool scst_cm_check_block_all_devs(struct scst_cmd *cmd);
diff --git a/scst/scst/src/scst_sysfs.c b/scst/scst/src/scst_sysfs.c
index 0205fa7..8fed173 100644
--- a/scst/scst/src/scst_sysfs.c
+++ b/scst/scst/src/scst_sysfs.c
@@ -34,7 +34,6 @@
 #include "scst_pres.h"
 #include "scst_mem.h"
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
 #ifdef CONFIG_LOCKDEP
 static struct lock_class_key scst_tgtt_key;
 static struct lockdep_map scst_tgtt_dep_map =
@@ -68,7 +67,6 @@
 static struct lockdep_map scst_tg_dep_map =
 	STATIC_LOCKDEP_MAP_INIT("scst_tg_kref", &scst_tg_key);
 #endif
-#endif
 
 static DECLARE_COMPLETION(scst_sysfs_root_release_completion);
 
@@ -317,37 +315,6 @@
 
 #endif /* defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING) */
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 34) &&	\
-	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 6 ||	\
-	 (RHEL_MAJOR -0 == 6 && RHEL_MINOR -0 < 6))
-/*
- ** Backported sysfs functions.
- **/
-
-static int sysfs_create_files(struct kobject *kobj,
-			      const struct attribute **ptr)
-{
-	int err = 0;
-	int i;
-
-	for (i = 0; ptr[i] && !err; i++)
-		err = sysfs_create_file(kobj, ptr[i]);
-	if (err)
-		while (--i >= 0)
-			sysfs_remove_file(kobj, ptr[i]);
-	return err;
-}
-
-static void sysfs_remove_files(struct kobject *kobj,
-			       const struct attribute **ptr)
-{
-	int i;
-
-	for (i = 0; ptr[i]; i++)
-		sysfs_remove_file(kobj, ptr[i]);
-}
-#endif
-
 /*
  ** Sysfs work
  **/
@@ -453,19 +420,15 @@
 
 		TRACE_DBG("Sysfs work %p", work);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
 		if (work->dep_map) {
 			mutex_acquire(work->dep_map, 0, 0, _RET_IP_);
 			lock_acquired(work->dep_map, _RET_IP_);
 		}
-#endif
 
 		work->work_res = work->sysfs_work_fn(work);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
 		if (work->dep_map)
 			mutex_release(work->dep_map, _RET_IP_);
-#endif
 
 		spin_lock(&sysfs_work_lock);
 		if (!work->read_only_action)
@@ -806,20 +769,12 @@
 		return -EIO;
 }
 
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34))
 const struct sysfs_ops scst_sysfs_ops = {
-#else
-struct sysfs_ops scst_sysfs_ops = {
-#endif
 	.show = scst_show,
 	.store = scst_store,
 };
 
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34))
 const struct sysfs_ops *scst_sysfs_get_sysfs_ops(void)
-#else
-struct sysfs_ops *scst_sysfs_get_sysfs_ops(void)
-#endif
 {
 	return &scst_sysfs_ops;
 }
@@ -1184,7 +1139,7 @@
 
 void scst_kobject_put_and_wait(struct kobject *kobj, const char *category,
 			       struct completion *c
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) && defined(CONFIG_LOCKDEP)
+#if defined(CONFIG_LOCKDEP)
 			       , struct lockdep_map *dep_map
 #endif
 			       )
@@ -1197,9 +1152,7 @@
 
 	kobject_put(kobj);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
 	mutex_acquire(dep_map, 0, 0, _RET_IP_);
-#endif
 
 	if (wait_for_completion_timeout(c, HZ) > 0)
 		goto out_free;
@@ -1211,10 +1164,8 @@
 		   category, name ? : "(?)");
 
 out_free:
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
 	lock_acquired(dep_map, _RET_IP_);
 	mutex_release(dep_map, _RET_IP_);
-#endif
 
 	kfree(name);
 
@@ -1571,7 +1522,7 @@
 		"\n"
 		"where parameters are one or more "
 		"param_name=value pairs separated by ';'\n"
-		"\nThe following parameters available: read_only.\n";
+		"\nThe following parameters available: read_only\n";
 
 	return sprintf(buf, "%s", help);
 }
@@ -1960,10 +1911,7 @@
 {
 	int res;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
-	res = cpumask_scnprintf(buf, SCST_SYSFS_BLOCK_SIZE,
-		acg->acg_cpu_mask);
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
 	res = cpumask_scnprintf(buf, SCST_SYSFS_BLOCK_SIZE,
 		&acg->acg_cpu_mask);
 #else
@@ -2944,11 +2892,18 @@
 	&scst_tgt_none_cmd_count_attr.attr,
 	NULL,
 };
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
+ATTRIBUTE_GROUPS(scst_tgt);
+#endif
 
 static struct kobj_type tgt_ktype = {
 	.sysfs_ops	= &scst_sysfs_ops,
 	.release	= scst_tgt_release,
-	.default_attrs	= scst_tgt_attrs,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
+	.default_groups = scst_tgt_groups,
+#else
+	.default_attrs  = scst_tgt_attrs,
+#endif
 };
 
 /*
@@ -3778,6 +3733,9 @@
 	&dev_block_attr.attr,
 	NULL,
 };
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
+ATTRIBUTE_GROUPS(scst_dev);
+#endif
 
 static void scst_sysfs_dev_release(struct kobject *kobj)
 {
@@ -3905,7 +3863,11 @@
 static struct kobj_type scst_dev_ktype = {
 	.sysfs_ops = &scst_sysfs_ops,
 	.release = scst_sysfs_dev_release,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
+	.default_groups = scst_dev_groups,
+#else
 	.default_attrs = scst_dev_attrs,
+#endif
 };
 
 /*
@@ -4270,6 +4232,9 @@
 	&tgt_dev_active_commands_attr.attr,
 	NULL,
 };
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
+ATTRIBUTE_GROUPS(scst_tgt_dev);
+#endif
 
 static void scst_sysfs_tgt_dev_release(struct kobject *kobj)
 {
@@ -4288,7 +4253,11 @@
 static struct kobj_type scst_tgt_dev_ktype = {
 	.sysfs_ops = &scst_sysfs_ops,
 	.release = scst_sysfs_tgt_dev_release,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
+	.default_groups = scst_tgt_dev_groups,
+#else
 	.default_attrs = scst_tgt_dev_attrs,
+#endif
 };
 
 int scst_tgt_dev_sysfs_create(struct scst_tgt_dev *tgt_dev)
@@ -4380,7 +4349,7 @@
 #else
 	uint64_t sum = 0, sumsq = 0;
 #endif
-	unsigned count = 0, numst = 0;
+	unsigned int count = 0, numst = 0;
 	u64 d_min_div_10, d_max_div_10, avg_div_10, stddev_div_10;
 	u32 d_min_mod_10, d_max_mod_10, avg_mod_10, stddev_mod_10;
 	char state_name[32];
@@ -4841,6 +4810,9 @@
 	&session_none_cmd_count_attr.attr,
 	NULL,
 };
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
+ATTRIBUTE_GROUPS(scst_session);
+#endif
 
 static void scst_sysfs_session_release(struct kobject *kobj)
 {
@@ -4859,7 +4831,11 @@
 static struct kobj_type scst_session_ktype = {
 	.sysfs_ops = &scst_sysfs_ops,
 	.release = scst_sysfs_session_release,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
+	.default_groups = scst_session_groups,
+#else
 	.default_attrs = scst_session_attrs,
+#endif
 };
 
 #define SCST_LAT_ATTRS(size)		\
@@ -5095,15 +5071,22 @@
 static struct kobj_attribute lun_options_attr =
 	__ATTR(read_only, S_IRUGO, scst_lun_rd_only_show, NULL);
 
-static struct attribute *lun_attrs[] = {
+static struct attribute *acg_dev_attrs[] = {
 	&lun_options_attr.attr,
 	NULL,
 };
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
+ATTRIBUTE_GROUPS(acg_dev);
+#endif
 
 static struct kobj_type acg_dev_ktype = {
 	.sysfs_ops = &scst_sysfs_ops,
 	.release = scst_acg_dev_release,
-	.default_attrs = lun_attrs,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
+	.default_groups = acg_dev_groups,
+#else
+	.default_attrs = acg_dev_attrs,
+#endif
 };
 
 /*
@@ -5663,11 +5646,9 @@
 	int res = 0;
 	struct scst_acg *acg = acn->acg;
 	struct kobj_attribute *attr = NULL;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 	static struct lock_class_key __key;
 #endif
-#endif
 
 	TRACE_ENTRY();
 
@@ -5689,14 +5670,9 @@
 		goto out_free;
 	}
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36)
-	attr->attr.owner = THIS_MODULE;
-#endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34)
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 	attr->attr.key = &__key;
 #endif
-#endif
 
 	attr->attr.mode = S_IRUGO;
 	attr->show = scst_acn_file_show;
@@ -5819,15 +5795,22 @@
 static struct kobj_attribute scst_devt_type_attr =
 	__ATTR(type, S_IRUGO, scst_devt_type_show, NULL);
 
-static struct attribute *scst_devt_default_attrs[] = {
+static struct attribute *scst_devt_def_attrs[] = {
 	&scst_devt_type_attr.attr,
 	NULL,
 };
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
+ATTRIBUTE_GROUPS(scst_devt_def);
+#endif
 
 static struct kobj_type scst_devt_ktype = {
 	.sysfs_ops = &scst_sysfs_ops,
 	.release = scst_devt_release,
-	.default_attrs = scst_devt_default_attrs,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
+	.default_groups = scst_devt_def_groups,
+#else
+	.default_attrs = scst_devt_def_attrs,
+#endif
 };
 
 static char *scst_dev_params(struct scst_dev_type *devt)
@@ -7261,8 +7244,6 @@
 	__ATTR(max_tasklet_cmd, S_IRUGO | S_IWUSR, scst_max_tasklet_cmd_show,
 	       scst_max_tasklet_cmd_store);
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
-
 static ssize_t scst_poll_us_show(struct kobject *kobj,
 				  struct kobj_attribute *attr, char *buf)
 {
@@ -7310,8 +7291,6 @@
 	__ATTR(poll_us, S_IRUGO | S_IWUSR, scst_poll_us_show,
 	       scst_poll_us_store);
 
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0) */
-
 static ssize_t scst_suspend_show(struct kobject *kobj,
 				 struct kobj_attribute *attr, char *buf)
 {
@@ -7614,14 +7593,12 @@
 	__ATTR(last_sysfs_mgmt_res, S_IRUGO,
 		scst_last_sysfs_mgmt_res_show, NULL);
 
-static struct attribute *scst_sysfs_root_default_attrs[] = {
+static struct attribute *scst_sysfs_root_def_attrs[] = {
 	&scst_measure_latency_attr.attr,
 	&scst_threads_attr.attr,
 	&scst_setup_id_attr.attr,
 	&scst_max_tasklet_cmd_attr.attr,
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
 	&scst_poll_us_attr.attr,
-#endif
 	&scst_suspend_attr.attr,
 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
 	&scst_main_trace_level_attr.attr,
@@ -7633,6 +7610,9 @@
 	&scst_last_sysfs_mgmt_res_attr.attr,
 	NULL,
 };
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
+ATTRIBUTE_GROUPS(scst_sysfs_root_def);
+#endif
 
 static void scst_sysfs_root_release(struct kobject *kobj)
 {
@@ -7642,7 +7622,11 @@
 static struct kobj_type scst_sysfs_root_ktype = {
 	.sysfs_ops = &scst_sysfs_ops,
 	.release = scst_sysfs_root_release,
-	.default_attrs = scst_sysfs_root_default_attrs,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
+	.default_groups = scst_sysfs_root_def_groups,
+#else
+	.default_attrs = scst_sysfs_root_def_attrs,
+#endif
 };
 
 /*
diff --git a/scst/scst/src/scst_targ.c b/scst/scst/src/scst_targ.c
index c9c3255..d57a706 100644
--- a/scst/scst/src/scst_targ.c
+++ b/scst/scst/src/scst_targ.c
@@ -59,7 +59,16 @@
 
 	i = &scst_percpu_infos[smp_processor_id()];
 
-	if (atomic_read(&i->cpu_cmd_count) <= scst_max_tasklet_cmd) {
+	/*
+	 * Commands are removed from the list they are on before being
+	 * processed. If both lists are empty that means that at most two
+	 * commands are being processed and hence that processing a
+	 * command in tasklet context is possible without making a CPU core
+	 * spend all its time in interrupt and tasklet context and thereby
+	 * starving threads scheduled on the same CPU core.
+	 */
+	if (list_empty_careful(&i->tasklet_cmd_list) &&
+	    list_empty_careful(&cmd->cmd_threads->active_cmd_list)) {
 		spin_lock_irqsave(&i->tasklet_lock, flags);
 		TRACE_DBG("Adding cmd %p to tasklet %d cmd list", cmd,
 			smp_processor_id());
@@ -69,8 +78,8 @@
 		tasklet_schedule(&i->tasklet);
 	} else {
 		spin_lock_irqsave(&cmd->cmd_threads->cmd_list_lock, flags);
-		TRACE_DBG("Too many tasklet commands (%d), adding cmd %p to "
-			"active cmd list", atomic_read(&i->cpu_cmd_count), cmd);
+		TRACE_DBG("Too many tasklet commands, adding cmd %p to active cmd list",
+			  cmd);
 		list_add_tail(&cmd->cmd_list_entry,
 			&cmd->cmd_threads->active_cmd_list);
 		wake_up(&cmd->cmd_threads->cmd_list_waitQ);
@@ -565,7 +574,7 @@
 {
 	TRACE_ENTRY();
 
-	WARN_ON_ONCE(cmd->cpu_cmd_counter);
+	WARN_ON_ONCE(cmd->counted);
 
 	cmd->sess = sess;
 	scst_sess_get(sess);
@@ -2613,14 +2622,7 @@
 		  scsi_dev->host->host_no, scsi_dev->channel, scsi_dev->id,
 		  (u64)scsi_dev->lun);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
-	rc = scst_exec_req(scsi_dev, cmd->cdb, cmd->cdb_len,
-			cmd->data_direction, cmd->sg, cmd->bufflen,
-			cmd->sg_cnt, cmd->timeout, cmd->retries, cmd,
-			scst_pass_through_cmd_done, cmd->cmd_gfp_mask);
-#else
 	rc = scst_scsi_exec_async(cmd, cmd, scst_pass_through_cmd_done);
-#endif
 	if (unlikely(rc != 0)) {
 		PRINT_ERROR("scst pass-through exec failed: %d", rc);
 		/* "Sectors" are hardcoded as 512 bytes in the kernel */
@@ -2671,11 +2673,6 @@
 	res = scst_do_real_exec(cmd);
 	if (likely(res == SCST_EXEC_COMPLETED)) {
 		scst_post_exec_sn(cmd, true);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
-		if (cmd->dev->scsi_dev != NULL)
-			generic_unplug_device(
-				cmd->dev->scsi_dev->request_queue);
-#endif
 	} else
 		sBUG();
 
@@ -2924,11 +2921,6 @@
 
 	*active_cmd = cmd;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 39)
-	if (ref_cmd->dev->scsi_dev != NULL)
-		generic_unplug_device(ref_cmd->dev->scsi_dev->request_queue);
-#endif
-
 	__scst_cmd_put(ref_cmd);
 	/* !! At this point sess, dev and tgt_dev can be already freed !! */
 
@@ -3672,11 +3664,11 @@
 		/*
 		 * Those counters protect from not getting too long processing
 		 * latency, so we should decrement them after cmd completed.
+		 *
+		 * @cmd processing for SCST device is complete.
 		 */
-		smp_mb__before_atomic_dec();
 		WARN_ON_ONCE(!cmd->owns_refcnt);
 		cmd->owns_refcnt = false;
-		atomic_dec(&cmd->tgt_dev->tgt_dev_cmd_count);
 		percpu_ref_put(&cmd->dev->refcnt);
 #ifdef CONFIG_SCST_PER_DEVICE_CMD_COUNT_LIMIT
 		atomic_dec(&cmd->dev->dev_cmd_count);
@@ -3883,6 +3875,22 @@
 		}
 	}
 
+	if (likely(cmd->tgt_dev != NULL)) {
+		/*
+		 * We must decrement @tgt_dev->tgt_dev_cmd_count
+		 * after scst_tgt_cmd_done() was called. Otherwise,
+		 * this may lead to a race condition between
+		 * scst_acg_repl_lun() and scst_tgt_cmd_done()'s
+		 * cmd processing.
+		 *
+		 * See also https://github.com/SCST-project/scst/pull/27
+		 *
+		 * @cmd processing for target device is complete.
+		 */
+		smp_mb__before_atomic_dec();
+		atomic_dec(&cmd->tgt_dev->tgt_dev_cmd_count);
+	}
+
 	atomic_dec(&sess->sess_cmd_count);
 
 	spin_lock_irq(&sess->sess_list_lock);
@@ -4128,14 +4136,12 @@
 	struct list_head *head;
 	struct scst_tgt_dev *tgt_dev;
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
 #if defined(CONFIG_SCST_EXTRACHECKS) && defined(CONFIG_PREEMPT_RCU) && \
 	defined(CONFIG_DEBUG_LOCK_ALLOC)
 	WARN_ON_ONCE(debug_locks &&
 		     !lockdep_is_held(&sess->tgt_dev_list_mutex) &&
 		     rcu_preempt_depth() == 0);
 #endif
-#endif
 
 	head = &sess->sess_tgt_dev_list[SESS_TGT_DEV_LIST_HASH_FN(lun)];
 	list_for_each_entry_rcu(tgt_dev, head, sess_tgt_dev_list_entry) {
@@ -4150,7 +4156,7 @@
  * scst_translate_lun() - Translate @cmd->lun into a tgt_dev pointer.
  * @cmd: SCSI command for which to translate the LUN number.
  *
- * Initialize the following @cmd members: cpu_cmd_counter, cmd_threads,
+ * Initialize the following @cmd members: counted, cmd_threads,
  * tgt_dev, cur_order_data, dev and devt.
  *
  * The caller must not hold any locks. May be called from IRQ context. The data
@@ -4168,9 +4174,7 @@
 
 	TRACE_ENTRY();
 
-	cmd->cpu_cmd_counter = scst_get();
-
-	if (likely(!test_bit(SCST_FLAG_SUSPENDED, &scst_flags))) {
+	if (likely(scst_get_cmd(cmd))) {
 		TRACE_DBG("Finding tgt_dev for cmd %p (lun %lld)", cmd,
 			(unsigned long long)cmd->lun);
 		res = -1;
@@ -4213,12 +4217,9 @@
 					cmd->sess->initiator_name, cmd->tgt->tgt_name);
 				scst_event_queue_lun_not_found(cmd);
 			}
-			scst_put(cmd->cpu_cmd_counter);
-			cmd->cpu_cmd_counter = NULL;
+			scst_put_cmd(cmd);
 		}
 	} else {
-		scst_put(cmd->cpu_cmd_counter);
-		cmd->cpu_cmd_counter = NULL;
 		TRACE_MGMT_DBG("%s", "FLAG SUSPENDED set, skipping");
 		res = 1;
 	}
@@ -4333,7 +4334,7 @@
 
 		scst_set_cmd_state(cmd, SCST_CMD_STATE_PARSE);
 
-		cnt = atomic_read(&tgt_dev->tgt_dev_cmd_count) - 1;
+		cnt = atomic_read(&tgt_dev->tgt_dev_cmd_count);
 		if (unlikely(cnt > dev->max_tgt_dev_commands)) {
 			TRACE(TRACE_FLOW_CONTROL,
 				"Too many pending commands (%d) in "
@@ -4344,8 +4345,7 @@
 		}
 
 #ifdef CONFIG_SCST_PER_DEVICE_CMD_COUNT_LIMIT
-		atomic_inc(&dev->dev_cmd_count);
-		cnt = atomic_read(&dev->dev_cmd_count);
+		cnt = atomic_inc_return(&dev->dev_cmd_count);
 		if (unlikely(cnt > SCST_MAX_DEV_COMMANDS)) {
 			if (!failure) {
 				TRACE(TRACE_FLOW_CONTROL,
@@ -4427,7 +4427,8 @@
 	 * There is no need for read barrier here, because we don't care where
 	 * this check will be done.
 	 */
-	susp = test_bit(SCST_FLAG_SUSPENDED, &scst_flags);
+	susp = scst_activity_suspended();
+
 	if (scst_init_poll_cnt > 0)
 		scst_init_poll_cnt--;
 
@@ -4488,13 +4489,13 @@
 	return;
 }
 
-static inline int test_init_cmd_list(void)
+/* Whether or not scst_init_thread() should stop waiting. */
+static inline bool test_init_cmd_list(void)
 {
-	int res = (!list_empty(&scst_init_cmd_list) &&
-		   !test_bit(SCST_FLAG_SUSPENDED, &scst_flags)) ||
-		  unlikely(kthread_should_stop()) ||
-		  (scst_init_poll_cnt > 0);
-	return res;
+	return (!list_empty(&scst_init_cmd_list) &&
+		!scst_activity_suspended()) ||
+		unlikely(kthread_should_stop()) ||
+		(scst_init_poll_cnt > 0);
 }
 
 int scst_init_thread(void *arg)
@@ -4542,7 +4543,6 @@
  */
 static void scst_ioctx_get(struct scst_cmd_threads *p_cmd_threads)
 {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
 	mutex_lock(&p_cmd_threads->io_context_mutex);
 
 	WARN_ON(current->io_context);
@@ -4564,31 +4564,16 @@
 			 */
 			put_io_context(p_cmd_threads->io_context);
 		} else {
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) && (LINUX_VERSION_CODE < KERNEL_VERSION(3, 6, 0)))
-#warning IO context sharing functionality disabled on 3.5 kernels due to bug in them. \
-See "http://lkml.org/lkml/2012/7/17/515" for more details.
-			static int q;
-
-			if (q == 0) {
-				q++;
-				PRINT_WARNING("IO context sharing functionality "
-					"disabled on 3.5 kernels due to bug in "
-					"them. See http://lkml.org/lkml/2012/7/17/515 "
-					"for more details.");
-			}
-#else
 			ioc_task_link(p_cmd_threads->io_context);
 			current->io_context = p_cmd_threads->io_context;
 			TRACE_DBG("Linked IO context %p "
 				"(p_cmd_threads %p)", p_cmd_threads->io_context,
 				p_cmd_threads);
-#endif
 		}
 		p_cmd_threads->io_context_refcnt++;
 	}
 
 	mutex_unlock(&p_cmd_threads->io_context_mutex);
-#endif
 
 	smp_wmb();
 	p_cmd_threads->io_context_ready = true;
@@ -4600,14 +4585,12 @@
  */
 static void scst_ioctx_put(struct scst_cmd_threads *p_cmd_threads)
 {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
 	if (p_cmd_threads != &scst_main_cmd_threads) {
 		mutex_lock(&p_cmd_threads->io_context_mutex);
 		if (--p_cmd_threads->io_context_refcnt == 0)
 			p_cmd_threads->io_context = NULL;
 		mutex_unlock(&p_cmd_threads->io_context_mutex);
 	}
-#endif
 	return;
 }
 
@@ -4953,10 +4936,9 @@
 
 				if (++thr_cnt == 2)
 					break;
-				else {
-					spin_lock_irq(&thr->thr_cmd_list_lock);
-					thr_locked = true;
-				}
+
+				spin_lock_irq(&thr->thr_cmd_list_lock);
+				thr_locked = true;
 			}
 		} while (someth_done);
 
@@ -4967,7 +4949,6 @@
 			thr_locked = false;
 		}
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 9, 0)
 		if (scst_poll_ns > 0) {
 			ktime_t end, kt;
 
@@ -4985,7 +4966,6 @@
 				kt = ktime_get();
 			} while (ktime_before(kt, end));
 		}
-#endif
 		spin_lock_irq(&p_cmd_threads->cmd_list_lock);
 		spin_lock(&thr->thr_cmd_list_lock);
 	}
@@ -5015,9 +4995,8 @@
 }
 
 /*
- * Returns 0 on success, or > 0 if SCST_FLAG_SUSPENDED set and
- * SCST_FLAG_SUSPENDING - not. No locks, protection is done by the
- * suspended activity.
+ * Returns 0 on success, or > 0 upon failure. No locks, protection is done by
+ * suspending activity.
  */
 static int scst_get_mgmt(struct scst_mgmt_cmd *mcmd)
 {
@@ -5025,12 +5004,7 @@
 
 	TRACE_ENTRY();
 
-	mcmd->cpu_cmd_counter = scst_get();
-
-	if (unlikely(test_bit(SCST_FLAG_SUSPENDED, &scst_flags) &&
-		     !test_bit(SCST_FLAG_SUSPENDING, &scst_flags))) {
-		scst_put(mcmd->cpu_cmd_counter);
-		mcmd->cpu_cmd_counter = NULL;
+	if (unlikely(!scst_get_mcmd(mcmd))) {
 		TRACE_MGMT_DBG("%s", "FLAG SUSPENDED set, skipping");
 		res = 1;
 		goto out;
@@ -5042,9 +5016,8 @@
 }
 
 /*
- * Returns 0 on success, < 0 if there is no device handler or
- * > 0 if SCST_FLAG_SUSPENDED set and SCST_FLAG_SUSPENDING - not.
- * No locks, protection is done by the suspended activity.
+ * Returns 0 on success, < 0 if there is no device handler or > 0 if activity
+ * has been suspended. No locks, protection is done by the suspended activity.
  */
 static int scst_mgmt_translate_lun(struct scst_mgmt_cmd *mcmd)
 {
@@ -5069,8 +5042,7 @@
 		mcmd->mcmd_tgt_dev = tgt_dev;
 		res = 0;
 	} else {
-		scst_put(mcmd->cpu_cmd_counter);
-		mcmd->cpu_cmd_counter = NULL;
+		scst_put_mcmd(mcmd);
 		res = -1;
 	}
 
@@ -5504,7 +5476,7 @@
 /* No locks. Returns 0, if mcmd should be processed further. */
 static int scst_set_mcmd_next_state(struct scst_mgmt_cmd *mcmd)
 {
-	int res;
+	int res = 0;
 
 	spin_lock_irq(&scst_mcmd_lock);
 
@@ -5513,7 +5485,6 @@
 	case SCST_MCMD_STATE_EXEC:
 		if (mcmd->cmd_done_wait_count == 0) {
 			mcmd->state = SCST_MCMD_STATE_AFFECTED_CMDS_DONE;
-			res = 0;
 		} else {
 			TRACE(TRACE_SCSI|TRACE_MGMT_DEBUG,
 				"cmd_done_wait_count(%d) not 0, "
@@ -5526,7 +5497,6 @@
 	case SCST_MCMD_STATE_AFFECTED_CMDS_DONE:
 		if (mcmd->cmd_finish_wait_count == 0) {
 			mcmd->state = SCST_MCMD_STATE_DONE;
-			res = 0;
 		} else {
 			TRACE(TRACE_SCSI|TRACE_MGMT_DEBUG,
 				"cmd_finish_wait_count(%d) not 0, "
@@ -5539,7 +5509,6 @@
 
 	case SCST_MCMD_STATE_DONE:
 		mcmd->state = SCST_MCMD_STATE_FINISHED;
-		res = 0;
 		break;
 
 	default:
@@ -5902,10 +5871,14 @@
 			res = scst_set_mcmd_next_state(mcmd);
 			goto out;
 		}
-		__scst_cmd_get(cmd);
 		tgt_dev = cmd->tgt_dev;
-		if (tgt_dev != NULL)
-			mcmd->cpu_cmd_counter = scst_get();
+		if (tgt_dev && !scst_get_mcmd(mcmd)) {
+			TRACE_MGMT_DBG("Suspended; skipping mcmd");
+			spin_unlock_irq(&sess->sess_list_lock);
+			res = 1;
+			goto ret;
+		}
+		__scst_cmd_get(cmd);
 		spin_unlock_irq(&sess->sess_list_lock);
 		TRACE_DBG("Cmd to abort %p for tag %llu found (tgt_dev %p)",
 			cmd, (unsigned long long)mcmd->tag, tgt_dev);
@@ -5963,6 +5936,7 @@
 out:
 	scst_event_queue_tm_fn_received(mcmd);
 
+ret:
 	TRACE_EXIT_RES(res);
 	return res;
 }
@@ -5977,10 +5951,8 @@
 	int arg = SG_SCSI_RESET_TARGET;
 
 	return scsi_ioctl_reset(sdev, (__force __user int *)&arg);
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
-	return scsi_reset_provider(sdev, SCSI_TRY_RESET_TARGET);
 #else
-	return scsi_reset_provider(sdev, SCSI_TRY_RESET_BUS);
+	return scsi_reset_provider(sdev, SCSI_TRY_RESET_TARGET);
 #endif
 }
 
@@ -6522,21 +6494,21 @@
 
 	case SCST_CLEAR_ACA:
 		res = scst_clear_aca_mcmd(mcmd);
-		goto out_done;
+		if (unlikely(res))
+			break;
+
+		res = scst_set_mcmd_next_state(mcmd);
+		break;
 
 	default:
 		PRINT_ERROR("Unknown task management function %d", mcmd->fn);
 		scst_mgmt_cmd_set_status(mcmd, SCST_MGMT_STATUS_REJECTED);
-		goto out_done;
+		res = scst_set_mcmd_next_state(mcmd);
+		break;
 	}
 
-out:
 	TRACE_EXIT_RES(res);
 	return res;
-
-out_done:
-	res = scst_set_mcmd_next_state(mcmd);
-	goto out;
 }
 
 static void scst_call_task_mgmt_affected_cmds_done(struct scst_mgmt_cmd *mcmd)
@@ -6842,9 +6814,7 @@
 			rc = scst_process_mgmt_cmd(mcmd);
 			spin_lock_irq(&scst_mcmd_lock);
 			if (rc > 0) {
-				if (test_bit(SCST_FLAG_SUSPENDED, &scst_flags) &&
-				    !test_bit(SCST_FLAG_SUSPENDING,
-						&scst_flags)) {
+				if (scst_mcmd_suspended()) {
 					TRACE_MGMT_DBG("Adding mgmt cmd %p to "
 						"head of delayed mgmt cmd list",
 						mcmd);
diff --git a/scst/scst/src/scst_tg.c b/scst/scst/src/scst_tg.c
index 9a2ccc0..72feb67 100644
--- a/scst/scst/src/scst_tg.c
+++ b/scst/scst/src/scst_tg.c
@@ -50,15 +50,10 @@
 static DEFINE_MUTEX(scst_dg_mutex);
 static LIST_HEAD(scst_dev_group_list);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) || \
-	defined(RHEL_MAJOR) && RHEL_MAJOR -0 <= 5
-static int alua_invariant_check;
-#else
 static bool alua_invariant_check;
-#endif
 module_param(alua_invariant_check, bool, 0644);
 MODULE_PARM_DESC(alua_invariant_check,
-		 "Enables a run-time ALUA state invariant check.");
+		 "Enables a run-time ALUA state invariant check. (default: false)");
 
 /* Global SCST ALUA lock/unlock functions (scst_dg_mutex) */
 void scst_alua_lock(void)
@@ -181,19 +176,29 @@
 	return NULL;
 }
 
+static bool __scst_tg_have_tgt(struct scst_target_group *tg,
+			       const struct scst_tgt *tgt)
+{
+	struct scst_tg_tgt *tg_tgt;
+
+	list_for_each_entry(tg_tgt, &tg->tgt_list, entry)
+		if (tg_tgt->tgt == tgt)
+			return true;
+
+	return false;
+}
+
 /* Look up a target group by target port. */
 static struct scst_target_group *__lookup_tg_by_tgt(struct scst_dev_group *dg,
 						    const struct scst_tgt *tgt)
 {
 	struct scst_target_group *tg;
-	struct scst_tg_tgt *tg_tgt;
 
 	lockdep_assert_held(&scst_dg_mutex);
 
 	list_for_each_entry(tg, &dg->tg_list, entry)
-		list_for_each_entry(tg_tgt, &tg->tgt_list, entry)
-			if (tg_tgt->tgt == tgt)
-				return tg;
+		if (__scst_tg_have_tgt(tg, tgt))
+			return tg;
 
 	return NULL;
 }
@@ -404,24 +409,14 @@
 
 struct scst_alua_retry {
 	struct scst_cmd *alua_retry_cmd;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	struct work_struct alua_retry_work;
-#else
 	struct delayed_work alua_retry_work;
-#endif
 };
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void scst_alua_transitioning_work_fn(void *p)
-{
-	struct scst_alua_retry *retry = p;
-#else
 static void scst_alua_transitioning_work_fn(struct work_struct *work)
 {
 	struct scst_alua_retry *retry =
 		container_of(work, struct scst_alua_retry,
 			     alua_retry_work.work);
-#endif
 	struct scst_cmd *cmd = retry->alua_retry_cmd;
 
 	TRACE_ENTRY();
@@ -493,13 +488,8 @@
 
 		/* No get is needed, because cmd is sync here */
 		retry->alua_retry_cmd = cmd;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-		INIT_WORK(&retry->alua_retry_work,
-			  scst_alua_transitioning_work_fn, retry);
-#else
 		INIT_DELAYED_WORK(&retry->alua_retry_work,
 				  scst_alua_transitioning_work_fn);
-#endif
 		cmd->already_transitioning = 1;
 		schedule_delayed_work(&retry->alua_retry_work, HZ/2);
 		res = SCST_ALUA_CHECK_DELAYED;
@@ -679,12 +669,7 @@
 	if (!tg_tgt)
 		goto out;
 	tg_tgt->tg = tg;
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 24)
 	kobject_init(&tg_tgt->kobj, &scst_tg_tgt_ktype);
-#else
-	kobject_init(&tg_tgt->kobj);
-	tg_tgt->kobj.ktype = &scst_tg_tgt_ktype;
-#endif
 	tg_tgt->name = kstrdup(name, GFP_KERNEL);
 	if (!tg_tgt->name)
 		goto out_put;
@@ -809,12 +794,7 @@
 	tg = kzalloc(sizeof(*tg), GFP_KERNEL);
 	if (!tg)
 		goto out;
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 24)
 	kobject_init(&tg->kobj, &scst_tg_ktype);
-#else
-	kobject_init(&tg->kobj);
-	tg->kobj.ktype = &scst_tg_ktype;
-#endif
 	tg->name = kstrdup(name, GFP_KERNEL);
 	if (!tg->name)
 		goto out_put;
@@ -1009,9 +989,9 @@
 	struct scst_dg_dev *dg_dev;
 	struct scst_device *dev;
 	struct scst_tgt_dev *tgt_dev;
-	struct scst_tg_tgt *tg_tgt;
 	struct scst_tgt *tgt;
 	bool invoke_callbacks;
+	bool tg_is_remote;
 
 	sBUG_ON(state >= ARRAY_SIZE(scst_alua_filter));
 	lockdep_assert_held(&scst_dg_mutex);
@@ -1019,21 +999,53 @@
 	if (tg->state == state)
 		return;
 
+	/*
+	 * If the target group has a target with NULL target device,
+	 * that means that this target is remote one, so we shouldn't
+	 * call on_alua_state_change_*() callbacks then.
+	 *
+	 * See also 29548a4a ("scst: Remove the on_alua_state_change_*()
+	 * callback functions") and d333ce82 ("Restore the
+	 * on_alua_state_change_*() callback functions").
+	 */
+	tg_is_remote = __scst_tg_have_tgt(tg, NULL);
+
 	list_for_each_entry(dg_dev, &tg->dg->dev_list, entry) {
 		invoke_callbacks = true;
 		dev = dg_dev->dev;
+
 		list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
 				    dev_tgt_dev_list_entry) {
 			tgt = tgt_dev->sess->tgt;
-			list_for_each_entry(tg_tgt, &tg->tgt_list, entry) {
-				if (tg_tgt->tgt == tgt) {
-					__scst_tgt_set_state(tg, tgt_dev, state,
-							     invoke_callbacks);
-					invoke_callbacks = false;
-					break;
-				}
+
+			if (__scst_tg_have_tgt(tg, tgt)) {
+				__scst_tgt_set_state(tg, tgt_dev, state,
+						     invoke_callbacks);
+				invoke_callbacks = false;
 			}
 		}
+
+		/*
+		 * There are several cases when `invoke_callbacks` can
+		 * still be true here:
+		 * - The SCST device still doesn't have any target
+		 *   devices, or have only those that aren't included
+		 *   in the given target group (e.g. the default copy
+		 *   manager for a blockio devices).
+		 * - Target group has remote targets.
+		 *
+		 * We should call on_alua_state_chage_*() callbacks only
+		 * in the first case.
+		 *
+		 * See also https://github.com/SCST-project/scst/issues/55.
+		 */
+		if (invoke_callbacks && !tg_is_remote) {
+			if (dev->handler->on_alua_state_change_start)
+				dev->handler->on_alua_state_change_start(dev, tg->state, state);
+
+			if (dev->handler->on_alua_state_change_finish)
+				dev->handler->on_alua_state_change_finish(dev, tg->state, state);
+		}
 	}
 
 	tg->state = state;
@@ -1077,7 +1089,6 @@
 	struct scst_dg_dev *dg_dev;
 	struct scst_device *dev;
 	struct scst_tgt_dev *tgt_dev;
-	struct scst_tg_tgt *tg_tgt;
 	struct scst_tgt *tgt;
 
 	lockdep_assert_held(&scst_dg_mutex);
@@ -1087,13 +1098,9 @@
 		list_for_each_entry(tgt_dev, &dev->dev_tgt_dev_list,
 				    dev_tgt_dev_list_entry) {
 			tgt = tgt_dev->sess->tgt;
-			list_for_each_entry(tg_tgt, &tg->tgt_list, entry) {
-				if (tg_tgt->tgt == tgt) {
-					scst_gen_aen_or_ua(tgt_dev,
-			SCST_LOAD_SENSE(scst_sense_asym_access_state_changed));
-					break;
-				}
-			}
+			if (__scst_tg_have_tgt(tg, tgt))
+				scst_gen_aen_or_ua(tgt_dev,
+					SCST_LOAD_SENSE(scst_sense_asym_access_state_changed));
 		}
 	}
 }
@@ -1316,12 +1323,7 @@
 	dg = kzalloc(sizeof(*dg), GFP_KERNEL);
 	if (!dg)
 		goto out;
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 24)
 	kobject_init(&dg->kobj, &scst_dg_ktype);
-#else
-	kobject_init(&dg->kobj);
-	dg->kobj.ktype = &scst_dg_ktype;
-#endif
 	dg->name = kstrdup(name, GFP_KERNEL);
 	if (!dg->name)
 		goto out_put;
diff --git a/scst/scst_local/Makefile b/scst/scst_local/Makefile
index e00aae2..4e7626f 100644
--- a/scst/scst_local/Makefile
+++ b/scst/scst_local/Makefile
@@ -43,7 +43,6 @@
 	  $(shell [ -n "$(PASS_CC_TO_MAKE)" ] && echo CC="$(CC)")	\
 	  $$([ -n "$(DEPMOD)" ] && echo "DEPMOD=$(DEPMOD)")		\
 	  CONFIG_MODULE_SIG_ALL= modules_install
-	chmod u+x $(INSTALL_DIR)/*.ko
 
 uninstall:
 	rm -f $(INSTALL_DIR)/scst_local.ko
diff --git a/scst/scst_local/scst_local.c b/scst/scst_local/scst_local.c
index 79ca63a..e9a0390 100644
--- a/scst/scst_local/scst_local.c
+++ b/scst/scst_local/scst_local.c
@@ -46,10 +46,6 @@
 #include <scst_debug.h>
 #endif
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25))
-#define SG_MAX_SINGLE_ALLOC	(PAGE_SIZE / sizeof(struct scatterlist))
-#endif
-
 #ifndef INSIDE_KERNEL_TREE
 #if defined(CONFIG_HIGHMEM4G) || defined(CONFIG_HIGHMEM64G)
 #warning HIGHMEM kernel configurations are not supported by this module, \
@@ -75,21 +71,7 @@
 static unsigned long scst_local_trace_flag = SCST_LOCAL_DEFAULT_LOG_FLAGS;
 #endif
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19))
-/*
- * Provide some local definitions that are not provided for some earlier
- * kernels so we operate over a wider range of kernels
- *
- * Some time before 2.6.24 scsi_sg_count, scsi_sglist and scsi_bufflen were
- * not available. Make it available for 2.6.18 which is used still on some
- * distros, like CentOS etc.
- */
-#define scsi_sg_count(cmd) ((cmd)->use_sg)
-#define scsi_sglist(cmd) ((struct scatterlist *)(cmd)->request_buffer)
-#define scsi_bufflen(cmd) ((cmd)->request_bufflen)
-#endif
-
-#define SCST_LOCAL_VERSION "3.5.0"
+#define SCST_LOCAL_VERSION "3.7.0"
 static const char *scst_local_version_date = "20110901";
 
 /* Some statistics */
@@ -97,12 +79,7 @@
 static atomic_t num_dev_resets = ATOMIC_INIT(0);
 static atomic_t num_target_resets = ATOMIC_INIT(0);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) \
-    || defined(RHEL_MAJOR) && RHEL_MAJOR -0 <= 5
-static int scst_local_add_default_tgt = true;
-#else
 static bool scst_local_add_default_tgt = true;
-#endif
 module_param_named(add_default_tgt, scst_local_add_default_tgt, bool, S_IRUGO);
 MODULE_PARM_DESC(add_default_tgt, "add (default) or not on start default "
 	"target scst_local_tgt with default session scst_local_host");
@@ -163,31 +140,6 @@
 static void __scst_local_remove_target(struct scst_local_tgt *tgt);
 static void scst_local_remove_target(struct scst_local_tgt *tgt);
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25))
-
-/*
- * Maintains data that is needed during command processing ...
- * We have a single element scatterlist in here in case the scst_cmnd
- * we are given has a buffer, not a scatterlist, but we only need this for
- * kernels less than 2.6.25.
- */
-struct scst_local_tgt_specific {
-	struct scsi_cmnd *cmnd;
-	void (*done)(struct scsi_cmnd *);
-	struct scatterlist sgl;
-};
-
-/*
- * We use a pool of objects maintaind by the kernel so that it is less
- * likely to have to allocate them when we are in the data path.
- *
- * Note, we only need this for kernels in which we are likely to get non
- * scatterlist requests.
- */
-static struct kmem_cache *tgt_specific_pool;
-
-#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25)) */
-
 static atomic_t scst_local_sess_num = ATOMIC_INIT(0);
 
 static LIST_HEAD(scst_local_tgts_list);
@@ -483,6 +435,19 @@
  ** Session attributes
  **/
 
+static ssize_t host_no_show(struct kobject *kobj,
+	struct kobj_attribute *attr, char *buf)
+{
+	struct scst_session *scst_sess =
+		container_of(kobj, struct scst_session, sess_kobj);
+	struct scst_local_sess *sess = scst_sess_get_tgt_priv(scst_sess);
+	struct Scsi_Host *host = sess->shost;
+
+	return host ? snprintf(buf, PAGE_SIZE, "%u\n", host->host_no) : -EINVAL;
+}
+
+static struct kobj_attribute scst_local_host_no_attr = __ATTR_RO(host_no);
+
 static ssize_t scst_local_transport_id_show(struct kobject *kobj,
 	struct kobj_attribute *attr, char *buf)
 {
@@ -571,6 +536,7 @@
 		scst_local_transport_id_store);
 
 static const struct attribute *scst_local_sess_attrs[] = {
+	&scst_local_host_no_attr.attr,
 	&scst_local_transport_id_attr.attr,
 	NULL,
 };
@@ -749,7 +715,7 @@
 	sess = to_scst_lcl_sess(scsi_get_device(scmd->device->host));
 
 	ret = scst_rx_mgmt_fn_tag(sess->scst_sess, SCST_ABORT_TASK,
-				  blk_mq_unique_tag(scmd->request),
+				  blk_mq_unique_tag(scsi_cmd_to_rq(scmd)),
 				  false, &dev_reset_completion);
 
 	/* Now wait for the completion ... */
@@ -793,7 +759,6 @@
 	return ret;
 }
 
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25))
 static int scst_local_target_reset(struct scsi_cmnd *scmd)
 {
 	struct scst_local_sess *sess;
@@ -822,7 +787,6 @@
 	TRACE_EXIT_RES(ret);
 	return ret;
 }
-#endif
 
 static void scst_local_copy_sense(struct scsi_cmnd *cmnd, struct scst_cmd *scst_cmnd)
 {
@@ -874,19 +838,9 @@
  * This does the heavy lifting ... we pass all the commands on to the
  * target driver and have it do its magic ...
  */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
 static int scst_local_queuecommand(struct Scsi_Host *host,
 				   struct scsi_cmnd *scmd)
-#else
-static int scst_local_queuecommand_lck(struct scsi_cmnd *scmd,
-				       void (*done)(struct scsi_cmnd *))
-	__acquires(&h->host_lock)
-	__releases(&h->host_lock)
-#endif
 {
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25))
-	struct scst_local_tgt_specific *tgt_specific = NULL;
-#endif
 	struct scst_local_sess *sess;
 	struct scatterlist *sgl = NULL;
 	int sgl_count = 0;
@@ -899,39 +853,16 @@
 	TRACE_DBG("lun %lld, cmd: 0x%02X", (u64)scmd->device->lun,
 		  scmd->cmnd[0]);
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
-	/*
-	 * We save a pointer to the done routine in scmd->scsi_done and
-	 * we save that as tgt specific stuff below.
-	 */
-	scmd->scsi_done = done;
-#endif
-
 	sess = to_scst_lcl_sess(scsi_get_device(scmd->device->host));
 
 	if (sess->unregistering) {
 		scmd->result = DID_BAD_TARGET << 16;
-		scmd->scsi_done(scmd);
+		scsi_done(scmd);
 		return 0;
 	}
 
 	scsi_set_resid(scmd, 0);
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25))
-	/*
-	 * Allocate a tgt_specific_structure. We need this in case we need
-	 * to construct a single element SGL.
-	 */
-	tgt_specific = kmem_cache_alloc(tgt_specific_pool, GFP_ATOMIC);
-	if (!tgt_specific) {
-		PRINT_ERROR("Unable to create tgt_specific (size %zu)",
-			sizeof(*tgt_specific));
-		return SCSI_MLQUEUE_HOST_BUSY;
-	}
-	tgt_specific->cmnd = scmd;
-	tgt_specific->done = done;
-#endif
-
 	/*
 	 * Tell the target that we have a command ... but first we need
 	 * to get the LUN into a format that SCST understand
@@ -948,7 +879,7 @@
 		return SCSI_MLQUEUE_HOST_BUSY;
 	}
 
-	scst_cmd_set_tag(scst_cmd, blk_mq_unique_tag(scmd->request));
+	scst_cmd_set_tag(scst_cmd, blk_mq_unique_tag(scsi_cmd_to_rq(scmd)));
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
 	if (scmd->device->tagged_supported && scmd->device->simple_tags)
 		scst_cmd_set_queue_type(scst_cmd, SCST_CMD_QUEUE_SIMPLE);
@@ -972,43 +903,11 @@
 	}
 #endif
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25))
-	/*
-	 * If the command has a request, not a scatterlist, then convert it
-	 * to one. We use scsi_sg_count to isolate us from the changes from
-	 * version to version
-	 */
-	if (scsi_sg_count(scmd)) {
-		sgl = scsi_sglist(scmd);
-		sgl_count = scsi_sg_count(scmd);
-	} else {
-		/*
-		 * Build a one-element scatter list out of the buffer
-		 * We will not even get here if the kernel version we
-		 * are building on only supports scatterlists. See #if above.
-		 *
-		 * We use the sglist and bufflen function/macros to isolate
-		 * us from kernel version differences.
-		 */
-		if (scsi_sglist(scmd)) {
-			sg_init_one(&tgt_specific->sgl,
-				    scsi_sglist(scmd),
-				    scsi_bufflen(scmd));
-			sgl	  = &tgt_specific->sgl;
-			sgl_count = 1;
-		} else {
-			sgl = NULL;
-			sgl_count = 0;
-		}
-	}
-#else
 	sgl = scsi_sglist(scmd);
 	sgl_count = scsi_sg_count(scmd);
-#endif
 
 	if (scsi_bidi_cmnd(scmd)) {
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 24) &&	\
-	LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0) && \
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 1, 0) && 		\
 	(!defined(RHEL_RELEASE_CODE) ||				\
 	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(8, 3))
 		/* Some of these symbols are only defined after 2.6.24 */
@@ -1037,11 +936,7 @@
 	}
 
 	/* Save the correct thing below depending on version */
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25))
-	scst_cmd_set_tgt_priv(scst_cmd, tgt_specific);
-#else
 	scst_cmd_set_tgt_priv(scst_cmd, scmd);
-#endif
 
 	scst_cmd_init_done(scst_cmd, SCST_CONTEXT_THREAD);
 
@@ -1088,10 +983,7 @@
 	return scsi_change_queue_depth(sdev, depth);
 }
 
-#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) || \
-    defined(CONFIG_SUSE_KERNEL) || \
-    !(!defined(RHEL_RELEASE_CODE) || \
-     RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1))
+#else
 
 static int scst_local_change_queue_depth(struct scsi_device *sdev, int depth,
 	int reason)
@@ -1139,15 +1031,7 @@
 	return res;
 }
 
-#else /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) || defined(CONFIG_SUSE_KERNEL) || !(!defined(RHEL_RELEASE_CODE) || RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1)) */
-
-static int scst_local_change_queue_depth(struct scsi_device *sdev, int qdepth)
-{
-	scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
-	return sdev->queue_depth;
-}
-
-#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) || defined(CONFIG_SUSE_KERNEL) || !(!defined(RHEL_RELEASE_CODE) || RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1)) */
+#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) */
 
 static int scst_local_slave_alloc(struct scsi_device *sdev)
 {
@@ -1242,18 +1126,10 @@
 	return;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void scst_aen_work_fn(void *ctx)
-#else
 static void scst_aen_work_fn(struct work_struct *work)
-#endif
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	struct scst_local_sess *sess = ctx;
-#else
 	struct scst_local_sess *sess =
 		container_of(work, struct scst_local_sess, aen_work);
-#endif
 
 	TRACE_ENTRY();
 
@@ -1330,18 +1206,10 @@
 	return 0;
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-static void scst_remove_work_fn(void *ctx)
-#else
 static void scst_remove_work_fn(struct work_struct *work)
-#endif
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	struct scst_local_sess *sess = ctx;
-#else
 	struct scst_local_sess *sess =
 		container_of(work, struct scst_local_sess, remove_work);
-#endif
 
 	scst_local_remove_adapter(sess);
 }
@@ -1382,9 +1250,6 @@
 
 static int scst_local_targ_xmit_response(struct scst_cmd *scst_cmd)
 {
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25))
-	struct scst_local_tgt_specific *tgt_specific;
-#endif
 	struct scsi_cmnd *scmd = NULL;
 	void (*done)(struct scsi_cmnd *);
 
@@ -1400,13 +1265,11 @@
 	    (scst_cmd_get_data_direction(scst_cmd) & SCST_DATA_READ))
 		scst_copy_sg(scst_cmd, SCST_SG_COPY_TO_TARGET);
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25))
-	tgt_specific = scst_cmd_get_tgt_priv(scst_cmd);
-	scmd = tgt_specific->cmnd;
-	done = tgt_specific->done;
-#else
 	scmd = scst_cmd_get_tgt_priv(scst_cmd);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)
 	done = scmd->scsi_done;
+#else
+	done = scsi_done;
 #endif
 
 	/*
@@ -1441,21 +1304,6 @@
 	return SCST_TGT_RES_SUCCESS;
 }
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25))
-static void scst_local_targ_on_free_cmd(struct scst_cmd *scst_cmd)
-{
-	struct scst_local_tgt_specific *tgt_specific;
-
-	TRACE_ENTRY();
-
-	tgt_specific = scst_cmd_get_tgt_priv(scst_cmd);
-	kmem_cache_free(tgt_specific_pool, tgt_specific);
-
-	TRACE_EXIT();
-	return;
-}
-#endif
-
 static void scst_local_targ_task_mgmt_done(struct scst_mgmt_cmd *mgmt_cmd)
 {
 	struct completion *compl;
@@ -1499,11 +1347,7 @@
 
 static struct scst_tgt_template scst_local_targ_tmpl = {
 	.name			= "scst_local",
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
-	.sg_tablesize		= SG_MAX_SINGLE_ALLOC,
-#else
 	.sg_tablesize		= 0xffff,
-#endif
 	.xmit_response_atomic	= 1,
 	.multithreaded_init_done = 1,
 	.enabled_attr_not_needed = 1,
@@ -1520,9 +1364,6 @@
 	.close_session		= scst_local_close_session,
 	.pre_exec		= scst_local_targ_pre_exec,
 	.xmit_response		= scst_local_targ_xmit_response,
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25))
-	.on_free_cmd		= scst_local_targ_on_free_cmd,
-#endif
 	.task_mgmt_fn_done	= scst_local_targ_task_mgmt_done,
 	.report_aen		= scst_local_report_aen,
 	.get_initiator_port_transport_id = scst_local_get_initiator_port_transport_id,
@@ -1536,27 +1377,17 @@
 
 static struct scsi_host_template scst_lcl_ini_driver_template = {
 	.name				= SCST_LOCAL_NAME,
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 37)
-	.queuecommand			= scst_local_queuecommand_lck,
-#else
 	.queuecommand			= scst_local_queuecommand,
-#endif
 	.change_queue_depth		= scst_local_change_queue_depth,
 	.slave_alloc			= scst_local_slave_alloc,
 	.slave_configure		= scst_local_slave_configure,
 	.eh_abort_handler		= scst_local_abort,
 	.eh_device_reset_handler	= scst_local_device_reset,
-#if (LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 25))
 	.eh_target_reset_handler	= scst_local_target_reset,
-#endif
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) && \
 	LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0)
 	.use_blk_tags			= true,
 #endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) || \
-    defined(CONFIG_SUSE_KERNEL) || \
-    !(!defined(RHEL_RELEASE_CODE) || \
-     RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(6, 1))
 	.can_queue			= 2048,
 	/*
 	 * Set it low for the "Drop back to untagged" case in
@@ -1564,10 +1395,6 @@
 	 * default in slave_configure()
 	 */
 	.cmd_per_lun			= 3,
-#else
-	.can_queue			= 256,
-	.cmd_per_lun			= 32,
-#endif
 	.this_id			= -1,
 	.sg_tablesize			= 0xFFFF,
 	.max_sectors			= 0xffff,
@@ -1620,11 +1447,7 @@
 	 * kernels. If we don't,  max_cmd_size gets set to 4 (and we get
 	 * a compiler warning) so a scan never occurs.
 	 */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26)
-	hpnt->max_cmd_len = 16;
-#else
 	hpnt->max_cmd_len = 260;
-#endif
 
 	ret = scsi_add_host(hpnt, &sess->dev);
 	if (ret) {
@@ -1639,7 +1462,18 @@
 	return ret;
 }
 
-static int scst_local_driver_remove(struct device *dev)
+/*
+ * See also commit fc7a6209d571 ("bus: Make remove callback return void")
+ */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) &&		\
+	(!defined(RHEL_RELEASE_CODE) ||				\
+	 RHEL_RELEASE_CODE -0 < RHEL_RELEASE_VERSION(9, 2))
+#define DRIVER_REMOVE_RET int
+#else
+#define DRIVER_REMOVE_RET void
+#endif
+
+static DRIVER_REMOVE_RET scst_local_driver_remove(struct device *dev)
 {
 	struct scst_local_sess *sess;
 	struct Scsi_Host *shost = NULL;
@@ -1656,7 +1490,7 @@
 	scsi_host_put(shost);
 
 	TRACE_EXIT();
-	return 0;
+	return (DRIVER_REMOVE_RET)0;
 }
 
 static int scst_local_bus_match(struct device *dev,
@@ -1680,26 +1514,7 @@
 	.bus	= &scst_local_lld_bus,
 };
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29)
-static void scst_local_root_release(struct device *dev)
-{
-	TRACE_ENTRY();
-
-	TRACE_EXIT();
-	return;
-}
-
-static struct device scst_local_root = {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
-	.bus_id		= "scst_local_root",
-#else
-	.init_name	= "scst_local_root",
-#endif
-	.release	= scst_local_root_release,
-};
-#else
 static struct device *scst_local_root;
-#endif
 
 static void scst_local_free_sess(struct scst_session *scst_sess)
 {
@@ -1731,15 +1546,7 @@
 	 * work.
 	 */
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
-	/*
-	 * cancel_work_sync() was introduced in 2.6.22. We can only wait until
-	 * all scheduled work is done.
-	 */
-	flush_workqueue(aen_workqueue);
-#else
 	cancel_work_sync(&sess->aen_work);
-#endif
 
 	spin_lock(&sess->aen_lock);
 	WARN_ON_ONCE(!sess->unregistering);
@@ -1776,13 +1583,8 @@
 	/*
 	 * Init this stuff we need for scheduling AEN work
 	 */
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20))
-	INIT_WORK(&sess->aen_work, scst_aen_work_fn, sess);
-	INIT_WORK(&sess->remove_work, scst_remove_work_fn, sess);
-#else
 	INIT_WORK(&sess->aen_work, scst_aen_work_fn);
 	INIT_WORK(&sess->remove_work, scst_remove_work_fn);
-#endif
 	spin_lock_init(&sess->aen_lock);
 	INIT_LIST_HEAD(&sess->aen_work_list);
 
@@ -1795,17 +1597,9 @@
 	}
 
 	sess->dev.bus     = &scst_local_lld_bus;
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29))
-	sess->dev.parent  = &scst_local_root;
-#else
 	sess->dev.parent = scst_local_root;
-#endif
 	sess->dev.release = &scst_local_release_adapter;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30)
-	snprintf(sess->dev.bus_id, sizeof(sess->dev.bus_id), initiator_name);
-#else
 	sess->dev.init_name = kobject_name(&sess->scst_sess->sess_kobj);
-#endif
 
 	res = device_register(&sess->dev);
 	if (res != 0)
@@ -1959,44 +1753,11 @@
 #endif
 #endif
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25))
-	/*
-	 * Allocate a pool of structures for tgt_specific structures.
-	 * We only need this if we could get non scatterlist requests
-	 */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
-	tgt_specific_pool = kmem_cache_create("scst_tgt_specific",
-				      sizeof(struct scst_local_tgt_specific),
-				      0, SCST_SLAB_FLAGS, NULL);
-#else
-	tgt_specific_pool = kmem_cache_create("scst_tgt_specific",
-				      sizeof(struct scst_local_tgt_specific),
-				      0, SCST_SLAB_FLAGS, NULL, NULL);
-#endif
-	if (!tgt_specific_pool) {
-		PRINT_ERROR("%s", "Unable to initialize tgt_specific_pool");
-		ret = -ENOMEM;
-		goto out;
-	}
-#endif
-
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29))
-	ret = device_register(&scst_local_root);
-	if (ret < 0) {
-		PRINT_ERROR("Root device_register() error: %d", ret);
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25))
-		goto destroy_kmem;
-#else
-		goto out;
-#endif
-	}
-#else
 	scst_local_root = root_device_register(SCST_LOCAL_NAME);
 	if (IS_ERR(scst_local_root)) {
 		ret = PTR_ERR(scst_local_root);
 		goto out;
 	}
-#endif
 
 	ret = bus_register(&scst_local_lld_bus);
 	if (ret < 0) {
@@ -2058,16 +1819,8 @@
 	bus_unregister(&scst_local_lld_bus);
 
 dev_unreg:
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29))
-	device_unregister(&scst_local_root);
-#else
 	root_device_unregister(scst_local_root);
-#endif
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25))
-destroy_kmem:
-	kmem_cache_destroy(tgt_specific_pool);
-#endif
 	goto out;
 }
 
@@ -2090,21 +1843,11 @@
 
 	driver_unregister(&scst_local_driver);
 	bus_unregister(&scst_local_lld_bus);
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29))
-	device_unregister(&scst_local_root);
-#else
 	root_device_unregister(scst_local_root);
-#endif
 
 	/* Now unregister the target template */
 	scst_unregister_target_template(&scst_local_targ_tmpl);
 
-#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 25))
-	/* Free the non scatterlist pool we allocated */
-	if (tgt_specific_pool)
-		kmem_cache_destroy(tgt_specific_pool);
-#endif
-
 	/* To make lockdep happy */
 	up_write(&scst_local_exit_rwsem);
 
diff --git a/scst/scstadmin/Makefile b/scst/scstadmin/Makefile
index 3526d5e..5d962ea 100644
--- a/scst/scstadmin/Makefile
+++ b/scst/scstadmin/Makefile
@@ -4,9 +4,11 @@
 
 REVISION ?= $(shell if [ -e .svn ]; then				\
 		      svn info | sed -n 's/^Revision:[[:blank:]]*/./p';	\
-		else git log | grep -c ^commit;				\
+		    elif [ -e .git ]; then				\
+                      echo -n .;					\
+		      git log | grep -c ^commit;			\
 		    fi)
-VERSION = $(shell echo -n "$$(sed -n 's/^[[:blank:]]*\$$VERSION[[:blank:]]*=[[:blank:]]*[\"'"'"']\([0-9.]*\)[\"'"'"'];$$/\1/p' scstadmin/scst-*/lib/SCST/SCST.pm)")
+VERSION = $(shell echo -n "$$(sed -n 's/^[[:blank:]]*\$$VERSION[[:blank:]]*=[[:blank:]]*[\"'"'"']\([0-9.]*\)[\"'"'"'];$$/\1/p' scstadmin/scst-*/lib/SCST/SCST.pm)$(REVISION)")
 
 SCSTADMIN_DIR = $(shell if [ ! -h scstadmin ]; then		\
 			    rm -f scstadmin;			\
@@ -149,9 +151,7 @@
 	sed "s/@rpm_version@/$(VERSION)/g"				\
 		<$${name}.spec.in >$${name}.spec &&			\
 	MAKE="$(MAKE)"							\
-	rpmbuild --define="%_topdir $${rpmtopdir}"                      \
-		--define="%rpm_release $(REVISION)"                     \
-		-ba $${name}.spec &&														\
+	rpmbuild --define="%_topdir $${rpmtopdir}" -ba $${name}.spec &&	\
 	rm -f $${name}-$(VERSION).tar.bz2
 
 clean:
diff --git a/scst/scstadmin/default/scst b/scst/scstadmin/default/scst
index 4b466ad..6d90290 100644
--- a/scst/scstadmin/default/scst
+++ b/scst/scstadmin/default/scst
@@ -1,5 +1,2 @@
 # iscsi-scstd command-line options. See also man iscsi-scstd.
 # ISCSID_OPTIONS="-u0 -g0 -p3260"
-# For SCST specify all SCST target drivers in SCST_TARGET_MODULES.
-# An example:
-SCST_TARGET_MODULES="scst_local iscsi_scst ocs_fc_scst scst_user"
diff --git a/scst/scstadmin/examples/scst.conf.sysfs b/scst/scstadmin/examples/scst.conf.sysfs
index 873614a..b20dfb6 100644
--- a/scst/scstadmin/examples/scst.conf.sysfs
+++ b/scst/scstadmin/examples/scst.conf.sysfs
@@ -79,14 +79,14 @@
 }
 
 TARGET_DRIVER ib_srpt {
-	TARGET ib_srpt_target_0 {
+	TARGET fe80:0000:0000:0000:0002:c903:0005:f34b {
 		enabled 1
 
 		LUN 0 disk1
 		LUN 1 disk2
 	}
 
-	TARGET ib_srpt_target_1 {
+	TARGET fe80:0000:0000:0000:0002:c903:0005:f34c {
 		enabled 1
 
 		LUN 0 disk1
@@ -101,7 +101,7 @@
 		group_id 1
 		state active
 
-		TARGET ib_srpt_target_0 {
+		TARGET fe80:0000:0000:0000:0002:c903:0005:f34b {
 			rel_tgt_id 1
 		}
 	}
@@ -110,7 +110,7 @@
 		group_id 2
 		state offline
 
-		TARGET ib_srpt_target_1 {
+		TARGET fe80:0000:0000:0000:0002:c903:0005:f34c {
 			rel_tgt_id 2
 		}
 	}
@@ -123,7 +123,7 @@
 		group_id 1
 		state offline
 
-		TARGET ib_srpt_target_0 {
+		TARGET fe80:0000:0000:0000:0002:c903:0005:f34b {
 			rel_tgt_id 1
 		}
 	}
@@ -132,7 +132,7 @@
 		group_id 2
 		state active
 
-		TARGET ib_srpt_target_1 {
+		TARGET fe80:0000:0000:0000:0002:c903:0005:f34c {
 			rel_tgt_id 2
 		}
 	}
diff --git a/scst/scstadmin/init.d/scst b/scst/scstadmin/init.d/scst
index 960c404..9490e1b 100755
--- a/scst/scstadmin/init.d/scst
+++ b/scst/scstadmin/init.d/scst
@@ -69,8 +69,6 @@
 PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin
 SCST_DFLT=/etc/default/scst
 
-[ -x "$(which scstadmin)" ] || exit 5
-
 if [ -f $SCST_DFLT ]; then
     . $SCST_DFLT
 fi
@@ -253,12 +251,6 @@
             tmpout=/tmp/scstadmin-output-$$
             if scstadmin -config $SCST_CFG >$tmpout 2>&1; then
                 rm -f $tmpout
-                for m in $SCST_MODULES; do
-                    if [ "$m" = "ocs_fc_scst" ]; then
-                        for i in $(ls /sys/kernel/scst_tgt/targets/ocs_xe201/53:8a:95:f2:26*/enabled); do echo 1 > $i; done
-                        for h in $(ls /sys/class/fc_host/); do echo "1" > /sys/class/fc_host/$h/issue_lip; done
-                    fi
-                done
                 return 0
             else
                 cat $tmpout
diff --git a/scst/scstadmin/scstadmin.spec.in b/scst/scstadmin/scstadmin.spec.in
index e53308e..086aa6f 100644
--- a/scst/scstadmin/scstadmin.spec.in
+++ b/scst/scstadmin/scstadmin.spec.in
@@ -3,7 +3,7 @@
 
 Name:		scstadmin
 Version:        %{rpm_version}
-Release:        %{rpm_release}
+Release:        1
 Summary:	SCST configuration tool
 Group:		Productivity/Networking/Other
 License:	GPLv2
diff --git a/scst/scstadmin/scstadmin.sysfs/Makefile b/scst/scstadmin/scstadmin.sysfs/Makefile
index f23e189..c749bb4 100644
--- a/scst/scstadmin/scstadmin.sysfs/Makefile
+++ b/scst/scstadmin/scstadmin.sysfs/Makefile
@@ -15,21 +15,24 @@
 	install -m 755 $(TOOL) $(DESTDIR)$(SBINDIR)
 	regex="s|%INSTALLSITELIB%|$$(make -sC scst-$(MODULE_VERSION) print-INSTALLSITELIB | grep -v ^make)|"; echo "$${regex}"; sed -i "$${regex}" $(DESTDIR)$(SBINDIR)/$(TOOL)
 
-uninstall:
+makefile:
+	@cd ./scst-$(MODULE_VERSION) &&		\
+	perl Makefile.PL PREFIX=$(PREFIX)
+
+uninstall: makefile
 	-rm -f $(DESTDIR)$(SBINDIR)/$(TOOL)
 	$(MAKE) -C scst-$(MODULE_VERSION) uninstall
 
-perl-module:
+perl-module: makefile
 	@cd ./scst-$(MODULE_VERSION) &&		\
-	perl Makefile.PL PREFIX=$(PREFIX) &&	\
 	printf '\nprint-%%:\n\t@echo '"'"'$$($$*)'"'"'\n' >> Makefile
 	$(MAKE) -C scst-$(MODULE_VERSION)
 
-test:
+test: makefile
 	export PERL_TEST_DIFF=diff
 	$(MAKE) -C scst-$(MODULE_VERSION) test #TEST_VERBOSE=1
 
-clean:
+clean: makefile
 	-$(MAKE) -C scst-$(MODULE_VERSION) clean
 
 distclean: clean
@@ -38,4 +41,4 @@
 
 extraclean: distclean
 
-.PHONY: all install uninstall perl-module clean distclean extraclean
+.PHONY: all install uninstall makefile perl-module clean distclean extraclean
diff --git a/scst/scstadmin/scstadmin.sysfs/man5/scst.conf.5 b/scst/scstadmin/scstadmin.sysfs/man5/scst.conf.5
index 1bf2f52..edf733e 100644
--- a/scst/scstadmin/scstadmin.sysfs/man5/scst.conf.5
+++ b/scst/scstadmin/scstadmin.sysfs/man5/scst.conf.5
@@ -250,7 +250,7 @@
 .IP
 TARGET_DRIVER ib_srpt {
 .br
-	TARGET ib_srpt_target_0 {
+	TARGET fe80:0000:0000:0000:0002:c903:0005:f34b {
 .br
 		enabled 1
 .IP
@@ -260,7 +260,7 @@
 .br
 	}
 .IP
-	TARGET ib_srpt_target_1 {
+	TARGET fe80:0000:0000:0000:0002:c903:0005:f34c {
 .br
 		enabled 1
 .IP
@@ -306,7 +306,7 @@
 .br
 		state active
 .IP
-		TARGET ib_srpt_target_0 {
+		TARGET fe80:0000:0000:0000:0002:c903:0005:f34b {
 .br
 			rel_tgt_id 1
 .br
@@ -320,7 +320,7 @@
 .br
 		state offline
 .IP
-		TARGET ib_srpt_target_1 {
+		TARGET fe80:0000:0000:0000:0002:c903:0005:f34c {
 .br
 			rel_tgt_id 2
 .br
@@ -340,7 +340,7 @@
 .br
 		state offline
 .IP
-		TARGET ib_srpt_target_0 {
+		TARGET fe80:0000:0000:0000:0002:c903:0005:f34b {
 .br
 			rel_tgt_id 1
 .br
@@ -354,7 +354,7 @@
 .br
 		state active
 .IP
-		TARGET ib_srpt_target_1 {
+		TARGET fe80:0000:0000:0000:0002:c903:0005:f34c {
 .br
 			rel_tgt_id 2
 .br
@@ -368,7 +368,7 @@
 module parameter. Parameters for an SCST kernel module can be configured in
 /etc/modprobe.d/99-local.conf just like for any other kernel module. An example:
 .IP
-options ib_srpt use_node_guid_in_target_name=1
+options ib_srpt rdma_cm_port=5000
 .SH FILES
 .IP "/etc/scst.conf"
 The configuration file read by
diff --git a/scst/scstadmin/scstadmin.sysfs/scst-1.0.0/lib/SCST/SCST.pm b/scst/scstadmin/scstadmin.sysfs/scst-1.0.0/lib/SCST/SCST.pm
index 5a5ab48..df34d00 100644
--- a/scst/scstadmin/scstadmin.sysfs/scst-1.0.0/lib/SCST/SCST.pm
+++ b/scst/scstadmin/scstadmin.sysfs/scst-1.0.0/lib/SCST/SCST.pm
@@ -11,6 +11,7 @@
 use warnings;
 use 5.005;
 use Fcntl ':mode';
+use File::Spec;
 use IO::Handle;
 use IO::File;
 use Carp qw(cluck);
@@ -4635,17 +4636,7 @@
 }
 
 sub make_path {
-	my $path;
-
-	foreach my $element (@_) {
-		if ($path && rindex($path, '/') != length($path) - 1) {
-			$path .= '/';
-		}
-		cluck("make_path: invalid argument") if !valid($element);
-		$path .= $element;
-	}
-
-	return $path;
+	return File::Spec->catdir(@_);
 }
 
 ;1 __END__
diff --git a/scst/scstadmin/scstadmin.sysfs/scstadmin b/scst/scstadmin/scstadmin.sysfs/scstadmin
index 15aeeef..83c6089 100755
--- a/scst/scstadmin/scstadmin.sysfs/scstadmin
+++ b/scst/scstadmin/scstadmin.sysfs/scstadmin
@@ -3,7 +3,7 @@
 use strict;
 use warnings;
 
-my $Version  = 'SCST Configurator v3.5.0';
+my $Version  = 'SCST Configurator v3.7.0';
 
 # Configures SCST
 #
@@ -21,7 +21,7 @@
      -h, -help, --help       : Show this information.
 
 General Operations
-     -config <file>          : Configure Actifio SCST given the specified <file>.
+     -config <file>          : Configure SCST given the specified <file>.
      -check_config <file>    : Checks the saved configuration <file>.
      -write_config <file>    : Writes the current configuration to <file>.
      -clear_config           : Clear all SCST configuration.
@@ -222,7 +222,6 @@
                                even deletions (DANGER!).
      -noprompt               : Do not prompt or pause. Use with caution!
      -cont_on_err            : Continue after an error occurred.
-	 -skipReads				 : Used with force to skip readingWorkingConfig on device attribute change
 
 Debugging (limited support)
      -debug                  : Debug mode - don\'t do anything destructive.
@@ -254,7 +253,7 @@
 use File::Spec;
 
 BEGIN {
-  my $site_lib = '/usr/share/perl5';
+  my $site_lib = '%INSTALLSITELIB%';
 	if ($site_lib =~ '^%') {
 		my $scstadmindir = dirname(abs_path($0));
 		$site_lib = File::Spec->catdir($scstadmindir, "scst-1.0.0",
@@ -305,11 +304,6 @@
 
 use vars qw($Version);
 
-# Temporary (hopefully) workaround so we don't need to 
-# add -force and -noprompt flags in multiple places in
-# psrv
-unshift(@ARGV, "-force", "-noprompt");
-
 &main();
 
 sub getArgs {
@@ -436,8 +430,6 @@
 	my $force;
 	my $dumpAttrs;
 
-	my $skipRead;
-
 	my $p = new Getopt::Long::Parser;
 
 	if (!$p->getoptions('config:s'		=> \$applyConfig,
@@ -565,7 +557,6 @@
 			    'noprompt'		=> \$_NOPROMPT_,
 			    'cont_on_err'       => \$_CONT_ON_ERR_,
 			    'force'		=> \$force,
-				'skipRead'	=> \$skipRead,
 			    'dumpAttrs'		=> \$dumpAttrs,
 			    'debug'             => \$_DEBUG_))
 	{
@@ -585,7 +576,6 @@
 	$nonkey = TRUE if (defined($nonkey));
 	$lip    = TRUE if (defined($lip));
 	$noLip  = TRUE if (defined($noLip));
-	$skipRead  = TRUE if (defined($skipRead));
 
 	my $query_mode = defined($listHandler) || defined($listDevice) || defined($listDeviceGroup) || defined($listTargetGroup) ||
 	  defined($listDriver) || defined($listTarget) || defined($listGroup) || defined($listSessions) ||
@@ -943,7 +933,6 @@
 		nonkey				=> $nonkey,
 		force				=> $force,
 		dumpAttrs			=> $dumpAttrs,
-		skipRead 			=> $skipRead,
 	    );
 	return \%args;
 }
@@ -1080,7 +1069,6 @@
 	my $nonkey			= $args->{nonkey};
 	my $force			= $args->{force};
 	my $dumpAttrs			= $args->{dumpAttrs};
-	my $skipRead			= $args->{skipRead};
 
 	$SCST = new SCST::SCST($_DEBUG_);
 
@@ -1095,7 +1083,7 @@
 			$rc = checkConfiguration();
 			condExit("Configuration has errors, aborting.") if ($rc);
 			last if ($force && prompt());
-			my $changes = applyConfiguration($force, $skipRead);
+			my $changes = applyConfiguration($force);
 			$rc = issueLip() if ($changes && $lip);
 			last SWITCH;
 		};
@@ -1759,86 +1747,54 @@
 #***********************************************************************************************
 # Returns 0 upon success and 1 upon error.
 sub writePeerConfiguration {
-	my $nonkey = shift;
+        my $nonkey = shift;
 	my $errorString;
 
-	my $io = new IO::File $CONFIGFILE, O_CREAT|O_WRONLY|O_TRUNC;
+        my $io = new IO::File $CONFIGFILE, O_CREAT|O_WRONLY|O_TRUNC;
 
-	if (!$io) {
-		print "Failed to save configuration to file '$CONFIGFILE': $!\n";
-		return 1;
-	}
+        if (!$io) {
+                print "Failed to save configuration to file '$CONFIGFILE': $!\n";
+                return 1;
+        }
 
-	print "Writing current configuration to file '$CONFIGFILE'.. ";
+        print "Writing current configuration to file '$CONFIGFILE'.. ";
 
+        print $io "# Automatically generated by $Version.\n\n";
 
-        # Prune devices (and targets) out of iscsi
-        $CURRENT{assign}->{iscsi} = {};
+        {
+                my ($attributes, $errorString) = $SCST->scstAttributes();
+                immediateExit($errorString);
 
-        # Accumulate all remaining devices in use
-        my %ALL_DEVICES=();
-        while (my ($assign, $targets) = each %{$CURRENT{assign}}) {
-            next if $assign eq "iscsi";
-            for my $target (keys %$targets) {
-                my $groups = $targets->{$target}->{GROUP};
-                for my $group (values %$groups) {
-                    my $luns = $group->{LUN};
-                    for my $lun (values %$luns) {
-                        my $device = $lun;
-                        ++$ALL_DEVICES{$device}
+                print $io serializeKeyAttr("", $attributes);
+                if ($nonkey) {
+                    my $nk = serializeNkAttr("", $attributes);
+                    if ($nk) {
+                        print $io "# Non-key attributes\n";
+                        print $io $nk;
                     }
                 }
-            }
+                print $io "\n";
         }
 
-        # Prune all devices not in use
-        for my $handler (keys %{$CURRENT{handler}}) {
-            $CURRENT{handler}->{$handler} = [grep exists $ALL_DEVICES{$_}, @{$CURRENT{handler}->{$handler}}]
-        }
-
-        for my $dgroup (keys %{$CURRENT{dgroups}}) {            
-            $CURRENT{dgroups}->{$dgroup}->{devices} = [grep exists $ALL_DEVICES{$_}, @{$CURRENT{dgroups}->{$dgroup}->{devices}}]
-        }
-
-	print $io "# Automatically generated by $Version.\n\n";
-
-	{
-		my $attributes;
-		($attributes, $errorString) = $SCST->scstAttributes();
-		immediateExit($errorString);
-
-		print $io serializeKeyAttr("", $attributes);
-		if ($nonkey) {
-		    my $nk = serializeNkAttr("", $attributes);
-		    if ($nk) {
-			print $io "# Non-key attributes\n";
-			print $io $nk;
-		    }
-		}
-		print $io "\n";
-	}
-
 	foreach my $handler (sort keys %{$CURRENT{'handler'}}) {
-		my $handler_buff = "";
-		my $handler_buff_nk = "";
+
+                my $handler_buff;
+                my $handler_buff_nk;
 		my $handler_attrs;
 		my $attributes;
 
-		($handler_attrs, $errorString) = $SCST->deviceCreateAttributes($handler);
-		($attributes, $errorString) = $SCST->handlerAttributes($handler);
-		$handler_buff = serializeKeyAttr("\t", $attributes);
-		$handler_buff_nk = serializeNkAttr("\t", $attributes) if ($nonkey);
+                ($handler_attrs, $errorString) = $SCST->deviceCreateAttributes($handler);
+                ($attributes, $errorString) = $SCST->handlerAttributes($handler);
 
-		my $devices = $CURRENT{'handler'}->{$handler};
+                $handler_buff = serializeKeyAttr("\t", $attributes);
+                $handler_buff_nk = serializeNkAttr("\t", $attributes) if ($nonkey);
 
-		my $device_buff = "";
-		foreach my $device (sort @{$devices}) {
+                my $devices = $CURRENT{'handler'}->{$handler};
 
-			my $attributes;
-			my $attribute_buff = "";
-			my $attribute_buff_nk = "";
+                my $device_buff;
+                foreach my $device (sort @{$devices}) {
 
-			($attributes, $errorString) = $SCST->deviceAttributes($device);
+                        my ($attributes, $errorString) = $SCST->deviceAttributes($device);
 
                         if ($handler eq 'vdisk_fileio') {
 				if ($device eq 'disk00') {
@@ -1852,61 +1808,68 @@
 				$attributes->{active}->{keys}->{0}->{value} = "0";
 			}
 
-			$attribute_buff = serializeKeyAttr("\t\t", $attributes, $handler_attrs);
-			$attribute_buff_nk = serializeNkAttr("\t\t", $attributes, $handler_attrs) if ($nonkey);
-			$attribute_buff .= "\n" if ($attribute_buff);
-			$attribute_buff_nk .= "\n" if ($attribute_buff_nk);
+                        my $attribute_buff;
+                        my $attribute_buff_nk;
 
-			if ($attribute_buff_nk) {
-				$attribute_buff .= "\t\t# Non-key attributes\n";
-				$attribute_buff .= $attribute_buff_nk;
-			}
+                        $attribute_buff = serializeKeyAttr("\t\t", $attributes, $handler_attrs);
+                        $attribute_buff_nk = serializeNkAttr("\t\t", $attributes, $handler_attrs) if ($nonkey);
+                        $attribute_buff .= "\n" if ($attribute_buff);
+                        $attribute_buff_nk .= "\n" if ($attribute_buff_nk);
 
-			$attribute_buff =~ s/\n+$/\n/;
+                        if ($attribute_buff_nk) {
+                                $attribute_buff .= "\t\t# Non-key attributes\n";
+                                $attribute_buff .= $attribute_buff_nk;
+                        }
 
-			if ($attribute_buff) {
-				$device_buff .= " {\n";
-				$device_buff .= $attribute_buff;
-				$device_buff .= "\t}\n\n";
-			} else {
-				$device_buff .= "\n";
-			}
-		}
+                        $attribute_buff =~ s/\n+$/\n/;
 
-		$device_buff =~ s/\n+$/\n/;
+                        if ($attribute_buff) {
+                                $device_buff .= " {\n";
+                                $device_buff .= $attribute_buff;
+                                $device_buff .= "\t}\n\n";
+                        } else {
+                                $device_buff .= "\n";
+                        }
+                }
 
-		$handler_buff .= $device_buff;
+                $device_buff =~ s/\n+$/\n/;
 
-		if ($handler_buff_nk) {
-			$handler_buff .= "\t# Non-key attributes\n";
-			$handler_buff .= $handler_buff_nk;
-		}
+                $handler_buff .= $device_buff;
 
-		if ($handler_buff) {
-			print $io "HANDLER $handler {\n";
-			print $io $handler_buff;
-			print $io "}\n\n";
-		}
-	}
-	foreach my $driver (sort keys %{$CURRENT{'assign'}}) {
-		my $driver_buff = "";
+                if ($handler_buff_nk) {
+                        $handler_buff .= "\t# Non-key attributes\n";
+                        $handler_buff .= $handler_buff_nk;
+                }
+
+                if ($handler_buff) {
+                        print $io "HANDLER $handler {\n";
+                        print $io $handler_buff;
+                        print $io "}\n\n";
+                }
+        }
+        foreach my $driver (sort keys %{$CURRENT{'assign'}}) {
+                my $driver_buff;
 
 		my $drv_attrs;
 		my $drv_attr_buff = "";
 		my $drv_attr_buff_nk = "";
 
-		($drv_attrs, $errorString) = $SCST->driverAttributes($driver);
-		$drv_attr_buff = serializeKeyAttr("\t", $drv_attrs);
-		$drv_attr_buff_nk = serializeNkAttr("\t", $drv_attrs) if ($nonkey);
-		$drv_attr_buff .= "\n" if ($drv_attr_buff);
-		$drv_attr_buff_nk .= "\n" if ($drv_attr_buff_nk);
+                ($drv_attrs, $errorString) = $SCST->driverAttributes($driver);
+                $drv_attr_buff = serializeKeyAttr("\t", $drv_attrs);
+                $drv_attr_buff_nk = serializeNkAttr("\t", $drv_attrs) if ($nonkey);
+                $drv_attr_buff .= "\n" if ($drv_attr_buff);
+                $drv_attr_buff_nk .= "\n" if ($drv_attr_buff_nk);
 
-		my $targets = $CURRENT{'assign'}->{$driver};
-		my $tgt_attrs;
-		($tgt_attrs, $errorString) = $SCST->targetCreateAttributes($driver);
+                my $targets = $CURRENT{'assign'}->{$driver};
+                my ($tgt_attrs, $errorString) = $SCST->targetCreateAttributes($driver);
 
-		my $target_buff = "";
-		foreach my $target (sort keys %{$targets}) {
+                my $target_buff;
+
+use Data::Dumper;
+#warn Dumper ($attributes, $handler_attrs, $handler);
+#warn Dumper ($handler_attrs);
+
+                foreach my $target (sort keys %{$targets}) {
                         if ($target eq $MYWWPN1) {
                                 $target_buff .= "\tTARGET $PEERWWPN1";
                         } elsif ($target eq $MYWWPN2) {
@@ -1916,329 +1879,328 @@
                         } elsif ($target eq $MYWWPN4) {
                                 $target_buff .= "\tTARGET $PEERWWPN4";
                         } else {
-        			$target_buff .= "\tTARGET $target";
+                                $target_buff .= "\tTARGET $target";
                         }
 
 			my $attributes;
 			my $attribute_buff = "";
 			my $attribute_buff_nk = "";
 
-			($attributes, $errorString) = $SCST->targetAttributes($driver, $target);
-			if (defined($$attributes{'hw_target'}) &&
-			  ($$attributes{'hw_target'}->{'value'} == TRUE)) {
-				$attribute_buff = "\t\tHW_TARGET\n\n";
-			}
+                        ($attributes, $errorString) = $SCST->targetAttributes($driver, $target);
 
-			$attribute_buff .= serializeKeyAttr("\t\t", $attributes, $tgt_attrs);
-			$attribute_buff_nk .= serializeNkAttr("\t\t", $attributes, $tgt_attrs) if ($nonkey);
-			$attribute_buff .= "\n" if ($attribute_buff);
-			$attribute_buff_nk .= "\n" if ($attribute_buff_nk);
+                        if (defined($$attributes{'hw_target'}) &&
+                          ($$attributes{'hw_target'}->{'value'} == TRUE)) {
+                                $attribute_buff = "\t\tHW_TARGET\n\n";
+                        }
 
-			my $luns = $CURRENT{'assign'}->{$driver}->{$target}->{'LUN'};
-			my $lun_attrs;
-			($lun_attrs, $errorString) = $SCST->lunCreateAttributes($driver, $target);
+                        $attribute_buff .= serializeKeyAttr("\t\t", $attributes, $tgt_attrs);
+                        $attribute_buff_nk .= serializeNkAttr("\t\t", $attributes, $tgt_attrs) if ($nonkey);
+                        $attribute_buff .= "\n" if ($attribute_buff);
+                        $attribute_buff_nk .= "\n" if ($attribute_buff_nk);
 
-			my $t_lun_buff = "";
-			foreach my $lun (sort numerically keys %{$luns}) {
-				my $lun_dev = $$luns{$lun};
+                        my $luns = $CURRENT{'assign'}->{$driver}->{$target}->{'LUN'};
+                        my ($lun_attrs, $errorString) = $SCST->lunCreateAttributes($driver, $target);
 
-				# Do not save copy_manager LUN definitions
-				# for LUNs associated with an SCST device
+                        my $t_lun_buff;
+                        foreach my $lun (sort numerically keys %{$luns}) {
+                                my $lun_dev = $$luns{$lun};
+
+                                # Do not save copy_manager LUN definitions
+                                # for LUNs associated with an SCST device
 				# handler.
 
-				next if ($driver eq 'copy_manager' &&
-					 isPassthroughDev($lun_dev));
+                                next if ($driver eq 'copy_manager' &&
+                                         isPassthroughDev($lun_dev));
 
-				$t_lun_buff .= "\t\tLUN $lun $lun_dev";
+                                $t_lun_buff .= "\t\tLUN $lun $lun_dev";
 
-				my $attributes;
-				($attributes, $errorString) = $SCST->lunAttributes($driver, $target, $lun);
-				my $l_attribute_buff =
-				    serializeKeyAttr("\t\t\t",
-						     $attributes,
-						     $lun_attrs);
-				my $l_attribute_buff_nk =
-				    serializeNkAttr("\t\t\t",
-						    $attributes,
-						    $lun_attrs) if ($nonkey);
+                                my ($attributes, $errorString) = $SCST->lunAttributes($driver, $target, $lun);
+                                my $l_attribute_buff =
+                                    serializeKeyAttr("\t\t\t",
+                                                     $attributes,
+                                                     $lun_attrs);
+                                my $l_attribute_buff_nk =
+                                    serializeNkAttr("\t\t\t",
+                                                    $attributes,
+                                                    $lun_attrs) if ($nonkey);
 
-				if ($l_attribute_buff_nk) {
-					$l_attribute_buff .= "\t\t\t# Non-key attributes\n";
-					$l_attribute_buff .= $l_attribute_buff_nk;
-				}
+                                if ($l_attribute_buff_nk) {
+                                        $l_attribute_buff .= "\t\t\t# Non-key attributes\n";
+                                        $l_attribute_buff .= $l_attribute_buff_nk;
+                                }
 
-				if ($l_attribute_buff) {
-					$t_lun_buff .= " {\n";
-					$t_lun_buff .= $l_attribute_buff;
-					$t_lun_buff .= "\t\t}\n\n";
-				} else {
-					$t_lun_buff .= "\n";
-				}
-			}
+                                if ($l_attribute_buff) {
+                                        $t_lun_buff .= " {\n";
+                                        $t_lun_buff .= $l_attribute_buff;
+                                        $t_lun_buff .= "\t\t}\n\n";
+                                } else {
+                                        $t_lun_buff .= "\n";
+                                }
+                        }
 
-			$t_lun_buff .= "\n" if ($t_lun_buff);
-			$t_lun_buff =~ s/\n+$/\n\n/;
+                        $t_lun_buff .= "\n" if ($t_lun_buff);
+                        $t_lun_buff =~ s/\n+$/\n\n/;
 
-			my $groups = $CURRENT{'assign'}->{$driver}->{$target}->{'GROUP'};
+                        my $groups = $CURRENT{'assign'}->{$driver}->{$target}->{'GROUP'};
 
-			my $group_buff = "";
-			foreach my $group (sort keys %{$groups}) {
+                        my $group_buff;
+                        foreach my $group (sort keys %{$groups}) {
 				my $lun_attrs;
 				my $ini_attrs;
 
-				($lun_attrs, $errorString) = $SCST->lunCreateAttributes($driver, $target, $group);
-				($ini_attrs, $errorString) = $SCST->initiatorCreateAttributes($driver, $target, $group);
-				$group_buff .= "\t\tGROUP $group";
+                                ($lun_attrs, $errorString) = $SCST->lunCreateAttributes($driver, $target, $group);
+                                ($ini_attrs, $errorString) = $SCST->initiatorCreateAttributes($driver, $target, $group);
 
-				my $luns = $CURRENT{'assign'}->{$driver}->{$target}->{'GROUP'}->{$group}->{'LUN'};
+                                $group_buff .= "\t\tGROUP $group";
 
-				my $lun_buff = "";
-				foreach my $lun (sort numerically keys %{$luns}) {
-					my $lun_dev = $$luns{$lun};
+                                my $luns = $CURRENT{'assign'}->{$driver}->{$target}->{'GROUP'}->{$group}->{'LUN'};
 
-					$lun_buff .= "\t\t\tLUN $lun $lun_dev";
+                                my $lun_buff;
+                                foreach my $lun (sort numerically keys %{$luns}) {
+                                        my $lun_dev = $$luns{$lun};
 
-					my $attributes;
-					($attributes, $errorString) = $SCST->lunAttributes($driver, $target, $lun, $group);
+                                        $lun_buff .= "\t\t\tLUN $lun $lun_dev";
 
-					my $l_attribute_buff
-					    = serializeKeyAttr("\t\t\t\t",
-							       $attributes,
-							       $lun_attrs);
-					my $l_attribute_buff_nk
-					    = serializeNkAttr("\t\t\t\t",
-							      $attributes,
-							      $lun_attrs)
-					    if ($nonkey);
+                                        my ($attributes, $errorString) = $SCST->lunAttributes($driver, $target, $lun, $group);
 
-					if ($l_attribute_buff_nk) {
-						$l_attribute_buff .= "\t\t\t\t# Non-key attributes\n";
-						$l_attribute_buff .= $l_attribute_buff_nk;
-					}
+                                        my $l_attribute_buff
+                                            = serializeKeyAttr("\t\t\t\t",
+                                                               $attributes,
+                                                               $lun_attrs);
+                                        my $l_attribute_buff_nk
+                                            = serializeNkAttr("\t\t\t\t",
+                                                              $attributes,
+                                                              $lun_attrs)
+                                            if ($nonkey);
 
-					if ($l_attribute_buff) {
-						$lun_buff .= " {\n";
-						$lun_buff .= $l_attribute_buff;
-						$lun_buff .= "\t\t\t}\n";
-					} else {
-						$lun_buff .= "\n";
-					}
+                                        if ($l_attribute_buff_nk) {
+                                                $l_attribute_buff .= "\t\t\t\t# Non-key attributes\n";
+                                                $l_attribute_buff .= $l_attribute_buff_nk;
+                                        }
 
-				}
+                                        if ($l_attribute_buff) {
+                                                $lun_buff .= " {\n";
+                                                $lun_buff .= $l_attribute_buff;
+                                                $lun_buff .= "\t\t\t}\n";
+                                        } else {
+                                                $lun_buff .= "\n";
+                                        }
 
-				my $inits = $CURRENT{'assign'}->{$driver}->{$target}->{'GROUP'}->{$group}->{'INITIATORS'};
+                                }
 
-				my $init_buff = "";
-				foreach my $init (sort @{$inits}) {
-					$init_buff .= "\n\t\t\tINITIATOR " . escapeMeta($init);
+                                my $inits = $CURRENT{'assign'}->{$driver}->{$target}->{'GROUP'}->{$group}->{'INITIATORS'};
 
-					my $attributes;
-					($attributes, $errorString) = $SCST->initiatorAttributes($driver, $target, $group, $init);
+                                my $init_buff;
+                                foreach my $init (sort @{$inits}) {
+                                        $init_buff .= "\n\t\t\tINITIATOR " . escapeMeta($init);
 
-					my $i_attribute_buff
-					    = serializeKeyAttr("\t\t\t\t",
-							       $attributes,
-							       $ini_attrs);
-					my $i_attribute_buff_nk
-					    = serializeNkAttr("\t\t\t\t",
-							      $attributes,
-							      $ini_attrs)
-					    if ($nonkey);
+                                        my ($attributes, $errorString) = $SCST->initiatorAttributes($driver, $target, $group, $init);
 
-					if ($i_attribute_buff_nk) {
-						$i_attribute_buff .= "\t\t\t\t# Non-key attributes\n";
-						$i_attribute_buff .= $i_attribute_buff_nk;
-					}
+                                        my $i_attribute_buff
+                                            = serializeKeyAttr("\t\t\t\t",
+                                                               $attributes,
+                                                               $ini_attrs);
+                                        my $i_attribute_buff_nk
+                                            = serializeNkAttr("\t\t\t\t",
+                                                              $attributes,
+                                                              $ini_attrs)
+                                            if ($nonkey);
 
-					if ($i_attribute_buff) {
-						$init_buff .= " {\n";
-						$init_buff .= $i_attribute_buff;
-						$init_buff .= "\t\t\t}\n";
-					} else {
-						$init_buff .= "\n";
-					}
-				}
+                                        if ($i_attribute_buff_nk) {
+                                                $i_attribute_buff .= "\t\t\t\t# Non-key attributes\n";
+                                                $i_attribute_buff .= $i_attribute_buff_nk;
+                                        }
+
+                                        if ($i_attribute_buff) {
+                                                $init_buff .= " {\n";
+                                                $init_buff .= $i_attribute_buff;
+                                                $init_buff .= "\t\t\t}\n";
+                                        } else {
+                                                $init_buff .= "\n";
+                                        }
+                                }
 
 				my $grp_attributes;
-				($grp_attributes, $errorString) = $SCST->groupAttributes($driver, $target, $group);
-				my $g_attribute_buff
-				    = serializeKeyAttr("\t\t\t",
-						       $grp_attributes);
-				my $g_attribute_buff_nk
-				    = serializeNkAttr("\t\t\t",
-						      $grp_attributes)
-				    if ($nonkey);
+                                ($grp_attributes, $errorString) = $SCST->groupAttributes($driver, $target, $group);
+                                my $g_attribute_buff
+                                    = serializeKeyAttr("\t\t\t",
+                                                       $grp_attributes);
+                                my $g_attribute_buff_nk
+                                    = serializeNkAttr("\t\t\t",
+                                                      $grp_attributes)
+                                    if ($nonkey);
 
-				if ($lun_buff || $init_buff ||
-				    $g_attribute_buff || $g_attribute_buff_nk) {
-					$group_buff .= " {\n";
-					$group_buff .= $lun_buff;
-					$group_buff .= $init_buff;
-				}
+                                if ($lun_buff || $init_buff ||
+                                    $g_attribute_buff || $g_attribute_buff_nk) {
+                                        $group_buff .= " {\n";
+                                        $group_buff .= $lun_buff;
+                                        $group_buff .= $init_buff;
+                                }
 
-				if ($g_attribute_buff_nk) {
-					$g_attribute_buff .= "\n" if ($g_attribute_buff);
-					$g_attribute_buff .= "\t\t\t# Non-key attributes\n";
-					$g_attribute_buff .= $g_attribute_buff_nk;
-				}
+                                if ($g_attribute_buff_nk) {
+                                        $g_attribute_buff .= "\n" if ($g_attribute_buff);
+                                        $g_attribute_buff .= "\t\t\t# Non-key attributes\n";
+                                        $g_attribute_buff .= $g_attribute_buff_nk;
+                                }
 
-				if ($g_attribute_buff) {
-					$group_buff .= "\n";
-					$group_buff .= $g_attribute_buff;
-				}
+                                if ($g_attribute_buff) {
+                                        $group_buff .= "\n";
+                                        $group_buff .= $g_attribute_buff;
+                                }
 
-				if ($group_buff && ($lun_buff || $init_buff ||
-						    $g_attribute_buff || $g_attribute_buff_nk)) {
-					$group_buff .= "\t\t}\n\n";
-					$group_buff =~ s/\n+$/\n/;
-				}
+                                if ($group_buff && ($lun_buff || $init_buff ||
+                                                    $g_attribute_buff || $g_attribute_buff_nk)) {
+                                        $group_buff .= "\t\t}\n\n";
+                                        $group_buff =~ s/\n+$/\n/;
+                                }
 
-				$group_buff .= "\n" if ($group_buff);
-			}
+                                $group_buff .= "\n" if ($group_buff);
+                        }
 
-			if ($attribute_buff_nk) {
-				$attribute_buff .= "\t\t# Non-key attributes\n";
-				$attribute_buff .= $attribute_buff_nk;
-			}
+                        if ($attribute_buff_nk) {
+                                $attribute_buff .= "\t\t# Non-key attributes\n";
+                                $attribute_buff .= $attribute_buff_nk;
+                        }
 
-			if ($attribute_buff || $t_lun_buff || $group_buff ) {
-				$target_buff .= " {\n";
+                        if ($attribute_buff || $t_lun_buff || $group_buff ) {
+                                $target_buff .= " {\n";
 
-				$target_buff .= $attribute_buff;
-				$target_buff .= $t_lun_buff;
-				$target_buff .= $group_buff;
+                                $target_buff .= $attribute_buff;
+                                $target_buff .= $t_lun_buff;
+                                $target_buff .= $group_buff;
 
-				$target_buff =~ s/\n\n$/\n/;
-				$target_buff .= "\t}\n\n";
-			} else {
-				$target_buff .= "\n";
-			}
-		}
+                                $target_buff =~ s/\n\n$/\n/;
+                                $target_buff .= "\t}\n\n";
+                        } else {
+                                $target_buff .= "\n";
+                        }
+                }
 
-		if ($drv_attr_buff_nk) {
-			$drv_attr_buff .= "\t# Non-key attributes\n";
-			$drv_attr_buff .= $drv_attr_buff_nk;
-		}
+                if ($drv_attr_buff_nk) {
+                        $drv_attr_buff .= "\t# Non-key attributes\n";
+                        $drv_attr_buff .= $drv_attr_buff_nk;
+                }
 
-		$driver_buff .= $drv_attr_buff;
-		$driver_buff .= $target_buff;
-		$driver_buff =~ s/\n\n$/\n/;
+                $driver_buff .= $drv_attr_buff;
+                $driver_buff .= $target_buff;
+                $driver_buff =~ s/\n\n$/\n/;
 
-		if ($driver_buff) {
-			print $io "TARGET_DRIVER $driver {\n";
+                if ($driver_buff) {
+                        print $io "TARGET_DRIVER $driver {\n";
                         if($driver eq 'iscsi') {
                                 print $io "\tenabled 1\n";
                         } elsif ($driver eq 'copy_manager') {
-                                print $io "\tTARGET copy_manager_tgt\n";
+                                print $io "\tTARGET copy_manager_tgt\n";;
                         } else {
-			print $io $driver_buff;
+                                print $io $driver_buff;
                         }
-			print $io "}\n\n";
-		}
-	}
+                        print $io "}\n\n";
+                }
+        }
 
 	my $dga;
-	($dga, $errorString) = $SCST->aluaAttributes();
-	my $dga_buff = serializeKeyAttr("\t", $dga);
-	my $dga_buff_nk = serializeNkAttr("\t", $dga) if ($nonkey);
-	if ($dga_buff_nk) {
-		$dga_buff .= "\t# Non-key attributes\n";
-		$dga_buff .= $dga_buff_nk;
-	}
-	if ($dga_buff) {
-		print $io "ALUA {\n";
-		print $io $dga_buff;
-		print $io "}\n\n";
-	}
+        ($dga, $errorString) = $SCST->aluaAttributes();
+        my $dga_buff = serializeKeyAttr("\t", $dga);
+        my $dga_buff_nk = serializeNkAttr("\t", $dga) if ($nonkey);
+        if ($dga_buff_nk) {
+                $dga_buff .= "\t# Non-key attributes\n";
+                $dga_buff .= $dga_buff_nk;
+        }
+        if ($dga_buff) {
+                print $io "ALUA {\n";
+                print $io $dga_buff;
+                print $io "}\n\n";
+        }
 
-	foreach my $dgroup (sort keys %{$CURRENT{'dgroups'}}) {
-		my $dgroup_buff = "";
-		my $dgroup_attrs;
-		my $dgrp_attr_buff = "";
-		my $dgrp_attr_buff_nk = "";
+        foreach my $dgroup (sort keys %{$CURRENT{'dgroups'}}) {
+                my $dgroup_buff;
 
-		($dgroup_attrs, $errorString) = $SCST->deviceGroupAttributes($dgroup);
-		$dgrp_attr_buff = serializeKeyAttr("\t", $dgroup_attrs);
-		$dgrp_attr_buff_nk = serializeNkAttr("\t", $dgroup_attrs) if ($nonkey);
-		$dgrp_attr_buff .= "\n" if ($dgrp_attr_buff);
-		$dgrp_attr_buff_nk .= "\n" if ($dgrp_attr_buff_nk);
+                my ($dgroup_attrs, $errorString) = $SCST->deviceGroupAttributes($dgroup);
 
-		my $devices_buff = "";
+                my $dgrp_attr_buff;
+                my $dgrp_attr_buff_nk;
 
-		my $devices = $CURRENT{'dgroups'}->{$dgroup}->{'devices'};
+                $dgrp_attr_buff = serializeKeyAttr("\t", $dgroup_attrs);
+                $dgrp_attr_buff_nk = serializeNkAttr("\t", $dgroup_attrs) if ($nonkey);
+                $dgrp_attr_buff .= "\n" if ($dgrp_attr_buff);
+                $dgrp_attr_buff_nk .= "\n" if ($dgrp_attr_buff_nk);
 
-		foreach my $device (sort @{$devices}) {
-			$devices_buff .= "\tDEVICE $device\n";
-		}
+                my $devices_buff;
 
-		$devices_buff .= "\n" if ($devices_buff);
+                my $devices = $CURRENT{'dgroups'}->{$dgroup}->{'devices'};
 
-		my $tgroups = $CURRENT{'dgroups'}->{$dgroup}->{'tgroups'};
+                foreach my $device (sort @{$devices}) {
+                        $devices_buff .= "\tDEVICE $device\n";
+                }
 
-		my $tgroup_buff = "";
+                $devices_buff .= "\n" if ($devices_buff);
 
-		foreach my $tgroup (sort keys %{$tgroups}) {
-			$tgroup_buff .= "\tTARGET_GROUP $tgroup";
+                my $tgroups = $CURRENT{'dgroups'}->{$dgroup}->{'tgroups'};
 
-			my $attributes;
-			my $attribute_buff = "";
-			my $attribute_buff_nk = "";
+                my $tgroup_buff;
 
-			($attributes, $errorString) = $SCST->targetGroupAttributes($dgroup, $tgroup);
-			$attribute_buff .= serializeKeyAttr("\t\t", $attributes);
-			$attribute_buff_nk .= serializeNkAttr("\t\t", $attributes) if ($nonkey);
-			$attribute_buff .= "\n" if ($attribute_buff);
-			$attribute_buff_nk .= "\n" if ($attribute_buff_nk);
+                foreach my $tgroup (sort keys %{$tgroups}) {
+                        $tgroup_buff .= "\tTARGET_GROUP $tgroup";
 
-			my $tgts = $CURRENT{'dgroups'}->{$dgroup}->{'tgroups'}->{$tgroup}->{'targets'};
+                        my ($attributes, $errorString) = $SCST->targetGroupAttributes($dgroup, $tgroup);
 
-			my $tgt_buff = "";
+                        my $attribute_buff;
+                        my $attribute_buff_nk;
+
+                        $attribute_buff .= serializeKeyAttr("\t\t", $attributes);
+                        $attribute_buff_nk .= serializeNkAttr("\t\t", $attributes) if ($nonkey);
+                        $attribute_buff .= "\n" if ($attribute_buff);
+                        $attribute_buff_nk .= "\n" if ($attribute_buff_nk);
+
+                        my $tgts = $CURRENT{'dgroups'}->{$dgroup}->{'tgroups'}->{$tgroup}->{'targets'};
+
+                        my $tgt_buff;
                         if ($tgroup eq $PEERNAME) {
                                 $tgt_buff .= "\t\tTARGET $PEERWWPN1\n";
                                 $tgt_buff .= "\t\tTARGET $PEERWWPN2\n";
                                 $tgt_buff .= "\t\tTARGET $PEERWWPN3\n";
-                                $tgt_buff .= "\t\tTARGET $PEERWWPN4\n";			
-			}
+                                $tgt_buff .= "\t\tTARGET $PEERWWPN4\n";
+                        }
 
-			if ($attribute_buff_nk) {
-				$attribute_buff .= "\t\t# Non-key attributes\n";
-				$attribute_buff .= $attribute_buff_nk;
-			}
+                        if ($attribute_buff_nk) {
+                                $attribute_buff .= "\t\t# Non-key attributes\n";
+                                $attribute_buff .= $attribute_buff_nk;
+                        }
 
-			if ($attribute_buff || $tgt_buff) {
-				$tgroup_buff .= " {\n";
+                        if ($attribute_buff || $tgt_buff) {
+                                $tgroup_buff .= " {\n";
 
-				$tgroup_buff .= $attribute_buff;
-				$tgroup_buff .= $tgt_buff;
+                                $tgroup_buff .= $attribute_buff;
+                                $tgroup_buff .= $tgt_buff;
 
-				$tgroup_buff =~ s/\n\n$/\n/;
-				$tgroup_buff .= "\t}\n\n";
-			} else {
-				$tgroup_buff .= "\n";
-			}
-		}
+                                $tgroup_buff =~ s/\n\n$/\n/;
+                                $tgroup_buff .= "\t}\n\n";
+                        } else {
+                                $tgroup_buff .= "\n";
+                        }
+                }
 
-		if ($dgrp_attr_buff_nk) {
-			$dgrp_attr_buff .= "\t# Non-key attributes\n";
-			$dgrp_attr_buff .= $dgrp_attr_buff_nk;
-		}
+                if ($dgrp_attr_buff_nk) {
+                        $dgrp_attr_buff .= "\t# Non-key attributes\n";
+                        $dgrp_attr_buff .= $dgrp_attr_buff_nk;
+                }
 
-		$dgroup_buff .= $dgrp_attr_buff;
-		$dgroup_buff .= $devices_buff;
-		$dgroup_buff .= $tgroup_buff;
-		$dgroup_buff =~ s/\n\n$/\n/;
-		if ($dgroup_buff) {
-			print $io "DEVICE_GROUP $dgroup {\n";
-			print $io $dgroup_buff;
-			print $io "}\n\n";
-		}
-	}
+                $dgroup_buff .= $dgrp_attr_buff;
+                $dgroup_buff .= $devices_buff;
+                $dgroup_buff .= $tgroup_buff;
+                $dgroup_buff =~ s/\n\n$/\n/;
+                if ($dgroup_buff) {
+                        print $io "DEVICE_GROUP $dgroup {\n";
+                        print $io $dgroup_buff;
+                        print $io "}\n\n";
+                }
+        }
 
-	$io->flush;
-	$io->sync;
-	close $io;
+        $io->flush;
+        $io->sync;
+        close $io;
 
-	return 0;
+        return 0;
 }
 
 #***********************************************************************************************
@@ -2767,7 +2729,6 @@
 			$attribute_buff =~ s/\n+$/\n/;
 
 			if ($attribute_buff) {
-#                       print $io "HANDLER $temphandler {\n";
 				$device_buff .= " {\n";
 				$device_buff .= $attribute_buff;
 				$device_buff .= "\t}\n\n";
@@ -3334,7 +3295,6 @@
 
 sub applyConfiguration {
 	my $force = shift;
-	my $skipRead = shift;
 	my $changes = 0;
 
 	readConfigFile() if (!$CONFIG);
@@ -3349,7 +3309,7 @@
 	}
 
 	# Apply config additions
-	$changes += applyConfigDevices($CONFIG, $force, $skipRead);
+	$changes += applyConfigDevices($CONFIG, $force);
 	$changes += applyConfigAssignments($CONFIG, $force);
 	$changes += applyConfigAlua($CONFIG, $force);
 	$changes += applyConfigDeviceGroups($CONFIG, $force);
@@ -3378,7 +3338,6 @@
 sub applyConfigDevices {
 	my $config = shift;
 	my $deletions = shift;
-	my $skipRead = shift;
 	my $changes = 0;
 	my $errorString;
 
@@ -3435,10 +3394,8 @@
 							closeDevice($handler, $device, $deletions);
 							openDevice($handler, $device, $create_attrs);
 							$changes += 2;
-							if (!$skipRead){
-								my $rc = readWorkingConfig($deletions);
-								exit $rc if ($rc);
-							}
+							my $rc = readWorkingConfig($deletions);
+							exit $rc if ($rc);
 						} else {
 							print "\t  -> Use -force to re-open device with new attributes. ".
 							  "NOTE: This will disrupt all initiators using this device.\n";
@@ -3475,11 +3432,6 @@
 
 	}
 
-	if ($skipRead && $deletions && $changes) {
-		my $rc = readWorkingConfig($deletions);
-		exit $rc if ($rc);
-	}
-
 	return $changes;
 }
 
@@ -3584,7 +3536,8 @@
 
 				if (!defined($$config{'TARGET_DRIVER'}->{$driver}->{'TARGET'}->{$target}->{'GROUP'}->{$group})) {
 					if ($deletions) {
-						removeGroup($driver, $target, $group);
+						removeGroup($driver, $target,
+							    $group, TRUE);
 						$changes++;
 					} else {
 						print "\t-> Group '$group' is not in configuration. Use -force to remove.\n";
diff --git a/scst/srpt/Makefile b/scst/srpt/Makefile
index 5e398ea..5f2a97a 100644
--- a/scst/srpt/Makefile
+++ b/scst/srpt/Makefile
@@ -182,6 +182,12 @@
 	echo "$(call run_conftest,cm_listen,				\
 		-DIB_CM_LISTEN_TAKES_FOURTH_ARG)" >"$@"
 
+conftest/cm_listen_2/result-$(KVER).txt:				\
+	conftest/cm_listen_2/cm_listen_2.c				\
+	conftest/cm_listen_2/Kbuild
+	echo "$(call run_conftest,cm_listen_2,				\
+		-DIB_CM_LISTEN_TAKES_THIRD_ARG)" >"$@"
+
 conftest/create_cq/result-$(KVER).txt:					\
 	conftest/create_cq/create_cq.c					\
 	conftest/create_cq/Kbuild
diff --git a/scst/srpt/README b/scst/srpt/README
index 334fb5f..b574574 100644
--- a/scst/srpt/README
+++ b/scst/srpt/README
@@ -175,10 +175,8 @@
 Target names
 ------------
 
-The name assigned by the ib_srpt target driver to an SCST target is either
-ib_srpt_target_<n>, the node GUID of a HCA in hexadecimal form with a colon
-after every fourth digit or the port GID with a colon afer every fourth
-digit. The HCA node GUID and the port GIDs can be obtained via the
+The name assigned by the ib_srpt target driver to an SCST target is the port
+GID with a colon afer every fourth digit. The port GIDs can be obtained via the
 ibv_devinfo command. An example:
 
 # ibv_devinfo -v | grep -E '[^a-z]port:|guid|GID'
diff --git a/scst/iscsi-scst/conftest/cm_listen/Kbuild b/scst/srpt/conftest/cm_listen_2/Kbuild
similarity index 69%
rename from scst/iscsi-scst/conftest/cm_listen/Kbuild
rename to scst/srpt/conftest/cm_listen_2/Kbuild
index 50520e3..e997664 100644
--- a/scst/iscsi-scst/conftest/cm_listen/Kbuild
+++ b/scst/srpt/conftest/cm_listen_2/Kbuild
@@ -1,3 +1,3 @@
 LINUXINCLUDE := $(CONFTEST_CFLAGS) $(LINUXINCLUDE)
 
-obj-m += cm_listen.o
+obj-m += cm_listen_2.o
diff --git a/scst/iscsi-scst/conftest/cm_listen/cm_listen.c b/scst/srpt/conftest/cm_listen_2/cm_listen_2.c
similarity index 76%
rename from scst/iscsi-scst/conftest/cm_listen/cm_listen.c
rename to scst/srpt/conftest/cm_listen_2/cm_listen_2.c
index 4f871ef..927af0b 100644
--- a/scst/iscsi-scst/conftest/cm_listen/cm_listen.c
+++ b/scst/srpt/conftest/cm_listen_2/cm_listen_2.c
@@ -3,7 +3,7 @@
 
 static int __init modinit(void)
 {
-	return ib_cm_listen(NULL, 0, 0, NULL);
+	return ib_cm_listen(NULL, 0, 0);
 }
 
 module_init(modinit);
diff --git a/scst/srpt/src/ib_srpt.c b/scst/srpt/src/ib_srpt.c
index 9f60e63..a0298d5 100644
--- a/scst/srpt/src/ib_srpt.c
+++ b/scst/srpt/src/ib_srpt.c
@@ -47,11 +47,7 @@
 #if !defined(INSIDE_KERNEL_TREE)
 #include <linux/version.h>
 #endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37)
 #include <linux/atomic.h>
-#else
-#include <asm/atomic.h>
-#endif
 #include <rdma/ib_cache.h>
 #include "ib_srpt.h"
 #define LOG_PREFIX "ib_srpt" /* Prefix for SCST tracing macros. */
@@ -63,8 +59,8 @@
 
 /* Name of this kernel module. */
 #define DRV_NAME		"ib_srpt"
-#define DRV_VERSION		"3.5.0" "#" __stringify(OFED_FLAVOR)
-#define DRV_RELDATE		"21 December 2020"
+#define DRV_VERSION		"3.7.0" "#" __stringify(OFED_FLAVOR)
+#define DRV_RELDATE		"26 December 2022"
 #if defined(CONFIG_SCST_DEBUG) || defined(CONFIG_SCST_TRACING)
 /* Flags to be used in SCST debug tracing statements. */
 #define DEFAULT_SRPT_TRACE_FLAGS (TRACE_OUT_OF_MEM | TRACE_MINOR \
@@ -112,12 +108,7 @@
 MODULE_PARM_DESC(srp_max_rsp_size,
 		 "Maximum size of SRP response messages in bytes.");
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) \
-	|| defined(RHEL_MAJOR) && RHEL_MAJOR -0 <= 5
-static int use_srq;
-#else
 static bool use_srq;
-#endif
 module_param(use_srq, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(use_srq, "Whether or not to use SRQ");
 
@@ -130,27 +121,6 @@
 module_param(srpt_sq_size, int, 0444);
 MODULE_PARM_DESC(srpt_sq_size, "Per-channel send queue (SQ) size.");
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) \
-	|| defined(RHEL_MAJOR) && RHEL_MAJOR -0 <= 5
-static int use_port_guid_in_session_name;
-#else
-static bool use_port_guid_in_session_name;
-#endif
-module_param(use_port_guid_in_session_name, bool, 0444);
-MODULE_PARM_DESC(use_port_guid_in_session_name,
-		 "Use target port ID in the session name such that"
-		 " redundant paths between multiport systems can be masked.");
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) \
-	|| defined(RHEL_MAJOR) && RHEL_MAJOR -0 <= 5
-static int use_node_guid_in_target_name;
-#else
-static bool use_node_guid_in_target_name;
-#endif
-module_param(use_node_guid_in_target_name, bool, 0444);
-MODULE_PARM_DESC(use_node_guid_in_target_name,
-		 "Use HCA node GUID as SCST target name.");
-
 #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
 static int srpt_get_u64_x(char *buffer, struct kernel_param *kp)
 #else
@@ -164,10 +134,6 @@
 MODULE_PARM_DESC(srpt_service_guid,
 		 "Using this value for ioc_guid, id_ext, and cm_listen_id instead of using the node_guid of the first HCA.");
 
-static unsigned int max_sge_delta;
-module_param(max_sge_delta, uint, 0444);
-MODULE_PARM_DESC(max_sge_delta, "Number to subtract from max_sge (obsolete).");
-
 /*
  * Note: changing any of the two constants below into SCST_CONTEXT_DIRECT is
  * dangerous because it might cause IB completions to be processed too late
@@ -357,15 +323,10 @@
 
 	switch (event->event) {
 	case IB_EVENT_COMM_EST:
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) || defined(BACKPORT_LINUX_WORKQUEUE_TO_2_6_19)
 		if (ch->using_rdma_cm)
 			rdma_notify(ch->rdma_cm.cm_id, event->event);
 		else
 			ib_cm_notify(ch->ib_cm.cm_id, event->event);
-#else
-		/* Vanilla 2.6.19 kernel (or before) without OFED. */
-		pr_err("how to perform ib_cm_notify() on a vanilla 2.6.18 kernel ???\n");
-#endif
 		break;
 	case IB_EVENT_QP_LAST_WQE_REACHED:
 		pr_debug("%s-%d, state %s: received Last WQE event.\n",
@@ -1095,7 +1056,6 @@
 
 static inline void *srpt_get_desc_buf(struct srp_cmd *srp_cmd)
 {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
 	/*
 	 * The pointer computations below will only be compiled correctly
 	 * if srp_cmd::add_data is declared as s8*, u8*, s8[] or u8[], so check
@@ -1103,7 +1063,6 @@
 	 */
 	BUILD_BUG_ON(!__same_type(srp_cmd->add_data[0], (s8)0) &&
 		     !__same_type(srp_cmd->add_data[0], (u8)0));
-#endif
 
 	/*
 	 * According to the SRP spec, the lower two bits of the 'ADDITIONAL
@@ -2281,11 +2240,7 @@
 		goto out;
 
 retry:
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) && \
-	!defined(RHEL_RELEASE_CODE)
-	ch->cq = ib_create_cq(sdev->device, srpt_completion, NULL, ch,
-			      ch->rq_size + sq_size);
-#elif !defined(IB_CREATE_CQ_HAS_INIT_ATTR)
+#if !defined(IB_CREATE_CQ_HAS_INIT_ATTR)
 	ch->cq = ib_create_cq(sdev->device, srpt_completion, NULL, ch,
 			      ch->rq_size + sq_size, ch->comp_vector);
 #else
@@ -2604,14 +2559,8 @@
 
 	WARN_ON_ONCE(irqs_disabled());
 
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18)
-	WARN_ON(!sdev || !req);
-	if (!sdev || !req)
-		return -EINVAL;
-#else
 	if (WARN_ON(!sdev || !req))
 		return -EINVAL;
-#endif
 
 	it_iu_len = be32_to_cpu(req->req_it_iu_len);
 
@@ -3887,18 +3836,9 @@
 	srpt_put_send_ioctx(ioctx);
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) && !defined(BACKPORT_LINUX_WORKQUEUE_TO_2_6_19)
-/* A vanilla 2.6.19 or older kernel without backported OFED kernel headers. */
-static void srpt_refresh_port_work(void *ctx)
-#else
 static void srpt_refresh_port_work(struct work_struct *work)
-#endif
 {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) && !defined(BACKPORT_LINUX_WORKQUEUE_TO_2_6_19)
-	struct srpt_port *sport = ctx;
-#else
 	struct srpt_port *sport = container_of(work, struct srpt_port, work);
-#endif
 
 	srpt_refresh_port(sport);
 }
@@ -4013,9 +3953,7 @@
 
 	if (!sport)
 		goto out;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 28)
-	res = cpumask_scnprintf(buf, PAGE_SIZE, sport->comp_v_mask);
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0)
 	res = cpumask_scnprintf(buf, PAGE_SIZE, &sport->comp_v_mask);
 #else
 	res = scnprintf(buf, PAGE_SIZE, "%*pb",
@@ -4046,11 +3984,7 @@
 	res = -ENOMEM;
 	if (!alloc_cpumask_var(&mask, GFP_KERNEL))
 		goto out;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
-	res = bitmap_parse(buf, count, cpumask_bits(mask), nr_cpumask_bits);
-#else
 	res = cpumask_parse(buf, mask);
-#endif
 	if (res)
 		goto free_mask;
 	res = -EINVAL;
@@ -4110,7 +4044,6 @@
 	if (!sport)
 		goto out;
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 37) /* commit a3f5adaf4 */
 	switch (rdma_port_get_link_layer(sport->sdev->device, sport->port)) {
 	case IB_LINK_LAYER_INFINIBAND:
 		lln = "InfiniBand";
@@ -4122,7 +4055,6 @@
 	default:
 		break;
 	}
-#endif
 	res = sprintf(buf, "%s\n", lln);
 
 out:
@@ -4268,7 +4200,6 @@
 	return sprintf(buf, "%s\n", get_ch_state_name(ch->state));
 }
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) || defined(RHEL_RELEASE_CODE)
 static ssize_t show_comp_vector(struct kobject *kobj,
 				struct kobj_attribute *attr, char *buf)
 {
@@ -4279,7 +4210,6 @@
 	ch = scst_sess_get_tgt_priv(sess);
 	return ch ? sprintf(buf, "%u\n", ch->comp_vector) : -ENOENT;
 }
-#endif
 
 static const struct kobj_attribute srpt_req_lim_attr =
 	__ATTR(req_lim,       S_IRUGO, show_req_lim,       NULL);
@@ -4287,18 +4217,14 @@
 	__ATTR(req_lim_delta, S_IRUGO, show_req_lim_delta, NULL);
 static const struct kobj_attribute srpt_ch_state_attr =
 	__ATTR(ch_state, S_IRUGO, show_ch_state, NULL);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) || defined(RHEL_RELEASE_CODE)
 static const struct kobj_attribute srpt_comp_vector_attr =
 	__ATTR(comp_vector, S_IRUGO, show_comp_vector, NULL);
-#endif
 
 static const struct attribute *srpt_sess_attrs[] = {
 	&srpt_req_lim_attr.attr,
 	&srpt_req_lim_delta_attr.attr,
 	&srpt_ch_state_attr.attr,
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) || defined(RHEL_RELEASE_CODE)
 	&srpt_comp_vector_attr.attr,
-#endif
 	NULL
 };
 
@@ -4397,9 +4323,7 @@
 	srq_attr.attr.max_wr = sdev->srq_size;
 	srq_attr.attr.max_sge = 1;
 	srq_attr.attr.srq_limit = 0;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0)
 	srq_attr.srq_type = IB_SRQT_BASIC;
-#endif
 
 	sdev->srq = use_srq ? ib_create_srq(sdev->pd, &srq_attr) :
 		ERR_PTR(-EOPNOTSUPP);
@@ -4448,15 +4372,7 @@
 		sport->sdev = sdev;
 		sport->port = i;
 		srpt_init_sport(sport, sdev->device);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20) && !defined(BACKPORT_LINUX_WORKQUEUE_TO_2_6_19)
-		/*
-		 * A vanilla 2.6.19 or older kernel without backported OFED
-		 * kernel headers.
-		 */
-		INIT_WORK(&sport->work, srpt_refresh_port_work, sport);
-#else
 		INIT_WORK(&sport->work, srpt_refresh_port_work);
-#endif
 		ret = srpt_refresh_port(sport);
 		if (ret) {
 			pr_err("MAD registration failed for %s-%d.\n",
@@ -4487,9 +4403,12 @@
 	 * in the system as service_id; therefore, the target_id will change
 	 * if this HCA is gone bad and replaced by different HCA
 	 */
-	ret = ib_cm_listen(sdev->cm_id, cpu_to_be64(srpt_service_guid), 0
+	ret = ib_cm_listen(sdev->cm_id, cpu_to_be64(srpt_service_guid)
 #ifdef IB_CM_LISTEN_TAKES_FOURTH_ARG
+			   , 0
 			   , NULL
+#elif defined(IB_CM_LISTEN_TAKES_THIRD_ARG)
+			   , 0
 #endif
 			   );
 	if (ret) {
@@ -4567,16 +4486,7 @@
 
 	/* Cancel any work queued by the just unregistered IB event handler. */
 	for (i = 0; i < sdev->device->phys_port_cnt; i++)
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
 		cancel_work_sync(&sdev->port[i].work);
-#else
-		/*
-		 * cancel_work_sync() was introduced in kernel 2.6.22. Older
-		 * kernels do not have a facility to cancel scheduled work, so
-		 * wait until the scheduled work finished.
-		 */
-		flush_scheduled_work();
-#endif
 
 	ib_destroy_cm_id(sdev->cm_id);
 
@@ -4667,7 +4577,7 @@
 		goto out;
 	}
 
-	srpt_wq = alloc_workqueue("srpt", WQ_SYSFS | WQ_NON_REENTRANT, 0);
+	srpt_wq = alloc_workqueue("srpt", WQ_SYSFS, 0);
 	if (!srpt_wq) {
 		pr_err("Couldn't allocate the ib_srpt workqueue\n");
 		ret = -ENOMEM;
@@ -4685,11 +4595,7 @@
 	if (rdma_cm_port) {
 		struct sockaddr_in addr;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0) && \
-	(!defined(RHEL_MAJOR) || RHEL_MAJOR -0 < 6)
-		rdma_cm_id = rdma_create_id(srpt_rdma_cm_handler, NULL,
-					    RDMA_PS_TCP);
-#elif !RDMA_CREATE_ID_TAKES_NET_ARG
+#if !RDMA_CREATE_ID_TAKES_NET_ARG
 		rdma_cm_id = rdma_create_id(srpt_rdma_cm_handler, NULL,
 					    RDMA_PS_TCP, IB_QPT_RC);
 #else
diff --git a/scst/srpt/src/ib_srpt.h b/scst/srpt/src/ib_srpt.h
index a6a1849..a52cc43 100644
--- a/scst/srpt/src/ib_srpt.h
+++ b/scst/srpt/src/ib_srpt.h
@@ -204,12 +204,6 @@
 	RDMA_COMPL_TIMEOUT_S = 80,
 };
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 1, 0) &&			\
-	!defined(HAVE_IB_EVENT_GID_CHANGE)
-/* See also patch "IB/core: Add GID change event" (commit 761d90ed4). */
-enum { IB_EVENT_GID_CHANGE = 18 };
-#endif
-
 enum srpt_opcode {
 	SRPT_RECV,
 	SRPT_SEND,
@@ -445,9 +439,7 @@
 	struct list_head	list;
 	struct list_head	cmd_wait_list;
 	uint16_t		pkey;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) || defined(RHEL_RELEASE_CODE)
 	u16			comp_vector;
-#endif
 	bool			using_rdma_cm;
 	bool			processing_wait_list;
 	struct scst_session	*sess;
diff --git a/scst/usr/fileio/Makefile b/scst/usr/fileio/Makefile
index 54fc649..a5135e3 100644
--- a/scst/usr/fileio/Makefile
+++ b/scst/usr/fileio/Makefile
@@ -18,12 +18,6 @@
         PREFIX=/usr/local
 endif
 
-REVISION ?= $(shell if svn info >/dev/null 2>&1;                        \
-	then svn info | sed -n 's/^Revision:[[:blank:]]*/r/p';\
-	else git log | grep -c ^commit;                      \
-	fi)
-VERSION = $(shell echo -n "$$(sed -n 's/^\#define[[:blank:]]VERSION_STR[[:blank:]]*\"\([^-]*\).*\"/\1/p' ../include/version.h).$(REVISION)")
-
 SHELL=/bin/bash
 
 SRCS_F = fileio.c common.c debug.c crc32.c
@@ -38,12 +32,8 @@
 DEBUG_INC_DIR := ../include
 INSTALL_DIR := $(DESTDIR)$(PREFIX)/bin/scst
 
-RPM_INC_DIR = ./scst/include
-RPM_INC_DIR_EXTRA = ./include
-RPM_ARC_DIR_EXTRA = ../include
-
 CFLAGS += -O2 -Wall -Wextra -Wno-unused-parameter -Wstrict-prototypes \
-	-I$(SCST_INC_DIR) -I$(DEBUG_INC_DIR) -I$(RPM_INC_DIR) -I$(RPM_INC_DIR_EXTRA) -D_GNU_SOURCE -D__USE_FILE_OFFSET64 \
+	-I$(SCST_INC_DIR) -I$(DEBUG_INC_DIR) -D_GNU_SOURCE -D__USE_FILE_OFFSET64 \
 	-D__USE_LARGEFILE64
 PROGS = fileio_tgt
 LIBS = -lpthread
@@ -105,8 +95,6 @@
 
 extraclean: clean
 	rm -f *.orig *.rej
-	rm -f *.orig *.rej *.tar.bz2
-	@find . -type d -name rpmbuilddir | xargs rm -rf
 
 2release:
 	-$(MAKE) clean
@@ -118,25 +106,6 @@
 	-$(MAKE) clean
 
 release-archive:
-	../../scripts/generate-release-archive fileio_tgt $(VERSION)
-
-rpm-archive:
-	../../scripts/generate-release-archive fileio_tgt $(VERSION) \
-	"$$(../../scripts/list-source-files)" $(SCST_INC_DIR)/* $(RPM_ARC_DIR_EXTRA)
-
-rpm:
-	name=fileio_tgt &&                                              \
-	rpmtopdir="$$(if [ $$(id -u) = 0 ]; then echo /usr/src/packages;\
-		else echo $$PWD/rpmbuilddir; fi)" &&                    \
-	$(MAKE) rpm-archive &&                                          \
-	for d in BUILD RPMS SOURCES SPECS SRPMS; do                     \
-		mkdir -p $${rpmtopdir}/$$d;                                   \
-	done &&                                                         \
-	cp $${name}-$(VERSION).tar.bz2 $${rpmtopdir}/SOURCES &&         \
-	MAKE="$(MAKE)"                                                  \
-	rpmbuild --define="%_topdir $${rpmtopdir}"                      \
-		--define="%rpm_version $(VERSION)"                      \
-		-ba $${name}.spec &&                                    \
-	rm -f $${name}-$(VERSION).tar.bz2
+	../../scripts/generate-release-archive fileio_tgt "$$(sed -n 's/^#define[[:blank:]]VERSION_STR[[:blank:]]*\"\([^\"]*\)\".*/\1/p' ../include/version.h)"
 
 .PHONY: all install uninstall clean extraclean 2release 2debug 2perf
diff --git a/scst/usr/fileio/README b/scst/usr/fileio/README
index bacdd13..fa18414 100644
--- a/scst/usr/fileio/README
+++ b/scst/usr/fileio/README
@@ -1,7 +1,7 @@
 User space FILEIO handler
 =========================
 
-Version 3.5.0, 21 December 2020
+Version 3.7.0, 26 December 2022
 ----------------------------
 
 User space program fileio_tgt uses interface of SCST's scst_user dev
diff --git a/scst/usr/fileio/common.c b/scst/usr/fileio/common.c
index b2d242f..48d031c 100644
--- a/scst/usr/fileio/common.c
+++ b/scst/usr/fileio/common.c
@@ -792,7 +792,7 @@
 		struct scst_user_reply_cmd replies[MULTI_CMDS_CNT];
 		struct scst_user_get_multi multi_cmd;
 		struct scst_user_get_cmd cmds[MULTI_CMDS_CNT];
-	} multi;
+	} multi = {};
 
 	TRACE_ENTRY();
 
diff --git a/scst/usr/fileio/common.h b/scst/usr/fileio/common.h
index 6b0efdb..7c8df98 100644
--- a/scst/usr/fileio/common.h
+++ b/scst/usr/fileio/common.h
@@ -30,7 +30,7 @@
 /* 8 byte ASCII Vendor */
 #define VENDOR				"ACTIFIO"
 /* 4 byte ASCII Product Revision Level - left aligned */
-#define FIO_REV				"350 "
+#define FIO_REV				"370 "
 
 #define MAX_USN_LEN			(20+1) /* For '\0' */
 
diff --git a/scst/usr/fileio/fileio_tgt.spec b/scst/usr/fileio/fileio_tgt.spec
deleted file mode 100644
index 0cdabfc..0000000
--- a/scst/usr/fileio/fileio_tgt.spec
+++ /dev/null
@@ -1,53 +0,0 @@
-%define make %{expand:%%(echo ${MAKE:-make})}
-%define name fileio_tgt
-
-Name:          %{name}
-Version:       %{rpm_version}
-Release:       1
-Summary:       SCST file IO user-mode backend
-Group:         System/User
-License:       GPLv2
-URL:           http://scst.sourceforge.net
-Source:                %{name}-%{rpm_version}.tar.bz2
-BuildRoot:     %{_tpmpath}/%{name}-%{rpm_version}-%{name}-build
-
-%description
-User space program fileio_tgt uses interface of SCST's scst_user dev
-handler and allows to see how it works in various modes. Fileio_tgt
-provides mostly the same functionality as the kernel space SCST's
-scst_vdisk handler with the only exceptions that it supports O_DIRECT
-mode and doesn't support BLOCKIO one. O_DIRECT mode is basically the
-same as BLOCKIO, but also supports files, so for some loads it could be
-significantly faster, than the regular FILEIO access. All the words
-about BLOCKIO mode from SCST's README file apply to O_DIRECT mode as
-well.
-
-%prep
-%setup -q
-
-
-%build
-%{make} DESTDIR=%{buildroot}
-
-
-%install
-%{make} install INSTALL_DIR=%{buildroot}/opt/act/bin
-
-
-%clean
-rm -rf %{buildroot}
-
-
-%pre
-rm -rf %{buildroot}/opt/act/bin/*
-
-
-%files
-%defattr(-,root,root,-)
-%dir /opt/act/bin
-/opt/act/bin/fileio_tgt
-
-
-%changelog
-* Mon Apr 20 2020 Jim McCarthy <jim.mccarthy@actifio.com>
-* - Initial spec file.
diff --git a/scst/usr/include/version.h b/scst/usr/include/version.h
index df6d06f..5eca6d7 100644
--- a/scst/usr/include/version.h
+++ b/scst/usr/include/version.h
@@ -19,6 +19,6 @@
 #ifndef __VERSION_H
 #define __VERSION_H
 
-#define VERSION_STR "3.5.0"
+#define VERSION_STR "3.7.0"
 
 #endif /* __VERSION_H */
diff --git a/scst/www/Makefile b/scst/www/Makefile
new file mode 100644
index 0000000..069466a
--- /dev/null
+++ b/scst/www/Makefile
@@ -0,0 +1,2 @@
+check:
+	find -name '*.html' | while read f; do tidy -e < "$$f" | sed "s|^-e|$$f|"; done
diff --git a/scst/www/comparison.html b/scst/www/comparison.html
index 4b3374e..4bb50d8 100644
--- a/scst/www/comparison.html
+++ b/scst/www/comparison.html
@@ -13,15 +13,15 @@
 <!-- wrap starts here -->
 <div id="wrap">
 	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
+		<div class="logoimg"></div><h1 id="logo"><span class="orange">&nbsp;</span></h1>
 		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
 	</div>
 
 	<div id="menu">
 		<ul>
 			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
+			<li><a href="https://github.com/SCST-project/scst">Main</a></li>
+			<li><a href="https://github.com/SCST-project/scst/releases">News</a></li>
 			<li><a href="targets.html">Drivers</a></li>
 			<li><a href="downloads.html">Downloads</a></li>
 			<li><a href="contributing.html">Contributing</a></li>
@@ -55,7 +55,7 @@
 
 				<p><small>As on June 2011, briefly reviewed April 2013.</small></p>
 
-<table bgcolor="#F0F0F0" border="1" cellspacing="1" cellpadding="7" style="text-align:center" width="620">
+<table bgcolor="#F0F0F0" border="1" cellspacing="1" cellpadding="7" style="text-align:center" width="620" summary="">
 
 <tr>
 <td>
@@ -269,9 +269,6 @@
 									(not completed) </td>	<td> - </td>		<td> - </td>		<td> - </td>
 </tr>
 <tr>
-<td align="left"><b>IBM pSeries Virtual SCSI</b></td>			<td> + </td>		<td> + </td>		<td> - </td>		<td> Preliminary </td>
-</tr>
-<tr>
 <td align="left"><b>Local access to emulated backstorage devices
 <sup><A HREF="#6">6</A></sup></b></td>					<td> scst_local </td>	<td> - </td>		<td> - </td>		<td> tcm_loop </td>
 </tr>
@@ -546,7 +543,7 @@
 <!-- wrap ends here -->
 <!-- footer starts here -->
 <div id="footer">
-	<p>&copy; Copyright 2004 - 2020 <b><font color="#EC981F">Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
+	<p>&copy; Copyright 2004 - 2021 <b><font color="#EC981F">Vladislav Bolkhovitin, Bart Van Assche &amp; others</font></b>&nbsp;&nbsp;
 	   Design by: <b><font color="#EC981F">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 </div>
 <!-- footer ends here -->
diff --git a/scst/www/contributing.html b/scst/www/contributing.html
index f784d44..df6f092 100644
--- a/scst/www/contributing.html
+++ b/scst/www/contributing.html
@@ -11,14 +11,15 @@
 <body>
 <div id="wrap">
 	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
+		<div class="logoimg"></div><h1 id="logo"><span class="orange">&nbsp;</span></h1>
 		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
 	</div>
+
 	<div id="menu">
 		<ul>
 			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
+			<li><a href="https://github.com/SCST-project/scst">Main</a></li>
+			<li><a href="https://github.com/SCST-project/scst/releases">News</a></li>
 			<li><a href="targets.html">Drivers</a></li>
 			<li><a href="downloads.html">Downloads</a></li>
 			<li id="current"><a href="contributing.html">Contributing</a></li>
@@ -26,6 +27,7 @@
 			<li><a href="users.html">Users</a></li>
 		</ul>
 	</div>
+
 	<div id="content-wrap">
 	  		<div id="main">
 				<h1>Contributing to SCST</h1>
@@ -183,7 +185,7 @@
 <!-- wrap ends here -->
 <!-- footer starts here -->
 <div id="footer">
-	<p>&copy; Copyright 2004 - 2020 <b><font color="#EC981F">Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
+	<p>&copy; Copyright 2004 - 2021 <b><font color="#EC981F">Vladislav Bolkhovitin, Bart Van Assche &amp; others</font></b>&nbsp;&nbsp;
 	   Design by: <b><font color="#EC981F">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 </div>
 <!-- footer ends here -->
diff --git a/scst/www/downloads.html b/scst/www/downloads.html
index f48d8c5..bd5b724 100644
--- a/scst/www/downloads.html
+++ b/scst/www/downloads.html
@@ -13,15 +13,15 @@
 <!-- wrap starts here -->
 <div id="wrap">
 	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
+		<div class="logoimg"></div><h1 id="logo"><span class="orange">&nbsp;</span></h1>
 		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
 	</div>
 
 	<div id="menu">
 		<ul>
 			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
+			<li><a href="https://github.com/SCST-project/scst">Main</a></li>
+			<li><a href="https://github.com/SCST-project/scst/releases">News</a></li>
 			<li><a href="targets.html">Drivers</a></li>
 			<li id="current"><a href="downloads.html">Downloads</a></li>
 			<li><a href="contributing.html">Contributing</a></li>
@@ -35,10 +35,9 @@
 			<div id="main">
 				<h1>SCST Downloads</h1>
 
-				<p>The latest stable version of SCST is 3.3.
-				  The latest updates for that version are
-				  available on the 3.3.x branch in the SVN
-				  repository.</p>
+				<p>The latest stable version of SCST is 3.7.
+				  Updates for that version are available on
+				  the 3.7.x branch in the Git repository.</p>
 
 				<p>Debian packages can be built by running
 				"make dpkg". RPMs can be built by running
@@ -48,30 +47,30 @@
 
 				<p>There is also a well done user space port, which you can find <a href="https://github.com/DavidButterfield/SCST-Usermode-Adaptation">here</a>.</p>
 
-				<p>The latest development version of SCST is 3.4. You can download it as well as target drivers and user space
-				utilities directly from the SCST SVN. You can access it using either
-				<a href="http://sourceforge.net/p/scst/svn/HEAD/tree/trunk">web-based SVN repository viewer</a> or using anonymous access:</p>
+				<p>The latest development version of SCST is
+				3.8. That version including target drivers and
+				user space utilities can be downloaded
+				directly from the SCST Git repository.</p>
+				<p>You can access it using either
+				<a href="https://github.com/SCST-project/scst">GitHub repository viewer</a> or using 'git clone':</p>
+				<p><code>git clone https://github.com/SCST-project/scst.git</code></p>
 
+				<p>Also you can find in the SCST Git reposity the latest updates for the stable branches.</p>
+
+				<p>History of the pre-Git SCST development is available in SCST SVN repository, which is accessible using
+				<a href="http://sourceforge.net/p/scst/svn/HEAD/tree/trunk">web-based SVN repository viewer</a>, or using anonymous access:</p>
 				<p><code>svn checkout svn://svn.code.sf.net/p/scst/svn/trunk scst-trunk</code></p>
 
-				<p>Also you can find in the SCST SVN the latest updates for the stable branches. More information about accessing SVN repository may be found
-				<a href="https://sourceforge.net/p/forge/documentation/svn/">here</a>. Or, alternatively, you can download it as a GNU tarball from
-				<a href="http://sourceforge.net/p/scst/svn/HEAD/tarball?path=/branches/3.3.x">here</a>.</p>
-
-				<p>History of the pre-SVN SCST development is available in SCST CVS repository, which is accessible using
-				<a href="http://scst.cvs.sourceforge.net">web-based CVS repository viewer</a>, or anonymous CVS access.</p>
-
 				<p class="post-footer align-right">
-					<a href="http://sourceforge.net/project/showfiles.php?group_id=110471" class="readmore">Download released versions</a>
-					<a href="http://sourceforge.net/p/scst/svn/HEAD/tree/" class="readmore">SCST SVN Repository</a>
-					<a href="https://github.com/bvanassche/scst" class="readmore">Official git mirror</a>
+					<a href="https://github.com/SCST-project/scst/releases" class="readmore">Download released versions</a>
+					<a href="https://github.com/SCST-project/scst" class="readmore">SCST Git Repository</a>
 				</p>
 			</div>
 	</div>
 </div>
 <!-- footer starts here -->
 <div id="footer">
-	<p>&copy; Copyright 2004 - 2020<b><font class="names"> Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
+	<p>&copy; Copyright 2004 - 2021<b><font class="names"> Vladislav Bolkhovitin, Bart Van Assche &amp; others</font></b>&nbsp;&nbsp;
 	Design by: <b><font class="names">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 </div>
 <!-- footer ends here -->
diff --git a/scst/www/handler_fileio_tgt.html b/scst/www/handler_fileio_tgt.html
index 6e11239..758e817 100644
--- a/scst/www/handler_fileio_tgt.html
+++ b/scst/www/handler_fileio_tgt.html
@@ -13,15 +13,15 @@
 <!-- wrap starts here -->
 <div id="wrap">
 	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
+		<div class="logoimg"></div><h1 id="logo"><span class="orange">&nbsp;</span></h1>
 		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
 	</div>
 
 	<div id="menu">
 		<ul>
 			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
+			<li><a href="https://github.com/SCST-project/scst">Main</a></li>
+			<li><a href="https://github.com/SCST-project/scst/releases">News</a></li>
 			<li id="current"><a href="targets.html">Drivers</a></li>
 			<li><a href="downloads.html">Downloads</a></li>
 			<li><a href="contributing.html">Contributing</a></li>
@@ -38,13 +38,9 @@
 					<li><a href="target_iscsi.html">ISCSI-SCST with iSER</a></li>
 					<li><a href="target_qla2x00t.html">QLogic FC qla2x00t</a></li>
 					<li><a href="target_srp.html">SCSI RDMA Protocol (SRP)</a></li>
-					<li><a href="target_mvsas.html">Marvell SAS adapters</a></li>
 					<li><a href="target_emulex.html">Emulex FC/FCoE</a></li>
-					<li><a href="target_lsi.html">LSI/MPT adapters</a></li>
 					<li><a href="target_fcoe.html">FCoE Target</a></li>
 					<li><a href="target_local.html">Local Target Driver</a></li>
-					<li><a href="target_ibmvscsi.html">IBM pSeries Virtual SCSI</a></li>
-					<li><a href="target_old.html">Old Unsupported</a></li>
 				</ul>
 				<h1>User utilities</h1>
 				<ul class="sidemenu">
@@ -66,18 +62,20 @@
 				All the words about BLOCKIO mode from SCST's README file apply to
 				O_DIRECT mode as well.</p>
 
-				<p>You can find the latest development version of this handler in the SCST SVN. See the download page how to setup
+				<p>You can find the latest development version
+				of this handler in the SCST Git
+				repository. See the download page how to setup
 				access to it.</p>
 				<p class="post-footer align-right">
 					<a href="downloads.html" class="readmore">Download</a>
-					<a href="http://sourceforge.net/p/scst/svn/HEAD/tree/" class="readmore">SCST SVN Repository</a>
+					<a href="https://github.com/SCST-project/scst" class="readmore">SCST Git Repository</a>
 				</p>
 	  		</div>
 	</div>
 </div>
 <!-- footer starts here -->
 <div id="footer">
-	<p>&copy; Copyright 2004 - 2020 <b><font class="names"> Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
+	<p>&copy; Copyright 2004 - 2021 <b><font class="names"> Vladislav Bolkhovitin, Bart Van Assche &amp; others</font></b>&nbsp;&nbsp;
 	Design by: <b><font class="names">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 </div>
 <!-- footer ends here -->
diff --git a/scst/www/images/sourceforge_badges/oss-community-choice-white.svg b/scst/www/images/sourceforge_badges/oss-community-choice-white.svg
new file mode 100644
index 0000000..76d73fd
--- /dev/null
+++ b/scst/www/images/sourceforge_badges/oss-community-choice-white.svg
@@ -0,0 +1 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1500 1500"><defs><style>.cls-1{fill:#898989;}.cls-2{fill:#eae9ee;stroke:#c7c2bd;}.cls-2,.cls-3,.cls-5{stroke-miterlimit:10;}.cls-2,.cls-5{stroke-width:5.1px;}.cls-3{fill:#fff;stroke-width:25.5px;}.cls-3,.cls-5{stroke:#898989;}.cls-4{fill:#3f3f3f;}.cls-5{fill:url(#linear-gradient);}.cls-6{fill:#ff6700;}</style><linearGradient id="linear-gradient" x1="78.74" y1="1183.39" x2="1421.26" y2="1183.39" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#eaeaec"/><stop offset="1" stop-color="#fffefe"/></linearGradient></defs><polygon class="cls-1" points="1421.26 1032.79 78.74 1032.79 200.73 965.7 1299.27 965.7 1421.26 1032.79"/><circle class="cls-2" cx="750" cy="750" r="655.64"/><circle class="cls-3" cx="750" cy="750" r="575.63"/><path class="cls-4" d="M271.67,758.71a48.62,48.62,0,0,1,19-19.22,55.08,55.08,0,0,1,27.61-6.88q18.92,0,32.38,10t18,27.23H340.21a23,23,0,0,0-9-10.14A25.28,25.28,0,0,0,318,756.21q-12,0-19.37,8.32t-7.41,22.24q0,13.93,7.41,22.25T318,817.34a25.28,25.28,0,0,0,13.23-3.48,23,23,0,0,0,9-10.14h28.44q-4.53,17.25-18,27.16t-32.38,9.91a55.08,55.08,0,0,1-27.61-6.88,48.75,48.75,0,0,1-19-19.14q-6.81-12.25-6.81-28A57.08,57.08,0,0,1,271.67,758.71Z"/><path class="cls-4" d="M407.31,834.13a52,52,0,0,1-19.82-19.44,57.19,57.19,0,0,1,0-56.06,52.18,52.18,0,0,1,19.82-19.36,57.65,57.65,0,0,1,54.92,0,51.1,51.1,0,0,1,19.67,19.36,58,58,0,0,1-.08,56.06,51.64,51.64,0,0,1-19.67,19.44,57.49,57.49,0,0,1-54.84,0ZM455.12,809q7.63-8.47,7.64-22.4t-7.64-22.46q-7.65-8.4-20.35-8.4-12.87,0-20.5,8.32t-7.64,22.54q0,14.07,7.64,22.47t20.5,8.4Q447.48,817.49,455.12,809Z"/><path class="cls-4" d="M623.74,733.82V840H597.87v-63.7L574.11,840H553.23l-23.9-63.85V840H503.46V733.82H534l29.81,73.53,29.5-73.53Z"/><path class="cls-4" d="M762.64,733.82V840H736.76v-63.7L713,840H692.13l-23.91-63.85V840H642.35V733.82h30.56l29.81,73.53,29.5-73.53Z"/><path class="cls-4" d="M806.66,733.82v63.55q0,9.52,4.69,14.67t13.77,5.15q9.09,0,13.92-5.15t4.84-14.67V733.82h25.88v63.39q0,14.24-6.06,24.06a38.7,38.7,0,0,1-16.26,14.83,51.14,51.14,0,0,1-22.77,5,49.88,49.88,0,0,1-22.47-4.92,36.54,36.54,0,0,1-15.66-14.82q-5.74-9.92-5.75-24.14V733.82Z"/><path class="cls-4" d="M982.93,840H957.05l-43.27-65.51V840H887.91V733.82h25.87l43.27,65.82V733.82h25.88Z"/><path class="cls-4" d="M1027.56,733.82V840h-25.87V733.82Z"/><path class="cls-4" d="M1122.73,733.82v20.73h-28.14V840h-25.88V754.55h-28.14V733.82Z"/><path class="cls-4" d="M1226.82,733.82l-36.77,71.11V840h-25.87v-35.1l-36.77-71.11h29.36l20.57,44.48,20.43-44.48Z"/><path class="cls-4" d="M462.38,903.21a48.69,48.69,0,0,1,19-19.22A55.11,55.11,0,0,1,509,877.11q18.91,0,32.38,10t18,27.24H530.92a23,23,0,0,0-9-10.14,25.34,25.34,0,0,0-13.24-3.48q-12,0-19.37,8.32t-7.41,22.24q0,13.93,7.41,22.25t19.37,8.32a25.34,25.34,0,0,0,13.24-3.48,23,23,0,0,0,9-10.14h28.45q-4.54,17.25-18,27.16T509,985.29a55.21,55.21,0,0,1-27.61-6.88,48.77,48.77,0,0,1-19-19.15q-6.81-12.24-6.81-28A57.08,57.08,0,0,1,462.38,903.21Z"/><path class="cls-4" d="M667.24,878.32V984.53H641.37V940.81H601.12v43.72H575.25V878.32h25.87v41.61h40.25V878.32Z"/><path class="cls-4" d="M708.62,978.63a52,52,0,0,1-19.82-19.44,57.19,57.19,0,0,1,0-56.06,52.18,52.18,0,0,1,19.82-19.36,57.65,57.65,0,0,1,54.92,0,51.1,51.1,0,0,1,19.67,19.36,58,58,0,0,1-.07,56.06,51.71,51.71,0,0,1-19.67,19.44,57.51,57.51,0,0,1-54.85,0Zm47.81-25.11q7.64-8.47,7.64-22.4t-7.64-22.46q-7.63-8.4-20.35-8.4t-20.5,8.32q-7.65,8.32-7.64,22.54t7.64,22.47q7.63,8.4,20.5,8.4Q748.78,962,756.43,953.52Z"/><path class="cls-4" d="M830.64,878.32V984.53H804.77V878.32Z"/><path class="cls-4" d="M851.82,903.21a48.69,48.69,0,0,1,19-19.22,55.13,55.13,0,0,1,27.61-6.88q18.92,0,32.38,10t18,27.24H920.36a22.94,22.94,0,0,0-9-10.14,25.31,25.31,0,0,0-13.24-3.48q-12,0-19.36,8.32t-7.42,22.24q0,13.93,7.42,22.25t19.36,8.32a25.31,25.31,0,0,0,13.24-3.48,22.94,22.94,0,0,0,9-10.14h28.45q-4.54,17.25-18,27.16t-32.38,9.91a55.24,55.24,0,0,1-27.61-6.88,48.77,48.77,0,0,1-19-19.15q-6.8-12.24-6.8-28A57.19,57.19,0,0,1,851.82,903.21Z"/><path class="cls-4" d="M990.57,899.05v21.48h34.64v20H990.57v23.3h39.18v20.73H964.69V878.32h65.06v20.73Z"/><path class="cls-5" d="M1421.26,1032.78H78.74l33.72,166.56A168,168,0,0,0,277.13,1334h945.74a168,168,0,0,0,164.67-134.66Z"/><path class="cls-6" d="M345.4,1186.65c0-30-10.59-43.72-16.2-48.91a1.6,1.6,0,0,0-2.65,1.42c1.09,17-20.11,21.22-20.11,47.8v.16c0,16.19,12.16,29.4,27.12,29.4s27.11-13.21,27.11-29.4V1187c0-7.55-2.8-14.78-5.61-20.13-.62-1.1-2.18-.63-2,.32C358.18,1190.11,345.4,1204.26,345.4,1186.65Z"/><path class="cls-6" d="M313.14,1250a2.89,2.89,0,0,1-1.87-.79l-69.34-70a2.88,2.88,0,0,1,0-3.78l73.24-73.91a3.29,3.29,0,0,1,1.71-.63h21a2.46,2.46,0,0,1,2.34,1.58,2.58,2.58,0,0,1-.62,2.83l-68.73,69.5a3.54,3.54,0,0,0,0,5l54.39,55a2.91,2.91,0,0,1,0,3.78l-10.44,10.69a3.3,3.3,0,0,1-1.72.63ZM327,1262.29a2.45,2.45,0,0,1-2.33-1.58,2.54,2.54,0,0,1,.62-2.83l68.88-69.5a3.76,3.76,0,0,0,1.09-2.52,3.15,3.15,0,0,0-1.09-2.52l-54.54-55a2.87,2.87,0,0,1,0-3.77l10.59-10.69a2.58,2.58,0,0,1,1.87-.79,2.39,2.39,0,0,1,1.72.94L423,1184a2.68,2.68,0,0,1,0,3.78l-73.24,73.91a2.57,2.57,0,0,1-1.87.78H327Z"/><path class="cls-4" d="M499.22,1184.44a28,28,0,0,0-6.71-3.93,61,61,0,0,0-7.48-2.83c-2.65-.78-5.14-1.73-7.48-2.51a33.61,33.61,0,0,1-6.23-3.15,14.61,14.61,0,0,1-4.36-4.4,11.08,11.08,0,0,1-1.56-6.29,14.06,14.06,0,0,1,1.09-5.66,16,16,0,0,1,3.27-4.72,15,15,0,0,1,5.3-3.3,21.86,21.86,0,0,1,7.48-1.26,22.18,22.18,0,0,1,7.17,1.1,35.23,35.23,0,0,1,4.83,2.36,20.46,20.46,0,0,1,3.12,2.2c2.18,2,3.89,1.57,4.83-.16l1.71-3.3-.46-.47a31.13,31.13,0,0,0-9.51-6.45,30.54,30.54,0,0,0-11.69-2,26.79,26.79,0,0,0-10.28,1.88,23.58,23.58,0,0,0-7.64,4.88,21.33,21.33,0,0,0-4.68,7.08,21.94,21.94,0,0,0-1.55,8.33,20.17,20.17,0,0,0,1.87,9.12,18.5,18.5,0,0,0,4.83,6.13,27.56,27.56,0,0,0,6.7,3.93c2.34,1,4.83,1.89,7.48,2.68s5.14,1.73,7.48,2.51a25.49,25.49,0,0,1,6.23,3,14.79,14.79,0,0,1,4.37,4.41,12.18,12.18,0,0,1,1.55,6.6,17.94,17.94,0,0,1-1.24,7.08,15.64,15.64,0,0,1-3.74,5.66,19.94,19.94,0,0,1-6.08,3.77,22.05,22.05,0,0,1-8.26,1.42,23.42,23.42,0,0,1-6.08-.63,21.17,21.17,0,0,1-4.67-1.57,19.17,19.17,0,0,1-3.59-2.05c-.93-.79-1.87-1.57-2.65-2.2s-1.55-1.26-2-1.73a2.69,2.69,0,0,0-1.87-.94,2.86,2.86,0,0,0-2.34,1.25l-2,3.15.47.47a34.61,34.61,0,0,0,10.44,8c3.9,2.05,8.73,3,14.18,3a30.9,30.9,0,0,0,10.91-1.89,26,26,0,0,0,8.42-5.5,23.81,23.81,0,0,0,5.3-8.18,27.56,27.56,0,0,0,1.87-10.06,18.94,18.94,0,0,0-1.87-8.81A18.29,18.29,0,0,0,499.22,1184.44Z"/><path class="cls-4" d="M584.46,1151A35,35,0,0,0,572,1142.3a43.52,43.52,0,0,0-16-3,40.3,40.3,0,0,0-15.89,3.15,34.79,34.79,0,0,0-12.47,8.65,40.43,40.43,0,0,0-8.11,13.52,48.41,48.41,0,0,0-3,17.46,51.83,51.83,0,0,0,3,17.45,38.9,38.9,0,0,0,8.11,13.52,34.79,34.79,0,0,0,12.47,8.65,42.66,42.66,0,0,0,15.89,3,38.53,38.53,0,0,0,16-3.14,35.13,35.13,0,0,0,12.47-8.65,40.56,40.56,0,0,0,8.1-13.53,55.62,55.62,0,0,0,0-34.91A40.41,40.41,0,0,0,584.46,1151ZM586,1197.5a32.93,32.93,0,0,1-6.55,11.48,26.66,26.66,0,0,1-10.28,7.23,32.69,32.69,0,0,1-13.25,2.52,33.22,33.22,0,0,1-13.25-2.52,28.47,28.47,0,0,1-10.28-7.23,31,31,0,0,1-6.7-11.48,47.67,47.67,0,0,1-2.34-15.41,47.09,47.09,0,0,1,2.34-15.42,33.06,33.06,0,0,1,6.7-11.47,28.37,28.37,0,0,1,10.28-7.24,36.23,36.23,0,0,1,26.5,0,28.37,28.37,0,0,1,10.28,7.24,32.79,32.79,0,0,1,6.55,11.47,47.73,47.73,0,0,1,2.34,15.42A47,47,0,0,1,586,1197.5Z"/><path class="cls-4" d="M664.41,1192a31.49,31.49,0,0,1-1.56,10.38,24.72,24.72,0,0,1-4.68,8.18,20.18,20.18,0,0,1-7.48,5.5,24.49,24.49,0,0,1-10,1.89,25.27,25.27,0,0,1-10-2,22.23,22.23,0,0,1-7.48-5.51,23.35,23.35,0,0,1-4.67-8.18,37.75,37.75,0,0,1-1.56-10.37v-51.43h-7.79v51.43a38.61,38.61,0,0,0,2.18,12.89,32.43,32.43,0,0,0,6.23,10.54,28.93,28.93,0,0,0,10,7.07,35.26,35.26,0,0,0,26.18,0,29,29,0,0,0,10-7.07,32.64,32.64,0,0,0,6.24-10.54,38.61,38.61,0,0,0,2.18-12.89v-51.43h-7.79Z"/><path class="cls-4" d="M717.7,1186.17a35.71,35.71,0,0,0,7.64-2.2,28.6,28.6,0,0,0,7.48-5,21.9,21.9,0,0,0,4.83-7.08,23.53,23.53,0,0,0,1.71-9c0-7.39-2.49-13.05-7.32-16.83s-12-5.66-21.51-5.66H688.87v83.35h7.79v-76.9h13.87c7,0,12.31,1.42,15.9,4.09s5.3,6.6,5.3,12.11a18.21,18.21,0,0,1-1.4,7.23,14.58,14.58,0,0,1-4.21,5.51,19,19,0,0,1-6.86,3.61,34.32,34.32,0,0,1-9.35,1.26h-5c-2.49,0-2.8.79-2.8,2.2V1187h.93a52.69,52.69,0,0,1,6.39.47,5.33,5.33,0,0,1,1.72,1.57L737,1222a4.31,4.31,0,0,0,1.55,1.41,4.25,4.25,0,0,0,2,.48h7l-29-36.64A6.81,6.81,0,0,0,717.7,1186.17Z"/><path class="cls-4" d="M812.76,1208.35a2,2,0,0,0-.78.47,48.54,48.54,0,0,1-4.67,3.77,39.3,39.3,0,0,1-5.14,2.68,25.35,25.35,0,0,1-6.08,1.73,37.63,37.63,0,0,1-7.64.63,32.05,32.05,0,0,1-12.46-2.36,28.08,28.08,0,0,1-10-6.92,31.54,31.54,0,0,1-6.54-11.17,45,45,0,0,1-2.34-15.09,44.52,44.52,0,0,1,2.49-14.79,30.06,30.06,0,0,1,17-18.24,33.15,33.15,0,0,1,13.25-2.51,44,44,0,0,1,7,.63,24.88,24.88,0,0,1,5.45,1.41,33.37,33.37,0,0,1,4,1.89,16.87,16.87,0,0,1,3,1.88,12.32,12.32,0,0,1,2,1.42,3.72,3.72,0,0,0,1.87.79,2.44,2.44,0,0,0,2.18-1.1l2.19-3.31-.63-.63a45.84,45.84,0,0,0-5.61-4.4,27.33,27.33,0,0,0-6.08-3.14,41.57,41.57,0,0,0-15.27-2.52,43.07,43.07,0,0,0-16.52,3.14,38.28,38.28,0,0,0-12.78,8.81,43.94,43.94,0,0,0-8.26,13.53,47.45,47.45,0,0,0-3,17.29,51.9,51.9,0,0,0,2.81,17.46,38.68,38.68,0,0,0,8,13.52,34.19,34.19,0,0,0,12.31,8.65,41.83,41.83,0,0,0,15.74,3,33,33,0,0,0,9.19-1.1,29.48,29.48,0,0,0,8-2.36,31.13,31.13,0,0,0,6.7-3.77,42.74,42.74,0,0,0,5.61-5l.62-.63-3-3.14A2.14,2.14,0,0,0,812.76,1208.35Zm-.46-55.4a2.9,2.9,0,0,1-.22-.19A1.86,1.86,0,0,0,812.3,1153Zm-2.34-1.84h0c.39.31.78.59,1.13.84C810.74,1151.7,810.35,1151.42,810,1151.11Z"/><polygon class="cls-4" points="831.15 1223.92 881.49 1223.92 881.49 1217 838.95 1217 838.95 1184.92 874.32 1184.92 874.32 1178.15 838.95 1178.15 838.95 1147.17 881.49 1147.17 881.49 1140.41 831.15 1140.41 831.15 1223.92"/><path class="cls-4" d="M1165.58,1187.12a2.8,2.8,0,0,0,.94,2,3.81,3.81,0,0,0,2.34.79h10.28v18.87a41.15,41.15,0,0,1-7.32,2.67,38,38,0,0,1-9,1,29,29,0,0,1-11.22-2.21,25.62,25.62,0,0,1-8.57-6,27.91,27.91,0,0,1-5.61-9.44,36.5,36.5,0,0,1-2-12.89,37.16,37.16,0,0,1,1.87-12.11,25.77,25.77,0,0,1,5.3-9.28,22.89,22.89,0,0,1,8.42-6,27.67,27.67,0,0,1,11.06-2,42.52,42.52,0,0,1,6.7.47,42.05,42.05,0,0,1,5.14,1.26,17,17,0,0,1,4.21,1.89c1.25.78,2.49,1.41,3.74,2.2a5,5,0,0,0,2.81.94,4.09,4.09,0,0,0,3.43-2.2l4.51-7.07a41.34,41.34,0,0,0-12.46-7.87c-4.83-2-10.76-3-17.46-3a49.09,49.09,0,0,0-17.76,3.14,38.17,38.17,0,0,0-13.56,8.81,39,39,0,0,0-8.57,13.52,47.93,47.93,0,0,0-3,17.3,47.31,47.31,0,0,0,3.11,17.14,42,42,0,0,0,8.73,13.52,36.37,36.37,0,0,0,13.09,8.81,44.46,44.46,0,0,0,16.68,3.14,88,88,0,0,0,9.5-.47,44.59,44.59,0,0,0,8.26-1.57,51.4,51.4,0,0,0,7.33-2.83,34.6,34.6,0,0,0,6.54-3.93v-37.43h-27.43Z"/><path class="cls-4" d="M1091.87,1188.22a25.45,25.45,0,0,0,7.79-3.62,23.7,23.7,0,0,0,5.77-5.34,21.47,21.47,0,0,0,3.58-6.77,26.93,26.93,0,0,0,1.25-8,24.67,24.67,0,0,0-1.87-9.75,18.76,18.76,0,0,0-5.61-7.54,26.12,26.12,0,0,0-9.82-4.88,55.53,55.53,0,0,0-14.49-1.73h-25.25v83.35h15.43v-71.4h10c5.61,0,9.81,1.26,12.62,3.46s4.21,5.51,4.21,9.75a17.6,17.6,0,0,1-1.09,5.82,11.46,11.46,0,0,1-3.12,4.56,15.68,15.68,0,0,1-5.3,3,22.52,22.52,0,0,1-7.64,1.1h-3.58s-1.71,0-1.87,1.41v10.07h5.45c.94.16,1.87,1.57,2.34,2.2l17.3,26.73a5.6,5.6,0,0,0,2.34,2.36,7.36,7.36,0,0,0,3.43.79h13.87l-20.73-30.82A15.69,15.69,0,0,0,1091.87,1188.22Z"/><polygon class="cls-4" points="900.97 1223.92 916.4 1223.92 916.4 1189.95 947.25 1189.95 947.25 1177.53 916.4 1177.53 916.4 1152.84 953.02 1152.84 953.02 1140.57 900.97 1140.57 900.97 1223.92"/><path class="cls-4" d="M1258.78,1152.84v-12.43h-52v83.35h52v-12.43h-36.47v-21.07h25.09c3.43,0,3.58-2.67,3.58-2.67v-9.12h-28.83v-25.63Z"/><path class="cls-4" d="M1031.09,1151.74a41.11,41.11,0,0,0-13.24-9,47.26,47.26,0,0,0-34.29-.16,41.27,41.27,0,0,0-13.24,9,39,39,0,0,0-8.57,13.52,50.21,50.21,0,0,0,0,34,39.26,39.26,0,0,0,21.81,22.49,46.37,46.37,0,0,0,17.15,3.14,43.29,43.29,0,0,0,17.14-3.14,39.32,39.32,0,0,0,21.82-22.49,46.4,46.4,0,0,0,3-17,48.18,48.18,0,0,0-3-16.83A41.51,41.51,0,0,0,1031.09,1151.74Zm-6.07,42.61a27.92,27.92,0,0,1-5.15,9.28,23.8,23.8,0,0,1-8.26,5.82,30.17,30.17,0,0,1-21.81,0,23.89,23.89,0,0,1-8.26-5.82,25.77,25.77,0,0,1-5.3-9.28,41.16,41.16,0,0,1,0-24.53,25.77,25.77,0,0,1,5.3-9.28,24,24,0,0,1,8.26-5.82,30.17,30.17,0,0,1,21.81,0,22.6,22.6,0,0,1,8.26,5.82,27.92,27.92,0,0,1,5.15,9.28,41.16,41.16,0,0,1,0,24.53Z"/><path class="cls-6" d="M846.36,578.22c-1.85,11-3.72,22-5.54,33-.91,5.46-2,10.91-2.54,16.41-.37,3.5-2.08,4.75-5.33,5.5a353.22,353.22,0,0,1-159.3,0c-3.61-.82-4.91-2.45-5.44-6.07-2.21-15-4.78-30-7.25-45a5,5,0,0,0-2-3.39v50c-3-.82-5.2-1.1-7.11-2-12.61-5.89-25.21-11.8-37.66-18-1.88-.94-4.22-3.53-4.19-5.33.32-22.55.36-45.14,1.9-67.63A67.41,67.41,0,0,1,631,493.21a100.61,100.61,0,0,1,26.08-19.09,133.86,133.86,0,0,1,51.15-14.22c6.4-.49,13.31,1.42,19.53,3.56,18.23,6.29,36.65,6.3,54-1.11,9.13-3.91,17.19-3.19,25.56-1.35a259.18,259.18,0,0,1,35.3,10.11c28.15,10.59,45.34,31.25,48.39,61.07,2.48,24.21,1.72,48.75,2.25,73.15a5.5,5.5,0,0,1-1.86,4.14C878.22,618,864,624.08,847.89,628.84V578.31Z"/><path class="cls-6" d="M753.81,299c41.6.13,74.78,33.83,74.56,75.73-.2,41.15-34.24,74.78-75.36,74.46a75,75,0,0,1-74.84-75C678.19,332.1,711.59,298.88,753.81,299Z"/><path class="cls-6" d="M938.07,552.91V519.48l-1.32-.19c-1.13,6.51-2.4,13-3.37,19.54-2.3,15.34-2.24,15.35-17.52,18.29-2,.39-4,.63-7,1.09-.4-27.8-4.76-54.18-23.57-75.85-18.61-21.44-44-29.62-70.77-37.05,8.3-2.07,16.57-4.3,24.92-6.18a27.45,27.45,0,0,1,16.74,1.22c12.48,4.95,25.22,4.24,37.89-.27a26.13,26.13,0,0,1,11.2-1.46c15.59,1.73,30.51,5.5,43.4,15.28,12,9.11,17.55,21.66,18.68,36,1.17,14.9.91,29.92,1,44.89a6.4,6.4,0,0,1-2.68,4.84C957.05,544,948.16,548.11,938.07,552.91Z"/><path class="cls-6" d="M594.4,557a10.41,10.41,0,0,1-1.79.4c-16.58-2.64-16.56-2.64-19.27-19.37a89.88,89.88,0,0,0-4.48-18.43V552.9c-10.19-4.83-19.21-9-28.09-13.46a5.08,5.08,0,0,1-2.37-3.81c.29-16.2-.5-32.56,1.57-48.54,3.28-25.32,19.69-40.32,47.06-45.95,3.61-.74,7.2-1.61,10.83-2.21,5.47-.9,10.63-.92,16.15,1.31,12.46,5,25.19,4.42,37.9-.08a24.81,24.81,0,0,1,11.82-1.39A193.58,193.58,0,0,1,692.36,446C631.62,458.85,594.32,491.67,594.4,557Z"/><path class="cls-6" d="M924.74,377c0,24.17-12,42.26-30.84,50.13a50.9,50.9,0,0,1-54.79-10.35c-2.94-2.87-3.49-5.22-1.89-9.09a86.45,86.45,0,0,0,3.21-57.83c-1.23-4.37-.3-6.83,3.14-9.28,16.64-11.88,34.46-14.09,52.83-5.22C915.11,344.34,924,360.14,924.74,377Z"/><path class="cls-6" d="M632.55,431a50.45,50.45,0,0,1-30-91.1,50.8,50.8,0,0,1,59.81,0c3.73,2.7,5.11,5.14,3.66,10.14a84.81,84.81,0,0,0,3.1,57.2c1.93,4.67,1.07,7.32-2.43,10.49A51.35,51.35,0,0,1,632.55,431Z"/></svg>
\ No newline at end of file
diff --git a/scst/www/images/sourceforge_badges/oss-community-leader-white.svg b/scst/www/images/sourceforge_badges/oss-community-leader-white.svg
new file mode 100644
index 0000000..093268b
--- /dev/null
+++ b/scst/www/images/sourceforge_badges/oss-community-leader-white.svg
@@ -0,0 +1 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1500 1500"><defs><style>.cls-1{fill:#898989;}.cls-2{fill:#eae9ee;stroke:#c7c2bd;}.cls-2,.cls-3,.cls-6{stroke-miterlimit:10;}.cls-2,.cls-6{stroke-width:5.1px;}.cls-3{fill:#fff;stroke-width:25.5px;}.cls-3,.cls-6{stroke:#898989;}.cls-4{fill:#3f3f3f;}.cls-5{fill:#ff6700;}.cls-6{fill:url(#linear-gradient);}</style><linearGradient id="linear-gradient" x1="1362.99" y1="1185.9" x2="123.78" y2="1185.9" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fffefe"/><stop offset="1" stop-color="#eaeaec"/></linearGradient></defs><polygon class="cls-1" points="1421.26 1035.29 78.74 1035.29 251.77 968.2 1248.23 968.2 1421.26 1035.29"/><path class="cls-2" d="M1281.53,1079.45V420.55a11.09,11.09,0,0,0-5-9.44L755,81.65a9.25,9.25,0,0,0-10,0L223.45,411.11a11.09,11.09,0,0,0-5,9.44v658.9a11.09,11.09,0,0,0,5,9.44L745,1418.35a9.25,9.25,0,0,0,10,0l521.57-329.46A11.09,11.09,0,0,0,1281.53,1079.45Z"/><path class="cls-3" d="M1175.61,1018.91V491.3a8.91,8.91,0,0,0-4-7.56L754,219.94a7.39,7.39,0,0,0-8,0L328.38,483.74a8.91,8.91,0,0,0-4,7.56v527.61a8.92,8.92,0,0,0,4,7.56L746,1290.27a7.39,7.39,0,0,0,8,0l417.63-263.8A8.92,8.92,0,0,0,1175.61,1018.91Z"/><path class="cls-4" d="M389.22,821.84a37.09,37.09,0,0,1,14.5-14.68,42.11,42.11,0,0,1,21.1-5.26q14.44,0,24.74,7.63a36.66,36.66,0,0,1,13.76,20.8H441.58a17.53,17.53,0,0,0-6.88-7.74,19.34,19.34,0,0,0-10.11-2.66,18.89,18.89,0,0,0-14.8,6.36q-5.66,6.36-5.66,17t5.66,17a18.91,18.91,0,0,0,14.8,6.35A19.25,19.25,0,0,0,434.7,864a17.53,17.53,0,0,0,6.88-7.74h21.74A36.44,36.44,0,0,1,449.56,877q-10.29,7.57-24.74,7.57a42.11,42.11,0,0,1-21.1-5.26,37.27,37.27,0,0,1-14.5-14.62,46.62,46.62,0,0,1,0-42.83Z"/><path class="cls-4" d="M492.85,879.46a39.61,39.61,0,0,1-15.14-14.85,43.68,43.68,0,0,1,0-42.83A39.84,39.84,0,0,1,492.85,807a44.1,44.1,0,0,1,42,0,39.07,39.07,0,0,1,15,14.8,44.3,44.3,0,0,1-.06,42.83,39.44,39.44,0,0,1-15,14.85,43.93,43.93,0,0,1-41.91,0Zm36.53-19.18q5.83-6.48,5.84-17.11T529.38,826q-5.83-6.42-15.55-6.42t-15.66,6.36q-5.85,6.36-5.84,17.23t5.84,17.16q5.83,6.42,15.66,6.42T529.38,860.28Z"/><path class="cls-4" d="M658.21,802.82V884H638.45V835.3L620.3,884H604.35l-18.27-48.78V884H566.31V802.82h23.36L612.44,859,635,802.82Z"/><path class="cls-4" d="M764.34,802.82V884H744.57V835.3L726.42,884H710.47L692.2,835.19V884H672.43V802.82h23.36L718.56,859l22.54-56.18Z"/><path class="cls-4" d="M798,802.82v48.55q0,7.29,3.58,11.22t10.52,3.93q6.93,0,10.63-3.93t3.7-11.22V802.82h19.77v48.44q0,10.86-4.62,18.38A29.71,29.71,0,0,1,829.13,881a39.11,39.11,0,0,1-17.4,3.81A38.1,38.1,0,0,1,794.57,881a28,28,0,0,1-12-11.33q-4.4-7.58-4.39-18.44V802.82Z"/><path class="cls-4" d="M932.65,884H912.88l-33.06-50.05V884H860.05V802.82h19.77l33.06,50.29V802.82h19.77Z"/><path class="cls-4" d="M966.75,802.82V884H947V802.82Z"/><path class="cls-4" d="M1039.46,802.82v15.84H1018V884H998.19V818.66h-21.5V802.82Z"/><path class="cls-4" d="M1119,802.82l-28.09,54.33V884h-19.77V857.15L1043,802.82h22.43l15.72,34,15.61-34Z"/><path class="cls-4" d="M564.52,969h25.9v15.26H544.75V903.12h19.77Z"/><path class="cls-4" d="M619.66,919v16.41h26.48v15.26H619.66v17.8H649.6v15.84H599.9V903.12h49.7V919Z"/><path class="cls-4" d="M713,969.94H682.66l-4.85,14.33H657.12l29.36-81.15h22.89l29.36,81.15H717.81Zm-5.08-15.26L697.81,925l-9.94,29.71Z"/><path class="cls-4" d="M800.46,908.21a36.37,36.37,0,0,1,14.86,14.27,45.37,45.37,0,0,1,0,42.37,36.14,36.14,0,0,1-14.92,14.34A47.27,47.27,0,0,1,778,984.27H747.63V903.12H778Q790.87,903.12,800.46,908.21ZM794.22,961q6.24-6.13,6.24-17.34t-6.24-17.46Q788,920,776.76,920H767.4v47.16h9.36Q788,967.16,794.22,961Z"/><path class="cls-4" d="M851.44,919v16.41h26.47v15.26H851.44v17.8h29.94v15.84H831.67V903.12h49.71V919Z"/><path class="cls-4" d="M935.59,984.27l-16.87-30.63H914v30.63H894.21V903.12h33.18a36.55,36.55,0,0,1,16.35,3.35,23.16,23.16,0,0,1,10.12,9.19,25.73,25.73,0,0,1,3.35,13,24.19,24.19,0,0,1-4.56,14.45q-4.57,6.36-13.47,9l18.72,32.14ZM914,939.65h12.25c3.62,0,6.34-.88,8.15-2.66s2.72-4.28,2.72-7.51a9.72,9.72,0,0,0-2.72-7.28c-1.81-1.78-4.53-2.66-8.15-2.66H914Z"/><path class="cls-5" d="M802.26,700.12a12,12,0,0,0-9.27-9.68,14.15,14.15,0,0,0-5.66-.77H698.67c-1.57,0-3.17-.17-4.63.67-4.19.86-6.88,3.54-8.74,7.22-1.1,2.16-1,4.61-1.6,6.86q-6,21.45-12,42.84h143c-.66-4.91-2.49-9.53-3.7-14.3C808.22,722,804.55,711.24,802.26,700.12Z"/><path class="cls-5" d="M707.71,492.48q4.75,29.91,7.55,60.06c2.23,24.37,4.86,48.7,7.32,73.05,1.26,12.45,2.46,24.91,3.84,37.35.53,4.81,3.47,7.56,8.29,8.41a81.71,81.71,0,0,0,21,0c5-1,7.75-3.77,8.27-8.83q3.35-32.14,6.57-64.32c2.37-23.58,4.61-47.18,7.08-70.75.57-5.39-.34-4.42,5.24-4.48a18.58,18.58,0,0,0,18.24-13.45c1.27-4,.32-7.85-.23-11.72-3.06-21.56-6.27-43.09-9.3-64.65-1.24-8.75-7.8-15.11-16.21-15.33l-1.87-.1h0a1.89,1.89,0,0,0-2.22,0c-9.77,0-19.54-.12-29.31-.11-9,0-18,.13-26.94.2a7.54,7.54,0,0,0-4.91.94c-2.56,1.54-3.36.26-4.4-1.84-6.6-13.23-13.23-26.43-20-39.56-4-7.79-12.88-10.41-20.9-6.48a15.3,15.3,0,0,0-7.48,20.58c1.17,2.56,2.5,5.05,3.77,7.57q21.82,43.19,43.64,86.39C705.9,487.67,707.27,489.76,707.71,492.48Zm65-74.41-.57-.08C772,418,772.74,417.67,772.7,418.07Z"/><path class="cls-5" d="M652.66,502.14a12.93,12.93,0,0,0-11.5-11.8c-11.66-.65-23.33-.15-35-.26-3.24,0-6.51-.39-9.73.31-6.08.95-10.25,5.62-10.95,12-2.34,21.2-4.69,42.4-7.17,63.58-1.19,10.2,3.92,16.38,14.18,16.45,2.41,0,3,.89,3.23,3.07q4.71,41.2,9.59,82.38c.46,3.91,2.6,6,6.72,6.84a87.43,87.43,0,0,0,13.54.07c4.3-.54,6.58-2.62,7.22-6.71.25-1.55.29-3.14.48-4.71,3.08-25.77,6.22-51.53,9.18-77.31.33-2.9,1.22-3.66,4.1-3.65,8.84,0,14.4-6.56,13.4-15.29Q656.23,534.61,652.66,502.14Z"/><path class="cls-5" d="M902.22,501.62A12.68,12.68,0,0,0,891.3,490.4c-6.16-.75-12.33-.32-18.49-.31-8.75,0-17.5-.4-26.24.25a12.74,12.74,0,0,0-11.37,11.51q-3.8,33.23-7.45,66.46a12.4,12.4,0,0,0,10,13.53l.24.15.27,0h0a5.12,5.12,0,0,0,3.61.46c2.38-.26,3.25.45,3.5,3,.9,9.31,2.15,18.57,3.24,27.86,2.13,18.11,4.2,36.23,6.38,54.34.57,4.72,3,6.84,7.83,7.13a87.76,87.76,0,0,0,12.87-.07c4-.72,6.29-2.86,6.73-6.43,1-7.85,1.85-15.7,2.78-23.55q3.49-29.43,7-58.85c.25-2.14.53-3.55,3.49-3.52,9.62.1,14.9-6,13.83-15.77Q906,534.08,902.22,501.62Zm-60,79.93-.16,0a8.16,8.16,0,0,0,1.1-.09A4.34,4.34,0,0,1,842.18,581.55Z"/><path class="cls-5" d="M554.49,508.51c-.68-5.8-5-9.64-11-9.95H507c-6.26.29-10.58,4.56-10.89,10.77l-.09,1.16h0c-.19,1.1-.44,2.19-.56,3.3-1.93,16.92-3.74,33.86-5.79,50.78-1.09,9,3.17,14.42,12.23,14.58,2,0,2.79.62,3,2.6.35,3.94.89,7.85,1.35,11.77q3.49,30.09,7,60.18c.37,3.19,1.83,5.54,5.32,6a67.28,67.28,0,0,0,12.85.07c3.43-.14,5.31-2.18,5.84-5.31s.63-6.39.91-9.59l.12-1.18h0a6,6,0,0,0,.42-1.26c2.32-19.74,4.7-39.46,6.86-59.21.33-3,1-4.21,4.33-4.16,7.12.1,12-5.59,11.22-12.69Q557.84,537.45,554.49,508.51Zm-17,133.71a2.54,2.54,0,0,1,.17.45l0,.11A3.27,3.27,0,0,0,537.52,642.22Z"/><path class="cls-5" d="M997.6,566.18q-3.2-28.59-6.48-57.19c-.55-4.76-2.92-8.2-7.62-9.77a4.74,4.74,0,0,0-2.89-.77q-18.87,0-37.75.13c-5.84.73-9.68,4.45-10.34,10.25-2.12,18.59-4.11,37.21-6.3,55.79-1.08,9.09,3.13,14.35,12.26,14.52,2.06,0,2.75.73,2.92,2.65.37,4,.91,8.07,1.37,12.1q3.48,29.91,7,59.81c.44,3.76,2.17,5.46,6,6.1a69.9,69.9,0,0,0,12.19,0c3.81-.63,5.55-2.37,6-6.11,2.76-23.55,5.56-47.08,8.19-70.64.32-2.89,1-4,4.17-3.94C993.55,579.19,998.41,573.47,997.6,566.18Z"/><path class="cls-5" d="M741.5,404.9c2,.37,4.07.89,6.13.1h0c19.82-.14,33.73-23.11,25.44-42a30.38,30.38,0,0,0-57.87,17C717.26,393.1,729.08,404.29,741.5,404.9Z"/><path class="cls-5" d="M868.67,482.87a22,22,0,0,0,.34-44,22,22,0,1,0-.34,44Z"/><path class="cls-5" d="M619.07,482.88a22,22,0,1,0-22-21.93A22.06,22.06,0,0,0,619.07,482.88Z"/><path class="cls-5" d="M522.52,491.71a10,10,0,0,0,6.09-.07c10.6-1.55,17.65-11.87,15.51-22.71a19.31,19.31,0,0,0-23-14.83c-9.6,2.23-15.62,10.51-14.94,20.54C506.72,483.43,513.92,490.93,522.52,491.71Z"/><path class="cls-5" d="M958.45,491.63a9.87,9.87,0,0,0,6.08.08c10.35-.89,17.76-10.8,16.3-21.79-1.25-9.49-9.92-16.57-19.85-16.23a19.1,19.1,0,0,0-18.32,17.91C942.08,481.65,948.86,490.25,958.45,491.63Z"/><path class="cls-6" d="M1421.26,1035.29H78.74l33.72,166.55a168,168,0,0,0,164.67,134.67h945.74a168,168,0,0,0,164.67-134.67Z"/><path class="cls-5" d="M345.4,1189.15c0-30-10.59-43.72-16.2-48.91a1.6,1.6,0,0,0-2.65,1.42c1.09,17-20.11,21.23-20.11,47.8v.16c0,16.2,12.16,29.41,27.12,29.41s27.11-13.21,27.11-29.41v-.16c0-7.54-2.8-14.78-5.61-20.12-.62-1.11-2.18-.63-2,.31C358.18,1192.61,345.4,1206.76,345.4,1189.15Z"/><path class="cls-5" d="M313.14,1252.52a2.88,2.88,0,0,1-1.87-.78l-69.34-70a2.88,2.88,0,0,1,0-3.78l73.24-73.9a3.21,3.21,0,0,1,1.71-.63h21a2.45,2.45,0,0,1,2.34,1.57,2.57,2.57,0,0,1-.62,2.83l-68.73,69.51a3.54,3.54,0,0,0,0,5l54.39,55a2.89,2.89,0,0,1,0,3.77l-10.44,10.69a3.21,3.21,0,0,1-1.72.63ZM327,1264.79a2.46,2.46,0,0,1-2.33-1.57,2.54,2.54,0,0,1,.62-2.83l68.88-69.51a3.74,3.74,0,0,0,1.09-2.52,3.13,3.13,0,0,0-1.09-2.51l-54.54-55a2.88,2.88,0,0,1,0-3.78l10.59-10.69a2.58,2.58,0,0,1,1.87-.79,2.4,2.4,0,0,1,1.72,1l69.19,70a2.67,2.67,0,0,1,0,3.77l-73.24,73.91a2.58,2.58,0,0,1-1.87.79H327Z"/><path class="cls-4" d="M501.71,1153.77a1.56,1.56,0,0,1-1.56.94,2.94,2.94,0,0,1-1.87-1.1,18,18,0,0,0-3.12-2.36,18.59,18.59,0,0,0-5-2.36,21,21,0,0,0-7.48-1.1,23.8,23.8,0,0,0-7.8,1.26,17,17,0,0,0-5.61,3.46,13.77,13.77,0,0,0-3.42,5,15.77,15.77,0,0,0-1.25,6,13.38,13.38,0,0,0,1.71,6.92A14.5,14.5,0,0,0,471,1175a25.45,25.45,0,0,0,6.54,3.14c2.5.95,5,1.73,7.48,2.52a70.45,70.45,0,0,1,7.48,2.83,29.78,29.78,0,0,1,6.55,3.77,16.58,16.58,0,0,1,4.68,5.66,18.92,18.92,0,0,1,1.71,8.34,27.58,27.58,0,0,1-1.71,9.59,21.89,21.89,0,0,1-5,7.86,27.12,27.12,0,0,1-8.11,5.35,29.89,29.89,0,0,1-10.9,1.89,30.63,30.63,0,0,1-13.72-2.83,32.73,32.73,0,0,1-10.13-7.87l1.56-2.51A1.73,1.73,0,0,1,459,1212a1.88,1.88,0,0,1,1.25.63c.46.47,1.24,1.1,1.87,1.73.78.63,1.71,1.42,2.8,2.2a21.3,21.3,0,0,0,3.74,2.2,28.35,28.35,0,0,0,4.83,1.73,25.5,25.5,0,0,0,6.24.63,24,24,0,0,0,8.57-1.41,20.29,20.29,0,0,0,6.39-3.93,17.83,17.83,0,0,0,4.05-6,20.16,20.16,0,0,0,1.4-7.39,14,14,0,0,0-1.71-7.08,13.54,13.54,0,0,0-4.68-4.72,25.78,25.78,0,0,0-6.54-3.14c-2.5-.79-5-1.73-7.48-2.52s-5-1.73-7.48-2.67a24.54,24.54,0,0,1-6.55-3.77,19.12,19.12,0,0,1-4.67-5.82,20.43,20.43,0,0,1-1.72-8.81,19.69,19.69,0,0,1,1.56-7.86,21.22,21.22,0,0,1,4.52-6.76,24.5,24.5,0,0,1,7.32-4.72,28.28,28.28,0,0,1,10-1.73,29.44,29.44,0,0,1,11.37,2,31.39,31.39,0,0,1,9.2,6.14Z"/><path class="cls-4" d="M479.42,1227.36c-5.45,0-10.28-.94-14.18-3a34.29,34.29,0,0,1-10.44-8l-.47-.47,2-3.14a2.88,2.88,0,0,1,2.34-1.26,2.69,2.69,0,0,1,1.87.94c.47.48,1.24,1.1,2,1.73s1.72,1.42,2.65,2.2a19.17,19.17,0,0,0,3.59,2.05,20.18,20.18,0,0,0,4.67,1.57,23.42,23.42,0,0,0,6.08.63,22.26,22.26,0,0,0,8.26-1.41,20.16,20.16,0,0,0,6.08-3.78,15.56,15.56,0,0,0,3.74-5.66,17.91,17.91,0,0,0,1.24-7.08,12.18,12.18,0,0,0-1.55-6.6,14.65,14.65,0,0,0-4.37-4.4,25.13,25.13,0,0,0-6.23-3c-2.34-.79-4.83-1.73-7.48-2.52s-5.14-1.73-7.48-2.67a27.88,27.88,0,0,1-6.7-3.93,18.64,18.64,0,0,1-4.83-6.14,20.14,20.14,0,0,1-1.87-9.12,21.94,21.94,0,0,1,1.55-8.33,21.69,21.69,0,0,1,4.68-7.08,23.72,23.72,0,0,1,7.64-4.87,26.6,26.6,0,0,1,10.28-1.89,30.54,30.54,0,0,1,11.69,2,31,31,0,0,1,9.5,6.45l.47.47-1.71,3.31c-.94,1.73-2.65,2.2-4.83.15a21.19,21.19,0,0,0-3.12-2.2,36.42,36.42,0,0,0-4.83-2.36,22.18,22.18,0,0,0-7.17-1.1,21.86,21.86,0,0,0-7.48,1.26,15.27,15.27,0,0,0-5.3,3.3,16.14,16.14,0,0,0-3.27,4.72,14.06,14.06,0,0,0-1.09,5.66,11.06,11.06,0,0,0,1.56,6.29,14.5,14.5,0,0,0,4.36,4.4,33,33,0,0,0,6.23,3.15c2.34.79,4.83,1.73,7.48,2.52a59.38,59.38,0,0,1,7.48,2.83,28,28,0,0,1,6.71,3.93,18.15,18.15,0,0,1,4.83,6,18.94,18.94,0,0,1,1.87,8.81,27.56,27.56,0,0,1-1.87,10.06,23.81,23.81,0,0,1-5.3,8.18,26.17,26.17,0,0,1-8.42,5.51,30.92,30.92,0,0,1-10.91,1.88Zm-22.59-11.79a29.22,29.22,0,0,0,22.75,9.91,28.28,28.28,0,0,0,10.6-1.89,24.91,24.91,0,0,0,7.79-5,22.47,22.47,0,0,0,4.83-7.55,25.86,25.86,0,0,0,1.71-9.28,20.45,20.45,0,0,0-1.55-8,16.28,16.28,0,0,0-4.37-5.35,34.83,34.83,0,0,0-6.23-3.77c-2.34-.94-4.83-1.89-7.33-2.83-2.49-.79-5-1.73-7.48-2.52a38,38,0,0,1-6.7-3.3,16.13,16.13,0,0,1-4.83-4.87,13.4,13.4,0,0,1-1.87-7.4,18.17,18.17,0,0,1,1.25-6.44,14,14,0,0,1,3.58-5.35,15.23,15.23,0,0,1,5.92-3.62A22.08,22.08,0,0,1,483,1147a23.22,23.22,0,0,1,7.79,1.1,24.58,24.58,0,0,1,8.42,5c.93.78,1.24.78,1.24.78a.59.59,0,0,0,.63-.47l1.09-2a28.29,28.29,0,0,0-8.26-5.51A30.2,30.2,0,0,0,483,1144a26.45,26.45,0,0,0-9.66,1.73,22.3,22.3,0,0,0-7,4.4,18.94,18.94,0,0,0-5.76,14,18.84,18.84,0,0,0,1.71,8.33,15.13,15.13,0,0,0,4.37,5.5,26.69,26.69,0,0,0,6.23,3.62c2.34.94,4.83,1.89,7.32,2.67s5,1.58,7.48,2.52a29.44,29.44,0,0,1,6.7,3.3,13.59,13.59,0,0,1,4.84,5,14.49,14.49,0,0,1,1.87,7.7,21.83,21.83,0,0,1-1.41,7.86,15.85,15.85,0,0,1-4.2,6.29,22.3,22.3,0,0,1-6.71,4.25,26.34,26.34,0,0,1-8.88,1.57,28.73,28.73,0,0,1-6.54-.78,31.4,31.4,0,0,1-5-1.73,16.35,16.35,0,0,1-3.9-2.36c-1.09-.79-2-1.58-2.8-2.2a23,23,0,0,1-1.87-1.73c-.47-.48-.78-.48-.78-.48a.84.84,0,0,0-.78.48Z"/><path class="cls-4" d="M594.43,1184.59a50,50,0,0,1-2.8,17.14,37.3,37.3,0,0,1-8,13.21,36.89,36.89,0,0,1-12.15,8.49,42.53,42.53,0,0,1-31.33,0,34.72,34.72,0,0,1-12.15-8.49,37.3,37.3,0,0,1-7.95-13.21,53.68,53.68,0,0,1,0-34.28,37.3,37.3,0,0,1,7.95-13.21,36.79,36.79,0,0,1,12.15-8.49,42.53,42.53,0,0,1,31.33,0,34.81,34.81,0,0,1,12.15,8.49,37.3,37.3,0,0,1,8,13.21A50,50,0,0,1,594.43,1184.59Zm-6.07,0a47.67,47.67,0,0,0-2.34-15.41,32.84,32.84,0,0,0-6.55-11.48,28.23,28.23,0,0,0-10.28-7.23,36.09,36.09,0,0,0-26.5,0,28.23,28.23,0,0,0-10.28,7.23,33.1,33.1,0,0,0-6.7,11.48,47,47,0,0,0-2.34,15.41,47.67,47.67,0,0,0,2.34,15.41,31,31,0,0,0,6.7,11.48,28.47,28.47,0,0,0,10.28,7.23,33.22,33.22,0,0,0,13.25,2.52,32.69,32.69,0,0,0,13.25-2.52,26.66,26.66,0,0,0,10.28-7.23A32.93,32.93,0,0,0,586,1200,47,47,0,0,0,588.36,1184.59Z"/><path class="cls-4" d="M555.94,1227.21a42.86,42.86,0,0,1-15.89-3,35,35,0,0,1-12.47-8.65,39,39,0,0,1-8.11-13.53,51.78,51.78,0,0,1-3-17.45,48.41,48.41,0,0,1,3-17.46,40.43,40.43,0,0,1,8.11-13.52,35,35,0,0,1,12.47-8.65,40.47,40.47,0,0,1,15.89-3.14,43.72,43.72,0,0,1,16,3,35.13,35.13,0,0,1,12.47,8.65,40.56,40.56,0,0,1,8.1,13.53,55.62,55.62,0,0,1,0,34.91,40.41,40.41,0,0,1-8.1,13.52,34.88,34.88,0,0,1-12.47,8.65A38.53,38.53,0,0,1,555.94,1227.21Zm0-83.35a37.73,37.73,0,0,0-15.27,3,34.33,34.33,0,0,0-19.48,21.07,51.78,51.78,0,0,0,0,33.65,39.13,39.13,0,0,0,7.64,12.9,34.69,34.69,0,0,0,11.84,8.17,42.47,42.47,0,0,0,30.54,0,34.82,34.82,0,0,0,11.85-8.17,36,36,0,0,0,7.63-12.74,51.78,51.78,0,0,0,0-33.65,39.28,39.28,0,0,0-7.63-12.9,34.82,34.82,0,0,0-11.85-8.17A34.4,34.4,0,0,0,555.94,1143.86Z"/><path class="cls-4" d="M640.72,1221.23a24.89,24.89,0,0,0,10.44-2A23,23,0,0,0,663.78,1205a33.41,33.41,0,0,0,1.72-10.69v-50.48h5.76v50.48a36.8,36.8,0,0,1-2.18,12.58,30.6,30.6,0,0,1-6.08,10.22,28.27,28.27,0,0,1-9.66,6.76,32.94,32.94,0,0,1-12.78,2.52,30.05,30.05,0,0,1-22.44-9.28,29.25,29.25,0,0,1-6.08-10.22,37.49,37.49,0,0,1-2.18-12.58v-50.48h5.92v50.32a34.22,34.22,0,0,0,1.72,10.7,25,25,0,0,0,4.83,8.49,23.78,23.78,0,0,0,7.79,5.66,23.33,23.33,0,0,0,10.6,2.2Z"/><path class="cls-4" d="M640.72,1227.36a31.88,31.88,0,0,1-13.09-2.51,28.82,28.82,0,0,1-10-7.08,32.33,32.33,0,0,1-6.23-10.54,38.61,38.61,0,0,1-2.18-12.89v-51.42H617v51.42a37.76,37.76,0,0,0,1.56,10.38,23.53,23.53,0,0,0,4.67,8.18,22.34,22.34,0,0,0,7.48,5.5,25.1,25.1,0,0,0,10,2,24.66,24.66,0,0,0,10-1.88,20.2,20.2,0,0,0,7.48-5.51,24.78,24.78,0,0,0,4.68-8.17,31.58,31.58,0,0,0,1.56-10.38v-51.58h7.79v51.42a38.61,38.61,0,0,1-2.18,12.89,32.53,32.53,0,0,1-6.24,10.54,28.88,28.88,0,0,1-10,7.08,31.91,31.91,0,0,1-13.09,2.51ZM611,1144.8v49.54a34.94,34.94,0,0,0,2,12.26,28.76,28.76,0,0,0,5.92,9.91,25.4,25.4,0,0,0,9.35,6.61,31,31,0,0,0,12.47,2.36,32.05,32.05,0,0,0,12.46-2.36,26.83,26.83,0,0,0,9.36-6.61,31.66,31.66,0,0,0,5.92-9.91,34.94,34.94,0,0,0,2-12.26V1144.8h-3.89v49.54a36.21,36.21,0,0,1-1.72,11,23.2,23.2,0,0,1-5.14,9,23.78,23.78,0,0,1-8.1,6,26.41,26.41,0,0,1-10.76,2.2,25.85,25.85,0,0,1-10.75-2.2,23.68,23.68,0,0,1-8.1-6,25.13,25.13,0,0,1-5-9,35.49,35.49,0,0,1-1.71-11V1144.8Z"/><path class="cls-4" d="M704.77,1184.12h4.83a35.85,35.85,0,0,0,9.66-1.26,19.42,19.42,0,0,0,7.17-3.77,16.33,16.33,0,0,0,4.52-5.82,17.48,17.48,0,0,0,1.56-7.71c0-5.82-1.87-10.06-5.61-12.89s-9.2-4.25-16.52-4.25H695.57v77.06h-5.76v-81.62h20.57c9.35,0,16.36,1.89,20.88,5.5,4.68,3.62,6.86,9,6.86,16a21.19,21.19,0,0,1-1.56,8.5,20.23,20.23,0,0,1-4.68,6.76,23.06,23.06,0,0,1-7.16,4.72,35.48,35.48,0,0,1-9.51,2.51,12,12,0,0,1,2.49,2.36l27.9,35.23h-5.15a4.44,4.44,0,0,1-1.55-.32,2.79,2.79,0,0,1-1.25-1.1l-25.87-33a6.4,6.4,0,0,0-2-1.73c-.77-.32-5.29-.47-6.85-.47,0-4.25.62-4.72,1.87-4.72Z"/><path class="cls-4" d="M747.62,1226.42h-7a4.37,4.37,0,0,1-2-.47,4.43,4.43,0,0,1-1.55-1.42l-25.87-33a5.11,5.11,0,0,0-1.72-1.57,50.3,50.3,0,0,0-6.39-.48h-.93v-4.08c0-1.42.31-2.21,2.8-2.21h5a34.73,34.73,0,0,0,9.35-1.25,19.21,19.21,0,0,0,6.86-3.62,14.47,14.47,0,0,0,4.21-5.5,18.29,18.29,0,0,0,1.4-7.24c0-5.5-1.72-9.43-5.3-12.11s-8.88-4.09-15.9-4.09H696.66v76.9h-7.79v-83.34h21.66q14.26,0,21.51,5.66c4.83,3.77,7.32,9.43,7.32,16.82a23.58,23.58,0,0,1-1.71,9,22,22,0,0,1-4.83,7.07,28.63,28.63,0,0,1-7.48,5,36.3,36.3,0,0,1-7.64,2.2,6.71,6.71,0,0,1,.94,1.1Zm-43.79-38.84c1.72,0,5.3.15,6.24.63a7.6,7.6,0,0,1,2.49,2l25.87,33a2.63,2.63,0,0,0,.93,1,2.37,2.37,0,0,0,1.09.31h3.12l-26.65-33.65a6.63,6.63,0,0,0-2.18-2l-2.34-1.42,2.81-.31a27.62,27.62,0,0,0,16.21-6.92,18.29,18.29,0,0,0,4.36-6.45,23.25,23.25,0,0,0,1.56-8.18c0-6.76-2.18-11.79-6.55-15.25s-11.22-5.19-20.26-5.19H690.9v79.41h3.89v-77h15.74c7.33,0,13.09,1.41,17,4.4q6.08,4.48,6.08,13.68a21.7,21.7,0,0,1-1.56,8,16.52,16.52,0,0,1-4.83,6.13,21.38,21.38,0,0,1-7.48,3.94,32.69,32.69,0,0,1-10,1.41h-4.83c-.46,0-.93,0-1.09,2.52Z"/><path class="cls-4" d="M814,1211.64a1.11,1.11,0,0,1,.94.47l2.33,2.51a40.22,40.22,0,0,1-5.45,4.88,33.89,33.89,0,0,1-6.55,3.62,54.47,54.47,0,0,1-7.63,2.36,43.29,43.29,0,0,1-9.2.78,37.91,37.91,0,0,1-15.42-3,32.65,32.65,0,0,1-12-8.49,39.22,39.22,0,0,1-7.8-13.21,50,50,0,0,1-2.8-17.14,46.2,46.2,0,0,1,3-17,39,39,0,0,1,8.1-13.21,36.61,36.61,0,0,1,12.47-8.49,41,41,0,0,1,16-3,43.77,43.77,0,0,1,8.11.63,43.11,43.11,0,0,1,6.85,1.89,26.4,26.4,0,0,1,5.92,3.14,46.36,46.36,0,0,1,5.46,4.25l-1.72,2.51a1.51,1.51,0,0,1-1.4.63,1.84,1.84,0,0,1-1.24-.63c-.47-.47-1.25-.94-2-1.57a24.6,24.6,0,0,0-3-1.89,13.26,13.26,0,0,0-4.21-1.88,36.11,36.11,0,0,0-5.61-1.58,33,33,0,0,0-7.17-.63,34.21,34.21,0,0,0-13.56,2.52,29.44,29.44,0,0,0-10.59,7.39,35.6,35.6,0,0,0-7,11.48,42.36,42.36,0,0,0-2.49,15.25,43.31,43.31,0,0,0,2.49,15.42,33.65,33.65,0,0,0,6.86,11.48,30.81,30.81,0,0,0,23.06,9.74,39.34,39.34,0,0,0,7.8-.62,24.68,24.68,0,0,0,6.23-1.73,28.76,28.76,0,0,0,5.3-2.83,28.29,28.29,0,0,0,4.83-3.94c.16-.15.31-.31.47-.31C813.54,1211.79,813.86,1211.64,814,1211.64Z"/><path class="cls-4" d="M788.3,1227.36a41.83,41.83,0,0,1-15.74-3,34.28,34.28,0,0,1-12.31-8.64,38.73,38.73,0,0,1-8-13.53,51.85,51.85,0,0,1-2.81-17.45,47.54,47.54,0,0,1,3-17.3,43.94,43.94,0,0,1,8.26-13.53,38.11,38.11,0,0,1,12.78-8.8A42.88,42.88,0,0,1,790,1142a41.57,41.57,0,0,1,15.27,2.52,28.2,28.2,0,0,1,6.08,3.14A46,46,0,0,1,817,1152l.63.63-2.19,3.3a2.44,2.44,0,0,1-2.18,1.1,3.63,3.63,0,0,1-1.87-.79,11.75,11.75,0,0,0-2-1.41,16.3,16.3,0,0,0-3-1.89,33.37,33.37,0,0,0-4-1.89,25.45,25.45,0,0,0-5.45-1.41,44,44,0,0,0-7-.63,33.15,33.15,0,0,0-13.25,2.52,30,30,0,0,0-17,18.24,44.42,44.42,0,0,0-2.49,14.78,45.13,45.13,0,0,0,2.34,15.1,31.58,31.58,0,0,0,6.54,11.16,27.83,27.83,0,0,0,10,6.92,32.05,32.05,0,0,0,12.46,2.36,37.63,37.63,0,0,0,7.64-.63,24.47,24.47,0,0,0,6.08-1.73,40.54,40.54,0,0,0,5.14-2.67,47.2,47.2,0,0,0,4.67-3.78,2.25,2.25,0,0,1,.78-.47,2.15,2.15,0,0,1,2.65.47l3,3.15-.62.63a42.66,42.66,0,0,1-5.61,5,31.13,31.13,0,0,1-6.7,3.77,29.48,29.48,0,0,1-8,2.36A32.62,32.62,0,0,1,788.3,1227.36Zm1.71-83.5a39,39,0,0,0-15.74,3,33.14,33.14,0,0,0-12.15,8.33,37.39,37.39,0,0,0-8,12.9,47.29,47.29,0,0,0-2.81,16.67,48.18,48.18,0,0,0,2.81,16.82,39.28,39.28,0,0,0,7.63,12.9,34.18,34.18,0,0,0,11.69,8.17,38.42,38.42,0,0,0,15,2.84,53.85,53.85,0,0,0,9-.79,27.75,27.75,0,0,0,7.48-2.36,26.76,26.76,0,0,0,6.24-3.62,38.73,38.73,0,0,0,4.67-4.09l-1.71-1.88a.29.29,0,0,0-.47,0c-.16,0-.31.16-.47.31a38.44,38.44,0,0,1-5,3.93,36.1,36.1,0,0,1-5.45,3,29.16,29.16,0,0,1-6.39,1.73,57.5,57.5,0,0,1-7.95.63,36.6,36.6,0,0,1-13.24-2.52,31.46,31.46,0,0,1-10.6-7.39,35.14,35.14,0,0,1-7-11.79,50.2,50.2,0,0,1,0-31.29,33.15,33.15,0,0,1,7.17-11.8,31,31,0,0,1,10.9-7.55,36.38,36.38,0,0,1,13.87-2.67,35.51,35.51,0,0,1,7.33.63,42.68,42.68,0,0,1,5.76,1.57,22.54,22.54,0,0,1,4.37,2,31.06,31.06,0,0,1,3,2c.78.63,1.56,1.1,2,1.57s.78.47.78.47c.47,0,.63-.15.63-.31l1.24-1.89a46.22,46.22,0,0,0-4.67-3.61,28.26,28.26,0,0,0-5.77-3,41,41,0,0,0-6.7-1.89,21.33,21.33,0,0,0-7.48-1.1Z"/><path class="cls-4" d="M880.55,1143.86v4.88H838v33h35.38v4.72H838v34h42.54v4.88H832.09v-81.46Z"/><path class="cls-4" d="M881.49,1226.42H831.15v-83.5h50.34v6.76H839v31h35.37v6.76H839v32.08h42.54ZM833,1224.53h46.6v-3H837.08v-36h35.37v-2.83H837.08v-34.91h42.54v-3H833Z"/><path class="cls-4" d="M953,1142.92v12.42H916.4V1180h30.85v12.42H916.4v34H901v-83.35h52Zm89.61,41.67a46.4,46.4,0,0,1-3,17,39.16,39.16,0,0,1-8.58,13.53,39.57,39.57,0,0,1-13.24,9,43.29,43.29,0,0,1-17.14,3.15,46.37,46.37,0,0,1-17.15-3.15,39.19,39.19,0,0,1-21.81-22.49,50.18,50.18,0,0,1,0-34,39.14,39.14,0,0,1,8.57-13.53,41.25,41.25,0,0,1,13.24-9,47.12,47.12,0,0,1,34.29.16,39.26,39.26,0,0,1,21.82,22.48A48.27,48.27,0,0,1,1042.63,1184.59Zm-15.74,0a37.73,37.73,0,0,0-1.87-12.27,27.87,27.87,0,0,0-5.15-9.27,22.41,22.41,0,0,0-8.25-5.82,30.06,30.06,0,0,0-21.82,0,23.76,23.76,0,0,0-8.26,5.82,25.73,25.73,0,0,0-5.3,9.27,41.19,41.19,0,0,0,0,24.54,25.73,25.73,0,0,0,5.3,9.27,23.76,23.76,0,0,0,8.26,5.82,30.06,30.06,0,0,0,21.82,0,23.72,23.72,0,0,0,8.25-5.82,27.87,27.87,0,0,0,5.15-9.27A37.73,37.73,0,0,0,1026.89,1184.59Zm47.84-1.89h3.58a22.28,22.28,0,0,0,7.64-1.1,15.51,15.51,0,0,0,5.3-3,11.39,11.39,0,0,0,3.12-4.56,17.6,17.6,0,0,0,1.09-5.82c0-4.24-1.41-7.54-4.21-9.74s-7-3.46-12.62-3.46h-10v71.39h-15.43v-83.35h25.25a55.53,55.53,0,0,1,14.49,1.73,26.12,26.12,0,0,1,9.82,4.88,18.79,18.79,0,0,1,5.61,7.55,24.6,24.6,0,0,1,1.87,9.75,27,27,0,0,1-1.25,8,21.52,21.52,0,0,1-3.58,6.76,23.94,23.94,0,0,1-5.77,5.35,25.2,25.2,0,0,1-7.79,3.61,15.81,15.81,0,0,1,5,4.72l20.73,30.82h-13.87a7.37,7.37,0,0,1-3.43-.78,5.66,5.66,0,0,1-2.34-2.36l-17.3-26.74c-.47-.63-1.4-2-2.34-2.2h-5.45v-10.06c.16-1.42,1.87-1.42,1.87-1.42Zm88,32.24a38.57,38.57,0,0,0,9-.94,41.18,41.18,0,0,0,7.32-2.68v-18.87h-10.28a3.8,3.8,0,0,1-2.34-.78,2.8,2.8,0,0,1-.94-2.05v-8.8H1193v37.42a34.6,34.6,0,0,1-6.54,3.93,50.23,50.23,0,0,1-7.33,2.83,45.43,45.43,0,0,1-8.26,1.58,90.69,90.69,0,0,1-9.5.47,44.27,44.27,0,0,1-16.68-3.15,36.47,36.47,0,0,1-13.09-8.8,42.19,42.19,0,0,1-8.73-13.53,47.31,47.31,0,0,1-3.11-17.14,47.93,47.93,0,0,1,3-17.3,39,39,0,0,1,8.57-13.52,38.44,38.44,0,0,1,13.56-8.81,49.09,49.09,0,0,1,17.76-3.14c6.7,0,12.63.94,17.46,3a41.12,41.12,0,0,1,12.46,7.86l-4.51,7.08a4.11,4.11,0,0,1-3.43,2.2,5,5,0,0,1-2.81-1c-1.25-.78-2.49-1.41-3.74-2.2a17.37,17.37,0,0,0-4.21-1.88,38.68,38.68,0,0,0-5.14-1.26,41.35,41.35,0,0,0-6.7-.47,27.67,27.67,0,0,0-11.06,2,22.92,22.92,0,0,0-8.42,6,25.73,25.73,0,0,0-5.3,9.27,37.16,37.16,0,0,0-1.87,12.11,36.55,36.55,0,0,0,2,12.9,27.86,27.86,0,0,0,5.61,9.43,25.65,25.65,0,0,0,8.57,6,29.22,29.22,0,0,0,11.22,2.2Zm84.62-22.17h-25.09v21.07h36.47v12.42h-52v-83.34h52v12.42h-36.63V1181H1251v9.12s-.15,2.68-3.58,2.68Z"/></svg>
\ No newline at end of file
diff --git a/scst/www/images/sourceforge_badges/oss-open-source-excellence-white.svg b/scst/www/images/sourceforge_badges/oss-open-source-excellence-white.svg
new file mode 100644
index 0000000..1cca345
--- /dev/null
+++ b/scst/www/images/sourceforge_badges/oss-open-source-excellence-white.svg
@@ -0,0 +1 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1500 1500"><defs><style>.cls-1{fill:#dbdbdb;}.cls-2{fill:#eae9ee;stroke:#c7c2bd;}.cls-2,.cls-3,.cls-4{stroke-miterlimit:10;}.cls-2,.cls-4{stroke-width:5.1px;}.cls-3{fill:#fff;stroke-width:25.5px;}.cls-3,.cls-4{stroke:#898989;}.cls-4{fill:url(#linear-gradient);}.cls-5{fill:#cacdcc;}.cls-6{fill:#ff6700;}.cls-7{fill:#3f3f3f;}</style><linearGradient id="linear-gradient" x1="117.03" y1="1013.6" x2="1382.97" y2="1013.6" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#eaeaec"/><stop offset="1" stop-color="#fffefe"/></linearGradient></defs><polygon class="cls-1" points="305.42 1239.51 13.18 1239.51 75.36 1098.66 13.18 946.07 305.42 946.07 305.42 1239.51"/><polygon class="cls-1" points="1194.58 957.81 1486.82 957.81 1424.64 1098.66 1486.82 1251.24 1194.58 1251.24 1194.58 957.81"/><path class="cls-2" d="M1338.35,117.61H161.65V1382.39L750,1171.49l588.35,210.9V117.61Z"/><path class="cls-3" d="M1287.9,216.42V1320.67l-517-185.34-20.9-7.49-20.9,7.49-517,185.34V157.33H1287.9Z"/><rect class="cls-4" x="117.03" y="876.32" width="1265.93" height="274.57"/><circle class="cls-5" cx="186.65" cy="1019.1" r="26.74"/><circle class="cls-5" cx="1313.35" cy="1019.1" r="26.74"/><path class="cls-6" d="M345.4,1016.31c0-30-10.59-43.71-16.2-48.9a1.6,1.6,0,0,0-2.65,1.41c1.09,17-20.11,21.23-20.11,47.81v.16c0,16.19,12.16,29.4,27.12,29.4s27.11-13.21,27.11-29.4v-.16c0-7.55-2.8-14.78-5.61-20.13-.62-1.1-2.18-.63-2,.32C358.18,1019.77,345.4,1033.93,345.4,1016.31Z"/><path class="cls-6" d="M313.14,1079.69a2.89,2.89,0,0,1-1.87-.79l-69.34-70a2.87,2.87,0,0,1,0-3.77l73.24-73.91a3.29,3.29,0,0,1,1.71-.63h21a2.46,2.46,0,0,1,2.34,1.57,2.57,2.57,0,0,1-.62,2.83l-68.73,69.51a3.54,3.54,0,0,0,0,5l54.39,55a2.91,2.91,0,0,1,0,3.78l-10.44,10.69a3.3,3.3,0,0,1-1.72.63ZM327,1092a2.45,2.45,0,0,1-2.33-1.57,2.54,2.54,0,0,1,.62-2.83L394.18,1018a3.72,3.72,0,0,0,1.09-2.51,3.14,3.14,0,0,0-1.09-2.52l-54.54-55a2.87,2.87,0,0,1,0-3.77l10.59-10.69a2.54,2.54,0,0,1,1.87-.79,2.39,2.39,0,0,1,1.72.94l69.19,70a2.6,2.6,0,0,1,.78,1.89,2.64,2.64,0,0,1-.78,1.89l-73.24,73.9a2.54,2.54,0,0,1-1.87.79H327Z"/><path class="cls-7" d="M501.71,980.93a1.57,1.57,0,0,1-1.56,1,3,3,0,0,1-1.87-1.1,17.47,17.47,0,0,0-3.12-2.36,18.29,18.29,0,0,0-5-2.36,20.82,20.82,0,0,0-7.48-1.1,24.07,24.07,0,0,0-7.8,1.25,17.12,17.12,0,0,0-5.61,3.46,13.89,13.89,0,0,0-3.42,5,15.72,15.72,0,0,0-1.25,6,13.38,13.38,0,0,0,1.71,6.92,14.5,14.5,0,0,0,4.68,4.56,25.46,25.46,0,0,0,6.54,3.15c2.5.94,5,1.73,7.48,2.51a72.69,72.69,0,0,1,7.48,2.83,29.81,29.81,0,0,1,6.55,3.78,16.48,16.48,0,0,1,4.68,5.66,18.84,18.84,0,0,1,1.71,8.33,27.63,27.63,0,0,1-1.71,9.6,22,22,0,0,1-5,7.86,27.12,27.12,0,0,1-8.11,5.35,30.1,30.1,0,0,1-10.9,1.88,30.49,30.49,0,0,1-13.72-2.83,32.85,32.85,0,0,1-10.13-7.86l1.56-2.52a1.72,1.72,0,0,1,1.56-.78,1.84,1.84,0,0,1,1.25.63c.46.47,1.24,1.1,1.87,1.73.78.62,1.71,1.41,2.8,2.2a21.94,21.94,0,0,0,3.74,2.2,28.35,28.35,0,0,0,4.83,1.73,25.5,25.5,0,0,0,6.24.63,23.8,23.8,0,0,0,8.57-1.42,20.11,20.11,0,0,0,6.39-3.93,17.63,17.63,0,0,0,4.05-6,20.12,20.12,0,0,0,1.4-7.39,13.93,13.93,0,0,0-1.71-7.07,13.46,13.46,0,0,0-4.68-4.72,25.46,25.46,0,0,0-6.54-3.15c-2.5-.78-5-1.72-7.48-2.51s-5-1.73-7.48-2.67a24.84,24.84,0,0,1-6.55-3.78A19,19,0,0,1,461,999.8a20.42,20.42,0,0,1-1.72-8.8,19.71,19.71,0,0,1,1.56-7.87,21.45,21.45,0,0,1,4.52-6.76,24.7,24.7,0,0,1,7.32-4.72,28.28,28.28,0,0,1,10-1.73A29.26,29.26,0,0,1,494.07,972a31.35,31.35,0,0,1,9.2,6.13Z"/><path class="cls-7" d="M479.42,1054.53a30.57,30.57,0,0,1-14.18-3,34.61,34.61,0,0,1-10.44-8l-.47-.47,2-3.15a2.88,2.88,0,0,1,2.34-1.26,2.71,2.71,0,0,1,1.87.95c.47.47,1.24,1.1,2,1.73s1.72,1.41,2.65,2.2a19.15,19.15,0,0,0,3.59,2,20.67,20.67,0,0,0,4.67,1.58,23.42,23.42,0,0,0,6.08.63,22.25,22.25,0,0,0,8.26-1.42,19.94,19.94,0,0,0,6.08-3.77,15.6,15.6,0,0,0,3.74-5.67,17.89,17.89,0,0,0,1.24-7.07,12.22,12.22,0,0,0-1.55-6.61,14.76,14.76,0,0,0-4.37-4.4,25.49,25.49,0,0,0-6.23-3c-2.34-.78-4.83-1.73-7.48-2.51s-5.14-1.73-7.48-2.68a27.88,27.88,0,0,1-6.7-3.93,18.5,18.5,0,0,1-4.83-6.13,20.17,20.17,0,0,1-1.87-9.12,22,22,0,0,1,1.55-8.34,21.54,21.54,0,0,1,4.68-7.07,23.58,23.58,0,0,1,7.64-4.88,26.6,26.6,0,0,1,10.28-1.88,30.35,30.35,0,0,1,11.69,2,31.13,31.13,0,0,1,9.51,6.45l.46.47-1.71,3.3c-.94,1.73-2.65,2.2-4.83.16a20.46,20.46,0,0,0-3.12-2.2,34.11,34.11,0,0,0-4.83-2.36,22.18,22.18,0,0,0-7.17-1.1,21.86,21.86,0,0,0-7.48,1.26,15,15,0,0,0-5.3,3.3,16,16,0,0,0-3.27,4.72A14.06,14.06,0,0,0,465.4,991a11.1,11.1,0,0,0,1.56,6.29,14.61,14.61,0,0,0,4.36,4.4,33.61,33.61,0,0,0,6.23,3.15c2.34.78,4.84,1.72,7.48,2.51a61,61,0,0,1,7.48,2.83,28,28,0,0,1,6.71,3.93,18.29,18.29,0,0,1,4.83,6,18.93,18.93,0,0,1,1.87,8.8,27.65,27.65,0,0,1-1.87,10.07,23.9,23.9,0,0,1-5.3,8.18,26,26,0,0,1-8.42,5.5,30.9,30.9,0,0,1-10.91,1.89Zm-22.59-11.8a29.13,29.13,0,0,0,22.75,9.91,28.28,28.28,0,0,0,10.6-1.89,24.56,24.56,0,0,0,7.79-5,22.37,22.37,0,0,0,4.83-7.55,25.8,25.8,0,0,0,1.71-9.27,20.45,20.45,0,0,0-1.55-8,16,16,0,0,0-4.37-5.35,34.86,34.86,0,0,0-6.23-3.78c-2.34-.94-4.83-1.88-7.33-2.83-2.49-.78-5-1.73-7.48-2.51a39.42,39.42,0,0,1-6.7-3.3,16.05,16.05,0,0,1-4.83-4.88,13.36,13.36,0,0,1-1.87-7.39,18.26,18.26,0,0,1,1.25-6.45,14,14,0,0,1,3.58-5.34,15,15,0,0,1,5.92-3.62,22.08,22.08,0,0,1,8.11-1.26,23.48,23.48,0,0,1,7.79,1.1,25,25,0,0,1,8.42,5c.93.79,1.24.79,1.24.79a.59.59,0,0,0,.63-.47l1.09-2a28.25,28.25,0,0,0-8.26-5.5A30,30,0,0,0,483,971.18a26.45,26.45,0,0,0-9.66,1.73,22.32,22.32,0,0,0-7,4.41,18.9,18.9,0,0,0-5.76,14,18.92,18.92,0,0,0,1.71,8.34,15.21,15.21,0,0,0,4.37,5.5,27.41,27.41,0,0,0,6.23,3.62c2.34.94,4.83,1.88,7.32,2.67s5,1.57,7.48,2.52a29,29,0,0,1,6.7,3.3,13.64,13.64,0,0,1,4.84,5,14.5,14.5,0,0,1,1.87,7.71,21.87,21.87,0,0,1-1.41,7.86,15.92,15.92,0,0,1-4.2,6.29,22.28,22.28,0,0,1-6.71,4.24,26.09,26.09,0,0,1-8.88,1.58,28.71,28.71,0,0,1-6.54-.79,30.43,30.43,0,0,1-5-1.73,16,16,0,0,1-3.9-2.36c-1.09-.78-2-1.57-2.8-2.2a23,23,0,0,1-1.87-1.73c-.47-.47-.78-.47-.78-.47a.84.84,0,0,0-.78.47Z"/><path class="cls-7" d="M594.43,1011.75a50.08,50.08,0,0,1-2.8,17.15,36.41,36.41,0,0,1-20.1,21.7,42.66,42.66,0,0,1-31.33,0,34.74,34.74,0,0,1-12.15-8.5,37.25,37.25,0,0,1-7.95-13.2,53.71,53.71,0,0,1,0-34.29,36.34,36.34,0,0,1,20.1-21.7,42.53,42.53,0,0,1,31.33,0,34.94,34.94,0,0,1,12.15,8.49,37.3,37.3,0,0,1,8,13.21A50,50,0,0,1,594.43,1011.75Zm-6.07,0A47.67,47.67,0,0,0,586,996.34a32.93,32.93,0,0,0-6.55-11.48,28.47,28.47,0,0,0-10.28-7.23,36.09,36.09,0,0,0-26.5,0,28.47,28.47,0,0,0-10.28,7.23,33.2,33.2,0,0,0-6.7,11.48,47,47,0,0,0-2.34,15.41,47.73,47.73,0,0,0,2.34,15.42,30.89,30.89,0,0,0,6.7,11.47,28.37,28.37,0,0,0,10.28,7.24,33.22,33.22,0,0,0,13.25,2.51,32.68,32.68,0,0,0,13.25-2.51,26.57,26.57,0,0,0,10.28-7.24,32.79,32.79,0,0,0,6.55-11.47A47.09,47.09,0,0,0,588.36,1011.75Z"/><path class="cls-7" d="M555.94,1054.37a42.66,42.66,0,0,1-15.89-3,34.79,34.79,0,0,1-12.47-8.65,38.9,38.9,0,0,1-8.11-13.52,51.88,51.88,0,0,1-3-17.46,48.36,48.36,0,0,1,3-17.45,40.43,40.43,0,0,1,8.11-13.52,34.79,34.79,0,0,1,12.47-8.65A40.3,40.3,0,0,1,555.94,969a43.52,43.52,0,0,1,16,3,34.88,34.88,0,0,1,12.47,8.65,40.41,40.41,0,0,1,8.1,13.52,55.62,55.62,0,0,1,0,34.91,40.56,40.56,0,0,1-8.1,13.53,35.13,35.13,0,0,1-12.47,8.65A38.53,38.53,0,0,1,555.94,1054.37Zm0-83.34a37.89,37.89,0,0,0-15.27,3,34.31,34.31,0,0,0-19.48,21.08,51.78,51.78,0,0,0,0,33.65,39,39,0,0,0,7.64,12.89,34.85,34.85,0,0,0,11.84,8.18,42.61,42.61,0,0,0,30.54,0,35,35,0,0,0,11.85-8.18,36,36,0,0,0,7.63-12.73,51.81,51.81,0,0,0,0-33.66,39.13,39.13,0,0,0-7.63-12.89,35,35,0,0,0-11.85-8.18A34.53,34.53,0,0,0,555.94,971Z"/><path class="cls-7" d="M640.72,1048.39a24.73,24.73,0,0,0,10.44-2,23.78,23.78,0,0,0,7.79-5.66,24.1,24.1,0,0,0,4.83-8.49,33.43,33.43,0,0,0,1.72-10.7V971h5.76v50.47a36.8,36.8,0,0,1-2.18,12.58,30.55,30.55,0,0,1-6.08,10.23,28.27,28.27,0,0,1-9.66,6.76,33.12,33.12,0,0,1-12.78,2.51,30,30,0,0,1-22.44-9.27,29.2,29.2,0,0,1-6.08-10.23,37.45,37.45,0,0,1-2.18-12.58V971h5.92v50.32A34.16,34.16,0,0,0,617.5,1032a25,25,0,0,0,4.83,8.49,23.64,23.64,0,0,0,7.79,5.66,23.2,23.2,0,0,0,10.6,2.2Z"/><path class="cls-7" d="M640.72,1054.53a31.88,31.88,0,0,1-13.09-2.52,28.82,28.82,0,0,1-10-7.08,32.28,32.28,0,0,1-6.23-10.53,38.67,38.67,0,0,1-2.18-12.9V970.08H617v51.42a37.76,37.76,0,0,0,1.56,10.38,23.35,23.35,0,0,0,4.67,8.18,22.07,22.07,0,0,0,7.48,5.5,25.1,25.1,0,0,0,10,2,24.49,24.49,0,0,0,10-1.89,20.18,20.18,0,0,0,7.48-5.5,24.72,24.72,0,0,0,4.68-8.18,31.49,31.49,0,0,0,1.56-10.38V970.08h7.79v51.42a38.67,38.67,0,0,1-2.18,12.9,32.48,32.48,0,0,1-6.24,10.53,28.88,28.88,0,0,1-10,7.08,31.92,31.92,0,0,1-13.09,2.52ZM611,972v49.53a35,35,0,0,0,2,12.27,28.85,28.85,0,0,0,5.92,9.91,25.26,25.26,0,0,0,9.35,6.6,31,31,0,0,0,12.47,2.36,32.05,32.05,0,0,0,12.46-2.36,26.68,26.68,0,0,0,9.36-6.6,31.78,31.78,0,0,0,5.92-9.91,35,35,0,0,0,2-12.27V972h-3.89v49.53a36.16,36.16,0,0,1-1.72,11,23.24,23.24,0,0,1-5.14,9,23.75,23.75,0,0,1-8.1,6,26.24,26.24,0,0,1-10.76,2.2,25.69,25.69,0,0,1-10.75-2.2,23.66,23.66,0,0,1-8.1-6,25.17,25.17,0,0,1-5-9,35.4,35.4,0,0,1-1.71-11V972Z"/><path class="cls-7" d="M704.77,1011.28h4.83a35.85,35.85,0,0,0,9.66-1.26,19.42,19.42,0,0,0,7.17-3.77,16.25,16.25,0,0,0,4.52-5.82,17.44,17.44,0,0,0,1.56-7.7c0-5.82-1.87-10.07-5.61-12.9s-9.2-4.24-16.52-4.24H695.57v77.05h-5.76V971h20.57c9.35,0,16.36,1.88,20.88,5.5,4.68,3.62,6.86,9,6.86,16a21.17,21.17,0,0,1-1.56,8.49,20,20,0,0,1-4.68,6.76,22.71,22.71,0,0,1-7.16,4.72,35.48,35.48,0,0,1-9.51,2.52,11.75,11.75,0,0,1,2.49,2.36l27.9,35.22h-5.15a4.68,4.68,0,0,1-1.55-.31,2.79,2.79,0,0,1-1.25-1.1l-25.87-33a6.67,6.67,0,0,0-2-1.73,43,43,0,0,0-6.85-.47c0-4.25.62-4.72,1.87-4.72Z"/><path class="cls-7" d="M747.62,1053.58h-7a4.25,4.25,0,0,1-2-.47,4.31,4.31,0,0,1-1.55-1.41l-25.87-33a5.33,5.33,0,0,0-1.72-1.57,50.7,50.7,0,0,0-6.39-.47h-.93v-4.09c0-1.41.31-2.2,2.8-2.2h5a34.32,34.32,0,0,0,9.35-1.26,19.05,19.05,0,0,0,6.86-3.62,14.47,14.47,0,0,0,4.21-5.5,18.21,18.21,0,0,0,1.4-7.23c0-5.51-1.72-9.44-5.3-12.11s-8.88-4.09-15.9-4.09H696.66v76.9h-7.79V970.08h21.66q14.26,0,21.51,5.66c4.83,3.78,7.32,9.44,7.32,16.83a23.53,23.53,0,0,1-1.71,9,22,22,0,0,1-4.83,7.08,28.6,28.6,0,0,1-7.48,5,36.3,36.3,0,0,1-7.64,2.2,7.15,7.15,0,0,1,.94,1.1Zm-43.79-38.84c1.72,0,5.3.16,6.24.63a7.52,7.52,0,0,1,2.49,2l25.87,33a2.59,2.59,0,0,0,.93.94,2.26,2.26,0,0,0,1.09.32h3.12L716.92,1018a6.63,6.63,0,0,0-2.18-2l-2.34-1.42,2.81-.31a27.62,27.62,0,0,0,16.21-6.92,18.2,18.2,0,0,0,4.36-6.45,23.2,23.2,0,0,0,1.56-8.17c0-6.77-2.18-11.8-6.55-15.26s-11.22-5.19-20.26-5.19H690.9v79.42h3.89V974.64h15.74c7.33,0,13.09,1.42,17,4.41q6.08,4.47,6.08,13.68a21.74,21.74,0,0,1-1.56,8,16.52,16.52,0,0,1-4.83,6.13,21.2,21.2,0,0,1-7.48,3.93,32.68,32.68,0,0,1-10,1.42h-4.83c-.46,0-.93,0-1.09,2.51Z"/><path class="cls-7" d="M814,1038.8a1.13,1.13,0,0,1,.94.47l2.33,2.52a39.49,39.49,0,0,1-5.45,4.87,32.86,32.86,0,0,1-6.55,3.62,53.17,53.17,0,0,1-7.63,2.36,42.63,42.63,0,0,1-9.2.79,38.08,38.08,0,0,1-15.42-3,32.88,32.88,0,0,1-12-8.49,39.32,39.32,0,0,1-7.8-13.21,50,50,0,0,1-2.8-17.14,46.21,46.21,0,0,1,3-17,39.1,39.1,0,0,1,8.1-13.21A36.9,36.9,0,0,1,774,972.91a41.22,41.22,0,0,1,16-3,39.81,39.81,0,0,1,15,2.52,26.85,26.85,0,0,1,5.92,3.15,46.27,46.27,0,0,1,5.46,4.24l-1.72,2.52a1.51,1.51,0,0,1-1.4.63,1.84,1.84,0,0,1-1.24-.63c-.47-.47-1.25-1-2-1.57a24.6,24.6,0,0,0-3-1.89,13,13,0,0,0-4.21-1.89,35.1,35.1,0,0,0-5.61-1.57,33,33,0,0,0-7.17-.63,34.21,34.21,0,0,0-13.56,2.52,29.32,29.32,0,0,0-10.59,7.39,35.38,35.38,0,0,0-7,11.48,42.36,42.36,0,0,0-2.49,15.25,43.26,43.26,0,0,0,2.49,15.41,33.55,33.55,0,0,0,6.86,11.48,30.86,30.86,0,0,0,23.07,9.75,39.25,39.25,0,0,0,7.79-.63,23.88,23.88,0,0,0,6.23-1.73,28.76,28.76,0,0,0,5.3-2.83,28.22,28.22,0,0,0,4.83-3.93c.16-.16.31-.32.47-.32C813.54,1039,813.86,1038.8,814,1038.8Z"/><path class="cls-7" d="M788.3,1054.53a41.83,41.83,0,0,1-15.74-3,34.31,34.31,0,0,1-12.31-8.65,38.68,38.68,0,0,1-8-13.52,51.9,51.9,0,0,1-2.81-17.46,47.54,47.54,0,0,1,3-17.3,43.77,43.77,0,0,1,8.26-13.52,38.28,38.28,0,0,1,12.78-8.81A43.07,43.07,0,0,1,790,969.14a45.38,45.38,0,0,1,8.26.63,46.29,46.29,0,0,1,7,1.88,27.79,27.79,0,0,1,6.08,3.15,45.84,45.84,0,0,1,5.61,4.4l.63.63-2.19,3.3a2.42,2.42,0,0,1-2.18,1.1,3.69,3.69,0,0,1-1.87-.78,12.32,12.32,0,0,0-2-1.42,16.31,16.31,0,0,0-3-1.88,33.37,33.37,0,0,0-4-1.89,25.44,25.44,0,0,0-5.45-1.42,45.22,45.22,0,0,0-7-.63,33.15,33.15,0,0,0-13.25,2.52,30.06,30.06,0,0,0-17,18.24,44.51,44.51,0,0,0-2.49,14.78,45,45,0,0,0,2.34,15.1A31.63,31.63,0,0,0,766,1038a27.93,27.93,0,0,0,10,6.91,31.87,31.87,0,0,0,12.46,2.36,36.93,36.93,0,0,0,7.64-.63,24.47,24.47,0,0,0,6.08-1.73,38.1,38.1,0,0,0,5.14-2.67,49.88,49.88,0,0,0,4.68-3.77,1.89,1.89,0,0,1,.77-.47,2.13,2.13,0,0,1,2.65.47l3,3.14-.62.63a42,42,0,0,1-5.61,5,31.16,31.16,0,0,1-6.7,3.78,29.84,29.84,0,0,1-8,2.36A33,33,0,0,1,788.3,1054.53ZM790,971a39.16,39.16,0,0,0-15.74,3,33,33,0,0,0-12.15,8.34,37.25,37.25,0,0,0-8,12.89,47.33,47.33,0,0,0-2.81,16.67,48.24,48.24,0,0,0,2.81,16.83,39.13,39.13,0,0,0,7.63,12.89,34.35,34.35,0,0,0,11.69,8.18,38.42,38.42,0,0,0,15,2.83,52.82,52.82,0,0,0,9-.79,34.09,34.09,0,0,0,13.72-6,39.62,39.62,0,0,0,4.67-4.09l-1.71-1.89a.3.3,0,0,0-.47,0c-.16,0-.31.16-.47.32a39.32,39.32,0,0,1-5,3.93,37,37,0,0,1-5.45,3,30.32,30.32,0,0,1-6.39,1.73,59.1,59.1,0,0,1-7.95.63,36.81,36.81,0,0,1-13.24-2.52,31.6,31.6,0,0,1-10.6-7.39,35.29,35.29,0,0,1-7-11.8,50.2,50.2,0,0,1,0-31.29,33,33,0,0,1,7.17-11.79,30.89,30.89,0,0,1,10.9-7.55,36.38,36.38,0,0,1,13.87-2.67,35.54,35.54,0,0,1,7.33.62,44.14,44.14,0,0,1,5.76,1.58,22.55,22.55,0,0,1,4.37,2,32.86,32.86,0,0,1,3,2c.78.62,1.56,1.1,2,1.57s.77.47.77.47c.47,0,.63-.16.63-.31l1.24-1.89A45,45,0,0,0,810,977a28.77,28.77,0,0,0-5.77-3,42,42,0,0,0-6.7-1.88A21.33,21.33,0,0,0,790,971Z"/><path class="cls-7" d="M880.55,971v4.87H838v33h35.38v4.72H838v34h42.54v4.87H832.09V971Z"/><path class="cls-7" d="M881.49,1053.58H831.15v-83.5h50.34v6.76H839v31h35.37v6.76H839v32.08h42.54ZM833,1051.7h46.6v-3H837.08v-36h35.37v-2.83H837.08V975h42.54v-3H833Z"/><path class="cls-7" d="M953,970.08v12.43H916.4v24.68h30.85v12.43H916.4v34H901V970.24h52Zm89.61,41.67a46.42,46.42,0,0,1-3,17,39,39,0,0,1-8.58,13.52,39.59,39.59,0,0,1-13.24,9,43.29,43.29,0,0,1-17.14,3.14,46.37,46.37,0,0,1-17.15-3.14,39.26,39.26,0,0,1-21.81-22.49,50.21,50.21,0,0,1,0-34,39,39,0,0,1,8.57-13.52,41.27,41.27,0,0,1,13.24-9,47.26,47.26,0,0,1,34.29.16,39.24,39.24,0,0,1,21.82,22.49A48.17,48.17,0,0,1,1042.63,1011.75Zm-15.74,0a37.67,37.67,0,0,0-1.87-12.26,27.92,27.92,0,0,0-5.15-9.28,22.53,22.53,0,0,0-8.25-5.82,30.19,30.19,0,0,0-21.82,0,23.89,23.89,0,0,0-8.26,5.82,25.77,25.77,0,0,0-5.3,9.28,41.16,41.16,0,0,0,0,24.53,25.77,25.77,0,0,0,5.3,9.28,24.12,24.12,0,0,0,8.26,5.82,30.19,30.19,0,0,0,21.82,0,24,24,0,0,0,8.25-5.82A27.92,27.92,0,0,0,1025,1024,37.73,37.73,0,0,0,1026.89,1011.75Zm47.84-1.88h3.58a22.52,22.52,0,0,0,7.64-1.1,15.68,15.68,0,0,0,5.3-3,11.46,11.46,0,0,0,3.12-4.56,17.65,17.65,0,0,0,1.09-5.82c0-4.25-1.41-7.55-4.21-9.75s-7-3.46-12.62-3.46h-10v71.39h-15.43V970.24h25.25A55.53,55.53,0,0,1,1093,972a26.26,26.26,0,0,1,9.82,4.87,18.85,18.85,0,0,1,5.61,7.55,24.64,24.64,0,0,1,1.87,9.75,26.93,26.93,0,0,1-1.25,8,21.31,21.31,0,0,1-3.58,6.76,23.54,23.54,0,0,1-5.77,5.35,25.45,25.45,0,0,1-7.79,3.62,15.77,15.77,0,0,1,5,4.71l20.73,30.83h-13.87a7.36,7.36,0,0,1-3.43-.79,5.6,5.6,0,0,1-2.34-2.36l-17.3-26.73c-.47-.63-1.4-2-2.34-2.2h-5.45v-10.07c.16-1.41,1.87-1.41,1.87-1.41Zm88,32.23a38,38,0,0,0,9-.94,41.15,41.15,0,0,0,7.32-2.67v-18.87h-10.28a3.81,3.81,0,0,1-2.34-.79,2.8,2.8,0,0,1-.94-2V1008H1193v37.43a34.6,34.6,0,0,1-6.54,3.93,51.4,51.4,0,0,1-7.33,2.83,45.43,45.43,0,0,1-8.26,1.57,88,88,0,0,1-9.5.47,44.46,44.46,0,0,1-16.68-3.14,36.49,36.49,0,0,1-13.09-8.81,42,42,0,0,1-8.73-13.52,47.31,47.31,0,0,1-3.11-17.14,47.93,47.93,0,0,1,3-17.3,39,39,0,0,1,8.57-13.52,38.17,38.17,0,0,1,13.56-8.81,48.88,48.88,0,0,1,17.76-3.15c6.7,0,12.63.95,17.46,3a41.51,41.51,0,0,1,12.46,7.86l-4.51,7.08a4.09,4.09,0,0,1-3.43,2.2,5,5,0,0,1-2.81-.94c-1.25-.79-2.49-1.42-3.74-2.2a17,17,0,0,0-4.21-1.89,42.05,42.05,0,0,0-5.14-1.26,42.52,42.52,0,0,0-6.7-.47,27.85,27.85,0,0,0-11.06,2,23,23,0,0,0-8.42,6,25.77,25.77,0,0,0-5.3,9.28,37.12,37.12,0,0,0-1.87,12.11,36.5,36.5,0,0,0,2,12.89,27.91,27.91,0,0,0,5.61,9.44,25.62,25.62,0,0,0,8.57,6,29,29,0,0,0,11.22,2.2Zm84.62-22.17h-25.09V1041h36.47v12.43h-52V970.08h52v12.43h-36.63v25.63H1251v9.12s-.15,2.67-3.58,2.67Z"/><path class="cls-7" d="M283.92,706.32A49.64,49.64,0,0,1,265,687.75a54.62,54.62,0,0,1,0-53.54,49.86,49.86,0,0,1,18.93-18.49,55,55,0,0,1,52.45,0,48.87,48.87,0,0,1,18.79,18.49,55.42,55.42,0,0,1-.07,53.54,49.34,49.34,0,0,1-18.79,18.57,54.9,54.9,0,0,1-52.38,0Zm45.66-24q7.31-8.08,7.3-21.38t-7.3-21.46q-7.29-8-19.43-8t-19.58,7.95q-7.31,8-7.3,21.53t7.3,21.46q7.29,8,19.58,8Q322.29,690.43,329.58,682.33Z"/><path class="cls-7" d="M447.57,659.29a29.73,29.73,0,0,1-12.43,11.77q-8.38,4.49-20.81,4.48H399V712H374.3V610.52h40q12.14,0,20.52,4.19a28.7,28.7,0,0,1,12.57,11.56,33.63,33.63,0,0,1,4.19,16.9A32.78,32.78,0,0,1,447.57,659.29ZM423,652.57q3.47-3.33,3.47-9.4T423,633.78q-3.47-3.31-10.55-3.32H399v25.43h13.44Q419.53,655.89,423,652.57Z"/><path class="cls-7" d="M487.74,630.31v20.52h33.09v19.08H487.74v22.25h37.42V712H463V610.52h62.13v19.79Z"/><path class="cls-7" d="M630.5,712H605.79l-41.32-62.57V712H539.76V610.52h24.71l41.32,62.85V610.52H630.5Z"/><path class="cls-7" d="M692.92,709.36a32.6,32.6,0,0,1-14.09-10.7,29.22,29.22,0,0,1-5.56-17h26.3q.57,5.64,3.9,8.6a12.58,12.58,0,0,0,8.67,3q5.49,0,8.67-2.53a8.45,8.45,0,0,0,3.18-7,8.28,8.28,0,0,0-2.53-6.21,20.55,20.55,0,0,0-6.21-4,100.5,100.5,0,0,0-10.48-3.61,112.76,112.76,0,0,1-16-6.07,29.56,29.56,0,0,1-10.69-9q-4.49-5.91-4.48-15.46,0-14.16,10.26-22.18t26.73-8q16.77,0,27,8t11,22.33H721.82a10.4,10.4,0,0,0-3.61-7.73,12.73,12.73,0,0,0-8.52-2.82,10.63,10.63,0,0,0-7.23,2.38,8.61,8.61,0,0,0-2.75,6.87,8.51,8.51,0,0,0,4.63,7.66q4.62,2.73,14.45,5.92a135.38,135.38,0,0,1,16,6.36,30.45,30.45,0,0,1,10.62,8.81q4.47,5.79,4.48,14.88a29.22,29.22,0,0,1-4.41,15.76,30.91,30.91,0,0,1-12.79,11.27q-8.38,4.19-19.8,4.19A52.34,52.34,0,0,1,692.92,709.36Z"/><path class="cls-7" d="M785.33,706.32a49.64,49.64,0,0,1-18.93-18.57,54.62,54.62,0,0,1,0-53.54,49.86,49.86,0,0,1,18.93-18.49,55,55,0,0,1,52.45,0,48.87,48.87,0,0,1,18.79,18.49,55.42,55.42,0,0,1-.07,53.54,49.34,49.34,0,0,1-18.79,18.57,54.9,54.9,0,0,1-52.38,0Zm45.66-24q7.29-8.08,7.3-21.38T831,639.49q-7.29-8-19.43-8T792,639.42q-7.31,8-7.3,21.53t7.3,21.46q7.29,8,19.58,8Q823.7,690.43,831,682.33Z"/><path class="cls-7" d="M900,610.52v60.69q0,9.1,4.48,14t13.15,4.92q8.67,0,13.29-4.92t4.63-14V610.52h24.71v60.54q0,13.59-5.78,23a37,37,0,0,1-15.54,14.16A48.82,48.82,0,0,1,917.19,713a47.64,47.64,0,0,1-21.46-4.7,35,35,0,0,1-15-14.16q-5.49-9.47-5.49-23.05V610.52Z"/><path class="cls-7" d="M1027.87,712l-21.1-38.3h-5.92V712H976.14V610.52h41.47q12,0,20.45,4.19a28.82,28.82,0,0,1,12.64,11.49,32.06,32.06,0,0,1,4.19,16.25,30.18,30.18,0,0,1-5.7,18.06q-5.71,7.95-16.84,11.28L1055.76,712Zm-27-55.78h15.32q6.78,0,10.18-3.33t3.4-9.39q0-5.78-3.4-9.1T1016.17,631h-15.32Z"/><path class="cls-7" d="M1071.22,634.29a46.44,46.44,0,0,1,18.13-18.35,52.61,52.61,0,0,1,26.37-6.58q18.08,0,30.93,9.54t17.19,26h-27.16a21.94,21.94,0,0,0-8.6-9.68,24.13,24.13,0,0,0-12.64-3.33q-11.42,0-18.5,8t-7.08,21.24q0,13.3,7.08,21.24t18.5,7.95a24.12,24.12,0,0,0,12.64-3.32,22,22,0,0,0,8.6-9.68h27.16q-4.33,16.47-17.19,25.93t-30.93,9.47a52.61,52.61,0,0,1-26.37-6.58,46.53,46.53,0,0,1-18.13-18.28,58.35,58.35,0,0,1,0-53.53Z"/><path class="cls-7" d="M1202.28,630.31v20.52h33.09v19.08h-33.09v22.25h37.43V712h-62.14V610.52h62.14v19.79Z"/><path class="cls-7" d="M348.8,754.41v20.52h33.09V794H348.8v22.25h37.43v19.8H324.09V734.62h62.14v19.79Z"/><path class="cls-7" d="M463,836.06,442.29,805l-18.21,31.07h-28l32.51-51.59-33.23-49.85h28.75l20.38,30.63,17.92-30.63h28l-32.22,51.15,33.52,50.29Z"/><path class="cls-7" d="M505,758.39A46.47,46.47,0,0,1,523.14,740a52.55,52.55,0,0,1,26.37-6.58q18.06,0,30.92,9.54t17.2,26H570.46a21.92,21.92,0,0,0-8.59-9.68A24.16,24.16,0,0,0,549.22,756q-11.41,0-18.5,8t-7.07,21.24q0,13.3,7.07,21.24t18.5,8a24.16,24.16,0,0,0,12.65-3.32,22,22,0,0,0,8.59-9.68h27.17q-4.33,16.47-17.2,25.93t-30.92,9.47a52.55,52.55,0,0,1-26.37-6.58A46.55,46.55,0,0,1,505,811.92a58.35,58.35,0,0,1,0-53.53Z"/><path class="cls-7" d="M636.07,754.41v20.52h33.09V794H636.07v22.25h37.42v19.8H611.36V734.62h62.13v19.79Z"/><path class="cls-7" d="M712.79,817h32.37v19.08H688.08V734.62h24.71Z"/><path class="cls-7" d="M780.27,817h32.37v19.08H755.56V734.62h24.71Z"/><path class="cls-7" d="M847.75,754.41v20.52h33.09V794H847.75v22.25h37.43v19.8H823V734.62h62.14v19.79Z"/><path class="cls-7" d="M990.52,836.06H965.81l-41.33-62.57v62.57H899.77V734.62h24.71l41.33,62.85V734.62h24.71Z"/><path class="cls-7" d="M1009.3,758.39A46.47,46.47,0,0,1,1027.44,740a52.55,52.55,0,0,1,26.37-6.58q18.06,0,30.92,9.54t17.2,26h-27.17a21.88,21.88,0,0,0-8.6-9.68,24.13,24.13,0,0,0-12.64-3.33q-11.41,0-18.5,8t-7.08,21.24q0,13.3,7.08,21.24t18.5,8a24.12,24.12,0,0,0,12.64-3.32,21.93,21.93,0,0,0,8.6-9.68h27.17q-4.34,16.47-17.2,25.93t-30.92,9.47a52.55,52.55,0,0,1-26.37-6.58,46.55,46.55,0,0,1-18.14-18.28,58.35,58.35,0,0,1,0-53.53Z"/><path class="cls-7" d="M1140.36,754.41v20.52h33.09V794h-33.09v22.25h37.43v19.8h-62.14V734.62h62.14v19.79Z"/><path class="cls-6" d="M721.49,266.64c13,9.82,22.79,21.29,28.48,35.88,6,15.41,4.15,30.05-5.22,43.6-4,5.81-8.69,11.17-12.94,16.83-6.22,8.27-10.66,17.25-10.95,30.09,4.82-6.19,8.22-11.05,12.12-15.48,9.53-10.85,19.65-21.2,28.87-32.31,25.76-31,26.58-63.79,2.86-96.36-1-1.36-2-2.71-4-5.44,3.07.85,4.78,1.09,6.28,1.78,34.34,15.75,52.11,49,44.26,82.78-3.14,13.48-10.79,24.26-20.71,33.43-8.52,7.88-18.07,14.68-26.28,22.85-9.17,9.13-17.17,19.43-26.07,28.84-2.16,2.29-5.81,4.31-8.84,4.42-13.3.47-26.63-.05-39.94.32-5.59.16-8.67-1.81-11.26-6.74-14.13-26.86-11-58.72,8.52-81.88a101.39,101.39,0,0,1,11.2-11.59C713.4,304.32,725,289.27,721.49,266.64Z"/><path class="cls-6" d="M682.68,463V433.67H804.15V463Z"/><path class="cls-6" d="M751.87,415.48c5.55-6.17,10.58-13,16.79-18.37,7.63-6.64,16.09-12.38,24.49-18.08C807,369.6,819.93,359.31,827,342.66c9.37,23.59-.68,59.22-19.87,72.94-1.12.8-2.45,1.83-3.69,1.84-16.79.12-33.58.08-50.37.08Z"/><path class="cls-6" d="M728.57,547.28V475h29.54v72.31Z"/></svg>
\ No newline at end of file
diff --git a/scst/www/images/sourceforge_badges/oss-sf-favorite-white.svg b/scst/www/images/sourceforge_badges/oss-sf-favorite-white.svg
new file mode 100644
index 0000000..5a4aa0b
--- /dev/null
+++ b/scst/www/images/sourceforge_badges/oss-sf-favorite-white.svg
@@ -0,0 +1 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1500 1500"><defs><style>.cls-1{fill:#898989;}.cls-2{fill:#eae9ee;stroke:#c7c2bd;}.cls-2,.cls-5{stroke-miterlimit:10;stroke-width:5.1px;}.cls-3{fill:#fff;}.cls-4{fill:#3f3f3f;}.cls-5{stroke:#898989;fill:url(#linear-gradient);}.cls-6{fill:#ff6700;}</style><linearGradient id="linear-gradient" x1="1319.56" y1="1079.42" x2="89.91" y2="1079.42" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fffefe"/><stop offset="1" stop-color="#eaeaec"/></linearGradient></defs><polygon class="cls-1" points="1421.26 928.81 78.74 928.81 200.73 861.72 1299.27 861.72 1421.26 928.81"/><path class="cls-2" d="M1373,738.52a24.45,24.45,0,0,1-1.84-35.31,68.92,68.92,0,0,0-18-108.11,24.45,24.45,0,0,1-9.94-33.93,68.92,68.92,0,0,0-42.49-101,24.45,24.45,0,0,1-17.51-30.72,68.93,68.93,0,0,0-64.67-88.49,24.44,24.44,0,0,1-24.13-25.84A68.93,68.93,0,0,0,1111,243.92a24.44,24.44,0,0,1-29.45-19.57,68.92,68.92,0,0,0-97.53-50,24.44,24.44,0,0,1-33.17-12.24A68.94,68.94,0,0,0,844.41,136a24.44,24.44,0,0,1-35.1-4.25,68.87,68.87,0,0,0-108-2.76,24.07,24.07,0,0,1-33.38,3.62,68.87,68.87,0,0,0-105.06,25.14,24.46,24.46,0,0,1-33.38,11.69A68.92,68.92,0,0,0,430.8,217.1,24.45,24.45,0,0,1,401,236.18a68.91,68.91,0,0,0-85,69.16,24.46,24.46,0,0,1-24.57,25.44,68.93,68.93,0,0,0-66.75,86.93,24.45,24.45,0,0,1-18,30.42,68.92,68.92,0,0,0-44.88,100,24.44,24.44,0,0,1-10.51,33.76,68.93,68.93,0,0,0-20.57,107.65,24.45,24.45,0,0,1-2.43,35.28,68.93,68.93,0,0,0,4.85,109.49,24.46,24.46,0,0,1,5.78,34.89,68.92,68.92,0,0,0,30,105.41,24.45,24.45,0,0,1,13.68,32.61,68.93,68.93,0,0,0,53.53,95.64A24.44,24.44,0,0,1,257,1131.41a68.92,68.92,0,0,0,74.17,80.69,24.47,24.47,0,0,1,26.88,23,68.93,68.93,0,0,0,90.8,61.39,24.44,24.44,0,0,1,31.45,16.15,68.92,68.92,0,0,0,102.52,38.75,24.45,24.45,0,0,1,34.33,8.46,68.93,68.93,0,0,0,108.7,14,24.45,24.45,0,0,1,35.36.3,68.92,68.92,0,0,0,109-11.45,24.46,24.46,0,0,1,34.48-7.88,68.92,68.92,0,0,0,103.41-36.31,24.45,24.45,0,0,1,31.72-15.62,68.93,68.93,0,0,0,92.23-59.21,24.45,24.45,0,0,1,27.25-22.53,68.92,68.92,0,0,0,76.07-78.91,24.44,24.44,0,0,1,21.31-28.21,68.92,68.92,0,0,0,55.79-94.34,24.45,24.45,0,0,1,14.23-32.37,68.93,68.93,0,0,0,32.49-104.68,24.45,24.45,0,0,1,6.37-34.78A68.93,68.93,0,0,0,1373,738.52Z"/><circle class="cls-1" cx="750" cy="750" r="511.86" transform="translate(-310.66 750) rotate(-45)"/><circle class="cls-3" cx="750" cy="750" r="479.87" transform="translate(-310.66 750) rotate(-45)"/><path class="cls-4" d="M423.52,724.77v23.29H374.9v25.16h36.38v22.61H374.9v48.28H345.83V724.77Z"/><path class="cls-4" d="M513.11,823H468.57l-7.14,21.08H431l43.18-119.34h33.66L551,844.11H520.25Zm-7.48-22.44L490.84,756.9l-14.62,43.69Z"/><path class="cls-4" d="M676.14,724.77,633.81,844.11H597.43L555.1,724.77H586l29.58,90.1,29.75-90.1Z"/><path class="cls-4" d="M713.79,837.48a58.44,58.44,0,0,1-22.27-21.84,64.31,64.31,0,0,1,0-63,58.67,58.67,0,0,1,22.27-21.76,64.78,64.78,0,0,1,61.71,0,57.39,57.39,0,0,1,22.1,21.76,65.14,65.14,0,0,1-.08,63,58,58,0,0,1-22.1,21.84,64.62,64.62,0,0,1-61.63,0Zm53.72-28.22q8.6-9.53,8.59-25.16,0-15.81-8.59-25.24t-22.86-9.44q-14.45,0-23,9.35T713,784.1q0,15.81,8.58,25.24t23,9.44Q758.93,818.78,767.51,809.26Z"/><path class="cls-4" d="M882.69,844.11l-24.82-45.05h-7v45.05H821.83V724.77h48.79q14.1,0,24,4.93a34,34,0,0,1,14.88,13.51,37.81,37.81,0,0,1,4.93,19.13,35.5,35.5,0,0,1-6.72,21.25q-6.71,9.35-19.8,13.26l27.54,47.26ZM850.9,778.49h18q8,0,12-3.91t4-11.05q0-6.79-4-10.71t-12-3.91h-18Z"/><path class="cls-4" d="M961.74,724.77V844.11H932.67V724.77Z"/><path class="cls-4" d="M1068.67,724.77v23.29h-31.62v96.05H1008V748.06H976.36V724.77Z"/><path class="cls-4" d="M1112.36,748.06V772.2h38.93v22.44h-38.93v26.18h44v23.29h-73.1V724.77h73.1v23.29Z"/><path class="cls-5" d="M1421.26,928.81H78.74l33.72,166.55A168,168,0,0,0,277.13,1230h945.74a168,168,0,0,0,164.67-134.66Z"/><path class="cls-6" d="M345.4,1082.67c0-30-10.59-43.72-16.2-48.91a1.6,1.6,0,0,0-2.65,1.42c1.09,17-20.11,21.23-20.11,47.8v.16c0,16.2,12.16,29.41,27.12,29.41s27.11-13.21,27.11-29.41V1083c0-7.55-2.8-14.78-5.61-20.13-.62-1.1-2.18-.63-2,.32C358.18,1086.13,345.4,1100.28,345.4,1082.67Z"/><path class="cls-6" d="M313.14,1146a2.84,2.84,0,0,1-1.87-.79l-69.34-70a2.88,2.88,0,0,1,0-3.78l73.24-73.91a3.29,3.29,0,0,1,1.71-.63h21a2.46,2.46,0,0,1,2.34,1.58,2.58,2.58,0,0,1-.62,2.83l-68.73,69.5a3.55,3.55,0,0,0,0,5l54.39,55a2.91,2.91,0,0,1,0,3.78l-10.44,10.69a3.21,3.21,0,0,1-1.72.63ZM327,1158.31a2.45,2.45,0,0,1-2.33-1.58,2.54,2.54,0,0,1,.62-2.83l68.88-69.5a3.76,3.76,0,0,0,1.09-2.52,3.12,3.12,0,0,0-1.09-2.51l-54.54-55a2.88,2.88,0,0,1,0-3.78l10.59-10.69a2.58,2.58,0,0,1,1.87-.79,2.36,2.36,0,0,1,1.72.95L423,1080a2.64,2.64,0,0,1,.78,1.89,2.6,2.6,0,0,1-.78,1.89l-73.24,73.91a2.57,2.57,0,0,1-1.87.78H327Z"/><path class="cls-4" d="M501.71,1047.29a1.57,1.57,0,0,1-1.56.94,3,3,0,0,1-1.87-1.1,18,18,0,0,0-3.12-2.36,18.59,18.59,0,0,0-5-2.36,20.82,20.82,0,0,0-7.48-1.1,23.8,23.8,0,0,0-7.8,1.26,16.8,16.8,0,0,0-5.61,3.46,13.77,13.77,0,0,0-3.42,5,15.77,15.77,0,0,0-1.25,6,13.31,13.31,0,0,0,1.71,6.91,14.4,14.4,0,0,0,4.68,4.56,25.46,25.46,0,0,0,6.54,3.15c2.5.94,5,1.73,7.48,2.52a70.45,70.45,0,0,1,7.48,2.83,29.78,29.78,0,0,1,6.55,3.77,16.58,16.58,0,0,1,4.68,5.66,18.85,18.85,0,0,1,1.71,8.34,27.58,27.58,0,0,1-1.71,9.59,21.89,21.89,0,0,1-5,7.86,27.12,27.12,0,0,1-8.11,5.35,29.89,29.89,0,0,1-10.9,1.89,30.49,30.49,0,0,1-13.72-2.84,32.7,32.7,0,0,1-10.13-7.86l1.56-2.51a1.73,1.73,0,0,1,1.56-.79,1.88,1.88,0,0,1,1.25.63c.46.47,1.24,1.1,1.87,1.73.78.63,1.71,1.41,2.8,2.2a21.3,21.3,0,0,0,3.74,2.2,28.35,28.35,0,0,0,4.83,1.73,25.5,25.5,0,0,0,6.24.63,24,24,0,0,0,8.57-1.41,20.13,20.13,0,0,0,6.39-3.94,17.69,17.69,0,0,0,4.05-6,20.2,20.2,0,0,0,1.4-7.39,14,14,0,0,0-1.71-7.08,13.46,13.46,0,0,0-4.68-4.72,25.12,25.12,0,0,0-6.54-3.14c-2.5-.79-5-1.73-7.48-2.52s-5-1.73-7.48-2.67a24.3,24.3,0,0,1-6.55-3.78,19.08,19.08,0,0,1-4.67-5.81,20.43,20.43,0,0,1-1.72-8.81,19.66,19.66,0,0,1,1.56-7.86,21.22,21.22,0,0,1,4.52-6.76,24.5,24.5,0,0,1,7.32-4.72,28.28,28.28,0,0,1,10-1.73,29.44,29.44,0,0,1,11.37,2,31.55,31.55,0,0,1,9.2,6.13Z"/><path class="cls-4" d="M479.42,1120.88c-5.45,0-10.28-.94-14.18-3a34.29,34.29,0,0,1-10.44-8l-.47-.47,2-3.14a2.88,2.88,0,0,1,2.34-1.26,2.69,2.69,0,0,1,1.87.94c.47.47,1.24,1.1,2,1.73s1.72,1.42,2.65,2.2a19.17,19.17,0,0,0,3.59,2.05,20.18,20.18,0,0,0,4.67,1.57,23.42,23.42,0,0,0,6.08.63,22.05,22.05,0,0,0,8.26-1.42,19.94,19.94,0,0,0,6.08-3.77,15.56,15.56,0,0,0,3.74-5.66,17.91,17.91,0,0,0,1.24-7.08,12.18,12.18,0,0,0-1.55-6.6,14.79,14.79,0,0,0-4.37-4.41,25.47,25.47,0,0,0-6.23-3c-2.34-.79-4.83-1.73-7.48-2.52s-5.14-1.73-7.48-2.67a27.88,27.88,0,0,1-6.7-3.93,18.64,18.64,0,0,1-4.83-6.14,20.14,20.14,0,0,1-1.87-9.12,21.94,21.94,0,0,1,1.55-8.33,21.48,21.48,0,0,1,4.68-7.08,23.56,23.56,0,0,1,7.64-4.87,26.6,26.6,0,0,1,10.28-1.89,30.54,30.54,0,0,1,11.69,2,31,31,0,0,1,9.5,6.45l.47.47-1.71,3.3c-.94,1.73-2.65,2.21-4.83.16a21.19,21.19,0,0,0-3.12-2.2,35.23,35.23,0,0,0-4.83-2.36,22.18,22.18,0,0,0-7.17-1.1,21.86,21.86,0,0,0-7.48,1.26,15.13,15.13,0,0,0-5.3,3.3,16.14,16.14,0,0,0-3.27,4.72,14.06,14.06,0,0,0-1.09,5.66,11.06,11.06,0,0,0,1.56,6.29,14.5,14.5,0,0,0,4.36,4.4,33.61,33.61,0,0,0,6.23,3.15c2.34.78,4.83,1.73,7.48,2.51a61,61,0,0,1,7.48,2.83,28,28,0,0,1,6.71,3.94,18.15,18.15,0,0,1,4.83,6,18.94,18.94,0,0,1,1.87,8.81,27.56,27.56,0,0,1-1.87,10.06,23.81,23.81,0,0,1-5.3,8.18,26,26,0,0,1-8.42,5.5,30.9,30.9,0,0,1-10.91,1.89Zm-22.59-11.79a29.17,29.17,0,0,0,22.75,9.9,28.48,28.48,0,0,0,10.6-1.88,24.76,24.76,0,0,0,7.79-5,22.32,22.32,0,0,0,4.83-7.54,25.86,25.86,0,0,0,1.71-9.28,20.45,20.45,0,0,0-1.55-8,16.17,16.17,0,0,0-4.37-5.35,34.83,34.83,0,0,0-6.23-3.77c-2.34-.95-4.83-1.89-7.33-2.83-2.49-.79-5-1.73-7.48-2.52a38,38,0,0,1-6.7-3.3,16.05,16.05,0,0,1-4.83-4.88,13.34,13.34,0,0,1-1.87-7.39,18.17,18.17,0,0,1,1.25-6.44,13.94,13.94,0,0,1,3.58-5.35,15.23,15.23,0,0,1,5.92-3.62,22.08,22.08,0,0,1,8.11-1.26,23.48,23.48,0,0,1,7.79,1.1,22.57,22.57,0,0,1,5.14,2.52,21.33,21.33,0,0,1,3.28,2.52c.93.78,1.24.78,1.24.78a.59.59,0,0,0,.63-.47l1.09-2a28.29,28.29,0,0,0-8.26-5.51,30,30,0,0,0-10.91-1.88,26.24,26.24,0,0,0-9.66,1.73,22.3,22.3,0,0,0-7,4.4,18.94,18.94,0,0,0-5.76,14,18.85,18.85,0,0,0,1.71,8.34,15.13,15.13,0,0,0,4.37,5.5,26.69,26.69,0,0,0,6.23,3.62c2.34.94,4.83,1.89,7.32,2.67s5,1.58,7.48,2.52a29.44,29.44,0,0,1,6.7,3.3,13.64,13.64,0,0,1,4.84,5,14.53,14.53,0,0,1,1.87,7.71,21.83,21.83,0,0,1-1.41,7.86,15.85,15.85,0,0,1-4.2,6.29,22.3,22.3,0,0,1-6.71,4.25,26.34,26.34,0,0,1-8.88,1.57,28.16,28.16,0,0,1-6.54-.79,30.43,30.43,0,0,1-5-1.73,16,16,0,0,1-3.9-2.35c-1.09-.79-2-1.58-2.8-2.21a21.36,21.36,0,0,1-1.87-1.73c-.47-.47-.78-.47-.78-.47a.84.84,0,0,0-.78.47Z"/><path class="cls-4" d="M594.43,1078.11a50,50,0,0,1-2.8,17.14,36.34,36.34,0,0,1-20.1,21.7,42.53,42.53,0,0,1-31.33,0,34.85,34.85,0,0,1-12.15-8.49,37.3,37.3,0,0,1-7.95-13.21,53.68,53.68,0,0,1,0-34.28,37.21,37.21,0,0,1,7.95-13.21,36.79,36.79,0,0,1,12.15-8.49,42.53,42.53,0,0,1,31.33,0,34.68,34.68,0,0,1,12.15,8.49,37.21,37.21,0,0,1,8,13.21A50,50,0,0,1,594.43,1078.11Zm-6.07,0A47.67,47.67,0,0,0,586,1062.7a32.84,32.84,0,0,0-6.55-11.48,28.37,28.37,0,0,0-10.28-7.24,36.23,36.23,0,0,0-26.5,0,28.37,28.37,0,0,0-10.28,7.24,33.1,33.1,0,0,0-6.7,11.48,47,47,0,0,0-2.34,15.41,47.67,47.67,0,0,0,2.34,15.41,31,31,0,0,0,6.7,11.48,28.47,28.47,0,0,0,10.28,7.23,33.22,33.22,0,0,0,13.25,2.52,32.69,32.69,0,0,0,13.25-2.52,26.66,26.66,0,0,0,10.28-7.23,32.93,32.93,0,0,0,6.55-11.48A47,47,0,0,0,588.36,1078.11Z"/><path class="cls-4" d="M555.94,1120.72a42.86,42.86,0,0,1-15.89-3,35,35,0,0,1-12.47-8.65,39,39,0,0,1-8.11-13.53,51.83,51.83,0,0,1-3-17.45,48.41,48.41,0,0,1,3-17.46,40.43,40.43,0,0,1,8.11-13.52,34.79,34.79,0,0,1,12.47-8.65,40.48,40.48,0,0,1,15.89-3.15,43.72,43.72,0,0,1,16,3,35.13,35.13,0,0,1,12.47,8.65,40.61,40.61,0,0,1,8.1,13.52,55.65,55.65,0,0,1,0,34.92,40.51,40.51,0,0,1-8.1,13.52,35.13,35.13,0,0,1-12.47,8.65A38.53,38.53,0,0,1,555.94,1120.72Zm0-83.34a37.73,37.73,0,0,0-15.27,3,34.69,34.69,0,0,0-11.84,8.17,35.21,35.21,0,0,0-7.64,12.9,51.78,51.78,0,0,0,0,33.65,39.13,39.13,0,0,0,7.64,12.9,34.69,34.69,0,0,0,11.84,8.17,42.61,42.61,0,0,0,30.54,0,34.82,34.82,0,0,0,11.85-8.17,36,36,0,0,0,7.63-12.74,51.78,51.78,0,0,0,0-33.65,39.17,39.17,0,0,0-7.63-12.9,35,35,0,0,0-11.85-8.18A34.53,34.53,0,0,0,555.94,1037.38Z"/><path class="cls-4" d="M640.72,1114.75a24.73,24.73,0,0,0,10.44-2A23.64,23.64,0,0,0,659,1107a24,24,0,0,0,4.83-8.49,33.41,33.41,0,0,0,1.72-10.69v-50.48h5.76v50.48a36.8,36.8,0,0,1-2.18,12.58,30.6,30.6,0,0,1-6.08,10.22,28.27,28.27,0,0,1-9.66,6.76,33.12,33.12,0,0,1-12.78,2.52,30.05,30.05,0,0,1-22.44-9.28,29.25,29.25,0,0,1-6.08-10.22,37.49,37.49,0,0,1-2.18-12.58v-50.48h5.92v50.32a34.16,34.16,0,0,0,1.72,10.69,24.84,24.84,0,0,0,4.83,8.49,23.8,23.8,0,0,0,7.79,5.67,23.33,23.33,0,0,0,10.6,2.2Z"/><path class="cls-4" d="M640.72,1120.88a31.88,31.88,0,0,1-13.09-2.52,28.8,28.8,0,0,1-10-7.07,32.43,32.43,0,0,1-6.23-10.54,38.61,38.61,0,0,1-2.18-12.89v-51.42H617v51.42a37.88,37.88,0,0,0,1.56,10.38,23.39,23.39,0,0,0,4.67,8.17,22.23,22.23,0,0,0,7.48,5.51,25.1,25.1,0,0,0,10,2,24.49,24.49,0,0,0,10-1.89,20.07,20.07,0,0,0,7.48-5.5,24.72,24.72,0,0,0,4.68-8.18,31.49,31.49,0,0,0,1.56-10.38v-51.57h7.79v51.42a38.61,38.61,0,0,1-2.18,12.89,32.64,32.64,0,0,1-6.24,10.54,28.85,28.85,0,0,1-10,7.07,31.92,31.92,0,0,1-13.09,2.52ZM611,1038.32v49.54a34.94,34.94,0,0,0,2,12.26,28.76,28.76,0,0,0,5.92,9.91,25.26,25.26,0,0,0,9.35,6.6,30.82,30.82,0,0,0,12.47,2.36,31.87,31.87,0,0,0,12.46-2.36,26.68,26.68,0,0,0,9.36-6.6,31.66,31.66,0,0,0,5.92-9.91,34.94,34.94,0,0,0,2-12.26v-49.54h-3.89v49.54a36.1,36.1,0,0,1-1.72,11,23.24,23.24,0,0,1-5.14,9,23.75,23.75,0,0,1-8.1,6,26.25,26.25,0,0,1-10.76,2.21,25.69,25.69,0,0,1-10.75-2.21,23.66,23.66,0,0,1-8.1-6,25.17,25.17,0,0,1-5-9,35.38,35.38,0,0,1-1.71-11v-49.54Z"/><path class="cls-4" d="M704.77,1077.64h4.83a36.25,36.25,0,0,0,9.66-1.26,19.58,19.58,0,0,0,7.17-3.78,16.21,16.21,0,0,0,4.52-5.81,17.51,17.51,0,0,0,1.56-7.71c0-5.82-1.87-10.06-5.61-12.9s-9.2-4.24-16.52-4.24H695.57v77h-5.76v-81.61h20.57c9.35,0,16.36,1.89,20.88,5.5,4.68,3.62,6.86,9,6.86,16a21.11,21.11,0,0,1-1.56,8.49,20.17,20.17,0,0,1-4.68,6.77,22.86,22.86,0,0,1-7.16,4.71,35.12,35.12,0,0,1-9.51,2.52,11.75,11.75,0,0,1,2.49,2.36L745.6,1119h-5.15a4.44,4.44,0,0,1-1.55-.31,2.79,2.79,0,0,1-1.25-1.1l-25.87-33a6.67,6.67,0,0,0-2-1.73,43,43,0,0,0-6.85-.47c0-4.24.62-4.71,1.87-4.71Z"/><path class="cls-4" d="M747.62,1119.94h-7a4.37,4.37,0,0,1-2-.47,4.43,4.43,0,0,1-1.55-1.42l-25.87-33a5.26,5.26,0,0,0-1.72-1.58,52.69,52.69,0,0,0-6.39-.47h-.93v-4.09c0-1.41.31-2.2,2.8-2.2h5a34.32,34.32,0,0,0,9.35-1.26,19,19,0,0,0,6.86-3.61,14.58,14.58,0,0,0,4.21-5.51,18.21,18.21,0,0,0,1.4-7.23c0-5.5-1.72-9.44-5.3-12.11s-8.88-4.09-15.9-4.09H696.66v76.9h-7.79v-83.34h21.66q14.26,0,21.51,5.66c4.83,3.77,7.32,9.43,7.32,16.82a23.62,23.62,0,0,1-1.71,9,22,22,0,0,1-4.83,7.07,28.35,28.35,0,0,1-7.48,5,36.3,36.3,0,0,1-7.64,2.21,6.31,6.31,0,0,1,.94,1.1Zm-43.79-38.84c1.72,0,5.3.15,6.24.62a7.64,7.64,0,0,1,2.49,2l25.87,33a2.72,2.72,0,0,0,.93,1,2.37,2.37,0,0,0,1.09.31h3.12l-26.65-33.65a6.57,6.57,0,0,0-2.18-2.05l-2.34-1.41,2.81-.32a27.62,27.62,0,0,0,16.21-6.92,18.16,18.16,0,0,0,4.36-6.44,23.25,23.25,0,0,0,1.56-8.18c0-6.76-2.18-11.79-6.55-15.25s-11.22-5.19-20.26-5.19H690.9v79.41h3.89v-77h15.74c7.33,0,13.09,1.41,17,4.4q6.08,4.49,6.08,13.68a21.7,21.7,0,0,1-1.56,8,16.52,16.52,0,0,1-4.83,6.13,21.2,21.2,0,0,1-7.48,3.93,32.37,32.37,0,0,1-10,1.42h-4.83c-.46,0-.93,0-1.09,2.52Z"/><path class="cls-4" d="M814,1105.16a1.11,1.11,0,0,1,.94.47l2.33,2.51a40.22,40.22,0,0,1-5.45,4.88,33.32,33.32,0,0,1-6.55,3.61,53.17,53.17,0,0,1-7.63,2.36,42.63,42.63,0,0,1-9.2.79,37.91,37.91,0,0,1-15.42-3,32.76,32.76,0,0,1-12-8.49,39.32,39.32,0,0,1-7.8-13.21,50,50,0,0,1-2.8-17.14,46.2,46.2,0,0,1,3-17,39,39,0,0,1,8.1-13.21,36.61,36.61,0,0,1,12.47-8.49,41,41,0,0,1,16-3,40.09,40.09,0,0,1,15,2.51,26.85,26.85,0,0,1,5.92,3.15,47.35,47.35,0,0,1,5.46,4.24l-1.72,2.52a1.51,1.51,0,0,1-1.4.63,1.84,1.84,0,0,1-1.24-.63c-.47-.47-1.25-.94-2-1.57a24.6,24.6,0,0,0-3-1.89,13.26,13.26,0,0,0-4.21-1.89,36.18,36.18,0,0,0-5.61-1.57,33,33,0,0,0-7.17-.63,34.21,34.21,0,0,0-13.56,2.52,29.44,29.44,0,0,0-10.59,7.39,35.49,35.49,0,0,0-7,11.48,42.36,42.36,0,0,0-2.49,15.25,43.26,43.26,0,0,0,2.49,15.41,33.55,33.55,0,0,0,6.86,11.48,30.77,30.77,0,0,0,23.06,9.75,38.55,38.55,0,0,0,7.8-.63,23.88,23.88,0,0,0,6.23-1.73,31.82,31.82,0,0,0,10.13-6.76c.16-.15.31-.31.47-.31C813.54,1105.31,813.86,1105.16,814,1105.16Z"/><path class="cls-4" d="M788.3,1120.88a41.83,41.83,0,0,1-15.74-3,34.19,34.19,0,0,1-12.31-8.65,38.59,38.59,0,0,1-8-13.52,51.9,51.9,0,0,1-2.81-17.46,47.48,47.48,0,0,1,3-17.29,43.94,43.94,0,0,1,8.26-13.53,38.11,38.11,0,0,1,12.78-8.8,42.88,42.88,0,0,1,16.52-3.15,41.57,41.57,0,0,1,15.27,2.52,27.33,27.33,0,0,1,6.08,3.14,46,46,0,0,1,5.61,4.41l.63.62-2.19,3.31a2.44,2.44,0,0,1-2.18,1.1,3.72,3.72,0,0,1-1.87-.79,11.75,11.75,0,0,0-2-1.41,16.3,16.3,0,0,0-3-1.89,33.37,33.37,0,0,0-4-1.89,24.88,24.88,0,0,0-5.45-1.41,44,44,0,0,0-7-.63,33.15,33.15,0,0,0-13.25,2.51,30,30,0,0,0-17,18.25,44.42,44.42,0,0,0-2.49,14.78,45,45,0,0,0,2.34,15.09,31.54,31.54,0,0,0,6.54,11.17,27.83,27.83,0,0,0,10,6.92,32.05,32.05,0,0,0,12.46,2.36,37.63,37.63,0,0,0,7.64-.63,25.35,25.35,0,0,0,6.08-1.73,39.3,39.3,0,0,0,5.14-2.68,47.06,47.06,0,0,0,4.67-3.77,2.25,2.25,0,0,1,.78-.47,2.14,2.14,0,0,1,2.65.47l3,3.15-.62.62a42.74,42.74,0,0,1-5.61,5,31.13,31.13,0,0,1-6.7,3.77,29.48,29.48,0,0,1-8,2.36A32.62,32.62,0,0,1,788.3,1120.88Zm1.71-83.5a39,39,0,0,0-15.74,3,32.9,32.9,0,0,0-12.15,8.33,37.3,37.3,0,0,0-8,12.9,47.27,47.27,0,0,0-2.81,16.66,48.2,48.2,0,0,0,2.81,16.83,39.28,39.28,0,0,0,7.63,12.9,34.18,34.18,0,0,0,11.69,8.17,38.42,38.42,0,0,0,15,2.83,53.89,53.89,0,0,0,9-.78,27.75,27.75,0,0,0,7.48-2.36,27.12,27.12,0,0,0,6.24-3.62,39.62,39.62,0,0,0,4.67-4.09l-1.71-1.88a.29.29,0,0,0-.47,0c-.16,0-.31.15-.47.31a38.44,38.44,0,0,1-5,3.93,37,37,0,0,1-5.45,3,29.73,29.73,0,0,1-6.39,1.73,59.1,59.1,0,0,1-7.95.63,36.81,36.81,0,0,1-13.24-2.52,31.46,31.46,0,0,1-10.6-7.39,35.14,35.14,0,0,1-7-11.79,50.23,50.23,0,0,1,0-31.3,33,33,0,0,1,7.17-11.79,31,31,0,0,1,10.9-7.55,36.38,36.38,0,0,1,13.87-2.67,35.51,35.51,0,0,1,7.33.63,42.68,42.68,0,0,1,5.76,1.57,23.15,23.15,0,0,1,4.37,2,32.86,32.86,0,0,1,3,2.05c.78.63,1.56,1.1,2,1.57s.78.47.78.47c.47,0,.63-.15.63-.31l1.24-1.89a46.36,46.36,0,0,0-4.67-3.62,28.72,28.72,0,0,0-5.77-3,41,41,0,0,0-6.7-1.89,21.33,21.33,0,0,0-7.48-1.1Z"/><path class="cls-4" d="M880.55,1037.38v4.87H838v33h35.38V1080H838v34h42.54v4.88H832.09v-81.46Z"/><path class="cls-4" d="M881.49,1119.94H831.15v-83.5h50.34v6.76H839v31h35.37v6.76H839V1113h42.54ZM833,1118.05h46.6v-3H837.08v-36h35.37v-2.83H837.08v-34.91h42.54v-3H833Z"/><path class="cls-4" d="M953,1036.44v12.42H916.4v24.69h30.85V1086H916.4v34H901v-83.35h52Zm89.61,41.67a46.4,46.4,0,0,1-3,17,39,39,0,0,1-8.58,13.52,39.45,39.45,0,0,1-13.24,9,43.29,43.29,0,0,1-17.14,3.14,46.37,46.37,0,0,1-17.15-3.14,39.26,39.26,0,0,1-21.81-22.49,50.21,50.21,0,0,1,0-34,39,39,0,0,1,8.57-13.52,41.25,41.25,0,0,1,13.24-9,47.19,47.19,0,0,1,34.29.15,39.32,39.32,0,0,1,21.82,22.49A48.18,48.18,0,0,1,1042.63,1078.11Zm-15.74,0a37.73,37.73,0,0,0-1.87-12.27,27.92,27.92,0,0,0-5.15-9.28,22.51,22.51,0,0,0-8.25-5.81,30.06,30.06,0,0,0-21.82,0,23.87,23.87,0,0,0-8.26,5.81,25.77,25.77,0,0,0-5.3,9.28,41.16,41.16,0,0,0,0,24.53,25.69,25.69,0,0,0,5.3,9.28,23.89,23.89,0,0,0,8.26,5.82,30.19,30.19,0,0,0,21.82,0,23.85,23.85,0,0,0,8.25-5.82,27.82,27.82,0,0,0,5.15-9.28A37.63,37.63,0,0,0,1026.89,1078.11Zm47.84-1.89h3.58a22.28,22.28,0,0,0,7.64-1.1,15.68,15.68,0,0,0,5.3-3,11.46,11.46,0,0,0,3.12-4.56,17.6,17.6,0,0,0,1.09-5.82c0-4.24-1.41-7.55-4.21-9.75s-7-3.46-12.62-3.46h-10v71.4h-15.43v-83.35h25.25a55.53,55.53,0,0,1,14.49,1.73,26.12,26.12,0,0,1,9.82,4.88,18.72,18.72,0,0,1,5.61,7.55,24.59,24.59,0,0,1,1.87,9.74,26.88,26.88,0,0,1-1.25,8,21.47,21.47,0,0,1-3.58,6.77,23.7,23.7,0,0,1-5.77,5.34,25.22,25.22,0,0,1-7.79,3.62,15.69,15.69,0,0,1,5,4.72l20.73,30.82h-13.87a7.24,7.24,0,0,1-3.43-.79,5.54,5.54,0,0,1-2.34-2.36l-17.3-26.73c-.47-.63-1.4-2-2.34-2.2h-5.45v-10.06c.16-1.42,1.87-1.42,1.87-1.42Zm88,32.24a38,38,0,0,0,9-1,41.15,41.15,0,0,0,7.32-2.67V1086h-10.28a3.75,3.75,0,0,1-2.34-.79,2.78,2.78,0,0,1-.94-2v-8.81H1193v37.43a34.6,34.6,0,0,1-6.54,3.93,50.23,50.23,0,0,1-7.33,2.83,44.59,44.59,0,0,1-8.26,1.57,87.94,87.94,0,0,1-9.5.48,44.27,44.27,0,0,1-16.68-3.15,36.37,36.37,0,0,1-13.09-8.81,42,42,0,0,1-8.73-13.52,47.31,47.31,0,0,1-3.11-17.14,47.93,47.93,0,0,1,3-17.3,39,39,0,0,1,8.57-13.52,38.31,38.31,0,0,1,13.56-8.81,49.09,49.09,0,0,1,17.76-3.14c6.7,0,12.63.94,17.46,3a41.34,41.34,0,0,1,12.46,7.87l-4.51,7.07a4.1,4.1,0,0,1-3.43,2.21,5,5,0,0,1-2.81-1c-1.25-.78-2.49-1.41-3.74-2.2a17.39,17.39,0,0,0-4.21-1.89,40.35,40.35,0,0,0-5.14-1.25,41.26,41.26,0,0,0-6.7-.48,27.67,27.67,0,0,0-11.06,2,22.89,22.89,0,0,0-8.42,6,25.77,25.77,0,0,0-5.3,9.28,37.16,37.16,0,0,0-1.87,12.11,36.46,36.46,0,0,0,2,12.89,27.91,27.91,0,0,0,5.61,9.44,25.65,25.65,0,0,0,8.57,6,29.22,29.22,0,0,0,11.22,2.2Zm84.62-22.18h-25.09v21.08h36.47v12.42h-52v-83.34h52v12.42h-36.63v25.63H1251v9.12s-.15,2.67-3.58,2.67Z"/><path class="cls-6" d="M692.21,576.09c0-16.6.08-33.2-.06-49.8,0-3.37.65-5,4.46-5.35,5.47-.53,9.07-4.55,12.4-8.5,7.68-9.13,12.88-19.73,17.54-30.59q4.55-10.62,8.55-21.47c2.48-6.74,6-12.78,11.74-17.12,13-9.88,19.31-23.45,21.75-39.07,1.55-9.89,2.54-19.87,3.53-29.83.31-3.15,1.52-4.25,4.6-4.23,8.65.06,18.33,7.2,21.72,16.85,5.21,14.82,4.41,29.64-1.28,44.15-3.62,9.23-8.13,18.13-11.44,27.46a63.62,63.62,0,0,0-3,16.73c-.38,5.4,1.16,6.49,6.56,6.42,11.45-.15,22.9-.67,34.34-.45,8.73.17,17.44,1.18,26.15,1.94a9,9,0,0,1,4.29,1.37c8.16,5.85,12.93,13.92,14.77,23.71.25,1.34-.49,2.92-1,4.32a6.79,6.79,0,0,1-1.5,2.35c-7.36,7.79-6.69,15.81-.77,24,.27.38.43.84.7,1.22,3.41,4.75,3.41,9.28-.95,13.46-7.72,7.4-8.62,15.83-3.84,25.12,3.51,6.84,1.8,12.6-3.52,17.68-1.18,1.13-2.21,2.42-3.39,3.55a17.61,17.61,0,0,0-5.53,13.8c.28,15.36-11.93,28.36-27.26,30-17.38,1.86-34.7.66-52.08.22-25.21-.65-49.31-7.23-73.81-11.7-2.79-.51-3.78-1.77-3.76-4.7C692.28,610.46,692.21,593.27,692.21,576.09Z"/><path class="cls-6" d="M676.67,577.51q0,26.13,0,52.27c0,6.94-2.84,9.75-9.81,9.77q-13,0-26,0c-6.43,0-9.71-3-9.73-9.32q-.09-52.63,0-105.25c0-5.79,3-9,8.75-9.2q14.21-.39,28.41,0c5.7.16,8.33,3.33,8.34,9.09Q676.71,551.19,676.67,577.51Z"/></svg>
\ No newline at end of file
diff --git a/scst/www/images/sourceforge_badges/oss-users-love-us-white.svg b/scst/www/images/sourceforge_badges/oss-users-love-us-white.svg
new file mode 100644
index 0000000..5b9ba8c
--- /dev/null
+++ b/scst/www/images/sourceforge_badges/oss-users-love-us-white.svg
@@ -0,0 +1 @@
+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1500 1500"><defs><style>.cls-1{fill:#898989;}.cls-2{fill:#eae9ee;stroke:#c7c2bd;}.cls-2,.cls-4{stroke-miterlimit:10;stroke-width:5.1px;}.cls-3{fill:#fff;}.cls-4{stroke:#898989;fill:url(#linear-gradient);}.cls-5{fill:#ff6700;}.cls-6{fill:#3f3f3f;}</style><linearGradient id="linear-gradient" x1="1380.15" y1="1141.02" x2="131.16" y2="1141.02" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fffefe"/><stop offset="1" stop-color="#eaeaec"/></linearGradient></defs><polygon class="cls-1" points="1421.26 990.41 78.74 990.41 200.73 923.33 1299.27 923.33 1421.26 990.41"/><polygon class="cls-2" points="1374.25 832.18 1438.46 750 1374.25 667.82 1415 571.81 1331.71 509.05 1346.22 405.77 1249.53 366.7 1236.82 263.19 1133.3 250.47 1094.23 153.78 990.95 168.29 928.19 85 832.18 125.75 750 61.54 667.82 125.75 571.81 85 509.05 168.29 405.77 153.78 369.02 244.74 263.19 263.19 244.74 369.02 153.78 405.77 167.43 502.92 85 571.81 119.87 667.04 61.54 750 119.87 832.96 85 928.19 162.81 993.22 153.78 1094.23 245.77 1136.91 263.19 1236.82 363.09 1254.23 405.77 1346.22 506.78 1337.19 571.81 1415 667.04 1380.13 750 1438.46 832.96 1380.13 928.19 1415 993.22 1337.19 1094.23 1346.22 1139.44 1248.77 1236.82 1236.82 1248.77 1139.44 1346.22 1094.23 1336.65 987.23 1415 928.19 1374.25 832.18"/><circle class="cls-1" cx="750" cy="750" r="511.86" transform="translate(-310.66 750) rotate(-45)"/><circle class="cls-3" cx="750" cy="750" r="479.87" transform="translate(-310.66 750) rotate(-45)"/><path class="cls-4" d="M1421.26,990.41H78.74L112.46,1157a168,168,0,0,0,164.67,134.67h945.74A168,168,0,0,0,1387.54,1157Z"/><path class="cls-5" d="M345.4,1144.27c0-30-10.59-43.72-16.2-48.91a1.6,1.6,0,0,0-2.65,1.42c1.09,17-20.11,21.23-20.11,47.8v.16c0,16.2,12.16,29.41,27.12,29.41s27.11-13.21,27.11-29.41v-.16c0-7.54-2.8-14.78-5.61-20.12-.62-1.1-2.18-.63-2,.31C358.18,1147.73,345.4,1161.88,345.4,1144.27Z"/><path class="cls-5" d="M325.3,1192.55l-54.39-55a3.54,3.54,0,0,1,0-5L339.64,1063a2.57,2.57,0,0,0,.62-2.83,2.45,2.45,0,0,0-2.34-1.57h-21a3.21,3.21,0,0,0-1.71.63l-73.25,73.91a2.89,2.89,0,0,0,0,3.77l69.35,70a2.88,2.88,0,0,0,1.87.78,3.21,3.21,0,0,0,1.72-.63l10.44-10.69A2.89,2.89,0,0,0,325.3,1192.55Z"/><path class="cls-5" d="M353.82,1071.62a2.4,2.4,0,0,0-1.72-.95,2.58,2.58,0,0,0-1.87.79l-10.59,10.69a2.88,2.88,0,0,0,0,3.78l54.54,55a3.13,3.13,0,0,1,1.09,2.51,3.74,3.74,0,0,1-1.09,2.52l-68.88,69.51a2.54,2.54,0,0,0-.62,2.83,2.46,2.46,0,0,0,2.33,1.57v.16H347.9a2.58,2.58,0,0,0,1.87-.79L423,1145.37a2.67,2.67,0,0,0,0-3.77Z"/><path class="cls-6" d="M499.22,1142.07a28,28,0,0,0-6.71-3.93,59.38,59.38,0,0,0-7.48-2.83c-2.65-.79-5.14-1.73-7.48-2.52a33.59,33.59,0,0,1-6.23-3.14,14.64,14.64,0,0,1-4.36-4.41,11.06,11.06,0,0,1-1.56-6.29,14.09,14.09,0,0,1,1.09-5.66,16.14,16.14,0,0,1,3.27-4.72,15.27,15.27,0,0,1,5.3-3.3,21.86,21.86,0,0,1,7.48-1.26,22.18,22.18,0,0,1,7.17,1.1,36.42,36.42,0,0,1,4.83,2.36,22,22,0,0,1,3.12,2.2c2.18,2,3.89,1.58,4.83-.15l1.71-3.31-.47-.47a30.76,30.76,0,0,0-9.5-6.44,30.35,30.35,0,0,0-11.69-2,26.6,26.6,0,0,0-10.28,1.89,23.72,23.72,0,0,0-7.64,4.87,21.53,21.53,0,0,0-4.68,7.08,21.94,21.94,0,0,0-1.55,8.33,20.14,20.14,0,0,0,1.87,9.12,18.54,18.54,0,0,0,4.83,6.14,27.88,27.88,0,0,0,6.7,3.93c2.34.94,4.83,1.89,7.48,2.67s5.14,1.73,7.48,2.52a25.13,25.13,0,0,1,6.23,3,14.65,14.65,0,0,1,4.37,4.4,12.18,12.18,0,0,1,1.55,6.6,17.91,17.91,0,0,1-1.24,7.08,15.56,15.56,0,0,1-3.74,5.66,20.16,20.16,0,0,1-6.08,3.78,22.26,22.26,0,0,1-8.26,1.41,23.42,23.42,0,0,1-6.08-.63,20.18,20.18,0,0,1-4.67-1.57,19.73,19.73,0,0,1-3.59-2c-.93-.79-1.87-1.58-2.65-2.21a24.81,24.81,0,0,1-2-1.73,2.69,2.69,0,0,0-1.87-.94,2.88,2.88,0,0,0-2.34,1.26l-2,3.14.47.48a34.41,34.41,0,0,0,10.44,8c3.9,2,8.73,3,14.18,3a30.92,30.92,0,0,0,10.91-1.88,26.17,26.17,0,0,0,8.42-5.51,23.81,23.81,0,0,0,5.3-8.18,27.56,27.56,0,0,0,1.87-10.06,18.94,18.94,0,0,0-1.87-8.81A18.25,18.25,0,0,0,499.22,1142.07Z"/><path class="cls-6" d="M584.46,1108.57a35.13,35.13,0,0,0-12.47-8.65,43.72,43.72,0,0,0-16-3,40.47,40.47,0,0,0-15.89,3.14,35,35,0,0,0-12.47,8.65,40.7,40.7,0,0,0-8.11,13.52,48.41,48.41,0,0,0-3,17.46,51.78,51.78,0,0,0,3,17.45,39.19,39.19,0,0,0,8.11,13.53,35,35,0,0,0,12.47,8.65,42.86,42.86,0,0,0,15.89,3,38.53,38.53,0,0,0,16-3.15,34.88,34.88,0,0,0,12.47-8.65,40.41,40.41,0,0,0,8.1-13.52,55.62,55.62,0,0,0,0-34.91A40.56,40.56,0,0,0,584.46,1108.57Zm-28.52,67.78a33.22,33.22,0,0,1-13.25-2.52,28.35,28.35,0,0,1-10.28-7.23,30.93,30.93,0,0,1-6.7-11.48,47.67,47.67,0,0,1-2.34-15.41,47,47,0,0,1,2.34-15.41,33.1,33.1,0,0,1,6.7-11.48,28.35,28.35,0,0,1,10.28-7.23,36.09,36.09,0,0,1,26.5,0,28.35,28.35,0,0,1,10.28,7.23A32.84,32.84,0,0,1,586,1124.3a47.67,47.67,0,0,1,2.34,15.41,47,47,0,0,1-2.34,15.41,32.84,32.84,0,0,1-6.55,11.48,26.55,26.55,0,0,1-10.28,7.23A32.69,32.69,0,0,1,555.94,1176.35Z"/><path class="cls-6" d="M664.41,1149.62a31.58,31.58,0,0,1-1.56,10.38,24.78,24.78,0,0,1-4.68,8.17,20.2,20.2,0,0,1-7.48,5.51,24.66,24.66,0,0,1-10,1.88,25.1,25.1,0,0,1-10-2,22.34,22.34,0,0,1-7.48-5.5,23.53,23.53,0,0,1-4.67-8.18,37.76,37.76,0,0,1-1.56-10.38V1098h-7.79v51.42a38.61,38.61,0,0,0,2.18,12.89,32.33,32.33,0,0,0,6.23,10.54,28.82,28.82,0,0,0,10,7.08,35.39,35.39,0,0,0,26.18,0,28.88,28.88,0,0,0,10-7.08,32.53,32.53,0,0,0,6.24-10.54,38.61,38.61,0,0,0,2.18-12.89V1098h-7.79Z"/><path class="cls-6" d="M717.7,1143.8a36.3,36.3,0,0,0,7.64-2.2,28.63,28.63,0,0,0,7.48-5,22,22,0,0,0,4.83-7.07,23.58,23.58,0,0,0,1.71-9c0-7.39-2.49-13-7.32-16.82s-12-5.66-21.51-5.66H688.87v83.34h7.79v-76.9h13.87c7,0,12.31,1.42,15.9,4.09s5.3,6.61,5.3,12.11a18.26,18.26,0,0,1-1.4,7.24,14.47,14.47,0,0,1-4.21,5.5,19.21,19.21,0,0,1-6.86,3.62,34.73,34.73,0,0,1-9.35,1.25h-5c-2.49,0-2.8.79-2.8,2.21v4.08h.93a50.3,50.3,0,0,1,6.39.48,5.11,5.11,0,0,1,1.72,1.57l25.87,33a4.43,4.43,0,0,0,1.55,1.42,4.37,4.37,0,0,0,2,.47h7l-29-36.64A6.71,6.71,0,0,0,717.7,1143.8Z"/><path class="cls-6" d="M812.76,1166a2.25,2.25,0,0,0-.78.47,47.2,47.2,0,0,1-4.67,3.78,40.54,40.54,0,0,1-5.14,2.67,24.47,24.47,0,0,1-6.08,1.73,37.63,37.63,0,0,1-7.64.63,32.05,32.05,0,0,1-12.46-2.36,27.83,27.83,0,0,1-10-6.92,31.58,31.58,0,0,1-6.54-11.16,45.13,45.13,0,0,1-2.34-15.1,44.42,44.42,0,0,1,2.49-14.78,30,30,0,0,1,17-18.24,33.15,33.15,0,0,1,13.25-2.52,44.13,44.13,0,0,1,7,.63,25.45,25.45,0,0,1,5.45,1.41,33.37,33.37,0,0,1,4,1.89,16.3,16.3,0,0,1,3,1.89,11.37,11.37,0,0,1,2,1.41,3.63,3.63,0,0,0,1.87.79,2.44,2.44,0,0,0,2.18-1.1l2.19-3.3-.63-.63a46,46,0,0,0-5.61-4.41,28.2,28.2,0,0,0-6.08-3.14,41.57,41.57,0,0,0-15.27-2.52,42.88,42.88,0,0,0-16.52,3.15,38,38,0,0,0-12.78,8.81,43.77,43.77,0,0,0-8.26,13.52,47.54,47.54,0,0,0-3,17.3,51.85,51.85,0,0,0,2.81,17.45,38.73,38.73,0,0,0,8,13.53,34.28,34.28,0,0,0,12.31,8.64,41.83,41.83,0,0,0,15.74,3,32.62,32.62,0,0,0,9.19-1.1,29.48,29.48,0,0,0,8-2.36,30.89,30.89,0,0,0,6.7-3.77,42.66,42.66,0,0,0,5.61-5l.62-.63-3-3.15A2.15,2.15,0,0,0,812.76,1166Zm-1.18-56,.41.35a2.07,2.07,0,0,0,.32.28,2.47,2.47,0,0,1-.33-.28A5,5,0,0,0,811.58,1110Z"/><polygon class="cls-6" points="831.15 1181.54 881.49 1181.54 881.49 1174.62 838.95 1174.62 838.95 1142.54 874.32 1142.54 874.32 1135.78 838.95 1135.78 838.95 1104.8 881.49 1104.8 881.49 1098.04 831.15 1098.04 831.15 1181.54"/><path class="cls-6" d="M1165.58,1144.74a2.8,2.8,0,0,0,.94,2,3.8,3.8,0,0,0,2.34.78h10.28v18.87a41.18,41.18,0,0,1-7.32,2.68,38,38,0,0,1-9,.94,29.22,29.22,0,0,1-11.22-2.2,25.65,25.65,0,0,1-8.57-6,27.86,27.86,0,0,1-5.61-9.43,36.55,36.55,0,0,1-2-12.9,37.16,37.16,0,0,1,1.87-12.11,25.73,25.73,0,0,1,5.3-9.27,22.92,22.92,0,0,1,8.42-6,27.85,27.85,0,0,1,11.06-2,41.35,41.35,0,0,1,6.7.47,38.68,38.68,0,0,1,5.14,1.26,17.37,17.37,0,0,1,4.21,1.88c1.25.79,2.49,1.42,3.74,2.2a5,5,0,0,0,2.81,1,4.11,4.11,0,0,0,3.43-2.2l4.51-7.08a41.12,41.12,0,0,0-12.46-7.86c-4.83-2-10.76-3-17.46-3a49.09,49.09,0,0,0-17.76,3.14,38.44,38.44,0,0,0-13.56,8.81,39.1,39.1,0,0,0-8.57,13.52,47.93,47.93,0,0,0-3,17.3,47.27,47.27,0,0,0,3.11,17.14,42.19,42.19,0,0,0,8.73,13.53,36.47,36.47,0,0,0,13.09,8.8,44.27,44.27,0,0,0,16.68,3.15,90.69,90.69,0,0,0,9.5-.47,45.43,45.43,0,0,0,8.26-1.58,50.23,50.23,0,0,0,7.33-2.83,34.6,34.6,0,0,0,6.54-3.93v-37.42h-27.43Z"/><path class="cls-6" d="M1258.78,1110.46V1098h-52v83.34h52V1169h-36.47v-21.07h25.09c3.43,0,3.58-2.68,3.58-2.68v-9.12h-28.83v-25.63Z"/><path class="cls-6" d="M1091.87,1145.84a25.2,25.2,0,0,0,7.79-3.61,23.94,23.94,0,0,0,5.77-5.35,21.42,21.42,0,0,0,3.58-6.76,27,27,0,0,0,1.25-8,24.64,24.64,0,0,0-1.87-9.75,18.79,18.79,0,0,0-5.61-7.55,26.27,26.27,0,0,0-9.82-4.88,55.53,55.53,0,0,0-14.49-1.73h-25.25v83.35h15.43v-71.39h10c5.61,0,9.81,1.25,12.62,3.46s4.21,5.5,4.21,9.75a17.59,17.59,0,0,1-1.09,5.81,11.39,11.39,0,0,1-3.12,4.56,15.51,15.51,0,0,1-5.3,3,22.28,22.28,0,0,1-7.64,1.1h-3.58s-1.71,0-1.87,1.42v10.06h5.45c.94.16,1.87,1.57,2.34,2.2l17.3,26.74a5.66,5.66,0,0,0,2.34,2.36,7.37,7.37,0,0,0,3.43.78h13.87l-20.73-30.82A15.81,15.81,0,0,0,1091.87,1145.84Z"/><path class="cls-6" d="M1031.09,1109.36a41.25,41.25,0,0,0-13.24-9,47.12,47.12,0,0,0-34.29-.16,41.25,41.25,0,0,0-13.24,9,39.14,39.14,0,0,0-8.57,13.53,50.18,50.18,0,0,0,0,34,39.19,39.19,0,0,0,21.81,22.49,46.37,46.37,0,0,0,17.15,3.15,43.29,43.29,0,0,0,17.14-3.15,39.57,39.57,0,0,0,13.24-9,39.16,39.16,0,0,0,8.58-13.53,46.4,46.4,0,0,0,3-17,48.27,48.27,0,0,0-3-16.83A41.51,41.51,0,0,0,1031.09,1109.36ZM1025,1152a27.87,27.87,0,0,1-5.15,9.27,23.67,23.67,0,0,1-8.26,5.82,30,30,0,0,1-21.81,0,23.76,23.76,0,0,1-8.26-5.82,25.73,25.73,0,0,1-5.3-9.27,41.19,41.19,0,0,1,0-24.54,25.73,25.73,0,0,1,5.3-9.27,23.76,23.76,0,0,1,8.26-5.82,30,30,0,0,1,21.81,0,22.37,22.37,0,0,1,8.26,5.82,27.87,27.87,0,0,1,5.15,9.27,41.19,41.19,0,0,1,0,24.54Z"/><polygon class="cls-6" points="900.97 1181.54 916.4 1181.54 916.4 1147.57 947.25 1147.57 947.25 1135.15 916.4 1135.15 916.4 1110.46 953.02 1110.46 953.02 1098.19 900.97 1098.19 900.97 1181.54"/><path class="cls-6" d="M548.17,684.53v63.55q0,9.52,4.69,14.67t13.77,5.14q9.07,0,13.92-5.14t4.84-14.67V684.53h25.87v63.39q0,14.24-6,24.06a38.72,38.72,0,0,1-16.27,14.83,51.08,51.08,0,0,1-22.77,5,49.8,49.8,0,0,1-22.46-4.92,36.6,36.6,0,0,1-15.66-14.82q-5.76-9.91-5.75-24.14V684.53Z"/><path class="cls-6" d="M647,788a33.92,33.92,0,0,1-14.75-11.2A30.5,30.5,0,0,1,626.39,759h27.54q.6,5.89,4.08,9a13.13,13.13,0,0,0,9.08,3.1q5.74,0,9.08-2.65a8.84,8.84,0,0,0,3.33-7.33,8.73,8.73,0,0,0-2.65-6.51,21.43,21.43,0,0,0-6.51-4.23,106,106,0,0,0-11-3.79,119.49,119.49,0,0,1-16.79-6.35,31.12,31.12,0,0,1-11.2-9.38q-4.68-6.21-4.69-16.19,0-14.83,10.75-23.23t28-8.39q17.55,0,28.29,8.39t11.5,23.38h-28a11,11,0,0,0-3.78-8.1,13.39,13.39,0,0,0-8.93-3,11.16,11.16,0,0,0-7.57,2.5,9,9,0,0,0-2.87,7.19q0,5.15,4.84,8t15.13,6.2a139.76,139.76,0,0,1,16.72,6.66,31.68,31.68,0,0,1,11.12,9.23q4.68,6,4.69,15.58A30.63,30.63,0,0,1,702,775.61a32.31,32.31,0,0,1-13.39,11.8q-8.79,4.4-20.73,4.39A54.65,54.65,0,0,1,647,788Z"/><path class="cls-6" d="M748.34,705.26v21.48H783v20H748.34V770h39.18v20.73H722.46V684.53h65.06v20.73Z"/><path class="cls-6" d="M858.48,790.74l-22.09-40.09h-6.2v40.09H804.32V684.53h43.42q12.56,0,21.41,4.39a30.19,30.19,0,0,1,13.24,12,33.59,33.59,0,0,1,4.39,17,31.64,31.64,0,0,1-6,18.91q-6,8.32-17.63,11.8l24.51,42.06Zm-28.29-58.4h16q7.11,0,10.66-3.48t3.56-9.83q0-6.06-3.56-9.54T846.23,706h-16Z"/><path class="cls-6" d="M920.51,788a34,34,0,0,1-14.75-11.2A30.57,30.57,0,0,1,899.94,759h27.53q.62,5.89,4.09,9a13.11,13.11,0,0,0,9.08,3.1q5.75,0,9.07-2.65a8.84,8.84,0,0,0,3.33-7.33,8.72,8.72,0,0,0-2.64-6.51,21.55,21.55,0,0,0-6.51-4.23,106,106,0,0,0-11-3.79,119,119,0,0,1-16.79-6.35,31.12,31.12,0,0,1-11.2-9.38q-4.7-6.21-4.69-16.19,0-14.83,10.74-23.23T939,683q17.55,0,28.29,8.39t11.5,23.38h-28a11,11,0,0,0-3.78-8.1,13.37,13.37,0,0,0-8.93-3,11.14,11.14,0,0,0-7.56,2.5c-1.92,1.67-2.87,4.06-2.87,7.19q0,5.15,4.84,8t15.13,6.2a139.52,139.52,0,0,1,16.71,6.66,31.71,31.71,0,0,1,11.13,9.23q4.68,6,4.69,15.58a30.55,30.55,0,0,1-4.62,16.49,32.25,32.25,0,0,1-13.39,11.8q-8.77,4.4-20.73,4.39A54.65,54.65,0,0,1,920.51,788Z"/><path class="cls-6" d="M477.67,915.27h33.89v20H451.79V829h25.88Z"/><path class="cls-6" d="M546.73,929.34a52,52,0,0,1-19.82-19.44,57.19,57.19,0,0,1,0-56.06,52.18,52.18,0,0,1,19.82-19.36,57.67,57.67,0,0,1,54.93,0,51.07,51.07,0,0,1,19.66,19.36,58,58,0,0,1-.07,56.06,51.64,51.64,0,0,1-19.67,19.44,57.51,57.51,0,0,1-54.85,0Zm47.81-25.11q7.65-8.47,7.64-22.4t-7.64-22.47Q586.91,851,574.19,851t-20.5,8.32q-7.63,8.33-7.64,22.54t7.64,22.47q7.63,8.4,20.5,8.4T594.54,904.23Z"/><path class="cls-6" d="M742.59,829,704.92,935.24H672.54L634.86,829H662.4l26.33,80.19L715.2,829Z"/><path class="cls-6" d="M779.2,849.76v21.48h34.65v20H779.2v23.3h39.19v20.73H753.33V829h65.06v20.73Z"/><path class="cls-6" d="M892.67,829v63.55q0,9.52,4.69,14.67t13.77,5.14q9.07,0,13.92-5.14t4.84-14.67V829h25.88v63.39q0,14.24-6,24.06a38.78,38.78,0,0,1-16.27,14.83,51.14,51.14,0,0,1-22.77,5,49.88,49.88,0,0,1-22.47-4.92,36.6,36.6,0,0,1-15.66-14.82q-5.74-9.91-5.75-24.14V829Z"/><path class="cls-6" d="M991.47,932.52a34,34,0,0,1-14.75-11.2,30.57,30.57,0,0,1-5.82-17.85h27.53q.6,5.89,4.09,9a13.1,13.1,0,0,0,9.07,3.1c3.84,0,6.86-.88,9.08-2.65a8.84,8.84,0,0,0,3.33-7.33,8.69,8.69,0,0,0-2.65-6.51,21.37,21.37,0,0,0-6.5-4.23,106.89,106.89,0,0,0-11-3.79,119.68,119.68,0,0,1-16.8-6.35,31.09,31.09,0,0,1-11.19-9.38q-4.7-6.21-4.69-16.19,0-14.83,10.74-23.23t28-8.39q17.55,0,28.29,8.39t11.5,23.38h-28a11,11,0,0,0-3.78-8.1,13.37,13.37,0,0,0-8.93-3,11.12,11.12,0,0,0-7.56,2.5c-1.92,1.67-2.88,4.06-2.88,7.19q0,5.15,4.84,8t15.14,6.2a140.21,140.21,0,0,1,16.71,6.66,31.59,31.59,0,0,1,11.12,9.23q4.69,6,4.69,15.58a30.54,30.54,0,0,1-4.61,16.49,32.25,32.25,0,0,1-13.39,11.8q-8.77,4.4-20.73,4.39A54.65,54.65,0,0,1,991.47,932.52Z"/><path class="cls-5" d="M712.11,453.34c4.47,9.38,8.14,18.05,12.62,26.3,13.53,24.89,27.37,49.62,41.1,74.42.95,1.72,1.93,3.44,3.15,5.62,5.25-2.89,10.28-5.65,15.31-8.43,24-13.24,47.74-26.89,72-39.63,35.19-18.5,33.19-58.48,12.62-78.29-13.41-12.91-29.37-15.22-46.7-10.13-5.21,1.53-10.17,3.92-15.65,6.07-1.84-2.81-3.77-5.85-5.79-8.84-10.59-15.72-25.46-23.24-44.24-21.35C738,400.93,724.89,411,717,427.89a7.28,7.28,0,0,1-4.52,3.34c-4.19.93-8.52,1.22-13.24,1.82a25.61,25.61,0,0,1,.55-3.45c7-23.92,22.67-39.19,46.8-44.83,24.32-5.69,45.39,1,62.19,19.68,2.84,3.16,4.9,3.84,9.13,2.69,24.25-6.58,46.09-1.47,63.6,16.64s21.88,39.84,14.89,64.09c-4.77,16.55-15.53,28-30,36.15-30.1,16.91-60.38,33.51-90.62,50.16-8.39,4.63-15.79,2.34-20.49-6.23q-21.3-38.87-42.48-77.78c-4.11-7.56-8.47-15.07-11.73-23-4.41-10.75-4.18-10.25,7.11-13.39A18.87,18.87,0,0,1,712.11,453.34Z"/><path class="cls-5" d="M758.12,534c-3.79-6.85-7-12.22-9.69-17.8-.58-1.18.23-3.13.62-4.67,1.63-6.4,3.72-12.71,5-19.18,3.13-16.47-9.44-34.78-26.49-40.3a38.62,38.62,0,0,0-45.38,16.79c-2.43,4.11-4.2,8.62-6.51,13.46a38.09,38.09,0,0,1-4.79-1.27c-22.89-9.18-46.92,2.85-53.13,26.66-4.79,18.35,3.78,37.25,21.72,44.69,19.75,8.2,40,15.12,60.1,22.56,7.6,2.81,15.24,5.49,23.24,8.37,4.94-13.45,9.77-26.63,15-41,3.58,6.52,6.69,12.08,9.65,17.72a4.09,4.09,0,0,1-.22,2.81C744,572,741,581.1,737.3,590c-4.12,10-8.62,12.06-18.71,8.39-29.48-10.72-59.24-20.78-88.16-32.85-18.76-7.83-28.39-24.33-29.86-44.46a54.8,54.8,0,0,1,61.78-58.59c3.64.46,5.21-.88,7.11-3.62,13-18.82,31.3-27,53.8-24,21.57,2.94,36.25,15.62,44.14,35.78,4.47,11.41,3.61,23.25.33,34.79C765.12,514.65,761.67,523.58,758.12,534Z"/></svg>
\ No newline at end of file
diff --git a/scst/www/index.html b/scst/www/index.html
index dd1f9ce..eeeefea 100644
--- a/scst/www/index.html
+++ b/scst/www/index.html
@@ -13,14 +13,15 @@
 <body>
 <div id="wrap">
 	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
+		<div class="logoimg"></div><h1 id="logo"><span class="orange">&nbsp;</span></h1>
 		<h2 id="slogan">Generic SCSI Target Subsystem for Linux</h2>
 	</div>
+
 	<div id="menu">
 		<ul>
 			<li id="current"><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
+			<li><a href="https://github.com/SCST-project/scst">Main</a></li>
+			<li><a href="https://github.com/SCST-project/scst/releases">News</a></li>
 			<li><a href="targets.html">Drivers</a></li>
 			<li><a href="downloads.html">Downloads</a></li>
 			<li><a href="contributing.html">Contributing</a></li>
@@ -28,6 +29,7 @@
 			<li><a href="users.html">Users</a></li>
 		</ul>
 	</div>
+
 	<div id="content-wrap">
 	  		<div id="main">
 				<h1>Generic SCSI Target Subsystem for Linux</h1>
@@ -149,7 +151,7 @@
 				<h1>Documentation</h1>
 				<p><a href="scst_pg.html">HTML</a></p>
 				<p><a href="scst_pg.pdf">PDF</a></p>
-				<p><a href="http://events.linuxfoundation.org/sites/events/files/slides/lcna15_bvanassche.pdf">SCST overview slides</a></p>
+				<p><a href="https://events.static.linuxfound.org/sites/events/files/slides/lcna15_bvanassche.pdf">SCST overview slides</a></p>
 				<p><a href="http://monklinux.blogspot.com/2012/02/scst-configuration-how-to-using-gentoo.html">Gentoo HOWTO</a></p>
 				<p><a href="iscsi-scst-howto.txt">HOWTO For iSCSI-SCST</a></p>
 				<p><a href="SCST_Gentoo_HOWTO.txt">Gentoo HOWTO For iSCSI-SCST</a></p>
@@ -159,7 +161,7 @@
 				<h1>Articles</h1>
 				<p>By Marc Smith:</p>
 				<p><a href="http://marcitland.blogspot.com/2011/03/accelerating-vdi-using-scst-and-ssds.html">Accelerating VDI Using SCST and SSDs</a></p>
-				<p><a href="http://marcitland.blogspot.com/2013/04/building-using-highly-available-esos.html">Building &amp Using a Highly Available ESOS Disk Array</a></p>
+				<p><a href="http://marcitland.blogspot.com/2013/04/building-using-highly-available-esos.html">Building &amp; Using a Highly Available ESOS Disk Array</a></p>
 				<p><a href="http://marcitland.blogspot.com/2014/07/open-storage-dual-controller-oss-disk.html">Open Storage: Dual-Controller OSS Disk Array</a></p>
 				<h1>SCST 0.9.6 graphs</h1>
 				<p><a href="images/init_scst.png">init_scst</a></p>
@@ -172,10 +174,10 @@
 					scst-devel mailing list</a><br><br>
 					See <a href="http://sourceforge.net/mail/?group_id=110471">mailing lists page</a> for more info about SCST mailing
 					lists.</p>
-				<h1></h1>
+				<h1>&nbsp;</h1>
 				<p><a href="http://sourceforge.net/donate/index.php?group_id=110471">
 				<img src="http://images.sourceforge.net/images/project-support.jpg" width="88" height="32" border="0" alt="Support This Project"> </a></p>
-				<h1></h1>
+				<h1>&nbsp;</h1>
 				<p><a href="http://validator.w3.org/check?uri=referer">
 					<img src="http://www.w3.org/Icons/valid-html401" alt="Valid HTML 4.01 Transitional" height="31" width="88"></a>
   				</p>
@@ -187,7 +189,7 @@
 </div>
 <!-- footer starts here -->
 <div id="footer">
-	<p>&copy; Copyright 2004 - 2020<b><font class="names"> Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
+	<p>&copy; Copyright 2004 - 2021<b><font class="names"> Vladislav Bolkhovitin, Bart Van Assche &amp; others</font></b>&nbsp;&nbsp;
 	Design by: <b><font class="names">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 </div>
 <!-- footer ends here -->
diff --git a/scst/www/mc_s.html b/scst/www/mc_s.html
index 19e0d75..89b8fc5 100644
--- a/scst/www/mc_s.html
+++ b/scst/www/mc_s.html
@@ -13,15 +13,15 @@
 <!-- wrap starts here -->
 <div id="wrap">
 	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
+		<div class="logoimg"></div><h1 id="logo"><span class="orange">&nbsp;</span></h1>
 		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
 	</div>
 
 	<div id="menu">
 		<ul>
 			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
+			<li><a href="https://github.com/SCST-project/scst">Main</a></li>
+			<li><a href="https://github.com/SCST-project/scst/releases">News</a></li>
 			<li><a href="targets.html">Drivers</a></li>
 			<li><a href="downloads.html">Downloads</a></li>
 			<li><a href="contributing.html">Contributing</a></li>
@@ -246,7 +246,7 @@
 <!-- wrap ends here -->
 <!-- footer starts here -->
 <div id="footer">
-	<p>&copy; Copyright 2004 - 2020 <b><font color="#EC981F">Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
+	<p>&copy; Copyright 2004 - 2021 <b><font color="#EC981F">Vladislav Bolkhovitin, Bart Van Assche &amp; others</font></b>&nbsp;&nbsp;
 	   Design by: <b><font color="#EC981F">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 </div>
 <!-- footer ends here -->
diff --git a/scst/www/scst_admin.html b/scst/www/scst_admin.html
index 516a620..4ca49fc 100644
--- a/scst/www/scst_admin.html
+++ b/scst/www/scst_admin.html
@@ -13,15 +13,15 @@
 <!-- wrap starts here -->
 <div id="wrap">
 	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
+		<div class="logoimg"></div><h1 id="logo"><span class="orange">&nbsp;</span></h1>
 		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
 	</div>
 
 	<div id="menu">
 		<ul>
 			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
+			<li><a href="https://github.com/SCST-project/scst">Main</a></li>
+			<li><a href="https://github.com/SCST-project/scst/releases">News</a></li>
 			<li id="current"><a href="targets.html">Drivers</a></li>
 			<li><a href="downloads.html">Downloads</a></li>
 			<li><a href="contributing.html">Contributing</a></li>
@@ -38,13 +38,9 @@
 					<li><a href="target_iscsi.html">ISCSI-SCST with iSER</a></li>
 					<li><a href="target_qla2x00t.html">QLogic FC qla2x00t</a></li>
 					<li><a href="target_srp.html">SCSI RDMA Protocol (SRP)</a></li>
-					<li><a href="target_mvsas.html">Marvell SAS adapters</a></li>
 					<li><a href="target_emulex.html">Emulex FC/FCoE</a></li>
-					<li><a href="target_lsi.html">LSI/MPT adapters</a></li>
 					<li><a href="target_fcoe.html">FCoE Target</a></li>
 					<li><a href="target_local.html">Local Target Driver</a></li>
-					<li><a href="target_ibmvscsi.html">IBM pSeries Virtual SCSI</a></li>
-					<li><a href="target_old.html">Old Unsupported</a></li>
 				</ul>
 				<h1>User utilities</h1>
 				<ul class="sidemenu">
@@ -67,15 +63,15 @@
 
 				<p class="post-footer align-right">
 					<a href="downloads.html" class="readmore">Download</a>
-					<a href="http://sourceforge.net/p/scst/svn/HEAD/tree/" class="readmore">SCST SVN Repository</a>
+					<a href="https://github.com/SCST-project/scst" class="readmore">SCST Git Repository</a>
 				</p>
-				<table border=0><tr><td height="300px">&nbsp;</td></tr></table>
+				<table border=0 summary=""><tr><td style="height: 300px">&nbsp;</td></tr></table>
 	  		</div>
 	</div>
 </div>
 <!-- footer starts here -->
 <div id="footer">
-	<p>&copy; Copyright 2004 - 2020<b><font class="names"> Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
+	<p>&copy; Copyright 2004 - 2021<b><font class="names"> Vladislav Bolkhovitin, Bart Van Assche &amp; others</font></b>&nbsp;&nbsp;
 	Design by: <b><font class="names">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 </div>
 <!-- footer ends here -->
diff --git a/scst/www/scstvslio.html b/scst/www/scstvslio.html
index 1cf828b..e8d6e41 100644
--- a/scst/www/scstvslio.html
+++ b/scst/www/scstvslio.html
@@ -12,15 +12,15 @@
 <!-- wrap starts here -->
 <div id="wrap">
 	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
+		<div class="logoimg"></div><h1 id="logo"><span class="orange">&nbsp;</span></h1>
 		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
 	</div>
 
 	<div id="menu">
 		<ul>
 			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
+			<li><a href="https://github.com/SCST-project/scst">Main</a></li>
+			<li><a href="https://github.com/SCST-project/scst/releases">News</a></li>
 			<li><a href="targets.html">Drivers</a></li>
 			<li><a href="downloads.html">Downloads</a></li>
 			<li><a href="contributing.html">Contributing</a></li>
@@ -99,7 +99,7 @@
 <!-- wrap ends here -->
 <!-- footer starts here -->
 <div id="footer">
-	<p>&copy; Copyright 2004 - 2020 <b><font color="#EC981F">Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
+	<p>&copy; Copyright 2004 - 2021 <b><font color="#EC981F">Vladislav Bolkhovitin, Bart Van Assche &amp; others</font></b>&nbsp;&nbsp;
 	   Design by: <b><font color="#EC981F">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 </div>
 <!-- footer ends here -->
diff --git a/scst/www/scstvsstgt.html b/scst/www/scstvsstgt.html
index 8c30562..4fd682a 100644
--- a/scst/www/scstvsstgt.html
+++ b/scst/www/scstvsstgt.html
@@ -13,15 +13,15 @@
 <!-- wrap starts here -->
 <div id="wrap">
 	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
+		<div class="logoimg"></div><h1 id="logo"><span class="orange">&nbsp;</span></h1>
 		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
 	</div>
 
 	<div id="menu">
 		<ul>
 			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
+			<li><a href="https://github.com/SCST-project/scst">Main</a></li>
+			<li><a href="https://github.com/SCST-project/scst/releases">News</a></li>
 			<li><a href="targets.html">Drivers</a></li>
 			<li><a href="downloads.html">Downloads</a></li>
 			<li><a href="contributing.html">Contributing</a></li>
@@ -79,7 +79,7 @@
 <!-- wrap ends here -->
 <!-- footer starts here -->
 <div id="footer">
-	<p>&copy; Copyright 2004 - 2020 <b><font color="#EC981F">Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
+	<p>&copy; Copyright 2004 - 2021 <b><font color="#EC981F">Vladislav Bolkhovitin, Bart Van Assche &amp; others</font></b>&nbsp;&nbsp;
 	   Design by: <b><font color="#EC981F">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 </div>
 <!-- footer ends here -->
diff --git a/scst/www/target_emulex.html b/scst/www/target_emulex.html
index 8a8e357..d75b5e7 100644
--- a/scst/www/target_emulex.html
+++ b/scst/www/target_emulex.html
@@ -13,15 +13,15 @@
 <!-- wrap starts here -->
 <div id="wrap">
 	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
+		<div class="logoimg"></div><h1 id="logo"><span class="orange">&nbsp;</span></h1>
 		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
 	</div>
 
 	<div id="menu">
 		<ul>
 			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
+			<li><a href="https://github.com/SCST-project/scst">Main</a></li>
+			<li><a href="https://github.com/SCST-project/scst/releases">News</a></li>
 			<li id="current"><a href="targets.html">Drivers</a></li>
 			<li><a href="downloads.html">Downloads</a></li>
 			<li><a href="contributing.html">Contributing</a></li>
@@ -38,13 +38,9 @@
 					<li><a href="target_iscsi.html">ISCSI-SCST with iSER</a></li>
 					<li><a href="target_qla2x00t.html">QLogic FC qla2x00t</a></li>
 					<li><a href="target_srp.html">SCSI RDMA Protocol (SRP)</a></li>
-					<li><a href="target_mvsas.html">Marvell SAS adapters</a></li>
 					<li><a href="target_emulex.html">Emulex FC/FCoE</a></li>
-					<li><a href="target_lsi.html">LSI/MPT adapters</a></li>
 					<li><a href="target_fcoe.html">FCoE Target</a></li>
 					<li><a href="target_local.html">Local Target Driver</a></li>
-					<li><a href="target_ibmvscsi.html">IBM pSeries Virtual SCSI</a></li>
-					<li><a href="target_old.html">Old Unsupported</a></li>
 				</ul>
 				<h1>User utilities</h1>
 				<ul class="sidemenu">
@@ -72,7 +68,7 @@
 				<p class="post-footer align-right">
 					<a href="https://www.broadcom.com/products/storage/fibre-channel-host-bus-adapters/onecore-storage-sdk" class="readmore">Emulex OneCore Storage Drivers</a>
 				</p>
-				<table border=0><tr><td height="300px">&nbsp;</td></tr></table>
+				<table border=0 summary=""><tr><td style="height: 300px">&nbsp;</td></tr></table>
 	  		</div>
 	<!-- content-wrap ends here -->
 	</div>
@@ -81,7 +77,7 @@
 </div>
 <!-- footer starts here -->
 <div id="footer">
-	<p>&copy; Copyright 2004 - 2020<b><font class="names"> Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
+	<p>&copy; Copyright 2004 - 2021<b><font class="names"> Vladislav Bolkhovitin, Bart Van Assche &amp; others</font></b>&nbsp;&nbsp;
 	Design by: <b><font class="names">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 </div>
 <!-- footer ends here -->
diff --git a/scst/www/target_fcoe.html b/scst/www/target_fcoe.html
index 570e00d..193c853 100644
--- a/scst/www/target_fcoe.html
+++ b/scst/www/target_fcoe.html
@@ -13,15 +13,15 @@
 <!-- wrap starts here -->
 <div id="wrap">
 	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
+		<div class="logoimg"></div><h1 id="logo"><span class="orange">&nbsp;</span></h1>
 		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
 	</div>
 
 	<div id="menu">
 		<ul>
 			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
+			<li><a href="https://github.com/SCST-project/scst">Main</a></li>
+			<li><a href="https://github.com/SCST-project/scst/releases">News</a></li>
 			<li id="current"><a href="targets.html">Drivers</a></li>
 			<li><a href="downloads.html">Downloads</a></li>
 			<li><a href="contributing.html">Contributing</a></li>
@@ -38,13 +38,9 @@
 					<li><a href="target_iscsi.html">ISCSI-SCST with iSER</a></li>
 					<li><a href="target_qla2x00t.html">QLogic FC qla2x00t</a></li>
 					<li><a href="target_srp.html">SCSI RDMA Protocol (SRP)</a></li>
-					<li><a href="target_mvsas.html">Marvell SAS adapters</a></li>
 					<li><a href="target_emulex.html">Emulex FC/FCoE</a></li>
-					<li><a href="target_lsi.html">LSI/MPT adapters</a></li>
 					<li><a href="target_fcoe.html">FCoE Target</a></li>
 					<li><a href="target_local.html">Local Target Driver</a></li>
-					<li><a href="target_ibmvscsi.html">IBM pSeries Virtual SCSI</a></li>
-					<li><a href="target_old.html">Old Unsupported</a></li>
 				</ul>
 				<h1>User utilities</h1>
 				<ul class="sidemenu">
@@ -57,16 +53,17 @@
 				<h1>FCoE target</h1>
 				<p><img src="images/t_fcoe.gif" width="100" height="120" class="float-left" alt="SCST Fcoe">
 				SCST Fibre Channel over Ethernet (FCoE) target is developed by Open-FCoE team and Joe Eykholt.
-				Since February 2010 the main development place of it is SCST SVN repository.
+				Since December 2021 development of this driver
+				happens in the SCST Git repository.
 				</p>
-				<p>You can download the latest development version from the SCST SVN repository. See the download
+				<p>You can download the latest development version from the SCST Git repository. See the download
 				page how to setup access to it.
 				</p><br><br><br><br>
 				<p class="post-footer align-right">
-					<a href="http://sourceforge.net/p/scst/svn/HEAD/tree/" class="readmore">SCST SVN Repository</a>
+					<a href="https://github.com/SCST-project/scst" class="readmore">SCST Git Repository</a>
 					<a href="http://www.open-fcoe.org/" class="readmore">Open-FCoE Site</a>
 				</p>
-				<table border=0><tr><td height="300px">&nbsp;</td></tr></table>
+				<table border=0 summary=""><tr><td style="height: 300px">&nbsp;</td></tr></table>
 	  		</div>
 	<!-- content-wrap ends here -->
 	</div>
@@ -75,7 +72,7 @@
 </div>
 <!-- footer starts here -->
 <div id="footer">
-	<p>&copy; Copyright 2004 - 2020<b><font class="names"> Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
+	<p>&copy; Copyright 2004 - 2021<b><font class="names"> Vladislav Bolkhovitin, Bart Van Assche &amp; others</font></b>&nbsp;&nbsp;
 	Design by: <b><font class="names">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 </div>
 <!-- footer ends here -->
diff --git a/scst/www/target_ibmvscsi.html b/scst/www/target_ibmvscsi.html
deleted file mode 100644
index 6dafd58..0000000
--- a/scst/www/target_ibmvscsi.html
+++ /dev/null
@@ -1,96 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<head>
-<meta name="Keywords" content="SRP target, RDMA target">
-<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
-<meta name="author" content="Daniel Fernandes">
-<meta name="Robots" content="index,follow">
-<link rel="stylesheet" href="images/Orange.css" type="text/css">
-<title>SRP Target Driver</title>
-</head>
-
-<body>
-<!-- wrap starts here -->
-<div id="wrap">
-	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
-		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
-	</div>
-
-	<div id="menu">
-		<ul>
-			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
-			<li id="current"><a href="targets.html">Drivers</a></li>
-			<li><a href="downloads.html">Downloads</a></li>
-			<li><a href="contributing.html">Contributing</a></li>
-			<li><a href="comparison.html">Comparison</a></li>
-			<li><a href="users.html">Users</a></li>
-		</ul>
-	</div>
-
-	<!-- content-wrap starts here -->
-	<div id="content-wrap">
-			<div id="sidebar">
-				<h1>Target Drivers</h1>
-				<ul class="sidemenu">
-					<li><a href="target_iscsi.html">ISCSI-SCST with iSER</a></li>
-					<li><a href="target_qla2x00t.html">QLogic FC qla2x00t</a></li>
-					<li><a href="target_srp.html">SCSI RDMA Protocol (SRP)</a></li>
-					<li><a href="target_mvsas.html">Marvell SAS adapters</a></li>
-					<li><a href="target_emulex.html">Emulex FC/FCoE</a></li>
-					<li><a href="target_lsi.html">LSI/MPT adapters</a></li>
-					<li><a href="target_fcoe.html">FCoE Target</a></li>
-					<li><a href="target_local.html">Local Target Driver</a></li>
-					<li><a href="target_ibmvscsi.html">IBM pSeries Virtual SCSI</a></li>
-					<li><a href="target_old.html">Old Unsupported</a></li>
-				</ul>
-				<h1>User utilities</h1>
-				<ul class="sidemenu">
-					<li><a href="scst_admin.html">SCST Admin Utility</a></li>
-					<li><a href="handler_fileio_tgt.html">FILEIO_TGT handler</a></li>
-				</ul>
-			</div>
-
-	  		<div id="main">
-				<h1>IBM Virtual SCSI Target</h1>
-				<p>The virtual SCSI (VSCSI) protocol as defined in <a href="http://www.power.org" class="readmore">Power Architecture Standard</a>
-				is a protocol that allows one logical partition (LPAR) to access SCSI targets provided by another LPAR.
-				The LPAR that provides one or more SCSI targets is called the VIO server or
-				VIOS. The ibmvstgt driver is a VIOS driver that makes it possible to access
-				exported target devices via the VSCSI protocol.</p>
-				<p>This driver is based on ibmvstgt driver, but comparing to the original ibmvstgt has a number of important fixes and improvements.
-				The port was made by Bart Van Assche.</p>
-				<p>You can download it from the SCST SVN repository. See the download page how to setup access to it.</p>
-				<br><br><br>
-				<p class="post-footer align-right">
-					<a href="http://sourceforge.net/p/scst/svn/HEAD/tree/" class="readmore">SCST SVN Repository</a>
-				</p>
-				<table border=0><tr><td height="300px">&nbsp;</td></tr></table>
-	  		</div>
-	<!-- content-wrap ends here -->
-	</div>
-
-<!-- wrap ends here -->
-</div>
-<!-- footer starts here -->
-<div id="footer">
-	<p>&copy; Copyright 2004 - 2020<b><font class="names"> Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
-	Design by: <b><font class="names">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
-</div>
-<!-- footer ends here -->
-<!-- Piwik -->
-<script type="text/javascript">
-var pkBaseURL = (("https:" == document.location.protocol) ? "https://apps.sourceforge.net/piwik/scst/" : "http://apps.sourceforge.net/piwik/scst/");
-document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
-</script><script type="text/javascript">
-piwik_action_name = '';
-piwik_idsite = 1;
-piwik_url = pkBaseURL + "piwik.php";
-piwik_log(piwik_action_name, piwik_idsite, piwik_url);
-</script>
-<object><noscript><p><img src="http://apps.sourceforge.net/piwik/scst/piwik.php?idsite=1" alt="piwik"></p></noscript></object>
-<!-- End Piwik Tag -->
-</body>
-</html>
diff --git a/scst/www/target_iscsi.html b/scst/www/target_iscsi.html
index 6fee660..a2472fd 100644
--- a/scst/www/target_iscsi.html
+++ b/scst/www/target_iscsi.html
@@ -13,15 +13,15 @@
 <!-- wrap starts here -->
 <div id="wrap">
 	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
+		<div class="logoimg"></div><h1 id="logo"><span class="orange">&nbsp;</span></h1>
 		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
 	</div>
 
 	<div id="menu">
 		<ul>
-			<li><a href="http://scst.sourceforge.net">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
+			<li><a href="index.html">Home</a></li>
+			<li><a href="https://github.com/SCST-project/scst">Main</a></li>
+			<li><a href="https://github.com/SCST-project/scst/releases">News</a></li>
 			<li id="current"><a href="targets.html">Drivers</a></li>
 			<li><a href="downloads.html">Downloads</a></li>
 			<li><a href="contributing.html">Contributing</a></li>
@@ -38,13 +38,9 @@
 					<li><a href="target_iscsi.html">ISCSI-SCST with iSER</a></li>
 					<li><a href="target_qla2x00t.html">QLogic FC qla2x00t</a></li>
 					<li><a href="target_srp.html">SCSI RDMA Protocol (SRP)</a></li>
-					<li><a href="target_mvsas.html">Marvell SAS adapters</a></li>
 					<li><a href="target_emulex.html">Emulex FC/FCoE</a></li>
-					<li><a href="target_lsi.html">LSI/MPT adapters</a></li>
 					<li><a href="target_fcoe.html">FCoE Target</a></li>
 					<li><a href="target_local.html">Local Target Driver</a></li>
-					<li><a href="target_ibmvscsi.html">IBM pSeries Virtual SCSI</a></li>
-					<li><a href="target_old.html">Old Unsupported</a></li>
 				</ul>
 				<h1>User utilities</h1>
 				<ul class="sidemenu">
@@ -66,8 +62,9 @@
 				the SCST core. You can also use a migration tool developed by Scalable Informatics Inc., which will
 				convert your IET machine to an iSCSI-SCST machine. See README for more details.</p>
 
-				<p>You can find the latest development version of this driver in the SCST SVN. See the download page how to setup
-				access to it.</p>
+				<p>You can find the latest development version
+				of this driver in the SCST Git repository. See
+				the download page how to setup access to it.</p>
 
 				<h1>Certification</h1>
 
@@ -82,14 +79,14 @@
 					<a href="http://community.mellanox.com/docs/DOC-1479" class="readmore">iSER HOWTO</a>
 					<a href="SCST_Gentoo_HOWTO.txt" class="readmore">Gentoo HOWTO</a>
 					<a href="downloads.html" class="readmore">Download</a>
-					<a href="http://sourceforge.net/p/scst/svn/HEAD/tree/" class="readmore">SCST SVN Repository</a>
+					<a href="https://github.com/SCST-project/scst" class="readmore">SCST Git Repository</a>
 				</p>
 	  		</div>
 	</div>
 </div>
 <!-- footer starts here -->
 <div id="footer">
-	<p>&copy; Copyright 2004 - 2020<b><font class="names"> Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
+	<p>&copy; Copyright 2004 - 2021<b><font class="names"> Vladislav Bolkhovitin, Bart Van Assche &amp; others</font></b>&nbsp;&nbsp;
 	Design by: <b><font class="names">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 </div>
 <!-- footer ends here -->
diff --git a/scst/www/target_local.html b/scst/www/target_local.html
index 7125d70..7ba802a 100644
--- a/scst/www/target_local.html
+++ b/scst/www/target_local.html
@@ -13,15 +13,15 @@
 <!-- wrap starts here -->
 <div id="wrap">
 	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
+		<div class="logoimg"></div><h1 id="logo"><span class="orange">&nbsp;</span></h1>
 		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
 	</div>
 
 	<div id="menu">
 		<ul>
 			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
+			<li><a href="https://github.com/SCST-project/scst">Main</a></li>
+			<li><a href="https://github.com/SCST-project/scst/releases">News</a></li>
 			<li id="current"><a href="targets.html">Drivers</a></li>
 			<li><a href="downloads.html">Downloads</a></li>
 			<li><a href="contributing.html">Contributing</a></li>
@@ -38,13 +38,9 @@
 					<li><a href="target_iscsi.html">ISCSI-SCST with iSER</a></li>
 					<li><a href="target_qla2x00t.html">QLogic FC qla2x00t</a></li>
 					<li><a href="target_srp.html">SCSI RDMA Protocol (SRP)</a></li>
-					<li><a href="target_mvsas.html">Marvell SAS adapters</a></li>
 					<li><a href="target_emulex.html">Emulex FC/FCoE</a></li>
-					<li><a href="target_lsi.html">LSI/MPT adapters</a></li>
 					<li><a href="target_fcoe.html">FCoE Target</a></li>
 					<li><a href="target_local.html">Local Target Driver</a></li>
-					<li><a href="target_ibmvscsi.html">IBM pSeries Virtual SCSI</a></li>
-					<li><a href="target_old.html">Old Unsupported</a></li>
 				</ul>
 				<h1>User utilities</h1>
 				<ul class="sidemenu">
@@ -69,13 +65,13 @@
 				<p>This driver was made by Richard Sharpe.</p>
 
 				<p>You can download
-				the latest development version from the SCST SVN repository. See the
+				the latest development version from the SCST Git repository. See the
 				download page how to setup access to it. </p><br><br><br>
 				<p class="post-footer align-right">
 					<a href="downloads.html" class="readmore">Download</a>
-					<a href="http://sourceforge.net/p/scst/svn/HEAD/tree/" class="readmore">SCST SVN Repository</a>
+					<a href="https://github.com/SCST-project/scst" class="readmore">SCST Git Repository</a>
 				</p>
-				<table border=0><tr><td height="300px">&nbsp;</td></tr></table>
+				<table border=0 summary=""><tr><td style="height: 300px">&nbsp;</td></tr></table>
 	  		</div>
 	<!-- content-wrap ends here -->
 	</div>
@@ -84,7 +80,7 @@
 </div>
 <!-- footer starts here -->
 <div id="footer">
-	<p>&copy; Copyright 2004 - 2020<b><font class="names"> Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
+	<p>&copy; Copyright 2004 - 2021<b><font class="names"> Vladislav Bolkhovitin, Bart Van Assche &amp; others</font></b>&nbsp;&nbsp;
 	Design by: <b><font class="names">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 </div>
 <!-- footer ends here -->
diff --git a/scst/www/target_lsi.html b/scst/www/target_lsi.html
index ad6a95b..2574cca 100644
--- a/scst/www/target_lsi.html
+++ b/scst/www/target_lsi.html
@@ -13,15 +13,15 @@
 <!-- wrap starts here -->
 <div id="wrap">
 	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
+		<div class="logoimg"></div><h1 id="logo"><span class="orange">&nbsp;</span></h1>
 		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
 	</div>
 
 	<div id="menu">
 		<ul>
 			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
+			<li><a href="https://github.com/SCST-project/scst">Main</a></li>
+			<li><a href="https://github.com/SCST-project/scst/releases">News</a></li>
 			<li id="current"><a href="targets.html">Drivers</a></li>
 			<li><a href="downloads.html">Downloads</a></li>
 			<li><a href="contributing.html">Contributing</a></li>
@@ -38,13 +38,9 @@
 					<li><a href="target_iscsi.html">ISCSI-SCST with iSER</a></li>
 					<li><a href="target_qla2x00t.html">QLogic FC qla2x00t</a></li>
 					<li><a href="target_srp.html">SCSI RDMA Protocol (SRP)</a></li>
-					<li><a href="target_mvsas.html">Marvell SAS adapters</a></li>
 					<li><a href="target_emulex.html">Emulex FC/FCoE</a></li>
-					<li><a href="target_lsi.html">LSI/MPT adapters</a></li>
 					<li><a href="target_fcoe.html">FCoE Target</a></li>
 					<li><a href="target_local.html">Local Target Driver</a></li>
-					<li><a href="target_ibmvscsi.html">IBM pSeries Virtual SCSI</a></li>
-					<li><a href="target_old.html">Old Unsupported</a></li>
 				</ul>
 				<h1>User utilities</h1>
 				<ul class="sidemenu">
@@ -52,26 +48,6 @@
 					<li><a href="handler_fileio_tgt.html">FILEIO_TGT handler</a></li>
 				</ul>
 			</div>
-
-	  		<div id="main">
-				<h1>Target driver for LSI/MPT adapters</h1>
-				<p><img src="images/t_lsi.gif" width="100" height="120" class="float-left" alt="SCST LSI">
-				Target driver for LSI/MPT adapters was originally developed by Hu Gang, then Erik Habbinga has continued the development. </p>
-
-				<p>It supports parallel SCSI (SPI), including Wide SCSI, and Fibre Channel, but also should work with SAS. This driver is on the
-				alpha stage and available for download from the SCST SVN repository. See the download page how to setup access to it.
-				</p>
-
-				<p>Recently Theodore Vaida updated it for the latest hardware generation, including 12G support. You can download current version
-				from <a href="https://github.com/exactassembly/meta-xa-stm">Github</a>.</p>
-
-				<br><br><br>
-				<p class="post-footer align-right">
-					<a href="http://sourceforge.net/p/scst/svn/HEAD/tree/" class="readmore">SCST SVN Repository</a>
-					<a href="https://github.com/exactassembly/meta-xa-stm" class="readmore">Github</a>
-				</p>
-				<table border=0><tr><td height="300px">&nbsp;</td></tr></table>
-	  		</div>
 	<!-- content-wrap ends here -->
 	</div>
 
@@ -79,7 +55,7 @@
 </div>
 <!-- footer starts here -->
 <div id="footer">
-	<p>&copy; Copyright 2004 - 2020<b><font class="names"> Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
+	<p>&copy; Copyright 2004 - 2021<b><font class="names"> Vladislav Bolkhovitin, Bart Van Assche &amp; others</font></b>&nbsp;&nbsp;
 	Design by: <b><font class="names">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 </div>
 <!-- footer ends here -->
diff --git a/scst/www/target_mvsas.html b/scst/www/target_mvsas.html
deleted file mode 100644
index 842ee8a..0000000
--- a/scst/www/target_mvsas.html
+++ /dev/null
@@ -1,93 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<head>
-<meta name="Keywords" content="SCSI target, SAS target, Marvell target">
-<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
-<meta name="author" content="Daniel Fernandes">
-<meta name="Robots" content="index,follow">
-<link rel="stylesheet" href="images/Orange.css" type="text/css">
-<title>Marvell SAS Target Driver</title>
-</head>
-
-<body>
-<!-- wrap starts here -->
-<div id="wrap">
-	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
-		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
-	</div>
-
-	<div id="menu">
-		<ul>
-			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
-			<li id="current"><a href="targets.html">Drivers</a></li>
-			<li><a href="downloads.html">Downloads</a></li>
-			<li><a href="contributing.html">Contributing</a></li>
-			<li><a href="comparison.html">Comparison</a></li>
-			<li><a href="users.html">Users</a></li>
-		</ul>
-	</div>
-
-	<!-- content-wrap starts here -->
-	<div id="content-wrap">
-			<div id="sidebar">
-				<h1>Target Drivers</h1>
-				<ul class="sidemenu">
-					<li><a href="target_iscsi.html">ISCSI-SCST with iSER</a></li>
-					<li><a href="target_qla2x00t.html">QLogic FC qla2x00t</a></li>
-					<li><a href="target_srp.html">SCSI RDMA Protocol (SRP)</a></li>
-					<li><a href="target_mvsas.html">Marvell SAS adapters</a></li>
-					<li><a href="target_emulex.html">Emulex FC/FCoE</a></li>
-					<li><a href="target_lsi.html">LSI/MPT adapters</a></li>
-					<li><a href="target_fcoe.html">FCoE Target</a></li>
-					<li><a href="target_local.html">Local Target Driver</a></li>
-					<li><a href="target_ibmvscsi.html">IBM pSeries Virtual SCSI</a></li>
-					<li><a href="target_old.html">Old Unsupported</a></li>
-				</ul>
-				<h1>User utilities</h1>
-				<ul class="sidemenu">
-					<li><a href="scst_admin.html">SCST Admin Utility</a></li>
-					<li><a href="handler_fileio_tgt.html">FILEIO_TGT handler</a></li>
-				</ul>
-			</div>
-
-	  		<div id="main">
-				<h1>Target driver for Marvell SAS adapters</h1>
-				<p><img src="images/t_sas.gif" width="100" height="120" class="float-left" alt="SCST Marvell SAS">
-				<p>Target driver for Marvell SAS adapters is developed by Marvell and Andy Yan. It is fully functional
-				SAS target driver.</p>
-
-				<p>It is on the beta stage. You can download it from the SCST SVN repository. See the download page how
-				to setup access to it. </p><br><br><br>
-				<p class="post-footer align-right">
-					<a href="http://sourceforge.net/p/scst/svn/HEAD/tree/" class="readmore">SCST SVN Repository</a>
-				</p>
-				<table border=0><tr><td height="300px">&nbsp;</td></tr></table>
-	  		</div>
-	<!-- content-wrap ends here -->
-	</div>
-
-<!-- wrap ends here -->
-</div>
-<!-- footer starts here -->
-<div id="footer">
-	<p>&copy; Copyright 2004 - 2020<b><font class="names">Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
-	Design by: <b><font class="names">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
-</div>
-<!-- footer ends here -->
-<!-- Piwik -->
-<script type="text/javascript">
-var pkBaseURL = (("https:" == document.location.protocol) ? "https://apps.sourceforge.net/piwik/scst/" : "http://apps.sourceforge.net/piwik/scst/");
-document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
-</script><script type="text/javascript">
-piwik_action_name = '';
-piwik_idsite = 1;
-piwik_url = pkBaseURL + "piwik.php";
-piwik_log(piwik_action_name, piwik_idsite, piwik_url);
-</script>
-<object><noscript><p><img src="http://apps.sourceforge.net/piwik/scst/piwik.php?idsite=1" alt="piwik"></p></noscript></object>
-<!-- End Piwik Tag -->
-</body>
-</html>
diff --git a/scst/www/target_old.html b/scst/www/target_old.html
deleted file mode 100644
index 04faa80..0000000
--- a/scst/www/target_old.html
+++ /dev/null
@@ -1,129 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<head>
-<meta name="Keywords" content="SCST Old Unsupported Target Drivers">
-<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
-<meta name="author" content="Daniel Fernandes">
-<meta name="Robots" content="index,follow">
-<link rel="stylesheet" href="images/Orange.css" type="text/css">
-<title>Old Unsupported SCST Target Drivers</title>
-</head>
-
-<body>
-<!-- wrap starts here -->
-<div id="wrap">
-	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
-		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
-	</div>
-
-	<div id="menu">
-		<ul>
-			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
-			<li id="current"><a href="targets.html">Drivers</a></li>
-			<li><a href="downloads.html">Downloads</a></li>
-			<li><a href="contributing.html">Contributing</a></li>
-			<li><a href="comparison.html">Comparison</a></li>
-			<li><a href="users.html">Users</a></li>
-		</ul>
-	</div>
-
-	<!-- content-wrap starts here -->
-	<div id="content-wrap">
-			<div id="sidebar">
-				<h1>Target Drivers</h1>
-				<ul class="sidemenu">
-					<li><a href="target_iscsi.html">ISCSI-SCST with iSER</a></li>
-					<li><a href="target_qla2x00t.html">QLogic FC qla2x00t</a></li>
-					<li><a href="target_srp.html">SCSI RDMA Protocol (SRP)</a></li>
-					<li><a href="target_mvsas.html">Marvell SAS adapters</a></li>
-					<li><a href="target_emulex.html">Emulex FC/FCoE</a></li>
-					<li><a href="target_lsi.html">LSI/MPT adapters</a></li>
-					<li><a href="target_fcoe.html">FCoE Target</a></li>
-					<li><a href="target_local.html">Local Target Driver</a></li>
-					<li><a href="target_ibmvscsi.html">IBM pSeries Virtual SCSI</a></li>
-					<li><a href="target_old.html">Old Unsupported</a></li>
-				</ul>
-				<h1>User utilities</h1>
-				<ul class="sidemenu">
-					<li><a href="scst_admin.html">SCST Admin Utility</a></li>
-					<li><a href="handler_fileio_tgt.html">FILEIO_TGT handler</a></li>
-				</ul>
-			</div>
-
-	  		<div id="main">
-	  			<h1>Target driver for QLogic ISP chipsets</h1>
-				<p><img src="images/t_unsupported.gif" width="100" height="120" class="float-left" alt="SCST Unsupported">
-				This is an SCST driver for ISP QLogic chipsets commonly used in many SCSI and FC host bus adapters.
-				It is based on Matthew Jacob's (<a href="http://www.feral.com">http://www.feral.com</a>)
-				multiplatform driver for ISP chipsets. Update for SCST was made by Stanislaw Gruszka for Open-E Inc.</p>
-
-				<p>The latest release is 1.0.2. It supports kernel versions between 2.6.16 and 2.6.32.</p>
-				<p>This driver is obsoleted in favor of qla2x00t.</p>
-				<br>
-
-
-				<h1>Old target driver for QLogic qla2x00t adapters for 2.4 kernels</h1>
-				<p><img src="images/t_unsupported.gif" width="100" height="120" class="float-left" alt="SCST Unsupported">
-				Old target driver for QLogic qla2x00t adapters is capable to work on 2.4 kernels.
-				It has all required features and looks to be quite stable. It is designed to work in conjunction with the
-				initiator driver, which is intended to perform all the initialization and shutdown tasks. In the current release as
-				a base for the initiator driver was taken Red Hat's driver from the stock 2.4.20 kernel. Then it was patched to
-				enable the target mode and provide all necessary callbacks, and it's still able to work as initiator only. Mode,
-				when a host acts as the initiator and the target simultaneously, is also supported. This driver is obsoleted in
-				favor of 2.6-based driver.</p>
-				<p>The latest version is 0.9.3.4. Requires Linux kernel versions 2.4.20 or higher and SCST version 0.9.3-pre4 or
-				higher. If you are lucky, it works also on 2.6 kernels, see README file for details. Tested on i386 only, but
-				should work on any other supported by Linux platform.</p>
-				<p>Currently it is <strong>not supported</strong> and listed here for historical reasons only.</p>
-				<br>
-
-				<h1>Target drivers for Adaptec 7xxx and QLogic QLA12xx adapters</h1>
-				<p><img src="images/t_unsupported.gif" width="100" height="120" class="float-left" alt="SCST Unsupported">
-				Target drivers for Adaptec 7xxx and QLogic QLA12xx adapters have been developed by Hu Gang and they available for
-				download from <a href="http://bj.soulinfo.com/~hugang/scst/tgt/">http://bj.soulinfo.com/~hugang/scst/tgt/</a>.
-				These drivers are not completed, but looks to be a good starting point if you are going to use one of these adapters.
-				SCST team don't have the appropriate hardware, therefore have not tested and don't support these drivers.
-				Send all questions to <strong>Hu Gang < hugang at soulinfo com ></strong>. If some of these drivers don't compile for
-				you, try again with SCST version 0.9.3-pre2.</p><br><br>
-				<br>
-
-				<h1>Patches for UNH-iSCSI Target 1.5.03 and 1.6.00 to SCST</h1>
-				<p><img src="images/t_unsupported.gif" width="100" height="120" class="float-left" alt="SCST Unsupported">
-				SCST is much more advanced, than the internal mid-level of <a href="http://sourceforge.net/projects/unh-iscsi">
-				UNH-iSCSI target driver</a>. With SCST the iSCSI target benefits from all its features and gets ability to use all
-				its advantages, like high performance and scalability, SMP support, required SCSI functionality emulation, etc.</p>
-
-				<p>Since the interface between SCST and the target drivers is based on work, done by UNH IOL, it was relatively
-				simple to update UNH-iSCSI target to work over SCST. Mostly it was "search and replace" job. The built-in
-				scsi_target remains available as a compile-time option.</p>
-
-				<p>Requires Linux kernel versions 2.4.20 or higher or 2.6.7 or higher and SCST version 0.9.2 or higher.</p>
-				<p>Currently it is <strong>not supported</strong> and listed here for historical reasons only.</p>
-	<!-- content-wrap ends here -->
-			</div>
-	</div>
-<!-- wrap ends here -->
-</div>
-<!-- footer starts here -->
-<div id="footer">
-	<p>&copy; Copyright 2004 - 2020<b><font class="names"> Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
-	Design by: <b><font class="names">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
-</div>
-<!-- footer ends here -->
-<!-- Piwik -->
-<script type="text/javascript">
-var pkBaseURL = (("https:" == document.location.protocol) ? "https://apps.sourceforge.net/piwik/scst/" : "http://apps.sourceforge.net/piwik/scst/");
-document.write(unescape("%3Cscript src='" + pkBaseURL + "piwik.js' type='text/javascript'%3E%3C/script%3E"));
-</script><script type="text/javascript">
-piwik_action_name = '';
-piwik_idsite = 1;
-piwik_url = pkBaseURL + "piwik.php";
-piwik_log(piwik_action_name, piwik_idsite, piwik_url);
-</script>
-<object><noscript><p><img src="http://apps.sourceforge.net/piwik/scst/piwik.php?idsite=1" alt="piwik"></p></noscript></object>
-<!-- End Piwik Tag -->
-</body>
-</html>
diff --git a/scst/www/target_qla2x00t.html b/scst/www/target_qla2x00t.html
index 1bb7d79..195e3d0 100644
--- a/scst/www/target_qla2x00t.html
+++ b/scst/www/target_qla2x00t.html
@@ -13,15 +13,15 @@
 <!-- wrap starts here -->
 <div id="wrap">
 	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
+		<div class="logoimg"></div><h1 id="logo"><span class="orange">&nbsp;</span></h1>
 		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
 	</div>
 
 	<div id="menu">
 		<ul>
 			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
+			<li><a href="https://github.com/SCST-project/scst">Main</a></li>
+			<li><a href="https://github.com/SCST-project/scst/releases">News</a></li>
 			<li id="current"><a href="targets.html">Drivers</a></li>
 			<li><a href="downloads.html">Downloads</a></li>
 			<li><a href="contributing.html">Contributing</a></li>
@@ -38,13 +38,9 @@
 					<li><a href="target_iscsi.html">ISCSI-SCST with iSER</a></li>
 					<li><a href="target_qla2x00t.html">QLogic FC qla2x00t</a></li>
 					<li><a href="target_srp.html">SCSI RDMA Protocol (SRP)</a></li>
-					<li><a href="target_mvsas.html">Marvell SAS adapters</a></li>
 					<li><a href="target_emulex.html">Emulex FC/FCoE</a></li>
-					<li><a href="target_lsi.html">LSI/MPT adapters</a></li>
 					<li><a href="target_fcoe.html">FCoE Target</a></li>
 					<li><a href="target_local.html">Local Target Driver</a></li>
-					<li><a href="target_ibmvscsi.html">IBM pSeries Virtual SCSI</a></li>
-					<li><a href="target_old.html">Old Unsupported</a></li>
 				</ul>
 				<h1>User utilities</h1>
 				<ul class="sidemenu">
@@ -78,10 +74,10 @@
 					<a href="qla2x00t-howto.html" class="readmore">HOWTO</a>
 					<a href="https://blog.it-kb.ru/2018/03/12/configure-the-server-with-qlogic-fc-hba-on-debian-linux-9-and-scst-fc-target-as-storage-for-csv-volumes-in-the-hyper-v-cluster-for-highly-available-virtual-machines/" class="readmore"> Russian HOWTO</a>
 					<a href="downloads.html" class="readmore">Download</a>
-					<a href="http://sourceforge.net/p/scst/svn/HEAD/tree/trunk" class="readmore">SCST SVN trunk</a>
+					<a href="https://github.com/SCST-project/scst" class="readmore">SCST Git Repository</a>
 					<a href="scst-qla2xxx-unified-20180330.tgz" class="readmore">QLA git snapshot</a>
 				</p>
-				<table border=0><tr><td height="300px">&nbsp;</td></tr></table>
+				<table border=0 summary=""><tr><td style="height: 300px">&nbsp;</td></tr></table>
 	  		</div>
 	<!-- content-wrap ends here -->
 	</div>
@@ -90,7 +86,7 @@
 </div>
 <!-- footer starts here -->
 <div id="footer">
-	<p>&copy; Copyright 2004 - 2020<b><font class="names"> Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
+	<p>&copy; Copyright 2004 - 2021<b><font class="names"> Vladislav Bolkhovitin, Bart Van Assche &amp; others</font></b>&nbsp;&nbsp;
 	Design by: <b><font class="names">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 </div>
 <!-- footer ends here -->
diff --git a/scst/www/target_srp.html b/scst/www/target_srp.html
index 826abf3..784404d 100644
--- a/scst/www/target_srp.html
+++ b/scst/www/target_srp.html
@@ -13,15 +13,15 @@
 <!-- wrap starts here -->
 <div id="wrap">
 	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
+		<div class="logoimg"></div><h1 id="logo"><span class="orange">&nbsp;</span></h1>
 		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
 	</div>
 
 	<div id="menu">
 		<ul>
 			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
+			<li><a href="https://github.com/SCST-project/scst">Main</a></li>
+			<li><a href="https://github.com/SCST-project/scst/releases">News</a></li>
 			<li id="current"><a href="targets.html">Drivers</a></li>
 			<li><a href="downloads.html">Downloads</a></li>
 			<li><a href="contributing.html">Contributing</a></li>
@@ -38,13 +38,9 @@
 					<li><a href="target_iscsi.html">ISCSI-SCST with iSER</a></li>
 					<li><a href="target_qla2x00t.html">QLogic FC qla2x00t</a></li>
 					<li><a href="target_srp.html">SCSI RDMA Protocol (SRP)</a></li>
-					<li><a href="target_mvsas.html">Marvell SAS adapters</a></li>
 					<li><a href="target_emulex.html">Emulex FC/FCoE</a></li>
-					<li><a href="target_lsi.html">LSI/MPT adapters</a></li>
 					<li><a href="target_fcoe.html">FCoE Target</a></li>
 					<li><a href="target_local.html">Local Target Driver</a></li>
-					<li><a href="target_ibmvscsi.html">IBM pSeries Virtual SCSI</a></li>
-					<li><a href="target_old.html">Old Unsupported</a></li>
 				</ul>
 				<h1>User utilities</h1>
 				<ul class="sidemenu">
@@ -55,18 +51,29 @@
 
 	  		<div id="main">
 				<h1>Infiniband SCSI RDMA protocol (SRP) target driver</h1>
-				<p><img src="images/t_rdma.gif" width="100" height="120" class="float-left" alt="SCST SRP">
-				SCSI RDMA Protocol (SRP) target driver has been developed by Vu Pham. Since March
-				2008 the main development place of the SRP target driver is SCST SVN repository.
-				It is maintained by Bart Van Assche.</p>
-				<p>This driver is mainline Linux kernel ready and going to be pushed to it
-				together with other SCST patches.</p>
+				<p><img src="images/t_rdma.gif" width="100"
+				height="120" class="float-left" alt="SCST
+				SRP">The first version of the SCSI RDMA
+				Protocol (SRP) target driver has been written
+				by Vu Pham. Since March 2008 the main
+				development place of the SRP target driver is
+				SCST Git repository. The current maintainer is
+				Bart Van Assche.</p>
+				<p>The original version of this driver only
+				supported <a href="https://en.wikipedia.org/wiki/InfiniBand">InfiniBand</a>
+				networks, hence the "ib" in ib_srpt. The
+				current version supports InfiniBand,
+				<a href="https://en.wikipedia.org/wiki/RDMA_over_Converged_Ethernet">RoCE</a>
+				and <a href="https://en.wikipedia.org/wiki/IWARP">iWARP</a>.</p>
+				<p>A version of this driver is available in
+				  the upstream Linux kernel. See
+				  also <a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/ulp/srpt">drivers/infiniband/ulp/srpt</a>.</p>
 				<br><br><br>
 				<p class="post-footer align-right">
 					<a href="downloads.html" class="readmore">Download</a>
-					<a href="http://sourceforge.net/p/scst/svn/HEAD/tree/" class="readmore">SCST SVN Repository</a>
+					<a href="https://github.com/SCST-project/scst" class="readmore">SCST Git Repository</a>
 				</p>
-				<table border=0><tr><td height="300px">&nbsp;</td></tr></table>
+				<table border=0 summary=""><tr><td style="height: 300px">&nbsp;</td></tr></table>
 	  		</div>
 	<!-- content-wrap ends here -->
 	</div>
@@ -75,7 +82,7 @@
 </div>
 <!-- footer starts here -->
 <div id="footer">
-	<p>&copy; Copyright 2004 - 2020<b><font class="names"> Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
+	<p>&copy; Copyright 2004 - 2021<b><font class="names"> Vladislav Bolkhovitin, Bart Van Assche &amp; others</font></b>&nbsp;&nbsp;
 	Design by: <b><font class="names">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 </div>
 <!-- footer ends here -->
diff --git a/scst/www/targets.html b/scst/www/targets.html
index e58949b..4f219be 100644
--- a/scst/www/targets.html
+++ b/scst/www/targets.html
@@ -6,22 +6,22 @@
 <meta name="author" content="Daniel Fernandes">
 <meta name="Robots" content="index,follow">
 <link rel="stylesheet" href="images/Orange.css" type="text/css">
-<title>SCST SCSI Target Drivers &amp Utilities</title>
+<title>SCST SCSI Target Drivers &amp; Utilities</title>
 </head>
 
 <body>
 <!-- wrap starts here -->
 <div id="wrap">
 	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
+		<div class="logoimg"></div><h1 id="logo"><span class="orange">&nbsp;</span></h1>
 		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
 	</div>
 
 	<div id="menu">
 		<ul>
 			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
+			<li><a href="https://github.com/SCST-project/scst">Main</a></li>
+			<li><a href="https://github.com/SCST-project/scst/releases">News</a></li>
 			<li id="current"><a href="targets.html">Drivers</a></li>
 			<li><a href="downloads.html">Downloads</a></li>
 			<li><a href="contributing.html">Contributing</a></li>
@@ -38,13 +38,9 @@
 					<li><a href="target_iscsi.html">ISCSI-SCST with iSER</a></li>
 					<li><a href="target_qla2x00t.html">QLogic FC qla2x00t</a></li>
 					<li><a href="target_srp.html">SCSI RDMA Protocol (SRP)</a></li>
-					<li><a href="target_mvsas.html">Marvell SAS adapters</a></li>
 					<li><a href="target_emulex.html">Emulex FC/FCoE</a></li>
-					<li><a href="target_lsi.html">LSI/MPT adapters</a></li>
 					<li><a href="target_fcoe.html">FCoE Target</a></li>
 					<li><a href="target_local.html">Local Target Driver</a></li>
-					<li><a href="target_ibmvscsi.html">IBM pSeries Virtual SCSI</a></li>
-					<li><a href="target_old.html">Old Unsupported</a></li>
 				</ul>
 				<h1>User utilities</h1>
 				<ul class="sidemenu">
@@ -61,22 +57,19 @@
 						<li><span>iSCSI with iSER</span></li>
 						<li><span>Fibre Channel QLogic qla2xxx series</span></li>
 						<li><span>Infiniband SCSI RDMA Protocol (SRP)</span></li>
-						<li><span>Marvell SAS adapters</span></li>
 						<li><span>Emulex FC/FCoE</span></li>
-						<li><span>LSI/MPT adapters (parallel SCSI, including Wide Ultra320, SAS, Fibre Channel)</span></li>
 						<li><span>FCoE</span></li>
 						<li><span>Local access</span></li>
-						<li><span>IBM pSeries Virtual SCSI</span></li>
 						<li><span>...</span></li>
 					</ul>
 
-				<table border=0><tr><td height="300px">&nbsp;</td></tr></table>
+				<table border=0 summary=""><tr><td style="height: 300px">&nbsp;</td></tr></table>
 	  		</div>
 	</div>
 </div>
 <!-- footer starts here -->
 <div id="footer">
-	<p>&copy; Copyright 2004 - 2020<b><font class="names"> Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
+	<p>&copy; Copyright 2004 - 2021<b><font class="names"> Vladislav Bolkhovitin, Bart Van Assche &amp; others</font></b>&nbsp;&nbsp;
 	Design by: <b><font class="names">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 </div>
 <!-- footer ends here -->
diff --git a/scst/www/users.html b/scst/www/users.html
index 7b01897..4599a52 100644
--- a/scst/www/users.html
+++ b/scst/www/users.html
@@ -15,15 +15,15 @@
 <!-- wrap starts here -->
 <div id="wrap">
 	<div id="header">
-		<div class="logoimg"></div><h1 id="logo"><span class="orange"></span></h1>
+		<div class="logoimg"></div><h1 id="logo"><span class="orange">&nbsp;</span></h1>
 		<h2 id=slogan>Generic SCSI Target Subsystem for Linux</h2>
 	</div>
 
 	<div id="menu">
 		<ul>
 			<li><a href="index.html">Home</a></li>
-			<li><a href="http://www.sourceforge.net/projects/scst">Main</a></li>
-			<li><a href="http://sourceforge.net/news/?group_id=110471">News</a></li>
+			<li><a href="https://github.com/SCST-project/scst">Main</a></li>
+			<li><a href="https://github.com/SCST-project/scst/releases">News</a></li>
 			<li><a href="targets.html">Drivers</a></li>
 			<li><a href="downloads.html">Downloads</a></li>
 			<li><a href="contributing.html">Contributing</a></li>
@@ -41,7 +41,7 @@
 
 				<span class="companysubtitles">Companies developed SCST target drivers for their adapters</span>
 
-				<table width="100%" border="0" cellpadding="0" cellspacing="0" style="margin: 10px 15px;">
+				<table width="100%" border="0" cellpadding="0" cellspacing="0" style="margin: 10px 15px;" summary="">
 				<tr>
 					<td class="companybox" width="33%">
 						<a href="http://qlogic.com"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/a/a0/QLogic_Logo2.png/300px-QLogic_Logo2.png" style="height: 80px" alt="QLogic"></a></td>
@@ -62,7 +62,7 @@
 
 				<span class="companysubtitles">Companies using SCST in their products and solutions</span>
 
-				<table width="100%" border="0" cellpadding="0" cellspacing="0" style="margin: 10px 15px;">
+				<table width="100%" border="0" cellpadding="0" cellspacing="0" style="margin: 10px 15px;" summary="">
 				<tr>
 					<td class="companybox" width="33%">
 						<a href="https://www.onestopsystems.com/"><img src="https://www.onestopsystems.com/sites/default/files/onestopsystems-logo.png" style="width: 180px; height: 60px" alt="One Stop Systems"></a></td>
@@ -139,7 +139,7 @@
 
 				<span class="companysubtitles">Companies using SCST for their internal storage infrastructure</span>
 
-				<table width="100%" border="0" cellpadding="0" cellspacing="0" style="margin: 10px 15px;">
+				<table width="100%" border="0" cellpadding="0" cellspacing="0" style="margin: 10px 15px;" summary="">
 				<tr>
 					<td class="companybox" width="33%">
 						<a href="http://www.datacrunch.net"><img src="http://www.datacrunch.net/images/general/dc_logo.png" alt="DataCrunch Company"></a></td>
@@ -157,13 +157,13 @@
 				SCST-powered product or solution, we will be proud to write on our pages that SCST engine has successfully
 				passed the certification tests. This is the least appreciation your company can do for SCST.</p>
 
-				<table border=0><tr><td height="15px">&nbsp;</td></tr></table>
+				<table border=0 summary=""><tr><td style="height: 15px">&nbsp;</td></tr></table>
 	  		</div>
 	</div>
 </div>
 <!-- footer starts here -->
 <div id="footer">
-	<p>&copy; Copyright 2004 - 2020<b><font class="names"> Vladislav Bolkhovitin &amp others</font></b>&nbsp;&nbsp;
+	<p>&copy; Copyright 2004 - 2021<b><font class="names"> Vladislav Bolkhovitin, Bart Van Assche &amp; others</font></b>&nbsp;&nbsp;
 	Design by: <b><font class="names">Daniel Fernandes</font></b>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>
 </div>
 <!-- footer ends here -->